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

  1. /*---------------------------------------------------------------------------
  2.  
  3.   os2.c
  4.  
  5.   OS/2-specific routines for use with Info-ZIP's UnZip 5.1 and later.
  6.  
  7.   This file contains the OS/2 versions of the file name/attribute/time/etc
  8.   code.  Most or all of the routines which make direct use of OS/2 system
  9.   calls (i.e., the non-lowercase routines) are Kai Uwe Rommel's.  The read-
  10.   dir() suite was written by Michael Rendell and ported to OS/2 by Kai Uwe;
  11.   it is in the public domain.
  12.  
  13.   Contains:  GetCountryInfo()
  14.              GetFileTime()
  15.              SetPathInfo()
  16.              IsEA()
  17.              SetEAs()
  18.              SizeOfEAs()
  19.              GetLoadPath()
  20.              opendir()
  21.              closedir()
  22.              readdir()
  23.              [ seekdir() ]             not used
  24.              [ telldir() ]             not used
  25.              free_dircontents()
  26.              getdirent()
  27.              IsFileSystemFAT()
  28.              do_wild()
  29.              mapattr()
  30.              mapname()
  31.              checkdir()
  32.              isfloppy()
  33.              IsFileNameValid()
  34.              map2fat()
  35.              SetLongNameEA()
  36.              close_outfile()
  37.              check_for_newer()
  38.              dateformat()
  39.              version()
  40.              InitNLS()
  41.              IsUpperNLS()
  42.              ToLowerNLS()
  43.              StringLower()
  44.              DebugMalloc()
  45.  
  46.   ---------------------------------------------------------------------------*/
  47.  
  48.  
  49. #include "unzip.h"
  50. char *StringLower(char *szArg);
  51.  
  52. /* local prototypes */
  53. static int   isfloppy        __((int nDrive));
  54. static int   IsFileNameValid __((char *name));
  55. static void  map2fat         __((char *pathcomp, char **pEndFAT));
  56. static int   SetLongNameEA   __((char *name, char *longname));
  57. static void  InitNLS         __((void));
  58.  
  59. #ifndef __EMX__
  60. #  if (_MSC_VER >= 600) || defined(__IBMC__)
  61. #    include <direct.h>          /* have special MSC/IBM C mkdir prototype */
  62. #  else                          /* own prototype because dir.h conflicts? */
  63.      int mkdir(const char *path);
  64. #  endif
  65. #  define MKDIR(path,mode)   mkdir(path)
  66. #else
  67. #  define MKDIR(path,mode)   mkdir(path,mode)
  68. #endif
  69.  
  70. #define INCL_NOPM
  71. #define INCL_DOSNLS
  72. #define INCL_DOSPROCESS
  73. #define INCL_DOSDEVICES
  74. #define INCL_DOSDEVIOCTL
  75. #define INCL_DOSERRORS
  76. #include <os2.h>
  77.  
  78. #ifdef __32BIT__
  79.  
  80. USHORT DosDevIOCtl32(PVOID pData, USHORT cbData, PVOID pParms, USHORT cbParms,
  81.                      USHORT usFunction, USHORT usCategory, HFILE hDevice)
  82. {
  83.   ULONG ulParmLengthInOut = cbParms, ulDataLengthInOut = cbData;
  84.   return (USHORT) DosDevIOCtl(hDevice, usCategory, usFunction,
  85.                               pParms, cbParms, &ulParmLengthInOut,
  86.                               pData, cbData, &ulDataLengthInOut);
  87. }
  88.  
  89. #  define DosDevIOCtl DosDevIOCtl32
  90. #else
  91. #  define DosDevIOCtl DosDevIOCtl2
  92. #endif
  93.  
  94.  
  95. #define EAID     0x0009
  96.  
  97.  
  98. typedef struct
  99. {
  100.   ush nID;
  101.   ush nSize;
  102.   ulg lSize;
  103. }
  104. EAHEADER, *PEAHEADER;
  105.  
  106.  
  107. #ifndef __32BIT__
  108.  
  109. typedef struct
  110. {
  111.   ULONG oNextEntryOffset;
  112.   BYTE fEA;
  113.   BYTE cbName;
  114.   USHORT cbValue;
  115.   CHAR szName[1];
  116. }
  117. FEA2, *PFEA2;
  118.  
  119. typedef struct
  120. {
  121.   ULONG cbList;
  122.   FEA2 list[1];
  123. }
  124. FEA2LIST, *PFEA2LIST;
  125.  
  126.  
  127. #define DosSetPathInfo(p1, p2, p3, p4, p5) \
  128.         DosSetPathInfo(p1, p2, p3, p4, p5, 0)
  129. #define DosQueryPathInfo(p1, p2, p3, p4) \
  130.         DosQPathInfo(p1, p2, p3, p4, 0)
  131. #define DosMapCase DosCaseMap
  132. #define DosQueryCtryInfo DosGetCtryInfo
  133.  
  134. #endif /* !__32BIT__ */
  135.  
  136.  
  137.  
  138.  
  139.  
  140. /*
  141.  * @(#) dir.h 1.4 87/11/06   Public Domain.
  142.  */
  143.  
  144. #define MAXNAMLEN  256
  145. #define MAXPATHLEN 256
  146.  
  147. #define A_RONLY    0x01
  148. #define A_HIDDEN   0x02
  149. #define A_SYSTEM   0x04
  150. #define A_LABEL    0x08
  151. #define A_DIR      0x10
  152. #define A_ARCHIVE  0x20
  153.  
  154.  
  155. struct direct
  156. {
  157.   ino_t    d_ino;                   /* a bit of a farce */
  158.   int      d_reclen;                /* more farce */
  159.   int      d_namlen;                /* length of d_name */
  160.   char     d_name[MAXNAMLEN + 1];   /* null terminated */
  161.   /* nonstandard fields */
  162.   long     d_size;                  /* size in bytes */
  163.   unsigned d_mode;                  /* MS-DOS or OS/2 file attributes */
  164.   unsigned d_time;
  165.   unsigned d_date;
  166. };
  167.  
  168. /* The fields d_size and d_mode are extensions by me (Kai Uwe Rommel).  The
  169.  * find_first and find_next calls deliver these data without any extra cost.
  170.  * If these data are needed, the fields save a lot of extra calls to stat()
  171.  * (each stat() again performs a find_first call !).
  172.  */
  173.  
  174. struct _dircontents
  175. {
  176.   char *_d_entry;
  177.   long _d_size;
  178.   unsigned _d_mode, _d_time, _d_date;
  179.   struct _dircontents *_d_next;
  180. };
  181.  
  182. typedef struct _dirdesc
  183. {
  184.   int  dd_id;                   /* uniquely identify each open directory */
  185.   long dd_loc;                  /* where we are in directory entry is this */
  186.   struct _dircontents *dd_contents;   /* pointer to contents of dir */
  187.   struct _dircontents *dd_cp;         /* pointer to current position */
  188. }
  189. DIR;
  190.  
  191.  
  192. extern DIR *opendir(char *);
  193. extern struct direct *readdir(DIR *);
  194. extern void seekdir(DIR *, long);
  195. extern long telldir(DIR *);
  196. extern void closedir(DIR *);
  197. #define rewinddir(dirp) seekdir(dirp, 0L)
  198.  
  199. int IsFileSystemFAT(char *dir);
  200. char *StringLower(char *);
  201.  
  202.  
  203.  
  204.  
  205. /*
  206.  * @(#)dir.c 1.4 87/11/06 Public Domain.
  207.  */
  208.  
  209. #ifdef __32BIT__
  210. #  define DosFindFirst(p1, p2, p3, p4, p5, p6) \
  211.           DosFindFirst(p1, p2, p3, p4, p5, p6, 1)
  212. #else
  213. #  define DosQueryCurrentDisk DosQCurDisk
  214. #  define DosQueryFSAttach(p1, p2, p3, p4, p5) \
  215.           DosQFSAttach(p1, p2, p3, p4, p5, 0)
  216. #  define DosQueryPathInfo(p1, p2, p3, p4) \
  217.           DosQPathInfo(p1, p2, p3, p4, 0)
  218. #  define DosSetPathInfo(p1, p2, p3, p4, p5) \
  219.           DosSetPathInfo(p1, p2, p3, p4, p5, 0)
  220. #  define DosEnumAttribute(p1, p2, p3, p4, p5, p6, p7) \
  221.           DosEnumAttribute(p1, p2, p3, p4, p5, p6, p7, 0)
  222. #  define DosFindFirst(p1, p2, p3, p4, p5, p6) \
  223.           DosFindFirst(p1, p2, p3, p4, p5, p6, 0)
  224. #  define DosMapCase DosCaseMap
  225. #endif
  226.  
  227. #ifndef S_IFMT
  228. #  define S_IFMT 0xF000
  229. #endif
  230.  
  231.  
  232. #ifdef __WATCOMC__
  233.    unsigned char __near _osmode = OS2_MODE;
  234. #endif
  235.  
  236. #ifndef SFX
  237.    static char *getdirent(char *);
  238.    static void free_dircontents(struct _dircontents *);
  239.    static int attributes = A_DIR | A_HIDDEN | A_SYSTEM;
  240. #  ifdef __32BIT__
  241.      static HDIR hdir;
  242.      static ULONG count;
  243.      static FILEFINDBUF3 find;
  244. #  else
  245.      static HDIR hdir;            /* GRR:  is there a difference?  HDIR2?? */
  246.      static USHORT count;
  247.      static FILEFINDBUF find;
  248. #  endif
  249. #endif /* !SFX */
  250.  
  251.  
  252.  
  253.  
  254. static int created_dir;        /* used by mapname(), checkdir() */
  255. static int renamed_fullpath;   /* ditto */
  256. static int fnlen;              /* ditto */
  257. #ifdef __32BIT__
  258.    static ULONG nLabelDrive;   /* ditto */
  259. #else
  260.    static USHORT nLabelDrive;
  261. #endif
  262. static int longnameEA;         /* checkdir(), close_outfile() */
  263. static char *lastpathcomp;     /* ditto */
  264.  
  265.  
  266. int GetCountryInfo(void)
  267. {
  268.     COUNTRYINFO ctryi;
  269.     COUNTRYCODE ctryc;
  270. #ifdef __32BIT__
  271.     ULONG cbInfo;
  272. #else
  273.     USHORT cbInfo;
  274. #endif
  275.  
  276.   ctryc.country = ctryc.codepage = 0;
  277.  
  278.   if ( DosQueryCtryInfo(sizeof(ctryi), &ctryc, &ctryi, &cbInfo) != NO_ERROR )
  279.     return 0;
  280.  
  281.   return ctryi.fsDateFmt;
  282. }
  283.  
  284.  
  285. long GetFileTime(char *name)
  286. {
  287. #ifdef __32BIT__
  288.   FILESTATUS3 fs;
  289. #else
  290.   FILESTATUS fs;
  291. #endif
  292.   USHORT nDate, nTime;
  293.  
  294.   if ( DosQueryPathInfo(name, 1, (PBYTE) &fs, sizeof(fs)) )
  295.     return -1;
  296.  
  297.   nDate = * (USHORT *) &fs.fdateLastWrite;
  298.   nTime = * (USHORT *) &fs.ftimeLastWrite;
  299.  
  300.   return ((ULONG) nDate) << 16 | nTime;
  301. }
  302.  
  303.  
  304. void SetPathInfo(char *path, ush moddate, ush modtime, int flags)
  305. {
  306.   union {
  307.     FDATE fd;               /* system file date record */
  308.     ush zdate;              /* date word */
  309.   } ud;
  310.   union {
  311.     FTIME ft;               /* system file time record */
  312.     ush ztime;              /* time word */
  313.   } ut;
  314.   FILESTATUS fs;
  315.   USHORT nLength;
  316.   char szName[CCHMAXPATH];
  317.  
  318.   strcpy(szName, path);
  319.   nLength = strlen(szName);
  320.   if (szName[nLength - 1] == '/')
  321.     szName[nLength - 1] = 0;
  322.  
  323.   if ( DosQueryPathInfo(szName, FIL_STANDARD, (PBYTE) &fs, sizeof(fs)) )
  324.     return;
  325.  
  326.   ud.zdate = moddate;
  327.   ut.ztime = modtime;
  328.   fs.fdateLastWrite = fs.fdateCreation = ud.fd;
  329.   fs.ftimeLastWrite = fs.ftimeCreation = ut.ft;
  330.  
  331.   if ( flags != -1 )
  332.     fs.attrFile = flags; /* hidden, system, archive, read-only */
  333.  
  334.   DosSetPathInfo(szName, FIL_STANDARD, (PBYTE) &fs, sizeof(fs), 0);
  335. }
  336.  
  337.  
  338. typedef struct
  339. {
  340.   ULONG cbList;               /* length of value + 22 */
  341. #ifdef __32BIT__
  342.   ULONG oNext;
  343. #endif
  344.   BYTE fEA;                   /* 0 */
  345.   BYTE cbName;                /* length of ".LONGNAME" = 9 */
  346.   USHORT cbValue;             /* length of value + 4 */
  347.   BYTE szName[10];            /* ".LONGNAME" */
  348.   USHORT eaType;              /* 0xFFFD for length-preceded ASCII */
  349.   USHORT eaSize;              /* length of value */
  350.   BYTE szValue[CCHMAXPATH];
  351. }
  352. FEALST;
  353.  
  354.  
  355. int IsEA(void *extra_field)
  356. {
  357.   EAHEADER *pEAblock = (PEAHEADER) extra_field;
  358.   return extra_field != NULL && pEAblock -> nID == EAID;
  359. }
  360.  
  361.  
  362. void SetEAs(char *path, void *eablock)
  363. {
  364.   EAHEADER *pEAblock = (PEAHEADER) eablock;
  365. #ifdef __32BIT__
  366.   EAOP2 eaop;
  367.   PFEA2LIST pFEA2list;
  368. #else
  369.   EAOP eaop;
  370.   PFEALIST pFEAlist;
  371.   PFEA pFEA;
  372.   PFEA2LIST pFEA2list;
  373.   PFEA2 pFEA2;
  374.   ULONG nLength2;
  375. #endif
  376.   USHORT nLength;
  377.   char szName[CCHMAXPATH];
  378.  
  379.   if ( !IsEA(eablock) )
  380.     return;
  381.  
  382.   strcpy(szName, path);
  383.   nLength = strlen(szName);
  384.   if (szName[nLength - 1] == '/')
  385.     szName[nLength - 1] = 0;
  386.  
  387.   if ( (pFEA2list = (PFEA2LIST) malloc((size_t) pEAblock -> lSize)) == NULL )
  388.     return;
  389.  
  390.   if ( memextract((char *) pFEA2list, pEAblock -> lSize,
  391.                   (char *) (pEAblock + 1), 
  392.                   pEAblock -> nSize - sizeof(pEAblock -> lSize)) )
  393.   {
  394.     free(pFEA2list);
  395.     return;
  396.   }
  397.  
  398. #ifdef __32BIT__
  399.   eaop.fpGEA2List = NULL;
  400.   eaop.fpFEA2List = pFEA2list;
  401. #else
  402.   pFEAlist  = (PVOID) pFEA2list;
  403.   pFEA2 = pFEA2list -> list;
  404.   pFEA  = pFEAlist  -> list;
  405.  
  406.   do
  407.   {
  408.     nLength2 = pFEA2 -> oNextEntryOffset;
  409.     nLength = sizeof(FEA) + pFEA2 -> cbName + 1 + pFEA2 -> cbValue;
  410.  
  411.     memcpy(pFEA, (PCH) pFEA2 + sizeof(pFEA2 -> oNextEntryOffset), nLength);
  412.  
  413.     pFEA2 = (PFEA2) ((PCH) pFEA2 + nLength2);
  414.     pFEA = (PFEA) ((PCH) pFEA + nLength);
  415.   }
  416.   while ( nLength2 != 0 );
  417.  
  418.   pFEAlist -> cbList = (PCH) pFEA - (PCH) pFEAlist;
  419.  
  420.   eaop.fpGEAList = NULL;
  421.   eaop.fpFEAList = pFEAlist;
  422. #endif
  423.  
  424.   eaop.oError = 0;
  425.   DosSetPathInfo(szName, FIL_QUERYEASIZE, (PBYTE) &eaop, sizeof(eaop), 0);
  426.  
  427.   if (!tflag && (qflag < 2))
  428.     PRINTF(" (%ld bytes EA's)", pFEA2list -> cbList);
  429.  
  430.   free(pFEA2list);
  431. }
  432.  
  433.  
  434. ulg SizeOfEAs(void *extra_field)
  435. {
  436.     EAHEADER *pEAblock = (PEAHEADER)extra_field;
  437.  
  438.     if (extra_field != NULL  &&  makeword((uch *)&pEAblock->nID) == EAID)
  439.         return makelong((uch *)&pEAblock->lSize);
  440.  
  441.     return 0L;
  442. }
  443.  
  444.  
  445.  
  446.  
  447.  
  448. #ifdef SFX
  449.  
  450. char *GetLoadPath(void) 
  451. {
  452. #ifdef __32BIT__ /* generic for 32-bit API */
  453.  
  454.   PTIB pptib;
  455.   PPIB pppib;
  456.   char *szPath;
  457.  
  458.   DosGetInfoBlocks(&pptib, &pppib);
  459.   szPath = pppib -> pib_pchenv;
  460.  
  461.   while (*szPath) /* find end of process environment */
  462.     szPath = strchr(szPath, 0) + 1;
  463.  
  464.   return szPath + 1; /* .exe file name follows environment */
  465.  
  466. #else /* 16-bit, specific for MS C 6.00, note: requires large data model */
  467.  
  468.   extern char _far *_pgmptr;
  469.   return _pgmptr;
  470.  
  471. #endif
  472. } /* end function GetLoadPath() */
  473.  
  474.  
  475.  
  476.  
  477.  
  478. #else /* !SFX */
  479.  
  480. DIR *opendir(char *name)
  481. {
  482.   struct stat statb;
  483.   DIR *dirp;
  484.   char c;
  485.   char *s;
  486.   struct _dircontents *dp;
  487.   char nbuf[MAXPATHLEN + 1];
  488.   int len;
  489.  
  490.   strcpy(nbuf, name);
  491.   if ((len = strlen(nbuf)) == 0)
  492.     return NULL;
  493.  
  494.   if ( ((c = nbuf[len - 1]) == '\\' || c == '/') && (len > 1) )
  495.   {
  496.     nbuf[len - 1] = 0;
  497.     --len;
  498.  
  499.     if ( nbuf[len - 1] == ':' )
  500.     {
  501.       strcpy(nbuf+len, "\\.");
  502.       len += 2;
  503.     }
  504.   }
  505.   else
  506.     if ( nbuf[len - 1] == ':' )
  507.     {
  508.       strcpy(nbuf+len, ".");
  509.       ++len;
  510.     }
  511.  
  512.   /* GRR:  Borland and Watcom C return non-zero on wildcards... < 0 ? */
  513.   if (stat(nbuf, &statb) < 0 || (statb.st_mode & S_IFMT) != S_IFDIR)
  514.   {
  515.     Trace((stderr, "opendir:  stat(%s) returns negative or not directory\n",
  516.       nbuf));
  517.     return NULL;
  518.   }
  519.  
  520.   if ( (dirp = malloc(sizeof(DIR))) == NULL )
  521.     return NULL;
  522.  
  523.   if ( nbuf[len - 1] == '.' && (len == 1 || nbuf[len - 2] != '.') )
  524.     strcpy(nbuf+len-1, "*");
  525.   else
  526.     if ( ((c = nbuf[len - 1]) == '\\' || c == '/') && (len == 1) )
  527.       strcpy(nbuf+len, "*");
  528.     else
  529.       strcpy(nbuf+len, "\\*");
  530.  
  531.   /* len is no longer correct (but no longer needed) */
  532.   Trace((stderr, "opendir:  nbuf = [%s]\n", nbuf));
  533.  
  534.   dirp -> dd_loc = 0;
  535.   dirp -> dd_contents = dirp -> dd_cp = NULL;
  536.  
  537.   if ((s = getdirent(nbuf)) == NULL)
  538.     return dirp;
  539.  
  540.   do
  541.   {
  542.     if (((dp = malloc(sizeof(struct _dircontents))) == NULL) ||
  543.         ((dp -> _d_entry = malloc(strlen(s) + 1)) == NULL)      )
  544.     {
  545.       if (dp)
  546.         free(dp);
  547.       free_dircontents(dirp -> dd_contents);
  548.  
  549.       return NULL;
  550.     }
  551.  
  552.     if (dirp -> dd_contents)
  553.     {
  554.       dirp -> dd_cp -> _d_next = dp;
  555.       dirp -> dd_cp = dirp -> dd_cp -> _d_next;
  556.     }
  557.     else
  558.       dirp -> dd_contents = dirp -> dd_cp = dp;
  559.  
  560.     strcpy(dp -> _d_entry, s);
  561.     dp -> _d_next = NULL;
  562.  
  563.     dp -> _d_size = find.cbFile;
  564.     dp -> _d_mode = find.attrFile;
  565.     dp -> _d_time = *(unsigned *) &(find.ftimeLastWrite);
  566.     dp -> _d_date = *(unsigned *) &(find.fdateLastWrite);
  567.   }
  568.   while ((s = getdirent(NULL)) != NULL);
  569.  
  570.   dirp -> dd_cp = dirp -> dd_contents;
  571.  
  572.   return dirp;
  573. }
  574.  
  575.  
  576. void closedir(DIR * dirp)
  577. {
  578.   free_dircontents(dirp -> dd_contents);
  579.   free(dirp);
  580. }
  581.  
  582.  
  583. struct direct *readdir(DIR * dirp)
  584. {
  585.   static struct direct dp;
  586.  
  587.   if (dirp -> dd_cp == NULL)
  588.     return NULL;
  589.  
  590.   dp.d_namlen = dp.d_reclen =
  591.     strlen(strcpy(dp.d_name, dirp -> dd_cp -> _d_entry));
  592.  
  593.   dp.d_ino = 0;
  594.  
  595.   dp.d_size = dirp -> dd_cp -> _d_size;
  596.   dp.d_mode = dirp -> dd_cp -> _d_mode;
  597.   dp.d_time = dirp -> dd_cp -> _d_time;
  598.   dp.d_date = dirp -> dd_cp -> _d_date;
  599.  
  600.   dirp -> dd_cp = dirp -> dd_cp -> _d_next;
  601.   dirp -> dd_loc++;
  602.  
  603.   return &dp;
  604. }
  605.  
  606.  
  607.  
  608. #if 0  /* not used in unzip; retained for possibly future use */
  609.  
  610. void seekdir(DIR * dirp, long off)
  611. {
  612.   long i = off;
  613.   struct _dircontents *dp;
  614.  
  615.   if (off >= 0)
  616.   {
  617.     for (dp = dirp -> dd_contents; --i >= 0 && dp; dp = dp -> _d_next);
  618.  
  619.     dirp -> dd_loc = off - (i + 1);
  620.     dirp -> dd_cp = dp;
  621.   }
  622. }
  623.  
  624.  
  625. long telldir(DIR * dirp)
  626. {
  627.   return dirp -> dd_loc;
  628. }
  629.  
  630. #endif /* 0 */
  631.  
  632.  
  633.  
  634. static void free_dircontents(struct _dircontents * dp)
  635. {
  636.   struct _dircontents *odp;
  637.  
  638.   while (dp)
  639.   {
  640.     if (dp -> _d_entry)
  641.       free(dp -> _d_entry);
  642.  
  643.     dp = (odp = dp) -> _d_next;
  644.     free(odp);
  645.   }
  646. }
  647.  
  648.  
  649. static char *getdirent(char *dir)
  650. {
  651.   int done;
  652.   static int lower;
  653.  
  654.   if (dir != NULL)
  655.   {                                    /* get first entry */
  656.     hdir = HDIR_SYSTEM;
  657.     count = 1;
  658.     done = DosFindFirst(dir, &hdir, attributes, &find, sizeof(find), &count);
  659.     lower = IsFileSystemFAT(dir);
  660.   }
  661.   else                                 /* get next entry */
  662.     done = DosFindNext(hdir, &find, sizeof(find), &count);
  663.  
  664.   if (done == 0)
  665.   {
  666.     if ( lower )
  667.       StringLower(find.achName);
  668.     return find.achName;
  669.   }
  670.   else
  671.   {
  672.     DosFindClose(hdir);
  673.     return NULL;
  674.   }
  675. }
  676.  
  677.  
  678.  
  679. int IsFileSystemFAT(char *dir)     /* FAT / HPFS detection */
  680. {
  681.   static USHORT nLastDrive=(USHORT)(-1), nResult;
  682.   ULONG lMap;
  683.   BYTE bData[64], bName[3];
  684. #ifdef __32BIT__
  685.   ULONG nDrive, cbData;
  686.   PFSQBUFFER2 pData = (PFSQBUFFER2) bData;
  687. #else
  688.   USHORT nDrive, cbData;
  689.   PFSQBUFFER pData = (PFSQBUFFER) bData;
  690. #endif
  691.  
  692.   if ( _osmode == DOS_MODE )
  693.     return TRUE;
  694.   else
  695.   {
  696.     /* We separate FAT and HPFS+other file systems here.
  697.        at the moment I consider other systems to be similar to HPFS,
  698.        i.e. support long file names and case sensitive */
  699.  
  700.     if ( isalpha(dir[0]) && (dir[1] == ':') )
  701.       nDrive = toupper(dir[0]) - '@';
  702.     else
  703.       DosQueryCurrentDisk(&nDrive, &lMap);
  704.  
  705.     if ( nDrive == nLastDrive )
  706.       return nResult;
  707.  
  708.     bName[0] = (char) (nDrive + '@');
  709.     bName[1] = ':';
  710.     bName[2] = 0;
  711.  
  712.     nLastDrive = nDrive;
  713.     cbData = sizeof(bData);
  714.  
  715.     if ( !DosQueryFSAttach(bName, 0, FSAIL_QUERYNAME, (PVOID) pData, &cbData) )
  716.       nResult = !strcmp(pData -> szFSDName + pData -> cbName, "FAT");
  717.     else
  718.       nResult = FALSE;
  719.  
  720.     /* End of this ugly code */
  721.     return nResult;
  722.   }
  723. } /* end function IsFileSystemFAT() */
  724.  
  725.  
  726.  
  727.  
  728.  
  729. /************************/
  730. /*  Function do_wild()  */
  731. /************************/
  732.  
  733. char *do_wild(wildspec)
  734.     char *wildspec;         /* only used first time on a given dir */
  735. {
  736.     static DIR *dir = NULL;
  737.     static char *dirname, *wildname, matchname[FILNAMSIZ];
  738.     static int firstcall=TRUE, have_dirname, dirnamelen;
  739.     struct direct *file;
  740.  
  741.  
  742.     /* Even when we're just returning wildspec, we *always* do so in
  743.      * matchname[]--calling routine is allowed to append four characters
  744.      * to the returned string, and wildspec may be a pointer to argv[].
  745.      */
  746.     if (firstcall) {        /* first call:  must initialize everything */
  747.         firstcall = FALSE;
  748.  
  749.         /* break the wildspec into a directory part and a wildcard filename */
  750.         if ((wildname = strrchr(wildspec, '/')) == NULL &&
  751.             (wildname = strrchr(wildspec, ':')) == NULL) {
  752.             dirname = ".";
  753.             dirnamelen = 1;
  754.             have_dirname = FALSE;
  755.             wildname = wildspec;
  756.         } else {
  757.             ++wildname;     /* point at character after '/' or ':' */
  758.             dirnamelen = wildname - wildspec;
  759.             if ((dirname = (char *)malloc(dirnamelen+1)) == NULL) {
  760.                 FPRINTF(stderr, "warning:  can't allocate wildcard buffers\n");
  761.                 strcpy(matchname, wildspec);
  762.                 return matchname;   /* but maybe filespec was not a wildcard */
  763.             }
  764.             strncpy(dirname, wildspec, dirnamelen);
  765.             dirname[dirnamelen] = '\0';   /* terminate for strcpy below */
  766.             have_dirname = TRUE;
  767.         }
  768.         Trace((stderr, "do_wild:  dirname = [%s]\n", dirname));
  769.  
  770.         if ((dir = opendir(dirname)) != NULL) {
  771.             while ((file = readdir(dir)) != NULL) {
  772.                 Trace((stderr, "do_wild:  readdir returns %s\n", file->d_name));
  773.                 if (match(file->d_name, wildname, 1)) {  /* 1 == ignore case */
  774.                     Trace((stderr, "do_wild:  match() succeeds\n"));
  775.                     if (have_dirname) {
  776.                         strcpy(matchname, dirname);
  777.                         strcpy(matchname+dirnamelen, file->d_name);
  778.                     } else
  779.                         strcpy(matchname, file->d_name);
  780.                     return matchname;
  781.                 }
  782.             }
  783.             /* if we get to here directory is exhausted, so close it */
  784.             closedir(dir);
  785.             dir = NULL;
  786.         }
  787.         Trace((stderr, "do_wild:  opendir(%s) returns NULL\n", dirname));
  788.  
  789.         /* return the raw wildspec in case that works (e.g., directory not
  790.          * searchable, but filespec was not wild and file is readable) */
  791.         strcpy(matchname, wildspec);
  792.         return matchname;
  793.     }
  794.  
  795.     /* last time through, might have failed opendir but returned raw wildspec */
  796.     if (dir == NULL) {
  797.         firstcall = TRUE;  /* nothing left to try--reset for new wildspec */
  798.         if (have_dirname)
  799.             free(dirname);
  800.         return (char *)NULL;
  801.     }
  802.  
  803.     /* If we've gotten this far, we've read and matched at least one entry
  804.      * successfully (in a previous call), so dirname has been copied into
  805.      * matchname already.
  806.      */
  807.     while ((file = readdir(dir)) != NULL)
  808.         if (match(file->d_name, wildname, 1)) {   /* 1 == ignore case */
  809.             if (have_dirname) {
  810.                 /* strcpy(matchname, dirname); */
  811.                 strcpy(matchname+dirnamelen, file->d_name);
  812.             } else
  813.                 strcpy(matchname, file->d_name);
  814.             return matchname;
  815.         }
  816.  
  817.     closedir(dir);     /* have read at least one dir entry; nothing left */
  818.     dir = NULL;
  819.     firstcall = TRUE;  /* reset for new wildspec */
  820.     if (have_dirname)
  821.         free(dirname);
  822.     return (char *)NULL;
  823.  
  824. } /* end function do_wild() */
  825.  
  826. #endif /* !SFX */
  827.  
  828.  
  829.  
  830.  
  831.  
  832. /************************/
  833. /*  Function mapattr()  */
  834. /************************/
  835.  
  836. int mapattr()
  837. {
  838.     /* set archive bit (file is not backed up): */
  839.     pInfo->file_attr = (unsigned)(crec.external_file_attributes | 32) & 0xff;
  840.     return 0;
  841. }
  842.  
  843.  
  844.  
  845.  
  846.  
  847. /************************/
  848. /*  Function mapname()  */
  849. /************************/
  850.  
  851. /*
  852.  * There are presently two possibilities in OS/2:  the output filesystem is
  853.  * FAT, or it is HPFS.  If the former, we need to map to FAT, obviously, but
  854.  * we *also* must map to HPFS and store that version of the name in extended
  855.  * attributes.  Either way, we need to map to HPFS, so the main mapname
  856.  * routine does that.  In the case that the output file system is FAT, an
  857.  * extra filename-mapping routine is called in checkdir().  While it should
  858.  * be possible to determine the filesystem immediately upon entry to mapname(),
  859.  * it is conceivable that the DOS APPEND utility could be added to OS/2 some-
  860.  * day, allowing a FAT directory to be APPENDed to an HPFS drive/path.  There-
  861.  * fore we simply check the filesystem at each path component.
  862.  *
  863.  * Note that when alternative IFS's become available/popular, everything will
  864.  * become immensely more complicated.  For example, a Minix filesystem would
  865.  * have limited filename lengths like FAT but no extended attributes in which
  866.  * to store the longer versions of the names.  A BSD Unix filesystem would
  867.  * support paths of length 1024 bytes or more, but it is not clear that FAT
  868.  * EAs would allow such long .LONGNAME fields or that OS/2 would properly
  869.  * restore such fields when moving files from FAT to the new filesystem.
  870.  *
  871.  * GRR:  some or all of the following chars should be checked in either
  872.  *       mapname (HPFS) or map2fat (FAT), depending:  ,=^+'"[]<>|\t&
  873.  */
  874.  
  875. int mapname(renamed)  /* return 0 if no error, 1 if caution (filename trunc), */
  876.     int renamed;      /* 2 if warning (skip file because dir doesn't exist), */
  877. {                     /* 3 if error (skip file), 10 if no memory (skip file), */
  878.                       /* IZ_VOL_LABEL if can't do vol label, IZ_CREATED_DIR */
  879.     char pathcomp[FILNAMSIZ];    /* path-component buffer */
  880.     char *pp, *cp=(char *)NULL;  /* character pointers */
  881.     char *lastsemi=(char *)NULL; /* pointer to last semi-colon in pathcomp */
  882.     int quote = FALSE;           /* flag:  next char is literal */
  883.     int error = 0;
  884.     register unsigned workch;    /* hold the character being tested */
  885.  
  886.  
  887. /*---------------------------------------------------------------------------
  888.     Initialize various pointers and counters and stuff.
  889.   ---------------------------------------------------------------------------*/
  890.  
  891.     /* can create path as long as not just freshening, or if user told us */
  892.     create_dirs = (!fflag || renamed);
  893.  
  894.     created_dir = FALSE;        /* not yet */
  895.     renamed_fullpath = FALSE;
  896.     fnlen = strlen(filename);
  897.  
  898. /* GRR:  for VMS, convert to internal format now or later? or never? */
  899.     if (renamed) {
  900.         cp = filename - 1;      /* point to beginning of renamed name... */
  901.         while (*++cp)
  902.             if (*cp == '\\')    /* convert backslashes to forward */
  903.                 *cp = '/';
  904.         cp = filename;
  905.         /* use temporary rootpath if user gave full pathname */
  906.         if (filename[0] == '/') {
  907.             renamed_fullpath = TRUE;
  908.             pathcomp[0] = '/';  /* copy the '/' and terminate */
  909.             pathcomp[1] = '\0';
  910.             ++cp;
  911.         } else if (isalpha(filename[0]) && filename[1] == ':') {
  912.             renamed_fullpath = TRUE;
  913.             pp = pathcomp;
  914.             *pp++ = *cp++;      /* copy the "d:" (+ '/', possibly) */
  915.             *pp++ = *cp++;
  916.             if (*cp == '/')
  917.                 *pp++ = *cp++;  /* otherwise add "./"? */
  918.             *pp = '\0';
  919.         }
  920.     }
  921.  
  922.     /* pathcomp is ignored unless renamed_fullpath is TRUE: */
  923.     if ((error = checkdir(pathcomp, INIT)) != 0)    /* initialize path buffer */
  924.         return error;           /* ...unless no mem or vol label on hard disk */
  925.  
  926.     *pathcomp = '\0';           /* initialize translation buffer */
  927.     pp = pathcomp;              /* point to translation buffer */
  928.     if (!renamed) {             /* cp already set if renamed */
  929.         if (jflag)              /* junking directories */
  930. /* GRR:  watch out for VMS version... */
  931.             cp = (char *)strrchr(filename, '/');
  932.         if (cp == (char *)NULL)     /* no '/' or not junking dirs */
  933.             cp = filename;          /* point to internal zipfile-member pathname */
  934.         else
  935.             ++cp;                   /* point to start of last component of path */
  936.     }
  937.  
  938. /*---------------------------------------------------------------------------
  939.     Begin main loop through characters in filename.
  940.   ---------------------------------------------------------------------------*/
  941.  
  942.     while ((workch = (uch)*cp++) != 0) {
  943.  
  944.         if (quote) {              /* if character quoted, */
  945.             *pp++ = (char)workch; /*  include it literally */
  946.             quote = FALSE;
  947.         } else
  948.             switch (workch) {
  949.             case '/':             /* can assume -j flag not given */
  950.                 *pp = '\0';
  951.                 if ((error = checkdir(pathcomp, APPEND_DIR)) > 1)
  952.                     return error;
  953.                 pp = pathcomp;    /* reset conversion buffer for next piece */
  954.                 lastsemi = (char *)NULL; /* leave directory semi-colons alone */
  955.                 break;
  956.  
  957.             case ':':
  958.                 *pp++ = '_';      /* drive names not stored in zipfile, */
  959.                 break;            /*  so no colons allowed */
  960.  
  961.             case ';':             /* start of VMS version? */
  962.                 lastsemi = pp;    /* remove VMS version later... */
  963.                 *pp++ = ';';      /*  but keep semicolon for now */
  964.                 break;
  965.  
  966.             case '\026':          /* control-V quote for special chars */
  967.                 quote = TRUE;     /* set flag for next character */
  968.                 break;
  969.  
  970.             case ' ':             /* keep spaces unless specifically */
  971.                 if (sflag)        /*  requested to change to underscore */
  972.                     *pp++ = '_';
  973.                 else
  974.                     *pp++ = ' ';
  975.                 break;
  976.  
  977.             default:
  978.                 /* allow European characters in filenames: */
  979.                 if (isprint(workch) || (128 <= workch && workch <= 254))
  980.                     *pp++ = (char)workch;
  981.             } /* end switch */
  982.  
  983.     } /* end while loop */
  984.  
  985.     *pp = '\0';                   /* done with pathcomp:  terminate it */
  986.  
  987.     /* if not saving them, remove VMS version numbers (appended "###") */
  988.     if (!V_flag && lastsemi) {
  989.         pp = lastsemi + 1;        /* semi-colon was kept:  expect #'s after */
  990.         while (isdigit((uch)(*pp)))
  991.             ++pp;
  992.         if (*pp == '\0')          /* only digits between ';' and end:  nuke */
  993.             *lastsemi = '\0';
  994.     }
  995.  
  996. /*---------------------------------------------------------------------------
  997.     Report if directory was created (and no file to create:  filename ended
  998.     in '/'), check name to be sure it exists, and combine path and name be-
  999.     fore exiting.
  1000.   ---------------------------------------------------------------------------*/
  1001.  
  1002.     if (filename[fnlen-1] == '/') {
  1003.         checkdir(filename, GETPATH);
  1004.         if (created_dir && QCOND2) {
  1005. /* GRR:  trailing '/'?  need to strip or not? */
  1006.             FPRINTF(stdout, "   creating: %-22s\n", filename);
  1007.             if (extra_field)  /* zipfile extra field has extended attributes */
  1008.                 SetEAs(filename, extra_field);
  1009.             SetPathInfo(filename, lrec.last_mod_file_date,
  1010.                                   lrec.last_mod_file_time, -1);
  1011.             return IZ_CREATED_DIR;   /* dir time already set */
  1012.         }
  1013.         return 2;   /* dir existed already; don't look for data to extract */
  1014.     }
  1015.  
  1016.     if (*pathcomp == '\0') {
  1017.         FPRINTF(stderr, "mapname:  conversion of %s failed\n", filename);
  1018.         return 3;
  1019.     }
  1020.  
  1021.     checkdir(pathcomp, APPEND_NAME);   /* returns 1 if truncated:  care? */
  1022.     checkdir(filename, GETPATH);
  1023.     Trace((stderr, "mapname returns with filename = [%s] (error = %d)\n\n",
  1024.       filename, error));
  1025.  
  1026.     if (pInfo->vollabel) {   /* set the volume label now */
  1027.         VOLUMELABEL FSInfoBuf;
  1028. /* GRR:  "VOLUMELABEL" defined for IBM C and emx, but haven't checked MSC... */
  1029.  
  1030.         strcpy(FSInfoBuf.szVolLabel, filename);
  1031.         FSInfoBuf.cch = (BYTE)strlen(FSInfoBuf.szVolLabel);
  1032.  
  1033.         if (QCOND2)
  1034.             FPRINTF(stdout, "labelling %c: %-22s\n",
  1035.               (char)(nLabelDrive + 'a' - 1), filename);
  1036.         if (DosSetFSInfo(nLabelDrive, FSIL_VOLSER, (PBYTE)&FSInfoBuf,
  1037.                          sizeof(VOLUMELABEL)))
  1038.         {
  1039.             FPRINTF(stderr, "mapname:  error setting volume label\n");
  1040.             return 3;
  1041.         }
  1042.         return 2;   /* success:  skip the "extraction" quietly */
  1043.     }
  1044.  
  1045.     return error;
  1046.  
  1047. } /* end function mapname() */
  1048.  
  1049.  
  1050.  
  1051.  
  1052.  
  1053. /***********************/
  1054. /* Function checkdir() */
  1055. /***********************/
  1056.  
  1057. int checkdir(pathcomp, flag)
  1058.     char *pathcomp;
  1059.     int flag;
  1060. /*
  1061.  * returns:  1 - (on APPEND_NAME) truncated filename
  1062.  *           2 - path doesn't exist, not allowed to create
  1063.  *           3 - path doesn't exist, tried to create and failed; or
  1064.  *               path exists and is not a directory, but is supposed to be
  1065.  *           4 - path is too long
  1066.  *          10 - can't allocate memory for filename buffers
  1067.  */
  1068. {
  1069.     static int rootlen = 0;      /* length of rootpath */
  1070.     static char *rootpath;       /* user's "extract-to" directory */
  1071.     static char *buildpathHPFS;  /* full path (so far) to extracted file, */
  1072.     static char *buildpathFAT;   /*  both HPFS/EA (main) and FAT versions */
  1073.     static char *endHPFS;        /* corresponding pointers to end of */
  1074.     static char *endFAT;         /*  buildpath ('\0') */
  1075.  
  1076. #   define FN_MASK   7
  1077. #   define FUNCTION  (flag & FN_MASK)
  1078.  
  1079.  
  1080.  
  1081. /*---------------------------------------------------------------------------
  1082.     APPEND_DIR:  append the path component to the path being built and check
  1083.     for its existence.  If doesn't exist and we are creating directories, do
  1084.     so for this one; else signal success or error as appropriate.
  1085.   ---------------------------------------------------------------------------*/
  1086.  
  1087.     if (FUNCTION == APPEND_DIR) {
  1088.         char *p = pathcomp;
  1089.         int longdirEA, too_long=FALSE;
  1090.  
  1091.         Trace((stderr, "appending dir segment [%s]\n", pathcomp));
  1092.         while ((*endHPFS = *p++) != '\0')     /* copy to HPFS filename */
  1093.             ++endHPFS;
  1094.         if (IsFileNameValid(buildpathHPFS)) {
  1095.             longdirEA = FALSE;
  1096.             p = pathcomp;
  1097.             while ((*endFAT = *p++) != '\0')  /* copy to FAT filename, too */
  1098.                 ++endFAT;
  1099.         } else {
  1100.             longdirEA = TRUE;
  1101. /* GRR:  check error return? */
  1102.             map2fat(pathcomp, &endFAT);  /* map, put in FAT fn, update endFAT */
  1103.         }
  1104.  
  1105.         /* GRR:  could do better check, see if overrunning buffer as we go:
  1106.          * check endHPFS-buildpathHPFS after each append, set warning variable
  1107.          * if within 20 of FILNAMSIZ; then if var set, do careful check when
  1108.          * appending.  Clear variable when begin new path. */
  1109.  
  1110.         /* next check:  need to append '/', at least one-char name, '\0' */
  1111.         if ((endHPFS-buildpathHPFS) > FILNAMSIZ-3)
  1112.             too_long = TRUE;                 /* check if extracting dir? */
  1113. #ifdef MSC /* MSC 6.00 bug:  stat(non-existent-dir) == 0 [exists!] */
  1114.         if (GetFileTime(buildpathFAT) == -1 || stat(buildpathFAT, &statbuf))
  1115. #else
  1116.         if (stat(buildpathFAT, &statbuf))    /* path doesn't exist */
  1117. #endif
  1118.         {
  1119.             if (!create_dirs) {   /* told not to create (freshening) */
  1120.                 free(buildpathHPFS);
  1121.                 free(buildpathFAT);
  1122.                 return 2;         /* path doesn't exist:  nothing to do */
  1123.             }
  1124.             if (too_long) {   /* GRR:  should allow FAT extraction w/o EAs */
  1125.                 FPRINTF(stderr, "checkdir error:  path too long: %s\n",
  1126.                   buildpathHPFS);
  1127.                 fflush(stderr);
  1128.                 free(buildpathHPFS);
  1129.                 free(buildpathFAT);
  1130.                 return 4;         /* no room for filenames:  fatal */
  1131.             }
  1132.             if (MKDIR(buildpathFAT, 0777) == -1) {   /* create the directory */
  1133.                 FPRINTF(stderr, "checkdir error:  can't create %s\n\
  1134.                  unable to process %s.\n", buildpathFAT, filename);
  1135.                 fflush(stderr);
  1136.                 free(buildpathHPFS);
  1137.                 free(buildpathFAT);
  1138.                 return 3;      /* path didn't exist, tried to create, failed */
  1139.             }
  1140.             created_dir = TRUE;
  1141.             /* only set EA if creating directory */
  1142. /* GRR:  need trailing '/' before function call? */
  1143.             if (longdirEA) {
  1144. #ifdef DEBUG
  1145.                 int e =
  1146. #endif
  1147.                   SetLongNameEA(buildpathFAT, pathcomp);
  1148.                 Trace((stderr, "APPEND_DIR:  SetLongNameEA() returns %d\n", e));
  1149.             }
  1150.         } else if (!S_ISDIR(statbuf.st_mode)) {
  1151.             FPRINTF(stderr, "checkdir error:  %s exists but is not directory\n\
  1152.                  unable to process %s.\n", buildpathFAT, filename);
  1153.             fflush(stderr);
  1154.             free(buildpathHPFS);
  1155.             free(buildpathFAT);
  1156.             return 3;          /* path existed but wasn't dir */
  1157.         }
  1158.         if (too_long) {
  1159.             FPRINTF(stderr, "checkdir error:  path too long: %s\n",
  1160.               buildpathHPFS);
  1161.             fflush(stderr);
  1162.             free(buildpathHPFS);
  1163.             free(buildpathFAT);
  1164.             return 4;         /* no room for filenames:  fatal */
  1165.         }
  1166.         *endHPFS++ = '/';
  1167.         *endFAT++ = '/';
  1168.         *endHPFS = *endFAT = '\0';
  1169.         Trace((stderr, "buildpathHPFS now = [%s]\n", buildpathHPFS));
  1170.         Trace((stderr, "buildpathFAT now =  [%s]\n", buildpathFAT));
  1171.         return 0;
  1172.  
  1173.     } /* end if (FUNCTION == APPEND_DIR) */
  1174.  
  1175. /*---------------------------------------------------------------------------
  1176.     GETPATH:  copy full FAT path to the string pointed at by pathcomp (want
  1177.     filename to reflect name used on disk, not EAs; if full path is HPFS,
  1178.     buildpathFAT and buildpathHPFS will be identical).  Also free both paths.
  1179.   ---------------------------------------------------------------------------*/
  1180.  
  1181.     if (FUNCTION == GETPATH) {
  1182.         Trace((stderr, "getting and freeing FAT path [%s]\n", buildpathFAT));
  1183.         Trace((stderr, "freeing HPFS path [%s]\n", buildpathHPFS));
  1184.         strcpy(pathcomp, buildpathFAT);
  1185.         free(buildpathFAT);
  1186.         free(buildpathHPFS);
  1187.         buildpathHPFS = buildpathFAT = endHPFS = endFAT = (char *)NULL;
  1188.         return 0;
  1189.     }
  1190.  
  1191. /*---------------------------------------------------------------------------
  1192.     APPEND_NAME:  assume the path component is the filename; append it and
  1193.     return without checking for existence.
  1194.   ---------------------------------------------------------------------------*/
  1195.  
  1196.     if (FUNCTION == APPEND_NAME) {
  1197.         char *p = pathcomp;
  1198.         int error = 0;
  1199.  
  1200.         Trace((stderr, "appending filename [%s]\n", pathcomp));
  1201.         while ((*endHPFS = *p++) != '\0') {    /* copy to HPFS filename */
  1202.             ++endHPFS;
  1203.             if ((endHPFS-buildpathHPFS) >= FILNAMSIZ) {
  1204.                 *--endHPFS = '\0';
  1205.                 FPRINTF(stderr, "checkdir warning:  path too long; truncating\n\
  1206.                    %s\n                -> %s\n", filename, buildpathHPFS);
  1207.                 fflush(stderr);
  1208.                 error = 1;   /* filename truncated */
  1209.             }
  1210.         }
  1211.  
  1212. /* GRR:  how can longnameEA ever be set before this point???  we don't want
  1213.  * to save the original name to EAs if user renamed it, do we?
  1214.  *
  1215.  * if (!longnameEA && ((longnameEA = !IsFileNameValid(name)) != 0))
  1216.  */
  1217.         if (pInfo->vollabel || IsFileNameValid(buildpathHPFS)) {
  1218.             longnameEA = FALSE;
  1219.             p = pathcomp;
  1220.             while ((*endFAT = *p++) != '\0')   /* copy to FAT filename, too */
  1221.                 ++endFAT;
  1222.         } else {
  1223.             longnameEA = TRUE;
  1224.             if ((lastpathcomp = (char *)malloc(strlen(pathcomp)+1)) ==
  1225.                 (char *)NULL)
  1226.             {
  1227.                 FPRINTF(stderr,
  1228.                   "checkdir warning:  can't save longname EA: out of memory\n");
  1229.                 longnameEA = FALSE;
  1230.                 error = 1;   /* can't set .LONGNAME extended attribute */
  1231.             } else           /* used and freed in close_outfile() */
  1232.                 strcpy(lastpathcomp, pathcomp);
  1233.             map2fat(pathcomp, &endFAT);  /* map, put in FAT fn, update endFAT */
  1234.         }
  1235.         Trace((stderr, "buildpathHPFS: %s\nbuildpathFAT:  %s\n",
  1236.           buildpathHPFS, buildpathFAT));
  1237.  
  1238.         return error;  /* could check for existence, prompt for new name... */
  1239.  
  1240.     } /* end if (FUNCTION == APPEND_NAME) */
  1241.  
  1242. /*---------------------------------------------------------------------------
  1243.     INIT:  allocate and initialize buffer space for the file currently being
  1244.     extracted.  If file was renamed with an absolute path, don't prepend the
  1245.     extract-to path.
  1246.   ---------------------------------------------------------------------------*/
  1247.  
  1248.     if (FUNCTION == INIT) {
  1249.         Trace((stderr, "initializing buildpathHPFS and buildpathFAT to "));
  1250.         if ((buildpathHPFS = (char *)malloc(fnlen+rootlen+1)) == (char *)NULL)
  1251.             return 10;
  1252.         if ((buildpathFAT = (char *)malloc(fnlen+rootlen+1)) == (char *)NULL) {
  1253.             free(buildpathHPFS);
  1254.             return 10;
  1255.         }
  1256.         if (pInfo->vollabel) {  /* use root or renamed path, but don't store */
  1257. /* GRR:  for network drives, do strchr() and return IZ_VOL_LABEL if not [1] */
  1258.             if (renamed_fullpath && pathcomp[1] == ':')
  1259.                 *buildpathHPFS = ToLower(*pathcomp);
  1260.             else if (!renamed_fullpath && rootpath && rootpath[1] == ':')
  1261.                 *buildpathHPFS = ToLower(*rootpath);
  1262.             else {
  1263.                 ULONG lMap;
  1264.                 DosQueryCurrentDisk(&nLabelDrive, &lMap);
  1265.                 *buildpathHPFS = (char)(nLabelDrive - 1 + 'a');
  1266.             }
  1267.             nLabelDrive = *buildpathHPFS - 'a' + 1;     /* save for mapname() */
  1268.             if (volflag == 0 || *buildpathHPFS < 'a' ||   /* no labels/bogus? */
  1269.                 (volflag == 1 && !isfloppy(nLabelDrive))) {  /* -$:  no fixed */
  1270.                 free(buildpathHPFS);
  1271.                 free(buildpathFAT);
  1272.                 return IZ_VOL_LABEL;   /* skipping with message */
  1273.             }
  1274.             *buildpathHPFS = '\0';
  1275.         } else if (renamed_fullpath)   /* pathcomp = valid data */
  1276.             strcpy(buildpathHPFS, pathcomp);
  1277.         else if (rootlen > 0)
  1278.             strcpy(buildpathHPFS, rootpath);
  1279.         else
  1280.             *buildpathHPFS = '\0';
  1281.         endHPFS = buildpathHPFS;
  1282.         endFAT = buildpathFAT;
  1283.         while ((*endFAT = *endHPFS) != '\0') {
  1284.             ++endFAT;
  1285.             ++endHPFS;
  1286.         }
  1287.         Trace((stderr, "[%s]\n", buildpathHPFS));
  1288.         return 0;
  1289.     }
  1290.  
  1291. /*---------------------------------------------------------------------------
  1292.     ROOT:  if appropriate, store the path in rootpath and create it if neces-
  1293.     sary; else assume it's a zipfile member and return.  This path segment
  1294.     gets used in extracting all members from every zipfile specified on the
  1295.     command line.  Note that under OS/2 and MS-DOS, if a candidate extract-to
  1296.     directory specification includes a drive letter (leading "x:"), it is
  1297.     treated just as if it had a trailing '/'--that is, one directory level
  1298.     will be created if the path doesn't exist, unless this is otherwise pro-
  1299.     hibited (e.g., freshening).
  1300.   ---------------------------------------------------------------------------*/
  1301.  
  1302. #if (!defined(SFX) || defined(SFX_EXDIR))
  1303.     if (FUNCTION == ROOT) {
  1304.         Trace((stderr, "initializing root path to [%s]\n", pathcomp));
  1305.         if (pathcomp == (char *)NULL) {
  1306.             rootlen = 0;
  1307.             return 0;
  1308.         }
  1309.         if ((rootlen = strlen(pathcomp)) > 0) {
  1310.             int had_trailing_pathsep=FALSE, has_drive=FALSE, xtra=2;
  1311.  
  1312.             if (isalpha(pathcomp[0]) && pathcomp[1] == ':')
  1313.                 has_drive = TRUE;   /* drive designator */
  1314.             if (pathcomp[rootlen-1] == '/') {
  1315.                 pathcomp[--rootlen] = '\0';
  1316.                 had_trailing_pathsep = TRUE;
  1317.             }
  1318.             if (has_drive && (rootlen == 2)) {
  1319.                 if (!had_trailing_pathsep)   /* i.e., original wasn't "x:/" */
  1320.                     xtra = 3;      /* room for '.' + '/' + 0 at end of "x:" */
  1321.             } else if (rootlen > 0) {     /* need not check "x:." and "x:/" */
  1322. #ifdef MSC      /* MSC 6.00 bug:  stat(non-existent-dir) == 0 [exists!] */
  1323.                 if (GetFileTime(pathcomp) == -1 || 
  1324.                     SSTAT(pathcomp, &statbuf) || !S_ISDIR(statbuf.st_mode))
  1325. #else
  1326.                 if (SSTAT(pathcomp, &statbuf) || !S_ISDIR(statbuf.st_mode))
  1327. #endif
  1328.                 {   /* path does not exist */
  1329.                     if (!create_dirs                 /* || iswild(pathcomp) */
  1330. #ifdef OLD_EXDIR
  1331.                                      || (!has_drive && !had_trailing_pathsep)
  1332. #endif
  1333.                                                                              ) {
  1334.                         rootlen = 0;
  1335.                         return 2;   /* treat as stored file */
  1336.                     }
  1337.                     /* create directory (could add loop here to scan pathcomp
  1338.                      * and create more than one level, but really necessary?) */
  1339.                     if (MKDIR(pathcomp, 0777) == -1) {
  1340.                         FPRINTF(stderr,
  1341.                           "checkdir:  can't create extraction directory: %s\n",
  1342.                           pathcomp);
  1343.                         fflush(stderr);
  1344.                         rootlen = 0;   /* path didn't exist, tried to create, */
  1345.                         return 3;  /* failed:  file exists, or need 2+ levels */
  1346.                     }
  1347.                 }
  1348.             }
  1349.             if ((rootpath = (char *)malloc(rootlen+xtra)) == (char *)NULL) {
  1350.                 rootlen = 0;
  1351.                 return 10;
  1352.             }
  1353.             strcpy(rootpath, pathcomp);
  1354.             if (xtra == 3)                  /* had just "x:", make "x:." */
  1355.                 rootpath[rootlen++] = '.';
  1356.             rootpath[rootlen++] = '/';
  1357.             rootpath[rootlen] = '\0';
  1358.         }
  1359.         Trace((stderr, "rootpath now = [%s]\n", rootpath));
  1360.         return 0;
  1361.     }
  1362. #endif /* !SFX || SFX_EXDIR */
  1363.  
  1364. /*---------------------------------------------------------------------------
  1365.     END:  free rootpath, immediately prior to program exit.
  1366.   ---------------------------------------------------------------------------*/
  1367.  
  1368.     if (FUNCTION == END) {
  1369.         Trace((stderr, "freeing rootpath\n"));
  1370.         if (rootlen > 0)
  1371.             free(rootpath);
  1372.         return 0;
  1373.     }
  1374.  
  1375.     return 99;  /* should never reach */
  1376.  
  1377. } /* end function checkdir() */
  1378.  
  1379.  
  1380.  
  1381.  
  1382.  
  1383. /***********************/
  1384. /* Function isfloppy() */   /* more precisely, is it removable? */
  1385. /***********************/
  1386.  
  1387. static int isfloppy(nDrive)
  1388.     int nDrive;   /* 1 == A:, 2 == B:, etc. */
  1389. {
  1390.     uch ParmList[1] = {0};
  1391.     uch DataArea[1] = {0};
  1392.     char Name[3];
  1393.     HFILE handle;
  1394. #ifdef __32BIT__
  1395.     ULONG rc;
  1396.     ULONG action;
  1397. #else
  1398.     USHORT rc;
  1399.     UINT action;
  1400. #endif
  1401.  
  1402.  
  1403.     Name[0] = (char) (nDrive + 'A' - 1);
  1404.     Name[1] = ':';
  1405.     Name[2] = 0;
  1406.  
  1407.     rc = DosOpen(Name, &handle, &action, 0L, FILE_NORMAL, FILE_OPEN,
  1408.                  OPEN_FLAGS_DASD | OPEN_FLAGS_FAIL_ON_ERROR |
  1409.                  OPEN_ACCESS_READONLY | OPEN_SHARE_DENYNONE, 0L);
  1410.  
  1411.     if (rc == ERROR_NOT_READY)   /* must be removable */
  1412.       return TRUE;
  1413.     else if (rc) {   /* other error:  do default a/b heuristic instead */
  1414.       Trace((stderr, "error in DosOpen(DASD):  guessing...\n", rc));
  1415.       return (nDrive == 1 || nDrive == 2)? TRUE : FALSE;
  1416.     }
  1417.  
  1418.     rc = DosDevIOCtl(DataArea, sizeof(DataArea), ParmList, sizeof(ParmList),
  1419.                      DSK_BLOCKREMOVABLE, IOCTL_DISK, handle);
  1420.     DosClose(handle);
  1421.  
  1422.     if (rc) {   /* again, just check for a/b */
  1423.         Trace((stderr, "error in DosDevIOCtl category IOCTL_DISK, function "
  1424.           "DSK_BLOCKREMOVABLE\n  (rc = 0x%04x):  guessing...\n", rc));
  1425.         return (nDrive == 1 || nDrive == 2)? TRUE : FALSE;
  1426.     } else {
  1427.         return DataArea[0] ? FALSE : TRUE;
  1428.     }
  1429. } /* end function isfloppy() */
  1430.  
  1431.  
  1432.  
  1433.  
  1434.  
  1435. int IsFileNameValid(char *name)
  1436. {
  1437.   HFILE hf;
  1438. #ifdef __32BIT__
  1439.   ULONG uAction;
  1440. #else
  1441.   USHORT uAction;
  1442. #endif
  1443.  
  1444.   switch( DosOpen(name, &hf, &uAction, 0, 0, FILE_OPEN,
  1445.                   OPEN_ACCESS_READONLY | OPEN_SHARE_DENYNONE, 0) )
  1446.   {
  1447.   case ERROR_INVALID_NAME:
  1448.   case ERROR_FILENAME_EXCED_RANGE:
  1449.     return FALSE;
  1450.   case NO_ERROR:
  1451.     DosClose(hf);
  1452.   default:
  1453.     return TRUE;
  1454.   }
  1455. }
  1456.  
  1457.  
  1458.  
  1459.  
  1460.  
  1461. /**********************/
  1462. /* Function map2fat() */
  1463. /**********************/
  1464.  
  1465. void map2fat(pathcomp, pEndFAT)
  1466.     char *pathcomp, **pEndFAT;
  1467. {
  1468.     char *ppc = pathcomp;          /* variable pointer to pathcomp */
  1469.     char *pEnd = *pEndFAT;         /* variable pointer to buildpathFAT */
  1470.     char *pBegin = *pEndFAT;       /* constant pointer to start of this comp. */
  1471.     char *last_dot = (char *)NULL; /* last dot not converted to underscore */
  1472.     int dotname = FALSE;           /* flag:  path component begins with dot */
  1473.                                    /*  ("." and ".." don't count) */
  1474.     register unsigned workch;      /* hold the character being tested */
  1475.  
  1476.  
  1477.     /* Only need check those characters which are legal in HPFS but not
  1478.      * in FAT:  to get here, must already have passed through mapname.
  1479.      * (GRR:  oops, small bug--if char was quoted, no longer have any
  1480.      * knowledge of that.)  Also must truncate path component to ensure
  1481.      * 8.3 compliance...
  1482.      */
  1483.     while ((workch = (uch)*ppc++) != 0) {
  1484.         switch (workch) {
  1485.             case '[':
  1486.             case ']':
  1487.                 *pEnd++ = '_';      /* convert brackets to underscores */
  1488.                 break;
  1489.  
  1490.             case '.':
  1491.                 if (pEnd == *pEndFAT) {   /* nothing appended yet... */
  1492.                     if (*ppc == '\0')     /* don't bother appending a */
  1493.                         break;            /*  "./" component to the path */
  1494.                     else if (*ppc == '.' && ppc[1] == '\0') {   /* "../" */
  1495.                         *pEnd++ = '.';    /* add first dot, unchanged... */
  1496.                         ++ppc;            /* skip second dot, since it will */
  1497.                     } else {              /*  be "added" at end of if-block */
  1498.                         *pEnd++ = '_';    /* FAT doesn't allow null filename */
  1499.                         dotname = TRUE;   /*  bodies, so map .exrc -> _.exrc */
  1500.                     }                     /*  (extra '_' now, "dot" below) */
  1501.                 } else if (dotname) {     /* found a second dot, but still */
  1502.                     dotname = FALSE;      /*  have extra leading underscore: */
  1503.                     *pEnd = '\0';         /*  remove it by shifting chars */
  1504.                     pEnd = *pEndFAT + 1;  /*  left one space (e.g., .p1.p2: */
  1505.                     while (pEnd[1]) {     /*  __p1 -> _p1_p2 -> _p1.p2 when */
  1506.                         *pEnd = pEnd[1];  /*  finished) [opt.:  since first */
  1507.                         ++pEnd;           /*  two chars are same, can start */
  1508.                     }                     /*  shifting at second position] */
  1509.                 }
  1510.                 last_dot = pEnd;    /* point at last dot so far... */
  1511.                 *pEnd++ = '_';      /* convert dot to underscore for now */
  1512.                 break;
  1513.  
  1514.             default:
  1515.                 *pEnd++ = (char)workch;
  1516.  
  1517.         } /* end switch */
  1518.     } /* end while loop */
  1519.  
  1520.     *pEnd = '\0';                 /* terminate buildpathFAT */
  1521.  
  1522.     /* NOTE:  keep in mind that pEnd points to the end of the path
  1523.      * component, and *pEndFAT still points to the *beginning* of it...
  1524.      * Also note that the algorithm does not try to get too fancy:
  1525.      * if there are no dots already, the name either gets truncated
  1526.      * at 8 characters or the last underscore is converted to a dot
  1527.      * (only if more characters are saved that way).  In no case is
  1528.      * a dot inserted between existing characters.
  1529.      */
  1530.     if (last_dot == (char *)NULL) {  /* no dots:  check for underscores... */
  1531.         char *plu = strrchr(pBegin, '_');    /* pointer to last underscore */
  1532.  
  1533.         if (plu == (char *)NULL) { /* no dots, no underscores:  truncate at 8 */
  1534.             *pEndFAT += 8;        /* chars (or could insert '.' and keep 11?) */
  1535.             if (*pEndFAT > pEnd)
  1536.                 *pEndFAT = pEnd;  /* oops...didn't have 8 chars to truncate */
  1537.             else
  1538.                 **pEndFAT = '\0';
  1539.         } else if (MIN(plu - pBegin, 8) + MIN(pEnd - plu - 1, 3) > 8) {
  1540.             last_dot = plu;       /* be lazy:  drop through to next if-blk */
  1541.         } else if ((pEnd - *pEndFAT) > 8) {
  1542.             *pEndFAT += 8;        /* more fits into just basename than if */
  1543.             **pEndFAT = '\0';     /*  convert last underscore to dot */
  1544.         } else
  1545.             *pEndFAT = pEnd;      /* whole thing fits into 8 chars or less */
  1546.     }
  1547.  
  1548.     if (last_dot != (char *)NULL) {   /* one dot (or two, in the case of */
  1549.         *last_dot = '.';              /*  "..") is OK:  put it back in */
  1550.  
  1551.         if ((last_dot - pBegin) > 8) {
  1552.             char *p=last_dot, *q=pBegin+8;
  1553.             int i;
  1554.  
  1555.             for (i = 0;  (i < 4) && *p;  ++i)  /* too many chars in basename: */
  1556.                 *q++ = *p++;                   /*  shift .ext left and trun- */
  1557.             *q = '\0';                         /*  cate/terminate it */
  1558.             *pEndFAT = q;
  1559.         } else if ((pEnd - last_dot) > 4) {    /* too many chars in extension */
  1560.             *pEndFAT = last_dot + 4;
  1561.             **pEndFAT = '\0';
  1562.         } else
  1563.             *pEndFAT = pEnd;   /* filename is fine; point at terminating zero */
  1564.     }
  1565. } /* end function map2fat() */
  1566.  
  1567.  
  1568.  
  1569.  
  1570.  
  1571. int SetLongNameEA(char *name, char *longname)
  1572. {
  1573.   EAOP eaop;
  1574.   FEALST fealst;
  1575.  
  1576.   eaop.fpFEAList = (PFEALIST) &fealst;
  1577.   eaop.fpGEAList = NULL;
  1578.   eaop.oError = 0;
  1579.  
  1580.   strcpy(fealst.szName, ".LONGNAME");
  1581.   strcpy(fealst.szValue, longname);
  1582.  
  1583.   fealst.cbList  = sizeof(fealst) - CCHMAXPATH + strlen(fealst.szValue);
  1584.   fealst.cbName  = (BYTE) strlen(fealst.szName);
  1585.   fealst.cbValue = sizeof(USHORT) * 2 + strlen(fealst.szValue);
  1586.  
  1587. #ifdef __32BIT__
  1588.   fealst.oNext   = 0;
  1589. #endif
  1590.   fealst.fEA     = 0;
  1591.   fealst.eaType  = 0xFFFD;
  1592.   fealst.eaSize  = strlen(fealst.szValue);
  1593.  
  1594.   return DosSetPathInfo(name, FIL_QUERYEASIZE,
  1595.                         (PBYTE) &eaop, sizeof(eaop), 0);
  1596. }
  1597.  
  1598.  
  1599.  
  1600.  
  1601.  
  1602. /****************************/
  1603. /* Function close_outfile() */
  1604. /****************************/
  1605.  
  1606. void close_outfile()   /* only for extracted files, not directories */
  1607. {
  1608.     fclose(outfile);
  1609.  
  1610.     /* set extra fields, both stored-in-zipfile and .LONGNAME flavors */
  1611.     if (extra_field)
  1612.         SetEAs(filename, extra_field);
  1613.  
  1614.     if (longnameEA) {
  1615. #ifdef DEBUG
  1616.         int e =
  1617. #endif
  1618.           SetLongNameEA(filename, lastpathcomp);
  1619.         Trace((stderr, "close_outfile:  SetLongNameEA() returns %d\n", e));
  1620.         free(lastpathcomp);
  1621.     }
  1622.  
  1623.     /* set date/time and permissions */
  1624.     SetPathInfo(filename, lrec.last_mod_file_date,
  1625.                           lrec.last_mod_file_time, pInfo->file_attr);
  1626.  
  1627. } /* end function close_outfile() */
  1628.  
  1629.  
  1630.  
  1631.  
  1632.  
  1633. /******************************/
  1634. /* Function check_for_newer() */
  1635. /******************************/
  1636.  
  1637. int check_for_newer(filename)   /* return 1 if existing file newer or equal; */
  1638.     char *filename;             /*  0 if older; -1 if doesn't exist yet */
  1639. {
  1640.     long existing, archive;
  1641.  
  1642.     if ((existing = GetFileTime(filename)) == -1)
  1643.         return DOES_NOT_EXIST;
  1644.     archive = ((long) lrec.last_mod_file_date) << 16 | lrec.last_mod_file_time;
  1645.  
  1646.     return (existing >= archive);
  1647. }
  1648.  
  1649.  
  1650.  
  1651.  
  1652.  
  1653. #ifndef SFX
  1654.  
  1655. /*************************/
  1656. /* Function dateformat() */
  1657. /*************************/
  1658.  
  1659. int dateformat()
  1660. {
  1661. /*-----------------------------------------------------------------------------
  1662.   For those operating systems which support it, this function returns a value
  1663.   which tells how national convention says that numeric dates are displayed.
  1664.   Return values are DF_YMD, DF_DMY and DF_MDY.
  1665.  -----------------------------------------------------------------------------*/
  1666.  
  1667.     switch (GetCountryInfo()) {
  1668.         case 0:
  1669.             return DF_MDY;
  1670.         case 1:
  1671.             return DF_DMY;
  1672.         case 2:
  1673.             return DF_YMD;
  1674.     }
  1675.     return DF_MDY;   /* default if error */
  1676.  
  1677. } /* end function dateformat() */
  1678.  
  1679.  
  1680.  
  1681.  
  1682.  
  1683. /************************/
  1684. /*  Function version()  */
  1685. /************************/
  1686.  
  1687. void version()
  1688. {
  1689.     extern char Far  CompiledWith[];
  1690. #if defined(__IBMC__) || defined(__WATCOMC__) || defined(_MSC_VER)
  1691.     char buf[80];
  1692. #endif
  1693.  
  1694.     PRINTF(LoadFarString(CompiledWith),
  1695.  
  1696. #ifdef __GNUC__
  1697. #  ifdef __EMX__  /* __EMX__ is defined as "1" only (sigh) */
  1698.       "emx+gcc ", __VERSION__,
  1699. #  else
  1700.       "gcc/2 ", __VERSION__,
  1701. #  endif
  1702. #else
  1703. #ifdef __IBMC__
  1704.       "IBM C Set/2", (sprintf(buf, " %d.%02d", __IBMC__/100,__IBMC__%100), buf),
  1705. #else
  1706. #ifdef __WATCOMC__
  1707.       "Watcom C", (sprintf(buf, " (__WATCOMC__ = %d)", __WATCOMC__), buf),
  1708. #else
  1709. #ifdef __TURBOC__
  1710. #  ifdef __BORLANDC__
  1711.       "Borland C++",
  1712. #    if (__BORLANDC__ < 0x0200)
  1713.         " 1.0",
  1714. #    else
  1715. #    if (__BORLANDC__ == 0x0200)
  1716.         " 2.0",
  1717. #    else
  1718. #    if (__BORLANDC__ == 0x0400)
  1719.         " 3.0",
  1720. #    else
  1721. #    if (__BORLANDC__ == 0x0410)
  1722.         " 3.1",
  1723. #    else
  1724.         " later than 3.1",
  1725. #    endif
  1726. #    endif
  1727. #    endif
  1728. #    endif
  1729. #  else
  1730.       "Turbo C",
  1731. #    if (__TURBOC__ >= 661)
  1732.        "++ 1.0 or later",
  1733. #    else
  1734. #    if (__TURBOC__ == 661)
  1735.        " 3.0?",
  1736. #    else
  1737. #    if (__TURBOC__ == 397)
  1738.        " 2.0",
  1739. #    else
  1740.        " 1.0 or 1.5?",
  1741. #    endif
  1742. #    endif
  1743. #    endif
  1744. #  endif
  1745. #else
  1746. #ifdef MSC
  1747.       "Microsoft C ",
  1748. #  ifdef _MSC_VER
  1749.       (sprintf(buf, "%d.%02d", _MSC_VER/100, _MSC_VER%100), buf),
  1750. #  else
  1751.       "5.1 or earlier",
  1752. #  endif
  1753. #else
  1754.       "unknown compiler", "",
  1755. #endif /* MSC */
  1756. #endif /* __TURBOC__ */
  1757. #endif /* __WATCOMC__ */
  1758. #endif /* __IBMC__ */
  1759. #endif /* __GNUC__ */
  1760.  
  1761.       "OS/2",
  1762.  
  1763. /* GRR:  does IBM C/2 identify itself as IBM rather than Microsoft? */
  1764. #if (defined(MSC) || (defined(__WATCOMC__) && !defined(__386__)))
  1765. #  if defined(M_I86HM) || defined(__HUGE__)
  1766.       " (16-bit, huge)",
  1767. #  else
  1768. #  if defined(M_I86LM) || defined(__LARGE__)
  1769.       " (16-bit, large)",
  1770. #  else
  1771. #  if defined(M_I86MM) || defined(__MEDIUM__)
  1772.       " (16-bit, medium)",
  1773. #  else
  1774. #  if defined(M_I86CM) || defined(__COMPACT__)
  1775.       " (16-bit, compact)",
  1776. #  else
  1777. #  if defined(M_I86SM) || defined(__SMALL__)
  1778.       " (16-bit, small)",
  1779. #  else
  1780. #  if defined(M_I86TM) || defined(__TINY__)
  1781.       " (16-bit, tiny)",
  1782. #  else
  1783.       " (16-bit)",
  1784. #  endif
  1785. #  endif
  1786. #  endif
  1787. #  endif
  1788. #  endif
  1789. #  endif
  1790. #else
  1791.       " 2.x (32-bit)",
  1792. #endif
  1793.  
  1794. #ifdef __DATE__
  1795.       " on ", __DATE__
  1796. #else
  1797.       "", ""
  1798. #endif
  1799.       );
  1800.  
  1801.     /* temporary debugging code for Borland compilers only */
  1802. #ifdef __TURBOC__
  1803.     PRINTF("\t(__TURBOC__ = 0x%04x = %d)\n", __TURBOC__, __TURBOC__);
  1804. #ifdef __BORLANDC__
  1805.     PRINTF("\t(__BORLANDC__ = 0x%04x)\n", __BORLANDC__);
  1806. #else
  1807.     PRINTF("\tdebug(__BORLANDC__ not defined)\n");
  1808. #endif
  1809. #ifdef __TCPLUSPLUS__
  1810.     PRINTF("\t(__TCPLUSPLUS__ = 0x%04x)\n", __TCPLUSPLUS__);
  1811. #else
  1812.     PRINTF("\tdebug(__TCPLUSPLUS__ not defined)\n");
  1813. #endif
  1814. #ifdef __BCPLUSPLUS__
  1815.     PRINTF("\t(__BCPLUSPLUS__ = 0x%04x)\n\n", __BCPLUSPLUS__);
  1816. #else
  1817.     PRINTF("\tdebug(__BCPLUSPLUS__ not defined)\n\n");
  1818. #endif
  1819. #endif /* __TURBOC__ */
  1820.  
  1821. } /* end function version() */
  1822.  
  1823. #endif /* !SFX */
  1824.  
  1825.  
  1826.  
  1827.  
  1828.  
  1829. static unsigned char cUpperCase[256], cLowerCase[256];
  1830. static BOOL bInitialized;
  1831.  
  1832. /* Initialize the tables of upper- and lowercase characters, including
  1833.    handling of country-dependent characters. */
  1834.  
  1835. static void InitNLS(void)
  1836. {
  1837.   unsigned nCnt, nU;
  1838.   COUNTRYCODE cc;
  1839.  
  1840.   bInitialized = TRUE;
  1841.  
  1842.   for ( nCnt = 0; nCnt < 256; nCnt++ )
  1843.     cUpperCase[nCnt] = cLowerCase[nCnt] = (unsigned char) nCnt;
  1844.  
  1845.   cc.country = cc.codepage = 0;
  1846.   DosMapCase(sizeof(cUpperCase), &cc, (PCHAR) cUpperCase);
  1847.  
  1848.   for ( nCnt = 0; nCnt < 256; nCnt++ )
  1849.   {
  1850.     nU = cUpperCase[nCnt];
  1851.     if (nU != nCnt && cLowerCase[nU] == (unsigned char) nU)
  1852.       cLowerCase[nU] = (unsigned char) nCnt;
  1853.   }
  1854.  
  1855.   for ( nCnt = 'A'; nCnt <= 'Z'; nCnt++ )
  1856.     cLowerCase[nCnt] = (unsigned char) (nCnt - 'A' + 'a');
  1857. }
  1858.  
  1859.  
  1860. int IsUpperNLS(int nChr)
  1861. {
  1862.   if (!bInitialized)
  1863.     InitNLS();
  1864.   return (cUpperCase[nChr] == (unsigned char) nChr);
  1865. }
  1866.  
  1867.  
  1868. int ToLowerNLS(int nChr)
  1869. {
  1870.   if (!bInitialized)
  1871.     InitNLS();
  1872.   return cLowerCase[nChr];
  1873. }
  1874.  
  1875.  
  1876. char *StringLower(char *szArg)
  1877. {
  1878.   unsigned char *szPtr;
  1879.  
  1880.   if (!bInitialized)
  1881.     InitNLS();
  1882.   for ( szPtr = szArg; *szPtr; szPtr++ )
  1883.     *szPtr = cLowerCase[*szPtr];
  1884.   return szArg;
  1885. }
  1886.  
  1887.  
  1888. #if defined(__IBMC__) && defined(__DEBUG_ALLOC__)
  1889. void DebugMalloc(void)
  1890. {
  1891.   _dump_allocated(0); /* print out debug malloc memory statistics */
  1892. }
  1893. #endif
  1894.