Subversion Repositories Kolibri OS

Rev

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