Subversion Repositories Kolibri OS

Rev

Rev 6727 | Rev 6764 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

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