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