1/*
2  Copyright (c) 1990-2007 Info-ZIP.  All rights reserved.
3
4  See the accompanying file LICENSE, version 2007-Mar-04 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  netware.c
12
13  This file implements these functions for a NetWare Loadable Module (NLM):
14
15  Contains:  InitUnZipConsole()
16             do_wild()
17             mapattr()
18             mapname()
19             checkdir()
20             close_outfile()
21             stamp_file()
22             version()
23             screensize()
24
25  ---------------------------------------------------------------------------*/
26
27
28#define UNZIP_INTERNAL
29#include "unzip.h"
30#include <dirent.h>
31#include <nwdir.h>
32#include <nwnamspc.h>
33#include <nwconio.h>
34#include <nwthread.h>
35#include <nwadv.h>
36
37#ifdef ACORN_FTYPE_NFS
38/* Acorn bits for NFS filetyping */
39typedef struct {
40  uch ID[2];
41  uch size[2];
42  uch ID_2[4];
43  uch loadaddr[4];
44  uch execaddr[4];
45  uch attr[4];
46} RO_extra_block;
47
48#endif /* ACORN_FTYPE_NFS */
49
50static int created_dir;        /* used in mapname(), checkdir() */
51static int renamed_fullpath;   /* ditto */
52
53
54/*********************************/
55/*  Function InitUnZipConsole()  */
56/*********************************/
57
58void InitUnZipConsole()
59{
60    unsigned int myHandle = GetNLMHandle();
61    unsigned int *activeScreen =
62            ImportSymbol(myHandle, "activeScreen");
63    unsigned int *systemConsoleScreen =
64            ImportSymbol(myHandle, "systemConsoleScreen");
65    void (*pUseAccurateCaseForPaths)(int) =
66            ImportSymbol(myHandle, "UseAccurateCaseForPaths");
67
68    if (!activeScreen || !systemConsoleScreen ||
69            *activeScreen == *systemConsoleScreen)
70        CreateScreen("Info-ZIP UnZip Utility", 0);
71    else
72        CreateScreen("System Console", DONT_AUTO_ACTIVATE);
73
74    SetCurrentNameSpace(NW_NS_LONG);
75    if (pUseAccurateCaseForPaths)
76            pUseAccurateCaseForPaths(TRUE);
77
78    UnimportSymbol(myHandle, "activeScreen");
79    UnimportSymbol(myHandle, "systemConsoleScreen");
80    UnimportSymbol(myHandle, "UseAccurateCaseForPaths");
81}
82
83
84/**********************/
85/* Function do_wild() */   /* for porting: dir separator; match(ignore_case) */
86/**********************/
87
88char *do_wild(__G__ wildspec)
89    __GDEF
90    ZCONST char *wildspec;      /* only used first time on a given dir */
91{
92    static DIR *wild_dir = (DIR *)NULL;
93    static ZCONST char *wildname;
94    static char *dirname, matchname[FILNAMSIZ];
95    static int notfirstcall=FALSE, have_dirname, dirnamelen;
96    struct dirent *file;
97
98    /* Even when we're just returning wildspec, we *always* do so in
99     * matchname[]--calling routine is allowed to append four characters
100     * to the returned string, and wildspec may be a pointer to argv[].
101     */
102    if (!notfirstcall) {    /* first call:  must initialize everything */
103        notfirstcall = TRUE;
104
105        if (!iswild(wildspec)) {
106            strncpy(matchname, wildspec, FILNAMSIZ);
107            matchname[FILNAMSIZ-1] = '\0';
108            have_dirname = FALSE;
109            dir = NULL;
110            return matchname;
111        }
112
113        /* break the wildspec into a directory part and a wildcard filename */
114        if ((wildname = strrchr(wildspec, '/')) == (ZCONST char *)NULL) {
115            dirname = ".";
116            dirnamelen = 1;
117            have_dirname = FALSE;
118            wildname = wildspec;
119        } else {
120            ++wildname;     /* point at character after '/' */
121            dirnamelen = wildname - wildspec;
122            if ((dirname = (char *)malloc(dirnamelen+1)) == (char *)NULL) {
123                Info(slide, 0x201, ((char *)slide,
124                  "warning:  cannot allocate wildcard buffers\n"));
125                strncpy(matchname, wildspec, FILNAMSIZ);
126                matchname[FILNAMSIZ-1] = '\0';
127                return matchname;   /* but maybe filespec was not a wildcard */
128            }
129            strncpy(dirname, wildspec, dirnamelen);
130            dirname[dirnamelen] = '\0';   /* terminate for strcpy below */
131            have_dirname = TRUE;
132        }
133
134        if ((wild_dir = opendir(dirname)) != (DIR *)NULL) {
135            while ((file = readdir(wild_dir)) != (struct dirent *)NULL) {
136                Trace((stderr, "do_wild:  readdir returns %s\n",
137                  FnFilter1(file->d_name)));
138                if (file->d_name[0] == '.' && wildname[0] != '.')
139                    continue; /* Unix:  '*' and '?' do not match leading dot */
140                if (match(file->d_name, wildname, 0 WISEP) && /* 0=case sens.*/
141                    /* skip "." and ".." directory entries */
142                    strcmp(file->d_name, ".") && strcmp(file->d_name, "..")) {
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            /* if we get to here directory is exhausted, so close it */
153            closedir(wild_dir);
154            wild_dir = (DIR *)NULL;
155        }
156
157        /* return the raw wildspec in case that works (e.g., directory not
158         * searchable, but filespec was not wild and file is readable) */
159        strncpy(matchname, wildspec, FILNAMSIZ);
160        matchname[FILNAMSIZ-1] = '\0';
161        return matchname;
162    }
163
164    /* last time through, might have failed opendir but returned raw wildspec */
165    if (wild_dir == (DIR *)NULL) {
166        notfirstcall = FALSE; /* nothing left to try--reset for new wildspec */
167        if (have_dirname)
168            free(dirname);
169        return (char *)NULL;
170    }
171
172    /* If we've gotten this far, we've read and matched at least one entry
173     * successfully (in a previous call), so dirname has been copied into
174     * matchname already.
175     */
176    while ((file = readdir(wild_dir)) != (struct dirent *)NULL) {
177        Trace((stderr, "do_wild:  readdir returns %s\n",
178          FnFilter1(file->d_name)));
179        if (file->d_name[0] == '.' && wildname[0] != '.')
180            continue;   /* Unix:  '*' and '?' do not match leading dot */
181        if (match(file->d_name, wildname, 0 WISEP)) {   /* 0 == case sens. */
182            Trace((stderr, "do_wild:  match() succeeds\n"));
183            if (have_dirname) {
184                /* strcpy(matchname, dirname); */
185                strcpy(matchname+dirnamelen, file->d_name);
186            } else
187                strcpy(matchname, file->d_name);
188            return matchname;
189        }
190    }
191
192    closedir(wild_dir);     /* have read at least one entry; nothing left */
193    wild_dir = (DIR *)NULL;
194    notfirstcall = FALSE;  /* reset for new wildspec */
195    if (have_dirname)
196        free(dirname);
197    return (char *)NULL;
198
199} /* end function do_wild() */
200
201
202/**********************/
203/* Function mapattr() */
204/**********************/
205
206int mapattr(__G)
207    __GDEF
208{
209    ulg tmp = G.crec.external_file_attributes;
210
211    G.pInfo->file_attr = 0;
212    /* initialized to 0 for check in "default" branch below... */
213
214    switch (G.pInfo->hostnum) {
215        case AMIGA_:
216            tmp = (unsigned)(tmp>>17 & 7);   /* Amiga RWE bits */
217            G.pInfo->file_attr = (unsigned)(tmp<<6 | tmp<<3 | tmp);
218            break;
219        case UNIX_:
220        case VMS_:
221        case ACORN_:
222        case ATARI_:
223        case ATHEOS_:
224        case BEOS_:
225        case QDOS_:
226        case TANDEM_:
227            G.pInfo->file_attr = (unsigned)(tmp >> 16);
228            if (G.pInfo->file_attr != 0 || !G.extra_field) {
229                return 0;
230            } else {
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 from 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                int r = FALSE;
250
251                while (!r && ef_len >= EB_HEADSIZE) {
252                    ebID = makeword(ef);
253                    ebLen = (unsigned)makeword(ef+EB_LEN);
254                    if (ebLen > (ef_len - EB_HEADSIZE))
255                        /* discoverd some e.f. inconsistency! */
256                        break;
257                    switch (ebID) {
258                      case EF_ASIUNIX:
259                        if (ebLen >= (EB_ASI_MODE+2)) {
260                            G.pInfo->file_attr =
261                              (unsigned)makeword(ef+(EB_HEADSIZE+EB_ASI_MODE));
262                            /* force stop of loop: */
263                            ef_len = (ebLen + EB_HEADSIZE);
264                            break;
265                        }
266                        /* else: fall through! */
267                      case EF_PKVMS:
268                        /* "found nondecypherable e.f. with perm. attr" */
269                        r = TRUE;
270                      default:
271                        break;
272                    }
273                    ef_len -= (ebLen + EB_HEADSIZE);
274                    ef += (ebLen + EB_HEADSIZE);
275                }
276                if (!r)
277                    return 0;
278            }
279            /* fall through! */
280        /* all remaining cases:  expand MSDOS read-only bit into write perms */
281        case FS_FAT_:
282            /* PKWARE's PKZip for Unix marks entries as FS_FAT_, but stores the
283             * Unix attributes in the upper 16 bits of the external attributes
284             * field, just like Info-ZIP's Zip for Unix.  We try to use that
285             * value, after a check for consistency with the MSDOS attribute
286             * bits (see below).
287             */
288            G.pInfo->file_attr = (unsigned)(tmp >> 16);
289            /* fall through! */
290        case FS_HPFS_:
291        case FS_NTFS_:
292        case MAC_:
293        case TOPS20_:
294        default:
295            /* Ensure that DOS subdir bit is set when the entry's name ends
296             * in a '/'.  Some third-party Zip programs fail to set the subdir
297             * bit for directory entries.
298             */
299            if ((tmp & 0x10) == 0) {
300                extent fnlen = strlen(G.filename);
301                if (fnlen > 0 && G.filename[fnlen-1] == '/')
302                    tmp |= 0x10;
303            }
304            /* read-only bit --> write perms; subdir bit --> dir exec bit */
305            tmp = !(tmp & 1) << 1  |  (tmp & 0x10) >> 4;
306            if ((G.pInfo->file_attr & 0700) == (unsigned)(0400 | tmp<<6))
307                /* keep previous G.pInfo->file_attr setting, when its "owner"
308                 * part appears to be consistent with DOS attribute flags!
309                 */
310                return 0;
311            G.pInfo->file_attr = (unsigned)(0444 | tmp<<6 | tmp<<3 | tmp);
312            break;
313    } /* end switch (host-OS-created-by) */
314
315    /* for originating systems with no concept of "group," "other," "system": */
316    umask( (int)(tmp=umask(0)) );    /* apply mask to expanded r/w(/x) perms */
317    G.pInfo->file_attr &= ~tmp;
318
319    return 0;
320
321} /* end function mapattr() */
322
323
324
325
326/**********************/
327/* Function mapname() */
328/**********************/
329
330int mapname(__G__ renamed)
331    __GDEF
332    int renamed;
333/*
334 * returns:
335 *  MPN_OK          - no problem detected
336 *  MPN_INF_TRUNC   - caution (truncated filename)
337 *  MPN_INF_SKIP    - info "skip entry" (dir doesn't exist)
338 *  MPN_ERR_SKIP    - error -> skip entry
339 *  MPN_ERR_TOOLONG - error -> path is too long
340 *  MPN_NOMEM       - error (memory allocation failed) -> skip entry
341 *  [also MPN_VOL_LABEL, MPN_CREATED_DIR]
342 */
343{
344    char pathcomp[FILNAMSIZ];      /* path-component buffer */
345    char *pp, *cp=(char *)NULL;    /* character pointers */
346    char *lastsemi=(char *)NULL;   /* pointer to last semi-colon in pathcomp */
347#ifdef ACORN_FTYPE_NFS
348    char *lastcomma=(char *)NULL;  /* pointer to last comma in pathcomp */
349    RO_extra_block *ef_spark;      /* pointer Acorn FTYPE ef block */
350#endif
351    int killed_ddot = FALSE;       /* is set when skipping "../" pathcomp */
352    int error = MPN_OK;
353    register unsigned workch;      /* hold the character being tested */
354
355
356/*---------------------------------------------------------------------------
357    Initialize various pointers and counters and stuff.
358  ---------------------------------------------------------------------------*/
359
360    if (G.pInfo->vollabel)
361        return MPN_VOL_LABEL;   /* can't set disk volume labels in Netware */
362
363    /* can create path as long as not just freshening, or if user told us */
364    G.create_dirs = (!uO.fflag || renamed);
365
366    created_dir = FALSE;        /* not yet */
367
368    /* user gave full pathname:  don't prepend rootpath */
369    renamed_fullpath = (renamed && (*G.filename == '/'));
370
371    if (checkdir(__G__ (char *)NULL, INIT) == MPN_NOMEM)
372        return MPN_NOMEM;       /* initialize path buffer, unless no memory */
373
374    *pathcomp = '\0';           /* initialize translation buffer */
375    pp = pathcomp;              /* point to translation buffer */
376    if (uO.jflag)               /* junking directories */
377        cp = (char *)strrchr(G.filename, '/');
378    if (cp == (char *)NULL)     /* no '/' or not junking dirs */
379        cp = G.filename;        /* point to internal zipfile-member pathname */
380    else
381        ++cp;                   /* point to start of last component of path */
382
383/*---------------------------------------------------------------------------
384    Begin main loop through characters in filename.
385  ---------------------------------------------------------------------------*/
386
387    while ((workch = (uch)*cp++) != 0) {
388
389        switch (workch) {
390            case '/':             /* can assume -j flag not given */
391                *pp = '\0';
392                if (strcmp(pathcomp, ".") == 0) {
393                    /* don't bother appending "./" to the path */
394                    *pathcomp = '\0';
395                } else if (!uO.ddotflag && strcmp(pathcomp, "..") == 0) {
396                    /* "../" dir traversal detected, skip over it */
397                    *pathcomp = '\0';
398                    killed_ddot = TRUE;     /* set "show message" flag */
399                }
400                /* when path component is not empty, append it now */
401                if (*pathcomp != '\0' &&
402                    ((error = checkdir(__G__ pathcomp, APPEND_DIR))
403                     & MPN_MASK) > MPN_INF_TRUNC)
404                    return error;
405                pp = pathcomp;    /* reset conversion buffer for next piece */
406                lastsemi = (char *)NULL; /* leave direct. semi-colons alone */
407                break;
408
409            case ';':             /* VMS version (or DEC-20 attrib?) */
410                lastsemi = pp;
411                *pp++ = ';';      /* keep for now; remove VMS ";##" */
412                break;            /*  later, if requested */
413
414#ifdef ACORN_FTYPE_NFS
415            case ',':             /* NFS filetype extension */
416                lastcomma = pp;
417                *pp++ = ',';      /* keep for now; may need to remove */
418                break;            /*  later, if requested */
419#endif
420
421            default:
422                /* allow European characters in filenames: */
423                if (isprint(workch) || (128 <= workch && workch <= 254))
424                    *pp++ = (char)workch;
425        } /* end switch */
426
427    } /* end while loop */
428
429    /* Show warning when stripping insecure "parent dir" path components */
430    if (killed_ddot && QCOND2) {
431        Info(slide, 0, ((char *)slide,
432          "warning:  skipped \"../\" path component(s) in %s\n",
433          FnFilter1(G.filename)));
434        if (!(error & ~MPN_MASK))
435            error = (error & MPN_MASK) | PK_WARN;
436    }
437
438/*---------------------------------------------------------------------------
439    Report if directory was created (and no file to create:  filename ended
440    in '/'), check name to be sure it exists, and combine path and name be-
441    fore exiting.
442  ---------------------------------------------------------------------------*/
443
444    if (G.filename[strlen(G.filename) - 1] == '/') {
445        checkdir(__G__ G.filename, GETPATH);
446        if (created_dir) {
447            if (QCOND2) {
448                Info(slide, 0, ((char *)slide, "   creating: %s\n",
449                  FnFilter1(G.filename)));
450            }
451#if !defined(NO_CHMOD) && !defined(NLM)
452            /* In NetWare, chmod does not work on directories */
453            /* set approx. dir perms (make sure can still read/write in dir) */
454            if (chmod(G.filename, (0xffff & G.pInfo->file_attr) | 0700))
455                perror("chmod (directory attributes) error");
456#endif
457            /* set dir time (note trailing '/') */
458            return (error & ~MPN_MASK) | MPN_CREATED_DIR;
459        }
460        /* dir existed already; don't look for data to extract */
461        return (error & ~MPN_MASK) | MPN_INF_SKIP;
462    }
463
464    *pp = '\0';                   /* done with pathcomp:  terminate it */
465
466    /* if not saving them, remove VMS version numbers (appended ";###") */
467    if (!uO.V_flag && lastsemi) {
468        pp = lastsemi + 1;
469        while (isdigit((uch)(*pp)))
470            ++pp;
471        if (*pp == '\0')          /* only digits between ';' and end:  nuke */
472            *lastsemi = '\0';
473    }
474
475#ifdef ACORN_FTYPE_NFS
476    /* translate Acorn filetype information if asked to do so */
477    if (uO.acorn_nfs_ext &&
478        (ef_spark = (RO_extra_block *)
479                    getRISCOSexfield(G.extra_field, G.lrec.extra_field_length))
480        != (RO_extra_block *)NULL)
481    {
482        /* file *must* have a RISC OS extra field */
483        long ft = (long)makelong(ef_spark->loadaddr);
484        /*32-bit*/
485        if (lastcomma) {
486            pp = lastcomma + 1;
487            while (isxdigit((uch)(*pp))) ++pp;
488            if (pp == lastcomma+4 && *pp == '\0') *lastcomma='\0'; /* nuke */
489        }
490        if ((ft & 1<<31)==0) ft=0x000FFD00;
491        sprintf(pathcomp+strlen(pathcomp), ",%03x", (int)(ft>>8) & 0xFFF);
492    }
493#endif /* ACORN_FTYPE_NFS */
494
495    if (*pathcomp == '\0') {
496        Info(slide, 1, ((char *)slide, "mapname:  conversion of %s failed\n",
497          FnFilter1(G.filename)));
498        return (error & ~MPN_MASK) | MPN_ERR_SKIP;
499    }
500
501    checkdir(__G__ pathcomp, APPEND_NAME);  /* returns 1 if truncated: care? */
502    checkdir(__G__ G.filename, GETPATH);
503
504    return error;
505
506} /* end function mapname() */
507
508
509
510
511/***********************/
512/* Function checkdir() */
513/***********************/
514
515int checkdir(__G__ pathcomp, flag)
516    __GDEF
517    char *pathcomp;
518    int flag;
519/*
520 * returns:
521 *  MPN_OK          - no problem detected
522 *  MPN_INF_TRUNC   - (on APPEND_NAME) truncated filename
523 *  MPN_INF_SKIP    - path doesn't exist, not allowed to create
524 *  MPN_ERR_SKIP    - path doesn't exist, tried to create and failed; or path
525 *                    exists and is not a directory, but is supposed to be
526 *  MPN_ERR_TOOLONG - path is too long
527 *  MPN_NOMEM       - can't allocate memory for filename buffers
528 */
529{
530    static int rootlen = 0;   /* length of rootpath */
531    static char *rootpath;    /* user's "extract-to" directory */
532    static char *buildpath;   /* full path (so far) to extracted file */
533    static char *end;         /* pointer to end of buildpath ('\0') */
534
535#   define FN_MASK   7
536#   define FUNCTION  (flag & FN_MASK)
537
538
539/*---------------------------------------------------------------------------
540    APPEND_DIR:  append the path component to the path being built and check
541    for its existence.  If doesn't exist and we are creating directories, do
542    so for this one; else signal success or error as appropriate.
543  ---------------------------------------------------------------------------*/
544
545    if (FUNCTION == APPEND_DIR) {
546        int too_long = FALSE;
547#ifdef SHORT_NAMES
548        char *old_end = end;
549#endif
550
551        Trace((stderr, "appending dir segment [%s]\n", FnFilter1(pathcomp)));
552        while ((*end = *pathcomp++) != '\0')
553            ++end;
554#ifdef SHORT_NAMES   /* path components restricted to 14 chars, typically */
555        if ((end-old_end) > FILENAME_MAX)  /* GRR:  proper constant? */
556            *(end = old_end + FILENAME_MAX) = '\0';
557#endif
558
559        /* GRR:  could do better check, see if overrunning buffer as we go:
560         * check end-buildpath after each append, set warning variable if
561         * within 20 of FILNAMSIZ; then if var set, do careful check when
562         * appending.  Clear variable when begin new path. */
563
564        if ((end-buildpath) > FILNAMSIZ-3)  /* need '/', one-char name, '\0' */
565            too_long = TRUE;                /* check if extracting directory? */
566        if (stat(buildpath, &G.statbuf)) {  /* path doesn't exist */
567            if (!G.create_dirs) { /* told not to create (freshening) */
568                free(buildpath);
569                /* path doesn't exist:  nothing to do */
570                return MPN_INF_SKIP;
571            }
572            if (too_long) {
573                Info(slide, 1, ((char *)slide,
574                  "checkdir error:  path too long: %s\n",
575                  FnFilter1(buildpath)));
576                free(buildpath);
577                /* no room for filenames:  fatal */
578                return MPN_ERR_TOOLONG;
579            }
580            if (mkdir(buildpath) == -1) {   /* create the directory */
581                Info(slide, 1, ((char *)slide,
582                  "checkdir error:  cannot create %s\n\
583                 unable to process %s.\n",
584                  FnFilter2(buildpath), FnFilter1(G.filename)));
585                free(buildpath);
586                /* path didn't exist, tried to create, failed */
587                return MPN_ERR_SKIP;
588            }
589            created_dir = TRUE;
590        } else if (!S_ISDIR(G.statbuf.st_mode)) {
591            Info(slide, 1, ((char *)slide,
592              "checkdir error:  %s exists but is not directory\n\
593                 unable to process %s.\n",
594              FnFilter2(buildpath), FnFilter1(G.filename)));
595            free(buildpath);
596            /* path existed but wasn't dir */
597            return MPN_ERR_SKIP;
598        }
599        if (too_long) {
600            Info(slide, 1, ((char *)slide,
601              "checkdir error:  path too long: %s\n", FnFilter1(buildpath)));
602            free(buildpath);
603            /* no room for filenames:  fatal */
604            return MPN_ERR_TOOLONG;
605        }
606        *end++ = '/';
607        *end = '\0';
608        Trace((stderr, "buildpath now = [%s]\n", FnFilter1(buildpath)));
609        return MPN_OK;
610
611    } /* end if (FUNCTION == APPEND_DIR) */
612
613/*---------------------------------------------------------------------------
614    GETPATH:  copy full path to the string pointed at by pathcomp, and free
615    buildpath.
616  ---------------------------------------------------------------------------*/
617
618    if (FUNCTION == GETPATH) {
619        strcpy(pathcomp, buildpath);
620        Trace((stderr, "getting and freeing path [%s]\n",
621          FnFilter1(pathcomp)));
622        free(buildpath);
623        buildpath = end = (char *)NULL;
624        return MPN_OK;
625    }
626
627/*---------------------------------------------------------------------------
628    APPEND_NAME:  assume the path component is the filename; append it and
629    return without checking for existence.
630  ---------------------------------------------------------------------------*/
631
632    if (FUNCTION == APPEND_NAME) {
633#ifdef SHORT_NAMES
634        char *old_end = end;
635#endif
636
637        Trace((stderr, "appending filename [%s]\n", FnFilter1(pathcomp)));
638        while ((*end = *pathcomp++) != '\0') {
639            ++end;
640#ifdef SHORT_NAMES  /* truncate name at 14 characters, typically */
641            if ((end-old_end) > FILENAME_MAX)      /* GRR:  proper constant? */
642                *(end = old_end + FILENAME_MAX) = '\0';
643#endif
644            if ((end-buildpath) >= FILNAMSIZ) {
645                *--end = '\0';
646                Info(slide, 0x201, ((char *)slide,
647                  "checkdir warning:  path too long; truncating\n\
648                   %s\n                -> %s\n",
649                  FnFilter1(G.filename), FnFilter2(buildpath)));
650                return MPN_INF_TRUNC;   /* filename truncated */
651            }
652        }
653        Trace((stderr, "buildpath now = [%s]\n", FnFilter1(buildpath)));
654        /* could check for existence here, prompt for new name... */
655        return MPN_OK;
656    }
657
658/*---------------------------------------------------------------------------
659    INIT:  allocate and initialize buffer space for the file currently being
660    extracted.  If file was renamed with an absolute path, don't prepend the
661    extract-to path.
662  ---------------------------------------------------------------------------*/
663
664/* GRR:  for VMS and TOPS-20, add up to 13 to strlen */
665
666    if (FUNCTION == INIT) {
667        Trace((stderr, "initializing buildpath to "));
668#ifdef ACORN_FTYPE_NFS
669        if ((buildpath = (char *)malloc(strlen(G.filename)+rootlen+
670                                        (uO.acorn_nfs_ext ? 5 : 1)))
671#else
672        if ((buildpath = (char *)malloc(strlen(G.filename)+rootlen+1))
673#endif
674            == (char *)NULL)
675            return MPN_NOMEM;
676        if ((rootlen > 0) && !renamed_fullpath) {
677            strcpy(buildpath, rootpath);
678            end = buildpath + rootlen;
679        } else {
680            *buildpath = '\0';
681            end = buildpath;
682        }
683        Trace((stderr, "[%s]\n", FnFilter1(buildpath)));
684        return MPN_OK;
685    }
686
687/*---------------------------------------------------------------------------
688    ROOT:  if appropriate, store the path in rootpath and create it if neces-
689    sary; else assume it's a zipfile member and return.  This path segment
690    gets used in extracting all members from every zipfile specified on the
691    command line.
692  ---------------------------------------------------------------------------*/
693
694#if (!defined(SFX) || defined(SFX_EXDIR))
695    if (FUNCTION == ROOT) {
696        Trace((stderr, "initializing root path to [%s]\n",
697          FnFilter1(pathcomp)));
698        if (pathcomp == (char *)NULL) {
699            rootlen = 0;
700            return MPN_OK;
701        }
702        if ((rootlen = strlen(pathcomp)) > 0) {
703            if (pathcomp[rootlen-1] == '/') {
704                pathcomp[--rootlen] = '\0';
705            }
706            if (rootlen > 0 && (stat(pathcomp, &G.statbuf) ||
707                !S_ISDIR(G.statbuf.st_mode)))       /* path does not exist */
708            {
709                if (!G.create_dirs /* || iswild(pathcomp) */ ) {
710                    rootlen = 0;
711                    /* skip (or treat as stored file) */
712                    return MPN_INF_SKIP;
713                }
714                /* create the directory (could add loop here to scan pathcomp
715                 * and create more than one level, but why really necessary?) */
716                if (mkdir(pathcomp) == -1) {
717                    Info(slide, 1, ((char *)slide,
718                      "checkdir:  cannot create extraction directory: %s\n",
719                      FnFilter1(pathcomp)));
720                    rootlen = 0;
721                    /* path didn't exist, tried to create, and failed: */
722                    /* file exists, or 2+ subdirectory levels required */
723                    return MPN_ERR_SKIP;
724                }
725            }
726            if ((rootpath = (char *)malloc(rootlen+2)) == (char *)NULL) {
727                rootlen = 0;
728                return MPN_NOMEM;
729            }
730            strcpy(rootpath, pathcomp);
731            rootpath[rootlen++] = '/';
732            rootpath[rootlen] = '\0';
733            Trace((stderr, "rootpath now = [%s]\n", FnFilter1(rootpath)));
734        }
735        return MPN_OK;
736    }
737#endif /* !SFX || SFX_EXDIR */
738
739/*---------------------------------------------------------------------------
740    END:  free rootpath, immediately prior to program exit.
741  ---------------------------------------------------------------------------*/
742
743    if (FUNCTION == END) {
744        Trace((stderr, "freeing rootpath\n"));
745        if (rootlen > 0) {
746            free(rootpath);
747            rootlen = 0;
748        }
749        return MPN_OK;
750    }
751
752    return MPN_INVALID; /* should never reach */
753
754} /* end function checkdir() */
755
756
757
758/****************************/
759/* Function close_outfile() */
760/****************************/
761
762void close_outfile(__G)    /* GRR: change to return PK-style warning level */
763    __GDEF
764{
765    fclose(G.outfile);
766
767    /* skip restoring time stamps on user's request */
768    if (uO.D_flag <= 1) {
769        WORD date = G.lrec.last_mod_dos_datetime >> 16;
770        WORD time = G.lrec.last_mod_dos_datetime & 0xffff;
771        static struct ModifyStructure changeBuffer;
772
773        /* set the file's access and modification times */
774        changeBuffer.MLastAccessedDate = date;
775        changeBuffer.MLastUpdatedDate = date;
776        changeBuffer.MLastUpdatedTime = time;
777        if (ChangeDirectoryEntry(G.filename, &changeBuffer,
778              MLastAccessedDateBit | MLastUpdatedDateBit | MLastUpdatedTimeBit,
779              0))
780        {
781            if (uO.qflag)
782                Info(slide, 0x201, ((char *)slide,
783                  "warning:  cannot set times for %s\n",
784                  FnFilter1(G.filename)));
785            else
786                Info(slide, 0x201, ((char *)slide,
787                  " (warning) cannot set times"));
788        }
789    }
790
791/*---------------------------------------------------------------------------
792    Change the file permissions from default ones to those stored in the
793    zipfile.
794  ---------------------------------------------------------------------------*/
795
796    if (chmod(G.filename, 0xffff & G.pInfo->file_attr))
797        perror("chmod (file attributes) error");
798
799} /* end function close_outfile() */
800
801
802#ifdef TIMESTAMP
803
804/***************************/
805/*  Function stamp_file()  */
806/***************************/
807
808int stamp_file(fname, modtime)
809    ZCONST char *fname;
810    time_t modtime;
811{
812    ztimbuf tp;
813
814    tp.modtime = tp.actime = modtime;
815    return (utime(fname, &tp));
816
817} /* end function stamp_file() */
818
819#endif /* TIMESTAMP */
820
821
822/************************/
823/*  Function version()  */
824/************************/
825
826void version(__G)
827    __GDEF
828{
829    int len;
830#if defined(__IBMC__) || defined(__WATCOMC__) || defined(_MSC_VER)
831    char buf[80];
832#endif
833
834    len = sprintf((char *)slide, LoadFarString(CompiledWith),
835
836#if defined(__GNUC__)
837#  ifdef __EMX__  /* __EMX__ is defined as "1" only (sigh) */
838      "emx+gcc ", __VERSION__,
839#  else
840      "gcc/2 ", __VERSION__,
841#  endif
842#elif defined(__WATCOMC__)
843      "Watcom C", (sprintf(buf, " (__WATCOMC__ = %d)", __WATCOMC__), buf),
844#elif defined(__TURBOC__)
845#  ifdef __BORLANDC__
846      "Borland C++",
847#    if (__BORLANDC__ < 0x0460)
848        " 1.0",
849#    elif (__BORLANDC__ == 0x0460)
850        " 1.5",                     /* from Kai Uwe:  three less than DOS */
851#    else
852        " 2.0",                     /* (__BORLANDC__ == 0x0500)? */
853#    endif
854#  else
855      "Turbo C",                    /* these are probably irrelevant */
856#    if (__TURBOC__ >= 661)
857       "++ 1.0 or later",
858#    elif (__TURBOC__ == 661)
859       " 3.0?",
860#    elif (__TURBOC__ == 397)
861       " 2.0",
862#    else
863       " 1.0 or 1.5?",
864#    endif
865#  endif
866#elif defined(MSC)
867      "Microsoft C ",
868#  ifdef _MSC_VER
869      (sprintf(buf, "%d.%02d", _MSC_VER/100, _MSC_VER%100), buf),
870#  else
871      "5.1 or earlier",
872#  endif
873#else
874      "unknown compiler", "",
875#endif /* ?compilers */
876
877      "NetWare",
878      " (32-bit)",
879
880#ifdef __DATE__
881      " on ", __DATE__
882#else
883      "", ""
884#endif
885    );
886
887    (*G.message)((zvoid *)&G, slide, (ulg)len, 0);
888                                /* MSC can't handle huge macro expansions */
889
890} /* end function version() */
891
892
893#ifdef MORE
894
895/*************************/
896/* Function screensize() */
897/*************************/
898
899int screensize(int *tt_rows, int *tt_cols)
900{
901    WORD height;
902    WORD width;
903
904    if (GetSizeOfScreen(&height, &width) == 0) {
905        if (tt_rows != NULL) *tt_rows = height;
906        if (tt_cols != NULL) *tt_cols = width;
907        return 0;       /* signal success */
908    } else {
909        if (tt_rows != NULL) *tt_rows = 25;
910        if (tt_cols != NULL) *tt_cols = 80;
911        return 1;       /* signal failure */
912    }
913}
914
915#endif /* MORE */
916