Subversion Repositories Kolibri OS

Rev

Blame | Last modification | View Log | Download | RSS feed

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