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