[alt.sources] Rich $alz's cshar package, new alpha release, Part04/05

rsalz@bbn.com (Rich Salz) (02/21/89)

This is what I use to create shell archives of small-to-humongous source
distributions.  Hope you find it useful.  Hope you find bugs, and send
them to me.  This will appear in comp.sources.unix after the bug reports
die down.  It runs on all sort of Unix machines, and a raft of other OS's,
too.

For more details, see the README file in the first shar.

Please don't pass this version around, wait for the "REAL" one.
	/rich $alz

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 4 (of 5)."
# Contents:  makekit.c maniscan.c shar.c unshar.c
# Wrapped by rsalz@fig.bbn.com on Mon Feb 20 18:15:51 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'makekit.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'makekit.c'\"
else
echo shar: Extracting \"'makekit.c'\" \(11847 characters\)
sed "s/^X//" >'makekit.c' <<'END_OF_FILE'
X/*
X**  MAKEKIT
X**
X**  Split up source files into reasonably-sized shar lists.
X*/
X#include "shar.h"
X#ifdef	RCSID
Xstatic char RCS[] =
X	"$Header: makekit.c,v 2.2 88/06/06 22:04:45 rsalz Exp $";
X#endif	/* RCSID */
X
X
X/*
X**  Our block of information about the files we're doing.
X*/
Xtypedef struct _block {
X    char	*Name;			/* Filename			*/
X    char	*Text;			/* What it is			*/
X    int		 Where;			/* Where it is			*/
X    int		 Type;			/* Directory or file?		*/
X    long	 Bsize;			/* Size in bytes		*/
X} BLOCK;
X
X
X/*
X**  Our block of information about the archives we're making.
X*/
Xtypedef struct _archive {
X    int		 Count;			/* Number of files		*/
X    long	 Asize;			/* Bytes used by archive	*/
X} ARCHIVE;
X
X
X/*
X**  Global variables.
X*/
Xstatic char	*InName;		/* File with list to pack	*/
Xstatic char	*OutName;		/* Where our output goes	*/
Xstatic char	*SharName = "Part";	/* Prefix for name of each shar	*/
Xstatic char	*Trailer;		/* Text for shar to pack in	*/
Xstatic char	 TEMP[TEMPSIZE];	/* Temporary manifest file	*/
X#ifdef	USE_TEMP_MANIFEST
Xstatic char	 FLST[TEMPSIZE];	/* File with list for shar	*/
X#endif	/* USE_TEMP_MANIFEST */
Xstatic int	 ArkCount = 20;		/* Max number of archives	*/
Xstatic int	 ExcludeIt;		/* Leave out the output file?	*/
Xstatic int	 Header;		/* Lines of prolog in input	*/
Xstatic int	 Preserve;		/* Preserve order for Manifest?	*/
Xstatic int	 Working = TRUE;	/* Call shar when done?		*/
Xstatic long	 Size = 55000;		/* Largest legal archive size	*/
X
X
X/*
X**  Sorting predicate to put README first, then MANIFEST, then directories,
X**  then larger files, then smaller files, which is how we want to pack
X**  stuff in archives.
X*/
Xstatic int
XSizeP(t1, t2)
X    BLOCK	*t1;
X    BLOCK	*t2;
X{
X    long	 i;
X
X    if (t1->Type == F_DIR)
X	return t2->Type == F_DIR ? 0 : -1;
X    if (t2->Type == F_DIR)
X	return 1;
X    if (EQ(t1->Name, "PACKNOTES"))
X	return -1;
X    if (EQ(t2->Name, "PACKNOTES"))
X	return 1;
X    if (EQn(t1->Name, "README", 6))
X	return -1;
X    if (EQn(t2->Name, "README", 6))
X	return 1;
X    if (OutName && EQ(t1->Name, OutName))
X	return -1;
X    if (OutName && EQ(t2->Name, OutName))
X	return 1;
X    return (i = t1->Bsize - t2->Bsize) == 0L ? 0 : (i < 0L ? 1 : -1);
X}
X
X
X/*
X**  Sorting predicate to get things in alphabetical order, with the
X**  README and MANIFEST files as first and second, respectively.
X*/
Xstatic int
XReadmeP(t1, t2)
X    BLOCK	*t1;
X    BLOCK	*t2;
X{
X    int		 i;
X
X    if (EQ(t1->Name, "PACKNOTES"))
X	return -1;
X    if (EQ(t2->Name, "PACKNOTES"))
X	return 1;
X    if (EQ(t1->Name, "README"))
X	return -1;
X    if (EQ(t2->Name, "README"))
X	return 1;
X    if (EQ(t1->Name, "MANIFEST"))
X	return -1;
X    if (EQ(t2->Name, "MANIFEST"))
X	return 1;
X    return (i = *t1->Name - *t2->Name) ? i : strcmp(t1->Name, t2->Name);
X}
X
X
X/*
X**  Skip whitespace.
X*/
Xstatic char *
XSkip(p)
X    REGISTER char	*p;
X{
X    while (*p && WHITE(*p))
X	p++;
X    return p;
X}
X
X
X/*
X**  Signal handler.  Clean up and die.
X*/
Xstatic sigret_t
XCatch(s)
X    int		 s;
X{
X    int		 e;
X
X    e = errno;
X    if (TEMP[0])
X	(void)unlink(TEMP);
X#ifdef	USE_TEMP_MANIFEST
X    if (FLST[0])
X	(void)unlink(FLST);
X#endif	/* USE_TEMP_MANIFEST */
X    Fprintf(stderr, "Got signal %d, %s.\n", s, Ermsg(e));
X    exit(1);
X    /* NOTREACHED */
X}
X
X
Xmain(ac, av)
X    REGISTER int	 ac;
X    char		*av[];
X{
X    REGISTER FILE	*F;
X    REGISTER FILE	*In;
X    REGISTER BLOCK	*t;
X    REGISTER ARCHIVE	*k;
X    REGISTER char	*p;
X    REGISTER int	 i;
X    REGISTER int	 lines;
X    REGISTER int	 FoundOutname;
X    BLOCK		*Table;
X    BLOCK		*TabEnd;
X    ARCHIVE		*Ark;
X    ARCHIVE		*ArkEnd;
X    char		 buff[BUFSIZ];
X    long		 lsize;
X    int			 LastOne;
X    int			 Start;
X    int			 Notkits;
X    int			 Believer;
X    int			 Oops;
X    char		 EndArkNum[20];
X    char		 CurArkNum[20];
X
X    /* Collect input. */
X    Believer = FALSE;
X    Notkits = FALSE;
X    for (Oops = FALSE; (i = getopt(ac, av, "1beh:i:k:n:mo:ps:t:x")) != EOF; )
X	switch (i) {
X	default:
X	    Oops = TRUE;
X	    break;
X	case '1':
X	    Notkits = TRUE;
X	    break;
X	case 'b':
X	    Believer = TRUE;
X	    break;
X	case 'e':
X	    ExcludeIt = TRUE;
X	    break;
X	case 'h':
X	    Header = atoi(optarg);
X	    break;
X	case 'i':
X	    InName = optarg;
X	    break;
X	case 'k':
X	    ArkCount = atoi(optarg);
X	    break;
X	case 'm':
X	    InName = OutName = "MANIFEST";
X	    Header = 2;
X	    break;
X	case 'n':
X	    SharName = optarg;
X	    break;
X	case 'o':
X	    OutName = optarg;
X	    break;
X	case 'p':
X	    Preserve = TRUE;
X	    break;
X	case 's':
X	    Size = (long)atoi(optarg);
X	    if (IDX(optarg, 'k') || IDX(optarg, 'K'))
X		Size *= 1024;
X	    break;
X	case 't':
X	    Trailer = optarg;
X	    break;
X	case 'x':
X	    Working = FALSE;
X	    break;
X	}
X    ac -= optind;
X    av += optind;
X
X    if (Oops == FALSE && InName && av[0]) {
X	Fprintf(stderr,
X		"Can't list files on command line and use -i option.\n");
X	Oops = TRUE;
X    }
X
X    if (Oops) {
X	Fprintf(stderr, "Usage:\n  makekit %s\n          %s [files...]\n",
X		"[-1] [-b] [-e] [-x] [-k #] [-s #[k]] [-n Name] [-t Text]",
X		"[-p] [-m | -i MANIFEST -o MANIFEST -h 2]");
X	exit(1);
X	/* NOTREACHED */
X    }
X
X    /* Write the file list to a temp file. */
X    MakeTempName(TEMP, TEMP_NAME1);
X    F = fopen(TEMP, "w");
X    SetSigs(Catch);
X    if (av[0])
X	/* Got the arguments on the command line. */
X	while (*av)
X	    Fprintf(F, "%s\n", *av++);
X    else {
X	/* Got the name of the file from the command line. */
X	if (InName == NULL)
X	    In = stdin;
X	else if ((In = fopen(InName, "r")) == NULL) {
X	    Fprintf(stderr, "Can't read %s as manifest, %s.\n",
X		    InName, Ermsg(errno));
X	    exit(1);
X	    /* NOTREACHED */
X	}
X	/* Skip any possible prolog, then output rest of file. */
X	while (--Header >= 0 && fgets(buff, sizeof buff, In))
X	    ;
X	if (feof(In)) {
X	    Fprintf(stderr, "Nothing but header lines in list!?\n");
X	    exit(1);
X	    /* NOTREACHED */
X	}
X	while (fgets(buff, sizeof buff, In))
X	    fputs(buff, F);
X	if (In != stdin)
X	    (void)fclose(In);
X    }
X    (void)fclose(F);
X
X    /* Count number of files, allow for NULL and our output file. */
X    F = fopen(TEMP, "r");
X    for (lines = 2; fgets(buff, sizeof buff, F); lines++)
X	;
X    rewind(F);
X
X    /* Read lines and parse lines, see if we found our OutFile. */
X    FoundOutname = FALSE;
X    Table = NEW(BLOCK, lines);		/* Initialized in loop, below. */
X    for (t = Table, lines = 0; fgets(buff, sizeof buff, F); ) {
X	/* Read line, skip first word, check for blank line. */
X	if (p = IDX(buff, '\n'))
X	    *p = '\0';
X	else
X	    Fprintf(stderr, "Warning, line too long:\n\t%s\n", buff);
X	p = Skip(buff);
X	if (*p == '\0')
X	    continue;
X
X	/* Copy the line, snip off the first word. */
X	for (p = t->Name = COPY(p); *p && !WHITE(*p); p++)
X	    ;
X	if (*p)
X	    *p++ = '\0';
X
X	/* Skip <spaces><digits><spaces>; remainder is the file description. */
X	p = Skip(p);
X	t->Where = atoi(p);
X	while (*p && CTYPE(*p) && isdigit(*p))
X	    p++;
X	t->Text = Skip(p);
X
X	/* Get file type. */
X	if (!GetStat(t->Name)) {
X	    Fprintf(stderr, "Can't stat %s (%s), skipping.\n",
X		    t->Name, Ermsg(errno));
X	    continue;
X	}
X	t->Type = Ftype(t->Name);
X
X	/* Guesstimate its size when archived:  prolog, plus one char/line. */
X	t->Bsize = strlen(t->Name) * 4 + 200;
X	if (t->Type == F_FILE) {
X	    lsize = Fsize(t->Name);
X	    /* If we had a "wc" we could use it, but this is good enough. */
X	    if (IsProbablySource(t->Name))
X		/* Average chars/line in C, Pascal, Fortran.  Sort of. */
X		t->Bsize += lsize + lsize / 25;
X	    else
X		t->Bsize += lsize + lsize / 60;
X	}
X	if (t->Bsize > Size) {
X	    Fprintf(stderr, "At %ld bytes, %s is too big for any archive!\n",
X		    t->Bsize, t->Name);
X	    exit(1);
X	    /* NOTREACHED */
X	}
X
X	/* Is our ouput file there? */
X	if (!FoundOutname && OutName && EQ(OutName, t->Name))
X	    FoundOutname = TRUE;
X
X	/* All done -- advance to next entry. */
X	t++;
X	lines++;
X    }
X    (void)fclose(F);
X    (void)unlink(TEMP);
X
X    /* Add our output file? */
X    if (!ExcludeIt && !FoundOutname && OutName) {
X	t->Name = OutName;
X	t->Text = "This shipping list";
X	t->Type = F_FILE;
X	t->Bsize = (lines + 3) * 60;
X	t->Where = 0;
X	t++;
X    }
X
X    /* Sort by size. */
X    lines = t - Table;
X    TabEnd = &Table[lines];
X    if (!Preserve)
X	qsort((char *)Table, lines, sizeof Table[0], SizeP);
X
X    /* Get archive space, allow for initial overhead. */
X    Ark = NEW(ARCHIVE, ArkCount);
X    ArkEnd = &Ark[ArkCount];
X    for (k = Ark; k < ArkEnd; k++) {
X	k->Count = 0;
X	k->Asize = 500;
X    }
X
X    /* See if everyone has a place to be. */
X    if (Believer)
X	for (t = Table; t < TabEnd; t++)
X	    if (t->Where == 0) {
X		Fprintf(stderr, "Can't believe the manifest assignments.\n");
X		Believer = FALSE;
X		break;
X	    }
X
X    /* Loop through the pieces, and put everyone into an archive. */
X    if (!Believer) {
X	for (t = Table; t < TabEnd; t++) {
X	    for (k = Ark; k < ArkEnd; k++)
X		if (t->Bsize + k->Asize < Size) {
X		    k->Asize += t->Bsize;
X		    t->Where = k - Ark;
X		    k->Count++;
X		    break;
X		}
X	    if (k == ArkEnd) {
X		Fprintf(stderr,
X			"'%s' doesn't fit -- need more then %d archives.\n",
X			t->Name, ArkCount);
X		exit(1);
X		/* NOTREACHED */
X	    }
X	    /* Since our share doesn't build sub-directories... */
X	    if (t->Type == F_DIR && k != Ark)
X		Fprintf(stderr, "Warning, directory '%s' is in archive %d.\n",
X			t->Name, k - Ark + 1);
X	}
X    }
X
X    /* Open the output file. */
X    if (OutName == NULL)
X	F = stdout;
X    else {
X	if (Fexists(OutName))
X	    SafeRename(OutName);
X	if ((F = fopen(OutName, "w")) == NULL) {
X	    Fprintf(stderr, "Can't open '%s' for output, %s.\n",
X		    OutName, Ermsg(errno));
X	    exit(1);
X	    /* NOTREACHED */
X	}
X    }
X
X    /* Sort the shipping list, then write it. */
X    if (!Preserve)
X	qsort((char *)Table, lines, sizeof Table[0], ReadmeP);
X    Fprintf(F, "   File Name\t\tArchive #\tDescription\n");
X    Fprintf(F, "----------------------------------------------------------\n");
X    for (t = Table; t < TabEnd; t++)
X	Fprintf(F, MANI_FORMAT, t->Name, t->Where + 1, t->Text);
X
X    /* Close output.  Are we done? */
X    if (F != stdout)
X	(void)fclose(F);
X    if (!Working)
X	exit(0);
X	/* NOTREACHED */
X
X    /* Find last archive number. */
X    for (i = 0, t = Table; t < TabEnd; t++)
X	if (i < t->Where)
X	    i = t->Where;
X    LastOne = i + 1;
X
X    /* Find archive with most files in it and build an argv vector. */
X    for (i = 0, k = Ark; k < ArkEnd; k++)
X	if (i < k->Count)
X	    i = k->Count;
X    av = NEW(char*, i + 10);
X
X    /* Build the fixed part of the argument vector. */
X    av[0] = "shar";
X    i = 1;
X    if (Trailer) {
X	av[i++] = "-t";
X	av[i++] = Trailer;
X    }
X    if (Notkits == FALSE) {
X	(void)sprintf(EndArkNum, "%d", LastOne);
X	av[i++] = "-e";
X	av[i++] = EndArkNum;
X	av[i++] = "-n";
X	av[i++] = CurArkNum;
X    }
X    av[i++] = "-o";
X    av[i++] = buff;			/* See sprintf call in loop below. */
X
X#ifdef	USE_TEMP_MANIFEST
X    av[i++] = "-i";
X    MakeTempName(FLST, TEMP_NAME2);
X    av[i++] = FLST;
X    av[i] = NULL;
X#endif	/* USE_TEMP_MANIFEST */
X
X    /* Call shar to package up each archive. */
X    for (Start = i, i = 0; i < LastOne; i++) {
X	(void)sprintf(CurArkNum, "%d", i + 1);
X	(void)sprintf(buff, NAME_FORMAT, SharName, i + 1);
X#ifndef	USE_TEMP_MANIFEST
X	for (lines = Start, t = Table; t < TabEnd; t++)
X	    if (t->Where == i)
X		av[lines++] = t->Name;
X	av[lines] = NULL;
X#else
X	if ((F = fopen(FLST, "w")) == NULL) {
X	    Fprintf(stderr, "Can't open list file '%s' for output, %s.\n",
X		    FLST, Ermsg(errno));
X	    exit(1);
X	    /* NOTREACHED */
X	}
X	for (t = Table; t < TabEnd; t++)
X	    if (t->Where == i)
X		Fprintf(F, "%s\n", t->Name);
X	(void)fclose(F);
X#endif /* USE_TEMP_MANIFEST */
X	Fprintf(stderr, "Packing kit %d...\n", i + 1);
X	if (lines = Execute(av))
X	    Fprintf(stderr, "Warning, shar returned status %d.\n", lines);
X    }
X
X#ifdef	USE_TEMP_MANIFEST
X    (void)unlink(FLST);
X#endif	/* USE_TEMP_MANIFEST */
X
X    /* That's all she wrote. */
X    exit(0);
X    /* NOTREACHED */
X}
END_OF_FILE
if test 11847 -ne `wc -c <'makekit.c'`; then
    echo shar: \"'makekit.c'\" unpacked with wrong size!
