[comp.sources.unix] v15i019: Tools to create and unpack shell archives, Part02/03

rsalz@uunet.uu.net (Rich Salz) (05/28/88)

Submitted-by: Rich Salz <rsalz@uunet.uu.net>
Posting-number: Volume 15, Issue 19
Archive-name: cshar/part02

#! /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 2 (of 3)."
# Contents:  dir.amiga findsrc.c makekit.c shar.c unshar.c
# Wrapped by rsalz@fig.bbn.com on Fri May 27 14:15:08 1988
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'dir.amiga' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'dir.amiga'\"
else
echo shar: Extracting \"'dir.amiga'\" \(6596 characters\)
sed "s/^X//" >'dir.amiga' <<'END_OF_FILE'
X
X[  I have not tried this at all.  --r$  ]
X
Return-Path: mwm@violet.Berkeley.EDU
Received: from violet.berkeley.edu (violet.berkeley.edu.ARPA) by PINEAPPLE.BBN.COM (4.12/4.7)  id AA28194; Tue, 14 Jul 87 00:52:17 edt
Received: from localhost.ARPA
X	by violet.berkeley.edu (5.54 (CFC 4.22.3)/1.16.15)
X	id AA16462; Mon, 13 Jul 87 21:54:26 PDT
Message-Id: <8707140454.AA16462@violet.berkeley.edu>
To: Richard Salz <rsalz@pineapple.bbn.com>
Subject: Re: Amiga version of the dir library... 
Ultrix: Just say No!
In-Reply-To: Your message of Wed, 08 Jul 87 21:19:51 -0400
Date: Mon, 13 Jul 87 21:54:25 PDT
XFrom: Mike (My watch has windows) Meyer <mwm@violet.Berkeley.EDU>
X
Here's what I did. This is built to reflect the 4BSD manual pages, not
the SysV/dpANS manual pages.
X
I now know how to get the telldir/seekdir pair to work correctly with
multiple tell()s, but don't have much motivation to do so. If someone
out there does it, or is interested in doing it, I'd be interested in
the results or willing to help.
X
XFinal note: as with many micros, there's more than one C compiler.
This was written for the Lattice compiler, and uses features known
not to exist in other Amiga compilers. Fixing it should be trivial.
X
Oh, yeah - sorry for the delay in getting these two you.
X
X	<mike
X
X#! /bin/sh
X# This is a shell archive, meaning:
X# 1. Remove everything above the #! /bin/sh line.
X# 2. Save the resulting text in a file.
X# 3. Execute the file with /bin/sh (not csh) to create:
X#	dir.h
X#	ndir.c
X# By:	Mike (My watch has windows) Meyer (Missionaria Phonibalonica)
export PATH; PATH=/bin:/usr/bin:$PATH
echo shar: "extracting 'dir.h'" '(1708 characters)'
if test -f 'dir.h'
then
X	echo shar: "will not over-write existing file 'dir.h'"
else
sed 's/^X//' << \SHAR_EOF > 'dir.h'
XX#ifndef DIR_H
XX#define DIR_H
XX
XX#ifndef	EXEC_TYPES_H
XX#include "exec/types.h"
XX#endif
XX
XX#ifndef	LIBRARIES_DOS_H
XX#include "libraries/dos.h"
XX#endif
XX
XX#ifndef	LIBRARIES_DOSEXTENS_H
XX#include "libraries/dosextens.h"
XX#endif
XX/*
XX * MAXNAMELEN is the maximum length a file name can be. The direct structure
XX * is lifted form 4BSD, and has not been changed so that code which uses
XX * it will be compatable with 4BSD code. d_ino and d_reclen are unused,
XX * and will probably be set to some non-zero value.
XX */
XX#define	MAXNAMLEN	31		/* AmigaDOS file max length */
XX
XXstruct	direct {
XX	ULONG	d_ino ;			/* unused - there for compatability */
XX	USHORT	d_reclen ;		/* ditto */
XX	USHORT	d_namlen ;		/* length of string in d_name */
XX	char	d_name[MAXNAMLEN + 1] ;	/* name must be no longer than this */
XX};
XX/*
XX * The DIRSIZ macro gives the minimum record length which will hold
XX * the directory entry.  This requires the amount of space in struct direct
XX * without the d_name field, plus enough space for the name with a terminating
XX * null byte (dp->d_namlen+1), rounded up to a 4 byte boundary.
XX */
XX
XX#undef DIRSIZ
XX#define DIRSIZ(dp) \
XX    ((sizeof(struct direct) - (MAXNAMLEN+1)) + (((dp) -> d_namlen+1 + 3) &~ 3))
XX/*
XX * The DIR structure holds the things that AmigaDOS needs to know about
XX * a file to keep track of where it is and what it's doing.
XX */
XX
XXtypedef struct {
XX	struct FileInfoBlock	d_info ,	/* Default info block */
XX				d_seek ;	/* Info block for seeks */
XX	struct FileLock		*d_lock ;	/* Lock on directory */
XX	} DIR ;
XX	
XXextern	DIR *opendir(char *) ;
XXextern	struct direct *readdir(DIR *) ;
XXextern	long telldir(DIR *) ;
XXextern	void seekdir(DIR *, long) ;
XXextern	void rewinddir(DIR *) ;
XXextern	void closedir(DIR *) ;
XX#endif	DIR_H
SHAR_EOF
if test 1708 -ne "`wc -c < 'dir.h'`"
then
X	echo shar: "error transmitting 'dir.h'" '(should have been 1708 characters)'
fi
fi
echo shar: "extracting 'ndir.c'" '(2486 characters)'
if test -f 'ndir.c'
then
X	echo shar: "will not over-write existing file 'ndir.c'"
else
sed 's/^X//' << \SHAR_EOF > 'ndir.c'
XX/*
XX * ndir - routines to simulate the 4BSD new directory code for AmigaDOS.
XX */
XX#include <dir.h>
XX
XXDIR *
XXopendir(dirname) char *dirname; {
XX	register DIR	*my_dir, *AllocMem(int, int) ;
XX	struct FileLock	*Lock(char *, int), *CurrentDir(struct FileLock *) ;
XX
XX	if ((my_dir = AllocMem(sizeof(DIR), 0)) == NULL) return NULL ;
XX
XX
XX	if (((my_dir -> d_lock = Lock(dirname, ACCESS_READ)) == NULL)
XX	/* If we can't examine it */
XX	||  !Examine(my_dir -> d_lock, &(my_dir -> d_info))
XX	/* Or it's not a directory */
XX	||  (my_dir -> d_info . fib_DirEntryType < 0)) {
XX		FreeMem(my_dir, sizeof(DIR)) ;
XX		return NULL ;
XX		}
XX	return my_dir ;
XX	}
XX
XXstruct direct *
XXreaddir(my_dir) DIR *my_dir; {
XX	static struct direct	result ;
XX
XX	if (!ExNext(my_dir -> d_lock, &(my_dir -> d_info))) return NULL ;
XX
XX	result . d_reclen = result . d_ino = 1 ;	/* Not NULL! */
XX	(void) strcpy(result . d_name, my_dir -> d_info . fib_FileName) ;
XX	result . d_namlen = strlen(result . d_name) ;
XX	return &result ;
XX	}
XX
XXvoid
XXclosedir(my_dir) DIR *my_dir; {
XX
XX	UnLock(my_dir -> d_lock) ;
XX	FreeMem(my_dir, sizeof(DIR)) ;
XX	}
XX/*
XX * telldir and seekdir don't work quite right. The problem is that you have
XX * to save more than a long's worth of stuff to indicate position, and it's
XX * socially unacceptable to alloc stuff that you don't free later under
XX * AmigaDOS. So we fake it - you get one level of seek, and dat's all.
XX * As of now, these things are untested.
XX */
XX#define DIR_SEEK_RETURN		((long) 1)	/* Not 0! */
XXlong
XXtelldir(my_dir) DIR *my_dir; {
XX
XX	my_dir -> d_seek = my_dir -> d_info ;
XX	return (long) DIR_SEEK_RETURN ;
XX	}
XX
XXvoid
XXseekdir(my_dir, where) DIR *my_dir; long where; {
XX
XX	if (where == DIR_SEEK_RETURN)
XX		my_dir -> d_info = my_dir -> d_seek ;
XX	else	/* Makes the next readdir fail */
XX		setmem((char *) my_dir, sizeof(DIR), 0) ;
XX	}
XX
XXvoid
XXrewinddir(my_dir) DIR *my_dir; {
XX
XX	if (!Examine(my_dir -> d_lock, &(my_dir -> d_info)))
XX		setmem((char *) my_dir, sizeof(DIR), 0) ;
XX	}
XX#ifdef	TEST
XX/*
XX * Simple code to list the files in the argument directory,
XX *	lifted straight from the man page.
XX */
XX#include <stdio.h>
XXvoid
XXmain(argc, argv) int argc; char **argv; {
XX	register DIR		*dirp ;
XX	register struct direct	*dp ;
XX	register char		*name ;
XX
XX	if (argc < 2) name = "" ;
XX	else name = argv[1] ;
XX
XX	if ((dirp = opendir(name)) == NULL) {
XX		fprintf(stderr, "Bogus! Can't opendir %s\n", name) ;
XX		exit(1) ;
XX		}
XX
XX	for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp))
XX		printf("%s ", dp -> d_name) ;
XX	closedir(dirp);
XX	putchar('\n') ;
XX	}
XX#endif	TEST
XX
SHAR_EOF
if test 2486 -ne "`wc -c < 'ndir.c'`"
then
X	echo shar: "error transmitting 'ndir.c'" '(should have been 2486 characters)'
fi
fi
exit 0
X#	End of shell archive
END_OF_FILE
if test 6596 -ne `wc -c <'dir.amiga'`; then
    echo shar: \"'dir.amiga'\" unpacked with wrong size!
