/*                                                          */
/*                  UUARC V1.3 04/10/93                     */
/*                      J.G.Brandon                         */
/*                                                          */
/*                                                          */
/*  Archiving system based on the 'UU' encoding/decoding    */
/*  algorythms.                                             */
/*                                                          */
/*  Written in 'C' for the public domain using NorthC V1.3  */
/*  on a Commodore Amiga.  Ought to compile/run under most  */
/*  compilers/environments.                                 */
/*                                                          */
/*  No system specific commands/libraries/includes/headers  */
/*  used to make 'C' source portable.  The only system      */
/*  specific part of the program is the pre-processor       */
/*  defined function to identify 'directory separator'      */
/*  characters, used to separate file-names from their      */
/*  paths.                                                  */
/*                                                          */



/*  If required, comment the next line to stop checksums being added    */
/*  (This only effects encoding output, decoding will still check       */
/*  checksums if checksums are present)                                 */
#define ADDCHECK



/*  Title/version definitions   */

#define TITLE       "UUArc"
#define AUTHOR      "Miss J.G.Brandon"
#define VERSION     1
#define REVISION    3
#define TITLESTR    "\n%s Version %d.%d by %s %s.\n\n"
#define TITLESTRPAR TITLE, VERSION, REVISION, AUTHOR, __DATE__



/*  Required standard libraries */

#include    <stdio.h>
#include    <ctype.h>
#include    <string.h>
#include    <stdlib.h>
#include    <signal.h>

/*  When are standard includes not standard includes?       */
/*  When they miss out definitions that should be there.    */
/*  I'll name no names; but it's a very popular compiler... */
/*  (VERY naughty, as it sets the _STDC_ flag to indicate   */
/*   that it is completely ANSI standard conforming;        */
/*   and these are just the ones missed out that this       */
/*   program needs, there are many more.)                   */

#ifndef EXIT_SUCCESS
#define EXIT_SUCCESS        0
#endif

#ifndef EXIT_FAILURE
#define EXIT_FAILURE        20
#endif

#ifndef L_tmpnam
#define L_tmpnam            0x0FF
#endif


/*  Define conditional close function; attempts to close a file indicated   */
/*  by the supplied file pointer if the pointer is not null                 */

#define COND_CLOSE(fptr)    if (fptr != NULL) fclose(fptr)


/*  Define 'directory-separator' identifier - system specific unfortunately */

#define DIR_CHAR(c)         ((c == ':') || (c == '/'))


/*  Define uuencoding/decoding/checking functions   */

#define VALIDATE(c)         ((c < 0x020) || (c > 0x060))
#define DECODE(c)           ((c - 0x020) & 0x03F)
#define ENCODE(c)           (c ? ((c & 0x03F) + 0x020) : 0x060)


/*  Definitions required by program */

#define LISTCOM             'l'
#define TESTCOM             't'
#define EXTRACTCOM          'x'
#define DELETECOM           'd'
#define ADDCOM              'a'
#define MOVEOPT             'm'
#define PATHOPT             'p'

#define BUFSIZE             0x0FF

#define ENCODESIZE          45
#define DEFAULTMODE         0744

#define NOFILE              0
#define FOUNDFILE           1

#define DATALINE            0
#define BEGINLINE           1
#define ENDLINE             2
#define SIZELINE            3
#define BADLINE             4

#define STRIPPATH           0
#define LEAVEPATH           1

#define LEAVEFILE           0
#define MOVEFILE            1

#define SHOWUSAGE           0
#define NOUSAGE             1

#define DUNNO               0
#define YES                 1
#define NO                  2

#define CHECKSIZE           64



/*  If this is ANSI 'C', do function prototyping    */

#ifdef  _STDC_
char *prname(char *, int);
int anycasecmp(char *, char *)
int filecmp(char *, char *[], int)
void pr_head(char *)
void pr_tail(void)
void operout(char *, int)
void succout(char *, int)
void errsout(long)
void usage(FILE *)
void cleanexit(int)
void errorexit(char *, int)
void abnormalexit(int)
int listarc(void)
int deletearc(char *, char *[], int)
int extractarc(char, char *[], int)
int fileread(char *)
int addarc(char *[], int)
#endif



/*  Globally accessable archive, file and temporary file details    */

FILE *arc_fptr = NULL, *file_fptr = NULL, *temp_fptr = NULL;
char temp_name[L_tmpnam] = "";

/*  Globally accessable number of errors counter    */

long numerrors = 0;

/*  Globally accessable path strip flag */

int pathflag = STRIPPATH;

/*  Globally accessable move files flag */

int moveflag = LEAVEFILE;