fi
# end of 'makekit.c'
fi
if test -f 'maniscan.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'maniscan.c'\"
else
echo shar: Extracting \"'maniscan.c'\" \(9173 characters\)
sed "s/^X//" >'maniscan.c' <<'END_OF_FILE'
X/*
X**  MANISCAN
X**
X**  Read a manifest, looking for large files and those with binary data.
X*/
X#include "shar.h"
X#ifdef	RCSID
Xstatic char RCS[] =
X	"$Header$";
X#endif	/* RCSID */
X
X
X/*
X**  Global variables.
X*/
Xstatic long	 Size;			/* Bigger than this is bad	*/
Xstatic FILE	*Logfile;		/* Where to record problems	*/
X
X
X/*
X**  Safely add an extension to a filename.  On non-Unix systems, this
X**  might be a pain.
X*/
Xstatic void
XAddExtension(Name, Suffix, Buffer)
X    char	*Name;
X    char	*Suffix;
X    char	*Buffer;
X{
X#ifdef	UNIX
X    (void)sprintf(Buffer, "%s.%s", Name, Suffix);
X#else
X    /* Should fix this... */
X    (void)sprintf(Buffer, "%s%s", Name, Suffix);
X#endif	/* UNIX */
X}
X
X
X/*
X**  Do the grunge work of checking a file.  Note that we scan stuff more
X**  than once because, e.g., after it's been uuencoded and edited it
X**  might be too long.
X*/
Xstatic int
XCheckUp(Name, Commentary)
X    char		*Name;
X    char		*Commentary;
X{
X    REGISTER FILE	*F;
X    REGISTER FILE	*Sfile;
X    REGISTER char	*p;
X    REGISTER int	 i;
X    REGISTER int	 n;
X    REGISTER long	 s;
X    char		 buff[READ_CHUNK];
X    char		 Newname[LINE_SIZE];
X    char		 Digits[10];
X    int			 RanUUencode;
X    int			 Smudged;
X
X    Smudged = FALSE;
X
X    /* Open file, read a chunk. */
X    if ((F = fopen(Name, "r")) == NULL) {
X	Fprintf(stderr, "Can't open \"%s\" to check it, %s.\n",
X		Name, Ermsg(errno));
X	return Smudged;
X    }
X    if ((n = fread(buff, sizeof buff[0], READ_CHUNK, F)) <= 0) {
X	Fprintf(stderr, "Can't read \"%s\" to check it, %s.\n",
X		Name, Ermsg(errno));
X	(void)fclose(F);
X	return Smudged;
X    }
X
X    /* Is it binary?  Rough hueristic -- one third of the chunk isn't
X     * printable. */
X    for (p = buff, i = 0; p < &buff[n]; p++)
X	if (!CTYPE(*p) || !isprint(*p))
X	    i++;
X    if (RanUUencode = i > n / 3) {
X	AddExtension(Name, "UU", Buffer)
X	uuencode(Name, Newname);
X	if (Logfile) {
X	    if (!Smudged) {
X		Smudged = TRUE;
X		Fprintf(Logfile, "\n");
X	    }
X	    Fprintf(Logfile, "Run \"uudecode\" on \"%s\" to create \"%s\".\n",
X		    Newname, Name);
X	}
X	(void)strcpy(Name, Newname);
X	(void)fclose(F);
X	if ((F = fopen(Name, "r")) == NULL) {
X	    Fprintf(stderr, "Can't open \"%s\" to check it, %s.\n",
X		    Name, Ermsg(errno));
X	    return Smudged;
X	}
X    }
X
X    /* See if the input has any long lines, or bad characters. */
X    rewind(F);
X    for (i = 1; fgets(buff, sizeof buff, F); i++) {
X	if (p = IDX(buff, '\n'))
X	    *p = '\0';
X	if (p == NULL || p >= &buff[SAFE_WIDTH])
X	    if (Logfile) {
X		if (!Smudged) {
X		    Smudged = TRUE;
X		    Fprintf(Logfile, "\n");
X		}
X		Fprintf(Logfile, "\"%s\", line %d: line too long\n", Name, i);
X	    }
X
X	for (p = buff; *p; p++)
X	    if (!CTYPE(*p) || !(isprint(*p) || isspace(*p)))
X		if (Logfile) {
X		    if (!Smudged) {
X			Smudged = TRUE;
X			Fprintf(Logfile, "\n");
X		    }
X		    Fprintf(Logfile,
X			    "\"%s\", line %d: bad character 0%o (%s)\n",
X			    Name, i, *p, Seechar(*p));
X		}
X
X	/* Is last character whitespace?  Bitnet will choke. */
X	if (p > buff) {
X	    p--;
X	    if (!CTYPE(*p) || isspace(*p))
X		if (Logfile) {
X		    if (!Smudged) {
X			Smudged = TRUE;
X			Fprintf(Logfile, "\n");
X		    }
X		    Fprintf(Logfile,
X			    "\"%s\", line %d: ends with whitespace\n",
X			    Name, i);
X		}
X	}
X    }
X    (void)fclose(F);
X
X    /* Small enough to fit? */
X    if (Fsize(Name) <= Size) {
X	Printf("%s%s\n", Name, Commentary);
X	return Smudged;
X    }
X
X    /* Reduce size by slop so last line doesn't make us too long. */
X    Size -= 2 * SAFE_WIDTH;
X
X    /* Open input. */
X    if ((F = fopen(Name, "r")) == NULL) {
X	Fprintf(stderr, "Can't open \"%s\" to split it, %s.\n",
X		Name, Ermsg(errno));
X	return Smudged;
X    }
X
X    /* Open first output, write commentary. */
X    i = 1;
X    AddExtension(Name, "1", Newname);
X    if ((Sfile = fopen(Newname, "w")) == NULL) {
X	Fprintf(stderr, "Can't open \"%s\" for writing, %s.\n",
X		Newname, Ermsg(errno));
X	(void)fclose(F);
X	return Smudged;
X    }
X    Printf("%s%s (part %d)\n", Newname, Commentary, i);
X
X    /* Read input, when current output gets too big, start a new file. */
X    for (s = 0; fgets(buff, sizeof buff, F); ) {
X	s += strlen(buff);
X	if (s > Size) {
X	    /* Start a new file. */
X	    (void)fclose(Sfile);
X	    (void)sprintf(Newname, "%s.%d", Name, ++i);
X	    (void)sprintf(Digits, "%d", ++i);
X	    AddExtension(Name, Digits, Newname);
X	    if ((Sfile = fopen(Newname, "w")) == NULL) {
X		Fprintf(stderr, "Can't open \"%s\" for writing, %s.\n",
X			Newname, Ermsg(errno));
X		(void)fclose(F);
X		return Smudged;
X	    }
X	    Printf("%s%s (part %d)\n", Newname, Commentary, i);
X	    s = strlen(buff);
X	}
X	(void)fputs(buff, Sfile);
X    }
X
X    /* Close input and current output, remove temporary if we made one. */
X    (void)fclose(F);
X    (void)fclose(Sfile);
X    if (RanUUencode)
X	(void)unlink(Name);
X
X    /* Write summary message. */
X    if (Logfile) {
X	if (!Smudged) {
X	    Smudged = TRUE;
X	    Fprintf(Logfile, "\n");
X	}
X	Fprintf(Logfile,
X	    "File \"%s\" was split because of its size; to create it, do\n",
X	    Name);
X	if (i < 10)
X	    Fprintf(Logfile, "\tcat %s.? >%s\n", Name, Name);
X	else if (i < 100)
X	    Fprintf(Logfile, "\tcat %s.? %s.?? >%s\n", Name, Name, Name);
X	else
X	    Fprintf(Logfile, "\tWhatever's appropriate\n");
X    }
X    return Smudged;
X}
X
X
Xmain(ac, av)
X    REGISTER int	 ac;
X    REGISTER char	*av[];
X{
X    REGISTER FILE	*F;
X    REGISTER char	*p;
X    REGISTER int	 i;
X    REGISTER int	 Header;
X    REGISTER int	 LogDirty;
X    char		*InName;
X    char		*OutName;
X    char		*LogName;
X    char		 buff[BUFSIZ];
X    char		 Rest[LINE_SIZE];
X    char		 Name[LINE_SIZE];
X    char		 TempName[TEMPSIZE];
X    int			 ExcludeIt;
X    int			 Oops;
X
X    /* Parse JCL. */
X    ExcludeIt = FALSE;
X    InName = NULL;
X    OutName = NULL;
X    LogName = NULL;
X    Header = 0;
X    Size = 50000;
X    for (Oops = FALSE; (i = getopt(ac, av, "eh:i:l:mo:s:")) != EOF; )
X	switch (i) {
X	default:
X	    Oops = TRUE;
X	    break;
X	case 'e':
X	    ExcludeIt = TRUE;
X	    break;
X	case 'h':
X	    Header = atoi(optarg);
X	    break;
X	case 'i':
X	    InName = optarg;
X	    break;
X	case 'l':
X	    LogName = optarg;
X	    break;
X	case 'm':
X	    LogName = "PACKNOTES";
X	    InName = OutName = "MANIFEST";
X	    Header = 2;
X	    break;
X	case 'o':
X	    OutName = optarg;
X	    break;
X	case 's':
X	    Size = (long)atoi(optarg);
X	    if (IDX(optarg, 'k') || IDX(optarg, 'K'))
X		Size *= 1024;
X	    break;
X	}
X    ac -= optind;
X    av += optind;
X
X    if (ac != 0 || Oops) {
X	Fprintf(stderr, "Usage:\n  maniscan %s\n",
X	    "[-e] [-s #[k]] [-m | -i MANIFEST -o MANIFEST -h 2 -l PACKNOTES]");
X	exit(1);
X	/* NOTREACHED */
X    }
X
X    /* Open the input file. */
X    if (InName && freopen(InName, "r", stdin) == NULL) {
X	Fprintf(stderr, "Can't read %s as manifest, %s.\n",
X		InName, Ermsg(errno));
X	exit(1);
X	/* NOTREACHED */
X    }
X
X    /* Open the output file. */
X    if (OutName == NULL)
X	F = stdout;
X    else {
X	MakeTempName(TempName, TEMP_NAME1);
X	if ((F = fopen(TempName, "w")) == NULL) {
X	    Fprintf(stderr, "Can't open '%s' for output, %s.\n",
X		    TempName, Ermsg(errno));
X	    exit(1);
X	    /* NOTREACHED */
X	}
X    }
X
X    /* Open the log file. */
X    LogDirty = FALSE;
X    if (LogName) {
X	if (EQ(LogName, "-")) {
X	    Logfile = stderr;
X	    ExcludeIt = TRUE;
X	}
X	else if ((Logfile = fopen(LogName, "w")) == NULL) {
X	    Fprintf(stderr, "Can't open \"%s\" for output, %s.\n",
X		    LogName, Ermsg(errno));
X	    exit(1);
X	    /* NOTREACHED */
X	}
X    }
X
X    /* Skip any possible prolog, then output rest of file. */
X    while (--Header >= 0 && fgets(buff, sizeof buff, stdin))
X	(void)fputs(buff, F);
X    if (feof(stdin)) {
X	Fprintf(stderr, "Nothing but header lines in the manifest.\n");
X	exit(1);
X	/* NOTREACHED */
X    }
X
X    /* Scan rest of file. */
X    while (fgets(buff, sizeof buff, stdin)) {
X	if (p = IDX(buff, '\n'))
X	    *p = '\0';
X	else
X	    Fprintf(stderr, "Warning, line too long:\n\t%s\n", buff);
X
X	/* Skip leading whitespace and ignore all-blank lines. */
X	for (p = buff; *p && CTYPE(*p) && isspace(*p); )
X	    p++;
X	if (*p == '\0')
X	    continue;
X	/* Save name, find end of it, stuff rest of line away. */
X	while (*++p && CTYPE(*p) && !isspace(*p))
X	    ;
X	i = *p;
X	*p = '\0';
X	(void)strcpy(Name, buff);
X	*p = i;
X	(void)strcpy(Rest, p);
X
X	if (CheckUp(Name, Rest))
X	    LogDirty = TRUE;
X    }
X
X    /* Close logfile, possibly add it to the manifest. */
X    if (Logfile) {
X	if (Logfile != stderr)
X	    (void)fclose(Logfile);
X	if (LogDirty && !ExcludeIt)
X	    Printf(MANI_FORMAT,
X		    LogName, 1, "1 Warnings about long lines, etc");
X    }
X
X    /* Move temp file to new file? */
X    if (OutName) {
X	/* Open new file. */
X	(void)fclose(F);
X	if ((F = fopen(TempName, "r")) == NULL) {
X	    Fprintf(stderr, "Can't open \"%s\" for reading, %s.\n",
X		    Name, Ermsg(errno));
X	    exit(1);
X	    /* NOTREACHED */
X	}
X
X	/* Save old manifest. */
X	if (Fexists(OutName))
X	    SafeRename(OutName);
X	if (freopen(OutName, "w", stdout) == NULL) {
X	    Fprintf(stderr, "Can't open \"%s\" for writing, %s.\n",
X		    OutName, Ermsg(errno));
X	    exit(1);
X	    /* NOTREACHED */
X	}
X
X	/* Copy. */
X	while (fgets(buff, sizeof buff, F))
X	    (void)fputs(buff, stdout);
X    }
X
X    /* That's all she wrote. */
X    exit(0);
X    /* NOTREACHED */
X}
END_OF_FILE
if test 9173 -ne `wc -c <'maniscan.c'`; then
    echo shar: \"'maniscan.c'\" unpacked with wrong size!
