home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 9 Archive / 09-Archive.zip / unzip52.zip / win32 / win32.c < prev   
C/C++ Source or Header  |  1996-04-23  |  46KB  |  1,250 lines

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