v17i071: Zoo archive program, Part08/10
Rich Salz
rsalz at uunet.uu.net
Fri Feb 3 09:16:17 AEST 1989
Submitted-by: Rahul Dhesi <bsu-cs!dhesi>
Posting-number: Volume 17, Issue 71
Archive-name: zoo2/part08
#! /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 8 (of 10)."
# Wrapped by rsalz at papaya.bbn.com on Thu Feb 2 18:04:04 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'portable.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'portable.c'\"
else
echo shar: Extracting \"'portable.c'\" \(21282 characters\)
sed "s/^X//" >'portable.c' <<'END_OF_FILE'
X#ifndef LINT
X/* @(#) portable.c 2.24 88/08/24 01:22:06 */
Xstatic char sccsid[]="@(#) portable.c 2.24 88/08/24 01:22:06";
X#endif /* LINT */
X
X#include "options.h"
X/*
XCopyright (C) 1986, 1987 Rahul Dhesi -- All rights reserved
X(C) Copyright 1988 Rahul Dhesi -- All rights reserved
X*/
X/**********************
Xportable.c contains functions needed to make Zoo portable to various
Ximplementations of C.
X
XNote: Provided a 2's complement machine is used, all functions in
Xthis file are themselves machine-independent and need not be changed
Xwhen implementing Zoo on a different machine. Some code will choke
Xon 1's complement machines--I think.
X
XFor machine-dependent declarations see files "machine.h" and "options.h".
X
XFor machine-dependent functions see file "machine.c"
X*/
X
X#include "zooio.h"
X
X#include "various.h"
X#include "zoofns.h"
X
X#include "machine.h"
X#include "zoo.h"
X#include "debug.h"
X#include "assert.h"
X
X#ifdef NEEDCTYP
X#include <ctype.h> /* for tolower() */
X#endif
X
X#include "portable.h"
X
X#ifdef TRACE_IO
Xextern int verbose;
X#endif
X
X/* Functions defined for use within this file only. */
X#ifdef LINT_ARGS
Xlong to_long (BYTE[]);
Xint to_int (BYTE[]);
Xvoid b_to_zooh(struct zoo_header *, BYTE[]);
Xvoid b_to_dir(struct direntry *, BYTE[]);
Xint dir_to_b(BYTE[], struct direntry *);
Xvoid zooh_to_b(BYTE[], struct zoo_header *);
Xvoid splitlong(BYTE[], long);
Xvoid splitint(BYTE[], int);
X#else
Xlong to_long ();
Xint to_int ();
Xvoid b_to_zooh();
Xvoid b_to_dir();
Xint dir_to_b();
Xvoid zooh_to_b();
Xvoid splitlong();
Xvoid splitint();
X#endif
X
X#ifdef TRACE_IO
Xvoid show_h PARMS ((struct zoo_header *));
Xvoid show_dir PARMS ((struct direntry *));
X#endif /* TRACE_IO */
X
Xextern unsigned int crccode;
X
X/************************************************************************/
X/* I/O functions */
X/************************************************************************/
X
X/* some functions get defined only if they aren't already macros */
X
X#ifndef zooread
Xint zooread (file, buffer, count)
XZOOFILE file; char *buffer; int count;
X{ return (fread (buffer, 1, count, file)); }
X#endif /* zooread */
X
X#ifndef FIZ
X#ifndef zoowrite
Xint zoowrite (file, buffer, count)
XZOOFILE file; char *buffer; int count;
X{
X if (file == NULLFILE)
X return (count);
X else
X return (fwrite (buffer, 1, count, file));
X}
X#endif /* zoowrite */
X
XZOOFILE zoocreate (fname)
Xchar *fname;
X{ return ((ZOOFILE) fopen (fname, Z_NEW)); }
X
X/* truncates a file -- can be empty */
X/* force use of argument to keep Turbo C happy */
Xint zootrunc (file) ZOOFILE file; {return ((int) file); }
X#endif /* FIZ */
X
X#ifndef zooseek
Xlong zooseek (file, offset, whence)
XZOOFILE file; long offset; int whence;
X{ return (fseek (file, offset, whence)); }
X#endif /* zooseek */
X
XZOOFILE zooopen (fname, option)
Xchar *fname; char *option;
X{ return ((ZOOFILE) fopen (fname, option)); }
X
X#ifndef zootell
Xlong zootell (file)
XZOOFILE file;
X{ return ftell (file); }
X#endif /* zootell */
X
Xint zooclose (file)
XZOOFILE file;
X{ return fclose (file); }
X
X/************************************************************************/
X/*** Following are functions that make up for various implementations ***/
X/*** of C not having certain library routines. ***/
X/************************************************************************/
X
X#ifndef FIZ
X#ifndef STRLWR
X/**********************
Xstrlwr() converts a string to lowercase and returns a pointer to the string
X*/
Xchar *strlwr (str)
Xchar *str;
X{
X register char *s;
X s = str;
X while (*s != '\0') {
X *s = toascii(*s);
X if (isupper(*s))
X *s = tolower(*s);
X s++;
X }
X return (str);
X}
X#endif /* STRLWR */
X
X/**********************
Xstrcmpi() compares strings just like strcmp() but it does it without regard to
Xcase.
X*/
Xint strcmpi (s1, s2)
Xregister char *s1, *s2;
X{
X for ( ; tolower(*s1) == tolower(*s2); s1++, s2++)
X if (*s1 == '\0')
X return(0);
X return(tolower(*s1) - tolower(*s2));
X}
X
X#ifndef MEMSET
X/**********************
Xmemset() exists in Microsoft C and UNIX System V but not in Xenix. It sets
Xthe first "cnt" bytes of "dest" to the character "c" and returns a pointer to
X"dest".
X*/
Xchar * memset (dest, c, cnt)
Xchar *dest;
Xint c;
Xunsigned cnt;
X{
X register unsigned i;
X for (i = 0; i < cnt; i++) {
X *(dest + i) = c;
X }
X return (dest);
X}
X#endif /* MEMSET */
X
X#ifndef FPUTCHAR
X/**********************
Xfputchar() writes a character to stdout. It is identical to putchar
Xbut is a function, not a macro.
X*/
Xint fputchar (c)
Xint c;
X{
X return (fputc(c, stdout));
X}
X#endif /* FPUTCHAR */
X#endif /* FIZ */
X
X/***********************************************************************/
X/*** Following are declarations and functions that are written in a ***/
X/*** machine-independent way but they implement machine-dependent ***/
X/*** activities ***/
X/***********************************************************************/
X
X#ifndef TURBOC
X/**********************
Xto_long() converts four consecutive bytes, in order of increasing
Xsignificance, to a long integer. It is used to make Zoo independent of the
Xbyte order of the system.
X*/
Xlong to_long(data)
XBYTE data[];
X{
X return (long) ((unsigned long) data[0] | ((unsigned long) data[1] << 8) |
X ((unsigned long) data[2] << 16) | ((unsigned long) data[3] << 24));
X}
X
X#ifndef FIZ
X/********************
Xsplitlong() converts a long integer to four consecutive BYTEs in order
Xof increasing significance.
X*/
Xvoid splitlong(bytes, bigword)
XBYTE bytes[];
Xlong bigword;
X{
X int i;
X for (i = 0; i < 4; i++) {
X bytes[i] = bigword & 0xff;
X bigword = (unsigned long) bigword >> 8;
X }
X}
X#endif /* FIZ */
X
X/*******************
Xsplitint() converts an integer to two consecutive BYTEs in order
Xof increasing significance.
X*/
Xvoid splitint(bytes, word)
XBYTE bytes[];
Xint word;
X{
X bytes[0] = word & 0xff;
X word = (unsigned int) word >> 8;
X bytes[1] = word & 0xff;
X}
X
X/**********************
Xto_int() converts two consecutive bytes, in order of increasing
Xsignificance, to an integer, in a machine-independent manner
X*/
Xint to_int(data)
XBYTE data[];
X{
X return (int) ((unsigned int) data[0] | ((unsigned int) data[1] << 8));
X}
X
X#else /* else of ifndef TURBOC */
X
Xlong to_long(data)
XBYTE data[];
X{
X return ( * (long *) data );
X}
X
X#ifndef FIZ
X/********************
Xsplitlong() converts a long integer to four consecutive BYTEs in order
Xof increasing significance.
X*/
Xvoid splitlong(bytes, bigword)
XBYTE bytes[];
Xlong bigword;
X{
X * (long *) bytes = bigword;
X}
X#endif /* FIZ */
X
X/*******************
Xsplitint() converts an integer to two consecutive BYTEs in order
Xof increasing significance.
X*/
Xvoid splitint(bytes, word)
XBYTE bytes[];
Xint word;
X{
X * (int *) bytes = word;
X}
X
X/**********************
Xto_int() converts two consecutive bytes, in order of increasing
Xsignificance, to an integer.
X*/
Xint to_int(data)
XBYTE data[];
X{
X return (*(int *) data);
X}
X
X#endif /* ifndef TURBOC .. else ... */
X
X#ifndef FIZ
X/**********************
XFunction frd_zooh() reads the header of a Zoo archive in a machine-
Xindependent manner, from a ZOOFILE.
X*/
Xint frd_zooh(zoo_header, zoo_file)
Xstruct zoo_header *zoo_header;
XZOOFILE zoo_file;
X{
X int status;
X BYTE bytes[SIZ_ZOOH]; /* canonical header representation */
X#ifdef TRACE_IO
X if (verbose) {
X printf("At file position [%8lx] ", ftell(zoo_file));
X }
X#endif
X status = zooread (zoo_file, (char *) bytes, SIZ_ZOOH);
X b_to_zooh (zoo_header, bytes); /* convert array to structure */
X#ifdef TRACE_IO
X if (verbose) {
X printf("frd_zooh: reading\n");
X show_h(zoo_header);
X }
X#endif
X if (status < MINZOOHSIZ)
X return (-1);
X else
X return (0);
X}
X#endif /* FIZ */
X
X/**********************
XFunction frd_dir() reads a directory entry in a machine-independent manner,
Xfrom a ZOOFILE.
X*/
Xint frd_dir(direntry, zoo_file)
Xstruct direntry *direntry;
XZOOFILE zoo_file;
X{
X int status;
X BYTE bytes[MAXDIRSIZE]; /* big enough to hold variable part too */
X
X /* To simplify things, we read the maximum possible size of the
X directory entry including the variable size and discard what is not
X needed */
X#ifdef TRACE_IO
X if (verbose) {
X printf("At file position [%8lx] ", ftell(zoo_file));
X }
X#endif
X status = zooread (zoo_file, (char *) bytes, MAXDIRSIZE);
X if (status < SIZ_DIR)
X return (-1);
X b_to_dir (direntry, bytes);
X#ifdef TRACE_IO
X if (verbose) {
X printf("frd_dir: reading\n");
X show_dir(direntry);
X }
X#endif
X return (0);
X}
X
X#ifndef FIZ
X/***********************
XFunction fwr_dir() writes a directory entry in a machine-independent manner
Xto a ZOOFILE. Return value is -1 on error, else 0.
X*/
Xint fwr_dir(direntry, zoo_file)
Xstruct direntry *direntry;
XZOOFILE zoo_file;
X{
X int size;
X BYTE bytes[MAXDIRSIZE];
X assert (direntry->type <= 2);
X size = dir_to_b (bytes, direntry);
X#ifdef TRACE_IO
X if (verbose) {
X printf("At file position [%8lx] ", ftell(zoo_file));
X printf("fwr_dir: writing\n");
X show_dir(direntry);
X }
X#endif
X
X if (zoowrite (zoo_file, (char *) bytes, size) != size)
X return (-1);
X else
X return (0);
X}
X
X/***********************
XFunction fwr_zooh() writes an archive header in a machine-independent manner
Xto a ZOOFILE. Return value is -1 if error else 0.
X*/
Xint fwr_zooh(zoo_header, zoo_file)
Xstruct zoo_header *zoo_header;
XZOOFILE zoo_file;
X{
X BYTE bytes[SIZ_ZOOH]; /* was SIZ_DIR -- probably a typo */
X int hsize; /* how much to write -- depends on header type */
X hsize = MINZOOHSIZ; /* in case it's an old type 0 header */
X if (zoo_header->type > 0) /* but if it's a newer header... */
X hsize = SIZ_ZOOH; /* ...size of new type 1 header */
X zooh_to_b (bytes, zoo_header);
X if (zoowrite (zoo_file, (char *) bytes, hsize) != hsize)
X return (-1);
X else
X return (0);
X}
X
X/***********************
Xb_to_zooh() converts an array of BYTE to a zoo_header structure.
X*/
Xvoid b_to_zooh (zoo_header, bytes)
Xstruct zoo_header *zoo_header;
XBYTE bytes[];
X{
X int i;
X for (i = 0; i < SIZ_TEXT; i++) /* copy text */
X zoo_header->text[i] = bytes[TEXT_I + i];
X zoo_header->zoo_tag = to_long(&bytes[ZTAG_I]); /* copy zoo_tag */
X zoo_header->zoo_start = to_long(&bytes[ZST_I]); /* copy zoo_start */
X zoo_header->zoo_minus = to_long(&bytes[ZSTM_I]);
X zoo_header->major_ver = bytes[MAJV_I]; /* copy versions */
X zoo_header->minor_ver = bytes[MINV_I];
X /* default is no archive comment and a header type of 0 */
X zoo_header->type = 0;
X zoo_header->acmt_pos = 0L;
X zoo_header->acmt_len = 0;
X zoo_header->vdata = 0;
X if (zoo_header->zoo_start != FIXED_OFFSET) { /* if newer header */
X zoo_header->type = bytes[HTYPE_I];
X zoo_header->acmt_pos = to_long(&bytes[ACMTPOS_I]);
X zoo_header->acmt_len = to_int(&bytes[ACMTLEN_I]);
X zoo_header->vdata = bytes[HVDATA_I];
X }
X}
X
X/***********************
Xzooh_to_b() converts a zoo_header structure to an array of BYTE.
X*/
Xvoid zooh_to_b (bytes, zoo_header)
Xstruct zoo_header *zoo_header;
XBYTE bytes[];
X{
X int i;
X for (i = 0; i < SIZ_TEXT; i++) /* copy text */
X bytes[TEXT_I + i] = zoo_header->text[i];
X splitlong (&bytes[ZTAG_I], zoo_header->zoo_tag);
X splitlong (&bytes[ZST_I], zoo_header->zoo_start);
X splitlong (&bytes[ZSTM_I], zoo_header->zoo_minus);
X bytes[MAJV_I] = zoo_header->major_ver; /* copy versions */
X bytes[MINV_I] = zoo_header->minor_ver;
X bytes[HTYPE_I] = zoo_header->type; /* header type */
X if (zoo_header->type > 0) {
X splitlong (&bytes[ACMTPOS_I], zoo_header->acmt_pos); /* comment posn */
X splitint (&bytes[ACMTLEN_I], zoo_header->acmt_len); /* comment len */
X bytes[HVDATA_I] = zoo_header->vdata; /* version data */
X }
X} /* zooh_to_b() */
X
X/************************
Xdir_to_b() converts a directory entry structure to an array of BYTE.
X*/
Xint dir_to_b (bytes, direntry)
Xstruct direntry *direntry;
XBYTE bytes[];
X{
X int i;
X int cursize;
X int fixsize;
X int totalsize;
X splitlong(&bytes[DTAG_I], direntry->zoo_tag);
X bytes[DTYP_I] = direntry->type ;
X bytes[PKM_I] = direntry->packing_method ;
X splitlong(&bytes[NXT_I], direntry->next);
X splitlong(&bytes[OFS_I], direntry->offset);
X splitint(&bytes[DAT_I], direntry->date);
X splitint(&bytes[TIM_I], direntry->time);
X splitint(&bytes[CRC_I], direntry->file_crc);
X splitlong(&bytes[ORGS_I], direntry->org_size);
X splitlong(&bytes[SIZNOW_I], direntry->size_now);
X bytes[DMAJ_I] = direntry->major_ver;
X bytes[DMIN_I] = direntry->minor_ver;
X bytes[DEL_I] = direntry->deleted;
X bytes[STRUC_I] = direntry->struc;
X splitlong(&bytes[CMT_I], direntry->comment);
X splitint(&bytes[CMTSIZ_I], direntry->cmt_size);
X for (i = 0; i < FNM_SIZ; i++)
X bytes[FNAME_I + i] = direntry->fname[i];
X bytes[TZ_I] = NO_TZ; /* assume unknown */
X bytes[NAMLEN_I] = 0;
X bytes[DIRLEN_I] = 0;
X
X cursize = SIZ_DIR; /* to count size of directory */
X fixsize = SIZ_DIR; /* size of fixed part */
X assert (direntry->type <= 2);
X if (direntry->type == 2) { /* handle stuff relevant to type 2 */
X cursize = SIZ_DIRL;
X fixsize = SIZ_DIRL;
X bytes[TZ_I] = direntry->tz;
X assert(direntry->namlen < 256 && direntry->namlen >= 0);
X cursize += 2; /* space for namlen and dirlen */
X if (direntry->namlen != 0) {
X bytes[NAMLEN_I] = direntry->namlen;
X for (i = 0; i < direntry->namlen; i++)
X bytes[LFNAME_I+i] = direntry->lfname[i];
X cursize += direntry->namlen;
X }
X assert(direntry->dirlen < 256 && direntry->dirlen >= 0);
X if (direntry->dirlen != 0) {
X bytes[DIRLEN_I] = direntry->dirlen;
X for (i = 0; i < direntry->dirlen; i++)
X bytes[cursize+i] = direntry->dirname[i];
X cursize += direntry->dirlen;
X }
X /* Can't store system id if no namlen & dirlen...BUG!...now fixed.
X Fortunately, system_id was always 0 so far so it probably
X got interpreted as namlen=0 and dirlen=0 (2 bytes) */
X splitint(&bytes[cursize], direntry->system_id);
X cursize += 2;
X bytes[cursize] = direntry->fattr & 0xff; /* byte 0 */
X splitint(&bytes[cursize+1], (int) (direntry->fattr >> 8)); /* 1 & 2 */
X cursize += 3;
X bytes[cursize] = (direntry->vflag & 0xff); /* version flag */
X splitint(&bytes[cursize+1], direntry->version_no); /* version number */
X cursize += 3;
X }
X
X splitint(&bytes[VARDIRLEN_I], direntry->var_dir_len);
X assert(cursize ==
X ((bytes[DIRLEN_I] > 0 || bytes[NAMLEN_I] > 0) ? 2 : 0) +
X fixsize + bytes[DIRLEN_I] + bytes[NAMLEN_I]
X );
X
X /* total size of dir entry is size of fixed part + size of var. part */
X totalsize = fixsize + direntry->var_dir_len;
X
X /* Do CRC assuming CRC field is zero, and stuff CRC into field. */
X splitint(&bytes[DCRC_I], 0); /* fill with zeroes */
X crccode = 0;
X /* avoid mixing pointers to signed and unsigned char */
X addbfcrc((char *) bytes, totalsize); /* update CRC */
X splitint(&bytes[DCRC_I], crccode);
X
X /* return total length of directory entry */
X return (totalsize);
X
X} /* dir_to_b() */
X#endif /* FIZ */
X
X/* b_to_dir() converts bytes to directory entry structure. The CRC of the
Xdirectory bytes, if any, is checked and a zero or nonzero value is returned
Xin direntry->dir_crc according as the check is good or bad */
X
Xvoid b_to_dir(direntry, bytes)
Xstruct direntry *direntry;
XBYTE bytes[];
X{
X int i;
X int sysid_offs; /* temp variable */
X unsigned int savecrc;
X direntry->zoo_tag = to_long(&bytes[DTAG_I]);
X direntry->type = bytes[DTYP_I];
X direntry->packing_method = bytes[PKM_I];
X direntry->next = to_long(&bytes[NXT_I]);
X direntry->offset = to_long(&bytes[OFS_I]);
X direntry->date = to_int(&bytes[DAT_I]);
X direntry->time = to_int(&bytes[TIM_I]);
X direntry->file_crc = to_int(&bytes[CRC_I]);
X direntry->org_size = to_long(&bytes[ORGS_I]);
X direntry->size_now = to_long(&bytes[SIZNOW_I]);
X direntry->major_ver = bytes[DMAJ_I];
X direntry->minor_ver = bytes[DMIN_I];
X direntry->deleted = bytes[DEL_I];
X direntry->struc = bytes[STRUC_I];
X direntry->comment = to_long(&bytes[CMT_I]);
X direntry->cmt_size = to_int(&bytes[CMTSIZ_I]);
X /* for now, versions not implemented */
X direntry->vflag = 0;
X direntry->version_no = 0;
X for (i = 0; i < FNM_SIZ; i++)
X direntry->fname[i] = bytes[FNAME_I + i];
X
X /* start by assuming variable part is zero bytes */
X direntry->var_dir_len = direntry->dir_crc = 0;
X direntry->namlen = direntry->dirlen = 0;
X direntry->lfname[0] = direntry->dirname[0] = '\0';
X direntry->tz = NO_TZ; /* assume unknown */
X direntry->system_id = SYSID_NIX; /* default system_id if not present */
X direntry->fattr = NO_FATTR; /* assume none */
X
X assert (direntry->type <= 2);
X if (direntry->type == 2) {
X direntry->var_dir_len = to_int(&bytes[VARDIRLEN_I]);
X assert(direntry->var_dir_len <= MAXDIRSIZE);
X if (direntry->var_dir_len > MAXDIRSIZE)
X direntry->var_dir_len = MAXDIRSIZE;
X direntry->tz = bytes[TZ_I];
X if (direntry->var_dir_len > 0)
X direntry->namlen = bytes[NAMLEN_I];
X if (direntry->var_dir_len > 1)
X direntry->dirlen = bytes[DIRLEN_I];
X for (i = 0; i < direntry->namlen; i++)
X direntry->lfname[i] = bytes[LFNAME_I + i];
X for (i = 0; i < direntry->dirlen; i++)
X direntry->dirname[i] = bytes[DIRNAME_I + direntry->namlen + i];
X sysid_offs = DIRNAME_I + direntry->namlen + i; /* offset of system id */
X if (direntry->var_dir_len > direntry->namlen + direntry->dirlen + 2) {
X direntry->system_id = to_int(&bytes[sysid_offs]);
X }
X if (direntry->var_dir_len > direntry->namlen + direntry->dirlen + 4) {
X direntry->fattr = ((unsigned long) bytes[sysid_offs + 2]) |
X ((unsigned long) bytes[sysid_offs + 3] << 8) |
X ((unsigned long) bytes[sysid_offs + 4] << 16);
X }
X if (direntry->var_dir_len > direntry->namlen + direntry->dirlen + 7) {
X direntry->vflag = bytes[sysid_offs + 5];
X direntry->version_no = to_int(&bytes[sysid_offs + 6]);
X }
X /* do CRC calculation */
X savecrc = (unsigned int) to_int(&bytes[DCRC_I]);
X crccode = 0;
X splitint(&bytes[DCRC_I], 0);
X addbfcrc((char *) bytes, SIZ_DIRL + direntry->var_dir_len);
X direntry->dir_crc = crccode - savecrc;
X }
X}
X
X#ifdef FILTER
X#define TWOBYTES 2 /* better than literal 2; figure out why */
X
X/* rdint() reads two bytes from standard input in archive order */
Xint rdint (val)
Xunsigned int *val;
X{
X BYTE bytes[TWOBYTES];
X if (zooread (STDIN, bytes, TWOBYTES) == TWOBYTES) {
X *val = to_int(bytes);
X return (0);
X } else
X return (1);
X}
X
X/* wrint() writes an unsigned int to standard output in archive order */
Xint wrint (val)
Xunsigned int val;
X{
X BYTE bytes[TWOBYTES];
X splitint (bytes, val);
X if (zoowrite (STDOUT, bytes, TWOBYTES) == TWOBYTES)
X return (0);
X else
X return (1);
X}
X#endif /* FILTER */
X
X#ifdef TRACE_IO
X/* dump contents of archive header */
Xvoid show_h (zoo_header)
Xstruct zoo_header *zoo_header;
X{
X int i;
X printf ("Header text:\n");
X for (i = 0; i < SIZ_TEXT; i++) { /* ASSUMES ASCII TEXT */
X int c;
X c = zoo_header->text[i];
X if (c >= ' ' && c < 0x7f)
X putchar (c);
X else {
X putchar ('^');
X putchar (i & 0x40);
X }
X }
X putchar('\n');
X printf ("zoo_tag = [%8lx] zoo_start = [%8lx] zoo_minus = [%8lx]\n",
X zoo_header->zoo_tag, zoo_header->zoo_start,
X zoo_header->zoo_minus);
X printf ("major_ver.minor_ver = [%d.%d]\n",
X zoo_header->major_ver, zoo_header->minor_ver);
X if (zoo_header->zoo_start != FIXED_OFFSET) {
X printf ("type = [%d] ", zoo_header->type);
X printf ("acmt_pos = [%8lx] acmt_len = [%4x] vdata = [%2x]",
X zoo_header->acmt_pos, zoo_header->acmt_len, zoo_header->vdata);
X printf ("\n");
X }
X printf ("---------\n");
X}
X
X/* dump contents of directory entry */
Xvoid show_dir (direntry)
Xstruct direntry *direntry;
X{
X printf ("Directory entry for file [%s][%s]:\n",
X direntry->fname, direntry->lfname);
X printf ("tag = [%8lx] type = [%d] PM = [%d] Next = [%8lx] Offset = [%8lx]\n",
X direntry->zoo_tag, (int) direntry->type,
X (int) direntry->packing_method, direntry->next,
X direntry->offset);
X printf ("Orig size = [%ld] Size now = [%ld] dmaj_v.dmin_v = [%d.%d]\n",
X direntry->org_size, direntry->size_now,
X (int) direntry->major_ver, (int) direntry->minor_ver);
X printf ("Struc = [%d] DEL = [%d] comment_offset = [%8lx] cmt_size = [%d]\n",
X (int) direntry->struc, (int) direntry->deleted, direntry->comment,
X direntry->cmt_size);
X printf ("var_dir_len = [%d] TZ = [%d] dir_crc = [%4x]\n",
X direntry->var_dir_len, (int) direntry->tz, direntry->dir_crc);
X printf ("system_id = [%d] dirlen = [%d] namlen = [%d] fattr=[%24lx]\n",
X direntry->system_id, direntry->dirlen, direntry->namlen, direntry->fattr);
X printf ("vflag = [%4x] version_no = [%4x]\n",
X direntry->vflag, direntry->version_no);
X if (direntry->dirlen > 0)
X printf ("dirname = [%s]\n", direntry->dirname);
X printf ("---------\n");
X}
X#endif /* TRACE_IO */
END_OF_FILE
if test 21282 -ne `wc -c <'portable.c'`; then
echo shar: \"'portable.c'\" unpacked with wrong size!
fi
# end of 'portable.c'
fi
if test -f 'zooext.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'zooext.c'\"
else
echo shar: Extracting \"'zooext.c'\" \(21819 characters\)
sed "s/^X//" >'zooext.c' <<'END_OF_FILE'
X#ifndef LINT
X/* @(#) zooext.c 2.21 88/08/24 02:39:04 */
Xstatic char sccsid[]="@(#) zooext.c 2.21 88/08/24 02:39:04";
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/* 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 "zooio.h"
X#include "various.h"
X
X#ifndef NOSIGNAL
X#include <signal.h>
X#endif
X
X#include "zoofns.h"
X
X#ifdef LINT_ARGS
Xint makepath (char *);
Xint needed (char *, struct direntry *, struct zoo_header *);
Xvoid putstr (char *);
X#else
Xint needed ();
Xint makepath ();
Xvoid putstr ();
X#endif
X
X#ifdef FATTR
Xint setfattr PARMS ((char *, unsigned long));
X#endif /* FATTR */
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 */
Xchar prtfname[LFNAMESIZE]; /* name of extracted file on screen */
Xstatic ZOOFILE this_file; /* 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
XZOOFILE zoo_file; /* open archive */
Xlong next_ptr; /* pointer to within archive */
Xstruct zoo_header zoo_header; /* header for archive */
Xint status; /* error status */
Xint exit_status = 0; /* exit status */
Xint error_message; /* 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 supersede = 0; /* supersede newer files? */
Xint needdel = 0; /* extract deleted files too */
Xint usepath = 0; /* use path for extraction */
Xint todot = 0; /* extract relative to . */
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 */
Xlong dat_ofs = 0; /* .. and offset of file data */
Xint pipe = 0; /* are we piping output? */
Xint null_device = 0; /* are we sending to null device? */
X#ifndef PORTABLE
Xint fast_ext = 0; /* fast extract as *.?Z? */
Xint alloc_size; /* disk allocation unit size */
X#endif
Xstruct direntry direntry; /* directory entry */
Xint first_dir = 1; /* first dir entry seen? */
X
Xstatic char extract_ver[] = "Zoo %d.%d is needed to extract %s.\n";
Xstatic char no_space[] = "Insufficient disk space to extract %s.\n";
X
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 'S': supersede++; break;
X case 'd': needdel++; break;
X case 'q': quiet++; break;
X case '/': usepath++; break;
X case '.': todot++; break;
X case '@': /* if @m,n specified, fiz_ofs = m, dat_ofs = n */
X {
X char *comma_pos;
X ++option;
X comma_pos = strchr(option, ',');
X if (comma_pos != NULL) {
X dat_ofs = calc_ofs (comma_pos + 1);
X *comma_pos = '\0';
X }
X fiz_ofs = calc_ofs(option);
X goto no_more;
X }
X default:
X prterror ('f', inv_option, *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 ('f', inv_option, 'p');
X pipe = 0;
X}
X
Xif (overwrite && pipe)
X prterror ('w', option_ignored, 'O');
X
X#ifndef PORTABLE
Xif (null_device && fast_ext) {
X prterror ('w', inv_option, 'N');
X null_device = 0;
X}
X#endif
X
Xtofile = !pipe && !null_device; /* sending to actual file */
X
Xzoo_file = zooopen(zoo_path, Z_READ);
X
Xif (zoo_file == NOFILE)
X prterror ('f', could_not_open, zoo_path);
X
Xif (fiz_ofs != 0L) { /* if offset specified, start there */
X prterror ('m', start_ofs, fiz_ofs, dat_ofs);
X zooseek (zoo_file, fiz_ofs, 0);
X} else {
X /* read header */
X frd_zooh (&zoo_header, zoo_file);
X if ((zoo_header.zoo_start + zoo_header.zoo_minus) != 0L) {
X prterror ('w', failed_consistency);
X bad_header++;
X exit_status = 1;
X }
X zooseek (zoo_file, 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
Xdisk_space = MAXLONG; /* infinite disk space */
X#endif
X
X/* if piping output we open the output device just once */
Xif (null_device) {
X this_file = NULLFILE;
X} else if (pipe)
X this_file = STDOUT; /* standard output */
X
Xwhile (1) {
X frd_dir (&direntry, zoo_file);
X if (direntry.zoo_tag != ZOO_TAG) {
X long currpos, zoolength;
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 = zootell (zoo_file)) != -1L)
X if (zooseek (zoo_file, 0L, 2) != -1)
X if ((zoolength = zootell (zoo_file)) != -1L)
X printf (cant_process, zoolength - currpos);
X zooexit (1);
X }
X if (direntry.next == 0L) { /* END OF CHAIN */
X break; /* EXIT on end of chain */
X }
X /* when first direntry read, change dat_ofs from abs. pos. to rel. offset */
X if (first_dir && dat_ofs != 0) {
X dat_ofs -= direntry.offset;
X first_dir = 0;
X }
X next_ptr = direntry.next + dat_ofs; /* ptr to next dir entry */
X
X whichname = choosefname(&direntry); /* which filename */
X whichname = strdup(whichname); /* bug fix */
X fixfname(whichname); /* fix syntax */
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 extraction to subtree rooted at curr dir, modify pathname */
X#if 0
X#ifdef DIR_LBRACK
X if (todot && direntry.dirname[0] == *DIR_LBRACK &&
X direntry.dirname[1] != *CUR_DIR) {
X char tmpstr[PATHSIZE];
X strcpy (tmpstr, DIR_LBRACK);
X strcat (tmpstr, CUR_DIR);
X strcat (tmpstr, &direntry.dirname[1]);
X strcpy (direntry.dirname, tmpstr);
X }
X#endif
X#endif
X
X /* hard-coded '/' should be eventually removed */
X if (todot && *direntry.dirname == '/') {
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, &direntry, &zoo_header)) {
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 prterror ('e', extract_ver, direntry.major_ver,
X direntry.minor_ver, whichname);
X exit_status = 1;
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 strcpy(prtfname, extfname);
X if (zoo_header.vdata & VFL_ON)
X add_version (prtfname, &direntry);
X
X if (tofile) {
X int present = 0;
X
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 strcpy(prtfname, direntry.fname);
X add_version (prtfname, &direntry);
X }
X#endif /* ifndef PORTABLE */
X
X /* don't extract if archived file is older than disk copy */
X if (!supersede && exists(extfname)) {
X unsigned int ddate, dtime;
X#ifdef GETUTIME
X getutime (extfname, &ddate, &dtime);
X#else
X ZOOFILE tfile;
X ddate = dtime = 0xffff; /* assume maximum */
X tfile = zooopen(extfname, Z_READ);
X if (tfile == NOFILE)
X goto loop_again;
X gettime (tfile, &ddate, &dtime);
X zooclose (tfile);
X#endif
X if (cmpnum (direntry.date, direntry.time, ddate, dtime) <= 0) {
X prterror ('m', "%-14s -- skipped\n", prtfname);
X goto loop_again;
X }
X }
X
X if (overwrite) {
X this_file = zoocreate (extfname);
X#ifdef FATTR
X /* if can't open file, and OO option, make it writable first */
X if (this_file == NOFILE && overwrite >= 4 &&
X (direntry.fattr >> 22) == 1 && exists(extfname)) {
X setfattr (extfname, (unsigned long) (1L << 7) | direntry.fattr);
X this_file = zoocreate (extfname);
X }
X#endif /* FATTR */
X } else {
X if (exists (extfname)) {
X present = 1;
X this_file = NOFILE;
X } else
X this_file = zoocreate (extfname);
X }
X error_message = 1;
X if (this_file == NOFILE) {
X if (present == 1) { /* if file exists already */
X char ans[20]; /* answer to "Overwrite?" */
X do {
X#ifdef EXT_ANYWAY
X printf ("%s exists; extract anyway? [Yes/No/All] ",
X extfname);
X#else
X printf ("Overwrite %s (Yes/No/All)? ", extfname);
X#endif
X fflush (stdin);
X fgets (ans, sizeof(ans), stdin);
X strlwr (ans);
X } while (*ans != 'y' && *ans != 'n' && *ans != 'a');
X
X if (*ans == 'a')
X overwrite++;
X if (*ans == 'y' || *ans == 'a') {
X this_file = zoocreate(extfname);
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_file == NOFILE) { /* file couldn't be opened */
X if (error_message == 1) {
X prterror ('e', "Can't open %s for output.\n", extfname);
X exit_status = 1;
X
X#ifndef PORTABLE
X /* if error was due to full disk, abort */
X if (space(0, &alloc_size) < alloc_size)
X prterror ('f', disk_full);
X#endif
X
X }
X } else if (zooseek (zoo_file, (direntry.offset + dat_ofs), 0) == -1L) {
X prterror ('e', "Could not seek to file data.\n");
X exit_status = 1;
X close_file (this_file);
X } else {
X#ifndef PORTABLE
X /* check msdos'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_file (this_file);
X unlink (extfname);
X prterror ('f', disk_full);
X }
X }
X#endif
X if (tofile && disk_space < direntry.org_size) {
X#ifdef PORTABLE
X ;
X#else
X prterror ('e', no_space, prtfname);
X unlink (extfname); /* delete any created file */
X#endif /* portable */
X
X } else {
X
X#ifndef PORTABLE
X if (fast_ext) { /* fast ext -> create header */
X#ifdef LINT_ARGS
X void make_tnh (struct tiny_header *, struct direntry *);
X#else
X void make_tnh();
X#endif
X struct tiny_header tiny_header;
X make_tnh(&tiny_header, &direntry);
X zoowrite (this_file, (char *) &tiny_header, sizeof(tiny_header));
X
X if (direntry.cmt_size != 0) { /* copy comment */
X long save_pos;
X save_pos = zootell (zoo_file);
X zooseek (zoo_file, direntry.comment, 0);
X getfile (zoo_file, this_file,
X (long) direntry.cmt_size, 0);
X zooseek (zoo_file, save_pos, 0);
X }
X }
X#endif /* ifndef PORTABLE */
X
X crccode = 0; /* Initialize CRC before extraction */
X if (!pipe) {
X#ifdef PORTABLE
X prterror ('m', "%-14s -- ", prtfname);
X#else
X if (fast_ext)
X prterror ('m', "%-12s ==> %-12s -- ",
X prtfname, extfname);
X else
X prterror ('m', "%-12s -- ", prtfname);
X#endif /* PORTABLE */
X
X } else { /* must be pipe */
X prterror ('M',"\n\n********\n%s\n********\n",prtfname);
X
X#ifdef SETMODE
X MODE_BIN(this_file); /* make std output binary so
X ^Z won't cause error */
X#endif
X }
X#ifndef NOSIGNAL
X if (tofile)
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_file, this_file, direntry.size_now, 1);
X
X#ifndef PORTABLE
X else if (fast_ext)
X /* 4th param 0 means no CRC update */
X status = getfile (zoo_file, this_file, direntry.size_now, 0);
X#endif
X
X else if (direntry.packing_method == 1) {
X#ifdef UNBUF_IO
X /* NOT PORTABLE -- DO NOT TRY THIS AT HOME */
X long lseek PARMS ((int, long, int));
X long tell PARMS ((int));
X int this_fd, zoo_fd;
X
X /* get file descriptors */
X this_fd = null_device ? -2 : fileno (this_file);
X zoo_fd = fileno (zoo_file);
X
X zooseek (zoo_file, zootell (zoo_file), 0); /* synch */
X lseek (zoo_fd, zootell (zoo_file), 0); /* ..again */
X if (!null_device) {
X zooseek (this_file, zootell (this_file), 0); /* synch */
X lseek (this_fd, zootell (this_file), 0); /* ..again */
X }
X status = lzd(zoo_fd, this_fd); /* uncompress */
X zooseek (zoo_file, tell (zoo_fd), 0); /* resynch */
X if (!null_device)
X zooseek (this_file, tell (this_fd), 0);/* resynch */
X#else
X status = lzd (zoo_file, this_file); /* uncompress */
X#endif
X } else {
X prterror ('e', "File %s: impossible packing method.\n",
X whichname);
X unlink(extfname);
X goto loop_again;
X }
X
X
X#ifndef NOSIGNAL
X if (tofile)
X signal (SIGINT, oldsignal);
X#endif /* not NOSIGNAL */
X
X#ifdef SETMODE
X if (pipe)
X MODE_TEXT(this_file); /* restore text mode */
X#endif
X
X if (tofile) {
X /* set date/time of file being extracted */
X#ifdef GETTZ
X void tzadj();
X /* adjust for original timezone */
X tzadj (&direntry);
X#endif
X#ifdef NIXTIME
X close_file (this_file);
X setutime (extfname, direntry.date, direntry.time);
X#else
X settime (this_file, direntry.date, direntry.time);
X close_file (this_file);
X#endif
X#ifdef FATTR
X/* Restore file attributes. Bit 23==1 means system-specific; we currently
Xdon't recognize this. Bit 23==0 means use portable format, in which case
Xbit 22==0 means ignore attributes. Thus attributes are ignored if both
Xbits 23 and 22 are zero, which is the effect of a zero-filled file
Xattribute field. Currently we restore file attributes if and only if
Xbit 23==0 and bit 22==1. */
X
X if ((direntry.fattr >> 22) == 1) {
X setfattr (extfname, direntry.fattr);
X }
X#endif /* FATTR */
X } /* end of if (tofile) ... */
X if (status != 0) {
X exit_status = 1;
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 prterror ('e', no_space, prtfname);
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 if (
X#ifndef PORTABLE
X !fast_ext &&
X#endif
X direntry.file_crc != crccode
X ) {
X badcrc_count++;
X exit_status = 1;
X if (!pipe) {
X if (!null_device)
X prterror ('M', "extracted ");
X prterror ('w', bad_crc, prtfname);
X }
X else { /* duplicate to standard error */
X static char stars[] = "\n******\n";
X putstr (stars);
X prterror ('w', bad_crc, prtfname);
X putstr (stars);
X fprintf (stderr, "WARNING: ");
X fprintf (stderr, bad_crc, prtfname);
X }
X } else
X if (!pipe)
X prterror ('M', null_device ? "OK\n" : "extracted\n");
X
X } /* end if */
X } /* end if */
X } /* end if */
X } /* end if */
X
Xloop_again:
X zooseek (zoo_file, next_ptr, 0); /* ..seek to next dir entry */
X} /* end while */
X
Xclose_file (zoo_file);
Xif (!matched)
X putstr (no_match);
X
Xif (badcrc_count) {
X prterror ('w', "%d File(s) with bad CRC.\n", badcrc_count);
X} else if (null_device)
X prterror ('m', "Archive seems OK.\n");
X
Xzooexit (exit_status);
X
X} /* end zooext */
X
X/* close_file() */
X/* closes a file if and only if we aren't sending output to
X a pipe or to the null device */
X
Xvoid close_file (file)
XZOOFILE file;
X{
X if (tofile)
X zooclose (file);
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 zooclose (this_file);
X unlink (extfname);
X zooexit (1);
X}
X
X#ifndef PORTABLE
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}
X#endif /* ifndef PORTABLE */
END_OF_FILE
if test 21819 -ne `wc -c <'zooext.c'`; then
echo shar: \"'zooext.c'\" unpacked with wrong size!
fi
# end of 'zooext.c'
fi
echo shar: End of archive 8 \(of 10\).
cp /dev/null ark8isdone
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 at uunet.uu.net.
More information about the Comp.sources.unix
mailing list