[comp.sources.amiga] unshar.c

doc@s.cc.purdue.edu.UUCP (06/05/87)

	Here is a small unshar'ing program that I worked up real quick.
    It does not support directories or atob/btoa shell archives, but
    that may come later depending on demand.  It does work on all the
    different shell archive types I came across and works on both Unix
    and the Amiga.  Have fun and please send any bugfixes/enhancements
    to me!
	Craig Norborg
	comp.sources.amiga moderator

#	This is a shell archive.
#	Remove everything above and including the cut line.
#	Then run the rest of the file through sh.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# shar:	Shell Archiver
#	Run the following text with /bin/sh to create:
#	unshar.c
# This archive created: Fri Jun  5 02:05:34 1987
# By:	Craig Norborg (Purdue University Computing Center)
cat << \SHAR_EOF > unshar.c
/*	
 *	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>

#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 WCOUNT	"if test"	/* Leader for 'wc' testing */

/*
 * 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, *rindex();
	register int i = 0;

	/*
	 * 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 *) rindex(line, '<');	/* Find the last '<' on the
											 * line */
	while (isspace(*++tmp))
		;	/* Do nothing */
	if ('\'' == *tmp) {						/* Is it a quoted string? */
		while ('\'' != *++tmp)
			end[i++] = *tmp;
		tmp++;
	} else if ('\\' == *tmp) {				/* Is it a backslashed string? */
		while (!isspace(*++tmp))
			end[i++] = *tmp;
	} else {								/* Uh-oh! */
		(void) printf("Can't find eof string\n");
		exit(2);
	}
	end[i] = '\0';		/* Null off the end of the string */

	/*
	 * 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 
	 */
	i = 0;
	tmp = (char *) rindex(line, '>');
	while (isspace(*++tmp))
		;	/* Do Nothing */
	while (!isspace(*tmp)) {
		file[i++] = *tmp;
		tmp++;
	}
	file[i] = '\0';		/* Null off the end of the string */

#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 */

	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 (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 (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;
			}
			/* Did we want to do a word count on that last file? */
			if (0 == strncmp(buf, WCOUNT, strlen(WCOUNT))) {
				register int i = 0;

				while(!isdigit(buf[i]))
					i++;
				if (wc != atoi(&buf[i]))
					(void) printf("Error unsharing %s (wc should have been %d, but was %d)\n", filename, atoi(&buf[i]), wc);
				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);
}
SHAR_EOF
#	End of shell archive
exit 0

doc@s.cc.purdue.edu (Craig Norborg) (06/24/87)

    Here is a repost of my unshar program..  It works a bit better and
now handles checking to see if files exist before writing over them
(if it is in the shar file to do so).  It compiles under AZTEC C when
given the -DAMIGA flag using 16 bit ints.  Binaries will follow in
comp.binaries.amiga.


/*	
 *	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>
#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, *rindex();

	/*
	 * 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 *) rindex(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 *) rindex(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);
}