[alt.sources] shar 3.43 part 2 of 2

rhg@cpsolv.CPS.COM (Richard H. Gumpertz) (09/09/90)

Submitted-by: rhg@cpsolv.cps.com
Archive-name: shar3.43/part02

---- Cut Here and feed the following to sh ----
#!/bin/sh
# This is part 02 of shar3.43
# ============= shar.c ==============
if test -f 'shar.c' -a X"$1" != X"-c"; then
	echo 'x - skipping shar.c (File already exists)'
else
echo 'x - extracting shar.c (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'shar.c' &&
Xchar *revision = "3.43";
Xchar RCS_ID[] = "$Header: /u/rhg/src/shar/shar.c,v 3.43 90/08/13 07:50:34 rhg Exp $";
X/*
X** shar.c
X
X  Defined functions:
X	gen_mkdir(path)
X	gen_mkdir_script(path)
X	setTOUCH()
X	walktree(rtn,rootname)
X	header(argc,argv)
X	helpuser()
X	main(argc,argv)
X	mode_map(mode,mode_str)
X	shar(file,RstrName)
X
X*/
X/*+:EDITS:*/
X/*:08-06-1990-00:40-rhg@cps.com-revised Cut message to be more explanatory */
X/*:08-05-1990-14:04-rhg@cps.com-merged Rname into walktree */
X/*:08-05-1990-12:11-rhg@cps.com-added walktree & support for sharing dirs */
X/*:08-05-1990-09:05-rhg@cps.com-change -Bn, -t, and -b to -bn, -T, and -B */
X/*:08-04-1990-15:31-rhg@cps.com-added -Bn to set compress -bn (default 12) */
X/*:08-04-1990-15:31-rhg@cps.com-changed shar3_???_.tmp to _shar_???_.tmp */
X/*:08-04-1990-15:22-rhg@cps.com-added check for "exit 0" under OptPREFIX */
X/*:08-04-1990-14:32-rhg@cps.com-added -m to generate TOUCH (default off) */
X/*:08-04-1990-14:18-rhg@cps.com-reversed the meaning of -x and deleted -O */
X/*:06-14-1990-14:48-rhg@cps.com-made Split and eXists compatible.
X/*:06-14-1990-14:18-rhg@cps.com-made -x the default and added -O
X/*:06-14-1990-12:44-rhg@cps.com-always terminate the && and report failures
X/*:06-14-1990-12:28-rhg@cps.com-clear mkdir_already between -l files
X/*:06-14-1990-12:14-rhg@cps.com-change PREFIX from a #define to an int variable.
X/*:04-19-1990-22:49-rhg@cps.com-get rid of "set" so "sh sharfil -c" will work */
X/*:04-19-1990-21:52-rhg@cps.com-add -F to clear OptPREFIX */
X/*:04-18-1990-08:49-rhg@cps.com-add OptPREFIX (for now, always on) */
X/*:07-09-1990-19:24-wht@n4hgf-back to fgrep amc -- fits more -m touches */
X/*:07-01-1990-18:37-wht@n4hgf-wait() needed after fork() */
X/*:05-19-1990-02:47-wht@n4hgf-change fgrep amc to mmdd */
X/*:05-16-1990-01:53-wht@n4hgf-Archive-name had extra period sometimes */
X/*:05-10-1990-20:39-wht@n4hgf-altos does not not like at-sign in filenames */
X/*:05-10-1990-13:38-wht@n4hgf-add -V Vanilla mode */
X/*:05-07-1990-00:06-wht@n4hgf-test all mallocs for Purity Of Essence */
X/*:05-07-1990-00:06-wht@n4hgf-add -S switch */
X/*:05-05-1990-01:37-relay.EU.net!rivm!a3-dont assume vax is running BSD */
X/*:04-18-1990-02:01-wht@n4hgf-3.20 rhg@cps.com did all the NICE work */
X/*:04-17-1990-14:30-rhg@cps.com-pretty up if-then-else-fi in shar file */
X/*:04-17-1990-12:13-rhg@cps.com-add Split and renamed old -l to -L */
X/*:04-17-1990-12:13-rhg@cps.com-add -c option to shar file execution */
X/*:04-17-1990-11:20-rhg@cps.com-simplify TOUCH logic in shar file */
X/*:04-17-1990-10:27-rhg@cps.com-create setTOUCH to avoid duplicate code */
X/*:04-17-1990-04:43-rhg@cps.com-add missing && to commands in shar file(s) */
X/*:04-17-1990-02:03-rhg@cps.com-add Compress */
X/*:04-16-1990-17:08-rhg@cps.com-add AvoidPipes as well as code to use pipes */
X/*:04-03-1990-20:09-wht@n4hgf-3.11 */
X/*:04-01-1990-13:20-pat@rwing-correct case on M option in getopt() call */
X/*:04-01-1990-13:50-pat@rwing-change defaults on -v, -w to be on */
X/*:03-29-1990-18:23-wht@n4hgf-add automatic sequent support */
X/*:03-28-1990-15:56-wht@n4hgf-add mode and length net.bandwidth chrome */
X/*:03-28-1990-14:23-wht@n4hgf-correct some runtime diagnostics */
X/*:11-14-1989-02:21-wht-SHAR_EOF was botched if last file char not newline */
X/*:11-02-1989-14:11-wht-add touch -am */
X
X/*
X Shar puts readable text files together in a package
X from which they are easy to extract.
X earlier attribution wht@n4hgf has:	decvax!microsof!uw-beave!jim
X                                    (James Gosling at CMU)
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 wc of
X *			  the orignal file and the wc 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/*
X *  One good hack deserves another ... this version generates shell
X *  code which attempts to create missing directories
X *  handle deviants sun, vax, pyr (pyramid), SCO XENIX/UNIX automatically
X *  for sequent, add -DBSD42
X *  force Verbose on
X *  if unsharing system's touch Sys V compatible (allows touch -m),
X *  restore file dates
X *  -n switch puts an alpha "name" in header
X *  -a (if also -n) puts "Submitted-by:" & "Archive-name: <name>/part##
X *  use getopt
X *  as well as some other chrome-plated junque
X *  ...!gatech!emory!tridom!wht (wht%n4hgf@gatech.edu) Warren Tucker
X *
X *  3.11 - Fri Apr  6 14:21:51 EDT 1990
X *  With due deference to davidsen@sixhub, more changes..... copies
X *  of this, like 3.10,  were mailed to him:
X *  From wht  Fri Apr  6 15:14:30 1990 remote from n4hgf
X *  Received: by n4hgf.UUCP (smail2.5-UNIX/386 5.3.2)
X *  	id AA01781; 6 Apr 90 15:14:30 EDT (Fri)
X *  Date: Fri, 6 Apr 90 15:14:30 EDT
X *  X-Mailer: Mail User's Shell (6.5 4/17/89)
X *  From: wht@n4hgf (Warren Tucker)
X *  To: davidsen@sixhub
X *  Subject: shar 3.11
X *  X-Bang-Reply-to: gatech!n4hgf!wht -or- emory!tridom!n4hgf!wht
X *  Reply-to: wht%n4hgf@gatech.edu
X *  Message-Id: <9004061514.AA01781@n4hgf.UUCP>
X *
X *  1. changes suggested by pat@rwing (Pat Myrto) and silvert@cs.dal.ca
X *  (Bill Silvert)
X *  2. fixes to who_am_i code in who@where.c
X *
X *  3.20 - Wed Apr 18 01:58:32 EDT 1990
X *  changes were made per edit notes by
XFrom: gatech!mailrus!uunet!cpsolv.CPS.COM!rhg (Richard H. Gumpertz)
X *  ...!gatech!n4hgf!wht (wht%n4hgf@gatech.edu) Warren Tucker
X *
X */
X
X#include <stdio.h>
X#include <sys/types.h>
X#include <time.h>
X#include <sys/stat.h>
X#include <ctype.h>
X
X/* assume system v unless otherwise fixed */
X#if (defined(pyr) || defined(vax) || defined(sequent)) && !defined(BSD42) && !defined(SYS5)
X#define BSD42
X#endif
X#if defined(sun)	/* this miscreant doesn't exactly fit BSD or SYSV */
X#undef BSD42
X#undef SYS5
X#endif
X#if !defined(BSD42) && !defined(sun) && !defined(SYS5)
X#define SYS5
X#endif
X
X#if defined(sun) || defined(BSD42)
X#define strchr	index
X#define strrchr	rindex
X#endif
X
Xchar *strchr();
Xchar *strrchr();
X#ifdef __STDC__ /* my concession to ANSI-pansiness */
Xvoid *malloc();
X#else
Xchar *malloc();
X#endif
XFILE *fdopen();
XFILE *popen();
X
X#define	DELIM		"SHAR_EOF"/* put after each file */
X#define PREFIX1		'X'	/* goes in front of each line */
X#define PREFIX2		'Y'	/* goes in front of each line if Delim[0] == PREFIX1 */
X#define WC	        "wc -c <"
X
Xint PREFIX = PREFIX1;	/* Character to get at the beginning of each line */
X
Xint Archive_name = 0;	/* option to generate "Archive-name:" headers */
Xint Verbose = 1;		/* option to provide append/extract feedback */
Xint Wc_c = 1;			/* option to provide wc checking */
Xchar *Delim = DELIM;	/* pointer to delimiter string */
Xint OptPREFIX = 1;		/* suppress PREFIX unless 1st char forces it */
Xint Cut = 0;			/* option to provide cut mark */
Xchar *CutMessage = "---- Cut Here and feed the following to sh ----\n";
Xint Binary = 0;			/* flag for binary files */
Xint AvoidPipes = 0;		/* use temp file instead of pipe to feed uudecode, etc.
X						   (better error detection at expense of disk space) */
Xint Vanilla = 0;		/* no Brown-Shirt mode */
Xint Touch = 0;			/* generate $TOUCH commands */
Xint Compress = 0;		/* run input files through compress (requires Binary) */
Xint CompressBits = 12;	/* -b option to compress */
Xint Mixed = 0;			/* mixed text and binary files */
Xint eXists = 1;			/* check if file exists */
Xint InterOW = 0;		/* interactive overwrite */
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 *fpout = stdout;
Xint shar();
Xunsigned limit = 0;
Xint Split = 0;			/* Split files in the middle */
Xlong ftell();
Xlong TypePos;			/* position for archive type message */
Xlong EndHeadPos;		/* position for first file in the shar file */
Xchar outname[50];		/* base for output filename */
Xchar filename[50];		/* actual output filename */
Xchar *sharname = (char *)0;
Xchar *submitter = (char *)0;
Xint filenum = 0;		/* output file # */
Xstruct stat fst;		/* check file type, access */
X
Xmain(argc,argv)
Xchar **argv;
X{
Xint status = 0;
Xint stdin_file_list = 0;
Xchar *oname;
Xint c;
Xextern int optind;
Xextern char *optarg;
X
X	while((c = getopt(argc,argv,"VmSvwd:BTCb:xXcfMpPFas:n:l:L:o:h")) != -1)
X	{
X		switch(c)
X		{
X		case 'V':
X			Vanilla = 1;
X			break;
X		case 'm':
X			Touch = 1;
X			break;
X		case 'S':
X			stdin_file_list = 1;
X			break;
X		case 'v':
X			Verbose = 0;
X			break;
X		case 'w':
X			Wc_c = 0;
X			break;
X		case 'd':
X			Delim = optarg;
X			PREFIX = (Delim[0] == PREFIX1 ? PREFIX2 : PREFIX1);
X			break;
X		case 'B': /* binary files */
X			Binary = 1;
X			Compress = 0;
X			break;
X		case 'T': /* text mode */
X			Binary = 0;
X			Compress = 0;
X			break;
X		case 'b': /* Compress bits */
X			CompressBits = atoi(optarg);
X			/* fall through to -C */
X		case 'C': /* Compress */
X			Binary = 1;
X			Compress = 1;
X			break;
X		case 'x': /* don't worry whether the file exist */
X			eXists = 0;
X			break;
X		case 'X': /* ask the user whether to overwrite existing files */
X			InterOW = 1;
X			eXists = 1;
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 'P': /* use temp files instead of pipes in the shar file */
X			AvoidPipes = 1;
X			break;
X		case 'F': /* force PREFIX to be put out even if not required */
X			OptPREFIX = 0;
X			break;
X		case 'l': /* soft size limit in k */
X			if((limit = atoi(optarg)) > 1)
X				--limit;
X			Split = 0;
X			DeBug("Soft limit %dk\n",limit);
X			break;
X		case 'L': /* hard size limit in k */
X			if((limit = atoi(optarg)) > 1)
X				--limit;
X			Split = (limit != 0);
X			AvoidPipes = 1;
X			DeBug("Hard limit %dk\n",limit);
X			break;
X		case 'n': /* name of archive */
X			sharname = optarg;
X			break;
X		case 's': /* submitter */
X			submitter = optarg;
X			break;
X		case 'a': /* generate Archive-name: headers */
X			Archive_name = 1;
X			break;
X		case 'o': /* specify output file */
X			oname = optarg;
X			strcpy(outname,oname);
X			strcat(outname,".");
X			filenum = 1;
X			strcpy(filename,outname);
X			strcat(filename,"01");
X			fpout = fopen(filename,"w");
X			if(!fpout)
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		case 'h': /* help */
X			helpuser();
X			break;
X		}
X	}
X
X	if(Vanilla)
X	{
X		fprintf(stderr,"Vanilla mode disabling years of progress :-)\n");
X		Wc_c = 0;
X		OptPREFIX = 0;
X#ifdef V_AVOIDPIPES	/* pipes are benign and only used with uudecode anyway */
X		AvoidPipes = 1;
X#endif /* V_AVOIDPIPES */
X#ifdef V_NOFORCE	/* If the user specifies non-defaults, let him have them */
X#else
X		Touch = 0;
X		InterOW = 0;
X		if(Binary || Mixed || Compress || PosParam)
X			fprintf(stderr,"WARNING: non-Text storage options overridden.\n");
X		Binary = 0;
X		Mixed = 0;
X		Compress = 0;
X		PosParam = 0;
X#endif /* V_NOFORCE */
X	}
X
X	if(stdin_file_list)
X	{
X		char stdin_buf[258];
X		argc = 0;
X		if(!(argv = (char **)malloc(1024 * sizeof(char *))))
X			goto MEMORY_ERROR;
X		stdin_buf[0] = 0;
X		while(fgets(stdin_buf,sizeof(stdin_buf),stdin))
X		{
X			if(argc == 1024)
X			{
X				fprintf(stderr,"max files from stdin is 1024!\n");
X				exit(1);
X			}
X			if(stdin_buf[0])
X				stdin_buf[strlen(stdin_buf) - 1] = 0;
X			if(!(argv[argc] = malloc(strlen(stdin_buf) + 1)))
X			{
XMEMORY_ERROR: /* NOT likely, but free software must pure as snow! */
X				fprintf(stderr,"out of memory handling stdin input at %d\n",
X					argc);
X				exit(1);
X			}
X			strcpy(argv[argc],stdin_buf);
X			++argc;
X			stdin_buf[0] = 0;
X		}
X		optind = 0;
X	}
X
X	if(optind >= argc)
X	{
X		fprintf(stderr,"shar: No input files\n");
X		helpuser();
X		exit(1);
X	}
X
X	if(Archive_name && !sharname)
X	{
X		fprintf(stderr,"shar: -n must accompany -a\n");
X		helpuser();
X		exit(1);
X	}
X
X	if(!submitter)
X	{
X		if(!(submitter = malloc(128)))
X		{
X			fprintf(stderr,"memory allocation failed\n"); /* NOT likely */
X			exit(1);
X		}
X		who_where(submitter);
X	}
X
X	if(header(argc-optind,&argv[optind]))
X		exit(2);
X
X	if(InterOW)
X	{
X		Verbose = 1;
X		fputs("wish=\n",fpout);
X		if(Archive_name)
X		{
X			fprintf(stderr,
X				"PLEASE do not submit -X shars to the usenet or other\n");
X			fprintf(stderr,
X				"public networks.  They will cause problems.\n");
X		}
X	}
X
X	EndHeadPos = ftell(fpout);
X
X	for( ; optind < argc; ++optind)
X	{ /* process positional parameters and files */
X		if(PosParam)
X		{		/* allow -B and -T and -C inline */
X			if(strcmp(argv[optind],"-B") == 0)
X			{ /* set binary */
X				Binary = 1;
X				Compress = 0;
X				continue;
X			}
X			if(strcmp(argv[optind],"-T") == 0)
X			{ /* set mode text */
X				Binary = 0;
X				Compress = 0;
X				continue;
X			}
X			if(strcmp(argv[optind],"-C") == 0)
X			{ /* set compress */
X				Binary = 1;
X				Compress = 1;
X				continue;
X			}
X		}
X		status += walktree(shar,argv[optind]);
X	}
X
X	/* delete the sequence file, if any */
X	if(Split && filenum > 1)
X	{
X		fputs("rm -f _shar_seq_.tmp\n",fpout);
X		fputs("echo You have unpacked the last part\n",fpout);
X		if(!Verbose)
X			fprintf(stderr,"Created %d files\n",filenum);
X	}
X	fputs("exit 0\n",fpout);
X	exit(status);
X}
X
X/*+-----------------------------------------------------------------------
X	mode_map(mode,mode_str)	build drwxrwxrwx string
X------------------------------------------------------------------------*/
Xchar *
Xmode_map(mode,mode_str)
Xunsigned short mode;
Xchar *mode_str;
X{
Xregister unsigned ftype = mode & S_IFMT;
Xregister char *rtn;
Xstatic char result[12];
X
X	rtn = (mode_str == (char *)0) ? result : mode_str;
X
X	/*          drwxrwxrwx */
X	/*          0123456789 */
X	strcpy(rtn,"----------");
X
X#ifdef THIS_IS_NOT_NEEDED_FOR_SHAR
X	switch(ftype)
X	{
X		case S_IFIFO:	*rtn = 'p'; break; /* FIFO (named pipe) */
X		case S_IFDIR:	*rtn = 'd'; break; /* directory */
X		case S_IFCHR:	*rtn = 'c'; break; /* character special */
X		case S_IFBLK:	*rtn = 'b'; break; /* block special */
X		case S_IFREG:	*rtn = '-'; break; /* regular */
X
X#if defined(sun) | defined(BSD42)
X		case S_IFLNK:	*rtn = 'l'; break; /* symbolic link */
X		case S_IFSOCK:	*rtn = 's'; break; /* socket */
X#endif
X
X#if defined (SYS5)
X		case S_IFNAM:						/* name space entry */
X			if(mode & S_INSEM)				/* semaphore */
X			{
X				*rtn = 's';
X				break;
X			}
X			if(mode & S_INSHD)				/* shared memory */
X			{
X				*rtn = 'm';
X				break;
X			}
X#endif
X
X		default:		*rtn = '?'; break;	/* ??? */
X	}
X#endif /* THIS_IS_NOT_NEEDED_FOR_SHAR */
X
X	if(mode & 000400) *(rtn + 1) = 'r';
X	if(mode & 000200) *(rtn + 2) = 'w';
X	if(mode & 000100) *(rtn + 3) = 'x';
X	if(mode & 004000) *(rtn + 3) = 's';
X	if(mode & 000040) *(rtn + 4) = 'r';
X	if(mode & 000020) *(rtn + 5) = 'w';
X	if(mode & 000010) *(rtn + 6) = 'x';
X	if(mode & 002000) *(rtn + 6) = 's';
X	if(mode & 000004) *(rtn + 7) = 'r';
X	if(mode & 000002) *(rtn + 8) = 'w';
X	if(mode & 000001) *(rtn + 9) = 'x';
X	if(mode & 001000) *(rtn + 9) = 't';
X
X	return(rtn);
X
X}	/* end of mode_map */
X
Xvoid
XsetTOUCH()
X{
X	if(Touch)
X	{
X		fputs("if touch 2>&1 | fgrep 'amc' > /dev/null\n",fpout);
X		fputs(" then TOUCH=touch\n",fpout);
X		fputs(" else TOUCH=true\n",fpout);
X		fputs("fi\n",fpout);
X	}
X} /* end of setTOUCH */
X
X#ifdef NO_WALKTREE
X
Xint
Xwalktree(rtn,file)				/* dummy walktree */
Xint (*rtn)(/*file,rname*/);	/* may also assume fst is set */
Xchar *file;
X{
X	register char *rname;
X
X	if(stat(file,&fst))
X	{
X		fprintf(stderr,"shar: Can't access %s\n",file);
X		return(1);
X	}
X
X	if(FileStrip)
X	{ /* use just the filename */
X		rname = file + strlen(file);
X		while(rname > file && *rname != '/')
X			--rname;
X		if(*rname == '/')
X			++rname;
X	}
X	else
X		rname = file;
X	if(!strncmp(rname,"./",2) && rname[2])
X		rname += 2;
X
X	return((*rtn)(file,rname));
X}
X
X#else /* NO_WALKTREE*/
X
X#include <dirent.h>			/* Doug Gwyn's dirent routines */
XDIR *opendir();
Xstruct dirent *readdir();
Xint closedir();
X
Xint
Xwalkdown(rtn,file,filelen,rname)
Xint (*rtn)(/*file,rname*/);	/* may also assume fst is set */
Xchar *file, *rname;			/* *rname must be *file + n where n < filelen */
Xint filelen;
X{
X	DIR *dirp;
X	struct dirent *dp;
X
X	if(stat(file,&fst))
X	{
X		fprintf(stderr,"shar: Can't access %s\n",file);
X		return(1);
X	}
X
X	if((fst.st_mode & S_IFMT) != S_IFDIR)
X		return((*rtn)(file,rname));
X
X	if(!(dirp = opendir(file)))
X	{
X		fprintf(stderr,"shar: unable to open directory %s",file);
X		return(1);
X	}
X
X	if(!strcmp(rname,"."))
X		rname += 2;				/* avoid "./xxx" when sharing "." */
X
X	while((dp = readdir(dirp)))
X		if (strcmp(dp->d_name,".") && strcmp(dp->d_name,".."))
X		{
X			int newlen;
X
X			if((newlen = filelen + 1 + strlen(dp->d_name)) >= MAXNAMLEN)
X			{
X				fprintf(stderr,"shar: file name too long: %s/%s\n",
X						file,dp->d_name);
X				return(1);
X			}
X			sprintf(file + filelen,"/%s",dp->d_name);
X
X			if(walkdown(rtn,file,newlen,rname))
X				return(1);
X
X			file[filelen] = '\0';	/* in case we print any error messages */
X		}
X
X	if(closedir(dirp))
X	{
X		fprintf(stderr,"shar: unable to close directory %s",file);
X		return(1);
X	}
X}
X
Xint
Xwalktree(rtn,rootname)				/* real walktree */
Xint (*rtn)(/*file,rname*/);	/* may also assume fst is set */
Xchar *rootname;
X{
X	char file[MAXNAMLEN];
X	int filelen;
X	register char *rname;
X
X	if((filelen = strlen(rootname)) >= MAXNAMLEN)
X	{
X		fprintf(stderr,"shar: file name too long: %s\n",rootname);
X		return(1);
X	}
X	strcpy(file,rootname);
X
X	if(FileStrip)
X	{ /* use just the filename */
X		rname = file + filelen;
X		while(rname > file && *rname != '/')
X			--rname;
X		if(*rname == '/')
X			++rname;
X	}
X	else
X		rname = file;
X	if(!strncmp(rname,"./",2) && rname[2])
X		rname += 2;
X
X	return(walkdown(rtn,file,filelen,rname));
X}
X
X#endif /* NO_WALKTREE */
X
Xint
Xonecheck(file,rname)
Xchar *file, *rname;
X{
X	if(access(file,04))
X	{
X		fprintf(stderr,"shar: Can't access %s\n",file);
X		return(1);
X	}
X
X	return(0);
X}
X
Xint
Xoneheader(file,rname)
Xchar *file, *rname;
X{
X	fprintf(fpout,"# %6ld %s %s\n",fst.st_size,
X		mode_map(fst.st_mode & ~(S_ISUID|S_ISGID|S_ISVTX),(char *)0),rname);
X	return(0);
X}
X
Xheader(argc,argv)
Xchar **argv;
X{
Xint i;
XFILE *fpsource;	/* pipe temp */
Xchar s128[128];
Xlong now;
Xstruct tm *utc;
Xstruct tm *gmtime();
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 or -L option without -o\n");
X		helpuser();
X		return(1);
X	}
X
X	for(i = 0; i < argc; i++)
X	{ /* skip positional parameters */
X		if(PosParam &&
X			(strcmp(argv[i],"-B") == 0 ||
X		     strcmp(argv[i],"-T") == 0 ||
X		     strcmp(argv[i],"-C") == 0))
X			continue;
X
X		if(walktree(onecheck,argv[i]))
X			return(1);
X	}
X
X	if(Archive_name)
X	{
X		fprintf(fpout,"Submitted-by: %s\n",submitter);
X		fprintf(fpout,"Archive-name: %s%s%02d\n\n",
X			sharname,(strchr(sharname,'/')) ? "" : "/part",
X			(filenum) ? filenum : 1);
X	}
X
X	if(Cut)
X		fputs(CutMessage,fpout);
X	fputs("#!/bin/sh\n",fpout);
X	if(sharname)
X		fprintf(fpout,"# This is %s, a shell archive (shar %s)\n",
X			sharname,revision);
X	else
X		fprintf(fpout,"# This is a shell archive (shar %s)\n",revision);
X
X	time(&now);
X	utc = gmtime(&now);
X	fprintf(fpout,"# made %02d/%02d/%04d %02d:%02d UTC by %s\n",
X		utc->tm_mon + 1,utc->tm_mday,utc->tm_year + 1900,
X		utc->tm_hour,utc->tm_min,
X		submitter);
X
X#if defined(SYS5)
X	if(!(fpsource = popen("/bin/pwd","r")))
X		return(-1);
X	fgets(s128,sizeof(s128),fpsource);
X	s128[strlen(s128) - 1] = 0;
X	fclose(fpsource);
X#else
X#if defined(BSD42) || defined(sun)
X	getwd(s128);
X#else
X#include "Need_conditional_compile_fix"
X#endif
X#endif
X	fprintf(fpout,"# Source directory %s\n",s128);
X
X	fprintf(fpout,"#\n# existing files %s\n",
X		(eXists) ? "will NOT be overwritten unless -c is specified" 
X			: ((InterOW) ? "MAY be overwritten"
X				: "WILL be overwritten"));
X
X	if(InterOW)
X		fputs("# The unsharer will be INTERACTIVELY queried.\n",fpout);
X
X	if(Vanilla)
X	{
X		fputs("# This format requires very little intelligence at unshar time.\n",fpout);
X		fputs("# ",fpout);
X		if(eXists || Split)
X			fputs("\"if test\", ",fpout);
X		if(Split)
X			fputs("\"cat\", \"rm\", ",fpout);
X		fputs("\"echo\", \"true\", and \"sed\" may be needed.\n",fpout);
X	}
X
X	if(Split)
X	{ /* may be split, explain */
X		fputs("#\n",fpout);
X		TypePos = ftell(fpout);
X		fprintf(fpout,"%-75s\n%-75s\n","#","#");
X	}
X
X	fputs("#\n# This shar contains:\n",fpout);
X	fputs("# length  mode       name\n",fpout);
X	fputs("# ------ ---------- ------------------------------------------\n",
X		fpout);
X	for(i = 0; i < argc; i++)
X	{ /* output names of files but not parameters */
X		if(PosParam &&
X			(strcmp(argv[i],"-B") == 0 ||
X		     strcmp(argv[i],"-T") == 0 ||
X		     strcmp(argv[i],"-C") == 0))
X			continue;
X		if(walktree(oneheader,argv[i]))
X			exit(1);
X	}
X	fputs("#\n",fpout);
X
X	setTOUCH();
X
X	if(Split)
X	{ /* now check the sequence */
X		fputs("if test -r _shar_seq_.tmp; then\n",fpout);
X		fputs("\techo 'Must unpack archives in sequence!'\n",fpout);
X		fputs("\techo Please unpack part `cat _shar_seq_.tmp` next\n",fpout);
X		fputs("\texit 1\nfi\n",fpout);
X	}
X	return(0);
X}
X
X#define MAX_MKDIR_ALREADY	128	/* ridiculously enough */
Xchar *mkdir_already[MAX_MKDIR_ALREADY];
Xint mkdir_already_count = 0;
X
Xvoid
Xgen_mkdir(path)
Xchar *path;
X{
Xregister int ialready;
Xchar *cptr;
X
X/* if already generated code for this dir creation, don't do again */
X	for(ialready = 0; ialready < mkdir_already_count; ialready++)
X	{
X		if(!strcmp(path,mkdir_already[ialready]))
X			return;
X	}
X
X/* haven't done this one */
X	if(mkdir_already_count == MAX_MKDIR_ALREADY)
X	{
X		fprintf(stderr,"too many directories for mkdir generation\n");
X		exit(255);
X	}
X	if(!(cptr = mkdir_already[mkdir_already_count++] = malloc(strlen(path)+1)))
X	{
X		fprintf(stderr,"out of memory for mkdir generation\n");
X		exit(255);
X	}
X	strcpy(cptr,path);
X
X/* generate the text */
X	fprintf(fpout,"if test ! -d '%s'; then\n",path);
X	if(Verbose)
X		fprintf(fpout,"    echo 'x - creating directory %s'\n",path);
X	fprintf(fpout,"    mkdir '%s'\n",path);
X	fputs("fi\n",fpout);
X
X}	/* end of gen_mkdir */
X
Xvoid
Xgen_mkdir_script(path)
Xregister char *path;
X{
Xregister char *cptr;
X
X	for(cptr = strchr(path,'/'); cptr; cptr = strchr(cptr + 1,'/'))
X	{
X		/* avoid empty string if leading or double '/' */
X		if(cptr == path || *(cptr - 1) == '/')
X			continue;
X		/* omit '.' */
X		if((*(cptr - 1) == '.') && ((cptr == path + 1) || (*(cptr - 2) == '/')))
X			continue;
X		*cptr = 0;				/* temporarily terminate string */
X		gen_mkdir(path);
X		*cptr = '/';
X	}
X}	/* end of gen_mkdir_script */
X
Xint
Xshar(file,RstrName)
Xchar *file, *RstrName;
X{
Xchar line[BUFSIZ];
XFILE *fpsource;
Xlong cursize,remaining,ftell();
Xint split = 0;		/* file split flag */
Xchar *filetype;		/* text or binary */
Xstruct tm *lt;
Xchar *filename_base;
X
X	/* check to see that this is still a regular file  and readable */
X	if((fst.st_mode & S_IFMT) != S_IFREG)
X	{ /* this is not a regular file */
X		fprintf(stderr,"shar: %s is not a regular file\n",file);
X		return(1);
X	}
X	if(access(file,04))
X	{
X		fprintf(stderr,"shar: Can't access %s\n",file);
X		return(1);
X	}
X
X	/* if limit set, get the current output length */
X	if(limit)
X	{
X		cursize = ftell(fpout);
X		remaining = (limit * 1024L) - cursize;
X		DeBug("In shar: remaining size %ld\n",remaining);
X		
X		if(!Split && cursize > EndHeadPos &&
X			(Binary ? fst.st_size + fst.st_size/3 : fst.st_size) > remaining)
X		{ /* change to another file */
X			DeBug("Newfile, remaining %ld, ",remaining);
X			DeBug("limit still %d\n",limit);
X
X			/* close the "&&" and report an error if any of the above failed */
X			fprintf(fpout,"true || echo 'restore of %s failed'\n",RstrName);
X
X			fprintf(fpout, "echo End of part %d, continue with part %d\n",
X				filenum,filenum + 1);
X			fputs("exit 0\n",fpout);
X
X			fclose(fpout);
X
X			/* Clear mkdir_already in case the user unshars out of order */
X			while (mkdir_already_count > 0)
X				free(mkdir_already[--mkdir_already_count]);
X
X			/* form the next filename */
X			sprintf(filename,"%s%02d",outname,++filenum);
X			fpout = fopen(filename,"w");
X			if(Verbose)
X				fprintf(stderr,"Starting file %s\n",filename);
X
X			if(Archive_name)
X			{
X				fprintf(fpout,"Submitted-by: %s\n",submitter);
X				fprintf(fpout,"Archive-name: %s%s%02d\n\n",
X					sharname,(strchr(sharname,'/')) ? "" : "/part",
X					(filenum) ? filenum : 1);
X			}
X
X			if(Cut)
X				fputs(CutMessage,fpout);
X			if(!(filename_base = strrchr(filename,'/')))
X				filename_base = filename;
X			else
X				++filename_base;
X
X			fputs("#!/bin/sh\n",fpout);
X			fprintf(fpout,"# This is part %02d of %s\n",
X			    filenum,(sharname) ? sharname : "a multipart archive");
X
X			setTOUCH();
X
X			EndHeadPos = ftell(fpout);
X		}
X	}
X
X	fprintf(fpout,"# ============= %s ==============\n",RstrName);
X
X	gen_mkdir_script(RstrName);
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		fpsource = popen(line,"r");
X		fscanf(fpsource,"%d",&count);
X		pclose(fpsource);
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(fpout);
X
X		if(pid = fork())
X		{ /* parent, create a file to read */
X			if(pid < 0)
X			{
X				fprintf(stderr,"could not fork!\n");
X				exit(1);
X			}
X			close(pipex[1]);
X			fpsource = fdopen(pipex[0],"r");
X			filetype = (Compress ? "Compressed" : "Binary");
X		}
X		else
X		{ /* start writing the pipe with encodes */
X			FILE *outptr;
X
X			if(Compress)
X			{
X				sprintf(line, "compress -b%d < '%s'", CompressBits, file);
X				fpsource = popen(line, "r");
X			}
X			else
X				fpsource = fopen(file, "rb");
X			outptr = fdopen(pipex[1],"w");
X			fprintf(outptr,"begin 600 %s\n",
X				(Compress ? "_shar_cmp_.tmp" : RstrName));
X			encode(fpsource,outptr);
X			fprintf(outptr,"end\n");
X			if(Compress)
X				pclose(fpsource);
X			else
X			{
X				fclose(fpsource);
X			}
X			exit(0);
X		}
X	}
X	else
X	{
X		fpsource = fopen(file,"r");
X		filetype = "Text";
X	}
X
X	if(fpsource)
X	{
X		/* protect existing files */
X		if(eXists)
X		{
X			fprintf(fpout,"if test -f '%s' -a X\"$1\" != X\"-c\"; then\n",
X				RstrName);
X			if(InterOW)
X			{
X				fputs("\tcase $wish in\n",fpout);
X				fprintf(fpout,"\tA*|a*) echo 'x - overwriting %s';;\n",
X					RstrName);
X				fprintf(fpout,
X		"\t*) echo '? - overwrite %s -- [No], [Y]es, [A]ll, [Q]uit? '\n",
X					RstrName);
X				fputs("\t\tread wish;;\n",fpout);
X				fputs("\tesac\n",fpout);
X				fputs("\tcase $wish in\n",fpout);
X				fputs("\tQ*|q*) echo aborted; exit 86;;\n",fpout);
X				fputs("\tA*|a*|Y*|y*) x=Y;;\n",fpout);
X				fputs("\t*) x=N;;\n",fpout);
X				fputs("\tesac\n",fpout);
X				fputs("else\n",fpout);
X				fputs("\tx=Y\n",fpout);
X				fputs("fi\n",fpout);
X				fputs("if test $x != Y; then\n",fpout);
X				fprintf(fpout,"\techo 'x - skipping %s'\n",RstrName);
X			}
X			else
X				fprintf(fpout,"\techo 'x - skipping %s (File already exists)'\n",
X					RstrName);
X			if (Split)
X				fputs("\trm -f _shar_wnt_.tmp\n",fpout);
X			fputs("else\n",fpout);
X			if (Split)
X				fputs("> _shar_wnt_.tmp\n",fpout);	
X		}
X
X		fprintf(stderr,"shar: saving %s (%s)\n",file,filetype);
X		if(Verbose)
X		{ /* info on archive and unpack */
X			fprintf(fpout,"echo 'x - extracting %s (%s)'\n",
X			    RstrName,filetype);
X		}
X		if(Binary)
X		{ /* run sed through uudecode (via temp file if might get split) */
X			fprintf(fpout, "sed 's/^%c//' << '%s' %s &&\n",
X			   	PREFIX,Delim,
X				(AvoidPipes ? "> _shar_tmp_.tmp" : "| uudecode"));
X		}
X		else
X		{ /* just run it into the file */
X			fprintf(fpout,"sed 's/^%c//' << '%s' > '%s' &&\n",
X			    PREFIX,Delim,RstrName);
X		}
X		while(fgets(line,BUFSIZ,fpsource))
X		{ /* output a line and test the length */
X			if(OptPREFIX && isgraph(line[0]) && line[0] != PREFIX
X#ifdef STRNCMP_IS_FAST
X			   && strncmp(line,"exit 0",6))	/* See "unshar -e" */
X#else /* STRNCMP_IS_FAST */
X			   && (line[0] != 'e' || strncmp(line,"exit 0",6))) /* unshar -e */
X#endif /* STRNCMP_IS_FAST */
X				fputs(line,fpout);
X			else
X			{
X				fprintf(fpout,"%c%s",PREFIX,line);
X				--remaining;	/* count PREFIX (in case Split is in effect) */
X			}
X#ifdef MSDOS	/* This probably doesn't work but accounts for some old code */
X			if(Split && (remaining -= strlen(line) + 1) < 0)	/* 1 extra for CR */
X#else /* MSDOS */
X			if(Split && (remaining -= strlen(line)) < 0)
X#endif /* MSDOS */
X			{ /* change to another file */
X				DeBug("Newfile, remaining %ld, ",remaining);
X				DeBug("limit still %d\n",limit);
X
X				if(line[strlen(line) - 1] != '\n')
X					fputc('\n',fpout);
X
X				fprintf(fpout,"%s\n",Delim);
X
X				/* close the "&&" and report an error if any of the above failed */
X				fprintf(fpout,"true || echo 'restore of %s failed'\n",RstrName);
X
X				if (eXists)
X					fputs("fi\n",fpout);
X
X				if(Verbose)
X				{ /* output some reassurance */
X					fprintf(fpout, "echo 'End of %s part %d'\n",
X						(sharname) ? sharname : "",filenum);
X					fprintf(fpout, "echo 'File %s is continued in part %d'\n",
X					    RstrName,filenum + 1);
X				}
X				else
X					fprintf(fpout,
X					    "echo 'End of part %d, continue with part %d'\n",
X					    filenum,filenum + 1);
X				fprintf(fpout,"echo %d > _shar_seq_.tmp\n",filenum + 1);
X				fputs("exit 0\n",fpout);
X
X				if(filenum == 1)
X				{ /* rewrite the info lines on the firstheader */
X					fseek(fpout,TypePos,0);
X					fprintf(fpout,"%-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(fpout);
X
X				/* form the next filename */
X				sprintf(filename,"%s%02d",outname,++filenum);
X				fpout = fopen(filename,"w");
X
X				if(Archive_name)
X				{
X					fprintf(fpout,"Submitted-by: %s\n",submitter);
X					fprintf(fpout,"Archive-name: %s%s%02d\n\n",
X						sharname,(strchr(sharname,'/')) ? "" : "/part",
X						(filenum) ? filenum : 1);
X				}
X
X				if(Cut)
X					fputs(CutMessage,fpout);
X				if(!(filename_base = strrchr(filename,'/')))
X					filename_base = filename;
X				else
X					++filename_base;
X
X				fputs("#!/bin/sh\n",fpout);
X				fprintf(fpout,"# this is %s (part %d of %s)\n",
X					filename_base,
X				    filenum,
X				    (sharname) ? sharname : "a multipart archive");
X				fputs(
X		"# do not concatenate these parts, unpack them in order with /bin/sh\n",
X					fpout);
X				fprintf(fpout,"# file %s continued\n#\n",RstrName);
X
X				setTOUCH();
X				
X				fputs("if test ! -r _shar_seq_.tmp; then\n",fpout);
X				fputs("\techo 'Please unpack part 1 first!'\n",fpout);
X				fputs("\texit 1\nfi\n",fpout);
X				fputs("(read Scheck\n",fpout);
X				fprintf(fpout," if test \"$Scheck\" != %d; then\n",filenum);
X				fputs("\techo Please unpack part \"$Scheck\" next!\n",
X					fpout);
X				fputs("\texit 1\n",fpout);
X				fputs(" else\n\texit 0\n fi\n",fpout);
X				fputs(") < _shar_seq_.tmp || exit 1\n",fpout);
X
X				if(eXists)
X					if(Verbose)
X					{ /* keep everybody informed */
X						fputs("if test ! -f _shar_wnt_.tmp; then\n",fpout);
X						fprintf(fpout,"\techo 'x - still skipping %s'\n",
X								RstrName);
X						fputs("else\n",fpout);
X					}
X					else
X						fputs("if test -f _shar_wnt_.tmp; then\n",fpout);
X
X				if(Verbose)
X				{ /* keep everybody informed */
X					fprintf(stderr,"Starting file %s\n",filename);
X					fprintf(fpout,"echo 'x - continuing file %s'\n",RstrName);
X				}
X				fprintf(fpout,"sed 's/^%c//' << '%s' >> '%s' &&\n",
X				    PREFIX,Delim,
X					(Binary ? "_shar_tmp_.tmp" : RstrName));
X				remaining = limit * 1024L;
X				split = 1;
X			}
X		}
X
X		(void) fclose(fpsource);
X		while(wait((int *)0) >= 0)
X			;
X
X		if(line[strlen(line) - 1] != '\n')
X			fputc('\n',fpout);
X
X		fprintf(fpout,"%s\n",Delim);
X		if(split && Verbose)
X			fprintf(fpout,"echo 'File %s is complete' &&\n",RstrName);
X
X		/* if this file was uuencoded w/Split, decode it and drop the temp */
X		if(Binary && AvoidPipes)
X		{
X			if(Verbose)
X				fprintf(fpout,"echo 'uudecoding file %s' &&\n",RstrName);
X			fputs("uudecode < _shar_tmp_.tmp && rm -f _shar_tmp_.tmp &&\n",fpout);
X		}
X
X		/* if this file was compressed, uncompress it and drop the temp */
X		if(Compress)
X		{
X			if(Verbose)
X				fprintf(fpout,"echo 'uncompressing file %s' &&\n",RstrName);
X			fprintf(fpout,
X			    "compress -d < _shar_cmp_.tmp > '%s' && rm -f _shar_cmp_.tmp &&\n",
X				RstrName);
X		}
X
X		if(Touch)
X		{
X			/* set the dates as they were */
X			lt = localtime(&fst.st_mtime);
X			fprintf(fpout,"$TOUCH -am %02d%02d%02d%02d%02d '%s' &&\n",
X				lt->tm_mon + 1,
X				lt->tm_mday,
X				lt->tm_hour,
X				lt->tm_min,
X				lt->tm_year,
X				RstrName);
X		}
X
X		if(Vanilla)
X		{
X			/* close the "&&" and report an error if any of the above failed */
X			fprintf(fpout,"true || echo 'restore of %s failed'\n",RstrName);
X		}
X		else
X		{
X			/* set the permissions as they were */
X			fprintf(fpout,"chmod %04o %s ||\n",
X				fst.st_mode & 00777,RstrName);
X
X			/* report an error if any of the above failed */
X			fprintf(fpout,"echo 'restore of %s failed'\n",RstrName);
X
X			if(Wc_c)
X			{ /* validate the transferred file */
X				FILE *pfp;
X				char command[BUFSIZ];
X
X				sprintf(command,"%s '%s'",WC,file);
X				if((pfp = popen(command,"r")))
X				{
X					char wc[BUFSIZ];
X
X					fscanf(pfp,"%s",wc);
X					fprintf(fpout,"Wc_c=\"`%s '%s'`\"\n",WC,RstrName);
X					fprintf(fpout,"test %s -eq \"$Wc_c\" ||\n",wc);
X					fprintf(fpout,
X					    "\techo '%s: original size %s, current size' \"$Wc_c\"\n",
X						RstrName, wc);
X					pclose(pfp);
X				}
X			}
X		}
X
X		/* if the exists option is in place close the if */
X		if(eXists)
X		{
X			if (Split)
X				fputs("rm -f _shar_wnt_.tmp\n",fpout);
X
X			fputs("fi\n",fpout);
X		}
X
X		return(0);
X	}
X	else
X	{
X		fprintf(stderr,"shar: Can't open %s (%s): ",file,filetype);
X		perror("");
X		return(1);
X	}
X}
X
X
Xchar *helpinfo[] =
X{
X	"-V  produce \"vanilla\" shars demanding little of the unshar environment",
X	"-v  verbose messages OFF while executing",
X	"-m  restore file modification dates & times with \"touch\" commands",
X	"-w  don't check with 'wc -c' after unpack",
X	"-a  generate Submitted-by: & Archive-name: headers",
X	"-nXXX   use XXX as the name of the archive (documentation)",
X	"-s  override automatically determined submitter name",
X	"-x  overwrite existing files without checking if they already exist",
X	"-X  interactively overwrite existing files (NOT FOR NET SHARS)",
X	"-B  treat all files as binary, use uuencode",
X	"-T  treat all files as text (default)",
X	"-C  compress and uuencode all files",
X	"-bXX    pass -bXX (default 12) to compress when compressing (implies -C)",
X	"-p  allow positional parameter options. The options \"-B\" and \"-T\"",
X	"    and \"-C\" may be embedded, and files to the right of the",
X	"    option will be processed in the specified mode",
X	"-M  mixed mode. Determine if the files are text or",
X	"    binary and archive correctly.",
X	"-P  use temp files instead of pipes in the shar file",
X	"-F  force the prefix character on every line (even if not required)",
X	"-c  start the shar with a cut line",
X	"-f  restore by filename only, rather than path",
X	"-dXXX   use XXX to delimit the files in the shar",
X	"-oXXX   (or -o XXX) output to file XXX.01 thru XXX.nn",
X	"-lXX    limit output file size to XXk bytes (but don't split files)",
X	"-LXX    limit output file size to XXk bytes (may split files)",
X	"-S      read files to wrap from stdin, ignoring argument line",
X	"\nThe -S option reads filenames one per line from stdin; input",
X	"format must be similar to 'find' output, except that if -p",
X	"is specified, -B, -T or -C may be used (on lines by themselves)",
X	"e.g., find . -type f -print | sort | shar -C -l50 -o /tmp/big",
X	"\nThe 'o' option is required if the 'l' or 'L' option is used",
X	"The 'n' option is required if the 'a' option is used",
X	"\n-a generates sharname/part## headers. If the -a argument contains",
X	"a '/', then /part is not appended",
X	"The automatic submitter name is trivial: essentially `whoami`@`uname`",
X	(char *)0
X};
X
Xhelpuser()
X{				/* output a command format message */
X	register char **ptr;
X	fprintf(stderr,
X	    "shar %s\nusage: shar [ options ] file ...\n       shar -S [ options ]\n",
X		revision);
X	for(ptr = helpinfo; *ptr; ptr++)
X		fprintf(stderr,"%s\n",*ptr);
X
X	exit(1);
X}
X/* vi: set tabstop=4 shiftwidth=4: */
SHAR_EOF
chmod 0644 shar.c ||
echo 'restore of shar.c failed'
Wc_c="`wc -c < 'shar.c'`"
test 36835 -eq "$Wc_c" ||
	echo 'shar.c: original size 36835, current size' "$Wc_c"
fi
exit 0
-- 
  ==========================================================================
  | Richard H. Gumpertz    rhg@CPS.COM    (913) 642-1777 or (816) 891-3561 |
  | Computer Problem Solving, 8905 Mohawk Lane, Leawood, Kansas 66206-1749 |
  ==========================================================================