/*
 * nuadd.c - operations which add to a NuFX archive
 *
 * NuLib v3.2  March 1992  Freeware (distribute, don't sell)
 * By Andy McFadden (fadden@netcom.com)
 *
 * $Id: nuadd.c,v 1.4 1996/11/28 06:16:33 gdr Exp $
 */

#define SEGMENT_NAME "NuMain2___"

#define NEED_MASTER_ID             /* in nuread.h */

#include "nudefs.h"
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <ctype.h>
#include <assert.h>
#ifdef _AIX32
# include <sys/mode.h>
#endif
#ifdef BSD43
# include <strings.h>
#else
# include <string.h>
#endif

#ifdef UNIX
# include <sys/types.h>
# include <sys/stat.h>
# include <unistd.h>
# ifdef SYSV
#  ifdef XENIX386
#   include <sys/ndir.h>	/* maybe <sys/ndir.h>, <dirent.h>, <dir.h>...*/
#  else /*other SYSV*/
#   include <dirent.h>
#  endif
# endif
# ifdef __GNO__
#  include <dirent.h>
# elif defined(BSD43)
#  include <sys/dir.h>
# elif defined(SUNOS4)
#  include <dirent.h>
# endif
#endif

#ifdef __ORCAC__
# include <types.h>
# include <gsos.h>
# include <shell.h>
  extern char *getcwd(char *, size_t);
#endif

#ifdef APW
# include <types.h>
# include <prodos.h>
# include <shell.h>
# include <strings.h>
#endif

#ifdef MSDOS
# include <errno.h>
# include <time.h>
# include <io.h>
# include <sys/types.h>
# include <sys/stat.h>
#endif

#if defined(__sun__) && !defined(__SVR4)
#include "sunos4.h"
#endif

#include "nuread.h"
#include "nuadd.h"
#include "nupak.h"
#include "nuetc.h"

#ifdef DATAGENERAL					/* BAK */
# ifdef AOSVS						/* BAK */
#  define BROKEN_ON_MVs				/* MV/UX is NOT a full UNIX */
# endif						/* implem. so we just skip */
#endif						/* the 'UNIX' code on MVs */

#define MAXGSPREFIX 64

#ifdef __appleiigs__
extern int GnoActive;
#endif

static BOOLEAN domove;    /* are we M)oving the files in? */
static BOOLEAN docreate;  /* using the 'C' option? */


/*
 * Expand command args into filenames
 * Stuff number of names into int; build File Information Array.
 * (this routine is heavily implementation-specific, since no two systems
 *  expand wildcards or deal with subdirectories in the same way).
 *
 * *FIArray should be NULL the first time this routine is entered.
 * We have to grow the actual array as necessary.
 *
 * Recursively expands subdirectories, unless doSubdir is FALSE.
 */
int
EvalArgs
#ifdef __STDC__
__P((int count, char **names, fileInfoArray FIArray, BOOLEAN first))
#else
     (count, names, FIArray, first)
     int count;	            /* #of filenames */
     char **names;	    /* array of file names */
     fileInfoArray FIArray; /* pointer to array to fill with file info */
     BOOLEAN first;	    /* first time through? */
