1/*
2  Copyright (c) 1990-2002 Info-ZIP.  All rights reserved.
3
4  See the accompanying file LICENSE, version 2000-Apr-09 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 <stdlib.h>
10#include <string.h>
11#include "zip.h"
12
13#ifndef UTIL
14
15#define PAD 0
16#define PATH_END '/'
17
18
19local int wild_recurse(char *whole, char *wildtail);
20local int uxtime2acornftime(unsigned *pexadr, unsigned *pldadr, time_t ut);
21
22extern char *label;
23local ulg label_time = 0;
24local ulg label_mode = 0;
25local time_t label_utim = 0;
26
27char *readd(DIR *d)
28/* Return a pointer to the next name in the directory stream d, or NULL if
29   no more entries or an error occurs. */
30{
31   struct dirent *e;
32
33   e = readdir(d);
34   return (e == NULL ? (char *) NULL : e->d_name);
35}
36
37/* What we have here is a mostly-generic routine using opend()/readd() and */
38/* isshexp()/MATCH() to find all the files matching a multi-part filespec  */
39/* using the portable pattern syntax.  It shouldn't take too much fiddling */
40/* to make it usable for any other platform that has directory hierarchies */
41/* but no shell-level pattern matching.  It works for patterns throughout  */
42/* the pathname, such as "foo:*.?/source/x*.[ch]".                         */
43
44/* whole is a pathname with wildcards, wildtail points somewhere in the  */
45/* middle of it.  All wildcards to be expanded must come AFTER wildtail. */
46
47local int wild_recurse(whole, wildtail) char *whole; char *wildtail;
48{
49    DIR *dir;
50    char *subwild, *name, *newwhole = NULL, *glue = NULL, plug = 0, plug2;
51    ush newlen, amatch = 0;
52    struct stat statb;
53    int disk_not_mounted=0;
54    int e = ZE_MISS;
55
56    if (!isshexp(wildtail)) {
57        if (stat(whole,&statb)==0 && (statb.st_mode & S_IREAD)!=0) {
58            return procname(whole, 0);
59        } else
60            return ZE_MISS;                     /* woops, no wildcards! */
61    }
62
63    /* back up thru path components till existing dir found */
64    do {
65        name = wildtail + strlen(wildtail) - 1;
66        for (;;)
67            if (name-- <= wildtail || *name == '.') {
68                subwild = name + 1;
69                plug2 = *subwild;
70                *subwild = 0;
71                break;
72            }
73        if (glue)
74            *glue = plug;
75        glue = subwild;
76        plug = plug2;
77        dir = opendir(whole);
78    } while (!dir && !disk_not_mounted && subwild > wildtail);
79    wildtail = subwild;                 /* skip past non-wild components */
80
81    if ((subwild = strchr(wildtail + 1, '.')) != NULL) {
82        /* this "+ 1" dodges the   ^^^ hole left by *glue == 0 */
83        *(subwild++) = 0;               /* wildtail = one component pattern */
84        newlen = strlen(whole) + strlen(subwild) + 32;
85    } else
86        newlen = strlen(whole) + 31;
87    if (!dir || !(newwhole = malloc(newlen))) {
88        if (glue)
89            *glue = plug;
90        e = dir ? ZE_MEM : ZE_MISS;
91        goto ohforgetit;
92    }
93    strcpy(newwhole, whole);
94    newlen = strlen(newwhole);
95    if (glue)
96        *glue = plug;                           /* repair damage to whole */
97    if (!isshexp(wildtail)) {
98        e = ZE_MISS;                            /* non-wild name not found */
99        goto ohforgetit;
100    }
101
102    while (name = readd(dir)) {
103        if (MATCH(wildtail, name, 0)) {
104            strcpy(newwhole + newlen, name);
105            if (subwild) {
106                name = newwhole + strlen(newwhole);
107                *(name++) = '.';
108                strcpy(name, subwild);
109                e = wild_recurse(newwhole, name);
110            } else
111                e = procname(newwhole, 0);
112            newwhole[newlen] = 0;
113            if (e == ZE_OK)
114                amatch = 1;
115            else if (e != ZE_MISS)
116                break;
117        }
118    }
119
120  ohforgetit:
121    if (dir) closedir(dir);
122    if (subwild) *--subwild = '.';
123    if (newwhole) free(newwhole);
124    if (e == ZE_MISS && amatch)
125        e = ZE_OK;
126    return e;
127}
128
129int wild(p)
130char *p;
131{
132  char *path;
133  int toret;
134
135  /* special handling of stdin request */
136  if (strcmp(p, "-") == 0)   /* if compressing stdin */
137    return newname(p, 0, 0);
138
139  path=p;
140  if (strchr(p, ':')==NULL && *p!='@') {
141    if (!(path=malloc(strlen(p)+3))) {
142      return ZE_MEM;
143    }
144    strcpy(path,"@.");
145    strcat(path,p);
146  }
147
148  toret=wild_recurse(path, path);
149
150  if (path!=p) {
151    free(path);
152  }
153  return toret;
154}
155
156int procname(n, caseflag)
157char *n;                /* name to process */
158int caseflag;           /* true to force case-sensitive match */
159/* Process a name or sh expression to operate on (or exclude).  Return
160   an error code in the ZE_ class. */
161{
162  char *a;              /* path and name for recursion */
163  DIR *d;               /* directory stream from opendir() */
164  char *e;              /* pointer to name from readd() */
165  int m;                /* matched flag */
166  char *p;              /* path for recursion */
167  struct stat s;        /* result of stat() */
168  struct zlist far *z;  /* steps through zfiles list */
169
170  if (strcmp(n, "-") == 0)   /* if compressing stdin */
171    return newname(n, 0, caseflag);
172  else if (LSSTAT(n, &s))
173  {
174    /* Not a file or directory--search for shell expression in zip file */
175    p = ex2in(n, 0, (int *)NULL);       /* shouldn't affect matching chars */
176    m = 1;
177    for (z = zfiles; z != NULL; z = z->nxt) {
178      if (MATCH(p, z->iname, caseflag))
179      {
180        z->mark = pcount ? filter(z->zname, caseflag) : 1;
181        if (verbose)
182            fprintf(mesg, "zip diagnostic: %scluding %s\n",
183               z->mark ? "in" : "ex", z->name);
184        m = 0;
185      }
186    }
187    free((zvoid *)p);
188    return m ? ZE_MISS : ZE_OK;
189  }
190
191  /* Live name--use if file, recurse if directory */
192  if ((s.st_mode & S_IFDIR) == 0)
193  {
194    /* add or remove name of file */
195    if ((m = newname(n, 0, caseflag)) != ZE_OK)
196      return m;
197  } else {
198    /* Add trailing / to the directory name */
199    if ((p = malloc(strlen(n)+2)) == NULL)
200      return ZE_MEM;
201    if (strcmp(n, ".") == 0) {
202      *p = '\0';  /* avoid "./" prefix and do not create zip entry */
203    } else {
204      strcpy(p, n);
205      a = p + strlen(p);
206      if (a[-1] != '.')
207        strcpy(a, ".");
208      if (dirnames && (m = newname(p, 1, caseflag)) != ZE_OK) {
209        free((zvoid *)p);
210        return m;
211      }
212    }
213    /* recurse into directory */
214    if (recurse && (d = opendir(n)) != NULL)
215    {
216      while ((e = readd(d)) != NULL) {
217        if (strcmp(e, ".") && strcmp(e, ".."))
218        {
219          if ((a = malloc(strlen(p) + strlen(e) + 1)) == NULL)
220          {
221            closedir(d);
222            free((zvoid *)p);
223            return ZE_MEM;
224          }
225          strcat(strcpy(a, p), e);
226          if ((m = procname(a, caseflag)) != ZE_OK)   /* recurse on name */
227          {
228            if (m == ZE_MISS)
229              zipwarn("name not matched: ", a);
230            else
231              ziperr(m, a);
232          }
233          free((zvoid *)a);
234        }
235      }
236      closedir(d);
237    }
238    free((zvoid *)p);
239  } /* (s.st_mode & S_IFDIR) == 0) */
240  return ZE_OK;
241}
242
243char *ex2in(x, isdir, pdosflag)
244char *x;                /* external file name */
245int isdir;              /* input: x is a directory */
246int *pdosflag;          /* output: force MSDOS file attributes? */
247/* Convert the external file name to a zip file name, returning the malloc'ed
248   string or NULL if not enough memory. */
249{
250  char *n;              /* internal file name (malloc'ed) */
251  char *t;              /* shortened name */
252  char *tmp;
253  int dosflag;
254  char *lastlastdir=NULL;   /* pointer to 2 dirs before... */
255  char *lastdir=NULL;       /* pointer to last dir... */
256
257  /* Malloc space for internal name and copy it */
258  if ((tmp = malloc(strlen(x) + 1)) == NULL)
259    return NULL;
260  strcpy(tmp, x);
261
262  dosflag = dosify; /* default for non-DOS and non-OS/2 */
263
264  /* Find starting point in name before doing malloc */
265  for(t=tmp;*t;t++) {
266    if (*t=='/') {
267      *t='.';
268    }
269    else if (*t=='.') {
270      *t='/';
271      lastlastdir=lastdir;
272      lastdir=t+1;
273    }
274  }
275
276  t=strchr(tmp,'$');      /* skip FS name */
277  if (t!=NULL)
278    t+=2;                 /* skip '.' after '$' */
279  else
280    t=tmp;
281  if (*t=='@')            /* skip @. at the beginning of filenames */
282    t+=2;
283
284  /* Make changes, if any, to the copied name (leave original intact) */
285
286  /* return a pointer to '\0' if the file is a directory with the same
287     same name as an extension to swap (eg. 'c', 'h', etc.) */
288  if (isdir && exts2swap!=NULL) {
289    if (lastlastdir==NULL)
290      lastlastdir=t;
291    if (checkext(lastlastdir)) {
292      free((void *)tmp);
293      n=malloc(1);
294      if (n!=NULL)
295        *n='\0';
296      return n;
297    }
298  }
299
300  if (exts2swap!=NULL && lastdir!=NULL) {
301    if (lastlastdir==NULL)
302      lastlastdir=t;
303    if (checkext(lastlastdir)) {
304      if (swapext(lastlastdir,lastdir-1)) {
305        free((void *)tmp);
306        return NULL;
307      }
308    }
309  }
310
311  if (!pathput)
312    t = last(t, PATH_END);
313
314  /* Malloc space for internal name and copy it */
315  if ((n = malloc(strlen(t) + 1)) == NULL) {
316    free((void *)tmp);
317    return NULL;
318  }
319  strcpy(n, t);
320
321  free((void *)tmp);
322
323  if (dosify)
324    msname(n);
325
326  /* Returned malloc'ed name */
327  if (pdosflag)
328    *pdosflag = dosflag;
329  return n;
330}
331
332char *in2ex(n)
333char *n;                /* internal file name */
334/* Convert the zip file name to an external file name, returning the malloc'ed
335   string or NULL if not enough memory. */
336{
337  char *x;              /* external file name */
338  char *t;              /* scans name */
339  char *lastext=NULL;   /* pointer to last extension */
340  char *lastdir=NULL;   /* pointer to last dir */
341
342  if ((x = malloc(strlen(n) + 1 + PAD)) == NULL)
343    return NULL;
344  strcpy(x, n);
345
346  for(t=x;*t;t++) {
347    if (*t=='.') {
348      *t='/';
349      lastext=t+1;
350    }
351    else if (*t=='/') {
352      *t='.';
353      lastdir=t+1;
354    }
355  }
356
357  if (exts2swap!=NULL && (int)lastext>(int)lastdir) {
358    if (lastdir==NULL)
359      lastdir=x;
360    if (checkext(lastext)) {
361      if (swapext(lastdir,lastext-1)) {
362        free((void *)x);
363        return NULL;
364      }
365    }
366  }
367
368  return x;
369}
370
371local int uxtime2acornftime(unsigned *pexadr, unsigned *pldadr, time_t ut)
372{
373  unsigned timlo;       /* 3 lower bytes of acorn file-time plus carry byte */
374  unsigned timhi;       /* 2 high bytes of acorn file-time */
375
376  timlo = ((unsigned)ut & 0x00ffffffU) * 100 + 0x00996a00U;
377  timhi = ((unsigned)ut >> 24);
378  timhi = timhi * 100 + 0x0000336eU + (timlo >> 24);
379  if (timhi & 0xffff0000U)
380      return 1;         /* calculation overflow, do not change time */
381
382  /* insert the five time bytes into loadaddr and execaddr variables */
383  *pexadr = (timlo & 0x00ffffffU) | ((timhi & 0x000000ffU) << 24);
384  *pldadr = (*pldadr & 0xffffff00U) | ((timhi >> 8) & 0x000000ffU);
385
386  return 0;             /* subject to future extension to signal overflow */
387}
388
389void stamp(f, d)
390char *f;                /* name of file to change */
391ulg d;                  /* dos-style time to change it to */
392/* Set last updated and accessed time of file f to the DOS time d. */
393{
394  time_t m_time;
395  unsigned int loadaddr, execaddr;
396  int attr;
397
398  /* Convert DOS time to time_t format in m_time */
399  m_time = dos2unixtime(d);
400
401  /* set the file's modification time */
402  SWI_OS_File_5(f,NULL,&loadaddr,NULL,NULL,&attr);
403
404  if (uxtime2acornftime(&execaddr, &loadaddr, m_time) != 0)
405      return;
406
407  SWI_OS_File_1(f,loadaddr,execaddr,attr);
408}
409
410ulg filetime(f, a, n, t)
411char *f;                /* name of file to get info on */
412ulg *a;                 /* return value: file attributes */
413long *n;                /* return value: file size */
414iztimes *t;             /* return value: access, modific. and creation times */
415/* If file *f does not exist, return 0.  Else, return the file's last
416   modified date and time as an MSDOS date and time.  The date and
417   time is returned in a long with the date most significant to allow
418   unsigned integer comparison of absolute times.  Also, if a is not
419   a NULL pointer, store the file attributes there, with the high two
420   bytes being the Unix attributes, and the low byte being a mapping
421   of that to DOS attributes.  If n is not NULL, store the file size
422   there.  If t is not NULL, the file's access, modification and creation
423   times are stored there as UNIX time_t values.
424   If f is "-", use standard input as the file. If f is a device, return
425   a file size of -1 */
426{
427  struct stat s;        /* results of stat() */
428  /* convert FNMAX to malloc - 11/8/04 EG */
429  char *name;
430  unsigned int len = strlen(f);
431
432  if (f == label) {
433    if (a != NULL)
434      *a = label_mode;
435    if (n != NULL)
436      *n = -2L; /* convention for a label name */
437    if (t != NULL)
438      t->atime = t->mtime = t->ctime = label_utim;
439    return label_time;
440  }
441  if ((name = malloc(len + 1)) == NULL) {
442    ZIPERR(ZE_MEM, "filetime");
443  }
444  strcpy(name, f);
445  if (name[len - 1] == '.')
446    name[len - 1] = '\0';
447  /* not all systems allow stat'ing a file with / appended */
448
449  if (strcmp(f, "-") == 0) {
450  /* forge stat values for stdin since Amiga and RISCOS have no fstat() */
451    s.st_mode = (S_IREAD|S_IWRITE|S_IFREG);
452    s.st_size = -1;
453    s.st_mtime = time(&s.st_mtime);
454  } else if (LSSTAT(name, &s) != 0) {
455             /* Accept about any file kind including directories
456              * (stored with trailing / with -r option)
457              */
458    free(name);
459    return 0;
460  }
461  free(name);
462
463  if (a != NULL) {
464    *a = ((ulg)s.st_mode << 16) | !(s.st_mode & S_IWRITE);
465    if ((s.st_mode & S_IFDIR) != 0) {
466      *a |= MSDOS_DIR_ATTR;
467    }
468  }
469  if (n != NULL)
470    *n = (s.st_mode & S_IFMT) == S_IFREG ? s.st_size : -1L;
471  if (t != NULL) {
472    t->atime = s.st_atime;
473    t->mtime = s.st_mtime;
474    t->ctime = s.st_ctime;
475  }
476
477  return unix2dostime((time_t *) &s.st_mtime);
478}
479
480int set_extra_field(z, z_utim)
481        struct zlist far *z;
482        iztimes *z_utim;
483{
484#ifdef USE_EF_UT_TIME
485  char *eb_ptr;
486#endif /* USE_EF_UT_TIME */
487  char *name;
488  extra_block *block;
489
490#define EB_SPARK_LEN    20
491#define EB_SPARK_SIZE (EB_HEADSIZE+EB_SPARK_LEN)
492#ifdef USE_EF_UT_TIME
493# ifdef IZ_CHECK_TZ
494#  define EB_UTTIME_SIZE (zp_tz_is_valid ? EB_HEADSIZE+EB_UT_LEN(1) : 0)
495# else
496#  define EB_UTTIME_SIZE (EB_HEADSIZE+EB_UT_LEN(1))
497# endif
498#else
499#  define EB_UTTIME_SIZE 0
500#endif
501#define EF_SPARK_TOTALSIZE (EB_SPARK_SIZE + EB_UTTIME_SIZE)
502
503  if ((name=(char *)malloc(strlen(z->name)+1))==NULL) {
504    fprintf(stderr," set_extra_field: not enough memory for directory name\n");
505    return ZE_MEM;
506  }
507
508  strcpy(name,z->name);
509
510  if (name[strlen(name)-1]=='.') {  /* remove the last '.' in directory names */
511    name[strlen(name)-1]=0;
512  }
513
514  z->extra=(char *)malloc(EF_SPARK_TOTALSIZE);
515  if (z->extra==NULL) {
516    fprintf(stderr," set_extra_field: not enough memory\n");
517    free(name);
518    return ZE_MEM;
519  }
520  z->cextra = z->extra;
521  z->cext = z->ext = EF_SPARK_TOTALSIZE;
522
523  block=(extra_block *)z->extra;
524  block->ID=SPARKID;
525  block->size=EB_SPARK_LEN;
526  block->ID_2=SPARKID_2;
527  block->zero=0;
528
529  if (SWI_OS_File_5(name,NULL,&block->loadaddr,&block->execaddr,
530                    NULL,&block->attr) != NULL) {
531    fprintf(stderr," OS error while set_extra_field of %s\n",name);
532  }
533
534  free(name);
535
536#ifdef USE_EF_UT_TIME
537# ifdef IZ_CHECK_TZ
538  if (zp_tz_is_valid) {
539# endif
540    eb_ptr = z->extra + EB_SPARK_SIZE;
541
542    eb_ptr[0]  = 'U';
543    eb_ptr[1]  = 'T';
544    eb_ptr[2]  = EB_UT_LEN(1);          /* length of data part of e.f. */
545    eb_ptr[3]  = 0;
546    eb_ptr[4]  = EB_UT_FL_MTIME;
547    eb_ptr[5]  = (char)(z_utim->mtime);
548    eb_ptr[6]  = (char)(z_utim->mtime >> 8);
549    eb_ptr[7]  = (char)(z_utim->mtime >> 16);
550    eb_ptr[8]  = (char)(z_utim->mtime >> 24);
551# ifdef IZ_CHECK_TZ
552  }
553# endif
554#endif /* USE_EF_UT_TIME */
555
556  return ZE_OK;
557}
558
559#endif /* !UTIL */
560
561
562/******************************/
563/*  Function version_local()  */
564/******************************/
565
566void version_local()
567{
568    static ZCONST char CompiledWith[] = "Compiled with %s%s for %s%s%s%s.\n\n";
569
570    printf(CompiledWith,
571#ifdef __GNUC__
572      "gcc ", __VERSION__,
573#else
574#  ifdef __CC_NORCROFT
575      "Norcroft ", "cc",
576#  else
577      "cc", "",
578#  endif
579#endif
580
581      "RISC OS",
582
583      " (Acorn Computers Ltd)",
584
585#ifdef __DATE__
586      " on ", __DATE__
587#else
588      "", ""
589#endif
590      );
591
592} /* end function version_local() */
593