[comp.sources.unix] v11i015: File archive program, Part06/07

rs@uunet.UU.NET (Rich Salz) (08/19/87)

Submitted-by: iuvax!bsu-cs!dhesi@seismo.CSS.GOV (Rahul Dhesi)
Posting-number: Volume 11, Issue 15
Archive-name: zoo/Part06

#! /bin/sh
#
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create:
#	zooadd.c
#	zooadd2.c
#	zoodel.c
#	zooext.c
#	zoofns.h
export PATH; PATH=/bin:/usr/bin:$PATH
if test -f 'zooadd.c'
then
	echo shar: "will not over-write existing file 'zooadd.c'"
else
sed 's/^X//' << \SHAR_EOF > 'zooadd.c'
X#ifndef LINT
X/* @(#) zooadd.c 1.22 87/05/30 00:04:52 */
Xstatic char sccsid[]="@(#) zooadd.c 1.22 87/05/30 00:04:52";
X#endif /* LINT */
X
X/*
XCopyright (C) 1986, 1987 Rahul Dhesi -- All rights reserved
X*/
X#include "options.h"
X/* Adds files specified in parameter-list to archive zoo_path. */
X
X#define LONGEST	20					/* assumed length of longest filename */
X#include "zoomem.h"             /* to define MAXADD */
X#include "zoo.h"
X#include <stdio.h>
X#include "various.h"
X#include "parse.h"
X#include "debug.h"
X
X#include "portable.h"
X/* for low-level I/O */
X#ifdef NOFCNTL
X#include <file.h>
X#else
X#include <fcntl.h>
X#endif
X
X#ifdef FLAT
X#include <types.h>
X#include <stat.h>
X#else
X#include <sys/types.h>
X#include <sys/stat.h>
X#endif
X
X#ifdef IGNORECASE
X#define	COMPARE	strcmpi
X#else
X#define	COMPARE	strcmp
X#endif
X
X#include "zoofns.h"
X#include "errors.i"
Xextern int break_hit;
Xextern int quiet;
X
X#ifdef LINT_ARGS
Xvoid opts_add (char *, int *, int *, int *, int *, int *, int *,
X               int *, int *, int *, int *, int *);
Xint ver_too_high (struct zoo_header *);
Xget_comment (struct direntry *, FILE *, char *);
Xvoid copyfields (struct direntry *, struct tiny_header *);
Xvoid storefname (struct direntry *, char *, int);
Xchar *choosefname (struct direntry *);
X#else
Xvoid opts_add();
Xint ver_too_high ();
Xget_comment ();
Xvoid copyfields ();
Xvoid storefname ();
Xchar *choosefname ();
X#endif
X
Xextern struct zoo_header zoo_header;
X
Xextern char file_leader[];
Xextern unsigned int crccode;
X
Xvoid zooadd(zoo_path, argc, argv, option)
Xchar *zoo_path;      /* pathname of zoo archive to add to */
Xint argc;            /* how many filespecs supplied */
Xchar **argv;         /* array of pointers to filespecs */
Xchar *option;        /* option string */
X{
Xchar *whichname;                          /* which name to show user */
Xchar *flist[MAXADD];                      /* list of ptrs to input fnames */
Xint fptr;                                 /* will point to within flist */
Xint zoo_han;                              /* handle for open archive */
Xint this_han;                             /* handle of file to add */
Xchar zoo_fname[LFNAMESIZE];               /* basename of archive itself */
Xchar zoo_bak[LFNAMESIZE];                 /* name of archive's backup */
Xchar this_fname[LFNAMESIZE];              /* just filename of file to add */
Xchar latest_name[LFNAMESIZE];             /* latest name in archive */
Xlong last_old = 0L;                       /* last direntry in old chain */
XFILE *zoo_file;                           /* stream for open archive */
Xchar *this_path;                          /* pathname of file to add */
X
X#ifdef NOENUM
X#define NEW_ZOO 1
X#define OLD_ZOO 2
Xint zoo_status;
X#else
Xenum {NEW_ZOO, OLD_ZOO} zoo_status;       /* newly created or not */
X#endif
X
Xlong this_dir_offset;                     /* pointers to within archive */
Xlong save_position;                       /* pointer to within archive */
Xlong prev_pos;                            /* posn of prev file of same name */
Xstruct direntry direntry;                 /* directory entry */
Xstruct direntry dir2entry;                /* spare */
Xstruct tiny_header tiny_header;           /* for Z format archives */
Xint status;                               /* error status */
Xint success;                              /* successful addition of file? */
Xint addcount = 0;                         /* number added */
Xint update=0;                             /* add only files already in archive */
Xint suppress=0;                           /* suppress compression */
Xint new=0;                                /* add only files not in archive */
Xint zootime = 0;                          /* just set archive time */
Xint add_comment = 0;                      /* add comment */
Xint pack = 0;                             /* pack after adding */
Xint need_dir = 1;                         /* store directories too */
Xint delcount = 0;                         /* count of deleted entries */
Xint exit_status = 0;                      /* exit status to set */
X
Xunsigned int latest_date = 0;             /* to set time on archive itself */
Xunsigned int latest_time = 0;             /* .. same */
Xint move = 0;                             /* delete after adding to archive */
Xint longest;                              /* length of longest pathname added */
Xint firstfile = 1;                        /* first file being added? */
Xint z_fmt = 0;                            /* look for Z format files? */
Xint inargs = 0;									/* read filenames from stdin? */
X
X/* on entry option points to first letter */
X
Xopts_add (option, &zootime, &quiet, &suppress, &move, &new, &pack,
X          &update, &add_comment, &z_fmt, &need_dir, &inargs);
X
X/* POSSIBLE RACE CONDITION BETWEEN TESTING EXISTENCE AND CREATING FILE */
Xif (exists (zoo_path)) {
X	zoo_file = fopen (zoo_path, "r+b");			/* binary open, no create */
X   zoo_status = OLD_ZOO;
X} else {
X   if (!zootime)
X		zoo_file = fopen (zoo_path, "w+b");		/* binary create/truncate */
X   else
X      zoo_file = NULL;     /* don't create if just setting time */
X   zoo_status = NEW_ZOO;
X}
X
Xif (zoo_file == NULL)
X   prterror ('f', could_not_open, zoo_path);
Xzoo_han = fileno(zoo_file);			/* get file handle */
X
Xbasename(zoo_path, zoo_fname);      /* get basename of archive */
Xrootname (zoo_path, zoo_bak);       /* name without extension */
Xstrcat (zoo_bak, BACKUP_EXT);       /* name of backup of this archive */
X
X/* 
XFrom now on, both zoo_file and zoo_han can be used (one for stream I/O and
Xthe other for low-level I/O).  But before doing any low-level I/O, or 
Xbefore switching between reading and writing on the stream, a seek
Xmust be done on the stream to flush buffered data and synchronize file
Xpointers.
X*/
X
X/* Now we prepare the archive for adding one or more files.  If the archive
Xhas just been created, we write the archive header */
X
Xaddfname ("",0L,0,0); /* initialize table of files already in archive */
Xif (zoo_status == NEW_ZOO) {                 /* newly-created archive */
X   fwr_zooh (&zoo_header, zoo_file);
X   /* fwrite ((char *) &zoo_header, sizeof(zoo_header), 1, zoo_file); */
X   fseek (zoo_file, zoo_header.zoo_start, 0); /* seek to where data begins */
X} else {
X   /* read header and rewrite with updated version numbers */
X   rwheader (&zoo_header, zoo_file);
X   /* initialize latest_name to null string */
X	/* NOTE:  latest_name is not currently used for anything, but
X		may be used in the future for inserting files into the
X		archive in alphabetic order. */
X   *latest_name = '\0';
X
X   /* Skip existing files but add them to a list.  The variable last_old 
X   gets the tail of the old chain of directory entries */
X   skip_files (zoo_file, &latest_date, &latest_time, &delcount, 
X               latest_name, &last_old);
X}
X/* The file pointer is now positioned correctly to add a file to archive, 
Xunless the null directory entry is too short.  This will be fixed below. */
X
X/* If we are just setting time, do it and run. */
Xif (zootime) {
X#ifdef NIXTIME
X   fclose (zoo_file);
X   setutime (zoo_path, latest_date, latest_time);
X#else
X   settime (zoo_han, latest_date, latest_time);
X   fclose (zoo_file);
X#endif
X   prterror ('m', "Archive time adjusted.\n");
X   exit (0);
X}
X
X/* make list of files, excluding archive and its backup */
Xlongest = LONGEST;
Xif (!inargs) {
X   makelist(argc, argv, flist, MAXADD-2, zoo_fname, zoo_bak, ".", &longest);
X   /*                                    ^^         ^^       ^^ exclude */
X}
X
Xfptr = 0;	/* ready to get filename (if makelist() was called) or to
X					begin adding filenames (if reading them from stdin) */
X
Xwhile (1) {
X   unsigned int this_date, this_time;
X   int INLIST; /* boolean */
X   int RECENT; /* boolean */
X   int danger; /* if update requested and disk copy is out of date */
X	if (inargs) {
X	again: /* loop back if filename was same as archive name or its backup */
X		this_path = getstdin();			/* pathname from stdin, in static area */
X		modpath (this_path);
X		/* if moving files, add to list for later deletion;  if list overflows,
X			terminate addition loop and give warning message */
X		if (move) {
X			if (fptr >= MAXADD-2) {
X				prterror ('w', too_many_files, MAXADD-2);
X				this_path = NULL;
X			} else if (this_path != NULL) {
X				if (!COMPARE(zoo_fname,nameptr(this_path)) ||
X							!COMPARE(zoo_bak,nameptr(this_path)))
X					goto again;
X				else
X					flist[fptr++] = strdup (this_path);
X			}
X		}
X	} else  {
X		this_path = flist[fptr++];
X	}
X	/* exit the addition loop when no more pathnames are left */
X	if (this_path == NULL) {
X		/* in case stdin was being read, make sure flist is NULL-terminated */
X		flist[fptr] = NULL;
X		break;
X	}
X
X   basename (this_path, this_fname);   /* get just filename for later */
X
X   this_han = OPEN(this_path, F_READ);
X   if (this_han == -1) {
X      prterror ('e', could_not_open, this_path);
X      exit_status++;
X      continue;
X   }
X
X#ifndef PORTABLE
X   /* Test to see if this is a Z format file.  We assume the file is Z format
X      if (a) tag is correct and (b) type is 1 and (c) embedded filename
X      is not longer than FNAMESIZE.  
X   */
X   if (z_fmt) {
X      read (this_han, (char *) &tiny_header, sizeof(tiny_header));
X      if (tiny_header.tinytag == TINYTAG && tiny_header.type == 1 &&
X                        strlen (tiny_header.fname) <= FNAMESIZE)
X          /* ok */ ;
X      else {
X         close (this_han);
X         prterror ('e', "File %s does not have Z format.\n", this_fname);
X         exit_status++;
X         continue;
X      }
X   }
X#endif
X
X   /* get file time;  also fix name */
X#ifndef PORTABLE
X   if (z_fmt) {
X      direntry.date = tiny_header.date;
X      direntry.time = tiny_header.time;
X      strcpy (direntry.fname, tiny_header.fname);
X      direntry.dirlen = direntry.namlen = 0;
X   } else {
X#endif
X
X      /* Get timstamp of file being added */
X#ifdef GETUTIME
X      getutime (this_path, &direntry.date, &direntry.time);
X#else
X      gettime (this_han, &direntry.date, &direntry.time);
X#endif
X
X#ifdef FOLD
X      strlwr(this_fname);
X#endif
X      dosname (this_fname, direntry.fname);  /* MSDOS filename */
X
X   /*
X   Store long filename into direntry.lfname iff it is different from MSDOS
X   filename.  Also store directory name if need_dir is true.  Moved out of 
X   zooadd() so zooadd() doesn't get too big for optimization.
X   */
X   storefname (&direntry, this_path, need_dir);
X
X#ifndef PORTABLE
X   }
X#endif
X
X#ifdef DEBUG
Xprintf ("zooadd:  direntry.lfname = [%s]  direntry.dirname = [%s]\n",
X                  direntry.lfname, direntry.dirname);
X#endif
X
X   /* if update option, then we add file if it is already in the archive 
X      AND the archived file is older */
X
X   /* The following logic was derived from a Karnaugh map so it may
X      be hard to understand.  Essentially, if U=update requested,
X      N=new files requested, I=file is already in archive, and
X      R=file being archived is more recent than file already in
X      archive, then the boolean equation is:
X
X      add = U' (N' + I') + U (IR  + I'N)
X   */
X
X   /* Get the filename to use for this addition.  */
X   whichname = choosefname(&direntry);
X
X   /* get position in archive of any old file of same name, ignoring
X		any directory prefix if need_dir is not true */
X   prev_pos = inlist (fullpath (&direntry), &this_date, &this_time, !need_dir);
X
X   INLIST = prev_pos > 0;  /* already in archive if positive value */
X   if (INLIST) {
X      int result;
X      result = cmpnum (direntry.date, direntry.time, this_date, this_time);
X      RECENT = result > 0;
X      danger = result < 0;
X   } else
X      danger = 0; /* And RECENT is undefined and should not be used */
X
X   if (
X         !update && (!new || !INLIST) ||
X         update && (INLIST && RECENT || !INLIST && new)
X      )
X         ;  /* then continue and add file */
X   else {
X      if (update && danger)
X         prterror ('w', "Archived copy of %s is newer.\n", whichname);
X      close (this_han);
X      continue;   /* cycle back, skip this file */
X   }
X
X#ifdef CHEKDIR
X   /* Don't add if this is a directory */
X   if (isadir (this_han)) {
X      close (this_han);
X      continue;
X   }
X#endif
X
X#ifdef CHEKUDIR
X   /* Don't add if this is a directory */
X   if (isuadir (this_path)) {
X      close (this_han);
X      continue;
X   }
X#endif
X
X   /* Create directory entry for new file (but don't add just yet) */
X   /* NOTE:  we already got file date and time above for update option */
X	/* add tag, type, timezone, struc, system_id, and var_dir_len */
X	newdir (&direntry);
X
X   /* 
X   Write a null direntry entry.  Thus, if an error occurs or the program
X   is interrupted, the end of the archive will still be meaningful.
X   Special check needed for first one written.
X   */
X
X   direntry.next = direntry.offset = 0L;     /* trailing null entry */
X   this_dir_offset = ftell (zoo_file);
X   if (!firstfile) {
X      writedir (&direntry, zoo_file);
X   } else {
X      /*
X      Before adding the first file to the archive, we must make sure that
X      the previous directory chain (if any) is properly terminated with a
X      null entry of the right size.  If this is a new archive, we simply
X      write a new null entry of the right size.  If this is an existing
X      archive, we must check the size of the previous trailing null entry. 
X      If it is too small, we will back up to the most recent real directory
X      entry and change its .next field to point to end of file.  
X      */
X
X      if (zoo_status == NEW_ZOO) {
X         writedir (&direntry, zoo_file);        /* write null dir entry */
X      } else {
X#define  DIRLEN(x)   ((x.type<2) ? SIZ_DIR : (SIZ_DIRL+x.var_dir_len))
X         struct direntry tmpentry;
X         long tmppos;
X         int oldlen, newlen;
X         tmppos = ftell(zoo_file);
X         frd_dir (&tmpentry, zoo_file);
X         oldlen = DIRLEN(tmpentry);             /* get length of direntry */
X         newlen = DIRLEN(direntry);             /* ditto */
X
X         if (newlen > oldlen) {                 /* trouble */
X            fseek (zoo_file, last_old, 0);      /* back to previous entry */
X            frd_dir (&tmpentry, zoo_file);
X            fseek (zoo_file, 0L, 2);            /* get EOF position */
X            tmpentry.next = ftell(zoo_file);    /* point to EOF */
X            fseek (zoo_file, last_old, 0);      /* back to previous entry */
X            writedir (&tmpentry, zoo_file);     /* update it */
X            fseek (zoo_file, 0L, 2);            /* to EOF ... */
X            this_dir_offset = ftell (zoo_file);
X            writedir (&direntry, zoo_file);     /* ...write null dir entry */
X         } else
X            fseek (zoo_file, tmppos, 0);        /* long enough -- let it be */
X      } /* if (zoo_status == NEW_ZOO) ... */
X   } /* if (!firstfile) ... */
X
X   /* Now `this_dir_offset' is where the next directory entry will go */
X
X   /* first file added goes at EOF to avoid overwriting comments */
X   if (firstfile) {
X      fseek (zoo_file, 0L, 2);                     /* EOF */
X      direntry.offset = ftell (zoo_file) + SIZ_FLDR;
X   } else {
X      direntry.offset = this_dir_offset + SIZ_DIRL + 
X         direntry.var_dir_len + SIZ_FLDR;
X   }
X
X   direntry.major_ver = MAJOR_EXT_VER;    /* minimum version number needed */
X   direntry.minor_ver = MINOR_EXT_VER;    /* .. to extract */
X   direntry.deleted = 0;               /* not deleted, naturally */
X   direntry.comment = 0L;              /* no comment (yet) */
X   direntry.cmt_size = 0;          /* .. so no size either */
X
X   save_position = direntry.offset;          /* save position in case of error */
X
X   fseek (zoo_file, direntry.offset - SIZ_FLDR, 0);
X   fwrite (file_leader, SIZ_FLDR, 1, zoo_file);
X   fseek (zoo_file, ftell (zoo_file), 0);    /* for low-level I/O */
X
X#ifdef PORTABLE
X   prterror ('m', "%-*s -- ", longest, this_path);
X#else
X   if (z_fmt)
X      prterror ('m', "%-12s <== %-*s -- ", 
X         direntry.fname, longest, this_path);
X   else
X      prterror ('m', "%-*s -- ", longest, this_path);
X
X#ifdef COMMENT
X   prterror ('m', z_fmt ? "%-12s <== %-12s -- " : "%-12s -- ",
X                      direntry.fname, this_fname);       
X   prterror ('m', z_fmt ? "%-12s <== %-12s -- " : "%-12s -- ",
X                      direntry.fname, this_fname);       
X#endif /* COMMENT */
X#endif /* PORTABLE */
X
X   crccode = 0;
X   if (z_fmt) {
X      direntry.packing_method = tiny_header.packing_method;
X      lseek (this_han, (long) (sizeof(tiny_header) + tiny_header.cmt_size), 0);
X      status = getfile (this_han, zoo_han, tiny_header.size_now, 1);
X   } else if (suppress) {                    /* suppress compression */
X      direntry.packing_method = 0;           /* no compression */
X      status = getfile (this_han, zoo_han, -1L, 1);
X      /* status = copyfile (this_han, zoo_han); */
X   } else {
X      direntry.packing_method = 1;           /* compressed */
X      status = lzc(this_han, zoo_han);       /* add with compression */
X   }
X   if (status != 0) { /* if I */
X      ++exit_status;                         /* remember error */
X      if (status == 1)
X         prterror ('F', no_memory);
X      else if (status == 2)
X         prterror ('F', disk_full);
X      else if (status == 3)
X         prterror ('F', "Read error.\n");
X      else
X         prterror ('F', internal_error);
X      success = 0;
X   } else {
X      direntry.next  = ftell(zoo_file);
X      direntry.size_now = direntry.next - direntry.offset;
X
X      /* find and store original size of file just compressed */
X      direntry.org_size = tell (this_han);  /* should be EOF already */
X
X      /* If the compressed one is bigger, just copy */
X
X      if (direntry.size_now >= direntry.org_size &&   /* if II */
X            direntry.packing_method > 0) {
X         lseek (zoo_han, save_position, 0);     /* ..restore file pointer */
X         trunc (zoo_han);                       /* ..truncate file */
X         direntry.packing_method = 0;           /* ..and just copy */
X         lseek (this_han, 0L, 0);               /* (but rewind first!) */
X         crccode = 0;                           /* re-start crc from 0 */
X         status = getfile (this_han, zoo_han, -1L, 1);
X         /* status = copyfile (this_han, zoo_han); */
X         if (status != 0) {  /* if III */
X            success = 0;
X            printf (disk_full);
X            exit_status++;
X         } else {
X            success = 1;
X            direntry.next  = ftell(zoo_file);
X            direntry.size_now = direntry.next - direntry.offset;
X         } /* end if III */
X      } else {
X         success = 1;
X      } /* end if II */
X
X   } /* end if I */
X
X   if (success) {                               /* file successfully added */
X      addcount++;                               /* how many added */
X      direntry.file_crc = crccode;
X
X      /* remember most recent date and time */
X      if (cmpnum (direntry.date,direntry.time,latest_date,latest_time) > 0) {
X            latest_date = direntry.date;
X            latest_time = direntry.time;
X      }
X
X      /* mark any previous version of this file in archive as deleted */
X      dir2entry.comment = 0L;       /* for later use assigning to direntry */
X      dir2entry.cmt_size = 0;
X
X      if (!z_fmt)
X         prterror ('M', " (%2d%%) ", cfactor (direntry.org_size, direntry.size_now));
X
X      if (prev_pos > 0) {
X         long save_pos = ftell(zoo_file); /*DEBUG*/
X         ++delcount;          /* remember to pack */
X         prterror ('M', "replaced\n");
X         fseek (zoo_file, prev_pos, 0);
X         readdir (&dir2entry, zoo_file, 1);
X         dir2entry.deleted = 1;        /* mark as deleted */
X         fseek (zoo_file, prev_pos, 0);
X         writedir (&dir2entry, zoo_file);
X         fseek (zoo_file, save_pos, 0); /*DEBUG*/
X      } else prterror ('M', "added\n");
X
X      /* Preserve any old comment if we replaced the file */
X      direntry.comment = dir2entry.comment;
X      direntry.cmt_size = dir2entry.cmt_size;
X
X#ifndef PORTABLE
X      /* Copy comment if any from Z format file */
X      if (z_fmt && tiny_header.cmt_size != 0) {
X         lseek (this_han, (long) sizeof(tiny_header), 0); /* get to comment */
X#ifdef COMMENT
X         fseek (zoo_file, 0L, 2);   /* append comment to end */
X#endif
X         direntry.comment = ftell (zoo_file);
X         direntry.cmt_size = tiny_header.cmt_size;
X         /* 4th param is 0 for no CRC */
X         getfile (this_han, zoo_han, (long) tiny_header.cmt_size, 0);
X         direntry.next = ftell(zoo_file);
X      } 
X#endif
X
X      /* if user requested comments, any previous comment in a Z format
X         file may now be manually overwritten */
X      if (add_comment && !feof (stdin)) {
X         show_comment (&direntry, zoo_file, 1, whichname);
X         get_comment (&direntry, zoo_file, this_path);
X         direntry.next = ftell(zoo_file);    /* update .next ptr */
X      } /* end if */
X
X#ifndef PORTABLE
X      /* if adding Z format archive, copy relevant fields from its header */
X      if (z_fmt) {   /* moved out to shorten code & allow optimizer to work */
X         copyfields (&direntry, &tiny_header);
X      }
X#endif
X
X      debug((printf ("zooadd:  our new .next = [%lx].\n", direntry.next)))
X
X      {
X         long savepos = ftell(zoo_file);    /* save position */
X         fseek (zoo_file, this_dir_offset, 0);
X         writedir (&direntry, zoo_file);
X         fseek (zoo_file, savepos, 0);    /* restore position */
X      }
X
X   } else {                               /* file was not properly added */
X      lseek (zoo_han, save_position, 0);     /* ..restore file pointer */
X      trunc (zoo_han);                       /* ..truncate file */
X   } /* end if */
X   close (this_han);
Xif (!success)
X   break;
Xfirstfile = 0;
X} /* end for */
X
Xsave_position = ftell (zoo_file);
X
X/* Write a null direntry entry */
Xfseek (zoo_file, save_position, 0);
Xwritenull (zoo_han, MAXDIRSIZE);
Xtrunc (zoo_han);    /* truncate */
X
X#ifdef NIXTIME
Xfclose (zoo_file);
Xsetutime (zoo_path, latest_date, latest_time);
X#else
Xsettime (zoo_han, latest_date, latest_time);
Xfclose (zoo_file);
X#endif
X
Xif (!addcount) {                    /* no files added */
X   prterror ('m', "No files added.\n");
X   if (zoo_status == NEW_ZOO)
X      unlink (zoo_path);
X} else {
X   if (delcount && pack) { /* pack if user asked and found deleted entries */
X      prterror ('M', "-----\nPacking...");
X      zoopack (zoo_path, "PP");
X      prterror ('M', "done\n");
X   }
X
X   /* If files to move & we added some and no error so far, delete originals */
X   if (move && !exit_status)
X      if (kill_files (flist, longest) != 0)
X         exit_status++;
X}
X
Xif (exit_status)
X   exit (1);
X} /* end zoo_add */
SHAR_EOF
fi
if test -f 'zooadd2.c'
then
	echo shar: "will not over-write existing file 'zooadd2.c'"