#endif
{
    static char *procName = "EvalArgs";
#ifdef UNIX
    /* UNIX shells (sh, csh) won't expand subdirectories, but they do
     * expand wildcards in arguments before we get them
     */
    file_info_ptr fip = NULL;
    struct stat st;
    char *cp;  /* temp char pointer */
    /* dir stuff */
#ifndef BROKEN_ON_MVs
    DIR *dirp;
#endif
#ifdef HAS_DIRENT
    struct dirent *dp;
#else
    struct direct *dp;
#endif
    int nmlen;

    if (names == NULL) {
        /* we're recursing */
        assert(count == 0);
    }
    while (count--) {
	if (fip != NULL) {
	  /* we were here before, but didn't add the name to the list */
	  free(fip);
	}
        fip = (file_info *) Malloc(sizeof(file_info));

	if (stat(*names, &st) < 0) {  /* get file info */
	    if (errno == ENOENT) {
		fprintf(stderr, "%s: '%s' not found\n", prgName, *names);
		names++;
		continue;  /* with while */
	    }
	    Fatal("Bad stat()", procName);
	}

	if ((st.st_mode & S_IFDIR) && doSubdir) {  /* is it a directory? */
#ifndef BROKEN_ON_MVs
	    stringArray namelist;

	    namelist = newStringArray();
	    strcpy(tmpNameBuf, *names);  /* earlier dir stuff */
	    strcat(tmpNameBuf, "/");
	    nmlen = strlen(tmpNameBuf);

	    if ((dirp = opendir(*names)) == NULL)
		Fatal("Unable to open subdirectory", procName);
	    while ((dp = readdir(dirp)) != NULL) {
	        /* skip current and parent directories */
		if ((!strcmp(dp->d_name, ".")) || 
		    (!strcmp(dp->d_name, ".."))) {
		    continue;
		}
#ifdef SYSV
		assert(nmlen + strlen(dp->d_name) < MAXFILENAME);
#else
		assert((nmlen + dp->d_namlen) < MAXFILENAME);
#endif
		strcat(tmpNameBuf, dp->d_name);
	        addToStringArray(namelist, tmpNameBuf);
	        tmpNameBuf[nmlen] = '\0';
	    }
	    closedir(dirp);
	    EvalArgs(namelist->used, namelist->vec, FIArray, FALSE);

	    /* free up the space we allocated */
	    clearStringArray(namelist);
	    free(namelist);
	    names++;
#else	/* BROKEN_ON_MVs */
	    printf("Help, I ran into a directory and can't handle it!\n");
#endif
	    continue; /* don't add the directory name to the list */
	} else if ((st.st_mode & S_IFDIR) && !doSubdir) {
	    /* maybe print message? */
	    names++;
	    continue;	/* with while */
	} else if (st.st_mode & S_IFREG) {
	    fip->eof = (long) st.st_size;

	    if (st.st_mode & S_IWRITE)  /* write permission enabled? */
		fip->fAccess = (fourbyt) 0x00e3;	/* locked */
	    else
		fip->fAccess = (fourbyt) 0x0021;	/* unlocked */

	    fip->fileType = defFileType;
	    fip->auxType = diskData ? st.st_size/512 : defAuxType;
	    fip->fileSysID = 0x0001;  /* ProDOS */
	    fip->fileSysInfo = 0x2f;  /* '/' */
	    ExpandTime(&st.st_mtime, &fip->create_dt);  /*use mod.. */
	    ExpandTime(&st.st_mtime, &fip->mod_dt); /*time for both */
	    fip->marked = FALSE;

	    fip->pathname = (char *) Malloc(strlen(*names)+1);
	    strcpy(fip->pathname, *names);
	    fip->store_name = (char *) Malloc(strlen(*names)+1);
	    cp = *names;
	    while (*cp == '/') cp++;  /* advance past leading '/' */
	    strcpy(fip->store_name, cp);  /* can't otherwise fix */

	    names++;
	} else {
	    printf("Unknown storage type for '%s'\n", *names);
	    names++;
	    continue;	/* with while */
	}
        addToFileArray (FIArray, fip);
	fip = NULL;

    }
    return (FIArray->used);

#elif defined(__ORCAC__) /* && !defined(__GNO__) */
    /*
     * GNO should be using this method as well, except that wildcard
     * expansion is broken under GNO v2.0.4 wrt expanding subdirectories;
     * the subdirectories are never traversed.  Blech.
     */
    static FileInfoRecGS frec;
    static NextWildcardGSPB nwcb;
    static ResultBuf255 outname;
    static char prefix[FILENAME_MAX];   /* cwd */
    size_t prefixLen;
    Init_WildcardGSPB iwcb = { 2, NULL, 0x2000 };
    char buffer[256];
    char *fname;
    file_info_ptr fip = NULL;
    stringArray scratch = NULL;
    int i;

    /* initialize */
    assert(FIArray != NULL);
    assert(names != NULL);
    assert(first);
    outname.bufSize = 255;
    nwcb.pCount = 1;
    nwcb.pathName = &outname;
    if (getcwd(prefix, FILENAME_MAX) == NULL) {
      perror("couldn't determine current directory");
      QuitNulib(-1);
    }
    prefixLen = strlen(prefix);

    /* first expand all the files/directories in names FIArray */
    scratch = newStringArray();
    for (i=0; names[i] != NULL; i++) {
      iwcb.wFile = __C2GSMALLOC(names[i]);
      InitWildcardGS(&iwcb);
      TOOL_CHECK("init wildcard for", names[i], 1);
      
      for(;;) {
        int len;

	NextWildcardGS(&nwcb);
	TOOL_CHECK("get next wildcard", "", 1);
	if ((len = outname.bufString.length) == 0) {
	  break;
	}
	strncpy(buffer, outname.bufString.text, len);
	buffer[len] = '\0';
	addToStringArray(scratch, buffer);
      }
      free(iwcb.wFile);
    }

    /* now loop over the expanded file names */
    for (i=0; i < scratch->used; i++) {
      if (fip != NULL) {
	/* we were here before, but didn't add the name to the list */
	if (fip->pathname)   free(fip->pathname);
	if (fip->store_name) free(fip->store_name);
	free(fip);
      }
      fip = (file_info *) Malloc(sizeof(file_info));
      fname = scratch->vec[i];

      /* get file info */
      frec.pCount = 12;
      frec.pathname = __C2GSMALLOC(fname);
      GetFileInfoGS(&frec);
      TOOL_CHECK("get file info for", fname, 1);

      switch (frec.storageType) {
      case 0x01:
	/* standard file */
	fip->eof         = frec.eof;
	fip->fAccess     = frec.access;
	fip->fileType    = frec.fileType;
	fip->auxType     = frec.auxType;
	fip->storageType = frec.storageType;
	fip->fileSysID   = 0x0001;  /* ProDOS */
	fip->fileSysInfo = 0x2f;    /* '/' */
	fip->create_dt   = *((Time *) &frec.createDateTime);
	fip->mod_dt      = *((Time *) &frec.modDateTime);
	fip->marked      = FALSE;
	fip->pathname    = xstrdup(fname);

	/* 
	 * Orca wildcard expansion resolves to full pathnames.  Remove
	 * any leading prefix that matches the current working directory.
	 *
	 * This leaves the full pathname in for files residing in dirs
	 * other than cwd.  This may not be desirable.
	 */
	if (!strncmp(prefix, fname, prefixLen)) {
	  fip->store_name = xstrdup(&fname[prefixLen]);
	} else {
	  fip->store_name = xstrdup(fname);
	}
        addToFileArray (FIArray, fip);
	fip = NULL;

      case 0x05:
	/* extended (forked) file */
	printf("Can't handle Extended file '%s'\n", fname);
	break;

      case 0x0d:  /* subdirectory */
      case 0x0f:  /* volume directory */
	/* -- do nothing (subdirs not explicitly stored?) */
	break;

      default:
	printf("Unknown storage type for '%s'\n", fname);
	break;
      }
    }
    return (FIArray->used);

#elif defined(APW)
    /*
     * This section is probably severly broken.  I have changed the
     * UNIX and __ORCAC__ methods of doing this, but since I don't have
     * an APW compiler, I'm leaving it in its original version.   -- gdr
     */
    static int idx;  /* will eventually hold the total #of filenames */
    char *nextname = (char *) Malloc(MAXFILENAME);  /* for subdir expand */
    char prefix[MAXGSPREFIX+1];  /* Max ProDOS prefix size; now 64 */
    char *fnptr;
    FileRec finfo_p;
    PrefixRec prefix_p;
    OpenRec open_p;
    EOFRec eof_p;

    if (first) idx = 0;

    prefix_p.prefixNum = 0;  /* current dir */
    prefix_p.prefix = prefix;  /* prefix buffer */
    GET_PREFIX( &prefix_p );
    ToolErrChk();
    p2cstr(prefix);

    while (count) {
	strcpy(tmpNameBuf, *names);
	c2pstr(tmpNameBuf);
	INIT_WILDCARD(tmpNameBuf, 0);
	ToolErrChk();

	while (*NEXT_WILDCARD(tmpNameBuf)) {
	    if (idx >= MAXARGS) {
		fprintf(stderr, "Too many files (%d, %d max)\n", idx, MAXARGS);
		QuitNulib (-1);
	    }

	    finfo_p.pathname = tmpNameBuf;
	    GET_FILE_INFO( &finfo_p );
	    ToolErrChk();

	    open_p.openPathname = tmpNameBuf;
	    OPEN( &open_p );
	    ToolErrChk();

	    eof_p.eofRefNum = open_p.openRefNum;
	    GET_EOF( &eof_p );
	    ToolErrChk();

	    CLOSE( &open_p );
	    ToolErrChk();

	    p2cstr(tmpNameBuf);  /* also does p2cstr(finfo_p.pathname) */
	    switch (finfo_p.storageType) {
	    case 0x00:	/* standard ProDOS storage types */
	    case 0x01:
	    case 0x02:
	    case 0x03:
		FIArray[idx] = (file_info *) Malloc(sizeof(file_info));

		FIArray[idx]->eof = eof_p.eofPosition;
		FIArray[idx]->fAccess = finfo_p.fAccess;
		FIArray[idx]->fileType = (fourbyt) finfo_p.fileType;
		FIArray[idx]->auxType = (fourbyt) finfo_p.auxType;
		FIArray[idx]->storageType = finfo_p.storageType;
		FIArray[idx]->fileSysID = 0x0001;  /* ProDOS */
		FIArray[idx]->fileSysInfo = 0x2f;  /* '/' */
		ExpandTime(&finfo_p.createDate, &FIArray[idx]->create_dt);
		ExpandTime(&finfo_p.modDate, &FIArray[idx]->mod_dt);
		FIArray[idx]->marked = FALSE;

		FIArray[idx]->pathname = (char *) Malloc(strlen(tmpNameBuf)+1);
		strcpy(FIArray[idx]->pathname, tmpNameBuf);

		/* are we adding from current directory? */
		if (!strncmp(tmpNameBuf, prefix, strlen(prefix))) {
		    FIArray[idx]->store_name =			      /* yes */
		       (char *) Malloc(strlen(tmpNameBuf) - strlen(prefix) +1);
		  strcpy(FIArray[idx]->store_name, tmpNameBuf+ strlen(prefix));
		} else {
		    fnptr = RINDEX(tmpNameBuf, '/') + 1;	      /* no  */
		    FIArray[idx]->store_name = (char *)Malloc(strlen(fnptr)+1);
		    strcpy(FIArray[idx]->store_name, fnptr);
		}
		idx++;
		break;

	    case 0x05:
		printf("Can't handle Extended file '%s'\n", tmpNameBuf);
		break;
	    case 0x0d:
		if (doSubdir) {
		    strcpy(nextname, tmpNameBuf);  /* make new copy */
		    strcat(nextname, "/=");  /* APW-only wildcard */
		    EvalArgs(1, &nextname, FIArray, FALSE);  /* read subdir */
		}
		break;
	    default:
		printf("Unknown storage type for '%s'\n", tmpNameBuf);
		break;
	    }

	} /* inner while */

	names++, count--;
    }  /* outer while */
    free (nextname);
    return (idx);

#elif defined(MSDOS)
    /*
     * This section is probably severly broken.  I have changed the
     * UNIX and __ORCAC__ methods of doing this, but since I don't have
     * an MS-DOS compiler, I'm leaving it in its original version.   -- gdr
     */

    /* MS-DOS or other shell wildcard expansion here */
    int idx, error;
    struct stat fStat;

    idx = 0;

    while (count--) {

	error = stat (*names, &fStat);

	/* If the filename is a directory, we need to expand that too! */

	if (!error) {
	    FIArray[idx]              = (file_info *) Malloc(sizeof(file_info));
	    FIArray[idx]->pathname    = (char *) Malloc(strlen(*names)+1);
	    strcpy(FIArray[idx]->pathname, *names);
	    FIArray[idx]->store_name  = (char *) Malloc(strlen(*names)+1);
	    strcpy(FIArray[idx]->store_name, *names);
	    FIArray[idx]->fAccess     = 0x00e3L;  /* unlocked */
	    FIArray[idx]->fileType    = defFileType;
	    FIArray[idx]->auxType     = diskData ? fStat.st_size/512:defAuxType;
	    FIArray[idx]->storageType = 0x0000;
	    FIArray[idx]->fileSysID   = 0x0001;  /* ProDOS */
	    FIArray[idx]->fileSysInfo = 0x1c;  /* '\' */
	    ExpandTime(&fStat.st_ctime, &FIArray[idx]->create_dt);
	    ExpandTime(&fStat.st_mtime, &FIArray[idx]->mod_dt);
	    FIArray[idx]->eof = fStat.st_size;

	    FIArray[idx]->marked = FALSE;
	    idx++;
	}
	names++;
    }
    return (idx);

#else /* !UNIX && !__ORCAC__ !APW && !MSDOS */

    /* nothing else defined */

    /* +PORT+ */
    printf("\n[other] wildcard expansion/file info needs work\n");
    while (count--) {
	FIArray[count] = (file_info *) Malloc(sizeof(file_info));

	FIArray[count]->pathname = (char *) Malloc(strlen(*names)+1);
	strcpy(FIArray[count]->pathname, *names);
	FIArray[count]->store_name = (char *) Malloc(strlen(*names)+1);
	strcpy(FIArray[count]->store_name, *names);
	FIArray[count]->fAccess = 0x00e3L;  /* unlocked */
	FIArray[count]->fileType = 0x0006L;  /* BIN */
	FIArray[count]->auxType = 0L;
	FIArray[count]->storageType = 0x0000;
	FIArray[count]->fileSysID = 0x0001;  /* ProDOS */
	FIArray[count]->fileSysInfo = 0x1c;  /* '\' */
	ExpandTime((char *) NULL, &FIArray[count]->create_dt);
	ExpandTime((char *) NULL, &FIArray[count]->mod_dt);
	FIArray[count]->marked = FALSE;

	names++;
    }
    return (count);
#endif
}


