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/*
10 *  fileio.c by Mark Adler
11 */
12#define __FILEIO_C
13
14#include "zip.h"
15
16#ifdef MACOS
17#  include "helpers.h"
18#endif
19
20#include <time.h>
21
22#ifdef NO_MKTIME
23time_t mktime OF((struct tm *));
24#endif
25
26#ifdef OSF
27#define EXDEV 18   /* avoid a bug in the DEC OSF/1 header files. */
28#else
29#include <errno.h>
30#endif
31
32#ifdef NO_ERRNO
33extern int errno;
34#endif
35
36#if defined(VMS) || defined(TOPS20)
37#  define PAD 5
38#else
39#  define PAD 0
40#endif
41
42#ifdef NO_RENAME
43int rename OF((ZCONST char *, ZCONST char *));
44#endif
45
46
47#ifndef UTIL    /* the companion #endif is a bit of ways down ... */
48
49/* Local functions */
50local int fqcmp  OF((ZCONST zvoid *, ZCONST zvoid *));
51local int fqcmpz OF((ZCONST zvoid *, ZCONST zvoid *));
52
53/* Local module level variables. */
54char *label = NULL;                /* global, but only used in `system'.c */
55local struct stat zipstatb;
56#if (!defined(MACOS) && !defined(WINDLL))
57local int zipstate = -1;
58#else
59int zipstate;
60#endif
61/* -1 unknown, 0 old zip file exists, 1 new zip file */
62
63char *getnam(n, fp)
64char *n;                /* where to put name (must have >=FNMAX+1 bytes) */
65FILE *fp;
66/* Read a \n or \r delimited name from stdin into n, and return
67   n.  If EOF, then return NULL.  Also, if the name read is too big, return
68   NULL. */
69{
70  int c;                /* last character read */
71  char *p;              /* pointer into name area */
72
73  p = n;
74  while ((c = getc(fp)) == '\n' || c == '\r')
75    ;
76  if (c == EOF)
77    return NULL;
78  do {
79    if (p - n >= FNMAX)
80      return NULL;
81    *p++ = (char) c;
82    c = getc(fp);
83  } while (c != EOF && (c != '\n' && c != '\r'));
84#ifdef WIN32
85/*
86 * WIN32 strips off trailing spaces and periods in filenames
87 * XXX what about a filename that only consists of spaces ?
88 *     Answer: on WIN32, a filename must contain at least one non-space char
89 */
90  while (p > n) {
91    if ((c = p[-1]) != ' ' && c != '.')
92      break;
93    --p;
94  }
95#endif
96  *p = 0;
97  return n;
98}
99
100struct flist far *fexpel(f)
101struct flist far *f;    /* entry to delete */
102/* Delete the entry *f in the doubly-linked found list.  Return pointer to
103   next entry to allow stepping through list. */
104{
105  struct flist far *t;  /* temporary variable */
106
107  t = f->nxt;
108  *(f->lst) = t;                        /* point last to next, */
109  if (t != NULL)
110    t->lst = f->lst;                    /* and next to last */
111  if (f->name != NULL)                  /* free memory used */
112    free((zvoid *)(f->name));
113  if (f->zname != NULL)
114    free((zvoid *)(f->zname));
115  if (f->iname != NULL)
116    free((zvoid *)(f->iname));
117  farfree((zvoid far *)f);
118  fcount--;                             /* decrement count */
119  return t;                             /* return pointer to next */
120}
121
122
123local int fqcmp(a, b)
124ZCONST zvoid *a, *b;          /* pointers to pointers to found entries */
125/* Used by qsort() to compare entries in the found list by name. */
126{
127  return strcmp((*(struct flist far **)a)->name,
128                (*(struct flist far **)b)->name);
129}
130
131
132local int fqcmpz(a, b)
133ZCONST zvoid *a, *b;          /* pointers to pointers to found entries */
134/* Used by qsort() to compare entries in the found list by iname. */
135{
136  return strcmp((*(struct flist far **)a)->iname,
137                (*(struct flist far **)b)->iname);
138}
139
140
141char *last(p, c)
142char *p;                /* sequence of path components */
143int c;                  /* path components separator character */
144/* Return a pointer to the start of the last path component. For a directory
145 * name terminated by the character in c, the return value is an empty string.
146 */
147{
148  char *t;              /* temporary variable */
149
150  if ((t = strrchr(p, c)) != NULL)
151    return t + 1;
152  else
153#ifndef AOS_VS
154    return p;
155#else
156/* We want to allow finding of end of path in either AOS/VS-style pathnames
157 * or Unix-style pathnames.  This presents a few little problems ...
158 */
159  {
160    if (*p == '='  ||  *p == '^')      /* like ./ and ../ respectively */
161      return p + 1;
162    else
163      return p;
164  }
165#endif
166}
167
168
169char *msname(n)
170char *n;
171/* Reduce all path components to MSDOS upper case 8.3 style names. */
172{
173  int c;                /* current character */
174  int f;                /* characters in current component */
175  char *p;              /* source pointer */
176  char *q;              /* destination pointer */
177
178  p = q = n;
179  f = 0;
180  while ((c = (unsigned char)*POSTINCSTR(p)) != 0)
181    if (c == ' ' || c == ':' || c == '"' || c == '*' || c == '+' ||
182        c == ',' || c == ';' || c == '<' || c == '=' || c == '>' ||
183        c == '?' || c == '[' || c == ']' || c == '|')
184      continue;                         /* char is discarded */
185    else if (c == '/')
186    {
187      *POSTINCSTR(q) = (char)c;
188      f = 0;                            /* new component */
189    }
190#ifdef __human68k__
191    else if (ismbblead(c) && *p)
192    {
193      if (f == 7 || f == 11)
194        f++;
195      else if (*p && f < 12 && f != 8)
196      {
197        *q++ = c;
198        *q++ = *p++;
199        f += 2;
200      }
201    }
202#endif /* __human68k__ */
203    else if (c == '.')
204    {
205      if (f == 0)
206        continue;                       /* leading dots are discarded */
207      else if (f < 9)
208      {
209        *POSTINCSTR(q) = (char)c;
210        f = 9;                          /* now in file type */
211      }
212      else
213        f = 12;                         /* now just excess characters */
214    }
215    else
216      if (f < 12 && f != 8)
217      {
218        f += CLEN(p);                   /* do until end of name or type */
219        *POSTINCSTR(q) = (char)(to_up(c));
220      }
221  *q = 0;
222  return n;
223}
224
225int check_dup()
226/* Sort the found list and remove duplicates.
227   Return an error code in the ZE_ class. */
228{
229  struct flist far *f;          /* steps through found linked list */
230  extent j, k;                  /* indices for s */
231  struct flist far **s;         /* sorted table */
232  struct flist far **nodup;     /* sorted table without duplicates */
233
234  /* sort found list, remove duplicates */
235  if (fcount)
236  {
237    extent fl_size = fcount * sizeof(struct flist far *);
238    if ((fl_size / sizeof(struct flist far *)) != fcount ||
239        (s = (struct flist far **)malloc(fl_size)) == NULL)
240      return ZE_MEM;
241    for (j = 0, f = found; f != NULL; f = f->nxt)
242      s[j++] = f;
243    /* Check names as given (f->name) */
244    qsort((char *)s, fcount, sizeof(struct flist far *), fqcmp);
245    for (k = j = fcount - 1; j > 0; j--)
246      if (strcmp(s[j - 1]->name, s[j]->name) == 0)
247        /* remove duplicate entry from list */
248        fexpel(s[j]);           /* fexpel() changes fcount */
249      else
250        /* copy valid entry into destination position */
251        s[k--] = s[j];
252    s[k] = s[0];                /* First entry is always valid */
253    nodup = &s[k];              /* Valid entries are at end of array s */
254
255    /* sort only valid items and check for unique internal names (f->iname) */
256    qsort((char *)nodup, fcount, sizeof(struct flist far *), fqcmpz);
257    for (j = 1; j < fcount; j++)
258      if (strcmp(nodup[j - 1]->iname, nodup[j]->iname) == 0)
259      {
260        zipwarn("  first full name: ", nodup[j - 1]->name);
261        zipwarn(" second full name: ", nodup[j]->name);
262#ifdef EBCDIC
263        strtoebc(nodup[j]->iname, nodup[j]->iname);
264#endif
265        zipwarn("name in zip file repeated: ", nodup[j]->iname);
266#ifdef EBCDIC
267        strtoasc(nodup[j]->iname, nodup[j]->iname);
268#endif
269        return ZE_PARMS;
270      }
271    free((zvoid *)s);
272  }
273  return ZE_OK;
274}
275
276int filter(name, casesensitive)
277  char *name;
278  int casesensitive;
279  /* Scan the -R, -i and -x lists for matches to the given name.
280     Return TRUE if the name must be included, FALSE otherwise.
281     Give precedence to -x over -i and -R.
282     Note that if both R and i patterns are given then must
283     have a match for both.
284     This routine relies on the following global variables:
285       patterns                 array of match pattern structures
286       pcount                   total number of patterns
287       icount                   number of -i patterns
288       Rcount                   number of -R patterns
289     These data are set up by the command line parsing code.
290   */
291{
292   unsigned int n;
293   int slashes;
294   char *p, *q;
295   /* without -i patterns, every name matches the "-i select rules" */
296   int imatch = (icount == 0);
297   /* without -R patterns, every name matches the "-R select rules" */
298   int Rmatch = (Rcount == 0);
299
300   if (pcount == 0) return TRUE;
301
302   for (n = 0; n < pcount; n++) {
303      if (!patterns[n].zname[0])        /* it can happen... */
304         continue;
305      p = name;
306      switch (patterns[n].select) {
307       case 'R':
308         if (Rmatch)
309            /* one -R match is sufficient, skip this pattern */
310            continue;
311         /* With -R patterns, if the pattern has N path components (that is,
312            N-1 slashes), then we test only the last N components of name.
313          */
314         slashes = 0;
315         for (q = patterns[n].zname; (q = MBSCHR(q, '/')) != NULL; INCSTR(q))
316            slashes++;
317         /* The name may have M path components (M-1 slashes) */
318         for (q = p; (q = MBSCHR(q, '/')) != NULL; INCSTR(q))
319            slashes--;
320         /* Now, "slashes" contains the difference "N-M" between the number
321            of path components in the pattern (N) and in the name (M).
322          */
323         if (slashes < 0)
324            /* We found "M > N"
325                --> skip the first (M-N) path components of the name.
326             */
327            for (q = p; (q = MBSCHR(q, '/')) != NULL; INCSTR(q))
328               if (++slashes == 0) {
329                  p = q + 1;    /* q points at '/', mblen("/") is 1 */
330                  break;
331               }
332         break;
333       case 'i':
334         if (imatch)
335            /* one -i match is sufficient, skip this pattern */
336            continue;
337         break;
338      }
339      if (MATCH(patterns[n].zname, p, casesensitive)) {
340         switch (patterns[n].select) {
341            case 'x':
342               /* The -x match takes precedence over everything else */
343               return FALSE;
344            case 'R':
345               Rmatch = TRUE;
346               break;
347            default:
348               /* this must be a type -i match */
349               imatch = TRUE;
350               break;
351         }
352      }
353   }
354   return imatch && Rmatch;
355}
356
357int newname(name, isdir, casesensitive)
358char *name;             /* name to add (or exclude) */
359int  isdir;             /* true for a directory */
360int  casesensitive;     /* true for case-sensitive matching */
361/* Add (or exclude) the name of an existing disk file.  Return an error
362   code in the ZE_ class. */
363{
364  char *iname, *zname;  /* internal name, external version of iname */
365  char *undosm;         /* zname version with "-j" and "-k" options disabled */
366  struct flist far *f;  /* where in found, or new found entry */
367  struct zlist far *z;  /* where in zfiles (if found) */
368  int dosflag;
369
370  /* Search for name in zip file.  If there, mark it, else add to
371     list of new names to do (or remove from that list). */
372  if ((iname = ex2in(name, isdir, &dosflag)) == NULL)
373    return ZE_MEM;
374
375  /* Discard directory names with zip -rj */
376  if (*iname == '\0') {
377#ifndef AMIGA
378/* A null string is a legitimate external directory name in AmigaDOS; also,
379 * a command like "zip -r zipfile FOO:" produces an empty internal name.
380 */
381# ifndef RISCOS
382 /* If extensions needs to be swapped, we will have empty directory names
383    instead of the original directory. For example, zipping 'c.', 'c.main'
384    should zip only 'main.c' while 'c.' will be converted to '\0' by ex2in. */
385
386    if (pathput && !recurse) error("empty name without -j or -r");
387
388# endif /* !RISCOS */
389#endif /* !AMIGA */
390    free((zvoid *)iname);
391    return ZE_OK;
392  }
393  undosm = NULL;
394  if (dosflag || !pathput) {
395    int save_dosify = dosify, save_pathput = pathput;
396    dosify = 0;
397    pathput = 1;
398    /* zname is temporarly mis-used as "undosmode" iname pointer */
399    if ((zname = ex2in(name, isdir, NULL)) != NULL) {
400      undosm = in2ex(zname);
401      free(zname);
402    }
403    dosify = save_dosify;
404    pathput = save_pathput;
405  }
406  if ((zname = in2ex(iname)) == NULL)
407    return ZE_MEM;
408  if (undosm == NULL)
409    undosm = zname;
410  if ((z = zsearch(zname)) != NULL) {
411    if (pcount && !filter(undosm, casesensitive)) {
412      /* Do not clear z->mark if "exclude", because, when "dosify || !pathput"
413       * is in effect, two files with different filter options may hit the
414       * same z entry.
415       */
416      if (verbose)
417        fprintf(mesg, "excluding %s\n", zname);
418      free((zvoid *)iname);
419      free((zvoid *)zname);
420    } else {
421      z->mark = 1;
422      if ((z->name = malloc(strlen(name) + 1 + PAD)) == NULL) {
423        if (undosm != zname)
424          free((zvoid *)undosm);
425        free((zvoid *)iname);
426        free((zvoid *)zname);
427        return ZE_MEM;
428      }
429      strcpy(z->name, name);
430      z->dosflag = dosflag;
431
432#ifdef FORCE_NEWNAME
433      free((zvoid *)(z->iname));
434      z->iname = iname;
435#else
436      /* Better keep the old name. Useful when updating on MSDOS a zip file
437       * made on Unix.
438       */
439      free((zvoid *)iname);
440      free((zvoid *)zname);
441#endif /* ? FORCE_NEWNAME */
442    }
443    if (name == label) {
444       label = z->name;
445    }
446  } else if (pcount == 0 || filter(undosm, casesensitive)) {
447
448    /* Check that we are not adding the zip file to itself. This
449     * catches cases like "zip -m foo ../dir/foo.zip".
450     */
451#ifndef CMS_MVS
452/* Version of stat() for CMS/MVS isn't complete enough to see if       */
453/* files match.  Just let ZIP.C compare the filenames.  That's good    */
454/* enough for CMS anyway since there aren't paths to worry about.      */
455    struct stat statb;
456
457    if (zipstate == -1)
458       zipstate = strcmp(zipfile, "-") != 0 &&
459                   stat(zipfile, &zipstatb) == 0;
460
461    if (zipstate == 1 && (statb = zipstatb, stat(name, &statb) == 0
462      && zipstatb.st_mode  == statb.st_mode
463#ifdef VMS
464      && memcmp(zipstatb.st_ino, statb.st_ino, sizeof(statb.st_ino)) == 0
465      && strcmp(zipstatb.st_dev, statb.st_dev) == 0
466      && zipstatb.st_uid   == statb.st_uid
467#else /* !VMS */
468      && zipstatb.st_ino   == statb.st_ino
469      && zipstatb.st_dev   == statb.st_dev
470      && zipstatb.st_uid   == statb.st_uid
471      && zipstatb.st_gid   == statb.st_gid
472#endif /* ?VMS */
473      && zipstatb.st_size  == statb.st_size
474      && zipstatb.st_mtime == statb.st_mtime
475      && zipstatb.st_ctime == statb.st_ctime)) {
476      /* Don't compare a_time since we are reading the file */
477         if (verbose)
478           fprintf(mesg, "file matches zip file -- skipping\n");
479         if (undosm != zname)
480           free((zvoid *)zname);
481         if (undosm != iname)
482           free((zvoid *)undosm);
483         free((zvoid *)iname);
484         return ZE_OK;
485    }
486#endif  /* CMS_MVS */
487
488    /* allocate space and add to list */
489    if ((f = (struct flist far *)farmalloc(sizeof(struct flist))) == NULL ||
490        fcount + 1 < fcount ||
491        (f->name = malloc(strlen(name) + 1 + PAD)) == NULL)
492    {
493      if (f != NULL)
494        farfree((zvoid far *)f);
495      if (undosm != zname)
496        free((zvoid *)undosm);
497      free((zvoid *)iname);
498      free((zvoid *)zname);
499      return ZE_MEM;
500    }
501    strcpy(f->name, name);
502    f->iname = iname;
503    f->zname = zname;
504    f->dosflag = dosflag;
505    *fnxt = f;
506    f->lst = fnxt;
507    f->nxt = NULL;
508    fnxt = &f->nxt;
509    fcount++;
510    if (name == label) {
511      label = f->name;
512    }
513  }
514  if (undosm != zname)
515    free((zvoid *)undosm);
516  return ZE_OK;
517}
518
519
520ulg dostime(y, n, d, h, m, s)
521int y;                  /* year */
522int n;                  /* month */
523int d;                  /* day */
524int h;                  /* hour */
525int m;                  /* minute */
526int s;                  /* second */
527/* Convert the date y/n/d and time h:m:s to a four byte DOS date and
528   time (date in high two bytes, time in low two bytes allowing magnitude
529   comparison). */
530{
531  return y < 1980 ? DOSTIME_MINIMUM /* dostime(1980, 1, 1, 0, 0, 0) */ :
532        (((ulg)y - 1980) << 25) | ((ulg)n << 21) | ((ulg)d << 16) |
533        ((ulg)h << 11) | ((ulg)m << 5) | ((ulg)s >> 1);
534}
535
536
537ulg unix2dostime(t)
538time_t *t;              /* unix time to convert */
539/* Return the Unix time t in DOS format, rounded up to the next two
540   second boundary. */
541{
542  time_t t_even;
543  struct tm *s;         /* result of localtime() */
544
545  t_even = (time_t)(((unsigned long)(*t) + 1) & (~1));
546                                /* Round up to even seconds. */
547  s = localtime(&t_even);       /* Use local time since MSDOS does. */
548  if (s == (struct tm *)NULL) {
549      /* time conversion error; use current time as emergency value
550         (assuming that localtime() does at least accept this value!) */
551      t_even = (time_t)(((unsigned long)time(NULL) + 1) & (~1));
552      s = localtime(&t_even);
553  }
554  return dostime(s->tm_year + 1900, s->tm_mon + 1, s->tm_mday,
555                 s->tm_hour, s->tm_min, s->tm_sec);
556}
557
558int issymlnk(a)
559ulg a;                  /* Attributes returned by filetime() */
560/* Return true if the attributes are those of a symbolic link */
561{
562#ifndef QDOS
563#ifdef S_IFLNK
564#ifdef __human68k__
565  int *_dos_importlnenv(void);
566
567  if (_dos_importlnenv() == NULL)
568    return 0;
569#endif
570  return ((a >> 16) & S_IFMT) == S_IFLNK;
571#else /* !S_IFLNK */
572  return (int)a & 0;    /* avoid warning on unused parameter */
573#endif /* ?S_IFLNK */
574#else
575  return 0;
576#endif
577}
578
579#endif /* !UTIL */
580
581
582#if (!defined(UTIL) && !defined(ZP_NEED_GEN_D2U_TIME))
583   /* There is no need for dos2unixtime() in the ZipUtils' code. */
584#  define ZP_NEED_GEN_D2U_TIME
585#endif
586#if ((defined(OS2) || defined(VMS)) && defined(ZP_NEED_GEN_D2U_TIME))
587   /* OS/2 and VMS use a special solution to handle time-stams of files. */
588#  undef ZP_NEED_GEN_D2U_TIME
589#endif
590#if (defined(W32_STATROOT_FIX) && !defined(ZP_NEED_GEN_D2U_TIME))
591   /* The Win32 stat()-bandaid to fix stat'ing root directories needs
592    * dos2unixtime() to calculate the time-stamps. */
593#  define ZP_NEED_GEN_D2U_TIME
594#endif
595
596#ifdef ZP_NEED_GEN_D2U_TIME
597
598time_t dos2unixtime(dostime)
599ulg dostime;            /* DOS time to convert */
600/* Return the Unix time_t value (GMT/UTC time) for the DOS format (local)
601 * time dostime, where dostime is a four byte value (date in most significant
602 * word, time in least significant word), see dostime() function.
603 */
604{
605  struct tm *t;         /* argument for mktime() */
606  ZCONST time_t clock = time(NULL);
607
608  t = localtime(&clock);
609  t->tm_isdst = -1;     /* let mktime() determine if DST is in effect */
610  /* Convert DOS time to UNIX time_t format */
611  t->tm_sec  = (((int)dostime) <<  1) & 0x3e;
612  t->tm_min  = (((int)dostime) >>  5) & 0x3f;
613  t->tm_hour = (((int)dostime) >> 11) & 0x1f;
614  t->tm_mday = (int)(dostime >> 16) & 0x1f;
615  t->tm_mon  = ((int)(dostime >> 21) & 0x0f) - 1;
616  t->tm_year = ((int)(dostime >> 25) & 0x7f) + 80;
617
618  return mktime(t);
619}
620
621#undef ZP_NEED_GEN_D2U_TIME
622#endif /* ZP_NEED_GEN_D2U_TIME */
623
624
625#ifndef MACOS
626int destroy(f)
627char *f;                /* file to delete */
628/* Delete the file *f, returning non-zero on failure. */
629{
630  return unlink(f);
631}
632
633
634int replace(d, s)
635char *d, *s;            /* destination and source file names */
636/* Replace file *d by file *s, removing the old *s.  Return an error code
637   in the ZE_ class. This function need not preserve the file attributes,
638   this will be done by setfileattr() later.
639 */
640{
641  struct stat t;        /* results of stat() */
642#if defined(CMS_MVS)
643  /* cmsmvs.h defines FOPW_TEMP as memory(hiperspace).  Since memory is
644   * lost at end of run, always do copy instead of rename.
645   */
646  int copy = 1;
647#else
648  int copy = 0;
649#endif
650  int d_exists;
651
652#if defined(VMS) || defined(CMS_MVS)
653  /* stat() is broken on VMS remote files (accessed through Decnet).
654   * This patch allows creation of remote zip files, but is not sufficient
655   * to update them or compress remote files */
656  unlink(d);
657#else /* !(VMS || CMS_MVS) */
658  d_exists = (LSTAT(d, &t) == 0);
659  if (d_exists)
660  {
661    /*
662     * respect existing soft and hard links!
663     */
664    if (t.st_nlink > 1
665# ifdef S_IFLNK
666        || (t.st_mode & S_IFMT) == S_IFLNK
667# endif
668        )
669       copy = 1;
670    else if (unlink(d))
671       return ZE_CREAT;                 /* Can't erase zip file--give up */
672  }
673#endif /* ?(VMS || CMS_MVS) */
674#ifndef CMS_MVS
675  if (!copy) {
676      if (rename(s, d)) {               /* Just move s on top of d */
677          copy = 1;                     /* failed ? */
678#if !defined(VMS) && !defined(ATARI) && !defined(AZTEC_C)
679#if !defined(CMS_MVS) && !defined(RISCOS) && !defined(QDOS)
680    /* For VMS, ATARI, AMIGA Aztec, VM_CMS, MVS, RISCOS,
681       always assume that failure is EXDEV */
682          if (errno != EXDEV
683#  ifdef THEOS
684           && errno != EEXIST
685#  else
686#    ifdef ENOTSAM
687           && errno != ENOTSAM /* Used at least on Turbo C */
688#    endif
689#  endif
690              ) return ZE_CREAT;
691#endif /* !CMS_MVS && !RISCOS */
692#endif /* !VMS && !ATARI && !AZTEC_C */
693      }
694  }
695#endif /* !CMS_MVS */
696
697  if (copy) {
698    FILE *f, *g;        /* source and destination files */
699    int r;              /* temporary variable */
700
701#ifdef RISCOS
702    if (SWI_OS_FSControl_26(s,d,0xA1)!=NULL) {
703#endif
704
705    if ((f = fopen(s, FOPR)) == NULL) {
706      fprintf(stderr," replace: can't open %s\n", s);
707      return ZE_TEMP;
708    }
709    if ((g = fopen(d, FOPW)) == NULL)
710    {
711      fclose(f);
712      return ZE_CREAT;
713    }
714    r = fcopy(f, g, (ulg)-1L);
715    fclose(f);
716    if (fclose(g) || r != ZE_OK)
717    {
718      unlink(d);
719      return r ? (r == ZE_TEMP ? ZE_WRITE : r) : ZE_WRITE;
720    }
721    unlink(s);
722#ifdef RISCOS
723    }
724#endif
725  }
726  return ZE_OK;
727}
728#endif /* !MACOS */
729
730
731int getfileattr(f)
732char *f;                /* file path */
733/* Return the file attributes for file f or 0 if failure */
734{
735#ifdef __human68k__
736  struct _filbuf buf;
737
738  return _dos_files(&buf, f, 0xff) < 0 ? 0x20 : buf.atr;
739#else
740  struct stat s;
741
742  return SSTAT(f, &s) == 0 ? (int) s.st_mode : 0;
743#endif
744}
745
746
747int setfileattr(f, a)
748char *f;                /* file path */
749int a;                  /* attributes returned by getfileattr() */
750/* Give the file f the attributes a, return non-zero on failure */
751{
752#if defined(TOPS20) || defined (CMS_MVS)
753  return 0;
754#else
755#ifdef __human68k__
756  return _dos_chmod(f, a) < 0 ? -1 : 0;
757#else
758  return chmod(f, a);
759#endif
760#endif
761}
762
763#ifndef VMS /* VMS-specific function is in VMS.C. */
764
765char *tempname(zip)
766char *zip;              /* path name of zip file to generate temp name for */
767
768/* Return a temporary file name in its own malloc'ed space, using tempath. */
769{
770  char *t = zip;   /* malloc'ed space for name (use zip to avoid warning) */
771
772#ifdef CMS_MVS
773  if ((t = malloc(strlen(tempath)+L_tmpnam+2)) == NULL)
774    return NULL;
775#  ifdef VM_CMS
776  tmpnam(t);
777  /* Remove filemode and replace with tempath, if any. */
778  /* Otherwise A-disk is used by default */
779  *(strrchr(t, ' ')+1) = '\0';
780  if (tempath!=NULL)
781     strcat(t, tempath);
782  return t;
783#  else   /* !VM_CMS */
784  /* For MVS */
785  tmpnam(t);
786  if (tempath != NULL)
787  {
788    int l1 = strlen(t);
789    char *dot;
790    if (*t == '\'' && *(t+l1-1) == '\'' && (dot = strchr(t, '.')))
791    {
792      /* MVS and not OE.  tmpnam() returns quoted string of 5 qualifiers.
793       * First is HLQ, rest are timestamps.  User can only replace HLQ.
794       */
795      int l2 = strlen(tempath);
796      if (strchr(tempath, '.') || l2 < 1 || l2 > 8)
797        ziperr(ZE_PARMS, "On MVS and not OE, tempath (-b) can only be HLQ");
798      memmove(t+1+l2, dot, l1+1-(dot-t));  /* shift dot ready for new hlq */
799      memcpy(t+1, tempath, l2);            /* insert new hlq */
800    }
801    else
802    {
803      /* MVS and probably OE.  tmpnam() returns filename based on TMPDIR,
804       * no point in even attempting to change it.  User should modify TMPDIR
805       * instead.
806       */
807      zipwarn("MVS, assumed to be OE, change TMPDIR instead of option -b: ",
808              tempath);
809    }
810  }
811  return t;
812#  endif  /* !VM_CMS */
813#else /* !CMS_MVS */
814#ifdef TANDEM
815  char cur_subvol [FILENAME_MAX];
816  char temp_subvol [FILENAME_MAX];
817  char *zptr;
818  char *ptr;
819  char *cptr = &cur_subvol[0];
820  char *tptr = &temp_subvol[0];
821  short err;
822  FILE *tempf;
823  int attempts;
824
825  t = (char *)malloc(NAMELEN); /* malloc here as you cannot free */
826                               /* tmpnam allocated storage later */
827
828  zptr = strrchr(zip, TANDEM_DELIMITER);
829
830  if (zptr != NULL) {
831    /* ZIP file specifies a Subvol so make temp file there so it can just
832       be renamed at end */
833
834    *tptr = *cptr = '\0';
835    strcat(cptr, getenv("DEFAULTS"));
836
837    strncat(tptr, zip, _min(FILENAME_MAX, (zptr - zip)) ); /* temp subvol */
838    strncat(t, zip, _min(NAMELEN, ((zptr - zip) + 1)) );   /* temp stem   */
839
840    err = chvol(tptr);
841    ptr = t + strlen(t);  /* point to end of stem */
842  }
843  else
844    ptr = t;
845
846  /* If two zips are running in same subvol then we can get contention problems
847     with the temporary filename.  As a work around we attempt to create
848     the file here, and if it already exists we get a new temporary name */
849
850  attempts = 0;
851  do {
852    attempts++;
853    tmpnam(ptr);  /* Add filename */
854    tempf = fopen(ptr, FOPW_TMP);    /* Attempt to create file */
855  } while (tempf == NULL && attempts < 100);
856
857  if (attempts >= 100) {
858    ziperr(ZE_TEMP, "Could not get unique temp file name");
859  }
860  fclose(tempf);
861
862  if (zptr != NULL) {
863    err = chvol(cptr);  /* Put ourself back to where we came in */
864  }
865
866  return t;
867
868#else /* !CMS_MVS && !TANDEM */
869/*
870 * Do something with TMPDIR, TMP, TEMP ????
871 */
872  if (tempath != NULL)
873  {
874    if ((t = malloc(strlen(tempath)+12)) == NULL)
875      return NULL;
876    strcpy(t, tempath);
877#if (!defined(VMS) && !defined(TOPS20))
878#  ifdef MSDOS
879    {
880      char c = (char)lastchar(t);
881      if (c != '/' && c != ':' && c != '\\')
882        strcat(t, "/");
883    }
884#  else
885#    ifdef AMIGA
886    {
887      char c = (char)lastchar(t);
888      if (c != '/' && c != ':')
889        strcat(t, "/");
890    }
891#    else /* !AMIGA */
892#      ifdef RISCOS
893    if (lastchar(t) != '.')
894      strcat(t, ".");
895#      else /* !RISCOS */
896#        ifdef QDOS
897    if (lastchar(t) != '_')
898      strcat(t, "_");
899#        else
900    if (lastchar(t) != '/')
901      strcat(t, "/");
902#        endif /* ?QDOS */
903#      endif /* ?RISCOS */
904#    endif  /* ?AMIGA */
905#  endif /* ?MSDOS */
906#endif /* !VMS && !TOPS20 */
907  }
908  else
909  {
910    if ((t = malloc(12)) == NULL)
911      return NULL;
912    *t = 0;
913  }
914#ifdef NO_MKTEMP
915  {
916    char *p = t + strlen(t);
917    sprintf(p, "%08lx", (ulg)time(NULL));
918    return t;
919  }
920#else
921  strcat(t, "ziXXXXXX"); /* must use lowercase for Linux dos file system */
922  return mktemp(t);
923#endif /* NO_MKTEMP */
924#endif /* TANDEM */
925#endif /* CMS_MVS */
926}
927
928#endif /* !VMS */
929
930int fcopy(f, g, n)
931FILE *f, *g;            /* source and destination files */
932ulg n;                  /* number of bytes to copy or -1 for all */
933/* Copy n bytes from file *f to file *g, or until EOF if (long)n == -1.
934   Return an error code in the ZE_ class. */
935{
936  char *b;              /* malloc'ed buffer for copying */
937  extent k;             /* result of fread() */
938  ulg m;                /* bytes copied so far */
939
940  if ((b = malloc(CBSZ)) == NULL)
941    return ZE_MEM;
942  m = 0;
943  while (n == (ulg)(-1L) || m < n)
944  {
945    if ((k = fread(b, 1, n == (ulg)(-1) ?
946                   CBSZ : (n - m < CBSZ ? (extent)(n - m) : CBSZ), f)) == 0)
947    {
948      if (ferror(f))
949      {
950        free((zvoid *)b);
951        return ZE_READ;
952      }
953      else
954        break;
955    }
956    if (fwrite(b, 1, k, g) != k)
957    {
958      free((zvoid *)b);
959      fprintf(stderr," fcopy: write error\n");
960      return ZE_TEMP;
961    }
962    m += k;
963  }
964  free((zvoid *)b);
965  return ZE_OK;
966}
967
968#ifdef NO_RENAME
969int rename(from, to)
970ZCONST char *from;
971ZCONST char *to;
972{
973    unlink(to);
974    if (link(from, to) == -1)
975        return -1;
976    if (unlink(from) == -1)
977        return -1;
978    return 0;
979}
980
981#endif /* NO_RENAME */
982
983
984#ifdef ZMEM
985
986/************************/
987/*  Function memset()   */
988/************************/
989
990/*
991 * memset - for systems without it
992 *  bill davidsen - March 1990
993 */
994
995char *
996memset(buf, init, len)
997register char *buf;     /* buffer loc */
998register int init;      /* initializer */
999register unsigned int len;   /* length of the buffer */
1000{
1001    char *start;
1002
1003    start = buf;
1004    while (len--) *(buf++) = init;
1005    return(start);
1006}
1007
1008
1009/************************/
1010/*  Function memcpy()   */
1011/************************/
1012
1013char *
1014memcpy(dst,src,len)             /* v2.0f */
1015register char *dst, *src;
1016register unsigned int len;
1017{
1018    char *start;
1019
1020    start = dst;
1021    while (len--)
1022        *dst++ = *src++;
1023    return(start);
1024}
1025
1026
1027/************************/
1028/*  Function memcmp()   */
1029/************************/
1030
1031int
1032memcmp(b1,b2,len)                     /* jpd@usl.edu -- 11/16/90 */
1033register char *b1, *b2;
1034register unsigned int len;
1035{
1036
1037    if (len) do {       /* examine each byte (if any) */
1038      if (*b1++ != *b2++)
1039        return (*((uch *)b1-1) - *((uch *)b2-1));  /* exit when miscompare */
1040    } while (--len);
1041
1042    return(0);          /* no miscompares, yield 0 result */
1043}
1044
1045#endif  /* ZMEM */
1046