/*  ARC - Archive utility - ARCADD

    Version 3.40, created on 06/18/86 at 13:10:18

Copyright 1989-90 Lawrence J. Jones; All Rights Reserved
(C) COPYRIGHT 1985,86 by System Enhancement Associates; ALL RIGHTS RESERVED

    By:  Thom Henderson

    Description:
         This file contains the routines used to add files to an archive.

    Language:
         Computer Innovations Optimizing C86
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "arc.h"

typedef struct fspec {
   char *name;
   char *path;
   } FSPEC;

static int addbunch PROTO((int, FSPEC *, int, int, int));
static int namcmp PROTO((const char *, const char *));
static void addfile PROTO((FSPEC *, int, int));


void addarc(num,arg,move,update,fresh) /* add files to archive */
int num;                               /* number of arguments */
char *arg[];                           /* pointers to arguments */
int move;                              /* true if moving file */
int update;                            /* true if updating */
int fresh;                             /* true if freshening */
{
    char *d;                           /* directory junk */
    FSPEC *fs;                         /* file names & paths */
    int nslots;                        /* number of slots in list */
    int nfiles;                        /* number of files in list */
    int notemp;                        /* true until a template works */
    int nowork = 1;                    /* true until files are added */
    int n;                             /* index */

    if(num<1)                          /* if no files named */
    {    num = 1;                      /* then fake one */
         arg[0] = ALLFILES;            /* add everything */
    }

    nslots = num;                      /* assume each arg is one file */
    if (!(fs = (FSPEC *)malloc(nslots * sizeof *fs)))
         fatal("Out of memory");
    nfiles = 0;

    for(n=0; n<num; n++)               /* for each template supplied */
    {
         notemp = 1;                   /* reset files flag */
         for(d=dir(arg[n]); d; d=dir(NULL))
         {    notemp = 0;              /* template is giving results */
              if (++nfiles > nslots)   /* need more room in list */
              {    nslots *= 2;        /* double number of slots */
                   if (!(fs = (FSPEC *)realloc(fs, nslots * sizeof *fs)))
                        fatal("Out of memory");
              }
              fs[nfiles-1].path = d;
              fs[nfiles-1].name = basename(d);      /* save name */

              if(coreleft()<5120)
              {    nfiles = addbunch(nfiles,fs,move,update,fresh);
                   nowork = nowork && !nfiles;
                   while(nfiles)
                   {    free(fs[--nfiles].path);
                        free(fs[nfiles].name);
                   }
              }
         }
         if(notemp && warn)
              printf("No files match: %s\n",arg[n]);
    }

    if(nfiles)
    {    nfiles = addbunch(nfiles,fs,move,update,fresh);
         nowork = nowork && !nfiles;
         while(nfiles)
         {    free(fs[--nfiles].path);
              free(fs[nfiles].name);
         }
         free(fs);
    }

    if(nowork && warn)
         printf("No files were added.\n");
}

static int addbunch(nfiles,fs,move,update,fresh) /* add a bunch of files */
int nfiles;                            /* number of files to add */
FSPEC *fs;                             /* file names & paths */
int move;                              /* true if moving file */
int update;                            /* true if updating */
int fresh;                             /* true if freshening */
{
    int m, n;                          /* indices */
    struct heads hdr;                  /* file header data storage */

    qsort((char *)fs, nfiles, sizeof *fs, namcmp);

    for(n=0; n<nfiles; )               /* consolidate the list of names */
    {    if(n+1<nfiles && !strcmp(fs[n].path,fs[n+1].path) /* if duplicate names */
         || !strcmp(fs[n].path,arcname)   /* or this archive */
         || !strcmp(fs[n].path,newname)   /* or the new version */
         || !strcmp(fs[n].path,bakname))  /* or its backup */
         {    free(fs[n].path);           /* then forget the file */
              free(fs[n].name);

              for(m=n; m<nfiles-1; m++)
                   fs[m] = fs[m+1];
              nfiles--;
         }
         else n++;                     /* else test the next one */
    }

    if(!nfiles)                        /* make sure we got some */
         return 0;

    for(n=0; n<nfiles-1; n++)          /* watch out for duplicate names */
         if(!strcmp(fs[n].name,fs[n+1].name))
              fatal("Duplicate filenames:\n  %s\n  %s",fs[n].path,fs[n+1].path);

    openarc(1);                        /* open archive for changes */

    for(n=0; n<nfiles; n++)            /* add each file in the list */
         addfile(&fs[n],update,fresh);

    /* now we must copy over all files that follow our additions */

    while(readhdr(&hdr,arc))           /* while more entries to copy */
    {    writehdr(&hdr,new);
         filecopy(arc,new,hdr.size);
    }
    hdrver = 0;                        /* archive EOF type */
    writehdr(&hdr,new);                /* write out our end marker */
    closearc(1);                       /* close archive after changes */

    if(move)                           /* if this was a move */
    {    for(n=0; n<nfiles; n++)       /* then delete each file added */
         {    if(remove(fs[n].path) && warn)
              {    printf("Cannot unsave %s\n",fs[n].path);
                   nerrs++;
              }
         }
    }

    return nfiles;                     /* say how many were added */
}


