Subversion Repositories Kolibri OS

Rev

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