/*
 * Add a file onto the end of an archive; does not check to see if an entry
 * already exists.
 *
 * This creates the record entry, and calls subroutines to add the various
 * threads.  The archive fd should be open, the file fd should not.  Returns
 * the size of the record added.
 */
long
#ifdef __STDC__
AddFile(int arcfd, file_info *infoptr)
#else
AddFile(arcfd, infoptr)
int arcfd;
file_info *infoptr;
#endif
{
    int srcfd;	/* file to add */
    onebyt *recBuf;  /* record header block */
    twobyt *twoptr;
    THblock thread[1];	/* thread block */
    twobyt CRC;
    int idx;
    fourbyt total_threads;
    long recposn;  /* file posn for record entry */
    long thposn;   /* file posn for last thread */
    long tmpposn;  /* temporary file posn */
    static char *procName = "AddFile";

    if (verbose) {
#ifdef SHOW_DATA_SIZE
	printf("Adding '%s' (data, %ld bytes)...",
					  infoptr->store_name, infoptr->eof);
#else
	printf("Adding '%s' (data)...", infoptr->store_name);
#endif
	fflush(stdout);
    }

    recBuf = (onebyt *) Malloc(ATTSIZE);
    for (idx = 0; idx < ATTSIZE; idx++)  /* zero the buffer */
	*(recBuf+idx) = 0;

    total_threads = 0;

    strncpy((char *) recBuf+0, (char *) RecordID, 4);
    twoptr = (twobyt *) (recBuf+6);
    *twoptr = ATTSIZE;	/* don't have an attrib_count... */
    HiSwap((onebyt *) recBuf, 6, 7);
    twoptr = (twobyt *) (recBuf+8);
    *twoptr = OURVERS;	/* store new record with our rec vers */
    HiSwap((onebyt *) recBuf, 8, 9);
    /* total_threads */
/*    BCopy((onebyt *) &total_threads, (onebyt *) recBuf+10, 2, TRUE); */
    *(recBuf+12) = 0;  /* reserved1 */
    *(recBuf+13) = 0;
    BCopy((onebyt *) &infoptr->fileSysID, (onebyt *) recBuf+14, 2, TRUE);
    BCopy((onebyt *) &infoptr->fileSysInfo, (onebyt *) recBuf+16, 1, TRUE);
    *(recBuf+17) = 0;  /* reserved2 */
    BCopy((onebyt *) &infoptr->fAccess, (onebyt *) recBuf+18, 4, TRUE);
    BCopy((onebyt *) &infoptr->fileType, (onebyt *) recBuf+22, 4, TRUE);
    BCopy((onebyt *) &infoptr->auxType, (onebyt *) recBuf+26, 4, TRUE);
    BCopy((onebyt *) &infoptr->create_dt, (onebyt *) recBuf+32,
	sizeof(Time), FALSE);
    BCopy((onebyt *) &infoptr->mod_dt, (onebyt *) recBuf+40, sizeof(Time),
	FALSE);
    BCopy((onebyt *) GetTime(), (onebyt *) recBuf+48, sizeof(Time), FALSE);
    twoptr = (twobyt *) (recBuf + (ATTSIZE - 2));
    *twoptr = strlen(infoptr->store_name);

    /* correct strlen ordering */
    HiSwap((onebyt *) recBuf, ATTSIZE-2, ATTSIZE-1);

    thread[0].thread_class = 0x0002;  /* data */
    HiSwap((onebyt *) &thread[0].thread_class, 0, 1);
    thread[0].thread_kind = diskData ? 0x0001 : 0x0000; /* disk or data fork */
    HiSwap((onebyt *) &thread[0].thread_kind, 0, 1);
    thread[0].thread_format = 0x0000;  /* filled in later */
    thread[0].thread_crc = 0x0000;  /* not supported yet */
    /* so I don't forget if I support these */
    HiSwap((onebyt *) &thread[0].thread_crc, 0, 1); 
    thread[0].thread_eof = infoptr->eof;
    HiSwap((onebyt *) &thread[0].thread_eof, 0, 3);
    HiSwap((onebyt *) &thread[0].thread_eof, 1, 2);
    thread[0].comp_thread_eof = -1L;  /* filled in later */
    total_threads++;

    BCopy((onebyt *) &total_threads, (onebyt *) recBuf+10, 4, TRUE);

    /*
     * Because we don't know CRCs or compressed size yet, we must:
     * skip record entry and filename.
     * for each thread:
     *	 skip thread entry, write data, move back, write thread entry.
     * move back, write record entry and filename.
     * move forward to next position.
     */
    if ((srcfd = open(infoptr->pathname, O_RDONLY | O_BINARY)) < 0)
	Fatal("Unable to open file", procName);

    recposn = lseek(arcfd, (off_t) 0, S_REL);	/* save record posn */
    if (lseek(arcfd, (off_t) (ATTSIZE + strlen(infoptr->store_name)), S_REL)<0)
	Fatal("Bad seek (R.rel)", procName);

    /* loop... */
    thposn = lseek(arcfd, (off_t) 0, S_REL);  /* save thread posn */
    if (lseek(arcfd, (off_t) THsize, S_REL) < 0)
	Fatal("Bad seek (Th)", procName);

    /*
     * since we can store files as being packed without actually packing them,
     * we need to check "dopack" to see if we want packMethod or zero.  Note
     * that packing can fail for various reasons; the value returned by
     * PackFile() is the actual algorithm used to pack the file.
     *
     * NuLib uses version 0 records; thread_crcs are not stored.
     */

    thread[0].thread_format = 
	    PackFile(srcfd,arcfd, infoptr->eof, dopack ? packMethod:0, pakbuf);
    if (!dopack) thread[0].thread_format = packMethod;  /* for S subopt */
    HiSwap((onebyt *) &thread[0].thread_format, 0, 1);
    thread[0].comp_thread_eof = (fourbyt) packedSize;
    HiSwap((onebyt *) &thread[0].comp_thread_eof, 0, 3);  /* correct ordering*/
    HiSwap((onebyt *) &thread[0].comp_thread_eof, 1, 2);
    tmpposn = lseek(arcfd, (off_t) 0, S_REL);

    if (lseek(arcfd, (off_t) thposn, S_ABS) < 0)  /* seek back to thread posn */
	Fatal("Bad seek (Th2)", procName);
    if (write(arcfd, &thread[0], THsize) < THsize)  /* write updated thread */
	Fatal("Unable to write thread", procName);
    if (lseek(arcfd, (off_t)tmpposn, S_ABS) < 0)  /*seek back to where we were*/
	Fatal("Bad seek (TmpA)", procName);
    /* ...loop end */

    if (close(srcfd) < 0)
	Fatal("Unable to close file", procName);

    CRC = CalcCRC(0, (onebyt *) recBuf+6, ATTSIZE-6);
    CRC = CalcCRC(CRC, (onebyt *) infoptr->store_name,
	strlen(infoptr->store_name));
    CRC = CalcCRC(CRC, (onebyt *) &thread[0], THsize);
    twoptr = (twobyt *) (recBuf+4);
    *twoptr = CRC;
    HiSwap((onebyt *) recBuf, 4, 5);

    tmpposn = lseek(arcfd, (off_t) 0, S_REL);	   /* record posn (next posn) */
    if (lseek(arcfd, (off_t)recposn, S_ABS) < 0) /* seek back to record entry */
	Fatal("Bad seek (R.abs)", procName);
    if (write(arcfd, recBuf, ATTSIZE) < ATTSIZE)
	Fatal("Unable to write record", procName);
    if (write(arcfd, infoptr->store_name, strlen(infoptr->store_name))
				     < strlen(infoptr->store_name))
	Fatal("Unable to store filename", procName);
    if (lseek(arcfd, (off_t)tmpposn, S_ABS) < 0) /*seek back to where we were */
	Fatal("Bad seek (TmpB)", procName);

    if (verbose) printf("done.\n");
    free(recBuf);

    /* switch ordering back */
    HiSwap((onebyt *) &thread[0].comp_thread_eof, 0, 3);
    HiSwap((onebyt *) &thread[0].comp_thread_eof, 1, 2);
    return ( (long) (THsize * total_threads) + (long) ATTSIZE +
	  (long) strlen(infoptr->store_name) + thread[0].comp_thread_eof);
}


