Subversion Repositories Kolibri OS

Rev

Rev 6725 | Rev 6745 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
6725 siemargl 1
/*
2
Kolibri OS port for gcc 5.4
3
 
4
Started by Siemargl @Nov 2016
5
Borrowed code parts from other unzip ports
6
 
7
howto make:
8
go in unzip60 directory (below this file) and
9
>make -f kolibri\makefile.gcc
10
 
11
Contains:
12
    version()
13
    mapattr()
14
    checkdir()
15
 
16
extract.o:extract.c:(.text+0x1410): undefined reference to `mapname'
17
extract.o:extract.c:(.text+0x241d): undefined reference to `close_outfile'
18
extract.o:extract.c:(.text+0x2e09): undefined reference to `checkdir'
19
process.o:process.c:(.text+0x13e9): undefined reference to `checkdir'
20
process.o:process.c:(.text+0x15a1): undefined reference to `do_wild'
21
kolibri\makefile.gcc:276: recipe for target 'unzip.exe' failed
22
*/
23
 
24
#define FATTR   FS_HIDDEN+FS_SYSTEM+FS_SUBDIR
25
 
26
 
27
 
28
#define UNZIP_INTERNAL
29
#include "unzip.h"
30
 
31
// Siemargl fork of Kolibri system API
32
#include "kos32sys1.h"
33
 
34
/********************************************************************************************************************/
35
/***  Function version()  */
36
/********************************************************************************************************************/
37
 
38
void version(__G)
39
    __GDEF
40
{
41
    sprintf((char *)slide, LoadFarString(CompiledWith),
42
#if defined(__TINYC__)
43
      "TinyC", "",
44
#elif defined(__GNUC__)
45
      "GNU C ", __VERSION__,
46
#else
47
      "(unknown compiler) ","",
48
#endif
49
      "KolibriOS ",
50
 
51
#ifdef __POWERPC__
52
      "(PowerPC)",
53
#else
54
# ifdef __INTEL__
55
      "(x86)",
56
# else
57
      "(unknown)",   /* someday we may have other architectures... */
58
# endif
59
#endif
60
 
61
#ifdef __DATE__
62
      " on ", __DATE__
63
#else
64
      "", ""
65
#endif
66
    );
67
 
68
    (*G.message)((zvoid *)&G, slide, (ulg)strlen((char *)slide), 0);
69
 
70
} /* end function version() */
71
 
72
 
73
/********************************************************************************************************************/
74
/*** Function mapattr() */
75
/********************************************************************************************************************/
76
 
77
/* Identical to MS-DOS, OS/2 versions.  However, NT has a lot of extra
78
 * permission stuff, so this function should probably be extended in the
79
 * future. */
80
 
81
int mapattr(__G)
82
    __GDEF
