1/*
2  Copyright (c) 1990-2005 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#include "zip.h"
10
11#ifndef UTIL    /* little or no material in this file is used by UTIL */
12
13#include <dos.h>
14#include <time.h>
15
16
17#if defined(__GO32__) || defined(__TURBOC__)
18#  include <dir.h> /* prototypes of find*() */
19   typedef struct ffblk   ff_dir;
20#  define FATTR (hidden_files ? FA_HIDDEN+FA_SYSTEM+FA_DIREC : FA_DIREC)
21#  define FFIRST(n,d,a)   findfirst(n,(struct ffblk *)d,a)
22#  define FNEXT(d)        findnext((struct ffblk *)d)
23#  if (defined(__TURBOC__) || (defined(__DJGPP__) && (__DJGPP__ >=2)))
24#    if (defined(__DJGPP__) && (__DJGPP__ == 2) && (__DJGPP_MINOR__ == 0))
25#      include <libc/dosio.h>
26#    endif
27#    define GetFileMode(name) _chmod(name, 0)
28#    define SetFileMode(name, attr) _chmod(name, 1, attr)
29#  else /* DJGPP v1.x */
30#    define GetFileMode(name) bdosptr(0x43, (name), 0)
31#  endif
32#endif /* __GO32__ || __TURBOC__ */
33
34#if defined(MSC) || defined(__WATCOMC__)
35   typedef struct find_t  ff_dir;
36#  define FATTR (hidden_files ? _A_HIDDEN+_A_SYSTEM+_A_SUBDIR : _A_SUBDIR)
37#  ifndef FA_LABEL
38#    define FA_LABEL      _A_VOLID
39#  endif
40#  define FFIRST(n,d,a)   _dos_findfirst(n,a,(struct find_t *)d)
41#  define FNEXT(d)        _dos_findnext((struct find_t *)d)
42#  define ff_name         name
43#  define ff_fdate        wr_date
44#  define ff_ftime        wr_time
45#  define ff_attrib       attrib
46#endif /* MSC || __WATCOMC__ */
47
48#ifdef __EMX__
49#  ifdef EMX_OBSOLETE           /* emx 0.9b or earlier */
50#    define size_t xxx_size_t
51#    define wchar_t xxx_wchar_t
52#    define tm xxx_tm
53#    include <sys/emx.h>
54#    undef size_t
55#    undef wchar_t
56#    undef tm
57#  else /* !EMX_OBSOLETE */     /* emx 0.9c or newer */
58#    include <emx/syscalls.h>
59#  endif /* ?EMX_OBSOLETE */
60   typedef struct _find   ff_dir;
61#  define FATTR (hidden_files ? _A_HIDDEN+_A_SYSTEM+_A_SUBDIR : _A_SUBDIR)
62#  define FA_LABEL        _A_VOLID
63#  define FFIRST(n,d,a)   __findfirst(n,a,d)
64#  define FNEXT(d)        __findnext(d)
65#  define ff_name         name
66#  define ff_fdate        date
67#  define ff_ftime        time
68#  define ff_attrib       attr
69#  define GetFileMode(name) __chmod(name, 0, 0)
70#  define SetFileMode(name, attr) __chmod(name, 1, attr)
71#endif /* __EMX__ */
72
73#ifndef SetFileMode
74#  define SetFileMode(name, attr) _dos_setfileattr(name, attr)
75#endif
76
77
78#define PAD  0
79#define PATH_END '/'
80
81/* Library functions not in (most) header files */
82int rmdir OF((const char *));
83int utime OF((char *, ztimbuf *));
84
85/* Local functions */
86#ifndef GetFileMode
87int GetFileMode OF((char *name));
88#endif /* !GetFileMode */
89
90local int  initDirSearch OF((char *name, ff_dir *ff_context_p));
91local char *getVolumeLabel OF((int, ulg *, ulg *, time_t *));
92local int  wild_recurse OF((char *, char *));
93local int procname_dos OF((char *n, int caseflag, unsigned attribs));
94local int is_running_on_windows OF((void));
95
96#define MSDOS_INVALID_ATTR      0xFF
97#define getDirEntryAttr(d)      ((d)->ff_attrib)
98
99/* Module level variables */
100extern char *label;
101local ulg label_time = 0;
102local ulg label_mode = 0;
103local time_t label_utim = 0;
104
105/* Module level constants */
106local ZCONST char wild_match_all[] = "*.*";
107
108
109#ifndef GetFileMode
110int GetFileMode(char *name)
111{
112  unsigned int attr = 0;
113  return (_dos_getfileattr(name, &attr) ? -1 : attr);
114}
115#endif /* !GetFileMode */
116
117local int initDirSearch(name, ff_context_p)
118  char *name;                   /* name of directory to scan */
119  ff_dir *ff_context_p;         /* pointer to FFIRST/FNEXT context structure */
120{
121  int r;                        /* FFIRST return value */
122  char *p, *q;                  /* temporary copy of name, and aux pointer */
123
124  if ((p = malloc(strlen(name) + (2 + sizeof(wild_match_all)))) == NULL)
125    return ZE_MEM;
126
127  strcpy(p, name);
128  q = p + strlen(p);
129  if (q[-1] == ':')
130    *q++ = '.';
131  if ((q - p) > 0 && *(q - 1) != '/')
132    *q++ = '/';
133  strcpy(q, wild_match_all);
134  r = FFIRST(p, ff_context_p, FATTR);
135  free((zvoid *)p);
136
137  return (r ? ZE_MISS : ZE_OK);
138}
139
140local char *getVolumeLabel(drive, vtime, vmode, vutim)
141  int drive;    /* drive name: 'A' .. 'Z' or '\0' for current drive */
142  ulg *vtime;   /* volume label creation time (DOS format) */
143  ulg *vmode;   /* volume label file mode */
144  time_t *vutim;/* volume label creation time (UNIX format) */
145
146/* If a volume label exists for the given drive, return its name and
147   set its time and mode. The returned name must be static data. */
148{
149  static char vol[14];
150  ff_dir d;
151  char *p;
152
153  if (drive) {
154    vol[0] = (char)drive;
155    strcpy(vol+1, ":/");
156  } else {
157    strcpy(vol, "/");
158  }
159  strcat(vol, wild_match_all);
160  if (FFIRST(vol, &d, FA_LABEL) == 0) {
161    strncpy(vol, d.ff_name, sizeof(vol)-1);
162    vol[sizeof(vol)-1] = '\0';   /* just in case */
163    if ((p = strchr(vol, '.')) != NULL) /* remove dot, though PKZIP doesn't */
164      strcpy(p, p + 1);
165    *vtime = ((ulg)d.ff_fdate << 16) | ((ulg)d.ff_ftime & 0xffff);
166    *vmode = (ulg)d.ff_attrib;
167    *vutim = dos2unixtime(*vtime);
168    return vol;
169  }
170  return NULL;
171}
172
173
174#ifdef MSDOS16
175#define ONENAMELEN 12   /* no 16-bit compilers supports LFN */
176#else
177#define ONENAMELEN 255
178#endif
179
180/* whole is a pathname with wildcards, wildtail points somewhere in the  */
181/* middle of it.  All wildcards to be expanded must come AFTER wildtail. */
182
183local int wild_recurse(whole, wildtail)
184char *whole;
185char *wildtail;
186{
187    ff_dir dir;
188    char *subwild, *name, *newwhole = NULL, *glue = NULL, plug = 0, plug2;
189    ush newlen, amatch = 0;
190    int e = ZE_MISS;
191
192    if (!isshexp(wildtail)) {
193        struct stat s;                          /* dummy buffer for stat() */
194
195        if (!LSSTAT(whole, &s))                 /* file exists ? */
196            return procname(whole, 0);
197        else
198            return ZE_MISS;                     /* woops, no wildcards! */
199    }
200
201    /* back up thru path components till existing dir found */
202    do {
203        name = wildtail + strlen(wildtail) - 1;
204        for (;;)
205            if (name-- <= wildtail || *name == PATH_END) {
206                subwild = name + 1;
207                plug2 = *subwild;
208                *subwild = 0;
209                break;
210            }
211        if (glue)
212            *glue = plug;
213        glue = subwild;
214        plug = plug2;
215        e = initDirSearch(whole, &dir);
216    } while (e == ZE_MISS && subwild > wildtail);
217    wildtail = subwild;                 /* skip past non-wild components */
218    if (e != ZE_OK) {
219        if (glue)
220            *glue = plug;
221        goto ohforgetit;
222    }
223    subwild = strchr(wildtail + 1, PATH_END);
224    /* this "+ 1" dodges the  ^^^ hole left by *glue == 0 */
225    if (subwild != NULL) {
226        *(subwild++) = 0;               /* wildtail = one component pattern */
227        newlen = strlen(whole) + strlen(subwild) + (ONENAMELEN + 2);
228    } else
229        newlen = strlen(whole) + (ONENAMELEN + 1);
230    if ((newwhole = malloc(newlen)) == NULL) {
231        if (glue)
232            *glue = plug;
233        e = ZE_MEM;
234        goto ohforgetit;
235    }
236    strcpy(newwhole, whole);
237    newlen = strlen(newwhole);
238    if (glue)
239        *glue = plug;                           /* repair damage to whole */
240    if (!isshexp(wildtail)) {
241        e = ZE_MISS;                            /* non-wild name not found */
242        goto ohforgetit;
243    }
244
245    do {
246        if (strcmp(dir.ff_name, ".") && strcmp(dir.ff_name, "..")
247                                  && MATCH(wildtail, dir.ff_name, 0)) {
248            strcpy(newwhole + newlen, dir.ff_name);
249            if (subwild) {
250                name = newwhole + strlen(newwhole);
251                *(name++) = PATH_END;
252                strcpy(name, subwild);
253                e = wild_recurse(newwhole, name);
254            } else
255                e = procname_dos(newwhole, 0, getDirEntryAttr(&dir));
256            newwhole[newlen] = 0;
257            if (e == ZE_OK)
258                amatch = 1;
259            else if (e != ZE_MISS)
260                break;
261        }
262    } while (FNEXT(&dir) == 0);
263
264  ohforgetit:
265    if (subwild)
266        *--subwild = PATH_END;
267    if (newwhole)
268        free(newwhole);
269    if (e == ZE_MISS && amatch)
270        e = ZE_OK;
271    return e;
272}
273
274int wild(w)
275char *w;                /* path/pattern to match */
276/* If not in exclude mode, expand the pattern based on the contents of the
277   file system.  Return an error code in the ZE_ class. */
278{
279    char *p;            /* path */
280    char *q;            /* diskless path */
281    int e;              /* result */
282
283    if (volume_label == 1) {
284      volume_label = 2;
285      label = getVolumeLabel((w != NULL && w[1] == ':') ? to_up(w[0]) : '\0',
286                             &label_time, &label_mode, &label_utim);
287      if (label != NULL)
288        (void)newname(label, 0, 0);
289      if (w == NULL || (w[1] == ':' && w[2] == '\0')) return ZE_OK;
290      /* "zip -$ foo a:" can be used to force drive name */
291    }
292    /* special handling of stdin request */
293    if (strcmp(w, "-") == 0)   /* if compressing stdin */
294        return newname(w, 0, 0);
295
296    /* Allocate and copy pattern, leaving room to add "." if needed */
297    if ((p = malloc(strlen(w) + 2)) == NULL)
298        return ZE_MEM;
299    strcpy(p, w);
300
301    /* Normalize path delimiter as '/' */
302    for (q = p; *q; q++)                  /* use / consistently */
303        if (*q == '\\')
304            *q = '/';
305
306    /* Separate the disk part of the path */
307    q = strchr(p, ':');
308    if (q != NULL) {
309        if (strchr(++q, ':'))     /* sanity check for safety of wild_recurse */
310            return ZE_MISS;
311    } else
312        q = p;
313
314    /* Normalize bare disk names */
315    if (q > p && !*q)
316        strcpy(q, ".");
317
318    /* Here we go */
319    e = wild_recurse(p, q);
320    free((zvoid *)p);
321    return e;
322}
323
324local int procname_dos(n, caseflag, attribs)
325char *n;                /* name to process */
326int caseflag;           /* true to force case-sensitive match */
327unsigned attribs;       /* file attributes, if available */
328/* Process a name or sh expression to operate on (or exclude).  Return
329   an error code in the ZE_ class. */
330{
331  char *a;              /* path and name for recursion */
332  ff_dir *d;            /* control structure for FFIRST/FNEXT */
333  char *e;              /* pointer to name from readd() */
334  int m;                /* matched flag */
335  int ff_status;        /* return value of FFIRST/FNEXT */
336  char *p;              /* path for recursion */
337  struct stat s;        /* result of stat() */
338  struct zlist far *z;  /* steps through zfiles list */
339
340  if (n == NULL)        /* volume_label request in freshen|delete mode ?? */
341    return ZE_OK;
342
343  if (strcmp(n, "-") == 0)   /* if compressing stdin */
344    return newname(n, 0, caseflag);
345  else if (*n == '\0') return ZE_MISS;
346  else if (attribs != MSDOS_INVALID_ATTR)
347  {
348    /* Avoid calling stat() for performance reasons when it is already known
349       (from a previous directory scan) that the passed name corresponds to
350       a "real existing" file.  The only information needed further down in
351       this function is the distinction between directory entries and other
352       (typically normal file) entries.  This distinction can be derived from
353       the file's attributes that the directory lookup has already provided
354       "for free".
355     */
356    s.st_mode = ((attribs & MSDOS_DIR_ATTR) ? S_IFDIR : S_IFREG);
357  }
358  else if (LSSTAT(n, &s)
359#ifdef __TURBOC__
360           /* For this compiler, stat() succeeds on wild card names! */
361           || isshexp(n)
362#endif
363          )
364  {
365    /* Not a file or directory--search for shell expression in zip file */
366    if (caseflag) {
367      p = malloc(strlen(n) + 1);
368      if (p != NULL)
369        strcpy(p, n);
370    } else
371      p = ex2in(n, 0, (int *)NULL);     /* shouldn't affect matching chars */
372    m = 1;
373    for (z = zfiles; z != NULL; z = z->nxt) {
374      if (MATCH(p, z->iname, caseflag))
375      {
376        z->mark = pcount ? filter(z->zname, caseflag) : 1;
377        if (z->mark) z->dosflag = 1;    /* force DOS attribs for incl. names */
378        if (verbose)
379            fprintf(mesg, "zip diagnostic: %scluding %s\n",
380               z->mark ? "in" : "ex", z->name);
381        m = 0;
382      }
383    }
384    free((zvoid *)p);
385    return m ? ZE_MISS : ZE_OK;
386  }
387
388  /* Live name--use if file, recurse if directory */
389  for (p = n; *p; p++)          /* use / consistently */
390    if (*p == '\\')
391      *p = '/';
392  if ((s.st_mode & S_IFDIR) == 0)
393  {
394    /* add or remove name of file */
395    if ((m = newname(n, 0, caseflag)) != ZE_OK)
396      return m;
397  } else {
398    /* Add trailing / to the directory name */
399    if ((p = malloc(strlen(n)+2)) == NULL)
400      return ZE_MEM;
401    if (strcmp(n, ".") == 0 || strcmp(n, "/.") == 0) {
402      *p = '\0';  /* avoid "./" prefix and do not create zip entry */
403    } else {
404      strcpy(p, n);
405      a = p + strlen(p);
406      if (a[-1] != '/')
407        strcpy(a, "/");
408      if (dirnames && (m = newname(p, 1, caseflag)) != ZE_OK) {
409        free((zvoid *)p);
410        return m;
411      }
412    }
413    /* recurse into directory */
414    if (recurse)
415    {
416      if ((d = malloc(sizeof(ff_dir))) == NULL ||
417          (m = initDirSearch(n, d)) == ZE_MEM)
418      {
419        if (d != NULL)
420          free((zvoid *)d);
421        free((zvoid *)p);
422        return ZE_MEM;
423      }
424      for (e = d->ff_name, ff_status = m;
425           ff_status == 0;
426           ff_status = FNEXT(d))
427      {
428        if (strcmp(e, ".") && strcmp(e, ".."))
429        {
430          if ((a = malloc(strlen(p) + strlen(e) + 1)) == NULL)
431          {
432            free((zvoid *)d);
433            free((zvoid *)p);
434            return ZE_MEM;
435          }
436          strcat(strcpy(a, p), e);
437          if ((m = procname_dos(a, caseflag, getDirEntryAttr(d)))
438              != ZE_OK)         /* recurse on name */
439          {
440            if (m == ZE_MISS)
441              zipwarn("name not matched: ", a);
442            else
443              ziperr(m, a);
444          }
445          free((zvoid *)a);
446        }
447      }
448      free((zvoid *)d);
449    }
450    free((zvoid *)p);
451  } /* (s.st_mode & S_IFDIR) == 0) */
452  return ZE_OK;
453}
454
455int procname(n, caseflag)
456char *n;                /* name to process */
457int caseflag;           /* true to force case-sensitive match */
458{
459  return procname_dos(n, caseflag, MSDOS_INVALID_ATTR);
460}
461
462char *ex2in(x, isdir, pdosflag)
463char *x;                /* external file name */
464int isdir;              /* input: x is a directory */
465int *pdosflag;          /* output: force MSDOS file attributes? */
466/* Convert the external file name to a zip file name, returning the malloc'ed
467   string or NULL if not enough memory. */
468{
469  char *n;              /* internal file name (malloc'ed) */
470  char *t;              /* shortened name */
471  int dosflag;
472
473  dosflag = 1;
474
475  /* Find starting point in name before doing malloc */
476  /* Strip drive specification */
477  t = *x && *(x + 1) == ':' ? x + 2 : x;
478  /* Strip "//host/share/" part of a UNC name */
479  if ((!strncmp(x,"//",2) || !strncmp(x,"\\\\",2)) &&
480      (x[2] != '\0' && x[2] != '/' && x[2] != '\\')) {
481    n = x + 2;
482    while (*n != '\0' && *n != '/' && *n != '\\')
483      n++;              /* strip host name */
484    if (*n != '\0') {
485      n++;
486      while (*n != '\0' && *n != '/' && *n != '\\')
487        n++;            /* strip `share' name */
488    }
489    if (*n != '\0')
490      t = n + 1;
491  }
492  /* Strip leading "/" to convert an absolute path into a relative path */
493  while (*t == '/' || *t == '\\')
494    t++;
495  /* Skip leading "./" as well */
496  while (*t == '.' && (t[1] == '/' || t[1] == '\\'))
497    t += 2;
498
499  /* Make changes, if any, to the copied name (leave original intact) */
500  for (n = t; *n; n++)
501    if (*n == '\\')
502      *n = '/';
503
504  if (!pathput)
505    t = last(t, PATH_END);
506
507  /* Malloc space for internal name and copy it */
508  if ((n = malloc(strlen(t) + 1)) == NULL)
509    return NULL;
510  strcpy(n, t);
511
512  if (isdir == 42) return n;      /* avoid warning on unused variable */
513
514  if (dosify)
515    msname(n);
516  else
517#if defined(__DJGPP__) && __DJGPP__ >= 2
518    if (_USE_LFN == 0)
519#endif
520      strlwr(n);
521  if (pdosflag)
522    *pdosflag = dosflag;
523  return n;
524}
525
526char *in2ex(n)
527char *n;                /* internal file name */
528/* Convert the zip file name to an external file name, returning the malloc'ed
529   string or NULL if not enough memory. */
530{
531  char *x;              /* external file name */
532
533  if ((x = malloc(strlen(n) + 1 + PAD)) == NULL)
534      return NULL;
535  strcpy(x, n);
536
537  return x;
538}
539
540void stamp(f, d)
541char *f;                /* name of file to change */
542ulg d;                  /* dos-style time to change it to */
543/* Set last updated and accessed time of file f to the DOS time d. */
544{
545#if defined(__TURBOC__) || defined(__GO32__)
546  int h;                /* file handle */
547
548  if ((h = open(f, 0)) != -1)
549  {
550    setftime(h, (struct ftime *)(void *)&d);
551    close(h);
552  }
553#else /* !__TURBOC__ && !__GO32__ */
554  ztimbuf u;            /* argument for utime() */
555
556  /* Convert DOS time to time_t format in u.actime and u.modtime */
557  u.actime = u.modtime = dos2unixtime(d);
558
559  /* Set updated and accessed times of f */
560  utime(f, &u);
561#endif /* ?(__TURBOC__ || __GO32__) */
562}
563
564ulg filetime(f, a, n, t)
565char *f;                /* name of file to get info on */
566ulg *a;                 /* return value: file attributes */
567long *n;                /* return value: file size */
568iztimes *t;             /* return value: access, modific. and creation times */
569/* If file *f does not exist, return 0.  Else, return the file's last
570   modified date and time as an MSDOS date and time.  The date and
571   time is returned in a long with the date most significant to allow
572   unsigned integer comparison of absolute times.  Also, if a is not
573   a NULL pointer, store the file attributes there, with the high two
574   bytes being the Unix attributes, and the low byte being a mapping
575   of that to DOS attributes.  If n is not NULL, store the file size
576   there.  If t is not NULL, the file's access, modification and creation
577   times are stored there as UNIX time_t values.
578   If f is "-", use standard input as the file. If f is a device, return
579   a file size of -1 */
580{
581  struct stat s;        /* results of stat() */
582  /* convert FNMAX to malloc - 11/8/04 EG */
583  char *name;
584  int len = strlen(f);
585  int isstdin = !strcmp(f, "-");
586
587  if (f == label) {
588    if (a != NULL)
589      *a = label_mode;
590    if (n != NULL)
591      *n = -2L; /* convention for a label name */
592    if (t != NULL)
593      t->atime = t->mtime = t->ctime = label_utim;
594    return label_time;
595  }
596  if ((name = malloc(len + 1)) == NULL) {
597    ZIPERR(ZE_MEM, "filetime");
598  }
599  strcpy(name, f);
600  if (name[len - 1] == '/')
601    name[len - 1] = '\0';
602  /* not all systems allow stat'ing a file with / appended */
603
604  if (isstdin) {
605    if (fstat(fileno(stdin), &s) != 0) {
606      free(name);
607      error("fstat(stdin)");
608    }
609    time((time_t *)&s.st_mtime);       /* some fstat()s return time zero */
610  } else if (LSSTAT(name, &s) != 0) {
611             /* Accept about any file kind including directories
612              * (stored with trailing / with -r option)
613              */
614    free(name);
615    return 0;
616  }
617
618  if (a != NULL) {
619    *a = ((ulg)s.st_mode << 16) | (isstdin ? 0L : (ulg)GetFileMode(name));
620#if (S_IFREG != 0x8000)
621    /* kludge to work around non-standard S_IFREG flag used in DJGPP V2.x */
622    if ((s.st_mode & S_IFMT) == S_IFREG) *a |= 0x80000000L;
623#endif
624  }
625  free(name);
626  if (n != NULL)
627    *n = (s.st_mode & S_IFMT) == S_IFREG ? s.st_size : -1L;
628  if (t != NULL) {
629    t->atime = s.st_atime;
630    t->mtime = s.st_mtime;
631    t->ctime = s.st_ctime;
632  }
633
634  return unix2dostime((time_t *)&s.st_mtime);
635}
636
637int deletedir(d)
638char *d;                /* directory to delete */
639/* Delete the directory *d if it is empty, do nothing otherwise.
640   Return the result of rmdir(), delete(), or system().
641 */
642{
643    return rmdir(d);
644}
645
646int set_extra_field(z, z_utim)
647  struct zlist far *z;
648  iztimes *z_utim;
649  /* create extra field and change z->att if desired */
650{
651#ifdef USE_EF_UT_TIME
652#ifdef IZ_CHECK_TZ
653  if (!zp_tz_is_valid) return ZE_OK;    /* skip silently if no valid TZ info */
654#endif
655
656  if ((z->extra = (char *)malloc(EB_HEADSIZE+EB_UT_LEN(1))) == NULL)
657    return ZE_MEM;
658
659  z->extra[0]  = 'U';
660  z->extra[1]  = 'T';
661  z->extra[2]  = EB_UT_LEN(1);          /* length of data part of e.f. */
662  z->extra[3]  = 0;
663  z->extra[4]  = EB_UT_FL_MTIME;
664  z->extra[5]  = (char)(z_utim->mtime);
665  z->extra[6]  = (char)(z_utim->mtime >> 8);
666  z->extra[7]  = (char)(z_utim->mtime >> 16);
667  z->extra[8]  = (char)(z_utim->mtime >> 24);
668
669  z->cext = z->ext = (EB_HEADSIZE+EB_UT_LEN(1));
670  z->cextra = z->extra;
671
672  return ZE_OK;
673#else /* !USE_EF_UT_TIME */
674  return (int)(z-z);
675#endif /* ?USE_EF_UT_TIME */
676}
677
678
679#ifdef MY_ZCALLOC       /* Special zcalloc function for MEMORY16 (MSDOS/OS2) */
680
681#if defined(__TURBOC__) && !defined(OS2)
682/* Small and medium model are for now limited to near allocation with
683 * reduced MAX_WBITS and MAX_MEM_LEVEL
684 */
685
686/* Turbo C malloc() does not allow dynamic allocation of 64K bytes
687 * and farmalloc(64K) returns a pointer with an offset of 8, so we
688 * must fix the pointer. Warning: the pointer must be put back to its
689 * original form in order to free it, use zcfree().
690 */
691
692#define MAX_PTR 10
693/* 10*64K = 640K */
694
695local int next_ptr = 0;
696
697typedef struct ptr_table_s {
698    zvoid far *org_ptr;
699    zvoid far *new_ptr;
700} ptr_table;
701
702local ptr_table table[MAX_PTR];
703/* This table is used to remember the original form of pointers
704 * to large buffers (64K). Such pointers are normalized with a zero offset.
705 * Since MSDOS is not a preemptive multitasking OS, this table is not
706 * protected from concurrent access. This hack doesn't work anyway on
707 * a protected system like OS/2. Use Microsoft C instead.
708 */
709
710zvoid far *zcalloc (unsigned items, unsigned size)
711{
712    zvoid far *buf;
713    ulg bsize = (ulg)items*size;
714
715    if (bsize < (65536L-16L)) {
716        buf = farmalloc(bsize);
717        if (*(ush*)&buf != 0) return buf;
718    } else {
719        buf = farmalloc(bsize + 16L);
720    }
721    if (buf == NULL || next_ptr >= MAX_PTR) return NULL;
722    table[next_ptr].org_ptr = buf;
723
724    /* Normalize the pointer to seg:0 */
725    *((ush*)&buf+1) += ((ush)((uch*)buf-NULL) + 15) >> 4;
726    *(ush*)&buf = 0;
727    table[next_ptr++].new_ptr = buf;
728    return buf;
729}
730
731zvoid zcfree (zvoid far *ptr)
732{
733    int n;
734    if (*(ush*)&ptr != 0) { /* object < 64K */
735        farfree(ptr);
736        return;
737    }
738    /* Find the original pointer */
739    for (n = next_ptr - 1; n >= 0; n--) {
740        if (ptr != table[n].new_ptr) continue;
741
742        farfree(table[n].org_ptr);
743        while (++n < next_ptr) {
744            table[n-1] = table[n];
745        }
746        next_ptr--;
747        return;
748    }
749    ziperr(ZE_MEM, "zcfree: ptr not found");
750}
751#endif /* __TURBOC__ */
752
753#if defined(MSC) || defined(__WATCOMC__)
754#if (!defined(_MSC_VER) || (_MSC_VER < 700))
755#  define _halloc  halloc
756#  define _hfree   hfree
757#endif
758
759zvoid far *zcalloc (unsigned items, unsigned size)
760{
761    return (zvoid far *)_halloc((long)items, size);
762}
763
764zvoid zcfree (zvoid far *ptr)
765{
766    _hfree((void huge *)ptr);
767}
768#endif /* MSC || __WATCOMC__ */
769
770#endif /* MY_ZCALLOC */
771
772#if (defined(__WATCOMC__) && defined(ASMV) && !defined(__386__))
773/* This is a hack to connect "call _exit" in match.asm to exit() */
774#pragma aux xit "_exit" parm caller []
775void xit(void)
776{
777    exit(20);
778}
779#endif
780
781local int is_running_on_windows(void)
782{
783    char * var = getenv("OS");
784
785    /* if the OS env.var says 'Windows_NT' then */
786    /* we're likely running on a variant of WinNT */
787
788    if ((NULL != var) && (0 == strcmp("Windows_NT", var)))
789    {
790        return 1;
791    }
792
793    /* if the windir env.var is non-null then */
794    /* we're likely running on a variant of Win9x */
795    /* DOS mode of Win9x doesn't define windir, only winbootdir */
796    /* NT's command.com can't see lowercase env. vars */
797
798    var = getenv("windir");
799    if ((NULL != var) && (0 != var[0]))
800    {
801        return 1;
802    }
803
804    return 0;
805}
806
807void check_for_windows(char *app)
808{
809    /* Print a warning for users running under Windows */
810    /* to reduce bug reports due to running DOS version */
811    /* under Windows, when Windows version usually works correctly */
812
813    /* This is only called from the DOS version */
814
815    if (is_running_on_windows())
816    {
817        printf("\nzip warning:  You are running MSDOS %s on Windows.\n"
818               "Try the Windows version before reporting any problems.\n",
819               app);
820    }
821}
822
823#endif /* !UTIL */
824
825
826#ifndef WINDLL
827/******************************/
828/*  Function version_local()  */
829/******************************/
830
831static ZCONST char CompiledWith[] = "Compiled with %s%s for %s%s%s.\n\n";
832                        /* At module level to keep Turbo C++ 1.0 happy !! */
833
834void version_local()
835{
836#if defined(__DJGPP__) || defined(__WATCOMC__) || \
837    (defined(_MSC_VER) && (_MSC_VER != 800))
838    char buf[80];
839#endif
840
841/* Define the compiler name and version strings */
842#if defined(__GNUC__)
843#  if defined(__DJGPP__)
844    sprintf(buf, "djgpp v%d.%02d / gcc ", __DJGPP__, __DJGPP_MINOR__);
845#    define COMPILER_NAME1      buf
846#  elif defined(__GO32__)         /* __GO32__ is defined as "1" only (sigh) */
847#    define COMPILER_NAME1      "djgpp v1.x / gcc "
848#  elif defined(__EMX__)          /* ...so is __EMX__ (double sigh) */
849#    define COMPILER_NAME1      "emx+gcc "
850#  else
851#    define COMPILER_NAME1      "gcc "
852#  endif
853#  define COMPILER_NAME2        __VERSION__
854#elif defined(__WATCOMC__)
855#  if (__WATCOMC__ % 10 > 0)
856/* We do this silly test because __WATCOMC__ gives two digits for the  */
857/* minor version, but Watcom packaging prefers to show only one digit. */
858    sprintf(buf, "Watcom C/C++ %d.%02d", __WATCOMC__ / 100,
859            __WATCOMC__ % 100);
860#  else
861    sprintf(buf, "Watcom C/C++ %d.%d", __WATCOMC__ / 100,
862            (__WATCOMC__ % 100) / 10);
863#  endif
864#  define COMPILER_NAME1        buf
865#  define COMPILER_NAME2        ""
866#elif defined(__TURBOC__)
867#  ifdef __BORLANDC__
868#    define COMPILER_NAME1      "Borland C++"
869#    if (__BORLANDC__ < 0x0200)
870#      define COMPILER_NAME2    " 1.0"
871#    elif (__BORLANDC__ == 0x0200)   /* James:  __TURBOC__ = 0x0297 */
872#      define COMPILER_NAME2    " 2.0"
873#    elif (__BORLANDC__ == 0x0400)
874#      define COMPILER_NAME2    " 3.0"
875#    elif (__BORLANDC__ == 0x0410)   /* __BCPLUSPLUS__ = 0x0310 */
876#      define COMPILER_NAME2    " 3.1"
877#    elif (__BORLANDC__ == 0x0452)   /* __BCPLUSPLUS__ = 0x0320 */
878#      define COMPILER_NAME2    " 4.0 or 4.02"
879#    elif (__BORLANDC__ == 0x0460)   /* __BCPLUSPLUS__ = 0x0340 */
880#      define COMPILER_NAME2    " 4.5"
881#    elif (__BORLANDC__ == 0x0500)   /* __TURBOC__ = 0x0500 */
882#      define COMPILER_NAME2    " 5.0"
883#    else
884#      define COMPILER_NAME2    " later than 5.0"
885#    endif
886#  else
887#    define COMPILER_NAME1      "Turbo C"
888#    if (__TURBOC__ > 0x0401)
889#      define COMPILER_NAME2    "++ later than 3.0"
890#    elif (__TURBOC__ == 0x0401)     /* Kevin:  3.0 -> 0x0401 */
891#      define COMPILER_NAME2    "++ 3.0"
892#    elif (__TURBOC__ == 0x0296)     /* [662] checked by SPC */
893#      define COMPILER_NAME2    "++ 1.01"
894#    elif (__TURBOC__ == 0x0295)     /* [661] vfy'd by Kevin */
895#      define COMPILER_NAME2    "++ 1.0"
896#    elif (__TURBOC__ == 0x0201)     /* Brian:  2.01 -> 0x0201 */
897#      define COMPILER_NAME2    " 2.01"
898#    elif ((__TURBOC__ >= 0x018d) && (__TURBOC__ <= 0x0200)) /* James: 0x0200 */
899#      define COMPILER_NAME2    " 2.0"
900#    elif (__TURBOC__ > 0x0100)
901#      define COMPILER_NAME2    " 1.5"          /* James:  0x0105? */
902#    else
903#      define COMPILER_NAME2    " 1.0"          /* James:  0x0100 */
904#    endif
905#  endif
906#elif defined(MSC)
907#  if defined(_QC) && !defined(_MSC_VER)
908#    define COMPILER_NAME1      "Microsoft Quick C"
909#    define COMPILER_NAME2      ""      /* _QC is defined as 1 */
910#  else
911#    define COMPILER_NAME1      "Microsoft C "
912#    ifdef _MSC_VER
913#      if (_MSC_VER == 800)
914#        define COMPILER_NAME2  "8.0/8.0c (Visual C++ 1.0/1.5)"
915#      else
916#        define COMPILER_NAME2 \
917           (sprintf(buf, "%d.%02d", _MSC_VER/100, _MSC_VER%100), buf)
918#      endif
919#    else
920#      define COMPILER_NAME2    "5.1 or earlier"
921#    endif
922#  endif
923#else
924#    define COMPILER_NAME1      "unknown compiler"
925#    define COMPILER_NAME2      ""
926#endif
927
928/* Define the OS name and memory environment strings */
929#if defined(__WATCOMC__) || defined(__TURBOC__) || defined(MSC) || \
930    defined(__GNUC__)
931#  define OS_NAME1      "\nMS-DOS"
932#else
933#  define OS_NAME1      "MS-DOS"
934#endif
935
936#if (defined(__GNUC__) || (defined(__WATCOMC__) && defined(__386__)))
937#  define OS_NAME2      " (32-bit)"
938#elif defined(M_I86HM) || defined(__HUGE__)
939#  define OS_NAME2      " (16-bit, huge)"
940#elif defined(M_I86LM) || defined(__LARGE__)
941#  define OS_NAME2      " (16-bit, large)"
942#elif defined(M_I86MM) || defined(__MEDIUM__)
943#  define OS_NAME2      " (16-bit, medium)"
944#elif defined(M_I86CM) || defined(__COMPACT__)
945#  define OS_NAME2      " (16-bit, compact)"
946#elif defined(M_I86SM) || defined(__SMALL__)
947#  define OS_NAME2      " (16-bit, small)"
948#elif defined(M_I86TM) || defined(__TINY__)
949#  define OS_NAME2      " (16-bit, tiny)"
950#else
951#  define OS_NAME2      " (16-bit)"
952#endif
953
954/* Define the compile date string */
955#ifdef __DATE__
956#  define COMPILE_DATE " on " __DATE__
957#else
958#  define COMPILE_DATE ""
959#endif
960
961    printf(CompiledWith, COMPILER_NAME1, COMPILER_NAME2,
962           OS_NAME1, OS_NAME2, COMPILE_DATE);
963
964} /* end function version_local() */
965#endif /* !WINDLL */
966
967
968#if 0 /* inserted here for future use (clearing of archive bits) */
969#if (defined(__GO32__) && (!defined(__DJGPP__) || (__DJGPP__ < 2)))
970
971#include <errno.h>
972int volatile _doserrno;
973
974unsigned _dos_setfileattr(char *name, unsigned attr)
975{
976#if 0   /* stripping of trailing '/' is not needed for zip-internal use */
977    unsigned namlen = strlen(name);
978    char *i_name = alloca(namlen + 1);
979
980    strcpy(i_name, name);
981    if (namlen > 1 && i_name[namlen-1] == '/' && i_name[namlen-2] != ':')
982        i_name[namlen-1] = '\0';
983    asm("movl %0, %%edx": : "g" (i_name));
984#else
985    asm("movl %0, %%edx": : "g" (name));
986#endif
987    asm("movl %0, %%ecx": : "g" (attr));
988    asm("movl $0x4301, %eax");
989    asm("int $0x21": : : "%eax", "%ebx", "%ecx", "%edx", "%esi", "%edi");
990    _doserrno = 0;
991    asm("jnc 1f");
992    asm("movl %%eax, %0": "=m" (_doserrno));
993    switch (_doserrno) {
994    case 2:
995    case 3:
996           errno = ENOENT;
997           break;
998    case 5:
999           errno = EACCES;
1000           break;
1001    }
1002    asm("1:");
1003    return (unsigned)_doserrno;
1004}
1005
1006#endif /* DJGPP v1.x */
1007#endif /* never (not yet used) */
1008
1009
1010#if (defined(__DJGPP__) && (__DJGPP__ >= 2))
1011
1012/* Disable determination of "x" bit in st_mode field for [f]stat() calls. */
1013int _is_executable (const char *path, int fhandle, const char *ext)
1014{
1015    return 0;
1016}
1017
1018/* Prevent globbing of filenames.  This gives the same functionality as
1019 * "stubedit <program> globbing=no" did with DJGPP v1.
1020 */
1021#ifndef USE_DJGPP_GLOB
1022char **__crt0_glob_function(char *_arg)
1023{
1024    return NULL;
1025}
1026#endif
1027
1028/* Reduce the size of the executable and remove the functionality to read
1029 * the program's environment from whatever $DJGPP points to.
1030 */
1031#if !defined(USE_DJGPP_ENV) || defined(UTIL)
1032void __crt0_load_environment_file(char *_app_name)
1033{
1034}
1035#endif
1036
1037#endif /* __DJGPP__ >= 2 */
1038
1039
1040#if defined(_MSC_VER) && _MSC_VER == 700
1041
1042/*
1043 * ARGH.  MSC 7.0 libraries think times are based on 1899 Dec 31 00:00, not
1044 *  1970 Jan 1 00:00.  So we have to diddle time_t's appropriately:  add
1045 *  70 years' worth of seconds for localtime() wrapper function;
1046 *  (70*365 regular days + 17 leap days + 1 1899 day) * 86400 ==
1047 *  (25550 + 17 + 1) * 86400 == 2209075200 seconds.
1048 *  Let time() and stat() return seconds since 1970 by using our own
1049 *  _dtoxtime() which is the routine that is called by these two functions.
1050 */
1051
1052
1053#ifdef UTIL
1054#  include <time.h>
1055#endif
1056
1057#ifndef UTIL
1058#undef localtime
1059struct tm *localtime(const time_t *);
1060
1061struct tm *msc7_localtime(const time_t *clock)
1062{
1063   time_t t = *clock;
1064
1065   t += 2209075200L;
1066   return localtime(&t);
1067}
1068#endif /* !UTIL */
1069
1070
1071void __tzset(void);
1072int _isindst(struct tm *);
1073
1074extern int _days[];
1075
1076/* Nonzero if `y' is a leap year, else zero. */
1077#define leap(y) (((y) % 4 == 0 && (y) % 100 != 0) || (y) % 400 == 0)
1078
1079/* Number of leap years from 1970 to `y' (not including `y' itself). */
1080#define nleap(y) (((y) - 1969) / 4 - ((y) - 1901) / 100 + ((y) - 1601) / 400)
1081
1082time_t _dtoxtime(year, month, mday, hour, min, sec)
1083int year, month, mday, year, hour, min, sec;
1084{
1085   struct tm tm;
1086   time_t t;
1087   int days;
1088
1089   days = _days[month - 1] + mday;
1090   year += 1980;
1091   if (leap(year) && month > 2)
1092     ++days;
1093   tm.tm_yday = days;
1094   tm.tm_mon = month - 1;
1095   tm.tm_year = year - 1900;
1096   tm.tm_hour = hour;
1097   __tzset();
1098   days += 365 * (year - 1970) + nleap (year);
1099   t = 86400L * days + 3600L * hour + 60 * min + sec + _timezone;
1100   if (_daylight && _isindst(&tm))
1101      t -= 3600;
1102   return t;
1103}
1104
1105#endif /* _MSC_VER && _MSC_VER == 700 */
1106
1107
1108#ifdef __WATCOMC__
1109
1110/* This papers over a bug in Watcom 10.6's standard library... sigh */
1111/* Apparently it applies to both the DOS and Win32 stat()s.         */
1112
1113int stat_bandaid(const char *path, struct stat *buf)
1114{
1115  char newname[4];
1116  if (!stat(path, buf))
1117    return 0;
1118  else if (!strcmp(path, ".") || (path[0] && !strcmp(path + 1, ":."))) {
1119    strcpy(newname, path);
1120    newname[strlen(path) - 1] = '\\';   /* stat(".") fails for root! */
1121    return stat(newname, buf);
1122  } else
1123    return -1;
1124}
1125
1126#endif
1127