/*
 * Certain options can cause an archive to be created (add, create, move).
 * If the archive does not already exist, then an empty file is created (of
 * type $e0/8002 under ProDOS) and an empty archive struct is built.
 *
 * Note that this requires certain options to deal with archive structures that
 * do not have any records, and archive files that are empty.
 *
 * If the file exists, this will call NuRead() to read it; otherwise, it will
 * create it.
 */
static ListHdr *
#ifdef __STDC__
CreateMaybe(char *filename)
#else
CreateMaybe(filename)
char *filename;
#endif
{
    ListHdr *archive;
    MHblock *MHptr;
    onebyt *bufPtr;
#if 0
    twobyt *twoptr;
#endif
    fourbyt *fourptr;
    int idx;
#ifdef APW
    FileRec create_p;
#endif
#if 0
    static char *procName = "CreateMaybe";
#endif

    if (Exists(filename)) {
	archive = NuRead(filename);
	return (archive);
    }

    if (!docreate)
	printf("Archive does not exist; creating archive file...\n");

    archive = (ListHdr *) Malloc(sizeof(ListHdr));
    archive->arc_name = (char *) Malloc(strlen(filename)+1);
    strcpy(archive->arc_name, filename);
    archive->MHptr = (MHblock *) Malloc(sizeof(MHblock));
    archive->RNodePtr = (RNode *) NULL;
    archive->nextposn = (long) MHsize;

    bufPtr = (onebyt *) archive->MHptr;
    for (idx = 0; idx < MHsize; idx++)
	*(bufPtr+idx) = '\0';

    /* total_records -> zero */
    MHptr = archive->MHptr;
    strncpy((char *) MHptr->ID, (char *) MasterID, 6);
    BCopy((onebyt *) GetTime(), (onebyt *) &(MHptr->arc_create_when),8, FALSE);
    BCopy((onebyt *) bufPtr+12, (onebyt *) &(MHptr->arc_mod_when), 8, FALSE);
    fourptr = (fourbyt *) (&(MHptr->master_eof));
    *fourptr = (fourbyt) MHsize;

/*    twoptr = (twobyt *) (&(MHptr->master_crc));
    *twoptr = CalcCRC(0, (onebyt *) bufPtr+8, MHsize-8); */

    ArcfiCreate(filename);  /* create SHK file */
    return (archive);
}