static int namcmp(n1, n2)
   const char *n1, *n2;
   {

   return strcmp(((FSPEC *)n1)->name, ((FSPEC *)n2)->name);
   }

static void addfile(fs,update,fresh)   /* add named file to archive */
FSPEC *fs;                             /* file name and path */
int update;                            /* true if updating */
int fresh;                             /* true if freshening */
{
    struct heads nhdr;                 /* data regarding the new file */
    struct heads ohdr;                 /* data regarding an old file */
    FILE *f;                           /* file to add */
    long starts;                       /* file locations */
    int upd = 0;                       /* true if replacing an entry */

    if(!(f = opnfilrd(fs->path)))
    {    if(warn)
         {    perror(fs->path);
              nerrs++;
         }
         return;
    }

    strcpy(nhdr.name,fs->name);        /* save name */
    nhdr.size = 0;                     /* clear out size storage */
    nhdr.crc = 0;                      /* clear out CRC check storage */
    getstamp(f,&nhdr.date,&nhdr.time);

    /* position archive to spot for new file */

    if(arc)                            /* if adding to existing archive */
    {    starts = ftell(arc);          /* where are we? */
         while(readhdr(&ohdr,arc))     /* while more files to check */
         {    if(!strcmp(ohdr.name,nhdr.name))
              {    upd = 1;            /* replace existing entry */
                   if(update || fresh) /* if updating or freshening */
                   {    if(nhdr.date<ohdr.date
                        || (nhdr.date==ohdr.date && nhdr.time<=ohdr.time))
                        {    fseek(arc,starts,0);
                             clsfil(f,(char *)NULL,0,0);
                             return;   /* skip if not newer */
                        }
                   }
              }

              if(strcmp(ohdr.name,nhdr.name)>=0)
                   break;              /* found our spot */

              writehdr(&ohdr,new);     /* entry preceeds update; keep it */
              filecopy(arc,new,ohdr.size);
              starts = ftell(arc);     /* now where are we? */
         }

         if(upd)                       /* if an update */
         {    if(note)
                   printf("Updating file: %-12s  ",fs->name);
              fseek(arc,ohdr.size,1);
         }
         else if(fresh)                /* else if freshening */
         {    fseek(arc,starts,0);     /* then do not add files */
              clsfil(f,(char *)NULL,0,0);
              return;
         }
         else                          /* else adding a new file */
         {    if(note)
                   printf("Adding file:   %-12s  ",fs->name);
              fseek(arc,starts,0);     /* reset for next time */
         }
    }

    else                               /* no existing archive */
    {    if(fresh)                     /* cannot freshen nothing */
         {    clsfil(f,(char *)NULL,0,0);
              return;
         }
         else if(note)                 /* else adding a file */
              printf("Adding file:   %-12s  ",fs->name);
    }

    starts = ftell(new);               /* note where header goes */
    hdrver = ARCVER;                   /* anything but end marker */
    writehdr(&nhdr,new);               /* write out header skeleton */
    pack(f,new,&nhdr);                 /* pack file into archive */
    fseek(new,starts,0);               /* move back to header skeleton */
    writehdr(&nhdr,new);               /* write out real header */
    fseek(new,nhdr.size,1);            /* skip over data to next header */
    clsfil(f,(char *)NULL,0,0);        /* all done with the file */
}
