guido@boring.UUCP (02/01/85)
I fixed a few small things in 'unshar.c', recently posted here. While unshar describes itself as a 'filter', it fails to have the filter property: when called without file arguments, it should read its standard input! I also added a '-d' option in the style of 'patch' so you can say '|unshar -d some_directory' while in rn. You may notice a copy of getopt tacked to the end, to make the program self-contained (I also added the missing 'quit' function). Since the program is so small, I post the whole thing anew, rather than diffs. Guido van Rossum, "Stamp Out BASIC" Committee, CWI, Amsterdam guido@mcvax.UUCP #!/bin/sh echo 'Start of modified unshar distribution, part 01 of 01:' echo 'x - unshar.c' sed 's/^X//' > unshar.c << '/' X/**************************************************************** X * unshar.c: Unpackage one or more shell archive files X * X * Usage: unshar [ -d directory ] [ file ] ... X * X * Description: unshar is a filter which removes the front part X * of a file and passes the rest to the 'sh' command. X * It understands phrases like "cut here", and also X * knows about shell comment characters and the Unix X * commands "echo", "cat", and "sed". X * X * HISTORY X * 1-Feb-85 Guido van Rossum (guido@mcvax) at CWI, Amsterdam X * Added missing 'quit' routine; X * added -d flag to change to directory first; X * added filter mode (read stdin when no arguments); X * added 'getopt' to get flags (makes it self-contained). X * 29-Jan-85 Michael Mauldin (mlm) at Carnegie-Mellon University X * Created. X ****************************************************************/ X X# include <stdio.h> X# define EOL '\n' X Xextern char *optarg; Xextern int optind; X Xmain (argc, argv) Xint argc; Xchar *argv[]; X{ int i, ch; X FILE *in; X X /* Process options */ X X while ((ch = getopt (argc, argv, "d:")) != EOF) { X switch (ch) { X case 'd': X if (chdir (optarg) == -1) { X fprintf (stderr, "unshar: cannot chdir to '%s'\n", optarg); X exit(2); X } X break; X default: X quit (2, "Usage: unshar [-d directory] [file] ...\n"); X } X } X X if (optind < argc) X { for (i= optind; i < argc; ++i) X { if ((in = fopen (argv[i], "r")) == NULL) X { fprintf (stderr, "unshar: file '%s' not found\n", argv[i]); X exit (1); X } X process (argv[i], in); X fclose (in); X } X } X else X process ("standard input", stdin); X X exit (0); X} X X Xprocess (name, in) Xchar *name; XFILE *in; X{ char ch; X FILE *shpr, *popen(); X X if (position (name, in)) X { printf ("%s:\n", name); X if ((shpr = popen ("sh", "w")) == NULL) X quit (1, "unshar: cannot open 'sh' process\n"); X X while ((ch = fgetc (in)) != EOF) X fputc (ch, shpr); X X pclose (shpr); X } X} X X/**************************************************************** X * position: position 'fil' at the start of the shell command X * portion of a shell archive file. X ****************************************************************/ X Xposition (fn, fil) Xchar *fn; XFILE *fil; X{ char buf[BUFSIZ]; X long pos, ftell (); X X /* Results from star matcher */ X static char res1[BUFSIZ], res2[BUFSIZ], res3[BUFSIZ], res4[BUFSIZ]; X static char *result[] = { res1, res2, res3, res4 }; X X rewind (fil); X X while (1) X { /* Record position of the start of this line */ X pos = ftell (fil); X X /* Read next line, fail if no more */ X if (fgets (buf, BUFSIZ, fil) == NULL) X { fprintf (stderr, "unshar: found no shell commands in %s\n", fn); X return (0); X } X X /* Bail out if we see C preprocessor commands or C comments */ X if (stlmatch (buf, "#include") || stlmatch (buf, "# include") || X stlmatch (buf, "#define") || stlmatch (buf, "# define") || X stlmatch (buf, "#ifdef") || stlmatch (buf, "# ifdef") || X stlmatch (buf, "#ifndef") || stlmatch (buf, "# ifndef") || X stlmatch (buf, "/*")) X { fprintf (stderr, X "unshar: %s looks like raw C code, not a shell archive\n", fn); X return (0); X } X X /* Does this line start with a shell command or comment */ X if (stlmatch (buf, "#") || stlmatch (buf, ":") || X stlmatch (buf, "echo ") || stlmatch (buf, "sed ") || X stlmatch (buf, "cat ")) X { fseek (fil, pos, 0); return (1); } X X /* Does this line say "Cut here" */ X if (smatch (buf, "*CUT*HERE*", result) || X smatch (buf, "*cut*here*", result) || X smatch (buf, "*TEAR*HERE*", result) || X smatch (buf, "*tear*here*", result) || X smatch (buf, "*CUT*CUT*", result) || X smatch (buf, "*cut*cut*", result)) X { X /* Read next line after "cut here", skipping blank lines */ X while (1) X { pos = ftell (fil); X X if (fgets (buf, BUFSIZ, fil) == NULL) X { fprintf (stderr, X "unshar: found no shell commands after 'cut' in %s\n", fn); X return (0); X } X X if (*buf != '\n') break; X } X X /* Win if line starts with a comment character of lower case letter */ X if (*buf == '#' || *buf == ':' || (('a' <= *buf) && ('z' >= *buf))) X { fseek (fil, pos, 0); X return (1); X } X X /* Cut here message lied to us */ X fprintf (stderr, "unshar: %s is probably not a shell archive,\n", fn); X fprintf (stderr, " the 'cut' line was followed by: %s", buf); X return (0); X } X } X} X X/***************************************************************** X * stlmatch -- match leftmost part of string X * X * Usage: i = stlmatch (big,small) X * int i; X * char *small, *big; X * X * Returns 1 iff initial characters of big match small exactly; X * else 0. X * X * HISTORY X * 18-May-82 Michael Mauldin (mlm) at Carnegie-Mellon University X * Ripped out of CMU lib for Rog-O-Matic portability X * 20-Nov-79 Steven Shafer (sas) at Carnegie-Mellon University X * Rewritten for VAX from Ken Greer's routine. X * X * Originally from klg (Ken Greer) on IUS/SUS UNIX X *****************************************************************/ X Xint stlmatch (big, small) Xchar *small, *big; X{ register char *s, *b; X s = small; X b = big; X do X { if (*s == '\0') X return (1); X } X while (*s++ == *b++); X return (0); X} X X/***************************************************************** X * smatch: Given a data string and a pattern containing one or X * more embedded stars (*) (which match any number of characters) X * return true if the match succeeds, and set res[i] to the X * characters matched by the 'i'th *. X *****************************************************************/ X Xsmatch (dat, pat, res) Xregister char *dat, *pat, **res; X{ register char *star = 0, *starend, *resp; X int nres = 0; X X while (1) X { if (*pat == '*') X { star = ++pat; /* Pattern after * */ X starend = dat; /* Data after * match */ X resp = res[nres++]; /* Result string */ X *resp = '\0'; /* Initially null */ X } X else if (*dat == *pat) /* Characters match */ X { if (*pat == '\0') /* Pattern matches */ X return (1); X pat++; /* Try next position */ X dat++; X } X else X { if (*dat == '\0') /* Pattern fails - no more */ X return (0); /* data */ X if (star == 0) /* Pattern fails - no * to */ X return (0); /* adjust */ X pat = star; /* Restart pattern after * */ X *resp++ = *starend; /* Copy character to result */ X *resp = '\0'; /* null terminate */ X dat = ++starend; /* Rescan after copied char */ X } X } X} X X/***************************************************************** X * Addendum: quit subroutine (print a message and exit) X *****************************************************************/ X Xquit (status, message) Xint status; Xchar *message; X{ X fprintf(stderr, message); X exit(status); X} X X/***************************************************************** X * Public Domain getopt routine X *****************************************************************/ X X/* X * get option letter from argument vector X */ Xint opterr = 1, /* useless, never set or used */ X optind = 1, /* index into parent argv vector */ X optopt; /* character checked for validity */ Xchar *optarg; /* argument associated with option */ X X#define BADCH (int)'?' X#define EMSG "" X#define tell(s) fputs(*nargv,stderr);fputs(s,stderr); \ X fputc(optopt,stderr);fputc('\n',stderr);return(BADCH); X Xgetopt(nargc,nargv,ostr) Xint nargc; Xchar **nargv, X *ostr; X{ X static char *place = EMSG; /* option letter processing */ X register char *oli; /* option letter list index */ X char *index(); X X if(!*place) { /* update scanning pointer */ X if(optind >= nargc || *(place = nargv[optind]) != '-' || !*++place) return(EOF); X if (*place == '-') { /* found "--" */ X ++optind; X return(EOF); X } X } /* option letter okay? */ X if ((optopt = (int)*place++) == (int)':' || !(oli = index(ostr,optopt))) { X if(!*place) ++optind; X tell(": illegal option -- "); X } X if (*++oli != ':') { /* don't need argument */ X optarg = NULL; X if (!*place) ++optind; X } X else { /* need an argument */ X if (*place) optarg = place; /* no white space */ X else if (nargc <= ++optind) { /* no arg */ X place = EMSG; X tell(": option requires an argument -- "); X } X else optarg = nargv[optind]; /* white space */ X place = EMSG; X ++optind; X } X return(optopt); /* dump back option letter */ X} / echo 'Part 01 of modified unshar distribution complete.' exit