/*
 * Return a pointer to a valid Master Header block
 * Anything that isn't set to a default value needs to be passed as a
 * parameter [ right now I can't remember why ].
 */
onebyt *
#ifdef __STDC__
MakeMHblock (ListHdr *archive, fourbyt total_records, fourbyt master_eof)
#else
MakeMHblock(archive, total_records, master_eof)
ListHdr *archive;
fourbyt total_records;
fourbyt master_eof;
#endif
{
    static onebyt buf[MHsize];  /* must be static */
    twobyt *twoptr;
#if 0
    static char *procName = "MakeMHblock";
#endif
    int idx;

    for (idx = 0; idx < MHsize ; idx++)
	buf[idx] = '\0';

    /* messy... should've used MHptr->thing here, but if it ain't broke... */
    strncpy((char *) buf, (char *) MasterID, 6);
    BCopy((onebyt *) &total_records, (onebyt *) &buf[8], 4, TRUE);
    BCopy((onebyt *) &archive->MHptr->arc_create_when, (onebyt *) &buf[12],
	sizeof(Time), FALSE);
    BCopy((onebyt *) GetTime(), (onebyt *) &buf[20], sizeof(Time), FALSE);
    twoptr = (twobyt *) &buf[28];  /* master version */
    *twoptr = OURMVERS;
    HiSwap((onebyt *) buf, 28, 29);  /* correct byte ordering */
    BCopy((onebyt *) &master_eof, (onebyt *) &buf[38], 4, TRUE);
    twoptr = (twobyt *) &buf[6];
    *twoptr = CalcCRC(0, (onebyt *) &buf[8], MHsize-8);
    HiSwap((onebyt *) buf, 6, 7);

    return (buf);
}


