1 /*
2 Copyright (c) 1990-2007 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 unzip.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
11 acorn.c
12
13 RISCOS-specific routines for use with Info-ZIP's UnZip 5.2 and later.
14
15 Contains: do_wild() <-- generic enough to put in fileio.c?
16 mapattr()
17 mapname()
18 checkdir()
19 mkdir()
20 setRISCOSexfield()
21 printRISCOSexfield()
22 close_outfile()
23 stamp_file()
24 version()
25
26 ---------------------------------------------------------------------------*/
27
28
29 #define UNZIP_INTERNAL
30 #include "^.unzip.h"
31 #include "riscos.h"
32
33 #define FTYPE_FFF (1<<17) /* set filetype to &FFF when extracting */
34
35 #ifdef WILD_STOP_AT_DIR
36 # define WESEP , (oU.W_flag ? '.' : '\0')
37 #else
38 # define WESEP
39 #endif
40
41 static int created_dir; /* used in mapname(), checkdir() */
42 static int renamed_fullpath; /* ditto */
43 static int has_mimemap = -1; /* used in mimemap() */
44
45 extern int mkdir(const char *path, int mode);
46 static int has_NFS_ext(const char *name);
47 static void setRISCOSexfield(ZCONST char *path, ZCONST void *ef_spark);
48 #ifdef DEBUG
49 static void printRISCOSexfield(int isdir, ZCONST void *extra_field);
50 #endif
51 static int uxtime2acornftime(unsigned *pexadr, unsigned *pldadr, time_t ut);
52 static int mimemap(const char *name);
53
54
55 #ifndef SFX
56
57 /**********************/
58 /* Function do_wild() */ /* for porting: dir separator; match(ignore_case) */
59 /**********************/
60
61 char *do_wild(__G__ wildspec)
62 __GDEF
63 ZCONST char *wildspec; /* only used first time on a given dir */
64 {
65 static DIR *wild_dir = (DIR *)NULL;
66 static ZCONST char *wildname;
67 static char *dirname, matchname[FILNAMSIZ];
68 static int notfirstcall=FALSE, have_dirname, dirnamelen;
69 struct dirent *file;
70
71 /* Even when we're just returning wildspec, we *always* do so in
72 * matchname[]--calling routine is allowed to append four characters
73 * to the returned string, and wildspec may be a pointer to argv[].
74 */
75 if (!notfirstcall) { /* first call: must initialize everything */
76 notfirstcall = TRUE;
77
78 /* break the wildspec into a directory part and a wildcard filename */
79 if ((wildname = (ZCONST char *)strrchr(wildspec, '.')) ==
80 (ZCONST char *)NULL)
81 {
82 dirname = ".";
83 dirnamelen = 1;
84 have_dirname = FALSE;
85 wildname = wildspec;
86 } else {
87 ++wildname; /* point at character after '/' */
88 dirnamelen = wildname - wildspec;
89 if ((dirname = (char *)malloc(dirnamelen+1)) == (char *)NULL) {
90 Info(slide, 0x201, ((char *)slide,
91 "warning: cannot allocate wildcard buffers\n"));
92 strncpy(matchname, wildspec, FILNAMSIZ);
93 matchname[FILNAMSIZ-1] = '\0';
94 return matchname; /* but maybe filespec was not a wildcard */
95 }
96 strncpy(dirname, wildspec, dirnamelen);
97 dirname[dirnamelen] = '\0'; /* terminate for strcpy below */
98 have_dirname = TRUE;
99 }
100
101 if ((wild_dir = opendir(dirname)) != (DIR *)NULL) {
102 while ((file = readdir(wild_dir)) != (struct dirent *)NULL) {
103 if (file->d_name[0] == '/' && wildname[0] != '/')
104 continue; /* Unix: '*' and '?' do not match leading dot */
105 if (match(file->d_name, wildname, 0 WESEP)) { /* 0=case sens.*/
106 if (have_dirname) {
107 strcpy(matchname, dirname);
108 strcpy(matchname+dirnamelen, file->d_name);
109 } else
110 strcpy(matchname, file->d_name);
111 return matchname;
112 }
113 }
114 /* if we get to here directory is exhausted, so close it */
115 closedir(wild_dir);
116 wild_dir = (DIR *)NULL;
117 }
118
119 /* return the raw wildspec in case that works (e.g., directory not
120 * searchable, but filespec was not wild and file is readable) */
121 strncpy(matchname, wildspec, FILNAMSIZ);
122 matchname[FILNAMSIZ-1] = '\0';
123 return matchname;
124 }
125
126 /* last time through, might have failed opendir but returned raw wildspec */
127 if (wild_dir == (DIR *)NULL) {
128 notfirstcall = FALSE; /* nothing left to try--reset for new wildspec */
129 if (have_dirname)
130 free(dirname);
131 return (char *)NULL;
132 }
133
134 /* If we've gotten this far, we've read and matched at least one entry
135 * successfully (in a previous call), so dirname has been copied into
136 * matchname already.
137 */
138 while ((file = readdir(wild_dir)) != (struct dirent *)NULL)
139 if (match(file->d_name, wildname, 0 WESEP)) { /* 0 == case sens. */
140 if (have_dirname) {
141 /* strcpy(matchname, dirname); */
142 strcpy(matchname+dirnamelen, file->d_name);
143 } else
144 strcpy(matchname, file->d_name);
145 return matchname;
146 }
147
148 closedir(wild_dir); /* have read at least one dir entry; nothing left */
149 wild_dir = (DIR *)NULL;
150 notfirstcall = FALSE; /* reset for new wildspec */
151 if (have_dirname)
152 free(dirname);
153 return (char *)NULL;
154
155 } /* end function do_wild() */
156
157 #endif /* !SFX */
158
159
160
161 /**************************/
162 /* Function has_NFS_ext() */
163 /**************************/
164
has_NFS_ext(const char * name)165 static int has_NFS_ext(const char* name)
166 {
167 int i = strlen(name) - 4;
168
169 return (i >= 0 && name[i] == ',' && (i > 0 || name[i-1]=='/') &&
170 isxdigit(name[i+1]) && isxdigit(name[i+2]) && isxdigit(name[i+3]));
171 } /* end function has_NFS_ext() */
172
173
174
175 /**********************/
176 /* Function mapattr() */
177 /**********************/
178
mapattr(__G)179 int mapattr(__G)
180 __GDEF
181 {
182 ulg tmp = G.crec.external_file_attributes;
183
184 switch (G.pInfo->hostnum) {
185 case AMIGA_:
186 tmp = (unsigned)(tmp>>17 & 7); /* Amiga RWE bits */
187 G.pInfo->file_attr = (unsigned)(tmp<<6 | tmp<<3 | tmp);
188 break;
189 case THEOS_:
190 tmp &= 0xF1FFFFFFL;
191 if ((tmp & 0xF0000000L) != 0x40000000L)
192 tmp &= 0x01FFFFFFL; /* not a dir, mask all ftype bits */
193 else
194 tmp &= 0x41FFFFFFL; /* leave directory bit as set */
195 /* fall through! */
196 case ACORN_:
197 case UNIX_:
198 case VMS_:
199 case ATARI_:
200 case ATHEOS_:
201 case BEOS_:
202 case QDOS_:
203 case TANDEM_:
204 G.pInfo->file_attr = (unsigned)(tmp >> 16);
205 if (G.pInfo->file_attr != 0 || !G.extra_field) {
206 break;
207 } else {
208 /* Some (non-Info-ZIP) implementations of Zip for Unix and
209 VMS (and probably others ??) leave 0 in the upper 16-bit
210 part of the external_file_attributes field. Instead, they
211 store file permission attributes in some extra field.
212 As a work-around, we search for the presence of one of
213 these extra fields and fall back to the MSDOS compatible
214 part of external_file_attributes if one of the known
215 e.f. types has been detected.
216 Later, we might implement extraction of the permission
217 bits from the VMS extra field. But for now, the work-around
218 should be sufficient to provide "readable" extracted files.
219 (For ASI Unix e.f., an experimental remap of the e.f.
220 mode value IS already provided!)
221 */
222 ush ebID;
223 unsigned ebLen;
224 uch *ef = G.extra_field;
225 unsigned ef_len = G.crec.extra_field_length;
226 int r = FALSE;
227
228 while (!r && ef_len >= EB_HEADSIZE) {
229 ebID = makeword(ef);
230 ebLen = (unsigned)makeword(ef+EB_LEN);
231 if (ebLen > (ef_len - EB_HEADSIZE))
232 /* discoverd some e.f. inconsistency! */
233 break;
234 switch (ebID) {
235 case EF_ASIUNIX:
236 if (ebLen >= (EB_ASI_MODE+2)) {
237 G.pInfo->file_attr =
238 (unsigned)makeword(ef+(EB_HEADSIZE+EB_ASI_MODE));
239 /* force stop of loop: */
240 ef_len = (ebLen + EB_HEADSIZE);
241 break;
242 }
243 /* else: fall through! */
244 case EF_PKVMS:
245 /* "found nondecypherable e.f. with perm. attr" */
246 r = TRUE;
247 default:
248 break;
249 }
250 ef_len -= (ebLen + EB_HEADSIZE);
251 ef += (ebLen + EB_HEADSIZE);
252 }
253 if (!r)
254 break;
255 }
256 /* fall through! */
257 /* all remaining cases: expand MSDOS read-only bit into write perms */
258 case FS_FAT_:
259 /* PKWARE's PKZip for Unix marks entries as FS_FAT_, but stores the
260 * Unix attributes in the upper 16 bits of the external attributes
261 * field, just like Info-ZIP's Zip for Unix. We try to use that
262 * value, after a check for consistency with the MSDOS attribute
263 * bits (see below).
264 */
265 G.pInfo->file_attr = (unsigned)(tmp >> 16);
266 /* fall through! */
267 case FS_HPFS_:
268 case FS_NTFS_:
269 case MAC_:
270 case TOPS20_:
271 default:
272 /* Ensure that DOS subdir bit is set when the entry's name ends
273 * in a '/'. Some third-party Zip programs fail to set the subdir
274 * bit for directory entries.
275 */
276 if ((tmp & 0x10) == 0) {
277 extent fnlen = strlen(G.filename);
278 if (fnlen > 0 && G.filename[fnlen-1] == '/')
279 tmp |= 0x10;
280 }
281 /* read-only bit --> write perms; subdir bit --> dir exec bit */
282 tmp = !(tmp & 1) << 1 | (tmp & 0x10) >> 4;
283 if ((G.pInfo->file_attr & 0700) == (unsigned)(0400 | tmp<<6))
284 /* keep previous G.pInfo->file_attr setting, when its "owner"
285 * part appears to be consistent with DOS attribute flags!
286 */
287 break;
288 G.pInfo->file_attr = (unsigned)(0444 | tmp<<6 | tmp<<3 | tmp);
289 break;
290 } /* end switch (host-OS-created-by) */
291
292 G.pInfo->file_attr&=0xFFFF;
293
294 G.pInfo->file_attr|=(0xFFDu<<20);
295
296 if (has_NFS_ext(G.filename)) {
297 int ftype=strtol(G.filename+strlen(G.filename)-3,NULL,16)&0xFFF;
298
299 G.pInfo->file_attr = (G.pInfo->file_attr & 0x000FFFFF) | (ftype<<20);
300 } else {
301 int type = mimemap(G.filename);
302 if (type == -1)
303 type = (G.crec.internal_file_attributes & 1) ? 0xFFF : 0xFFD;
304 G.pInfo->file_attr = (G.pInfo->file_attr & 0x000FFFFF) | (type<<20);
305 }
306
307 return 0;
308
309 } /* end function mapattr() */
310
311
312
313 /************************/
314 /* Function mimemap() */
315 /************************/
316
mimemap(const char * name)317 static int mimemap(const char *name)
318 {
319 const char *ext = name;
320 int type;
321
322 if (has_mimemap < 0)
323 has_mimemap =
324 !(SWI_OS_CLI("%RMEnsure MimeMap 0.05 RMLoad System:Modules.Network.MimeMap")
325 || SWI_OS_CLI("%RMEnsure MimeMap 0.05"));
326
327 if (!has_mimemap)
328 return -1; /* no MimeMap module; fall back on text flag test */
329
330 do {
331 while (*ext && *ext!='.')
332 ext++;
333 if (!*ext)
334 return -1; /* no suitable extension; fallback */
335 type = SWI_MimeMap_Translate(ext++);
336 } while (type == -1);
337
338 return type;
339 }
340
341
342
343 /************************/
344 /* Function mapname() */
345 /************************/
346
347 int mapname(__G__ renamed)
348 __GDEF
349 int renamed;
350 /*
351 * returns:
352 * MPN_OK - no problem detected
353 * MPN_INF_TRUNC - caution (truncated filename)
354 * MPN_INF_SKIP - info "skip entry" (dir doesn't exist)
355 * MPN_ERR_SKIP - error -> skip entry
356 * MPN_ERR_TOOLONG - error -> path is too long
357 * MPN_NOMEM - error (memory allocation failed) -> skip entry
358 * [also MPN_VOL_LABEL, MPN_CREATED_DIR]
359 */
360 {
361 char pathcomp[FILNAMSIZ]; /* path-component buffer */
362 char *pp, *cp=(char *)NULL; /* character pointers */
363 char *lastsemi=(char *)NULL; /* pointer to last semi-colon in pathcomp */
364 int error = MPN_OK;
365 register unsigned workch; /* hold the character being tested */
366 char *checkswap=NULL; /* pointer the the extension to check */
367
368
369 /*---------------------------------------------------------------------------
370 Initialize various pointers and counters and stuff.
371 ---------------------------------------------------------------------------*/
372
373 if (G.pInfo->vollabel)
374 return MPN_VOL_LABEL; /* can't set disk volume labels in RISCOS */
375
376 /* can create path as long as not just freshening, or if user told us */
377 G.create_dirs = (!uO.fflag || renamed);
378
379 created_dir = FALSE; /* not yet */
380
381 /* user gave full pathname: don't prepend rootpath */
382 renamed_fullpath = (renamed && (*G.filename == '/'));
383
384 if (checkdir(__G__ (char *)NULL, INIT) == MPN_NOMEM)
385 return MPN_NOMEM; /* initialize path buffer, unless no memory */
386
387 *pathcomp = '\0'; /* initialize translation buffer */
388 pp = pathcomp; /* point to translation buffer */
389 if (uO.jflag) /* junking directories */
390 cp = (char *)strrchr(G.filename, '/');
391 if (cp == (char *)NULL) /* no '/' or not junking dirs */
392 cp = G.filename; /* point to internal zipfile-member pathname */
393 else
394 ++cp; /* point to start of last component of path */
395
396 /*---------------------------------------------------------------------------
397 Begin main loop through characters in filename.
398 ---------------------------------------------------------------------------*/
399
400 while ((workch = (uch)*cp++) != 0) {
401
402 switch (workch) {
403 case '/': /* can assume -j flag not given */
404 *pp = '\0';
405 if (((error = checkdir(__G__ pathcomp, APPEND_DIR))
406 & MPN_MASK) > MPN_INF_TRUNC)
407 return error;
408 pp = pathcomp; /* reset conversion buffer for next piece */
409 lastsemi = (char *)NULL; /* leave direct. semi-colons alone */
410 checkswap=NULL; /* reset checking at start of new leafname */
411 break;
412
413 case '.':
414 *pp++ = '/';
415 checkswap=pp;
416 break;
417
418 case ';': /* VMS version (or DEC-20 attrib?) */
419 lastsemi = pp;
420 *pp++ = ';'; /* keep for now; remove VMS ";##" */
421 break; /* later, if requested */
422
423 case ' ': /* change spaces to hard-spaces */
424 *pp++ = 160; /* (ISO 8859-1 Latin-1 codepage) */
425 break;
426
427 /* The following substitutions, unless stated otherwise, follow
428 * those for DOSFS. They translate special symbols into other
429 * characters which have no special meaning to RISC OS. */
430 case '#': *pp++ = '?'; break; /* single-char wildcard */
431 case '&': *pp++ = '+'; break;
432 case '@': *pp++ = '='; break;
433 case '%': *pp++ = ';'; break;
434 case '$': *pp++ = '<'; break;
435 case '^': *pp++ = '>'; break; /* parent-dir reference */
436
437 /* The following substitutions deal with the remaining special
438 * symbols. ('.' is handled above.) */
439 case '*': *pp++ = 0xD7; break; /* Latin-1 'multiply' */
440 case '"': *pp++ = '~'; break;
441 case ':': *pp++ = ';'; break;
442 case '\\': *pp++ = '/'; break;
443 case '|': *pp++ = 0xA6; break; /* Latin-1 'broken bar' */
444
445 default:
446 /* allow European characters in filenames: */
447 if (isprint(workch) || (128 <= workch && workch <= 254))
448 *pp++ = (char)workch;
449 } /* end switch */
450
451 } /* end while loop */
452
453 /*---------------------------------------------------------------------------
454 Report if directory was created (and no file to create: filename ended
455 in '/'), check name to be sure it exists, and combine path and name be-
456 fore exiting.
457 ---------------------------------------------------------------------------*/
458
459 if (G.filename[strlen(G.filename) - 1] == '/') {
460 checkdir(__G__ G.filename, GETPATH);
461 if (created_dir) {
462 if (QCOND2) {
463 Info(slide, 0, ((char *)slide, " creating: %s\n",
464 FnFilter1(G.filename)));
465 }
466 /* set dir time (note trailing '/') */
467 return (error & ~MPN_MASK) | MPN_CREATED_DIR;
468 }
469 /* dir existed already; don't look for data to extract */
470 return (error & ~MPN_MASK) | MPN_INF_SKIP;
471 }
472
473 *pp = '\0'; /* done with pathcomp: terminate it */
474
475 /* if not saving them, remove VMS version numbers (appended ";###") */
476 if (!uO.V_flag && lastsemi) {
477 pp = lastsemi + 1;
478 while (isdigit((uch)(*pp)))
479 ++pp;
480 if (*pp == '\0') /* only digits between ';' and end: nuke */
481 *lastsemi = '\0';
482 }
483
484 if (*pathcomp == '\0') {
485 Info(slide, 1, ((char *)slide, "mapname: conversion of %s failed\n",
486 FnFilter1(G.filename)));
487 return (error & ~MPN_MASK) | MPN_ERR_SKIP;
488 }
489
490 if (checkswap!=NULL) {
491 if (checkext(checkswap)) {
492 if ((error = checkdir(__G__ checkswap, APPEND_DIR)) > 1)
493 return error;
494 *(checkswap-1)=0; /* remove extension from pathcomp */
495 }
496 }
497
498 if (!uO.acorn_nfs_ext && has_NFS_ext(pathcomp)) {
499 /* remove the filetype extension unless requested otherwise */
500 /* the filetype should be already set by mapattr() */
501 pathcomp[strlen(pathcomp)-4]=0;
502 }
503
504 checkdir(__G__ pathcomp, APPEND_NAME); /* returns 1 if truncated: care? */
505 checkdir(__G__ G.filename, GETPATH);
506
507 return error;
508
509 } /* end function mapname() */
510
511
512
513
514 /***********************/
515 /* Function checkdir() */
516 /***********************/
517
518 int checkdir(__G__ pathcomp, flag)
519 __GDEF
520 char *pathcomp;
521 int flag;
522 /*
523 * returns:
524 * MPN_OK - no problem detected
525 * MPN_INF_TRUNC - (on APPEND_NAME) truncated filename
526 * MPN_INF_SKIP - path doesn't exist, not allowed to create
527 * MPN_ERR_SKIP - path doesn't exist, tried to create and failed; or path
528 * exists and is not a directory, but is supposed to be
529 * MPN_ERR_TOOLONG - path is too long
530 * MPN_NOMEM - can't allocate memory for filename buffers
531 */
532 {
533 static int rootlen = 0; /* length of rootpath */
534 static char *rootpath; /* user's "extract-to" directory */
535 static char *buildpath; /* full path (so far) to extracted file */
536 static char *end; /* pointer to end of buildpath ('\0') */
537
538 # define FN_MASK 7
539 # define FUNCTION (flag & FN_MASK)
540
541
542 /*---------------------------------------------------------------------------
543 APPEND_DIR: append the path component to the path being built and check
544 for its existence. If doesn't exist and we are creating directories, do
545 so for this one; else signal success or error as appropriate.
546 ---------------------------------------------------------------------------*/
547
548 if (FUNCTION == APPEND_DIR) {
549 int too_long = FALSE;
550 #ifdef SHORT_NAMES
551 char *old_end = end;
552 #endif
553
554 Trace((stderr, "appending dir segment [%s]\n", FnFilter1(pathcomp)));
555 while ((*end = *pathcomp++) != '\0')
556 ++end;
557 #ifdef SHORT_NAMES /* path components restricted to 14 chars, typically */
558 if ((end-old_end) > FILENAME_MAX) /* GRR: proper constant? */
559 *(end = old_end + FILENAME_MAX) = '\0';
560 #endif
561
562 /* GRR: could do better check, see if overrunning buffer as we go:
563 * check end-buildpath after each append, set warning variable if
564 * within 20 of FILNAMSIZ; then if var set, do careful check when
565 * appending. Clear variable when begin new path. */
566
567 /* next check: need to append '/', at least one-char name, '\0' */
568 if ((end-buildpath) > FILNAMSIZ-3)
569 too_long = TRUE; /* check if extracting dir? */
570 if (stat(buildpath, &G.statbuf)) { /* path doesn't exist */
571 if (!G.create_dirs) { /* told not to create (freshening) */
572 free(buildpath);
573 return MPN_INF_SKIP; /* path doesn't exist: nothing to do */
574 }
575 if (too_long) {
576 Info(slide, 1, ((char *)slide,
577 "checkdir error: path too long: %s\n",
578 FnFilter1(buildpath)));
579 fflush(stderr);
580 free(buildpath);
581 /* no room for filenames: fatal */
582 return MPN_ERR_TOOLONG;
583 }
584 if (mkdir(buildpath, 0777) == -1) { /* create the directory */
585 Info(slide, 1, ((char *)slide,
586 "checkdir error: cannot create %s\n\
587 unable to process %s.\n",
588 FnFilter2(buildpath), FnFilter1(G.filename)));
589 free(buildpath);
590 /* path didn't exist, tried to create, failed */
591 return MPN_ERR_SKIP;
592 }
593 created_dir = TRUE;
594 } else if (!S_ISDIR(G.statbuf.st_mode)) {
595 Info(slide, 1, ((char *)slide,
596 "checkdir error: %s exists but is not directory\n\
597 unable to process %s.\n",
598 FnFilter2(buildpath), FnFilter1(G.filename)));
599 free(buildpath);
600 /* path existed but wasn't dir */
601 return MPN_ERR_SKIP;
602 }
603 if (too_long) {
604 Info(slide, 1, ((char *)slide,
605 "checkdir error: path too long: %s\n", FnFilter1(buildpath)));
606 free(buildpath);
607 /* no room for filenames: fatal */
608 return MPN_ERR_TOOLONG;
609 }
610 *end++ = '.'; /************* was '/' *************/
611 *end = '\0';
612 Trace((stderr, "buildpath now = [%s]\n", FnFilter1(buildpath)));
613 return MPN_OK;
614
615 } /* end if (FUNCTION == APPEND_DIR) */
616
617 /*---------------------------------------------------------------------------
618 GETPATH: copy full path to the string pointed at by pathcomp, and free
619 buildpath.
620 ---------------------------------------------------------------------------*/
621
622 if (FUNCTION == GETPATH) {
623 strcpy(pathcomp, buildpath);
624 Trace((stderr, "getting and freeing path [%s]\n",
625 FnFilter1(pathcomp)));
626 free(buildpath);
627 buildpath = end = (char *)NULL;
628 return MPN_OK;
629 }
630
631 /*---------------------------------------------------------------------------
632 APPEND_NAME: assume the path component is the filename; append it and
633 return without checking for existence.
634 ---------------------------------------------------------------------------*/
635
636 if (FUNCTION == APPEND_NAME) {
637 #ifdef SHORT_NAMES
638 char *old_end = end;
639 #endif
640
641 Trace((stderr, "appending filename [%s]\n", FnFilter1(pathcomp)));
642 while ((*end = *pathcomp++) != '\0') {
643 ++end;
644 #ifdef SHORT_NAMES /* truncate name at 14 characters, typically */
645 if ((end-old_end) > FILENAME_MAX) /* GRR: proper constant? */
646 *(end = old_end + FILENAME_MAX) = '\0';
647 #endif
648 if ((end-buildpath) >= FILNAMSIZ) {
649 *--end = '\0';
650 Info(slide, 0x201, ((char *)slide,
651 "checkdir warning: path too long; truncating\n\
652 %s\n -> %s\n",
653 FnFilter1(G.filename), FnFilter2(buildpath)));
654 return MPN_INF_TRUNC; /* filename truncated */
655 }
656 }
657 Trace((stderr, "buildpath now = [%s]\n", FnFilter1(buildpath)));
658 /* could check for existence here, prompt for new name... */
659 return MPN_OK;
660 }
661
662 /*---------------------------------------------------------------------------
663 INIT: allocate and initialize buffer space for the file currently being
664 extracted. If file was renamed with an absolute path, don't prepend the
665 extract-to path.
666 ---------------------------------------------------------------------------*/
667
668 /* GRR: for VMS and TOPS-20, add up to 13 to strlen */
669
670 if (FUNCTION == INIT) {
671 Trace((stderr, "initializing buildpath to "));
672 if ((buildpath = (char *)malloc(strlen(G.filename)+rootlen+1))
673 == (char *)NULL)
674 return MPN_NOMEM;
675 if ((rootlen > 0) && !renamed_fullpath) {
676 strcpy(buildpath, rootpath);
677 end = buildpath + rootlen;
678 } else {
679 *buildpath = '\0';
680 end = buildpath;
681 }
682 Trace((stderr, "[%s]\n", FnFilter1(buildpath)));
683 return MPN_OK;
684 }
685
686 /*---------------------------------------------------------------------------
687 ROOT: if appropriate, store the path in rootpath and create it if
688 necessary; else assume it's a zipfile member and return. This path
689 segment gets used in extracting all members from every zipfile specified
690 on the command line.
691 ---------------------------------------------------------------------------*/
692
693 #if (!defined(SFX) || defined(SFX_EXDIR))
694 if (FUNCTION == ROOT) {
695 Trace((stderr, "initializing root path to [%s]\n",
696 FnFilter1(pathcomp)));
697 if (pathcomp == (char *)NULL) {
698 rootlen = 0;
699 return MPN_OK;
700 }
701 if (rootlen > 0) /* rootpath was already set, nothing to do */
702 return MPN_OK;
703 if ((rootlen = strlen(pathcomp)) > 0) {
704 char *tmproot;
705
706 if ((tmproot = (char *)malloc(rootlen+2)) == (char *)NULL) {
707 rootlen = 0;
708 return MPN_NOMEM;
709 }
710 strcpy(tmproot, pathcomp);
711 if (tmproot[rootlen-1] == '.') { /****** was '/' ********/
712 tmproot[--rootlen] = '\0';
713 }
714 if (rootlen > 0 && (SSTAT(tmproot, &G.statbuf) ||
715 !S_ISDIR(G.statbuf.st_mode)))
716 { /* path does not exist */
717 if (!G.create_dirs /* || isshexp(tmproot) */ ) {
718 free(tmproot);
719 rootlen = 0;
720 /* skip (or treat as stored file) */
721 return MPN_INF_SKIP;
722 }
723 /* create the directory (could add loop here scanning tmproot
724 * to create more than one level, but why really necessary?) */
725 if (mkdir(tmproot, 0777) == -1) {
726 Info(slide, 1, ((char *)slide,
727 "checkdir: cannot create extraction directory: %s\n",
728 FnFilter1(tmproot)));
729 free(tmproot);
730 rootlen = 0;
731 /* path didn't exist, tried to create, and failed: */
732 /* file exists, or 2+ subdir levels required */
733 return MPN_ERR_SKIP;
734 }
735 }
736 tmproot[rootlen++] = '.'; /*********** was '/' *************/
737 tmproot[rootlen] = '\0';
738 if ((rootpath = (char *)realloc(tmproot, rootlen+1)) == NULL) {
739 free(tmproot);
740 rootlen = 0;
741 return MPN_NOMEM;
742 }
743 Trace((stderr, "rootpath now = [%s]\n", FnFilter1(rootpath)));
744 }
745 return MPN_OK;
746 }
747 #endif /* !SFX || SFX_EXDIR */
748
749 /*---------------------------------------------------------------------------
750 END: free rootpath, immediately prior to program exit.
751 ---------------------------------------------------------------------------*/
752
753 if (FUNCTION == END) {
754 Trace((stderr, "freeing rootpath\n"));
755 if (rootlen > 0) {
756 free(rootpath);
757 rootlen = 0;
758 }
759 return MPN_OK;
760 }
761
762 return MPN_INVALID; /* should never reach */
763
764 } /* end function checkdir() */
765
766
767
768
769
770 /********************/
771 /* Function mkdir() */
772 /********************/
773
mkdir(path,mode)774 int mkdir(path, mode)
775 const char *path;
776 int mode; /* ignored */
777 /*
778 * returns: 0 - successful
779 * -1 - failed (errno not set, however)
780 */
781 {
782 return (SWI_OS_File_8((char *)path) == NULL)? 0 : -1;
783 }
784
785
786
787
788 /*********************************/
789 /* extra_field-related functions */
790 /*********************************/
791
setRISCOSexfield(ZCONST char * path,ZCONST void * ef_spark)792 static void setRISCOSexfield(ZCONST char *path, ZCONST void *ef_spark)
793 {
794 if (ef_spark!=NULL) {
795 extra_block *block=(extra_block *)ef_spark;
796 SWI_OS_File_1((char *)path,block->loadaddr,block->execaddr,block->attr);
797 }
798 }
799
800 #ifdef DEBUG
printRISCOSexfield(int isdir,ZCONST void * extra_field)801 static void printRISCOSexfield(int isdir, ZCONST void *extra_field)
802 {
803 extra_block *block=(extra_block *)extra_field;
804 printf("\n This file has RISC OS file informations in the local extra field.\n");
805
806 if (isdir) {
807 /* I prefer not to print this string... should change later... */
808 /* printf(" The file is a directory.\n");*/
809 } else if ((block->loadaddr & 0xFFF00000) != 0xFFF00000) {
810 printf(" Load address: %.8X\n",block->loadaddr);
811 printf(" Exec address: %.8X\n",block->execaddr);
812 } else {
813 /************* should change this to use OS_FSControl 18 to get filetype string ************/
814 char tmpstr[16];
815 char ftypestr[32];
816 int flen;
817 sprintf(tmpstr,"File$Type_%03x",(block->loadaddr & 0x000FFF00) >> 8);
818 if (SWI_OS_ReadVarVal(tmpstr,ftypestr,32,&flen)==NULL) {
819 ftypestr[flen]=0;
820 printf(" Filetype: %s (&%.3X)\n",ftypestr,(block->loadaddr & 0x000FFF00) >> 8);
821 } else {
822 printf(" Filetype: &%.3X\n",(block->loadaddr & 0x000FFF00) >> 8);
823 }
824 }
825 printf(" Access: ");
826 if (block->attr & (1<<3))
827 printf("L");
828 if (block->attr & (1<<0))
829 printf("W");
830 if (block->attr & (1<<1))
831 printf("R");
832 printf("/");
833 if (block->attr & (1<<4))
834 printf("w");
835 if (block->attr & (1<<5))
836 printf("r");
837 printf("\n\n");
838 }
839 #endif /* DEBUG */
840
841
842 /**********************************************/
843 /* internal help function for time conversion */
844 /**********************************************/
uxtime2acornftime(unsigned * pexadr,unsigned * pldadr,time_t ut)845 static int uxtime2acornftime(unsigned *pexadr, unsigned *pldadr, time_t ut)
846 {
847 unsigned timlo; /* 3 lower bytes of acorn file-time plus carry byte */
848 unsigned timhi; /* 2 high bytes of acorn file-time */
849
850 timlo = ((unsigned)ut & 0x00ffffffU) * 100 + 0x00996a00U;
851 timhi = ((unsigned)ut >> 24);
852 timhi = timhi * 100 + 0x0000336eU + (timlo >> 24);
853 if (timhi & 0xffff0000U)
854 return 1; /* calculation overflow, do not change time */
855
856 /* insert the five time bytes into loadaddr and execaddr variables */
857 *pexadr = (timlo & 0x00ffffffU) | ((timhi & 0x000000ffU) << 24);
858 *pldadr = (*pldadr & 0xffffff00U) | ((timhi >> 8) & 0x000000ffU);
859
860 return 0; /* subject to future extension to signal overflow */
861 }
862
863
864 /****************************/
865 /* Function close_outfile() */
866 /****************************/
867
close_outfile(__G)868 void close_outfile(__G)
869 __GDEF
870 {
871 zvoid *spark_ef;
872
873 fclose(G.outfile);
874
875 if ((spark_ef = getRISCOSexfield(G.extra_field, G.lrec.extra_field_length))
876 != NULL) {
877 setRISCOSexfield(G.filename, spark_ef);
878 } else {
879 unsigned int loadaddr, execaddr;
880 int attr;
881 int mode=G.pInfo->file_attr&0xffff; /* chmod equivalent mode */
882
883 time_t m_time;
884 #ifdef USE_EF_UT_TIME
885 iztimes z_utime;
886 #endif
887
888 /* skip restoring time stamps on user's request */
889 if (uO.D_flag <= 1) {
890 #ifdef USE_EF_UT_TIME
891 if (G.extra_field &&
892 #ifdef IZ_CHECK_TZ
893 G.tz_is_valid &&
894 #endif
895 (ef_scan_for_izux(G.extra_field, G.lrec.extra_field_length, 0,
896 G.lrec.last_mod_dos_datetime, &z_utime, NULL)
897 & EB_UT_FL_MTIME))
898 {
899 TTrace((stderr, "close_outfile: Unix e.f. modif. time = %ld\n",
900 z_utime.mtime));
901 m_time = z_utime.mtime;
902 } else
903 #endif /* USE_EF_UT_TIME */
904 m_time = dos_to_unix_time(G.lrec.last_mod_dos_datetime);
905 }
906
907 /* set the file's time-stamp and attributes */
908 SWI_OS_File_5(G.filename, NULL, &loadaddr, NULL, NULL, &attr);
909
910 if (uO.D_flag <= 1)
911 /* set the file's modification time */
912 uxtime2acornftime(&execaddr, &loadaddr, m_time);
913
914 loadaddr = (loadaddr & 0xfff000ffU) |
915 ((G.pInfo->file_attr&0xfff00000) >> 12);
916
917 attr=(attr&0xffffff00) | ((mode&0400) >> 8) | ((mode&0200) >> 6) |
918 ((mode&0004) << 2) | ((mode&0002) << 4);
919
920 SWI_OS_File_1(G.filename, loadaddr, execaddr, attr);
921 }
922
923 } /* end function close_outfile() */
924
925
926
927
928 #ifdef TIMESTAMP
929
930 /***************************/
931 /* Function stamp_file() */
932 /***************************/
933
stamp_file(fname,modtime)934 int stamp_file(fname, modtime)
935 ZCONST char *fname;
936 time_t modtime;
937 {
938 unsigned int loadaddr, execaddr;
939 int attr;
940
941 /* set the file's modification time */
942 if (SWI_OS_File_5((char *)fname, NULL, &loadaddr, NULL, NULL, &attr)
943 != NULL)
944 return -1;
945
946 if (uxtime2acornftime(&execaddr, &loadaddr, modtime) != 0)
947 return -1;
948
949 return (SWI_OS_File_1((char *)fname, loadaddr, execaddr, attr) == NULL) ?
950 0 : -1;
951
952 } /* end function stamp_file() */
953
954 #endif /* TIMESTAMP */
955
956
957
958
959 #ifndef SFX
960
961 /************************/
962 /* Function version() */
963 /************************/
964
version(__G)965 void version(__G)
966 __GDEF
967 {
968 sprintf((char *)slide, LoadFarString(CompiledWith),
969 #ifdef __GNUC__
970 "gcc ", __VERSION__,
971 #else
972 # ifdef __CC_NORCROFT
973 "Norcroft ", "cc",
974 # else
975 "cc", "",
976 # endif
977 #endif
978
979 "RISC OS",
980
981 " (Acorn Computers Ltd)",
982
983 #ifdef __DATE__
984 " on ", __DATE__
985 #else
986 "", ""
987 #endif
988 );
989
990 (*G.message)((zvoid *)&G, slide, (ulg)strlen((char *)slide), 0);
991
992 } /* end function version() */
993
994 #endif /* !SFX */
995