[comp.binaries.ibm.pc.d] UNSHAR

wkt@csadfa.cs.adfa.oz.au (Warren Toomey) (12/19/89)

This is the WRONG newsgroup to post to, but I saw somebody ask for
an Unshar for the PC. I knocked this up a few weeks ago, and although
it was intended to run under Unix, it does compile & run under MS-DOS.

Modifications may be needed to get it to work with _every_ shell archive!

	Hope this is what you wanted!		Warren Toomey

----ok, you have to use Unix, or manually unshar this shell archive -----

#!/bin/sh
# to extract, remove the header and type "sh filename"
if `test ! -s Makefile`
then
echo "writing Makefile"
cat > Makefile << '\Rogue\Monster\'
O= s

unshar: Makefile unshar.$(O)
	cc -o unshar unshar.$(O) -lgetopt
	strip unshar
	chmem =10000 unshar

unshar.$(O): Makefile unshar.c unshar.h
	cc -c -O unshar.c

shar: README Makefile unshar.h unshar.c unshar.1
	shar README Makefile unshar.h unshar.c unshar.1 > unshar.shar
\Rogue\Monster\
else
  echo "will not over write Makefile"
fi
if `test ! -s README`
then
echo "writing README"
cat > README << '\Rogue\Monster\'
Here is a version of unshar that I have `knocked up' in the past few weeks.
It isn't as flash as the standard unshar, but I've made it modular so it
can be enhanced easily.

However, it has some advantages:

	- it does not call any other program, especially /bin/sh
	- it can extract specific files from the shar
	- can give a table of contents without extracting files
	- takes several input files, or the standard input
	- ignores leading `junk' e.g news headers
	- emulates sed, gres and cat to do the unsharing
	- avoids the Minix `cat /dev/null > file' problem :-)
	- it works `as is' under MS-DOS (needs getopt)

I've used it to unshar lots of different files; there are bound to be
some that it can't extract, but fixing the program should be easy.

	Warren Toomey  -  wtoomey@csadfa.oz.au@munnari.oz[@uunet]
\Rogue\Monster\
else
  echo "will not over write README"
fi
if `test ! -s getopt.c`
then
echo "writing getopt.c"
cat > getopt.c << '\Rogue\Monster\'
/*
**	@(#)getopt.c	2.2 (smail) 1/26/87
*/

/*
 * Here's something you've all been waiting for:  the AT&T public domain
 * source for getopt(3).  It is the code which was given out at the 1985
 * UNIFORUM conference in Dallas.  I obtained it by electronic mail
 * directly from AT&T.  The people there assure me that it is indeed
 * in the public domain.
 * 
 * There is no manual page.  That is because the one they gave out at
 * UNIFORUM was slightly different from the current System V Release 2
 * manual page.  The difference apparently involved a note about the
 * famous rules 5 and 6, recommending using white space between an option
 * and its first argument, and not grouping options that have arguments.
 * Getopt itself is currently lenient about both of these things White
 * space is allowed, but not mandatory, and the last option in a group can
 * have an argument.  That particular version of the man page evidently
 * has no official existence, and my source at AT&T did not send a copy.
 * The current SVR2 man page reflects the actual behavor of this getopt.
 * However, I am not about to post a copy of anything licensed by AT&T.
 */

#ifdef BSD
#include <strings.h>
#else
#include <string.h>
#endif

/*LINTLIBRARY*/
#define NULL	0
#define EOF	(-1)
#define ERR(s, c)	if(opterr){\
	extern int write();\
	char errbuf[2];\
	errbuf[0] = c; errbuf[1] = '\n';\
	(void) write(2, argv[0], (unsigned)strlen(argv[0]));\
	(void) write(2, s, (unsigned)strlen(s));\
	(void) write(2, errbuf, 2);}

extern char *index();

int	opterr = 1;
int	optind = 1;
int	optopt;
char	*optarg;