fi
# end of 'dir.amiga'
fi
if test -f 'findsrc.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'findsrc.c'\"
else
echo shar: Extracting \"'findsrc.c'\" \(7195 characters\)
sed "s/^X//" >'findsrc.c' <<'END_OF_FILE'
X/*
X**  FINDSRC
X**  Walk directories, trying to find source files.
X*/
X#include "shar.h"
X#ifdef	RCSID
static char RCS[] =
X	"$Header: findsrc.c,v 2.0 88/05/27 13:26:05 rsalz Exp $";
X#endif	/* RCSID */
X
X
X/*
X**  Global variables.
X*/
int	 DoDOTFILES;			/* Do .newsrc and friends?	*/
int	 DoRCS;				/* Do RCS and SCCS files?	*/
int	 Default;			/* Default answer from user	*/
int	 Verbose;			/* List rejected files, too?	*/
char	*Dname;				/* Filename of directory list	*/
char	*Fname;				/* Filename of file list	*/
XFILE	*Dfile;				/* List of directories found	*/
XFILE	*Ffile;				/* List of files found		*/
XFILE	*DEVTTY;			/* The tty, if in filter mode	*/
X
X
X/*
X**  Signal handler.  Clean up and die.
X*/
static sigret_t
Catch(s)
X    int		 s;
X{
X    int		 e;
X
X    e = errno;
X    if (Dname)
X	(void)unlink(Dname);
X    if (Fname)
X    (void)unlink(Fname);
X    Fprintf(stderr, "Got signal %d, %s.\n", s, Ermsg(e));
X    exit(1);
X}
X
X
X/*
X**  Given a filename, apply heuristics to see if we want it.
X*/
static int
Wanted(Name)
X    REGISTER char	*Name;
X{
X    REGISTER FILE	*F;
X    REGISTER char	*s;
X    REGISTER char	*p;
X    REGISTER char	*d;
X    char		 buff[BUFSIZ];
X
X    /* Get down to brass tacks. */
X    s = (p = RDX(Name, '/')) ? p + 1 : Name;
X
X    /* Only do directories other than . and .. and regular files. */
X    if ((Ftype(Name) != F_DIR && Ftype(Name) != F_FILE)
X     || EQ(s, ".") || EQ(s, ".."))
X	return(FALSE);
X
X    /* Common cruft we never want. */
X    if (EQ(s, "foo") || EQ(s, "core") || EQ(s, "tags") || EQ(s, "lint"))
X	return(FALSE);
X
X    /* Disregard stuff with bogus suffixes. */
X    d = RDX(s, '.');
X    if ((p = d)
X     && (EQ(++p, "out") || EQ(p, "orig") || EQ(p, "rej") || EQ(p, "BAK")
X      || EQ(p, "CKP") || EQ(p, "old") || EQ(p, "o") || EQ(p, "EXE")
X      || EQ(p, "OBJ")))
X	return(FALSE);
X
X    /* Want .cshrc, .newsrc, etc.? */
X    if (*s == '.' && isalpha(s[1]))
X	return(DoDOTFILES);
X
X    /* RCS or SCCS file or directory? */
X    if (EQ(s, "RCS")
X     || ((p = RDX(s, ',')) && *++p == 'v' && *++p == '\0')
X     || EQ(s, "SCCS") || (s[0] == 's' && s[1] == '.'))
X	return(DoRCS);
X
X    /* Mlisp, m4 (yes to .m? but no to .mo)? */
X    /* really, no to .mo but yes to names matching the regexp ".m?" */
X    if ((p = d) && *++p == 'm' && p[2] == '\0')
X	return(*++p != 'o');
X
X    /* Gnu Emacs elisp (yes to .el*, but no to .elc)? */
X    if ((p = d) && *++p == 'e' && *++p == 'l')
X	return(*++p != 'c' || *++p);
X
X    /* C source (*.[ch])? */
X    if ((p = d) && (*++p == 'c' || *p == 'h') && *++p == '\0')
X
X    /* Manpage (*.[1234567890] or *.man)? */
X    if ((p = d) && isdigit(*p))
X	return(TRUE);
X    if ((p = d) && *++p == 'm' && *++p == 'a' && *++p == 'n')
X	return(TRUE);
X
X    /* Make control file? */
X    if ((*s == 'M' || *s == 'm') && EQ(s + 1, "akefile"))
X	return(TRUE);
X
X    /* Convert to lowercase, and see if it's a README or MANIFEST. */
X    for (p = strcpy(buff, s); *p; p++)
X	if (isascii(*p) && isupper(*p))
X	    *p = tolower(*p);
X    if (EQ(buff, "readme") || EQ(buff, "read_me") || EQ(buff, "read-me")
X     || EQ(buff, "manifest"))
X	return(TRUE);
X
X    /* Larry Wall-style template file (*.SH)? */
X    if ((p = d) && *++p == 'S' && *++p == 'H')
X	return(TRUE);
X
X    /* If we have a default, give it back. */
X    if (Default)
X	return(Default == 'y');
X
X#ifdef	CAN_POPEN
X    /* See what file(1) has to say; if it says executable, punt. */
X    (void)sprintf(buff, "exec file '%s'", Name);
X    if (F = popen(buff, "r")) {
X	(void)fgets(buff, sizeof buff, F);
X	(void)pclose(F);
X	for (p = buff; p = IDX(p, 'e'); p++)
X	    if (PREFIX(p, "executable"))
X		return(FALSE);
X	(void)fputs(buff, stderr);
X    }
X#endif	/* CAN_POPEN */
X
X    /* Add it? */
X    while (TRUE) {
X	if (DEVTTY == NULL)
X	    DEVTTY = fopen(THE_TTY, "r");
X	Fprintf(stderr, "Add this one (y or n)[y]?  ");
X	(void)fflush(stderr);
X	if (fgets(buff, sizeof buff, DEVTTY) == NULL
X	 || buff[0] == '\n' || buff[0] == 'y' || buff[0] == 'Y')
X	    break;
X	if (buff[0] == 'n' || buff[0] == 'N')
X	    return(FALSE);
X	if (buff[0] == '!' )
X	    (void)system(&buff[1]);
X	Fprintf(stderr, "--------------------\n%s:  ", Name);
X	clearerr(DEVTTY);
X    }
X    return(TRUE);
X}
X
X
X/*
X**  Quick and dirty recursive routine to walk down directory tree.
X**  Could be made more general, but why bother?
X*/
static void
Process(p, level)
X    REGISTER char	 *p;
X    REGISTER int	  level;
X{
X    REGISTER char	 *q;
X    DIR			 *Dp;
X    DIRENTRY		 *E;
X    char		  buff[BUFSIZ];
X
X#ifdef	MSDOS
X    strlwr(p);
X#endif	/* MSDOS */
X
X    if (!GetStat(p))
X	Fprintf(stderr, "Can't walk down %s, %s.\n", Ermsg(errno));
X    else {
X	/* Skip leading ./ which find(1), e.g., likes to put out. */
X	if (p[0] == '.' && p[1] == '/')
X	    p += 2;
X
X	if (Wanted(p))
X	    Fprintf(Ftype(p) == F_FILE ? Ffile : Dfile, "%s\n", p);
X	else if (Verbose)
X	    Fprintf(Ftype(p) == F_FILE ? Ffile : Dfile, "PUNTED %s\n", p);
X
X	if (Ftype(p) == F_DIR)
X	    if (++level == MAX_LEVELS)
X		Fprintf(stderr, "Won't walk down %s -- more than %d levels.\n",
X			p, level);
X	    else if (Dp = opendir(p)) {
X		q = buff + strlen(strcpy(buff, p));
X		for (*q++ = '/'; E = readdir(Dp); )
X		    if (!EQ(E->d_name, ".") && !EQ(E->d_name, "..")) {
X			(void)strcpy(q, E->d_name);
X			Process(buff, level);
X		    }
X		(void)closedir(Dp);
X	    }
X	    else
X		Fprintf(stderr, "Can't open directory %s, %s.\n",
X			p, Ermsg(errno));
X    }
X}
X
X
main(ac, av)
X    REGISTER int	 ac;
X    REGISTER char	*av[];
X{
X    REGISTER char	*p;
X    REGISTER int	 i;
X    REGISTER int	 Oops;
X    char		 buff[BUFSIZ];
X
X    /* Parse JCL. */
X    for (Oops = 0; (i = getopt(ac, av, ".d:o:RSv")) != EOF; )
X	switch (i) {
X	default:
X	    Oops++;
X	    break;
X	case '.':
X	    DoDOTFILES++;
X	    break;
X	case 'd':
X	    switch (optarg[0]) {
X	    default:
X		Oops++;
X		break;
X	    case 'y':
X	    case 'Y':
X		Default = 'y';
X		break;
X	    case 'n':
X	    case 'N':
X		Default = 'n';
X		break;
X	    }
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	    }
X	case 'R':
X	case 'S':
X	    DoRCS++;
X	    break;
X	case 'v':
X	    Verbose++;
X	    break;
X	}
X    if (Oops) {
X	Fprintf(stderr, "Usage: findsrc [-d{yn}] [-.] [-{RS}] [-v] files...\n");
X	exit(1);
X    }
X    av += optind;
X
X    /* Set signal catcher, open temp files. */
X    SetSigs(TRUE, Catch);
X    Dname = mktemp("/tmp/findDXXXXXX");
X    Fname = mktemp("/tmp/findFXXXXXX");
X    Dfile = fopen(Dname, "w");
X    Ffile = fopen(Fname, "w");
X
X    /* Read list of files, determine their status. */
X    if (*av)
X	for (DEVTTY = stdin; *av; av++)
X	    Process(*av, 0);
X    else
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	    Process(buff, 0);
X	}
X
X    /* First print directories. */
X    if (freopen(Dname, "r", Dfile)) {
X	while (fgets(buff, sizeof buff, Dfile))
X	    (void)fputs(buff, stdout);
X	(void)fclose(Dfile);
X    }
X
X    /* Now print regular files. */
X    if (freopen(Fname, "r", Ffile)) {
X	while (fgets(buff, sizeof buff, Ffile))
X	    (void)fputs(buff, stdout);
X	(void)fclose(Ffile);
X    }
X
X    /* That's all she wrote. */
X    (void)unlink(Dname);
X    (void)unlink(Fname);
X    exit(0);
X}
END_OF_FILE
if test 7195 -ne `wc -c <'findsrc.c'`; then
    echo shar: \"'findsrc.c'\" unpacked with wrong size!
