Subversion Repositories Kolibri OS

Rev

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