int
getopt(argc, argv, opts)
int	argc;
char	**argv, *opts;
{
	static int sp = 1;
	register int c;
	register char *cp;

	if(sp == 1)
		if(optind >= argc ||
		   argv[optind][0] != '-' || argv[optind][1] == '\0')
			return(EOF);
		else if(strcmp(argv[optind], "--") == NULL) {
			optind++;
			return(EOF);
		}
	optopt = c = argv[optind][sp];
	if(c == ':' || (cp=index(opts, c)) == NULL) {
		ERR(": illegal option -- ", c);
		if(argv[optind][++sp] == '\0') {
			optind++;
			sp = 1;
		}
		return('?');
	}
	if(*++cp == ':') {
		if(argv[optind][sp+1] != '\0')
			optarg = &argv[optind++][sp+1];
		else if(++optind >= argc) {
			ERR(": option requires an argument -- ", c);
			sp = 1;
			return('?');
		} else
			optarg = argv[optind++];
		sp = 1;
	} else {
		if(argv[optind][++sp] == '\0') {
			sp = 1;
			optind++;
		}
		optarg = NULL;
	}
	return(c);
}
\Rogue\Monster\
else
  echo "will not over write getopt.c"
fi
if `test ! -s index.c`
then
echo "writing index.c"
cat > index.c << '\Rogue\Monster\'
char *index(s, c)
register char *s, c;
{
  do {
	if (*s == c)
		return(s);
  } while (*s++ != 0);
  return(0);
}
\Rogue\Monster\
else
  echo "will not over write index.c"
fi
if `test ! -s unshar.1`
then
echo "writing unshar.1"
cat > unshar.1 << '\Rogue\Monster\'
.T1 unshar 1L
.SH NAME
unshar \- extract files from shell archive(s)
.SH SYNTAX
unshar [\-t] [\-b] [-v] [-xfile] [file(s)]
.SH OPTIONS
Options are processed by
.B getopts(3).
.TP 8
.B \-t   
Do not extract files, just list the files in the archive(s).
.TP
.B \-b
Extract files from the archive(s) brutally, with no regard at all to things
such as testing if the file exists, or chmoding the file etc. Currently
this is the only method supported, but other methods would be easy to add.
.TP
.B \-v
Be verbose. Used only for debugging purposes.
.TP
.B \-xfile
Extract the named file from the shell archive. If the \-x flag is used,
only those files specified will be extracted.
.SH DESCRIPTION
.I Unshar
extracts files from one or more shell archives, and places them in the current
directory. It does no forking to achieve this, and is relatively fast.
If no file name is given as an agrumemt,
.I unshar
will use standard input.
At the moment
.I unshar
is very simple-minded, and does its extraction rather brutally. The code
has been written to allow more intelligent unarchiving methods to be
added.
.SH SEE ALSO
shar(1), sh(1).
\Rogue\Monster\
else
  echo "will not over write unshar.1"
fi
if `test ! -s unshar.c`
then
echo "writing unshar.c"
cat > unshar.c << '\Rogue\Monster\'
#include <stdio.h>
#include "unshar.h"

/*
 * Unshar - extract files from shell archive
 *
 * Written by Warren Toomey. Nov, 1989.
 * You may freely copy or give away this source as
 * long as this notice remains intact.
 *
 */

/* Global variables */

int table;			/* Generate a table, or extract */
int verbose;			/* Unshar verbosely - debugging */
int numext;			/* Number of files to extract */
char *exfile[100];		/* Files to extract */


#define getline(x,y)	fgetline(stdin,x,y)

int fgetline(zin,how,buf)	/* Get a line from a file */
 FILE *zin;
 int how;			/* Ignore leading whitespace if */
 char *buf;			/* how == NOWHITE */
 {
  int ch=NULL;

  *buf=NULL;			/* Null the buffer */
  if (how==NOWHITE)		/* If skip any whitespace */
    {
     while (((ch=fgetc(zin))==' ') || (ch=='\t'));
     if (ch==EOF)  return(EOF);	/* Returning EOF or NULL */
     if (ch=='\n') return(NULL);
     *buf++ =ch;		/* Put char in buffer */
    }

  while ((ch=fgetc(zin))!='\n')	/* Now get the line */
   {
    if (ch==EOF) { *buf=NULL; return(EOF); }
    *buf++ = ch;
   }
  
  *buf=NULL;			/* Finally null-terminate the buffer */
  return(NULL);			/* and return */
 }



char *getstring(buf)		/* Get the next string from the buffer */
 char *buf;			/* ignoring any quotes */
 {
  char out[BUFSIZE];
  char *temp=out;
  while ((*buf==' ') || (*buf=='\t')) buf++;	/* Skip whitespace */

  switch(*buf)			/* Now check first char */
   {
    case '\'' : buf++;
		while (*buf!='\'') *temp++ = *buf++;
		*temp=NULL;
		return(out);
    case '\"' : buf++;
		while (*buf!='\"') *temp++ = *buf++;
		*temp=NULL;
		return(out);
    case NULL : return(NULL);
    default   : while ((*buf!=' ') && (*buf!='\t'))
		   if (*buf!='\\') *temp++ = *buf++;
		   else buf++;
		*temp=NULL;
		return(out);
   }
 }


