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