Subversion Repositories Kolibri OS

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
6725 siemargl 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 
31
#include 
32
#include 
33
#include 
34
#include 
35
#include 
36
 
37
#ifdef ACORN_FTYPE_NFS
38
/* Acorn bits for NFS filetyping */
39
typedef 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
 
50
static int created_dir;        /* used in mapname(), checkdir() */
51
static int renamed_fullpath;   /* ditto */
52
 
53
 
54
/*********************************/
55
/*  Function InitUnZipConsole()  */
56
/*********************************/
57
 
58
void 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
 
88
char *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
 
206
int 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
 
330
int 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
 
515
int 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
 
762
void 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
 
808
int 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
 
826
void 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
 
899
int 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 */