[comp.os.minix] Unshar.c patch for Andy's new shar files

wkt@csadfa.cs.adfa.oz.au (Warren Toomey) (02/20/90)

Andy has used a new shar program with some the 1.5.3 patches, and all of
the lates beta-group patches; unshar(1) cannot handle lines of the form:

	echo Extracting 'sys_00.uue'
	sed 's/^X//' > 'sys_00.uue' << '+ END-OF-FILE ''sys_00.uue'

I've just changed one of the functions to handle this, and the old
shar format. The following is an alpha-test of the new unshar(1).
Please test it on as many shar formats as possible!

BTW, Andy could you report your indent.pro file which is used for all
the files in Minix. I have to repost the whole file as I can't get
my `working source' into the Minix style. Thanks!

	Warren Toomey	wkt@csadfa.oz.au@munnari.oz.au[@uunet.uu.net]


---------------------------- unshar here -------------------------------
echo x - unshar.c
sed '/^X/s///' > unshar.c << '/'
X/* unshar - extract files from a shell archive	Author: Warren Toomey */
X
X
X/*
X * Unshar - extract files from shell archive 
X *
X * Written by Warren Toomey [wkt@csadfa.oz.au@munnari.oz@uunet.uu.net] You may
X * freely copy or give away this source as long as this notice remains
X * intact. 
X *
X * Definitions used by unshar 
X */
X
X
X#include <stdio.h>
X
X/* Methods of unsharing */
X#define UNKNOWN	0
X#define BRUTAL	1
X
X/* Whitespace indicators */
X#define WHITE	0
X#define NOWHITE 1
X
X/* Leading character indicators */
X#define NOX	0
X#define YESX	1
X
X/* Emulation types available */
X
X#define NUMTOKS    4		/* Must change NUMTOKS to equal the */
X/* Define UNKNOWN  0 *//* number of emulation types */
X#define SED	   1
X#define GRES 	   2
X#define CAT	   3
X
Xstatic char    *token[NUMTOKS] /= *The list of emulation types ! */
X{"",
X "sed",
X "gres",
X "cat"
X};
X
X
X/* Misc. constants */
X#define BUFSIZE	512		/* Size of line buffer */
X
X/* Global variables */
Xint             table;		/* Generate a table, or extract */
Xint             verbose;	/* Unshar verbosely - debugging */
Xint             numext;		/* Number of files to extract */
Xint             binfile;	/* Binary file - err indicator */
Xchar           *exfile[100];	/* Files to extract */
X
X
X#define getline(x,y)	fgetline(stdin,x,y)
X
Xint
Xfgetline(zin, how, buf)		/* Get a line from a file */
X	FILE           *zin;
X	int             how;	/* Ignore leading whitespace if */
X	char           *buf;	/* how == NOWHITE */
X{
X	int             ch = 0;
X
X	*buf = 0;		/* Null the buffer */
X	if (how == NOWHITE) {	/* If skip any whitespace */
X		while (((ch = fgetc(zin)) == ' ') || (ch == '\t'));
X		if (ch == EOF)
X			return (EOF);	/* Returning EOF or 0 */
X		if (ch == '\n')
X			return (0);
X		*buf++ = ch;	/* Put char in buffer */
X	}
X	while ((ch = fgetc(zin)) != '\n') {	/* Now get the line */
X		if (ch == EOF) {
X			*buf = 0;
X			return (EOF);
X		}
X		if (ch > 127) {
X			binfile = 1;
X			return (0);
X		}
X		*buf++ = ch;
X	}
X
X	*buf = 0;		/* Finally null-terminate the buffer */
X	return (0);		/* and return */
X}
X
X
X
Xchar           *
Xgetstring(buf)			/* Get the next string from the buffer */
X	char           *buf;	/* ignoring any quotes */
X{
X	char            out[BUFSIZE];
X	char           *temp = out;
X	char            inquotes = 0, ok = 1;
X	while ((*buf == ' ') || (*buf == '\t'))
X		buf++;		/* Skip whitespace */
X
X	if (verbose)
X		printf("In getstring...\n");
X	*temp = 0;
X	while (ok) {		/* Parse line */
X#ifdef DEBUG
X		if (verbose)
X			printf("*%s*\n", out);
X#endif
X		switch (*buf) {
X		case '\"':
X		case '\'':
X			buf++;
X			inquotes != inquotes;	/* Toggle inquotes */
X			break;
X		case 0:
X		case '\n':	/* Stop on <, >, NULL */
X		case '>':	/* \n, and sometimes */
X		case '<':
X			ok = 0;
X			break;	/* space & tab */
X		case '\t':
X		case ' ':
X			if (!inquotes)
X				ok = 0;
X		case '\\':
X			if (!inquotes) {	/* Ignore backquotes */
X				buf++;
X				break;
X			}
X		default:
X			*temp++ = *buf++;	/* Copy chars :-) */
X		}
X	}
X	*temp = 0;
X	if (verbose)
X		printf("Returning *%s*\n", out);
X	return (out);
X}
X
X
Xint
Xfirstword(buf)			/* Return token value of first word */
X	char           *buf;	/* in the buffer. Assume no leading */
X{				/* whitespace in the buffer */
X	int             i;
X
X	for (i = 1; i < NUMTOKS; i++)
X		if (strncmp(buf, token[i], strlen(token[i])) == 0)
X			return (i);
X
X	return (UNKNOWN);
X}
X
X
Xint
Xmustget(s1)			/* Return 1 if s1 is in the list of  */
X	char           *s1;	/* files to extract. Return 0 if not */
X{
X	int             i;
X
X	if (numext == 0)
X		return (0);
X	for (i = 0; i < numext; i++)
X		if (!strcmp(s1, exfile[i]))
X			return (1);
X	return (0);
X}
X
X
Xvoid
Xextract(how, file, end, lead)	/* Extract file, up until end word */
X	int             how;	/* If how==YESX, then ignore lead   */
X	char           *file;	/* character on every line */
X	char           *end;
X	int             lead;
X{
X	FILE           *zout;
X	char            line[BUFSIZE];
X	char           *temp;
X	int             ch;
X
X	zout = fopen(file, "w");/* Open output file */
X	if (zout == NULL) {
X		perror("unshar1");
X		return;
X	}
X	while (1) {
X		binfile = 0;
X		ch = getline(WHITE, line);	/* Get a line of file */
X		temp = line;
X		if (binfile || (ch == EOF)) {
X			fprintf(zout, "%s\n", line);
X			fclose(zout);
X			return;
X		}
X		if ((how == YESX) && (*temp == lead))
X			temp++;	/* Skip any lead */
X
X		if (strcmp(temp, end) == 0) {	/* If end word */
X			fclose(zout);	/* close the file */
X			return;
X		}
X		fprintf(zout, "%s\n", temp);
X	}
X}
X
X
Xvoid
Xgetnames(buf, file, word)	/* Get the file & end word */
X	char           *buf, *file, *word;	/* from the buffer */
X{
X	char           *temp;
X
X	temp = buf;
X	if (verbose)
X		printf("Getnames: buf is %s\n", buf);
X
X	while (*temp != 0) {	/* Scan along buffer */
X		switch (*temp) {/* Get file or end word */
X		case '>':
X			strcpy(file, getstring(++temp));	/* Get the file name */
X			break;
X		case '<':
X			if (*(++temp) == '<')
X				++temp;	/* Skip 2nd < */
X			strcpy(word, getstring(temp));	/* Get next word */
X			break;
X		default:
X			temp++;
X		}
X	}
X}
X
X
X
Xvoid
Xdisembowel()
X{				/* Unshar brutally! */
X	char            buf[BUFSIZE];	/* Line buffer */
X	char            file[BUFSIZE];	/* File name */
X	char            word[BUFSIZE];	/* Word buffer */
X	int             ch, x;
X
X	if (verbose)
X		printf("Entering disembowel\n");
X	x = 'X';		/* Leading X character */
X	while (1) {
X		binfile = 0;
X		ch = getline(NOWHITE, buf);	/* Get a line from file */
X		if (ch == EOF)
X			return;
X		if (binfile)
X			continue;
X
X		switch (firstword(buf)) {	/* Extract, depending on
X						 * first word */
X		case CAT:
X			if (verbose)
X				printf("About to do getnames\n");
X			getnames(buf, file, word);
X			if (table == 0) {
X				if ((numext == 0) || (mustget(file))) {
X					printf("unshar: Extracting  %s\n", file);
X					if (verbose)
X						printf("        stopping at %s\n", word);
X					extract(NOX, file, word, x);
X				}
X			} else
X				printf("  %s\n", file);
X			break;
X		case GRES:
X		case SED:
X			if (verbose)
X				printf("About to do getnames\n");
X			getnames(buf, file, word);
X			if (table == 0) {
X				if ((numext == 0) || (mustget(file))) {
X					printf("unshar: Extracting  %s\n", file);
X					if (verbose)
X						printf("        stopping at %s\n", word);
X					extract(YESX, file, word, x);
X				}
X			} else
X				printf("  %s\n", file);
X			break;
X		default:
X			break;
X		}
X	}
X}
X
X
X
Xusage()
X{
X	fprintf(stderr, "Usage: unshar [-t] [-b] [-v] [-xfile] [file(s)]\n");
X	exit(0);
X}
X
X
Xmain(argc, argv)
X	int             argc;
X	char           *argv[];
X{
X	extern int      optind;
X	extern char    *optarg;
X	int             i, c, first;
X
X	FILE           *zin;	/* Dummy file descriptor */
X	int             method;	/* Method of unsharing */
X
X	method = BRUTAL;	/* Only BRUTAL currently available */
X	table = 0;		/* Don't generate a table */
X	verbose = 0;		/* Nor be very verbose */
X	numext = 0;		/* Initially no files to extract */
X
X
X	while ((c = getopt(argc, argv, "x:tbv")) != EOF)
X		switch (c) {
X		case 't':
X			table = 1;	/* Get the various options */
X			break;
X		case 'b':
X			method = BRUTAL;
X			break;
X		case 'v':
X			verbose = 1;
X			break;
X		case 'x':
X			exfile[numext] = (char *) malloc(strlen(optarg) + 1);
X			strcpy(exfile[numext++], optarg);
X			break;
X		default:
X			usage();
X		}
X
X	if (argc == 1)
X		first = argc;	/* Find first file argument */
X	else
X		for (first = 1; first < argc; first++)
X			if (argv[first][0] != '-')
X				break;
X
X	if (first == argc) {	/* If no file argument *//* use stdin only */
X		switch (method) {
X		case BRUTAL:
X			disembowel();	/* Unshar brutally! */
X			break;
X		default:
X			fprintf(stderr, "unshar: Unknown method of unsharing\n");
X			exit(1);
X		}
X	} else
X		for (i = first; i < argc; i++) {	/* open stdio with every
X							 * file */
X			if (table)
X				printf("%s:\n", argv[i]);
X			fclose(stdin);
X			if ((zin = fopen(argv[i], "r")) == NULL) {
X				perror("unshar2");
X				exit(1);
X			}
X			switch (method) {
X			case BRUTAL:
X				disembowel();	/* Unshar brutally! */
X				break;
X			default:
X				fprintf(stderr, "unshar: Unknown method of unsharing\n");
X				exit(1);
X			}
X		}
X	exit(0);
X}
/
     Warren Toomey VK2XWT, still 386-less.
     Deep in the bowels of ADFA Comp Science.
`The rain is coming in thru the windows, is that ok?'