else
sed 's/^X//' << \SHAR_EOF > 'zooadd2.c'
X#ifndef LINT
X/* @(#) zooadd2.c 1.11 87/05/29 12:55:51 */
Xstatic char sccsid[]="@(#) zooadd2.c 1.11 87/05/29 12:55:51";
X#endif /* LINT */
X
X/*
XCopyright (C) 1986, 1987 Rahul Dhesi -- All rights reserved
X*/
X#include "options.h"
X#include "zoo.h"
X#include <stdio.h>
X#include "various.h"
X#include "zoofns.h"
X#include "errors.i"
X#include "assert.h"
X#include "debug.h"
X#include "parse.h"
X
X/*
XMiscellaneous routines to support zooadd().
X*/
X
X/****************
XThis function is called with zoo_file positioned to the first
Xdirectory entry in an archive.  It skips past all existing files,
Xcounts the number of deleted files, saves the latest data and time
Xencountered, and adds all filenames encountered to a global list. The
Xlong filename is added if available, else the MSDOS filename is
Xadded.  
X*/
X
Xvoid skip_files (zoo_file, latest_date, latest_time, delcount, 
X                  latest_name, latest_pos)
XFILE *zoo_file;
Xunsigned int *latest_date, *latest_time;
Xint *delcount;
Xchar latest_name[];
Xlong *latest_pos;
X{
X   char *whichname;                    /* will point to name to use */
X   long save_offset, next_ptr;
X   struct direntry direntry;
X   struct direntry *drp = &direntry;
X
X   *latest_pos = 0L;
X   do {
X      /* read a directory entry */
X      save_offset = ftell (zoo_file);     /* save pos'n of this dir entry */
X      readdir (&direntry, zoo_file, 1);   /* read directory entry */
X      if (drp->next == 0L) {                 /* END OF CHAIN */
X         fseek (zoo_file, save_offset, 0);      /* back up */
X         break;                                 /* EXIT on end of chain */
X      } else
X         *latest_pos = save_offset;
X      /* remember most recent date and time, for files not marked deleted */
X      if (!drp->deleted)
X         if (drp->date > *latest_date ||
X            (drp->date == *latest_date && drp->time > *latest_time)) {
X               *latest_date = drp->date;
X               *latest_time = drp->time;
X         }
X      next_ptr = drp->next;            /* ptr to next dir entry */
X      if (drp->deleted)
X         ++(*delcount);                      /* count deleted entries */
X      /* add name of file and position of direntry into global list */
X      /* but only if the entry is not deleted */
X      if (!drp->deleted) {
X#ifdef FOLD
X			/* IS THIS REALLY NEEDED?  IF SO, WHAT ABOUT drp->lfname? */
X         strlwr(drp->fname);
X#endif
X			/* add full pathname to global list */
X			strcpy (latest_name, fullpath (drp));
X         addfname (latest_name, save_offset, drp->date, drp->time);
X      }
X      fseek (zoo_file, next_ptr, 0);   /* ..seek to next dir entry */
X   } while (next_ptr != 0L);              /* loop terminates on null ptr */
X}
X
X/*******************/
X/* kill_files() deletes all files in the supplied list of pointers to
Xfilenames */
X
Xint kill_files (flist, pathlength)
Xchar *flist[];                      /* list of ptrs to input fnames */
Xint pathlength;                     /* length of longest pathname */
X{
X   int status = 0;
X   int fptr;
X   prterror ('M', "-----\nErasing added files...\n");
X   for (fptr = 0;  flist[fptr] != NULL; fptr++) {
X      prterror ('m', "%-*s -- ", pathlength, flist[fptr]);
X      if (unlink (flist[fptr]) == 0) {
X         prterror ('M', "erased\n");
X      } else {
X         prterror ('w', "Could not erase %s.\n", flist[fptr]);
X         status = 1;
X      }
X   }
X   return (status);
X}
X
X#ifndef PORTABLE
X/*******************/
Xvoid copyfields (drp, thp)
Xstruct direntry *drp;
Xstruct tiny_header *thp;
X{
X   drp->org_size = thp->org_size;
X   drp->file_crc = thp->file_crc;
X   drp->size_now = thp->size_now;
X   drp->major_ver = thp->major_ver;
X   drp->minor_ver = thp->minor_ver;
X}
X#endif
X
X/*******************/
X/* processes option switches for zooadd() */
Xvoid opts_add (option, zootime, quiet, suppress, move, new, pack,
X          update, add_comment, z_fmt, need_dir, inargs)
Xchar *option;
Xint *zootime, *quiet, *suppress, *move, *new, *pack,
X   *update, *add_comment, *z_fmt, *need_dir, *inargs;
X
X{
X   if (*option == 'T') {
X      (*zootime)++;
X      option++;
X      while (*option) {
X         switch (*option) {
X            case 'q': (*quiet)++; break;
X            default:
X               prterror ('f', inv_option, *option);
X         }
X      option++;
X      }
X   }
X   
X   while (*option) {
X      switch (*option) {
X         case 'a': break;  
X         case 'f': (*suppress)++; break;        /* suppress compression */
X         case 'M': (*move)++; break;            /* delete files after adding them */
X         case 'n': (*new)++; break;             /* add only files not in archive */
X         case 'P': (*pack)++; break;            /* pack after adding */
X         case 'u': (*update)++;   break;        /* add only files already in archive */
X         case 'q': (*quiet)++; break;           /* be quiet */
X         case 'c': (*add_comment)++; break;     /* add comment */
X         case ':': *need_dir = 0; break;        /* don't store directories */
X         case 'I': (*inargs)++; break;        	/* get filenames from stdin */
X#ifndef PORTABLE
X         case 'z': (*z_fmt)++; break;           /* look for Z format files */
X#endif
X         default:
X            prterror ('f', inv_option, *option);
X      }
X      option++;
X   } /* end while */
X}  
X
X
X/* 
XStores long filename into direntry.lfname iff it is different from MSDOS
Xfilename.  Also stores directory name if need_dir is true.  Moved out of 
Xzooadd() so zooadd() doesn't get too big for optimization.
X*/
Xvoid storefname (direntry, this_path, need_dir)
Xstruct direntry *direntry;
Xchar *this_path;
Xint need_dir;
X{
X   struct path_st path_st;
X   parse (&path_st, this_path);
X   direntry->lfname[0] = '\0';
X   direntry->namlen = 0;
X   if (strcmp(path_st.lfname,direntry->fname) != 0) {
X         strcpy (direntry->lfname, path_st.lfname); /* full filename */
X         direntry->namlen = strlen(direntry->lfname) + 1;
X   }
X   if (need_dir) {
X      strcpy (direntry->dirname, path_st.dir);   /* directory name */
X      direntry->dirlen = strlen(direntry->dirname) + 1;
X      if (direntry->dirlen == 1) /* don't store trailing null alone */
X         direntry->dirlen = 0;
X   } else {
X      direntry->dirname[0] = '\0';
X      direntry->dirlen = 0;
X   }
X}
X
X/* 
XFunction getsdtin() gets a pathname from standard input, cleans
Xit if necessary by removing any following blanks/tabs and other
Xjunk, and returns it in a static area that is overwritten by each
Xcall.
X*/
Xchar *getstdin()
X{
X	char *chptr;									/* temp pointer */
X	static char tempname[PATHSIZE];
X	do {
X		if (fgets (tempname, PATHSIZE, stdin) == NULL)
X			return (NULL);
X		/* remove trailing blank, tab, newline */
X		for (chptr = tempname; *chptr != '\0';  chptr++) {
X			if (
X	/* PURIFY means remove trailing blanks/tabs and all subsequent chars */
X#ifdef PURIFY
X					*chptr == '\t' || *chptr == ' ' ||
X#endif
X					*chptr == '\n'						/* always remove trailing \n */
X				)
X			{
X	
X				*chptr = '\0';
X				break;
X			}
X		}
X	} while (*tempname == '\0');				/* get a nonempty line */
X#ifdef FOLD
X	strlwr (tempname);
X#endif
X	return (tempname);
X}
X
X/* 
XFunction newdir() adds some default information to a directory entry.
XThis will be a new directory entry added to an archive.
X*/
Xvoid newdir (direntry)
Xregister struct direntry *direntry;
X{
X   direntry->zoo_tag = ZOO_TAG;
X   direntry->type = 2;                  /* type is now 2 */
X   direntry->tz = 127;                  /* timezone unknown */
X   direntry->struc = 0;                 /* unstructured file */
X   direntry->system_id = 0;             /* identify UNIX filesystem */
X   direntry->var_dir_len = 
X      (direntry->dirlen > 0 || direntry->namlen > 0 ? 2 : 0) + 
X      direntry->dirlen + direntry->namlen;
X}
SHAR_EOF
fi
if test -f 'zoodel.c'
then
	echo shar: "will not over-write existing file 'zoodel.c'"
