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