[comp.sources.unix] v17i072: Zoo archive program, Part09/10

rsalz@uunet.uu.net (Rich Salz) (02/03/89)

Submitted-by: Rahul Dhesi <bsu-cs!dhesi>
Posting-number: Volume 17, Issue 72
Archive-name: zoo2/part09

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 9 (of 10)."
# Wrapped by rsalz@papaya.bbn.com on Thu Feb  2 18:04:05 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'zooio.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'zooio.h'\"
else
echo shar: Extracting \"'zooio.h'\" \(2074 characters\)
sed "s/^X//" >'zooio.h' <<'END_OF_FILE'
X/* @(#) zooio.h 2.7 88/01/27 19:39:24 */
X
X/*
XDeclarations for portable I/O
X
XThe contents of this file are hereby placed in the public domain.
X
X											-- Rahul Dhesi 1988/01/24
X*/
X#ifndef	OK_STDIO
X#include <stdio.h>
X#define	OK_STDIO
X#endif
X
X#ifndef PARMS
X#ifdef LINT_ARGS
X#define	PARMS(x)		x
X#else
X#define	PARMS(x)		()
X#endif
X#endif
X
X/*
XIn theory, all I/O using buffered files could be replaced with unbuffered
XI/O simply by changing the following definitions.  This has not been tried
Xout yet, and there may be some remaining holes in the scheme.  On systems
Xwith limited memory, it might prove necessary to use unbuffered I/O
Xonly.
X*/
Xtypedef FILE *ZOOFILE;
X#define NOFILE		((ZOOFILE) 0)
X#define NULLFILE	((ZOOFILE) -1)		/* or any unique value */
X#define STDOUT		stdout
X
X#ifdef FILTER
X#define STDIN		stdin
X#endif
X
X#ifdef IO_MACROS
X#define zooread(file, buffer, count)		fread (buffer, 1, count, file)
X#define zoowrite(file, buffer, count) \
X	(file == NULLFILE ? count : fwrite (buffer, 1, count, file))
X#define zooseek(file, offset, whence)		fseek (file, offset, whence)
X#define zootell(file)							ftell (file)
X#else
Xint zooread PARMS((ZOOFILE, char *, int));
Xint zoowrite PARMS((ZOOFILE, char *, int));
Xlong zooseek PARMS((ZOOFILE, long, int));
Xlong zootell PARMS((ZOOFILE));
X#endif /* IO_MACROS */
X
XZOOFILE zooopen PARMS((char *, char *));
XZOOFILE zoocreate PARMS((char *));
Xint zooclose PARMS((ZOOFILE));
Xint zootrunc PARMS((ZOOFILE));
X
X#ifdef LINT_ARGS
Xchar *choosefname (struct direntry *);
Xchar *fullpath (struct direntry *);
Xint frd_zooh (struct zoo_header *, ZOOFILE);
Xint frd_dir (struct direntry *, ZOOFILE);
Xint fwr_dir (struct direntry *, ZOOFILE);
Xint fwr_zooh (struct zoo_header *, ZOOFILE);
Xint readdir (struct direntry *, ZOOFILE, int);
Xvoid rwheader (struct zoo_header *, ZOOFILE, int);
Xvoid newdir (struct direntry *);
Xvoid writedir (struct direntry *, ZOOFILE);
X#else
Xchar *choosefname ();
Xchar *fullpath ();
Xint frd_zooh ();
Xint frd_dir ();
Xint fwr_dir ();
Xint fwr_zooh ();
Xint readdir ();
Xvoid rwheader ();
Xvoid newdir ();
Xvoid writedir ();
X#endif
END_OF_FILE
if test 2074 -ne `wc -c <'zooio.h'`; then
    echo shar: \"'zooio.h'\" unpacked with wrong size!
