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