/*
 *	unshar	-- undo a shell archive file
 *	(C) Copyright June 4 1987 by Craig Norborg
 *	Permission given to use this code in any form as long as it is
 *	not sold or marketed in any form without the written permission
 *	of its author.	Removal of this copyright notice is expressly
 *	forbidden as well as any alteration of it.
 */
/*
 *	Here is a small unshar program written to be as portable as possible.
 *	It was written under Aztec C on an Amiga and tested on a VAX 11/780,
 *	pdp11/70 and a Sequent Balance 21000.  Any bug reports or enhancements
 *	should be reported to the author.  Some further enhancements may
 *	include the correct handling of sub-directories and the handling of
 *	btoa/atob type shars.  If you find a type of shar that this code
 *	does not work on, please send it to me, doc@j.cc.purdue.edu.
 */

#include <stdio.h>
#include <ctype.h>
#include "/version.h"

IDENT(".00");

#ifdef unix
#include <sys/file.h>
#endif unix
#ifdef AMIGA
#define F_OK	0
#endif AMIGA

#define BUFSIZE 512		/* Max length of an input line */
#define STRLEN	25		/* Max length of a file name or delimiter */
#define CAT	"cat"           /* The name of the 'cat' program */
#define SED	"sed"           /* The name of the 'sed' program */
#define TEST	"if test"       /* Leader for test types */

/*
 * This is a small routine that given the beginning of a quoted, backslashed
 * or just plain string, will return it in a given buffer.
 */
void
copystring(source, dest)
char *source, *dest;
{
	register int i = 0;
	char c;

	if ('\'' == *source || '\"' == *source) {/* Is it a quoted string? */
		c = *source;
		while (c != *++source)
			dest[i++] = *source;
		source++;
	} else if ('\\' == *source) {                   /* Is it a backslashed string? */
		while (!isspace(*++source))
			dest[i++] = *source;
	} else {								/* Just a string */
		while (!isspace(*source)) {
			dest[i++] = *source;
			source++;
		}
	}
	dest[i] = '\0';
}

void
wordcount(buf, filename, wc)
char *buf, *filename;
int wc;
{
	if (wc != atoi(buf)) {
		(void) printf("Error unsharing %s (wc should have been %d, but was %d)\n", filename, atoi(buf), wc);
	}
}

int
checkfile(string)
char *string;
{
	char filename[BUFSIZE];

	while (0 != isspace(*string))
		string++;

	copystring(string, filename);
	if (0 == access(filename, F_OK))
		return 1;

	return 0;
}

/*
 * This is a small routine that given a 'cat' or 'sed' string, will pull out
 * the end of file string and the file name
 */
void
getendfile(line, end, file)
char   *line,			/* The 'sed' or 'cat' string */
       *end,			/* Place to store the end of file marker */
       *file;			/* Place for the filename */
{
	char   *tmp, *strrchr();

	/*
	 * This section of code finds the end of file string.  It assumes
	 * that the eof string is the string of characters immediately
	 * following the last '<' and that it has either a '\' preceding it
	 * or is surrounded by single quotes.
	 */
	tmp = (char *) strrchr(line, '<');       /* Find the last '<' on the
											 * line */
	while (isspace(*++tmp))
		;	/* Do nothing */
	copystring(tmp, end);

	/*
	 * This section of code finds the name of the file.  It assumes that
	 * the name of the file is the string immediately following the last
	 * '>' in the line
	 */
	tmp = (char *) strrchr(line, '>');
	while (isspace(*++tmp))
		;	/* Do Nothing */
	copystring(tmp, file);

#ifdef DEBUG
	(void) printf("EOF = %s, FILE = %s\n", end, file);
#endif DEBUG
}

int
main(argc, argv)
int	argc;
char  **argv;
{
	FILE   *fp, *dfp, *fopen();     /* input file pointer and dest file
					 * pointer */
	char	buf[BUFSIZE],		/* line buffer */
		prefix[STRLEN], 	/* SED leader if any */
		endstring[STRLEN],	/* EOF marker */
		filename[STRLEN];	/* file name */
	int	infile = 0,			/* var to tell if we're in the middle of a
								 * file or not */
		wc = 0, 			/* variable to keep a word count */
			fileexists = 0; 	/* does the file exist? */

	if (1 == argc) {        /* check usage */
		(void) printf("usage: unshar <file>");
	}
	if (NULL == (fp = fopen(argv[1], "r"))) {
		(void) printf("Error opening input file\n");
		exit(1);
	}
	while (NULL != fgets(buf, BUFSIZE, fp)) {       /* while there are lines
							 * to get */
#ifdef DEBUG
		puts(buf);
#endif DEBUG

		if (0 == infile) {      /* if we are not in the middle of a
					 * file */
			if ('#' == buf[0])      /* comment? */
				continue;

			/* Is this a CAT type shar? */
			if (0 == strncmp(buf, CAT, strlen(CAT))) {
				prefix[0] = '\0';
				getendfile(buf, endstring, filename);
				if (fileexists != 0) {
					fprintf(stderr, "File exists (%s), skipping\n", filename);
					fileexists = 0;
					continue;
				}
				if (NULL == (dfp = fopen(filename, "w"))) {
					(void) printf("Error opening output file %s\n", filename);
					exit(1);
				}
				(void) printf("Extracting %s ... ", filename);
				(void) fflush(stdout);
				infile = 1;
				wc = 0;
				continue;
			}
			/* Is it a SED type shar? */
			if (0 == strncmp(buf, SED, strlen(SED))) {
				register int i = 0, j = 0;

				while ('^' != buf[i++])
					;
				while ('/' != buf[i]) {
					prefix[j++] = buf[i++];
				}
				prefix[j] = '\0';
				getendfile(&buf[i], endstring, filename);
				if (fileexists != 0) {
					fprintf(stderr, "File exists (%s), skipping\n", filename);
					fileexists = 0;
					continue;
				}
				if (NULL == (dfp = fopen(filename, "w"))) {
					(void) printf("Error opening output file %s\n", filename);
					exit(1);
				}
				(void) printf("Extracting %s ... ", filename);
				(void) fflush(stdout);
				infile = 1;
				wc = 0;
				continue;
			}
			/* Do we want to do a test of sorts on a file? */
			if (0 == strncmp(buf, TEST, strlen(TEST))) {
				register int i = 0;

				while(!isdigit(buf[i]) && '-' != buf[i] && NULL != buf[i])
					i++;

				if (0 != isdigit(buf[i])) {
					wordcount(&buf[i], filename, wc);
				}

				if ('f' == buf[++i]) {
					fileexists = checkfile(&buf[++i]);
				}
				continue;
			}
		} else {	/* We are in the middle of a file */

			/* Are we at the end of this one? */
			if (0 == strncmp(buf, endstring, strlen(endstring))) {
				(void) printf("Done\n");
				(void) fclose(dfp);
				infile = 0;
				continue;
			}
			/* No, then does it have a prefix? */
			if ('\0' == prefix[0]) {
				fputs(buf, dfp);
				wc = wc + strlen(buf);
			} else {

				/*
				 * If it does have a prefix, is there one on
				 * this line?
				 */
				if (0 != strncmp(buf, prefix, strlen(prefix))) {
					fputs(buf, dfp);
				} else {
					fputs(&buf[strlen(prefix)], dfp);
					wc = wc + strlen(buf) - strlen(prefix);
				}
			}
		}
	}
	(void) printf("All Done!\n");
	(void) fclose(fp);
}
