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