else
sed 's/^X//' << \SHAR_EOF > 'zoodel.c'
X#ifndef LINT
Xstatic char sccsid[]="@(#) zoodel.c 1.8 87/05/29 12:56:02";
X#endif /* LINT */
X
X/*
XCopyright (C) 1986, 1987 Rahul Dhesi -- All rights reserved
X*/
X#include "options.h"
X/* Deletes or undeletes entries from an archive.  choice=1 requests
X   deletion and choice=0 requests undeletion. */
X#include "portable.h"
X#include <stdio.h>
X#include "various.h" /* may not be needed */
X#include "zoo.h"
X#include "zoofns.h"
X#include "errors.i"
X
X#ifndef NOSIGNAL
X#include <signal.h>
X#endif
X
Xextern int quiet;
X
Xvoid zoodel (zoo_path, option, choice)
Xchar *zoo_path;
Xchar *option;
Xint choice;
X{
X#ifndef NOSIGNAL
X   int (*oldsignal)();        /* to save previous SIGINT handler */
X#endif
X   int delcount = 0;          /* how many entries we [un]deleted */
X   char matchname[PATHSIZE];  /* will hold full pathname */
X   register FILE *zoo_file;
X   struct zoo_header zoo_header;
X   struct direntry direntry;
X   unsigned int latest_date = 0;      /* so we can set time of archive later */
X   unsigned int latest_time = 0;
X   int pack = 0;              /* pack after deletion? */
X   int file_deleted = 0;      /* any files deleted? */
X   int one = 0;               /* del/undel one file only */
X   int done;                  /* loop control */   
Xwhile (*(++option)) {
X   switch (*option) {
X      case 'P': pack++; break;            /* pack after adding */
X      case 'q': quiet++; break;           /* be quiet */
X      case '1': one++; break;             /* del or undel only one file */
X      default:
X         prterror ('f', inv_option, *option);
X   }
X} /* end while */
X
X   /* Open archive for read/write/binary access.  It must already exist */
X   if ((zoo_file = fopen (zoo_path, FRWSTR)) == NULL) {
X      prterror ('f', could_not_open, zoo_path);
X   }
X   
X   /* read archive header */
X   frd_zooh (&zoo_header, zoo_file);
X   /* fread ((char *) &zoo_header, sizeof(zoo_header), 1, zoo_file); */
X   if ((zoo_header.zoo_start + zoo_header.zoo_minus) != 0L)
X      prterror ('f', failed_consistency);
X   fseek (zoo_file, zoo_header.zoo_start, 0); /* seek to where data begins */
X   
X   done = 0;            /* loop not done yet */
X   /* Go into loop deleting requested entries */
X   while (1) {
X      long this_dir_offset;
X      this_dir_offset = ftell (zoo_file);    /* save pos'n of this dir entry */
X      frd_dir (&direntry, zoo_file);
X      /* fread ((char *) &direntry, sizeof (direntry), 1, zoo_file); */
X      if (direntry.zoo_tag != ZOO_TAG) {
X         prterror ('f', bad_directory);
X      }
X      if (direntry.next == 0L) {                /* END OF CHAIN */
X         break;                                 /* EXIT on end of chain */
X      }
X      
X      /* Now [un]delete this entry if it isn't already [un]deleted and 
X         if filename matches.  WARNING: convention of choice=1 for
X         deleted entry must be same as in direntry definition in zoo.h */
X   
X      /* Test for "done" so if "one" option requested, [un]del only 1 file */
X      /* But we go through the whole archive to adjust archive time */
X
X		strcpy (matchname, fullpath (&direntry));		/* get full pathname */
X
X      if (!done && direntry.deleted != choice && 
X                                             needed(matchname)) {
X         prterror ('m', "%-14s -- ", matchname);
X         delcount++;
X         direntry.deleted = choice;
X         if (choice) 
X            file_deleted++;      /* remember if any files actually deleted */
X
X         fseek (zoo_file, this_dir_offset, 0);
X
X#ifndef NOSIGNAL
X         oldsignal = signal (SIGINT, SIG_IGN);  /* disable ^C for write */
X#endif
X         if (fwr_dir (&direntry, zoo_file) == -1)
X         /* if (fwrite ((char *) &direntry, sizeof(direntry), 1, zoo_file) < 1) */
X            prterror ('f', "Could not write to archive\n");
X#ifndef NOSIGNAL
X         signal (SIGINT, oldsignal);
X#endif
X         prterror ('M', choice ? "deleted\n" : "undeleted\n");
X         if (one)
X            done = 1;            /* if 1 option, done after 1 file */
X      }
X
X      /* remember most recent date and time if entry is not deleted */
X      if (!direntry.deleted)
X         if (direntry.date > latest_date ||
X            (direntry.date == latest_date && direntry.time > latest_time)) {
X               latest_date = direntry.date;
X               latest_time = direntry.time;
X         }
X      fseek (zoo_file, direntry.next, 0); /* ..seek to next dir entry */
X   } /* endwhile */
X
X   if (!delcount)
X      printf ("Zoo:  No files matched.\n");
X   else {
X#ifdef NIXTIME
X      fclose (zoo_file);
X      setutime (zoo_path, latest_date, latest_time);
X#else
X      fflush (zoo_file);         /* superstition:  might help time stamp */
X      settime (fileno(zoo_file), latest_date, latest_time);
X#endif
X   }
X
X#ifndef NIXTIME
Xfclose (zoo_file);
X#endif
X
Xif (file_deleted && pack) {   /* pack if files were deleted and user asked */
X   prterror ('M', "-----\nPacking...");
X   zoopack (zoo_path, "PP");
X   prterror ('M', "done\n");
X}
X
X}
SHAR_EOF
fi
if test -f 'zooext.c'
then
	echo shar: "will not over-write existing file 'zooext.c'"