/*
 * Add files to archive
 *
 * Read files from disk, adding them to the end of the archive as we go.
 * Update the master header block after all files have been added.
 *
 * Upon entry, <filename> is the name of the shrinkit archive.
 * <namecount> is the number of files (to add) given on the command line,
 * and <names> is an array of those file names.
 */
static void
#ifdef __STDC__
Add (char *filename, int namecount, char **names)
#else
Add(filename, namecount, names)
char *filename;
int namecount;
char **names;
#endif
{
    ListHdr *archive;
    int arcfd;
#if 0
    file_info *FIArray[MAXARGS];  /* entries malloc()ed by EvalArgs */
#else
    fileInfoArray FIArray = NULL;
#endif
    int idx;
    onebyt *mptr;  /* points to a MHblock suitable for writing */
    long addSize;
    static char *procName = "Add";

    FIArray = newFileArray();

    /* expand wildcards/subdirectories, and get file info */
    namecount = EvalArgs(namecount, names, FIArray, TRUE);
    if (!namecount) {
	if (verbose) printf("No files selected.\n");
	QuitNulib (0);
    }
    assert(namecount == FIArray->used);

    archive = CreateMaybe(filename);
    if ((arcfd = open(archive->arc_name, O_RDWR | O_BINARY)) < 0)
	Fatal("Unable to open archive", procName);
    if (lseek(arcfd, (off_t)archive->nextposn, S_ABS) < 0)  /* seek to end */
	Fatal("Unable to seek in archive", procName);

    for (idx = 0 ; idx < namecount; idx++) {
#ifdef __appleiigs__
	if (!GnoActive && STOP_KEY()) QuitNulib(1);  /* check for OA-. */
#endif
	addSize = AddFile(arcfd, FIArray->vec[idx]);
	archive->MHptr->master_eof += addSize;
	archive->nextposn += addSize;
	archive->MHptr->total_records++;
    }

    mptr = MakeMHblock(archive, archive->MHptr->total_records,
		archive->MHptr->master_eof);
    if (lseek(arcfd, (off_t) 0, S_ABS) < 0)
	Fatal("Unable to rewind archive for master header", procName);

    if (write(arcfd, mptr, MHsize) < MHsize)
	Fatal("Unable to update master header", procName);
    if (close(arcfd) < 0)
	Fatal("Unable to close archive", procName);

    if (domove) {
	if (verbose) printf("Deleteing files...\n");
	for (idx = 0; idx < namecount; idx++) {
	    if (verbose) {
		printf("%s...", FIArray->vec[idx]->pathname);
		fflush(stdout);
	    }
	    if (unlink(FIArray->vec[idx]->pathname) < 0) {
		if (verbose) printf("failed.\n");
	    } else {
		if (verbose) printf("done.\n");
	    }
	}
    }
}