fi
# end of 'maniscan.c'
fi
if test -f 'shar.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'shar.c'\"
else
echo shar: Extracting \"'shar.c'\" \(8463 characters\)
sed "s/^X//" >'shar.c' <<'END_OF_FILE'
X/*
X**  SHAR
X**
X**  Make a shell archive of a list of files.
X*/
X#include "shar.h"
X#ifdef	RCSID
Xstatic char RCS[] =
X	"$Header: shar.c,v 2.1 88/06/03 11:51:12 rsalz Locked $";
X#endif	/* RCSID */
X
X/*
X**  Minimum allocation of file name pointers used in "-i" option processing.
X*/
X#define	MIN_FILES	50
X
X
X/*
X**  This prolog is output before the archive.
X*/
Xstatic char	 *Prolog[] = {
X  "! /bin/sh",
X  " This is a shell archive.  Remove anything before this line, then feed it",
X  " into a shell via \"sh file\" or similar.  To overwrite existing files,",
X  " type \"sh file -c\".",
X#ifdef	PEDAGOGY
X  " The tool that generated this appeared in the comp.sources.unix newsgroup;",
X  " send mail to comp-sources-unix@uunet.uu.net if you want that tool.",
X#endif	/* PEDAGOGY */
X  " If this archive is complete, you will see the following message at the end:",
X  NULL
X};
X
X
X/*
X**  Package up one file or directory.
X*/
Xstatic void
Xshar(file, Basename)
X    char		*file;
X    int			 Basename;
X{
X    REGISTER char	*s;
X    REGISTER char	*Name;
X    REGISTER int	 Bads;
X    REGISTER off_t	 Size;
X    REGISTER long	 l;
X    char		 buff[BUFSIZ];
X
X    /* Just in case. */
X    if (EQ(file, ".") || EQ(file, ".."))
X	return;
X
X    Size = Fsize(file);
X    Name = Basename && (Name = RDX(file, '/')) ? Name + 1 : file;
X
X    /* Making a directory? */
X    if (Ftype(file) == F_DIR) {
X	Printf("if test ! -d '%s' ; then\n", Name);
X	Printf("    echo shar: Creating directory \\\"'%s'\\\"\n", Name);
X	Printf("    mkdir '%s'\n", Name);
X	Printf("fi\n");
X    }
X    else {
X	if (freopen(file, "r", stdin) == NULL) {
X	    Fprintf(stderr, "Can't open \"%s\" %s\n", file, Ermsg(errno));
X	    exit(1);
X	    /* NOTREACHED */
X	}
X
X	/* Emit the per-file prolog. */
X	Printf("if test -f '%s' -a \"${1}\" != \"-c\" ; then \n", Name);
X	Printf("  echo shar: Will not clobber existing file \\\"'%s'\\\"\n",
X	    Name);
X	Printf("else\n");
X	Printf("echo shar: Extracting \\\"'%s'\\\" \\(%ld character%s\\)\n",
X	       Name, (long)Size, Size == 1 ? "" : "s");
X	Printf("sed \"s/^X//\" >'%s' <<'END_OF_FILE'\n", Name, Name);
X
X	/* Output the file contents. */
X	for (l = 0, Bads = 0; fgets(buff, sizeof buff, stdin); l++) {
X	    if (s = IDX(buff, '\n'))
X		*s = '\0';
X	    else
X		Fprintf(stderr, "Warning, line too long:\n\t%s\n", buff);
X
X	    (void)putchar('X');
X	    for (s = buff; *s; s++) {
X		if (BADCHAR(*s)) {
X		    Fprintf(stderr,
X			    "Bad character 0%o in line %ld of \"%s\".\n",
X			    *s, l, Name);
X		    Bads++;
X		}
X		(void)putchar(*s);
X	    }
X	    (void)putchar('\n');
X	}
X
X	/* Check for missing \n at end of file. */
X	if (*--s != '\n')
X	    (void)putchar('\n');
X	Printf("END_OF_FILE\n",Name);
X	if (*s != '\n') {
X	    Printf("echo shar: NEWLINE appended to \\\"'%s'\\\"\n", Name);
X	    Fprintf(stderr, "NEWLINE appended to \"%s\"\n", Name);
X	    Size++;
X	 }
X
X	/* Tell about any control characters. */
X	if (Bads) {
X	    Printf(
X    "echo shar: %d control character%s may be missing from \\\"'%s'\\\"\n",
X		   Bads, Bads == 1 ? "" : "s", Name);
X	    Fprintf(stderr, "Found %d control char%s in \"%s\"\n",
X		    Bads, Bads == 1 ? "" : "s", Name);
X	}
X
X	/* Output size check. */
X	Printf("if test %ld -ne `wc -c <'%s'`; then\n", (long)Size, Name);
X	Printf("    echo shar: \\\"'%s'\\\" unpacked with wrong size!\n", Name);
X	Printf("fi\n");
X
X	/* Executable? */
X	if (Fexecute(file))
X	    Printf("chmod +x '%s'\n", Name);
X
X	Printf("# end of '%s'\nfi\n", Name);
X    }
X}
X
X
X/*
X**  Read list of files from file.
X*/
Xstatic char **
XGetFiles(Name)
X    char		 *Name;
X{
X    REGISTER FILE	 *F;
X    REGISTER int	  i;
X    REGISTER int	  count;
X    REGISTER char	**files;
X    REGISTER char	**temp;
X    REGISTER int	  j;
X    char		  buff[BUFSIZ];
X    char		 *p;
X
X    /* Open the file. */
X    if (EQ(Name, "-"))
X	F = stdin;
X    else if ((F = fopen(Name, "r")) == NULL) {
X	Fprintf(stderr, "Can't open \"%s\" for input.\n", Name);
X	return NULL;
X    }
X
X    /* Get space. */
X    count = MIN_FILES;
X    files = NEW(char*, count);
X
X    /* Read lines. */
X    for (i = 0; fgets(buff, sizeof buff, F); ) {
X	if (p = IDX(buff, '\n'))
X	    *p = '\0';
X	files[i] = COPY(buff);
X	if (++i == count - 2) {
X	    /* Get more space; some systems don't have realloc()... */
X	    count += MIN_FILES;
X	    for (temp = NEW(char*, count), j = 0; j < i; j++)
X		temp[j] = files[j];
X	    files = temp;
X	}
X    }
X
X    /* Clean up, close up, return. */
X    files[i] = NULL;
X    (void)fclose(F);
X    return files;
X}
X
X
Xmain(ac, av)
X    int			 ac;
X    REGISTER char	*av[];
X{
X    REGISTER char	*Trailer;
X    REGISTER char	*p;
X    REGISTER char	*q;
X    REGISTER int	 i;
X    REGISTER int	 length;
X    REGISTER int	 Oops;
X    REGISTER int	 Knum;
X    REGISTER int	 Kmax;
X    REGISTER int	 Basename;
X    REGISTER int	 j;
X    time_t		 clock;
X    char		**Flist;
X
X    /* Parse JCL. */
X    Basename = 0;
X    Knum = 0;
X    Kmax = 0;
X    Trailer = NULL;
X    Flist = NULL;
X    for (Oops = FALSE; (i = getopt(ac, av, "be:i:n:o:t:")) != EOF; )
X	switch (i) {
X	default:
X	    Oops = TRUE;
X	    break;
X	case 'b':
X	    Basename = TRUE;
X	    break;
X	case 'e':
X	    Kmax = atoi(optarg);
X	    break;
X	case 'i':
X	    Flist = GetFiles(optarg);
X	    break;
X	case 'n':
X	    Knum = atoi(optarg);
X	    break;
X	case 'o':
X	    if (freopen(optarg, "w", stdout) == NULL) {
X		Fprintf(stderr, "Can't open \"%s\" for output, %s.\n",
X			optarg, Ermsg(errno));
X		exit(1);
X		/* NOTREACHED */
X	    }
X	    break;
X	case 't':
X	    Trailer = optarg;
X	    break;
X	}
X    ac -= optind;
X    av += optind;
X
X    /* If user hasn't screwed up yet, make sure we exactly one of
X     * the -i flag or files named on the command line. */
X    if (!Oops
X     && ((Flist == NULL && ac == 0) || (Flist != NULL && ac != 0))) {
X	Fprintf(stderr,
X		"What files to shar?  Use -i or command line, not both.\n");
X	Oops = TRUE;
X    }
X
X    if (Oops) {
X	Fprintf(stderr,
X		"Usage:\n  shar %s files...\n",
X		"[-b] [-o outfile] [-i infile] [-n# -e# -t'text']");
X	exit(1);
X	/* NOTREACHED */
X    }
X
X    /* If we didn't get the list from -i, rest of argv is the file list. */
X    if (Flist == NULL)
X	/* Rest of arguments are files. */
X	Flist = av;
X
X    /* Everything readable and reasonably-named? */
X    for (Oops = FALSE, i = 0; p = Flist[i]; i++)
X	if (freopen(p, "r", stdin) == NULL) {
X	    Fprintf(stderr, "Can't read \"%s\", %s.\n", p, Ermsg(errno));
X	    Oops = TRUE;
X	}
X	else
X	    for (; *p; p++)
X		if (!CTYPE(*p)) {
X		    Fprintf(stderr, "Bad character '%c' in \"%s\".\n",
X			    *p, Flist[i]);
X		    Oops = TRUE;
X		}
X    if (Oops)
X	exit(1);
X	/* NOTREACHED */
X
X    /* Prolog. */
X    for (i = 0; p = Prolog[i]; i++)
X	Printf("#%s\n", p);
X    if (Knum && Kmax)
X	Printf("#\t\t\"End of archive %d (of %d).\"\n", Knum, Kmax);
X    else
X        Printf("#\t\t\"End of shell archive.\"\n");
X    Printf("# Contents: ");
X    for (length = 12, i = 0; p = Flist[i++]; length += j) {
X	if (Basename && (q = RDX(p, '/')))
X	    p = q + 1;
X	j = strlen(p) + 1;
X	if (length + j < WIDTH)
X	    Printf(" %s", p);
X	else {
X	    Printf("\n#   %s", p);
X	    length = 4;
X	}
X    }
X    Printf("\n");
X    clock = time((time_t *)NULL);
X    Printf("# Wrapped by %s@%s on %s", User(), Host(), ctime(&clock));
X    Printf("PATH=/bin:/usr/bin:/usr/ucb ; export PATH\n");
X
X    /* Do it. */
X    while (*Flist)
X	shar(*Flist++, Basename);
X
X    /* Epilog. */
X    if (Knum && Kmax) {
X	Printf("echo shar: End of archive %d \\(of %d\\).\n", Knum, Kmax);
X	Printf("cp /dev/null ark%disdone\n", Knum);
X	Printf("MISSING=\"\"\n");
X	Printf("for I in");
X	for (i = 0; i < Kmax; i++)
X	    Printf(" %d", i + 1);
X	Printf(" ; do\n");
X	Printf("    if test ! -f ark${I}isdone ; then\n");
X	Printf("\tMISSING=\"${MISSING} ${I}\"\n");
X	Printf("    fi\n");
X	Printf("done\n");
X	Printf("if test \"${MISSING}\" = \"\" ; then\n");
X	if (Kmax == 1)
X	    Printf("    echo You have the archive.\n");
X	else if (Kmax == 2)
X	    Printf("    echo You have unpacked both archives.\n");
X	else
X	    Printf("    echo You have unpacked all %d archives.\n", Kmax);
X	if (Trailer && *Trailer)
X	    Printf("    echo \"%s\"\n", Trailer);
X	Printf("    rm -f ark[1-9]isdone%s\n",
X	       Kmax >= 9 ? " ark[1-9][0-9]isdone" : "");
X	Printf("else\n");
X	Printf("    echo You still need to unpack the following archives:\n");
X	Printf("    echo \"        \" ${MISSING}\n");
X	Printf("fi\n");
X	Printf("##  End of shell archive.\n");
X    }
X    else {
X        Printf("echo shar: End of shell archive.\n");
X	if (Trailer && *Trailer)
X	    Printf("echo \"%s\"\n", Trailer);
X    }
X
X    Printf("exit 0\n");
X
X    exit(0);
X    /* NOTREACHED */
X}
END_OF_FILE
if test 8463 -ne `wc -c <'shar.c'`; then
    echo shar: \"'shar.c'\" unpacked with wrong size!
