Subversion Repositories Kolibri OS

Rev

Blame | Last modification | View Log | Download | RSS feed

  1. /*
  2.   Copyright (c) 1990-2007 Info-ZIP.  All rights reserved.
  3.  
  4.   See the accompanying file LICENSE, version 2000-Apr-09 or later
  5.   (the contents of which are also included in unzip.h) for terms of use.
  6.   If, for some reason, all these files are missing, the Info-ZIP license
  7.   also may be found at:  ftp://ftp.info-zip.org/pub/infozip/license.html
  8. */
  9. /*---------------------------------------------------------------------------
  10.  
  11.   human68k.c
  12.  
  13.   Human68k-specific routines for use with Info-ZIP's UnZip 5.41 and later.
  14.  
  15.   Contains:  do_wild()
  16.              mapattr()
  17.              mapname()
  18.              checkdir()
  19.              close_outfile()
  20.              stamp_file()                   (TIMESTAMP only)
  21.              version()
  22.              main()                         (for UnZipSFX)
  23.  
  24.   ---------------------------------------------------------------------------*/
  25.  
  26.  
  27. #include <dirent.h>
  28. #include <string.h>
  29. #include <sys/dos.h>
  30. #include <sys/xunistd.h>
  31. #ifdef HAVE_TWONCALL_H
  32. #include <twoncall.h>
  33. #endif
  34. #define UNZIP_INTERNAL
  35. #include "unzip.h"
  36.  
  37. #if defined (SFX) && defined (MAIN)
  38. #include <sys/xstart.h>
  39. int MAIN(int argc, char *argv[]);
  40. #endif
  41.  
  42. static void map2fat(char *pathcomp, char *last_dot);
  43. static char *trunc_name(char *name, int maxlen);
  44.  
  45. static int created_dir;        /* used in mapname(), checkdir() */
  46. static int renamed_fullpath;   /* ditto */
  47.  
  48. static char multi_period, special_char;
  49.  
  50. #ifndef SFX
  51.  
  52. /**********************/
  53. /* Function do_wild() */
  54. /**********************/
  55.  
  56. char *do_wild(__G__ wildspec)
  57.     __GDEF
  58.     ZCONST char *wildspec;  /* only used first time on a given dir */
  59. {
  60.     static DIR *wild_dir = (DIR *)NULL;
  61.     static ZCONST char *wildname;
  62.     static char *dirname, matchname[FILNAMSIZ];
  63.     static int notfirstcall=FALSE, have_dirname, dirnamelen;
  64.     struct dirent *file;
  65.  
  66.     /* Even when we're just returning wildspec, we *always* do so in
  67.      * matchname[]--calling routine is allowed to append four characters
  68.      * to the returned string, and wildspec may be a pointer to argv[].
  69.      */
  70.     if (!notfirstcall) {    /* first call:  must initialize everything */
  71.         notfirstcall = TRUE;
  72.  
  73.         if (!iswild(wildspec)) {
  74.             strncpy(matchname, wildspec, FILNAMSIZ);
  75.             matchname[FILNAMSIZ-1] = '\0';
  76.             have_dirname = FALSE;
  77.             wild_dir = (DIR *)NULL;
  78.             return matchname;
  79.         }
  80.  
  81.         /* break the wildspec into a directory part and a wildcard filename */
  82.         if ((wildname = strrchr(wildspec, '/')) == NULL) {
  83.             dirname = ".";
  84.             dirnamelen = 1;
  85.             have_dirname = FALSE;
  86.             wildname = wildspec;
  87.         } else {
  88.             ++wildname;     /* point at character after '/' */
  89.             dirnamelen = wildname - wildspec;
  90.             if ((dirname = (char *)malloc(dirnamelen+1)) == NULL) {
  91.                 Info(slide, 1, ((char *)slide,
  92.                   "warning:  cannot allocate wildcard buffers\n"));
  93.                 strcpy(matchname, wildspec);
  94.                 return matchname;   /* but maybe filespec was not a wildcard */
  95.             }
  96.             strncpy(dirname, wildspec, dirnamelen);
  97.             dirname[dirnamelen] = '\0';   /* terminate for strcpy below */
  98.             have_dirname = TRUE;
  99.         }
  100.         Trace((stderr, "do_wild:  dirname = [%s]\n", FnFilter1(dirname)));
  101.  
  102.         if ((wild_dir = opendir(dirname)) != (DIR *)NULL) {
  103.             while ((file = readdir(wild_dir)) != (struct dirent *)NULL) {
  104.                 Trace((stderr, "do_wild:  readdir returns %s\n",
  105.                   FnFilter1(file->d_name)));
  106.                 if (file->d_name[0] == '.' && wildname[0] != '.')
  107.                     continue; /* Unix:  '*' and '?' do not match leading dot */
  108.                 if (match(file->d_name, wildname, 0 WISEP) && /* 0=case sens.*/
  109.                     /* skip "." and ".." directory entries */
  110.                     strcmp(file->d_name, ".") && strcmp(file->d_name, "..")) {
  111.                     Trace((stderr, "do_wild:  match() succeeds\n"));
  112.                     if (have_dirname) {
  113.                         strcpy(matchname, dirname);
  114.                         strcpy(matchname+dirnamelen, file->d_name);
  115.                     } else
  116.                         strcpy(matchname, file->d_name);
  117.                     return matchname;
  118.                 }
  119.             }
  120.             /* if we get to here directory is exhausted, so close it */
  121.             closedir(wild_dir);
  122.             wild_dir = (DIR *)NULL;
  123.         }
  124. #ifdef DEBUG
  125.         else {
  126.             Trace((stderr, "do_wild:  Opendir(%s) returns NULL\n",
  127.               FnFilter1(dirname)));
  128.         }
  129. #endif /* DEBUG */
  130.  
  131.         /* return the raw wildspec in case that works (e.g., directory not
  132.          * searchable, but filespec was not wild and file is readable) */
  133.         strncpy(matchname, wildspec, FILNAMSIZ);
  134.         matchname[FILNAMSIZ-1] = '\0';
  135.         return matchname;
  136.     }
  137.  
  138.     /* last time through, might have failed opendir but returned raw wildspec */
  139.     if (wild_dir == (DIR *)NULL) {
  140.         notfirstcall = FALSE; /* nothing left to try--reset for new wildspec */
  141.         if (have_dirname)
  142.             free(dirname);
  143.         return (char *)NULL;
  144.     }
  145.  
  146.     /* If we've gotten this far, we've read and matched at least one entry
  147.      * successfully (in a previous call), so dirname has been copied into
  148.      * matchname already.
  149.      */
  150.     while ((file = readdir(wild_dir)) != (struct dirent *)NULL) {
  151.         Trace((stderr, "do_wild:  readdir returns %s\n",
  152.           FnFilter1(file->d_name)));
  153.         if (file->d_name[0] == '.' && wildname[0] != '.')
  154.             continue;   /* Unix:  '*' and '?' do not match leading dot */
  155.         if (match(file->d_name, wildname, 0 WISEP)) {   /* 0 == case sens. */
  156.             Trace((stderr, "do_wild:  match() succeeds\n"));
  157.             if (have_dirname) {
  158.                 /* strcpy(matchname, dirname); */
  159.                 strcpy(matchname+dirnamelen, file->d_name);
  160.             } else
  161.                 strcpy(matchname, file->d_name);
  162.             return matchname;
  163.         }
  164.     }
  165.  
  166.     closedir(wild_dir);     /* have read at least one entry; nothing left */
  167.     wild_dir = (DIR *)NULL;
  168.     notfirstcall = FALSE;   /* reset for new wildspec */
  169.     if (have_dirname)
  170.         free(dirname);
  171.     return (char *)NULL;
  172.  
  173. } /* end function do_wild() */
  174.  
  175. #endif /* !SFX */
  176.  
  177.  
  178.  
  179.  
  180. /**********************/
  181. /* Function mapattr() */
  182. /**********************/
  183.  
  184. int mapattr(__G)
  185.     __GDEF
  186. {
  187.     ulg  tmp = G.crec.external_file_attributes;
  188.  
  189.     switch (G.pInfo->hostnum) {
  190.         case UNIX_:
  191.             if (tmp & 0xff)
  192.                 break;
  193.             /* fall through */
  194.         case VMS_:
  195.         case ACORN_:
  196.         case ATARI_:
  197.         case ATHEOS_:
  198.         case BEOS_:
  199.         case QDOS_:
  200.             G.pInfo->file_attr = _mode2dos(tmp >> 16);
  201.             return 0;
  202.         default:
  203.             break;
  204.     }
  205.  
  206.     /* set archive bit (file is not backed up) */
  207.     if((tmp & 0x08) == 0)
  208.         tmp |= 0x20;
  209.     G.pInfo->file_attr = tmp & 0xff;
  210.     return 0;
  211.  
  212. } /* end function mapattr() */
  213.  
  214.  
  215.  
  216.  
  217.  
  218. /**********************/
  219. /* Function mapname() */
  220. /**********************/
  221.  
  222. int mapname(__G__ renamed)
  223.     __GDEF
  224.     int renamed;
  225. /*
  226.  * returns:
  227.  *  MPN_OK          - no problem detected
  228.  *  MPN_INF_TRUNC   - caution (truncated filename)
  229.  *  MPN_INF_SKIP    - info "skip entry" (dir doesn't exist)
  230.  *  MPN_ERR_SKIP    - error -> skip entry
  231.  *  MPN_ERR_TOOLONG - error -> path is too long
  232.  *  MPN_NOMEM       - error (memory allocation failed) -> skip entry
  233.  *  [also MPN_VOL_LABEL, MPN_CREATED_DIR]
  234.  */
  235. {
  236.     char pathcomp[FILNAMSIZ];      /* path-component buffer */
  237.     char *pp, *cp=(char *)NULL;    /* character pointers */
  238.     char *lastsemi=(char *)NULL;   /* pointer to last semi-colon in pathcomp */
  239.     char *last_dot=(char *)NULL;   /* last dot */
  240.     int error = MPN_OK;
  241.     register unsigned workch;      /* hold the character being tested */
  242.  
  243. #ifdef HAVE_TWONCALL_H
  244.     static char twentyone_flag;
  245.  
  246.     /* Get TwentyOne options */
  247.     if (twentyone_flag == 0) {
  248.         twentyone_flag++;
  249.         if (GetTwentyOneID () == TWON_ID) {
  250.             int flags = GetTwentyOneOptions ();
  251.  
  252.             if (flags & (1 << TWON_PERIOD_BIT))
  253.                 multi_period = TRUE;
  254.             if (flags & (1 << TWON_SPECIAL_BIT))
  255.                 special_char = TRUE;
  256.         }
  257.     }
  258. #endif
  259.  
  260. /*---------------------------------------------------------------------------
  261.     Initialize various pointers and counters and stuff.
  262.   ---------------------------------------------------------------------------*/
  263.  
  264.     /* can create path as long as not just freshening, or if user told us */
  265.     G.create_dirs = (!uO.fflag || renamed);
  266.  
  267.     created_dir = FALSE;        /* not yet */
  268.     renamed_fullpath = FALSE;
  269.  
  270.     if (renamed) {
  271.         cp = G.filename - 1;    /* point to beginning of renamed name... */
  272.         while (*++cp)
  273.             if (*cp == '\\')    /* convert backslashes to forward */
  274.                 *cp = '/';
  275.         cp = G.filename;
  276.         if ((G.filename[0] == '/')
  277.          || (isalpha(G.filename[0]) && G.filename[1] == ':')) {
  278.             /* user gave full pathname:  don't prepend rootpath */
  279.             renamed_fullpath = TRUE;
  280.         }
  281.     }
  282.  
  283.     if ((error = checkdir(__G__ (char *)NULL, INIT)) != 0)
  284.         return error;           /* initialize path buffer, unless no memory */
  285.  
  286.     *pathcomp = '\0';           /* initialize translation buffer */
  287.     pp = pathcomp;              /* point to translation buffer */
  288.     if (uO.jflag)               /* junking directories */
  289.         cp = (char *)strrchr(G.filename, '/');
  290.     if (cp == (char *)NULL)     /* no '/' or not junking dirs */
  291.         cp = G.filename;        /* point to internal zipfile-member pathname */
  292.     else
  293.         ++cp;                   /* point to start of last component of path */
  294.  
  295. /*---------------------------------------------------------------------------
  296.     Begin main loop through characters in filename.
  297.   ---------------------------------------------------------------------------*/
  298.  
  299.     while ((workch = (uch)*cp++) != 0) {
  300.  
  301.         if (_ismbblead((unsigned char)workch)) {
  302.             if (*cp) {
  303.                 *pp++ = (char)workch;
  304.                 *pp++ = (char)*cp++;
  305.             }
  306.             else
  307.                 *pp++ = '_';
  308.             continue;
  309.         }
  310.  
  311.         switch (workch) {
  312.             case '/':             /* can assume -j flag not given */
  313.                 *pp = '\0';
  314.                 map2fat(pathcomp, last_dot);   /* 18.3 trunc. (in place) */
  315.                 if (strcmp(pathcomp, ".") == 0) {
  316.                     /* don't bother appending "./" to the path */
  317.                     *pathcomp = '\0';
  318.                 } else if (!uO.ddotflag && strcmp(pathcomp, "..") == 0) {
  319.                     /* "../" dir traversal detected, skip over it */
  320.                     *pathcomp = '\0';
  321.                     killed_ddot = TRUE;     /* set "show message" flag */
  322.                 }
  323.                 /* when path component is not empty, append it now */
  324.                 if (*pathcomp != '\0' &&
  325.                     ((error = checkdir(__G__ pathcomp, APPEND_DIR))
  326.                      & MPN_MASK) > MPN_INF_TRUNC)
  327.                     return error;
  328.                 pp = pathcomp;    /* reset conversion buffer for next piece */
  329.                 lastsemi = (char *)NULL; /* leave direct. semi-colons alone */
  330.                 break;
  331.  
  332.             /* drive names are not stored in zipfile, so no colons allowed;
  333.              *  no brackets or most other punctuation either (all of which
  334.              *  can appear in Unix-created archives; backslash is particularly
  335.              *  bad unless all necessary directories exist) */
  336.  
  337.             case '[':          /* these punctuation characters forbidden */
  338.             case ']':          /*  only on plain FAT file systems */
  339.             case '+':
  340.             case ',':
  341.             case '=':
  342.             case '<':
  343.             case '>':
  344.             case '|':
  345.             case '\"':
  346.             case '\'':
  347.                 if (!special_char)
  348.                     workch = '_';
  349.                 *pp++ = (char)workch;
  350.                 break;
  351.  
  352.             case '-':
  353.                 if (pp == pathcomp && !special_char)
  354.                     workch = '_';
  355.                 *pp++ = (char)workch;
  356.                 break;
  357.  
  358.             case ':':
  359.             case '\\':
  360.             case '*':
  361.             case '?':
  362.                 *pp++ = '_';
  363.                 break;
  364.  
  365.             case ';':             /* VMS version (or DEC-20 attrib?) */
  366.                 lastsemi = pp;
  367.                 if (!special_char)
  368.                     workch = '_';
  369.                 *pp++ = (char)workch;  /* keep for now; remove VMS ";##" */
  370.                 break;                 /*  later, if requested */
  371.  
  372.             case ' ':                      /* change spaces to underscores */
  373. #if 0  /* do it always */
  374.                 if (uO.sflag)              /*  only if requested */
  375. #endif
  376.                     workch = '_';
  377.                 *pp++ = (char)workch;
  378.                 break;
  379.  
  380.             default:
  381.                 /* allow European characters in filenames: */
  382.                 if (isprint(workch) || workch >= 128)
  383.                     *pp++ = (char)workch;
  384.  
  385.         } /* end switch */
  386.     } /* end while loop */
  387.  
  388.     /* Show warning when stripping insecure "parent dir" path components */
  389.     if (killed_ddot && QCOND2) {
  390.         Info(slide, 0, ((char *)slide,
  391.           "warning:  skipped \"../\" path component(s) in %s\n",
  392.           FnFilter1(G.filename)));
  393.         if (!(error & ~MPN_MASK))
  394.             error = (error & MPN_MASK) | PK_WARN;
  395.     }
  396.  
  397. /*---------------------------------------------------------------------------
  398.     Report if directory was created (and no file to create:  filename ended
  399.     in '/'), check name to be sure it exists, and combine path and name be-
  400.     fore exiting.
  401.   ---------------------------------------------------------------------------*/
  402.  
  403.     if (G.filename[strlen(G.filename) - 1] == '/') {
  404.         checkdir(__G__ G.filename, GETPATH);
  405.         if (created_dir) {
  406.             if (QCOND2) {
  407.                 Info(slide, 0, ((char *)slide, "   creating: %s\n",
  408.                   FnFilter1(G.filename)));
  409.             }
  410.             /* set dir time (note trailing '/') */
  411.             return (error & ~MPN_MASK) | MPN_CREATED_DIR;
  412.         }
  413.         /* dir existed already; don't look for data to extract */
  414.         return (error & ~MPN_MASK) | MPN_INF_SKIP;
  415.     }
  416.  
  417.     *pp = '\0';                   /* done with pathcomp:  terminate it */
  418.  
  419.     /* if not saving them, remove VMS version numbers (appended ";###") */
  420.     if (!uO.V_flag && lastsemi) {
  421.         pp = lastsemi + 1;
  422.         while (isdigit((uch)(*pp)))
  423.             ++pp;
  424.         if (*pp == '\0')          /* only digits between ';' and end:  nuke */
  425.             *lastsemi = '\0';
  426.     }
  427.  
  428.     map2fat(pathcomp, last_dot);  /* 18.3 truncation (in place) */
  429.  
  430.     if (*pathcomp == '\0') {
  431.         Info(slide, 1, ((char *)slide, "mapname:  conversion of %s failed\n",
  432.           FnFilter1(G.filename)));
  433.         return (error & ~MPN_MASK) | MPN_ERR_SKIP;
  434.     }
  435.  
  436.     checkdir(__G__ pathcomp, APPEND_NAME);  /* returns 1 if truncated: care? */
  437.     checkdir(__G__ G.filename, GETPATH);
  438.  
  439.     if (G.pInfo->vollabel) {    /* set the volume label now */
  440.         int fd;
  441.  
  442.         if (QCOND2)
  443.             Info(slide, 0, ((char *)slide, "  labelling: %s\n",
  444.               FnFilter1(G.filename)));
  445.         if ((fd = _dos_newfile(G.filename, G.pInfo->file_attr)) < 0) {
  446.             Info(slide, 1, ((char *)slide,
  447.               "mapname:  error setting volume label\n"));
  448.             return (error & ~MPN_MASK) | MPN_ERR_SKIP;
  449.         }
  450.         _dos_close(fd);
  451.         /* success:  skip the "extraction" quietly */
  452.         return (error & ~MPN_MASK) | MPN_INF_SKIP;
  453.     }
  454.  
  455.     return error;
  456.  
  457. } /* end function mapname() */
  458.  
  459.  
  460.  
  461.  
  462. /**********************/
  463. /* Function map2fat() */
  464. /**********************/
  465.  
  466. static void map2fat(pathcomp, last_dot)
  467.     char *pathcomp, *last_dot;
  468. {
  469.     char *np;
  470.  
  471.     if (pathcomp == last_dot) {         /* dotfile(e.g. ".foo") */
  472.         pathcomp = last_dot;
  473.         last_dot = (char *)NULL;
  474.     }
  475.  
  476.     if (multi_period) {
  477.         if (strlen(pathcomp) <= 18)
  478.             return;
  479.     }
  480.     else {
  481.         char *p;
  482.  
  483.         for (p = pathcomp; *p; p++)
  484.             if (*p == (char)'.' && p != last_dot)
  485.                 *p = '_';
  486.     }
  487.  
  488.     if (last_dot) {
  489.         *last_dot++ = '\0';
  490.         trunc_name(last_dot, 3);
  491.     }
  492.     np = trunc_name(pathcomp, 18);
  493.     if (last_dot) {
  494.         *--last_dot = '.';
  495.         if (np)
  496.             strcpy(np, last_dot);
  497.     }
  498.  
  499. } /* end function map2fat() */
  500.  
  501. static char *trunc_name(char *name, int maxlen)
  502. {
  503.  
  504.     if (strlen(name) <= maxlen)
  505.         return (char *)NULL;
  506.  
  507.     do {
  508.         if (_ismbblead((unsigned char)*name)) {
  509.             if (--maxlen == 0)
  510.                 break;
  511.             name++;
  512.         }
  513.         name++;
  514.         maxlen--;
  515.     } while (maxlen > 0);
  516.     *name = '\0';
  517.  
  518.     return name;
  519. }
  520.  
  521.  
  522.  
  523.  
  524. /***********************/
  525. /* Function checkdir() */
  526. /***********************/
  527.  
  528. int checkdir(__G__ pathcomp, flag)
  529.     __GDEF
  530.     char *pathcomp;
  531.     int flag;
  532. /*
  533.  * returns:
  534.  *  MPN_OK          - no problem detected
  535.  *  MPN_INF_TRUNC   - (on APPEND_NAME) truncated filename
  536.  *  MPN_INF_SKIP    - path doesn't exist, not allowed to create
  537.  *  MPN_ERR_SKIP    - path doesn't exist, tried to create and failed; or path
  538.  *                    exists and is not a directory, but is supposed to be
  539.  *  MPN_ERR_TOOLONG - path is too long
  540.  *  MPN_NOMEM       - can't allocate memory for filename buffers
  541.  */
  542. {
  543.     static int rootlen = 0;   /* length of rootpath */
  544.     static char *rootpath;    /* user's "extract-to" directory */
  545.     static char *buildpath;   /* full path (so far) to extracted file */
  546.     static char *end;         /* pointer to end of buildpath ('\0') */
  547.  
  548. #   define FN_MASK   7
  549. #   define FUNCTION  (flag & FN_MASK)
  550.  
  551.  
  552. /*---------------------------------------------------------------------------
  553.     APPEND_DIR:  append the path component to the path being built and check
  554.     for its existence.  If doesn't exist and we are creating directories, do
  555.     so for this one; else signal success or error as appropriate.
  556.   ---------------------------------------------------------------------------*/
  557.  
  558.     if (FUNCTION == APPEND_DIR) {
  559.         int too_long = FALSE;
  560.  
  561.         Trace((stderr, "appending dir segment [%s]\n", FnFilter1(pathcomp)));
  562.         while ((*end = *pathcomp++) != '\0')
  563.             ++end;
  564.  
  565.         /* GRR:  could do better check, see if overrunning buffer as we go:
  566.          * check end-buildpath after each append, set warning variable if
  567.          * within 20 of FILNAMSIZ; then if var set, do careful check when
  568.          * appending.  Clear variable when begin new path. */
  569.  
  570.         if ((end-buildpath) > FILNAMSIZ-3)  /* need '/', one-char name, '\0' */
  571.             too_long = TRUE;                /* check if extracting directory? */
  572.         if (SSTAT(buildpath, &G.statbuf))   /* path doesn't exist */
  573.         {
  574.             if (!G.create_dirs) { /* told not to create (freshening) */
  575.                 free(buildpath);
  576.                 return MPN_INF_SKIP;    /* path doesn't exist: nothing to do */
  577.             }
  578.             if (too_long) {
  579.                 Info(slide, 1, ((char *)slide,
  580.                   "checkdir error:  path too long: %s\n",
  581.                   FnFilter1(buildpath)));
  582.                 free(buildpath);
  583.                 /* no room for filenames:  fatal */
  584.                 return MPN_ERR_TOOLONG;
  585.             }
  586.             if (mkdir(buildpath, 0777) == -1) {   /* create the directory */
  587.                 Info(slide, 1, ((char *)slide,
  588.                   "checkdir error:  cannot create %s\n\
  589.                 unable to process %s.\n",
  590.                   FnFilter2(buildpath), FnFilter1(G.filename)));
  591.                 free(buildpath);
  592.                 /* path didn't exist, tried to create, failed */
  593.                 return MPN_ERR_SKIP;
  594.             }
  595.             created_dir = TRUE;
  596.         } else if (!S_ISDIR(G.statbuf.st_mode)) {
  597.             Info(slide, 1, ((char *)slide,
  598.               "checkdir error:  %s exists but is not directory\n\
  599.                 unable to process %s.\n",
  600.               FnFilter2(buildpath), FnFilter1(G.filename)));
  601.             free(buildpath);
  602.             /* path existed but wasn't dir */
  603.             return MPN_ERR_SKIP;
  604.         }
  605.         if (too_long) {
  606.             Info(slide, 1, ((char *)slide,
  607.               "checkdir error:  path too long: %s\n", FnFilter1(buildpath)));
  608.             free(buildpath);
  609.             /* no room for filenames:  fatal */
  610.             return MPN_ERR_TOOLONG;
  611.         }
  612.         *end++ = '/';
  613.         *end = '\0';
  614.         Trace((stderr, "buildpath now = [%s]\n", FnFilter1(buildpath)));
  615.         return MPN_OK;
  616.  
  617.     } /* end if (FUNCTION == APPEND_DIR) */
  618.  
  619. /*---------------------------------------------------------------------------
  620.     GETPATH:  copy full path to the string pointed at by pathcomp, and free
  621.     buildpath.
  622.   ---------------------------------------------------------------------------*/
  623.  
  624.     if (FUNCTION == GETPATH) {
  625.         strcpy(pathcomp, buildpath);
  626.         Trace((stderr, "getting and freeing path [%s]\n",
  627.           FnFilter1(pathcomp)));
  628.         free(buildpath);
  629.         buildpath = end = (char *)NULL;
  630.         return MPN_OK;
  631.     }
  632.  
  633. /*---------------------------------------------------------------------------
  634.     APPEND_NAME:  assume the path component is the filename; append it and
  635.     return without checking for existence.
  636.   ---------------------------------------------------------------------------*/
  637.  
  638.     if (FUNCTION == APPEND_NAME) {
  639.  
  640.         Trace((stderr, "appending filename [%s]\n", FnFilter1(pathcomp)));
  641.         while ((*end = *pathcomp++) != '\0') {
  642.             ++end;
  643.             if ((end-buildpath) >= FILNAMSIZ) {
  644.                 *--end = '\0';
  645.                 Info(slide, 1, ((char *)slide,
  646.                   "checkdir warning:  path too long; truncating\n\
  647.                   %s\n                -> %s\n",
  648.                   FnFilter1(G.filename), FnFilter2(buildpath)));
  649.                 return MPN_INF_TRUNC;   /* filename truncated */
  650.             }
  651.         }
  652.         Trace((stderr, "buildpath now = [%s]\n", FnFilter1(buildpath)));
  653.         /* could check for existence here, prompt for new name... */
  654.         return MPN_OK;
  655.     }
  656.  
  657. /*---------------------------------------------------------------------------
  658.     INIT:  allocate and initialize buffer space for the file currently being
  659.     extracted.  If file was renamed with an absolute path, don't prepend the
  660.     extract-to path.
  661.   ---------------------------------------------------------------------------*/
  662.  
  663.     if (FUNCTION == INIT) {
  664.         Trace((stderr, "initializing buildpath to "));
  665.         /* allocate space for full filename, root path, and maybe "./" */
  666.         if ((buildpath = (char *)malloc(strlen(G.filename)+rootlen+3)) ==
  667.             (char *)NULL)
  668.             return MPN_NOMEM;
  669.         if ((rootlen > 0) && !renamed_fullpath) {
  670.             strcpy(buildpath, rootpath);
  671.             end = buildpath + rootlen;
  672.         } else {
  673.             *buildpath = '\0';
  674.             end = buildpath;
  675.         }
  676.         Trace((stderr, "[%s]\n", FnFilter1(buildpath)));
  677.         return MPN_OK;
  678.     }
  679.  
  680. /*---------------------------------------------------------------------------
  681.     ROOT:  if appropriate, store the path in rootpath and create it if neces-
  682.     sary; else assume it's a zipfile member and return.  This path segment
  683.     gets used in extracting all members from every zipfile specified on the
  684.     command line.
  685.   ---------------------------------------------------------------------------*/
  686.  
  687. #if (!defined(SFX) || defined(SFX_EXDIR))
  688.     if (FUNCTION == ROOT) {
  689.         Trace((stderr, "initializing root path to [%s]\n",
  690.           FnFilter1(pathcomp)));
  691.         if (pathcomp == (char *)NULL) {
  692.             rootlen = 0;
  693.             return MPN_OK;
  694.         }
  695.         if (rootlen > 0)        /* rootpath was already set, nothing to do */
  696.             return MPN_OK;
  697.         if ((rootlen = strlen(pathcomp)) > 0) {
  698.             int had_trailing_pathsep=FALSE, has_drive=FALSE, add_dot=FALSE;
  699.             char *tmproot;
  700.  
  701.             if ((tmproot = (char *)malloc(rootlen+3)) == (char *)NULL) {
  702.                 rootlen = 0;
  703.                 return MPN_NOMEM;
  704.             }
  705.             strcpy(tmproot, pathcomp);
  706.             if (isalpha((uch)tmproot[0]) && tmproot[1] == ':')
  707.                 has_drive = TRUE;   /* drive designator */
  708.             if (tmproot[rootlen-1] == '/' || tmproot[rootlen-1] == '\\') {
  709.                 tmproot[--rootlen] = '\0';
  710.                 had_trailing_pathsep = TRUE;
  711.             }
  712.             if (has_drive && (rootlen == 2)) {
  713.                 if (!had_trailing_pathsep)   /* i.e., original wasn't "x:/" */
  714.                     add_dot = TRUE;    /* relative path: add '.' before '/' */
  715.             } else if (rootlen > 0 && (SSTAT(tmproot, &G.statbuf) ||
  716.                        !S_ISDIR(G.statbuf.st_mode))) /* path does not exist */
  717.             {
  718.                 if (!G.create_dirs /* || iswild(tmproot) */ ) {
  719.                     free(tmproot);
  720.                     rootlen = 0;
  721.                     /* skip (or treat as stored file) */
  722.                     return MPN_INF_SKIP;
  723.                 }
  724.                 /* create the directory (could add loop here scanning tmproot
  725.                  * to create more than one level, but why really necessary?) */
  726.                 if (mkdir(tmproot, 0777) == -1) {
  727.                     Info(slide, 1, ((char *)slide,
  728.                       "checkdir:  cannot create extraction directory: %s\n",
  729.                       FnFilter1(tmproot)));
  730.                     free(tmproot);
  731.                     rootlen = 0;
  732.                     /* path didn't exist, tried to create, and failed: */
  733.                     /* file exists, or 2+ subdir levels required */
  734.                     return MPN_ERR_SKIP;
  735.                 }
  736.             }
  737.             if (add_dot)                    /* had just "x:", make "x:." */
  738.                 tmproot[rootlen++] = '.';
  739.             tmproot[rootlen++] = '/';
  740.             tmproot[rootlen] = '\0';
  741.             if ((rootpath = (char *)realloc(tmproot, rootlen+1)) == NULL) {
  742.                 free(tmproot);
  743.                 rootlen = 0;
  744.                 return MPN_NOMEM;
  745.             }
  746.             Trace((stderr, "rootpath now = [%s]\n", FnFilter1(rootpath)));
  747.         }
  748.         return MPN_OK;
  749.     }
  750. #endif /* !SFX || SFX_EXDIR */
  751.  
  752. /*---------------------------------------------------------------------------
  753.     END:  free rootpath, immediately prior to program exit.
  754.   ---------------------------------------------------------------------------*/
  755.  
  756.     if (FUNCTION == END) {
  757.         Trace((stderr, "freeing rootpath\n"));
  758.         if (rootlen > 0) {
  759.             free(rootpath);
  760.             rootlen = 0;
  761.         }
  762.         return MPN_OK;
  763.     }
  764.  
  765.     return MPN_INVALID; /* should never reach */
  766.  
  767. } /* end function checkdir() */
  768.  
  769.  
  770.  
  771.  
  772. #if (defined(USE_EF_UT_TIME) || defined(TIMESTAMP))
  773. /* The following DOS date/time structure is machine-dependent as it
  774.  * assumes "little-endian" byte order.  For MSDOS-specific code, which
  775.  * is run on ix86 CPUs (or emulators), this assumption is valid; but
  776.  * care should be taken when using this code as template for other ports.
  777.  */
  778. typedef union {
  779.     ulg z_dostime;
  780.     struct {                    /* date and time words */
  781.         ush ztime;              /* DOS file modification time word */
  782.         ush zdate;              /* DOS file modification date word */
  783.     } zft;
  784.     struct {                    /* DOS date/time components bitfield */
  785.         unsigned zt_se : 5;
  786.         unsigned zt_mi : 6;
  787.         unsigned zt_hr : 5;
  788.         unsigned zd_dy : 5;
  789.         unsigned zd_mo : 4;
  790.         unsigned zd_yr : 7;
  791.     } z_dtf;
  792. } dos_fdatetime;
  793. #endif /* USE_EF_UT_TIME || TIMESTAMP */
  794.  
  795.  
  796. /****************************/
  797. /* Function close_outfile() */
  798. /****************************/
  799.  
  800. void close_outfile(__G)
  801.     __GDEF
  802. {
  803.     /* skip restoring time stamps on user's request */
  804.     if (uO.D_flag <= 1) {
  805. #ifdef USE_EF_UT_TIME
  806.         dos_fdatetime dos_dt;
  807.         iztimes z_utime;
  808.         struct tm *t;
  809. #endif /* USE_EF_UT_TIME */
  810.  
  811.  
  812. #ifdef USE_EF_UT_TIME
  813.         if (G.extra_field &&
  814. #ifdef IZ_CHECK_TZ
  815.             G.tz_is_valid &&
  816. #endif
  817.             (ef_scan_for_izux(G.extra_field, G.lrec.extra_field_length, 0,
  818.                               G.lrec.last_mod_dos_datetime, &z_utime, NULL)
  819.              & EB_UT_FL_MTIME))
  820.         {
  821.             TTrace((stderr, "close_outfile:  Unix e.f. modif. time = %ld\n",
  822.               z_utime.mtime));
  823.             /* round up (down if "up" overflows) to even seconds */
  824.             if (z_utime.mtime & 1)
  825.                 z_utime.mtime = (z_utime.mtime + 1 > z_utime.mtime) ?
  826.                                  z_utime.mtime + 1 : z_utime.mtime - 1;
  827.             TIMET_TO_NATIVE(z_utime.mtime) /* NOP unless MSC 7 or Macintosh */
  828.             t = localtime(&(z_utime.mtime));
  829.         } else
  830.             t = (struct tm *)NULL;
  831.         if (t != (struct tm *)NULL) {
  832.             if (t->tm_year < 80) {
  833.                 dos_dt.z_dtf.zt_se = 0;
  834.                 dos_dt.z_dtf.zt_mi = 0;
  835.                 dos_dt.z_dtf.zt_hr = 0;
  836.                 dos_dt.z_dtf.zd_dy = 1;
  837.                 dos_dt.z_dtf.zd_mo = 1;
  838.                 dos_dt.z_dtf.zd_yr = 0;
  839.             } else {
  840.                 dos_dt.z_dtf.zt_se = t->tm_sec >> 1;
  841.                 dos_dt.z_dtf.zt_mi = t->tm_min;
  842.                 dos_dt.z_dtf.zt_hr = t->tm_hour;
  843.                 dos_dt.z_dtf.zd_dy = t->tm_mday;
  844.                 dos_dt.z_dtf.zd_mo = t->tm_mon + 1;
  845.                 dos_dt.z_dtf.zd_yr = t->tm_year - 80;
  846.             }
  847.         } else {
  848.             dos_dt.z_dostime = G.lrec.last_mod_dos_datetime;
  849.         }
  850.         _dos_filedate(fileno(G.outfile), dos_dt.z_dostime);
  851. #else /* !USE_EF_UT_TIME */
  852.         _dos_filedate(fileno(G.outfile), G.lrec.last_mod_dos_datetime);
  853. #endif /* ?USE_EF_UT_TIME */
  854.     }
  855.  
  856.     fclose(G.outfile);
  857.  
  858.     _dos_chmod(G.filename, G.pInfo->file_attr);
  859.  
  860. } /* end function close_outfile() */
  861.  
  862.  
  863.  
  864.  
  865.  
  866. #ifdef TIMESTAMP
  867.  
  868. /*************************/
  869. /* Function stamp_file() */
  870. /*************************/
  871.  
  872. int stamp_file(fname, modtime)
  873.     ZCONST char *fname;
  874.     time_t modtime;
  875. {
  876.     dos_fdatetime dos_dt;
  877.     time_t t_even;
  878.     struct tm *t;
  879.     int fd;                             /* file handle */
  880.  
  881.     /* round up (down if "up" overflows) to even seconds */
  882.     t_even = ((modtime + 1 > modtime) ? modtime + 1 : modtime) & (~1);
  883.     TIMET_TO_NATIVE(t_even)             /* NOP unless MSC 7.0 or Macintosh */
  884.     t = localtime(&t_even);
  885.     if (t == (struct tm *)NULL)
  886.         return -1;                      /* time conversion error */
  887.     if (t->tm_year < 80) {
  888.         dos_dt.z_dtf.zt_se = 0;
  889.         dos_dt.z_dtf.zt_mi = 0;
  890.         dos_dt.z_dtf.zt_hr = 0;
  891.         dos_dt.z_dtf.zd_dy = 1;
  892.         dos_dt.z_dtf.zd_mo = 1;
  893.         dos_dt.z_dtf.zd_yr = 0;
  894.     } else {
  895.         dos_dt.z_dtf.zt_se = t->tm_sec >> 1;
  896.         dos_dt.z_dtf.zt_mi = t->tm_min;
  897.         dos_dt.z_dtf.zt_hr = t->tm_hour;
  898.         dos_dt.z_dtf.zd_dy = t->tm_mday;
  899.         dos_dt.z_dtf.zd_mo = t->tm_mon + 1;
  900.         dos_dt.z_dtf.zd_yr = t->tm_year - 80;
  901.     }
  902.     if (((fd = open((char *)fname, 0)) == -1) ||
  903.         (_dos_filedate(fd, dos_dt.z_dostime)))
  904.     {
  905.         if (fd != -1)
  906.             close(fd);
  907.         return -1;
  908.     }
  909.     close(fd);
  910.     return 0;
  911.  
  912. } /* end function stamp_file() */
  913.  
  914. #endif /* TIMESTAMP */
  915.  
  916.  
  917.  
  918.  
  919. #ifndef SFX
  920.  
  921. /************************/
  922. /*  Function version()  */
  923. /************************/
  924.  
  925. void version(__G)
  926.     __GDEF
  927. {
  928.     int len;
  929. #if 0
  930.     char buf[40];
  931. #endif
  932.  
  933.     len = sprintf((char *)slide, LoadFarString(CompiledWith),
  934.  
  935. #ifdef __GNUC__
  936.       "gcc ", __VERSION__,
  937. #else
  938. #  if 0
  939.       "cc ", (sprintf(buf, " version %d", _RELEASE), buf),
  940. #  else
  941.       "unknown compiler", "",
  942. #  endif
  943. #endif
  944.  
  945.       "Human68k",
  946. #ifdef __MC68020__
  947.       " (X68030)",
  948. #else
  949.       " (X680x0)",
  950. #endif
  951.  
  952. #ifdef __DATE__
  953.       " on ", __DATE__
  954. #else
  955.       "", ""
  956. #endif
  957.       );
  958.  
  959.     (*G.message)((zvoid *)&G, slide, (ulg)len, 0);
  960.  
  961. } /* end function version() */
  962.  
  963. #endif /* !SFX */
  964.  
  965.  
  966. #if defined (SFX) && defined (MAIN)
  967. int main(int argc, char *argv[])
  968. {
  969.     char argv0[92];
  970.  
  971.     /* make true argv[0] (startup routine makes it inaccuracy) */
  972.     argv[0] = strcat (strcpy (argv0, _procp->exe_path), _procp->exe_name);
  973.  
  974.     return MAIN(argc, argv);
  975. }
  976. #endif /* SFX && MAIN */
  977.