int firstword(buf)		/* Return token value of first word */
 char *buf;			/* in the buffer. Assume no leading */
 {				/* whitespace in the buffer */
  int i;

  for (i=1;i<NUMTOKS;i++)
     if (strncmp(buf,token[i],strlen(token[i]))==0)
	return(i);

  return(UNKNOWN);
 }


int mustget(s1)			/* Return 1 if s1 is in the list of  */
 char *s1;			/* files to extract. Return 0 if not */
 {				
  int i;

  if (numext==0) return(0);
  for (i=0;i<numext;i++)
   if (!strcmp(s1,exfile[i])) return(1);
  return(0);
 }


void extract(how,file,end,lead)	/* Extract file, up until end word */
 int how;			/* If how==YESX, then ignore lead   */
 char *file;			/* character on every line */
 char *end;
 int lead;
 {
  FILE *zout;
  char line[BUFSIZE];
  char *temp;
  int ch;

  zout=fopen(file,"w");		/* Open output file */
  if (zout==NULL)
    { perror("unshar");
      return;
    }

  while(1)
   {
    ch=getline(WHITE,line);	/* Get a line of file */
    temp=line;
    if (ch==EOF)
      { fprintf(zout,"%s\n",line);
	fclose(zout);
	return;
      }

    if (strncmp(line,end,strlen(end))==0)	/* If end word */
      { fclose(zout);				/* close the file */
	return;
      }

     if ((how==YESX) && (*temp==lead)) temp++;	/* Skip any lead */
     fprintf(zout,"%s\n",temp);
    }
 }


void getnames(buf,file,word)	/* Get the file & end word */
 char *buf, *file, *word;	/* from the buffer */
 {
  char *temp;

  temp=buf;
  if (verbose) printf("Getnames: buf is %s\n",buf);

  while (*temp!=NULL)		/* Scan along buffer */
   {
    switch(*temp)		/* Get file or end word */
     {
      case '>' : strcpy(file,getstring(++temp)); /* Get the file name */
	 	 break;
      case '<' : if (*(++temp)=='<') ++temp;	/* Skip 2nd < */
		 strcpy(word,getstring(temp));	/* Get next word */
		 break;
      default  : temp++;
     }
   }
 }



void disembowel()		/* Unshar brutally! */
 {
  char buf[BUFSIZE];		/* Line buffer */
  char file[BUFSIZE];		/* File name */
  char word[BUFSIZE];		/* Word buffer */
  int ch,x;

  if (verbose) printf("Entering disembowel\n");
  x='X';			/* Leading X character */
  while(1)
   {
    ch=getline(NOWHITE,buf);	/* Get a line from file */
    if (ch==EOF) 
	return;

    switch(firstword(buf))	/* Extract, depending on first word */
     {
      case CAT:
      case GRES:
      case SED:  if (verbose) printf("About to do getnames\n");
		 getnames(buf,file,word);
		 if (table==0)
		  {
		   if ((numext==0) || (mustget(file)))
		    {
		     printf("unshar: Extracting  %s\n",file);
		     if (verbose) 
			printf("        stopping at %s\n",word);
		     extract(YESX,file,word,x);
		    }
		  }
		 else printf("%s\n",file);
		 break;
      default:   break;
     }
   }
 }
  


usage()
 {
  fprintf(stderr,"Usage: unshar [-t] [-b] [-v] [-xfile] [file(s)]\n");
  exit(0);
 }