fi
# end of 'findsrc.c'
fi
if test -f 'makekit.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'makekit.c'\"
else
echo shar: Extracting \"'makekit.c'\" \(10488 characters\)
sed "s/^X//" >'makekit.c' <<'END_OF_FILE'
X/*
X**  MAKEKIT
X**  Split up source files into reasonably-sized shar lists.
X*/
X#include "shar.h"
X#ifdef	RCSID
static char RCS[] =
X	"$Header: makekit.c,v 2.0 88/05/27 13:27:31 rsalz Exp $";
X#endif	/* RCSID */
X
X
X/*
X**  Our block of information about the files we're doing.
X*/
typedef 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*/
typedef struct _archive {
X    int		 Count;			/* Number of files		*/
X    long	 Asize;			/* Bytes used by archive	*/
X} ARCHIVE;
X
X
X/*
X**  Format strings; these are strict K&R so you shouldn't have to change them.
X*/
X#define FORMAT1		" %-25s %2d\t%s\n"
X#define FORMAT2		"%s%2.2d"
X#ifdef	FMT02d
X#undef FORMAT2
X#define FORMAT2		"%s%02.2d"	/* I spoke too soon...		*/
X#endif	/* FMT02d */
X
X
X/*
X**  Global variables.
X*/
char	*InName;			/* File with list to pack	*/
char	*OutName;			/* Where our output goes	*/
char	*SharName = "Part";		/* Prefix for name of each shar	*/
char	*Trailer;			/* Text for shar to pack in	*/
char	*TEMP;				/* Temporary manifest file	*/
X#ifdef	MSDOS
char	*FLST;				/* File with list for shar	*/
X#endif	MSDOS
int	 ArkCount = 20;			/* Max number of archives	*/
int	 ExcludeIt;			/* Leave out the output file?	*/
int	 Header;			/* Lines of prolog in input	*/
int	 Preserve;			/* Preserve order for Manifest?	*/
int	 Working = TRUE;		/* Call shar when done?		*/
long	 Size = 55000;			/* Largest legal archive size	*/
X
X
X/*
X**  Sorting predicate to put README or MANIFEST first, then directories,
X**  then larger files, then smaller files, which is how we want to pack
X**  stuff in archives.
X*/
static int
SizeP(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 (EQn(t1->Name, "README", 6) || (OutName && EQ(t1->Name, OutName)))
X	return(-1);
X    if (EQn(t2->Name, "README", 6) || (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, which is how
X**  we write the Manifest file.
X*/
static int
NameP(t1, t2)
X    BLOCK	*t1;
X    BLOCK	*t2;
X{
X    int		 i;
X
X    return((i = *t1->Name - *t2->Name) ? i : strcmp(t1->Name, t2->Name));
X}
X
X
X/*
X**  Skip whitespace.
X*/
static char *
Skip(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*/
static sigret_t
Catch(s)
X    int		 s;
X{
X    int		 e;
X
X    e = errno;
X    if (TEMP)
X	(void)unlink(TEMP);
X#ifdef	MSDOS
X    if (FLST)
X	(void)unlink(FLST);
X#endif	/* MSDOS */
X    Fprintf(stderr, "Got signal %d, %s.\n", s, Ermsg(e));
X    exit(1);
X}
X
X
main(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	 Value;
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    char		 EndArkNum[20];
X    char		 CurArkNum[20];
X
X    /* Collect input. */
X    Value = FALSE;
X    Notkits = FALSE;
X    while ((i = getopt(ac, av, "1eh:i:k:n:mo:p:s:t:x")) != EOF)
X	switch (i) {
X	default:
X	    Fprintf(stderr,
X"usage: makekit -e -s# [-m | -iMANIFEST -oMANIFEST -h2] -nName -tText files..."
X		    );
X	    exit(1);
X	case '1':
X	    Notkits = 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 = 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    /* Write the file list to a temp file. */
X    TEMP = mktemp("/tmp/maniXXXXXX");
X    F = fopen(TEMP, "w");
X    SetSigs(TRUE, 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	}
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	}
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    Table = NEW(BLOCK, lines);
X    for (t = Table, Value = FALSE, 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 truncated:\n%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	for (p = Skip(p); *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) * 3 + 200;
X	if (t->Type == F_FILE) {
X	    lsize = Fsize(t->Name);
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	}
X
X	/* Is our ouput file there? */
X	if (!Value && OutName && EQ(OutName, t->Name))
X	    Value = TRUE;
X
X	/* All done -- advance to next entry. */
X	t++;
X    }
X    (void)fclose(F);
X    (void)unlink(TEMP);
X    SetSigs(S_RESET, (sigret_t (*)())NULL);
X
X    /* Add our output file? */
X    if (!ExcludeIt && !Value && OutName) {
X	t->Name = OutName;
X	t->Text = "This shipping list";
X	t->Type = F_FILE;
X	t->Bsize = lines * 60;
X	t++;
X    }
X
X    /* Sort by size, get archive space. */
X    lines = t - Table;
X    TabEnd = &Table[lines];
X    if (!Preserve)
X	qsort((char *)Table, lines, sizeof Table[0], SizeP);
X    Ark = NEW(ARCHIVE, ArkCount);
X    ArkEnd = &Ark[ArkCount];
X
X    /* Loop through the pieces, and put everyone into an archive. */
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, "'%s' doesn't fit -- need more then %d archives.\n",
X		    t->Name, ArkCount);
X	    exit(1);
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    /* Open the output file. */
X    if (OutName == NULL)
X	F = stdout;
X    else {
X	if (GetStat(OutName)) {
X	    /* Handle /foo/bar/VeryLongFileName.BAK for non-BSD sites. */
X	    (void)sprintf(buff, "%s.BAK", OutName);
X	    p = (p = RDX(buff, '/')) ? p + 1 : buff;
X	    if (strlen(p) > 14)
X		/* ... well, sort of handle it. */
X		(void)strcpy(&p[10], ".BAK");
X	    Fprintf(stderr, "Renaming %s to %s\n", OutName, buff);
X	    (void)unlink(buff);
X	    (void)link(OutName, buff);
X	    (void)unlink(OutName);
X	}
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	}
X    }
X
X    /* Sort the shipping list, then write it. */
X    if (!Preserve)
X	qsort((char *)Table, lines, sizeof Table[0], NameP);
X    Fprintf(F, "   File Name\t\tArchive #\tDescription\n");
X    Fprintf(F, "-----------------------------------------------------------\n");
X    for (t = Table; t < TabEnd; t++)
X	Fprintf(F, FORMAT1, 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
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. */
X    for (i = 0, k = Ark; k < ArkEnd; k++)
X	if (i < k->Count)
X	    i = k->Count;
X
X    /* Build the fixed part of the argument vector. */
X    av = NEW(char*, i + 10);
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#ifdef	MSDOS
X    av[i++] = "-i";
X    av[i++] = FLST = mktemp("/tmp/manlXXXXXX");
X#endif	/* MSDOS */
X
X    av[i++] = "-o";
X    av[i++] = buff;
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, FORMAT2, SharName, i + 1);
X#ifndef	MSDOS
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	}
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 /* MSDOS */
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	MSDOS
X    (void)unlink(FLST);
X#endif	/* MSDOS */
X    /* That's all she wrote. */
X    exit(0);
X}
END_OF_FILE
if test 10488 -ne `wc -c <'makekit.c'`; then
    echo shar: \"'makekit.c'\" unpacked with wrong size!
fi
# end of 'makekit.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'\" \(7551 characters\)
sed "s/^X//" >'shar.c' <<'END_OF_FILE'
X/*
X**  SHAR
X**  Make a shell archive of a list of files.
X*/
X#include "shar.h"
X#ifdef	RCSID
static char RCS[] =
X	"$Header: shar.c,v 2.0 88/05/27 14:10:47 rsalz Exp $";
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*/
static char	 *Prolog[] = {
X  "! /bin/sh",
X  " This is a shell archive.  Remove anything before this line, then unpack",
X  " it by saving it into a file and typing \"sh file\".  To overwrite existing",
X  " files, type \"sh file -c\".  You can also feed this as standard input via",
X  " unshar, or by typing \"sh <file\", e.g..  If this archive is complete, you",
X  " will see the following message at the end:",
X  NULL
X};
X
X
X/*
X**  Package up one file or directory.
X*/
static void
shar(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    char		 buf[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	}
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 (Bads = 0; fgets(buf, BUFSIZ, stdin); )
X	    if (buf[0]) {
X		if (buf[0] == 'X' || buf[0] == 'E' || buf[0] == 'F'
X		 || !isalpha(buf[0]))
X		    /* Protect non-alpha's, the shar pattern character, the
X		     * END_OF_FILE message, and mail "From" lines. */
X		    (void)putchar('X');
X		for (s = buf; *s; s++) {
X		    if (BADCHAR(*s))
X			Bads++;
X		    (void)putchar(*s);
X		}
X	    }
X
X	/* Tell about and control characters. */
X	Printf("END_OF_FILE\n", Name);
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*/
static char **
GetFiles(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	    for (count += MIN_FILES, 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
X
main(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    for (Oops = 0; (i = getopt(ac, av, "be:i:n:o:t:")) != EOF; )
X	switch (i) {
X	default:
X	    Oops++;
X	    break;
X	case 'b':
X	    Basename++;
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		Oops++;
X	    }
X	    break;
X	case 't':
X	    Trailer = optarg;
X	    break;
X	}
X
X    /* Rest of arguments are files. */
X    if  (Flist == NULL) {
X	av += optind;
X	if (*av == NULL) {
X	    Fprintf(stderr, "No input files\n");
X	    Oops++;
X	}
X	Flist = av;
X    }
X
X    if (Oops) {
X	Fprintf(stderr,
X		"USAGE: shar [-b] [-o:] [-i:] [-n:e:t:] file... >SHAR\n");
X	exit(1);
X    }
X
X    /* Everything readable and reasonably-named? */
X    for (Oops = 0, 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++;
X	}
X	else
X	    for (; *p; p++)
X		if (!isascii(*p)) {
X		    Fprintf(stderr, "Bad character '%c' in '%s'.\n",
X			    *p, Flist[i]);
X		    Oops++;
X		}
X    if (Oops)
X	exit(2);
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}
END_OF_FILE
if test 7551 -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'\" \(8368 characters\)
sed "s/^X//" >'unshar.c' <<'END_OF_FILE'
X/*
X**  UNSHAR
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
static char RCS[] =
X	"$Header: unshar.c,v 2.0 88/05/27 13:28:13 rsalz Exp $";
X#endif	/* RCSID */
X
X
X/*
X**  Print error message and die.
X*/
static void
Quit(text)
X    char	*text;
X{
X    int		 e;
X
X    e = errno;
X    Fprintf(stderr, "unshar:  %s", text);
X    if (e)
X	Fprintf(stderr, ", %s", Ermsg(e));
X    Fprintf(stderr, ".\n");
X    exit(1);
X}
X
X
X/*
X**  Does this look like a mail header line?
X*/
static int
IsHeader(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; *p == '-' || *p == '_' || *p == '.' || isalnum(*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*/
static int
IsSHcomment(p)
X    REGISTER char	*p;
X{
X    while (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*/
static int
Has(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;
again: 
X    while (*p) {
X	if (!isalpha(*p)) {
X	    p++;
X	    continue;
X	}
X	while (*p++ == *wd++) {
X	    if (*wd == '\0') {
X		if (!isalpha(*p)) {
X		    if (!first)
X			return(TRUE);
X		    first = FALSE;
X		    wd = wd2;
X		    goto again;
X		}
X		break;
X	    }
X	}
X	while (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*/
static 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] == '.' && isalpha(buff[1]) && isalpha(buff[2])
X		  && !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 (isascii(*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 == ':' || 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*/
static void
Unshar(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
main(ac, av)
X    REGISTER int	 ac;
X    REGISTER char	*av[];
X{
X    REGISTER FILE	*Stream;
X    REGISTER int	 i;
X    char		*p;
X    char		*HdrFile;
X    char		 cwd[BUFSIZ];
X    char		 dir[BUFSIZ];
X    char		 buff[BUFSIZ];
X    int			 Saveit;
X    int			 Forced;
X
X    /* Parse JCL. */
X    p = getenv("UNSHARDIR");
X    Saveit = DEF_SAVEIT;
X    HdrFile = NULL;
X    for (Forced = 0; (i = getopt(ac, av, "c:d:fh:ns")) != EOF; )
X	switch (i) {
X	default:
X	    Quit("Usage: unshar [-fs] [-c dir] [-h hdrfile] [input files]");
X	    /* NOTREACHED */
X	case 'c': 
X	case 'd': 
X	    p = optarg;
X	    break;
X	case 'f':
X	    Forced++;
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    av += optind;
X
X    /* Going somewhere? */
X    if (p) {
X	if (*p == '?') {
X	    /* Ask for name; go to THE_TTY if we're being piped into. */
X	    Stream = isatty(fileno(stdin)) ? stdin : fopen(THE_TTY, "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	    if (getenv("HOME") == NULL)
X		Quit("You have no $HOME?");
X	    (void)sprintf(dir, "%s/%s", getenv("HOME"), p + 1);
X	    p = dir;
X	}
X
X	/* If we're gonna move, first remember where we were. */
X	if (Cwd(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}
END_OF_FILE
if test 8368 -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 2 \(of 3\).
cp /dev/null ark2isdone
MISSING=""
for I in 1 2 3 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 3 archives.
    echo "See the README"
    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.