Subversion Repositories Kolibri OS

Rev

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