fi
# end of 'shar.c'
fi
if test -f 'unshar.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'unshar.c'\"
else
echo shar: Extracting \"'unshar.c'\" \(8836 characters\)
sed "s/^X//" >'unshar.c' <<'END_OF_FILE'
X/*
X**  UNSHAR
X**
X**  Unpack shell archives that might have gone through mail, notes, news, etc.
X**  This is Michael Mauldin's code which I have usurped and heavily modified.
X*/
X#include "shar.h"
X#ifdef	RCSID
Xstatic char RCS[] =
X	"$Header: unshar.c,v 2.2 88/06/03 16:08:14 rsalz Locked $";
X#endif	/* RCSID */
X
X
X/*
X**  Print error message and die.
X*/
Xstatic void
XQuit(text)
X    char	*text;
X{
X    Fprintf(stderr, "unshar:  %s, %s.\n", text, Ermsg(errno));
X    exit(1);
X    /* NOTREACHED */
X}
X
X
X/*
X**  Does this look like a mail header line?
X*/
Xstatic int
XIsHeader(p)
X    REGISTER char	*p;
X{
X    REGISTER int	 i;
X
X    if (*p == '\0' || *p == '\n')
X	return FALSE;
X    if (WHITE(*p))
X	return TRUE;
X    for (i = 0; (CTYPE(*p) && isalnum(*p)) || HDRCHAR(*p); i++)
X	p++;
X    return i && *p == ':';
X}
X
X
X/*
X**  Is this a /bin/sh comment line?  We check that because some shars
X**  output comments before the CUT line.
X*/
Xstatic int
XIsSHcomment(p)
X    REGISTER char	*p;
X{
X    while (CTYPE(*p) &&
X      (isalpha(*p) || WHITE(*p) || *p == '\n' || *p == ',' || *p == '.'))
X	p++;
X    return *p == '\0';
X}
X
X
X/*
X**  Return TRUE if p has wd1 and wd2 as words (i.e., no preceeding or
X**  following letters).  Clever code, Michael.
X*/
Xstatic int
XHas(p, wd1, wd2)
X    REGISTER char	*p;
X    REGISTER char	*wd1;
X    REGISTER char	*wd2;
X{
X    REGISTER char	*wd;
X    REGISTER int	 first;
X
X    wd = wd1;
X    first = TRUE;
Xagain: 
X    while (*p) {
X	if (!CTYPE(*p) || !isalpha(*p)) {
X	    p++;
X	    continue;
X	}
X	while (*p++ == *wd++) {
X	    if (*wd == '\0') {
X		if (!CTYPE(*p) || !isalpha(*p)) {
X		    if (!first)
X			return TRUE;
X		    first = FALSE;
X		    wd = wd2;
X		    goto again;
X		}
X		break;
X	    }
X	}
X	while (CTYPE(*p) && isalpha(*p))
X	    p++;
X	wd = first ? wd1 : wd2;
X    }
X    return FALSE;
X}
X
X
X/*
X**  Here's where the work gets done.  Skip headers and try to intuit
X**  if the file is, e.g., C code, etc.
X*/
Xstatic int
XFound(Name, buff, Forced, Stream, Header)
X    REGISTER char	*Name;
X    REGISTER char	*buff;
X    REGISTER int	 Forced;
X    REGISTER FILE	*Stream;
X    REGISTER FILE	*Header;
X{
X    REGISTER char	*p;
X    REGISTER int	 InHeader;
X    char		 lower[BUFSIZ];
X
X    if (Header)
X	InHeader = TRUE;
X
X    while (TRUE) {
X	/* Read next line, fail if no more */
X	if (fgets(buff, BUFSIZ, Stream) == NULL) {
X	    Fprintf(stderr, "unshar:  No shell commands in %s.\n", Name);
X	    return FALSE;
X	}
X
X	/* See if it looks like another language. */
X	if (!Forced) {
X	    if (PREFIX(buff, "#include") || PREFIX(buff, "# include")
X	     || PREFIX(buff, "#define") || PREFIX(buff, "# define")
X	     || PREFIX(buff, "#ifdef") || PREFIX(buff, "# ifdef")
X	     || PREFIX(buff, "#ifndef") || PREFIX(buff, "# ifndef")
X	     || (PREFIX(buff, "/*")
X	      && !PREFIX(buff, NOTES1) && !PREFIX(buff, NOTES2)))
X		p = "C code";
X	    else if (PREFIX(buff, "(*"))		/* For vi :-) */
X		p = "PASCAL code";
X	    else if (buff[0] == '.'
X		  && CTYPE(buff[1]) && isalpha(buff[1])
X		  && CTYPE(buff[2]) && isalpha(buff[2])
X		  && CTYPE(buff[3]) && !isalpha(buff[3]))
X		p = "TROFF source";
X	    else
X		p = NULL;
X	    if (p) {
X		Fprintf(stderr,
X			"unshar:  %s is apparently %s, not a shell archive.\n",
X			Name, p);
X		return FALSE;
X	    }
X	}
X
X	/* Does this line start with a shell command or comment? */
X	if ((buff[0] == '#' && !IsSHcomment(buff + 1))
X	 || buff[0] == ':' || PREFIX(buff, "echo ")
X	 || PREFIX(buff, "sed ") || PREFIX(buff, "cat ")) {
X	    return TRUE;
X	}
X
X	/* Does this line say "Cut here"? */
X	for (p = strcpy(lower, buff); *p; p++)
X	    if (CTYPE(*p) && islower(*p))
X		*p = toupper(*p);
X	if (PREFIX(buff, "-----") || Has(lower, "cut", "here")
X	 || Has(lower, "cut", "cut") || Has(lower, "tear", "here")) {
X	    /* Get next non-blank line. */
X	    do {
X		if (fgets(buff, BUFSIZ, Stream) == NULL) {
X		    Fprintf(stderr, "unshar:  cut line is last line of %s\n",
X			    Name);
X		    return FALSE;
X		}
X	    } while (*buff == '\n');
X
X	    /* If it starts with a comment or lower-case letter we win. */
X	    if (*buff == '#' || *buff == ':'
X	     || (CTYPE(*buff) && islower(*buff)))
X		return TRUE;
X
X	    /* The cut message lied. */
X	    Fprintf(stderr, "unshar: %s is not a shell archive,\n", Name);
X	    Fprintf(stderr, "        the 'cut' line was followed by: %s", buff);
X	    return FALSE;
X	}
X
X	if (Header) {
X	    (void)fputs(buff, Header);
X	    if (InHeader && !IsHeader(buff))
X		InHeader = FALSE;
X	}
X    }
X}
X
X
X/*
X**  Create file for the header, find true start of the archive,
X**  and send it off to the shell.
X*/
Xstatic void
XUnshar(Name, HdrFile, Stream, Saveit, Forced)
X    char		*Name;
X    char		*HdrFile;
X    REGISTER FILE 	*Stream;
X    int			 Saveit;
X    int			 Forced;
X{
X    REGISTER FILE	*Header;
X#ifndef	USE_MY_SHELL
X    REGISTER FILE	*Pipe;
X#endif	/* USE_MY_SHELL */
X    char		*p;
X    char		 buff[BUFSIZ];
X
X    if (Saveit) {
X	/* Create a name for the saved header. */
X	if (HdrFile)
X	    (void)strcpy(buff, HdrFile);
X	else if (Name) {
X	    p = RDX(Name, '/');
X	    (void)strncpy(buff, p ? p + 1 : Name, 14);
X	    buff[10] = 0;
X	    (void)strcat(buff, ".hdr");
X	}
X	else
X	    (void)strcpy(buff, "UNSHAR.HDR");
X
X	/* Tell user, and open the file. */
X	Fprintf(stderr, "unshar:  Sending header to %s.\n", buff);
X	if ((Header = fopen(buff, "a")) == NULL)
X	    Quit("Can't open file for header");
X    }
X    else
X	Header = NULL;
X
X    /* If name is NULL, we're being piped into... */
X    p = Name ? Name : "the standard input";
X    Printf("unshar:  Doing %s:\n", p);
X
X    if (Found(p, buff, Forced, Stream, Header)) {
X#ifdef	USE_MY_SHELL
X	BinSh(Name, Stream, buff);
X#else
X	if ((Pipe = popen("/bin/sh", "w")) == NULL)
X	    Quit("Can't open pipe to /bin/sh process");
X
X	(void)fputs(buff, Pipe);
X	while (fgets(buff, sizeof buff, Stream))
X	    (void)fputs(buff, Pipe);
X
X	(void)pclose(Pipe);
X#endif	/* USE_MY_SHELL */
X    }
X
X    /* Close the headers. */
X    if (Saveit)
X	(void)fclose(Header);
X}
X
X
Xmain(ac, av)
X    REGISTER int	 ac;
X    REGISTER char	*av[];
X{
X    REGISTER FILE	*Stream;
X    REGISTER int	 i;
X    char		*p;
X    char		*Home;
X    char		*HdrFile;
X    char		 cwd[BUFSIZ];
X    char		 dir[BUFSIZ];
X    char		 buff[BUFSIZ];
X    int			 Saveit;
X    int			 Forced;
X    int			 Oops;
X
X    /* Parse JCL. */
X    p = getenv("UNSHARDIR");
X    Saveit = DEF_SAVEIT;
X#ifdef	UNIX
X    HdrFile = NULL;
X#else
X    HdrFile = "UNSHAR.HDR";
X#endif	/* UNIX */
X    Forced = FALSE;
X    for (Oops = FALSE; (i = getopt(ac, av, "c:d:fh:ns")) != EOF; )
X	switch (i) {
X	default:
X	    Oops = TRUE;
X	    break;
X	case 'c': 
X	case 'd': 
X	    p = optarg;
X	    break;
X	case 'f':
X	    Forced = TRUE;
X	    break;
X	case 'h':
X	    HdrFile = optarg;
X	    /* FALLTHROUGH */
X	case 's': 
X	    Saveit = TRUE;
X	    break;
X	case 'n':
X	    Saveit = FALSE;
X	    break;
X	}
X    ac -= optind;
X    av += optind;
X
X    if (Oops) {
X	Fprintf(stderr, "Usage:\n  unshar %s [files...]\n",
X		"[-f] [-s] [-n] [-c dir] [-d dir] [-h file]");
X	exit(1);
X	/* NOTREACHED */
X    }
X
X    /* Going somewhere? */
X    if (p) {
X	if (*p == '?') {
X	    /* Ask for name. */
X	    Stream = isatty(fileno(stdin))
X		    ? stdin : fopen(ctermid((char *)NULL), "r");
X	    if (Stream == NULL)
X		Quit("Can't open tty to ask for directory");
X	    Printf("unshar:  what directory?  ");
X	    (void)fflush(stdout);
X	    if (fgets(buff, sizeof buff, Stream) == NULL
X	     || buff[0] == '\n'
X	     || (p = IDX(buff, '\n')) == NULL)
X		Quit("Okay, cancelled");
X	    *p = '\0';
X	    p = buff;
X	    if (Stream != stdin)
X		(void)fclose(Stream);
X	}
X
X	/* If name is ~/blah, he means $HOME/blah. */
X	if (*p == '~') {
X#ifdef	VMS
X	    (void)sprintf(dir, "sys$login:[%s]", p + 1);
X#else
X	    Home = getenv("HOME");
X	    if (Home == NULL) {
X		Home = "/tmp";
X		Fprintf(stderr, "Unshar warning, no $HOME; using \"%s\".\n",
X			Home);
X	    }
X	    (void)sprintf(dir, "%s/%s", Home, p + 1);
X#endif	/* VMS */
X	    p = dir;
X	}
X
X	/* If we're gonna move, first remember where we were. */
X	if (GetDir(cwd, sizeof cwd) == NULL) {
X	    Fprintf(stderr, "Unshar warning, Can't get current directory.\n");
X	    cwd[0] = '\0';
X	}
X
X	/* Got directory; try to go there.  Only make last component. */
X	if (chdir(p) < 0 && (mkdir(p, 0777) < 0 || chdir(p) < 0))
X	    Quit("Cannot chdir nor mkdir desired directory");
X    }
X    else
X	cwd[0] = '\0';
X
X    /* No buffering. */
X    (void)setbuf(stdout, (char *)NULL);
X    (void)setbuf(stderr, (char *)NULL);
X
X    if (*av)
X	/* Process filenames from command line. */
X	for (; *av; av++) {
X	    if (cwd[0] && av[0][0] != '/') {
X		(void)sprintf(buff, "%s/%s", cwd, *av);
X		*av = buff;
X	    }
X	    if ((Stream = fopen(*av, "r")) == NULL)
X		Fprintf(stderr, "unshar:  Can't open file '%s'.\n", *av);
X	    else {
X		Unshar(*av, HdrFile, Stream, Saveit, Forced);
X		(void)fclose(Stream);
X	    }
X	}
X    else
X	/* Do standard input. */
X	Unshar((char *)NULL, HdrFile, stdin, Saveit, Forced);
X
X    /* That's all she wrote. */
X    exit(0);
X    /* NOTREACHED */
X}
END_OF_FILE
if test 8836 -ne `wc -c <'unshar.c'`; then
    echo shar: \"'unshar.c'\" unpacked with wrong size!
fi
# end of 'unshar.c'
fi
echo shar: End of archive 4 \(of 5\).
cp /dev/null ark4isdone
MISSING=""
for I in 1 2 3 4 5 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 5 archives.
    echo "Now go find those bugs, and report them to rsalz@uunet.uu.net"
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0
-- 
Please send comp.sources.unix-related mail to rsalz@uunet.uu.net.