1/*
2  Copyright (c) 1990-2006 Info-ZIP.  All rights reserved.
3
4  See the accompanying file LICENSE, version 2005-Feb-10 or later
5  (the contents of which are also included in zip.h) for terms of use.
6  If, for some reason, all these files are missing, the Info-ZIP license
7  also may be found at:  ftp://ftp.info-zip.org/pub/infozip/license.html
8*/
9/*
10 *  zipfile.c by Mark Adler.
11 */
12#define __ZIPFILE_C
13
14#include "zip.h"
15#include "revision.h"
16
17#ifdef VMS
18#  include <rms.h>
19#  include "vms/vmsmunch.h"
20#  include "vms/vmsdefs.h"
21#endif
22
23#ifdef __RSXNT__
24#  include <windows.h>
25#endif
26
27/*
28 * XXX start of zipfile.h
29 */
30#ifdef THEOS
31/* Macros cause stack overflow in compiler */
32ush SH(uch* p) { return ((ush)(uch)((p)[0]) | ((ush)(uch)((p)[1]) << 8)); }
33ulg LG(uch* p) { return ((ulg)(SH(p)) | ((ulg)(SH((p)+2)) << 16)); }
34#else /* !THEOS */
35/* Macros for converting integers in little-endian to machine format */
36#define SH(a) ((ush)(((ush)(uch)(a)[0]) | (((ush)(uch)(a)[1]) << 8)))
37#define LG(a) ((ulg)SH(a) | ((ulg)SH((a)+2) << 16))
38#endif /* ?THEOS */
39
40/* Macros for writing machine integers to little-endian format */
41#define PUTSH(a,f) {putc((char)((a) & 0xff),(f)); putc((char)((a) >> 8),(f));}
42#define PUTLG(a,f) {PUTSH((a) & 0xffff,(f)) PUTSH((a) >> 16,(f))}
43
44
45/* -- Structure of a ZIP file -- */
46
47/* Signatures for zip file information headers */
48#define LOCSIG     0x04034b50L
49#define CENSIG     0x02014b50L
50#define ENDSIG     0x06054b50L
51#define EXTLOCSIG  0x08074b50L
52
53/* Offsets of values in headers */
54#define LOCVER  0               /* version needed to extract */
55#define LOCFLG  2               /* encrypt, deflate flags */
56#define LOCHOW  4               /* compression method */
57#define LOCTIM  6               /* last modified file time, DOS format */
58#define LOCDAT  8               /* last modified file date, DOS format */
59#define LOCCRC  10              /* uncompressed crc-32 for file */
60#define LOCSIZ  14              /* compressed size in zip file */
61#define LOCLEN  18              /* uncompressed size */
62#define LOCNAM  22              /* length of filename */
63#define LOCEXT  24              /* length of extra field */
64
65#define EXTCRC  0               /* uncompressed crc-32 for file */
66#define EXTSIZ  4               /* compressed size in zip file */
67#define EXTLEN  8               /* uncompressed size */
68
69#define CENVEM  0               /* version made by */
70#define CENVER  2               /* version needed to extract */
71#define CENFLG  4               /* encrypt, deflate flags */
72#define CENHOW  6               /* compression method */
73#define CENTIM  8               /* last modified file time, DOS format */
74#define CENDAT  10              /* last modified file date, DOS format */
75#define CENCRC  12              /* uncompressed crc-32 for file */
76#define CENSIZ  16              /* compressed size in zip file */
77#define CENLEN  20              /* uncompressed size */
78#define CENNAM  24              /* length of filename */
79#define CENEXT  26              /* length of extra field */
80#define CENCOM  28              /* file comment length */
81#define CENDSK  30              /* disk number start */
82#define CENATT  32              /* internal file attributes */
83#define CENATX  34              /* external file attributes */
84#define CENOFF  38              /* relative offset of local header */
85
86#define ENDDSK  0               /* number of this disk */
87#define ENDBEG  2               /* number of the starting disk */
88#define ENDSUB  4               /* entries on this disk */
89#define ENDTOT  6               /* total number of entries */
90#define ENDSIZ  8               /* size of entire central directory */
91#define ENDOFF  12              /* offset of central on starting disk */
92#define ENDCOM  16              /* length of zip file comment */
93
94
95
96/* Local functions */
97
98local int zqcmp OF((ZCONST zvoid *, ZCONST zvoid *));
99local int scanzipf_reg OF((FILE *f));
100#ifndef UTIL
101   local int rqcmp OF((ZCONST zvoid *, ZCONST zvoid *));
102   local int zbcmp OF((ZCONST zvoid *, ZCONST zvoid far *));
103   local void zipoddities OF((struct zlist far *));
104   local int scanzipf_fix OF((FILE *f));
105#  ifdef USE_EF_UT_TIME
106     local int ef_scan_ut_time OF((char *ef_buf, extent ef_len, int ef_is_cent,
107                                   iztimes *z_utim));
108#  endif /* USE_EF_UT_TIME */
109   local void cutpath OF((char *p, int delim));
110#endif /* !UTIL */
111
112/*
113 * XXX end of zipfile.h
114 */
115
116/* Local data */
117
118#ifdef HANDLE_AMIGA_SFX
119   ulg amiga_sfx_offset;        /* place where size field needs updating */
120#endif
121
122
123local int zqcmp(a, b)
124ZCONST zvoid *a, *b;          /* pointers to pointers to zip entries */
125/* Used by qsort() to compare entries in the zfile list.
126 * Compares the internal names z->iname */
127{
128  return namecmp((*(struct zlist far **)a)->iname,
129                 (*(struct zlist far **)b)->iname);
130}
131
132#ifndef UTIL
133
134local int rqcmp(a, b)
135ZCONST zvoid *a, *b;          /* pointers to pointers to zip entries */
136/* Used by qsort() to compare entries in the zfile list.
137 * Compare the internal names z->iname, but in reverse order. */
138{
139  return namecmp((*(struct zlist far **)b)->iname,
140                 (*(struct zlist far **)a)->iname);
141}
142
143
144local int zbcmp(n, z)
145ZCONST zvoid *n;        /* string to search for */
146ZCONST zvoid far *z;    /* pointer to a pointer to a zip entry */
147/* Used by search() to compare a target to an entry in the zfile list. */
148{
149  return namecmp((char *)n, ((struct zlist far *)z)->zname);
150}
151
152
153struct zlist far *zsearch(n)
154ZCONST char *n;         /* name to find */
155/* Return a pointer to the entry in zfile with the name n, or NULL if
156   not found. */
157{
158  zvoid far **p;        /* result of search() */
159
160  if (zcount &&
161      (p = search(n, (ZCONST zvoid far **)zsort, zcount, zbcmp)) != NULL)
162    return *(struct zlist far **)p;
163  else
164    return NULL;
165}
166
167#endif /* !UTIL */
168
169#ifndef VMS     /* See vms/vms.c for VMS-specific ziptyp(). */
170#  ifndef PATHCUT
171#    define PATHCUT '/'
172#  endif
173
174char *ziptyp(s)
175char *s;                /* file name to force to zip */
176/* If the file name *s has a dot (other than the first char), or if
177   the -A option is used (adjust self-extracting file) then return
178   the name, otherwise append .zip to the name.  Allocate the space for
179   the name in either case.  Return a pointer to the new name, or NULL
180   if malloc() fails. */
181{
182  char *q;              /* temporary pointer */
183  char *t;              /* pointer to malloc'ed string */
184#ifdef THEOS
185  char *r;              /* temporary pointer */
186  char *disk;
187#endif
188
189  if ((t = malloc(strlen(s) + 5)) == NULL)
190    return NULL;
191  strcpy(t, s);
192#ifdef __human68k__
193  _toslash(t);
194#endif
195#ifdef MSDOS
196  for (q = t; *q; INCSTR(q))
197    if (*q == '\\')
198      *q = '/';
199#endif /* MSDOS */
200#ifdef __RSXNT__   /* RSXNT/EMX C rtl uses OEM charset */
201  AnsiToOem(t, t);
202#endif
203  if (adjust) return t;
204#ifndef RISCOS
205# ifndef QDOS
206#  ifdef AMIGA
207  if ((q = MBSRCHR(t, '/')) == NULL)
208    q = MBSRCHR(t, ':');
209  if (MBSRCHR((q ? q + 1 : t), '.') == NULL)
210#  else /* !AMIGA */
211#    ifdef THEOS
212  /* the argument expansion add a dot to the end of file names when
213   * there is no extension and at least one of a argument has wild cards.
214   * So check for at least one character in the extension if there is a dot
215   * in file name */
216  if ((q = MBSRCHR((q = MBSRCHR(t, PATHCUT)) == NULL ? t : q + 1, '.')) == NULL
217    || q[1] == '\0') {
218#    else /* !THEOS */
219#      ifdef TANDEM
220  if (MBSRCHR((q = MBSRCHR(t, '.')) == NULL ? t : q + 1, ' ') == NULL)
221#      else /* !TANDEM */
222  if (MBSRCHR((q = MBSRCHR(t, PATHCUT)) == NULL ? t : q + 1, '.') == NULL)
223#      endif /* ?TANDEM */
224#    endif /* ?THEOS */
225#  endif /* ?AMIGA */
226#  ifdef CMS_MVS
227    if (strncmp(t,"dd:",3) != 0 && strncmp(t,"DD:",3) != 0)
228#  endif /* CMS_MVS */
229#  ifdef THEOS
230    /* insert .zip extension before disk name */
231    if ((r = MBSRCHR(t, ':')) != NULL) {
232        /* save disk name */
233        if ((disk = strdup(r)) == NULL)
234            return NULL;
235        strcpy(r[-1] == '.' ? r - 1 : r, ".zip");
236        strcat(t, disk);
237        free(disk);
238    } else {
239        if (q != NULL && *q == '.')
240          strcpy(q, ".zip");
241        else
242          strcat(t, ".zip");
243    }
244  }
245#  else /* !THEOS */
246#    ifdef TANDEM     /*  Tandem can't cope with extensions */
247    strcat(t, " ZIP");
248#    else /* !TANDEM */
249    strcat(t, ".zip");
250#    endif /* ?TANDEM */
251#  endif /* ?THEOS */
252# else /* QDOS */
253  q = LastDir(t);
254  if(MBSRCHR(q, '_') == NULL && MBSRCHR(q, '.') == NULL)
255  {
256      strcat(t, "_zip");
257  }
258# endif /* QDOS */
259#endif /* !RISCOS */
260  return t;
261}
262#endif /* !VMS */
263
264#ifndef UTIL
265
266local void zipoddities(z)
267struct zlist far *z;
268{
269    if ((z->vem >> 8) >= NUM_HOSTS)
270    {
271        sprintf(errbuf, "made by version %d.%d on system type %d: ",
272                (ush)(z->vem & 0xff) / (ush)10, (ush)(z->vem & 0xff) % (ush)10,
273                z->vem >> 8);
274        zipwarn(errbuf, z->zname);
275    }
276    if (z->ver != 10 && z->ver != 11 && z->ver != 20)
277    {
278        sprintf(errbuf, "needs unzip %d.%d on system type %d: ",
279                (ush)(z->ver & 0xff) / (ush)10,
280                (ush)(z->ver & 0xff) % (ush)10, z->ver >> 8);
281        zipwarn(errbuf, z->zname);
282    }
283    if (z->flg != z->lflg)
284    {
285        sprintf(errbuf, "local flags = 0x%04x, central = 0x%04x: ",
286                z->lflg, z->flg);
287        zipwarn(errbuf, z->zname);
288    }
289    else if (z->flg & ~0xf)
290    {
291        sprintf(errbuf, "undefined bits used in flags = 0x%04x: ", z->flg);
292        zipwarn(errbuf, z->zname);
293    }
294    if (z->how > DEFLATE)
295    {
296        sprintf(errbuf, "unknown compression method %u: ", z->how);
297        zipwarn(errbuf, z->zname);
298    }
299    if (z->dsk)
300    {
301        sprintf(errbuf, "starts on disk %u: ", z->dsk);
302        zipwarn(errbuf, z->zname);
303    }
304    if (z->att!=ASCII && z->att!=BINARY && z->att!=__EBCDIC)
305    {
306        sprintf(errbuf, "unknown internal attributes = 0x%04x: ", z->att);
307        zipwarn(errbuf, z->zname);
308    }
309#if 0
310/* This test is ridiculous, it produces an error message for almost every */
311/* platform of origin other than MS-DOS, Unix, VMS, and Acorn!  Perhaps   */
312/* we could test "if (z->dosflag && z->atx & ~0xffL)", but what for?      */
313    if (((n = z->vem >> 8) != 3) && n != 2 && n != 13 && z->atx & ~0xffL)
314    {
315        sprintf(errbuf, "unknown external attributes = 0x%08lx: ", z->atx);
316        zipwarn(errbuf, z->zname);
317    }
318#endif
319    if (z->ext || z->cext)
320    {
321        if (z->ext && z->cext && z->extra != z->cextra)
322        {
323          sprintf(errbuf,
324                  "local extra (%ld bytes) != central extra (%ld bytes): ",
325                  (ulg)z->ext, (ulg)z->cext);
326          if (noisy) fprintf(stderr, "\tzip info: %s%s\n", errbuf, z->zname);
327        }
328#if (!defined(RISCOS) && !defined(CMS_MVS))
329        /* in noisy mode, extra field sizes are always reported */
330        else if (noisy)
331#else /* RISCOS || CMS_MVS */
332/* avoid warnings for zipfiles created on the same type of OS system! */
333/* or, was this warning really intended (eg. OS/2)? */
334        /* Only give info if extra bytes were added by another system */
335        else if (noisy && ((z->vem >> 8) != (OS_CODE >> 8)))
336#endif /* ?(RISCOS || CMS_MVS) */
337        {
338            fprintf(stderr, "zip info: %s has %ld bytes of %sextra data\n",
339                    z->zname, z->ext ? (ulg)z->ext : (ulg)z->cext,
340                    z->ext ? (z->cext ? "" : "local ") : "central ");
341        }
342    }
343}
344
345/*
346 * scanzipf_fix is called with zip -F or zip -FF
347 * read the file from front to back and pick up the pieces
348 * NOTE: there are still checks missing to see if the header
349 *       that was found is *VALID*
350 */
351local int scanzipf_fix(f)
352  FILE *f;                      /* zip file */
353/*
354   The name of the zip file is pointed to by the global "zipfile".  The globals
355   zipbeg, cenbeg, zfiles, zcount, zcomlen, zcomment, and zsort are filled in.
356   Return an error code in the ZE_ class.
357*/
358{
359    ulg a = 0L;                 /* attributes returned by filetime() */
360    char b[CENHEAD];            /* buffer for central headers */
361    ush flg;                    /* general purpose bit flag */
362    int m;                      /* mismatch flag */
363    extent n;                   /* length of name */
364    ulg p;                      /* current file offset */
365    ulg s;                      /* size of data, start of central */
366    struct zlist far * far *x;  /* pointer last entry's link */
367    struct zlist far *z;        /* current zip entry structure */
368
369    /* Get any file attribute valid for this OS, to set in the central
370     * directory when fixing the archive:
371     */
372#ifndef UTIL
373    filetime(zipfile, &a, (long*)&s, NULL);
374#endif
375    x = &zfiles;                        /* first link */
376    p = 0;                              /* starting file offset */
377#ifdef HANDLE_AMIGA_SFX
378    amiga_sfx_offset = 0L;
379#endif
380
381    /* Find start of zip structures */
382    for (;;) {
383      while ((m = getc(f)) != EOF && m != 0x50)    /* 0x50 == 'P' */
384      {
385#ifdef HANDLE_AMIGA_SFX
386        if (p == 0 && m == 0)
387          amiga_sfx_offset = 1L;
388        else if (amiga_sfx_offset) {
389          if ((p == 1 && m != 0) || (p == 2 && m != 3)
390                                 || (p == 3 && (uch) m != 0xF3))
391            amiga_sfx_offset = 0L;
392        }
393#endif /* HANDLE_AMIGA_SFX */
394        p++;
395      }
396      b[0] = (char) m;
397      if (fread(b+1, 3, 1, f) != 1 || (s = LG(b)) == LOCSIG || s == ENDSIG)
398        break;
399      if (fseek(f, -3L, SEEK_CUR))
400        return ferror(f) ? ZE_READ : ZE_EOF;
401      p++;
402    }
403    zipbeg = p;
404#ifdef HANDLE_AMIGA_SFX
405    if (amiga_sfx_offset && zipbeg >= 12 && (zipbeg & 3) == 0
406        && fseek(f, -12L, SEEK_CUR) == 0 && fread(b, 12, 1, f) == 1
407        && LG(b + 4) == 0xF1030000 /* 1009 in Motorola byte order */)
408      amiga_sfx_offset = zipbeg - 4;
409    else
410      amiga_sfx_offset = 0L;
411#endif /* HANDLE_AMIGA_SFX */
412
413    /* Read local headers */
414    while (LG(b) == LOCSIG)
415    {
416      if ((z = (struct zlist far *)farmalloc(sizeof(struct zlist))) == NULL ||
417          zcount + 1 < zcount)
418        return ZE_MEM;
419      if (fread(b, LOCHEAD, 1, f) != 1) {
420          farfree((zvoid far *)z);
421          break;
422      }
423
424      z->ver = SH(LOCVER + b);
425      z->vem = (ush)(dosify ? 20 : OS_CODE + Z_MAJORVER * 10 + Z_MINORVER);
426      z->dosflag = dosify;
427      flg = z->flg = z->lflg = SH(LOCFLG + b);
428      z->how = SH(LOCHOW + b);
429      z->tim = LG(LOCTIM + b);          /* time and date into one long */
430      z->crc = LG(LOCCRC + b);
431      z->siz = LG(LOCSIZ + b);
432      z->len = LG(LOCLEN + b);
433      n = z->nam = SH(LOCNAM + b);
434      z->cext = z->ext = SH(LOCEXT + b);
435
436      z->com = 0;
437      z->dsk = 0;
438      z->att = 0;
439      z->atx = dosify ? a & 0xff : a;     /* Attributes from filetime() */
440      z->mark = 0;
441      z->trash = 0;
442
443      s = fix > 1 ? 0L : z->siz; /* discard compressed size with -FF */
444
445      /* Initialize all fields pointing to malloced data to NULL */
446      z->zname = z->name = z->iname = z->extra = z->cextra = z->comment = NULL;
447
448      /* Link into list */
449      *x = z;
450      z->nxt = NULL;
451      x = &z->nxt;
452
453      /* Read file name and extra field and skip data */
454      if (n == 0)
455      {
456        sprintf(errbuf, "%lu", (ulg)zcount + 1);
457        zipwarn("zero-length name for entry #", errbuf);
458#ifndef DEBUG
459        return ZE_FORM;
460#endif
461      }
462      if ((z->iname = malloc(n+1)) ==  NULL ||
463          (z->ext && (z->extra = malloc(z->ext)) == NULL))
464        return ZE_MEM;
465      if (fread(z->iname, n, 1, f) != 1 ||
466          (z->ext && fread(z->extra, z->ext, 1, f) != 1) ||
467          (s && fseek(f, (long)s, SEEK_CUR)))
468        return ferror(f) ? ZE_READ : ZE_EOF;
469      /* If there is an extended local header, s is either 0 or
470       * the correct compressed size.
471       */
472      z->iname[n] = '\0';               /* terminate name */
473      z->zname = in2ex(z->iname);       /* convert to external name */
474      if (z->zname == NULL)
475        return ZE_MEM;
476      z->name = z->zname;
477      z->cextra = z->extra;
478      if (noisy) fprintf(mesg, "zip: reading %s\n", z->zname);
479
480      /* Save offset, update for next header */
481      z->off = p;
482      p += 4 + LOCHEAD + n + z->ext + s;
483      zcount++;
484
485      /* Skip extended local header if there is one */
486      if ((flg & 8) != 0) {
487        /* Skip the compressed data if compressed size is unknown.
488         * For safety, we should use the central directory.
489         */
490        if (s == 0) {
491          for (;;) {
492            while ((m = getc(f)) != EOF && m != 0x50) ;  /* 0x50 == 'P' */
493            b[0] = (char) m;
494            if (fread(b+1, 15, 1, f) != 1 || LG(b) == EXTLOCSIG)
495              break;
496            if (fseek(f, -15L, SEEK_CUR))
497              return ferror(f) ? ZE_READ : ZE_EOF;
498          }
499          s = LG(4 + EXTSIZ + b);
500          p += s;
501          if ((ulg) ftell(f) != p+16L) {
502            zipwarn("bad extended local header for ", z->zname);
503            return ZE_FORM;
504          }
505        } else {
506          /* compressed size non-zero, assume that it is valid: */
507          Assert(p == ftell(f), "bad compressed size with extended header");
508
509          if (fseek(f, p, SEEK_SET) || fread(b, 16, 1, f) != 1)
510            return ferror(f) ? ZE_READ : ZE_EOF;
511          if (LG(b) != EXTLOCSIG) {
512            zipwarn("extended local header not found for ", z->zname);
513            return ZE_FORM;
514          }
515        }
516        /* overwrite the unknown values of the local header: */
517
518        /* already in host format */
519        z->crc = LG(4 + EXTCRC + b);
520        z->siz = s;
521        z->len = LG(4 + EXTLEN + b);
522
523        p += 16L;
524      }
525      else if (fix > 1) {
526        /* Don't trust the compressed size */
527        for (;;) {
528          while ((m = getc(f)) != EOF && m != 0x50) p++; /* 0x50 == 'P' */
529          b[0] = (char) m;
530          if (fread(b+1, 3, 1, f) != 1 || (s = LG(b)) == LOCSIG || s == CENSIG)
531            break;
532          if (fseek(f, -3L, SEEK_CUR))
533            return ferror(f) ? ZE_READ : ZE_EOF;
534          p++;
535        }
536        s = p - (z->off + 4 + LOCHEAD + n + z->ext);
537        if (s != z->siz) {
538          fprintf(mesg, " compressed size %ld, actual size %ld for %s\n",
539                  z->siz, s, z->zname);
540          z->siz = s;
541        }
542        /* next LOCSIG already read at this point, don't read it again: */
543        continue;
544      }
545
546      /* Read next signature */
547      if (fread(b, 4, 1, f) != 1)
548          break;
549    }
550
551    s = p;                              /* save start of central */
552
553    if (LG(b) != CENSIG && noisy) {
554      fprintf(mesg, "zip warning: %s %s truncated.\n", zipfile,
555              fix > 1 ? "has been" : "would be");
556
557      if (fix == 1) {
558        fprintf(mesg,
559   "Retry with option -qF to truncate, with -FF to attempt full recovery\n");
560        ZIPERR(ZE_FORM, NULL);
561      }
562    }
563
564    cenbeg = s;
565
566    if (zipbeg && noisy)
567      fprintf(mesg, "%s: adjusting offsets for a preamble of %lu bytes\n",
568              zipfile, zipbeg);
569
570    return ZE_OK;
571}
572
573#endif /* !UTIL */
574
575/*
576 * scanzipf_reg starts searching for the End Signature at the end of the file
577 * The End Signature points to the Central Directory Signature which points
578 * to the Local Directory Signature
579 * XXX probably some more consistency checks are needed
580 */
581local int scanzipf_reg(f)
582  FILE *f;                      /* zip file */
583/*
584   The name of the zip file is pointed to by the global "zipfile".  The globals
585   zipbeg, cenbeg, zfiles, zcount, zcomlen, zcomment, and zsort are filled in.
586   Return an error code in the ZE_ class.
587*/
588{
589    char b[CENHEAD];            /* buffer for central headers */
590    ush flg;                    /* general purpose bit flag */
591    int m;                      /* mismatch flag */
592    extent n;                   /* length of name */
593    struct zlist far * far *x;  /* pointer last entry's link */
594    struct zlist far *z;        /* current zip entry structure */
595    char *t;                    /* temporary pointer */
596    char far *u;                /* temporary variable */
597    int found;
598    char *buf;                  /* temp buffer for reading zipfile */
599    long deltaoff;
600
601    buf = malloc(4096 + 4);
602    if (buf == NULL)
603      return ZE_MEM;
604
605#ifdef HANDLE_AMIGA_SFX
606    amiga_sfx_offset = (fread(buf, 1, 4, f) == 4 && LG(buf) == 0xF3030000);
607    /* == 1 if this file is an Amiga executable (presumably UnZipSFX) */
608#endif
609    found = 0;
610    t = &buf[4096];
611    t[1] = '\0';
612    t[2] = '\0';
613    t[3] = '\0';
614    if (fseek(f, -4096L, SEEK_END) == 0) {
615      zipbeg = (ulg) (ftell(f) + 4096L);
616      while (!found && zipbeg >= 4096) {
617        zipbeg -= 4096L;
618        buf[4096] = t[1];
619        buf[4097] = t[2];
620        buf[4098] = t[3];
621/*
622 * XXX error check ??
623 */
624        fread(buf, 1, 4096, f);
625        fseek(f, -8192L, SEEK_CUR);
626        t = &buf[4095];
627/*
628 * XXX far pointer arithmetic in DOS
629 */
630        while (t >= buf) {
631          /* Check for ENDSIG the End Of Central Directory Record signature
632             ("PK\5\6" in ASCII) */
633          if (LG(t) == ENDSIG) {
634            found = 1;
635/*
636 * XXX error check ??
637 * XXX far pointer arithmetic in DOS
638 */
639            zipbeg += (ulg) (t - buf);
640            fseek(f, (long) zipbeg + 4L, SEEK_SET);
641            break;
642          }
643          --t;
644        }
645      }
646    }
647    else
648       zipbeg = 4096L;
649/*
650 * XXX warn: garbage at the end of the file ignored
651 */
652    if (!found && zipbeg > 0) {
653      size_t s;
654
655      fseek(f, 0L, SEEK_SET);
656      clearerr(f);
657      s = fread(buf, 1, (size_t) zipbeg, f);
658      buf[s] = t[1];
659      buf[s + 1] = t[2];
660      buf[s + 2] = t[3];
661      t = &buf[s - 1];
662/*
663 * XXX far pointer comparison in DOS
664 */
665      while (t >= buf) {
666        /* Check for ENDSIG ("PK\5\6" in ASCII) */
667        if (LG(t) == ENDSIG) {
668          found = 1;
669/*
670 * XXX far pointer arithmetic in DOS
671 */
672          zipbeg = (ulg) (t - buf);
673          fseek(f, (long) zipbeg + 4L, SEEK_SET);
674          break;
675        }
676        --t;
677      }
678    }
679    free(buf);
680    if (!found) {
681      zipwarn("missing end signature--probably not a zip file (did you", "");
682      zipwarn("remember to use binary mode when you transferred it?)", "");
683      return ZE_FORM;
684    }
685
686/*
687 * Read the End Of Central Directory Record
688 */
689    /* Read end header */
690    if (fread(b, ENDHEAD, 1, f) != 1)
691      return ferror(f) ? ZE_READ : ZE_EOF;
692    if (SH(ENDDSK + b) || SH(ENDBEG + b) ||
693        SH(ENDSUB + b) != SH(ENDTOT + b))
694      zipwarn("multiple disk information ignored", "");
695    zcomlen = SH(ENDCOM + b);
696    if (zcomlen)
697    {
698      if ((zcomment = malloc(zcomlen)) == NULL)
699        return ZE_MEM;
700      if (fread(zcomment, zcomlen, 1, f) != 1)
701      {
702        free((zvoid *)zcomment);
703        zcomment = NULL;
704        return ferror(f) ? ZE_READ : ZE_EOF;
705      }
706#ifdef EBCDIC
707      if (zcomment)
708         memtoebc(zcomment, zcomment, zcomlen);
709#endif /* EBCDIC */
710    }
711/*
712 * XXX assumes central header immediately precedes end header
713 */
714    cenbeg = zipbeg - LG(ENDSIZ + b);
715    deltaoff = adjust ? cenbeg - LG(b + ENDOFF) : 0L;
716    if (fseek(f, cenbeg, SEEK_SET) != 0) {
717        perror("fseek");
718        return ZE_FORM; /* XXX */
719    }
720
721    x = &zfiles;                        /* first link */
722
723    if (fread(b, 4, 1, f) != 1)
724      return ferror(f) ? ZE_READ : ZE_EOF;
725
726    while (LG(b) == CENSIG) {
727      /* Read central header. The portion of the central header that should
728         be in common with local header is read raw, for later comparison.
729         (this requires that the offset of ext in the zlist structure
730         be greater than or equal to LOCHEAD) */
731      if (fread(b, CENHEAD, 1, f) != 1)
732        return ferror(f) ? ZE_READ : ZE_EOF;
733      if ((z = (struct zlist far *)farmalloc(sizeof(struct zlist))) == NULL)
734        return ZE_MEM;
735      z->vem = SH(CENVEM + b);
736      for (u = (char far *)(&(z->ver)), n = 0; n < (CENNAM-CENVER); n++)
737        u[n] = b[CENVER + n];
738      z->nam = SH(CENNAM + b);          /* used before comparing cen vs. loc */
739      z->cext = SH(CENEXT + b);         /* may be different from z->ext */
740      z->com = SH(CENCOM + b);
741      z->dsk = SH(CENDSK + b);
742      z->att = SH(CENATT + b);
743      z->atx = LG(CENATX + b);
744      z->off = LG(CENOFF + b) + deltaoff;
745      z->dosflag = (z->vem & 0xff00) == 0;
746
747      /* Initialize all fields pointing to malloced data to NULL */
748      z->zname = z->name = z->iname = z->extra = z->cextra = z->comment = NULL;
749
750      /* Link into list */
751      *x = z;
752      z->nxt = NULL;
753      x = &z->nxt;
754
755      /* Read file name, extra field and comment field */
756      if (z->nam == 0)
757      {
758        sprintf(errbuf, "%lu", (ulg)zcount + 1);
759        zipwarn("zero-length name for entry #", errbuf);
760#ifndef DEBUG
761        farfree((zvoid far *)z);
762        return ZE_FORM;
763#endif
764      }
765      if ((z->iname = malloc(z->nam+1)) ==  NULL ||
766          (z->cext && (z->cextra = malloc(z->cext)) == NULL) ||
767          (z->com && (z->comment = malloc(z->com)) == NULL))
768        return ZE_MEM;
769      if (fread(z->iname, z->nam, 1, f) != 1 ||
770          (z->cext && fread(z->cextra, z->cext, 1, f) != 1) ||
771          (z->com && fread(z->comment, z->com, 1, f) != 1))
772        return ferror(f) ? ZE_READ : ZE_EOF;
773      z->iname[z->nam] = '\0';                  /* terminate name */
774#ifdef EBCDIC
775      if (z->com)
776         memtoebc(z->comment, z->comment, z->com);
777#endif /* EBCDIC */
778      /* Update zipbeg offset, prepare for next header */
779      if (z->off < zipbeg)
780         zipbeg = z->off;
781      zcount++;
782      /* Read next signature */
783      if (fread(b, 4, 1, f) != 1)
784          return ferror(f) ? ZE_READ : ZE_EOF;
785    }
786
787    /* Point to start of header list and read local headers */
788    z = zfiles;
789    while (z != NULL) {
790      /* Read next signature */
791      if (fseek(f, z->off, SEEK_SET) != 0 || fread(b, 4, 1, f) != 1)
792        return ferror(f) ? ZE_READ : ZE_EOF;
793      if (LG(b) == LOCSIG) {
794        if (fread(b, LOCHEAD, 1, f) != 1)
795            return ferror(f) ? ZE_READ : ZE_EOF;
796        z->lflg = SH(LOCFLG + b);
797        n = SH(LOCNAM + b);
798        z->ext = SH(LOCEXT + b);
799
800        /* Compare name and extra fields */
801        if (n != z->nam)
802        {
803#ifdef EBCDIC
804          strtoebc(z->iname, z->iname);
805#endif
806          zipwarn("name lengths in local and central differ for ", z->iname);
807          return ZE_FORM;
808        }
809        if ((t = malloc(z->nam)) == NULL)
810          return ZE_MEM;
811        if (fread(t, z->nam, 1, f) != 1)
812        {
813          free((zvoid *)t);
814          return ferror(f) ? ZE_READ : ZE_EOF;
815        }
816        if (memcmp(t, z->iname, z->nam))
817        {
818          free((zvoid *)t);
819#ifdef EBCDIC
820          strtoebc(z->iname, z->iname);
821#endif
822          zipwarn("names in local and central differ for ", z->iname);
823          return ZE_FORM;
824        }
825        free((zvoid *)t);
826        if (z->ext)
827        {
828          if ((z->extra = malloc(z->ext)) == NULL)
829            return ZE_MEM;
830          if (fread(z->extra, z->ext, 1, f) != 1)
831          {
832            free((zvoid *)(z->extra));
833            return ferror(f) ? ZE_READ : ZE_EOF;
834          }
835          if (z->ext == z->cext && memcmp(z->extra, z->cextra, z->ext) == 0)
836          {
837            free((zvoid *)(z->extra));
838            z->extra = z->cextra;
839          }
840        }
841
842        /* Check extended local header if there is one */
843        if ((z->lflg & 8) != 0)
844        {
845          char buf2[16];
846          ulg s;                        /* size of compressed data */
847
848          s = LG(LOCSIZ + b);
849          if (s == 0)
850            s = LG((CENSIZ-CENVER) + (char far *)(&(z->ver)));
851          if (fseek(f, (z->off + (4+LOCHEAD) + z->nam + z->ext + s), SEEK_SET)
852              || (fread(buf2, 16, 1, f) != 1))
853            return ferror(f) ? ZE_READ : ZE_EOF;
854          if (LG(buf2) != EXTLOCSIG)
855          {
856#ifdef EBCDIC
857            strtoebc(z->iname, z->iname);
858#endif
859            zipwarn("extended local header not found for ", z->iname);
860            return ZE_FORM;
861          }
862          /* overwrite the unknown values of the local header: */
863          for (n = 0; n < 12; n++)
864            b[LOCCRC+n] = buf2[4+n];
865        }
866
867        /* Compare local header with that part of central header (except
868           for the reserved bits in the general purpose flags and except
869           for the already checked entry name length */
870        u = (char far *)(&(z->ver));
871        flg = SH((CENFLG-CENVER) + u);          /* Save central flags word */
872        u[CENFLG-CENVER+1] &= 0x1f;             /* Mask reserved flag bits */
873        b[LOCFLG+1] &= 0x1f;
874        for (m = 0, n = 0; n < LOCNAM; n++)
875          if (b[n] != u[n])
876          {
877            if (!m)
878            {
879              zipwarn("local and central headers differ for ", z->zname);
880              m = 1;
881            }
882            if (noisy)
883            {
884              sprintf(errbuf, " offset %u--local = %02x, central = %02x",
885                      (unsigned)n, (uch)b[n], (uch)u[n]);
886              zipwarn(errbuf, "");
887            }
888          }
889        if (m && !adjust)
890          return ZE_FORM;
891
892        /* Complete the setup of the zlist entry by translating the remaining
893         * central header fields in memory, starting with the fields with
894         * highest offset. This order of the conversion commands takes into
895         * account potential buffer overlaps caused by structure padding.
896         */
897        z->len = LG((CENLEN-CENVER) + u);
898        z->siz = LG((CENSIZ-CENVER) + u);
899        z->crc = LG((CENCRC-CENVER) + u);
900        z->tim = LG((CENTIM-CENVER) + u);   /* time and date into one long */
901        z->how = SH((CENHOW-CENVER) + u);
902        z->flg = flg;                       /* may be different from z->lflg */
903        z->ver = SH((CENVER-CENVER) + u);
904
905        /* Clear actions */
906        z->mark = 0;
907        z->trash = 0;
908#ifdef UTIL
909/* We only need z->iname in the utils */
910        z->name = z->iname;
911#ifdef EBCDIC
912/* z->zname is used for printing and must be coded in native charset */
913        if ((z->zname = malloc(z->nam+1)) ==  NULL)
914          return ZE_MEM;
915        strtoebc(z->zname, z->iname);
916#else
917        z->zname = z->iname;
918#endif
919#else /* !UTIL */
920        z->zname = in2ex(z->iname);       /* convert to external name */
921        if (z->zname == NULL)
922          return ZE_MEM;
923        z->name = z->zname;
924#endif /* ?UTIL */
925      }
926      else {
927#ifdef EBCDIC
928        strtoebc(z->iname, z->iname);
929#endif
930        zipwarn("local header not found for ", z->iname);
931        return ZE_FORM;
932      }
933#ifndef UTIL
934      if (verbose)
935        zipoddities(z);
936#endif
937      z = z->nxt;
938    }
939
940    if (zipbeg && noisy)
941      fprintf(mesg, "%s: %s a preamble of %lu bytes\n",
942              zipfile, adjust ? "adjusting offsets for" : "found", zipbeg);
943
944#ifdef HANDLE_AMIGA_SFX
945    if (zipbeg < 12 || (zipbeg & 3) != 0 /* must be longword aligned */)
946      amiga_sfx_offset = 0;
947    else if (amiga_sfx_offset) {
948      char buf2[16];
949      if (!fseek(f, zipbeg - 12, SEEK_SET) && fread(buf2, 12, 1, f) == 1) {
950        if (LG(buf2 + 4) == 0xF1030000 /* 1009 in Motorola byte order */)
951          /* could also check if LG(buf2) == 0xF2030000... no for now */
952          amiga_sfx_offset = zipbeg - 4;
953        else
954          amiga_sfx_offset = 0L;
955      }
956    }
957#endif /* HANDLE_AMIGA_SFX */
958    return ZE_OK;
959}
960
961
962/*
963 * readzipfile initializes the global variables that hold the zipfile
964 * directory info and opens the zipfile. For the actual zipfile scan,
965 * the subroutine scanzipf_reg() or scanzipf_fix() is called,
966 * depending on the mode of operation (regular processing, or zipfix mode).
967 */
968int readzipfile()
969/*
970   The name of the zip file is pointed to by the global "zipfile".
971   The globals zipbeg, zfiles, zcount, and zcomlen are initialized.
972   Return an error code in the ZE_ class.
973*/
974{
975  FILE *f;              /* zip file */
976  int retval;           /* return code */
977  int readable;         /* 1 if zipfile exists and is readable */
978
979  /* Initialize zip file info */
980  zipbeg = 0;
981  zfiles = NULL;                        /* Points to first header */
982  zcount = 0;                           /* number of files */
983  zcomlen = 0;                          /* zip file comment length */
984  retval = ZE_OK;
985  f = NULL;                             /* shut up some compilers */
986
987  /* If zip file exists, read headers and check structure */
988#ifdef VMS
989  if (zipfile == NULL || !(*zipfile) || !strcmp(zipfile, "-"))
990    return ZE_OK;
991  {
992    int rtype;
993
994    if ((VMSmunch(zipfile, GET_RTYPE, (char *)&rtype) == RMS$_NORMAL) &&
995        (rtype == FAT$C_VARIABLE)) {
996      fprintf(stderr,
997     "\n     Error:  zipfile is in variable-length record format.  Please\n\
998     run \"bilf b %s\" to convert the zipfile to fixed-length\n\
999     record format.\n\n", zipfile);
1000      return ZE_FORM;
1001    }
1002  }
1003  readable = ((f = fopen(zipfile, FOPR)) != NULL);
1004#else /* !VMS */
1005  readable = (zipfile != NULL && *zipfile && strcmp(zipfile, "-") &&
1006              (f = fopen(zipfile, FOPR)) != NULL);
1007#endif /* ?VMS */
1008#ifdef MVS
1009  /* Very nasty special case for MVS.  Just because the zipfile has been
1010   * opened for reading does not mean that we can actually read the data.
1011   * Typical JCL to create a zipfile is
1012   *
1013   * //ZIPFILE  DD  DISP=(NEW,CATLG),DSN=prefix.ZIP,
1014   * //             SPACE=(CYL,(10,10))
1015   *
1016   * That creates a VTOC entry with an end of file marker (DS1LSTAR) of zero.
1017   * Alas the VTOC end of file marker is only used when the file is opened in
1018   * append mode.  When a file is opened in read mode, the "other" end of file
1019   * marker is used, a zero length data block signals end of file when reading.
1020   * With a brand new file which has not been written to yet, it is undefined
1021   * what you read off the disk.  In fact you read whatever data was in the same
1022   * disk tracks before the zipfile was allocated.  You would be amazed at the
1023   * number of application programmers who still do not understand this.  Makes
1024   * for interesting and semi-random errors, GIGO.
1025   *
1026   * Newer versions of SMS will automatically write a zero length block when a
1027   * file is allocated.  However not all sites run SMS or they run older levels
1028   * so we cannot rely on that.  The only safe thing to do is close the file,
1029   * open in append mode (we already know that the file exists), close it again,
1030   * reopen in read mode and try to read a data block.  Opening and closing in
1031   * append mode will write a zero length block where DS1LSTAR points, making
1032   * sure that the VTOC and internal end of file markers are in sync.  Then it
1033   * is safe to read data.  If we cannot read one byte of data after all that,
1034   * it is a brand new zipfile and must not be read.
1035   */
1036  if (readable)
1037  {
1038    char c;
1039    fclose(f);
1040    /* append mode */
1041    if ((f = fopen(zipfile, "ab")) == NULL) {
1042      ZIPERR(ZE_OPEN, zipfile);
1043    }
1044    fclose(f);
1045    /* read mode again */
1046    if ((f = fopen(zipfile, FOPR)) == NULL) {
1047      ZIPERR(ZE_OPEN, zipfile);
1048    }
1049    if (fread(&c, 1, 1, f) != 1) {
1050      /* no actual data */
1051      readable = 0;
1052      fclose(f);
1053    }
1054    else{
1055      fseek(f, 0, SEEK_SET);  /* at least one byte in zipfile, back to the start */
1056    }
1057  }
1058#endif /* MVS */
1059  if (readable)
1060  {
1061#ifndef UTIL
1062    retval = (fix && !adjust) ? scanzipf_fix(f) : scanzipf_reg(f);
1063#else
1064    retval = scanzipf_reg(f);
1065#endif
1066
1067    /* Done with zip file for now */
1068    fclose(f);
1069
1070    /* If one or more files, sort by name */
1071    if (zcount)
1072    {
1073      struct zlist far * far *x;    /* pointer into zsort array */
1074      struct zlist far *z;          /* pointer into zfiles linked list */
1075      extent zl_size = zcount * sizeof(struct zlist far *);
1076
1077      if (zl_size / sizeof(struct zlist far *) != zcount ||
1078          (x = zsort = (struct zlist far **)malloc(zl_size)) == NULL)
1079        return ZE_MEM;
1080      for (z = zfiles; z != NULL; z = z->nxt)
1081        *x++ = z;
1082      qsort((char *)zsort, zcount, sizeof(struct zlist far *), zqcmp);
1083    }
1084  }
1085  return retval;
1086}
1087
1088
1089int putlocal(z, f)
1090struct zlist far *z;    /* zip entry to write local header for */
1091FILE *f;                /* file to write to */
1092/* Write a local header described by *z to file *f.  Return an error code
1093   in the ZE_ class. */
1094{
1095  PUTLG(LOCSIG, f);
1096  PUTSH(z->ver, f);
1097  PUTSH(z->lflg, f);
1098  PUTSH(z->how, f);
1099  PUTLG(z->tim, f);
1100  PUTLG(z->crc, f);
1101  PUTLG(z->siz, f);
1102  PUTLG(z->len, f);
1103  PUTSH(z->nam, f);
1104  PUTSH(z->ext, f);
1105  if (fwrite(z->iname, 1, z->nam, f) != z->nam ||
1106      (z->ext && fwrite(z->extra, 1, z->ext, f) != z->ext))
1107    return ZE_TEMP;
1108  return ZE_OK;
1109}
1110
1111int putextended(z, f)
1112struct zlist far *z;    /* zip entry to write local header for */
1113FILE *f;                /* file to write to */
1114/* Write an extended local header described by *z to file *f.
1115 * Return an error code in the ZE_ class. */
1116{
1117  PUTLG(EXTLOCSIG, f);
1118  PUTLG(z->crc, f);
1119  PUTLG(z->siz, f);
1120  PUTLG(z->len, f);
1121  return ZE_OK;
1122}
1123
1124int putcentral(z, f)
1125struct zlist far *z;    /* zip entry to write central header for */
1126FILE *f;                /* file to write to */
1127/* Write a central header described by *z to file *f.  Return an error code
1128   in the ZE_ class. */
1129{
1130  PUTLG(CENSIG, f);
1131  PUTSH(z->vem, f);
1132  PUTSH(z->ver, f);
1133  PUTSH(z->flg, f);
1134  PUTSH(z->how, f);
1135  PUTLG(z->tim, f);
1136  PUTLG(z->crc, f);
1137  PUTLG(z->siz, f);
1138  PUTLG(z->len, f);
1139  PUTSH(z->nam, f);
1140  PUTSH(z->cext, f);
1141  PUTSH(z->com, f);
1142  PUTSH(z->dsk, f);
1143  PUTSH(z->att, f);
1144  PUTLG(z->atx, f);
1145  PUTLG(z->off, f);
1146#ifdef EBCDIC
1147  if (z->com)
1148    memtoasc(z->comment, z->comment, z->com);
1149#endif /* EBCDIC */
1150  if (fwrite(z->iname, 1, z->nam, f) != z->nam ||
1151      (z->cext && fwrite(z->cextra, 1, z->cext, f) != z->cext) ||
1152      (z->com && fwrite(z->comment, 1, z->com, f) != z->com))
1153    return ZE_TEMP;
1154  return ZE_OK;
1155}
1156
1157
1158int putend(n, s, c, m, z, f)
1159unsigned n;             /* number of entries in central directory */
1160ulg s;                  /* size of central directory */
1161ulg c;                  /* offset of central directory */
1162extent m;               /* length of zip file comment (0 if none) */
1163char *z;                /* zip file comment if m != 0 */
1164FILE *f;                /* file to write to */
1165/* Write the end of central directory data to file *f.  Return an error code
1166   in the ZE_ class. */
1167{
1168  PUTLG(ENDSIG, f);
1169  PUTSH(0, f);
1170  PUTSH(0, f);
1171  PUTSH(n, f);
1172  PUTSH(n, f);
1173  PUTLG(s, f);
1174  PUTLG(c, f);
1175  PUTSH(m, f);
1176/* Write the comment, if any */
1177#ifdef EBCDIC
1178  memtoasc(z, z, m);
1179#endif
1180  if (m && fwrite(z, 1, m, f) != m)
1181    return ZE_TEMP;
1182
1183#ifdef HANDLE_AMIGA_SFX
1184  if (amiga_sfx_offset && zipbeg /* -J zeroes this */) {
1185    s = ftell(f);
1186    while (s & 3) s++, putc(0, f);   /* final marker must be longword aligned */
1187    PUTLG(0xF2030000 /* 1010 in Motorola byte order */, f);
1188    c = (s - amiga_sfx_offset - 4) / 4;  /* size of archive part in longwords */
1189    if (fseek(f, amiga_sfx_offset, SEEK_SET) != 0)
1190      return ZE_TEMP;
1191    c = ((c >> 24) & 0xFF) | ((c >> 8) & 0xFF00)
1192         | ((c & 0xFF00) << 8) | ((c & 0xFF) << 24);     /* invert byte order */
1193    PUTLG(c, f);
1194    fseek(f, 0, SEEK_END);                                    /* just in case */
1195  }
1196#endif
1197  return ZE_OK;
1198}
1199
1200
1201/* Note: a zip "entry" includes a local header (which includes the file
1202   name), an encryption header if encrypting, the compressed data
1203   and possibly an extended local header. */
1204
1205int zipcopy(z, x, y)
1206struct zlist far *z;    /* zip entry to copy */
1207FILE *x, *y;            /* source and destination files */
1208/* Copy the zip entry described by *z from file *x to file *y.  Return an
1209   error code in the ZE_ class.  Also update tempzn by the number of bytes
1210   copied. */
1211{
1212  ulg n;                /* holds local header offset */
1213
1214  Trace((stderr, "zipcopy %s\n", z->zname));
1215  n = (ulg)(4 + LOCHEAD) + (ulg)z->nam + (ulg)z->ext;
1216
1217  if (fix > 1) {
1218    if (fseek(x, z->off + n, SEEK_SET)) /* seek to compressed data */
1219      return ferror(x) ? ZE_READ : ZE_EOF;
1220
1221    if (fix > 2) {
1222      /* Update length of entry's name, it may have been changed.  This is
1223         needed to support the ZipNote ability to rename archive entries. */
1224      z->nam = strlen(z->iname);
1225      n = (ulg)(4 + LOCHEAD) + (ulg)z->nam + (ulg)z->ext;
1226    }
1227
1228    /* do not trust the old compressed size */
1229    if (putlocal(z, y) != ZE_OK)
1230      return ZE_TEMP;
1231
1232    z->off = tempzn;
1233    tempzn += n;
1234    n = z->siz;
1235  } else {
1236    if (fseek(x, z->off, SEEK_SET))     /* seek to local header */
1237      return ferror(x) ? ZE_READ : ZE_EOF;
1238
1239    z->off = tempzn;
1240    n += z->siz;
1241  }
1242  /* copy the compressed data and the extended local header if there is one */
1243  if (z->lflg & 8) n += 16;
1244  tempzn += n;
1245  return fcopy(x, y, n);
1246}
1247
1248
1249#ifndef UTIL
1250
1251#ifdef USE_EF_UT_TIME
1252
1253local int ef_scan_ut_time(ef_buf, ef_len, ef_is_cent, z_utim)
1254char *ef_buf;                   /* buffer containing extra field */
1255extent ef_len;                  /* total length of extra field */
1256int ef_is_cent;                 /* flag indicating "is central extra field" */
1257iztimes *z_utim;                /* return storage: atime, mtime, ctime */
1258/* This function scans the extra field for EF_TIME or EF_IZUNIX blocks
1259 * containing Unix style time_t (GMT) values for the entry's access, creation
1260 * and modification time.
1261 * If a valid block is found, all time stamps are copied to the iztimes
1262 * structure.
1263 * The presence of an EF_TIME or EF_IZUNIX2 block results in ignoring
1264 * all data from probably present obsolete EF_IZUNIX blocks.
1265 * If multiple blocks of the same type are found, only the information from
1266 * the last block is used.
1267 * The return value is the EF_TIME Flags field (simulated in case of an
1268 * EF_IZUNIX block) or 0 in case of failure.
1269 */
1270{
1271  int flags = 0;
1272  unsigned eb_id;
1273  extent eb_len;
1274  int have_new_type_eb = FALSE;
1275
1276  if (ef_len == 0 || ef_buf == NULL)
1277    return 0;
1278
1279  Trace((stderr,"\nef_scan_ut_time: scanning extra field of length %u\n",
1280         ef_len));
1281  while (ef_len >= EB_HEADSIZE) {
1282    eb_id = SH(EB_ID + ef_buf);
1283    eb_len = SH(EB_LEN + ef_buf);
1284
1285    if (eb_len > (ef_len - EB_HEADSIZE)) {
1286      /* Discovered some extra field inconsistency! */
1287      Trace((stderr,"ef_scan_ut_time: block length %u > rest ef_size %u\n",
1288             eb_len, ef_len - EB_HEADSIZE));
1289      break;
1290    }
1291
1292    switch (eb_id) {
1293      case EF_TIME:
1294        flags &= ~0x00ff;       /* ignore previous IZUNIX or EF_TIME fields */
1295        have_new_type_eb = TRUE;
1296        if ( eb_len >= EB_UT_MINLEN && z_utim != NULL) {
1297           unsigned eb_idx = EB_UT_TIME1;
1298           Trace((stderr,"ef_scan_ut_time: Found TIME extra field\n"));
1299           flags |= (ef_buf[EB_HEADSIZE+EB_UT_FLAGS] & 0x00ff);
1300           if ((flags & EB_UT_FL_MTIME)) {
1301              if ((eb_idx+4) <= eb_len) {
1302                 z_utim->mtime = LG((EB_HEADSIZE+eb_idx) + ef_buf);
1303                 eb_idx += 4;
1304                 Trace((stderr,"  Unix EF modtime = %ld\n", z_utim->mtime));
1305              } else {
1306                 flags &= ~EB_UT_FL_MTIME;
1307                 Trace((stderr,"  Unix EF truncated, no modtime\n"));
1308              }
1309           }
1310           if (ef_is_cent) {
1311              break;            /* central version of TIME field ends here */
1312           }
1313           if (flags & EB_UT_FL_ATIME) {
1314              if ((eb_idx+4) <= eb_len) {
1315                 z_utim->atime = LG((EB_HEADSIZE+eb_idx) + ef_buf);
1316                 eb_idx += 4;
1317                 Trace((stderr,"  Unix EF acctime = %ld\n", z_utim->atime));
1318              } else {
1319                 flags &= ~EB_UT_FL_ATIME;
1320              }
1321           }
1322           if (flags & EB_UT_FL_CTIME) {
1323              if ((eb_idx+4) <= eb_len) {
1324                 z_utim->ctime = LG((EB_HEADSIZE+eb_idx) + ef_buf);
1325                 /* eb_idx += 4; */  /* superfluous for now ... */
1326                 Trace((stderr,"  Unix EF cretime = %ld\n", z_utim->ctime));
1327              } else {
1328                 flags &= ~EB_UT_FL_CTIME;
1329              }
1330           }
1331        }
1332        break;
1333
1334      case EF_IZUNIX2:
1335        if (!have_new_type_eb) {
1336           flags &= ~0x00ff;    /* ignore any previous IZUNIX field */
1337           have_new_type_eb = TRUE;
1338        }
1339        break;
1340
1341      case EF_IZUNIX:
1342        if (eb_len >= EB_UX_MINLEN) {
1343           Trace((stderr,"ef_scan_ut_time: Found IZUNIX extra field\n"));
1344           if (have_new_type_eb) {
1345              break;            /* Ignore IZUNIX extra field block ! */
1346           }
1347           z_utim->atime = LG((EB_HEADSIZE+EB_UX_ATIME) + ef_buf);
1348           z_utim->mtime = LG((EB_HEADSIZE+EB_UX_MTIME) + ef_buf);
1349           Trace((stderr,"  Unix EF access time = %ld\n",z_utim->atime));
1350           Trace((stderr,"  Unix EF modif. time = %ld\n",z_utim->mtime));
1351           flags |= (EB_UT_FL_MTIME | EB_UT_FL_ATIME);  /* signal success */
1352        }
1353        break;
1354
1355      case EF_THEOS:
1356/*      printf("Not implemented yet\n"); */
1357        break;
1358
1359      default:
1360        break;
1361    }
1362    /* Skip this extra field block */
1363    ef_buf += (eb_len + EB_HEADSIZE);
1364    ef_len -= (eb_len + EB_HEADSIZE);
1365  }
1366
1367  return flags;
1368}
1369
1370int get_ef_ut_ztime(z, z_utim)
1371struct zlist far *z;
1372iztimes *z_utim;
1373{
1374  int r;
1375
1376#ifdef IZ_CHECK_TZ
1377  if (!zp_tz_is_valid) return 0;
1378#endif
1379
1380  /* First, scan local extra field. */
1381  r = ef_scan_ut_time(z->extra, z->ext, FALSE, z_utim);
1382
1383  /* If this was not successful, try central extra field, but only if
1384     it is really different. */
1385  if (!r && z->cext > 0 && z->cextra != z->extra)
1386    r = ef_scan_ut_time(z->cextra, z->cext, TRUE, z_utim);
1387
1388  return r;
1389}
1390
1391#endif /* USE_EF_UT_TIME */
1392
1393
1394local void cutpath(p, delim)
1395char *p;                /* path string */
1396int delim;              /* path component separator char */
1397/* Cut the last path component off the name *p in place.
1398 * This should work on both internal and external names.
1399 */
1400{
1401  char *r;              /* pointer to last path delimiter */
1402
1403#ifdef VMS                      /* change [w.x.y]z to [w.x]y.DIR */
1404  if ((r = MBSRCHR(p, ']')) != NULL)
1405  {
1406    *r = 0;
1407    if ((r = MBSRCHR(p, '.')) != NULL)
1408    {
1409      *r = ']';
1410      strcat(r, ".DIR;1");     /* this assumes a little padding--see PAD */
1411    } else {
1412      *p = 0;
1413    }
1414  } else {
1415    if ((r = MBSRCHR(p, delim)) != NULL)
1416      *r = 0;
1417    else
1418      *p = 0;
1419  }
1420#else /* !VMS */
1421  if ((r = MBSRCHR(p, delim)) != NULL)
1422    *r = 0;
1423  else
1424    *p = 0;
1425#endif /* ?VMS */
1426}
1427
1428int trash()
1429/* Delete the compressed files and the directories that contained the deleted
1430   files, if empty.  Return an error code in the ZE_ class.  Failure of
1431   destroy() or deletedir() is ignored. */
1432{
1433  extent i;             /* counter on deleted names */
1434  extent n;             /* number of directories to delete */
1435  struct zlist far **s; /* table of zip entries to handle, sorted */
1436  struct zlist far *z;  /* current zip entry */
1437
1438  /* Delete marked names and count directories */
1439  n = 0;
1440  for (z = zfiles; z != NULL; z = z->nxt)
1441    if (z->mark == 1 || z->trash)
1442    {
1443      z->mark = 1;
1444      if (z->iname[z->nam - 1] != (char)0x2f) { /* don't unlink directory */
1445        if (verbose)
1446          fprintf(mesg, "zip diagnostic: deleting file %s\n", z->name);
1447        if (destroy(z->name)) {
1448          zipwarn("error deleting ", z->name);
1449        }
1450        /* Try to delete all paths that lead up to marked names. This is
1451         * necessary only with the -D option.
1452         */
1453        if (!dirnames) {
1454          cutpath(z->name, '/');  /* XXX wrong ??? */
1455          cutpath(z->iname, 0x2f); /* 0x2f = ascii['/'] */
1456          z->nam = strlen(z->iname);
1457          if (z->nam > 0) {
1458            z->iname[z->nam - 1] = (char)0x2f;
1459            z->iname[z->nam++] = '\0';
1460          }
1461          if (z->nam > 0) n++;
1462        }
1463      } else {
1464        n++;
1465      }
1466    }
1467
1468  /* Construct the list of all marked directories. Some may be duplicated
1469   * if -D was used.
1470   */
1471  if (n)
1472  {
1473    if ((s = (struct zlist far **)malloc(n*sizeof(struct zlist far *))) ==
1474        NULL)
1475      return ZE_MEM;
1476    n = 0;
1477    for (z = zfiles; z != NULL; z = z->nxt) {
1478      if (z->mark && z->nam > 0 && z->iname[z->nam - 1] == (char)0x2f /* '/' */
1479          && (n == 0 || strcmp(z->name, s[n-1]->name) != 0)) {
1480        s[n++] = z;
1481      }
1482    }
1483    /* Sort the files in reverse order to get subdirectories first.
1484     * To avoid problems with strange naming conventions as in VMS,
1485     * we sort on the internal names, so x/y/z will always be removed
1486     * before x/y. On VMS, x/y/z > x/y but [x.y.z] < [x.y]
1487     */
1488    qsort((char *)s, n, sizeof(struct zlist far *), rqcmp);
1489
1490    for (i = 0; i < n; i++) {
1491      char *p = s[i]->name;
1492      if (*p == '\0') continue;
1493      if (p[strlen(p) - 1] == '/') { /* keep VMS [x.y]z.dir;1 intact */
1494        p[strlen(p) - 1] = '\0';
1495      }
1496      if (i == 0 || strcmp(s[i]->name, s[i-1]->name) != 0) {
1497        if (verbose) {
1498          fprintf(mesg, "deleting directory %s (if empty)                \n",
1499                  s[i]->name);
1500        }
1501        deletedir(s[i]->name);
1502      }
1503    }
1504    free((zvoid *)s);
1505  }
1506  return ZE_OK;
1507}
1508
1509#endif /* !UTIL */
1510