/*  Strip path name from a filename if required */

char *prname(filename, flag)
char *filename;
int flag;
{
    char *stripname;

    /*  If full path has been requested, leave it in    */
    if (flag == LEAVEPATH)
        return filename;

    /*  Hunt from left to right for a path-indicator character  */
    for(stripname = filename+strlen(filename);
        ((stripname > filename) && (!DIR_CHAR(*stripname)));
        stripname--)
        ;

    /*  If path-indicator character found, increment pointer to */
    /*  point to start of file name                             */
    if DIR_CHAR(*stripname)
        stripname++;

    /*  Return pointer to filename without path extension   */
    return stripname;
}



/*  String comparison, ignoring case - result same as strcmp */

int anycasecmp(string1, string2)
char *string1, *string2;
{
    int i;
    for (i = 0; toupper(string1[i]) == toupper(string2[i]); i++)
        if (string1[i] == '\0')
            return 0;
    return toupper(string1[i]) - toupper(string2[i]);
}



/*  Check filename against list of given filenames   */

int filecmp(filename, argv, argc)
char *filename;
char *argv[];
int argc;
{
    int targc;
    int filematch = NOFILE;
    if (argc == 3)
        filematch = FOUNDFILE;
    else
        for (targc = 3; targc < argc; targc++)
            if (anycasecmp(prname(filename, pathflag),
                prname(argv[targc], pathflag)) == 0)
                filematch = FOUNDFILE;
    return filematch;
}



/*  Print archive header information    */

void pr_head(archive)
char *archive;
{
    printf(" archive '%s':\n", archive);
    printf("---------------------------------------------\n");
}



/*  Print archive tail information  */

void pr_tail()
{
    printf("---------------------------------------------\n");
}



/*  Outputs details of an operation, taking plurals into account    */

void operout(operstr,numfiles)
char *operstr;
int numfiles;
{
    if (numfiles == 0)
        printf("No ");
    else
        printf("%d ",numfiles);

    if (numfiles != 1)
        printf("files ");
    else
        printf("file ");

    printf("%s.\n",operstr);
}



/*  Outputs details of success of an operation  */

void succout(operstr, numerrs)
char *operstr;
int numerrs;
{
    if (numerrs == 0)
        printf("%s successful.\n\n",operstr);
    else
        fprintf(stderr,"%s not entirely successful.\n\n",operstr);
}



/*  Outputs details of errors, taking plurals into account  */

void errsout(numerrs)
long numerrs;
{
    if (numerrs != 0) {
        fprintf(stderr, "%d error", numerrs);
        if (numerrs != 1)
            fprintf(stderr, "s.\n");
        else
            fprintf(stderr, ".\n");
    }
}



/*  Sends details of command usage to output stream 'ostrm'  */

void usage(ostrm)
FILE *ostrm;
{
    fprintf(ostrm,
        "\nUSAGE:\t%s -<command>[%c][%c] <archive> [<filename> ... ]\n",
        TITLE, PATHOPT, MOVEOPT);

    fprintf(ostrm, "\nWhere <command> is one of-\n");
    fprintf(ostrm, "\t%c = List contents of <archive>.\n", LISTCOM);
    fprintf(ostrm, "\t%c = Test contents of <archive>.\n", TESTCOM);
    fprintf(ostrm, "\t%c = Add <filename(s)> to <archive>.\n", ADDCOM);
    fprintf(ostrm, "\t%c = Extract <filenames> from <archive>.\n", EXTRACTCOM);
    fprintf(ostrm,  "\t    (All files if no <filenames> given.)\n");
    fprintf(ostrm, "\t%c = Delete <filenames> from <archive>.\n", DELETECOM);
    fprintf(ostrm,  "\t    (All files if no <filenames> given.)\n");

    fprintf(ostrm,
        "\nIf included after the archiver command, the '%c' option\n",
        PATHOPT);
    fprintf(ostrm,
        "specifies full path names to be considered; otherwise path\n");
    fprintf(ostrm,
        "names will be ignored.\n");

    fprintf(ostrm,
        "\nIf included after an add/extract archiver command, the '%c'\n",
        MOVEOPT);
    fprintf(ostrm,
        "option specifies files to be moved from source, i.e. source\n");
    fprintf(ostrm,
        "files will be deleted; otherwise they are left as is.\n");

    fprintf(ostrm,
        "\nIf applicable, <archive> must include the '.uue' extension.\n\n");
}



/*  A 'clean' exit routine; closes any open files, deletes the temporary    */
/*  file if used, and exits with return code 'retcode'                      */

