1/*
2  Copyright (c) 1990-2009 Info-ZIP.  All rights reserved.
3
4  See the accompanying file LICENSE, version 2009-Jan-02 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  aosvs.c
12
13  AOS/VS-specific routines for use with Info-ZIP's UnZip 5.2 and later.
14[GRR:  copied from unix.c -> undoubtedly has unnecessary stuff: delete at will]
15
16  Contains:  readdir()
17             do_wild()
18             open_outfile()
19             mapattr()
20             mapname()
21             checkdir()
22             close_outfile()
23             version()             <-- GRR:  needs work!  (Unix, not AOS/VS)
24             zvs_create()
25             zvs_credir()
26             ux_to_vs_name()
27             dgdate()
28
29  ---------------------------------------------------------------------------*/
30
31
32#define UNZIP_INTERNAL
33#include "unzip.h"
34#include "aosvs/aosvs.h"
35#include <packets/create.h>
36#include <sys_calls.h>
37#include <paru.h>
38
39#define symlink(resname,linkname) \
40  zvs_create(linkname,-1L,-1L,-1L,ux_to_vs_name(vs_resname,resname),$FLNK,-1,-1)
41                                             *  file type */
42
43#ifdef DIRENT
44#  include <dirent.h>
45#else
46#  ifdef SYSV
47#    ifdef SYSNDIR
48#      include <sys/ndir.h>
49#    else
50#      include <ndir.h>
51#    endif
52#  else /* !SYSV */
53#    ifndef NO_SYSDIR
54#      include <sys/dir.h>
55#    endif
56#  endif /* ?SYSV */
57#  ifndef dirent
58#    define dirent direct
59#  endif
60#endif /* ?DIRENT */
61
62static int            created_dir;          /* used in mapname(), checkdir() */
63static int            renamed_fullpath;     /* ditto */
64
65static ZEXTRAFLD      zzextrafld;           /* buffer for extra field containing
66                                             *  ?FSTAT packet & ACL buffer */
67static char           vs_resname[2*$MXPL];
68static char           vs_path[2*$MXPL];     /* buf for AOS/VS pathname */
69static char           Vs_path[512];         /* should be big enough [GRR: ?] */
70static P_CTIM         zztimeblock;          /* time block for file creation */
71static ZVSCREATE_STRU zzcreatepacket;       /* packet for sys_create(), any
72
73
74/***************************/
75/* Strings used in aosvs.c */
76/***************************/
77
78static ZCONST char Far CannotDeleteOldFile[] =
79  "error:  cannot delete old %s\n";
80static ZCONST char Far CannotCreateFile[] = "error:  cannot create %s\n";
81
82
83#ifndef SFX
84#ifdef NO_DIR                  /* for AT&T 3B1 */
85
86#define opendir(path) fopen(path,"r")
87#define closedir(dir) fclose(dir)
88typedef FILE DIR;
89
90/*
91 *  Apparently originally by Rich Salz.
92 *  Cleaned up and modified by James W. Birdsall.
93 */
94struct dirent *readdir(dirp)
95    DIR *dirp;
96{
97    static struct dirent entry;
98
99    if (dirp == NULL)
100        return NULL;
101
102    for (;;)
103        if (fread(&entry, sizeof (struct dirent), 1, dirp) == 0)
104            return (struct dirent *)NULL;
105        else if (entry.d_ino)
106            return &entry;
107
108} /* end function readdir() */
109
110#endif /* NO_DIR */
111
112
113/**********************/
114/* Function do_wild() */   /* for porting: dir separator; match(ignore_case) */
115/**********************/
116
117char *do_wild(__G__ wildspec)
118    __GDEF
119    ZCONST char *wildspec;  /* only used first time on a given dir */
120{
121    static DIR *wild_dir = (DIR *)NULL;
122    static ZCONST char *wildname;
123    static char *dirname, matchname[FILNAMSIZ];
124    static int notfirstcall=FALSE, have_dirname, dirnamelen;
125    struct dirent *file;
126
127    /* Even when we're just returning wildspec, we *always* do so in
128     * matchname[]--calling routine is allowed to append four characters
129     * to the returned string, and wildspec may be a pointer to argv[].
130     */
131    if (!notfirstcall) {    /* first call:  must initialize everything */
132        notfirstcall = TRUE;
133
134        if (!iswild(wildspec)) {
135            strncpy(matchname, wildspec, FILNAMSIZ);
136            matchname[FILNAMSIZ-1] = '\0';
137            have_dirname = FALSE;
138            dir = NULL;
139            return matchname;
140        }
141
142        /* break the wildspec into a directory part and a wildcard filename */
143        if ((wildname = strrchr(wildspec, '/')) == (ZCONST char *)NULL) {
144            dirname = ".";
145            dirnamelen = 1;
146            have_dirname = FALSE;
147            wildname = wildspec;
148        } else {
149            ++wildname;     /* point at character after '/' */
150            dirnamelen = wildname - wildspec;
151            if ((dirname = (char *)malloc(dirnamelen+1)) == (char *)NULL) {
152                Info(slide, 0x201, ((char *)slide,
153                  "warning:  cannot allocate wildcard buffers\n"));
154                strncpy(matchname, wildspec, FILNAMSIZ);
155                matchname[FILNAMSIZ-1] = '\0';
156                return matchname;   /* but maybe filespec was not a wildcard */
157            }
158            strncpy(dirname, wildspec, dirnamelen);
159            dirname[dirnamelen] = '\0';   /* terminate for strcpy below */
160            have_dirname = TRUE;
161        }
162
163        if ((wild_dir = opendir(dirname)) != (DIR *)NULL) {
164            while ((file = readdir(wild_dir)) != (struct dirent *)NULL) {
165                Trace((stderr, "do_wild:  readdir returns %s\n",
166                  FnFilter1(file->d_name)));
167                if (file->d_name[0] == '.' && wildname[0] != '.')
168                    continue; /* Unix:  '*' and '?' do not match leading dot */
169                if (match(file->d_name, wildname, 0 WISEP) && /* 0=case sens.*/
170                    /* skip "." and ".." directory entries */
171                    strcmp(file->d_name, ".") && strcmp(file->d_name, "..")) {
172                    Trace((stderr, "do_wild:  match() succeeds\n"));
173                    if (have_dirname) {
174                        strcpy(matchname, dirname);
175                        strcpy(matchname+dirnamelen, file->d_name);
176                    } else
177                        strcpy(matchname, file->d_name);
178                    return matchname;
179                }
180            }
181            /* if we get to here directory is exhausted, so close it */
182            closedir(wild_dir);
183            wild_dir = (DIR *)NULL;
184        }
185
186        /* return the raw wildspec in case that works (e.g., directory not
187         * searchable, but filespec was not wild and file is readable) */
188        strncpy(matchname, wildspec, FILNAMSIZ);
189        matchname[FILNAMSIZ-1] = '\0';
190        return matchname;
191    }
192
193    /* last time through, might have failed opendir but returned raw wildspec */
194    if (wild_dir == (DIR *)NULL) {
195        notfirstcall = FALSE; /* nothing left to try--reset for new wildspec */
196        if (have_dirname)
197            free(dirname);
198        return (char *)NULL;
199    }
200
201    /* If we've gotten this far, we've read and matched at least one entry
202     * successfully (in a previous call), so dirname has been copied into
203     * matchname already.
204     */
205    while ((file = readdir(wild_dir)) != (struct dirent *)NULL) {
206        Trace((stderr, "do_wild:  readdir returns %s\n",
207          FnFilter1(file->d_name)));
208        if (file->d_name[0] == '.' && wildname[0] != '.')
209            continue;   /* Unix:  '*' and '?' do not match leading dot */
210        if (match(file->d_name, wildname, 0 WISEP)) {   /* 0 ==  case sens. */
211            Trace((stderr, "do_wild:  match() succeeds\n"));
212            if (have_dirname) {
213                /* strcpy(matchname, dirname); */
214                strcpy(matchname+dirnamelen, file->d_name);
215            } else
216                strcpy(matchname, file->d_name);
217            return matchname;
218        }
219    }
220
221    closedir(wild_dir);     /* have read at least one entry; nothing left */
222    wild_dir = (DIR *)NULL;
223    notfirstcall = FALSE;   /* reset for new wildspec */
224    if (have_dirname)
225        free(dirname);
226    return (char *)NULL;
227
228} /* end function do_wild() */
229
230#endif /* !SFX */
231
232
233
234
235
236/***************************/
237/* Function open_outfile() */
238/***************************/
239
240int open_outfile(__G)           /* return 1 if fail */
241    __GDEF
242{
243    int errc = 1;    /* init to show no success with AOS/VS info */
244    long dmm, ddd, dyy, dhh, dmin, dss;
245
246
247#ifdef DLL
248    if (G.redirect_data)
249        return redirect_outfile(__G)==FALSE;
250#endif
251
252    if (stat(G.filename, &G.statbuf) == 0 && unlink(G.filename) < 0) {
253        Info(slide, 0x401, ((char *)slide, LoadFarString(CannotDeleteOldFile),
254          FnFilter1(G.filename)));
255        return 1;
256    }
257
258/*---------------------------------------------------------------------------
259    If the file didn't already exist, we created it earlier.  But we just
260    deleted it, which we still had to do in case we are overwriting an exis-
261    ting file.  So we must create it now, again, to set the creation time
262    properly.  (The creation time is the best functional approximation of
263    the Unix mtime.  Really!)
264
265    If we stored this with an AOS/VS Zip that set the extra field to contain
266    the ?FSTAT packet and the ACL, we should use info from the ?FSTAT call
267    now.  Otherwise (or if that fails), we should create anyway as best we
268    can from the normal Zip info.
269
270    In theory, we should look through an entire series of extra fields that
271    might exist for the same file, but we're not going to bother.  If we set
272    up other types of extra fields, or if other environments we run into may
273    add their own stuff to existing entries in Zip files, we'll have to.
274
275    Note that all the packet types for sys_fstat() are the same size & mostly
276    have the same structure, with some fields being unused, etc.  Ditto for
277    sys_create().  Thus, we assume a normal one here, not a dir/cpd or device
278    or IPC file, & make little adjustments as necessary.  We will set ACLs
279    later (to reduce the chance of lacking access to what we create now); note
280    that for links the resolution name should be stored in the ACL field (once
281    we get Zip recognizing links OK).
282  ---------------------------------------------------------------------------*/
283
284    if (G.extra_field != NULL) {
285        memcpy((char *) &zzextrafld, G.extra_field, sizeof(zzextrafld));
286        if (!memcmp(ZEXTRA_HEADID, zzextrafld.extra_header_id,
287                    sizeof(zzextrafld.extra_header_id))  &&
288            !memcmp(ZEXTRA_SENTINEL, zzextrafld.extra_sentinel),
289                    sizeof(zzextrafld.extra_sentinel))
290        {
291            zzcreatepacket.norm_create_packet.cftyp_format =
292              zzextrafld.fstat_packet.norm_fstat_packet.styp_format;
293            zzcreatepacket.norm_create_packet.cftyp_entry =
294              zzextrafld.fstat_packet.norm_fstat_packet.styp_type;
295
296            /* for DIRS/CPDs, the next one will give the hash frame size; for
297             * IPCs it will give the port number */
298            zzcreatepacket.norm_create_packet.ccps =
299              zzextrafld.fstat_packet.norm_fstat_packet.scps;
300
301            zzcreatepacket.norm_create_packet.ctim = &zztimeblock;
302            zztimeblock.tcth = zzextrafld.fstat_packet.norm_fstat_packet.stch;
303
304            /* access & modification times default to current */
305            zztimeblock.tath.long_time = zztimeblock.tmth.long_time = -1;
306
307            /* give it current process's ACL unless link; then give it
308             * resolution name */
309            zzcreatepacket.norm_create_packet.cacp = (char *)(-1);
310
311            if (zzcreatepacket.norm_create_packet.cftyp_entry == $FLNK)
312                zzcreatepacket.norm_create_packet.cacp = zzextrafld.aclbuf;
313
314            zzcreatepacket.dir_create_packet.cmsh =
315              zzextrafld.fstat_packet.dir_fstat_packet.scsh;
316            if (zzcreatepacket.norm_create_packet.cftyp_entry != $FCPD) {
317                /* element size for normal files */
318                zzcreatepacket.norm_create_packet.cdel =
319                  zzextrafld.fstat_packet.norm_fstat_packet.sdeh;
320            }
321            zzcreatepacket.norm_create_packet.cmil =
322              zzextrafld.fstat_packet.norm_fstat_packet.smil;
323
324            if ((errc = sys_create(ux_to_vs_name(vs_path, G.filename),
325                 &zzcreatepacket)) != 0)
326                Info(slide, 0x201, ((char *)slide,
327                  "error creating %s with AOS/VS info -\n\
328                  will try again with ordinary Zip info\n",
329                  FnFilter1(G.filename)));
330        }
331    }
332
333    /* do it the hard way if no AOS/VS info was stored or if we had problems */
334    if (errc) {
335        /* skip restoring time stamps on user's request */
336        if (uO.D_flag <= 1) {
337            dyy = (G.lrec.last_mod_dos_datetime >> 25) + 1980;
338            dmm = (G.lrec.last_mod_dos_datetime >> 21) & 0x0f;
339            ddd = (G.lrec.last_mod_dos_datetime >> 16) & 0x1f;
340            dhh = (G.lrec.last_mod_dos_datetime >> 11) & 0x1f;
341            dmin = (G.lrec.last_mod_dos_datetime >> 5) & 0x3f;
342            dss = (G.lrec.last_mod_dos_datetime << 1) & 0x3e;
343        }
344
345        if (zvs_create(G.filename,
346            (uO.D_flag <= 1
347             ? (((ulg)dgdate(dmm, ddd, dyy)) << 16) |
348               (dhh*1800L + dmin*30L + dss/2L)
349             : -1L),
350            -1L, -1L, (char *) -1, -1, -1, -1))
351        {
352            Info(slide, 0x201, ((char *)slide, "error: %s: cannot create\n",
353              FnFilter1(G.filename)));
354            return 1;
355        }
356    }
357
358    Trace((stderr, "open_outfile:  doing fopen(%s) for writing\n",
359      FnFilter1(G.filename)));
360    if ((G.outfile = fopen(G.filename, FOPW)) == (FILE *)NULL) {
361        Info(slide, 0x401, ((char *)slide, LoadFarString(CannotCreateFile),
362          FnFilter1(G.filename)));
363        return 1;
364    }
365    Trace((stderr, "open_outfile:  fopen(%s) for writing succeeded\n",
366      FnFilter1(G.filename)));
367
368#ifdef USE_FWRITE
369#ifdef _IOFBF  /* make output fully buffered (works just about like write()) */
370    setvbuf(G.outfile, (char *)slide, _IOFBF, WSIZE);
371#else
372    setbuf(G.outfile, (char *)slide);
373#endif
374#endif /* USE_FWRITE */
375    return 0;
376
377} /* end function open_outfile() */
378
379
380
381
382
383/**********************/
384/* Function mapattr() */
385/**********************/
386
387int mapattr(__G)
388    __GDEF
389{
390    ulg tmp = G.crec.external_file_attributes;
391
392    G.pInfo->file_attr = 0;
393    /* initialized to 0 for check in "default" branch below... */
394
395    switch (G.pInfo->hostnum) {
396        case AMIGA_:
397            tmp = (unsigned)(tmp>>17 & 7);   /* Amiga RWE bits */
398            G.pInfo->file_attr = (unsigned)(tmp<<6 | tmp<<3 | tmp);
399            break;
400        case THEOS_:
401            tmp &= 0xF1FFFFFFL;
402            if ((tmp & 0xF0000000L) != 0x40000000L)
403                tmp &= 0x01FFFFFFL;     /* not a dir, mask all ftype bits */
404            else
405                tmp &= 0x41FFFFFFL;     /* leave directory bit as set */
406            /* fall through! */
407        case UNIX_:
408        case VMS_:
409        case ACORN_:
410        case ATARI_:
411        case ATHEOS_:
412        case BEOS_:
413        case QDOS_:
414        case TANDEM_:
415            G.pInfo->file_attr = (unsigned)(tmp >> 16);
416            if (G.pInfo->file_attr != 0 || !G.extra_field) {
417                return 0;
418            } else {
419                /* Some (non-Info-ZIP) implementations of Zip for Unix and
420                 * VMS (and probably others ??) leave 0 in the upper 16-bit
421                 * part of the external_file_attributes field. Instead, they
422                 * store file permission attributes in some extra field.
423                 * As a work-around, we search for the presence of one of
424                 * these extra fields and fall back to the MSDOS compatible
425                 * part of external_file_attributes if one of the known
426                 * e.f. types has been detected.
427                 * Later, we might implement extraction of the permission
428                 * bits from the VMS extra field. But for now, the work-around
429                 * should be sufficient to provide "readable" extracted files.
430                 * (For ASI Unix e.f., an experimental remap from the e.f.
431                 * mode value IS already provided!)
432                 */
433                ush ebID;
434                unsigned ebLen;
435                uch *ef = G.extra_field;
436                unsigned ef_len = G.crec.extra_field_length;
437                int r = FALSE;
438
439                while (!r && ef_len >= EB_HEADSIZE) {
440                    ebID = makeword(ef);
441                    ebLen = (unsigned)makeword(ef+EB_LEN);
442                    if (ebLen > (ef_len - EB_HEADSIZE))
443                        /* discoverd some e.f. inconsistency! */
444                        break;
445                    switch (ebID) {
446                      case EF_ASIUNIX:
447                        if (ebLen >= (EB_ASI_MODE+2)) {
448                            G.pInfo->file_attr =
449                              (unsigned)makeword(ef+(EB_HEADSIZE+EB_ASI_MODE));
450                            /* force stop of loop: */
451                            ef_len = (ebLen + EB_HEADSIZE);
452                            break;
453                        }
454                        /* else: fall through! */
455                      case EF_PKVMS:
456                        /* "found nondecypherable e.f. with perm. attr" */
457                        r = TRUE;
458                      default:
459                        break;
460                    }
461                    ef_len -= (ebLen + EB_HEADSIZE);
462                    ef += (ebLen + EB_HEADSIZE);
463                }
464                if (!r)
465                    return 0;
466            }
467            /* fall through! */
468        /* all remaining cases:  expand MSDOS read-only bit into write perms */
469        case FS_FAT_:
470            /* PKWARE's PKZip for Unix marks entries as FS_FAT_, but stores the
471             * Unix attributes in the upper 16 bits of the external attributes
472             * field, just like Info-ZIP's Zip for Unix.  We try to use that
473             * value, after a check for consistency with the MSDOS attribute
474             * bits (see below).
475             */
476            G.pInfo->file_attr = (unsigned)(tmp >> 16);
477            /* fall through! */
478        case FS_HPFS_:
479        case FS_NTFS_:
480        case MAC_:
481        case TOPS20_:
482        default:
483            /* Ensure that DOS subdir bit is set when the entry's name ends
484             * in a '/'.  Some third-party Zip programs fail to set the subdir
485             * bit for directory entries.
486             */
487            if ((tmp & 0x10) == 0) {
488                extent fnlen = strlen(G.filename);
489                if (fnlen > 0 && G.filename[fnlen-1] == '/')
490                    tmp |= 0x10;
491            }
492            /* read-only bit --> write perms; subdir bit --> dir exec bit */
493            tmp = !(tmp & 1) << 1  |  (tmp & 0x10) >> 4;
494            if ((G.pInfo->file_attr & 0700) == (unsigned)(0400 | tmp<<6))
495                /* keep previous G.pInfo->file_attr setting, when its "owner"
496                 * part appears to be consistent with DOS attribute flags!
497                 */
498                return 0;
499            G.pInfo->file_attr = (unsigned)(0444 | tmp<<6 | tmp<<3 | tmp);
500            break;
501    } /* end switch (host-OS-created-by) */
502
503    /* for originating systems with no concept of "group," "other," "system": */
504    umask( (int)(tmp=umask(0)) );    /* apply mask to expanded r/w(/x) perms */
505    G.pInfo->file_attr &= ~tmp;
506
507    return 0;
508
509} /* end function mapattr() */
510
511
512
513
514
515/************************/
516/*  Function mapname()  */
517/************************/
518
519int mapname(__G__ renamed)
520    __GDEF
521    int renamed;
522/*
523 * returns:
524 *  MPN_OK          - no problem detected
525 *  MPN_INF_TRUNC   - caution (truncated filename)
526 *  MPN_INF_SKIP    - info "skip entry" (dir doesn't exist)
527 *  MPN_ERR_SKIP    - error -> skip entry
528 *  MPN_ERR_TOOLONG - error -> path is too long
529 *  MPN_NOMEM       - error (memory allocation failed) -> skip entry
530 *  [also MPN_VOL_LABEL, MPN_CREATED_DIR]
531 */
532{
533    char pathcomp[FILNAMSIZ];      /* path-component buffer */
534    char *pp, *cp=(char *)NULL;    /* character pointers */
535    char *lastsemi=(char *)NULL;   /* pointer to last semi-colon in pathcomp */
536#ifdef ACORN_FTYPE_NFS
537    char *lastcomma=(char *)NULL;  /* pointer to last comma in pathcomp */
538    RO_extra_block *ef_spark;      /* pointer Acorn FTYPE ef block */
539#endif
540    int killed_ddot = FALSE;       /* is set when skipping "../" pathcomp */
541    int error = MPN_OK;
542    register unsigned workch;      /* hold the character being tested */
543
544
545/*---------------------------------------------------------------------------
546    Initialize various pointers and counters and stuff.
547  ---------------------------------------------------------------------------*/
548
549    if (G.pInfo->vollabel)
550        return MPN_VOL_LABEL;   /* can't set disk volume labels in Unix */
551
552    /* can create path as long as not just freshening, or if user told us */
553    G.create_dirs = (!uO.fflag || renamed);
554
555    created_dir = FALSE;        /* not yet */
556
557    /* user gave full pathname:  don't prepend rootpath */
558    renamed_fullpath = (renamed && (*G.filename == '/'));
559
560    if (checkdir(__G__ (char *)NULL, INIT) == MPN_NOMEM)
561        return MPN_NOMEM;       /* initialize path buffer, unless no memory */
562
563    *pathcomp = '\0';           /* initialize translation buffer */
564    pp = pathcomp;              /* point to translation buffer */
565    if (uO.jflag)               /* junking directories */
566        cp = (char *)strrchr(G.filename, '/');
567    if (cp == (char *)NULL)     /* no '/' or not junking dirs */
568        cp = G.filename;        /* point to internal zipfile-member pathname */
569    else
570        ++cp;                   /* point to start of last component of path */
571
572/*---------------------------------------------------------------------------
573    Begin main loop through characters in filename.
574  ---------------------------------------------------------------------------*/
575
576    while ((workch = (uch)*cp++) != 0) {
577
578        switch (workch) {
579            case '/':             /* can assume -j flag not given */
580                *pp = '\0';
581                if (strcmp(pathcomp, ".") == 0) {
582                    /* don't bother appending "./" to the path */
583                    *pathcomp = '\0';
584                } else if (!uO.ddotflag && strcmp(pathcomp, "..") == 0) {
585                    /* "../" dir traversal detected, skip over it */
586                    *pathcomp = '\0';
587                    killed_ddot = TRUE;     /* set "show message" flag */
588                }
589                /* when path component is not empty, append it now */
590                if (*pathcomp != '\0' &&
591                    ((error = checkdir(__G__ pathcomp, APPEND_DIR))
592                     & MPN_MASK) > MPN_INF_TRUNC)
593                    return error;
594                pp = pathcomp;    /* reset conversion buffer for next piece */
595                lastsemi = (char *)NULL; /* leave direct. semi-colons alone */
596                break;
597
598            case ';':             /* VMS version (or DEC-20 attrib?) */
599                lastsemi = pp;
600                *pp++ = ';';      /* keep for now; remove VMS ";##" */
601                break;            /*  later, if requested */
602
603#ifdef ACORN_FTYPE_NFS
604            case ',':             /* NFS filetype extension */
605                lastcomma = pp;
606                *pp++ = ',';      /* keep for now; may need to remove */
607                break;            /*  later, if requested */
608#endif
609
610#ifdef MTS
611            case ' ':             /* change spaces to underscore under */
612                *pp++ = '_';      /*  MTS; leave as spaces under Unix */
613                break;
614#endif
615
616            default:
617                /* allow European characters in filenames: */
618                if (isprint(workch) || (128 <= workch && workch <= 254))
619                    *pp++ = (char)workch;
620        } /* end switch */
621
622    } /* end while loop */
623
624    /* Show warning when stripping insecure "parent dir" path components */
625    if (killed_ddot && QCOND2) {
626        Info(slide, 0, ((char *)slide,
627          "warning:  skipped \"../\" path component(s) in %s\n",
628          FnFilter1(G.filename)));
629        if (!(error & ~MPN_MASK))
630            error = (error & MPN_MASK) | PK_WARN;
631    }
632
633/*---------------------------------------------------------------------------
634    Report if directory was created (and no file to create:  filename ended
635    in '/'), check name to be sure it exists, and combine path and name be-
636    fore exiting.
637  ---------------------------------------------------------------------------*/
638
639    if (G.filename[strlen(G.filename) - 1] == '/') {
640        checkdir(__G__ G.filename, GETPATH);
641        if (created_dir) {
642            if (QCOND2) {
643                Info(slide, 0, ((char *)slide, "   creating: %s\n",
644                  FnFilter1(G.filename)));
645            }
646            /* set dir time (note trailing '/') */
647            return (error & ~MPN_MASK) | MPN_CREATED_DIR;
648        }
649        /* dir existed already; don't look for data to extract */
650        return (error & ~MPN_MASK) | MPN_INF_SKIP;
651    }
652
653    *pp = '\0';                   /* done with pathcomp:  terminate it */
654
655    /* if not saving them, remove VMS version numbers (appended ";###") */
656    if (!uO.V_flag && lastsemi) {
657        pp = lastsemi + 1;
658        while (isdigit((uch)(*pp)))
659            ++pp;
660        if (*pp == '\0')          /* only digits between ';' and end:  nuke */
661            *lastsemi = '\0';
662    }
663
664    /* On UNIX (and compatible systems), "." and ".." are reserved for
665     * directory navigation and cannot be used as regular file names.
666     * These reserved one-dot and two-dot names are mapped to "_" and "__".
667     */
668    if (strcmp(pathcomp, ".") == 0)
669        *pathcomp = '_';
670    else if (strcmp(pathcomp, "..") == 0)
671        strcpy(pathcomp, "__");
672
673#ifdef ACORN_FTYPE_NFS
674    /* translate Acorn filetype information if asked to do so */
675    if (uO.acorn_nfs_ext &&
676        (ef_spark = (RO_extra_block *)
677                    getRISCOSexfield(G.extra_field, G.lrec.extra_field_length))
678        != (RO_extra_block *)NULL)
679    {
680        /* file *must* have a RISC OS extra field */
681        long ft = (long)makelong(ef_spark->loadaddr);
682        /*32-bit*/
683        if (lastcomma) {
684            pp = lastcomma + 1;
685            while (isxdigit((uch)(*pp))) ++pp;
686            if (pp == lastcomma+4 && *pp == '\0') *lastcomma='\0'; /* nuke */
687        }
688        if ((ft & 1<<31)==0) ft=0x000FFD00;
689        sprintf(pathcomp+strlen(pathcomp), ",%03x", (int)(ft>>8) & 0xFFF);
690    }
691#endif /* ACORN_FTYPE_NFS */
692
693    if (*pathcomp == '\0') {
694        Info(slide, 1, ((char *)slide, "mapname:  conversion of %s failed\n",
695          FnFilter1(G.filename)));
696        return (error & ~MPN_MASK) | MPN_ERR_SKIP;
697    }
698
699    checkdir(__G__ pathcomp, APPEND_NAME);  /* returns 1 if truncated: care? */
700    checkdir(__G__ G.filename, GETPATH);
701
702    return error;
703
704} /* end function mapname() */
705
706
707
708
709#if 0  /*========== NOTES ==========*/
710
711  extract-to dir:      a:path/
712  buildpath:           path1/path2/ ...   (NULL-terminated)
713  pathcomp:                filename
714
715  mapname():
716    loop over chars in zipfile member name
717      checkdir(path component, COMPONENT | CREATEDIR) --> map as required?
718        (d:/tmp/unzip/)                    (disk:[tmp.unzip.)
719        (d:/tmp/unzip/jj/)                 (disk:[tmp.unzip.jj.)
720        (d:/tmp/unzip/jj/temp/)            (disk:[tmp.unzip.jj.temp.)
721    finally add filename itself and check for existence? (could use with rename)
722        (d:/tmp/unzip/jj/temp/msg.outdir)  (disk:[tmp.unzip.jj.temp]msg.outdir)
723    checkdir(name, GETPATH)     -->  copy path to name and free space
724
725#endif /* 0 */
726
727
728
729
730/***********************/
731/* Function checkdir() */
732/***********************/
733
734int checkdir(__G__ pathcomp, flag)
735    __GDEF
736    char *pathcomp;
737    int flag;
738/*
739 * returns:
740 *  MPN_OK          - no problem detected
741 *  MPN_INF_TRUNC   - (on APPEND_NAME) truncated filename
742 *  MPN_INF_SKIP    - path doesn't exist, not allowed to create
743 *  MPN_ERR_SKIP    - path doesn't exist, tried to create and failed; or path
744 *                    exists and is not a directory, but is supposed to be
745 *  MPN_ERR_TOOLONG - path is too long
746 *  MPN_NOMEM       - can't allocate memory for filename buffers
747 */
748{
749    static int rootlen = 0;   /* length of rootpath */
750    static char *rootpath;    /* user's "extract-to" directory */
751    static char *buildpath;   /* full path (so far) to extracted file */
752    static char *end;         /* pointer to end of buildpath ('\0') */
753
754#   define FN_MASK   7
755#   define FUNCTION  (flag & FN_MASK)
756
757
758
759/*---------------------------------------------------------------------------
760    APPEND_DIR:  append the path component to the path being built and check
761    for its existence.  If doesn't exist and we are creating directories, do
762    so for this one; else signal success or error as appropriate.
763  ---------------------------------------------------------------------------*/
764
765    if (FUNCTION == APPEND_DIR) {
766        int too_long = FALSE;
767#ifdef SHORT_NAMES
768        char *old_end = end;
769#endif
770
771        Trace((stderr, "appending dir segment [%s]\n", FnFilter1(pathcomp)));
772        while ((*end = *pathcomp++) != '\0')
773            ++end;
774#ifdef SHORT_NAMES   /* path components restricted to 14 chars, typically */
775        if ((end-old_end) > FILENAME_MAX)  /* GRR:  proper constant? */
776            *(end = old_end + FILENAME_MAX) = '\0';
777#endif
778
779        /* GRR:  could do better check, see if overrunning buffer as we go:
780         * check end-buildpath after each append, set warning variable if
781         * within 20 of FILNAMSIZ; then if var set, do careful check when
782         * appending.  Clear variable when begin new path. */
783
784        /* next check: need to append '/', at least one-char name, '\0' */
785        if ((end-buildpath) > FILNAMSIZ-3)
786            too_long = TRUE;                    /* check if extracting dir? */
787        /* for AOS/VS, try to create so as to not use searchlist: */
788        if ( /*SSTAT(buildpath, &G.statbuf)*/ 1) {
789            if (!G.create_dirs) { /* told not to create (freshening) */
790                free(buildpath);
791                return MPN_INF_SKIP;    /* path doesn't exist: nothing to do */
792            }
793            if (too_long) {
794                Info(slide, 1, ((char *)slide,
795                  "checkdir error:  path too long: %s\n",
796                  FnFilter1(buildpath)));
797                free(buildpath);
798                /* no room for filenames:  fatal */
799                return MPN_ERR_TOOLONG;
800            }
801            /* create the directory */
802            if (zvs_credir(buildpath,-1L,-1L,-1L,(char *) -1,-1,0L,-1,-1) == -1)
803            {
804                Info(slide, 1, ((char *)slide,
805                  "checkdir error:  cannot create %s\n\
806                 %s\n\
807                 unable to process %s.\n",
808                  FnFilter2(buildpath),
809                  strerror(errno),
810                  FnFilter1(G.filename)));
811                free(buildpath);
812                /* path didn't exist, tried to create, failed */
813                return MPN_ERR_SKIP;
814            }
815            created_dir = TRUE;
816        } else if (!S_ISDIR(G.statbuf.st_mode)) {
817            Info(slide, 1, ((char *)slide,
818              "checkdir error:  %s exists but is not directory\n\
819                 unable to process %s.\n",
820              FnFilter2(buildpath), FnFilter1(G.filename)));
821            free(buildpath);
822            /* path existed but wasn't dir */
823            return MPN_ERR_SKIP;
824        }
825        if (too_long) {
826            Info(slide, 1, ((char *)slide,
827              "checkdir error:  path too long: %s\n", FnFilter1(buildpath)));
828            free(buildpath);
829            /* no room for filenames:  fatal */
830            return MPN_ERR_TOOLONG;
831        }
832        *end++ = '/';
833        *end = '\0';
834        Trace((stderr, "buildpath now = [%s]\n", FnFilter1(buildpath)));
835        return MPN_OK;
836
837    } /* end if (FUNCTION == APPEND_DIR) */
838
839/*---------------------------------------------------------------------------
840    GETPATH:  copy full path to the string pointed at by pathcomp, and free
841    buildpath.
842  ---------------------------------------------------------------------------*/
843
844    if (FUNCTION == GETPATH) {
845        strcpy(pathcomp, buildpath);
846        Trace((stderr, "getting and freeing path [%s]\n",
847          FnFilter1(pathcomp)));
848        free(buildpath);
849        buildpath = end = (char *)NULL;
850        return MPN_OK;
851    }
852
853/*---------------------------------------------------------------------------
854    APPEND_NAME:  assume the path component is the filename; append it and
855    return without checking for existence.
856  ---------------------------------------------------------------------------*/
857
858    if (FUNCTION == APPEND_NAME) {
859#ifdef SHORT_NAMES
860        char *old_end = end;
861#endif
862
863        Trace((stderr, "appending filename [%s]\n", FnFilter1(pathcomp)));
864        while ((*end = *pathcomp++) != '\0') {
865            ++end;
866#ifdef SHORT_NAMES  /* truncate name at 14 characters, typically */
867            if ((end-old_end) > FILENAME_MAX)      /* GRR:  proper constant? */
868                *(end = old_end + FILENAME_MAX) = '\0';
869#endif
870            if ((end-buildpath) >= FILNAMSIZ) {
871                *--end = '\0';
872                Info(slide, 1, ((char *)slide,
873                  "checkdir warning:  path too long; truncating\n\
874                   %s\n                -> %s\n",
875                  FnFilter1(G.filename), FnFilter2(buildpath)));
876                return MPN_INF_TRUNC;   /* filename truncated */
877            }
878        }
879        Trace((stderr, "buildpath now = [%s]\n", FnFilter1(buildpath)));
880        /* could check for existence here, prompt for new name... */
881        return MPN_OK;
882    }
883
884/*---------------------------------------------------------------------------
885    INIT:  allocate and initialize buffer space for the file currently being
886    extracted.  If file was renamed with an absolute path, don't prepend the
887    extract-to path.
888  ---------------------------------------------------------------------------*/
889
890/* GRR:  for VMS and TOPS-20, add up to 13 to strlen */
891
892    if (FUNCTION == INIT) {
893        Trace((stderr, "initializing buildpath to "));
894        if ((buildpath = (char *)malloc(strlen(G.filename)+rootlen+1))
895            == (char *)NULL)
896            return MPN_NOMEM;
897        if ((rootlen > 0) && !renamed_fullpath) {
898            strcpy(buildpath, rootpath);
899            end = buildpath + rootlen;
900        } else {
901            *buildpath = '\0';
902            end = buildpath;
903        }
904        Trace((stderr, "[%s]\n", FnFilter1(buildpath)));
905        return MPN_OK;
906    }
907
908/*---------------------------------------------------------------------------
909    ROOT:  if appropriate, store the path in rootpath and create it if
910    necessary; else assume it's a zipfile member and return.  This path
911    segment gets used in extracting all members from every zipfile specified
912    on the command line.
913  ---------------------------------------------------------------------------*/
914
915#if (!defined(SFX) || defined(SFX_EXDIR))
916    if (FUNCTION == ROOT) {
917        Trace((stderr, "initializing root path to [%s]\n",
918          FnFilter1(pathcomp)));
919        if (pathcomp == (char *)NULL) {
920            rootlen = 0;
921            return MPN_OK;
922        }
923        if (rootlen > 0)        /* rootpath was already set, nothing to do */
924            return MPN_OK;
925        if ((rootlen = strlen(pathcomp)) > 0) {
926            char *tmproot;
927
928            if ((tmproot = (char *)malloc(rootlen+2)) == (char *)NULL) {
929                rootlen = 0;
930                return MPN_NOMEM;
931            }
932            strcpy(tmproot, pathcomp);
933            if (tmproot[rootlen-1] == '/') {
934                tmproot[--rootlen] = '\0';
935            }
936            if (rootlen > 0 && (SSTAT(tmproot, &G.statbuf) ||
937                                !S_ISDIR(G.statbuf.st_mode)))
938            {   /* path does not exist */
939                if (!G.create_dirs /* || iswild(tmproot) */ ) {
940                    free(tmproot);
941                    rootlen = 0;
942                    /* skip (or treat as stored file) */
943                    return MPN_INF_SKIP;
944                }
945                /* create the directory (could add loop here scanning tmproot
946                 * to create more than one level, but why really necessary?) */
947                if (zvs_credir(tmproot,-1L,-1L,-1L,(char *) -1,-1,0L,-1,-1)
948                    == -1)
949                {
950                    Info(slide, 1, ((char *)slide,
951                      "checkdir:  cannot create extraction directory: %s\n\
952           %s\n",
953                      FnFilter1(tmproot), strerror(errno)));
954                    free(tmproot);
955                    rootlen = 0;
956                    /* path didn't exist, tried to create, and failed: */
957                    /* file exists, or 2+ subdir levels required */
958                    return MPN_ERR_SKIP;
959                }
960            }
961            tmproot[rootlen++] = '/';
962            tmproot[rootlen] = '\0';
963            if ((rootpath = (char *)realloc(tmproot, rootlen+1)) == NULL) {
964                free(tmproot);
965                rootlen = 0;
966                return MPN_NOMEM;
967            }
968            Trace((stderr, "rootpath now = [%s]\n", FnFilter1(rootpath)));
969        }
970        return MPN_OK;
971    }
972#endif /* !SFX || SFX_EXDIR */
973
974/*---------------------------------------------------------------------------
975    END:  free rootpath, immediately prior to program exit.
976  ---------------------------------------------------------------------------*/
977
978    if (FUNCTION == END) {
979        Trace((stderr, "freeing rootpath\n"));
980        if (rootlen > 0) {
981            free(rootpath);
982            rootlen = 0;
983        }
984        return MPN_OK;
985    }
986
987    return MPN_INVALID; /* should never reach */
988
989} /* end function checkdir() */
990
991
992
993
994
995/****************************/
996/* Function close_outfile() */
997/****************************/
998
999void close_outfile(__G)    /* GRR: change to return PK-style warning level */
1000    __GDEF
1001{
1002
1003/*---------------------------------------------------------------------------
1004    If symbolic links are supported, allocate storage for a symlink control
1005    structure, put the uncompressed "data" and other required info in it, and
1006    add the structure to the "deferred symlinks" chain.  Since we know it's a
1007    symbolic link to start with, we shouldn't have to worry about overflowing
1008    unsigned ints with unsigned longs.
1009  ---------------------------------------------------------------------------*/
1010
1011#ifdef SYMLINKS
1012    if (G.symlnk) {
1013        extent ucsize = (extent)G.lrec.ucsize;
1014        /* size of the symlink entry is the sum of
1015         *  (struct size (includes 1st '\0') + 1 additional trailing '\0'),
1016         *  system specific attribute data size (might be 0),
1017         *  and the lengths of name and link target.
1018         */
1019        extent slnk_entrysize = (sizeof(slinkentry) + 1) +
1020                                ucsize + strlen(G.filename);
1021        slinkentry *slnk_entry;
1022
1023        if (slnk_entrysize < ucsize) {
1024            Info(slide, 0x201, ((char *)slide,
1025              "warning:  symbolic link (%s) failed: mem alloc overflow\n",
1026              FnFilter1(G.filename)));
1027            fclose(G.outfile);
1028            return;
1029        }
1030
1031        if ((slnk_entry = (slinkentry *)malloc(slnk_entrysize)) == NULL) {
1032            Info(slide, 0x201, ((char *)slide,
1033              "warning:  symbolic link (%s) failed: no mem\n",
1034              FnFilter1(G.filename)));
1035            fclose(G.outfile);
1036            return;
1037        }
1038        slnk_entry->next = NULL;
1039        slnk_entry->targetlen = ucsize;
1040        slnk_entry->attriblen = 0;      /* don't set attributes for symlinks */
1041        slnk_entry->target = slnk_entry->buf;
1042        slnk_entry->fname = slnk_entry->target + ucsize + 1;
1043        strcpy(slnk_entry->fname, G.filename);
1044
1045        /* move back to the start of the file to re-read the "link data" */
1046        rewind(G.outfile);
1047
1048        if (fread(slnk_entry->target, 1, ucsize, G.outfile) != ucsize)
1049        {
1050            Info(slide, 0x201, ((char *)slide,
1051              "warning:  symbolic link (%s) failed\n",
1052              FnFilter1(G.filename)));
1053            free(slnk_entry);
1054            fclose(G.outfile);
1055            return;
1056        }
1057        fclose(G.outfile);                  /* close "link" file for good... */
1058        slnk_entry->target[ucsize] = '\0';
1059        if (QCOND2)
1060            Info(slide, 0, ((char *)slide, "-> %s ",
1061              FnFilter1(slnk_entry->target)));
1062        /* add this symlink record to the list of deferred symlinks */
1063        if (G.slink_last != NULL)
1064            G.slink_last->next = slnk_entry;
1065        else
1066            G.slink_head = slnk_entry;
1067        G.slink_last = slnk_entry;
1068        return;
1069    }
1070#endif /* SYMLINKS */
1071
1072    fclose(G.outfile);
1073
1074/*---------------------------------------------------------------------------
1075    Change the file permissions from default ones to those stored in the
1076    zipfile.
1077  ---------------------------------------------------------------------------*/
1078
1079#ifndef NO_CHMOD
1080    if (chmod(G.filename, 0xffff & G.pInfo->file_attr))
1081        perror("chmod (file attributes) error");
1082#endif
1083
1084/*---------------------------------------------------------------------------
1085    AOS/VS only allows setting file times at creation but has its own permis-
1086    sions scheme which is better invoked here if the necessary information
1087    was in fact stored.  In theory, we should look through an entire series
1088    of extra fields that might exist for the same file, but we're not going
1089    to bother.  If we set up other types of extra fields, or if we run into
1090    other environments that add their own stuff to existing entries in ZIP
1091    files, we'll have to.  NOTE:  already copied extra-field stuff into
1092    zzextrafld structure when file was created.
1093  ---------------------------------------------------------------------------*/
1094
1095    if (G.extra_field != NULL) {
1096        if (!memcmp(ZEXTRA_HEADID, zzextrafld.extra_header_id,
1097                    sizeof(zzextrafld.extra_header_id))  &&
1098            !memcmp(ZEXTRA_SENTINEL, zzextrafld.extra_sentinel,
1099                    sizeof(zzextrafld.extra_sentinel))  &&
1100            zzextrafld.fstat_packet.norm_fstat_packet.styp_type != $FLNK)
1101            /* (AOS/VS links don't have ACLs) */
1102        {
1103            /* vs_path was set (in this case) when we created the file */
1104            if (sys_sacl(vs_path, zzextrafld.aclbuf)) {
1105                Info(slide, 0x201, ((char *)slide,
1106                  "error: cannot set ACL for %s\n", FnFilter1(G.filename)));
1107                perror("sys_sacl()");
1108            }
1109        }
1110    }
1111} /* end function close_outfile() */
1112
1113
1114
1115
1116#ifndef SFX
1117
1118/************************/
1119/*  Function version()  */
1120/************************/
1121
1122void version(__G)
1123    __GDEF
1124{
1125#if (defined(__GNUC__) && defined(NX_CURRENT_COMPILER_RELEASE))
1126    char cc_namebuf[40];
1127    char cc_versbuf[40];
1128#else
1129#if (defined(CRAY) && defined(_RELEASE))
1130    char cc_versbuf[40];
1131#endif
1132#endif
1133#if ((defined(CRAY) || defined(cray)) && defined(_UNICOS))
1134    char os_namebuf[40];
1135#else
1136#if defined(__NetBSD__)
1137    char os_namebuf[40];
1138#endif
1139#endif
1140
1141    /* Pyramid, NeXT have problems with huge macro expansion, too:  no Info() */
1142    sprintf((char *)slide, LoadFarString(CompiledWith),
1143
1144#ifdef __GNUC__
1145#  ifdef NX_CURRENT_COMPILER_RELEASE
1146      (sprintf(cc_namebuf, "NeXT DevKit %d.%02d ",
1147        NX_CURRENT_COMPILER_RELEASE/100, NX_CURRENT_COMPILER_RELEASE%100),
1148       cc_namebuf),
1149      (strlen(__VERSION__) > 8)? "(gcc)" :
1150        (sprintf(cc_versbuf, "(gcc %s)", __VERSION__), cc_versbuf),
1151#  else
1152      "gcc ", __VERSION__,
1153#  endif
1154#else
1155#  if defined(CRAY) && defined(_RELEASE)
1156      "cc ", (sprintf(cc_versbuf, "version %d", _RELEASE), cc_versbuf),
1157#  else
1158#  ifdef __VERSION__
1159#   ifndef IZ_CC_NAME
1160#    define IZ_CC_NAME "cc "
1161#   endif
1162      IZ_CC_NAME, __VERSION__
1163#  else
1164#   ifndef IZ_CC_NAME
1165#    define IZ_CC_NAME "cc"
1166#   endif
1167      IZ_CC_NAME, "",
1168#  endif
1169#  endif
1170#endif /* ?__GNUC__ */
1171
1172#ifndef IZ_OS_NAME
1173#  define IZ_OS_NAME "Unix"
1174#endif
1175      IZ_OS_NAME,
1176
1177#if defined(sgi) || defined(__sgi)
1178      " (Silicon Graphics IRIX)",
1179#else
1180#ifdef sun
1181#  ifdef sparc
1182#    ifdef __SVR4
1183      " (Sun SPARC/Solaris)",
1184#    else /* may or may not be SunOS */
1185      " (Sun SPARC)",
1186#    endif
1187#  else
1188#  if defined(sun386) || defined(i386)
1189      " (Sun 386i)",
1190#  else
1191#  if defined(mc68020) || defined(__mc68020__)
1192      " (Sun 3)",
1193#  else /* mc68010 or mc68000:  Sun 2 or earlier */
1194      " (Sun 2)",
1195#  endif
1196#  endif
1197#  endif
1198#else
1199#ifdef __hpux
1200      " (HP/UX)",
1201#else
1202#ifdef __osf__
1203      " (DEC OSF/1)",
1204#else
1205#ifdef _AIX
1206      " (IBM AIX)",
1207#else
1208#ifdef aiws
1209      " (IBM RT/AIX)",
1210#else
1211#if defined(CRAY) || defined(cray)
1212#  ifdef _UNICOS
1213      (sprintf(os_namebuf, " (Cray UNICOS release %d)", _UNICOS), os_namebuf),
1214#  else
1215      " (Cray UNICOS)",
1216#  endif
1217#else
1218#if defined(uts) || defined(UTS)
1219      " (Amdahl UTS)",
1220#else
1221#ifdef NeXT
1222#  ifdef mc68000
1223      " (NeXTStep/black)",
1224#  else
1225      " (NeXTStep for Intel)",
1226#  endif
1227#else              /* the next dozen or so are somewhat order-dependent */
1228#ifdef LINUX
1229#  ifdef __ELF__
1230      " (Linux ELF)",
1231#  else
1232      " (Linux a.out)",
1233#  endif
1234#else
1235#ifdef MINIX
1236      " (Minix)",
1237#else
1238#ifdef M_UNIX
1239      " (SCO Unix)",
1240#else
1241#ifdef M_XENIX
1242      " (SCO Xenix)",
1243#else
1244#ifdef __NetBSD__
1245#  ifdef NetBSD0_8
1246      (sprintf(os_namebuf, " (NetBSD 0.8%c)", (char)(NetBSD0_8 - 1 + 'A')),
1247       os_namebuf),
1248#  else
1249#  ifdef NetBSD0_9
1250      (sprintf(os_namebuf, " (NetBSD 0.9%c)", (char)(NetBSD0_9 - 1 + 'A')),
1251       os_namebuf),
1252#  else
1253#  ifdef NetBSD1_0
1254      (sprintf(os_namebuf, " (NetBSD 1.0%c)", (char)(NetBSD1_0 - 1 + 'A')),
1255       os_namebuf),
1256#  else
1257      (BSD4_4 == 0.5)? " (NetBSD before 0.9)" : " (NetBSD 1.1 or later)",
1258#  endif
1259#  endif
1260#  endif
1261#else
1262#ifdef __FreeBSD__
1263      (BSD4_4 == 0.5)? " (FreeBSD 1.x)" : " (FreeBSD 2.0 or later)",
1264#else
1265#ifdef __bsdi__
1266      (BSD4_4 == 0.5)? " (BSD/386 1.0)" : " (BSD/386 1.1 or later)",
1267#else
1268#ifdef __386BSD__
1269      (BSD4_4 == 1)? " (386BSD, post-4.4 release)" : " (386BSD)",
1270#else
1271#if defined(i486) || defined(__i486) || defined(__i486__)
1272      " (Intel 486)",
1273#else
1274#if defined(i386) || defined(__i386) || defined(__i386__)
1275      " (Intel 386)",
1276#else
1277#ifdef pyr
1278      " (Pyramid)",
1279#else
1280#ifdef ultrix
1281#  ifdef mips
1282      " (DEC/MIPS)",
1283#  else
1284#  ifdef vax
1285      " (DEC/VAX)",
1286#  else /* __alpha? */
1287      " (DEC/Alpha)",
1288#  endif
1289#  endif
1290#else
1291#ifdef gould
1292      " (Gould)",
1293#else
1294#ifdef MTS
1295      " (MTS)",
1296#else
1297#ifdef __convexc__
1298      " (Convex)",
1299#else
1300      "",
1301#endif /* Convex */
1302#endif /* MTS */
1303#endif /* Gould */
1304#endif /* DEC */
1305#endif /* Pyramid */
1306#endif /* 386 */
1307#endif /* 486 */
1308#endif /* 386BSD */
1309#endif /* BSDI BSD/386 */
1310#endif /* NetBSD */
1311#endif /* FreeBSD */
1312#endif /* SCO Xenix */
1313#endif /* SCO Unix */
1314#endif /* Minix */
1315#endif /* Linux */
1316#endif /* NeXT */
1317#endif /* Amdahl */
1318#endif /* Cray */
1319#endif /* RT/AIX */
1320#endif /* AIX */
1321#endif /* OSF/1 */
1322#endif /* HP/UX */
1323#endif /* Sun */
1324#endif /* SGI */
1325
1326#ifdef __DATE__
1327      " on ", __DATE__
1328#else
1329      "", ""
1330#endif
1331    );
1332
1333    (*G.message)((zvoid *)&G, slide, (ulg)strlen((char *)slide), 0);
1334
1335} /* end function version() */
1336
1337#endif /* !SFX */
1338
1339
1340
1341
1342
1343/* ===================================================================
1344 * ZVS_CREATE()
1345 * Function to create a file with specified times.  The times should be sent
1346 * as long ints in DG time format; use -1 to set to the current times.  You
1347 * may also specify a pointer to the ACL, the file type (see PARU.H, and do
1348 * not specify dirs or links), the element size, and the max index level.
1349 * For all of these parameters you may use -1 to specify the default.
1350 *
1351 * Returns 0 if no error, or the error code returned by ?CREATE.
1352 *
1353 *    HISTORY:
1354 *        15-dec-93 dbl
1355 *        31-may-94 dbl: added call to convert pathname to AOS/VS
1356 *
1357 *
1358 */
1359
1360int zvs_create(ZCONST char *fname, long cretim, long modtim, long acctim,
1361               char *pacl, int ftyp, int eltsize, int maxindlev)
1362{
1363    P_CREATE    pcr_stru;
1364    P_CTIM      pct_stru;
1365
1366    pcr_stru.cftyp_format = 0;           /* unspecified record format */
1367    if (ftyp == -1)                      /* default file type to UNX */
1368        pcr_stru.cftyp_entry = $FUNX;
1369    else
1370        pcr_stru.cftyp_entry = ftyp;
1371    pcr_stru.ctim = &pct_stru;
1372    pcr_stru.cacp = pacl;
1373    pcr_stru.cdel = eltsize;
1374    pcr_stru.cmil = maxindlev;
1375
1376    pct_stru.tcth.long_time = cretim;
1377    pct_stru.tath.long_time = acctim;
1378    pct_stru.tmth.long_time = modtim;
1379
1380    return (sys_create(ux_to_vs_name(Vs_path, fname), &pcr_stru));
1381
1382} /* end zvs_create() */
1383
1384
1385
1386/* ===================================================================
1387 * ZVS_CREDIR()
1388 * Function to create a dir as specified.  The times should be sent
1389 * as long ints in DG time format; use -1 to set to the current times.  You
1390 * may also specify a pointer to the ACL, the file type (either $FDIR or $FCPD; see PARU.H),
1391 * the max # blocks (if a CPD), the hash frame size, and the max index level.
1392 * For all of these parameters (except for the CPD's maximum blocks),
1393 * you may use -1 to specify the default.
1394 *
1395 * (The System Call Dictionary says both that you may specify a
1396 * maximum-index-level value up to the maximum, with 0 for a contiguous
1397 * directory, and that 3 is always used for this whatever you specify.)
1398 *
1399 * If you specify anything other than CPD for the file type, DIR will
1400 * be used.
1401 *
1402 * Returns 0 if no error, or the error code returned by ?CREATE.
1403 *
1404 *    HISTORY:
1405 *        1-jun-94 dbl
1406 *
1407 *
1408 */
1409
1410int zvs_credir(ZCONST char *dname, long cretim, long modtim, long acctim,
1411               char *pacl, int ftyp, long maxblocks, int hashfsize,
1412               int maxindlev)
1413{
1414    P_CREATE_DIR    pcr_stru;
1415    P_CTIM          pct_stru;
1416
1417    if (ftyp != $FCPD)                      /* default file type to UNX */
1418        pcr_stru.cftyp_entry = $FDIR;
1419    else
1420    {
1421        pcr_stru.cftyp_entry = ftyp;
1422        pcr_stru.cmsh = maxblocks;
1423    }
1424
1425    pcr_stru.ctim = &pct_stru;
1426    pcr_stru.cacp = pacl;
1427    pcr_stru.chfs = hashfsize;
1428    pcr_stru.cmil = maxindlev;
1429
1430    pct_stru.tcth.long_time = cretim;
1431    pct_stru.tath.long_time = acctim;
1432    pct_stru.tmth.long_time = modtim;
1433
1434    return (sys_create(ux_to_vs_name(Vs_path, dname), &pcr_stru));
1435
1436} /* end zvs_credir() */
1437
1438
1439
1440/* ===================================================================
1441 * UX_TO_VS_NAME() - makes a somewhat dumb pass at converting a Unix
1442 *           filename to an AOS/VS filename.  This should
1443 *           be just about adequate to handle the results
1444 *           of similarly-simple AOS/VS-to-Unix conversions
1445 *           in the ZIP program.  It does not guarantee a
1446 *           legal AOS/VS filename for every Unix filename;
1447 *           conspicuous examples would be names with
1448 *           embedded ./ and ../ (which will receive no
1449 *           special treatment).
1450 *
1451 *       RETURNS: pointer to the result (which is an input parameter)
1452 *
1453 *       NOTE: calling code is responsible for making sure
1454 *           the output buffer is big enough!
1455 *
1456 *       HISTORY:
1457 *           31-may-94 dbl
1458 *
1459 */
1460char *ux_to_vs_name(char *outname, ZCONST char *inname)
1461{
1462    ZCONST char *ip=inname, *op=outname;
1463
1464
1465    if (ip[0] == '.') {
1466        if (ip[1] == '/') {
1467            *(op++) = '=';
1468            ip += 2;
1469        } else if (ip[1] == '.'  &&  ip[2] == '/') {
1470            *(op++) = '^';
1471            ip += 3;
1472        }
1473    }
1474
1475    do {
1476        if (*ip == '/')
1477            *(op++) = ':';
1478        else if (strchr(
1479           "0123456789_$?.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ",
1480           *ip) != NULL)
1481        {
1482            *(op++) = *ip;
1483        } else
1484            *(op++) = '?';
1485
1486    } while (*(ip++) != '\0');
1487
1488    return outname;
1489
1490} /* end ux_to_vs_name() */
1491
1492
1493
1494/* =================================================================== */
1495
1496/* DGDATE
1497   Two functions do encode/decode dates in DG system format.
1498
1499   Usage:
1500    long value,year,month,day;
1501
1502    value=dgdate(month,day,year);
1503    undgdate(value,&month,&day,&year);   [GRR:  not used in UnZip: removed]
1504
1505   Notes:
1506
1507   1. DG date functions only work on dates within the range
1508      Jan 1, 1968 through Dec 31, 2099.  I have tested these
1509      functions through the same range with exact agreement.
1510      For dates outside of that range, the DG system calls
1511      may return different values than these functions.
1512
1513   2. dgundate() accepts values between 0 and 48213 inclusive.
1514      These correspond to 12/31/1967 and 12/31/2099.
1515
1516   3. Both functions assume the data is in the native OS byte
1517      order.  So if you're reading or writing these fields from
1518      a file that has been passed between AOS/VS and PC-DOS you
1519      will need to swap byte order.
1520
1521   4. With reference to byte order, the entire range of values
1522      supported by these functions will fit into an unsigned
1523      short int.  In most cases the input or output will be
1524      in that variable type.  You are better off casting the
1525      value to/from unsigned short so you only need to concern
1526      yourself with swapping two bytes instead of four.
1527
1528  Written by: Stanley J. Gula
1529              US&T, Inc.
1530              529 Main Street, Suite 1
1531              Indian Orchard, MA 01151
1532              (413)-543-3672
1533              Copyright (c) 1990 US&T, Inc.
1534              All rights reserved.
1535
1536              I hereby release these functions into the public
1537              domain.  You may use these routines freely as long
1538              as the US&T copyright remains intact in the source
1539              code.
1540
1541              Stanley J. Gula     July 24, 1990
1542*/
1543
1544long motable[13]={0,31,59,90,120,151,181,212,243,273,304,334,365};
1545
1546long yrtable[132]={
1547      366,  731, 1096, 1461, 1827, 2192, 2557, 2922, 3288, 3653,
1548     4018, 4383, 4749, 5114, 5479, 5844, 6210, 6575, 6940, 7305,
1549     7671, 8036, 8401, 8766, 9132, 9497, 9862,10227,10593,10958,
1550    11323,11688,12054,12419,12784,13149,13515,13880,14245,14610,
1551    14976,15341,15706,16071,16437,16802,17167,17532,17898,18263,
1552    18628,18993,19359,19724,20089,20454,20820,21185,21550,21915,
1553    22281,22646,23011,23376,23742,24107,24472,24837,25203,25568,
1554    25933,26298,26664,27029,27394,27759,28125,28490,28855,29220,
1555    29586,29951,30316,30681,31047,31412,31777,32142,32508,32873,
1556    33238,33603,33969,34334,34699,35064,35430,35795,36160,36525,
1557    36891,37256,37621,37986,38352,38717,39082,39447,39813,40178,
1558    40543,40908,41274,41639,42004,42369,42735,43100,43465,43830,
1559    44196,44561,44926,45291,45657,46022,46387,46752,47118,47483,
1560    47848,48213};
1561
1562/* Given y,m,d return # of days since 12/31/67 */
1563long int dgdate(short mm, short dd, short yy)
1564{
1565    long int temp;
1566    short ytmp;
1567
1568    if (mm<1 || mm>12 || dd<1 || dd>31 || yy<1968 || yy>2099)
1569        return 0L;
1570
1571    /* Figure in whole years since 1968 + whole months plus days */
1572    temp=365L*(long)(yy-1968) + motable[mm-1] + (long)dd;
1573
1574    /* Adjust for leap years - note we don't account for skipped leap
1575       year in years divisible by 1000 but not by 4000.  We're correct
1576       through the year 2099 */
1577    temp+=(yy-1965)/4;
1578
1579    /* Correct for this year */
1580    /* In leap years, if date is 3/1 or later, bump */
1581    if ((yy%4==0) && (mm>2))
1582        temp++;
1583
1584    return temp;
1585}
1586