/*
 * Main entry point for adding files.
 */
void
#ifdef __STDC__
NuAdd (char *filename, int namecount, char **names, char *options)
#else
NuAdd(filename, namecount, names, options)
char *filename;
int namecount;
char **names;
char *options;
#endif
{
#if 0
    char *optr;
    int idx;
    char type[5];
    static char *procName = "NuAdd";
#endif

    if (*options == 'm')
	domove = TRUE;

    if (*options == 'c')
	docreate = TRUE;

    /* change T subopt to convert FROM current system TO <subopt> */
    if (transfrom >= 0) {
	transto = transfrom;
	transfrom = -1;
    }

    Add(filename, namecount, names);  /* do main processing */
}

/*
 * newFileArray
 *
 * create and initialize an array of file_info pointers.
 */

fileInfoArray 
newFileArray __P((void))
{
  fileInfoArray result;

  result = xmalloc(sizeof(fileInfoArrayElem));
  result->vec     = NULL;
  result->alloced = 0;
  result->used    = 0;
  return result;

}

/*
 * addToFileArray
 *
 * maintain an array of file_info pointers
 */

#define SLOTS_QUANTUM 64

void 
addToFileArray
#ifdef __STDC__
__P((fileInfoArray array, file_info_ptr entry))
#else
     (array, entry)
     fileInfoArray array;
     file_info_ptr entry;