void cleanexit(retcode)
int retcode;
{
    COND_CLOSE(arc_fptr);
    COND_CLOSE(file_fptr);
    COND_CLOSE(temp_fptr);
    if (strcmp(temp_name,"") != 0)
        remove(temp_name);

    exit(retcode);
}



/*  Exit routine for errors; displays error 'errorstr' and command usage    */
/*  to the standard error output stream, then 'cleanexit's with a failure   */
/*  return code                                                             */

void errorexit(errorstr, usageflag)
char *errorstr;
int usageflag;
{
    fprintf(stderr, "\n\aERROR: %s.\n\n", errorstr);

    if (usageflag == SHOWUSAGE)
        usage(stderr);

    cleanexit(EXIT_FAILURE);
}



/*  Deal with abnormal program termination */

void abnormalexit(sig)
int sig; 
{
    fprintf(stderr, "\n\aERROR: Program aborted.\n\n");
    cleanexit(EXIT_FAILURE);
}



/*  List files in the opened archive    */

int listarc()
{
    char buffer[BUFSIZE], filestr[BUFSIZE];
    int filemode, numfiles = 0;

    /*  Go through archive until error/end-of-file, listing any files found */

    while(fgets(buffer, BUFSIZE, arc_fptr) != NULL) {
        if (sscanf(buffer, "begin %o %s", &filemode, filestr) == 2) {
            numfiles++;
            printf("Found                 - %s\n", filestr);
        }
    }

    return numfiles;
}



/*  Delete files from an archive    */

int deletearc(arcname, argv, argc)
char *arcname;
char *argv[];
int argc;
{
    char buffer[BUFSIZE], filestr[BUFSIZE], arcfile[BUFSIZE];
    int filemode, numdel = 0, fileflag = NOFILE;
    int deleteflag = NOFILE, lastdelete = NOFILE;
    int linetype, lastline = DATALINE;
    long expectedsize;


    /*  Open archive for input, and a temporary file for output/input   */

    if (arc_fptr == NULL)
        if ((arc_fptr = fopen(arcname, "r")) == NULL)
            errorexit("Can't open archive for input", SHOWUSAGE);

    if ((temp_fptr = fopen((tmpnam(temp_name)), "w+")) == NULL)
        errorexit("Can't open temporary file", NOUSAGE);


    /*  Deal with each line in archive until end-of-file/error  */

    while(fgets(buffer, BUFSIZE, arc_fptr) != NULL) {


        /*  Signal a data line by default   */
        linetype = DATALINE;


        /*  Deal with a begin statement */

        if (sscanf(buffer, "begin %o %s", &filemode, filestr) == 2) {

            /*  Indicate command line */
            linetype = BEGINLINE;

            /*  If already dealing with a file, error - missing end */
            if (fileflag == FOUNDFILE) {
                fprintf(stderr, "Missing 'end'         - %s\n", arcfile);
                numerrors++;
                printf("Deleted               - %s\n", arcfile);
                numdel++;
            }

            /*  Remember file name, signal that a file's been found */
            strcpy(arcfile, filestr);
            fileflag = FOUNDFILE;

            /*  Store previous delete status    */
            lastdelete = deleteflag;

            /*  Signal whether or not file is to be deleted */
            deleteflag = filecmp(arcfile, argv, argc);

        }


        /*  Deal with a size statement */

        if (sscanf(buffer, "size %d", &expectedsize) == 1) {

            /*  Indicate command line */
            linetype = SIZELINE;

            /*  A size statement is only expected after an end statement    */
            if (lastline != ENDLINE) {
                fprintf(stderr, "Misplaced 'size'      - %s\n", arcfile);
                numerrors++;
            }
        }


        /*  Copy lines not to be deleted from archive to temporary file */

        if (!((deleteflag == FOUNDFILE) ||
             ((lastline == ENDLINE) &&
              (linetype == SIZELINE) &&
              (lastdelete == FOUNDFILE))))
            if (fputs(buffer, temp_fptr) == EOF)
                errorexit("File error writing to temporary file", NOUSAGE);


        /*  Deal with an end statement  */

        if (strncmp(buffer, "end", 3) == 0) {
            
            /*  Indicate command line */
            linetype = ENDLINE;

            /*  An end statement; so make sure we had a start statement */
            if (fileflag == FOUNDFILE) {
                /*  Signal that file has been processed, and increment  */
                /*  number of files deleted counter if necessary        */
                if (deleteflag == FOUNDFILE) {
                    printf("Deleted               - %s\n", arcfile);
                    numdel++;
                }
                else
                    printf("Skipped               - %s\n", arcfile);
            }
            else {
                /*  If no relevant start statement, error - missing begin   */
                fprintf(stderr, "Missing 'begin'       - ?\n");
                numerrors++;
            }

            /*  Store previous delete status    */
            lastdelete = deleteflag;

            /*  Clear deletion and processing a file    */
            deleteflag = NOFILE;
            fileflag = NOFILE;

        }

        lastline = linetype;

    }

    /*  If we hit end of archive file whilst still expecting data, error    */
    if (fileflag != NOFILE) {
        fprintf(stderr, "Missing (final) 'end' - %s\n", arcfile);
        numerrors++;
        printf("Deleted               - %s\n", arcfile);
        numdel++;
    }


    /*  Go back to start of temporary file  */
    rewind(temp_fptr);

    /*  Close archive file, and re-open it (cleared) for output */
    COND_CLOSE(arc_fptr);
    if ((arc_fptr = fopen(arcname, "w")) == NULL)
        errorexit("Can't open archive for output", SHOWUSAGE);

    /*  Copy contents of temporary file to archive file */
    while(fgets(buffer, BUFSIZE, temp_fptr) != NULL)
        if (fputs(buffer, arc_fptr) == EOF)
            errorexit("File error writing to archive file", NOUSAGE);


    /*  Return number of files deleted  */
    return numdel;
}



