~ubuntu-branches/ubuntu/precise/unzip/precise-proposed

« back to all changes in this revision

Viewing changes to atari/atari.c

  • Committer: Bazaar Package Importer
  • Author(s): Santiago Vila
  • Date: 2004-06-06 17:57:46 UTC
  • Revision ID: james.westby@ubuntu.com-20040606175746-nl7p2dgp3aobyc2c
Tags: upstream-5.51
ImportĀ upstreamĀ versionĀ 5.51

Show diffs side-by-side

added added

removed removed

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