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