fi
# end of 'zooio.h'
fi
if test -f 'zoomem.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'zoomem.h'\"
else
echo shar: Extracting \"'zoomem.h'\" \(2456 characters\)
sed "s/^X//" >'zoomem.h' <<'END_OF_FILE'
X/* @(#) zoomem.h 2.1 87/12/25 12:26:18 */
X
X/*
XThe contents of this file are hereby released to the public domain.
X
X                           -- Rahul Dhesi 1986/11/14
X
XDefines parameters used for memory allocation.  
X
X   Xenix notes:  Under Xenix release 3.0 on an Intel 310 machine with an
X   80286 cpu, memory is very tight when the small memory model is used. 
X   Ooz won't fit if the buffers are 2 K for input and 6 K for output.  
X   It does fit if both input and output buffers are 1 K each.  Under
X   the large memory model there is no problem.  Zoo requires the large
X   memory model.
X
X   AT&T 3B2:  There seem to be no problem at all.  Buffers can always
X   be 8192 each.
X*/
X
X#ifdef OOZ
X#define  IN_BUF_SIZE       8192
X#define  OUT_BUF_SIZE      8192
X#else
X#define  IN_BUF_SIZE       8192
X#define  OUT_BUF_SIZE      8192
X#endif
X
X#define  MEM_BLOCK_SIZE    (IN_BUF_SIZE + OUT_BUF_SIZE)
X
X/* 
XLIST_SIZE is the size of the list built by Zoo to hold all filenames
Xencountered in an archive.  This is used to know when to replace an
Xalready existing file.  The date and time stored with the filename is
Xused in comparisons when an archive update has been requested.  To 
Xadd a file to an archive, the archive must not already have more than
XLIST_SIZE files in it.
X
XZOOCOUNT is the number of archive names that may be matched by the
Xarchive filespec specified for a list.
X
XMAXADD is the number of filenames that may be added to an archive
Xat one go.  The total number of files that an archive may contain
Xis not determined by MAXADD but is determined by LIST_SIZE.
X
XIf USE_MALLOC is defined it tells the memory allocation routine 
Xemalloc() not to bother with maintaining its own memory blocks but just to 
Xpass on requests to the library function malloc().  If USE_MALLOC is not 
Xdefined, emalloc() does its own memory management to save overhead on a 
Xmachine with limited memory.
X*/
X
X#ifdef   SMALL_MEM
X#define  LIST_SIZE  (200)
X#define  ZOOCOUNT   (30)
X#define  MAXADD     (100)
X#define  USE_MALLOC
X#endif
X
X#ifdef   MED_MEM
X#define  LIST_SIZE  (400)
X#define  ZOOCOUNT   (50)
X#define  MAXADD     (200)
X#define  USE_MALLOC
X#endif
X
X#ifdef   BIG_MEM
X#define  LIST_SIZE  (4000)
X#define  ZOOCOUNT   (400)
X#define  MAXADD     (4000)
X#define  USE_MALLOC
X#endif
X
X/* Customizable sizes */
X#ifdef   SPEC_MEM
X#define  LIST_SIZE   (600)
X#define  ZOOCOUNT    (100)
X#define  MAXADD      (400)
X#endif
X
Xextern char *out_buf_adr;              /* global I/O buffer */
END_OF_FILE
if test 2456 -ne `wc -c <'zoomem.h'`; then
    echo shar: \"'zoomem.h'\" unpacked with wrong size!
