summaryrefslogtreecommitdiff
path: root/zipfile.c
diff options
context:
space:
mode:
Diffstat (limited to 'zipfile.c')
-rw-r--r--zipfile.c1565
1 files changed, 1565 insertions, 0 deletions
diff --git a/zipfile.c b/zipfile.c
new file mode 100644
index 0000000..647c31f
--- /dev/null
+++ b/zipfile.c
@@ -0,0 +1,1565 @@
+/*
+ Copyright (c) 1990-2005 Info-ZIP. All rights reserved.
+
+ See the accompanying file LICENSE, version 2004-May-22 or later
+ (the contents of which are also included in zip.h) for terms of use.
+ If, for some reason, both of these files are missing, the Info-ZIP license
+ also may be found at: ftp://ftp.info-zip.org/pub/infozip/license.html
+*/
+/*
+ * zipfile.c by Mark Adler.
+ */
+#define __ZIPFILE_C
+
+#include "zip.h"
+#include "revision.h"
+
+#ifdef VMS
+# include <rms.h>
+# include <starlet.h>
+# include "vms/vmsmunch.h"
+# include "vms/vmsdefs.h"
+#endif
+
+#ifdef __RSXNT__
+# include <windows.h>
+#endif
+
+/*
+ * XXX start of zipfile.h
+ */
+#ifdef THEOS
+/* Macros cause stack overflow in compiler */
+ush SH(uch* p) { return ((ush)(uch)((p)[0]) | ((ush)(uch)((p)[1]) << 8)); }
+ulg LG(uch* p) { return ((ulg)(SH(p)) | ((ulg)(SH((p)+2)) << 16)); }
+#else /* !THEOS */
+/* Macros for converting integers in little-endian to machine format */
+#define SH(a) ((ush)(((ush)(uch)(a)[0]) | (((ush)(uch)(a)[1]) << 8)))
+#define LG(a) ((ulg)SH(a) | ((ulg)SH((a)+2) << 16))
+#endif /* ?THEOS */
+
+/* Macros for writing machine integers to little-endian format */
+#define PUTSH(a,f) {putc((char)((a) & 0xff),(f)); putc((char)((a) >> 8),(f));}
+#define PUTLG(a,f) {PUTSH((a) & 0xffff,(f)) PUTSH((a) >> 16,(f))}
+
+
+/* -- Structure of a ZIP file -- */
+
+/* Signatures for zip file information headers */
+#define LOCSIG 0x04034b50L
+#define CENSIG 0x02014b50L
+#define ENDSIG 0x06054b50L
+#define EXTLOCSIG 0x08074b50L
+
+/* Offsets of values in headers */
+#define LOCVER 0 /* version needed to extract */
+#define LOCFLG 2 /* encrypt, deflate flags */
+#define LOCHOW 4 /* compression method */
+#define LOCTIM 6 /* last modified file time, DOS format */
+#define LOCDAT 8 /* last modified file date, DOS format */
+#define LOCCRC 10 /* uncompressed crc-32 for file */
+#define LOCSIZ 14 /* compressed size in zip file */
+#define LOCLEN 18 /* uncompressed size */
+#define LOCNAM 22 /* length of filename */
+#define LOCEXT 24 /* length of extra field */
+
+#define EXTCRC 0 /* uncompressed crc-32 for file */
+#define EXTSIZ 4 /* compressed size in zip file */
+#define EXTLEN 8 /* uncompressed size */
+
+#define CENVEM 0 /* version made by */
+#define CENVER 2 /* version needed to extract */
+#define CENFLG 4 /* encrypt, deflate flags */
+#define CENHOW 6 /* compression method */
+#define CENTIM 8 /* last modified file time, DOS format */
+#define CENDAT 10 /* last modified file date, DOS format */
+#define CENCRC 12 /* uncompressed crc-32 for file */
+#define CENSIZ 16 /* compressed size in zip file */
+#define CENLEN 20 /* uncompressed size */
+#define CENNAM 24 /* length of filename */
+#define CENEXT 26 /* length of extra field */
+#define CENCOM 28 /* file comment length */
+#define CENDSK 30 /* disk number start */
+#define CENATT 32 /* internal file attributes */
+#define CENATX 34 /* external file attributes */
+#define CENOFF 38 /* relative offset of local header */
+
+#define ENDDSK 0 /* number of this disk */
+#define ENDBEG 2 /* number of the starting disk */
+#define ENDSUB 4 /* entries on this disk */
+#define ENDTOT 6 /* total number of entries */
+#define ENDSIZ 8 /* size of entire central directory */
+#define ENDOFF 12 /* offset of central on starting disk */
+#define ENDCOM 16 /* length of zip file comment */
+
+
+
+/* Local functions */
+
+local int zqcmp OF((ZCONST zvoid *, ZCONST zvoid *));
+local int scanzipf_reg OF((FILE *f));
+#ifndef UTIL
+ local int rqcmp OF((ZCONST zvoid *, ZCONST zvoid *));
+ local int zbcmp OF((ZCONST zvoid *, ZCONST zvoid far *));
+ local void zipoddities OF((struct zlist far *));
+ local int scanzipf_fix OF((FILE *f));
+# ifdef USE_EF_UT_TIME
+ local int ef_scan_ut_time OF((char *ef_buf, extent ef_len, int ef_is_cent,
+ iztimes *z_utim));
+# endif /* USE_EF_UT_TIME */
+ local void cutpath OF((char *p, int delim));
+#endif /* !UTIL */
+
+/*
+ * XXX end of zipfile.h
+ */
+
+/* Local data */
+
+#ifdef HANDLE_AMIGA_SFX
+ ulg amiga_sfx_offset; /* place where size field needs updating */
+#endif
+
+
+local int zqcmp(a, b)
+ZCONST zvoid *a, *b; /* pointers to pointers to zip entries */
+/* Used by qsort() to compare entries in the zfile list.
+ * Compares the internal names z->iname */
+{
+ return namecmp((*(struct zlist far **)a)->iname,
+ (*(struct zlist far **)b)->iname);
+}
+
+#ifndef UTIL
+
+local int rqcmp(a, b)
+ZCONST zvoid *a, *b; /* pointers to pointers to zip entries */
+/* Used by qsort() to compare entries in the zfile list.
+ * Compare the internal names z->iname, but in reverse order. */
+{
+ return namecmp((*(struct zlist far **)b)->iname,
+ (*(struct zlist far **)a)->iname);
+}
+
+
+local int zbcmp(n, z)
+ZCONST zvoid *n; /* string to search for */
+ZCONST zvoid far *z; /* pointer to a pointer to a zip entry */
+/* Used by search() to compare a target to an entry in the zfile list. */
+{
+ return namecmp((char *)n, ((struct zlist far *)z)->zname);
+}
+
+
+struct zlist far *zsearch(n)
+ZCONST char *n; /* name to find */
+/* Return a pointer to the entry in zfile with the name n, or NULL if
+ not found. */
+{
+ zvoid far **p; /* result of search() */
+
+ if (zcount &&
+ (p = search(n, (ZCONST zvoid far **)zsort, zcount, zbcmp)) != NULL)
+ return *(struct zlist far **)p;
+ else
+ return NULL;
+}
+
+#endif /* !UTIL */
+
+#ifndef VMS
+# ifndef PATHCUT
+# define PATHCUT '/'
+# endif
+
+char *ziptyp(s)
+char *s; /* file name to force to zip */
+/* If the file name *s has a dot (other than the first char), or if
+ the -A option is used (adjust self-extracting file) then return
+ the name, otherwise append .zip to the name. Allocate the space for
+ the name in either case. Return a pointer to the new name, or NULL
+ if malloc() fails. */
+{
+ char *q; /* temporary pointer */
+ char *t; /* pointer to malloc'ed string */
+#ifdef THEOS
+ char *r; /* temporary pointer */
+ char *disk;
+#endif
+
+ if ((t = malloc(strlen(s) + 5)) == NULL)
+ return NULL;
+ strcpy(t, s);
+#ifdef __human68k__
+ _toslash(t);
+#endif
+#ifdef MSDOS
+ for (q = t; *q; INCSTR(q))
+ if (*q == '\\')
+ *q = '/';
+#endif /* MSDOS */
+#ifdef __RSXNT__ /* RSXNT/EMX C rtl uses OEM charset */
+ AnsiToOem(t, t);
+#endif
+ if (adjust) return t;
+#ifndef RISCOS
+# ifndef QDOS
+# ifdef AMIGA
+ if ((q = MBSRCHR(t, '/')) == NULL)
+ q = MBSRCHR(t, ':');
+ if (MBSRCHR((q ? q + 1 : t), '.') == NULL)
+# else /* !AMIGA */
+# ifdef THEOS
+ /* the argument expansion add a dot to the end of file names when
+ * there is no extension and at least one of a argument has wild cards.
+ * So check for at least one character in the extension if there is a dot
+ * in file name */
+ if ((q = MBSRCHR((q = MBSRCHR(t, PATHCUT)) == NULL ? t : q + 1, '.')) == NULL
+ || q[1] == '\0') {
+# else /* !THEOS */
+# ifdef TANDEM
+ if (MBSRCHR((q = MBSRCHR(t, '.')) == NULL ? t : q + 1, ' ') == NULL)
+# else /* !TANDEM */
+ if (MBSRCHR((q = MBSRCHR(t, PATHCUT)) == NULL ? t : q + 1, '.') == NULL)
+# endif /* ?TANDEM */
+# endif /* ?THEOS */
+# endif /* ?AMIGA */
+# ifdef CMS_MVS
+ if (strncmp(t,"dd:",3) != 0 && strncmp(t,"DD:",3) != 0)
+# endif /* CMS_MVS */
+# ifdef THEOS
+ /* insert .zip extension before disk name */
+ if ((r = MBSRCHR(t, ':')) != NULL) {
+ /* save disk name */
+ if ((disk = strdup(r)) == NULL)
+ return NULL;
+ strcpy(r[-1] == '.' ? r - 1 : r, ".zip");
+ strcat(t, disk);
+ free(disk);
+ } else {
+ if (q != NULL && *q == '.')
+ strcpy(q, ".zip");
+ else
+ strcat(t, ".zip");
+ }
+ }
+# else /* !THEOS */
+# ifdef TANDEM /* Tandem can't cope with extensions */
+ strcat(t, " ZIP");
+# else /* !TANDEM */
+ strcat(t, ".zip");
+# endif /* ?TANDEM */
+# endif /* ?THEOS */
+# else /* QDOS */
+ q = LastDir(t);
+ if(MBSRCHR(q, '_') == NULL && MBSRCHR(q, '.') == NULL)
+ {
+ strcat(t, "_zip");
+ }
+# endif /* QDOS */
+#endif /* !RISCOS */
+ return t;
+}
+
+#else /* VMS */
+
+# define PATHCUT ']'
+
+char *ziptyp(s)
+char *s;
+{ int status;
+ struct FAB fab;
+ struct NAM nam;
+ static char zero=0;
+ char result[NAM$C_MAXRSS+1],exp[NAM$C_MAXRSS+1];
+ char *p;
+
+ fab = cc$rms_fab;
+ nam = cc$rms_nam;
+
+ fab.fab$l_fna = s;
+ fab.fab$b_fns = strlen(fab.fab$l_fna);
+
+ fab.fab$l_dna = "sys$disk:[].zip"; /* Default fspec */
+ fab.fab$b_dns = strlen(fab.fab$l_dna);
+
+ fab.fab$l_nam = &nam;
+
+ nam.nam$l_rsa = result; /* Put resultant name of */
+ nam.nam$b_rss = sizeof(result)-1; /* existing zipfile here */
+
+ nam.nam$l_esa = exp; /* For full spec of */
+ nam.nam$b_ess = sizeof(exp)-1; /* file to create */
+
+ status = sys$parse(&fab);
+ if( (status & 1) == 0 )
+ return &zero;
+
+ status = sys$search(&fab);
+ if( status & 1 )
+ { /* Existing ZIP file */
+ int l;
+ if( (p=malloc( (l=nam.nam$b_rsl) + 1 )) != NULL )
+ { result[l] = 0;
+ strcpy(p,result);
+ }
+ }
+ else
+ { /* New ZIP file */
+ int l;
+ if( (p=malloc( (l=nam.nam$b_esl) + 1 )) != NULL )
+ { exp[l] = 0;
+ strcpy(p,exp);
+ }
+ }
+ return p;
+}
+
+#endif /* VMS */
+
+#ifndef UTIL
+
+local void zipoddities(z)
+struct zlist far *z;
+{
+ if ((z->vem >> 8) >= NUM_HOSTS)
+ {
+ sprintf(errbuf, "made by version %d.%d on system type %d: ",
+ (ush)(z->vem & 0xff) / (ush)10, (ush)(z->vem & 0xff) % (ush)10,
+ z->vem >> 8);
+ zipwarn(errbuf, z->zname);
+ }
+ if (z->ver != 10 && z->ver != 11 && z->ver != 20)
+ {
+ sprintf(errbuf, "needs unzip %d.%d on system type %d: ",
+ (ush)(z->ver & 0xff) / (ush)10,
+ (ush)(z->ver & 0xff) % (ush)10, z->ver >> 8);
+ zipwarn(errbuf, z->zname);
+ }
+ if (z->flg != z->lflg)
+ {
+ sprintf(errbuf, "local flags = 0x%04x, central = 0x%04x: ",
+ z->lflg, z->flg);
+ zipwarn(errbuf, z->zname);
+ }
+ else if (z->flg & ~0xf)
+ {
+ sprintf(errbuf, "undefined bits used in flags = 0x%04x: ", z->flg);
+ zipwarn(errbuf, z->zname);
+ }
+ if (z->how > DEFLATE)
+ {
+ sprintf(errbuf, "unknown compression method %u: ", z->how);
+ zipwarn(errbuf, z->zname);
+ }
+ if (z->dsk)
+ {
+ sprintf(errbuf, "starts on disk %u: ", z->dsk);
+ zipwarn(errbuf, z->zname);
+ }
+ if (z->att!=ASCII && z->att!=BINARY && z->att!=__EBCDIC)
+ {
+ sprintf(errbuf, "unknown internal attributes = 0x%04x: ", z->att);
+ zipwarn(errbuf, z->zname);
+ }
+#if 0
+/* This test is ridiculous, it produces an error message for almost every */
+/* platform of origin other than MS-DOS, Unix, VMS, and Acorn! Perhaps */
+/* we could test "if (z->dosflag && z->atx & ~0xffL)", but what for? */
+ if (((n = z->vem >> 8) != 3) && n != 2 && n != 13 && z->atx & ~0xffL)
+ {
+ sprintf(errbuf, "unknown external attributes = 0x%08lx: ", z->atx);
+ zipwarn(errbuf, z->zname);
+ }
+#endif
+ if (z->ext || z->cext)
+ {
+ if (z->ext && z->cext && z->extra != z->cextra)
+ {
+ sprintf(errbuf,
+ "local extra (%ld bytes) != central extra (%ld bytes): ",
+ (ulg)z->ext, (ulg)z->cext);
+ if (noisy) fprintf(stderr, "\tzip info: %s%s\n", errbuf, z->zname);
+ }
+#if (!defined(RISCOS) && !defined(CMS_MVS))
+ /* in noisy mode, extra field sizes are always reported */
+ else if (noisy)
+#else /* RISCOS || CMS_MVS */
+/* avoid warnings for zipfiles created on the same type of OS system! */
+/* or, was this warning really intended (eg. OS/2)? */
+ /* Only give info if extra bytes were added by another system */
+ else if (noisy && ((z->vem >> 8) != (OS_CODE >> 8)))
+#endif /* ?(RISCOS || CMS_MVS) */
+ {
+ fprintf(stderr, "zip info: %s has %ld bytes of %sextra data\n",
+ z->zname, z->ext ? (ulg)z->ext : (ulg)z->cext,
+ z->ext ? (z->cext ? "" : "local ") : "central ");
+ }
+ }
+}
+
+/*
+ * scanzipf_fix is called with zip -F or zip -FF
+ * read the file from front to back and pick up the pieces
+ * NOTE: there are still checks missing to see if the header
+ * that was found is *VALID*
+ */
+local int scanzipf_fix(f)
+ FILE *f; /* zip file */
+/*
+ The name of the zip file is pointed to by the global "zipfile". The globals
+ zipbeg, cenbeg, zfiles, zcount, zcomlen, zcomment, and zsort are filled in.
+ Return an error code in the ZE_ class.
+*/
+{
+ ulg a = 0L; /* attributes returned by filetime() */
+ char b[CENHEAD]; /* buffer for central headers */
+ ush flg; /* general purpose bit flag */
+ int m; /* mismatch flag */
+ extent n; /* length of name */
+ ulg p; /* current file offset */
+ ulg s; /* size of data, start of central */
+ struct zlist far * far *x; /* pointer last entry's link */
+ struct zlist far *z; /* current zip entry structure */
+
+ /* Get any file attribute valid for this OS, to set in the central
+ * directory when fixing the archive:
+ */
+#ifndef UTIL
+ filetime(zipfile, &a, (long*)&s, NULL);
+#endif
+ x = &zfiles; /* first link */
+ p = 0; /* starting file offset */
+#ifdef HANDLE_AMIGA_SFX
+ amiga_sfx_offset = 0L;
+#endif
+
+ /* Find start of zip structures */
+ for (;;) {
+ while ((m = getc(f)) != EOF && m != 0x50) /* 0x50 == 'P' */
+ {
+#ifdef HANDLE_AMIGA_SFX
+ if (p == 0 && m == 0)
+ amiga_sfx_offset = 1L;
+ else if (amiga_sfx_offset) {
+ if ((p == 1 && m != 0) || (p == 2 && m != 3)
+ || (p == 3 && (uch) m != 0xF3))
+ amiga_sfx_offset = 0L;
+ }
+#endif /* HANDLE_AMIGA_SFX */
+ p++;
+ }
+ b[0] = (char) m;
+ if (fread(b+1, 3, 1, f) != 1 || (s = LG(b)) == LOCSIG || s == ENDSIG)
+ break;
+ if (fseek(f, -3L, SEEK_CUR))
+ return ferror(f) ? ZE_READ : ZE_EOF;
+ p++;
+ }
+ zipbeg = p;
+#ifdef HANDLE_AMIGA_SFX
+ if (amiga_sfx_offset && zipbeg >= 12 && (zipbeg & 3) == 0
+ && fseek(f, -12L, SEEK_CUR) == 0 && fread(b, 12, 1, f) == 1
+ && LG(b + 4) == 0xF1030000 /* 1009 in Motorola byte order */)
+ amiga_sfx_offset = zipbeg - 4;
+ else
+ amiga_sfx_offset = 0L;
+#endif /* HANDLE_AMIGA_SFX */
+
+ /* Read local headers */
+ while (LG(b) == LOCSIG)
+ {
+ if ((z = (struct zlist far *)farmalloc(sizeof(struct zlist))) == NULL ||
+ zcount + 1 < zcount)
+ return ZE_MEM;
+ if (fread(b, LOCHEAD, 1, f) != 1) {
+ farfree((zvoid far *)z);
+ break;
+ }
+
+ z->ver = SH(LOCVER + b);
+ z->vem = (ush)(dosify ? 20 : OS_CODE + Z_MAJORVER * 10 + Z_MINORVER);
+ z->dosflag = dosify;
+ flg = z->flg = z->lflg = SH(LOCFLG + b);
+ z->how = SH(LOCHOW + b);
+ z->tim = LG(LOCTIM + b); /* time and date into one long */
+ z->crc = LG(LOCCRC + b);
+ z->siz = LG(LOCSIZ + b);
+ z->len = LG(LOCLEN + b);
+ n = z->nam = SH(LOCNAM + b);
+ z->cext = z->ext = SH(LOCEXT + b);
+
+ z->com = 0;
+ z->dsk = 0;
+ z->att = 0;
+ z->atx = dosify ? a & 0xff : a; /* Attributes from filetime() */
+ z->mark = 0;
+ z->trash = 0;
+
+ s = fix > 1 ? 0L : z->siz; /* discard compressed size with -FF */
+
+ /* Initialize all fields pointing to malloced data to NULL */
+ z->zname = z->name = z->iname = z->extra = z->cextra = z->comment = NULL;
+
+ /* Link into list */
+ *x = z;
+ z->nxt = NULL;
+ x = &z->nxt;
+
+ /* Read file name and extra field and skip data */
+ if (n == 0)
+ {
+ sprintf(errbuf, "%lu", (ulg)zcount + 1);
+ zipwarn("zero-length name for entry #", errbuf);
+#ifndef DEBUG
+ return ZE_FORM;
+#endif
+ }
+ if ((z->iname = malloc(n+1)) == NULL ||
+ (z->ext && (z->extra = malloc(z->ext)) == NULL))
+ return ZE_MEM;
+ if (fread(z->iname, n, 1, f) != 1 ||
+ (z->ext && fread(z->extra, z->ext, 1, f) != 1) ||
+ (s && fseek(f, (long)s, SEEK_CUR)))
+ return ferror(f) ? ZE_READ : ZE_EOF;
+ /* If there is an extended local header, s is either 0 or
+ * the correct compressed size.
+ */
+ z->iname[n] = '\0'; /* terminate name */
+ z->zname = in2ex(z->iname); /* convert to external name */
+ if (z->zname == NULL)
+ return ZE_MEM;
+ z->name = z->zname;
+ z->cextra = z->extra;
+ if (noisy) fprintf(mesg, "zip: reading %s\n", z->zname);
+
+ /* Save offset, update for next header */
+ z->off = p;
+ p += 4 + LOCHEAD + n + z->ext + s;
+ zcount++;
+
+ /* Skip extended local header if there is one */
+ if ((flg & 8) != 0) {
+ /* Skip the compressed data if compressed size is unknown.
+ * For safety, we should use the central directory.
+ */
+ if (s == 0) {
+ for (;;) {
+ while ((m = getc(f)) != EOF && m != 0x50) ; /* 0x50 == 'P' */
+ b[0] = (char) m;
+ if (fread(b+1, 15, 1, f) != 1 || LG(b) == EXTLOCSIG)
+ break;
+ if (fseek(f, -15L, SEEK_CUR))
+ return ferror(f) ? ZE_READ : ZE_EOF;
+ }
+ s = LG(4 + EXTSIZ + b);
+ p += s;
+ if ((ulg) ftell(f) != p+16L) {
+ zipwarn("bad extended local header for ", z->zname);
+ return ZE_FORM;
+ }
+ } else {
+ /* compressed size non-zero, assume that it is valid: */
+ Assert(p == ftell(f), "bad compressed size with extended header");
+
+ if (fseek(f, p, SEEK_SET) || fread(b, 16, 1, f) != 1)
+ return ferror(f) ? ZE_READ : ZE_EOF;
+ if (LG(b) != EXTLOCSIG) {
+ zipwarn("extended local header not found for ", z->zname);
+ return ZE_FORM;
+ }
+ }
+ /* overwrite the unknown values of the local header: */
+
+ /* already in host format */
+ z->crc = LG(4 + EXTCRC + b);
+ z->siz = s;
+ z->len = LG(4 + EXTLEN + b);
+
+ p += 16L;
+ }
+ else if (fix > 1) {
+ /* Don't trust the compressed size */
+ for (;;) {
+ while ((m = getc(f)) != EOF && m != 0x50) p++; /* 0x50 == 'P' */
+ b[0] = (char) m;
+ if (fread(b+1, 3, 1, f) != 1 || (s = LG(b)) == LOCSIG || s == CENSIG)
+ break;
+ if (fseek(f, -3L, SEEK_CUR))
+ return ferror(f) ? ZE_READ : ZE_EOF;
+ p++;
+ }
+ s = p - (z->off + 4 + LOCHEAD + n + z->ext);
+ if (s != z->siz) {
+ fprintf(mesg, " compressed size %ld, actual size %ld for %s\n",
+ z->siz, s, z->zname);
+ z->siz = s;
+ }
+ /* next LOCSIG already read at this point, don't read it again: */
+ continue;
+ }
+
+ /* Read next signature */
+ if (fread(b, 4, 1, f) != 1)
+ break;
+ }
+
+ s = p; /* save start of central */
+
+ if (LG(b) != CENSIG && noisy) {
+ fprintf(mesg, "zip warning: %s %s truncated.\n", zipfile,
+ fix > 1 ? "has been" : "would be");
+
+ if (fix == 1) {
+ fprintf(mesg,
+ "Retry with option -qF to truncate, with -FF to attempt full recovery\n");
+ ZIPERR(ZE_FORM, NULL);
+ }
+ }
+
+ cenbeg = s;
+
+ if (zipbeg && noisy)
+ fprintf(mesg, "%s: adjusting offsets for a preamble of %lu bytes\n",
+ zipfile, zipbeg);
+
+ return ZE_OK;
+}
+
+#endif /* !UTIL */
+
+/*
+ * scanzipf_reg starts searching for the End Signature at the end of the file
+ * The End Signature points to the Central Directory Signature which points
+ * to the Local Directory Signature
+ * XXX probably some more consistency checks are needed
+ */
+local int scanzipf_reg(f)
+ FILE *f; /* zip file */
+/*
+ The name of the zip file is pointed to by the global "zipfile". The globals
+ zipbeg, cenbeg, zfiles, zcount, zcomlen, zcomment, and zsort are filled in.
+ Return an error code in the ZE_ class.
+*/
+{
+ char b[CENHEAD]; /* buffer for central headers */
+ ush flg; /* general purpose bit flag */
+ int m; /* mismatch flag */
+ extent n; /* length of name */
+ struct zlist far * far *x; /* pointer last entry's link */
+ struct zlist far *z; /* current zip entry structure */
+ char *t; /* temporary pointer */
+ char far *u; /* temporary variable */
+ int found;
+ char *buf; /* temp buffer for reading zipfile */
+ long deltaoff;
+
+ buf = malloc(4096 + 4);
+ if (buf == NULL)
+ return ZE_MEM;
+
+#ifdef HANDLE_AMIGA_SFX
+ amiga_sfx_offset = (fread(buf, 1, 4, f) == 4 && LG(buf) == 0xF3030000);
+ /* == 1 if this file is an Amiga executable (presumably UnZipSFX) */
+#endif
+ found = 0;
+ t = &buf[4096];
+ t[1] = '\0';
+ t[2] = '\0';
+ t[3] = '\0';
+ if (fseek(f, -4096L, SEEK_END) == 0) {
+ zipbeg = (ulg) (ftell(f) + 4096L);
+ while (!found && zipbeg >= 4096) {
+ zipbeg -= 4096L;
+ buf[4096] = t[1];
+ buf[4097] = t[2];
+ buf[4098] = t[3];
+/*
+ * XXX error check ??
+ */
+ fread(buf, 1, 4096, f);
+ fseek(f, -8192L, SEEK_CUR);
+ t = &buf[4095];
+/*
+ * XXX far pointer arithmetic in DOS
+ */
+ while (t >= buf) {
+ /* Check for ENDSIG the End Of Central Directory Record signature
+ ("PK\5\6" in ASCII) */
+ if (LG(t) == ENDSIG) {
+ found = 1;
+/*
+ * XXX error check ??
+ * XXX far pointer arithmetic in DOS
+ */
+ zipbeg += (ulg) (t - buf);
+ fseek(f, (long) zipbeg + 4L, SEEK_SET);
+ break;
+ }
+ --t;
+ }
+ }
+ }
+ else
+ zipbeg = 4096L;
+/*
+ * XXX warn: garbage at the end of the file ignored
+ */
+ if (!found && zipbeg > 0) {
+ size_t s;
+
+ fseek(f, 0L, SEEK_SET);
+ clearerr(f);
+ s = fread(buf, 1, (size_t) zipbeg, f);
+ buf[s] = t[1];
+ buf[s + 1] = t[2];
+ buf[s + 2] = t[3];
+ t = &buf[s - 1];
+/*
+ * XXX far pointer comparison in DOS
+ */
+ while (t >= buf) {
+ /* Check for ENDSIG ("PK\5\6" in ASCII) */
+ if (LG(t) == ENDSIG) {
+ found = 1;
+/*
+ * XXX far pointer arithmetic in DOS
+ */
+ zipbeg = (ulg) (t - buf);
+ fseek(f, (long) zipbeg + 4L, SEEK_SET);
+ break;
+ }
+ --t;
+ }
+ }
+ free(buf);
+ if (!found) {
+ zipwarn("missing end signature--probably not a zip file (did you", "");
+ zipwarn("remember to use binary mode when you transferred it?)", "");
+ return ZE_FORM;
+ }
+
+/*
+ * Read the End Of Central Directory Record
+ */
+ /* Read end header */
+ if (fread(b, ENDHEAD, 1, f) != 1)
+ return ferror(f) ? ZE_READ : ZE_EOF;
+ if (SH(ENDDSK + b) || SH(ENDBEG + b) ||
+ SH(ENDSUB + b) != SH(ENDTOT + b))
+ zipwarn("multiple disk information ignored", "");
+ zcomlen = SH(ENDCOM + b);
+ if (zcomlen)
+ {
+ if ((zcomment = malloc(zcomlen)) == NULL)
+ return ZE_MEM;
+ if (fread(zcomment, zcomlen, 1, f) != 1)
+ {
+ free((zvoid *)zcomment);
+ zcomment = NULL;
+ return ferror(f) ? ZE_READ : ZE_EOF;
+ }
+#ifdef EBCDIC
+ if (zcomment)
+ memtoebc(zcomment, zcomment, zcomlen);
+#endif /* EBCDIC */
+ }
+/*
+ * XXX assumes central header immediately precedes end header
+ */
+ cenbeg = zipbeg - LG(ENDSIZ + b);
+ deltaoff = adjust ? cenbeg - LG(b + ENDOFF) : 0L;
+ if (fseek(f, cenbeg, SEEK_SET) != 0) {
+ perror("fseek");
+ return ZE_FORM; /* XXX */
+ }
+
+ x = &zfiles; /* first link */
+
+ if (fread(b, 4, 1, f) != 1)
+ return ferror(f) ? ZE_READ : ZE_EOF;
+
+ while (LG(b) == CENSIG) {
+ /* Read central header. The portion of the central header that should
+ be in common with local header is read raw, for later comparison.
+ (this requires that the offset of ext in the zlist structure
+ be greater than or equal to LOCHEAD) */
+ if (fread(b, CENHEAD, 1, f) != 1)
+ return ferror(f) ? ZE_READ : ZE_EOF;
+ if ((z = (struct zlist far *)farmalloc(sizeof(struct zlist))) == NULL)
+ return ZE_MEM;
+ z->vem = SH(CENVEM + b);
+ for (u = (char far *)(&(z->ver)), n = 0; n < (CENNAM-CENVER); n++)
+ u[n] = b[CENVER + n];
+ z->nam = SH(CENNAM + b); /* used before comparing cen vs. loc */
+ z->cext = SH(CENEXT + b); /* may be different from z->ext */
+ z->com = SH(CENCOM + b);
+ z->dsk = SH(CENDSK + b);
+ z->att = SH(CENATT + b);
+ z->atx = LG(CENATX + b);
+ z->off = LG(CENOFF + b) + deltaoff;
+ z->dosflag = (z->vem & 0xff00) == 0;
+
+ /* Initialize all fields pointing to malloced data to NULL */
+ z->zname = z->name = z->iname = z->extra = z->cextra = z->comment = NULL;
+
+ /* Link into list */
+ *x = z;
+ z->nxt = NULL;
+ x = &z->nxt;
+
+ /* Read file name, extra field and comment field */
+ if (z->nam == 0)
+ {
+ sprintf(errbuf, "%lu", (ulg)zcount + 1);
+ zipwarn("zero-length name for entry #", errbuf);
+#ifndef DEBUG
+ farfree((zvoid far *)z);
+ return ZE_FORM;
+#endif
+ }
+ if ((z->iname = malloc(z->nam+1)) == NULL ||
+ (z->cext && (z->cextra = malloc(z->cext)) == NULL) ||
+ (z->com && (z->comment = malloc(z->com)) == NULL))
+ return ZE_MEM;
+ if (fread(z->iname, z->nam, 1, f) != 1 ||
+ (z->cext && fread(z->cextra, z->cext, 1, f) != 1) ||
+ (z->com && fread(z->comment, z->com, 1, f) != 1))
+ return ferror(f) ? ZE_READ : ZE_EOF;
+ z->iname[z->nam] = '\0'; /* terminate name */
+#ifdef EBCDIC
+ if (z->com)
+ memtoebc(z->comment, z->comment, z->com);
+#endif /* EBCDIC */
+ /* Update zipbeg offset, prepare for next header */
+ if (z->off < zipbeg)
+ zipbeg = z->off;
+ zcount++;
+ /* Read next signature */
+ if (fread(b, 4, 1, f) != 1)
+ return ferror(f) ? ZE_READ : ZE_EOF;
+ }
+
+ /* Point to start of header list and read local headers */
+ z = zfiles;
+ while (z != NULL) {
+ /* Read next signature */
+ if (fseek(f, z->off, SEEK_SET) != 0 || fread(b, 4, 1, f) != 1)
+ return ferror(f) ? ZE_READ : ZE_EOF;
+ if (LG(b) == LOCSIG) {
+ if (fread(b, LOCHEAD, 1, f) != 1)
+ return ferror(f) ? ZE_READ : ZE_EOF;
+ z->lflg = SH(LOCFLG + b);
+ n = SH(LOCNAM + b);
+ z->ext = SH(LOCEXT + b);
+
+ /* Compare name and extra fields */
+ if (n != z->nam)
+ {
+#ifdef EBCDIC
+ strtoebc(z->iname, z->iname);
+#endif
+ zipwarn("name lengths in local and central differ for ", z->iname);
+ return ZE_FORM;
+ }
+ if ((t = malloc(z->nam)) == NULL)
+ return ZE_MEM;
+ if (fread(t, z->nam, 1, f) != 1)
+ {
+ free((zvoid *)t);
+ return ferror(f) ? ZE_READ : ZE_EOF;
+ }
+ if (memcmp(t, z->iname, z->nam))
+ {
+ free((zvoid *)t);
+#ifdef EBCDIC
+ strtoebc(z->iname, z->iname);
+#endif
+ zipwarn("names in local and central differ for ", z->iname);
+ return ZE_FORM;
+ }
+ free((zvoid *)t);
+ if (z->ext)
+ {
+ if ((z->extra = malloc(z->ext)) == NULL)
+ return ZE_MEM;
+ if (fread(z->extra, z->ext, 1, f) != 1)
+ {
+ free((zvoid *)(z->extra));
+ return ferror(f) ? ZE_READ : ZE_EOF;
+ }
+ if (z->ext == z->cext && memcmp(z->extra, z->cextra, z->ext) == 0)
+ {
+ free((zvoid *)(z->extra));
+ z->extra = z->cextra;
+ }
+ }
+
+ /* Check extended local header if there is one */
+ if ((z->lflg & 8) != 0)
+ {
+ char buf2[16];
+ ulg s; /* size of compressed data */
+
+ s = LG(LOCSIZ + b);
+ if (s == 0)
+ s = LG((CENSIZ-CENVER) + (char far *)(&(z->ver)));
+ if (fseek(f, (z->off + (4+LOCHEAD) + z->nam + z->ext + s), SEEK_SET)
+ || (fread(buf2, 16, 1, f) != 1))
+ return ferror(f) ? ZE_READ : ZE_EOF;
+ if (LG(buf2) != EXTLOCSIG)
+ {
+#ifdef EBCDIC
+ strtoebc(z->iname, z->iname);
+#endif
+ zipwarn("extended local header not found for ", z->iname);
+ return ZE_FORM;
+ }
+ /* overwrite the unknown values of the local header: */
+ for (n = 0; n < 12; n++)
+ b[LOCCRC+n] = buf2[4+n];
+ }
+
+ /* Compare local header with that part of central header (except
+ for the reserved bits in the general purpose flags and except
+ for the already checked entry name length */
+ u = (char far *)(&(z->ver));
+ flg = SH((CENFLG-CENVER) + u); /* Save central flags word */
+ u[CENFLG-CENVER+1] &= 0x1f; /* Mask reserved flag bits */
+ b[LOCFLG+1] &= 0x1f;
+ for (m = 0, n = 0; n < LOCNAM; n++)
+ if (b[n] != u[n])
+ {
+ if (!m)
+ {
+ zipwarn("local and central headers differ for ", z->zname);
+ m = 1;
+ }
+ if (noisy)
+ {
+ sprintf(errbuf, " offset %u--local = %02x, central = %02x",
+ (unsigned)n, (uch)b[n], (uch)u[n]);
+ zipwarn(errbuf, "");
+ }
+ }
+ if (m && !adjust)
+ return ZE_FORM;
+
+ /* Complete the setup of the zlist entry by translating the remaining
+ * central header fields in memory, starting with the fields with
+ * highest offset. This order of the conversion commands takes into
+ * account potential buffer overlaps caused by structure padding.
+ */
+ z->len = LG((CENLEN-CENVER) + u);
+ z->siz = LG((CENSIZ-CENVER) + u);
+ z->crc = LG((CENCRC-CENVER) + u);
+ z->tim = LG((CENTIM-CENVER) + u); /* time and date into one long */
+ z->how = SH((CENHOW-CENVER) + u);
+ z->flg = flg; /* may be different from z->lflg */
+ z->ver = SH((CENVER-CENVER) + u);
+
+ /* Clear actions */
+ z->mark = 0;
+ z->trash = 0;
+#ifdef UTIL
+/* We only need z->iname in the utils */
+ z->name = z->iname;
+#ifdef EBCDIC
+/* z->zname is used for printing and must be coded in native charset */
+ if ((z->zname = malloc(z->nam+1)) == NULL)
+ return ZE_MEM;
+ strtoebc(z->zname, z->iname);
+#else
+ z->zname = z->iname;
+#endif
+#else /* !UTIL */
+ z->zname = in2ex(z->iname); /* convert to external name */
+ if (z->zname == NULL)
+ return ZE_MEM;
+ z->name = z->zname;
+#endif /* ?UTIL */
+ }
+ else {
+#ifdef EBCDIC
+ strtoebc(z->iname, z->iname);
+#endif
+ zipwarn("local header not found for ", z->iname);
+ return ZE_FORM;
+ }
+#ifndef UTIL
+ if (verbose)
+ zipoddities(z);
+#endif
+ z = z->nxt;
+ }
+
+ if (zipbeg && noisy)
+ fprintf(mesg, "%s: %s a preamble of %lu bytes\n",
+ zipfile, adjust ? "adjusting offsets for" : "found", zipbeg);
+
+#ifdef HANDLE_AMIGA_SFX
+ if (zipbeg < 12 || (zipbeg & 3) != 0 /* must be longword aligned */)
+ amiga_sfx_offset = 0;
+ else if (amiga_sfx_offset) {
+ char buf2[16];
+ if (!fseek(f, zipbeg - 12, SEEK_SET) && fread(buf2, 12, 1, f) == 1) {
+ if (LG(buf2 + 4) == 0xF1030000 /* 1009 in Motorola byte order */)
+ /* could also check if LG(buf2) == 0xF2030000... no for now */
+ amiga_sfx_offset = zipbeg - 4;
+ else
+ amiga_sfx_offset = 0L;
+ }
+ }
+#endif /* HANDLE_AMIGA_SFX */
+ return ZE_OK;
+}
+
+
+/*
+ * readzipfile initializes the global variables that hold the zipfile
+ * directory info and opens the zipfile. For the actual zipfile scan,
+ * the subroutine scanzipf_reg() or scanzipf_fix() is called,
+ * depending on the mode of operation (regular processing, or zipfix mode).
+ */
+int readzipfile()
+/*
+ The name of the zip file is pointed to by the global "zipfile".
+ The globals zipbeg, zfiles, zcount, and zcomlen are initialized.
+ Return an error code in the ZE_ class.
+*/
+{
+ FILE *f; /* zip file */
+ int retval; /* return code */
+ int readable; /* 1 if zipfile exists and is readable */
+
+ /* Initialize zip file info */
+ zipbeg = 0;
+ zfiles = NULL; /* Points to first header */
+ zcount = 0; /* number of files */
+ zcomlen = 0; /* zip file comment length */
+ retval = ZE_OK;
+ f = NULL; /* shut up some compilers */
+
+ /* If zip file exists, read headers and check structure */
+#ifdef VMS
+ if (zipfile == NULL || !(*zipfile) || !strcmp(zipfile, "-"))
+ return ZE_OK;
+ {
+ int rtype;
+
+ if ((VMSmunch(zipfile, GET_RTYPE, (char *)&rtype) == RMS$_NORMAL) &&
+ (rtype == FAT$C_VARIABLE)) {
+ fprintf(stderr,
+ "\n Error: zipfile is in variable-length record format. Please\n\
+ run \"bilf b %s\" to convert the zipfile to fixed-length\n\
+ record format.\n\n", zipfile);
+ return ZE_FORM;
+ }
+ }
+ readable = ((f = fopen(zipfile, FOPR)) != NULL);
+#else /* !VMS */
+ readable = (zipfile != NULL && *zipfile && strcmp(zipfile, "-") &&
+ (f = fopen(zipfile, FOPR)) != NULL);
+#endif /* ?VMS */
+#ifdef MVS
+ /* Very nasty special case for MVS. Just because the zipfile has been
+ * opened for reading does not mean that we can actually read the data.
+ * Typical JCL to create a zipfile is
+ *
+ * //ZIPFILE DD DISP=(NEW,CATLG),DSN=prefix.ZIP,
+ * // SPACE=(CYL,(10,10))
+ *
+ * That creates a VTOC entry with an end of file marker (DS1LSTAR) of zero.
+ * Alas the VTOC end of file marker is only used when the file is opened in
+ * append mode. When a file is opened in read mode, the "other" end of file
+ * marker is used, a zero length data block signals end of file when reading.
+ * With a brand new file which has not been written to yet, it is undefined
+ * what you read off the disk. In fact you read whatever data was in the same
+ * disk tracks before the zipfile was allocated. You would be amazed at the
+ * number of application programmers who still do not understand this. Makes
+ * for interesting and semi-random errors, GIGO.
+ *
+ * Newer versions of SMS will automatically write a zero length block when a
+ * file is allocated. However not all sites run SMS or they run older levels
+ * so we cannot rely on that. The only safe thing to do is close the file,
+ * open in append mode (we already know that the file exists), close it again,
+ * reopen in read mode and try to read a data block. Opening and closing in
+ * append mode will write a zero length block where DS1LSTAR points, making
+ * sure that the VTOC and internal end of file markers are in sync. Then it
+ * is safe to read data. If we cannot read one byte of data after all that,
+ * it is a brand new zipfile and must not be read.
+ */
+ if (readable)
+ {
+ char c;
+ fclose(f);
+ /* append mode */
+ if ((f = fopen(zipfile, "ab")) == NULL) {
+ ZIPERR(ZE_OPEN, zipfile);
+ }
+ fclose(f);
+ /* read mode again */
+ if ((f = fopen(zipfile, FOPR)) == NULL) {
+ ZIPERR(ZE_OPEN, zipfile);
+ }
+ if (fread(&c, 1, 1, f) != 1) {
+ /* no actual data */
+ readable = 0;
+ fclose(f);
+ }
+ else{
+ fseek(f, 0, SEEK_SET); /* at least one byte in zipfile, back to the start */
+ }
+ }
+#endif /* MVS */
+ if (readable)
+ {
+#ifndef UTIL
+ retval = (fix && !adjust) ? scanzipf_fix(f) : scanzipf_reg(f);
+#else
+ retval = scanzipf_reg(f);
+#endif
+
+ /* Done with zip file for now */
+ fclose(f);
+
+ /* If one or more files, sort by name */
+ if (zcount)
+ {
+ struct zlist far * far *x; /* pointer into zsort array */
+ struct zlist far *z; /* pointer into zfiles linked list */
+ extent zl_size = zcount * sizeof(struct zlist far *);
+
+ if (zl_size / sizeof(struct zlist far *) != zcount ||
+ (x = zsort = (struct zlist far **)malloc(zl_size)) == NULL)
+ return ZE_MEM;
+ for (z = zfiles; z != NULL; z = z->nxt)
+ *x++ = z;
+ qsort((char *)zsort, zcount, sizeof(struct zlist far *), zqcmp);
+ }
+ }
+ return retval;
+}
+
+
+int putlocal(z, f)
+struct zlist far *z; /* zip entry to write local header for */
+FILE *f; /* file to write to */
+/* Write a local header described by *z to file *f. Return an error code
+ in the ZE_ class. */
+{
+ PUTLG(LOCSIG, f);
+ PUTSH(z->ver, f);
+ PUTSH(z->lflg, f);
+ PUTSH(z->how, f);
+ PUTLG(z->tim, f);
+ PUTLG(z->crc, f);
+ PUTLG(z->siz, f);
+ PUTLG(z->len, f);
+ PUTSH(z->nam, f);
+ PUTSH(z->ext, f);
+ if (fwrite(z->iname, 1, z->nam, f) != z->nam ||
+ (z->ext && fwrite(z->extra, 1, z->ext, f) != z->ext))
+ return ZE_TEMP;
+ return ZE_OK;
+}
+
+int putextended(z, f)
+struct zlist far *z; /* zip entry to write local header for */
+FILE *f; /* file to write to */
+/* Write an extended local header described by *z to file *f.
+ * Return an error code in the ZE_ class. */
+{
+ PUTLG(EXTLOCSIG, f);
+ PUTLG(z->crc, f);
+ PUTLG(z->siz, f);
+ PUTLG(z->len, f);
+ return ZE_OK;
+}
+
+int putcentral(z, f)
+struct zlist far *z; /* zip entry to write central header for */
+FILE *f; /* file to write to */
+/* Write a central header described by *z to file *f. Return an error code
+ in the ZE_ class. */
+{
+ PUTLG(CENSIG, f);
+ PUTSH(z->vem, f);
+ PUTSH(z->ver, f);
+ PUTSH(z->flg, f);
+ PUTSH(z->how, f);
+ PUTLG(z->tim, f);
+ PUTLG(z->crc, f);
+ PUTLG(z->siz, f);
+ PUTLG(z->len, f);
+ PUTSH(z->nam, f);
+ PUTSH(z->cext, f);
+ PUTSH(z->com, f);
+ PUTSH(z->dsk, f);
+ PUTSH(z->att, f);
+ PUTLG(z->atx, f);
+ PUTLG(z->off, f);
+#ifdef EBCDIC
+ if (z->com)
+ memtoasc(z->comment, z->comment, z->com);
+#endif /* EBCDIC */
+ if (fwrite(z->iname, 1, z->nam, f) != z->nam ||
+ (z->cext && fwrite(z->cextra, 1, z->cext, f) != z->cext) ||
+ (z->com && fwrite(z->comment, 1, z->com, f) != z->com))
+ return ZE_TEMP;
+ return ZE_OK;
+}
+
+
+int putend(n, s, c, m, z, f)
+int n; /* number of entries in central directory */
+ulg s; /* size of central directory */
+ulg c; /* offset of central directory */
+extent m; /* length of zip file comment (0 if none) */
+char *z; /* zip file comment if m != 0 */
+FILE *f; /* file to write to */
+/* Write the end of central directory data to file *f. Return an error code
+ in the ZE_ class. */
+{
+ PUTLG(ENDSIG, f);
+ PUTSH(0, f);
+ PUTSH(0, f);
+ PUTSH(n, f);
+ PUTSH(n, f);
+ PUTLG(s, f);
+ PUTLG(c, f);
+ PUTSH(m, f);
+/* Write the comment, if any */
+#ifdef EBCDIC
+ memtoasc(z, z, m);
+#endif
+ if (m && fwrite(z, 1, m, f) != m)
+ return ZE_TEMP;
+
+#ifdef HANDLE_AMIGA_SFX
+ if (amiga_sfx_offset && zipbeg /* -J zeroes this */) {
+ s = ftell(f);
+ while (s & 3) s++, putc(0, f); /* final marker must be longword aligned */
+ PUTLG(0xF2030000 /* 1010 in Motorola byte order */, f);
+ c = (s - amiga_sfx_offset - 4) / 4; /* size of archive part in longwords */
+ if (fseek(f, amiga_sfx_offset, SEEK_SET) != 0)
+ return ZE_TEMP;
+ c = ((c >> 24) & 0xFF) | ((c >> 8) & 0xFF00)
+ | ((c & 0xFF00) << 8) | ((c & 0xFF) << 24); /* invert byte order */
+ PUTLG(c, f);
+ fseek(f, 0, SEEK_END); /* just in case */
+ }
+#endif
+ return ZE_OK;
+}
+
+
+/* Note: a zip "entry" includes a local header (which includes the file
+ name), an encryption header if encrypting, the compressed data
+ and possibly an extended local header. */
+
+int zipcopy(z, x, y)
+struct zlist far *z; /* zip entry to copy */
+FILE *x, *y; /* source and destination files */
+/* Copy the zip entry described by *z from file *x to file *y. Return an
+ error code in the ZE_ class. Also update tempzn by the number of bytes
+ copied. */
+{
+ ulg n; /* holds local header offset */
+
+ Trace((stderr, "zipcopy %s\n", z->zname));
+ n = (ulg)(4 + LOCHEAD) + (ulg)z->nam + (ulg)z->ext;
+
+ if (fix > 1) {
+ if (fseek(x, z->off + n, SEEK_SET)) /* seek to compressed data */
+ return ferror(x) ? ZE_READ : ZE_EOF;
+
+ if (fix > 2) {
+ /* Update length of entry's name, it may have been changed. This is
+ needed to support the ZipNote ability to rename archive entries. */
+ z->nam = strlen(z->iname);
+ n = (ulg)(4 + LOCHEAD) + (ulg)z->nam + (ulg)z->ext;
+ }
+
+ /* do not trust the old compressed size */
+ if (putlocal(z, y) != ZE_OK)
+ return ZE_TEMP;
+
+ z->off = tempzn;
+ tempzn += n;
+ n = z->siz;
+ } else {
+ if (fseek(x, z->off, SEEK_SET)) /* seek to local header */
+ return ferror(x) ? ZE_READ : ZE_EOF;
+
+ z->off = tempzn;
+ n += z->siz;
+ }
+ /* copy the compressed data and the extended local header if there is one */
+ if (z->lflg & 8) n += 16;
+ tempzn += n;
+ return fcopy(x, y, n);
+}
+
+
+#ifndef UTIL
+
+#ifdef USE_EF_UT_TIME
+
+local int ef_scan_ut_time(ef_buf, ef_len, ef_is_cent, z_utim)
+char *ef_buf; /* buffer containing extra field */
+extent ef_len; /* total length of extra field */
+int ef_is_cent; /* flag indicating "is central extra field" */
+iztimes *z_utim; /* return storage: atime, mtime, ctime */
+/* This function scans the extra field for EF_TIME or EF_IZUNIX blocks
+ * containing Unix style time_t (GMT) values for the entry's access, creation
+ * and modification time.
+ * If a valid block is found, all time stamps are copied to the iztimes
+ * structure.
+ * The presence of an EF_TIME or EF_IZUNIX2 block results in ignoring
+ * all data from probably present obsolete EF_IZUNIX blocks.
+ * If multiple blocks of the same type are found, only the information from
+ * the last block is used.
+ * The return value is the EF_TIME Flags field (simulated in case of an
+ * EF_IZUNIX block) or 0 in case of failure.
+ */
+{
+ int flags = 0;
+ unsigned eb_id;
+ extent eb_len;
+ int have_new_type_eb = FALSE;
+
+ if (ef_len == 0 || ef_buf == NULL)
+ return 0;
+
+ Trace((stderr,"\nef_scan_ut_time: scanning extra field of length %u\n",
+ ef_len));
+ while (ef_len >= EB_HEADSIZE) {
+ eb_id = SH(EB_ID + ef_buf);
+ eb_len = SH(EB_LEN + ef_buf);
+
+ if (eb_len > (ef_len - EB_HEADSIZE)) {
+ /* Discovered some extra field inconsistency! */
+ Trace((stderr,"ef_scan_ut_time: block length %u > rest ef_size %u\n",
+ eb_len, ef_len - EB_HEADSIZE));
+ break;
+ }
+
+ switch (eb_id) {
+ case EF_TIME:
+ flags &= ~0x00ff; /* ignore previous IZUNIX or EF_TIME fields */
+ have_new_type_eb = TRUE;
+ if ( eb_len >= EB_UT_MINLEN && z_utim != NULL) {
+ unsigned eb_idx = EB_UT_TIME1;
+ Trace((stderr,"ef_scan_ut_time: Found TIME extra field\n"));
+ flags |= (ef_buf[EB_HEADSIZE+EB_UT_FLAGS] & 0x00ff);
+ if ((flags & EB_UT_FL_MTIME)) {
+ if ((eb_idx+4) <= eb_len) {
+ z_utim->mtime = LG((EB_HEADSIZE+eb_idx) + ef_buf);
+ eb_idx += 4;
+ Trace((stderr," Unix EF modtime = %ld\n", z_utim->mtime));
+ } else {
+ flags &= ~EB_UT_FL_MTIME;
+ Trace((stderr," Unix EF truncated, no modtime\n"));
+ }
+ }
+ if (ef_is_cent) {
+ break; /* central version of TIME field ends here */
+ }
+ if (flags & EB_UT_FL_ATIME) {
+ if ((eb_idx+4) <= eb_len) {
+ z_utim->atime = LG((EB_HEADSIZE+eb_idx) + ef_buf);
+ eb_idx += 4;
+ Trace((stderr," Unix EF acctime = %ld\n", z_utim->atime));
+ } else {
+ flags &= ~EB_UT_FL_ATIME;
+ }
+ }
+ if (flags & EB_UT_FL_CTIME) {
+ if ((eb_idx+4) <= eb_len) {
+ z_utim->ctime = LG((EB_HEADSIZE+eb_idx) + ef_buf);
+ /* eb_idx += 4; */ /* superfluous for now ... */
+ Trace((stderr," Unix EF cretime = %ld\n", z_utim->ctime));
+ } else {
+ flags &= ~EB_UT_FL_CTIME;
+ }
+ }
+ }
+ break;
+
+ case EF_IZUNIX2:
+ if (!have_new_type_eb) {
+ flags &= ~0x00ff; /* ignore any previous IZUNIX field */
+ have_new_type_eb = TRUE;
+ }
+ break;
+
+ case EF_IZUNIX:
+ if (eb_len >= EB_UX_MINLEN) {
+ Trace((stderr,"ef_scan_ut_time: Found IZUNIX extra field\n"));
+ if (have_new_type_eb) {
+ break; /* Ignore IZUNIX extra field block ! */
+ }
+ z_utim->atime = LG((EB_HEADSIZE+EB_UX_ATIME) + ef_buf);
+ z_utim->mtime = LG((EB_HEADSIZE+EB_UX_MTIME) + ef_buf);
+ Trace((stderr," Unix EF access time = %ld\n",z_utim->atime));
+ Trace((stderr," Unix EF modif. time = %ld\n",z_utim->mtime));
+ flags |= (EB_UT_FL_MTIME | EB_UT_FL_ATIME); /* signal success */
+ }
+ break;
+
+ case EF_THEOS:
+/* printf("Not implemented yet\n"); */
+ break;
+
+ default:
+ break;
+ }
+ /* Skip this extra field block */
+ ef_buf += (eb_len + EB_HEADSIZE);
+ ef_len -= (eb_len + EB_HEADSIZE);
+ }
+
+ return flags;
+}
+
+int get_ef_ut_ztime(z, z_utim)
+struct zlist far *z;
+iztimes *z_utim;
+{
+ int r;
+
+#ifdef IZ_CHECK_TZ
+ if (!zp_tz_is_valid) return 0;
+#endif
+
+ /* First, scan local extra field. */
+ r = ef_scan_ut_time(z->extra, z->ext, FALSE, z_utim);
+
+ /* If this was not successful, try central extra field, but only if
+ it is really different. */
+ if (!r && z->cext > 0 && z->cextra != z->extra)
+ r = ef_scan_ut_time(z->cextra, z->cext, TRUE, z_utim);
+
+ return r;
+}
+
+#endif /* USE_EF_UT_TIME */
+
+
+local void cutpath(p, delim)
+char *p; /* path string */
+int delim; /* path component separator char */
+/* Cut the last path component off the name *p in place.
+ * This should work on both internal and external names.
+ */
+{
+ char *r; /* pointer to last path delimiter */
+
+#ifdef VMS /* change [w.x.y]z to [w.x]y.DIR */
+ if ((r = MBSRCHR(p, ']')) != NULL)
+ {
+ *r = 0;
+ if ((r = MBSRCHR(p, '.')) != NULL)
+ {
+ *r = ']';
+ strcat(r, ".DIR;1"); /* this assumes a little padding--see PAD */
+ } else {
+ *p = 0;
+ }
+ } else {
+ if ((r = MBSRCHR(p, delim)) != NULL)
+ *r = 0;
+ else
+ *p = 0;
+ }
+#else /* !VMS */
+ if ((r = MBSRCHR(p, delim)) != NULL)
+ *r = 0;
+ else
+ *p = 0;
+#endif /* ?VMS */
+}
+
+int trash()
+/* Delete the compressed files and the directories that contained the deleted
+ files, if empty. Return an error code in the ZE_ class. Failure of
+ destroy() or deletedir() is ignored. */
+{
+ extent i; /* counter on deleted names */
+ extent n; /* number of directories to delete */
+ struct zlist far **s; /* table of zip entries to handle, sorted */
+ struct zlist far *z; /* current zip entry */
+
+ /* Delete marked names and count directories */
+ n = 0;
+ for (z = zfiles; z != NULL; z = z->nxt)
+ if (z->mark == 1 || z->trash)
+ {
+ z->mark = 1;
+ if (z->iname[z->nam - 1] != (char)0x2f) { /* don't unlink directory */
+ if (verbose)
+ fprintf(mesg, "zip diagnostic: deleting file %s\n", z->name);
+ if (destroy(z->name)) {
+ zipwarn("error deleting ", z->name);
+ }
+ /* Try to delete all paths that lead up to marked names. This is
+ * necessary only with the -D option.
+ */
+ if (!dirnames) {
+ cutpath(z->name, '/'); /* XXX wrong ??? */
+ cutpath(z->iname, 0x2f); /* 0x2f = ascii['/'] */
+ z->nam = strlen(z->iname);
+ if (z->nam > 0) {
+ z->iname[z->nam - 1] = (char)0x2f;
+ z->iname[z->nam++] = '\0';
+ }
+ if (z->nam > 0) n++;
+ }
+ } else {
+ n++;
+ }
+ }
+
+ /* Construct the list of all marked directories. Some may be duplicated
+ * if -D was used.
+ */
+ if (n)
+ {
+ if ((s = (struct zlist far **)malloc(n*sizeof(struct zlist far *))) ==
+ NULL)
+ return ZE_MEM;
+ n = 0;
+ for (z = zfiles; z != NULL; z = z->nxt) {
+ if (z->mark && z->nam > 0 && z->iname[z->nam - 1] == (char)0x2f /* '/' */
+ && (n == 0 || strcmp(z->name, s[n-1]->name) != 0)) {
+ s[n++] = z;
+ }
+ }
+ /* Sort the files in reverse order to get subdirectories first.
+ * To avoid problems with strange naming conventions as in VMS,
+ * we sort on the internal names, so x/y/z will always be removed
+ * before x/y. On VMS, x/y/z > x/y but [x.y.z] < [x.y]
+ */
+ qsort((char *)s, n, sizeof(struct zlist far *), rqcmp);
+
+ for (i = 0; i < n; i++) {
+ char *p = s[i]->name;
+ if (*p == '\0') continue;
+ if (p[strlen(p) - 1] == '/') { /* keep VMS [x.y]z.dir;1 intact */
+ p[strlen(p) - 1] = '\0';
+ }
+ if (i == 0 || strcmp(s[i]->name, s[i-1]->name) != 0) {
+ if (verbose) {
+ fprintf(mesg, "deleting directory %s (if empty) \n",
+ s[i]->name);
+ }
+ deletedir(s[i]->name);
+ }
+ }
+ free((zvoid *)s);
+ }
+ return ZE_OK;
+}
+
+#endif /* !UTIL */