else
sed 's/^X//' << \SHAR_EOF > 'zooext.c'
X#ifndef LINT
Xstatic char sccsid[]="@(#) zooext.c 1.8 87/05/29 12:56:07";
X#endif /* LINT */
X
X/*
XCopyright (C) 1986, 1987 Rahul Dhesi -- All rights reserved
X*/
X#include "options.h"
X/* Extract file from archive.  Extracts files specified in parameter-list 
X   from archive zoo_path.  If none specified, extracts all files from 
X   archive. */
X
X#include "zoo.h"
X#include "parse.h"      /* defines struct for parse() */
X
X#include "portable.h"   /* portable I/O definitions */
X#include "machine.h"    /* machine-specific declarations */
X
X#include <stdio.h>
X#include "various.h"
X
X#ifndef NOSIGNAL
X#include <signal.h>
X#endif
X
X#include "zoofns.h"
X
X/* for low-level I/O */
X#ifdef FLAT
X#include <types.h>
X#include <stat.h>
X#else
X#include <sys/types.h>
X#include <sys/stat.h>
X#endif
X
X#ifdef NOFCNTL
X#include <file.h>
X#else
X#include <fcntl.h>
X#endif
X
Xextern int quiet;
X
X#include "errors.i"
X
X/* Following two are used by ctrl_c() also, hence declared here */
Xchar extfname[LFNAMESIZE];             /* filename of extracted file */
Xstatic int this_han;                   /* handle of file to extract */
X
Xstatic int tofile;                     /* true if not pipe or null device */
Xextern unsigned int crccode;
Xextern char *out_buf_adr;              /* address of output buffer */
X
Xvoid zooext(zoo_path, option)
Xchar *zoo_path, *option;
X{
Xchar *whichname;                          /* which name to extract */
Xchar matchname[PATHSIZE];                 /* for pattern matching only */
X#ifndef NOSIGNAL
Xint (*oldsignal)();        /* to save previous SIGINT handler */
X#endif
Xint zoo_han;                              /* handle for open archive */
Xlong next_ptr;                            /* pointer to within archive */
Xstruct zoo_header zoo_header;             /* header for archive */
Xint status;                               /* error status */
Xchar ans[3];                              /* answer to "Overwrite?" */
Xint error_message = 1;                    /* Whether to give error message */
Xunsigned long disk_space;                 /* disk space left */
Xint matched = 0;                          /* Any files matched? */
Xint overwrite = 0;                        /* force overwrite of files? */
Xint needdel = 0;                          /* extract deleted files too */
Xint usepath = 0;                          /* use path for extraction */
Xint todot = 0;                            /* extract relative to . */
Xint zootime = 0;                          /* just set archive time */
Xint badcrc_count = 0;                     /* how many files with bad CRC */
Xint bad_header = 0;                       /* to avoid spurious messages later */
Xlong fiz_ofs = 0;                         /* offset where to start */
Xint pipe = 0;                             /* are we piping output? */
Xint fast_ext = 0;                         /* fast extract as *.?Z? */
Xint null_device = 0;                      /* are we sending to null device? */
Xint alloc_size;                           /* disk allocation unit size */
Xstruct direntry direntry;                 /* directory entry */
X
X#ifdef OOZ
X   static char extract_ver[] = "A higher version of OOZ is needed to extract ";
X   static char no_space[] = "Insufficient disk space to extract ";
X#else
X   static char extract_ver[] = "Zoo %d.%d is needed to extract %s.\n";
X   static char no_space[] = "Insufficient disk space to extract %s.\n";
X#endif
X
X#ifndef OOZ
Xwhile (*option) {
X   switch (*option) {
X#ifndef PORTABLE
X      case 'z': fast_ext++; break;
X#endif
X      case 'x':
X      case 'e': break;
X      case 'N': null_device++; break;
X      case 'O': overwrite += 2; break;
X      case 'o': overwrite++; break;
X      case 'p': pipe++; break;
X      case 'd': needdel++; break;
X      case 'q': quiet++; break;
X      case '/': usepath++; break;
X      case '.': todot++; break;
X      case '@': 
X         ++option;
X         fiz_ofs = calc_ofs(option); 
X         goto no_more;
X      default:
X         prterror ('w', option_ignored, *option);
X         break;
X   }
X   option++;
X}
X
Xno_more: /* come from exit in while loop above */
X
X
Xif (overwrite == 1)                 /* must be at least 2 to begin with */
X   overwrite--;
X
Xif (null_device && pipe) {
X   prterror ('w', option_ignored, 'p');
X   pipe = 0;
X}
X
Xif (overwrite && pipe)
X   prterror ('w', option_ignored, 'O');
X
Xif (null_device && fast_ext) {
X   prterror ('w', option_ignored, 'N');
X   null_device = 0;
X}
X
Xtofile = !pipe && !null_device;     /* sending to actual file */
X#else
Xtofile = 1;
X#endif
X
X
Xzoo_han = OPEN(zoo_path, F_READ);
X
Xif (zoo_han == -1)
X#ifdef OOZ
X   prterror ('f', could_not_open, zoo_path, ".\n");
X#else
X   prterror ('f', could_not_open, zoo_path);
X#endif
X
Xif (fiz_ofs != 0L) {                /* if offset specified, start there */
X   prterror('m', "Starting at %ld\n", fiz_ofs);
X   lseek (zoo_han, fiz_ofs, 0);
X} else {
X   /* read header */
X   rd_zooh (&zoo_header, zoo_han);
X   /* read (zoo_han, (char *) &zoo_header, sizeof(zoo_header)); */
X   if ((zoo_header.zoo_start + zoo_header.zoo_minus) != 0L) {
X      prterror ('w', failed_consistency, (char *) NULL, (char *) NULL);
X      bad_header++;
X   }
X   lseek (zoo_han, zoo_header.zoo_start, 0); /* seek to where data begins */
X}
X
X#ifndef PORTABLE
Xdisk_space = space (0, &alloc_size);         /* remember disk space left */
X#else
Xalloc_size = 0;
Xdisk_space = MAXLONG;              /* infinite disk space */
X#endif
X
X#ifndef OOZ
X/* Ooz does no piping */
X/* if piping output we open the output device just once */
X/* NOTE:  the DOS device "NUL" was causing the system to hang up.
X   therefore we create our own.  All functions called must recognize
X   -2 as a null output handle and ignore all output to that */
Xif (null_device) {                  /* -2 means null device */
X   this_han = -2;
X} else if (pipe)
X   this_han = (int) fileno(stdout);    /* standard output */
X#endif
X
Xwhile (1) {
X   rd_dir (&direntry, zoo_han);
X   /* read (zoo_han, (char *) &direntry, sizeof(direntry)); */
X   if (direntry.zoo_tag != ZOO_TAG) {
X      long currpos, zoolength;
X
X#ifdef OOZ
X         prterror ('f', invalid_header, (char *) NULL, (char *) NULL);
X#else
X         prterror ('F', invalid_header);
X
X         /* Note:  if header was bad, there's no point trying to find
X            how many more bytes aren't processed -- our seek position is
X            likely very wrong */
X   
X         if (!bad_header)
X            if ((currpos = tell (zoo_han)) != -1L)
X               if (lseek (zoo_han, 0L, 2) != -1)
X                  if ((zoolength = tell (zoo_han)) != -1L)
X                     printf (cant_process, zoolength - currpos);              
X         exit (1);
X#endif
X   }
X   if (direntry.next == 0L) {                /* END OF CHAIN */
X      break;                                 /* EXIT on end of chain */
X   }
X   next_ptr = direntry.next;                 /* ptr to next dir entry */
X
X   whichname = choosefname(&direntry);       /* which filename */
X   fixfname(whichname);                      /* fix syntax */
X	strcpy (matchname, fullpath (&direntry));	/* get full pathname */
X
X   if (todot && *direntry.dirname == *PATH_CH) {
X      char tmpstr[PATHSIZE];
X      strcpy(tmpstr, direntry.dirname);
X      strcpy(direntry.dirname,CUR_DIR);
X      strcat(direntry.dirname, tmpstr);
X   }
X
X   /* matchname now holds the full pathname for pattern matching */
X
X   if ( ( (needdel && direntry.deleted) ||
X            (needdel < 2 && !direntry.deleted)
X        ) && needed(matchname)) {
X      matched++;           /* update count of files extracted */
X
X      if (direntry.major_ver > MAJOR_EXT_VER ||
X         (direntry.major_ver == MAJOR_EXT_VER && 
X            direntry.minor_ver > MINOR_EXT_VER)) {
X
X#ifdef OOZ
X            prterror ('e', extract_ver, whichname, ".\n");
X#else
X            prterror ('e', extract_ver, direntry.major_ver, 
X                           direntry.minor_ver, whichname);
X#endif
X            goto loop_again;
X      }
X
X      /* 
X      If extracting to null device, or if user requested extraction
X      of entire path, include any directory name in filename.
X      If extraction to current directory requested, and if extfname
X      begins with path separator, fix it */
X
X      strcpy (extfname, whichname);
X      if ((usepath || null_device) && direntry.dirlen > 0) {
X         combine(extfname, direntry.dirname, whichname);
X         if (usepath > 1 && !null_device)
X            makepath(direntry.dirname);         /* make dir prefix */
X      }
X
X      if (tofile) {
X         int present = 0;
X
X#ifndef OOZ
X#ifndef PORTABLE
X         /* 
X         if Z format (fast) extraction, extension is created as
X         follows:  for no current extension, new extension is "zzz";
X         for current extension "a", new extension is "azz";  for 
X         current extension "ab", new extension is "azb";  and for
X         current extension "abc", new extension is "azc".
X         */
X           
X         if (fast_ext) {
X            int length;
X            struct path_st path_st;
X            parse (&path_st, extfname);         /* split filename */
X            strcpy (extfname, path_st.fname);   /* just root filename */
X            length = strlen (path_st.ext);
X            strcat (extfname, ".");
X            if (length == 0)
X               strcat (extfname, "zzz");        /* no ext -> .zzz */
X            else if (length == 1) {
X               strcat (extfname, path_st.ext);
X               strcat (extfname, "zz");         /* *.?    -> *.?zz */
X            } else { /* length is 2 or 3 */
X               if (length == 2)                 /* allow .aa, .ab, etc. */
X                  path_st.ext[2] = path_st.ext[1];
X               path_st.ext[1] = 'z';
X               strcat (extfname, path_st.ext);  /* *.??   -> *.?z? */
X            }
X         }
X#endif   /* ifndef PORTABLE */
X#endif   /* ifndef OOZ */
X
X
X         if (overwrite) {
X            this_han = CREATE(extfname, F_RDWR);
X         } else {
X            if (exists (extfname)) {
X               present = 1;
X               this_han = -1;
X            } else
X               this_han = CREATE(extfname, F_RDWR);
X         }
X         if (this_han == -1) {
X            if (present == 1) {      /* if file exists already */
X               do {
X
X#ifdef OOZ
X                     putstr ("Overwrite "); putstr (extfname);
X                     putstr (" (Yes/No/All)? ");
X#else
X                     printf ("Overwrite %s (Yes/No/All)? ", extfname);
X#endif
X                  fflush (stdin);
X                  fgets (ans, 3, stdin);
X                  strlwr (ans);
X               } while (*ans != 'y' && *ans != 'n' && *ans != 'a');
X   
X               /* NOTE:  We use CREATE even if we are trying to overwrite
X                  the file, because a data path utility (Search or Dpath)
X                  could have fooled us before. */
X               if (*ans == 'a')
X                  overwrite++;
X               if (*ans == 'y' || *ans == 'a') {
X                  this_han = CREATE(extfname, F_RDWR);
X                  error_message = 1; /* give error message if open fails */
X               } else {
X                  error_message = 0; /* user said 'n', so no error message */
X               }
X            } else {
X               error_message = 1;   /* Real error -- give error message */
X            }
X         } /* end if */
X      } /* end if */
X
X      if (this_han == -1) {         /* file couldn't be opened */
X         if (error_message == 1) {
X            unlink (extfname);
X#ifdef OOZ
X               prterror ('e', could_not_open, extfname, " for output.\n");
X#else
X               prterror ('e', "Can't open %s for output.\n", extfname);
X#endif
X
X#ifndef PORTABLE
X            /* if error was due to full disk, abort */
X            if (space(0, &alloc_size) < alloc_size)
X#endif
X               prterror ('f', disk_full, (char *) NULL, (char *) NULL);
X
X         }
X      } else if (lseek (zoo_han, direntry.offset, 0) == -1L) {
X         prterror ('e', "Could not seek to file data.\n");
X         close_han (this_han);
X      } else {
X#ifndef PORTABLE
X         /* check Dos's free disk space if we seem to be running low 
X            (within 1 cluster of being full) */
X         if (tofile && disk_space < direntry.org_size + alloc_size) {
X            disk_space = space (0, &alloc_size);
X            if (disk_space < alloc_size) {
X               close_han (this_han);
X               unlink (extfname);
X               prterror ('f', disk_full, (char *) NULL, (char *) NULL);
X            }              
X         }
X#endif
X         if (tofile && disk_space < direntry.org_size) {
X#ifdef PORTABLE
X            ;
X#else
X#ifdef OOZ
X               prterror ('e', no_space, extfname, ".\n");
X#else
X               prterror ('e', no_space, extfname);
X#endif
X
X            unlink (extfname);               /* delete any created file */
X#endif   /* portable */
X
X         } else { 
X
X#ifndef OOZ
X#ifndef PORTABLE
X            if (fast_ext) {            /* fast ext -> create header */
X               void make_tnh();
X               struct tiny_header tiny_header;
X               make_tnh(&tiny_header, &direntry);
X               write (this_han, (char *) &tiny_header, sizeof (tiny_header));
X
X               if (direntry.cmt_size != 0) { /* copy comment */
X                  long save_pos;
X                  save_pos = tell (zoo_han);
X                  lseek (zoo_han, direntry.comment, 0);
X                  getfile (zoo_han, this_han, 
X                          (long) direntry.cmt_size, 0);
X                  lseek (zoo_han, save_pos, 0);
X               }
X            }
X#endif /* ifndef PORTABLE */
X#endif /* ifndef OOZ */
X
X            crccode = 0;      /* Initialize CRC before extraction */
X#ifdef OOZ
X               putstr (extfname); 
X               {
X                  register int i;
X                  for (i = strlen(direntry.fname); i < 13; i++)
X                     putstr (" ");
X                  putstr ("-- ");
X               }
X#else
X               if (!pipe) {
X#ifdef PORTABLE
X                  prterror ('m', "%-14s -- ", extfname);
X#else
X                  if (fast_ext)
X                     prterror ('m', "%-12s ==> %-12s -- ", 
X                        direntry.fname, extfname);
X                  else
X                     prterror ('m', "%-12s -- ", extfname);
X#ifdef COMMENT
X                  prterror ('m', 
X                        fast_ext ? "%-12s ==> %-12s -- " : "%-12s -- ",
X                        direntry.fname, extfname);
X#endif /* COMMENT */
X#endif /* PORTABLE */
X
X               } else {            /* must be pipe */
X                  prterror ('M', "\n\n********\n%s\n********\n", direntry.fname);
X
X#ifdef COMMENT
X#ifdef SETMODE
X                  MODE_BIN(this_han);           /* make std output binary so
X                                                   ^Z won't cause error */
X#endif
X#endif
X               }
X#endif /* OOZ */
X
X#ifndef NOSIGNAL
X#ifndef OOZ
X            if (tofile)
X#endif /* not OOZ */
X               {
X                  oldsignal = signal (SIGINT, SIG_IGN);
X                  if (oldsignal != SIG_IGN) 
X                     signal (SIGINT, ctrl_c); /* Trap ^C & erase partial file */
X               }
X#endif /* not NOSIGNAL */
X
X            if (direntry.packing_method == 0)
X               /* 4th param 1 means CRC update */
X               status = getfile (zoo_han, this_han, direntry.size_now, 1);
X
X#ifndef OOZ
X            else if (fast_ext)
X               /* 4th param 0 means no CRC update */
X               status = getfile (zoo_han, this_han, direntry.size_now, 0);
X#endif
X
X
X            else if (direntry.packing_method == 1)
X               status = lzd (zoo_han, this_han); /* uncompress */
X            else {
X#ifdef OOZ
X               prterror ('e', "File ", whichname,
X                  ":  impossible packing method.\n", "");
X#else
X               prterror ('e', "File %s:  impossible packing method.\n",
X                  whichname);
X#endif
X                  unlink(extfname);
X                  goto loop_again;
X            }
X
X
X#ifndef NOSIGNAL
X#ifndef OOZ
X            if (tofile)
X#endif /* not OOZ */
X               signal (SIGINT, oldsignal);
X#endif /* not NOSIGNAL */
X
X#ifdef COMMENT
X#ifndef OOZ
X#ifdef SETMODE
X            if (pipe)
X               MODE_TEXT(this_han);          /* restore text mode */
X#endif
X#endif
X#endif
X   
X#ifndef OOZ
X            if (tofile) {
X#endif
X               /* set date/time of file being extracted */
X#ifdef NIXTIME
X               close_han (this_han);
X               setutime (extfname, direntry.date, direntry.time);
X#else
X               settime (this_han, direntry.date, direntry.time);
X               close_han (this_han);
X#endif
X#ifndef OOZ
X            }
X#endif
X            if (status != 0) {
X               if (tofile)
X                  unlink (extfname);
X               if (status == 1) {
X                  memerr();
X               /* To avoid spurious errors due to ^Z being sent to screen,
X                  we don't check for I/O error if output was piped */
X               } else if (!pipe && (status == 2 || status == 3)) {
X#ifdef OOZ
X                     prterror ('e', no_space, direntry.fname, ".\n");
X#else
X                     prterror ('e', no_space, direntry.fname);
X#endif
X               }
X            } else {
X               /* file extracted, so update disk space.  */
X               /* we subtract the original size of the file, rounded
X                  UP to the nearest multiple of the disk allocation
X                  size. */
X#ifndef PORTABLE
X               {
X                  unsigned long temp;
X                  temp = (direntry.org_size + alloc_size) / alloc_size;
X                  disk_space -= temp * alloc_size;
X               }
X#endif
X
X#ifdef OOZ
X               if (direntry.file_crc != crccode)
X                  putstr ("extracted  \007WARNING:  Bad CRC.\n");
X               else
X                  putstr ("extracted\n");
X#else
X               if (!fast_ext && direntry.file_crc != crccode) {
X                  badcrc_count++;
X                  if (!pipe) {
X                     if (!null_device)
X                        prterror ('M', "extracted   ");
X                     prterror ('w', bad_crc, direntry.fname);
X                  }
X                  else {   /* duplicate to standard error */
X                     static char stars[] = "\n******\n";
X                     putstr (stars);
X                     prterror ('w', bad_crc, direntry.fname);
X                     putstr (stars);
X                     fprintf (stderr, "WARNING:  ");
X                     fprintf (stderr, bad_crc, direntry.fname);
X                  }
X               } else
X                  if (!pipe)
X                     prterror ('M', null_device ? "OK\n" : "extracted\n");
X#endif
X
X            } /* end if */
X         } /* end if */
X      } /* end if */
X   } /* end if */
X
Xloop_again:
X   lseek (zoo_han, next_ptr, 0); /* ..seek to next dir entry */
X} /* end while */
X
Xclose (zoo_han);
Xif (!matched)
X   putstr (no_match);
X
X#ifndef OOZ
Xif (badcrc_count) {
X   prterror ('w', "%d File(s) with bad CRC.\n", badcrc_count);
X   exit (1);
X} else if (null_device)
X   prterror ('m', "Archive seems OK.\n");
X#endif
X
X} /* end zooext */
X
X/* close_han() */
X/* closes a handle if and only if we aren't sending output to 
X   a pipe or to the null device */
X
Xvoid close_han (handle)
Xint handle;
X{
X   if (tofile)
X      close (handle);
X}
X
X/* Ctrl_c() is called if ^C is hit while a file is being extracted.
X   It closes the files, deletes it, and exits. */
Xint ctrl_c()
X{
X#ifndef NOSIGNAL
X   signal (SIGINT, SIG_IGN);     /* ignore any more */
X#endif
X   close (this_han);
X   unlink (extfname);
X   exit (1);
X}
X
X/* make_tnh copies creates a tiny_header */
Xvoid make_tnh (tiny_header, direntry)
Xstruct tiny_header *tiny_header;
Xstruct direntry *direntry;
X{
X   tiny_header->tinytag = TINYTAG;
X   tiny_header->type = 1;
X   tiny_header->packing_method = direntry->packing_method;
X   tiny_header->date = direntry->date;
X   tiny_header->time = direntry->time;
X   tiny_header->file_crc = direntry->file_crc;
X   tiny_header->org_size = direntry->org_size;
X   tiny_header->size_now = direntry->size_now;
X   tiny_header->major_ver = direntry->major_ver;
X   tiny_header->minor_ver = direntry->minor_ver;
X   tiny_header->cmt_size = direntry->cmt_size;
X   strcpy (tiny_header->fname, direntry->fname);
X} 
SHAR_EOF
fi
if test -f 'zoofns.h'
then
	echo shar: "will not over-write existing file 'zoofns.h'"