/*  Extract/Test files in the opened archive    */

int extractarc(extcom, argv, argc)
char extcom;
char *argv[];
int argc;
{
    char buffer[BUFSIZE], filestr[BUFSIZE], arcfile[BUFSIZE], outstr[BUFSIZE];
    int ic1, ic2, ic3, ic4;
    int filemode, numfiles = 0, fileflag = NOFILE;
    unsigned int checksum;
    int checkflag;
    int linetype, lastline = DATALINE, numdata, datapos, linelen;
    long filesize = 0, expectsize;


    /*  Go through archive until error/end-of-file  */

    while(fgets(buffer, BUFSIZE, arc_fptr) != NULL) {


        /*  As a default, assume line to be a line of UUencoded data    */

        linetype = DATALINE;


        /*  Deal with a begin statement */

        if (sscanf(buffer, "begin %o %s", &filemode, filestr) == 2) {

            /*  Flag line as being a command line   */
            linetype = BEGINLINE;

            /*  If already dealing with a file, error - missing end */
            if (fileflag == FOUNDFILE) {
                fprintf(stderr, "Missing 'end'         - %s\n", arcfile);
                numerrors++;
                COND_CLOSE(file_fptr);
                file_fptr = NULL;
                /*  If just testing archive, indicate that file has been    */
                /*  processed and increment number of files counter         */
                if (extcom == TESTCOM) {
                    printf("%21d - %s\n", filesize, arcfile);
                    numfiles++;
                }
                else {
                    /*  If extracting, indicate that file has been      */
                    /*  processed and increment number of files counter */
                    /*  if necessary                                    */
                    if (file_fptr != NULL) {
                        printf("%21d - %s\n", filesize, arcfile);
                        numfiles++;
                    }
                    else
                        printf("Skipped               - %s\n", arcfile);
                }
            }

            /*  Remember file name, signal that a file's been found,    */
            /*  set file size counter to 0, and reset checksum status   */
            strcpy(arcfile, filestr);
            fileflag = FOUNDFILE;
            filesize = 0;
            checkflag = DUNNO;

            /*  If not just testing the archive, open file for output   */
            if ((extcom != TESTCOM) && 
                (filecmp(arcfile, argv, argc) == FOUNDFILE))
                if ((file_fptr = fopen(prname(arcfile,
                    pathflag), "w")) == NULL) {
                    fprintf(stderr, "Can't open for output - %s\n",
                        prname(arcfile, pathflag));
                    numerrors++;
                }
        }


        /*  Deal with an end statement  */

        if (strncmp(buffer, "end", 3) == 0) {
            
            /*  Flag line as being a command line   */
            linetype = ENDLINE;

            /*  An end statement; so make sure we had a start statement */
            if (fileflag == FOUNDFILE) {
                /*  If just testing archive, indicate that file has been    */
                /*  processed and increment number of files counter         */
                fileflag = NOFILE;
                if (extcom == TESTCOM) {
                    printf("%21d - %s\n", filesize, arcfile);
                    numfiles++;
                }
                else {
                    /*  If extracting, indicate that file has been      */
                    /*  processed and increment number of files counter */
                    /*  if necessary                                    */
                    if (file_fptr != NULL) {
                        printf("%21d - %s\n", filesize, arcfile);
                        numfiles++;
                    }
                    else
                        printf("Skipped               - %s\n", arcfile);
                }
            }
            else {
                /*  If no relevant start statement, error - missing begin   */
                fprintf(stderr, "Missing 'begin'       - ?\n");
                numerrors++;
            }

            COND_CLOSE(file_fptr);
            file_fptr = NULL;

        }


        /*  Deal with a size statement  */

        if (sscanf(buffer, "size %d", &expectsize) == 1) {

            /*  Flag line as being a command line   */
            linetype = SIZELINE;

            /*  A size statement is only expected after an end statement    */
            if (lastline != ENDLINE) {
                fprintf(stderr, "Misplaced 'size'      - %s\n", arcfile);
                numerrors++;
            }
            else {
                /*  Check the size of the file was as expected  */
                if ((filesize != expectsize) &&
                    ((extcom == TESTCOM) || (file_fptr != NULL))) {
                    fprintf(stderr, "File size wrong       - %s\n", arcfile);
                    numerrors++;
                }
            }
        }

 
        /*  Deal with a line of data    */

        if (linetype == DATALINE) {

            /*  Only want data lines within begin/end statements    */
            /*  And only want to bother decoding file if required   */
            if ((fileflag == FOUNDFILE) &&
                ((extcom == TESTCOM) || (file_fptr != NULL))) {

                /*  Strip any white-space characters from end of line   */
                /*  that aren't pure space characters                   */
                if ((datapos = strlen(buffer)-1) >= 0)
                    while (isspace(buffer[datapos]) &&
                            (buffer[datapos] != ' ') &&
                            (datapos >= 0)) {
                        buffer[datapos] = '\0';
                        datapos--;
                    }

                linelen=strlen(buffer);

                /*  If any data is in the line  */
                if (linelen > 0) {

                    /*  Reset line checksum */
                    checksum = 0;

                    /*  Error if any characters not within limits   */
                    for(datapos = 0;
                        ((datapos < linelen) && (linetype == DATALINE));
                        datapos++)
                        if VALIDATE(buffer[datapos]) {
                            fprintf(stderr,
                                "Corrupt byte in line  - %s\n", arcfile);
                            numerrors++;
                            linetype = BADLINE;
                        }


                    /*  Attempt to decode line  */
                    if (linetype == DATALINE) {

                        /*  Fetch total number of bytes to written out  */
                        numdata = DECODE(*buffer);
                        /*  Decode relevant number of bytes from archive    */
                        for(datapos = 1;
                            (((datapos+numdata)<linelen) && (numdata>0));
                            datapos+=4) {
                            ic1 = DECODE(buffer[datapos]);
                            ic2 = DECODE(buffer[datapos+1]);
                            ic3 = DECODE(buffer[datapos+2]);
                            ic4 = DECODE(buffer[datapos+3]);
                            outstr[numdata+2] = ic1 << 2 | ic2 >> 4;
                            outstr[numdata+1] = ic2 << 4 | ic3 >> 2;
                            outstr[numdata] =   ic3 << 6 | ic4     ;
                            checksum = ( checksum +
                                         outstr[numdata+2] +
                                         outstr[numdata+1] +
                                         outstr[numdata]     ) % CHECKSIZE;
                            numdata-=3;  
                        }

                        /*  Error if not enough data in line    */
                        if ((numdata > 0) || ((checkflag == YES) &&
                            ((linelen-datapos) < 1))) {
                            fprintf(stderr,
                                "Data line too short   - %s\n", arcfile);
                            numerrors++;
                            linetype = BADLINE;
                        }

                        /*  If line is okay, and checksums have been        */
                        /*  present, then analyse checksum                  */
                        if ((checkflag == YES) && (linetype == DATALINE)) {
                            if (checksum != DECODE(buffer[datapos])) {
                                fprintf(stderr,
                                    "Checksum error        - %s\n", arcfile);
                                numerrors++;
                                linetype = BADLINE;
                            }
                        }

                        /*  Error if too much data in line - allow extra    */
                        /*  pure space characters though                    */
                        if (((checkflag != NO) && ((linelen-datapos) > 2)) ||
                            ((checkflag == NO) && ((linelen-datapos) > 1))) {
                            if (checkflag == NO)
                                datapos++;
                            else
                                datapos=datapos + 2;
                            while((datapos<linelen)
                                && (linetype != BADLINE))
                                if (buffer[datapos++] != ' ')
                                    linetype = BADLINE;
                            if (linetype == BADLINE) {
                                fprintf(stderr,
                                    "Data line too long    - %s\n", arcfile);
                                numerrors++;
                            }
                        }

                        /*  If the line was okay, and file checksum status  */
                        /*  is still unknown, then establish the status     */
                        if ((checkflag == DUNNO) && (linetype == DATALINE)) {
                            if ((linelen-datapos) == 0)
                                checkflag = NO;
                            else {
                                if (checksum == DECODE(buffer[datapos])) {
                                    checkflag = YES;
                                }
                                else {
                                    fprintf(stderr,
                                        "Bad checksum/linesize - %s\n",
                                        arcfile);
                                    numerrors++;
                                    linetype = BADLINE;
                                }
                            }
                        }
                    }

                    /*  If line is completely error free    */
                    if (linetype == DATALINE) {
                        /*  Increment file size counter */
                        filesize += (numdata = DECODE(*buffer));
                        /*  If file has been opened for output, write out   */
                        /*  decoded bytes                                   */
                        if (file_fptr != NULL)
                            while(numdata > 0) {
                                if (fputc(outstr[numdata+2],
                                    file_fptr) == EOF)
                                    errorexit("File error writing file",
                                        NOUSAGE);
                                numdata--;
                            }
                    }

                }

                else {
                    fprintf(stderr, "No data, empty line   - %s\n", arcfile);
                    numerrors++;
                }

            }

        }

        lastline = linetype;

    }

    /*  If we hit end of archive file whilst still expecting data, error    */
    if (fileflag != NOFILE) {
        fprintf(stderr, "Missing (final) 'end' - %s\n", arcfile);
        numerrors++;
        /*  If just testing archive, indicate that file has been    */
        /*  processed and increment number of files counter         */
        if (extcom == TESTCOM) {
            printf("%21d - %s\n", filesize, arcfile);
            numfiles++;
        }
        else {
            /*  If extracting, indicate that file has been      */
            /*  processed and increment number of files counter */
            /*  if necessary                                    */
            if (file_fptr != NULL) {
                printf("%21d - %s\n", filesize, arcfile);
                numfiles++;
            }
            else
                printf("Skipped               - %s\n", arcfile);
        }
    }

    /*  Return number of files extracted    */
    return numfiles;
}



