[comp.sources.misc] v03i014: shar2

allbery@ncoast.UUCP (05/10/88)

comp.sources.misc: Volume 3, Issue 14
Submitted-By: "Wm E. Davidsen" <davidsen@crdos1.UUCP>
Archive-Name: shar2

[This is an enhanced "shar" program.  I think I'll stick with mine for
 now, but it's nice to know someone's working on it.  ++bsa]

#!/bin/sh
# shar:	Shell Archiver  (v1.22)
#	Packed Tue May 10 11:43:47 EDT 1988 by davidsen
#	from directory /usr2/davidsen/bin/src/shar2
#
#	Run the following text with /bin/sh to create:
#	  shar2.readme
#	  shar2.1
#	  unshar.1
#	  shar2.c
#	  uushar.c
#	  unshar.sh
#	  Makefile
#	  unshar.c
#
echo "x - extracting shar2.readme (Text)"
sed 's/^X//' << 'SHAR_EOF' > shar2.readme &&
X
X  SHAR2 is a program to create shell archives. I wrote it to solve the
Xproblems inherent in sharing large files, namely:
X
X	breaking up large postings to avoid truncation
X	    This is now done by the program
X	concatenating shar parts before unpacking when large files
X	won't fit into one mailable file
X	    Each output file may be unpacked separately, and the
X	    unpack is sequence checked
X	binary files
X	    The shar program will optionally handle binary files, using
X	    uuencode to process them. The results are able to be broken
X	    into multiple files, as with text.
X	mixed text and binary files
X	    The shar program can optionally handle a mixture of file
X	    types, using the 'file' command to test them. The mixing of
X	    text and binary files may also be done manually, for files
X	    which are text files but which contain characters which may
X	    be damaged when mailed.
X	losing file permissions
X	    file permissions are preserved, allowing shell scripts to
X	    retain execute permission and read only files to be
X	    protected.
X
X
XThese are the features of shar2 over the many shar versions around:
X
X	 1.  It optionally produces multiple output files for mailing.
X	 2.  Multiple output files do not have to be concatenated before
X	     unpacking. Each may be unpacked independently, input files
X	     spanning several output files are joined correctly.
X	 3.  Unpacking of multiple-file archives is sequence checked.
X	 4.  binary files may be processed, using uuencode and uudecode.
X	     this allows large binary files to be easily moved by mail.
X	 5.  file permissions are preserved.
X	 6.  error checking is available as an option.
X	 7.  Fast - written in C.
X	 8.  Rejects pipes and directories.
X	 9.  Can handle mixed text and binary files, using automatic
X	     or manual file typing.
X
X  I wrote this because the version in shell script was too slow, I got a
Xsource for a minimal version which could be enhanced, and to avoid
Xhaving to join parts of binary postings before unpacking.  I hope
Xit will be useful.
X
X				bill davidsen, 4/6/88 (davidsen@sixhub.uucp)
X
X  Latest revision, change checking to use wc instead of sun, since not
Xall versions of sum are compatible.  Thanks to Keith Petersen.  Added
Xthe ability to make the temp filenames DOS compatible to allow use of
X'unshar' on DOS. 
X
SHAR_EOF
chmod 0644 shar2.readme || echo "restore of shar2.readme fails"
set `wc -c shar2.readme`;Sum=$1
if test "$Sum" != "2317"
then echo original size 2317, current size $Sum;fi
echo "x - extracting shar2.1 (Text)"
sed 's/^X//' << 'SHAR_EOF' > shar2.1 &&
X'\" must be run through 'tbl' as well as nroff
X.TH SHAR2 1 local
X.SH NAME
Xshar2 - create shell archives
X.SH SYNOPSIS
Xshar2 [ options ] files
X.SH DESCRIPTION
Xshar2 creates "shell archives" (or shar files) which are in text format
Xand can be mailed. These files may be unpacked later by executing them
Xwith /bin/sh.
X.SS Options
X.TS
Xtab(;);
Xlb lw(50).
X-v;T{
Xverbose. This option causes a running commentary as the archive is
Xcreated, and another as it is unpacked. This option does not control the
Xerror messages, only the informational messages.
XT}
X
X-s;T{
Xsum. The files are tested with the \fIsum\fR command for damage in
Xtransit. Errors are reported.
XT}
X
X-x;T{
Xdon't overwrite existing files. The unpack will check for an existing
Xfile before unpacking a given file from the archive.
XT}
X
X-b;T{
Xbinary. This option causes all files to be treated as binary files, and
Xthe files are changed to text using \fIuuencode\fR. Commands are
Xembedded in the output files to change use uudecode and recreate the
Xbinary files.
XT}
X
X-M;T{
Xmixed text and binary.  This option causes each file to be
Xexamined to determine if it is text or data.  Binary files will be
Xprocessed using uuencode.
XT}
X
X-D;T{
Xgive detail of archive. The date, user, and working directory are added
Xto the archive as comments.
XT}
X
X-c;T{
Xproduce cut line. This option causes a line saying "cut\ here" to be
Xplaced at the start of each output file. This is for sites not offering
Xthe \fIunshar\fR command or shell script.
XT}
X
X-f;T{
Xfilename only. By default the entire path name is used as given on the
Xcommand line. This option causes only the file names to be used, which
Xis useful when building a shar from several directories, or another
Xdirectory.
XT}
X
X-dXXX;T{
Xchange delimiter to XXX. The default delimiter is SHAR_EOF, placed at
Xthe end of each file. This option is left for historical reasons, to
Xamuse those who want their shar files to be personalized.
XT}
X
X-o\fIfile\fR;T{
Xoutput filename. The file \fIfile\fR is used for output instead of the
Xstandard output. This option is required to use the \fB-l\fR (limit
Xfilesize) option.
XT}
X
X-l\fInum\fR;T{
Xfile size limit. The output files created by \fIshar2\fR will be smaller
Xthan \fInum\fRk in length. The \fB-o\fR option must be used to specify
Xan output filename, and the digits 00 through 99 will be appended to
Xform a series of output filenames. Informative messages and sequence
Xchecking are included.
XT}
X.TE
X.SH EXAMPLES
X'\"  examples use spaces, not tabs
X  shar2 *.c > cprog.shar        # all C prog sources
X  shar2 -v *.[ch] > cprog.shar  # verbose, .c and .h files
X  shar2 -b -l28 -oarc.sh *.arc  # all binary .arc files, into
X                                # files arc.sh01 thru arc.shNN
X  shar2 -f /lcl/src/u*.c > u.sh	# use only the filenames
X.SH WARNINGS
XUse of the \fB-s\fR, \fB-b\fR, and \fB-M\fR options will slow the
Xarchive process considerably, depending on the number of files.
X.SH FILES
XTemp files ._temp_ and ._seq_ are created and delted as needed.
X.SH SEE ALSO
Xunshar or unshar.sh.
X.SH DIAGNOSTICS
XError messages for missing or unaccessable files.
X.SH LIMITATIONS
XDoes not chase directory names, limited to the number of names on a
Xcommand line. Uses the \fIfile\fR command to determine file type, may be
Xaffected by its limitations. The -D option calls the \fIdate\fR,
X\fIwho\ am\ i\fR, and \fIpwd\fR commands, and will produce garbage if
Xthey are not present.
X.sp
XRequires the \fIuudecode\fR program to unpack binary files. On some
Xsystems, such as Sun, this program requires world write on the current
Xdirectory.
X.sp
XThe first file archived may not have a name starting with a hyphen, and
Xif the file type is being set manually (-p) no file may be named
X\fB-t\fR or \fB-b\fR. Option parsing should be done with getopt.
X.SH AUTHOR
XBill Davidsen, (davidsen@sixhub.uucp).
X.SH COPYRIGHT
XCopyright 1988 by Bill Davidsen. This program and documentation may be
Xused by any person for any purpose.
SHAR_EOF
chmod 0666 shar2.1 || echo "restore of shar2.1 fails"
set `wc -c shar2.1`;Sum=$1
if test "$Sum" != "3932"
then echo original size 3932, current size $Sum;fi
echo "x - extracting unshar.1 (Text)"
sed 's/^X//' << 'SHAR_EOF' > unshar.1 &&
X'\" xxx
X.TH UNSHAR 1 local
X'\" Heading: name(sect)    center (paren)    name(sect)
X.SH NAME
Xunshar - unpack a shar file
X.SH SYNOPSIS
Xunshar file [ file [ file ] ]
X.SH DESCRIPTION
Xunshar scans mail messages looking for the start of a shell archive. It
Xthen passes the archive through a copy of the shell to unpack it. It
Xwill accept multiple files.
X.SH EXAMPLES
X  unshar msg4
X  unshar msg4.*
X.SH WARNINGS
XIf a line in the introduction starts with a ':' or '#' unshar will
Xbecome confused.
X.SH FILES
X.SH SEE ALSO
Xshar, shar2.
X.SH DIAGNOSTICS
XAny message from the shell may be displayed.
X.SH LIMITATIONS
XProblems will occur if the shar file does not end with an exit command. 
X.SH AUTHOR
XBill Davidsen (davidsen@sixhub)
X.SH COPYRIGHT
XCopyright 1988 by Bill Davidsen. This program may be used by any person
Xfor any purpose.
SHAR_EOF
chmod 0666 unshar.1 || echo "restore of unshar.1 fails"
set `wc -c unshar.1`;Sum=$1
if test "$Sum" != "820"
then echo original size 820, current size $Sum;fi
echo "x - extracting shar2.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > shar2.c &&
X/*
X** cshar.c
X*/
X
X#ifndef lint
X#ifdef RCSIDENT
Xstatic char *rcsid[] =
X{
X    "$Header: shar.c,v 1.3 84/12/09 19:38:53 thompson Exp $",
X    "$Locker:  $"
X};
X#endif RCSIDENT
X#endif lint
X#include <stdio.h>
X#include <sys/types.h>
X#include <sys/stat.h>
X
X/*
X Shar puts readable text files together in a package
Xfrom which they are easy to extract.  The original version
Xwas a shell script posted to the net, shown below:
X	#Date: Mon Oct 18 11:08:34 1982
X	#From: decvax!microsof!uw-beave!jim (James Gosling at CMU)
X	AR=$1
X	shift
X	for i do
X		echo a - $i
X		echo "echo x - $i" >>$AR
X		echo "cat >$i <<'!Funky!Stuff!'" >>$AR
X		cat $i >>$AR
X		echo "!Funky!Stuff!" >>$AR
X	done
XI rewrote this version in C to provide better diagnostics
Xand to run faster.  The main difference is that my version
Xdoes not affect any files because it prints to the standard
Xoutput.  Mine also has a -v (verbose) option.
X*/
X/*
X *	I have made several mods to this program:
X *
X *	1) the -----Cut Here-----... now preceds the script.
X *	2) the cat has been changed to a sed which removes a prefix
X *	character from the beginning of each line of the extracted
X *	file, this prefix character is added to each line of the archived
X *	files and is not the same as the first character of the
X *	file delimeter.
X *	3) added several options:
X *		-c	- add the -----Cut Here-----... line.
X *		-d'del' - change the file delimeter to del.
X *		-s	- cause the resulting script to print the sum of
X *			  the orignal file and the sum of the extracted
X *			  file.
X *
X *				Michael A. Thompson
X *				Dalhousie University
X *				Halifax, N.S., Canada.
X */
X
X/*
X *	I, too, have been hacking this code. This is the version on sixhub
X *		bill davidsen (davidsen@sixhub.uucp)
X *
X *	- added support for binary files
X *	- automatic creation of limited size multiple file archives,
X *	  each of which may be unpacked separately, and with sequence
X *	  checking.
X *	- support for mixed text and binary files
X *	- preserve file permissions
X *	- restore to filename rather than pathname
X *
X */
X
Xstatic char *SCCSid =
X             "@(#)cshar, sixhub version 1.22, last change 5/2/88";
X
X#define	DELIM           "SHAR_EOF"/* put after each file */
X#define PREFIX1	        'X'	/* goes infront of each line */
X#define PREFIX2		'Y'	/* goes infront of each line if Delim
X				   starts with PREFIX1 */
X#define PREFIX		(Delim[0] == PREFIX1 ? PREFIX2 : PREFIX1)
X#define	SHAR            "shar"	/* the name of this program */
X#define	READ_PERMISSION 4	/* access permission */
X#define SUM	        "wc -c"
X
Xint  Verbose = 0;		/* option to provide append/extract
X				   feedback */
Xint  Sum = 0;			/* option to provide sum checking */
Xchar *Delim = DELIM;		/* pointer to delimiter string */
Xint  Cut = 0;			/* option to provide cut mark */
Xint  Binary = 0;		/* flag for binary files */
Xint  Mixed = 0;			/* mixed text and binary files */
Xint  Detail = 0;		/* extra detail in shar header */
Xint  eXists = 0;		/* check if file exists */
Xint  PosParam = 0;		/* allow positional parameters */
Xint  FileStrip;			/* strip directories from filenames */
X#ifdef	DEBUG
Xint de_bug = 0;			/* switch for debugging on */
X#define DeBug(f,v) if (de_bug) printf(f, v)
X#else	/* normal compile */
X#define DeBug(f,v)		/* do nothing */
X#endif
X
XFILE *popen ();
XFILE *outfile = stdout, *fopen ();
Xchar *Rname();			/* file restore name */
Xunsigned  limit = 0;
Xlong  ftell ();
Xlong  TypePos;			/* position for archive type message */
Xchar  outname[50],		/* base for output filename */
X      filename[50];		/* actual output filename */
Xint  filenum = 0;		/* output file # */
Xstruct stat  filestat;		/* check file type, access */
X
Xmain (argc, argv)
X    char **argv;
X{
X    int  status = 0;
X    char *oname;
X
X    while (argv[1][0] == '-')
X    {
X	switch (argv[1][1])
X	{
X	case 'v': 
X	    Verbose = 1;
X	    break;
X	case 's': 
X	    Sum = 1;
X	    break;
X	case 'D': /* add detail */
X	    Detail = 1;
X	    break;
X	case 'd': 
X	    if (argv[1][2])
X		Delim = &argv[1][2];
X	    break;
X	case 'b': /* binary files */
X	    Binary = 1;
X	    break;
X	case 't': /* text mode */
X	    Binary = 0;
X	    break;
X	case 'x': /* does the file exist */
X	    eXists = 1;
X	    if (limit) exit_incompat();
X	    break;
X	case 'c': 
X	    Cut = 1;
X	    break;
X	case 'f': /* filenames only */
X	    FileStrip = 1;
X	    break;
X	case 'M': /* mixed text and binary */
X	    Mixed = 1;
X	    break;
X	case 'p': /* allow positional parameters */
X	    PosParam = 1;
X	    break;
X	case 'l': /* size limit in k */
X	    limit = atoi (argv[1] + 2) - 1;
X	    if (eXists) exit_incompat();
X	    DeBug("Limit %dk\n", limit);
X	    break;
X	case 'o': /* specify output file */
X	    if (argv[1][2])
X		oname = &argv[1][2];
X	    else
X	    { /* use the next arg */
X		if (argc > 1 && argv[2][0] != '-')
X		{ /* if the next one isn't an option */
X		    oname = argv[2];
X		    argc--;
X		    argv++;
X		}
X		else
X		{ /* format error in the -o option */
X		    fprintf (stderr, "Format error in -o option\n");
X		    exit (1);
X		}
X	    }
X	    strcpy (outname, oname);
X	    filenum = 1;
X	    sprintf (filename, "%s01", outname);
X	    outfile = fopen (filename, "w");
X	    if (outfile == NULL)
X	    { /* creation error */
X		perror ("can't create output file");
X		exit (1);
X	    }
X	    break;
X#ifdef	DEBUG
X	case '$': /* totally undocumented $ option, debug on */
X	    de_bug = 1;
X	    break;
X#endif
X	default: /* invalid option */
X	    fprintf (stderr, "%s: invalid argument: %s\n", SHAR, argv[1]);
X	case 'h': /* help */
X	    helpuser ();
X	    break;
X	}
X	argc--;
X	argv++;
X    }
X    if (argc == 1)
X    {
X	fprintf (stderr, "%s: No input files\n", SHAR);
X	helpuser ();
X	exit (1);
X    }
X    if (header (argc, argv))
X	exit (2);
X    while (--argc)
X    { /* process positional parameters and files */
X	argv++;
X	if (PosParam)
X	{ /* allow -b and -t inline */
X	    if (strcmp (*argv, "-b") == 0)
X	    { /* set binary */
X		Binary = 1;
X		continue;
X	    }
X	    if (strcmp (*argv, "-t") == 0)
X	    { /* set mode text */
X		Binary = 0;
X		continue;
X	    }
X	}
X	status += shar (*argv);
X    }
X
X /* delete the sequence file, if any */
X    if (limit && filenum > 1)
X    {
X	fputs ("rm -f s2_seq_.tmp\n", outfile);
X	fputs ("echo \"You have unpacked the last part\"\n", outfile);
X	if (!Verbose)
X	    fprintf (stderr, "Created %d files\n", filenum);
X    }
X    fputs ("exit 0\n", outfile);
X    exit (status);
X}
X
Xheader (argc, argv)
X    char **argv;
X{
X    int  i;
X    int  status;
X    FILE *ptemp;		/* pipe temp */
X
X /* see if any conflicting options */
X    if (limit && !filenum)
X    { /* can't rename what you don't have */
X	fprintf (stderr, "Can't use -l option without -o\n");
X	helpuser ();
X    }
X
X    for (i = 1; i < argc; i++)
X    { /* skip positional parameters */
X	if (PosParam && (strcmp (argv[i], "-b") == 0 ||
X		    strcmp (argv[i], "-t") == 0))
X	    continue;
X
X    /* see if access and correct type */
X	if (access (argv[i], READ_PERMISSION))
X	{
X	    fprintf (stderr, "%s: Can't access %s\n", SHAR, argv[i]);
X	    return 1;
X	}
X
X    /* get file type */
X	stat (argv[i], &filestat);
X	status = filestat.st_mode & S_IFMT;
X
X    /* at this point I check to see that this is a regular file */
X	if (status != S_IFREG)
X	{ /* this is not a regular file */
X	    fprintf (stderr, "%s: %s is not a file\n", SHAR, argv[i]);
X	    return 1;
X	}
X    }
X
X    if (Cut)
X	fputs ("---- Cut Here and unpack ----\n", outfile);
X    fputs ("#!/bin/sh\n", outfile);
X    fprintf (outfile, "# %s:	Shell Archiver  (v1.22)\n", SHAR);
X    if (Detail)
X    {
X	char  dateout[50],	/* pipe output, who */
X	      whoout[50];	/* and who am i */
X
X	ptemp = popen ("date; who am i; pwd", "r");
X	fgets (dateout, 50, ptemp);
X	dateout[strlen (dateout) - 1] = 0;
X	fscanf (ptemp, "%s", whoout);
X	fprintf (outfile, "#\tPacked %s by %s\n", dateout, whoout);
X	while (getc (ptemp) != '\n');
X	fgets (dateout, 50, ptemp);
X	fprintf (outfile, "#\tfrom directory %s", dateout);
X
X	pclose (ptemp);
X    }
X
X    if (limit)
X    { /* may be split, explain */
X        fprintf(outfile, "#\n");
X        TypePos = ftell(outfile);
X	fprintf (outfile, "%-75s\n%-75s\n", "#", "#");
X    }
X
X    fputs ("#\n#\tRun the following text with /bin/sh to create:\n", outfile);
X    for (i = 1; i < argc; i++)
X    { /* output names of files but not parameters */
X	if (PosParam && (strcmp (argv[i], "-b") == 0 ||
X		    strcmp (argv[i], "-t") == 0))
X	    continue;
X	fprintf (outfile, "#\t  %s\n", Rname(argv[i]));
X    }
X    fputs ("#\n", outfile);
X
X    if (limit)
X    { /* now check the sequence */
X	fprintf (outfile, "%s%s%s%s",
X		"if test -r s2_seq_.tmp\n",
X		"then echo \"Must unpack archives in sequence!\"\n",
X		"     next=`cat s2_seq_.tmp`; echo \"Please unpack part $next next\"\n",
X		"     exit 1; fi\n");
X    }
X    return (0);
X}
X
Xshar (file)
X    char *file;
X{
X    char  line[BUFSIZ];
X    FILE *ioptr, *fdopen ();
X    long  cursize, remaining, ftell ();
X    int  split = 0;		/* file split flag */
X    char *filetype;		/* text or binary */
X    char *RstrName;		/* name for restore */
X
X /* if limit set, get the current output length */
X    if (limit)
X    {
X	cursize = ftell (outfile);
X	remaining = (limit * 1024L) - cursize;
X	DeBug("In shar: remaining size %ld\n", remaining);
X    }
X
X /* determine the name to use for restore */
X    RstrName = Rname(file);
X
X /* if mixed, determine the file type */
X    if (Mixed)
X    {
X	int  count;
X	sprintf (line, "file %s | egrep -c \"text|shell\"", file);
X	ioptr = popen (line, "r");
X	fscanf (ioptr, "%d", &count);
X	pclose (ioptr);
X	Binary = (count != 1);
X    }
X
X    if (Binary)
X    { /* fork a uuencode process */
X	static int  pid, pipex[2];
X
X	pipe (pipex);
X	fflush (outfile);
X
X	if (pid = fork ())
X	{ /* parent, create a file to read */
X	    close (pipex[1]);
X	    ioptr = fdopen (pipex[0], "r");
X	}
X	else
X	{ /* start writing the pipe with encodes */
X	    FILE *outptr;
X
X	    ioptr = fopen (file, "rb");
X	    outptr = fdopen (pipex[1], "w");
X	    fprintf (outptr, "begin 600 %s\n", RstrName);
X	    encode (ioptr, outptr);
X	    fprintf (outptr, "end\n");
X	    exit (0);
X	}
X	filetype = "Binary";
X    }
X    else
X    {
X	ioptr = fopen (file, "r");
X	filetype = "Text";
X    }
X
X    if (ioptr != NULL)
X    {
X    /* protect existing files */
X	if (eXists)
X	    fprintf (outfile,
X		    "if test -f %s; then echo \"File %s exists\"; else\n",
X		    RstrName, RstrName);
X
X	if (Verbose)
X	{ /* info on archive and unpack */
X	    fprintf (stderr, "%s: saving %s (%s)\n",
X		    SHAR, file, filetype);
X	    fprintf (outfile, "echo \"x - extracting %s (%s)\"\n",
X		    RstrName, filetype);
X	}
X	if (Binary)
X	{ /* run sed through uudecode via temp file */
X	    fprintf (outfile, "sed 's/^%c//' << '%s' > s2_temp_.tmp &&\n",
X		    PREFIX, Delim);
X	}
X	else
X	{ /* just run it into the file */
X	    fprintf (outfile, "sed 's/^%c//' << '%s' > %s &&\n",
X		    PREFIX, Delim, RstrName);
X	}
X	while (fgets (line, BUFSIZ, ioptr))
X	{ /* output a line and test the length */
X	    fprintf (outfile, "%c%s", PREFIX, line);
X	    if (limit && (remaining -= strlen (line) + 2) < 0)
X	    { /* change to another file */
X		DeBug("Newfile, remaining %ld, ", remaining);
X		DeBug("limit still %d\n", limit);
X		fprintf (outfile, "%s\n", Delim);
X		if (Verbose)
X		{ /* output some reassurance */
X		    fprintf (outfile,
X			    "echo \"End of part %d\"\n", filenum);
X		    fprintf (outfile,
X			    "echo \"File %s is continued in part %d\"\n",
X			    RstrName, filenum + 1);
X		}
X		else
X		    fprintf (outfile,
X			    "echo \"End of part %d, continue with part %d\"\n",
X			    filenum, filenum + 1);
X		fprintf (outfile, "echo \"%d\" > s2_seq_.tmp\n", filenum + 1);
X		fprintf (outfile, "exit 0\n");
X
X	        if (filenum == 1)
X		{ /* rewrite the info lines on the firstheader */
X		    fseek(outfile, TypePos, 0);
X		    fprintf (outfile, "%-75s\n%-75s\n",
X		        "# This is part 1 of a multipart archive",
X			"# do not concatenate these parts, unpack them in order with /bin/sh");
X		}
X		fclose (outfile);
X
X	    /* form the next filename */
X		sprintf (filename, "%s%02d", outname, ++filenum);
X		outfile = fopen (filename, "w");
X		if (Cut)
X		    fputs ("---- Cut Here and unpack ----\n", outfile);
X		fprintf (outfile, "#!/bin/sh\n");
X		fprintf (outfile,
X			"# this is part %d of a multipart archive\n%s%s",
X			filenum, "# do not concatenate these parts, ",
X			"unpack them in order with /bin/sh\n");
X		fprintf (outfile, "# file %s continued\n#\n", RstrName);
X		fprintf (outfile, "CurArch=%d\n", filenum);
X		fprintf (outfile, "%s%s%s%s%s%s%s%s%s",
X			"if test ! -r s2_seq_.tmp\n",
X			"then echo \"Please unpack part 1 first!\"\n",
X			"     exit 1; fi\n",
X			"( read Scheck\n",
X			"  if test \"$Scheck\" != $CurArch\n",
X			"  then echo \"Please unpack part $Scheck next!\"\n",
X			"       exit 1;\n",
X			"  else exit 0; fi\n",
X			") < s2_seq_.tmp || exit 1\n");
X
X		if (Verbose)
X		{ /* keep everybody informed */
X		    fprintf (stderr, "Starting file %s\n", filename);
X		    fprintf (outfile,
X			    "echo \"x - Continuing file %s\"\n", RstrName);
X		}
X		fprintf (outfile,
X			"sed 's/^%c//' << '%s' >> %s\n",
X			PREFIX, Delim, (Binary ? "s2_temp_.tmp" : RstrName));
X		remaining = limit * 1024L;
X		split = 1;
X	    }
X	}
X
X	(void) fclose (ioptr);
X	fprintf (outfile, "%s\n", Delim);
X	if (split && Verbose)
X	    fprintf (outfile,
X		    "echo \"File %s is complete\"\n", RstrName);
X
X    /* if this file was uuencoded, decode it and drop the temp */
X	if (Binary)
X	{
X	    if (Verbose)
X		fprintf (outfile, "echo \"uudecoding file %s\"\n", RstrName);
X	    fprintf (outfile,
X	            "uudecode < s2_temp_.tmp && rm -f s2_temp_.tmp &&\n");
X	}
X
X    /* set the permissions as they were */
X	stat (file, &filestat);
X	fprintf (outfile,
X		"chmod %04o %s || echo \"restore of %s fails\"\n",
X		filestat.st_mode & 07777, RstrName, RstrName);
X
X	if (Sum)
X	{ /* validate the transferred file */
X	    FILE *pfp;
X	    char  command[BUFSIZ];
X
X	    sprintf (command, "%s %s", SUM, file);
X	    if ((pfp = popen (command, "r")))
X	    {
X		char  sum[BUFSIZ];
X
X		fscanf (pfp, "%s", sum);
X		fprintf (outfile, "set `%s %s`;Sum=$1\n",
X		    SUM, RstrName);
X		fprintf (outfile,
X			"if test \"$Sum\" != \"%s\"\n", sum);
X		fprintf (outfile,
X			"then echo original size %s, current size $Sum;fi\n",
X			sum);
X		pclose (pfp);
X	    }
X	}
X
X    /* if the exists option is in place close the if */
X	if (eXists)
X	    fprintf (outfile, "fi\n");
X
X	return (0);
X    }
X    else
X    {
X	fprintf (stderr, "%s: Can't open %s\n", SHAR, file);
X	return (1);
X    }
X}
X
Xchar *Rname(file)
X    register char *file;
X{
X    register char *RstrName;
X
X    if (FileStrip)
X    { /* use just the filename */
X        RstrName = file+strlen(file);
X	while (RstrName > file && *RstrName != '/')
X	    RstrName--;
X	if (*RstrName == '/') RstrName++;
X    }
X    else RstrName = file;
X    return (RstrName);
X}
X
X/*****************************************************************
X |  exit_incompat - incompatible options
X ****************************************************************/
X
Xexit_incompat()
X{
X    fprintf(stderr, "%s%s",
X        "The -x and -l options are currently incompatible\n",
X	"Please reenter the command without one option\n");
X	exit(1);
X}
X
Xhelpuser ()
X{				/* output a command format message */
X    register char **ptr;
X    static char *helpinfo[] =
X    {
X	"-v\tverbose messages while executing",
X	"-s\tdo checking with 'sum' after unpack",
X	"-x\tdon't overwrite existing files",
X	"-b\ttreat all files as binary, use uuencode",
X	"-t\ttreat all files as text (default)",
X	"-p\tallow positional parameter options. The options \"-b\"",
X	"\tand \"-t\" may be embedded, and files to the right of the",
X	"\toption will be processed in the specified mode",
X	"-M\tmixed mode. Determine if the files are text or",
X	"\tbinary and archive correctly.",
X	"-D\toutput date, user, and directory comments to the archive",
X	"-c\tstart the shar with 'cut here'",
X	"-f\trestore by filename only, rather than path",
X	"-dXXX\tuse XXX to delimit the files in the shar",
X	"-oXXX\t(or -o XXX) output to file XXX01 thru XXXnn",
X	"-lXX\tlimit output file size to XXk bytes",
X	"\nThe 'o' option is required if the 'l' option is used",
X	NULL
X    };
X    fprintf (stderr,
X	    "\nFormat:\n  %s [ options ] file [ file1 ... ] ]\n",
X	    SHAR);
X    for (ptr = helpinfo; *ptr; ptr++)
X	fprintf (stderr, "%s\n", *ptr);
X
X    exit (1);
X}
SHAR_EOF
chmod 0444 shar2.c || echo "restore of shar2.c fails"
set `wc -c shar2.c`;Sum=$1
if test "$Sum" != "16149"
then echo original size 16149, current size $Sum;fi
echo "x - extracting uushar.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > uushar.c &&
X#include <stdio.h>
X#include <sys/types.h>
X#include <sys/stat.h>
X
X/* ENC is the basic 1 character encoding function to make a char printing */
X#define ENC(c) ((((c) & 077) + ' ') | ((c & 077) == 0 ? 0100 : 0))
X
Xencode (in, out)
X    FILE *in;
X    FILE *out;
X{
X    char  buf[80];
X    int  i, n;
X
X    for (;;)
X    {
X    /* 1 (up to) 45 character line */
X	n = fr (in, buf, 45);
X	putc (ENC (n), out);
X
X	for (i = 0; i < n; i += 3)
X	    outdec (&buf[i], out);
X
X	putc ('\n', out);
X	if (n <= 0)
X	    break;
X    }
X}
X
X/*
X * output one group of 3 bytes, pointed at by p, on file f.
X */
Xoutdec (p, f)
X    char *p;
X    FILE *f;
X{
X    int  c1, c2, c3, c4;
X
X    c1 = *p >> 2;
X    c2 = (*p << 4) & 060 | (p[1] >> 4) & 017;
X    c3 = (p[1] << 2) & 074 | (p[2] >> 6) & 03;
X    c4 = p[2] & 077;
X    putc (ENC (c1), f);
X    putc (ENC (c2), f);
X    putc (ENC (c3), f);
X    putc (ENC (c4), f);
X}
X
X/* fr: like read but stdio */
Xint
X     fr (fp, buf, cnt)
X    FILE *fp;
X    char *buf;
X    int  cnt;
X{
X    int  c, i;
X
X    for (i = 0; i < cnt; i++)
X    {
X	c = getc (fp);
X	if (c == EOF)
X	    return (i);
X	buf[i] = c;
X    }
X    return (cnt);
X}
SHAR_EOF
chmod 0644 uushar.c || echo "restore of uushar.c fails"
set `wc -c uushar.c`;Sum=$1
if test "$Sum" != "1114"
then echo original size 1114, current size $Sum;fi
echo "x - extracting unshar.sh (Text)"
sed 's/^X//' << 'SHAR_EOF' > unshar.sh &&
X#!/bin/sh
X#
X#  unshar shell - unpack one or more shar files
X#
X#  Copyright 1988 bill davidsen
X#	this program may be used by any person for any purpose.
X
Xfor Name in $*
Xdo	sed -n '/^[:#]/,$p' $Name | sh
Xdone
SHAR_EOF
chmod 0755 unshar.sh || echo "restore of unshar.sh fails"
set `wc -c unshar.sh`;Sum=$1
if test "$Sum" != "207"
then echo original size 207, current size $Sum;fi
echo "x - extracting Makefile (Text)"
sed 's/^X//' << 'SHAR_EOF' > Makefile &&
X#
X#  makefile for shar2
X#
X
XCFLAGS	= -O
X
XSRC	= shar2.c uushar.c unshar.sh
XOBJ	= shar2.o uushar.o
XDOC	= shar2.1 unshar.1 shar2.readme
X
Xall	: xshar unshar
X
Xxshar	: ${OBJ}
X	$(CC) shar2.o uushar.o -o xshar
X
Xunshar	: unshar.c
X	if test -f /vmunix; \
X	then cc -O unshar.c -s -o unshar; \
X	else cc -O -Dindex=strchr unshar.c -s -o unshar; \
X	fi
SHAR_EOF
chmod 0644 Makefile || echo "restore of Makefile fails"
set `wc -c Makefile`;Sum=$1
if test "$Sum" != "336"
then echo original size 336, current size $Sum;fi
echo "x - extracting unshar.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > unshar.c &&
X/****************************************************************
X * unshar.c: Unpackage one or more shell archive files
X *
X * Usage:	unshar [ -d directory ] [ file ] ...
X *
X * Description:	unshar is a filter which removes the front part
X *		of a file and passes the rest to the 'sh' command.
X *		It understands phrases like "cut here", and also
X *		knows about shell comment characters and the Unix
X *		commands "echo", "cat", and "sed".
X *
X * HISTORY
X *  1-Feb-85  Guido van Rossum (guido@mcvax) at CWI, Amsterdam
X *	Added missing 'quit' routine;
X *	added -d flag to change to directory first;
X *	added filter mode (read stdin when no arguments);
X *	added 'getopt' to get flags (makes it self-contained).
X * 29-Jan-85  Michael Mauldin (mlm) at Carnegie-Mellon University
X *	Created.
X ****************************************************************/
X
X# include <stdio.h>
X# define EOL '\n'
X
Xextern char *optarg;
Xextern int optind;
X
Xmain (argc, argv)
Xint argc;
Xchar *argv[];
X{ int i, ch;
X  FILE *in;
X
X  /* Process options */
X
X  while ((ch = getopt (argc, argv, "d:")) != EOF) {
X    switch (ch) {
X    case 'd':
X      if (chdir (optarg) == -1) {
X	fprintf (stderr, "unshar: cannot chdir to '%s'\n", optarg);
X	exit(2);
X      }
X      break;
X    default:
X      quit (2, "Usage: unshar [-d directory] [file] ...\n");
X    }
X  }
X
X  if (optind < argc)
X  { for (i= optind; i < argc; ++i)
X    { if ((in = fopen (argv[i], "r")) == NULL)
X      { fprintf (stderr, "unshar: file '%s' not found\n", argv[i]);
X        exit (1);
X      }
X      process (argv[i], in);
X      fclose (in);
X    }
X  }
X  else
X    process ("standard input", stdin);
X
X  exit (0);
X}
X
X
Xprocess (name, in)
Xchar *name;
XFILE *in;
X{ char ch;
X  FILE *shpr, *popen();
X
X   if (position (name, in))
X    { printf ("%s:\n", name);
X      if ((shpr = popen ("sh", "w")) == NULL)
X	quit (1, "unshar: cannot open 'sh' process\n");
X
X	while ((ch = fgetc (in)) != EOF)
X	  fputc (ch, shpr);
X
X	pclose (shpr);
X    }
X}
X
X/****************************************************************
X * position: position 'fil' at the start of the shell command
X * portion of a shell archive file.
X ****************************************************************/
X
Xposition (fn, fil)
Xchar *fn;
XFILE *fil;
X{ char buf[BUFSIZ];
X  long pos, ftell ();
X
X  /* Results from star matcher */
X  static char res1[BUFSIZ], res2[BUFSIZ], res3[BUFSIZ], res4[BUFSIZ];
X  static char *result[] = { res1, res2, res3, res4 };
X
X  rewind (fil);
X
X  while (1)
X  { /* Record position of the start of this line */
X    pos = ftell (fil);
X
X    /* Read next line, fail if no more */
X    if (fgets (buf, BUFSIZ, fil) == NULL)
X    { fprintf (stderr, "unshar: found no shell commands in %s\n", fn);
X      return (0);
X    }
X
X    /* Bail out if we see C preprocessor commands or C comments */
X    if (stlmatch (buf, "#include")	|| stlmatch (buf, "# include") ||
X	stlmatch (buf, "#define")	|| stlmatch (buf, "# define") ||
X	stlmatch (buf, "#ifdef")	|| stlmatch (buf, "# ifdef") ||
X	stlmatch (buf, "#ifndef")	|| stlmatch (buf, "# ifndef") ||
X	stlmatch (buf, "/*"))
X    { fprintf (stderr,
X	       "unshar: %s looks like raw C code, not a shell archive\n", fn);
X      return (0);
X    }
X
X    /* Does this line start with a shell command or comment */
X    if (stlmatch (buf, "#")	|| stlmatch (buf, ":") ||
X	stlmatch (buf, "echo ")	|| stlmatch (buf, "sed ") ||
X	stlmatch (buf, "cat "))
X    { fseek (fil, pos, 0); return (1); }
X
X    /* Does this line say "Cut here" */
X    if (smatch (buf, "*CUT*HERE*", result) ||
X	smatch (buf, "*cut*here*", result) ||
X	smatch (buf, "*TEAR*HERE*", result) ||
X	smatch (buf, "*tear*here*", result) ||
X	smatch (buf, "*CUT*CUT*", result) ||
X	smatch (buf, "*cut*cut*", result))
X    {
X      /* Read next line after "cut here", skipping blank lines */
X      while (1)
X      { pos = ftell (fil);
X
X        if (fgets (buf, BUFSIZ, fil) == NULL)
X	{ fprintf (stderr,
X		"unshar: found no shell commands after 'cut' in %s\n", fn);
X	  return (0);
X	}
X	
X	if (*buf != '\n') break;
X      }
X
X      /* Win if line starts with a comment character of lower case letter */
X      if (*buf == '#' || *buf == ':' || (('a' <= *buf) && ('z' >= *buf)))
X      { fseek (fil, pos, 0);
X	return (1);
X      }
X
X      /* Cut here message lied to us */      
X      fprintf (stderr, "unshar: %s is probably not a shell archive,\n", fn);
X      fprintf (stderr, "        the 'cut' line was followed by: %s", buf);
X      return (0);
X    }
X  }
X}
X
X/*****************************************************************
X * stlmatch  --  match leftmost part of string
X *
X * Usage:  i = stlmatch (big,small)
X *	int i;
X *	char *small, *big;
X *
X * Returns 1 iff initial characters of big match small exactly;
X * else 0.
X *
X * HISTORY
X * 18-May-82 Michael Mauldin (mlm) at Carnegie-Mellon University
X *      Ripped out of CMU lib for Rog-O-Matic portability
X * 20-Nov-79  Steven Shafer (sas) at Carnegie-Mellon University
X *	Rewritten for VAX from Ken Greer's routine.
X *
X *  Originally from klg (Ken Greer) on IUS/SUS UNIX
X *****************************************************************/
X
Xint   stlmatch (big, small)
Xchar *small, *big;
X{ register char *s, *b;
X  s = small;
X  b = big;
X  do
X  { if (*s == '\0')
X      return (1);
X  }
X  while (*s++ == *b++);
X  return (0);
X}
X
X/*****************************************************************
X * smatch: Given a data string and a pattern containing one or
X * more embedded stars (*) (which match any number of characters)
X * return true if the match succeeds, and set res[i] to the
X * characters matched by the 'i'th *.
X *****************************************************************/
X
Xsmatch (dat, pat, res)
Xregister char *dat, *pat, **res;
X{ register char *star = 0, *starend, *resp;
X  int nres = 0;
X
X  while (1)
X  { if (*pat == '*')
X    { star = ++pat; 			     /* Pattern after * */
X      starend = dat; 			     /* Data after * match */
X      resp = res[nres++]; 		     /* Result string */
X      *resp = '\0'; 			     /* Initially null */
X    }
X    else if (*dat == *pat) 		     /* Characters match */
X    { if (*pat == '\0') 		     /* Pattern matches */
X	return (1);
X      pat++; 				     /* Try next position */
X      dat++;
X    }
X    else
X    { if (*dat == '\0') 		     /* Pattern fails - no more */
X	return (0); 			     /* data */
X      if (star == 0) 			     /* Pattern fails - no * to */
X	return (0); 			     /* adjust */
X      pat = star; 			     /* Restart pattern after * */
X      *resp++ = *starend; 		     /* Copy character to result */
X      *resp = '\0'; 			     /* null terminate */
X      dat = ++starend; 			     /* Rescan after copied char */
X    }
X  }
X}
X
X/*****************************************************************
X * Addendum: quit subroutine (print a message and exit)
X *****************************************************************/
X
Xquit (status, message)
Xint status;
Xchar *message;
X{
X  fprintf(stderr, message);
X  exit(status);
X}
X
X/*****************************************************************
X * Public Domain getopt routine
X *****************************************************************/
X
X/*
X * get option letter from argument vector
X */
Xint	opterr = 1,		/* useless, never set or used */
X	optind = 1,		/* index into parent argv vector */
X	optopt;			/* character checked for validity */
Xchar	*optarg;		/* argument associated with option */
X
X#define BADCH	(int)'?'
X#define EMSG	""
X#define tell(s)	fputs(*nargv,stderr);fputs(s,stderr); \
X		fputc(optopt,stderr);fputc('\n',stderr);return(BADCH);
X
Xgetopt(nargc,nargv,ostr)
Xint	nargc;
Xchar	**nargv,
X	*ostr;
X{
X	static char	*place = EMSG;	/* option letter processing */
X	register char	*oli;		/* option letter list index */
X	char	*index();
X
X	if(!*place) {			/* update scanning pointer */
X		if(optind >= nargc || *(place = nargv[optind]) != '-' || !*++place) return(EOF);
X		if (*place == '-') {	/* found "--" */
X			++optind;
X			return(EOF);
X		}
X	}				/* option letter okay? */
X	if ((optopt = (int)*place++) == (int)':' || !(oli = index(ostr,optopt))) {
X		if(!*place) ++optind;
X		tell(": illegal option -- ");
X	}
X	if (*++oli != ':') {		/* don't need argument */
X		optarg = NULL;
X		if (!*place) ++optind;
X	}
X	else {				/* need an argument */
X		if (*place) optarg = place;	/* no white space */
X		else if (nargc <= ++optind) {	/* no arg */
X			place = EMSG;
X			tell(": option requires an argument -- ");
X		}
X	 	else optarg = nargv[optind];	/* white space */
X		place = EMSG;
X		++optind;
X	}
X	return(optopt);			/* dump back option letter */
X}
SHAR_EOF
chmod 0644 unshar.c || echo "restore of unshar.c fails"
set `wc -c unshar.c`;Sum=$1
if test "$Sum" != "8429"
then echo original size 8429, current size $Sum;fi
exit 0