os2.c
上传用户:andy_li
上传日期:2007-01-06
资源大小:1019k
文件大小:69k
源码类别:

压缩解压

开发平台:

MultiPlatform

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