else
sed 's/^X//' << \SHAR_EOF > 'zoofns.h'
X/* @(#) zoofns.h 1.7 87/05/03 16:02:33 */
X
X/*
XThe contents of this file are hereby released to the public domain.
X
X                           -- Rahul Dhesi 1986/11/14
X*/
X
X
X/* Defines function declarations for all Zoo functions */
X
X#ifdef LINT_ARGS
Xlong calc_ofs(char *);
Xchar *addext (char *, char *);
Xchar *choosefname (struct direntry *);
Xchar *combine (char[], char *, char *);
Xchar *emalloc (unsigned int);
Xchar *erealloc (char *, unsigned int);
Xchar *findlast (char *, char *);
Xchar *fixfname (char *);
Xchar *fullpath (struct direntry *);
Xchar *getstdin ();
Xchar *lastptr (char *);
Xchar *nameptr (char *);
Xchar *newcat (char *, char *);
Xchar *memset (char *, int, unsigned);
Xchar *nextfile (int, char *, int);
Xint addfile (int, int);
Xint cfactor (long, long);
Xint chname (char *, char *);
Xint cmpnum (unsigned int, unsigned int, unsigned int, unsigned int);
Xint ctrl_c();
Xint exists (char *);
Xint frd_zooh (struct zoo_header *, FILE *);
Xint frd_dir (struct direntry *, FILE *);
Xint fwr_dir (struct direntry *, FILE *);
Xint fwr_zooh (struct zoo_header *, FILE *);
Xint getfile (int, int, long, int);
Xint handle_break();
Xint kill_files (char *[], int);
Xint lzc (int, int);
Xint lzd (int, int);
Xint match_half (char *, char *);
Xint match (char *, char *);
Xint readdir (struct direntry *, FILE *, int);
Xint settime (int, unsigned int, unsigned int);
Xint strcmpi (char *, char *);
Xint rd_zooh (struct zoo_header *, int);
Xint rd_dir (struct direntry *, int);
Xint wr_zooh (struct zoo_header *, int);
Xint wr_dir (struct direntry *, int);
Xlong inlist (char *, unsigned int *, unsigned int *, int);
Xlong tell (int);
Xunsigned long space (int, int *);
Xvoid addbfcrc(char *, int);
Xvoid addfname (char *, long, unsigned int, unsigned int);
Xvoid basename (char *, char []);
Xvoid break_off();
Xvoid close_han (int);
Xvoid comment (char *, char *);
Xvoid extension (char *, char []);
Xvoid fcbpath (struct dta_t *, char *, char *);
Xvoid fixslash (char *);
Xvoid makelist (int, char *[], char *[], int, char *, char *, char *, int *);
Xvoid memerr();
Xvoid newdir (struct direntry *);
Xvoid parse (struct path_st *, char *);
Xvoid prterror(int, char *, );
Xvoid rootname (char *, char *);
Xvoid skip_files (FILE *, unsigned int *, unsigned int *, int *, 
X                  char [], long *);
Xvoid writedir (struct direntry *, FILE *);
Xvoid writenull (int, int);
Xvoid zooadd(char *, int, char **, char *);
Xvoid zoodel(char *, char *, int);
Xvoid zooext(char *, char *);
Xvoid zoolist(char **, char *, int);
Xvoid zoopack (char *, char *);
X
X#else
X/* if no LINT_ARGS */
X
Xlong calc_ofs();
Xchar *addext ();
Xchar *choosefname ();
Xchar *combine ();
Xchar *emalloc ();
Xchar *erealloc ();
Xchar *findlast ();
Xchar *fixfname ();
Xchar *fullpath ();
Xchar *getstdin ();
Xchar *lastptr ();
Xchar *nameptr ();
Xchar *newcat ();
Xchar *memset ();
Xchar *nextfile ();
Xint addfile ();
Xint cfactor ();
Xint chname ();
Xint cmpnum ();
Xint ctrl_c();
Xint exists ();
Xint frd_zooh ();
Xint frd_dir ();
Xint fwr_dir ();
Xint fwr_zooh ();
Xint getfile ();
Xint handle_break();
Xint kill_files ();
Xint lzc ();
Xint lzd ();
Xint match_half ();
Xint match ();
Xint readdir ();
Xint settime ();
Xint strcmpi ();
Xint rd_zooh ();
Xint rd_dir ();
Xint wr_zooh ();
Xint wr_dir ();
Xlong inlist ();
Xlong tell ();
Xunsigned long space ();
Xvoid addbfcrc();
Xvoid addfname ();
Xvoid basename ();
Xvoid break_off();
Xvoid close_han ();
Xvoid comment ();
Xvoid extension ();
Xvoid fcbpath ();
Xvoid fixslash ();
Xvoid makelist ();
Xvoid memerr();
Xvoid newdir ();
Xvoid parse ();
Xvoid prterror();
Xvoid rootname ();
Xvoid skip_files ();
Xvoid writedir ();
Xvoid writenull ();
Xvoid zooadd();
Xvoid zoodel();
Xvoid zooext();
Xvoid zoolist();
Xvoid zoopack ();
X#endif /* end of no LINT_ARGS */
SHAR_EOF
fi
exit 0
#	End of shell archive
-- 
Rahul Dhesi         UUCP:  {ihnp4,seismo}!{iuvax,pur-ee}!bsu-cs!dhesi

-- 

Rich $alz
Cronus Project, BBN Labs			rsalz@bbn.com
Moderator, comp.sources.unix			sources@uunet.uu.net