Subversion Repositories Kolibri OS

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
6725 siemargl 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           /* 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 */