#endif
{
  assert(array);
  assert(array->used <= array->alloced);

  if (entry == NULL) return;

  /* grow the size of the array, if necessary */
  if (array->alloced - array->used < 2) {
    array->alloced+=SLOTS_QUANTUM;
    array->vec = xrealloc(array->vec, array->alloced * sizeof(file_info_ptr));
  }

  /* add in the entry */
  array->vec[array->used] = entry;
  array->used++;
  array->vec[array->used] = NULL;
  return;
}

stringArray
newStringArray __P((void))
{
  stringArray result;

  result = xmalloc(sizeof(stringArrayElem));
  result->vec     = NULL;
  result->alloced = 0;
  result->used    = 0;
  return result;
}

void
#ifdef __STDC__
clearStringArray __P((stringArray array))
#else
clearStringArray (array)
     stringArray array;
#endif
{
  int i;
  
  for (i=0; i<array->used; i++) {
    free(array->vec[i]);
  }
  free(array->vec);
  array->vec     = NULL;
  array->alloced = 0;
  array->used    = 0;
}

void 
addToStringArray
#ifdef __STDC__
__P((stringArray array, char *entry))
#else
     (array, entry)
     stringArray array;
     char *entry;
#endif
{
  assert(array);
  assert(array->used <= array->alloced);

  if (entry == NULL) return;

  /* grow the size of the array, if necessary */
  if (array->alloced - array->used < 2) {
    array->alloced+=SLOTS_QUANTUM;
    array->vec = xrealloc(array->vec, array->alloced * sizeof(char *));
  }

  /* add in the entry */
  array->vec[array->used] = xstrdup(entry);
  array->used++;
  array->vec[array->used] = NULL;
  return;
}

void *
#ifdef __STDC__
xmalloc __P((size_t size))
#else
xmalloc (size)
     size_t size;
#endif
{
  char *p;

  if ((p = malloc(size)) == NULL) {
    perror("xmalloc failed");
    exit(-1);
  }
  return (void *) p;
}

void *
#ifdef __STDC__
xrealloc __P((void *ptr, size_t size))
#else
xrealloc (ptr, size)
     void *ptr;
     size_t size;
#endif
{
  char *p;

  /* some reallocs are broken when passed a NULL pointer; ensure it works */
  if (ptr == NULL) {
    p = malloc(size);
  } else {
    p = realloc(ptr, size);
  }
  if (p == NULL) {
    perror("xrealloc failed");
    exit(-1);
  }
  return p;
}

char *
#ifdef __STDC__
xstrdup __P((const char *str))
#else
xstrdup (str)
     const char *str;
#endif
{
  char *result;

  if ((result = malloc(strlen(str) + 1)) == NULL) {
    perror("xstrdup failed");
    exit(-1);
  }
  strcpy (result, str);
  return result;
}
