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.   flexos.c
  12.  
  13.   FlexOS-specific routines for use with Info-ZIP's UnZip 5.2 and later.
  14.  
  15.   Based upon the MSDOS version of this file (msdos/msdos.c)
  16.  
  17.   Contains:  do_wild()
  18.              mapattr()
  19.              mapname()
  20.              map2fat()
  21.              checkdir()
  22.              close_outfile()
  23.              dateformat()
  24.              version()
  25.              _wildarg()
  26.  
  27.   ---------------------------------------------------------------------------*/
  28.  
  29.  
  30.  
  31. #define UNZIP_INTERNAL
  32. #include "unzip.h"
  33.  
  34. #include <flexif.h>
  35.  
  36. /* The following should really be a static declaration,  but the compiler
  37.    complains (crappy compiler can't cope with a static forward declaration).
  38.  */
  39. extern void map2fat OF((char *pathcomp, char *last_dot));
  40.  
  41. static int created_dir;        /* used by mapname(), checkdir() */
  42. static int renamed_fullpath;   /* ditto */
  43.  
  44. /*****************************/
  45. /*  Strings used in flexos.c  */
  46. /*****************************/
  47.  
  48. #ifndef SFX
  49.   static ZCONST char Far CantAllocateWildcard[] =
  50.     "warning:  cannot allocate wildcard buffers\n";
  51. #endif
  52. static ZCONST char Far WarnDirTraversSkip[] =
  53.   "warning:  skipped \"../\" path component(s) in %s\n";
  54. static ZCONST char Far Creating[] = "   creating: %s\n";
  55. static ZCONST char Far ConversionFailed[] =
  56.   "mapname:  conversion of %s failed\n";
  57. static ZCONST char Far PathTooLong[] = "checkdir error:  path too long: %s\n";
  58. static ZCONST char Far CantCreateDir[] = "checkdir error:  cannot create %s\n\
  59.                 unable to process %s.\n";
  60. static ZCONST char Far DirIsntDirectory[] =
  61.   "checkdir error:  %s exists but is not directory\n\
  62.                 unable to process %s.\n";
  63. static ZCONST char Far PathTooLongTrunc[] =
  64.   "checkdir warning:  path too long; truncating\n                   %s\n\
  65.                -> %s\n";
  66. #if (!defined(SFX) || defined(SFX_EXDIR))
  67.    static ZCONST char Far CantCreateExtractDir[] =
  68.      "checkdir:  cannot create extraction directory: %s\n";
  69. #endif
  70.  
  71. #include <dirent.h>
  72.  
  73. #ifndef SFX
  74.  
  75. /************************/
  76. /*  Function do_wild()  */   /* identical to OS/2 version */
  77. /************************/
  78.  
  79. char *do_wild(__G__ wildspec)
  80.     __GDEF
  81.     ZCONST char *wildspec;   /* only used first time on a given dir */
  82. {
  83.     static DIR *wild_dir = (DIR *)NULL;
  84.     static ZCONST char *wildname;
  85.     static char *dirname, matchname[FILNAMSIZ];
  86.     static int notfirstcall=FALSE, have_dirname, dirnamelen;
  87.     char *fnamestart;
  88.     struct dirent *file;
  89.  
  90.     /* Even when we're just returning wildspec, we *always* do so in
  91.      * matchname[]--calling routine is allowed to append four characters
  92.      * to the returned string, and wildspec may be a pointer to argv[].
  93.      */
  94.     if (!notfirstcall) {    /* first call:  must initialize everything */
  95.         notfirstcall = TRUE;
  96.  
  97.         if (!iswild(wildspec)) {
  98.             strncpy(matchname, wildspec, FILNAMSIZ);
  99.             matchname[FILNAMSIZ-1] = '\0';
  100.             have_dirname = FALSE;
  101.             dir = NULL;
  102.             return matchname;
  103.         }
  104.  
  105.         /* break the wildspec into a directory part and a wildcard filename */
  106.         if ((wildname = strrchr(wildspec, '/')) == (ZCONST char *)NULL &&
  107.             (wildname = strrchr(wildspec, ':')) == (ZCONST char *)NULL) {
  108.             dirname = ".";
  109.             dirnamelen = 1;
  110.             have_dirname = FALSE;
  111.             wildname = wildspec;
  112.         } else {
  113.             ++wildname;     /* point at character after '/' or ':' */
  114.             dirnamelen = (int)(wildname - wildspec);
  115.             if ((dirname = (char *)malloc(dirnamelen+1)) == (char *)NULL) {
  116.                 Info(slide, 1, ((char *)slide,
  117.                   LoadFarString(CantAllocateWildcard)));
  118.                 strncpy(matchname, wildspec, FILNAMSIZ);
  119.                 matchname[FILNAMSIZ-1] = '\0';
  120.                 return matchname;   /* but maybe filespec was not a wildcard */
  121.             }
  122. /* GRR:  cannot strip trailing char for opendir since might be "d:/" or "d:"
  123.  *       (would have to check for "./" at end--let opendir handle it instead) */
  124.             strncpy(dirname, wildspec, dirnamelen);
  125.             dirname[dirnamelen] = '\0';   /* terminate for strcpy below */
  126.             have_dirname = TRUE;
  127.         }
  128.         Trace((stderr, "do_wild:  dirname = [%s]\n", FnFilter1(dirname)));
  129.  
  130.         if ((wild_dir = opendir(dirname)) != (DIR *)NULL) {
  131.             if (have_dirname) {
  132.                 strcpy(matchname, dirname);
  133.                 fnamestart = matchname + dirnamelen;
  134.             } else
  135.                 fnamestart = matchname;
  136.             while ((file = readdir(wild_dir)) != (struct dirent *)NULL) {
  137.                 Trace((stderr, "do_wild:  readdir returns %s\n",
  138.                   FnFilter1(file->d_name)));
  139.                 strcpy(fnamestart, file->d_name);
  140.                 if (strrchr(fnamestart, '.') == (char *)NULL)
  141.                     strcat(fnamestart, ".");
  142.                 if (match(fnamestart, wildname, 1 WISEP) && /* 1=ignore case */
  143.                     /* skip "." and ".." directory entries */
  144.                     strcmp(fnamestart, ".") && strcmp(fnamestart, "..")) {
  145.                     Trace((stderr, "do_wild:  match() succeeds\n"));
  146.                     /* remove trailing dot */
  147.                     fnamestart += strlen(fnamestart) - 1;
  148.                     if (*fnamestart == '.')
  149.                         *fnamestart = '\0';
  150.                     return matchname;
  151.                 }
  152.             }
  153.             /* if we get to here directory is exhausted, so close it */
  154.             closedir(wild_dir);
  155.             wild_dir = (DIR *)NULL;
  156.         }
  157. #ifdef DEBUG
  158.         else {
  159.             Trace((stderr, "do_wild:  opendir(%s) returns NULL\n",
  160.               FnFilter1(dirname)));
  161.         }
  162. #endif /* DEBUG */
  163.  
  164.         /* return the raw wildspec in case that works (e.g., directory not
  165.          * searchable, but filespec was not wild and file is readable) */
  166.         strncpy(matchname, wildspec, FILNAMSIZ);
  167.         matchname[FILNAMSIZ-1] = '\0';
  168.         return matchname;
  169.     }
  170.  
  171.     /* last time through, might have failed opendir but returned raw wildspec */
  172.     if (wild_dir == (DIR *)NULL) {
  173.         notfirstcall = FALSE; /* nothing left to try--reset for new wildspec */
  174.         if (have_dirname)
  175.             free(dirname);
  176.         return (char *)NULL;
  177.     }
  178.  
  179.     /* If we've gotten this far, we've read and matched at least one entry
  180.      * successfully (in a previous call), so dirname has been copied into
  181.      * matchname already.
  182.      */
  183.     if (have_dirname) {
  184.         /* strcpy(matchname, dirname); */
  185.         fnamestart = matchname + dirnamelen;
  186.     } else
  187.         fnamestart = matchname;
  188.     while ((file = readdir(wild_dir)) != (struct dirent *)NULL) {
  189.         Trace((stderr, "do_wild:  readdir returns %s\n",
  190.           FnFilter1(file->d_name)));
  191.         strcpy(fnamestart, file->d_name);
  192.         if (strrchr(fnamestart, '.') == (char *)NULL)
  193.             strcat(fnamestart, ".");
  194.         if (match(fnamestart, wildname, 1 WISEP)) {     /* 1 == ignore case */
  195.             Trace((stderr, "do_wild:  match() succeeds\n"));
  196.             /* remove trailing dot */
  197.             fnamestart += strlen(fnamestart) - 1;
  198.             if (*fnamestart == '.')
  199.                 *fnamestart = '\0';
  200.             return matchname;
  201.         }
  202.     }
  203.  
  204.     closedir(wild_dir);     /* have read at least one entry; nothing left */
  205.     wild_dir = (DIR *)NULL;
  206.     notfirstcall = FALSE;   /* reset for new wildspec */
  207.     if (have_dirname)
  208.         free(dirname);
  209.     return (char *)NULL;
  210.  
  211. } /* end function do_wild() */
  212.  
  213. #endif /* !SFX */
  214.  
  215.  
  216.  
  217. /**********************/
  218. /* Function mapattr() */
  219. /**********************/
  220.  
  221. int mapattr(__G)
  222.     __GDEF
  223. {
  224.     /* set archive bit (file is not backed up): */
  225.     G.pInfo->file_attr = (unsigned)(G.crec.external_file_attributes & 7) | 32;
  226.     return 0;
  227. }
  228.  
  229.  
  230.  
  231. /**********************/
  232. /* Function mapname() */
  233. /**********************/
  234.  
  235. int mapname(__G__ renamed)
  236.     __GDEF
  237.     int renamed;
  238. /*
  239.  * returns:
  240.  *  MPN_OK          - no problem detected
  241.  *  MPN_INF_TRUNC   - caution (truncated filename)
  242.  *  MPN_INF_SKIP    - info "skip entry" (dir doesn't exist)
  243.  *  MPN_ERR_SKIP    - error -> skip entry
  244.  *  MPN_ERR_TOOLONG - error -> path is too long
  245.  *  MPN_NOMEM       - error (memory allocation failed) -> skip entry
  246.  *  [also MPN_VOL_LABEL, MPN_CREATED_DIR]
  247.  */
  248. {
  249.     char pathcomp[FILNAMSIZ];      /* path-component buffer */
  250.     char *pp, *cp=(char *)NULL;    /* character pointers */
  251.     char *lastsemi=(char *)NULL;   /* pointer to last semi-colon in pathcomp */
  252.     char *last_dot=(char *)NULL;   /* last dot not converted to underscore */
  253.     int dotname = FALSE;           /* path component begins with dot? */
  254.     int killed_ddot = FALSE;       /* is set when skipping "../" pathcomp */
  255.     int error = MPN_OK;
  256.     register unsigned workch;      /* hold the character being tested */
  257.  
  258.  
  259.     if (G.pInfo->vollabel)
  260.         return MPN_VOL_LABEL;   /* Cannot set disk volume labels in FlexOS */
  261.  
  262. /*---------------------------------------------------------------------------
  263.     Initialize various pointers and counters and stuff.
  264.   ---------------------------------------------------------------------------*/
  265.  
  266.     /* can create path as long as not just freshening, or if user told us */
  267.     G.create_dirs = (!uO.fflag || renamed);
  268.  
  269.     created_dir = FALSE;        /* not yet */
  270.     renamed_fullpath = FALSE;
  271.  
  272.     if (renamed) {
  273.         cp = G.filename - 1;    /* point to beginning of renamed name... */
  274.         while (*++cp)
  275.             if (*cp == '\\')    /* convert backslashes to forward */
  276.                 *cp = '/';
  277.         cp = G.filename;
  278.         /* use temporary rootpath if user gave full pathname */
  279.         if (G.filename[0] == '/') {
  280.             renamed_fullpath = TRUE;
  281.             pathcomp[0] = '/';  /* copy the '/' and terminate */
  282.             pathcomp[1] = '\0';
  283.             ++cp;
  284.         } else if (isalpha((uch)G.filename[0]) && G.filename[1] == ':') {
  285.             renamed_fullpath = TRUE;
  286.             pp = pathcomp;
  287.             *pp++ = *cp++;      /* copy the "d:" (+ '/', possibly) */
  288.             *pp++ = *cp++;
  289.             if (*cp == '/')
  290.                 *pp++ = *cp++;  /* otherwise add "./"? */
  291.             *pp = '\0';
  292.         }
  293.     }
  294.  
  295.     /* pathcomp is ignored unless renamed_fullpath is TRUE: */
  296.     if ((error = checkdir(__G__ pathcomp, INIT)) != 0) /* initialize path buf */
  297.         return error;           /* ...unless no mem or vol label on hard disk */
  298.  
  299.     *pathcomp = '\0';           /* initialize translation buffer */
  300.     pp = pathcomp;              /* point to translation buffer */
  301.     if (!renamed) {             /* cp already set if renamed */
  302.         if (uO.jflag)           /* junking directories */
  303.             cp = (char *)strrchr(G.filename, '/');
  304.         if (cp == (char *)NULL) /* no '/' or not junking dirs */
  305.             cp = G.filename;    /* point to internal zipfile-member pathname */
  306.         else
  307.             ++cp;               /* point to start of last component of path */
  308.     }
  309.  
  310. /*---------------------------------------------------------------------------
  311.     Begin main loop through characters in filename.
  312.   ---------------------------------------------------------------------------*/
  313.  
  314.     while ((workch = (uch)*cp++) != 0) {
  315.  
  316.         switch (workch) {
  317.             case '/':             /* can assume -j flag not given */
  318.                 *pp = '\0';
  319.                 map2fat(pathcomp, last_dot);   /* 8.3 truncation (in place) */
  320.                 last_dot = (char *)NULL;
  321.                 if (strcmp(pathcomp, ".") == 0) {
  322.                     /* don't bother appending "./" to the path */
  323.                     *pathcomp = '\0';
  324.                 } else if (!uO.ddotflag && strcmp(pathcomp, "..") == 0) {
  325.                     /* "../" dir traversal detected, skip over it */
  326.                     *pathcomp = '\0';
  327.                     killed_ddot = TRUE;     /* set "show message" flag */
  328.                 }
  329.                 /* when path component is not empty, append it now */
  330.                 if (*pathcomp != '\0' &&
  331.                     ((error = checkdir(__G__ pathcomp, APPEND_DIR))
  332.                      & MPN_MASK) > MPN_INF_TRUNC)
  333.                     return error;
  334.                 pp = pathcomp;    /* reset conversion buffer for next piece */
  335.                 lastsemi = (char *)NULL; /* leave direct. semi-colons alone */
  336.                 break;
  337.  
  338.             case '.':
  339.                 if (pp == pathcomp) {     /* nothing appended yet... */
  340.                     if (*cp == '.' && cp[1] == '/') {   /* "../" */
  341.                         *pp++ = '.';      /* add first dot, unchanged... */
  342.                         ++cp;             /* skip second dot, since it will */
  343.                     } else {              /*  be "added" at end of if-block */
  344.                         *pp++ = '_';      /* FAT doesn't allow null filename */
  345.                         dotname = TRUE;   /*  bodies, so map .exrc -> _.exrc */
  346.                     }                     /*  (extra '_' now, "dot" below) */
  347.                 } else if (dotname) {     /* found a second dot, but still */
  348.                     dotname = FALSE;      /*  have extra leading underscore: */
  349.                     *pp = '\0';           /*  remove it by shifting chars */
  350.                     pp = pathcomp + 1;    /*  left one space (e.g., .p1.p2: */
  351.                     while (pp[1]) {       /*  __p1 -> _p1_p2 -> _p1.p2 when */
  352.                         *pp = pp[1];      /*  finished) [opt.:  since first */
  353.                         ++pp;             /*  two chars are same, can start */
  354.                     }                     /*  shifting at second position] */
  355.                 }
  356.                 last_dot = pp;    /* point at last dot so far... */
  357.                 *pp++ = '_';      /* convert dot to underscore for now */
  358.                 break;
  359.  
  360.             /* drive names are not stored in zipfile, so no colons allowed;
  361.              *  no brackets or most other punctuation either (all of which
  362.              *  can appear in Unix-created archives; backslash is particularly
  363.              *  bad unless all necessary directories exist) */
  364.             case '[':          /* these punctuation characters forbidden */
  365.             case ']':          /*  only on plain FAT file systems */
  366.             case '+':
  367.             case ',':
  368.             case '=':
  369.             case ':':           /* special shell characters of command.com */
  370.             case '\\':          /*  (device and directory limiters, wildcard */
  371.             case '"':           /*  characters, stdin/stdout redirection and */
  372.             case '<':           /*  pipe indicators and the quote sign) are */
  373.             case '>':           /*  never allowed in filenames on (V)FAT */
  374.             case '|':
  375.             case '*':
  376.             case '?':
  377.                 *pp++ = '_';
  378.                 break;
  379.  
  380.             case ';':             /* start of VMS version? */
  381.                 lastsemi = pp;
  382.                 break;
  383.  
  384.             case ' ':                      /* change spaces to underscores */
  385.                 if (uO.sflag)              /*  only if requested */
  386.                     *pp++ = '_';
  387.                 else
  388.                     *pp++ = (char)workch;
  389.                 break;
  390.  
  391.             default:
  392.                 /* allow ASCII 255 and European characters in filenames: */
  393.                 if (isprint(workch) || workch >= 127)
  394.                     *pp++ = (char)workch;
  395.  
  396.         } /* end switch */
  397.     } /* end while loop */
  398.  
  399.     /* Show warning when stripping insecure "parent dir" path components */
  400.     if (killed_ddot && QCOND2) {
  401.         Info(slide, 0, ((char *)slide, LoadFarString(WarnDirTraversSkip),
  402.           FnFilter1(G.filename)));
  403.         if (!(error & ~MPN_MASK))
  404.             error = (error & MPN_MASK) | PK_WARN;
  405.     }
  406.  
  407. /*---------------------------------------------------------------------------
  408.     Report if directory was created (and no file to create:  filename ended
  409.     in '/'), check name to be sure it exists, and combine path and name be-
  410.     fore exiting.
  411.   ---------------------------------------------------------------------------*/
  412.  
  413.     if (G.filename[strlen(G.filename) - 1] == '/') {
  414.         checkdir(__G__ G.filename, GETPATH);
  415.         if (created_dir) {
  416.             if (QCOND2) {
  417.                 Info(slide, 0, ((char *)slide, LoadFarString(Creating),
  418.                   FnFilter1(G.filename)));
  419.             }
  420.             /* set dir time (note trailing '/') */
  421.             return (error & ~MPN_MASK) | MPN_CREATED_DIR;
  422.         }
  423.         /* dir existed already; don't look for data to extract */
  424.         return (error & ~MPN_MASK) | MPN_INF_SKIP;
  425.     }
  426.  
  427.     *pp = '\0';                   /* done with pathcomp:  terminate it */
  428.  
  429.     /* if not saving them, remove VMS version numbers (appended ";###") */
  430.     if (!uO.V_flag && lastsemi) {
  431.         pp = lastsemi;            /* semi-colon was omitted:  expect all #'s */
  432.         while (isdigit((uch)(*pp)))
  433.             ++pp;
  434.         if (*pp == '\0')          /* only digits between ';' and end:  nuke */
  435.             *lastsemi = '\0';
  436.     }
  437.  
  438.     map2fat(pathcomp, last_dot);  /* 8.3 truncation (in place) */
  439.  
  440.     if (*pathcomp == '\0') {
  441.         Info(slide, 1, ((char *)slide, LoadFarString(ConversionFailed),
  442.           FnFilter1(G.filename)));
  443.         return (error & ~MPN_MASK) | MPN_ERR_SKIP;
  444.     }
  445.  
  446.     checkdir(__G__ pathcomp, APPEND_NAME);  /* returns 1 if truncated: care? */
  447.     checkdir(__G__ G.filename, GETPATH);
  448.  
  449.     return error;
  450.  
  451. } /* end function mapname() */
  452.  
  453.  
  454.  
  455.  
  456. /**********************/
  457. /* Function map2fat() */
  458. /**********************/
  459.  
  460. static void map2fat(pathcomp, last_dot)
  461.     char *pathcomp, *last_dot;
  462. {
  463.     char *pEnd = pathcomp + strlen(pathcomp);
  464.  
  465. /*---------------------------------------------------------------------------
  466.     Case 1:  filename has no dot, so figure out if we should add one.  Note
  467.     that the algorithm does not try to get too fancy:  if there are no dots
  468.     already, the name either gets truncated at 8 characters or the last un-
  469.     derscore is converted to a dot (only if more characters are saved that
  470.     way).  In no case is a dot inserted between existing characters.
  471.  
  472.               GRR:  have problem if filename is volume label??
  473.  
  474.   ---------------------------------------------------------------------------*/
  475.  
  476.     /* pEnd = pathcomp + strlen(pathcomp); */
  477.     if (last_dot == (char *)NULL) {   /* no dots:  check for underscores... */
  478.         char *plu = strrchr(pathcomp, '_');   /* pointer to last underscore */
  479.  
  480.         if (plu == (char *)NULL) {  /* no dots, no underscores:  truncate at */
  481.             if (pEnd > pathcomp+8)  /* 8 chars (could insert '.' and keep 11) */
  482.                 *(pEnd = pathcomp+8) = '\0';
  483.         } else if (MIN(plu - pathcomp, 8) + MIN(pEnd - plu - 1, 3) > 8) {
  484.             last_dot = plu;       /* be lazy:  drop through to next if-block */
  485.         } else if ((pEnd - pathcomp) > 8)    /* more fits into just basename */
  486.             pathcomp[8] = '\0';    /* than if convert last underscore to dot */
  487.         /* else whole thing fits into 8 chars or less:  no change */
  488.     }
  489.  
  490. /*---------------------------------------------------------------------------
  491.     Case 2:  filename has dot in it, so truncate first half at 8 chars (shift
  492.     extension if necessary) and second half at three.
  493.   ---------------------------------------------------------------------------*/
  494.  
  495.     if (last_dot != (char *)NULL) {   /* one dot (or two, in the case of */
  496.         *last_dot = '.';              /*  "..") is OK:  put it back in */
  497.  
  498.         if ((last_dot - pathcomp) > 8) {
  499.             char *p=last_dot, *q=pathcomp+8;
  500.             int i;
  501.  
  502.             for (i = 0;  (i < 4) && *p;  ++i)  /* too many chars in basename: */
  503.                 *q++ = *p++;                   /*  shift extension left and */
  504.             *q = '\0';                         /*  truncate/terminate it */
  505.         } else if ((pEnd - last_dot) > 4)
  506.             last_dot[4] = '\0';                /* too many chars in extension */
  507.         /* else filename is fine as is:  no change */
  508.     }
  509. } /* end function map2fat() */
  510.  
  511.  
  512.  
  513.  
  514. /***********************/
  515. /* Function checkdir() */
  516. /***********************/
  517.  
  518. int checkdir(__G__ pathcomp, flag)
  519.     __GDEF
  520.     char *pathcomp;
  521.     int flag;
  522. /*
  523.  * returns:
  524.  *  MPN_OK          - no problem detected
  525.  *  MPN_INF_TRUNC   - (on APPEND_NAME) truncated filename
  526.  *  MPN_INF_SKIP    - path doesn't exist, not allowed to create
  527.  *  MPN_ERR_SKIP    - path doesn't exist, tried to create and failed; or path
  528.  *                    exists and is not a directory, but is supposed to be
  529.  *  MPN_ERR_TOOLONG - path is too long
  530.  *  MPN_NOMEM       - can't allocate memory for filename buffers
  531.  */
  532. {
  533.     static int rootlen = 0;   /* length of rootpath */
  534.     static char *rootpath;    /* user's "extract-to" directory */
  535.     static char *buildpath;   /* full path (so far) to extracted file */
  536.     static char *end;         /* pointer to end of buildpath ('\0') */
  537.  
  538. #   define FN_MASK   7
  539. #   define FUNCTION  (flag & FN_MASK)
  540.  
  541.  
  542. /*---------------------------------------------------------------------------
  543.     APPEND_DIR:  append the path component to the path being built and check
  544.     for its existence.  If doesn't exist and we are creating directories, do
  545.     so for this one; else signal success or error as appropriate.
  546.   ---------------------------------------------------------------------------*/
  547.  
  548.     if (FUNCTION == APPEND_DIR) {
  549.         int too_long = FALSE;
  550.  
  551.         Trace((stderr, "appending dir segment [%s]\n", FnFilter1(pathcomp)));
  552.         while ((*end = *pathcomp++) != '\0')
  553.             ++end;
  554.  
  555.         /* GRR:  could do better check, see if overrunning buffer as we go:
  556.          * check end-buildpath after each append, set warning variable if
  557.          * within 20 of FILNAMSIZ; then if var set, do careful check when
  558.          * appending.  Clear variable when begin new path. */
  559.  
  560.         if ((end-buildpath) > FILNAMSIZ-3)  /* need '/', one-char name, '\0' */
  561.             too_long = TRUE;                /* check if extracting directory? */
  562.         if (stat(buildpath, &G.statbuf))    /* path doesn't exist */
  563.         {
  564.             if (!G.create_dirs) { /* told not to create (freshening) */
  565.                 free(buildpath);
  566.                 return MPN_INF_SKIP;    /* path doesn't exist: nothing to do */
  567.             }
  568.             if (too_long) {
  569.                 Info(slide, 1, ((char *)slide, LoadFarString(PathTooLong),
  570.                   FnFilter1(buildpath)));
  571.                 free(buildpath);
  572.                 /* no room for filenames:  fatal */
  573.                 return MPN_ERR_TOOLONG;
  574.             }
  575.             if (mkdir(buildpath, 0777) == -1) {   /* create the directory */
  576.                 Info(slide, 1, ((char *)slide, LoadFarString(CantCreateDir),
  577.                   FnFilter2(buildpath), FnFilter1(G.filename)));
  578.                 free(buildpath);
  579.                 /* path didn't exist, tried to create, failed */
  580.                 return MPN_ERR_SKIP;
  581.             }
  582.             created_dir = TRUE;
  583.         } else if (!S_ISDIR(G.statbuf.st_mode)) {
  584.             Info(slide, 1, ((char *)slide, LoadFarString(DirIsntDirectory),
  585.               FnFilter2(buildpath), FnFilter1(G.filename)));
  586.             free(buildpath);
  587.             /* path existed but wasn't dir */
  588.             return MPN_ERR_SKIP;
  589.         }
  590.         if (too_long) {
  591.             Info(slide, 1, ((char *)slide, LoadFarString(PathTooLong),
  592.               FnFilter1(buildpath)));
  593.             free(buildpath);
  594.             /* no room for filenames:  fatal */
  595.             return MPN_ERR_TOOLONG;
  596.         }
  597.         *end++ = '/';
  598.         *end = '\0';
  599.         Trace((stderr, "buildpath now = [%s]\n", FnFilter1(buildpath)));
  600.         return MPN_OK;
  601.  
  602.     } /* end if (FUNCTION == APPEND_DIR) */
  603.  
  604. /*---------------------------------------------------------------------------
  605.     GETPATH:  copy full path to the string pointed at by pathcomp, and free
  606.     buildpath.
  607.   ---------------------------------------------------------------------------*/
  608.  
  609.     if (FUNCTION == GETPATH) {
  610.         strcpy(pathcomp, buildpath);
  611.         Trace((stderr, "getting and freeing path [%s]\n",
  612.           FnFilter1(pathcomp)));
  613.         free(buildpath);
  614.         buildpath = end = (char *)NULL;
  615.         return MPN_OK;
  616.     }
  617.  
  618. /*---------------------------------------------------------------------------
  619.     APPEND_NAME:  assume the path component is the filename; append it and
  620.     return without checking for existence.
  621.   ---------------------------------------------------------------------------*/
  622.  
  623.     if (FUNCTION == APPEND_NAME) {
  624.         Trace((stderr, "appending filename [%s]\n", FnFilter1(pathcomp)));
  625.         while ((*end = *pathcomp++) != '\0') {
  626.             ++end;
  627.             if ((end-buildpath) >= FILNAMSIZ) {
  628.                 *--end = '\0';
  629.                 Info(slide, 1, ((char *)slide, LoadFarString(PathTooLongTrunc),
  630.                   FnFilter1(G.filename), FnFilter2(buildpath)));
  631.                 return MPN_INF_TRUNC;   /* filename truncated */
  632.             }
  633.         }
  634.         Trace((stderr, "buildpath now = [%s]\n", FnFilter1(buildpath)));
  635.         /* could check for existence here, prompt for new name... */
  636.         return MPN_OK;
  637.     }
  638.  
  639. /*---------------------------------------------------------------------------
  640.     INIT:  allocate and initialize buffer space for the file currently being
  641.     extracted.  If file was renamed with an absolute path, don't prepend the
  642.     extract-to path.
  643.   ---------------------------------------------------------------------------*/
  644.  
  645.     if (FUNCTION == INIT) {
  646.         Trace((stderr, "initializing buildpath to "));
  647.         /* allocate space for full filename, root path, and maybe "./" */
  648.         if ((buildpath = (char *)malloc(strlen(G.filename)+rootlen+3)) ==
  649.             (char *)NULL)
  650.             return MPN_NOMEM;
  651.         if (renamed_fullpath) {   /* pathcomp = valid data */
  652.             end = buildpath;
  653.             while ((*end = *pathcomp++) != '\0')
  654.                 ++end;
  655.         } else if (rootlen > 0) {
  656.             strcpy(buildpath, rootpath);
  657.             end = buildpath + rootlen;
  658.         } else {
  659.             *buildpath = '\0';
  660.             end = buildpath;
  661.         }
  662.         Trace((stderr, "[%s]\n", FnFilter1(buildpath)));
  663.         return MPN_OK;
  664.     }
  665.  
  666. /*---------------------------------------------------------------------------
  667.     ROOT:  if appropriate, store the path in rootpath and create it if neces-
  668.     sary; else assume it's a zipfile member and return.  This path segment
  669.     gets used in extracting all members from every zipfile specified on the
  670.     command line.  Note that under FlexOS, if a candidate extract-to
  671.     directory specification includes a drive letter (leading "x:"), it is
  672.     treated just as if it had a trailing '/'--that is, one directory level
  673.     will be created if the path doesn't exist, unless this is otherwise pro-
  674.     hibited (e.g., freshening).
  675.   ---------------------------------------------------------------------------*/
  676.  
  677. #if (!defined(SFX) || defined(SFX_EXDIR))
  678.     if (FUNCTION == ROOT) {
  679.         Trace((stderr, "initializing root path to [%s]\n",
  680.           FnFilter1(pathcomp)));
  681.         if (pathcomp == (char *)NULL) {
  682.             rootlen = 0;
  683.             return MPN_OK;
  684.         }
  685.         if (rootlen > 0)        /* rootpath was already set, nothing to do */
  686.             return MPN_OK;
  687.         if ((rootlen = strlen(pathcomp)) > 0) {
  688.             int had_trailing_pathsep=FALSE, add_dot=FALSE;
  689.             char *tmproot;
  690.  
  691.             if ((tmproot = (char *)malloc(rootlen+3)) == (char *)NULL) {
  692.                 rootlen = 0;
  693.                 return MPN_NOMEM;
  694.             }
  695.             strcpy(tmproot, pathcomp);
  696.             if (tmproot[rootlen-1] == '/' || tmproot[rootlen-1] == '\\') {
  697.                 tmproot[--rootlen] = '\0';
  698.                 had_trailing_pathsep = TRUE;
  699.             }
  700.             if (tmproot[rootlen-1] == ':') {
  701.                 if (!had_trailing_pathsep)  /* i.e., original wasn't "xxx:/" */
  702.                     add_dot = TRUE;     /* relative path: add '.' before '/' */
  703.             } else if (rootlen > 0) &&  /* need not check "xxx:." and "xxx:/" */
  704.                        (SSTAT(tmproot, &G.statbuf) ||
  705.                         !S_ISDIR(G.statbuf.st_mode))
  706.             {
  707.                 /* path does not exist */
  708.                 if (!G.create_dirs /* || iswild(tmproot) */ ) {
  709.                     free(tmproot);
  710.                     rootlen = 0;
  711.                     /* treat as stored file */
  712.                     return MPN_INF_SKIP;
  713.                 }
  714. /* GRR:  scan for wildcard characters?  OS-dependent...
  715.  * if find any, return MPN_INF_SKIP: treat as stored file(s) */
  716.                 /* create directory (could add loop here scanning tmproot
  717.                  * to create more than one level, but really necessary?) */
  718.                 if (mkdir(tmproot, 0777) == -1) {
  719.                     Info(slide, 1, ((char *)slide,
  720.                       LoadFarString(CantCreateExtractDir),
  721.                       FnFilter1(tmproot)));
  722.                     free(tmproot);
  723.                     rootlen = 0;
  724.                     /* path didn't exist, tried to create, and failed: */
  725.                     /* file exists, or 2+ subdir levels required */
  726.                     return MPN_ERR_SKIP;
  727.                 }
  728.             }
  729.             if (add_dot)                    /* had just "x:", make "x:." */
  730.                 tmproot[rootlen++] = '.';
  731.             tmproot[rootlen++] = '/';
  732.             tmproot[rootlen] = '\0';
  733.             if ((rootpath = (char *)realloc(tmproot, rootlen+1)) == NULL) {
  734.                 free(tmproot);
  735.                 rootlen = 0;
  736.                 return MPN_NOMEM;
  737.             }
  738.             Trace((stderr, "rootpath now = [%s]\n", FnFilter1(rootpath)));
  739.         }
  740.         return MPN_OK;
  741.     }
  742. #endif /* !SFX || SFX_EXDIR */
  743.  
  744. /*---------------------------------------------------------------------------
  745.     END:  free rootpath, immediately prior to program exit.
  746.   ---------------------------------------------------------------------------*/
  747.  
  748.     if (FUNCTION == END) {
  749.         Trace((stderr, "freeing rootpath\n"));
  750.         if (rootlen > 0) {
  751.             free(rootpath);
  752.             rootlen = 0;
  753.         }
  754.         return MPN_OK;
  755.     }
  756.  
  757.     return MPN_INVALID; /* should never reach */
  758.  
  759. } /* end function checkdir() */
  760.  
  761.  
  762.  
  763.  
  764. /****************************/
  765. /* Function close_outfile() */
  766. /****************************/
  767.  
  768. void close_outfile(__G)
  769.     __GDEF
  770.  /*
  771.   * FlexOS VERSION
  772.   *
  773.   * Set the output file date/time stamp according to information from the
  774.   * zipfile directory record for this member, then close the file and set
  775.   * its permissions (archive, hidden, read-only, system).  Aside from closing
  776.   * the file, this routine is optional (but most compilers support it).
  777.   */
  778. {
  779.     DISKFILE    df;
  780.     LONG        fnum;
  781.  
  782.     struct {                /* date and time words */
  783.         union {             /* DOS file modification time word */
  784.             ush ztime;
  785.             struct {
  786.                 unsigned zt_se : 5;
  787.                 unsigned zt_mi : 6;
  788.                 unsigned zt_hr : 5;
  789.             } _tf;
  790.         } _t;
  791.         union {             /* DOS file modification date word */
  792.             ush zdate;
  793.             struct {
  794.                 unsigned zd_dy : 5;
  795.                 unsigned zd_mo : 4;
  796.                 unsigned zd_yr : 7;
  797.             } _df;
  798.         } _d;
  799.     } zt;
  800.  
  801. #ifdef USE_EF_UT_TIME
  802.     iztimes z_utime;
  803.     struct tm *t;
  804. #endif /* ?USE_EF_UT_TIME */
  805.  
  806.     fclose(G.outfile);
  807.  
  808.     if ((fnum = s_open(A_SET, G.filename)) < 0) {
  809.         Info(slide, 0x201, ((char *)slide,
  810.           "warning:  cannot open %s to set the time\n",
  811.           FnFilter1(G.filename)));
  812.         return;
  813.     }
  814.  
  815.     if (s_get(T_FILE, fnum, &df, DSKFSIZE) < 0) {
  816.         s_close(0, fnum);
  817.  
  818.         Info(slide, 0x201, ((char *)slide,
  819.           "warning:  cannot get info on %s\n", FnFilter1(G.filename)));
  820.         return;
  821.     }
  822.  
  823.     /* skip restoring time stamps on user's request */
  824.     if (uO.D_flag <= 1) {
  825.  
  826. /*---------------------------------------------------------------------------
  827.     Copy and/or convert time and date variables, if necessary; then fill in
  828.     the file time/date.
  829.   ---------------------------------------------------------------------------*/
  830.  
  831. #ifdef USE_EF_UT_TIME
  832.         if (G.extra_field &&
  833. #ifdef IZ_CHECK_TZ
  834.             G.tz_is_valid &&
  835. #endif
  836.             (ef_scan_for_izux(G.extra_field, G.lrec.extra_field_length, 0,
  837.              G.lrec.last_mod_dos_datetime, &z_utime, NULL) & EB_UT_FL_MTIME))
  838.         {
  839.             TTrace((stderr, "close_outfile:  Unix e.f. modif. time = %ld\n",
  840.               z_utime.mtime));
  841.             t = localtime(&(z_utime.mtime));
  842.         } else
  843.             t = (struct tm *)NULL;
  844.         if (t != (struct tm *)NULL) {
  845.             if (t->tm_year < 80) {
  846.                 df.df_modyear = 1980;
  847.                 df.df_modmonth = 1;
  848.                 df.df_modday = 1;
  849.                 df.df_modhr = 0;
  850.                 df.df_modmin = 0;
  851.                 df.df_modsec = 0;
  852.             } else {
  853.                 df.df_modyear = t->tm_year + 1900;
  854.                 df.df_modmonth = t->tm_mon + 1;
  855.                 df.df_modday = t->tm_mday;
  856.                 df.df_modhr = t->tm_hour;
  857.                 df.df_modmin = t->tm_min;
  858.                 df.df_modsec = t->tm_sec;
  859.             }
  860.         } else
  861. #endif /* ?USE_EF_UX_TIME */
  862.         {
  863.             zt._t.ztime = (ush)(G.lrec.last_mod_dos_datetime) & 0xffff;
  864.             zt._d.zdate = (ush)(G.lrec.last_mod_dos_datetime >> 16);
  865.  
  866.             df.df_modyear = 1980 + zt._d._df.zd_yr;
  867.             df.df_modmonth = zt._d._df.zd_mo;
  868.             df.df_modday = zt._d._df.zd_dy;
  869.             df.df_modhr = zt._t._tf.zt_hr;
  870.             df.df_modmin = zt._t._tf.zt_mi;
  871.             df.df_modsec = zt._t._tf.zt_se << 1;
  872.         }
  873.     }
  874.  
  875. /*---------------------------------------------------------------------------
  876.     Fill in the file attributes.
  877.   ---------------------------------------------------------------------------*/
  878.  
  879.     df.df_attr1 = (UBYTE)G.pInfo->file_attr;
  880.  
  881. /*---------------------------------------------------------------------------
  882.     Now we try to set the attributes & date/time.
  883.   ---------------------------------------------------------------------------*/
  884.  
  885.     if (s_set(T_FILE, fnum, &df, DSKFSIZE) < 0)
  886.         Info(slide, 0x201, ((char *)slide,
  887.           "warning:  cannot set info for %s\n", FnFilter1(G.filename)));
  888.  
  889.     s_close(0, fnum);
  890. } /* end function close_outfile() */
  891.  
  892. #ifndef SFX
  893.  
  894. /*************************/
  895. /* Function dateformat() */
  896. /*************************/
  897.  
  898. int dateformat()
  899. {
  900.     return DF_DMY;   /* default for systems without locale info */
  901. }
  902.  
  903. /************************/
  904. /*  Function version()  */
  905. /************************/
  906.  
  907. void version(__G)
  908.     __GDEF
  909. {
  910.     int len;
  911.  
  912.     len = sprintf((char *)slide, LoadFarString(CompiledWith),
  913.             "MetaWare High C",
  914.             "",
  915.             "FlexOS",
  916.             " (16-bit, big)",
  917.  
  918. #ifdef __DATE__
  919.       " on ", __DATE__
  920. #else
  921.       "", ""
  922. #endif
  923.     );
  924.  
  925.     (*G.message)((zvoid *)&G, slide, (ulg)len, 0);
  926. }
  927.  
  928. #endif /* !SFX */
  929.  
  930. /************************/
  931. /*  Function _wildarg() */
  932. /************************/
  933.  
  934. /* This prevents the PORTLIB startup code from preforming argument globbing */
  935.  
  936. _wildarg() {}
  937.