home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 9 Archive / 09-Archive.zip / unzip512.zip / nt / nt.c < prev   
C/C++ Source or Header  |  1994-08-16  |  41KB  |  1,117 lines

  1. /*---------------------------------------------------------------------------
  2.  
  3.   nt.c                                              Last updated:  15 Aug 94
  4.  
  5.   WinNT-specific routines for use with Info-ZIP's UnZip 5.1 and later.
  6.   (Borrowed, pilfered and plundered code from OS/2 and MS-DOS versions and
  7.   from ZIP; modified as necessary.)
  8.  
  9.   Contains:  GetLoadPath()
  10.              opendir()
  11.              readdir()
  12.              closedir()
  13.              mapattr()
  14.              getNTfiletime()
  15.              close_outfile()
  16.              isfloppy()
  17.              IsVolumeOldFAT()
  18.              IsFileNameValid()
  19.              map2fat()
  20.              checkdir()
  21.              do_wild()
  22.              mapname()
  23.              version()
  24.  
  25.   ---------------------------------------------------------------------------*/
  26.  
  27. #include <windows.h>
  28. #include "unzip.h"
  29.  
  30.  
  31. #define MKDIR(path,mode)   mkdir(path)
  32.  
  33. struct direct
  34. {
  35.     char    reserved [21];
  36.     char    ff_attrib;
  37.     short   ff_ftime;
  38.     short   ff_fdate;
  39.     long    size;
  40.     char    d_name[MAX_PATH];
  41.     int     d_first;
  42.     HANDLE  d_hFindFile;
  43. };
  44.  
  45.  
  46. static int created_dir;         /* used by mapname(), checkdir() */
  47. static int renamed_fullpath;    /* ditto */
  48. static int fnlen;               /* ditto */
  49. static unsigned nLabelDrive;    /* ditto */
  50.  
  51.  
  52.  
  53. #ifdef SFX
  54.  
  55. /**************************/
  56. /* Function GetLoadPath() */
  57. /**************************/
  58.  
  59. char *GetLoadPath(void) 
  60. {
  61. #ifdef MSC
  62.     extern char *_pgmptr;
  63.     return _pgmptr;
  64.  
  65. #else    /* use generic API call */
  66.     GetModuleFileName(NULL, filename, FILNAMSIZ);
  67.     return filename;
  68. #endif
  69.  
  70. } /* end function GetLoadPath() */
  71.  
  72.  
  73.  
  74.  
  75.  
  76. #else /* !SFX */
  77.  
  78. /**********************/        /* Borrowed from ZIP 2.0 sources             */
  79. /* Function opendir() */        /* Difference: no special handling for       */
  80. /**********************/        /*             hidden or system files.       */
  81.  
  82. static struct direct *opendir(n)
  83.     const char *n;  /* directory to open */
  84. {
  85.     struct direct *d;       /* malloc'd return value */
  86.     char *p;                /* malloc'd temporary string */
  87.     WIN32_FIND_DATA fd;
  88.     int len = strlen(n);
  89.  
  90.     /* Start searching for files in the MSDOS directory n */
  91.  
  92.     if ((d = (struct direct *)malloc(sizeof(struct direct))) == NULL ||
  93.         (p = malloc(strlen(n) + 5)) == NULL)
  94.     {
  95.         if (d != (struct direct *)NULL)
  96.             free((void *)d);
  97.         return (struct direct *)NULL;
  98.     }
  99.     strcpy(p, n);
  100.     if (p[len-1] == ':')
  101.         p[len++] = '.';   /* x: => x:. */
  102.     else if (p[len-1] == '/' || p[len-1] == '\\')
  103.         --len;            /* foo/ => foo */
  104.     strcpy(p+len, "/*");
  105.  
  106.     if (INVALID_HANDLE_VALUE == (d->d_hFindFile = FindFirstFile(p, &fd))) {
  107.         free((voidp *)d);
  108.         free((voidp *)p);
  109.         return NULL;
  110.     }
  111.     strcpy(d->d_name, fd.cFileName);
  112.  
  113.     free((voidp *)p);
  114.     d->d_first = 1;
  115.     return d;
  116.  
  117. } /* end of function opendir() */
  118.  
  119.  
  120.  
  121.  
  122. /**********************/        /* Borrowed from ZIP 2.0 sources             */
  123. /* Function readdir() */        /* Difference: no special handling for       */
  124. /**********************/        /*             hidden or system files.       */
  125.  
  126. static struct direct *readdir(d)
  127.     struct direct *d;         /* directory stream from which to read */
  128. {
  129.     /* Return pointer to first or next directory entry, or NULL if end. */
  130.  
  131.     if ( d->d_first )
  132.         d->d_first = 0;
  133.     else
  134.     {
  135.         WIN32_FIND_DATA fd;
  136.  
  137.         if ( !FindNextFile(d->d_hFindFile, &fd) )
  138.             return NULL;
  139.  
  140.         strcpy(d->d_name, fd.cFileName);
  141.     }
  142.     return (struct direct *)d;
  143.  
  144. } /* end of function readdir() */
  145.  
  146.  
  147.  
  148.  
  149. /***********************/
  150. /* Function closedir() */       /* Borrowed from ZIP 2.0 sources */
  151. /***********************/
  152.  
  153. static void closedir(d)
  154.     struct direct *d;         /* directory stream to close */
  155. {
  156.     FindClose(d->d_hFindFile);
  157.     free(d);
  158. }
  159.  
  160. #endif /* ?SFX */
  161.  
  162.  
  163.  
  164.  
  165. /**********************/
  166. /* Function mapattr() */
  167. /**********************/
  168.  
  169. /* Identical to MS-DOS, OS/2 versions.                                       */
  170. /* However, NT has a lot of extra permission stuff, so this function should  */
  171. /*  probably be extended in the future.                                      */
  172.  
  173. int mapattr()
  174. {
  175.     /* set archive bit (file is not backed up): */
  176.     pInfo->file_attr = (unsigned)(crec.external_file_attributes | 32) & 0xff;
  177.     return 0;
  178.  
  179. } /* end function mapattr() */
  180.  
  181.  
  182.  
  183.  
  184. /****************************/      /* Get the file time in a format that */
  185. /* Function getNTfiletime() */      /* can be used by SetFileTime() in NT */
  186. /****************************/
  187.  
  188. int getNTfiletime(FILETIME *ft)
  189. {
  190.     FILETIME lft;      /* 64-bit value made up of two 32 bit [low & high] */
  191.     WORD wDOSDate;     /* for converting from DOS date to Windows NT      */
  192.     WORD wDOSTime;
  193.  
  194.     /* Copy and/or convert time and date variables, if necessary;   */
  195.     /* then set the file time/date.                                 */
  196.     wDOSTime = (WORD)lrec.last_mod_file_time;
  197.     wDOSDate = (WORD)lrec.last_mod_file_date;
  198.  
  199.     /* The DosDateTimeToFileTime() function converts a DOS date/time    */
  200.     /* into a 64 bit Windows NT file time                               */
  201.     if (!DosDateTimeToFileTime(wDOSDate, wDOSTime, &lft))
  202.     {
  203.         PRINTF("DosDateTime failed: %d\n", GetLastError());
  204.         return FALSE;
  205.     }
  206.     if (!LocalFileTimeToFileTime( &lft, ft))
  207.     {
  208.         PRINTF("LocalFileTime failed: %d\n", GetLastError());
  209.         *ft = lft;
  210.     }
  211.     return TRUE;
  212. }
  213.  
  214.  
  215.  
  216.  
  217. /****************************/
  218. /* Function close_outfile() */
  219. /****************************/
  220.  
  221. void close_outfile()
  222. {
  223.     FILETIME ft;       /* File time type defined in NT */
  224.     HANDLE hFile;      /* File handle defined in NT    */
  225.     int gotTime;
  226.  
  227.     /* don't set the time stamp on standard output */
  228.     if (cflag) {
  229.         fclose(outfile);
  230.         return;
  231.     }
  232.  
  233.     gotTime = getNTfiletime(&ft);
  234.  
  235.     /* Close the file and then re-open it using the Win32
  236.      * CreateFile call, so that the file can be created
  237.      * with GENERIC_WRITE access, otherwise the SetFileTime
  238.      * call will fail. */
  239.     fclose(outfile);
  240.  
  241.     hFile = CreateFile(filename, GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
  242.          FILE_ATTRIBUTE_NORMAL, NULL);
  243.     if ( hFile == INVALID_HANDLE_VALUE ) {
  244.         FPRINTF(stderr, "\nCreateFile error %d when trying set file time\n",
  245.                 GetLastError());
  246.     }
  247.     else {
  248.         if (gotTime)
  249.             if (!SetFileTime(hFile, NULL, NULL, &ft))
  250.                 PRINTF("\nSetFileTime failed: %d\n", GetLastError());
  251.         CloseHandle(hFile);
  252.     }
  253.  
  254.     /* HG: I think this could be done in the CreateFile call above - just  */
  255.     /*     replace 'FILE_ATTRIBUTE_NORMAL' with 'pInfo->file_attr & 0x7F'  */
  256.     if (!SetFileAttributes(filename, pInfo->file_attr & 0x7F))
  257.         FPRINTF(stderr, "\nwarning (%d): could not set file attributes\n",
  258.                 GetLastError());
  259.  
  260.     return;
  261.  
  262. } /* end function close_outfile() */
  263.  
  264.  
  265.  
  266. /* ============================================================================
  267. */
  268.  
  269.  
  270.  
  271. /***********************/
  272. /* Function isfloppy() */   /* more precisely, is it removable? */
  273. /***********************/
  274.  
  275. static int isfloppy(int nDrive)   /* 1 == A:, 2 == B:, etc. */
  276. {
  277.     char rootPathName[4];
  278.  
  279.     rootPathName[0] = 'A' + nDrive - 1;   /* Build the root path name, */
  280.     rootPathName[1] = ':';                /* e.g. "A:/"                */
  281.     rootPathName[2] = '/';
  282.     rootPathName[3] = '\0';
  283.  
  284.     return (GetDriveType(rootPathName) == DRIVE_REMOVABLE);
  285.  
  286. } /* end function isfloppy() */
  287.  
  288.  
  289.  
  290.  
  291. /*****************************/
  292. /* Function IsVolumeOldFAT() */
  293. /*****************************/
  294.  
  295. /*
  296.  * Note:  8.3 limits on filenames apply only to old-style FAT filesystems.
  297.  *        More recent versions of Windows (Windows NT 3.5 / Windows 4.0)
  298.  *        can support long filenames (LFN) on FAT filesystems.  Check the
  299.  *        filesystem maximum component length field to detect LFN support.
  300.  *        [GRR:  this routine is only used to determine whether spaces in
  301.  *        filenames are supported...]
  302.  */
  303.  
  304. static int IsVolumeOldFAT(char *name)
  305. {
  306.     char     *tmp0;
  307.     char      rootPathName[4];
  308.     char      tmp1[MAX_PATH], tmp2[MAX_PATH];
  309.     unsigned  volSerNo, maxCompLen, fileSysFlags;
  310.  
  311.     if (isalpha(name[0]) && (name[1] == ':'))
  312.         tmp0 = name;
  313.     else
  314.     {
  315.         GetFullPathName(name, MAX_PATH, tmp1, &tmp0);
  316.         tmp0 = &tmp1[0];
  317.     }
  318.     strncpy(rootPathName, tmp0, 3);   /* Build the root path name, */
  319.     rootPathName[3] = '\0';           /* e.g. "A:/"                */
  320.  
  321.     GetVolumeInformation(rootPathName, tmp1, MAX_PATH, &volSerNo,
  322.                          &maxCompLen, &fileSysFlags, tmp2, MAX_PATH);
  323.  
  324.     /* Long Filenames (LFNs) are available if the component length is > 12 */
  325.     return maxCompLen <= 12;
  326. /*  return !strncmp(strupr(tmp2), "FAT", 3);   old version */
  327.  
  328. }
  329.  
  330.  
  331.  
  332.  
  333. /******************************/
  334. /* Function IsFileNameValid() */
  335. /******************************/
  336.  
  337. static int IsFileNameValid(char *name)
  338. {
  339.     HFILE    hf;
  340.     OFSTRUCT of;
  341.  
  342.     hf = OpenFile(name, &of, OF_READ | OF_SHARE_DENY_NONE);
  343.     if (hf == HFILE_ERROR)
  344.         switch (GetLastError())
  345.         {
  346.             case ERROR_INVALID_NAME:
  347.             case ERROR_FILENAME_EXCED_RANGE:
  348.                 return FALSE;
  349.             default:
  350.                 return TRUE;
  351.         }
  352.     else
  353.         _lclose(hf);
  354.     return TRUE;
  355. }
  356.  
  357.  
  358.  
  359.  
  360. /**********************/
  361. /* Function map2fat() */        /* Identical to OS/2 version */
  362. /**********************/
  363.  
  364. void map2fat(pathcomp, pEndFAT)
  365.     char *pathcomp, **pEndFAT;
  366. {
  367.     char *ppc = pathcomp;       /* variable pointer to pathcomp */
  368.     char *pEnd = *pEndFAT;      /* variable pointer to buildpathFAT */
  369.     char *pBegin = *pEndFAT;    /* constant pointer to start of this comp. */
  370.     char *last_dot = NULL;      /* last dot not converted to underscore */
  371.     int dotname = FALSE;        /* flag:  path component begins with dot */
  372.                                 /*  ("." and ".." don't count) */
  373.     register unsigned workch;   /* hold the character being tested */
  374.  
  375.  
  376.     /* Only need check those characters which are legal in HPFS but not
  377.      * in FAT:  to get here, must already have passed through mapname.
  378.      * (GRR:  oops, small bug--if char was quoted, no longer have any
  379.      * knowledge of that.)  Also must truncate path component to ensure
  380.      * 8.3 compliance...
  381.      */
  382.     while ((workch = (uch)*ppc++) != 0) {
  383.         switch (workch) {
  384.             case '[':
  385.             case ']':
  386.                 *pEnd++ = '_';      /* convert brackets to underscores */
  387.                 break;
  388.  
  389.             case '.':
  390.                 if (pEnd == *pEndFAT) {   /* nothing appended yet... */
  391.                     if (*ppc == '\0')     /* don't bother appending a */
  392.                         break;            /*  "./" component to the path */
  393.                     else if (*ppc == '.' && ppc[1] == '\0') {   /* "../" */
  394.                         *pEnd++ = '.';    /* add first dot, unchanged... */
  395.                         ++ppc;            /* skip second dot, since it will */
  396.                     } else {              /*  be "added" at end of if-block */
  397.                         *pEnd++ = '_';    /* FAT doesn't allow null filename */
  398.                         dotname = TRUE;   /*  bodies, so map .exrc -> _.exrc */
  399.                     }                     /*  (extra '_' now, "dot" below) */
  400.                 } else if (dotname) {     /* found a second dot, but still */
  401.                     dotname = FALSE;      /*  have extra leading underscore: */
  402.                     *pEnd = '\0';         /*  remove it by shifting chars */
  403.                     pEnd = *pEndFAT + 1;  /*  left one space (e.g., .p1.p2: */
  404.                     while (pEnd[1]) {     /*  __p1 -> _p1_p2 -> _p1.p2 when */
  405.                         *pEnd = pEnd[1];  /*  finished) [opt.:  since first */
  406.                         ++pEnd;           /*  two chars are same, can start */
  407.                     }                     /*  shifting at second position] */
  408.                 }
  409.                 last_dot = pEnd;    /* point at last dot so far... */
  410.                 *pEnd++ = '_';      /* convert dot to underscore for now */
  411.                 break;
  412.  
  413.             default:
  414.                 *pEnd++ = (char)workch;
  415.  
  416.         } /* end switch */
  417.     } /* end while loop */
  418.  
  419.     *pEnd = '\0';                 /* terminate buildpathFAT */
  420.  
  421.     /* NOTE:  keep in mind that pEnd points to the end of the path
  422.      * component, and *pEndFAT still points to the *beginning* of it...
  423.      * Also note that the algorithm does not try to get too fancy:
  424.      * if there are no dots already, the name either gets truncated
  425.      * at 8 characters or the last underscore is converted to a dot
  426.      * (only if more characters are saved that way).  In no case is
  427.      * a dot inserted between existing characters.
  428.      */
  429.     if (last_dot == NULL) {       /* no dots:  check for underscores... */
  430.         char *plu = strrchr(pBegin, '_');   /* pointer to last underscore */
  431.  
  432.         if (plu == NULL) {   /* no dots, no underscores:  truncate at 8 chars */
  433.             *pEndFAT += 8;        /* (or could insert '.' and keep 11...?) */
  434.             if (*pEndFAT > pEnd)
  435.                 *pEndFAT = pEnd;  /* oops...didn't have 8 chars to truncate */
  436.             else
  437.                 **pEndFAT = '\0';
  438.         } else if (MIN(plu - pBegin, 8) + MIN(pEnd - plu - 1, 3) > 8) {
  439.             last_dot = plu;       /* be lazy:  drop through to next if-blk */
  440.         } else if ((pEnd - *pEndFAT) > 8) {
  441.             *pEndFAT += 8;        /* more fits into just basename than if */
  442.             **pEndFAT = '\0';     /*  convert last underscore to dot */
  443.         } else
  444.             *pEndFAT = pEnd;      /* whole thing fits into 8 chars or less */
  445.     }
  446.  
  447.     if (last_dot != NULL) {       /* one dot (or two, in the case of */
  448.         *last_dot = '.';          /*  "..") is OK:  put it back in */
  449.  
  450.         if ((last_dot - pBegin) > 8) {
  451.             char *p=last_dot, *q=pBegin+8;
  452.             int i;
  453.  
  454.             for (i = 0;  (i < 4) && *p;  ++i)  /* too many chars in basename: */
  455.                 *q++ = *p++;                   /*  shift .ext left and trun- */
  456.             *q = '\0';                         /*  cate/terminate it */
  457.             *pEndFAT = q;
  458.         } else if ((pEnd - last_dot) > 4) {    /* too many chars in extension */
  459.             *pEndFAT = last_dot + 4;
  460.             **pEndFAT = '\0';
  461.         } else
  462.             *pEndFAT = pEnd;   /* filename is fine; point at terminating zero */
  463.     }
  464. } /* end function map2fat() */
  465.  
  466.  
  467.  
  468.  
  469. /***********************/       /* Borrowed from os2.c for UnZip 5.1.        */
  470. /* Function checkdir() */       /* Difference: no EA stuff                   */
  471. /***********************/       /*             HPFS stuff works on NTFS too  */
  472.  
  473. int checkdir(pathcomp, flag)
  474.     char *pathcomp;
  475.     int flag;
  476. /*
  477.  * returns:  1 - (on APPEND_NAME) truncated filename
  478.  *           2 - path doesn't exist, not allowed to create
  479.  *           3 - path doesn't exist, tried to create and failed; or
  480.  *               path exists and is not a directory, but is supposed to be
  481.  *           4 - path is too long
  482.  *          10 - can't allocate memory for filename buffers
  483.  */
  484. {
  485.     static int rootlen = 0;      /* length of rootpath */
  486.     static char *rootpath;       /* user's "extract-to" directory */
  487.     static char *buildpathHPFS;  /* full path (so far) to extracted file, */
  488.     static char *buildpathFAT;   /*  both HPFS/EA (main) and FAT versions */
  489.     static char *endHPFS;        /* corresponding pointers to end of */
  490.     static char *endFAT;         /*  buildpath ('\0') */
  491.  
  492. #   define FN_MASK   7
  493. #   define FUNCTION  (flag & FN_MASK)
  494.  
  495.  
  496.  
  497. /*---------------------------------------------------------------------------
  498.     APPEND_DIR:  append the path component to the path being built and check
  499.     for its existence.  If doesn't exist and we are creating directories, do
  500.     so for this one; else signal success or error as appropriate.
  501.   ---------------------------------------------------------------------------*/
  502.  
  503.     if (FUNCTION == APPEND_DIR) {
  504.         char *p = pathcomp;
  505.         int too_long=FALSE;
  506.  
  507.         Trace((stderr, "appending dir segment [%s]\n", pathcomp));
  508.         while ((*endHPFS = *p++) != '\0')     /* copy to HPFS filename */
  509.             ++endHPFS;
  510.         if (IsFileNameValid(buildpathHPFS)) {
  511.             p = pathcomp;
  512.             while ((*endFAT = *p++) != '\0')  /* copy to FAT filename, too */
  513.                 ++endFAT;
  514.         } else {
  515. /* GRR:  check error return? */
  516.             map2fat(pathcomp, &endFAT);  /* map, put in FAT fn, update endFAT */
  517.         }
  518.  
  519.         /* GRR:  could do better check, see if overrunning buffer as we go:
  520.          * check endHPFS-buildpathHPFS after each append, set warning variable
  521.          * if within 20 of FILNAMSIZ; then if var set, do careful check when
  522.          * appending.  Clear variable when begin new path. */
  523.  
  524.         /* next check:  need to append '/', at least one-char name, '\0' */
  525.         if ((endHPFS-buildpathHPFS) > FILNAMSIZ-3)
  526.             too_long = TRUE;                 /* check if extracting dir? */
  527.         if (stat(buildpathFAT, &statbuf))    /* path doesn't exist */
  528.         {
  529.             if (!create_dirs) {   /* told not to create (freshening) */
  530.                 free(buildpathHPFS);
  531.                 free(buildpathFAT);
  532.                 return 2;         /* path doesn't exist:  nothing to do */
  533.             }
  534.             if (too_long) {   /* GRR:  should allow FAT extraction w/o EAs */
  535.                 FPRINTF(stderr, "checkdir error:  path too long: %s\n",
  536.                   buildpathHPFS);
  537.                 fflush(stderr);
  538.                 free(buildpathHPFS);
  539.                 free(buildpathFAT);
  540.                 return 4;         /* no room for filenames:  fatal */
  541.             }
  542.             if (MKDIR(buildpathFAT, 0777) == -1) {   /* create the directory */
  543.                 FPRINTF(stderr, "checkdir error:  can't create %s\n\
  544.                  unable to process %s.\n", buildpathFAT, filename);
  545.                 fflush(stderr);
  546.                 free(buildpathHPFS);
  547.                 free(buildpathFAT);
  548.                 return 3;      /* path didn't exist, tried to create, failed */
  549.             }
  550.             created_dir = TRUE;
  551.         } else if (!S_ISDIR(statbuf.st_mode)) {
  552.             FPRINTF(stderr, "checkdir error:  %s exists but is not directory\n\
  553.                  unable to process %s.\n", buildpathFAT, filename);
  554.             fflush(stderr);
  555.             free(buildpathHPFS);
  556.             free(buildpathFAT);
  557.             return 3;          /* path existed but wasn't dir */
  558.         }
  559.         if (too_long) {
  560.             FPRINTF(stderr, "checkdir error:  path too long: %s\n",
  561.               buildpathHPFS);
  562.             fflush(stderr);
  563.             free(buildpathHPFS);
  564.             free(buildpathFAT);
  565.             return 4;         /* no room for filenames:  fatal */
  566.         }
  567.         *endHPFS++ = '/';
  568.         *endFAT++ = '/';
  569.         *endHPFS = *endFAT = '\0';
  570.         Trace((stderr, "buildpathHPFS now = [%s]\n", buildpathHPFS));
  571.         Trace((stderr, "buildpathFAT now =  [%s]\n", buildpathFAT));
  572.         return 0;
  573.  
  574.     } /* end if (FUNCTION == APPEND_DIR) */
  575.  
  576. /*---------------------------------------------------------------------------
  577.     GETPATH:  copy full FAT path to the string pointed at by pathcomp (want
  578.     filename to reflect name used on disk, not EAs; if full path is HPFS,
  579.     buildpathFAT and buildpathHPFS will be identical).  Also free both paths.
  580.   ---------------------------------------------------------------------------*/
  581.  
  582.     if (FUNCTION == GETPATH) {
  583.         Trace((stderr, "getting and freeing FAT path [%s]\n", buildpathFAT));
  584.         Trace((stderr, "freeing HPFS path [%s]\n", buildpathHPFS));
  585.         strcpy(pathcomp, buildpathFAT);
  586.         free(buildpathFAT);
  587.         free(buildpathHPFS);
  588.         buildpathHPFS = buildpathFAT = endHPFS = endFAT = NULL;
  589.         return 0;
  590.     }
  591.  
  592. /*---------------------------------------------------------------------------
  593.     APPEND_NAME:  assume the path component is the filename; append it and
  594.     return without checking for existence.
  595.   ---------------------------------------------------------------------------*/
  596.  
  597.     if (FUNCTION == APPEND_NAME) {
  598.         char *p = pathcomp;
  599.         int error = 0;
  600.  
  601.         Trace((stderr, "appending filename [%s]\n", pathcomp));
  602.         while ((*endHPFS = *p++) != '\0') {    /* copy to HPFS filename */
  603.             ++endHPFS;
  604.             if ((endHPFS-buildpathHPFS) >= FILNAMSIZ) {
  605.                 *--endHPFS = '\0';
  606.                 FPRINTF(stderr, "checkdir warning:  path too long; truncating\n\
  607.                    %s\n                -> %s\n", filename, buildpathHPFS);
  608.                 fflush(stderr);
  609.                 error = 1;   /* filename truncated */
  610.             }
  611.         }
  612.  
  613.         if ( pInfo->vollabel || IsFileNameValid(buildpathHPFS)) {
  614.             p = pathcomp;
  615.             while ((*endFAT = *p++) != '\0')   /* copy to FAT filename, too */
  616.                 ++endFAT;
  617.         } else {
  618.             map2fat(pathcomp, &endFAT);  /* map, put in FAT fn, update endFAT */
  619.         }
  620.         Trace((stderr, "buildpathHPFS: %s\nbuildpathFAT:  %s\n",
  621.           buildpathHPFS, buildpathFAT));
  622.  
  623.         return error;  /* could check for existence, prompt for new name... */
  624.  
  625.     } /* end if (FUNCTION == APPEND_NAME) */
  626.  
  627. /*---------------------------------------------------------------------------
  628.     INIT:  allocate and initialize buffer space for the file currently being
  629.     extracted.  If file was renamed with an absolute path, don't prepend the
  630.     extract-to path.
  631.   ---------------------------------------------------------------------------*/
  632.  
  633.     if (FUNCTION == INIT) {
  634.  
  635. /* HG: variable not used here */
  636. /*      char *p;              */
  637.  
  638.         Trace((stderr, "initializing buildpathHPFS and buildpathFAT to "));
  639.         if ((buildpathHPFS = (char *)malloc(fnlen+rootlen+1)) == NULL)
  640.             return 10;
  641.         if ((buildpathFAT = (char *)malloc(fnlen+rootlen+1)) == NULL) {
  642.             free(buildpathHPFS);
  643.             return 10;
  644.         }
  645.         if (pInfo->vollabel) {  /* use root or renamed path, but don't store */
  646. /* GRR:  for network drives, do strchr() and return IZ_VOL_LABEL if not [1] */
  647.             if (renamed_fullpath && pathcomp[1] == ':')
  648.                 *buildpathHPFS = ToLower(*pathcomp);
  649.             else if (!renamed_fullpath && rootpath && rootpath[1] == ':')
  650.                 *buildpathHPFS = ToLower(*rootpath);
  651.             else {
  652.                 char tmpN[MAX_PATH], *tmpP;
  653.                 if (GetFullPathName(".", MAX_PATH, tmpN, &tmpP) > MAX_PATH)
  654.                 { /* by definition of MAX_PATH we should never get here */
  655.                     FPRINTF(stderr,
  656.                             "checkdir warning: current dir path too long\n");
  657.                     return 1;   /* can't get drive letter */
  658.                 }
  659.                 nLabelDrive = *tmpN - 'a' + 1;
  660.                 *buildpathHPFS = (char)(nLabelDrive - 1 + 'a');
  661.             }
  662.             nLabelDrive = *buildpathHPFS - 'a' + 1;     /* save for mapname() */
  663.             if (volflag == 0 || *buildpathHPFS < 'a' ||   /* no labels/bogus? */
  664.                 (volflag == 1 && !isfloppy(nLabelDrive))) {  /* -$:  no fixed */
  665.                 free(buildpathHPFS);
  666.                 free(buildpathFAT);
  667.                 return IZ_VOL_LABEL;   /* skipping with message */
  668.             }
  669.             *buildpathHPFS = '\0';
  670.         } else if (renamed_fullpath)   /* pathcomp = valid data */
  671.             strcpy(buildpathHPFS, pathcomp);
  672.         else if (rootlen > 0)
  673.             strcpy(buildpathHPFS, rootpath);
  674.         else
  675.             *buildpathHPFS = '\0';
  676.         endHPFS = buildpathHPFS;
  677.         endFAT = buildpathFAT;
  678.         while ((*endFAT = *endHPFS) != '\0') {
  679.             ++endFAT;
  680.             ++endHPFS;
  681.         }
  682.         Trace((stderr, "[%s]\n", buildpathHPFS));
  683.         return 0;
  684.     }
  685.  
  686. /*---------------------------------------------------------------------------
  687.     ROOT:  if appropriate, store the path in rootpath and create it if neces-
  688.     sary; else assume it's a zipfile member and return.  This path segment
  689.     gets used in extracting all members from every zipfile specified on the
  690.     command line.  Note that under OS/2 and MS-DOS, if a candidate extract-to
  691.     directory specification includes a drive letter (leading "x:"), it is
  692.     treated just as if it had a trailing '/'--that is, one directory level
  693.     will be created if the path doesn't exist, unless this is otherwise pro-
  694.     hibited (e.g., freshening).
  695.   ---------------------------------------------------------------------------*/
  696.  
  697. #if (!defined(SFX) || defined(SFX_EXDIR))
  698.     if (FUNCTION == ROOT) {
  699.         Trace((stderr, "initializing root path to [%s]\n", pathcomp));
  700.         if (pathcomp == NULL) {
  701.             rootlen = 0;
  702.             return 0;
  703.         }
  704.         if ((rootlen = strlen(pathcomp)) > 0) {
  705.             int had_trailing_pathsep=FALSE, has_drive=FALSE, xtra=2;
  706.  
  707.             if (isalpha(pathcomp[0]) && pathcomp[1] == ':')
  708.                 has_drive = TRUE;   /* drive designator */
  709.             if (pathcomp[rootlen-1] == '/') {
  710.                 pathcomp[--rootlen] = '\0';
  711.                 had_trailing_pathsep = TRUE;
  712.             }
  713.             if (has_drive && (rootlen == 2)) {
  714.                 if (!had_trailing_pathsep)   /* i.e., original wasn't "x:/" */
  715.                     xtra = 3;      /* room for '.' + '/' + 0 at end of "x:" */
  716.             } else if (rootlen > 0) {     /* need not check "x:." and "x:/" */
  717.                 if (SSTAT(pathcomp, &statbuf) || !S_ISDIR(statbuf.st_mode)) {
  718.                     /* path does not exist */
  719.                     if (!create_dirs                 /* || iswild(pathcomp) */
  720. #ifdef OLD_EXDIR
  721.                                      || (!has_drive && !had_trailing_pathsep)
  722. #endif
  723.                                                                              ) {
  724.                         rootlen = 0;
  725.                         return 2;   /* treat as stored file */
  726.                     }
  727.                     /* create directory (could add loop here to scan pathcomp
  728.                      * and create more than one level, but really necessary?) */
  729.                     if (MKDIR(pathcomp, 0777) == -1) {
  730.                         FPRINTF(stderr,
  731.                           "checkdir:  can't create extraction directory: %s\n",
  732.                           pathcomp);
  733.                         fflush(stderr);
  734.                         rootlen = 0;   /* path didn't exist, tried to create, */
  735.                         return 3;  /* failed:  file exists, or need 2+ levels */
  736.                     }
  737.                 }
  738.             }
  739.             if ((rootpath = (char *)malloc(rootlen+xtra)) == NULL) {
  740.                 rootlen = 0;
  741.                 return 10;
  742.             }
  743.             strcpy(rootpath, pathcomp);
  744.             if (xtra == 3)                  /* had just "x:", make "x:." */
  745.                 rootpath[rootlen++] = '.';
  746.             rootpath[rootlen++] = '/';
  747.             rootpath[rootlen] = '\0';
  748.         }
  749.         Trace((stderr, "rootpath now = [%s]\n", rootpath));
  750.         return 0;
  751.     }
  752. #endif /* !SFX || SFX_EXDIR */
  753.  
  754. /*---------------------------------------------------------------------------
  755.     END:  free rootpath, immediately prior to program exit.
  756.   ---------------------------------------------------------------------------*/
  757.  
  758.     if (FUNCTION == END) {
  759.         Trace((stderr, "freeing rootpath\n"));
  760.         if (rootlen > 0)
  761.             free(rootpath);
  762.         return 0;
  763.     }
  764.  
  765.     return 99;  /* should never reach */
  766.  
  767. } /* end function checkdir() */
  768.  
  769.  
  770.  
  771.  
  772. #ifndef SFX
  773.  
  774. /************************/
  775. /*  Function do_wild()  */   /* identical to OS/2 version */
  776. /************************/
  777.  
  778. char *do_wild(wildspec)
  779.     char *wildspec;         /* only used first time on a given dir */
  780. {
  781.     static struct direct *dir = NULL;
  782.     static char *dirname, *wildname, matchname[FILNAMSIZ];
  783.     static int firstcall=TRUE, have_dirname, dirnamelen;
  784.     struct direct *file;
  785.  
  786.  
  787.     /* Even when we're just returning wildspec, we *always* do so in
  788.      * matchname[]--calling routine is allowed to append four characters
  789.      * to the returned string, and wildspec may be a pointer to argv[].
  790.      */
  791.     if (firstcall) {        /* first call:  must initialize everything */
  792.         firstcall = FALSE;
  793.  
  794.         /* break the wildspec into a directory part and a wildcard filename */
  795.         if ((wildname = strrchr(wildspec, '/')) == NULL &&
  796.             (wildname = strrchr(wildspec, ':')) == NULL) {
  797.             dirname = ".";
  798.             dirnamelen = 1;
  799.             have_dirname = FALSE;
  800.             wildname = wildspec;
  801.         } else {
  802.             ++wildname;     /* point at character after '/' or ':' */
  803.             dirnamelen = wildname - wildspec;
  804.             if ((dirname = (char *)malloc(dirnamelen+1)) == NULL) {
  805.                 FPRINTF(stderr, "warning:  can't allocate wildcard buffers\n");
  806.                 strcpy(matchname, wildspec);
  807.                 return matchname;   /* but maybe filespec was not a wildcard */
  808.             }
  809.             strncpy(dirname, wildspec, dirnamelen);
  810.             dirname[dirnamelen] = '\0';   /* terminate for strcpy below */
  811.             have_dirname = TRUE;
  812.         }
  813.         Trace((stderr, "do_wild:  dirname = [%s]\n", dirname));
  814.  
  815.         if ((dir = opendir(dirname)) != NULL) {
  816.             while ((file = readdir(dir)) != NULL) {
  817.                 Trace((stderr, "do_wild:  readdir returns %s\n", file->d_name));
  818.                 if (match(file->d_name, wildname, 1)) {  /* 1 == ignore case */
  819.                     Trace((stderr, "do_wild:  match() succeeds\n"));
  820.                     if (have_dirname) {
  821.                         strcpy(matchname, dirname);
  822.                         strcpy(matchname+dirnamelen, file->d_name);
  823.                     } else
  824.                         strcpy(matchname, file->d_name);
  825.                     return matchname;
  826.                 }
  827.             }
  828.             /* if we get to here directory is exhausted, so close it */
  829.             closedir(dir);
  830.             dir = NULL;
  831.         }
  832.         Trace((stderr, "do_wild:  opendir(%s) returns NULL\n", dirname));
  833.  
  834.         /* return the raw wildspec in case that works (e.g., directory not
  835.          * searchable, but filespec was not wild and file is readable) */
  836.         strcpy(matchname, wildspec);
  837.         return matchname;
  838.     }
  839.  
  840.     /* last time through, might have failed opendir but returned raw wildspec */
  841.     if (dir == NULL) {
  842.         firstcall = TRUE;  /* nothing left to try--reset for new wildspec */
  843.         if (have_dirname)
  844.             free(dirname);
  845.         return (char *)NULL;
  846.     }
  847.  
  848.     /* If we've gotten this far, we've read and matched at least one entry
  849.      * successfully (in a previous call), so dirname has been copied into
  850.      * matchname already.
  851.      */
  852.     while ((file = readdir(dir)) != NULL)
  853.         if (match(file->d_name, wildname, 1)) {   /* 1 == ignore case */
  854.             if (have_dirname) {
  855.                 /* strcpy(matchname, dirname); */
  856.                 strcpy(matchname+dirnamelen, file->d_name);
  857.             } else
  858.                 strcpy(matchname, file->d_name);
  859.             return matchname;
  860.         }
  861.  
  862.     closedir(dir);     /* have read at least one dir entry; nothing left */
  863.     dir = NULL;
  864.     firstcall = TRUE;  /* reset for new wildspec */
  865.     if (have_dirname)
  866.         free(dirname);
  867.     return (char *)NULL;
  868.  
  869. } /* end function do_wild() */
  870.  
  871. #endif /* !SFX */
  872.  
  873.  
  874.  
  875.  
  876. /************************/
  877. /*  Function mapname()  */
  878. /************************/
  879.  
  880. /*
  881.  * There are presently two possibilities in OS/2:  the output filesystem is
  882.  * FAT, or it is HPFS.  If the former, we need to map to FAT, obviously, but
  883.  * we *also* must map to HPFS and store that version of the name in extended
  884.  * attributes.  Either way, we need to map to HPFS, so the main mapname
  885.  * routine does that.  In the case that the output file system is FAT, an
  886.  * extra filename-mapping routine is called in checkdir().  While it should
  887.  * be possible to determine the filesystem immediately upon entry to mapname(),
  888.  * it is conceivable that the DOS APPEND utility could be added to OS/2 some-
  889.  * day, allowing a FAT directory to be APPENDed to an HPFS drive/path.  There-
  890.  * fore we simply check the filesystem at each path component.
  891.  *
  892.  * Note that when alternative IFS's become available/popular, everything will
  893.  * become immensely more complicated.  For example, a Minix filesystem would
  894.  * have limited filename lengths like FAT but no extended attributes in which
  895.  * to store the longer versions of the names.  A BSD Unix filesystem would
  896.  * support paths of length 1024 bytes or more, but it is not clear that FAT
  897.  * EAs would allow such long .LONGNAME fields or that OS/2 would properly
  898.  * restore such fields when moving files from FAT to the new filesystem.
  899.  *
  900.  * GRR:  some or all of the following chars should be checked in either
  901.  *       mapname (HPFS) or map2fat (FAT), depending:  ,=^+'"[]<>|\t&
  902.  */
  903.  
  904. int mapname(renamed)  /* return 0 if no error, 1 if caution (filename trunc), */
  905.     int renamed;      /* 2 if warning (skip file because dir doesn't exist), */
  906. {                     /* 3 if error (skip file), 10 if no memory (skip file), */
  907.                       /* IZ_VOL_LABEL if can't do vol label, IZ_CREATED_DIR */
  908.     char pathcomp[FILNAMSIZ];   /* path-component buffer */
  909.     char *pp, *cp=NULL;         /* character pointers */
  910.     char *lastsemi = NULL;      /* pointer to last semi-colon in pathcomp */
  911.     int quote = FALSE;          /* flag:  next char is literal */
  912.     int error = 0;
  913.     register unsigned workch;   /* hold the character being tested */
  914.  
  915.  
  916. /*---------------------------------------------------------------------------
  917.     Initialize various pointers and counters and stuff.
  918.   ---------------------------------------------------------------------------*/
  919.  
  920.     /* can create path as long as not just freshening, or if user told us */
  921.     create_dirs = (!fflag || renamed);
  922.  
  923.     created_dir = FALSE;        /* not yet */
  924.     renamed_fullpath = FALSE;
  925.     fnlen = strlen(filename);
  926.  
  927.     if (renamed) {
  928.         cp = filename - 1;      /* point to beginning of renamed name... */
  929.         while (*++cp)
  930.             if (*cp == '\\')    /* convert backslashes to forward */
  931.                 *cp = '/';
  932.         cp = filename;
  933.         /* use temporary rootpath if user gave full pathname */
  934.         if (filename[0] == '/') {
  935.             renamed_fullpath = TRUE;
  936.             pathcomp[0] = '/';  /* copy the '/' and terminate */
  937.             pathcomp[1] = '\0';
  938.             ++cp;
  939.         } else if (isalpha(filename[0]) && filename[1] == ':') {
  940.             renamed_fullpath = TRUE;
  941.             pp = pathcomp;
  942.             *pp++ = *cp++;      /* copy the "d:" (+ '/', possibly) */
  943.             *pp++ = *cp++;
  944.             if (*cp == '/')
  945.                 *pp++ = *cp++;  /* otherwise add "./"? */
  946.             *pp = '\0';
  947.         }
  948.     }
  949.  
  950.     /* pathcomp is ignored unless renamed_fullpath is TRUE: */
  951.     if ((error = checkdir(pathcomp, INIT)) != 0)    /* initialize path buffer */
  952.         return error;           /* ...unless no mem or vol label on hard disk */
  953.  
  954.     *pathcomp = '\0';           /* initialize translation buffer */
  955.     pp = pathcomp;              /* point to translation buffer */
  956.     if (!renamed) {             /* cp already set if renamed */
  957.         if (jflag)              /* junking directories */
  958.             cp = (char *)strrchr(filename, '/');
  959.         if (cp == NULL)             /* no '/' or not junking dirs */
  960.             cp = filename;          /* point to internal zipfile-member pathname */
  961.         else
  962.             ++cp;                   /* point to start of last component of path */
  963.     }
  964.  
  965. /*---------------------------------------------------------------------------
  966.     Begin main loop through characters in filename.
  967.   ---------------------------------------------------------------------------*/
  968.  
  969.     while ((workch = (uch)*cp++) != 0) {
  970.  
  971.         if (quote) {              /* if character quoted, */
  972.             *pp++ = (char)workch; /*  include it literally */
  973.             quote = FALSE;
  974.         } else
  975.             switch (workch) {
  976.             case '/':             /* can assume -j flag not given */
  977.                 *pp = '\0';
  978.                 if ((error = checkdir(pathcomp, APPEND_DIR)) > 1)
  979.                     return error;
  980.                 pp = pathcomp;    /* reset conversion buffer for next piece */
  981.                 lastsemi = NULL;  /* leave directory semi-colons alone */
  982.                 break;
  983.  
  984.             case ':':
  985.                 *pp++ = '_';      /* drive names not stored in zipfile, */
  986.                 break;            /*  so no colons allowed */
  987.  
  988.             case ';':             /* start of VMS version? */
  989.                 lastsemi = pp;    /* remove VMS version later... */
  990.                 *pp++ = ';';      /*  but keep semicolon for now */
  991.                 break;
  992.  
  993.             case '\026':          /* control-V quote for special chars */
  994.                 quote = TRUE;     /* set flag for next character */
  995.                 break;
  996.  
  997.             case ' ':             /* keep spaces unless specifically */
  998.                 /* NT cannot create filenames with spaces on FAT volumes */
  999.                 if (sflag || IsVolumeOldFAT(filename))
  1000.                     *pp++ = '_';
  1001.                 else
  1002.                     *pp++ = ' ';
  1003.                 break;
  1004.  
  1005.             default:
  1006.                 /* allow European characters in filenames: */
  1007.                 if (isprint(workch) || (128 <= workch && workch <= 254))
  1008.                     *pp++ = (char)workch;
  1009.             } /* end switch */
  1010.  
  1011.     } /* end while loop */
  1012.  
  1013.     *pp = '\0';                   /* done with pathcomp:  terminate it */
  1014.  
  1015.     /* if not saving them, remove VMS version numbers (appended "###") */
  1016.     if (!V_flag && lastsemi) {
  1017.         pp = lastsemi + 1;        /* semi-colon was kept:  expect #'s after */
  1018.         while (isdigit((uch)(*pp)))
  1019.             ++pp;
  1020.         if (*pp == '\0')          /* only digits between ';' and end:  nuke */
  1021.             *lastsemi = '\0';
  1022.     }
  1023.  
  1024. /*---------------------------------------------------------------------------
  1025.     Report if directory was created (and no file to create:  filename ended
  1026.     in '/'), check name to be sure it exists, and combine path and name be-
  1027.     fore exiting.
  1028.   ---------------------------------------------------------------------------*/
  1029.  
  1030.     if (filename[fnlen-1] == '/') {
  1031.         checkdir(filename, GETPATH);
  1032.         if (created_dir && QCOND2) {
  1033. /* GRR:  trailing '/'?  need to strip or not? */
  1034.             FPRINTF(stdout, "   creating: %-22s\n", filename);
  1035.             /* HG: are we setting the date&time on a newly created dir?  */
  1036.             /*     Not quite sure how to do this. It does not seem to    */
  1037.             /*     be done in the MS-DOS version of mapname().           */
  1038.             return IZ_CREATED_DIR;   /* dir time already set */
  1039.         }
  1040.         return 2;   /* dir existed already; don't look for data to extract */
  1041.     }
  1042.  
  1043.     if (*pathcomp == '\0') {
  1044.         FPRINTF(stderr, "mapname:  conversion of %s failed\n", filename);
  1045.         return 3;
  1046.     }
  1047.  
  1048.     checkdir(pathcomp, APPEND_NAME);   /* returns 1 if truncated:  care? */
  1049.     checkdir(filename, GETPATH);
  1050.     Trace((stderr, "mapname returns with filename = [%s] (error = %d)\n\n",
  1051.       filename, error));
  1052.  
  1053.     if (pInfo->vollabel) {   /* set the volume label now */
  1054.         char drive[3];
  1055.  
  1056.         /* Build a drive string, e.g. "b:" */
  1057.         drive[0] = 'a' + nLabelDrive - 1;
  1058.         drive[1] = ':';
  1059.         drive[2] = '\0';
  1060.         if (QCOND2)
  1061.             FPRINTF(stdout, "labelling %s %-22s\n", drive, filename);
  1062.         if (!SetVolumeLabel(drive, filename)) {
  1063.             FPRINTF(stderr, "mapname:  error setting volume label\n");
  1064.             return 3;
  1065.         }
  1066.         return 2;   /* success:  skip the "extraction" quietly */
  1067.     }
  1068.  
  1069.     return error;
  1070.  
  1071. } /* end function mapname() */
  1072.  
  1073.  
  1074.  
  1075.  
  1076.  
  1077. #ifndef SFX
  1078.  
  1079. /************************/
  1080. /*  Function version()  */
  1081. /************************/
  1082.  
  1083. void version()
  1084. {
  1085.     extern char Far  CompiledWith[];
  1086. #if defined(_MSC_VER)
  1087.     char buf[80];
  1088. #endif
  1089.  
  1090.     PRINTF(LoadFarString(CompiledWith),
  1091.  
  1092. #ifdef _MSC_VER  /* MSC == VC++, but what about SDK compiler? */
  1093.       (sprintf(buf, "Microsoft C %d.%02d ", _MSC_VER/100, _MSC_VER%100), buf),
  1094. #  if (_MSC_VER >= 800)
  1095.         "(Visual C++)",
  1096. #  else
  1097.         "(bad version)",
  1098. #  endif
  1099. #else
  1100.       "unknown compiler (SDK?)", "",
  1101. #endif
  1102.  
  1103.       "Windows NT", " (32-bit)",
  1104.  
  1105. #ifdef __DATE__
  1106.       " on ", __DATE__
  1107. #else
  1108.       "", ""
  1109. #endif
  1110.       );
  1111.  
  1112.     return;
  1113.  
  1114. } /* end function version() */
  1115.  
  1116. #endif /* !SFX */
  1117.