main(argc,argv)
 int argc;
 char *argv[];
 {
  extern int optind;
  extern char *optarg;
  int i,c,first;

  FILE *zin;			/* Dummy file descriptor */
  int method;			/* Method of unsharing */

  method= BRUTAL;		/* Only BRUTAL currently available */
  table=  0;			/* Don't generate a table */
  verbose=0;			/* Nor be very verbose */
  numext= 0;			/* Initially no files to extract */


  while ((c=getopt(argc,argv,"x:tbv"))!=EOF)
    switch(c)
     {
      case 't' : table=1;	/* Get the various options */
		 break;
      case 'b' : method= BRUTAL;
		 break;
      case 'v' : verbose=1;
		 break;
      case 'x' : exfile[numext]= (char *)malloc(strlen(optarg)+1);
		 strcpy(exfile[numext++],optarg);
		 break;
      default  : usage();
     }

  if (argc==1) first=argc;		/* Find first file argument */
  else for (first=1;first<argc;first++)
	if (argv[first][0]!='-') break;

  if (first==argc)			/* If no file argument */
   {					/* use stdin only */
    switch(method)
     {
      case BRUTAL: disembowel();	/* Unshar brutally! */
		   break;
      default:	   fprintf(stderr,"unshar: Unknown method of unsharing\n");
		   exit(1);
      }
   }
  else
   for (i=first;i<argc;i++)	/* open stdio with every file */
   {
    fclose(stdin);
    if ((zin=fopen(argv[i],"r"))==NULL)
      { perror("unshar");
        exit(1);
      }

    switch(method)
     {
      case BRUTAL: disembowel();	/* Unshar brutally! */
		   break;
      default:	   fprintf(stderr,"unshar: Unknown method of unsharing\n");
		   exit(1);
      }
   }
  exit(0);
 }
\Rogue\Monster\
else
  echo "will not over write unshar.c"
fi
if `test ! -s unshar.h`
then
echo "writing unshar.h"
cat > unshar.h << '\Rogue\Monster\'
/*
 * Unshar - extract files from shell archive
 *
 * Written by Warren Toomey. Nov, 1989.
 * You may freely copy or give away this source as
 * long as this notice remains intact.
 *
 * Definitions used by unshar
 */

	/* Methods of unsharing */

#define UNKNOWN	0
#define BRUTAL	1


	/* Whitespace indicators */

#define WHITE	0
#define NOWHITE 1


	/* Leading character indicators */

#define NOX	0
#define YESX	1


	/* Emulation types available */

#define NUMTOKS    4		/* Must change NUMTOKS to equal the */
/* define UNKNOWN  0 */		/* number of emulation types */
#define SED	   1
#define GRES 	   2
#define CAT	   3

static char *token[NUMTOKS] =	/* The list of emulation types! */
	{ "",
	  "sed",
	  "gres",
	  "cat"
	};



	/* Misc. constants */

#define BUFSIZE	256		/* Size of line buffer */
\Rogue\Monster\
else
  echo "will not over write unshar.h"
fi
echo "Finished archive 1 of 1"
exit
  Warren Toomey VK2XWT, electric guitar licker.
  Deep in the bowels of ADFA Comp Science.
  Canberra. ACT. 2600.  Email: wkt@csadfa.oz.au 
  `Worth the money but not the risk -CC'

seanp@amix.commodore.com (Sean Petty) (02/16/91)

 
 
I'm sure this question is asked quite often, but I'll go ahead and ask it 
because I need to know. 
 
Is there an UNSHAR'ing utility for MS-DOS? I recently saw the jargon file 
on misc.misc and would like to un-shar it, yet have not been able to find 
a utility to do so... 
 
 Could someone please mail, or post one? 
 
 Thanks in advance...



Sean

_______
Sean Petty - Somewhere in Pennsylvania

There is something fascinating about science. One gets such wholesale
returns of conjecture out of such a trifling investment of fact.
                -- Mark Twain

w8sdz@rigel.acs.oakland.edu (Keith Petersen) (02/17/91)

undrground!seanp@amix.commodore.com (Sean Petty) writes:
>Is there an UNSHAR'ing utility for MS-DOS? I recently saw the jargon file 
>on misc.misc and would like to un-shar it, yet have not been able to find 
>a utility to do so... 

WSMR-SIMTEL20.ARMY.MIL [26.2.0.74]

NOTE: Type B is Binary; Type A is ASCII

Directory PD1:<MSDOS.FILUTL>
 Filename   Type Length   Date    Description
==============================================
SHAREXE.ARC   B  159288  880221  Unix-like shar/unshar for DOS
TOADSHR1.ARC  B   34760  890615  DOS Unix-compatible shar/unshar, w/TP 5.0 src
UNSHAR.ARC    B   24192  880326  Disassemble a shar file

Keith
-- 
Keith Petersen
Maintainer of SIMTEL20's MSDOS, MISC & CP/M archives [IP address 26.2.0.74]
Internet: w8sdz@WSMR-SIMTEL20.Army.Mil    or     w8sdz@vela.acs.oakland.edu
Uucp: uunet!umich!vela!w8sdz                          BITNET: w8sdz@OAKLAND