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 
19 local int wild_recurse(char *whole, char *wildtail);
20 local int uxtime2acornftime(unsigned *pexadr, unsigned *pldadr, time_t ut);
21 
22 extern char *label;
23 local ulg label_time = 0;
24 local ulg label_mode = 0;
25 local time_t label_utim = 0;
26 
readd(DIR * d)27 char *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 
wild_recurse(whole,wildtail)47 local 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 
wild(p)129 int wild(p)
130 char *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 
procname(n,caseflag)156 int procname(n, caseflag)
157 char *n;                /* name to process */
158 int 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 
ex2in(x,isdir,pdosflag)243 char *ex2in(x, isdir, pdosflag)
244 char *x;                /* external file name */
245 int isdir;              /* input: x is a directory */
246 int *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 
in2ex(n)332 char *in2ex(n)
333 char *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 
uxtime2acornftime(unsigned * pexadr,unsigned * pldadr,time_t ut)371 local 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 
stamp(f,d)389 void stamp(f, d)
390 char *f;                /* name of file to change */
391 ulg 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 
filetime(f,a,n,t)410 ulg filetime(f, a, n, t)
411 char *f;                /* name of file to get info on */
412 ulg *a;                 /* return value: file attributes */
413 long *n;                /* return value: file size */
414 iztimes *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 
set_extra_field(z,z_utim)480 int 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 
version_local()566 void 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