[net.sources] One more SHAR, one more time ...

roger@mips.UUCP (Roger March) (03/07/87)

[suck on this, line eater!

	  Though I love to clog up mail boxes and news spool directories,
	I wouldn't be reposting if it weren't for good reason, well maybe.
	Anyway, the following is a complete reposting on my previously
	posted SHAR. It now contains a "scandir()" emulation for System V,
	supplied by Bob Esposito (Hot damn!). The bug fixes and enhancements
	posted are also here. So this wad should be complete and up to date.
	Have fun with it, and thanks Bob.

							Rogue Monster


-------- cut -------- cut -------- cut -------- cut -------- cut --------
#!/bin/sh
# to extract, remove the header and type "sh filename"
if `test ! -s ./README`
then
echo "writting ./README"
cat > ./README << '\Rogue\Monster\'

	  Yet another SHAR ?!? Good lord, why in the world would anyone
	create still one more of these? I must confess that of all the
	many variants I have kicking around, there wasn't one that did
	the combination of creeping through directories and packing the
	data into mailable pieces. This SHAR supposedly does that plus
	the usual garbage.
	  Enough justification, here are the command line switches it
	accepts:

	-c		=	Creates code for checking file sizes.
	-f <name>	=	The only required switch, it defines
				the archive root. Archive files will
				be called "<name>.shr1" and so on.
	-i		=	Usually the files to be archived are
				specified in the command line. This
				causes the file names to be taken from
				the standard input.
	-m <number>	=	This redefines the target number of bytes
				per archive. The default is 60000.
	-o		=	Causes code to over writing existing files,
				by default this is not done.
	-p		=	The original permissions are given to the
				un-SHAR-ed files.
	-q		=	The un-SHAR-ing doesn't print out status.

	Oh boy, how about an example?

		shar -f junk *.c

	Will generate archive files like "junk.1", "junk.2" ... where
	each is about 60Kb in size. Or for macho folks:

		ls *.c | shar -f junk -m 1000000 -c -i

	creates "junk.*" files about 1Mb each, with checking and gets its
	file names from a pipe (oooh, aaah).
	  My original version was BSD 4.[23] specific in that it used the
	function "scandir()". Well thanks to the gracious efforts of
	Bob Esposito, a system V version of "scandir()" is now available
	(thunderous applause). The system V folks need only change the
	CFLAGS line to include "-DSYSV" and they too can be SHAR-ing to
	their hearts delight.
	  Bug reports and/or comments can be sent to the creature
	indicated below.

-Rogue Monster                                   (also known as Roger March)
UUCP: {decvax,ucbvax,ihnp4,hplabs}!decwrl!mips!roger
USPS: MIPS Computer Systems, 930 Arques, Sunnyvale, CA 94086, (408) 991-0220
\Rogue\Monster\
else
  echo "will not over write ./README"
fi
if `test ! -s ./Makefile`
then
echo "writting ./Makefile"
cat > ./Makefile << '\Rogue\Monster\'
#
#	Rogue Monster shar makefile
#
#	System V folks, use the following CFLAGS line:
#
#CFLAGS = -O -s -DSYSV
#
#	BSD folks, use the following CFLAGS line:
#
CFLAGS = -O -s

SOURCES = getarg.c shar.c scandir.c
OBJECTS = getarg.o shar.o scandir.o

shar :	$(OBJECTS)
	cc $(CFLAGS) $(OBJECTS) -o shar

getarg.o :	getarg.c
shar.o :	shar.c
scandir.o :	scandir.c
\Rogue\Monster\
else
  echo "will not over write ./Makefile"
fi
if `test ! -s ./scandir.c`
then
echo "writting ./scandir.c"
cat > ./scandir.c << '\Rogue\Monster\'
/*
 *	scandir - a SysV library routine that simulates the
 *		  BSD 4.3 version.
 *
 *	returns:
 *		-1 for failures
 *		# of files found if successful
 *
 *	Produces a list of files which are contained in the
 *	directory "dirname".
 *
 *	The list is returned through "namelist" which is an
 *	array of pointers to (struct direct).  This list of
 *	pointers are malloc'ed and should be freed when
 *	done with.
 *
 *	The passed function "select" is passed a (struct direct)
 *	pointer for each directory entry.  If "select" returns
 *	non-zero the entry is included in "namelist", otherwise
 *	it is not.  If "select" is NULL, all entries are returned.
 *
 *	The function "compar" is passed a pair of pointers to
 *	(struct direct) and is used to sort "namelist" via qsort.
 *	If "compar" is NULL, the names are unsorted.
 *
 *	This source is public domain and the author claims no
 *	rights to it.  It may be copied, spindled, or mutilated.
 *
 *	R.J. Esposito - Bell of Pennsylvania
 *
 */

#include <stdio.h>
#ifdef	SYSV
#include <sys/types.h>
#include <ndir.h>

int
scandir(dirname, namelist, select, compar)
char		*dirname;
struct direct	*(*namelist[]);
int		(*select)();
int		(*compar)();
{
	DIR		*dfp;
	struct direct	*dp;
	register int	ii, nf;
	char		*malloc();

	if ((dfp = opendir(dirname)) == NULL)	/* can't open directory */
		return(-1);

	nf = 0;
	while ((dp = readdir(dfp)) != NULL)	/* read thru direcetory */
		if (select == NULL || (*select)(dp))
			nf++;

	if (!nf)				/* nothing found */
		return(0);

	/* malloc memory for the namelist array */

	*namelist = (struct direct **)malloc((nf+1)*sizeof(struct direct *));
	if (*namelist == NULL) {
		fprintf(stderr, "scandir: out of memory\n");
		return(-1);
	}

	for (ii = 0; ii < nf; ii++) {
		(*namelist)[ii] = (struct direct *)malloc(sizeof(struct direct));
		if ((*namelist)[ii] == NULL) {
			fprintf(stderr, "scandir: out of memory\n");
			return(-1);
		}
	}

	/* now re-read the directory loading up the namelist array */

	(*namelist)[ii] = 0;
	seekdir(dfp, 0L);
	ii = 0;

	while ((dp = readdir(dfp)) != NULL) {
		if (select == NULL || (*select)(dp)) {
			(*namelist)[ii]->d_ino = dp->d_ino;
			(*namelist)[ii]->d_reclen = dp->d_reclen;
			(*namelist)[ii]->d_namlen = dp->d_namlen;
			strcpy((*namelist)[ii]->d_name, dp->d_name);
			ii++;
		}
	}

	closedir(dfp);
	if (compar != NULL)		/* sort the list if required */
		qsort((char **)*namelist, nf, sizeof(struct direct *), compar);

	return(nf);
}
#else
static dummy(){}
#endif	SYSV
\Rogue\Monster\
else
  echo "will not over write ./scandir.c"
fi
if `test ! -s ./getarg.c`
then
echo "writting ./getarg.c"
cat > ./getarg.c << '\Rogue\Monster\'
/*
 *	$Header: getarg.c,v 1.4 86/10/04 12:31:14 roger Exp $
 */
/********************************************************
 *							*
 *			getarg.c			*
 *							*
 ********************************************************/
#include <stdio.h>
/*
 *	External variables.
 */
extern char *index();
/*
 *	Accumulation variables.
 */
char *optarg;		/* Global argument pointer. */
int optind = 0;		/* Global argv index. */
/*
 *	Getarg:
 *
 *	  Parses the command line sequentially. Arguments starting with
 *	a '-' are defined to be switches. The following character is the
 *	switch value. If it is present in the option string, its value
 *	is returned. If the option character is followed by the ':'
 *	character, the most meaningful argument string is returned in
 *	the argument 'optarg' and its index is returned as 'optind'. By
 *	the way, 'optind' is the working index and can be carefully
 *	fiddled with. A '\0' value is returned for unswitched arguments
 *	and 'optarg'/'optind' refer to the argument. A '?' is returned
 *	for switches that are unspecified. An EOF is returned if the
 *	command line is empty.
 */
int getarg(argc,argv,optstring)
register int argc;
register char *argv[];
register char *optstring;
{
  /*
   *	Locals.
   */
  register int i;
  register char *swc;
  /*
   *	Set up the global variables. Restrict the range of 'optind'
   *	and terminate if it is upside out of bounds.
   */
  optarg = NULL;
  if (++optind < 1)
    optind  = 1;
  else if (optind >= argc)
    return (EOF);
  /*
   *	See a switch is present. If not, then return a switchless
   *	argument.
   */
  if (*argv[optind] != '-'){
    optarg = argv[optind];
    return ('\0');
  }
  /*
   *	See if the switch is in the option string.
   */
  if (!(swc = index(optstring,*(argv[optind] + 1))))
    return ('?');
  /*
   *	See if an argument needs to be extracted.
   */
  if (swc[1] != ':')
    return (swc[0]);
  /*
   *	Extract the argument.
   */
  if (*(argv[optind] + 2))
    optarg = argv[optind] + 2;
  else if (++optind < argc)
    optarg = argv[optind];
  return (swc[0]);
}
\Rogue\Monster\
else
  echo "will not over write ./getarg.c"
fi
if `test ! -s ./shar.c`
then
echo "writting ./shar.c"
cat > ./shar.c << '\Rogue\Monster\'
/*
 *	$Header$
 */
/********************************************************
 *							*
 *			shar.c				*
 *							*
 ********************************************************/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/dir.h>
/*
 *	External functions.
 */
extern char *malloc();
extern int strlen();
extern char *strcpy();
extern int strcmp();
extern int getarg();
extern stat();
extern char strcat();
extern long atol();
/*
 *	External variables.
 */
extern int optind;
extern char *optarg;
/*
 *	File tree struture:
 *
 *	  Defines files or directories, and holds the processing
 *	state flags.
 */
typedef struct Leaf {
  struct Leaf *Parent,*Sybling,*Child;	/* tree linkage */
  char *Name;				/* leaf name */
  char Flags;				/* state flags */
  u_short Mode;				/* original permissions */
  long Size;				/* file size */
} Leaf;
#define	F_FILE		01		/* file type */
#define	F_DIR		02		/* directory type */
#define	F_WALK		04		/* traversed */
#define	F_USED		010		/* written */
#define	F_BAD		020		/* unarchivable object */
/*
 *	Out file linkage.
 */
typedef struct Ref {
  struct Ref *Next;			/* pointer to next leaf reference */
  struct Leaf *Leaf;			/* associated leaf */
} Ref;
typedef struct Out {
  struct Out *Next;			/* pointer to next out file */
  short Number;				/* file number */
  long Size;				/* approximate file size */
  struct Ref *Ref;			/* list of contained leaves */
} Out;
/*
 *	Static variables.
 */
static char *RootName = NULL;		/* output file name prefix */
static long Size = 60 * 1024;		/* target file size */
static char Quiet = 0;			/* generate quiet code */
static char Check = 0;			/* check file sizes */
static char Perm = 0;			/* use original permissions */
static char OverWrite = 0;		/* overwrite existing files */
static char InStream = 0;		/* take names from standard input */
static Leaf Dot = {			/* relative tree */
  NULL,NULL,NULL,".",F_DIR};
static Leaf Abs = {			/* absolute tree */
  NULL,NULL,NULL,"",F_DIR};
static char CharBuf[128];		/* useful string buffer */
static char *SourceName = NULL;		/* original file name string */
static char PathName[1024];		/* pathname return string */
static int PathSp = 0;			/* path stack pointer */
static char *PathStack[64];		/* path name stack */
static int FileCount = 0;		/* output file number */
static Out *FileList = NULL;		/* list of output files */
static Out *CurFile = NULL;		/* current file */
static char *Terminal =			/* file terminator */
  "\\Rogue\\Monster\\";
static char Buffer[4096];		/* file data buffer */
/*
 *	Make path name:
 *
 *	  The passed tree leaf is converted to a path name string.
 *	The result is left in the global variable "PathName".
 */
static MakePathName(tre)
register Leaf *tre;
{
  /*
   *	Climb the tree, pushing file names on to the stack.
   */
  for (PathSp = 0; tre; tre = tre->Parent)
    PathStack[PathSp++] = tre->Name;
  /*
   *	Unwind the stack and form the path name.
   */
  strcpy(PathName,PathStack[--PathSp]);
  while (PathSp--){
    strcat(PathName,"/");
    strcat(PathName,PathStack[PathSp]);
  }
}
/*
 *	Parse file name:
 *
 *	  This creates an entry in the file tree from the passed leaf.
 *	A "." has special meaning in that there is no movement down
 *	the tree. An attempt to move through a name which is not a
 *	directory also causes an error. A ".." is legal if a move outside
 *	the tree top is not attempted. A pointer to the terminal leaf
 *	is returned.
 */
static Leaf *ParseFileName(tre,nam)
register Leaf *tre;
register char *nam;
{
  /*
   *	Locals.
   */
  register int i;
  /*
   *	Extract current file name.
   */
  for (i = 0; *nam && *nam != '/';)
    CharBuf[i++] = *nam++;
  CharBuf[i] = '\0';
  /*
   *	Handle the "." and ".." cases.
   */
  if (!strcmp(CharBuf,".")){
    if (*nam)
      return (ParseFileName(tre,++nam));
    return (tre);
  } else if (!strcmp(CharBuf,"..")){
    if (!tre->Parent){
      fprintf(stderr,"untraversable path name: \"%s\"\n",SourceName);
      return (NULL);
    } else if (*nam)
      return (ParseFileName(tre->Parent,++nam));
    else
      return (tre->Parent);
  } if (!(tre->Flags & F_DIR)){
    /*
     *	The current tree leaf is not a directory, give an error.
     */
    fprintf(stderr,"\"%s\" in \"%s\" is not a directory\n",CharBuf,
      SourceName);
    return (NULL);
  } else {
    register Leaf *wlk,*owk;
    /*
     *	See if the name is already in the tree.
     */
    for (owk = NULL, wlk = tre->Child; wlk; wlk = (owk = wlk)->Sybling)
      if (!strcmp(CharBuf,wlk->Name)){
        if (owk){
          owk->Sybling = wlk->Sybling;
          wlk->Sybling = tre->Child;
          tre->Child = wlk;
        }
        break;
      }
    /*
     *	If the name doesn't already exist in the tree, create it.
     */
    if (!wlk){
      struct stat fs;
      wlk = (Leaf *) malloc(sizeof(Leaf));
      wlk->Parent = tre;
      wlk->Sybling = tre->Child;
      tre->Child = wlk;
      wlk->Child = NULL;
      wlk->Name = strcpy(malloc(strlen(CharBuf) + 1),CharBuf);
      /*
       *	Form the path name and get the file stats.
       */
      MakePathName(wlk);
      stat(PathName,&fs);
      /*
       *	Only directories and files are acceptable; too late
       *	to fix things, but give an error and mark bad.
       */
      wlk->Flags = 0;
      if (fs.st_mode & S_IFDIR)
        wlk->Flags |= F_DIR;
      else if (fs.st_mode & S_IFREG){
        wlk->Flags |= F_FILE;
        wlk->Size = fs.st_size;
      } else {
        fprintf(stderr,"\"%s\" is not a file or directory\n",SourceName);
        wlk->Flags |= F_BAD;
      }
      /*
       *	Save the original permissions.
       */
      wlk->Mode = fs.st_mode;
    }
    /*
     *	If required, continue to parse the name.
     */
    if (*nam)
      return (ParseFileName(wlk,++nam));
    else
      return (wlk);
  }
}
/*
 *	Expand filter:
 *
 *	  This removes the "." and ".." objects from the directory scans.
 */
static int ExpandFilter(dir)
register struct direct *dir;
{
  /*
   *	Do gross string comparisons.
   */
  return (strcmp(dir->d_name,".") && strcmp(dir->d_name,".."));
}
/*
 *	Expand tree:
 *
 *	  The passed leaf must be a directory. This function recursively
 *	expands the directory until its bottoms out.
 */
static ExpandTree(tre)
Leaf *tre;
{
  /*
   *	Locals.
   */
  register int i,Count;
  struct direct **NameList;
  /*
   *	Get the names of all objects in the directory.
   */
  MakePathName(tre);
  SourceName = PathName;
  if ((Count = scandir(PathName,&NameList,ExpandFilter,NULL)) < 0){
    fprintf(stderr,"Cannot access \"%s\"\n",SourceName);
    tre->Flags = (tre->Flags & ~F_DIR) | F_BAD;
    return;
  } else if (!Count)
    return;
  /*
   *	See if a new entry needs to be defined. If so create it.
   *	If the object is a directory, expand its tree too.
   */
  for (i = 0; i < Count; ++i){
    register Leaf *obj;
    struct stat fs;
    for (obj = tre->Child; obj; obj = obj->Sybling)
      if (!strcmp(obj->Name,NameList[i]->d_name))
        break;
    if (!obj){
      /*
       *	Create the new entry.
       */
      obj = (Leaf *) malloc(sizeof(Leaf));
      obj->Parent = tre;
      obj->Sybling = tre->Child;
      tre->Child = obj;
      obj->Child = NULL;
      obj->Name = strcpy(malloc(strlen(NameList[i]->d_name) + 1),
        NameList[i]->d_name);
      /*
       *	Get the file status of the object.
       */
      MakePathName(obj);
      stat(PathName,&fs);
      /*
       *	Determine the file type.
       */
      obj->Flags = 0;
      obj->Mode = fs.st_mode;
      if (fs.st_mode & S_IFDIR)
        obj->Flags |= F_DIR;
      else if (fs.st_mode & S_IFREG){
        obj->Flags |= F_FILE;
        obj->Size = fs.st_size;
      } else {
        fprintf(stderr,"\"%s\" is not a file or directory\n",PathName);
        obj->Flags |= F_BAD;
      }
    }
    /*
     *	See if further expansion is required.
     */
    free(NameList[i]);
    if (obj->Flags & F_DIR)
      ExpandTree(obj); 
  }
  free(NameList);
}
/*
 *	Make out files:
 *
 *	  This runs recursively moves through the passed tree and
 *	adds file leaves to output files.
 */
static MakeOutFiles(tre)
register Leaf *tre;
{
  /*
   *	Locals.
   */
  register Leaf *wlk;
  register Ref *ref;
  /*
   *	Move through the children of the passed tree. Directories
   *	cause immediate descent and bad entries are ignored.
   */
  for (wlk = tre->Child; wlk; wlk = wlk->Sybling)
    if (wlk->Flags & F_DIR)
      MakeOutFiles(wlk);
    else if (wlk->Flags & F_FILE){
      /*
       *	See if the leaf is too big for the current file, flush.
       */
      if (CurFile && (CurFile->Size + wlk->Size) > Size){
      	CurFile->Number = ++FileCount;
      	CurFile->Next = FileList;
      	FileList = CurFile;
      	CurFile = NULL;
      }
      /*
       *	Make sure that a current file exists.
       */
      if (!CurFile){
        CurFile = (Out *) malloc(sizeof(Out));
        CurFile->Size = 0;
        CurFile->Ref = NULL;
      }
      /*
       *	Add the leaf to the current file.
       */
      ref = (Ref *) malloc(sizeof(Ref));
      ref->Next = CurFile->Ref;
      CurFile->Ref = ref;
      ref->Leaf = wlk;
      CurFile->Size += wlk->Size;
    }
}
/*
 *	Clear tree:
 *
 *	  The WALK bits in the passed tree are cleared.
 */
static ClearTree(tre)
register Leaf *tre;
{
  /*
   *	Locals.
   */
  register Leaf *wlk;
  /*
   *	Clear the current tree. Move through the children, only directories
   *	need to be cleared.
   */
  tre->Flags &= ~F_WALK;
  for (wlk = tre->Child; wlk; wlk = wlk->Sybling)
    if (wlk->Flags & F_DIR)
      ClearTree(wlk);
}
/*
 *	Check tree:
 *
 *	  This makes sure that the directory structure is in place
 *	within the current archive file.
 */
static CheckTree(stm,tre)
FILE *stm;
register Leaf *tre;
{
  /*
   *	Make sure this directory exists, and if so, is unmarked.
   */
  if (!tre || !tre->Parent || (tre->Flags & F_WALK))
    return;
  /*
   *	Handle its parent then do it.
   */
  CheckTree(stm,tre->Parent);
  MakePathName(tre);
  fprintf(stm,"if `test ! -d %s`\nthen\n  mkdir %s\n",PathName,PathName);
  if (!Quiet)
    fprintf(stm,"  echo \"mkdir %s\"\n",PathName);
  fprintf(stm,"fi\n");
  tre->Flags |= F_WALK;
}
/*
 *	Output ref:
 *
 *	  The passed reference is written to the stream, using the
 *	user's rules.
 */
static OutputRef(stm,ref)
FILE *stm;
register Ref *ref;
{
  /*
   *	Locals.
   */
  register int count;
  FILE *inp;
  /*
   *	Make sure that the tree to support this file already defined.
   */
  CheckTree(stm,ref->Leaf->Parent);
  /*
   *	Put out the existance check header if required.
   */
  MakePathName(ref->Leaf);
  if (!OverWrite)
    fprintf(stm,"if `test ! -s %s`\nthen\n",PathName);
  /*
   *	Open the input file. Echo file name written.
   */
  if (!(inp = fopen(PathName,"r"))){
    fprintf(stderr,"Unable to read \"%s\"\n",PathName);
    fprintf(stm,"  echo \"read error when SHAR-ed: %s\"\nfi\n",PathName);
    return;
  }
  if (!Quiet)
    fprintf(stm,"echo \"writing %s\"\n",PathName);
  fprintf(stm,"cat > %s << '%s'\n",PathName,Terminal);
  /*
   *	Move the file data into the archive.
   */
  while (count = fread(Buffer,sizeof(char),sizeof(Buffer),inp))
    fwrite(Buffer,sizeof(char),count,stm);
  /*
   *	Take care of the tail end linkage.
   */
  fprintf(stm,"%s\n",Terminal);
  if (!OverWrite && !Quiet)
    fprintf(stm,"else\n  echo \"will not over write %s\"\nfi\n",PathName);
  else if (!OverWrite)
    fprintf(stm,"fi\n");
  /*
   *	Handle the permission stuff.
   */
  if (Perm)
    fprintf(stm,"chmod %o %s\n",ref->Leaf->Mode & 07777,PathName);
  /*
   *	Add the checking code.
   */
  if (Check){
    fprintf(stm,"if [ `wc -c %s | awk '{printf $1}'` -ne %d ]\nthen\n",
      PathName,ref->Leaf->Size);
    fprintf(stm,
      "echo `wc -c %s | awk '{print \"Got \" $1 \", Expected \" %d}'`\nfi\n",
      PathName,ref->Leaf->Size);
  }
  /*
   *	Close the input file.
   */
  fclose(inp);
}
/*
 *	Pack archive:
 *
 *	  This turns the passed file specification into a packed archive.
 */
static PackArchive(fil)
Out *fil;
{
  /*
   *	Locals.
   */
  register Ref *ref;
  FILE *stm;
  /*
   *	Open the output file, failure here causes a return.
   */
  sprintf(CharBuf,"%s.shr%d",RootName,fil->Number);
  if (!(stm = fopen(CharBuf,"w"))){
    fprintf(stderr,"Unable to open archive: %s\n",CharBuf);
    return;
  } else
    fprintf(stderr,"\tForming archive: %s\n",CharBuf);
  /*
   *	Put out the header.
   */
  fprintf(stm,"#!/bin/sh\n");
  fprintf(stm,"# to extract, remove the header and type \"sh filename\"\n");
  /*
   *	Move through all file references and put them out.
   */
  ClearTree(&Abs);
  ClearTree(&Dot);
  for (ref = fil->Ref; ref; ref = ref->Next)
    OutputRef(stm,ref);
  /*
   *	Close the output file before leaving.
   */
  if (!Quiet)
    fprintf(stm,"echo \"Finished archive %d of %d\"\n",fil->Number,FileCount);
  fprintf(stm,"exit\n");
  fclose(stm);
}
/*
 *	Main body:
 *
 *	  This parses the command line and executes its directives.
 */
main(argc,argv)
int argc;
char *argv[];
{
  /*
   *	Locals.
   */
  register int c;
  Leaf *tre;
  /*
   *	Command line switches:
   *
   *	-c			=	add checking code
   *	-f <name>		=	output file name root
   *	-i			=	take names from standard input
   *	-m <number>		=	sets the target segment size
   *	-o			=	overwrite existing files
   *	-p			=	use original permissions
   *	-q			=	generate quite code
   */
  while ((c = getarg(argc,argv,"cf:im:opq")) != EOF)
    switch (c){
      case 'c':
        Check = 1;
        break;
      case 'f':
        RootName = optarg;
        break;
      case 'i':
        InStream = 1;
        break;
      case 'm':
        Size = atol(optarg) * 19;	/* reserve 5% for overhead */
        Size /= 20;
        break;
      case 'o':
        OverWrite = 1;
        break;
      case 'p':
        Perm = 1;
        break;
      case 'q':
        Quiet = 1;
        break;
      case '?':
        fprintf(stderr,"Unknown switch: -%c\n",c);
        break;
      default:
        /*
         *	File names can be absolute or relative, decide which
         *	tree the filename goes in.
         */
        SourceName = optarg;
        if (*optarg == '/')
          tre = ParseFileName(&Abs,++optarg);
        else
          tre = ParseFileName(&Dot,optarg);
        /*
         *	If the name results in a directory, expand the directory.
         */
        if (tre && (tre->Flags & F_DIR))
          ExpandTree(tre);
    }
  /*
   *	Do tests, additional tree building if required.
   */
  if (!RootName){
    fprintf(stderr,"A \"-f <filename>\" must be specified\n");
    exit(1);
  } else if (InStream){
    char buf[1024];
    while (scanf("%s",buf) == 1){	/* read names from stdin */
      SourceName = buf;
      if (buf[0] == '/')
        tre = ParseFileName(&Abs,&buf[1]);
      else
        tre = ParseFileName(&Dot,buf);
      /*
       *	If a directory, expand it.
       */
      if (tre && (tre->Flags & F_DIR))
        ExpandTree(tre);
    }
  }
  /*
   *	Construct the list of output files.
   */
  MakeOutFiles(&Abs);
  MakeOutFiles(&Dot);
  if (CurFile){
    CurFile->Number = ++FileCount;
    CurFile->Next = FileList;
    FileList = CurFile;
  }
  fprintf(stderr,"***  Archive contains %d files  ***\n",FileCount);
  /*
   *	Move through the file list and pack up the data.
   */
  for (CurFile = FileList; CurFile; CurFile = CurFile->Next)
    PackArchive(CurFile);
}
\Rogue\Monster\
else
  echo "will not over write ./shar.c"
fi
echo "Finished archive 1 of 1"
exit

-- 
-Rogue Monster                                   (also known as Roger March)
UUCP: {decvax,ucbvax,ihnp4,hplabs}!decwrl!mips!roger
USPS: MIPS Computer Systems, 930 Arques, Sunnyvale, CA 94086, (408) 991-0220