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  amiga.c
12
13  Amiga-specific routines for use with Info-ZIP's UnZip 5.1 and later.
14  See History.5xx for revision history.
15
16  Contents:   do_wild()
17              mapattr()
18              mapname()
19              checkdir()
20              close_outfile()
21              stamp_file()
22              _abort()                (Aztec C only)
23             [dateformat()]           (currently not used)
24              screensize()
25              version()
26
27  ------------------------------------------------------------------------*/
28
29
30#define UNZIP_INTERNAL
31#ifdef AZTEC_C
32#  define NO_FCNTL_H
33#endif
34#include "unzip.h"
35#include "unzvers.h"
36
37/* Globular varibundus -- now declared in SYSTEM_SPECIFIC_GLOBALS in amiga.h */
38
39/* static int created_dir; */      /* used in mapname(), checkdir() */
40/* static int renamed_fullpath; */ /* ditto */
41
42#define PERMS   0777
43#define MKDIR(path,mode) mkdir(path)
44
45#ifndef S_ISCRIPT          /* not having one implies you have none */
46#  define S_IARCHIVE 0020  /* not modified since this bit was last set */
47#  define S_IREAD    0010  /* can be opened for reading */
48#  define S_IWRITE   0004  /* can be opened for writing */
49#  define S_IDELETE  0001  /* can be deleted */
50#endif /* S_ISCRIPT */
51
52#ifndef S_IRWD
53#  define S_IRWD     0015  /* useful combo of Amiga privileges */
54#endif /* !S_IRWD */
55
56#ifndef S_IHIDDEN
57#  define S_IHIDDEN  0200  /* hidden supported in future AmigaDOS (someday) */
58#endif /* !S_HIDDEN */
59
60#ifndef SFX
61/* Make sure the number here matches unzvers.h in the *EXACT* form */
62/* UZ_MAJORVER "." UZ_MINORVER UZ_PATCHLEVEL vvvv  No non-digits!  */
63const char version_id[]  = "\0$VER: UnZip " UZ_VER_STRING " ("
64#include "env:VersionDate"
65   ")\r\n";
66#endif /* SFX */
67
68
69static int ispattern(ZCONST char *p)
70{
71    register char c;
72    while (c = *p++)
73        if (c == '\\') {
74            if (!*++p)
75                return FALSE;
76        } else if (c == '?' || c == '*')
77            return TRUE;
78        else if (c == '[') {
79            for (;;) {
80                if (!(c = *p++))
81                    return FALSE;
82                else if (c == '\\') {
83                    if (!*++p)
84                        return FALSE;
85                } else if (c == ']')
86                    return TRUE;
87            }
88        }
89    return FALSE;
90}
91
92/**********************/
93/* Function do_wild() */
94/**********************/
95
96char *do_wild(__G__ wildspec)
97    __GDEF
98    ZCONST char *wildspec;  /* only used first time on a given dir */
99{
100/* these statics are now declared in SYSTEM_SPECIFIC_GLOBALS in amiga.h:
101    static DIR *wild_dir = NULL;
102    static ZCONST char *wildname;
103    static char *dirname, matchname[FILNAMSIZ];
104    static int notfirstcall = FALSE, dirnamelen;
105*/
106    struct dirent *file;
107    BPTR lok = 0;
108
109    /* Even when we're just returning wildspec, we *always* do so in
110     * matchname[]--calling routine is allowed to append four characters
111     * to the returned string, and wildspec may be a pointer to argv[].
112     */
113    if (!G.notfirstcall) {      /* first call:  must initialize everything */
114        G.notfirstcall = TRUE;
115
116        /* avoid needless readdir() scans: */
117        if (!ispattern(wildspec) ||
118                (lok = Lock((char *)wildspec, ACCESS_READ))) {
119            if (lok) UnLock(lok);       /* ^^ we ignore wildcard chars if */
120            G.dirnamelen = 0;           /* the name matches a real file   */
121            strncpy(G.matchname, wildspec, FILNAMSIZ);
122            G.matchname[FILNAMSIZ-1] = '\0';
123            return G.matchname;
124        }
125
126        /* break the wildspec into a directory part and a wildcard filename */
127        if ((G.wildname = (ZCONST char *)strrchr(wildspec, '/')) == NULL &&
128            (G.wildname = (ZCONST char *)strrchr(wildspec, ':')) == NULL) {
129            G.dirname = "";             /* current dir */
130            G.dirnamelen = 0;
131            G.wildname = wildspec;
132        } else {
133            ++G.wildname;     /* point at character after '/' or ':' */
134            G.dirnamelen = G.wildname - wildspec;
135            if ((G.dirname = (char *)malloc(G.dirnamelen+1)) == NULL) {
136                Info(slide, 1, ((char *)slide,
137                     "warning:  cannot allocate wildcard buffers\n"));
138                strncpy(G.matchname, wildspec, FILNAMSIZ);
139                G.matchname[FILNAMSIZ-1] = '\0';
140                return G.matchname; /* but maybe filespec was not a wildcard */
141            }
142            strncpy(G.dirname, wildspec, G.dirnamelen);
143            G.dirname[G.dirnamelen] = '\0';
144        }
145
146        if ((G.wild_dir = opendir(G.dirname)) != NULL) {
147            while ((file = readdir(G.wild_dir)) != NULL) {
148                if (match(file->d_name, G.wildname, 1 WISEP)) {/* ignore case */
149                    strcpy(G.matchname, G.dirname);
150                    strcpy(G.matchname + G.dirnamelen, file->d_name);
151                    return G.matchname;
152                }
153            }
154            /* if we get to here directory is exhausted, so close it */
155            closedir(G.wild_dir);
156            G.wild_dir = NULL;
157        }
158
159        /* return the raw wildspec in case that works (e.g., directory not
160         * searchable, but filespec was not wild and file is readable) */
161        strncpy(G.matchname, wildspec, FILNAMSIZ);
162        G.matchname[FILNAMSIZ-1] = '\0';
163        return G.matchname;
164    }
165
166    /* last time through, might have failed opendir but returned raw wildspec */
167    if (G.wild_dir == NULL) {
168        G.notfirstcall = FALSE;    /* nothing left to try -- reset */
169        if (G.dirnamelen > 0)
170            free(G.dirname);
171        return (char *)NULL;
172    }
173
174    /* If we've gotten this far, we've read and matched at least one entry
175     * successfully (in a previous call), so dirname has been copied into
176     * matchname already.
177     */
178    while ((file = readdir(G.wild_dir)) != NULL)
179        if (match(file->d_name, G.wildname, 1 WISEP)) { /* 1 == ignore case */
180            /* strcpy(G.matchname, dirname); */
181            strcpy(G.matchname + G.dirnamelen, file->d_name);
182            return G.matchname;
183        }
184
185    closedir(G.wild_dir);  /* have read at least one dir entry; nothing left */
186    G.wild_dir = NULL;
187    G.notfirstcall = FALSE; /* reset for new wildspec */
188    if (G.dirnamelen > 0)
189        free(G.dirname);
190    return (char *)NULL;
191
192} /* end function do_wild() */
193
194
195
196
197/**********************/
198/* Function mapattr() */
199/**********************/
200
201int mapattr(__G)      /* Amiga version */
202    __GDEF
203{
204    ulg  tmp = G.crec.external_file_attributes;
205
206
207    /* Amiga attributes = hsparwed = hidden, script, pure, archive,
208     * read, write, execute, delete */
209
210    switch (G.pInfo->hostnum) {
211        case AMIGA_:
212            if ((tmp & 1) == (tmp>>18 & 1))
213                tmp ^= 0x000F0000;      /* PKAZip compatibility kluge */
214            /* turn off archive bit for restored Amiga files */
215            G.pInfo->file_attr = (unsigned)((tmp>>16) & (~S_IARCHIVE));
216            break;
217
218        case UNIX_:   /* preserve read, write, execute:  use logical-OR of */
219        case VMS_:    /* user, group, and other; if writable, set delete bit */
220        case ACORN_:
221        case ATARI_:
222        case ATHEOS_:
223        case BEOS_:
224        case QDOS_:
225        case TANDEM_:
226            {
227              unsigned uxattr = (unsigned)(tmp >> 16);
228              int r = FALSE;
229
230              if (uxattr == 0 && G.extra_field) {
231                /* Some (non-Info-ZIP) implementations of Zip for Unix and
232                   VMS (and probably others ??) leave 0 in the upper 16-bit
233                   part of the external_file_attributes field. Instead, they
234                   store file permission attributes in some extra field.
235                   As a work-around, we search for the presence of one of
236                   these extra fields and fall back to the MSDOS compatible
237                   part of external_file_attributes if one of the known
238                   e.f. types has been detected.
239                   Later, we might implement extraction of the permission
240                   bits from the VMS extra field. But for now, the work-around
241                   should be sufficient to provide "readable" extracted files.
242                   (For ASI Unix e.f., an experimental remap of the e.f.
243                   mode value IS already provided!)
244                 */
245                ush ebID;
246                unsigned ebLen;
247                uch *ef = G.extra_field;
248                unsigned ef_len = G.crec.extra_field_length;
249
250                while (!r && ef_len >= EB_HEADSIZE) {
251                    ebID = makeword(ef);
252                    ebLen = (unsigned)makeword(ef+EB_LEN);
253                    if (ebLen > (ef_len - EB_HEADSIZE))
254                        /* discoverd some e.f. inconsistency! */
255                        break;
256                    switch (ebID) {
257                      case EF_ASIUNIX:
258                        if (ebLen >= (EB_ASI_MODE+2)) {
259                            uxattr =
260                              (unsigned)makeword(ef+(EB_HEADSIZE+EB_ASI_MODE));
261                            /* force stop of loop: */
262                            ef_len = (ebLen + EB_HEADSIZE);
263                            break;
264                        }
265                        /* else: fall through! */
266                      case EF_PKVMS:
267                        /* "found nondecypherable e.f. with perm. attr" */
268                        r = TRUE;
269                      default:
270                        break;
271                    }
272                    ef_len -= (ebLen + EB_HEADSIZE);
273                    ef += (ebLen + EB_HEADSIZE);
274                }
275              }
276              if (!r) {
277                uxattr = (( uxattr>>6 | uxattr>>3 | uxattr) & 07) << 1;
278                G.pInfo->file_attr = (unsigned)(uxattr&S_IWRITE ?
279                                                uxattr|S_IDELETE : uxattr);
280                break;
281              }
282            }
283            /* fall through! */
284
285        /* all other platforms:  assume read-only bit in DOS half of attribute
286         * word is set correctly ==> will become READ or READ+WRITE+DELETE */
287        case FS_FAT_:
288        case FS_HPFS_:  /* can add S_IHIDDEN check to MSDOS/OS2/NT eventually */
289        case FS_NTFS_:
290        case MAC_:
291        case TOPS20_:
292        default:
293            G.pInfo->file_attr = (unsigned)(tmp&1? S_IREAD : S_IRWD);
294            break;
295
296    } /* end switch (host-OS-created-by) */
297
298    G.pInfo->file_attr &= 0xff;   /* mask off all but lower eight bits */
299    return 0;
300
301} /* end function mapattr() */
302
303
304
305
306/************************/
307/*  Function mapname()  */
308/************************/
309
310int mapname(__G__ renamed)
311    __GDEF
312    int renamed;
313/*
314 * returns:
315 *  MPN_OK          - no problem detected
316 *  MPN_INF_TRUNC   - caution (truncated filename)
317 *  MPN_INF_SKIP    - info "skip entry" (dir doesn't exist)
318 *  MPN_ERR_SKIP    - error -> skip entry
319 *  MPN_ERR_TOOLONG - error -> path is too long
320 *  MPN_NOMEM       - error (memory allocation failed) -> skip entry
321 *  [also MPN_VOL_LABEL, MPN_CREATED_DIR]
322 */
323{
324    char pathcomp[FILNAMSIZ];   /* path-component buffer */
325    char *pp, *cp=NULL;         /* character pointers */
326    char *lastsemi = NULL;      /* pointer to last semi-colon in pathcomp */
327    int killed_ddot = FALSE;    /* is set when skipping "../" pathcomp */
328    int error = MPN_OK;
329    register unsigned workch;   /* hold the character being tested */
330
331
332/*---------------------------------------------------------------------------
333    Initialize various pointers and counters and stuff.
334  ---------------------------------------------------------------------------*/
335
336    if (G.pInfo->vollabel)
337        return MPN_VOL_LABEL;   /* can't set disk volume labels in AmigaDOS */
338
339    /* can create path as long as not just freshening, or if user told us */
340    G.create_dirs = (!uO.fflag || renamed);
341
342    G.created_dir = FALSE;      /* not yet */
343
344    /* user gave full pathname:  don't prepend G.rootpath */
345#ifndef OLD_AMIGA_RENAMED
346    G.renamed_fullpath = (renamed &&
347                          (*G.filename == '/' || *G.filename == ':'));
348#else
349    /* supress G.rootpath even when user gave a relative pathname */
350# if 1
351    G.renamed_fullpath = (renamed && strpbrk(G.filename, ":/");
352# else
353    G.renamed_fullpath = (renamed &&
354                          (strchr(G.filename, ':') || strchr(G.filename, '/')));
355# endif
356#endif
357
358    if (checkdir(__G__ (char *)NULL, INIT) == MPN_NOMEM)
359        return MPN_NOMEM;       /* initialize path buffer, unless no memory */
360
361    *pathcomp = '\0';           /* initialize translation buffer */
362    pp = pathcomp;              /* point to translation buffer */
363    if (uO.jflag)               /* junking directories */
364        cp = (char *)strrchr(G.filename, '/');
365    if (cp == (char *)NULL)     /* no '/' or not junking dirs */
366        cp = G.filename;        /* point to internal zipfile-member pathname */
367    else
368        ++cp;                   /* point to start of last component of path */
369
370/*---------------------------------------------------------------------------
371    Begin main loop through characters in filename.
372  ---------------------------------------------------------------------------*/
373
374    while ((workch = (uch)*cp++) != 0) {
375
376        switch (workch) {
377            case '/':             /* can assume -j flag not given */
378                *pp = '\0';
379                if (strcmp(pathcomp, ".") == 0) {
380                    /* don't bother appending "./" to the path */
381                    *pathcomp = '\0';
382                } else if (!uO.ddotflag && strcmp(pathcomp, "..") == 0) {
383                    /* "../" dir traversal detected, skip over it */
384                    *pathcomp = '\0';
385                    killed_ddot = TRUE;     /* set "show message" flag */
386                }
387                /* when path component is not empty, append it now */
388                if (*pathcomp != '\0' &&
389                    ((error = checkdir(__G__ pathcomp, APPEND_DIR))
390                     & MPN_MASK) > MPN_INF_TRUNC)
391                    return error;
392                pp = pathcomp;    /* reset conversion buffer for next piece */
393                lastsemi = (char *)NULL; /* leave direct. semi-colons alone */
394                break;
395
396            case ';':             /* VMS version (or DEC-20 attrib?) */
397                lastsemi = pp;         /* keep for now; remove VMS ";##" */
398                *pp++ = (char)workch;  /*  later, if requested */
399                break;
400
401            default:
402                /* allow ISO European characters in filenames: */
403                if (isprint(workch) || (160 <= workch && workch <= 255))
404                    *pp++ = (char)workch;
405        } /* end switch */
406
407    } /* end while loop */
408
409    /* Show warning when stripping insecure "parent dir" path components */
410    if (killed_ddot && QCOND2) {
411        Info(slide, 0, ((char *)slide,
412          "warning:  skipped \"../\" path component(s) in %s\n",
413          FnFilter1(G.filename)));
414        if (!(error & ~MPN_MASK))
415            error = (error & MPN_MASK) | PK_WARN;
416    }
417
418/*---------------------------------------------------------------------------
419    Report if directory was created (and no file to create:  filename ended
420    in '/'), check name to be sure it exists, and combine path and name be-
421    fore exiting.
422  ---------------------------------------------------------------------------*/
423
424    if (G.filename[strlen(G.filename) - 1] == '/') {
425        checkdir(__G__ G.filename, GETPATH);
426        if (G.created_dir) {
427            if (QCOND2) {
428                Info(slide, 0, ((char *)slide, "   creating: %s\n",
429                  FnFilter1(G.filename)));
430            }
431            /* set dir time (note trailing '/') */
432            return (error & ~MPN_MASK) | MPN_CREATED_DIR;
433        }
434        /* dir existed already; don't look for data to extract */
435        return (error & ~MPN_MASK) | MPN_INF_SKIP;
436    }
437
438    *pp = '\0';                   /* done with pathcomp:  terminate it */
439
440    /* if not saving them, remove VMS version numbers (appended ";###") */
441    if (!uO.V_flag && lastsemi) {
442        pp = lastsemi + 1;
443        while (isdigit((uch)(*pp)))
444            ++pp;
445        if (*pp == '\0')          /* only digits between ';' and end:  nuke */
446            *lastsemi = '\0';
447    }
448
449    if (*pathcomp == '\0') {
450        Info(slide, 1, ((char *)slide, "mapname:  conversion of %s failed\n",
451          FnFilter1(G.filename)));
452        return (error & ~MPN_MASK) | MPN_ERR_SKIP;
453    }
454
455    error = (error & ~MPN_MASK) | checkdir(__G__ pathcomp, APPEND_NAME);
456    if ((error & MPN_MASK) == MPN_INF_TRUNC) {
457        /* GRR:  OK if truncated here:  warn and continue */
458        /* (warn in checkdir?) */
459    }
460    checkdir(__G__ G.filename, GETPATH);
461
462    return error;
463
464} /* end function mapname() */
465
466
467
468
469/***********************/
470/* Function checkdir() */
471/***********************/
472
473int checkdir(__G__ pathcomp, flag)
474    __GDEF
475    char *pathcomp;
476    int flag;
477/*
478 * returns:
479 *  MPN_OK          - no problem detected
480 *  MPN_INF_TRUNC   - (on APPEND_NAME) truncated filename
481 *  MPN_INF_SKIP    - path doesn't exist, not allowed to create
482 *  MPN_ERR_SKIP    - path doesn't exist, tried to create and failed; or path
483 *                    exists and is not a directory, but is supposed to be
484 *  MPN_ERR_TOOLONG - path is too long
485 *  MPN_NOMEM       - can't allocate memory for filename buffers
486 */
487{
488/* these statics are now declared in SYSTEM_SPECIFIC_GLOBALS in amiga.h: */
489/*  static int rootlen = 0; */   /* length of rootpath */
490/*  static char *rootpath;  */   /* user's "extract-to" directory */
491/*  static char *buildpath; */   /* full path (so far) to extracted file */
492/*  static char *end;       */   /* pointer to end of buildpath ('\0') */
493
494#   define FN_MASK   7
495#   define FUNCTION  (flag & FN_MASK)
496
497
498/*---------------------------------------------------------------------------
499    APPEND_DIR:  append the path component to the path being built and check
500    for its existence.  If doesn't exist and we are creating directories, do
501    so for this one; else signal success or error as appropriate.
502  ---------------------------------------------------------------------------*/
503
504/* GRR:  check path length after each segment:  warn about truncation */
505
506    if (FUNCTION == APPEND_DIR) {
507        int too_long = FALSE;
508
509        Trace((stderr, "appending dir segment [%s]\n", FnFilter1(pathcomp)));
510        while ((*G.build_end = *pathcomp++) != '\0')
511            ++G.build_end;
512        /* Truncate components over 30 chars? Nah, the filesystem handles it. */
513        if ((G.build_end-G.buildpath) > FILNAMSIZ-3)       /* room for "/a\0" */
514            too_long = TRUE;                    /* check if extracting dir? */
515        if (SSTAT(G.buildpath, &G.statbuf)) {   /* path doesn't exist */
516            if (!G.create_dirs) { /* told not to create (freshening) */
517                free(G.buildpath);
518                return MPN_INF_SKIP;    /* path doesn't exist: nothing to do */
519            }
520            if (too_long) {
521                Info(slide, 1, ((char *)slide,
522                  "checkdir error:  path too long: %s\n",
523                  FnFilter1(G.buildpath)));
524                free(G.buildpath);
525                /* no room for filenames:  fatal */
526                return MPN_ERR_TOOLONG;
527            }
528            if (MKDIR(G.buildpath, 0777) == -1) {   /* create the directory */
529                Info(slide, 1, ((char *)slide,
530                  "checkdir error:  cannot create %s\n\
531                 unable to process %s.\n",
532                  FnFilter2(G.buildpath), FnFilter1(G.filename)));
533                free(G.buildpath);
534                /* path didn't exist, tried to create, failed */
535                return MPN_ERR_SKIP;
536            }
537            G.created_dir = TRUE;
538        } else if (!S_ISDIR(G.statbuf.st_mode)) {
539            Info(slide, 1, ((char *)slide,
540              "checkdir error:  %s exists but is not directory\n\
541                 unable to process %s.\n",
542              FnFilter2(G.buildpath), FnFilter1(G.filename)));
543            free(G.buildpath);
544            /* path existed but wasn't dir */
545            return MPN_ERR_SKIP;
546        }
547        if (too_long) {
548            Info(slide, 1, ((char *)slide,
549              "checkdir error:  path too long: %s\n", FnFilter1(G.buildpath)));
550            free(G.buildpath);
551            /* no room for filenames:  fatal */
552            return MPN_ERR_TOOLONG;
553        }
554        *G.build_end++ = '/';
555        *G.build_end = '\0';
556        Trace((stderr, "buildpath now = [%s]\n", FnFilter1(G.buildpath)));
557        return MPN_OK;
558
559    } /* end if (FUNCTION == APPEND_DIR) */
560
561/*---------------------------------------------------------------------------
562    GETPATH:  copy full path to the string pointed at by pathcomp, and free
563    G.buildpath.  Not our responsibility to worry whether pathcomp has room.
564  ---------------------------------------------------------------------------*/
565
566    if (FUNCTION == GETPATH) {
567        strcpy(pathcomp, G.buildpath);
568        Trace((stderr, "getting and freeing path [%s]\n",
569          FnFilter1(pathcomp)));
570        free(G.buildpath);
571        G.buildpath = G.build_end = (char *)NULL;
572        return MPN_OK;
573    }
574
575/*---------------------------------------------------------------------------
576    APPEND_NAME:  assume the path component is the filename; append it and
577    return without checking for existence.
578  ---------------------------------------------------------------------------*/
579
580    if (FUNCTION == APPEND_NAME) {
581        Trace((stderr, "appending filename [%s]\n", FnFilter1(pathcomp)));
582        while ((*G.build_end = *pathcomp++) != '\0') {
583            ++G.build_end;
584            if ((G.build_end-G.buildpath) >= FILNAMSIZ) {
585                *--G.build_end = '\0';
586                Info(slide, 0x201, ((char *)slide,
587                  "checkdir warning:  path too long; truncating\n\
588                   %s\n                -> %s\n",
589                  FnFilter1(G.filename), FnFilter2(G.buildpath)));
590                return MPN_INF_TRUNC;   /* filename truncated */
591            }
592        }
593        Trace((stderr, "buildpath now = [%s]\n", FnFilter1(G.buildpath)));
594        /* could check for existence here, prompt for new name... */
595        return MPN_OK;
596    }
597
598/*---------------------------------------------------------------------------
599    INIT:  allocate and initialize buffer space for the file currently being
600    extracted.  If file was renamed with an absolute path, don't prepend the
601    extract-to path.
602  ---------------------------------------------------------------------------*/
603
604    if (FUNCTION == INIT) {
605        Trace((stderr, "initializing buildpath to "));
606        if ((G.buildpath = (char *)malloc(strlen(G.filename)+G.rootlen+1))
607            == (char *)NULL)
608            return MPN_NOMEM;
609        if ((G.rootlen > 0) && !G.renamed_fullpath) {
610            strcpy(G.buildpath, G.rootpath);
611            G.build_end = G.buildpath + G.rootlen;
612        } else {
613            *G.buildpath = '\0';
614            G.build_end = G.buildpath;
615        }
616        Trace((stderr, "[%s]\n", FnFilter1(G.buildpath)));
617        return MPN_OK;
618    }
619
620/*---------------------------------------------------------------------------
621    ROOT:  if appropriate, store the path in G.rootpath and create it if
622    necessary; else assume it's a zipfile member and return.  This path
623    segment gets used in extracting all members from every zipfile specified
624    on the command line.
625  ---------------------------------------------------------------------------*/
626
627#if (!defined(SFX) || defined(SFX_EXDIR))
628    if (FUNCTION == ROOT) {
629        Trace((stderr, "initializing root path to [%s]\n",
630          FnFilter1(pathcomp)));
631        if (pathcomp == (char *)NULL) {
632            G.rootlen = 0;
633            return MPN_OK;
634        }
635        if (G.rootlen > 0)      /* rootpath was already set, nothing to do */
636            return MPN_OK;
637        if ((G.rootlen = strlen(pathcomp)) > 0) {
638            if (stat(pathcomp, &G.statbuf) || !S_ISDIR(G.statbuf.st_mode)) {
639                /* path does not exist */
640                if (!G.create_dirs) {
641                    G.rootlen = 0;
642                    /* skip (or treat as stored file) */
643                    return MPN_INF_SKIP;
644                }
645                /* create the directory (could add loop here scanning pathcomp
646                 * to create more than one level, but why really necessary?) */
647                if (MKDIR(pathcomp, 0777) == -1) {
648                    Info(slide, 1, ((char *)slide,
649                      "checkdir:  cannot create extraction directory: %s\n",
650                      FnFilter1(pathcomp)));
651                    G.rootlen = 0;
652                    /* path didn't exist, tried to create, and failed: */
653                    /* file exists, or 2+ subdir levels required */
654                    return MPN_ERR_SKIP;
655                }
656            }
657            if ((G.rootpath = (char *)malloc(G.rootlen+2)) == NULL) {
658                G.rootlen = 0;
659                return MPN_NOMEM;
660            }
661            strcpy(G.rootpath, pathcomp);
662            if (G.rootpath[G.rootlen-1] != ':' && G.rootpath[G.rootlen-1] != '/')
663                G.rootpath[G.rootlen++] = '/';
664            G.rootpath[G.rootlen] = '\0';
665            Trace((stderr, "rootpath now = [%s]\n", FnFilter1(G.rootpath)));
666        }
667        return MPN_OK;
668    }
669#endif /* !SFX || SFX_EXDIR */
670
671/*---------------------------------------------------------------------------
672    END:  free G.rootpath, immediately prior to program exit.
673  ---------------------------------------------------------------------------*/
674
675    if (FUNCTION == END) {
676        Trace((stderr, "freeing rootpath\n"));
677        if (G.rootlen > 0) {
678            free(G.rootpath);
679            G.rootlen = 0;
680        }
681        return MPN_OK;
682    }
683
684    return MPN_INVALID; /* should never reach */
685
686} /* end function checkdir() */
687
688
689
690
691
692/**************************************/
693/* Function close_outfile() */
694/**************************************/
695/* this part differs slightly with Zip */
696/*-------------------------------------*/
697
698void close_outfile(__G)
699    __GDEF
700{
701    time_t m_time;
702#ifdef USE_EF_UT_TIME
703    iztimes z_utime;
704#endif
705    LONG FileDate();
706
707    if (uO.cflag)               /* can't set time or filenote on stdout */
708        return;
709
710  /* close the file *before* setting its time under AmigaDOS */
711
712    fclose(G.outfile);
713
714    /* skip restoring time stamps on user's request */
715    if (uO.D_flag <= 1) {
716#ifdef USE_EF_UT_TIME
717        if (G.extra_field &&
718#ifdef IZ_CHECK_TZ
719            G.tz_is_valid &&
720#endif
721            (ef_scan_for_izux(G.extra_field, G.lrec.extra_field_length, 0,
722                              G.lrec.last_mod_dos_datetime, &z_utime, NULL)
723             & EB_UT_FL_MTIME))
724        {
725            TTrace((stderr, "close_outfile:  Unix e.f. modif. time = %ld\n",
726                             z_utime.mtime));
727            m_time = z_utime.mtime;
728        } else {
729            /* Convert DOS time to time_t format */
730            m_time = dos_to_unix_time(G.lrec.last_mod_dos_datetime);
731        }
732#else /* !USE_EF_UT_TIME */
733        /* Convert DOS time to time_t format */
734        m_time = dos_to_unix_time(G.lrec.last_mod_dos_datetime);
735#endif /* ?USE_EF_UT_TIME */
736
737#ifdef DEBUG
738        Info(slide, 1, ((char *)slide, "\nclose_outfile(): m_time=%s\n",
739             ctime(&m_time)));
740#endif
741
742        if (!FileDate(G.filename, &m_time))
743            Info(slide, 1, ((char *)slide,
744                 "warning:  cannot set the time for %s\n", G.filename));
745    }
746
747    /* set file perms after closing (not done at creation)--see mapattr() */
748
749    chmod(G.filename, G.pInfo->file_attr);
750
751    /* give it a filenote from the zipfile comment, if appropriate */
752
753    if (uO.N_flag && G.filenotes[G.filenote_slot]) {
754        SetComment(G.filename, G.filenotes[G.filenote_slot]);
755        free(G.filenotes[G.filenote_slot]);
756        G.filenotes[G.filenote_slot] = NULL;
757    }
758
759} /* end function close_outfile() */
760
761
762#ifdef TIMESTAMP
763
764/*************************/
765/* Function stamp_file() */
766/*************************/
767
768int stamp_file(fname, modtime)
769    ZCONST char *fname;
770    time_t modtime;
771{
772    time_t m_time;
773    LONG FileDate();
774
775    m_time = modtime;
776    return (FileDate((char *)fname, &m_time));
777
778} /* end function stamp_file() */
779
780#endif /* TIMESTAMP */
781
782
783#ifndef __SASC
784/********************************************************************/
785/* Load filedate as a separate external file; it's used by Zip, too.*/
786/*                                                                  */
787#  include "amiga/filedate.c"                                    /* */
788/*                                                                  */
789/********************************************************************/
790
791/********************* do linewise with stat.c **********************/
792
793#  include "amiga/stat.c"
794/* this is the exact same stat.c used by Zip */
795#endif /* !__SASC */
796/* SAS/C makes separate object modules of these; there is less  */
797/* trouble that way when redefining standard library functions. */
798
799#include <stdio.h>
800
801void _abort(void)               /* called when ^C is pressed */
802{
803    /* echon(); */
804    close_leftover_open_dirs();
805    fflush(stdout);
806    fputs("\n^C\n", stderr);
807    exit(1);
808}
809
810
811/************************************************************/
812/* function screensize() -- uses sendpkt() from filedate.c: */
813/************************************************************/
814
815#include <devices/conunit.h>
816#include <dos/dosextens.h>
817#include <exec/memory.h>
818#include <clib/exec_protos.h>
819
820extern long sendpkt(struct MsgPort *pid, long action, long *args, long nargs);
821
822int screensize(int *ttrows, int *ttcols)
823{
824    BPTR fh = Output();
825    if (fh && IsInteractive(fh)) {
826        struct ConUnit *conunit = NULL;
827        void *conp = ((struct FileHandle *) (fh << 2))->fh_Type;
828        struct InfoData *ind = AllocMem(sizeof(*ind), MEMF_PUBLIC);
829        long argp = ((unsigned long) ind) >> 2;
830
831        if (ind && conp && sendpkt(conp, ACTION_DISK_INFO, &argp, 1))
832            conunit = (void *) ((struct IOStdReq *) ind->id_InUse)->io_Unit;
833        if (ind)
834            FreeMem(ind, sizeof(*ind));
835        if (conunit) {
836            if (ttrows) *ttrows = conunit->cu_YMax + 1;
837            if (ttcols) *ttcols = conunit->cu_XMax + 1;
838            return 0;     /* success */
839        }
840    }
841    if (ttrows) *ttrows = INT_MAX;
842    if (ttcols) *ttcols = INT_MAX;
843    return 1;             /* failure */
844}
845
846
847#ifdef AMIGA_VOLUME_LABELS
848/* This function is for if we someday implement -$ on the Amiga. */
849#  include <dos/dosextens.h>
850#  include <dos/filehandler.h>
851#  include <clib/macros.h>
852
853BOOL is_floppy(ZCONST char *path)
854{
855    BOOL okay = FALSE;
856    char devname[32], *debna;
857    ushort i;
858    BPTR lok = Lock((char *)path, ACCESS_READ), pok;
859    struct FileSysStartupMsg *fart;
860    struct DeviceNode *debb, devlist = (void *) BADDR((struct DosInfo *)
861                                BADDR(DOSBase->dl_Root->rn_Info)->di_DevInfo);
862    if (!lok)
863        return FALSE;                   /* should not happen */
864    if (pok = ParentDir((char *)path)) {
865        UnLock(lok);
866        UnLock(pok);
867        return FALSE;                   /* it's not a root directory path */
868    }
869    Forbid();
870    for (debb = devlist; debb; debb = BADDR(debb->dn_Next))
871        if (debb->dn_Type == DLT_DEVICE && (debb->dn_Task == lick->fl_Task))
872            if (fart = BADDR(debb->dn_Startup)) {
873                debna = (char *) BADDR(fart->fssm_Device) + 1;
874                if ((i = debna[-1]) > 31) i = 30;
875                strncpy(devname, debna, i);
876                devname[i] = 0;
877                okay = !strcmp(devname, "trackdisk.device")
878                                || !strcmp(devname, "mfm.device")
879                                || !strcmp(devname, "messydisk.device");
880                break;  /* We only support obvious floppy drives, not tricky */
881            }           /* things like removable cartrige hard drives, or    */
882    Permit();           /* any unusual kind of floppy device driver.         */
883    return okay;
884}
885#endif /* AMIGA_VOLUME_LABELS */
886
887
888#ifndef SFX
889
890# if 0
891/* As far as I can tell, all the locales AmigaDOS 2.1 knows about all */
892/* happen to use DF_MDY ordering, so there's no point in using this.  */
893
894/*************************/
895/* Function dateformat() */
896/*************************/
897
898#include <clib/locale_protos.h>
899#ifdef AZTEC_C
900#  include <pragmas/locale_lib.h>
901#endif
902
903int dateformat()
904{
905/*---------------------------------------------------------------------------
906    For those operating systems which support it, this function returns a
907    value which tells how national convention says that numeric dates are
908    displayed.  Return values are DF_YMD, DF_DMY and DF_MDY (the meanings
909    should be fairly obvious).
910  ---------------------------------------------------------------------------*/
911    struct Library *LocaleBase;
912    struct Locale *ll;
913    int result = DF_MDY;        /* the default */
914
915    if ((LocaleBase = OpenLibrary("locale.library", 0))) {
916        if (ll = OpenLocale(NULL)) {
917            uch *f = ll->loc_ShortDateFormat;
918            /* In this string, %y|%Y is year, %b|%B|%h|%m is month, */
919            /* %d|%e is day day, and %D|%x is short for mo/da/yr.   */
920            if (!strstr(f, "%D") && !strstr(f, "%x")) {
921                uch *da, *mo, *yr;
922                if (!(mo = strstr(f, "%b")) && !(mo = strstr(f, "%B"))
923                                    && !(mo = strstr(f, "%h")))
924                    mo = strstr(f, "%m");
925                if (!(da = strstr(f, "%d")))
926                    da = strstr(f, "%e");
927                if (!(yr = strstr(f, "%y")))
928                    yr = strstr(f, "%Y");
929                if (yr && yr < mo)
930                    result = DF_YMD;
931                else if (da && da < mo)
932                    result = DF_DMY;
933            }
934            CloseLocale(ll);
935        }
936        CloseLibrary(LocaleBase);
937    }
938    return result;
939}
940
941# endif /* 0 */
942
943
944/************************/
945/*  Function version()  */
946/************************/
947
948
949/* NOTE:  the following include depends upon the environment
950 *        variable $Workbench to be set correctly.  (Set by
951 *        default, by kickstart during startup)
952 */
953int WBversion = (int)
954#include "ENV:Workbench"
955;
956
957void version(__G)
958   __GDEF
959{
960/* Define buffers. */
961
962   char buf1[16];  /* compiler name */
963   char buf2[16];  /* revstamp */
964   char buf3[16];  /* OS */
965   char buf4[16];  /* Date */
966/*   char buf5[16];  /* Time */
967
968/* format "with" name strings */
969
970#ifdef AMIGA
971# ifdef __SASC
972   strcpy(buf1,"SAS/C ");
973# else
974#  ifdef LATTICE
975    strcpy(buf1,"Lattice C ");
976#  else
977#   ifdef AZTEC_C
978     strcpy(buf1,"Manx Aztec C ");
979#   else
980     strcpy(buf1,"UNKNOWN ");
981#   endif
982#  endif
983# endif
984/* "under" */
985  sprintf(buf3,"AmigaDOS v%d",WBversion);
986#else
987  strcpy(buf1,"Unknown compiler ");
988  strcpy(buf3,"Unknown OS");
989#endif
990
991/* Define revision, date, and time strings.
992 * NOTE:  Do not calculate run time, be sure to use time compiled.
993 * Pass these strings via your makefile if undefined.
994 */
995
996#if defined(__VERSION__) && defined(__REVISION__)
997  sprintf(buf2,"version %d.%d",__VERSION__,__REVISION__);
998#else
999# ifdef __VERSION__
1000  sprintf(buf2,"version %d",__VERSION__);
1001# else
1002  sprintf(buf2,"unknown version");
1003# endif
1004#endif
1005
1006#ifdef __DATE__
1007  sprintf(buf4," on %s",__DATE__);
1008#else
1009  strcpy(buf4," unknown date");
1010#endif
1011
1012/******
1013#ifdef __TIME__
1014  sprintf(buf5," at %s",__TIME__);
1015#else
1016  strcpy(buf5," unknown time");
1017#endif
1018******/
1019
1020/* Print strings using "CompiledWith" mask defined in unzip.c (used by all).
1021 *  ("Compiled with %s%s for %s%s%s%s.")
1022 */
1023
1024   printf(LoadFarString(CompiledWith),
1025     buf1,
1026     buf2,
1027     buf3,
1028     buf4,
1029     "",    /* buf5 not used */
1030     "" );  /* buf6 not used */
1031
1032} /* end function version() */
1033
1034#endif /* !SFX */
1035