83
{
84
    /* set archive bit for file entries (file is not backed up): */
85
    G.pInfo->file_attr = ((unsigned)G.crec.external_file_attributes |
86
      (G.crec.external_file_attributes & FS_SUBDIR ?
87
 
88
    return 0;
89
 
90
} /* end function mapattr() */
91
 
92
 
93
/********************************************************************************************************************/
6727 siemargl 94
/***  Function mapname()  */
6725 siemargl 95
/********************************************************************************************************************/
96
 
97
int mapname(__G__ renamed)
98
    __GDEF
99
    int renamed;
100
/*
101
 * returns:
102
 *  MPN_OK          - no problem detected
103
 *  MPN_INF_TRUNC   - caution (truncated filename)
104
 *  MPN_INF_SKIP    - info "skip entry" (dir doesn't exist)
105
 *  MPN_ERR_SKIP    - error -> skip entry
106
 *  MPN_ERR_TOOLONG - error -> path is too long
107
 *  MPN_NOMEM       - error (memory allocation failed) -> skip entry
108
 *  [also MPN_VOL_LABEL, MPN_CREATED_DIR]
109
 */
110
{
111
    char pathcomp[FILNAMSIZ];      /* path-component buffer */
112
    char *pp, *cp=(char *)NULL;    /* character pointers */
113
    char *lastsemi=(char *)NULL;   /* pointer to last semi-colon in pathcomp */
114
#ifdef ACORN_FTYPE_NFS
115
    char *lastcomma=(char *)NULL;  /* pointer to last comma in pathcomp */
116
    RO_extra_block *ef_spark;      /* pointer Acorn FTYPE ef block */
117
#endif
118
    int killed_ddot = FALSE;       /* is set when skipping "../" pathcomp */
119
    int error = MPN_OK;
120
    register unsigned workch;      /* hold the character being tested */
121
 
122
 
123
/*---------------------------------------------------------------------------
124
    Initialize various pointers and counters and stuff.
125
  ---------------------------------------------------------------------------*/
126
 
127
    if (G.pInfo->vollabel)
128
        return MPN_VOL_LABEL;   /* can't set disk volume labels in Unix */
129
 
130
    /* can create path as long as not just freshening, or if user told us */
131
    G.create_dirs = (!uO.fflag || renamed);
132
 
133
    G.created_dir = FALSE;      /* not yet */
134
 
135
    /* user gave full pathname:  don't prepend rootpath */
136
    G.renamed_fullpath = (renamed && (*G.filename == '/'));
137
 
138
    if (checkdir(__G__ (char *)NULL, INIT) == MPN_NOMEM)
139
        return MPN_NOMEM;       /* initialize path buffer, unless no memory */
140
 
141
    *pathcomp = '\0';           /* initialize translation buffer */
142
    pp = pathcomp;              /* point to translation buffer */
143
    if (uO.jflag)               /* junking directories */
144
        cp = (char *)strrchr(G.filename, '/');
145
    if (cp == (char *)NULL)     /* no '/' or not junking dirs */
146
        cp = G.filename;        /* point to internal zipfile-member pathname */
147
    else
148
        ++cp;                   /* point to start of last component of path */
149
 
150
/*---------------------------------------------------------------------------
151
    Begin main loop through characters in filename.
152
  ---------------------------------------------------------------------------*/
153
 
154
    while ((workch = (uch)*cp++) != 0) {
155
 
156
        switch (workch) {
157
            case '/':             /* can assume -j flag not given */
158
                *pp = '\0';
159
                if (strcmp(pathcomp, ".") == 0) {
160
                    /* don't bother appending "./" to the path */
161
                    *pathcomp = '\0';
162
                } else if (!uO.ddotflag && strcmp(pathcomp, "..") == 0) {
163
                    /* "../" dir traversal detected, skip over it */
164
                    *pathcomp = '\0';
165
                    killed_ddot = TRUE;     /* set "show message" flag */
166
                }
167
                /* when path component is not empty, append it now */
168
                if (*pathcomp != '\0' &&
169
                    ((error = checkdir(__G__ pathcomp, APPEND_DIR))
170
                     & MPN_MASK) > MPN_INF_TRUNC)
171
                    return error;
172
                pp = pathcomp;    /* reset conversion buffer for next piece */
173
                lastsemi = (char *)NULL; /* leave direct. semi-colons alone */
174
                break;
175
 
176
#ifdef __CYGWIN__   /* Cygwin runs on Win32, apply FAT/NTFS filename rules */
177
            case ':':         /* drive spec not stored, so no colon allowed */
178
            case '\\':        /* '\\' may come as normal filename char (not */
179
            case '<':         /*  dir sep char!) from unix-like file system */
180
            case '>':         /* no redirection symbols allowed either */
181
            case '|':         /* no pipe signs allowed */
182
            case '"':         /* no double quotes allowed */
183
            case '?':         /* no wildcards allowed */
184
            case '*':
185
                *pp++ = '_';  /* these rules apply equally to FAT and NTFS */
186
                break;
187
#endif
188
 
189
            case ';':             /* VMS version (or DEC-20 attrib?) */
190
                lastsemi = pp;
191
                *pp++ = ';';      /* keep for now; remove VMS ";##" */
192
                break;            /*  later, if requested */
193
 
194
#ifdef ACORN_FTYPE_NFS
195
            case ',':             /* NFS filetype extension */
196
                lastcomma = pp;
197
                *pp++ = ',';      /* keep for now; may need to remove */
198
                break;            /*  later, if requested */
199
#endif
200
 
201
#ifdef MTS
202
            case ' ':             /* change spaces to underscore under */
203
                *pp++ = '_';      /*  MTS; leave as spaces under Unix */
204
                break;
205
#endif
206
 
207
            default:
208
                /* disable control character filter when requested,
209
                 * else allow 8-bit characters (e.g. UTF-8) in filenames:
210
                 */
6727 siemargl 211
                if ((isprint(workch) || (128 <= workch && workch <= 254)))
212
                    *pp++ = (char)workch;
6725 siemargl 213
/*kos
214
                if (uO.cflxflag ||
215
                    (isprint(workch) || (128 <= workch && workch <= 254)))
216
                    *pp++ = (char)workch;
217
*/
218
        } /* end switch */
219
 
220
    } /* end while loop */
221
 
222
    /* Show warning when stripping insecure "parent dir" path components */
223
    if (killed_ddot && QCOND2) {
224
        Info(slide, 0, ((char *)slide,
225
          "warning:  skipped \"../\" path component(s) in %s\n",
226
          FnFilter1(G.filename)));
227
        if (!(error & ~MPN_MASK))
228
            error = (error & MPN_MASK) | PK_WARN;
229
    }
230
 
231
/*---------------------------------------------------------------------------
232
    Report if directory was created (and no file to create:  filename ended
233
    in '/'), check name to be sure it exists, and combine path and name be-
234
    fore exiting.
235
  ---------------------------------------------------------------------------*/
236
 
237
    if (G.filename[strlen(G.filename) - 1] == '/') {
238
        checkdir(__G__ G.filename, GETPATH);
239
        if (G.created_dir) {
240
            if (QCOND2) {
241
                Info(slide, 0, ((char *)slide, "   creating: %s\n",
242
                  FnFilter1(G.filename)));
243
            }
244
#ifndef NO_CHMOD
245
            /* Filter out security-relevant attributes bits. */
246
            G.pInfo->file_attr = filtattr(__G__ G.pInfo->file_attr);
247
            /* When extracting non-UNIX directories or when extracting
248
             * without UID/GID restoration or SGID preservation, any
249
             * SGID flag inherited from the parent directory should be
250
             * maintained to allow files extracted into this new folder
251
             * to inherit the GID setting from the parent directory.
252
             */
253
            if (G.pInfo->hostnum != UNIX_ || !(uO.X_flag || uO.K_flag)) {
254
                /* preserve SGID bit when inherited from parent dir */
255
                if (!SSTAT(G.filename, &G.statbuf)) {
256
                    G.pInfo->file_attr |= G.statbuf.st_mode & S_ISGID;
257
                } else {
258
                    perror("Could not read directory attributes");
259
                }
260
            }
261
 
262
            /* set approx. dir perms (make sure can still read/write in dir) */
263
            if (chmod(G.filename, G.pInfo->file_attr | 0700))
264
                perror("chmod (directory attributes) error");
265
#endif
266
            /* set dir time (note trailing '/') */
267
            return (error & ~MPN_MASK) | MPN_CREATED_DIR;
268
        }
269
        /* dir existed already; don't look for data to extract */
270
        return (error & ~MPN_MASK) | MPN_INF_SKIP;
271
    }
272
 
273
    *pp = '\0';                   /* done with pathcomp:  terminate it */
274
 
275
    /* if not saving them, remove VMS version numbers (appended ";###") */
276
    if (!uO.V_flag && lastsemi) {
277
        pp = lastsemi + 1;
278
        while (isdigit((uch)(*pp)))
279
            ++pp;
280
        if (*pp == '\0')          /* only digits between ';' and end:  nuke */
281
            *lastsemi = '\0';
282
    }
283
 
284
    /* On UNIX (and compatible systems), "." and ".." are reserved for
285
     * directory navigation and cannot be used as regular file names.
286
     * These reserved one-dot and two-dot names are mapped to "_" and "__".
287
     */
288
    if (strcmp(pathcomp, ".") == 0)
289
        *pathcomp = '_';
290
    else if (strcmp(pathcomp, "..") == 0)
291
        strcpy(pathcomp, "__");
292
 
293
#ifdef ACORN_FTYPE_NFS
294
    /* translate Acorn filetype information if asked to do so */
295
    if (uO.acorn_nfs_ext &&
296
        (ef_spark = (RO_extra_block *)
297
                    getRISCOSexfield(G.extra_field, G.lrec.extra_field_length))
298
        != (RO_extra_block *)NULL)
299
    {
300
        /* file *must* have a RISC OS extra field */
301
        long ft = (long)makelong(ef_spark->loadaddr);
302
        /*32-bit*/
303
        if (lastcomma) {
304
            pp = lastcomma + 1;
305
            while (isxdigit((uch)(*pp))) ++pp;
306
            if (pp == lastcomma+4 && *pp == '\0') *lastcomma='\0'; /* nuke */
307
        }
308
        if ((ft & 1<<31)==0) ft=0x000FFD00;
309
        sprintf(pathcomp+strlen(pathcomp), ",%03x", (int)(ft>>8) & 0xFFF);
310
    }
311
#endif /* ACORN_FTYPE_NFS */
312
 
313
    if (*pathcomp == '\0') {
314
        Info(slide, 1, ((char *)slide, "mapname:  conversion of %s failed\n",
315
          FnFilter1(G.filename)));
316
        return (error & ~MPN_MASK) | MPN_ERR_SKIP;
317
    }
318
 
319
    checkdir(__G__ pathcomp, APPEND_NAME);  /* returns 1 if truncated: care? */
320
    checkdir(__G__ G.filename, GETPATH);
321
 
322
    return error;
323
 
324
} /* end function mapname() */
325
 
326
 
327
 
328
 
329
#if 0  /*========== NOTES ==========*/
330
 
331
  extract-to dir:      a:path/
332
  buildpath:           path1/path2/ ...   (NULL-terminated)
333
  pathcomp:                filename
334
 
335
  mapname():
336
    loop over chars in zipfile member name
337
      checkdir(path component, COMPONENT | CREATEDIR) --> map as required?
338
        (d:/tmp/unzip/)                    (disk:[tmp.unzip.)
339
        (d:/tmp/unzip/jj/)                 (disk:[tmp.unzip.jj.)
340
        (d:/tmp/unzip/jj/temp/)            (disk:[tmp.unzip.jj.temp.)
341
    finally add filename itself and check for existence? (could use with rename)
342
        (d:/tmp/unzip/jj/temp/msg.outdir)  (disk:[tmp.unzip.jj.temp]msg.outdir)
343
    checkdir(name, GETPATH)     -->  copy path to name and free space
344
 
345
#endif /* 0 */
346
 
347
 
348
 
349
 
6727 siemargl 350
/********************************************************************************************************************/
351
/*** Function checkdir() */
352
/********************************************************************************************************************/
6725 siemargl 353
 
6727 siemargl 354
 
6725 siemargl 355
int checkdir(__G__ pathcomp, flag)
356
    __GDEF
357
    char *pathcomp;
358
    int flag;
359
/*
360
 * returns:
361
 *  MPN_OK          - no problem detected
362
 *  MPN_INF_TRUNC   - (on APPEND_NAME) truncated filename
363
 *  MPN_INF_SKIP    - path doesn't exist, not allowed to create
364
 *  MPN_ERR_SKIP    - path doesn't exist, tried to create and failed; or path
365
 *                    exists and is not a directory, but is supposed to be
366
 *  MPN_ERR_TOOLONG - path is too long
367
 *  MPN_NOMEM       - can't allocate memory for filename buffers
368
 */
369
{
370
 /* static int rootlen = 0; */  /* length of rootpath */
371
 /* static char *rootpath;  */  /* user's "extract-to" directory */
372
 /* static char *buildpath; */  /* full path (so far) to extracted file */
373
 /* static char *end;       */  /* pointer to end of buildpath ('\0') */
374
 
375
#   define FN_MASK   7
376
#   define FUNCTION  (flag & FN_MASK)
377
 
378
 
379
 
380
/*---------------------------------------------------------------------------
381
    APPEND_DIR:  append the path component to the path being built and check
382
    for its existence.  If doesn't exist and we are creating directories, do
383
    so for this one; else signal success or error as appropriate.
384
  ---------------------------------------------------------------------------*/
385
 
386
    if (FUNCTION == APPEND_DIR) {
387
        int too_long = FALSE;
388
#ifdef SHORT_NAMES
389
        char *old_end = end;
390
#endif
391
 
392
        Trace((stderr, "appending dir segment [%s]\n", FnFilter1(pathcomp)));
393
        while ((*G.end = *pathcomp++) != '\0')
394
            ++G.end;
395
#ifdef SHORT_NAMES   /* path components restricted to 14 chars, typically */
396
        if ((G.end-old_end) > FILENAME_MAX)  /* GRR:  proper constant? */
397
            *(G.end = old_end + FILENAME_MAX) = '\0';
398
#endif
399
 
400
        /* GRR:  could do better check, see if overrunning buffer as we go:
401
         * check end-buildpath after each append, set warning variable if
402
         * within 20 of FILNAMSIZ; then if var set, do careful check when
403
         * appending.  Clear variable when begin new path. */
404
 
405
        /* next check: need to append '/', at least one-char name, '\0' */
406
        if ((G.end-G.buildpath) > FILNAMSIZ-3)
407
            too_long = TRUE;                    /* check if extracting dir? */
408
        if (SSTAT(G.buildpath, &G.statbuf)) {   /* path doesn't exist */
409
            if (!G.create_dirs) { /* told not to create (freshening) */
410
                free(G.buildpath);
411
                return MPN_INF_SKIP;    /* path doesn't exist: nothing to do */
412
            }
413
            if (too_long) {
414
                Info(slide, 1, ((char *)slide,
415
                  "checkdir error:  path too long: %s\n",
416
                  FnFilter1(G.buildpath)));
417
                free(G.buildpath);
418
                /* no room for filenames:  fatal */
419
                return MPN_ERR_TOOLONG;
420
            }
421
            if (mkdir(G.buildpath, 0777) == -1) {   /* create the directory */
422
                Info(slide, 1, ((char *)slide,
423
                  "checkdir error:  cannot create %s\n\
424
                 %s\n\
425
                 unable to process %s.\n",
426
                  FnFilter2(G.buildpath),
427
                  strerror(errno),
428
                  FnFilter1(G.filename)));
429
                free(G.buildpath);
430
                /* path didn't exist, tried to create, failed */
431
                return MPN_ERR_SKIP;
432
            }
433
            G.created_dir = TRUE;
434
        } else if (!S_ISDIR(G.statbuf.st_mode)) {
435
            Info(slide, 1, ((char *)slide,
436
              "checkdir error:  %s exists but is not directory\n\
437
                 unable to process %s.\n",
438
              FnFilter2(G.buildpath), FnFilter1(G.filename)));
439
            free(G.buildpath);
440
            /* path existed but wasn't dir */
441
            return MPN_ERR_SKIP;
442
        }
443
        if (too_long) {
444
            Info(slide, 1, ((char *)slide,
445
              "checkdir error:  path too long: %s\n", FnFilter1(G.buildpath)));
446
            free(G.buildpath);
447
            /* no room for filenames:  fatal */
448
            return MPN_ERR_TOOLONG;
449
        }
450
        *G.end++ = '/';
451
        *G.end = '\0';
452
        Trace((stderr, "buildpath now = [%s]\n", FnFilter1(G.buildpath)));
453
        return MPN_OK;
454
 
455
    } /* end if (FUNCTION == APPEND_DIR) */
456
 
457
/*---------------------------------------------------------------------------
458
    GETPATH:  copy full path to the string pointed at by pathcomp, and free
459
    G.buildpath.
460
  ---------------------------------------------------------------------------*/
461
 
462
    if (FUNCTION == GETPATH) {
463
        strcpy(pathcomp, G.buildpath);
464
        Trace((stderr, "getting and freeing path [%s]\n",
465
          FnFilter1(pathcomp)));
466
        free(G.buildpath);
467
        G.buildpath = G.end = (char *)NULL;
468
        return MPN_OK;
469
    }
470
 
471
/*---------------------------------------------------------------------------
472
    APPEND_NAME:  assume the path component is the filename; append it and
473
    return without checking for existence.
474
  ---------------------------------------------------------------------------*/
475
 
476
    if (FUNCTION == APPEND_NAME) {
477
#ifdef SHORT_NAMES
478
        char *old_end = end;
479
#endif
480
 
481
        Trace((stderr, "appending filename [%s]\n", FnFilter1(pathcomp)));
482
        while ((*G.end = *pathcomp++) != '\0') {
483
            ++G.end;
484
#ifdef SHORT_NAMES  /* truncate name at 14 characters, typically */
485
            if ((G.end-old_end) > FILENAME_MAX)    /* GRR:  proper constant? */
486
                *(G.end = old_end + FILENAME_MAX) = '\0';
487
#endif
488
            if ((G.end-G.buildpath) >= FILNAMSIZ) {
489
                *--G.end = '\0';
490
                Info(slide, 0x201, ((char *)slide,
491
                  "checkdir warning:  path too long; truncating\n\
492
                   %s\n                -> %s\n",
493
                  FnFilter1(G.filename), FnFilter2(G.buildpath)));
494
                return MPN_INF_TRUNC;   /* filename truncated */
495
            }
496
        }
497
        Trace((stderr, "buildpath now = [%s]\n", FnFilter1(G.buildpath)));
498
        /* could check for existence here, prompt for new name... */
499
        return MPN_OK;
500
    }
501
 
502
/*---------------------------------------------------------------------------
503
    INIT:  allocate and initialize buffer space for the file currently being
504
    extracted.  If file was renamed with an absolute path, don't prepend the
505
    extract-to path.
506
  ---------------------------------------------------------------------------*/
507
 
508
/* GRR:  for VMS and TOPS-20, add up to 13 to strlen */
509
 
510
    if (FUNCTION == INIT) {
511
        Trace((stderr, "initializing buildpath to "));
512
#ifdef ACORN_FTYPE_NFS
513
        if ((G.buildpath = (char *)malloc(strlen(G.filename)+G.rootlen+
514
                                          (uO.acorn_nfs_ext ? 5 : 1)))
515
#else
516
        if ((G.buildpath = (char *)malloc(strlen(G.filename)+G.rootlen+1))
517
#endif
518
            == (char *)NULL)
519
            return MPN_NOMEM;
520
        if ((G.rootlen > 0) && !G.renamed_fullpath) {
521
            strcpy(G.buildpath, G.rootpath);
522
            G.end = G.buildpath + G.rootlen;
523
        } else {
524
            *G.buildpath = '\0';
525
            G.end = G.buildpath;
526
        }
527
        Trace((stderr, "[%s]\n", FnFilter1(G.buildpath)));
528
        return MPN_OK;
529
    }
530
 
531
/*---------------------------------------------------------------------------
532
    ROOT:  if appropriate, store the path in rootpath and create it if
533
    necessary; else assume it's a zipfile member and return.  This path
534
    segment gets used in extracting all members from every zipfile specified
535
    on the command line.
536
  ---------------------------------------------------------------------------*/
537
 
538
#if (!defined(SFX) || defined(SFX_EXDIR))
539
    if (FUNCTION == ROOT) {
540
        Trace((stderr, "initializing root path to [%s]\n",
541
          FnFilter1(pathcomp)));
542
        if (pathcomp == (char *)NULL) {
543
            G.rootlen = 0;
544
            return MPN_OK;
545
        }
546
        if (G.rootlen > 0)      /* rootpath was already set, nothing to do */
547
            return MPN_OK;
548
        if ((G.rootlen = strlen(pathcomp)) > 0) {
549
            char *tmproot;
550
 
551
            if ((tmproot = (char *)malloc(G.rootlen+2)) == (char *)NULL) {
552
                G.rootlen = 0;
553
                return MPN_NOMEM;
554
            }
555
            strcpy(tmproot, pathcomp);
556
            if (tmproot[G.rootlen-1] == '/') {
557
                tmproot[--G.rootlen] = '\0';
558
            }
559
            if (G.rootlen > 0 && (SSTAT(tmproot, &G.statbuf) ||
560
                                  !S_ISDIR(G.statbuf.st_mode)))
561
            {   /* path does not exist */
562
                if (!G.create_dirs /* || iswild(tmproot) */ ) {
563
                    free(tmproot);
564
                    G.rootlen = 0;
565
                    /* skip (or treat as stored file) */
566
                    return MPN_INF_SKIP;
567
                }
568
                /* create the directory (could add loop here scanning tmproot
569
                 * to create more than one level, but why really necessary?) */
570
                if (mkdir(tmproot, 0777) == -1) {
571
                    Info(slide, 1, ((char *)slide,
572
                      "checkdir:  cannot create extraction directory: %s\n\
573
           %s\n",
574
                      FnFilter1(tmproot), strerror(errno)));
575
                    free(tmproot);
576
                    G.rootlen = 0;
577
                    /* path didn't exist, tried to create, and failed: */
578
                    /* file exists, or 2+ subdir levels required */
579
                    return MPN_ERR_SKIP;
580
                }
581
            }
582
            tmproot[G.rootlen++] = '/';
583
            tmproot[G.rootlen] = '\0';
584
            if ((G.rootpath = (char *)realloc(tmproot, G.rootlen+1)) == NULL) {
585
                free(tmproot);
586
                G.rootlen = 0;
587
                return MPN_NOMEM;
588
            }
589
            Trace((stderr, "rootpath now = [%s]\n", FnFilter1(G.rootpath)));
590
        }
591
        return MPN_OK;
592
    }
593
#endif /* !SFX || SFX_EXDIR */
594
 
595
/*---------------------------------------------------------------------------
596
    END:  free rootpath, immediately prior to program exit.
597
  ---------------------------------------------------------------------------*/
598
 
599
    if (FUNCTION == END) {
600
        Trace((stderr, "freeing rootpath\n"));
601
        if (G.rootlen > 0) {
602
            free(G.rootpath);
603
            G.rootlen = 0;
604
        }
605
        return MPN_OK;
606
    }
607
 
608
    return MPN_INVALID; /* should never reach */
609
 
610
} /* end function checkdir() */
611
 
612
static int get_extattribs OF((__GPRO__ iztimes *pzt, ulg z_uidgid[2]));
613
 
614
static int get_extattribs(__G__ pzt, z_uidgid)
615
    __GDEF
616
    iztimes *pzt;
617
    ulg z_uidgid[2];
618
{
619
/*---------------------------------------------------------------------------
620
    Convert from MSDOS-format local time and date to Unix-format 32-bit GMT
621
    time:  adjust base year from 1980 to 1970, do usual conversions from
622
    yy/mm/dd hh:mm:ss to elapsed seconds, and account for timezone and day-
623
    light savings time differences.  If we have a Unix extra field, however,
624
    we're laughing:  both mtime and atime are ours.  On the other hand, we
625
    then have to check for restoration of UID/GID.
626
  ---------------------------------------------------------------------------*/
627
    int have_uidgid_flg;
628
    unsigned eb_izux_flg;
629
 
630
    eb_izux_flg = 0; // kos32
631
/*
632
    (G.extra_field ? ef_scan_for_izux(G.extra_field,
633
                   G.lrec.extra_field_length, 0, G.lrec.last_mod_dos_datetime,
634
#ifdef IZ_CHECK_TZ
635
                   (G.tz_is_valid ? pzt : NULL),
636
#else
637
                   pzt,
638
#endif
639
                   z_uidgid) : 0);
640
*/
641
    if (eb_izux_flg & EB_UT_FL_MTIME) {
642
        TTrace((stderr, "\nget_extattribs:  Unix e.f. modif. time = %ld\n",
643
          pzt->mtime));
644
    } else {
645
        pzt->mtime = dos_to_unix_time(G.lrec.last_mod_dos_datetime);
646
    }
647
    if (eb_izux_flg & EB_UT_FL_ATIME) {
648
        TTrace((stderr, "get_extattribs:  Unix e.f. access time = %ld\n",
649
          pzt->atime));
650
    } else {
651
        pzt->atime = pzt->mtime;
652
        TTrace((stderr, "\nget_extattribs:  modification/access times = %ld\n",
653
          pzt->mtime));
654
    }
655
 
656
    /* if -X option was specified and we have UID/GID info, restore it */
657
    have_uidgid_flg =
658
#ifdef RESTORE_UIDGID
659
            (uO.X_flag && (eb_izux_flg & EB_UX2_VALID));
660
#else
661
            0;
662
#endif
663
    return have_uidgid_flg;
664
}
665
 
666
/****************************/
667
/* Function close_outfile() */
668
/****************************/
669
 
670
void close_outfile(__G)    /* GRR: change to return PK-style warning level */
671
    __GDEF
672
{
673
    union {
674
        iztimes t3;             /* mtime, atime, ctime */
675
        ztimbuf t2;             /* modtime, actime */
676
    } zt;
677
    ulg z_uidgid[2];
678
    int have_uidgid_flg;
679
 
680
    have_uidgid_flg = get_extattribs(__G__ &(zt.t3), z_uidgid);
681
 
682
/*---------------------------------------------------------------------------
683
    If symbolic links are supported, allocate storage for a symlink control
684
    structure, put the uncompressed "data" and other required info in it, and
685
    add the structure to the "deferred symlinks" chain.  Since we know it's a
686
    symbolic link to start with, we shouldn't have to worry about overflowing
687
    unsigned ints with unsigned longs.
688
  ---------------------------------------------------------------------------*/
689
 
690
#ifdef SYMLINKS
691
    if (G.symlnk) {
692
        extent ucsize = (extent)G.lrec.ucsize;
693
# ifdef SET_SYMLINK_ATTRIBS
694
        extent attribsize = sizeof(unsigned) +
695
                            (have_uidgid_flg ? sizeof(z_uidgid) : 0);
696
# else
697
        extent attribsize = 0;
698
# endif
699
        /* size of the symlink entry is the sum of
700
         *  (struct size (includes 1st '\0') + 1 additional trailing '\0'),
701
         *  system specific attribute data size (might be 0),
702
         *  and the lengths of name and link target.
703
         */
704
        extent slnk_entrysize = (sizeof(slinkentry) + 1) + attribsize +
705
                                ucsize + strlen(G.filename);
706
        slinkentry *slnk_entry;
707
 
708
        if (slnk_entrysize < ucsize) {
709
            Info(slide, 0x201, ((char *)slide,
710
              "warning:  symbolic link (%s) failed: mem alloc overflow\n",
711
              FnFilter1(G.filename)));
712
            fclose(G.outfile);
713
            return;
714
        }
715
 
716
        if ((slnk_entry = (slinkentry *)malloc(slnk_entrysize)) == NULL) {
717
            Info(slide, 0x201, ((char *)slide,
718
              "warning:  symbolic link (%s) failed: no mem\n",
719
              FnFilter1(G.filename)));
720
            fclose(G.outfile);
721
            return;
722
        }
723
        slnk_entry->next = NULL;
724
        slnk_entry->targetlen = ucsize;
725
        slnk_entry->attriblen = attribsize;
726
# ifdef SET_SYMLINK_ATTRIBS
727
        memcpy(slnk_entry->buf, &(G.pInfo->file_attr),
728
               sizeof(unsigned));
729
        if (have_uidgid_flg)
730
            memcpy(slnk_entry->buf + 4, z_uidgid, sizeof(z_uidgid));
731
# endif
732
        slnk_entry->target = slnk_entry->buf + slnk_entry->attriblen;
733
        slnk_entry->fname = slnk_entry->target + ucsize + 1;
734
        strcpy(slnk_entry->fname, G.filename);
735
 
736
        /* move back to the start of the file to re-read the "link data" */
737
        rewind(G.outfile);
738
 
739
        if (fread(slnk_entry->target, 1, ucsize, G.outfile) != ucsize)
740
        {
741
            Info(slide, 0x201, ((char *)slide,
742
              "warning:  symbolic link (%s) failed\n",
743
              FnFilter1(G.filename)));
744
            free(slnk_entry);
745
            fclose(G.outfile);
746
            return;
747
        }
748
        fclose(G.outfile);                  /* close "link" file for good... */
749
        slnk_entry->target[ucsize] = '\0';
750
        if (QCOND2)
751
            Info(slide, 0, ((char *)slide, "-> %s ",
752
              FnFilter1(slnk_entry->target)));
753
        /* add this symlink record to the list of deferred symlinks */
754
        if (G.slink_last != NULL)
755
            G.slink_last->next = slnk_entry;
756
        else
757
            G.slink_head = slnk_entry;
758
        G.slink_last = slnk_entry;
759
        return;
760
    }
761
#endif /* SYMLINKS */
762
 
763
#ifdef QLZIP
764
    if (G.extra_field) {
765
        static void qlfix OF((__GPRO__ uch *ef_ptr, unsigned ef_len));
766
 
767
        qlfix(__G__ G.extra_field, G.lrec.extra_field_length);
768
    }
769
#endif
770
 
771
#if (defined(NO_FCHOWN))
772
    fclose(G.outfile);
773
#endif
774
 
775
// kos. add set file datetime ?
776
#ifndef KOS32
777
    /* if -X option was specified and we have UID/GID info, restore it */
778
    if (have_uidgid_flg
779
        /* check that both uid and gid values fit into their data sizes */
780
        && ((ulg)(uid_t)(z_uidgid[0]) == z_uidgid[0])
781
        && ((ulg)(gid_t)(z_uidgid[1]) == z_uidgid[1])) {
782
        TTrace((stderr, "close_outfile:  restoring Unix UID/GID info\n"));
783
#if (defined(NO_FCHOWN))
784
        if (chown(G.filename, (uid_t)z_uidgid[0], (gid_t)z_uidgid[1]))
785
#else
786
        if (fchown(fileno(G.outfile), (uid_t)z_uidgid[0], (gid_t)z_uidgid[1]))
787
#endif
788
        {
789
            if (uO.qflag)
790
                Info(slide, 0x201, ((char *)slide, CannotSetItemUidGid,
791
                  z_uidgid[0], z_uidgid[1], FnFilter1(G.filename),
792
                  strerror(errno)));
793
            else
794
                Info(slide, 0x201, ((char *)slide, CannotSetUidGid,
795
                  z_uidgid[0], z_uidgid[1], strerror(errno)));
796
        }
797
    }
798
 
799
#if (!defined(NO_FCHOWN) && defined(NO_FCHMOD))
800
    fclose(G.outfile);
801
#endif
802
 
803
#if (!defined(NO_FCHOWN) && !defined(NO_FCHMOD))
804
/*---------------------------------------------------------------------------
805
    Change the file permissions from default ones to those stored in the
806
    zipfile.
807
  ---------------------------------------------------------------------------*/
808
 
809
    if (fchmod(fileno(G.outfile), filtattr(__G__ G.pInfo->file_attr)))
810
        perror("fchmod (file attributes) error");
811
 
812
    fclose(G.outfile);
813
#endif /* !NO_FCHOWN && !NO_FCHMOD */
814
 
815
    /* skip restoring time stamps on user's request */
816
    if (uO.D_flag <= 1) {
817
        /* set the file's access and modification times */
818
        if (utime(G.filename, &(zt.t2))) {
819
            if (uO.qflag)
820
                Info(slide, 0x201, ((char *)slide, CannotSetItemTimestamps,
821
                  FnFilter1(G.filename), strerror(errno)));
822
            else
823
                Info(slide, 0x201, ((char *)slide, CannotSetTimestamps,
824
                  strerror(errno)));
825
        }
826
    }
827
 
828
#if (defined(NO_FCHOWN) || defined(NO_FCHMOD))
829
/*---------------------------------------------------------------------------
830
    Change the file permissions from default ones to those stored in the
831
    zipfile.
832
  ---------------------------------------------------------------------------*/
833
 
834
#ifndef NO_CHMOD
835
    if (chmod(G.filename, filtattr(__G__ G.pInfo->file_attr)))
836
        perror("chmod (file attributes) error");
837
#endif
838
#endif /* NO_FCHOWN || NO_FCHMOD */
839
 
840
#endif // 0
841
 
842
} /* end function close_outfile() */
843
 
844
 
845
/**********************/
846
/* Function do_wild() */   /* for porting: dir separator; match(ignore_case) */
847
/**********************/
848
 
849
char *do_wild(__G__ wildspec)
850
    __GDEF
851
    ZCONST char *wildspec;  /* only used first time on a given dir */
852
{
853
/* these statics are now declared in SYSTEM_SPECIFIC_GLOBALS in unxcfg.h:
854
    static DIR *wild_dir = (DIR *)NULL;
855
    static ZCONST char *wildname;
856
    static char *dirname, matchname[FILNAMSIZ];
857
    static int notfirstcall=FALSE, have_dirname, dirnamelen;
858
*/
859
    struct dirent *file;
860
 
861
    /* Even when we're just returning wildspec, we *always* do so in
862
     * matchname[]--calling routine is allowed to append four characters
863
     * to the returned string, and wildspec may be a pointer to argv[].
864
     */
865
    if (!G.notfirstcall) {  /* first call:  must initialize everything */
866
        G.notfirstcall = TRUE;
867
 
868
        if (!iswild(wildspec)) {
869
            strncpy(G.matchname, wildspec, FILNAMSIZ);
870
            G.matchname[FILNAMSIZ-1] = '\0';
871
            G.have_dirname = FALSE;
872
            G.wild_dir = NULL;
873
            return G.matchname;
874
        }
875
 
876
        /* break the wildspec into a directory part and a wildcard filename */
877
        if ((G.wildname = (ZCONST char *)strrchr(wildspec, '/')) == NULL) {
878
            G.dirname = ".";
879
            G.dirnamelen = 1;
880
            G.have_dirname = FALSE;
881
            G.wildname = wildspec;
882
        } else {
883
            ++G.wildname;     /* point at character after '/' */
884
            G.dirnamelen = G.wildname - wildspec;
885
            if ((G.dirname = (char *)malloc(G.dirnamelen+1)) == (char *)NULL) {
886
                Info(slide, 0x201, ((char *)slide,
887
                  "warning:  cannot allocate wildcard buffers\n"));
888
                strncpy(G.matchname, wildspec, FILNAMSIZ);
889
                G.matchname[FILNAMSIZ-1] = '\0';
890
                return G.matchname; /* but maybe filespec was not a wildcard */
891
            }
892
            strncpy(G.dirname, wildspec, G.dirnamelen);
893
            G.dirname[G.dirnamelen] = '\0';   /* terminate for strcpy below */
894
            G.have_dirname = TRUE;
895
        }
896
 
897
        if ((G.wild_dir = (zvoid *)opendir(G.dirname)) != (zvoid *)NULL) {
898
            while ((file = readdir((DIR *)G.wild_dir)) !=
899
                   (struct dirent *)NULL) {
900
                Trace((stderr, "do_wild:  readdir returns %s\n",
901
                  FnFilter1(file->d_name)));
902
                if (file->d_name[0] == '.' && G.wildname[0] != '.')
903
                    continue; /* Unix:  '*' and '?' do not match leading dot */
904
                if (match(file->d_name, G.wildname, 0 WISEP) &&/*0=case sens.*/
905
                    /* skip "." and ".." directory entries */
906
                    strcmp(file->d_name, ".") && strcmp(file->d_name, "..")) {
907
                    Trace((stderr, "do_wild:  match() succeeds\n"));
908
                    if (G.have_dirname) {
909
                        strcpy(G.matchname, G.dirname);
910
                        strcpy(G.matchname+G.dirnamelen, file->d_name);
911
                    } else
912
                        strcpy(G.matchname, file->d_name);
913
                    return G.matchname;
914
                }
915
            }
916
            /* if we get to here directory is exhausted, so close it */
917
            closedir((DIR *)G.wild_dir);
918
            G.wild_dir = (zvoid *)NULL;
919
        }
920
        Trace((stderr, "do_wild:  opendir(%s) returns NULL\n",
921
          FnFilter1(G.dirname)));
922
 
923
        /* return the raw wildspec in case that works (e.g., directory not
924
         * searchable, but filespec was not wild and file is readable) */
925
        strncpy(G.matchname, wildspec, FILNAMSIZ);
926
        G.matchname[FILNAMSIZ-1] = '\0';
927
        return G.matchname;
928
    }
929
 
930
    /* last time through, might have failed opendir but returned raw wildspec */
931
    if ((DIR *)G.wild_dir == (DIR *)NULL) {
932
        G.notfirstcall = FALSE; /* nothing left--reset for new wildspec */
933
        if (G.have_dirname)
934
            free(G.dirname);
935
        return (char *)NULL;
936
    }
937
 
938
    /* If we've gotten this far, we've read and matched at least one entry
939
     * successfully (in a previous call), so dirname has been copied into
940
     * matchname already.
941
     */
942
    while ((file = readdir((DIR *)G.wild_dir)) != (struct dirent *)NULL) {
943
        Trace((stderr, "do_wild:  readdir returns %s\n",
944
          FnFilter1(file->d_name)));
945
        if (file->d_name[0] == '.' && G.wildname[0] != '.')
946
            continue;   /* Unix:  '*' and '?' do not match leading dot */
947
        if (match(file->d_name, G.wildname, 0 WISEP)) { /* 0 == case sens. */
948
            Trace((stderr, "do_wild:  match() succeeds\n"));
949
            if (G.have_dirname) {
950
                /* strcpy(G.matchname, G.dirname); */
951
                strcpy(G.matchname+G.dirnamelen, file->d_name);
952
            } else
953
                strcpy(G.matchname, file->d_name);
954
            return G.matchname;
955
        }
956
    }
957
 
958
    closedir((DIR *)G.wild_dir);  /* at least one entry read; nothing left */
959
    G.wild_dir = (zvoid *)NULL;
960
    G.notfirstcall = FALSE;       /* reset for new wildspec */
961
    if (G.have_dirname)
962
        free(G.dirname);
963
    return (char *)NULL;
964
 
965
} /* end function do_wild() */
966