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   human68k.c
12 
13   Human68k-specific routines for use with Info-ZIP's UnZip 5.41 and later.
14 
15   Contains:  do_wild()
16              mapattr()
17              mapname()
18              checkdir()
19              close_outfile()
20              stamp_file()                   (TIMESTAMP only)
21              version()
22              main()                         (for UnZipSFX)
23 
24   ---------------------------------------------------------------------------*/
25 
26 
27 #include <dirent.h>
28 #include <string.h>
29 #include <sys/dos.h>
30 #include <sys/xunistd.h>
31 #ifdef HAVE_TWONCALL_H
32 #include <twoncall.h>
33 #endif
34 #define UNZIP_INTERNAL
35 #include "unzip.h"
36 
37 #if defined (SFX) && defined (MAIN)
38 #include <sys/xstart.h>
39 int MAIN(int argc, char *argv[]);
40 #endif
41 
42 static void map2fat(char *pathcomp, char *last_dot);
43 static char *trunc_name(char *name, int maxlen);
44 
45 static int created_dir;        /* used in mapname(), checkdir() */
46 static int renamed_fullpath;   /* ditto */
47 
48 static char multi_period, special_char;
49 
50 #ifndef SFX
51 
52 /**********************/
53 /* Function do_wild() */
54 /**********************/
55 
56 char *do_wild(__G__ wildspec)
57     __GDEF
58     ZCONST char *wildspec;  /* only used first time on a given dir */
59 {
60     static DIR *wild_dir = (DIR *)NULL;
61     static ZCONST char *wildname;
62     static char *dirname, matchname[FILNAMSIZ];
63     static int notfirstcall=FALSE, have_dirname, dirnamelen;
64     struct dirent *file;
65 
66     /* Even when we're just returning wildspec, we *always* do so in
67      * matchname[]--calling routine is allowed to append four characters
68      * to the returned string, and wildspec may be a pointer to argv[].
69      */
70     if (!notfirstcall) {    /* first call:  must initialize everything */
71         notfirstcall = TRUE;
72 
73         if (!iswild(wildspec)) {
74             strncpy(matchname, wildspec, FILNAMSIZ);
75             matchname[FILNAMSIZ-1] = '\0';
76             have_dirname = FALSE;
77             wild_dir = (DIR *)NULL;
78             return matchname;
79         }
80 
81         /* break the wildspec into a directory part and a wildcard filename */
82         if ((wildname = strrchr(wildspec, '/')) == NULL) {
83             dirname = ".";
84             dirnamelen = 1;
85             have_dirname = FALSE;
86             wildname = wildspec;
87         } else {
88             ++wildname;     /* point at character after '/' */
89             dirnamelen = wildname - wildspec;
90             if ((dirname = (char *)malloc(dirnamelen+1)) == NULL) {
91                 Info(slide, 1, ((char *)slide,
92                   "warning:  cannot allocate wildcard buffers\n"));
93                 strcpy(matchname, wildspec);
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         Trace((stderr, "do_wild:  dirname = [%s]\n", FnFilter1(dirname)));
101 
102         if ((wild_dir = opendir(dirname)) != (DIR *)NULL) {
103             while ((file = readdir(wild_dir)) != (struct dirent *)NULL) {
104                 Trace((stderr, "do_wild:  readdir returns %s\n",
105                   FnFilter1(file->d_name)));
106                 if (file->d_name[0] == '.' && wildname[0] != '.')
107                     continue; /* Unix:  '*' and '?' do not match leading dot */
108                 if (match(file->d_name, wildname, 0 WISEP) && /* 0=case sens.*/
109                     /* skip "." and ".." directory entries */
110                     strcmp(file->d_name, ".") && strcmp(file->d_name, "..")) {
111                     Trace((stderr, "do_wild:  match() succeeds\n"));
112                     if (have_dirname) {
113                         strcpy(matchname, dirname);
114                         strcpy(matchname+dirnamelen, file->d_name);
115                     } else
116                         strcpy(matchname, file->d_name);
117                     return matchname;
118                 }
119             }
120             /* if we get to here directory is exhausted, so close it */
121             closedir(wild_dir);
122             wild_dir = (DIR *)NULL;
123         }
124 #ifdef DEBUG
125         else {
126             Trace((stderr, "do_wild:  Opendir(%s) returns NULL\n",
127               FnFilter1(dirname)));
128         }
129 #endif /* DEBUG */
130 
131         /* return the raw wildspec in case that works (e.g., directory not
132          * searchable, but filespec was not wild and file is readable) */
133         strncpy(matchname, wildspec, FILNAMSIZ);
134         matchname[FILNAMSIZ-1] = '\0';
135         return matchname;
136     }
137 
138     /* last time through, might have failed opendir but returned raw wildspec */
139     if (wild_dir == (DIR *)NULL) {
140         notfirstcall = FALSE; /* nothing left to try--reset for new wildspec */
141         if (have_dirname)
142             free(dirname);
143         return (char *)NULL;
144     }
145 
146     /* If we've gotten this far, we've read and matched at least one entry
147      * successfully (in a previous call), so dirname has been copied into
148      * matchname already.
149      */
150     while ((file = readdir(wild_dir)) != (struct dirent *)NULL) {
151         Trace((stderr, "do_wild:  readdir returns %s\n",
152           FnFilter1(file->d_name)));
153         if (file->d_name[0] == '.' && wildname[0] != '.')
154             continue;   /* Unix:  '*' and '?' do not match leading dot */
155         if (match(file->d_name, wildname, 0 WISEP)) {   /* 0 == case sens. */
156             Trace((stderr, "do_wild:  match() succeeds\n"));
157             if (have_dirname) {
158                 /* strcpy(matchname, dirname); */
159                 strcpy(matchname+dirnamelen, file->d_name);
160             } else
161                 strcpy(matchname, file->d_name);
162             return matchname;
163         }
164     }
165 
166     closedir(wild_dir);     /* have read at least one entry; nothing left */
167     wild_dir = (DIR *)NULL;
168     notfirstcall = FALSE;   /* reset for new wildspec */
169     if (have_dirname)
170         free(dirname);
171     return (char *)NULL;
172 
173 } /* end function do_wild() */
174 
175 #endif /* !SFX */
176 
177 
178 
179 
180 /**********************/
181 /* Function mapattr() */
182 /**********************/
183 
mapattr(__G)184 int mapattr(__G)
185     __GDEF
186 {
187     ulg  tmp = G.crec.external_file_attributes;
188 
189     switch (G.pInfo->hostnum) {
190         case UNIX_:
191             if (tmp & 0xff)
192                 break;
193             /* fall through */
194         case VMS_:
195         case ACORN_:
196         case ATARI_:
197         case ATHEOS_:
198         case BEOS_:
199         case QDOS_:
200             G.pInfo->file_attr = _mode2dos(tmp >> 16);
201             return 0;
202         default:
203             break;
204     }
205 
206     /* set archive bit (file is not backed up) */
207     if((tmp & 0x08) == 0)
208         tmp |= 0x20;
209     G.pInfo->file_attr = tmp & 0xff;
210     return 0;
211 
212 } /* end function mapattr() */
213 
214 
215 
216 
217 
218 /**********************/
219 /* Function mapname() */
220 /**********************/
221 
222 int mapname(__G__ renamed)
223     __GDEF
224     int renamed;
225 /*
226  * returns:
227  *  MPN_OK          - no problem detected
228  *  MPN_INF_TRUNC   - caution (truncated filename)
229  *  MPN_INF_SKIP    - info "skip entry" (dir doesn't exist)
230  *  MPN_ERR_SKIP    - error -> skip entry
231  *  MPN_ERR_TOOLONG - error -> path is too long
232  *  MPN_NOMEM       - error (memory allocation failed) -> skip entry
233  *  [also MPN_VOL_LABEL, MPN_CREATED_DIR]
234  */
235 {
236     char pathcomp[FILNAMSIZ];      /* path-component buffer */
237     char *pp, *cp=(char *)NULL;    /* character pointers */
238     char *lastsemi=(char *)NULL;   /* pointer to last semi-colon in pathcomp */
239     char *last_dot=(char *)NULL;   /* last dot */
240     int error = MPN_OK;
241     register unsigned workch;      /* hold the character being tested */
242 
243 #ifdef HAVE_TWONCALL_H
244     static char twentyone_flag;
245 
246     /* Get TwentyOne options */
247     if (twentyone_flag == 0) {
248         twentyone_flag++;
249         if (GetTwentyOneID () == TWON_ID) {
250             int flags = GetTwentyOneOptions ();
251 
252             if (flags & (1 << TWON_PERIOD_BIT))
253                 multi_period = TRUE;
254             if (flags & (1 << TWON_SPECIAL_BIT))
255                 special_char = TRUE;
256         }
257     }
258 #endif
259 
260 /*---------------------------------------------------------------------------
261     Initialize various pointers and counters and stuff.
262   ---------------------------------------------------------------------------*/
263 
264     /* can create path as long as not just freshening, or if user told us */
265     G.create_dirs = (!uO.fflag || renamed);
266 
267     created_dir = FALSE;        /* not yet */
268     renamed_fullpath = FALSE;
269 
270     if (renamed) {
271         cp = G.filename - 1;    /* point to beginning of renamed name... */
272         while (*++cp)
273             if (*cp == '\\')    /* convert backslashes to forward */
274                 *cp = '/';
275         cp = G.filename;
276         if ((G.filename[0] == '/')
277          || (isalpha(G.filename[0]) && G.filename[1] == ':')) {
278             /* user gave full pathname:  don't prepend rootpath */
279             renamed_fullpath = TRUE;
280         }
281     }
282 
283     if ((error = checkdir(__G__ (char *)NULL, INIT)) != 0)
284         return error;           /* initialize path buffer, unless no memory */
285 
286     *pathcomp = '\0';           /* initialize translation buffer */
287     pp = pathcomp;              /* point to translation buffer */
288     if (uO.jflag)               /* junking directories */
289         cp = (char *)strrchr(G.filename, '/');
290     if (cp == (char *)NULL)     /* no '/' or not junking dirs */
291         cp = G.filename;        /* point to internal zipfile-member pathname */
292     else
293         ++cp;                   /* point to start of last component of path */
294 
295 /*---------------------------------------------------------------------------
296     Begin main loop through characters in filename.
297   ---------------------------------------------------------------------------*/
298 
299     while ((workch = (uch)*cp++) != 0) {
300 
301         if (_ismbblead((unsigned char)workch)) {
302             if (*cp) {
303                 *pp++ = (char)workch;
304                 *pp++ = (char)*cp++;
305             }
306             else
307                 *pp++ = '_';
308             continue;
309         }
310 
311         switch (workch) {
312             case '/':             /* can assume -j flag not given */
313                 *pp = '\0';
314                 map2fat(pathcomp, last_dot);   /* 18.3 trunc. (in place) */
315                 if (strcmp(pathcomp, ".") == 0) {
316                     /* don't bother appending "./" to the path */
317                     *pathcomp = '\0';
318                 } else if (!uO.ddotflag && strcmp(pathcomp, "..") == 0) {
319                     /* "../" dir traversal detected, skip over it */
320                     *pathcomp = '\0';
321                     killed_ddot = TRUE;     /* set "show message" flag */
322                 }
323                 /* when path component is not empty, append it now */
324                 if (*pathcomp != '\0' &&
325                     ((error = checkdir(__G__ pathcomp, APPEND_DIR))
326                      & MPN_MASK) > MPN_INF_TRUNC)
327                     return error;
328                 pp = pathcomp;    /* reset conversion buffer for next piece */
329                 lastsemi = (char *)NULL; /* leave direct. semi-colons alone */
330                 break;
331 
332             /* drive names are not stored in zipfile, so no colons allowed;
333              *  no brackets or most other punctuation either (all of which
334              *  can appear in Unix-created archives; backslash is particularly
335              *  bad unless all necessary directories exist) */
336 
337             case '[':          /* these punctuation characters forbidden */
338             case ']':          /*  only on plain FAT file systems */
339             case '+':
340             case ',':
341             case '=':
342             case '<':
343             case '>':
344             case '|':
345             case '\"':
346             case '\'':
347                 if (!special_char)
348                     workch = '_';
349                 *pp++ = (char)workch;
350                 break;
351 
352             case '-':
353                 if (pp == pathcomp && !special_char)
354                     workch = '_';
355                 *pp++ = (char)workch;
356                 break;
357 
358             case ':':
359             case '\\':
360             case '*':
361             case '?':
362                 *pp++ = '_';
363                 break;
364 
365             case ';':             /* VMS version (or DEC-20 attrib?) */
366                 lastsemi = pp;
367                 if (!special_char)
368                     workch = '_';
369                 *pp++ = (char)workch;  /* keep for now; remove VMS ";##" */
370                 break;                 /*  later, if requested */
371 
372             case ' ':                      /* change spaces to underscores */
373 #if 0  /* do it always */
374                 if (uO.sflag)              /*  only if requested */
375 #endif
376                     workch = '_';
377                 *pp++ = (char)workch;
378                 break;
379 
380             default:
381                 /* allow European characters in filenames: */
382                 if (isprint(workch) || workch >= 128)
383                     *pp++ = (char)workch;
384 
385         } /* end switch */
386     } /* end while loop */
387 
388     /* Show warning when stripping insecure "parent dir" path components */
389     if (killed_ddot && QCOND2) {
390         Info(slide, 0, ((char *)slide,
391           "warning:  skipped \"../\" path component(s) in %s\n",
392           FnFilter1(G.filename)));
393         if (!(error & ~MPN_MASK))
394             error = (error & MPN_MASK) | PK_WARN;
395     }
396 
397 /*---------------------------------------------------------------------------
398     Report if directory was created (and no file to create:  filename ended
399     in '/'), check name to be sure it exists, and combine path and name be-
400     fore exiting.
401   ---------------------------------------------------------------------------*/
402 
403     if (G.filename[strlen(G.filename) - 1] == '/') {
404         checkdir(__G__ G.filename, GETPATH);
405         if (created_dir) {
406             if (QCOND2) {
407                 Info(slide, 0, ((char *)slide, "   creating: %s\n",
408                   FnFilter1(G.filename)));
409             }
410             /* set dir time (note trailing '/') */
411             return (error & ~MPN_MASK) | MPN_CREATED_DIR;
412         }
413         /* dir existed already; don't look for data to extract */
414         return (error & ~MPN_MASK) | MPN_INF_SKIP;
415     }
416 
417     *pp = '\0';                   /* done with pathcomp:  terminate it */
418 
419     /* if not saving them, remove VMS version numbers (appended ";###") */
420     if (!uO.V_flag && lastsemi) {
421         pp = lastsemi + 1;
422         while (isdigit((uch)(*pp)))
423             ++pp;
424         if (*pp == '\0')          /* only digits between ';' and end:  nuke */
425             *lastsemi = '\0';
426     }
427 
428     map2fat(pathcomp, last_dot);  /* 18.3 truncation (in place) */
429 
430     if (*pathcomp == '\0') {
431         Info(slide, 1, ((char *)slide, "mapname:  conversion of %s failed\n",
432           FnFilter1(G.filename)));
433         return (error & ~MPN_MASK) | MPN_ERR_SKIP;
434     }
435 
436     checkdir(__G__ pathcomp, APPEND_NAME);  /* returns 1 if truncated: care? */
437     checkdir(__G__ G.filename, GETPATH);
438 
439     if (G.pInfo->vollabel) {    /* set the volume label now */
440         int fd;
441 
442         if (QCOND2)
443             Info(slide, 0, ((char *)slide, "  labelling: %s\n",
444               FnFilter1(G.filename)));
445         if ((fd = _dos_newfile(G.filename, G.pInfo->file_attr)) < 0) {
446             Info(slide, 1, ((char *)slide,
447               "mapname:  error setting volume label\n"));
448             return (error & ~MPN_MASK) | MPN_ERR_SKIP;
449         }
450         _dos_close(fd);
451         /* success:  skip the "extraction" quietly */
452         return (error & ~MPN_MASK) | MPN_INF_SKIP;
453     }
454 
455     return error;
456 
457 } /* end function mapname() */
458 
459 
460 
461 
462 /**********************/
463 /* Function map2fat() */
464 /**********************/
465 
map2fat(pathcomp,last_dot)466 static void map2fat(pathcomp, last_dot)
467     char *pathcomp, *last_dot;
468 {
469     char *np;
470 
471     if (pathcomp == last_dot) {         /* dotfile(e.g. ".foo") */
472         pathcomp = last_dot;
473         last_dot = (char *)NULL;
474     }
475 
476     if (multi_period) {
477         if (strlen(pathcomp) <= 18)
478             return;
479     }
480     else {
481         char *p;
482 
483         for (p = pathcomp; *p; p++)
484             if (*p == (char)'.' && p != last_dot)
485                 *p = '_';
486     }
487 
488     if (last_dot) {
489         *last_dot++ = '\0';
490         trunc_name(last_dot, 3);
491     }
492     np = trunc_name(pathcomp, 18);
493     if (last_dot) {
494         *--last_dot = '.';
495         if (np)
496             strcpy(np, last_dot);
497     }
498 
499 } /* end function map2fat() */
500 
trunc_name(char * name,int maxlen)501 static char *trunc_name(char *name, int maxlen)
502 {
503 
504     if (strlen(name) <= maxlen)
505         return (char *)NULL;
506 
507     do {
508         if (_ismbblead((unsigned char)*name)) {
509             if (--maxlen == 0)
510                 break;
511             name++;
512         }
513         name++;
514         maxlen--;
515     } while (maxlen > 0);
516     *name = '\0';
517 
518     return name;
519 }
520 
521 
522 
523 
524 /***********************/
525 /* Function checkdir() */
526 /***********************/
527 
528 int checkdir(__G__ pathcomp, flag)
529     __GDEF
530     char *pathcomp;
531     int flag;
532 /*
533  * returns:
534  *  MPN_OK          - no problem detected
535  *  MPN_INF_TRUNC   - (on APPEND_NAME) truncated filename
536  *  MPN_INF_SKIP    - path doesn't exist, not allowed to create
537  *  MPN_ERR_SKIP    - path doesn't exist, tried to create and failed; or path
538  *                    exists and is not a directory, but is supposed to be
539  *  MPN_ERR_TOOLONG - path is too long
540  *  MPN_NOMEM       - can't allocate memory for filename buffers
541  */
542 {
543     static int rootlen = 0;   /* length of rootpath */
544     static char *rootpath;    /* user's "extract-to" directory */
545     static char *buildpath;   /* full path (so far) to extracted file */
546     static char *end;         /* pointer to end of buildpath ('\0') */
547 
548 #   define FN_MASK   7
549 #   define FUNCTION  (flag & FN_MASK)
550 
551 
552 /*---------------------------------------------------------------------------
553     APPEND_DIR:  append the path component to the path being built and check
554     for its existence.  If doesn't exist and we are creating directories, do
555     so for this one; else signal success or error as appropriate.
556   ---------------------------------------------------------------------------*/
557 
558     if (FUNCTION == APPEND_DIR) {
559         int too_long = FALSE;
560 
561         Trace((stderr, "appending dir segment [%s]\n", FnFilter1(pathcomp)));
562         while ((*end = *pathcomp++) != '\0')
563             ++end;
564 
565         /* GRR:  could do better check, see if overrunning buffer as we go:
566          * check end-buildpath after each append, set warning variable if
567          * within 20 of FILNAMSIZ; then if var set, do careful check when
568          * appending.  Clear variable when begin new path. */
569 
570         if ((end-buildpath) > FILNAMSIZ-3)  /* need '/', one-char name, '\0' */
571             too_long = TRUE;                /* check if extracting directory? */
572         if (SSTAT(buildpath, &G.statbuf))   /* path doesn't exist */
573         {
574             if (!G.create_dirs) { /* told not to create (freshening) */
575                 free(buildpath);
576                 return MPN_INF_SKIP;    /* path doesn't exist: nothing to do */
577             }
578             if (too_long) {
579                 Info(slide, 1, ((char *)slide,
580                   "checkdir error:  path too long: %s\n",
581                   FnFilter1(buildpath)));
582                 free(buildpath);
583                 /* no room for filenames:  fatal */
584                 return MPN_ERR_TOOLONG;
585             }
586             if (mkdir(buildpath, 0777) == -1) {   /* create the directory */
587                 Info(slide, 1, ((char *)slide,
588                   "checkdir error:  cannot create %s\n\
589                  unable to process %s.\n",
590                   FnFilter2(buildpath), FnFilter1(G.filename)));
591                 free(buildpath);
592                 /* path didn't exist, tried to create, failed */
593                 return MPN_ERR_SKIP;
594             }
595             created_dir = TRUE;
596         } else if (!S_ISDIR(G.statbuf.st_mode)) {
597             Info(slide, 1, ((char *)slide,
598               "checkdir error:  %s exists but is not directory\n\
599                  unable to process %s.\n",
600               FnFilter2(buildpath), FnFilter1(G.filename)));
601             free(buildpath);
602             /* path existed but wasn't dir */
603             return MPN_ERR_SKIP;
604         }
605         if (too_long) {
606             Info(slide, 1, ((char *)slide,
607               "checkdir error:  path too long: %s\n", FnFilter1(buildpath)));
608             free(buildpath);
609             /* no room for filenames:  fatal */
610             return MPN_ERR_TOOLONG;
611         }
612         *end++ = '/';
613         *end = '\0';
614         Trace((stderr, "buildpath now = [%s]\n", FnFilter1(buildpath)));
615         return MPN_OK;
616 
617     } /* end if (FUNCTION == APPEND_DIR) */
618 
619 /*---------------------------------------------------------------------------
620     GETPATH:  copy full path to the string pointed at by pathcomp, and free
621     buildpath.
622   ---------------------------------------------------------------------------*/
623 
624     if (FUNCTION == GETPATH) {
625         strcpy(pathcomp, buildpath);
626         Trace((stderr, "getting and freeing path [%s]\n",
627           FnFilter1(pathcomp)));
628         free(buildpath);
629         buildpath = end = (char *)NULL;
630         return MPN_OK;
631     }
632 
633 /*---------------------------------------------------------------------------
634     APPEND_NAME:  assume the path component is the filename; append it and
635     return without checking for existence.
636   ---------------------------------------------------------------------------*/
637 
638     if (FUNCTION == APPEND_NAME) {
639 
640         Trace((stderr, "appending filename [%s]\n", FnFilter1(pathcomp)));
641         while ((*end = *pathcomp++) != '\0') {
642             ++end;
643             if ((end-buildpath) >= FILNAMSIZ) {
644                 *--end = '\0';
645                 Info(slide, 1, ((char *)slide,
646                   "checkdir warning:  path too long; truncating\n\
647                    %s\n                -> %s\n",
648                   FnFilter1(G.filename), FnFilter2(buildpath)));
649                 return MPN_INF_TRUNC;   /* filename truncated */
650             }
651         }
652         Trace((stderr, "buildpath now = [%s]\n", FnFilter1(buildpath)));
653         /* could check for existence here, prompt for new name... */
654         return MPN_OK;
655     }
656 
657 /*---------------------------------------------------------------------------
658     INIT:  allocate and initialize buffer space for the file currently being
659     extracted.  If file was renamed with an absolute path, don't prepend the
660     extract-to path.
661   ---------------------------------------------------------------------------*/
662 
663     if (FUNCTION == INIT) {
664         Trace((stderr, "initializing buildpath to "));
665         /* allocate space for full filename, root path, and maybe "./" */
666         if ((buildpath = (char *)malloc(strlen(G.filename)+rootlen+3)) ==
667             (char *)NULL)
668             return MPN_NOMEM;
669         if ((rootlen > 0) && !renamed_fullpath) {
670             strcpy(buildpath, rootpath);
671             end = buildpath + rootlen;
672         } else {
673             *buildpath = '\0';
674             end = buildpath;
675         }
676         Trace((stderr, "[%s]\n", FnFilter1(buildpath)));
677         return MPN_OK;
678     }
679 
680 /*---------------------------------------------------------------------------
681     ROOT:  if appropriate, store the path in rootpath and create it if neces-
682     sary; else assume it's a zipfile member and return.  This path segment
683     gets used in extracting all members from every zipfile specified on the
684     command line.
685   ---------------------------------------------------------------------------*/
686 
687 #if (!defined(SFX) || defined(SFX_EXDIR))
688     if (FUNCTION == ROOT) {
689         Trace((stderr, "initializing root path to [%s]\n",
690           FnFilter1(pathcomp)));
691         if (pathcomp == (char *)NULL) {
692             rootlen = 0;
693             return MPN_OK;
694         }
695         if (rootlen > 0)        /* rootpath was already set, nothing to do */
696             return MPN_OK;
697         if ((rootlen = strlen(pathcomp)) > 0) {
698             int had_trailing_pathsep=FALSE, has_drive=FALSE, add_dot=FALSE;
699             char *tmproot;
700 
701             if ((tmproot = (char *)malloc(rootlen+3)) == (char *)NULL) {
702                 rootlen = 0;
703                 return MPN_NOMEM;
704             }
705             strcpy(tmproot, pathcomp);
706             if (isalpha((uch)tmproot[0]) && tmproot[1] == ':')
707                 has_drive = TRUE;   /* drive designator */
708             if (tmproot[rootlen-1] == '/' || tmproot[rootlen-1] == '\\') {
709                 tmproot[--rootlen] = '\0';
710                 had_trailing_pathsep = TRUE;
711             }
712             if (has_drive && (rootlen == 2)) {
713                 if (!had_trailing_pathsep)   /* i.e., original wasn't "x:/" */
714                     add_dot = TRUE;    /* relative path: add '.' before '/' */
715             } else if (rootlen > 0 && (SSTAT(tmproot, &G.statbuf) ||
716                        !S_ISDIR(G.statbuf.st_mode))) /* path does not exist */
717             {
718                 if (!G.create_dirs /* || iswild(tmproot) */ ) {
719                     free(tmproot);
720                     rootlen = 0;
721                     /* skip (or treat as stored file) */
722                     return MPN_INF_SKIP;
723                 }
724                 /* create the directory (could add loop here scanning tmproot
725                  * to create more than one level, but why really necessary?) */
726                 if (mkdir(tmproot, 0777) == -1) {
727                     Info(slide, 1, ((char *)slide,
728                       "checkdir:  cannot create extraction directory: %s\n",
729                       FnFilter1(tmproot)));
730                     free(tmproot);
731                     rootlen = 0;
732                     /* path didn't exist, tried to create, and failed: */
733                     /* file exists, or 2+ subdir levels required */
734                     return MPN_ERR_SKIP;
735                 }
736             }
737             if (add_dot)                    /* had just "x:", make "x:." */
738                 tmproot[rootlen++] = '.';
739             tmproot[rootlen++] = '/';
740             tmproot[rootlen] = '\0';
741             if ((rootpath = (char *)realloc(tmproot, rootlen+1)) == NULL) {
742                 free(tmproot);
743                 rootlen = 0;
744                 return MPN_NOMEM;
745             }
746             Trace((stderr, "rootpath now = [%s]\n", FnFilter1(rootpath)));
747         }
748         return MPN_OK;
749     }
750 #endif /* !SFX || SFX_EXDIR */
751 
752 /*---------------------------------------------------------------------------
753     END:  free rootpath, immediately prior to program exit.
754   ---------------------------------------------------------------------------*/
755 
756     if (FUNCTION == END) {
757         Trace((stderr, "freeing rootpath\n"));
758         if (rootlen > 0) {
759             free(rootpath);
760             rootlen = 0;
761         }
762         return MPN_OK;
763     }
764 
765     return MPN_INVALID; /* should never reach */
766 
767 } /* end function checkdir() */
768 
769 
770 
771 
772 #if (defined(USE_EF_UT_TIME) || defined(TIMESTAMP))
773 /* The following DOS date/time structure is machine-dependent as it
774  * assumes "little-endian" byte order.  For MSDOS-specific code, which
775  * is run on ix86 CPUs (or emulators), this assumption is valid; but
776  * care should be taken when using this code as template for other ports.
777  */
778 typedef union {
779     ulg z_dostime;
780     struct {                    /* date and time words */
781         ush ztime;              /* DOS file modification time word */
782         ush zdate;              /* DOS file modification date word */
783     } zft;
784     struct {                    /* DOS date/time components bitfield */
785         unsigned zt_se : 5;
786         unsigned zt_mi : 6;
787         unsigned zt_hr : 5;
788         unsigned zd_dy : 5;
789         unsigned zd_mo : 4;
790         unsigned zd_yr : 7;
791     } z_dtf;
792 } dos_fdatetime;
793 #endif /* USE_EF_UT_TIME || TIMESTAMP */
794 
795 
796 /****************************/
797 /* Function close_outfile() */
798 /****************************/
799 
close_outfile(__G)800 void close_outfile(__G)
801     __GDEF
802 {
803     /* skip restoring time stamps on user's request */
804     if (uO.D_flag <= 1) {
805 #ifdef USE_EF_UT_TIME
806         dos_fdatetime dos_dt;
807         iztimes z_utime;
808         struct tm *t;
809 #endif /* USE_EF_UT_TIME */
810 
811 
812 #ifdef USE_EF_UT_TIME
813         if (G.extra_field &&
814 #ifdef IZ_CHECK_TZ
815             G.tz_is_valid &&
816 #endif
817             (ef_scan_for_izux(G.extra_field, G.lrec.extra_field_length, 0,
818                               G.lrec.last_mod_dos_datetime, &z_utime, NULL)
819              & EB_UT_FL_MTIME))
820         {
821             TTrace((stderr, "close_outfile:  Unix e.f. modif. time = %ld\n",
822               z_utime.mtime));
823             /* round up (down if "up" overflows) to even seconds */
824             if (z_utime.mtime & 1)
825                 z_utime.mtime = (z_utime.mtime + 1 > z_utime.mtime) ?
826                                  z_utime.mtime + 1 : z_utime.mtime - 1;
827             TIMET_TO_NATIVE(z_utime.mtime) /* NOP unless MSC 7 or Macintosh */
828             t = localtime(&(z_utime.mtime));
829         } else
830             t = (struct tm *)NULL;
831         if (t != (struct tm *)NULL) {
832             if (t->tm_year < 80) {
833                 dos_dt.z_dtf.zt_se = 0;
834                 dos_dt.z_dtf.zt_mi = 0;
835                 dos_dt.z_dtf.zt_hr = 0;
836                 dos_dt.z_dtf.zd_dy = 1;
837                 dos_dt.z_dtf.zd_mo = 1;
838                 dos_dt.z_dtf.zd_yr = 0;
839             } else {
840                 dos_dt.z_dtf.zt_se = t->tm_sec >> 1;
841                 dos_dt.z_dtf.zt_mi = t->tm_min;
842                 dos_dt.z_dtf.zt_hr = t->tm_hour;
843                 dos_dt.z_dtf.zd_dy = t->tm_mday;
844                 dos_dt.z_dtf.zd_mo = t->tm_mon + 1;
845                 dos_dt.z_dtf.zd_yr = t->tm_year - 80;
846             }
847         } else {
848             dos_dt.z_dostime = G.lrec.last_mod_dos_datetime;
849         }
850         _dos_filedate(fileno(G.outfile), dos_dt.z_dostime);
851 #else /* !USE_EF_UT_TIME */
852         _dos_filedate(fileno(G.outfile), G.lrec.last_mod_dos_datetime);
853 #endif /* ?USE_EF_UT_TIME */
854     }
855 
856     fclose(G.outfile);
857 
858     _dos_chmod(G.filename, G.pInfo->file_attr);
859 
860 } /* end function close_outfile() */
861 
862 
863 
864 
865 
866 #ifdef TIMESTAMP
867 
868 /*************************/
869 /* Function stamp_file() */
870 /*************************/
871 
stamp_file(fname,modtime)872 int stamp_file(fname, modtime)
873     ZCONST char *fname;
874     time_t modtime;
875 {
876     dos_fdatetime dos_dt;
877     time_t t_even;
878     struct tm *t;
879     int fd;                             /* file handle */
880 
881     /* round up (down if "up" overflows) to even seconds */
882     t_even = ((modtime + 1 > modtime) ? modtime + 1 : modtime) & (~1);
883     TIMET_TO_NATIVE(t_even)             /* NOP unless MSC 7.0 or Macintosh */
884     t = localtime(&t_even);
885     if (t == (struct tm *)NULL)
886         return -1;                      /* time conversion error */
887     if (t->tm_year < 80) {
888         dos_dt.z_dtf.zt_se = 0;
889         dos_dt.z_dtf.zt_mi = 0;
890         dos_dt.z_dtf.zt_hr = 0;
891         dos_dt.z_dtf.zd_dy = 1;
892         dos_dt.z_dtf.zd_mo = 1;
893         dos_dt.z_dtf.zd_yr = 0;
894     } else {
895         dos_dt.z_dtf.zt_se = t->tm_sec >> 1;
896         dos_dt.z_dtf.zt_mi = t->tm_min;
897         dos_dt.z_dtf.zt_hr = t->tm_hour;
898         dos_dt.z_dtf.zd_dy = t->tm_mday;
899         dos_dt.z_dtf.zd_mo = t->tm_mon + 1;
900         dos_dt.z_dtf.zd_yr = t->tm_year - 80;
901     }
902     if (((fd = open((char *)fname, 0)) == -1) ||
903         (_dos_filedate(fd, dos_dt.z_dostime)))
904     {
905         if (fd != -1)
906             close(fd);
907         return -1;
908     }
909     close(fd);
910     return 0;
911 
912 } /* end function stamp_file() */
913 
914 #endif /* TIMESTAMP */
915 
916 
917 
918 
919 #ifndef SFX
920 
921 /************************/
922 /*  Function version()  */
923 /************************/
924 
version(__G)925 void version(__G)
926     __GDEF
927 {
928     int len;
929 #if 0
930     char buf[40];
931 #endif
932 
933     len = sprintf((char *)slide, LoadFarString(CompiledWith),
934 
935 #ifdef __GNUC__
936       "gcc ", __VERSION__,
937 #else
938 #  if 0
939       "cc ", (sprintf(buf, " version %d", _RELEASE), buf),
940 #  else
941       "unknown compiler", "",
942 #  endif
943 #endif
944 
945       "Human68k",
946 #ifdef __MC68020__
947       " (X68030)",
948 #else
949       " (X680x0)",
950 #endif
951 
952 #ifdef __DATE__
953       " on ", __DATE__
954 #else
955       "", ""
956 #endif
957       );
958 
959     (*G.message)((zvoid *)&G, slide, (ulg)len, 0);
960 
961 } /* end function version() */
962 
963 #endif /* !SFX */
964 
965 
966 #if defined (SFX) && defined (MAIN)
main(int argc,char * argv[])967 int main(int argc, char *argv[])
968 {
969     char argv0[92];
970 
971     /* make true argv[0] (startup routine makes it inaccuracy) */
972     argv[0] = strcat (strcpy (argv0, _procp->exe_path), _procp->exe_name);
973 
974     return MAIN(argc, argv);
975 }
976 #endif /* SFX && MAIN */
977