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