/*  Read from file, dealing with with any errors    */

int fileread(buf)
char *buf;
{
    int numread;

    /*  Read data into buffer   */
    numread = fread(buf, sizeof(char), ENCODESIZE, file_fptr);

    /*  If an error occured, exit cleanly with an error */
    if ferror(file_fptr)
        errorexit("File error reading file", NOUSAGE);

    /*  Return number of bytes read */
    return numread;
}



/*  Add files specified to the opened archive   */

int addarc(argv, argc)
char *argv[];
int argc;
{
    char buffer[BUFSIZE], outstr[BUFSIZE];
    int ic1, ic2, ic3, oc1, oc2, oc3, oc4;
    int inpos, outpos, targc, numfiles = 0, n;
    long filesize;
    #ifdef  ADDCHECK
    unsigned int checksum;
    #endif

    /*  Repeat for each filename given  */
    for (targc = 3; targc < argc; targc++) {

        /*  Reset filesize counter  */
        filesize = 0;

        /*  Open file for input */
        if ((file_fptr = fopen(argv[targc], "r")) == NULL) {
            fprintf(stderr, "Can't open for input  - %s\n", argv[targc]);
            numerrors++;
        }
        else {

            /*  Write begin statement   */
            fprintf(arc_fptr, "\nbegin %o %s\n",
                DEFAULTMODE, prname(argv[targc], pathflag));

            /*  Read and encode from file whilst data is available  */

            do {

                n = fileread(buffer);

                /*  Encode number of bytes read into first character    */
                /*  in line                                             */
                *outstr = ENCODE(n);

                #ifdef  ADDCHECK
                /*  Reset checksum  */
                checksum = 0;
                #endif

                /*  Encode data */
                outpos = 1;
                for(inpos = 0; inpos < n; inpos += 3) {
                    ic1 = buffer[inpos];
                    ic2 = buffer[inpos+1];
                    ic3 = buffer[inpos+2];
                    oc1 =  ic1 >> 2;
                    oc2 = (ic1 << 4) & 0x030 | (ic2 >> 4) & 0x00F;
                    oc3 = (ic2 << 2) & 0x03C | (ic3 >> 6) & 0x003;
                    oc4 =     ic3    & 0x03F;
                    outstr[outpos]   = ENCODE(oc1);
                    outstr[outpos+1] = ENCODE(oc2);
                    outstr[outpos+2] = ENCODE(oc3);
                    outstr[outpos+3] = ENCODE(oc4);
                    outpos += 4;
                    #ifdef  ADDCHECK
                    checksum = ( checksum + ic1 + ic2 + ic3 ) % CHECKSIZE;
                    #endif 
               }

                /*  Add termination to output string    */
                #ifdef  ADDCHECK
                outstr[outpos]   = ENCODE(checksum);
                outpos++;
                #endif
                outstr[outpos]   = '\n';
                outstr[outpos+1] = '\0';

                /*  Write data into archive */
                if (fputs(outstr, arc_fptr) == EOF)
                    errorexit("File error writing to archive file", NOUSAGE);

                filesize += n;

            } while (n > 0);

            /*  Write end statement   */
            fprintf(arc_fptr, "end\n");
            fprintf(arc_fptr, "size %d\n", filesize);

            /*  Indicate that file has been processed   */
            printf("%21d - %s\n", filesize, prname(argv[targc], pathflag));

            numfiles++;
    
            /*  Close file  */
            COND_CLOSE(file_fptr);
            file_fptr = NULL;

            /*  In the case of a move command, delete file  */
            if (moveflag == MOVEFILE)
                remove(argv[targc]);      

        }

    }

    return numfiles;
}