fi
# end of 'zoomem.h'
fi
if test -f 'zooadd.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'zooadd.c'\"
else
echo shar: Extracting \"'zooadd.c'\" \(25720 characters\)
sed "s/^X//" >'zooadd.c' <<'END_OF_FILE'
X#ifndef LINT
X/* @(#) zooadd.c 2.34 88/08/15 10:53:11 */
Xstatic char sccsid[]="@(#) zooadd.c 2.34 88/08/15 10:53:11";
X#endif /* LINT */
X
X/*
XCopyright (C) 1986, 1987 Rahul Dhesi -- All rights reserved
X(C) Copyright 1988 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 "zooio.h"
X#include "various.h"
X#include "parse.h"
X#include "debug.h"
X
X#include "portable.h"
X
X#include "zoofns.h"
X#include "errors.i"
Xextern int break_hit;
Xextern int quiet;
X
X#ifdef LINT_ARGS
Xvoid show_comment (struct direntry *, ZOOFILE, int, char *);
Xvoid dosname (char *, char *);
Xvoid modpath (char *);
Xvoid opts_add (char *, int *, int *, int *, int *, int *, int *,
X               int *, int *, int *, int *, int *, int *);
Xint ver_too_high (struct zoo_header *);
Xget_comment (struct direntry *, ZOOFILE, char *);
Xvoid copyfields (struct direntry *, struct tiny_header *);
Xvoid storefname (struct direntry *, char *, int);
Xchar *choosefname (struct direntry *);
X#else
Xvoid show_comment ();
Xvoid dosname ();
Xvoid modpath ();
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 */
XZOOFILE this_file;                        /* 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 */
XZOOFILE 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 */
Xint status;                               /* error status */
Xint success;                              /* successful addition of file? */
Xint addcount = 0;                         /* number added */
Xint update=0;                             /* 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#ifndef PORTABLE
Xstruct tiny_header tiny_header;           /* for Z format archives */
X#endif
X
Xunsigned this_version_no;							/* version no. of old file */
Xunsigned  high_vflag;								/* version flag of old file */
Xunsigned high_version_no;							/* highest version no of this file */
Xlong high_pos;										/* offset of file w/highest ver no */
Xunsigned int fgens;								/* gens. to preserve -- file */
Xunsigned int zgens;								/* gens. to preserve -- archive */
Xlong oldcmtpos;									/* to save old comment */
Xunsigned int oldcmtsiz;							/* to save old comment */
Xint genson = 0;									/* whether to turn generations on */
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, &genson);
X
X/* POSSIBLE RACE CONDITION BETWEEN TESTING EXISTENCE AND CREATING FILE */
Xif (exists (zoo_path)) {
X	zoo_file = zooopen (zoo_path, Z_RDWR);
X   zoo_status = OLD_ZOO;
X} else {
X   if (!zootime)
X		zoo_file = zoocreate (zoo_path);
X   else
X      zoo_file = NOFILE;     /* don't create if just setting time */
X   zoo_status = NEW_ZOO;
X}
X
Xif (zoo_file == NOFILE)
X   prterror ('f', could_not_open, zoo_path);
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/* 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,0,0); /* initialize table of files already in archive */
Xif (zoo_status == NEW_ZOO) {                 /* newly-created archive */
X	if (genson)											/* if no generations needed */
X		zoo_header.vdata = (VFL_ON|GEN_DEFAULT); /* generations on */
X   fwr_zooh (&zoo_header, zoo_file);
X	zgens = GEN_DEFAULT;
X   zooseek (zoo_file, zoo_header.zoo_start, 0); /* seek to where data begins */
X} else {
X   /* read header and rewrite with updated version numbers, preserving
X	   header type */
X   rwheader (&zoo_header, zoo_file, 1);
X	zgens = zoo_header.vdata & VFL_GEN;			/* get archive generations */
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   zooclose (zoo_file);
X   setutime (zoo_path, latest_date, latest_time);
X#else
X   settime (zoo_file, latest_date, latest_time);
X   zooclose (zoo_file);
X#endif
X   prterror ('m', "Archive time adjusted.\n");
X   zooexit (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		if (this_path != NULL) {
X			if (samefile (nameptr(zoo_fname),nameptr(this_path)) ||
X						samefile (nameptr(zoo_bak),nameptr(this_path)))
X				goto again; 				/* don't add archive to itself */
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
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_file = zooopen(this_path, Z_READ);
X   if (this_file == NOFILE) {
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      zooread (this_file, (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         zooclose (this_file);
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_file, &direntry.date, &direntry.time);
X#endif
X
X		/* save file attributes */
X#ifdef FATTR
X		/* we expect getfattr() to set all attr. bits;  currently
X			only the portable format is recognized */
X		{
X# ifdef FATTR_FNAME
X			unsigned long getfattr PARMS ((char *);
X			direntry.fattr = getfattr (this_path);
X# else
X			unsigned long getfattr PARMS ((ZOOFILE));
X			direntry.fattr = getfattr (this_file);
X# endif /* FATTR_FNAME */
X		}
X#else
X			direntry.fattr = NO_FATTR;	/* none */
X#endif /* FATTR */
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.  Also get its
X		date, time, version flag, and version number. */
X   prev_pos = inlist (fullpath (&direntry), &this_date, &this_time, 
X				&this_version_no, &high_vflag, &high_version_no, 
X				&high_pos, !need_dir);
X
X/* define DBG_INLIST for debugging by printing values returned by inlist() */
X#ifdef DBG_INLIST
X	printf ("FROM inlist(): prev_pos=%ld, high_pos=%ld\n", prev_pos, high_pos);
X	printf ("this_version_no=%u, high_vflag=%4x, high_version_no=%u\n",
X				this_version_no,    high_vflag,     high_version_no);
X#endif
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      zooclose (this_file);
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_file)) {
X      zooclose (this_file);
X      continue;
X   }
X#else
X# ifdef CHEKUDIR
X   /* Don't add if this is a directory */
X   if (isuadir (this_path)) {
X      zooclose (this_file);
X      continue;
X   }
X# endif /* CHEKUDIR */
X#endif /* CHEKDIR */
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	if (!genson && zoo_status == NEW_ZOO || 
X				(zoo_header.vdata & VFL_ON) == 0) {
X		direntry.vflag = 0;
X		direntry.version_no = 0;
X	}
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 = zootell (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         struct direntry tmpentry;
X         long tmppos;
X         int oldlen, newlen;
X         tmppos = zootell (zoo_file);
X         frd_dir (&tmpentry, zoo_file);
X#define  DIRLEN(x)   ((x.type<2) ? SIZ_DIR : (SIZ_DIRL+x.var_dir_len))
X         oldlen = DIRLEN(tmpentry);             /* get length of direntry */
X         newlen = DIRLEN(direntry);             /* ditto */
X
X         if (newlen > oldlen) {                 /* trouble */
X            zooseek (zoo_file, last_old, 0);    /* back to previous entry */
X            frd_dir (&tmpentry, zoo_file);
X            zooseek (zoo_file, 0L, 2);          /* get EOF position */
X            tmpentry.next = zootell (zoo_file);    /* point to EOF */
X            zooseek (zoo_file, last_old, 0);    /* back to previous entry */
X            writedir (&tmpentry, zoo_file);     /* update it */
X            zooseek (zoo_file, 0L, 2);          /* to EOF ... */
X            this_dir_offset = zootell (zoo_file);
X            writedir (&direntry, zoo_file);     /* ...write null dir entry */
X         } else
X            zooseek (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      zooseek (zoo_file, 0L, 2);                   /* EOF */
X      direntry.offset = zootell (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   zooseek (zoo_file, direntry.offset - SIZ_FLDR, 0);
X   zoowrite (zoo_file, file_leader, SIZ_FLDR);
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#endif /* PORTABLE */
X
X   crccode = 0;
X#ifndef PORTABLE
X   if (z_fmt) 
X	{
X      direntry.packing_method = tiny_header.packing_method;
X      zooseek (this_file, (long) (sizeof(tiny_header)+tiny_header.cmt_size), 0);
X      status = getfile (this_file, zoo_file, tiny_header.size_now, 1);
X   } else 
X#endif
X	if (suppress) {                    /* suppress compression */
X      direntry.packing_method = 0;           /* no compression */
X      status = getfile (this_file, zoo_file, -1L, 1);
X   } else {
X#ifdef UNBUF_IO	/* unbuffered I/O */
X		long lseek PARMS ((int, long, int));
X		long tell PARMS ((int));
X		int this_fd, zoo_fd;
X#endif
X      direntry.packing_method = 1;           		/* compressed */
X#ifdef UNBUF_IO
X		this_fd = fileno (this_file);						/* get ..					*/
X		zoo_fd = fileno (zoo_file);						/* ... file descriptors	*/
X		zooseek (zoo_file, zootell (zoo_file), 0);	/* synch */
X		zooseek (this_file, zootell (this_file), 0);	/* synch */
X      status = lzc(this_fd, zoo_fd);					/* add with compression */
X		zooseek (zoo_file, tell (zoo_fd), 0);			/* resynch	*/
X		zooseek (this_file, tell (this_fd), 0);		/* resynch	*/
X#else
X      status = lzc(this_file, zoo_file);			/* add with compression */
X#endif /* UNBUF_IO */
X
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  = zootell (zoo_file);
X      direntry.size_now = direntry.next - direntry.offset;
X
X      /* find and store original size of file just compressed */
X/*DEBUG VMS*/ zooseek (this_file, 0L, 2);	/* seek to EOF */
X
X      direntry.org_size = zootell (this_file);  /* 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         zooseek (zoo_file, save_position, 0);  /* ..restore file pointer */
X         zootrunc (zoo_file);                   /* ..truncate file */
X         direntry.packing_method = 0;           /* ..and just copy */
X         zooseek (this_file, 0L, 0);            /* (but rewind first!) */
X         crccode = 0;                           /* re-start crc from 0 */
X         status = getfile (this_file, zoo_file, -1L, 1);
X         if (status != 0) {  /* if III */
X            success = 0;
X            printf (disk_full);
X            exit_status++;
X         } else {
X            success = 1;
X            direntry.next  = zootell (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#if 0
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#endif
X
X      if (!z_fmt)
X         prterror ('M', " (%2d%%) ", cfactor (direntry.org_size, direntry.size_now));
X
X		oldcmtsiz = 0;								/* assume no old comment */
X		oldcmtpos = 0L;
X
X      if (prev_pos > 0) {										/* in archive */
X			int delold = 0;										/* delete old? */
X			/* if versions active both archive-wide and for file */
X			if ((zoo_header.vdata & VFL_ON) && (high_vflag & VFL_ON)) {
X				/* next test is optimization, to avoid redundant I/O */
X				if (high_pos != prev_pos || this_version_no == 1) {
X					/* prev highest is no longer highest so adjust vflag */
X					long save_pos = zootell (zoo_file);			/*DEBUG*/
X					zooseek (zoo_file, high_pos, 0);
X					readdir (&dir2entry, zoo_file, 1);
X					oldcmtpos = dir2entry.comment;
X					oldcmtsiz = dir2entry.cmt_size;
X					dir2entry.vflag &= (~VFL_LAST);				/* no longer highest */
X					zooseek (zoo_file, high_pos, 0);
X					writedir (&dir2entry, zoo_file);
X					zooseek (zoo_file, save_pos, 0);				/*DEBUG*/
X				}
X
X				direntry.version_no = high_version_no + 1; /* ..one higher */
X				direntry.vflag = high_vflag;
X				/* now see if we need to delete older version */
X				fgens = high_vflag & VFL_GEN;
X				if (fgens == 0)
X					fgens = zgens;
X				if (zgens != 0 && zgens < fgens)
X					fgens = zgens;
X				if (fgens != 0 && direntry.version_no - this_version_no >= fgens) {
X					delold = 1;
X					prterror ('M', "replaced+\n");
X				} else
X					prterror ('M', "added+\n");
X			} else {
X				prterror ('M', "replaced\n");
X				delold = 1;
X			}
X
X			if (delold) {											/* deleting old file */
X				long save_pos = zootell (zoo_file);			/*DEBUG*/
X				++delcount;											/* remember to pack */
X				zooseek (zoo_file, prev_pos, 0);
X				readdir (&dir2entry, zoo_file, 1);
X				if (dir2entry.cmt_size != 0) {		/* propagate latest comment */
X					oldcmtpos = dir2entry.comment;
X					oldcmtsiz = dir2entry.cmt_size;
X				}
X				dir2entry.deleted = 1;							/* mark as deleted */
X				/* following line is optimization if only 1 generation */
X				dir2entry.vflag &= (~VFL_LAST);				/* no longer highest */
X				zooseek (zoo_file, prev_pos, 0);
X				writedir (&dir2entry, zoo_file);
X				zooseek (zoo_file, save_pos, 0);				/*DEBUG*/
X			}
X      } else 														/* not in archive */
X			prterror ('M', "added\n");
X
X      /* Preserve any old comment if we replaced or superseded the file */
X      direntry.comment = oldcmtpos;
X      direntry.cmt_size = oldcmtsiz;
X
X#ifndef PORTABLE
X      /* Copy comment if any from Z format file */
X      if (z_fmt && tiny_header.cmt_size != 0) {
X         zooseek (this_file, (long) sizeof(tiny_header), 0); /* to comment */
X         direntry.comment = zootell (zoo_file);
X         direntry.cmt_size = tiny_header.cmt_size;
X         /* 4th param is 0 for no CRC */
X         getfile (this_file, zoo_file, (long) tiny_header.cmt_size, 0);
X         direntry.next = zootell (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 = zootell (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 = zootell (zoo_file);    /* save position */
X         zooseek (zoo_file, this_dir_offset, 0);
X         writedir (&direntry, zoo_file);
X         zooseek (zoo_file, savepos, 0);    /* restore position */
X      }
X
X   } else {                               /* file was not properly added */
X      zooseek (zoo_file, save_position, 0);  /* ..restore file pointer */
X      zootrunc (zoo_file);                   /* ..truncate file */
X   } /* end if */
X   zooclose (this_file);
Xif (!success)
X   break;
Xfirstfile = 0;
X} /* end for */
X
Xsave_position = zootell (zoo_file);
X
X/* Write a null direntry entry */
Xzooseek (zoo_file, save_position, 0);
Xwritenull (zoo_file, MAXDIRSIZE);
Xzootrunc (zoo_file);  /* truncate */
X
X#ifdef NIXTIME
Xzooclose (zoo_file);
Xsetutime (zoo_path, latest_date, latest_time);
X#else
Xsettime (zoo_file, latest_date, latest_time);
Xzooclose (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   zooexit (1);
X} /* end zoo_add */
END_OF_FILE
if test 25720 -ne `wc -c <'zooadd.c'`; then
    echo shar: \"'zooadd.c'\" unpacked with wrong size!
fi
# end of 'zooadd.c'
fi
if test -f 'zoodel.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'zoodel.c'\"
else
echo shar: Extracting \"'zoodel.c'\" \(9557 characters\)
sed "s/^X//" >'zoodel.c' <<'END_OF_FILE'
X#ifndef LINT
X/* @(#) zoodel.c 2.19 88/02/06 21:23:36 */
Xstatic char sccsid[]="@(#) zoodel.c 2.19 88/02/06 21:23:36";
X#endif /* LINT */
X
X/*
XCopyright (C) 1986, 1987 Rahul Dhesi -- All rights reserved
X(C) Copyright 1988 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#ifndef	OK_STDIO
X#include <stdio.h>
X#define	OK_STDIO
X#endif
X#include "various.h" /* may not be needed */
X#include "zooio.h"
X#include "zoo.h"
X#include "zoofns.h"
X#include "errors.i"
X
X#ifndef NOSIGNAL
X#include <signal.h>
X#endif
X
X#ifdef LINT_ARGS
Xint needed (char *, struct direntry *, struct zoo_header *);
Xint ver_too_high (struct zoo_header *);
X#else
Xint needed ();
Xint ver_too_high ();
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 ZOOFILE 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 */
X	int action;						/* delete/undelete or adjust generation */
X	int subopt;						/* sub option to action */
X	long gencount;					/* generation count */
X	int doarchive = 0;			/* whether to adjust archive gen count */
X	unsigned valtoshow;			/* value to show in informative message */
X	int dodel = 0;					/* selection of deleted files */
X	int selected;					/* if current direntry selected */
X
X/* values for action */
X#define NO_ACTION	0	/* nothing */
X#define DEL_UNDEL	1	/* delete or undelete file */
X#define ADJ_LIM	2	/* adjust generation limit */
X#define ADJ_GCNT	3	/* adjust generation count */
X#define GEN_ON		4	/* turn on generations */
X#define GEN_OFF		5	/* turn off generations */
X
X/* values for subopt */
X#define	SET		0
X#define	INC		1
X
Xaction = NO_ACTION;
Xif (*option == 'g') {
X	while (*(++option)) {
X		switch (*option) {
X			case 'A': doarchive = 1; break;
X			case 'q': quiet++; break;
X			case 'l': action = ADJ_LIM; break;
X			case 'c': action = ADJ_GCNT; break;
X			case '=':
X				subopt = SET; gencount = calc_ofs (++option);
X				if (action == ADJ_GCNT && gencount == 0)
X					prterror ('f', "Generation count must be nonzero.\n");
X				goto opts_done;
X			case '+':
X				if (action == NO_ACTION) {
X					if (option[1] =='\0') {
X						action = GEN_ON;
X						goto opts_done;
X					} else
X						prterror ('f', garbled);
X				} else {
X					subopt = INC; gencount = calc_ofs (++option);
X					goto opts_done;
X				}
X			case '-':
X				if (action == NO_ACTION) {
X					if (option[1] =='\0') {
X						action = GEN_OFF;
X						goto opts_done;
X					} else
X						prterror ('f', garbled);
X				} else {
X					subopt = INC; gencount = - calc_ofs (++option);
X					goto opts_done;
X				}
X			case 'd':
X				dodel++; break;
X			default:
X				prterror ('f', garbled);
X		} /* end switch */
X	} /* end while */
X	/* if normal exit from while loop, it means bad command string */
X	prterror ('f', garbled);
X	opts_done: 							/* jump here from exit in while loop above */
X		if (action == NO_ACTION)
X			prterror ('f', garbled);
X} else {
X	action = DEL_UNDEL;
X	while (*(++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
X   /* Open archive for read/write/binary access.  It must already exist */
X   if ((zoo_file = zooopen (zoo_path, Z_RDWR)) == NOFILE) {
X      prterror ('f', could_not_open, zoo_path);
X   }
X   
X   /* read archive header */
X   frd_zooh (&zoo_header, zoo_file);
X   if ((zoo_header.zoo_start + zoo_header.zoo_minus) != 0L)
X      prterror ('f', failed_consistency);
X   if (ver_too_high (&zoo_header))
X      prterror ('f', wrong_version, zoo_header.major_ver, zoo_header.minor_ver);
X
X	if (doarchive) {									/* manipulate archive gen val */
X		unsigned zoo_date, zoo_time;
X#ifdef GETUTIME
X		getutime (zoo_path, &zoo_date, &zoo_time);	/* save archive timestamp */
X#else
X		gettime (zoo_file, &zoo_date, &zoo_time);
X#endif
X		if (zoo_header.type == 0)
X			prterror ('f', packfirst);
X		if (action == ADJ_LIM)	{
X			unsigned newgencount;	
X			if (subopt == SET)
X				newgencount = (unsigned) gencount;
X			else																		/* INC */
X				newgencount = (zoo_header.vdata & VFL_GEN) + (unsigned) gencount;
X			newgencount &= VFL_GEN;			/* reduce to allowed bits */
X			zoo_header.vdata &= (~VFL_GEN);
X			zoo_header.vdata |= newgencount;
X			prterror ('M', "Archive generation limit is now %u\n", newgencount);
X		} else if (action == GEN_ON) {
X			zoo_header.vdata |= VFL_ON;
X			prterror ('M', "Archive generations on\n");
X		} else if (action == GEN_OFF) {
X			zoo_header.vdata &= (~VFL_ON);
X			prterror ('M', "Archive generations off\n");
X		} else 
X			prterror ('f', garbled);
X		zooseek (zoo_file, 0L, 0);		/* back to begining of file */
X		fwr_zooh (&zoo_header, zoo_file);
X#ifdef NIXTIME
X		zooclose (zoo_file);
X		setutime (zoo_path, zoo_date, zoo_time);	/* restore archive timestamp */
X#else
X		settime (zoo_file, zoo_date, zoo_time);
X		zooclose (zoo_file);
X#endif
X		return;
X	}
X
X   zooseek (zoo_file, zoo_header.zoo_start, 0); /* seek to where data begins */
X
X   done = 0;            /* loop not done yet */
X   while (1) {
X      long this_dir_offset;
X      this_dir_offset = zootell (zoo_file);   /* save pos'n of this dir entry */
X      frd_dir (&direntry, 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		/* select directory entry if it matches criteria */
X		selected = (
X						  (action == DEL_UNDEL && direntry.deleted != choice)
X                  ||
X						  (action != DEL_UNDEL &&
X                     	(dodel && direntry.deleted ||
X                    			(dodel < 2 && !direntry.deleted))
X						  )
X					  );
X
X		/* WARNING: convention of choice=1 for deleted entry must be same as
X		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		if (zoo_header.vdata & VFL_ON)
X			add_version (matchname, &direntry);			/* add version suffix */
X
X		if (!done && selected && needed(matchname, &direntry, &zoo_header)) {
X			prterror ('m', "%-14s -- ", matchname);
X			delcount++;
X			if (action == DEL_UNDEL) {
X				direntry.deleted = choice;
X				if (choice)
X					file_deleted++;      /* remember if any files actually deleted */
X			} else {							/* ADJ_LIM or ADJ_GENCNT */
X				if (direntry.vflag & VFL_ON) {		/* skip if no versions */
X					if (action == ADJ_LIM) {
X						unsigned newgencount;
X						if (subopt == SET)
X							newgencount = (unsigned) gencount;
X						else 													/* INC */
X							newgencount =
X								(int) (direntry.vflag & VFL_GEN) + (int) gencount;
X						newgencount &= VFL_GEN;
X						direntry.vflag &= (~VFL_GEN);
X						direntry.vflag |= newgencount;
X						valtoshow = newgencount;
X					} else {													/* ADJ_GCNT */
X						if (subopt == SET)
X							direntry.version_no = (unsigned) gencount;
X						else 													/* INC */
X							direntry.version_no += (int) gencount;
X						direntry.version_no &= VER_MASK; /* avoid extra bits */
X						valtoshow = direntry.version_no;
X					}
X				}
X			}
X
X			zooseek (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				prterror ('f', "Could not write to archive\n");
X#ifndef NOSIGNAL
X			signal (SIGINT, oldsignal);
X#endif
X			if (action == DEL_UNDEL)
X				prterror ('M', choice ? "deleted\n" : "undeleted\n");
X			else {
X				if (direntry.vflag & VFL_ON)
X					prterror ('M', "adjusted to %u\n", valtoshow);
X				else
X					prterror ('M', "no generations\n");
X			}
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      zooseek (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      zooclose (zoo_file);
X      setutime (zoo_path, latest_date, latest_time);
X#else
X#if 0
X      fflush (zoo_file);         /* superstition:  might help time stamp */
X#endif
X      settime (zoo_file, latest_date, latest_time);
X#endif
X   }
X
X#ifndef NIXTIME
Xzooclose (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}
END_OF_FILE
if test 9557 -ne `wc -c <'zoodel.c'`; then
    echo shar: \"'zoodel.c'\" unpacked with wrong size!
fi
# end of 'zoodel.c'
fi
echo shar: End of archive 9 \(of 10\).
cp /dev/null ark9isdone
MISSING=""
for I in 1 2 3 4 5 6 7 8 9 10 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 10 archives.
    rm -f ark[1-9]isdone ark[1-9][0-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0
-- 
Please send comp.sources.unix-related mail to rsalz@uunet.uu.net.