1/*
2  Copyright (c) 1990-1999 Info-ZIP.  All rights reserved.
3
4  See the accompanying file LICENSE, version 1999-Oct-05 or later
5  (the contents of which are also included in zip.h) for terms of use.
6  If, for some reason, both of these files are missing, the Info-ZIP license
7  also may be found at:  ftp://ftp.cdrom.com/pub/infozip/license.html
8*/
9/*---------------------------------------------------------------------------
10
11  theos.c (zip)
12
13  Contribution by Jean-Michel Dubois. 20-Jun-1995, 20-Dec-98
14
15  THEOS specific extra informations
16
17  ---------------------------------------------------------------------------*/
18
19
20#include <stdio.h>
21
22#ifndef UTIL
23
24#include "zip.h"
25
26#include <stdlib.h>
27#include <ctype.h>
28#include <time.h>
29#include <sc.h>
30#include <direct.h>
31#include <sys/utime.h>
32
33#define opendir(a) _opendir(a)
34extern struct dirent* _opendir(char* fname);
35
36#define PAD 0
37
38#define RET_ERROR 1
39#define RET_SUCCESS 0
40#define RET_EOF 0
41
42extern char *label;
43local ulg label_time = 0;
44local ulg label_mode = 0;
45local time_t label_utim = 0;
46
47/* match from Phase One Systems */
48
49int match(char *s, char *p)    /*S Returns non-zero if string matches
50                                   the literal mask */
51{
52  int matched, k;
53
54  if (!(*p))
55    return 1;
56  for(;;) {
57    if ( (!(*s)) && (!(*p)) )
58      return(1);
59    else if ( !(*p) )
60      return(0);
61    else if (*p == '*') {
62      if (!*(p+1))
63        return(1);
64      k=0;
65      do {
66        matched = match(s+k,p+1);
67        k++;
68      } while ( (!matched) && *(s+k));
69      return(matched);
70    } else if (*p == '@') {
71      if (!((*s >= 'a' && *s <= 'z')
72          ||(*s >= 'A' && *s <= 'Z')))
73        return(0);
74    } else if (*p == '#') {
75      if (*s < '0' || *s > '9')
76        return(0);
77    } else if (*p != '?') {
78      if (tolower(*s) != tolower(*p))
79        return(0);
80    }
81    s++; p++;
82  }
83}
84
85local char *readd(d)
86DIR *d; /* directory stream to read from */
87/* Return a pointer to the next name in the directory stream d, or NULL if
88   no more entries or an error occurs. */
89{
90  struct dirent *e;
91
92  e = readdir(d);
93  return e == NULL ? (char *) NULL : e->d_name;
94}
95
96/* check if file is a member of a library */
97
98int ismember(char* path)
99{
100    char* p;
101
102    if ((p = strrchr(path, '/')) == NULL)
103        p = path;
104    return ((p = strchr(p, '.')) && (p = strchr(p + 1, '.')));
105}
106
107/* extract library name from a file name */
108
109char* libname(char* path)
110{
111    char* p;
112    static char lib[FILENAME_MAX];
113    char drive[3];
114
115    strcpy(lib, path);
116    if (p = strrchr(lib, ':')) {
117        strncpy(drive, p, 2);
118        drive[2] = '\0';
119        *p = '\0' ;
120    } else
121        drive[0] = '\0';
122
123    if ((p = strrchr(lib, '/')) == NULL)
124        p = lib;
125
126    p = strchr(p, '.');
127    p = strchr(p + 1, '.');
128    *p = 0;
129    strcat(lib, drive);
130    return lib;
131}
132
133int procname(n, caseflag)
134char *n;                /* name to process */
135int caseflag;           /* true to force case-sensitive match */
136/* Process a name or sh expression to operate on (or exclude).  Return
137   an error code in the ZE_ class. */
138{
139  char *a;              /* path and name for recursion */
140  DIR *d;               /* directory stream from opendir() */
141  char *e;              /* pointer to name from readd() */
142  int m;                /* matched flag */
143  char *p;              /* path for recursion */
144  struct stat s;        /* result of stat() */
145  struct zlist *z;      /* steps through zfiles list */
146  struct flist *f;      /* steps through files list */
147  char* path;           /* full name */
148  char drive[3];        /* drive name */
149  int recursion;        /* save recurse flag */
150
151  if (strcmp(n, "-") == 0)   /* if compressing stdin */
152    return newname(n, 0, caseflag);
153  else if (LSSTAT(n, &s)) {
154    /* Not a file or directory--search for shell expression in zip file */
155    p = ex2in(n, 0, (int *)NULL);       /* shouldn't affect matching chars */
156    m = 1;
157    for (z = zfiles; z != NULL; z = z->nxt) {
158      if (match(z->iname, p))
159      {
160        z->mark = pcount ? filter(z->zname, caseflag) : 1;
161        if (verbose)
162            fprintf(mesg, "zip diagnostic: %scluding %s\n",
163               z->mark ? "in" : "ex", z->name);
164        m = 0;
165      }
166    }
167    free((zvoid *)p);
168    return m ? ZE_MISS : ZE_OK;
169  }
170
171  /* Live name--use if file, recurse if directory or library */
172  if (S_ISREG(s.st_mode)) {
173    if ((path = malloc(strlen(n) + 2)) == NULL)
174      return ZE_MEM;
175
176    strcpy(path, n);
177
178    /* if member name, include library name before any member name */
179    if (ismember(path)) {
180      strcpy(path, libname(path));
181      /* mask recursion flag to avoid endless loop recursion
182       * if -r is used with member names
183       */
184      recursion = recurse;
185      recurse = FALSE;
186      if ((m = procname(path, caseflag)) != ZE_OK)   /* recurse on name */
187      {
188        if (m == ZE_MISS)
189          zipwarn("name not matched: ", path);
190        else
191          ziperr(m, a);
192      }
193      /* restore recursion flag */
194      recurse = recursion;
195    }
196
197    strcpy(path, n);
198
199    if ((p = strchr(path, ':')) != NULL) {
200      p[2] = '\0';
201      strcpy(drive, p);
202    } else
203      drive[0] = '\0';
204
205    /* remove trailing dot in flat file name */
206    p = strend(path) - 1;
207    if (*p == '.')
208      *p = '\0';
209
210    strcat(path, drive);
211    /* add or remove name of file */
212    if ((m = newname(path, 0, caseflag)) != ZE_OK) {
213      free(path);
214      return m;
215    }
216    free(path);
217  } else if (S_ISLIB(s.st_mode)) {
218    if ((path = malloc(strlen(n) + 2)) == NULL)
219      return ZE_MEM;
220
221    strcpy(path, n);
222
223    if ((p = strchr(path, ':')) != NULL) {
224      p[2] = '\0';
225      strcpy(drive, p);
226    } else
227      drive[0] = '\0';
228
229    /* add a trailing dot in flat file name... */
230    p = strend(path) - 1;
231    if (*p != '/')
232      strcat(path, "/");
233    p = strend(path);
234    /* ... then add drive name */
235    strcpy(p, drive);
236
237    /* don't include the library name twice... or more */
238    for (f = found; f != NULL; f = f->nxt) {
239      if (! stricmp(path, f->name)) {
240        free(path);
241        return ZE_OK;
242      }
243    }
244    /* add or remove name of library */
245    if ((m = newname(path, 0, caseflag)) != ZE_OK) {
246      free(path);
247      return m;
248    }
249    /* recurse into library if required */
250    strcpy(p - 1, ".*");
251    strcat(p, drive);
252    if (recurse && diropen(path) == 0)
253    {
254      while ((e = dirread()) != NULL) {
255        if (strcmp(e, ".") && strcmp(e, ".."))
256        {
257          if (*drive == '\0')
258            *strchr(e, ':') = '\0';
259          if ((a = malloc(strlen(e) + 1)) == NULL)
260          {
261            dirclose();
262            free((zvoid *)p);
263            return ZE_MEM;
264          }
265          strcpy(a, e);
266          if ((m = procname(a, caseflag)) != ZE_OK)   /* recurse on name */
267          {
268            if (m == ZE_MISS)
269              zipwarn("name not matched: ", a);
270            else
271              ziperr(m, a);
272          }
273          free((zvoid *)a);
274        }
275      }
276      dirclose();
277    }
278    free(path);
279  } else {
280    /* Add trailing / to the directory name */
281    if ((p = malloc(strlen(n)+2)) == NULL)
282      return ZE_MEM;
283    if (strcmp(n, ".") == 0) {
284      *p = '\0';  /* avoid "./" prefix and do not create zip entry */
285    } else {
286      strcpy(p, n);
287      a = p + strlen(p);
288      if (a[-1] != '/')
289        strcpy(a, "/");
290      if (dirnames && (m = newname(p, 1, caseflag)) != ZE_OK) {
291        free((zvoid *)p);
292        return m;
293      }
294    }
295    /* recurse into directory */
296    if (recurse && (d = opendir(n)) != NULL)
297    {
298      while ((e = readd(d)) != NULL) {
299        if (strcmp(e, ".") && strcmp(e, ".."))
300        {
301          if ((a = malloc(strlen(p) + strlen(e) + 1)) == NULL)
302          {
303            closedir(d);
304            free((zvoid *)p);
305            return ZE_MEM;
306          }
307          strcat(strcpy(a, p), e);
308          if ((m = procname(a, caseflag)) != ZE_OK)   /* recurse on name */
309          {
310            if (m == ZE_MISS)
311              zipwarn("name not matched: ", a);
312            else
313              ziperr(m, a);
314          }
315          free((zvoid *)a);
316        }
317      }
318      closedir(d);
319    }
320    free((zvoid *)p);
321  }
322  return ZE_OK;
323}
324
325char *ex2in(x, isdir, pdosflag)
326char *x;                /* external file name */
327int isdir;              /* input: x is a directory */
328int *pdosflag;          /* output: force MSDOS file attributes? */
329/* Convert the external file name to a zip file name, returning the malloc'ed
330   string or NULL if not enough memory. */
331{
332  char *n;              /* internal file name (malloc'ed) */
333  char *t;              /* shortened name */
334  char *p;
335  int dosflag;
336
337  dosflag = dosify;  /* default for non-DOS and non-OS/2 */
338
339  /* Find starting point in name before doing malloc */
340  for (t = x; *t == '/'; t++)
341    ;
342
343  /* Make changes, if any, to the copied name (leave original intact) */
344  if (!pathput)
345    t = last(t, '/');
346
347  /* Malloc space for internal name and copy it */
348  if ((n = malloc(strlen(t) + 1)) == NULL)
349    return NULL;
350
351  strcpy(n, t);
352  if (p = strchr(n, ':'))
353    *p = '\0';
354  for (p = n; *p = tolower(*p); p++);
355
356  if (isdir == 42) return n;      /* avoid warning on unused variable */
357
358  if (dosify)
359    msname(n);
360  /* Returned malloc'ed name */
361  if (pdosflag)
362    *pdosflag = dosflag;
363  return n;
364}
365
366char *in2ex(n)
367char *n;                /* internal file name */
368/* Convert the zip file name to an external file name, returning the malloc'ed
369   string or NULL if not enough memory. */
370{
371  char *x;              /* external file name */
372
373  if ((x = malloc(strlen(n) + 1 + PAD)) == NULL)
374    return NULL;
375  strcpy(x, n);
376  return x;
377}
378
379/*
380 * XXX use ztimbuf in both POSIX and non POSIX cases ?
381 */
382void stamp(f, d)
383char *f;                /* name of file to change */
384ulg d;                  /* dos-style time to change it to */
385/* Set last updated and accessed time of file f to the DOS time d. */
386{
387  struct utimbuf u;     /* argument for utime()  const ?? */
388
389  /* Convert DOS time to time_t format in u */
390  u.actime = u.modtime = dos2unixtime(d);
391  utime(f, &u);
392}
393
394ulg filetime(f, a, n, t)
395char *f;                /* name of file to get info on */
396ulg *a;                 /* return value: file attributes */
397long *n;                /* return value: file size */
398iztimes *t;             /* return value: access, modific. and creation times */
399/* If file *f does not exist, return 0.  Else, return the file's last
400   modified date and time as an MSDOS date and time.  The date and
401   time is returned in a long with the date most significant to allow
402   unsigned integer comparison of absolute times.  Also, if a is not
403   a NULL pointer, store the file attributes there, with the high two
404   bytes being the Unix attributes, and the low byte being a mapping
405   of that to DOS attributes. Bits 8 to 15 contains native THEOS protection
406   code. If n is not NULL, store the file size there.  If t is not NULL,
407   the file's access, modification and creation times are stored there as
408   UNIX time_t values. If f is "-", use standard input as the file. If f is
409   a device, return a file size of -1 */
410{
411  struct stat s;        /* results of stat() */
412  /* from FNMAX to malloc - 11/8/04 EG */
413  char *name;
414  int len = strlen(f);
415
416  if (f == label) {
417    if (a != NULL)
418      *a = label_mode;
419    if (n != NULL)
420      *n = -2L; /* convention for a label name */
421    if (t != NULL)
422      t->atime = t->mtime = t->ctime = label_utim;
423    return label_time;
424  }
425  if ((name = malloc(len + 1)) == NULL) {
426    ZIPERR(ZE_MEM, "filetime");
427  }
428  strcpy(name, f);
429
430  if (name[len - 1] == '/' || name[len - 1] == '.')
431    name[len - 1] = '\0';
432
433  /* not all systems allow stat'ing a file with / appended */
434  if (strcmp(f, "-") == 0) {
435    if (fstat(fileno(stdin), &s) != 0) {
436      free(name);
437      error("fstat(stdin)");
438    }
439  } else if (LSSTAT(name, &s) != 0) {
440    /* Accept about any file kind including directories
441     * (stored with trailing / with -r option)
442     */
443    free(name);
444    return 0;
445  }
446  free(name);
447
448  if (a != NULL) {
449    *a = ((ulg)s.st_mode << 16) | !(s.st_mode & S_IWRITE);
450    if ((s.st_mode & S_IFMT) == S_IFDIR
451     || (s.st_mode & S_IFMT) == S_IFLIB) {
452      *a |= MSDOS_DIR_ATTR;
453    }
454    /* Map Theos' hidden attribute to DOS's hidden attribute */
455    if (!(st.st_protect & 0x80))
456      *a |= MSDOS_HIDDEN_ATTR;
457    *a |= ((ulg) s.st_protect) << 8;
458  }
459  if (n != NULL)
460    *n = (s.st_mode & S_IFMT) == S_IFREG ? s.st_size : -1L;
461  if (t != NULL) {
462    t->atime = s.st_atime;
463    t->mtime = s.st_mtime;
464    t->ctime = t->mtime;   /* best guess, (s.st_ctime: last status change!!) */
465  }
466  return unix2dostime(&s.st_mtime);
467}
468/*
469 *      Get file THEOS attributes and store them into extent fields.
470 *      On error leave z intact.
471 */
472
473/*
474*   Extra record format
475*   ===================
476*   signature       (2 bytes)   = 'T','h'
477*   size            (2 bytes)
478*   flags           (1 byte)
479*   filesize        (4 bytes)
480*   keylen          (2 bytes)
481*   reclen          (2 bytes)
482*   filegrow        (1 byte)
483*   reserved        (4 bytes)
484*/
485
486#define EB_L_THSIZE     4
487#define EB_L_TH_SIZE    14
488
489int set_extra_field(z, z_utim)
490  struct zlist *z;
491  iztimes *z_utim;
492  /* store full data in local header but just modification time stamp info
493     in central header */
494{
495  char *extra = NULL;
496  char *p;
497  char c;
498  struct stat st;
499  int status;
500
501  if (status = stat(z->name, &st)) {
502    p = &z->name[strlen(z->name) - 1];
503    if (*p == '/' || *p == '.') {
504      c = *p;
505      *p = '\0';
506      status = stat(z->name, &st);
507      *p = c;
508    }
509#ifdef DEBUG
510    fprintf(stderr, "set_extra_field: stat for file %s:\n status = %d\n",
511        z->name, status);
512#endif
513    if (status)
514      return RET_ERROR;
515  }
516
517  if ((extra = malloc(EB_L_TH_SIZE)) == NULL ) {
518    fprintf(stderr, "set_extra_field: Insufficient memory.\n" );
519    return RET_ERROR;
520  }
521
522
523  extra[0] = 'T';
524  extra[1] = 'h';
525  extra[2] = EB_L_TH_SIZE;
526  extra[3] = EB_L_TH_SIZE >> 8;
527  extra[4] = 0;
528  extra[5] = st.st_size;
529  extra[6] = st.st_size >> 8;
530  extra[7] = st.st_size >> 16;
531  extra[8] = st.st_size >> 24;
532  extra[9] = st.st_org;
533  extra[10] = st.st_rlen;
534  extra[11] = st.st_rlen >> 8;
535  extra[12] = st.st_klen;
536  extra[13] = st.st_klen >> 8;
537  extra[14] = st.st_grow;
538  extra[15] = st.st_protect;
539  extra[16] = 0;
540  extra[17] = 0;
541  z->ext = z->cext = EB_L_TH_SIZE + EB_HEADSIZE;
542  z->extra = z->cextra = extra;
543  return RET_SUCCESS;
544}
545#endif
546
547/******************************/
548/*  Function version_local()  */
549/******************************/
550
551void version_local()
552{
553  printf("Compiled with THEOS C 5.28 for THEOS 4.x on %s %s.\n\n",
554    __DATE__, __TIME__);
555}
556
557