/*  Main entry point, taking standard "main" style parameters   */

void main(argc, argv)
int argc;
char *argv[];
{
    char command[BUFSIZE];
    int numfiles, numdel, comlen, compos;

    /*  Trap program interruption, to exit cleanly      */
    /*  A 'certain' compiler gives warnings -the code   */
    /*  is correct, the compiler is wrong (sorry, but   */
    /*  according to the K&R book, and just about every */
    /*  other compiler - a signal handler is supposed   */
    /*  supposed to return void, _NOT_ int) - program   */
    /*  will still work, presumably to just ignore the  */
    /*  compiler warnings in this curcumstance is okay  */
    #ifdef SIGABRT
    signal(SIGABRT, abnormalexit);
    #endif
    #ifdef SIGINT
    signal(SIGINT, abnormalexit);
    #endif
    #ifdef SIGTERM
    signal(SIGTERM, abnormalexit);
    #endif

    /*  Display program title    */
    printf(TITLESTR, TITLESTRPAR);

    /*  If no arguments specified, display usage and exit   */
    if (argc < 2) {
        usage(stderr);
        cleanexit(EXIT_FAILURE);
    }

    /*  If only argument is a question-mark, display usage and exit */
    if (strcmp(argv[1], "?") == 0) {
        usage(stdout);
        cleanexit(EXIT_FAILURE);
    }

    /*  Check existance of an archiver command  */
    if (sscanf(argv[1], "-%s", command) != 1)
        errorexit("Archiver command missing/invalid", SHOWUSAGE);
    comlen = strlen(command);

    /*  Check command is right length   */
    if ((comlen < 1) || (comlen > 3))
        errorexit("Archiver command/options missing/invalid", SHOWUSAGE);

    /*  Deal with flags if given  */
    compos = 1;
    while (compos < comlen) {
        switch(tolower(command[compos])) {
            case PATHOPT:
                if (pathflag == LEAVEPATH)
                    errorexit("Archiver options invalid", SHOWUSAGE);
                pathflag = LEAVEPATH;
                break;
            case MOVEOPT:
                if (moveflag == MOVEFILE)
                    errorexit("Archiver options invalid", SHOWUSAGE);
                moveflag = MOVEFILE;
                break;
            default:
                errorexit("Archiver options invalid", SHOWUSAGE);
        }
        compos++;
    }

    /*  Remove case dependancy of command   */
    *command = tolower(*command);

    /*  Check validity of supplied archiver command */
    if ((*command != LISTCOM) &&
        (*command != TESTCOM) &&
        (*command != EXTRACTCOM) &&
        (*command != DELETECOM) &&
        (*command != ADDCOM))
        errorexit("Invalid archiver command", SHOWUSAGE);

    /*  Perform required action dependant upon user supplied command    */

    switch(*command) {

        /*  In the case of an archive listing request   */

        case LISTCOM:

            /*  Check archiver options are valid for this operation */
            if ((moveflag == MOVEFILE) || (pathflag == LEAVEPATH))
                errorexit("Archiver options invalid", SHOWUSAGE);

            /*  Check number of arguments   */
            if (argc != 3)
                errorexit("Wrong number of arguments", SHOWUSAGE);

            /*  Open archive for input  */
            if ((arc_fptr = fopen(argv[2], "r")) == NULL)
                errorexit("Can't open archive for input", SHOWUSAGE);

            /*  Print header information    */
            printf("Listing");
            pr_head(argv[2]);

            /*  Perform listing operation   */
            numfiles = listarc();

            /*  Print tail information  */
            pr_tail();

            /*  Give operation statistics   */
            operout("found",numfiles);
            succout("Listing", numerrors);

            /*  Exit program cleanly    */
            cleanexit(EXIT_SUCCESS);


        /*  In the case of an archive deletion request      */

        case DELETECOM:

            /*  Check archiver options are valid for this operation */
            if ((moveflag == MOVEFILE) || (pathflag == LEAVEPATH))
                errorexit("Archiver options invalid", SHOWUSAGE);

            /*  Check number of arguments   */
            if (argc < 3)
                errorexit("Wrong number of arguments", SHOWUSAGE);

            /*  Print header information    */
            printf("Deleting from");
            pr_head(argv[2]);

            /*  Perform deletion operation  */
            numdel = deletearc(argv[2], argv, argc);

            /*  Print tail information  */
            pr_tail();

            /*  Give operation statistics   */
            operout("deleted", numdel);
            errsout(numerrors);
            succout("Deletion", numerrors);

            /*  Exit program cleanly    */
            if (numerrors == 0)
                cleanexit(EXIT_SUCCESS);
            else
                cleanexit(EXIT_FAILURE);


        /*  In the case of an archive extraction/testing request    */

        case TESTCOM:
        case EXTRACTCOM:

            /*  Check archiver options are valid for this operation */
            if ((*command == TESTCOM) &&
                ((moveflag == MOVEFILE) || (pathflag == LEAVEPATH)))
                errorexit("Archiver options invalid", SHOWUSAGE);

            /*  Check number of arguments   */
            if ((*command == TESTCOM) && (argc != 3))
                errorexit("Wrong number of arguments", SHOWUSAGE);
            if ((*command != TESTCOM) && (argc < 3))
                errorexit("Not enough arguments", SHOWUSAGE);

            /*  Open archive for input  */
            if ((arc_fptr = fopen(argv[2], "r")) == NULL)
                errorexit("Can't open archive for input", SHOWUSAGE);

            /*  Print header information    */
            if (*command == TESTCOM)
                printf("Testing ");
            else
                printf("Extracting from ");
            pr_head(argv[2]);

            /*  Perform extraction operation    */
            numfiles = extractarc(*command, argv, argc);

            /*  Print tail information  */
            pr_tail();

            /*  Give operation statistics   */
            if (*command != TESTCOM) {
                operout("extracted", numfiles);
                errsout(numerrors);
                succout("Extraction", numerrors);
            }
            else {
                operout("tested", numfiles);
                errsout(numerrors);
                succout("Testing", numerrors);
            }

            /*  In the case of a move option    */
            if (moveflag == MOVEFILE) {

                /*  If there were errors in extraction, don't   */
                /*  risk deleting from archive                  */
                if (numerrors != 0)
                    printf("Not risking deleting from archive!\n");

                /*  Otherwise, delete files that were extracted */
                else {

                    /*  Close the archive file first    */
                    COND_CLOSE(arc_fptr);
                    arc_fptr = NULL;

                    /*  Print header information    */
                    printf("Deleting from");
                    pr_head(argv[2]);

                    /*  Perform deletion operation  */
                    numdel = deletearc(argv[2], argv, argc);

                    /*  Print tail information  */
                    pr_tail();

                    /*  Give operation statistics   */
                    operout("deleted", numdel);
                    errsout(numerrors);
                    succout("Deletion", numerrors);

                }

            }


            /*  Exit program cleanly    */
            if (numerrors == 0)
                cleanexit(EXIT_SUCCESS);
            else
                cleanexit(EXIT_FAILURE);


        /*  In the case of an add/move to an archive request    */

        case ADDCOM:

            /*  Check number of arguments   */
            if (argc < 4)
                errorexit("Not enough arguments", SHOWUSAGE);

            /*  Open archive for update  */
            if ((arc_fptr = fopen(argv[2], "a")) == NULL)
                errorexit("Can't open archive", SHOWUSAGE);

            /*  Print header information    */
            if (*command == ADDCOM)
                printf("Adding to");
            else
                printf("Moving to");
            pr_head(argv[2]);

            /*  Perform add operation   */
            numfiles = addarc(argv, argc);

            /*  Print tail information  */
            pr_tail();

            /*  Give operation statistics   */
            if (*command == ADDCOM) {
                operout("added", numfiles);
                errsout(numerrors);
                succout("Adding", numerrors);
            }
            else {
                operout("moved", numfiles);
                errsout(numerrors);
                succout("Moving", numerrors);
            }

            /*  Exit program cleanly    */
            if (numerrors == 0)
                cleanexit(EXIT_SUCCESS);
            else
                cleanexit(EXIT_FAILURE);

    }

    /*  By logic the program should never get this far */
    errorexit("Internal error - improper program execution", NOUSAGE);
}
