[net.sources] modified unshar

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