Subversion Repositories Kolibri OS

Rev

Details | Last modification | View Log | RSS feed

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