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