Subversion Repositories Kolibri OS

Rev

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