rs@mirror.UUCP (03/25/87)
This is the set of tools I've been using recently in mod.sources. It's designed to be portable to non-Unix systems, and includes a program that should let yuckky MS-DOS, iccky AMIGA-DOS, and similar beasties unpack shell archives. It does pretty well at doing many of the items in the mod.sources archive. Please beat on this and send me feedback, porting notes, and other brickbats; I'll publish this in mod.sources in a couple of weeks. /r$ #! /bin/sh # This is a shell archive. Remove anything before this line, # then unpack it by saving it in a file and typing "sh file". # If this archive is complete, you will see the message: # "End of archive 1 (of 2)." # Contents: MANIFEST Makefile README cwd.c findsrc.c findsrc.man # getopt.c glue.c host.c makekit.man mem.c shar.c shar.h shar.man # shell.c shell.man sysfuncs.c unshar.man # Wrapped by rs@mirror on Tue Mar 24 18:12:02 1987 PATH=/bin:/usr/bin:/usr/ucb ; export PATH echo shar: Extracting \"MANIFEST\" \(1276 characters\) if test -f MANIFEST ; then echo shar: Will not over-write existing file \"MANIFEST\" else sed "s/^X//" >MANIFEST <<'END_OF_MANIFEST' X File Name Archive # Description X----------------------------------------------------------- X MANIFEST 1 This shipping list X Makefile 1 Guess X README 1 Acknowledgements, installation notes X cwd.c 1 Routines to find current directory X findsrc.c 1 Find source files, based on filename X findsrc.man 1 Manpage for findsrc X getopt.c 1 For those that need it X glue.c 1 Glue for "safe" unshar X host.c 1 Find our machine name X makekit.c 2 Partition files into reasonable-sized kits X makekit.man 1 Manpage for makekit X mem.c 1 Memory allocator, uses calloc X parser.c 2 Interpreter for shell archives X shar.c 1 Create script to create files X shar.h 1 Header file, used by everyone X shar.man 1 Manpage for makekit X shell.c 1 Main routine for my shell interpreter X shell.man 1 Manpage for shell X sysfuncs.c 1 System-dependent routines X unshar.c 2 Strip news, notes, mail headers from shar's X unshar.man 1 Manpage for unshar END_OF_MANIFEST if test 1276 -ne `wc -c <MANIFEST`; then echo shar: \"MANIFEST\" unpacked with wrong size!? fi # end of overwriting check fi echo shar: Extracting \"Makefile\" \(2618 characters\) if test -f Makefile ; then echo shar: Will not over-write existing file \"Makefile\" else sed "s/^X//" >Makefile <<'END_OF_Makefile' X## X## SOURCE-SHIPPING TOOLS MAKEFILE X## $Header: Makefile,v 1.16 87/03/18 13:12:39 rs Exp $ X## X X## Edit appropriately. XCFLAGS = -O X X## Use these two lines if you use ranlib... XRANLIB = ranlib lib.a XAR_OBJ = $(LOBJ) X## ...or use these two if you don't. X#AR_OBJ = `lorder $(LOBJ) | tsort` X#RANLIB = @echo X X## Use this line if you have ctags (free from mod.sources)... XCTAGS = ctags -tw $(SRCS) $H X# ...or this line if you don't. X#CTAGS = @echo X X## Where executables should be put. XDESTDIR = /usr/local/bin X X## The manpage for "foo" goes in $(MANDIR)/foo.$1 XMANDIR = /usr/man/man1 X1 = 1 X#MANDIR = /usr/man/u_man/manl X#1 = 1L X X## X## END OF CONFIGURATION SECTION X## X X## Shorthands for sources, objects, etc. XH = shar.h XL = lib.a XPROGS = findsrc makekit shar unshar shell XSRCS = findsrc.c getopt.c makekit.c mem.c shar.c unshar.c XLSRC = cwd.c getopt.c host.c sysfuncs.c glue.c parser.c mem.c XLOBJ = cwd.o getopt.o host.o sysfuncs.o glue.o parser.o mem.o XMAN = findsrc.$1 makekit.$1 shar.$1 unshar.$1 shell.$1 X X Xall: programs tags X Xinstall: newprog newman X X X## You might want to change these actions... Xnewprog: $(PROGS) X cd $(DESTDIR) ; rm -f $(PROGS) X cp $(PROGS) $(DESTDIR) X cd $(DESTDIR) ; strip $(PROGS) ; chmod 755 $(PROGS) X# cd $(DESTDIR) ; /etc/chown root $(PROGS) X Xnewman: $(MAN) X cd $(MANDIR) ; rm -f $(MAN) X cp $(MAN) $(MANDIR) X X X## CREATING MANPAGES X.SUFFIXES: .$1 .man X.man.$1: X cp $< $@ X chmod 444 $@ X X## PROGRAM TARGETS Xprograms: $(PROGS) X Xfindsrc: findsrc.o $H $L X $(CC) $(CFLAGS) -o findsrc findsrc.o $L X Xmakekit: makekit.o $H $L X $(CC) $(CFLAGS) -o makekit makekit.o $L X Xshar: shar.o $H $L X $(CC) $(CFLAGS) -o shar shar.o $L X Xshell: shell.o $H $L X $(CC) $(CFLAGS) -o shell shell.o $L X X## Special -- gotta build these from the .c files... Xunshar: unshar.c $H $L X $(CC) $(CFLAGS) -o unshar unshar.c $L Xunshar.safe: unshar.c $H $L X $(CC) $(CFLAGS) -DUSE_MY_SHELL -o unshar unshar.c $L X X X## UTILITY TARGETS Xlib.a: $(LOBJ) X @rm -f lib.a X ar r lib.a $(AR_OBJ) X $(RANLIB) X$(LOBJ): $H X Xtags: $(SRCS) $H X $(CTAGS) X Xclean: X rm -f *.[oa] *.$1 *.BAK $(PROGS) unshar.safe X rm -f lint lib.ln tags core foo a.out X X X## LINT (probably only good for 4.[23]BSD Unices) X#LINTF = -p -ahb XLINTF = -ahb Xlint: programs lib.ln X exec lint $(LINTF)u >lint $(LSRC) X exec lint $(LINTF) >>lint findsrc.c lib.ln X exec lint $(LINTF) >>lint makekit.c lib.ln X exec lint $(LINTF) >>lint shar.c lib.ln X exec lint $(LINTF) >>lint shell.c lib.ln X exec lint $(LINTF) >>lint unshar.c lib.ln X# exec lint $(LINTF) -DUSE_MY_SHELL >>lint unshar.c lib.ln X X Xlib.ln: $(LSRC) X lint -CX $(LSRC) X mv llib-lX.ln lib.ln END_OF_Makefile if test 2618 -ne `wc -c <Makefile`; then echo shar: \"Makefile\" unpacked with wrong size!? fi # end of overwriting check fi echo shar: Extracting \"README\" \(4350 characters\) if test -f README ; then echo shar: Will not over-write existing file \"README\" else sed "s/^X//" >README <<'END_OF_README' XThis set of tools is designed to make it easier to ship sources around. XI wrote them because I do a lot of that as moderator of mod.sources, and Xnothing else did the job for me. This set isn't perfect, but's very Xclose. Included are a program to find source files, a program to partition Xthem into reasonable sizes, a program to make shell archives out of Xthem, a program to strip mail, news, and notes headers from archives Xbefore feeding them to a shell, and a program to simulate /bin/sh for Xnon-Unix system. X XThe sources in this distribution are being released into the public Xdomain; do what you want, but let your conscience be your guide. XIf you somehow enhance this package, please send it on to me so that Xothers can benefit. X XI'll try to answer any questions or problems that come up -- send me Xelectronic mail. X XTo install, edit the Makefile and shar.h as necessary (see detailed list Xof parameters, below), then run make; doing make install will put the Xmanpages and executables where you told it to. I don't think "make lint" Xwill work anywhere other than a 4.[23]BSD system. X XIf you aren't running on Unix, then you will have to write replacements Xfor the functions in sysfuncs.c; everything else should be OK. If you Xdon't have a Unix-like make available, you will have to write a command Xscript or otherwise work out something with your compiler. X XI freely stole ideas from a number of people who have been good enough to Xput their stuff out on Usenet. Particular thanks to Gary Perlman and Larry XWall for giving me something nice to reverse-engineer, and Michael Mauldin Xfor unshar. It may have been a mistake to take people's names out of the Xcode; if I upset anyone by doing this, please let me know. X XThere are a couple of things that could still be done, but I'm tired of Xworking on this, and I don't need them often enough to want to come up Xwith general solutions. Specifically: X Automatically splitting up large text files; X Automatically invoking uuencode when sending binaries; and X Perhaps arranging for the archive to invoke uudecode. X XEnjoy! X Richard $alz X Mirror Systems X 2067 Massachusetts Avenue X Cambridge, MA 02140 X {ihnp4, harvard!wjh12, cca, cbosgd, seismo}!mirror!rs X rs%mirror.UUCP@cca X rs@mirror.TMC.COM X XMy, my, my, aren't we anal: X $Header: README,v 1.7 87/03/24 18:09:04 rs Exp $ X XCOMPILATION PARAMETERS X---------------------- X[The Makefile is ... er ... self-documented.] X XCAN_POPEN X If #define'd, then findsrc will do a popen() to file(1) to see if X the file is executable, etc. X XDEF_SAVEIT X This is used by unshar, and sets whether or not to save the headers X by default. X XGETWD GETCWD PWDPOPEN PWDGETENV X These are used in cwd.c to determine how to find out the current X directory, which unshar sometimes needs to know. X XGETHOSTNAME HOST UNAME UUNAME WHOAMI X There are used in host.c to determine how to find out the host name, X which shar needs to know. X XIDX, RDX X Some Unices call index/rindex strchr/strrchr. X XIN_SYS_DIR IN_SYS_NDIR IN_NDIR X The findsrc program uses the BSD-style directory-reading routines. X These routines, and the structures they use, are typically defined in X one of <sys/dir.h> <sys/ndir.h> or "ndir.h", respectively. X XNEED_MKDIR X In sysfuncs, this includes a subroutine that calls system() to do a X mkdir command; otherwise mkdir() must be in your C library. X XNEED_GETOPT X Define this if getopt is not in your C library. X XSYS_WAIT X Define this if you have <sys/wait.h> and the vfork system call. X XTHE_TTY X This should be a "device" to open so we can read input from the X user's terminal if standard input is, e.g., coming from a file. X Unix systems should use the default "/dev/tty"; MS-DOS and would X be "CON:" I guess? X XUSE_MY_SHELL X Used in unshar. If #define'd, then the archive is passed to my shell X interpreter sub-routine. The default is to do a popen() to /bin/sh. X XUSER_ENV X The name of the environment variable that holds the user's name, X which shar needs to know. X Xtypedef int *align_t X This is a worst-case alignment, to get lint to shut up about malloc'd X space. For ANSI C, e.g., this should be "typedef void *align_t". X Xtypedef long time_t X What datatype is needed to hold a time value. X Xtypedef long off_t X What datatype is needed to hold a file size. END_OF_README if test 4350 -ne `wc -c <README`; then echo shar: \"README\" unpacked with wrong size!? fi # end of overwriting check fi echo shar: Extracting \"cwd.c\" \(941 characters\) if test -f cwd.c ; then echo shar: Will not over-write existing file \"cwd.c\" else sed "s/^X//" >cwd.c <<'END_OF_cwd.c' X/* X** Return current working directory. Something for everyone. X*/ X/* LINTLIBRARY */ X#include "shar.h" XRCS("$Header: cwd.c,v 1.3 87/03/02 11:03:21 rs Exp $") X X X#ifdef PWDGETENV X/* ARGSUSED */ Xchar * XCwd(p, i) X char *p; X int i; X{ X char *q; X X return((q = getenv(PWDGETENV)) ? strcpy(p, q) : NULL); X} X#endif /* PWDGETENV */ X X X#ifdef GETWD X/* ARGSUSED1 */ Xchar * XCwd(p, size) X char *p; X int size; X{ X return(getwd(p) ? p : NULL); X} X#endif /* GETWD */ X X X#ifdef GETCWD Xchar * XCwd(p, size) X char *p; X int size; X{ X return(getcwd(p, size) ? p : NULL); X} X#endif /* GETCWD */ X X X#ifdef PWDPOPEN Xextern FILE *popen(); Xchar * XCwd(p, size) X char *p; X int size; X{ X FILE *F; X int i; X X if (F = popen("exec pwd", "r")) { X if (fgets(p, size, F) && p[i = strlen(p) - 1] == '\n') { X p[i] = '\0'; X (void)fclose(F); X return(p); X } X (void)fclose(F); X } X return(NULL); X} X#endif /* PWDPOPEN */ END_OF_cwd.c if test 941 -ne `wc -c <cwd.c`; then echo shar: \"cwd.c\" unpacked with wrong size!? fi # end of overwriting check fi echo shar: Extracting \"findsrc.c\" \(6863 characters\) if test -f findsrc.c ; then echo shar: Will not over-write existing file \"findsrc.c\" else sed "s/^X//" >findsrc.c <<'END_OF_findsrc.c' X/* X** FINDSRC X** Walk directories, trying to find source files. X** X** Options: X** -d [yn] Set default answer to yes or no X** -. Do .cshrc, .profile, etc. X** -o file Redirect stdout X** -R Do RCS and SCCS files and directories X** -S Do SCCS and RCS files and directories X** -v Verbose, include those that were rejected X*/ X#include "shar.h" XRCS("$Header: findsrc.c,v 1.18 87/03/18 14:03:03 rs Exp $") X X X/* X** How many levels to walk down? X*/ X#define MAX_LEVELS 6 X X X/* X** Global variables. X*/ Xint DoDOTFILES; /* Do .newsrc and friends? */ Xint DoRCS; /* Do RCS and SCCS files? */ Xint Default; /* Default answer from user */ Xint Verbose; /* List rejected files, too? */ Xchar Dname[] = "/tmp/findDXXXXXX"; /* Filename of directory list */ Xchar Fname[] = "/tmp/findFXXXXXX"; /* 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*/ Xstatic XCatch(s) X int s; X{ X int e; X X e = errno; X (void)unlink(Dname); 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*/ Xstatic int XWanted(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"))) 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 (yes to .ml, no to .mo)? */ X if ((p = d) && *++p == 'm' && p[2] == '\0') X return(*++p = 'l'); X X /* C source or manpage? */ X if ((p = d) && (*++p == 'c' || *p == 'h' || isdigit(*p)) X && *++p == '\0') 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 (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 /* 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*/ Xstatic void XProcess(p, level) X register char *p; X register int level; X{ X register char *q; X DIR *Dp; X struct direct *E; X char buff[BUFSIZ]; 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 Xmain(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 case 'y': X case 'n': X break; X case 'Y': X Default = 'y'; X break; 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 Dfile = fopen(mktemp(Dname), "w"); X Ffile = fopen(mktemp(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_findsrc.c if test 6863 -ne `wc -c <findsrc.c`; then echo shar: \"findsrc.c\" unpacked with wrong size!? fi # end of overwriting check fi echo shar: Extracting \"findsrc.man\" \(1865 characters\) if test -f findsrc.man ; then echo shar: Will not over-write existing file \"findsrc.man\" else sed "s/^X//" >findsrc.man <<'END_OF_findsrc.man' X.TH FINDSRC 1 LOCAL X.\" $Header: findsrc.man,v 1.2 87/03/06 14:02:49 rs Exp $ X.SH NAME Xfindsrc \- walk directories, trying to find source files X.SH SYNOPSIS X.B findsrc X[ X.B \-. X] [ X.BI \-d y_or_n X] [ X.BI \-o output_file X] [ X.B \-R X] [ X.B \-S X] [ X.B \-v X] [ file... ] X.SH DESCRIPTION X.I Findsrc Xrecursively examines all directories and files specified on the command Xline, and determines, based on the file name, whether the file contains Xsource code or not. XAll directories are listed first, followed by all regular files, Xwith one item per line. X.PP XIf X.I findsrc Xis unable to make a decision, it invokes the X.IR file (1) Xcommand, and prompts the user for a decision. XIn reply to the prompt, type the letter ``y'' or ``n'' (either case); XRETURN means yes. XIf the reply starts with an exclamation point, the rest of the line Xis passed off to a sub-shell and the question is repeated. XThe ``\-d'' option may be used with an argument of ``y'' or ``n'' Xto by-pass the interaction, and provide a default answer. X.PP XThe ``\-o'' option may be used to specify an output filename. XThis is designed to prevent confusion if a command like the following Xis executed: X.RS Xfindsrc . * >LIST X.RE X.PP XBy default, X.I findsrc Xignores files whose names begin with a period, like ``.newsrc'' or X``.tags''; such files may be included by using the ``\-.'' option. X.I Findsrc Xalso normally ignores X.SM RCS Xand X.SM SCCS Xfiles and directories; using either the ``\-R'' or ``\-S'' option causes Xboth to be included. X.PP X.I Findsrc Xnormally lists only the names of those files that have been selected. XIf the ``\-v'' option is used, rejected files are also listed preceeded Xby the word ``PUNTED.'' X.PP XIf no files are specified on the command line, the program operates as Xa filter, reading a list of files and directories from the standard Xinput, one per line. X.SH "SEE ALSO" Xmakekit(1L). END_OF_findsrc.man if test 1865 -ne `wc -c <findsrc.man`; then echo shar: \"findsrc.man\" unpacked with wrong size!? fi # end of overwriting check fi echo shar: Extracting \"getopt.c\" \(1422 characters\) if test -f getopt.c ; then echo shar: Will not over-write existing file \"getopt.c\" else sed "s/^X//" >getopt.c <<'END_OF_getopt.c' X/* X** Return options and their values from the command line. X** X** This comes from the AT&T public-domain getopt published in mod.sources. X*/ X/* LINTLIBRARY */ X#include "shar.h" XRCS("$Header: getopt.c,v 1.4 87/03/02 11:03:24 rs Exp $") X X#ifdef NEED_GETOPT X X X#define TYPE int X X#define ERR(s, c) if(opterr){\ X char errbuf[2];\ X errbuf[0] = c; errbuf[1] = '\n';\ X (void) write(2, argv[0], (TYPE)strlen(argv[0]));\ X (void) write(2, s, (TYPE)strlen(s));\ X (void) write(2, errbuf, 2);} X Xextern int strcmp(); X Xint opterr = 1; Xint optind = 1; Xint optopt; Xchar *optarg; X Xint Xgetopt(argc, argv, opts) Xint argc; Xchar **argv, *opts; X{ X static int sp = 1; X register int c; X register char *cp; X X if(sp == 1) X if(optind >= argc || X argv[optind][0] != '-' || argv[optind][1] == '\0') X return(EOF); X else if(strcmp(argv[optind], "--") == NULL) { X optind++; X X } X optopt = c = argv[optind][sp]; X if(c == ':' || (cp=IDX(opts, c)) == NULL) { X ERR(": illegal option -- ", c); X if(argv[optind][++sp] == '\0') { X optind++; X sp = 1; X } X return('?'); X } X if(*++cp == ':') { X if(argv[optind][sp+1] != '\0') X optarg = &argv[optind++][sp+1]; X else if(++optind >= argc) { X ERR(": option requires an argument -- ", c); X sp = 1; X return('?'); X } else X optarg = argv[optind++]; X sp = 1; X } else { X if(argv[optind][++sp] == '\0') { X sp = 1; X optind++; X } X optarg = NULL; X } X return(c); X} X X#endif /* NEED_GETOPT */ END_OF_getopt.c if test 1422 -ne `wc -c <getopt.c`; then echo shar: \"getopt.c\" unpacked with wrong size!? fi # end of overwriting check fi echo shar: Extracting \"glue.c\" \(897 characters\) if test -f glue.c ; then echo shar: Will not over-write existing file \"glue.c\" else sed "s/^X//" >glue.c <<'END_OF_glue.c' X/* X** Subroutine to call the shell archive parser. This is "glue" X** between unshar and the parser proper. X*/ X#include "shar.h" XRCS("$Header: glue.c,v 1.1 87/03/09 16:24:17 rs Exp $") X X X/* X** Copy the input to a temporary file, then call the shell parser. X*/ XBinSh(Name, Stream, Pushback) X char *Name; X register FILE *Stream; X char *Pushback; X{ X static char TEMP[] = "/tmp/shellXXXXXX"; X register FILE *F; X char buff[BUFSIZ]; X char *vec[MAX_WORDS]; X X Interactive = Name == NULL; X File = mktemp(TEMP); X F = fopen(File, "w"); X (void)fputs(Pushback, F); X while (fgets(buff, sizeof buff, Stream)) X (void)fputs(buff, F); X (void)fclose(Stream); X X if ((Input = fopen(TEMP, "r")) == NULL) X fprintf(stderr, "Can't open %s, %s!?\n", TEMP, Ermsg(errno)); X else X while (GetLine(TRUE)) X if (Argify(vec)) X (void)Exec(vec); X X (void)unlink(TEMP); X} END_OF_glue.c if test 897 -ne `wc -c <glue.c`; then echo shar: \"glue.c\" unpacked with wrong size!? fi # end of overwriting check fi echo shar: Extracting \"host.c\" \(1645 characters\) if test -f host.c ; then echo shar: Will not over-write existing file \"host.c\" else sed "s/^X//" >host.c <<'END_OF_host.c' X/* X** Return name of this host. Something for everyone. X*/ X/* LINTLIBRARY */ X#include "shar.h" XRCS("$Header: host.c,v 1.5 87/03/02 11:03:26 rs Exp $") X X X#ifdef HOST Xchar * XHost() X{ X return(HOST); X} X#endif /* HOST */ X X X#ifdef GETHOSTNAME Xchar * XHost() X{ X static char buff[64]; X X return(gethostname(buff, sizeof buff) < 0 ? "SITE" : buff); X} X#endif /* GETHOSTNAME */ X X X#ifdef UNAME X#include <sys/utsname.h> Xchar * XHost() X{ X static struct utsname U; X X return(uname(&U) < 0 ? "SITE" : U.nodename); X} X#endif /* UNAME */ X X X#ifdef UUNAME Xextern FILE *popen(); Xchar * XHost() X{ X static char buff[50]; X char *p; X X if (F = popen("exec uuname -l", "r")) { X if (fgets(buff, sizeof buff, F) == buff && (p = IDX(name, '\n'))) { X (void)pclose(F); X *p = '\0'; X return(buff); X } X (void)pclose(F); X } X return("SITE"); X} X#endif /* UUNAME */ X X X#ifdef WHOAMI Xchar * XHost() X{ X static char name[64]; X register FILE *F; X register char *p; X char buff[100]; X X /* Try /etc/whoami; look for a single well-formed line. */ X if (F = fopen("/etc/whoami", "r")) { X if (fgets(name, sizeof name, F) && (p = IDX(name, '\n'))) X (void)fclose(F); X *p = '\0'; X return(name); X } X (void)fclose(F); X } X X /* Try /usr/include/whoami.h; look for #define sysname "foo" somewhere. */ X if (F = fopen("/usr/include/whoami.h", "r")) { X while (fgets(buff, sizeof buff, F)) X /* I don't like sscanf, nor do I trust it. Sigh. */ X if (sscanf(buff, "#define sysname \"%[^\"]\"", name) == 1) { X (void)fclose(F); X return(name); X } X (void)fclose(F); X } X return("SITE"); X} X#endif /* WHOAMI */ END_OF_host.c if test 1645 -ne `wc -c <host.c`; then echo shar: \"host.c\" unpacked with wrong size!? fi # end of overwriting check fi echo shar: Extracting \"makekit.man\" \(3676 characters\) if test -f makekit.man ; then echo shar: Will not over-write existing file \"makekit.man\" else sed "s/^X//" >makekit.man <<'END_OF_makekit.man' X.TH MAKEKIT 1 LOCAL X.\" $Header: makekit.man,v 1.7 87/03/13 12:56:33 rs Exp $ X.SH NAME Xmakekit \- split files up into shell archive packages X.SH SYNOPSIS X.B makekit X[ X.B -e X] [ X.BI -h # X] [ X.BI -i name X] [ X.BI -k # X] [ X.B -m X] [ X.BI -n name X] [ X.BI -o name X] [ X.B -p X] [ X.BI -s #[k] X] [ X.BI -t text X] [ Xfile... X] X.SH DESCRIPTION X.I Makekit Xreads a list of files and directories, determines their sizes, Xand parcels them up into a series of shell archives such that all the Xarchives are of reasonable size. XIt then invokes X.IR shar (1L) Xto actually create the archives. X.PP XBy default, no archive will be larger than about 50,000 bytes; this may be Xchanged by using the ``\-s'' option. XIf the number given with the ``\-s'' option ends with the letter ``k'' Xthen the size is multiplied by 1024, otherwise it is taken to be the Xdesired maximum size, in bytes. XEach archive will have a name that looks like X.IR Part nn, Xwhere ``nn'' represents the two-digit sequence number (with leading zero Xif needed). XThe leader part of the archive name may be changed with the ``\-n'' option. XThe ``\-n'' is also useful when write permission to the directory being Xarchive is defined; e.g., ``\-n/tmp/KERNEL.'' X.PP X.I Makekit Xreads its list of files on the command line, or standard input Xif none are given. XIt is also possible to specify an input filename with the ``\-i'' option. XThe input should contain a list of files, one to a line, to separate. XIn addition, if each input line looks like this: X.RS Xfilename\ \ \ whitespaces\ \ \ optional-digits\ \ \ whitespaces\ \ \ text X.RE Xthen X.I makekit Xwill ignore the spaces and digits, but remember the text associated with Xeach file, and output it with the filename when generating the ``shipping Xmanifest.'' XFurther, the ``\-h'' option may be given to have the program skip the Xindicated number of lines in the input; this option is provided so that X.I makekit Xcan more easily re-parse the manifests it has generated. X.PP XThe generated manifest will be sent to the standard output. XAn alternate output file may be given by using the ``\-o'' option; if Xthe output file exists, X.I makekit Xwill try to rename it with an extension of X.IR \&.BAK \&. XIf the ``\-o'' option is used, X.I makekit Xwill add that name to the list of files to be archived; the ``\-e'' Xoption may be given to exclude the manifest from the list. X.PP XThe ``\-m'' option is the same as given the options, X\&``-iMANIFEST -oMANIFEST -h2.'' X.PP XAfter partitioning the files and directories, X.I makekit Xcalls X.I shar Xwith the proper options to generate archives in a series. XEach resultant archive will, when executed, check to see if all the parts Xare present. XBy using the ``\-t'' option, you can specify a line of starting instructions Xto display to the recipient when all pieces have been unpacked. XSee X.I shar Xfor more information on multi-part archives. XIf the ``\-x'' option is used, X.I shar Xis not called, but the manifest is still created. X.PP X.I Makekit Xnormally reorders its input so that the archives are as ``dense'' as Xpossible, with the exception that directories are given priority over Xfiles, and a file named X.I README Xis the first of all. XThe manifest is also sorted in alphabetical order; this makes it easy Xto locate ``missing'' files when the distribution is a large one. XThe ``\-p'' option may be used to override both sortings, however, Xand preserve the original order of the input list in generating Xboth the manifest, and the shell archives. X.SH NOTES X.I Makekit Xtries to partition the files so that all directories are in the first archive. XThis usually means the first archive must be the first one to be unpacked. X.SH "SEE ALSO" Xfindsrc(1L), shar(1L) END_OF_makekit.man if test 3676 -ne `wc -c <makekit.man`; then echo shar: \"makekit.man\" unpacked with wrong size!? fi # end of overwriting check fi echo shar: Extracting \"mem.c\" \(460 characters\) if test -f mem.c ; then echo shar: Will not over-write existing file \"mem.c\" else sed "s/^X//" >mem.c <<'END_OF_mem.c' X/* X** Get some memory or die trying. X*/ X/* LINTLIBRARY */ X#include "shar.h" XRCS("$Header: mem.c,v 1.6 87/03/04 14:51:20 rs Exp $") X X Xalign_t Xgetmem(i, j) X unsigned int i; X unsigned int j; X{ X extern char *calloc(); X align_t p; X X if ((p = (align_t)calloc(i, j)) == NULL) { X /* Print the unsigned's as int's so ridiculous values show up. */ X fprintf(stderr, "Can't Calloc(%d,%d), %s.\n", i, j, Ermsg(errno)); X exit(1); X } X return(p); X} END_OF_mem.c if test 460 -ne `wc -c <mem.c`; then echo shar: \"mem.c\" unpacked with wrong size!? fi # end of overwriting check fi echo shar: Extracting \"shar.c\" \(6386 characters\) if test -f shar.c ; then echo shar: Will not over-write existing file \"shar.c\" else sed "s/^X//" >shar.c <<'END_OF_shar.c' X/* X** SHAR X** Make a shell archive of a list of files. X** X** Options: X** -b Strip leading directories off names before packing X** -n # Set number of this archive in a series X** -o foo Output goes to file foo, not stdout X** -e # Set ending archive number of series X** -t "See README" Set instructions for when all archives are done X*/ X#include "shar.h" XRCS("$Header: shar.c,v 1.18 87/03/18 14:10:21 rs Exp $") 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 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*/ 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 int Lastchar; 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 over-write 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_%s'\n", Name, Name); X X /* Output the file contents. */ X for (Bads = 0, Lastchar = '\n'; fgets(buf, BUFSIZ, stdin); ) { X printf("X"); X if (buf[0]) { X for (s = buf; *s; s++) { X if (BADCHAR(*s)) X Bads++; X (void)putchar(*s); X } X Lastchar = s[-1]; X } X else X Lastchar = 'X'; X } X X /* Tell about missing \n and control characters. */ X if (Lastchar != '\n') X (void)putchar('\n'); X printf("END_OF_%s\n", Name); X if (Lastchar != '\n') { X printf("echo shar: Missing newline added to \\\"%s\\\"\n", Name); X fprintf(stderr, "Missing newline added to \"%s\"\n", Name); X } 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 overwriting check\nfi\n"); X } X} X X Xmain(ac, av) X int ac; X register char *av[]; X{ X register char *Trailer; X register char *p; X register int i; X register int length; X register int Oops; X register int Knum; X register int Kmax; X time_t clock; X int Basename; 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: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 '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 av += optind; X if (*av == NULL) { X fprintf(stderr, "No input files\n"); X Oops++; X } X if (Oops) { X fprintf(stderr, "USAGE: shar [-b] [-o:] [-n:e:t:] file... >SHAR\n"); X exit(1); X } X X /* Everything readable and reasonably-named? */ X for (Oops = 0, i = 0; p = av[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 || (!isalnum(*p) && IDX(OK_CHARS, *p) == NULL)) { X fprintf(stderr, "Bad character '%c' in '%s'.\n", *p, av[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 = av[i++]; length += strlen(p) + 1) X if (length + strlen(p) + 1 < WIDTH) X printf(" %s", p); X else { X printf("\n# %s", p); X length = 4; 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 (*av) X shar(*av++, 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 == 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 X printf("exit 0\n"); X X exit(0); X} END_OF_shar.c if test 6386 -ne `wc -c <shar.c`; then echo shar: \"shar.c\" unpacked with wrong size!? fi # end of overwriting check fi echo shar: Extracting \"shar.h\" \(4373 characters\) if test -f shar.h ; then echo shar: Will not over-write existing file \"shar.h\" else sed "s/^X//" >shar.h <<'END_OF_shar.h' X/* X** Header file for shar and friends. X** X** $Header: shar.h,v 1.25 87/03/18 14:03:27 rs Exp $ X*/ X X X/* X** Edit as necessary. X*/ X X/* Variances in local dialects. */ X#define IDX index /* Maybe strchr? */ X#define RDX rindex /* Maybe strrchr? */ X/*efine NEED_MKDIR /* Don't have mkdir(2)? */ X/*efine NEED_QSORT /* Don't have qsort(3)? */ X#define NEED_GETOPT /* Need local getopt object? */ X#define CAN_POPEN /* Can invoke file(1) command? */ X/*efine USE_MY_SHELL /* Don't popen("/bin/sh")? */ X Xtypedef int *align_t; /* Worst-case alignment, for lint */ X/* typedef long time_t /* Needed for non-BSD sites? */ X/* typedef long off_t /* Needed for non-BSD sites? */ X X#define DEF_SAVEIT 1 /* Save headers by default? */ X X/* Where is BSD-compatible directory header file? Pick one. */ X#define IN_SYS_DIR /* <sys/dir.h> */ X/*efine IN_SYS_NDIR /* <sys/ndir.h> */ X/*efine IN_DIR /* "dir.h" */ X X/* Do you have <sys/wait.h>? If so, then you have vfork, too. */ X#define SYS_WAIT X X/* Login name from environment; pick one. */ X#define USER_ENV "USER" /* .. */ X/*efine USER_ENV "LOGNAME" /* .. */ X/*efine USER_ENV "NAME" /* .. */ X X/* What we should fopen() if stdin is not the tty. */ X#define THE_TTY "/dev/tty" /* Maybe "con:" for MS-DOS? */ X X/* Name of the machine we're running on; pick one. */ X#define GETHOSTNAME /* Use gethostname(2) call */ X/*efine UNAME /* Use uname(2) call */ X/*efine UUNAME /* Invoke "uuname -l" */ X/*efine WHOAMI /* Try /etc/whoami & <whoami.h> */ X/*efine HOST "SITE" /* If all else fails */ X X/* How do we find the current working directory? */ X#define GETWD /* Use getwd(3) routine */ X/* fine GETCWD /* Use getcwd(3) routine */ X/* fine PWDPOPEN /* Invoke "pwd" */ X/* fine PWDGETENV "PWD" /* Get $PWD from environment */ X X/* Prefixes for first two lines of saved NOTESFILES articles. */ X#define NOTES1 "/* Written " X#define NOTES2 "/* ---" X X/* Legal characters for filenames. Note that shar doesn't do quoting... */ X#define OK_CHARS "@%_-+=.,/" X X/* X** END OF CONFIGURATION SECTION X*/ X X#include <stdio.h> X#include <sys/types.h> X#include <ctype.h> X X#ifdef IN_SYS_DIR X#include <sys/dir.h> X#endif /* IN_SYS_DIR */ X#ifdef IN_SYS_NDIR X#include <sys/ndir.h> X#endif /* IN_SYS_NDIR */ X#ifdef IN_NDIR X#include "ndir.h" X#endif /* IN_DIR */ X X X/* X** Handy shorthands. X*/ X#define TRUE 1 X#define FALSE 0 X#define WIDTH 72 X#define F_DIR '$' /* Something is a directory */ X#define F_FILE 'A' /* Something is a regular file */ X#define S_IGNORE 'L' /* Ignore this signal */ X#define S_RESET 'Z' /* Reset signal to default */ X X/* These are used by the archive parser. */ X#define MAX_LINE_SIZE 200 /* Length of physical input line*/ X#define MAX_VAR_NAME 30 /* Length of a variable's name */ X#define MAX_VAR_VALUE 128 /* Length of a variable's value */ X#define MAX_VARS 20 /* Number of shell vars allowed */ X#define MAX_WORDS 30 /* Make words in command lnes */ X X X/* X** Keep RCS stuff away from lint. X*/ X#ifdef lint X#define RCS(text) /* NULL */ X#else X#define RCS(text) static char ID[] = text; X#endif /* lint */ X X X/* X** Memory hacking. X*/ X#define NEW(T, count) ((T *)getmem(sizeof(T), (unsigned int)(count))) X#define ALLOC(n) getmem(1, (unsigned int)(n)) X#define COPY(s) strcpy(NEW(char, strlen((s)) + 1), (s)) X X X/* X** Macros. X*/ X#define BADCHAR(c) (iscntrl((c)) && !isspace((c))) X#define EQ(a, b) (strcmp((a), (b)) == 0) X#define EQn(a, b, n) (strncmp((a), (b), (n)) == 0) X#define PREFIX(a, b) (EQn((a), (b), sizeof b - 1)) X#define WHITE(c) ((c) == ' ' || (c) == '\t') X X X/* X** Linked in later. X*/ Xextern int errno; Xextern int optind; Xextern char *optarg; X X/* From your C run-time library. */ Xextern FILE *popen(); Xextern time_t time(); Xextern long atol(); Xextern char *IDX(); Xextern char *RDX(); Xextern char *ctime(); Xextern char *gets(); Xextern char *mktemp(); Xextern char *strcat(); Xextern char *strcpy(); Xextern char *strncpy(); Xextern char *getenv(); X X/* From our local library. */ Xextern align_t getmem(); Xextern off_t Fsize(); Xextern char *Copy(); Xextern char *Cwd(); Xextern char *Ermsg(); Xextern char *Host(); Xextern char *User(); X X/* Exported by the archive parser. */ Xextern FILE *Input; /* Current input stream */ Xextern char *File; /* Input filename */ Xextern int Interactive; /* isatty(fileno(stdin))? */ Xextern void SynErr(); /* Fatal syntax error */ END_OF_shar.h if test 4373 -ne `wc -c <shar.h`; then echo shar: \"shar.h\" unpacked with wrong size!? fi # end of overwriting check fi echo shar: Extracting \"shar.man\" \(2834 characters\) if test -f shar.man ; then echo shar: Will not over-write existing file \"shar.man\" else sed "s/^X//" >shar.man <<'END_OF_shar.man' X.TH SHAR 1 LOCAL X.\" $Header: shar.man,v 1.1 87/02/27 13:45:27 rs Exp $ X.SH NAME Xshar \- create shell archive file for extraction by /bin/sh X.SH SYNOPSIS X.B shar X[ X.B \-b X] [ X.BI \-n seq_number X] [ X.BI \-e seq_end X] [ X.BI \-o output_file X] [ X.BI \-t final_text X] files X.SH DESCRIPTION X.I Shar Xtakes a list of files, and generates a X.IR /bin/sh Xscript that, when executed, will re-create those files in a different Xdirectory or on a different machine. XThe resultant script will use X.IR wc (1) Xto do a mild error-check, and will warn about possibly-omitted Xcontrol characters. X.PP X.I Shar Xgenerates scripts that will make directories and plain files. XIt will not try to generate intermediate filenames, however, so X.RS Xshar foo/bar/file X.RE Xwill not work. Do X.RS Xshar foo foo/bar foo/bar/file X.RE Xinstead. X.PP XThe script is normally sent to standard output; the ``\-o'' option may be Xused to specify an output filename. XThis is designed to prevent filling up the disk if X.RS Xshar * >SHAR X.RE Xcommand is done; do X.RS Xshar -o SHAR * X.RE Xinstead. X.PP XThe ``\-b'' option says that all leading directory names should be stripped Xfrom the file when they are packed into the archive. XFor example, X.RS Xshar -b /etc/termcap X.RE Xcreates an archive that, when executed, creates a file named X``termcap'' in the current directory, rather than overwrite the Xhost system file. XNote, however, that the scripts generated by X.I shar Xrefuse to overwrite pre-existing files; the ``\-b'' option is Xfor convenience, and may not strictly be necessary. X.SS "Multi\-part Archives" XMost larger software packages are usually sent out in two or more shell Xarchives. XThe ``\-n,'' ``\-e,'' and ``\-t'' options are used to make an archive Xthat is part of a series. XThe individual archives are often called ``kits'' when this is done. XThe ``\-n'' option specifies the archive number; the ``\-e'' option species Xthe highest number in the series. XWhen executed, the generated archives will then echo messages like X.RS Xshar: End of archive 3 of 9. X.RE Xat their end. X.PP XIn addition, each shar will generate a file named X.IR ark X isdone . XEach script will contain a loop to check for the presence of these Xfiles, and indicate to the recipient which archives still need to be Xexecuted. XThe ``\-t'' option may be used to give starting instructions to the recipient. XWhen the scripts determine that all the archives have been unpacked, Xthe text specified with this flag is displayed. XFor example, X.RS Xshar -n1 -k9 -t "Now do 'sh ./Configure'" *.c >SHAR X.RE XAdds commands to output the following when all the archives have been unpacked: X.RS X.nf XYou have run archive 1. X\& . X\& . X\& . XYou have run archive 9. XYou have run all 9 archives. XNow do 'sh ./Configure' X.fi X.RE X.SH "SEE ALSO" Xecho(1), findsrc(1L), makekit(1L), mkdir(1), sh(1), test(1), unshar(1L), Xwc(1). END_OF_shar.man if test 2834 -ne `wc -c <shar.man`; then echo shar: \"shar.man\" unpacked with wrong size!? fi # end of overwriting check fi echo shar: Extracting \"shell.c\" \(767 characters\) if test -f shell.c ; then echo shar: Will not over-write existing file \"shell.c\" else sed "s/^X//" >shell.c <<'END_OF_shell.c' X/* X** Stand-alone driver for shell. X*/ X#include "shar.h" XRCS("$Header: shell.c,v 1.4 87/03/24 16:19:56 rs Exp $") X X Xextern void SetVar(); X X Xmain(ac, av) X register int ac; X register char *av[]; X{ X char *vec[MAX_WORDS]; X char buff[MAX_VAR_VALUE]; X X if (Interactive = ac == 1) { X fprintf(stderr, "Testing shell interpreter...\n"); X Input = stdin; X File = "stdin"; X } X else { X if ((Input = fopen(File = av[1], "r")) == NULL) X SynErr("UNREADABLE INPUT"); X /* Build the positional parameters. */ X for (ac = 1; av[ac]; ac++) { X (void)sprintf(buff, "%d", ac - 1); X SetVar(buff, av[ac]); X } X } X X /* Read, parse, and execute. */ X while (GetLine(TRUE)) X if (Argify(vec)) X (void)Exec(vec); X X /* That's it. */ X exit(0); X} END_OF_shell.c if test 767 -ne `wc -c <shell.c`; then echo shar: \"shell.c\" unpacked with wrong size!? fi # end of overwriting check fi echo shar: Extracting \"shell.man\" \(1091 characters\) if test -f shell.man ; then echo shar: Will not over-write existing file \"shell.man\" else sed "s/^X//" >shell.man <<'END_OF_shell.man' X.TH SHELL 1 LOCAL X.\" $Header: shell.man,v 1.2 87/03/09 16:55:28 rs Exp $ X.SH NAME Xshell \- Interpreter for shell archives X.SH SYNOPSIS X.B shell X[ X.B file X] X.SH DESCRIPTION XThis program interprets enough UNIX shell syntax, and command usage, Xto enable it to unpack many different types of UNIX shell archives, Xor ``shar's.'' XIt is primarily intended to be used on non-UNIX systems that need to Xunpack such archives. X.PP X.I Shell Xdoes X.B not Xcheck for security holes, and will blithely execute commands like X.RS Xcp /dev/null /etc/passwd X.RE Xwhich, if executed by the super-user, can be disastrous. X.PP XThe X.I shell Xparser is line-based, where lines are then split into tokens; it is not a Xtrue token-based parser. XIn general, it is best if all X.I sh Xkeywords that can appear alone on a line do so, and that compound Xcommands (i.e., using a semi-colon) be avoided. XFor more details on the syntax, see the source (sorry...). X.SH BUGS XIt is probably easier to write a true portable replacement for /bin/sh Xthan it is to write something which understands all shar formats. X.SH SEE ALSO Xshar(1L). END_OF_shell.man if test 1091 -ne `wc -c <shell.man`; then echo shar: \"shell.man\" unpacked with wrong size!? fi # end of overwriting check fi echo shar: Extracting \"sysfuncs.c\" \(4828 characters\) if test -f sysfuncs.c ; then echo shar: Will not over-write existing file \"sysfuncs.c\" else sed "s/^X//" >sysfuncs.c <<'END_OF_sysfuncs.c' X/* X** System-specific stuff. This module will need to be ported to X** other systems. X*/ X/* LINTLIBRARY */ X#include "shar.h" X#include <signal.h> X#include <pwd.h> X#include <sys/types.h> X#include <sys/stat.h> X#ifdef SYS_WAIT X#include <sys/wait.h> X#else X#endif /* SYS_WAIT */ XRCS("$Header: sysfuncs.c,v 1.7 87/03/13 13:08:34 rs Exp $") X X X/* How to fork(), what to wait with. */ X#ifdef SYS_WAIT X#define FORK() vfork() X#define W_VAL(w) ((w).w_retcode) Xtypedef union wait WAITER; X#else X#define FORK() fork() X#define W_VAL(w) ((w) >> 8) Xtypedef int WAITER; X#endif /* SYS_WAIT */ X X X/* Mask of executable bits. */ X#define EXE_MASK (S_IEXEC | (S_IEXEC >> 3) | (S_IEXEC >> 6)) X X/* Stat buffer for last file. */ Xstatic struct stat Sb; X X X/* X** Get user name. Not secure, but who sends nastygrams as shell archives? X*/ Xchar * XUser() X{ X extern struct passwd *getpwuid(); X struct passwd *p; X char *g; X X if (g = getenv(USER_ENV)) X return(g); X return((p = getpwuid(getuid())) ? p->pw_name : "USER"); X} X X X/* X** Set up a signal handler. X*/ XSetSigs(What, Func) X int What; X int (*Func)(); X{ X if (What == S_IGNORE) X Func = SIG_IGN; X else if (What == S_RESET) X Func = SIG_DFL; X if (signal(SIGINT, SIG_IGN) != SIG_IGN) X (void)signal(SIGINT, Func); X if (signal(SIGQUIT, SIG_IGN) != SIG_IGN) X (void)signal(SIGQUIT, Func); X} X X X/* X** Stat the file if it's not the one we did last time. X*/ Xint XGetStat(p) X char *p; X{ X static char Name[BUFSIZ]; X X if (*p == Name[0] && EQ(p, Name)) X return(TRUE); X return(stat(strcpy(Name, p), &Sb) < 0 ? FALSE : TRUE); X} X X X/* X** Return the file type -- directory or regular file. X*/ Xint XFtype(p) X char *p; X{ X return(GetStat(p) && ((Sb.st_mode & S_IFMT) == S_IFDIR) ? F_DIR : F_FILE); X} X X X/* X** Return the file size. X*/ Xoff_t XFsize(p) X char *p; X{ X return(GetStat(p) ? Sb.st_size : 0); X} X X X/* X** Is a file executable? X*/ Xint XFexecute(p) X char *p; X{ X return(GetStat(p) && (Sb.st_mode & EXE_MASK) ? TRUE : FALSE); X} X X X/* X** Return the process ID. X*/ Xint XPid() X{ X static int X; X X if (X == 0) X X = getpid(); X return(X); X} X X X/* X** Return the text string that corresponds to errno. X*/ Xchar * XErmsg(e) X int e; X{ X extern int sys_nerr; X extern char *sys_errlist[]; X static char buff[30]; X X if (e > 0 && e < sys_nerr) X return(sys_errlist[e]); X (void)sprintf(buff, "Error code %d", e); X return(buff); X} X X X/* X** Fork off a command. X*/ Xint XExecute(av) X char *av[]; X{ X register int i; X register int j; X WAITER W; X X if ((i = FORK()) == 0) { X SetSigs(S_RESET, (int (*)())NULL); X (void)execvp(av[0], av); X perror(av[0]); X _exit(1); X } X X SetSigs(S_IGNORE, (int (*)())NULL); X while ((j = wait(&W)) < 0 && j != i) X ; X SetSigs(S_RESET, (int (*)())NULL); X return(W_VAL(W)); X} X X X#ifdef NEED_MKDIR X/* X** Quick and dirty mkdir routine for them that's need it. X*/ Xint Xmkdir(name, mode) X char *name; X int mode; X{ X char *av[3]; X int i; X X av[0] = "mkdir"; X av[1] = name; X av[2] = NULL; X U = umask(~mode); X i = Execute(av); X (void)umask(U); X return(i ? -1 : 0); X} X#endif /* NEED_MKDIR */ X X X#ifdef NEED_QSORT X/* X** Bubble sort an array of arbitrarily-sized elements. This routine X** can be used as an (inefficient) replacement for the Unix qsort X** routine, hence the name. If I were to put this into my C library, X** I'd do two things: X** -Make it be a quicksort; X** -Have a front routine which called specialized routines for X** cases where Width equals sizeof(int), sizeof(char *), etc. X*/ Xqsort(Table, Number, Width, Compare) X register char *Table; X register int Number; X register int Width; X register int (*Compare)(); X{ X register char *i; X register char *j; X X for (i = &Table[Number * Width]; (i -= Width) >= &Table[Width]; ) X for (j = i; (j -= Width) >= &Table[0]; ) X if ((*Compare)(i, j) < 0) { X register char *p; X register char *q; X register int t; X register int w; X X /* Swap elements pointed to by i and j. */ X for (w = Width, p = i, q = j; --w >= 0; *p++ = *q, *q++ = t) X t = *p; X } X} X#endif /* NEED_QSORT */ X X X#undef NOTDEF X#ifdef NOTDEF X/* X** Cons all the arguments together into a single command line and hand X** it off to the shell to execute. This is only here for someone to use X** as the basis of, say, an MSDOS port. X*/ Xint XExecute(av) X register char *av[]; X{ X register char **v; X register char *p; X register char *q; X register int i; X X /* Get length of command line. */ X for (i = 5, v = av; *v; v++) X i += strlen(*v) + 1; X X /* Create command line and execute it. */ X p = NEW(char, i); X for (q = p + strlen(strcpy(p, "exec")), v = av; *v; v++) { X *q++ = ' '; X q += strlen(strcpy(q, *v)); X } X X return(system(p)); X} X#endif /* NOTDEF */ END_OF_sysfuncs.c if test 4828 -ne `wc -c <sysfuncs.c`; then echo shar: \"sysfuncs.c\" unpacked with wrong size!? fi # end of overwriting check fi echo shar: Extracting \"unshar.man\" \(2178 characters\) if test -f unshar.man ; then echo shar: Will not over-write existing file \"unshar.man\" else sed "s/^X//" >unshar.man <<'END_OF_unshar.man' X.TH UNSHAR 1 LOCAL X.\" $Header: unshar.man,v 1.1 87/02/27 13:45:34 rs Exp $ X.SH NAME Xunshar \- unpack shell archives from news, mail, notes, etc. X.SH SYNOPSIS X.B unshar X[ X.BI \-c\| directory X] [ X.BI \-d\| directory X] [ X.B \-f X] [ X.B \-n X] [ X.B \-s X] [files] X.SH DESCRIPTION X.I Unshar Xremoves mail and news header lines from its input, and feeds the remainder Xto X.IR /bin/sh (1) Xso that a shell archive can be properly unpacked. XIf no files are specified, X.I unshar Xreads from standard input. XThe program is designed to be useful when unpacking archives directly Xfrom the news or mail systems (e.g., s | unshar). X.PP X.I Unshar Xnormally unpacks its files in the current directory. XUse the ``\-c'' option to have the program change to a new directory Xbefore invoking the shell. XIf the directory does not exist, it will try to create it. XIf the directory name starts with a question mark, then X.I unshar Xwill ask for the directory name before doing anything; this is most useful Xwith the environment variable UNSHAREDIR. XIf the directory name starts with a tilde, then the value of the HOME Xenvironment variable is inserted in place of that character. XFor convenience, the ``\-d'' option is a synonym for the ``\-c'' option. X.PP X.I Unshar Xnormally complains if the input looks like something other than a shar file. X(Among other things, it checks for files that resemble C, and Pascal code). XIt can be fooled, however, by nonstandard versions of news, notes, etc. XThe ``\-f'' option forces X.I unshar Xto try unpacking files, even if they look like something else. X.PP XDepending on how the program is installed, X.I unshar Xmay or may not try to preserve the header part of file ``foo'' Xinto the name ``foo.hdr'' (if the file is standard input, the name Xwill be ``UNSHAR.HDR''). XUsing the XUsing the ``\-s'' option forces the program to save the headers, while Xusing the ``\-n'' option forces it to discard the headers. XThe file is appended to, if it already exists, so all headers can be easily Xsaved in one file. X.SH ENVIRONMENT X.ta \w'UNSHAREDIR 'u XHOME Value used if a leading tilde is given in directory name. X.br XUNSHAREDIR Default value for ``\-c'' option. X.SH SEE ALSO Xshar(1). END_OF_unshar.man if test 2178 -ne `wc -c <unshar.man`; then echo shar: \"unshar.man\" unpacked with wrong size!? fi # end of overwriting check fi echo shar: End of archive 1 \(of 2\). cp /dev/null ark1isdone MISSING="" for I in 1 2 ; do if test ! -f ark${I}isdone ; then MISSING="${MISSING} ${I}" fi done if test "${MISSING}" = "" ; then echo You have unpacked both 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 -- -- Rich $alz "Drug tests p**s me off" Mirror Systems, Cambridge Massachusetts rs@mirror.TMC.COM {adelie, mit-eddie, ihnp4, harvard!wjh12, cca, cbosgd, seismo}!mirror!rs
rs@mirror.UUCP (03/25/87)
#! /bin/sh # This is a shell archive. Remove anything before this line, # then unpack it by saving it in a file and typing "sh file". # If this archive is complete, you will see the message: # "End of archive 2 (of 2)." # Contents: makekit.c parser.c unshar.c # Wrapped by rs@mirror on Tue Mar 24 18:12:09 1987 PATH=/bin:/usr/bin:/usr/ucb ; export PATH echo shar: Extracting \"makekit.c\" \(9987 characters\) if test -f makekit.c ; then echo shar: Will not over-write existing file \"makekit.c\" else sed "s/^X//" >makekit.c <<'END_OF_makekit.c' X/* X** MAKEKIT X** Split up source files into reasonably-sized shar lists. X** X** Options: X** -e Leave our output file out X** -h # Number of header lines in input file X** -i name Input file name X** -k # Maximum number of archives desired X** -m Same as "-i Manifest -o Manifest -s2" X** -n name Name for resultant archives X** -o name Output file name X** -p Preserve original input order X** -s #[k] Maximum size of each archvie X** -t text Set final instructions after all are unpacked X** -x Don't actually do the shar'ing X*/ X#include "shar.h" XRCS("$Header: makekit.c,v 1.15 87/03/13 12:56:41 rs Exp $") X X X/* X** Our block of information about the files we're doing. X*/ Xtypedef struct { X char *Name; /* Filename */ X char *Text; /* What it is */ X int Where; /* Where it is */ X int Type; /* Directory or file? */ X long Size; /* Size in bytes */ X} BLOCK; X X X/* X** Our block of information about the archives we're making. X*/ Xtypedef struct { X int Count; /* Number of files */ X long Size; /* Bytes used by archive */ X} ARCHIVE; X X X/* X** We re-use parts of one buffer to hold three different strings in our X** argument vector to exec(). X*/ X#define SEG0 0 /* Ending archive number */ X#define SEG1 10 /* Current archive number */ X#define SEG2 20 /* Output name (rest of buff) */ 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 X X/* X** Global variables. X*/ Xchar *InName; /* File with list to pack */ Xchar *OutName; /* Where our output goes */ Xchar *SharName = "Part"; /* Prefix for name of each shar */ Xchar *Trailer; /* Text for shar to pack in */ Xchar TEMP[] = "/tmp/arkXXXXXX"; /* Temporary manifest file */ Xint ArchCount = 20; /* Max number of archives */ Xint ExcludeIt; /* Leave out the output file? */ Xint Header; /* Lines of prolog in input */ Xint Preserve; /* Preserve order for Manifest? */ Xint Working = TRUE; /* Call shar when done? */ Xlong Size = 55000; /* Largest legal archive size */ X X X/* X** Sorting predicate to put README first, then directories, then large X** files, then smaller files, which is how we want to assign things to X** the archives. X*/ Xstatic int XSizeP(t1, t2) X BLOCK *t1; X BLOCK *t2; X{ X long i; X X if (EQ(t1->Name, "README") || EQ(t1->Name, "readme")) X return(-1); X if (EQ(t2->Name, "README") || EQ(t1->Name, "readme")) X return(1); X if (t1->Type != t2->Type) X return(t1->Type == F_DIR ? 1 : -1); X return((i = t1->Size - t2->Size) == 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*/ Xstatic int XNameP(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*/ 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 XCatch(s) X int s; X{ X int e; X X e = errno; X (void)unlink(TEMP); X fprintf(stderr, "Got signal %d, %s.\n", s, Ermsg(e)); X exit(1); 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 Value; X BLOCK *Table; X BLOCK *TabEnd; X ARCHIVE *Ark; X ARCHIVE *ArkEnd; X char buff[BUFSIZ]; X int LastOne; X int Start; X X /* Collect input. */ X Value = FALSE; X while ((i = getopt(ac, av, "eh:i:k:n:mop:s:t:x")) != EOF) X switch (i) { X default: X exit(1); 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 ArchCount = 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 F = fopen(mktemp(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 it's size when archived. */ X t->Size = strlen(t->Name) * 3 + 200; X if (t->Type == F_FILE) { X i = Fsize(t->Name); X t->Size += i + i / 60; X } X if (t->Size > Size) { X fprintf(stderr, "At %ld bytes, %s is too big for any archive!\n", X t->Size, 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, (int (*)())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->Size = 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, ArchCount); X ArkEnd = &Ark[ArchCount]; 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->Size + k->Size < Size) { X k->Size += t->Size; 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, ArchCount); 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 (void)sprintf(&buff[SEG0], "%d", LastOne); X av[i++] = "-e"; X av[i++] = &buff[SEG0]; X av[i++] = "-n"; X av[i++] = &buff[SEG1]; X av[i++] = "-o"; X av[i++] = &buff[SEG2]; X X /* Call shar to package up each archive. */ X for (Start = i, i = 0; i < LastOne; i++) { X (void)sprintf(&buff[SEG1], "%d", i + 1); X (void)sprintf(&buff[SEG2], FORMAT2, SharName, i + 1); X for (lines = Start, t = Table; t < TabEnd; t++) X if (t->Where == i) X av[lines++] = t->Name; X av[lines] = NULL; 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 /* That's all she wrote. */ X exit(0); X} END_OF_makekit.c if test 9987 -ne `wc -c <makekit.c`; then echo shar: \"makekit.c\" unpacked with wrong size!? fi # end of overwriting check fi echo shar: Extracting \"parser.c\" \(23917 characters\) if test -f parser.c ; then echo shar: Will not over-write existing file \"parser.c\" else sed "s/^X//" >parser.c <<'END_OF_parser.c' X/* X** An interpreter that can unpack many /bin/sh shell archives. X** This program should really be split up into a couple of smaller X** files; it started with Argify and SynTable as a cute 10-minute X** hack and it just grew. X** X** Also, note that (void) casts abound, and that every command goes X** to some trouble to return a value. That's because I decided X** not to implement $? "properly." X*/ X#include "shar.h" XRCS("$Header: parser.c,v 1.7 87/03/24 17:56:13 rs Exp $") X X X/* X** DATATYPES X*/ X X/* Command dispatch table. */ Xtypedef struct { X char Name[10]; /* Text of command name */ X int (*Func)(); /* Function that implements it */ X} COMTAB; X X/* A shell variable. We only have a few of these. */ Xtypedef struct { X char *Name; X char *Value; X} VAR; X X X/* X** Manifest constants, handy shorthands. X*/ X X/* Character classes used in the syntax table. */ X#define C_LETR 1 /* A letter within a word */ X#define C_WHIT 2 /* Whitespace to separate words */ X#define C_WORD 3 /* A single-character word */ X#define C_DUBL 4 /* Something like <<, e.g. */ X#define C_QUOT 5 /* Quotes to group a word */ X#define C_META 6 /* Heavy magic character */ X#define C_TERM 7 /* Line terminator */ X X/* Macros used to query character class. */ X#define ISletr(c) (SynTable[(c)] == C_LETR) X#define ISwhit(c) (SynTable[(c)] == C_WHIT) X#define ISword(c) (SynTable[(c)] == C_WORD) X#define ISdubl(c) (SynTable[(c)] == C_DUBL) X#define ISquot(c) (SynTable[(c)] == C_QUOT) X#define ISmeta(c) (SynTable[(c)] == C_META) X#define ISterm(c) (SynTable[(c)] == C_TERM) X X X/* X** Global variables. X*/ X XFILE *Input; /* Current input stream */ Xchar *File; /* Input filename */ Xint Interactive; /* isatty(fileno(stdin))? */ X Xextern COMTAB Dispatch[]; /* Defined below... */ Xstatic VAR VarList[MAX_VARS]; /* Our list of variables */ Xstatic char Text[BUFSIZ]; /* Current text line */ Xstatic int LineNum = 1; /* Current line number */ Xstatic int Running = TRUE; /* Working, or skipping? */ Xstatic short SynTable[256] = { /* Syntax table */ X /* \0 001 002 003 004 005 006 007 */ X C_TERM, C_WHIT, C_WHIT, C_WHIT, C_WHIT, C_WHIT, C_WHIT, C_WHIT, X /* \h \t \n 013 \f \r 016 017 */ X C_WHIT, C_WHIT, C_TERM, C_WHIT, C_TERM, C_TERM, C_WHIT, C_WHIT, X /* 020 021 022 023 024 025 026 027 */ X C_WHIT, C_WHIT, C_WHIT, C_WHIT, C_WHIT, C_WHIT, C_WHIT, C_WHIT, X /* can em sub esc fs gs rs us */ X C_WHIT, C_WHIT, C_WHIT, C_WHIT, C_WHIT, C_WHIT, C_WHIT, C_WHIT, X X /* sp ! " # $ % & ' */ X C_WHIT, C_LETR, C_QUOT, C_TERM, C_LETR, C_LETR, C_DUBL, C_QUOT, X /* ( ) * + , - . / */ X C_WORD, C_WORD, C_LETR, C_LETR, C_LETR, C_LETR, C_LETR, C_LETR, X /* 0 1 2 3 4 5 6 7 */ X C_LETR, C_LETR, C_LETR, C_LETR, C_LETR, C_LETR, C_LETR, C_LETR, X /* 8 9 : ; < = > ? */ X C_LETR, C_LETR, C_TERM, C_DUBL, C_DUBL, C_LETR, C_DUBL, C_LETR, X X /* @ A B C D E F G */ X C_LETR, C_LETR, C_LETR, C_LETR, C_LETR, C_LETR, C_LETR, C_LETR, X /* H I J K L M N O */ X C_LETR, C_LETR, C_LETR, C_LETR, C_LETR, C_LETR, C_LETR, C_LETR, X /* P Q R S T U V W */ X C_LETR, C_LETR, C_LETR, C_LETR, C_LETR, C_LETR, C_LETR, C_LETR, X /* X Y Z [ \ ] ^ _ */ X C_LETR, C_LETR, C_LETR, C_LETR, C_META, C_LETR, C_LETR, C_LETR, X X /* ` a b c d e f g */ X C_WORD, C_LETR, C_LETR, C_LETR, C_LETR, C_LETR, C_LETR, C_LETR, X /* h i j k l m n o */ X C_LETR, C_LETR, C_LETR, C_LETR, C_LETR, C_LETR, C_LETR, C_LETR, X /* p q r s t u v w */ X C_LETR, C_LETR, C_LETR, C_LETR, C_LETR, C_LETR, C_LETR, C_LETR, X /* x y z { | } ~ del */ X C_LETR, C_LETR, C_LETR, C_LETR, C_DUBL, C_LETR, C_LETR, C_WHIT, X}; X X/** X*** E R R O R R O U T I N E S X**/ X X X/* X** Print message with current line and line number. X*/ Xstatic void XNote(text, arg) X char *text; X char *arg; X{ X fprintf(stderr, "\nIn line %d of %s:\n\t", LineNum, File); X fprintf(stderr, text, arg); X fprintf(stderr, "Current line:\n\t%s\n", Text); X (void)fflush(stderr); X} X X X/* X** Print syntax message and die. X*/ Xvoid XSynErr(text) X char *text; X{ X Note("Fatal syntax error in %s statement.\n", text); X exit(1); X} X X/** X*** I N P U T R O U T I N E S X**/ X X X/* X** Miniscule regular-expression matcher; only groks the . meta-character. X*/ Xstatic int XMatches(p, text) X register char *p; X register char *text; X{ X for (; *p && *text; text++, p++) X if (*p != *text && *p != '.') X return(FALSE); X return(TRUE); X} X X X X/* X** Read input, possibly handling escaped returns. Returns a value so X** we can do things like "while (GetLine(TRUE))", which is a hack. This X** should also be split into two separate routines, and punt the Flag X** argument, but so it goes. X*/ Xint XGetLine(Flag) X register int Flag; X{ X register char *p; X register char *q; X char buf[MAX_LINE_SIZE]; X X if (Interactive) { X fprintf(stderr, "Line %d%s> ", LineNum, Running ? "" : "(SKIP)"); X (void)fflush(stderr); X } X Text[0] = '\0'; X for (q = Text; fgets(buf, sizeof buf, Input); q += strlen(strcpy(q, buf))) { X LineNum++; X p = &buf[strlen(buf) - 1]; X if (*p != '\n') { X Note("Input line too long.\n", (char *)NULL); X exit(1); X } X if (!Flag || p == buf || p[-1] != '\\') { X (void)strcpy(q, buf); X return(1); X } X p[-1] = '\0'; X if (Interactive) { X fprintf(stderr, "PS2> "); X (void)fflush(stderr); X } X } X Note("RAN OUT OF INPUT.\n", (char *)NULL); X exit(1); X /* NOTREACHED */ X} X X X/* X** Copy a sub-string of characters into dynamic space. X*/ Xstatic char * XCopyRange(Start, End) X char *Start; X char *End; X{ X char *p; X int i; X X i = End - Start + 1; X p = strncpy(NEW(char, i + 1), Start, i); X p[i] = '\0'; X return(p); X} X X X/* X** Split a line up into shell-style "words." X*/ Xint XArgify(ArgV) X char **ArgV; X{ X register char **av; X register char *p; X register char *q; X X for (av = ArgV, p = Text; *p; p++) { X /* Skip whitespace, but treat "\ " as a letter. */ X for (; ISwhit(*p); p++) X if (ISmeta(*p)) X p++; X if (ISterm(*p)) X break; X switch (SynTable[*p]) { X default: X Note("Bad case %x in Argify.\n", (char *)SynTable[*p]); X case C_META: X p++; X case C_WHIT: X case C_LETR: X for (q = p; ISletr(*++q) || ISmeta(q[-1]); ) X ; X *av++ = CopyRange(p, --q); X p = q; X break; X case C_DUBL: X if (*p == p[1]) { X *av++ = CopyRange(p, p + 1); X p++; X break; X } X case C_WORD: X *av++ = CopyRange(p, p); X break; X case C_QUOT: X for (q = p; *++q; ) X if (*q == *p && !ISmeta(q[-1])) X break; X *av++ = CopyRange(p + 1, q - 1); X p = q; X break; X } X } X *av = NULL; X if (av > &ArgV[MAX_WORDS - 1]) X SynErr("TOO MANY WORDS IN LINE"); X return(av - ArgV); X} X X/** X*** V A R I A B L E R O U T I N E S X**/ X X X/* X** Return the value of a variable, or an empty string. X*/ Xstatic char * XGetVar(Name) X register char *Name; X{ X register VAR *Vptr; X X for (Vptr = VarList; Vptr < &VarList[MAX_VARS]; Vptr++) X if (EQ(Vptr->Name, Name)) X return(Vptr->Value); X X /* Try the environment. */ X return((Name = getenv(Name)) ? Name : ""); X} X X X/* X** Insert a variable/value pair into the list of variables. X*/ Xvoid XSetVar(Name, Value) X register char *Name; X register char *Value; X{ X register VAR *Vptr; X register VAR *FreeVar; X X /* Skip leading whitespace in variable names, sorry... */ X while (ISwhit(*Name)) X Name++; X X /* Try to find the variable in the table. */ X for (Vptr = VarList, FreeVar = NULL; Vptr < &VarList[MAX_VARS]; Vptr++) X if (Vptr->Name) { X if (EQ(Vptr->Name, Name)) { X free(Vptr->Value); X Vptr->Value = COPY(Value); X return; X } X } X else if (FreeVar == NULL) X FreeVar = Vptr; X X if (FreeVar == NULL) { X fprintf(stderr, "Overflow, can't do '%s=%s'\n", Name, Value); X SynErr("ASSIGNMENT"); X } X FreeVar->Name = COPY(Name); X FreeVar->Value = COPY(Value); X} X X X/* X** Expand variable references inside a word that are of the form: X** foo${var}bar X** foo$$bar X** Returns a pointer to a static area which is overwritten every X** other time it is called, so that we can do EQ(Expand(a), Expand(b)). X*/ Xstatic char * XExpand(p) X register char *p; X{ X static char buff[2][MAX_VAR_VALUE]; X static int Flag; X register char *q; X register char *n; X register char Closer; X char name[MAX_VAR_NAME]; X X /* This is a hack, but it makes things easier in DoTEST, q.v. */ X if (p == NULL) X return(p); X X /* Pick the "other" buffer then loop over the string to be expanded. */ X for (Flag = 1 - Flag, q = buff[Flag]; *p; ) X if (*p == '$') X if (*++p == '$') { X (void)sprintf(name, "%d", Pid()); X q += strlen(strcpy(q, name)); X p++; X } X else if (*p == '?') { X /* Fake it -- all commands always succeed, here. */ X *q++ = '0'; X *q = '\0'; X p++; X } X else { X /* Read this line carefully... */ X if (Closer = *p == '{' ? '}' : '\0') X p++; X for (n = name; *p && *p != Closer; ) X *n++ = *p++; X if (*p) X p++; X *n = '\0'; X q += strlen(strcpy(q, GetVar(name))); X } X else X *q++ = *p++; X *q = '\0'; X return(buff[Flag]); X} X X X/* X** Do a variable assignment of the form: X** var=value X** var="quoted value" X** var="...${var}..." X** etc. X*/ Xstatic void XDoASSIGN(Name) X register char *Name; X{ X register char *Value; X register char *q; X register char Quote; X X /* Split out into name:value strings, and deal with quoted values. */ X Value = IDX(Name, '='); X *Value = '\0'; X if (ISquot(*++Value)) X for (Quote = *Value++, q = Value; *++q && *q != Quote; ) X ; X else X for (q = Value; ISletr(*q); q++) X ; X *q = '\0'; X X SetVar(Name, Expand(Value)); X} X X/** X*** " O U T P U T " C O M M A N D S X**/ X X X/* X** Do a cat command. Understands the following: X** cat >arg1 <<arg2 X** cat >>arg1 <<arg2 X** cat >>arg1 /dev/null X** Except that arg2 is assumed to be quoted -- i.e., no expansion of meta-chars X** inside the "here" document is done. The IO redirection can be in any order. X*/ X/* ARGSUSED */ Xstatic int XDoCAT(ac, av) X int ac; X register char *av[]; X{ X register FILE *Out; X register char *Ending; X register char *Source; X register int V; X register int l; X X /* Parse the I/O redirecions. */ X for (V = TRUE, Source = NULL, Out = NULL, Ending = NULL; *++av; ) X if (EQ(*av, ">") && av[1]) { X av++; X /* This is a hack, but maybe MS-DOS doesn't have /dev/null? */ X Out = Running ? fopen(Expand(*av), "w") : stderr; X } X else if (EQ(*av, ">>") && av[1]) { X av++; X /* And besides, things are actually faster this way. */ X Out = Running ? fopen(Expand(*av), "a") : stderr; X } X else if (EQ(*av, "<<") && av[1]) { X for (Ending = *++av; *Ending == '\\'; Ending++) X ; X l = strlen(Ending); X } X else if (!EQ(Source = *av, "/dev/null")) X SynErr("CAT (bad input filename)"); X X if (Out == NULL || (Ending == NULL && Source == NULL)) { X Note("Missing parameter in CAT command.\n", (char *)NULL); X V = FALSE; X } X X /* Read the input, spit it out. */ X if (V && Running && Out != stderr) { X if (Source == NULL) X while (GetLine(FALSE) && !EQn(Text, Ending, l)) X (void)fputs(Text, Out); X (void)fclose(Out); X } X else X while (GetLine(FALSE) && !EQn(Text, Ending, l)) X ; X X return(V); X} X X X/* X** Do a SED command. Understands the following: X** sed sX^yyyyXX >arg1 <<arg2 X** sed -e sX^yyyyXX >arg1 <<arg2 X** Where the yyyy is a miniscule regular expression; see Matches(), above. X** The "X" can be any single character and the ^ is optional (sigh). No X** shell expansion is done inside the "here' document. The IO redirection X** can be in any order. X*/ X/* ARGSUSED */ Xstatic int XDoSED(ac, av) X int ac; X register char *av[]; X{ X register FILE *Out; X register char *Pattern; X register char *Ending; X register char *p; X register int V; X register int l; X register int i; X X /* Parse IO redirection stuff. */ X for (V = TRUE, Out = NULL, Pattern = NULL, Ending = NULL; *++av; ) X if (EQ(*av, ">") && av[1]) { X av++; X Out = Running ? fopen(Expand(*av), "w") : stderr; X } X else if (EQ(*av, ">>") && av[1]) { X av++; X Out = Running ? fopen(Expand(*av), "a") : stderr; X } X else if (EQ(*av, "<<") && av[1]) { X for (Ending = *++av; *Ending == '\\'; Ending++) X ; X l = strlen(Ending); X } X else X Pattern = EQ(*av, "-e") && av[1] ? *++av : *av; X X /* All there? */ X if (Out == NULL || Ending == NULL || Pattern == NULL) { X Note("Missing parameter in SED command.\n", (char *)NULL); X V = FALSE; X } X X /* Parse the substitute command and its pattern. */ X if (*Pattern != 's') { X Note("Bad SED command -- not a substitute.\n", (char *)NULL); X V = FALSE; X } X else { X Pattern++; X p = Pattern + strlen(Pattern) - 1; X if (*p != *Pattern || *--p != *Pattern) { X Note("Bad substitute pattern in SED command.\n", (char *)NULL); X V = FALSE; X } X else { X /* Now check the pattern. */ X if (*++Pattern == '^') X Pattern++; X for (*p = '\0', i = strlen(Pattern), p = Pattern; *p; p++) X if (*p == '[' || *p == '*' || *p == '$') { X Note("Bad meta-character in SED pattern.\n", (char *)NULL); X V = FALSE; X } X } X } X X /* Spit out the input. */ X if (V && Running && Out != stderr) { X while (GetLine(FALSE) && !EQn(Text, Ending, l)) X (void)fputs(Matches(Pattern, Text) ? &Text[i] : Text, Out); X (void)fclose(Out); X } X else X while (GetLine(FALSE) && !EQn(Text, Ending, l)) X ; X X return(V); X} X X/** X*** " S I M P L E " C O M M A N D S X**/ X X X/* X** Parse a cp command of the form: X** cp /dev/null arg X** We should check if "arg" is a safe file to clobber, but... X*/ Xstatic int XDoCP(ac, av) X int ac; X char *av[]; X{ X FILE *F; X X if (Running) { X if (ac != 3 || !EQ(av[1], "/dev/null")) X SynErr("CP"); X if (F = fopen(Expand(av[2]), "w")) { X (void)fclose(F); X return(TRUE); X } X Note("Can't create %s.\n", av[2]); X } X return(FALSE); X} X X X/* X** Do a mkdir command of the form: X** mkdir arg X*/ Xstatic int XDoMKDIR(ac, av) X int ac; X char *av[]; X{ X if (Running) { X if (ac != 2) X SynErr("MKDIR"); X if (mkdir(Expand(av[1]), 0777) >= 0) X return(TRUE); X Note("Can't make directory %s.\n", av[1]); X } X return(FALSE); X} X X X/* X** Do a cd command of the form: X** cd arg X** chdir arg X*/ Xstatic int XDoCD(ac, av) X int ac; X char *av[]; X{ X if (Running) { X if (ac != 2) X SynErr("CD"); X if (chdir(Expand(av[1])) >= 0) X return(TRUE); X Note("Can't cd to %s.\n", av[1]); X } X return(FALSE); X} X X X/* X** Do the echo command. Understands the "-n" hack. X*/ X/* ARGSUSED */ Xstatic int XDoECHO(ac, av) X int ac; X char *av[]; X{ X int Flag; X X if (Running) { X if (Flag = av[1] != NULL && EQ(av[1], "-n")) X av++; X while (*++av) X fprintf(stderr, "%s ", Expand(*av)); X if (!Flag) X fprintf(stderr, "\n"); X (void)fflush(stderr); X } X return(TRUE); X} X X X/* X** Generic "handler" for commands we can't do. X*/ Xstatic int XDoIT(ac, av) X int ac; X char *av[]; X{ X if (Running) X fprintf(stderr, "You'll have to do this yourself:\n\t%s ", *av); X return(DoECHO(ac, av)); X} X X X/* X** Do an EXIT command. X*/ Xstatic XDoEXIT(ac, av) X int ac; X char *av[]; X{ X ac = *++av ? atoi(Expand(*av)) : 0; X fprintf(stderr, "Exiting, with status %d\n", ac); X exit(ac); X /* NOTREACHED */ X} X X X/* X** Do an EXPORT command. Often used to make sure the archive is being X** unpacked with the Bourne (or Korn?) shell. We look for: X** export PATH blah blah blah X*/ Xstatic int XDoEXPORT(ac, av) X int ac; X char *av[]; X{ X if (ac < 2 || !EQ(av[1], "PATH")) X SynErr("EXPORT"); X return(TRUE); X} X X/** X*** F L O W - O F - C O N T R O L C O M M A N D S X**/ X X X/* X** Parse a "test" statement. Returns TRUE or FALSE. Understands the X** following tests: X** test {!} -f arg Is arg {not} a plain file? X** test {!} -d arg Is arg {not} a directory? X** test {!} $var -eq $var Is the variable {not} equal to the variable? X** test {!} $var != $var Is the variable {not} equal to the variable? X** test {!} ddd -ne `wc -c {<} arg` X** Is size of arg {not} equal to ddd in bytes? X** test -f arg -a $var -eq val X** Used by my shar, check for file clobbering X** These last two tests are starting to really push the limits of what is X** reasonable to hard-code, but they are common cliches in shell archive X** "programming." We also understand the [ .... ] way of writing test. X** If we can't parse the test, we show the command and ask the luser. X*/ Xstatic int XDoTEST(ac, av) X register int ac; X register char *av[]; X{ X register char **p; X register char *Name; X register FILE *DEVTTY; X register int V; X register int i; X char buff[MAX_LINE_SIZE]; X X /* Quick test. */ X if (!Running) X return(FALSE); X X /* See if we're called as "[ ..... ]" */ X if (EQ(*av, "[")) { X for (i = 1; av[i] && !EQ(av[i], "]"); i++) X ; X free(av[i]); X av[i] = NULL; X ac--; X } X X /* Ignore the "test" argument. */ X av++; X ac--; X X /* Inverted test? */ X if (EQ(*av, "!")) { X V = FALSE; X av++; X ac--; X } X else X V = TRUE; X X /* Testing for file-ness? */ X if (ac == 2 && EQ(av[0], "-f") && (Name = Expand(av[1]))) X return(GetStat(Name) && Ftype(Name) == F_FILE ? V : !V); X X /* Testing for directory-ness? */ X if (ac == 2 && EQ(av[0], "-d") && (Name = Expand(av[1]))) X return(GetStat(Name) && Ftype(Name) == F_DIR ? V : !V); X X /* Testing a variable's value? */ X if (ac == 3 && (EQ(av[1], "-eq") || EQ(av[1], "="))) X return(EQ(Expand(av[0]), Expand(av[2])) ? V : !V); X if (ac == 3 && (EQ(av[1], "-ne") || EQ(av[1], "!="))) X return(!EQ(Expand(av[0]), Expand(av[2])) ? V : !V); X X /* Testing a file's size? */ X if (ac == (av[5] && EQ(av[5], "<") ? 7 : 6) && isdigit(av[0][0]) X && (EQ(av[1], "-ne") || EQ(av[1], "-eq")) X && EQ(av[2], "`") && EQ(av[3], "wc") X && EQ(av[4], "-c") && EQ(av[ac - 1], "`")) { X if (GetStat(av[ac - 2])) { X if (EQ(av[1], "-ne")) X return(Fsize(av[ac - 2]) != atol(av[0]) ? V : !V); X return(Fsize(av[ac - 2]) == atol(av[0]) ? V : !V); X } X Note("Can't get status of %s.\n", av[ac - 2]); X } X X /* Testing for existing, but can clobber? */ X if (ac == 6 && EQ(av[0], "-f") && EQ(av[2], "-a") && EQ(av[4], "-eq") X && GetStat(Name = Expand(av[1])) && Ftype(Name) == F_FILE) X return(EQ(Expand(av[3]), Expand(av[5])) ? V : !V); X X /* I give up -- print it out, and let's ask Mikey, he can do it... */ X fprintf(stderr, "Can't parse this test:\n\t"); X for (i = FALSE, p = av; *p; p++) { X fprintf(stderr, "%s ", *p); X if (p[0][0] == '$') X i = TRUE; X } X if (i) { X fprintf(stderr, "\n(Here it is with shell variables expanded...)\n\t"); X for (p = av; *p; p++) X fprintf(stderr, "%s ", Expand(*p)); X } X fprintf(stderr, "\n"); X X DEVTTY = fopen(THE_TTY, "r"); X do { X fprintf(stderr, "Is value true/false/quit [tfq] (q): "); X (void)fflush(stderr); X clearerr(DEVTTY); X if (fgets(buff, sizeof buff, DEVTTY) == NULL X || buff[0] == 'q' || buff[0] == 'Q' || buff[0] == '\n') X SynErr("TEST"); X if (buff[0] == 't' || buff[0] == 'T') { X (void)fclose(DEVTTY); X return(TRUE); X } X } while (buff[0] != 'f' && buff[0] != 'F'); X (void)fclose(DEVTTY); X return(FALSE); X} X X X/* X** Do an IF statement. X*/ Xstatic int XDoIF(ac, av) X register int ac; X register char *av[]; X{ X register char **p; X register int Flag; X char *vec[MAX_WORDS]; X char **Pushed; X X /* Skip first argument. */ X if (!EQ(*++av, "[") && !EQ(*av, "test")) X SynErr("IF"); X ac--; X X /* Look for " ; then " on this line, or "then" on next line. */ X for (Pushed = NULL, p = av; *p; p++) X if (Flag = EQ(*p, ";")) { X if (p[1] == NULL || !EQ(p[1], "then")) X SynErr("IF"); X *p = NULL; X ac -= 2; X break; X } X if (!Flag) { X (void)GetLine(TRUE); X if (Argify(vec) > 1) X Pushed = &vec[1]; X if (!EQ(vec[0], "then")) X SynErr("IF (missing THEN)"); X } X X if (DoTEST(ac, av)) { X if (Pushed) X (void)Exec(Pushed); X while (GetLine(TRUE)) { X if ((ac = Argify(vec)) == 1 && EQ(vec[0], "fi")) X break; X if (EQ(vec[0], "else")) { X DoUntil("fi", FALSE); X break; X } X (void)Exec(vec); X } X } X else X while (GetLine(TRUE)) { X if ((ac = Argify(vec)) == 1 && EQ(vec[0], "fi")) X break; X if (EQ(vec[0], "else")) { X if (ac > 1) X (void)Exec(&vec[1]); X DoUntil("fi", Running); X break; X } X } X return(TRUE); X} X X X/* X** Do a FOR statement. X*/ Xstatic int XDoFOR(ac, av) X register int ac; X register char *av[]; X{ X register char *Var; X register char **Values; X register int Found; X long Here; X char *vec[MAX_WORDS]; X X /* Check usage, get variable name and eat noise words. */ X if (ac < 4 || !EQ(av[2], "in")) X SynErr("FOR"); X Var = av[1]; X ac -= 3; X av += 3; X X /* Look for "; do" on this line, or just "do" on next line. */ X for (Values = av; *++av; ) X if (Found = EQ(*av, ";")) { X if (av[1] == NULL || !EQ(av[1], "do")) X SynErr("FOR"); X *av = NULL; X break; X } X if (!Found) { X (void)GetLine(TRUE); X if (Argify(vec) != 1 || !EQ(vec[0], "do")) X SynErr("FOR (missing DO)"); X } X X for (Here = ftell(Input); *Values; ) { X SetVar(Var, *Values); X DoUntil("done", Running); X ; X /* If we're not Running, only go through the loop once. */ X if (!Running) X break; X if (*++Values && (fseek(Input, Here, 0) < 0 || ftell(Input) != Here)) X SynErr("FOR (can't seek back)"); X } X X return(TRUE); X} X X X/* X** Do a CASE statement of the form: X** case $var in X** text1) X** ... X** ;; X** esac X** Where text1 is a simple word or an asterisk. X*/ Xstatic int XDoCASE(ac, av) X register int ac; X register char *av[]; X{ X register int FoundIt; X char *vec[MAX_WORDS]; X char Value[MAX_VAR_VALUE]; X X if (ac != 3 || !EQ(av[2], "in")) X SynErr("CASE"); X (void)strcpy(Value, Expand(av[1])); X X for (FoundIt = FALSE; GetLine(TRUE); ) { X ac = Argify(vec); X if (EQ(vec[0], "esac")) X break; X /* This is for vi: (-; sigh. */ X if (ac != 2 || !EQ(vec[1], ")")) X SynErr("CASE"); X if (!FoundIt && (EQ(vec[0], Value) || EQ(vec[0], "*"))) { X FoundIt = TRUE; X if (Running && ac > 2) X (void)Exec(&vec[2]); X DoUntil(";;", Running); X } X else X DoUntil(";;", FALSE); X } X return(TRUE); X} X X X X/* X** Dispatch table of known commands. X*/ Xstatic COMTAB Dispatch[] = { X { ":", DoIT }, X { "cat", DoCAT }, X { "case", DoCASE }, X { "cd", DoCD }, X { "chdir", DoCD }, X { "chmod", DoIT }, X { "cp", DoCP }, X { "echo", DoECHO }, X { "exit", DoEXIT }, X { "export", DoEXPORT }, X { "for", DoFOR }, X { "if", DoIF }, X { "mkdir", DoMKDIR }, X { "rm", DoIT }, X { "sed", DoSED }, X { "test", DoTEST }, X { "[", DoTEST }, X { "", NULL } X}; X X X/* X** Dispatch on a parsed line. X*/ Xint XExec(av) X register char *av[]; X{ X register int i; X register COMTAB *p; X X /* We have to re-calculate this because our callers can't always X pass the count down to us easily. */ X for (i = 0; av[i]; i++) X ; X if (i) { X /* Is this a command we know? */ X for (p = Dispatch; p->Func; p++) X if (EQ(av[0], p->Name)) { X i = (*p->Func)(i, av); X break; X } X X /* If not a command, try it as a variable assignment. */ X if (p->Func == NULL) X /* Yes, we look for "=" in the first word, but pass down X the whole line. */ X if (IDX(av[0], '=')) X DoASSIGN(Text); X else X Note("Command %s unknown.\n", av[0]); X X /* Free the line. */ X for (i = 0; av[i]; i++) X free(av[i]); X } X return(TRUE); X} X X X/* X** Do until we reach a specific terminator. X*/ Xstatic XDoUntil(Terminator, NewVal) X char *Terminator; X int NewVal; X{ X char *av[MAX_WORDS]; X int OldVal; X X for (OldVal = Running, Running = NewVal; GetLine(TRUE); ) X if (Argify(av)) { X if (EQ(av[0], Terminator)) X break; X (void)Exec(av); X } X X Running = OldVal; X} END_OF_parser.c if test 23917 -ne `wc -c <parser.c`; then echo shar: \"parser.c\" unpacked with wrong size!? fi # end of overwriting check fi echo shar: Extracting \"unshar.c\" \(8109 characters\) if test -f unshar.c ; then echo shar: Will not over-write existing file \"unshar.c\" else sed "s/^X//" >unshar.c <<'END_OF_unshar.c' X/* X** UNSHAR X** Unpack shell archives that might have gone through mail, notes, news, etc. X** X** Options: X** -c dir Change to directory 'dir' before starting X** -d dir Change to directory 'dir' before starting X** -f Don't try to intuit file type X** -s Save pre-shar headers into a file X** -n Don't save pre-shar headers into a file X*/ X X#include "shar.h" XRCS("$Header: unshar.c,v 1.16 87/03/18 14:03:19 rs Exp $") X X X/* X** Print error message and die. X*/ Xstatic void XQuit(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*/ 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; *p == '-' || *p == '_' || *p == '.' || isalnum(*p); i++) X p++; X return(i && *p == ':'); X} 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 (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). 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 (!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*/ 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] == '.' && 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, "unshar: %s is %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*/ Xstatic void XUnshar(Name, Stream, Saveit, Forced) X char *Name; 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 (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 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 for (Forced = 0; (i = getopt(ac, av, "c:d:fns")) != EOF; ) X switch (i) { X default: X Quit("Usage: unshar [-fs] [-c directory] [input files]"); X case 'c': X case 'd': X p = optarg; X break; X case 'f': X Forced++; X break; X case 'n': X Saveit = 0; X case 's': X Saveit++; 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' || (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. */ X while (chdir(p) < 0) X if (mkdir(p, 0777) < 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 /* Process args. */ X if (*av) 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: File '%s' not found.\n", *av); X else { X Unshar(*av, Stream, Saveit, Forced); X (void)fclose(Stream); X } X } X else X Unshar((char *)NULL, stdin, Saveit, Forced); X X /* That's all she wrote. */ X exit(0); X} END_OF_unshar.c if test 8109 -ne `wc -c <unshar.c`; then echo shar: \"unshar.c\" unpacked with wrong size!? fi # end of overwriting check fi echo shar: End of archive 2 \(of 2\). cp /dev/null ark2isdone MISSING="" for I in 1 2 ; do if test ! -f ark${I}isdone ; then MISSING="${MISSING} ${I}" fi done if test "${MISSING}" = "" ; then echo You have unpacked both 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 -- -- Rich $alz "Drug tests p**s me off" Mirror Systems, Cambridge Massachusetts rs@mirror.TMC.COM {adelie, mit-eddie, ihnp4, harvard!wjh12, cca, cbosgd, seismo}!mirror!rs