Subversion Repositories Kolibri OS

Rev

Blame | Last modification | View Log | Download | RSS feed

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