]> git.saurik.com Git - wxWidgets.git/blob - utils/Install/sfxzip/win32.c
applied patch 422308 (fixes for version info for wxWin DLL)
[wxWidgets.git] / utils / Install / sfxzip / win32.c
1 /*---------------------------------------------------------------------------
2
3 win32.c
4
5 32-bit Windows-specific (NT/95) routines for use with Info-ZIP's UnZip 5.3
6 and later.
7
8 Contains: GetLoadPath()
9 Opendir()
10 Readdir()
11 Closedir()
12 process_defer_NT() process any deferred items
13 SetSD() set security descriptor on file
14 EvalExtraFields() evaluate and process and extra field NOW
15 IsWinNT() indicate type of WIN32 platform
16 test_NTSD() test integrity of NT security data
17 utime2FileTime()
18 FStampIsLocTime()
19 FileTime2utime()
20 VFatFileTime2utime()
21 UTCtime2Localtime()
22 NTtzbugWorkaround()
23 getNTfiletime()
24 close_outfile()
25 stamp_file()
26 isfloppy()
27 NTQueryVolInfo()
28 IsVolumeOldFAT()
29 do_wild()
30 mapattr()
31 mapname()
32 map2fat()
33 checkdir()
34 version()
35 zstat_win32()
36 getch_win32()
37
38 ---------------------------------------------------------------------------*/
39
40
41 #define UNZIP_INTERNAL
42 #include "unzip.h"
43 #include <windows.h> /* must be AFTER unzip.h to avoid struct G problems */
44 #ifdef __RSXNT__
45 # include "rsxntwin.h"
46 #endif
47 #include "nt.h"
48
49 #ifndef FUNZIP /* most of this file is not used with fUnZip */
50
51 #if (defined(__GO32__) || defined(__EMX__) || defined(__CYGWIN32__))
52 # define MKDIR(path,mode) mkdir(path,mode)
53 #else
54 # define MKDIR(path,mode) mkdir(path)
55 #endif
56
57 #ifdef HAVE_WORKING_DIRENT_H
58 # undef HAVE_WORKING_DIRENT_H
59 #endif
60 /* The emxrtl dirent support of (__GO32__ || __EMX__) converts to lowercase! */
61 #if defined(__CYGWIN32__)
62 # define HAVE_WORKING_DIRENT_H
63 #endif
64
65 #ifndef SFX
66 # ifdef HAVE_WORKING_DIRENT_H
67 # include <dirent.h> /* use readdir() */
68 # define zdirent dirent
69 # define zDIR DIR
70 # define Opendir opendir
71 # define Readdir readdir
72 # define Closedir closedir
73 # else /* !HAVE_WORKING_DIRENT_H */
74 typedef struct zdirent {
75 char reserved [21];
76 char ff_attrib;
77 short ff_ftime;
78 short ff_fdate;
79 long size;
80 char d_name[MAX_PATH];
81 int d_first;
82 HANDLE d_hFindFile;
83 } zDIR;
84
85 static zDIR *Opendir (const char *n);
86 static struct zdirent *Readdir (zDIR *d);
87 static void Closedir (zDIR *d);
88 # endif /* ?HAVE_WORKING_DIRENT_H */
89 #endif /* !SFX */
90
91
92 /* Function prototypes */
93 #ifdef NTSD_EAS
94 static int SetSD(__GPRO__ char *path, PVOLUMECAPS VolumeCaps,
95 uch *eb_ptr, unsigned eb_len);
96 static int EvalExtraFields(__GPRO__ char *path, uch *ef_ptr,
97 unsigned ef_len);
98 #endif
99
100 #if (defined(USE_EF_UT_TIME) || defined(NT_TZBUG_WORKAROUND) || \
101 defined(TIMESTAMP))
102 static void utime2FileTime(time_t ut, FILETIME *pft);
103 static int FStampIsLocTime(__GPRO__ const char *path);
104 #endif /* USE_EF_UT_TIME || NT_TZBUG_WORKAROUND || TIMESTAMP */
105 #ifdef NT_TZBUG_WORKAROUND
106 static int FileTime2utime(const FILETIME *pft, time_t *ut);
107 #ifdef W32_STAT_BANDAID
108 static int VFatFileTime2utime(const FILETIME *pft, time_t *ut);
109 #endif
110 static time_t UTCtime2Localtime(time_t utctime);
111 static void NTtzbugWorkaround(time_t ut, FILETIME *pft);
112 #endif /* NT_TZBUG_WORKAROUND */
113
114 static int getNTfiletime (__GPRO__ FILETIME *pModFT, FILETIME *pAccFT,
115 FILETIME *pCreFT);
116 static int isfloppy (int nDrive);
117 static int NTQueryVolInfo (__GPRO__ const char *name);
118 static int IsVolumeOldFAT (__GPRO__ const char *name);
119 static void map2fat (char *pathcomp, char **pEndFAT);
120
121
122 #ifdef __MINGW32__
123 int _CRT_glob = 0; /* suppress command line globbing by C RTL */
124 #endif
125
126 /* static int created_dir; */ /* used by mapname(), checkdir() */
127 /* static int renamed_fullpath; */ /* ditto */
128 /* static int fnlen; */ /* ditto */
129 /* static unsigned nLabelDrive; */ /* ditto */
130
131 extern char Far TruncNTSD[]; /* in extract.c */
132
133
134
135 #ifdef SFX
136
137 /**************************/
138 /* Function GetLoadPath() */
139 /**************************/
140
141 char *GetLoadPath(__GPRO)
142 {
143 #ifdef MSC
144 extern char *_pgmptr;
145 return _pgmptr;
146
147 #else /* use generic API call */
148
149 GetModuleFileName(NULL, G.filename, FILNAMSIZ-1);
150 _ISO_INTERN(G.filename); /* translate to codepage of C rtl's stdio */
151 return G.filename;
152 #endif
153
154 } /* end function GetLoadPath() */
155
156
157
158
159
160 #else /* !SFX */
161
162 #ifndef HAVE_WORKING_DIRENT_H
163
164 /**********************/ /* Borrowed from ZIP 2.0 sources */
165 /* Function Opendir() */ /* Difference: no special handling for */
166 /**********************/ /* hidden or system files. */
167
168 static zDIR *Opendir(n)
169 const char *n; /* directory to open */
170 {
171 zDIR *d; /* malloc'd return value */
172 char *p; /* malloc'd temporary string */
173 WIN32_FIND_DATA fd;
174 extent len = strlen(n);
175
176 /* Start searching for files in directory n */
177
178 if ((d = (zDIR *)malloc(sizeof(zDIR))) == NULL ||
179 (p = malloc(strlen(n) + 5)) == NULL)
180 {
181 if (d != (zDIR *)NULL)
182 free((void *)d);
183 return (zDIR *)NULL;
184 }
185 INTERN_TO_ISO(n, p);
186 if (len > 0) {
187 if (p[len-1] == ':')
188 p[len++] = '.'; /* x: => x:. */
189 else if (p[len-1] == '/' || p[len-1] == '\\')
190 --len; /* foo/ => foo */
191 }
192 strcpy(p+len, "/*");
193
194 if (INVALID_HANDLE_VALUE == (d->d_hFindFile = FindFirstFile(p, &fd))) {
195 free((zvoid *)d);
196 free((zvoid *)p);
197 return NULL;
198 }
199 strcpy(d->d_name, fd.cFileName);
200
201 free((zvoid *)p);
202 d->d_first = 1;
203 return d;
204
205 } /* end of function Opendir() */
206
207
208
209
210 /**********************/ /* Borrowed from ZIP 2.0 sources */
211 /* Function Readdir() */ /* Difference: no special handling for */
212 /**********************/ /* hidden or system files. */
213
214 static struct zdirent *Readdir(d)
215 zDIR *d; /* directory stream from which to read */
216 {
217 /* Return pointer to first or next directory entry, or NULL if end. */
218
219 if ( d->d_first )
220 d->d_first = 0;
221 else
222 {
223 WIN32_FIND_DATA fd;
224
225 if ( !FindNextFile(d->d_hFindFile, &fd) )
226 return NULL;
227
228 ISO_TO_INTERN(fd.cFileName, d->d_name);
229 }
230 return (struct zdirent *)d;
231
232 } /* end of function Readdir() */
233
234
235
236
237 /***********************/
238 /* Function Closedir() */ /* Borrowed from ZIP 2.0 sources */
239 /***********************/
240
241 static void Closedir(d)
242 zDIR *d; /* directory stream to close */
243 {
244 FindClose(d->d_hFindFile);
245 free(d);
246 }
247
248 #endif /* !HAVE_WORKING_DIRENT_H */
249 #endif /* ?SFX */
250
251
252
253
254 #ifdef NTSD_EAS
255
256 /*********************************/
257 /* Function process_defer_NT() */
258 /*********************************/
259
260 void process_defer_NT(__G)
261 __GDEF
262 {
263 /* process deferred items */
264
265 DWORD dir, bytes;
266 DWORD dirfail, bytesfail;
267
268 ProcessDefer(&dir, &bytes, &dirfail, &bytesfail);
269
270 if (!uO.tflag && (uO.qflag < 2)) {
271 if (dir)
272 Info(slide, 0, ((char *)slide,
273 " updated: %lu directory entries with %lu bytes security",
274 (ulg)dir, (ulg)bytes));
275 if (dirfail)
276 Info(slide, 0, ((char *)slide,
277 " failed: %lu directory entries with %lu bytes security",
278 (ulg)dirfail, (ulg)bytesfail));
279 }
280 }
281
282
283
284 /**********************/
285 /* Function SetSD() */ /* return almost-PK errors */
286 /**********************/
287
288 static int SetSD(__G__ path, VolumeCaps, eb_ptr, eb_len)
289 __GDEF
290 char *path;
291 PVOLUMECAPS VolumeCaps;
292 uch *eb_ptr;
293 unsigned eb_len;
294 {
295 ulg ntsd_ucSize;
296 uch *security_data;
297 int error;
298
299 if (eb_ptr == NULL || eb_len < EB_NTSD_L_LEN)
300 return PK_OK; /* not a valid NTSD extra field: assume OK */
301
302 /* check if we know how to handle this version */
303 if (*(eb_ptr + (EB_HEADSIZE+EB_NTSD_VERSION)) > (uch)EB_NTSD_MAX_VER)
304 return PK_OK;
305
306 ntsd_ucSize = makelong(eb_ptr + (EB_HEADSIZE+EB_UCSIZE_P));
307 if (ntsd_ucSize > 0L && eb_len <= (EB_NTSD_L_LEN + EB_CMPRHEADLEN))
308 return IZ_EF_TRUNC; /* no compressed data! */
309
310 /* allocate storage for uncompressed data */
311 security_data = (uch *)malloc((extent)ntsd_ucSize);
312 if (security_data == (uch *)NULL)
313 return PK_MEM4;
314
315 error = memextract(__G__ security_data, ntsd_ucSize,
316 (eb_ptr + (EB_HEADSIZE+EB_NTSD_L_LEN)), (ulg)(eb_len - EB_NTSD_L_LEN));
317
318 if (error == PK_OK) {
319 if (SecuritySet(path, VolumeCaps, security_data)) {
320 error = PK_COOL;
321 if (!uO.tflag && (uO.qflag < 2) &&
322 (!(VolumeCaps->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)))
323 Info(slide, 0, ((char *)slide, " (%ld bytes security)",
324 ntsd_ucSize));
325 }
326 }
327
328 free(security_data);
329 return error;
330 }
331
332
333
334
335 /********************************/ /* scan extra fields for something */
336 /* Function EvalExtraFields() */ /* we happen to know */
337 /********************************/
338
339 static int EvalExtraFields(__G__ path, ef_ptr, ef_len)
340 __GDEF
341 char *path;
342 uch *ef_ptr;
343 unsigned ef_len;
344 {
345 int rc = PK_OK;
346
347 if (!uO.X_flag)
348 return PK_OK; /* user said don't process ACLs; for now, no other
349 extra block types are handled here */
350
351 while (ef_len >= EB_HEADSIZE)
352 {
353 unsigned eb_id = makeword(EB_ID + ef_ptr);
354 unsigned eb_len = makeword(EB_LEN + ef_ptr);
355
356 if (eb_len > (ef_len - EB_HEADSIZE)) {
357 /* discovered some extra field inconsistency! */
358 Trace((stderr,
359 "EvalExtraFields: block length %u > rest ef_size %u\n", eb_len,
360 ef_len - EB_HEADSIZE));
361 break;
362 }
363
364 switch (eb_id)
365 {
366 /* process security descriptor extra data if:
367 Caller is WinNT AND
368 Target local/remote drive supports acls AND
369 Target file is not a directory (else we defer processing
370 until later)
371 */
372 case EF_NTSD:
373 if (IsWinNT()) {
374 VOLUMECAPS VolumeCaps;
375
376 /* provide useful input */
377 VolumeCaps.dwFileAttributes = G.pInfo->file_attr;
378 VolumeCaps.bUsePrivileges = (uO.X_flag > 1);
379
380 /* check target volume capabilities - just fall through
381 * and try if fail */
382 if (GetVolumeCaps(G.rootpath, path, &VolumeCaps) &&
383 !(VolumeCaps.dwFileSystemFlags & FS_PERSISTENT_ACLS))
384 {
385 rc = PK_OK;
386 break;
387 }
388 rc = SetSD(__G__ path, &VolumeCaps, ef_ptr, eb_len);
389 } else
390 rc = PK_OK;
391 break;
392
393 #if 0
394 /* perhaps later we can add support for unzipping OS/2 EAs to NT */
395 case EF_OS2:
396 rc = SetEAs(__G__ path, ef_ptr);
397 break;
398
399 case EF_PKUNIX:
400 case EF_IZUNIX:
401 case EF_IZUNIX2:
402 case EF_TIME:
403 break; /* handled elsewhere */
404 #else /* ! 0 */
405 #ifdef DEBUG
406 case EF_AV:
407 case EF_OS2:
408 case EF_PKVMS:
409 case EF_PKW32:
410 case EF_PKUNIX:
411 case EF_IZVMS:
412 case EF_IZUNIX:
413 case EF_IZUNIX2:
414 case EF_TIME:
415 case EF_MAC3:
416 case EF_JLMAC:
417 case EF_ZIPIT:
418 case EF_VMCMS:
419 case EF_MVS:
420 case EF_ACL:
421 case EF_BEOS:
422 case EF_QDOS:
423 case EF_AOSVS:
424 case EF_SPARK:
425 case EF_MD5:
426 case EF_ASIUNIX:
427 break; /* shut up for other known e.f. blocks */
428 #endif /* DEBUG */
429 #endif /* ? 0 */
430
431 default:
432 Trace((stderr,
433 "EvalExtraFields: unknown extra field block, ID=%u\n",
434 eb_id));
435 break;
436 }
437
438 ef_ptr += (eb_len + EB_HEADSIZE);
439 ef_len -= (eb_len + EB_HEADSIZE);
440
441 if (rc != PK_OK)
442 break;
443 }
444
445 return rc;
446 }
447
448
449
450
451 #ifndef SFX
452
453 /**************************/
454 /* Function test_NTSD() */ /* returns PK_WARN when NTSD data is invalid */
455 /**************************/
456
457 #ifdef __BORLANDC__
458 /* Turn off warning about not using all parameters for this function only */
459 #pragma argsused
460 #endif
461 int test_NTSD(__G__ eb, eb_size, eb_ucptr, eb_ucsize)
462 __GDEF
463 uch *eb;
464 unsigned eb_size;
465 uch *eb_ucptr;
466 ulg eb_ucsize;
467 {
468 int r = PK_OK;
469
470 if (!ValidateSecurity(eb_ucptr))
471 r = PK_WARN;
472 return r;
473
474 } /* end function test_NTSD() */
475
476 #endif /* !SFX */
477 #endif /* NTSD_EAS */
478
479
480
481
482 /**********************/
483 /* Function IsWinNT() */
484 /**********************/
485
486 int IsWinNT(void) /* returns TRUE if real NT, FALSE if Win95 or Win32s */
487 {
488 static DWORD g_PlatformId = 0xFFFFFFFF; /* saved platform indicator */
489
490 if (g_PlatformId == 0xFFFFFFFF) {
491 /* note: GetVersionEx() doesn't exist on WinNT 3.1 */
492 if (GetVersion() < 0x80000000)
493 g_PlatformId = TRUE;
494 else
495 g_PlatformId = FALSE;
496 }
497 return (int)g_PlatformId;
498 }
499
500
501 /* DEBUG_TIME insertion: */
502 #ifdef DEBUG_TIME
503 static int show_NTFileTime(FILE *hdo, char *TTmsg, int isloc, FILETIME *pft);
504
505 static int show_NTFileTime(FILE *hdo, char *TTmsg, int isloc, FILETIME *pft)
506 {
507 SYSTEMTIME w32tm;
508 int rval;
509
510 rval = FileTimeToSystemTime(pft, &w32tm);
511 if (!rval) {
512 fprintf(hdo, "%s\n %08lX,%08lX (%s) -> Conversion failed !!!\n",
513 TTmsg, (ulg)(pft->dwHighDateTime), (ulg)(pft->dwLowDateTime),
514 (isloc ? "local" : "UTC"));
515 } else {
516 fprintf(hdo, "%s\n %08lx,%08lx -> %04u-%02u-%02u, %02u:%02u:%02u %s\n",
517 TTmsg, (ulg)(pft->dwHighDateTime), (ulg)(pft->dwLowDateTime),
518 w32tm.wYear, w32tm.wMonth, w32tm.wDay, w32tm.wHour,
519 w32tm.wMinute, w32tm.wSecond, (isloc ? "local" : "UTC"));
520 }
521 return rval;
522 }
523 #define FTTrace(x) show_NTFileTime x
524 #else
525 #define FTTrace(x)
526 #endif /* DEBUG_TIME */
527 /* end of TIME_DEBUG insertion */
528
529 #if (defined(USE_EF_UT_TIME) || defined(NT_TZBUG_WORKAROUND) || \
530 defined(TIMESTAMP))
531
532 #if ((defined(__GNUC__) || defined(ULONG_LONG_MAX)) && !defined(HAVE_INT64))
533 typedef long long LLONG64;
534 typedef unsigned long long ULLNG64;
535 # define HAVE_INT64
536 #endif
537 #if (defined(__WATCOMC__) && (__WATCOMC__ >= 1100) && !defined(HAVE_INT64))
538 typedef __int64 LLONG64;
539 typedef unsigned __int64 ULLNG64;
540 # define HAVE_INT64
541 #endif
542 #if (defined(_MSC_VER) && (_MSC_VER >= 1100) && !defined(HAVE_INT64))
543 typedef __int64 LLONG64;
544 typedef unsigned __int64 ULLNG64;
545 # define HAVE_INT64
546 #endif
547
548 /*****************************/
549 /* Function utime2FileTime() */ /* convert Unix time_t format into the */
550 /*****************************/ /* form used by SetFileTime() in NT/95 */
551
552 #define UNIX_TIME_ZERO_HI 0x019DB1DEUL
553 #define UNIX_TIME_ZERO_LO 0xD53E8000UL
554 #define NT_QUANTA_PER_UNIX 10000000L
555
556 static void utime2FileTime(time_t ut, FILETIME *pft)
557 {
558 #ifdef HAVE_INT64
559 ULLNG64 NTtime;
560
561 /* NT_QUANTA_PER_UNIX is small enough so that "ut * NT_QUANTA_PER_UNIX"
562 * cannot overflow in 64-bit signed calculation, regardless wether "ut"
563 * is signed or unsigned. */
564 NTtime = ((LLONG64)ut * NT_QUANTA_PER_UNIX) +
565 ((ULLNG64)UNIX_TIME_ZERO_LO + ((ULLNG64)UNIX_TIME_ZERO_HI << 32));
566 pft->dwLowDateTime = (DWORD)NTtime;
567 pft->dwHighDateTime = (DWORD)(NTtime >> 32);
568
569 #else /* !HAVE_INT64 (64-bit integer arithmetics may not be supported) */
570 unsigned int b1, b2, carry = 0;
571 unsigned long r0, r1, r2, r3;
572 long r4; /* signed, to catch environments with signed time_t */
573
574 b1 = ut & 0xFFFF;
575 b2 = (ut >> 16) & 0xFFFF; /* if ut is over 32 bits, too bad */
576 r1 = b1 * (NT_QUANTA_PER_UNIX & 0xFFFF);
577 r2 = b1 * (NT_QUANTA_PER_UNIX >> 16);
578 r3 = b2 * (NT_QUANTA_PER_UNIX & 0xFFFF);
579 r4 = b2 * (NT_QUANTA_PER_UNIX >> 16);
580 r0 = (r1 + (r2 << 16)) & 0xFFFFFFFFL;
581 if (r0 < r1)
582 carry++;
583 r1 = r0;
584 r0 = (r0 + (r3 << 16)) & 0xFFFFFFFFL;
585 if (r0 < r1)
586 carry++;
587 pft->dwLowDateTime = r0 + UNIX_TIME_ZERO_LO;
588 if (pft->dwLowDateTime < r0)
589 carry++;
590 pft->dwHighDateTime = r4 + (r2 >> 16) + (r3 >> 16)
591 + UNIX_TIME_ZERO_HI + carry;
592 #endif /* ?HAVE_INT64 */
593
594 } /* end function utime2FileTime() */
595
596
597
598 /******************************/
599 /* Function FStampIsLocTime() */
600 /******************************/
601
602 static int FStampIsLocTime(__GPRO__ const char *path)
603 {
604 return (NTQueryVolInfo(__G__ path) ? G.lastVolLocTim : FALSE);
605 }
606
607 #endif /* USE_EF_UT_TIME || NT_TZBUG_WORKAROUND || TIMESTAMP */
608
609
610
611 #ifndef NT_TZBUG_WORKAROUND
612 # define UTIME_BOUNDCHECK_1(utimval) \
613 if (fs_uses_loctime) { \
614 utime_dosmin = dos_to_unix_time(DOSTIME_MINIMUM); \
615 if ((ulg)utimval < (ulg)utime_dosmin) \
616 utimval = utime_dosmin; \
617 }
618 # define UTIME_BOUNDCHECK_N(utimval) \
619 if (fs_uses_loctime && ((ulg)utimval < (ulg)utime_dosmin)) \
620 utimval = utime_dosmin;
621 # define NT_TZBUG_PRECOMPENSATE(ut, pft)
622
623 #else /* NT_TZBUG_WORKAROUND */
624 # define UNIX_TIME_UMAX_HI 0x0236485EUL
625 # define UNIX_TIME_UMAX_LO 0xD4A5E980UL
626 # define UNIX_TIME_SMIN_HI 0x0151669EUL
627 # define UNIX_TIME_SMIN_LO 0xD53E8000UL
628 # define UNIX_TIME_SMAX_HI 0x01E9FD1EUL
629 # define UNIX_TIME_SMAX_LO 0xD4A5E980UL
630 # define UTIME_1980_JAN_01_00_00 315532800L
631 # define UTIME_BOUNDCHECK_1(utimval)
632 # define UTIME_BOUNDCHECK_N(utimval)
633 # define NT_TZBUG_PRECOMPENSATE(ut, pft) \
634 if (fs_uses_loctime) NTtzbugWorkaround(ut, pft);
635
636 /* nonzero if `y' is a leap year, else zero */
637 # define leap(y) (((y)%4 == 0 && (y)%100 != 0) || (y)%400 == 0)
638 /* number of leap years from 1970 to `y' (not including `y' itself) */
639 # define nleap(y) (((y)-1969)/4 - ((y)-1901)/100 + ((y)-1601)/400)
640
641 extern ZCONST ush ydays[]; /* defined in fileio.c */
642
643 /*****************************/
644 /* Function FileTime2utime() */
645 /*****************************/
646
647 static int FileTime2utime(const FILETIME *pft, time_t *ut)
648 {
649 #ifdef HAVE_INT64
650 ULLNG64 NTtime;
651
652 NTtime = ((ULLNG64)pft->dwLowDateTime +
653 ((ULLNG64)pft->dwHighDateTime << 32));
654
655 /* underflow and overflow handling */
656 #ifdef CHECK_UTIME_SIGNED_UNSIGNED
657 if ((time_t)0x80000000L < (time_t)0L)
658 {
659 if (NTtime < ((ULLNG64)UNIX_TIME_SMIN_LO +
660 ((ULLNG64)UNIX_TIME_SMIN_HI << 32))) {
661 *ut = (time_t)LONG_MIN;
662 return FALSE;
663 }
664 if (NTtime > ((ULLNG64)UNIX_TIME_SMAX_LO +
665 ((ULLNG64)UNIX_TIME_SMAX_HI << 32))) {
666 *ut = (time_t)LONG_MAX;
667 return FALSE;
668 }
669 }
670 else
671 #endif /* CHECK_UTIME_SIGNED_UNSIGNED */
672 {
673 if (NTtime < ((ULLNG64)UNIX_TIME_ZERO_LO +
674 ((ULLNG64)UNIX_TIME_ZERO_HI << 32))) {
675 *ut = (time_t)0;
676 return FALSE;
677 }
678 if (NTtime > ((ULLNG64)UNIX_TIME_UMAX_LO +
679 ((ULLNG64)UNIX_TIME_UMAX_HI << 32))) {
680 *ut = (time_t)ULONG_MAX;
681 return FALSE;
682 }
683 }
684
685 NTtime -= ((ULLNG64)UNIX_TIME_ZERO_LO +
686 ((ULLNG64)UNIX_TIME_ZERO_HI << 32));
687 *ut = (time_t)(NTtime / (unsigned long)NT_QUANTA_PER_UNIX);
688 return TRUE;
689 #else /* !HAVE_INT64 (64-bit integer arithmetics may not be supported) */
690 time_t days;
691 SYSTEMTIME w32tm;
692
693 /* underflow and overflow handling */
694 #ifdef CHECK_UTIME_SIGNED_UNSIGNED
695 if ((time_t)0x80000000L < (time_t)0L)
696 {
697 if ((pft->dwHighDateTime < UNIX_TIME_SMIN_HI) ||
698 ((pft->dwHighDateTime == UNIX_TIME_SMIN_HI) &&
699 (pft->dwLowDateTime < UNIX_TIME_SMIN_LO))) {
700 *ut = (time_t)LONG_MIN;
701 return FALSE;
702 if ((pft->dwHighDateTime > UNIX_TIME_SMAX_HI) ||
703 ((pft->dwHighDateTime == UNIX_TIME_SMAX_HI) &&
704 (pft->dwLowDateTime > UNIX_TIME_SMAX_LO))) {
705 *ut = (time_t)LONG_MAX;
706 return FALSE;
707 }
708 }
709 else
710 #endif /* CHECK_UTIME_SIGNED_UNSIGNED */
711 {
712 if ((pft->dwHighDateTime < UNIX_TIME_ZERO_HI) ||
713 ((pft->dwHighDateTime == UNIX_TIME_ZERO_HI) &&
714 (pft->dwLowDateTime < UNIX_TIME_ZERO_LO))) {
715 *ut = (time_t)0;
716 return FALSE;
717 }
718 if ((pft->dwHighDateTime > UNIX_TIME_UMAX_HI) ||
719 ((pft->dwHighDateTime == UNIX_TIME_UMAX_HI) &&
720 (pft->dwLowDateTime > UNIX_TIME_UMAX_LO))) {
721 *ut = (time_t)ULONG_MAX;
722 return FALSE;
723 }
724 }
725
726 FileTimeToSystemTime(pft, &w32tm);
727
728 /* set `days' to the number of days into the year */
729 days = w32tm.wDay - 1 + ydays[w32tm.wMonth-1] +
730 (w32tm.wMonth > 2 && leap (w32tm.wYear));
731
732 /* now set `days' to the number of days since 1 Jan 1970 */
733 days += 365 * (time_t)(w32tm.wYear - 1970) +
734 (time_t)(nleap(w32tm.wYear));
735
736 *ut = (time_t)(86400L * days + 3600L * (time_t)w32tm.wHour +
737 (time_t)(60 * w32tm.wMinute + w32tm.wSecond));
738 return TRUE;
739 #endif /* ?HAVE_INT64 */
740 } /* end function FileTime2utime() */
741
742
743
744 #ifdef W32_STAT_BANDAID
745 /*********************************/
746 /* Function VFatFileTime2utime() */
747 /*********************************/
748
749 static int VFatFileTime2utime(const FILETIME *pft, time_t *ut)
750 {
751 FILETIME lft;
752 #ifndef HAVE_MKTIME
753 WORD wDOSDate, wDOSTime;
754 #else
755 SYSTEMTIME w32tm;
756 struct tm ltm;
757 #endif
758
759 FileTimeToLocalFileTime(pft, &lft);
760 FTTrace((stdout, "VFatFT2utime, feed for mktime()", 1, &lft));
761 #ifndef HAVE_MKTIME
762 /* This version of the FILETIME-to-UNIXTIME conversion function
763 * uses DOS-DATE-TIME format as intermediate stage. For modification
764 * and access times, this is no problem. But, the extra fine resolution
765 * of the VFAT-stored creation time gets lost.
766 */
767 FileTimeToDosDateTime(&lft, &wDOSDate, &wDOSTime);
768 TTrace((stdout,"DosDateTime is %04u-%02u-%02u %02u:%02u:%02u\n",
769 (unsigned)((wDOSDate>>9)&0x7f)+1980,(unsigned)((wDOSDate>>5)&0x0f),
770 (unsigned)(wDOSDate&0x1f),(unsigned)((wDOSTime>>11)&0x1f),
771 (unsigned)((wDOSTime>>5)&0x3f),(unsigned)((wDOSTime<<1)&0x3e)));
772 *ut = dos_to_unix_time(((ulg)wDOSDate << 16) | (ulg)wDOSTime);
773
774 /* a cheap error check: dos_to_unix_time() only returns an odd time
775 * when clipping at maximum time_t value. DOS_DATE_TIME values have
776 * a resolution of 2 seconds and are therefore even numbers.
777 */
778 return (((*ut)&1) == (time_t)0);
779 #else /* HAVE_MKTIME */
780 FileTimeToSystemTime(&lft, &w32tm);
781 /* underflow and overflow handling */
782 /* TODO: The range checks are not accurate, the actual limits may
783 * be off by one daylight-saving-time shift (typically 1 hour),
784 * depending on the current state of "is_dst".
785 */
786 #ifdef CHECK_UTIME_SIGNED_UNSIGNED
787 if ((time_t)0x80000000L < (time_t)0L)
788 {
789 if ((pft->dwHighDateTime < UNIX_TIME_SMIN_HI) ||
790 ((pft->dwHighDateTime == UNIX_TIME_SMIN_HI) &&
791 (pft->dwLowDateTime < UNIX_TIME_SMIN_LO))) {
792 *ut = (time_t)LONG_MIN;
793 return FALSE;
794 if ((pft->dwHighDateTime > UNIX_TIME_SMAX_HI) ||
795 ((pft->dwHighDateTime == UNIX_TIME_SMAX_HI) &&
796 (pft->dwLowDateTime > UNIX_TIME_SMAX_LO))) {
797 *ut = (time_t)LONG_MAX;
798 return FALSE;
799 }
800 }
801 else
802 #endif /* CHECK_UTIME_SIGNED_UNSIGNED */
803 {
804 if ((pft->dwHighDateTime < UNIX_TIME_ZERO_HI) ||
805 ((pft->dwHighDateTime == UNIX_TIME_ZERO_HI) &&
806 (pft->dwLowDateTime < UNIX_TIME_ZERO_LO))) {
807 *ut = (time_t)0;
808 return FALSE;
809 }
810 if ((pft->dwHighDateTime > UNIX_TIME_UMAX_HI) ||
811 ((pft->dwHighDateTime == UNIX_TIME_UMAX_HI) &&
812 (pft->dwLowDateTime > UNIX_TIME_UMAX_LO))) {
813 *ut = (time_t)ULONG_MAX;
814 return FALSE;
815 }
816 }
817 ltm.tm_year = w32tm.wYear - 1900;
818 ltm.tm_mon = w32tm.wMonth - 1;
819 ltm.tm_mday = w32tm.wDay;
820 ltm.tm_hour = w32tm.wHour;
821 ltm.tm_min = w32tm.wMinute;
822 ltm.tm_sec = w32tm.wSecond;
823 ltm.tm_isdst = -1; /* let mktime determine if DST is in effect */
824 *ut = mktime(&ltm);
825
826 /* a cheap error check: mktime returns "(time_t)-1L" on conversion errors.
827 * Normally, we would have to apply a consistency check because "-1"
828 * could also be a valid time. But, it is quite unlikely to read back odd
829 * time numbers from file systems that store time stamps in DOS format.
830 * (The only known exception is creation time on VFAT partitions.)
831 */
832 return (*ut != (time_t)-1L);
833 #endif /* ?HAVE_MKTIME */
834
835 } /* end function VFatFileTime2utime() */
836 #endif /* W32_STAT_BANDAID */
837
838
839
840 /********************************/
841 /* Function UTCtime2Localtime() */ /* borrowed from Zip's mkgmtime() */
842 /********************************/
843
844 static time_t UTCtime2Localtime(time_t utctime)
845 {
846 time_t utc = utctime;
847 struct tm *tm;
848 unsigned years, months, days, hours, minutes, seconds;
849
850
851 #ifdef __BORLANDC__ /* Borland C++ 5.x crashes when trying to reference tm */
852 if (utc < UTIME_1980_JAN_01_00_00)
853 utc = UTIME_1980_JAN_01_00_00;
854 #endif
855 tm = localtime(&utc);
856 if (tm == (struct tm *)NULL)
857 /* localtime() did not accept given utc time value; as an emergency
858 exit, the unconverted utctime value is returned */
859 return utctime;
860
861 years = tm->tm_year + 1900; /* year - 1900 -> year */
862 months = tm->tm_mon; /* 0..11 */
863 days = tm->tm_mday - 1; /* 1..31 -> 0..30 */
864 hours = tm->tm_hour; /* 0..23 */
865 minutes = tm->tm_min; /* 0..59 */
866 seconds = tm->tm_sec; /* 0..61 in ANSI C */
867
868 /* set `days' to the number of days into the year */
869 days += ydays[months] + (months > 1 && leap(years));
870
871 /* now set `days' to the number of days since 1 Jan 1970 */
872 days += 365 * (years - 1970) + nleap(years);
873
874 return (time_t)(86400L * (ulg)days + 3600L * (ulg)hours +
875 (ulg)(60 * minutes + seconds));
876
877 } /* end function UTCtime2Localtime() */
878
879
880
881 /********************************/
882 /* Function NTtzbugWorkaround() */
883 /********************************/
884
885 static void NTtzbugWorkaround(time_t ut, FILETIME *pft)
886 {
887 FILETIME C_RTL_locft, NTAPI_locft;
888 time_t ux_loctime = UTCtime2Localtime(ut);
889
890 /* This routine is only used when the target file system stores time-
891 * stamps as local time in MSDOS format. Thus we make sure that the
892 * resulting timestamp is within the range of MSDOS date-time values. */
893 if (ux_loctime < UTIME_1980_JAN_01_00_00)
894 ux_loctime = UTIME_1980_JAN_01_00_00;
895
896 utime2FileTime(ux_loctime, &C_RTL_locft);
897 if (!FileTimeToLocalFileTime(pft, &NTAPI_locft))
898 return;
899 else {
900 long time_shift_l, time_shift_h;
901 int carry = 0;
902
903 time_shift_l = C_RTL_locft.dwLowDateTime - NTAPI_locft.dwLowDateTime;
904 if (C_RTL_locft.dwLowDateTime < NTAPI_locft.dwLowDateTime)
905 carry--;
906 time_shift_h = C_RTL_locft.dwHighDateTime - NTAPI_locft.dwHighDateTime;
907 pft->dwLowDateTime += time_shift_l;
908 if (pft->dwLowDateTime < (ulg)time_shift_l)
909 carry++;
910 pft->dwHighDateTime += time_shift_h + carry;
911 TTrace((stdout, "FileTime shift: %08lx:%08lx\n",
912 time_shift_h+carry,time_shift_l));
913 }
914 } /* end function NTtzbugWorkaround() */
915
916 #endif /* ?NT_TZBUG_WORKAROUND */
917
918
919
920 /****************************/ /* Get the file time in a format that */
921 /* Function getNTfiletime() */ /* can be used by SetFileTime() in NT */
922 /****************************/
923
924 static int getNTfiletime(__G__ pModFT, pAccFT, pCreFT)
925 __GDEF
926 FILETIME *pModFT;
927 FILETIME *pAccFT;
928 FILETIME *pCreFT;
929 {
930 #ifdef NT_TZBUG_WORKAROUND
931 time_t ux_modtime;
932 #else /* !NT_TZBUG_WORKAROUND */
933 FILETIME locft; /* 64-bit value made up of two 32-bit [low & high] */
934 WORD wDOSDate; /* for converting from DOS date to Windows NT */
935 WORD wDOSTime;
936 #endif /* ?NT_TZBUG_WORKAROUND */
937 #ifdef USE_EF_UT_TIME
938 unsigned eb_izux_flg;
939 iztimes z_utime; /* struct for Unix-style actime & modtime, + creatime */
940 #endif
941 #if (defined(USE_EF_UT_TIME) && !defined(NT_TZBUG_WORKAROUND))
942 time_t utime_dosmin;
943 # endif
944 #if (defined(USE_EF_UT_TIME) || defined(NT_TZBUG_WORKAROUND))
945 int fs_uses_loctime = FStampIsLocTime(__G__ G.filename);
946 #endif
947
948 /* Copy and/or convert time and date variables, if necessary;
949 * return a flag indicating which time stamps are available. */
950 #ifdef USE_EF_UT_TIME
951 if (G.extra_field &&
952 #ifdef IZ_CHECK_TZ
953 G.tz_is_valid &&
954 #endif
955 ((eb_izux_flg = ef_scan_for_izux(G.extra_field,
956 G.lrec.extra_field_length, 0, G.lrec.last_mod_dos_datetime,
957 &z_utime, NULL)) & EB_UT_FL_MTIME))
958 {
959 TTrace((stderr, "getNTfiletime: Unix e.f. modif. time = %lu\n",
960 z_utime.mtime));
961 UTIME_BOUNDCHECK_1(z_utime.mtime)
962 utime2FileTime(z_utime.mtime, pModFT);
963 NT_TZBUG_PRECOMPENSATE(z_utime.mtime, pModFT)
964 if (eb_izux_flg & EB_UT_FL_ATIME) {
965 UTIME_BOUNDCHECK_N(z_utime.atime)
966 utime2FileTime(z_utime.atime, pAccFT);
967 NT_TZBUG_PRECOMPENSATE(z_utime.atime, pAccFT)
968 }
969 if (eb_izux_flg & EB_UT_FL_CTIME) {
970 UTIME_BOUNDCHECK_N(z_utime.ctime)
971 utime2FileTime(z_utime.ctime, pCreFT);
972 NT_TZBUG_PRECOMPENSATE(z_utime.ctime, pCreFT)
973 }
974 return (int)eb_izux_flg;
975 }
976 #endif /* USE_EF_UT_TIME */
977 #ifdef NT_TZBUG_WORKAROUND
978 ux_modtime = dos_to_unix_time(G.lrec.last_mod_dos_datetime);
979 utime2FileTime(ux_modtime, pModFT);
980 NT_TZBUG_PRECOMPENSATE(ux_modtime, pModFT)
981 #else /* !NT_TZBUG_WORKAROUND */
982
983 wDOSTime = (WORD)(G.lrec.last_mod_dos_datetime);
984 wDOSDate = (WORD)(G.lrec.last_mod_dos_datetime >> 16);
985
986 /* The DosDateTimeToFileTime() function converts a DOS date/time
987 * into a 64-bit Windows NT file time */
988 if (!DosDateTimeToFileTime(wDOSDate, wDOSTime, &locft))
989 {
990 Info(slide, 0, ((char *)slide, "DosDateTime failed: %d\n",
991 (int)GetLastError()));
992 return 0;
993 }
994 if (!LocalFileTimeToFileTime(&locft, pModFT))
995 {
996 Info(slide, 0, ((char *)slide, "LocalFileTime failed: %d\n",
997 (int)GetLastError()));
998 *pModFT = locft;
999 }
1000 #endif /* ?NT_TZBUG_WORKAROUND */
1001 *pAccFT = *pModFT;
1002 return (EB_UT_FL_MTIME | EB_UT_FL_ATIME);
1003
1004 } /* end function getNTfiletime() */
1005
1006
1007
1008
1009 /****************************/
1010 /* Function close_outfile() */
1011 /****************************/
1012
1013 void close_outfile(__G)
1014 __GDEF
1015 {
1016 FILETIME Modft; /* File time type defined in NT, `last modified' time */
1017 FILETIME Accft; /* NT file time type, `last access' time */
1018 FILETIME Creft; /* NT file time type, `file creation' time */
1019 HANDLE hFile; /* File handle defined in NT */
1020 int gotTime;
1021 #ifdef __RSXNT__ /* RSXNT/EMX C rtl uses OEM charset */
1022 char *ansi_name = (char *)alloca(strlen(G.filename) + 1);
1023
1024 INTERN_TO_ISO(G.filename, ansi_name);
1025 # define Ansi_Fname ansi_name
1026 #else
1027 # define Ansi_Fname G.filename
1028 #endif
1029
1030 /* Close the file and then re-open it using the Win32
1031 * CreateFile call, so that the file can be created
1032 * with GENERIC_WRITE access, otherwise the SetFileTime
1033 * call will fail. */
1034 fclose(G.outfile);
1035
1036 /* don't set the time stamp and attributes on standard output */
1037 if (uO.cflag)
1038 return;
1039
1040 gotTime = getNTfiletime(__G__ &Modft, &Accft, &Creft);
1041
1042 /* open a handle to the file before processing extra fields;
1043 we do this in case new security on file prevents us from updating
1044 time stamps */
1045 hFile = CreateFile(Ansi_Fname, GENERIC_WRITE, FILE_SHARE_WRITE, NULL,
1046 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
1047
1048 /* sfield@microsoft.com: set attributes before time in case we decide to
1049 support other filetime members later. This also allows us to apply
1050 attributes before the security is changed, which may prevent this
1051 from succeeding otherwise. Also, since most files don't have
1052 any interesting attributes, only change them if something other than
1053 FILE_ATTRIBUTE_ARCHIVE appears in the attributes. This works well
1054 as an optimization because FILE_ATTRIBUTE_ARCHIVE gets applied to the
1055 file anyway, when it's created new. */
1056 if((G.pInfo->file_attr & 0x7F) & ~FILE_ATTRIBUTE_ARCHIVE) {
1057 if (!SetFileAttributes(Ansi_Fname, G.pInfo->file_attr & 0x7F))
1058 Info(slide, 1, ((char *)slide,
1059 "\nwarning (%d): could not set file attributes\n",
1060 (int)GetLastError()));
1061 }
1062
1063 #ifdef NTSD_EAS
1064 /* set extra fields, both stored-in-zipfile and .LONGNAME flavors */
1065 if (G.extra_field) { /* zipfile extra field may have extended attribs */
1066 int err = EvalExtraFields(__G__ G.filename, G.extra_field,
1067 G.lrec.extra_field_length);
1068
1069 if (err == IZ_EF_TRUNC) {
1070 if (uO.qflag)
1071 Info(slide, 1, ((char *)slide, "%-22s ",
1072 FnFilter1(G.filename)));
1073 Info(slide, 1, ((char *)slide, LoadFarString(TruncNTSD),
1074 makeword(G.extra_field+2)-10, uO.qflag? "\n":""));
1075 }
1076 }
1077 #endif /* NTSD_EAS */
1078
1079 if ( hFile == INVALID_HANDLE_VALUE )
1080 Info(slide, 1, ((char *)slide,
1081 "\nCreateFile error %d when trying set file time\n",
1082 (int)GetLastError()));
1083 else {
1084 if (gotTime) {
1085 FILETIME *pModft = (gotTime & EB_UT_FL_MTIME) ? &Modft : NULL;
1086 FILETIME *pAccft = (gotTime & EB_UT_FL_ATIME) ? &Accft : NULL;
1087 FILETIME *pCreft = (gotTime & EB_UT_FL_CTIME) ? &Creft : NULL;
1088
1089 if (!SetFileTime(hFile, pCreft, pAccft, pModft))
1090 Info(slide, 0, ((char *)slide, "\nSetFileTime failed: %d\n",
1091 (int)GetLastError()));
1092 }
1093 CloseHandle(hFile);
1094 }
1095
1096 return;
1097
1098 #undef Ansi_Fname
1099
1100 } /* end function close_outfile() */
1101
1102
1103
1104
1105 #ifdef TIMESTAMP
1106
1107 /*************************/
1108 /* Function stamp_file() */
1109 /*************************/
1110
1111 int stamp_file(__GPRO__ ZCONST char *fname, time_t modtime)
1112 {
1113 FILETIME Modft; /* File time type defined in NT, `last modified' time */
1114 HANDLE hFile; /* File handle defined in NT */
1115 int errstat = 0; /* return status: 0 == "OK", -1 == "Failure" */
1116 #ifndef NT_TZBUG_WORKAROUND
1117 time_t utime_dosmin; /* internal variable for UTIME_BOUNDCHECK_1 */
1118 #endif
1119 int fs_uses_loctime = FStampIsLocTime(__G__ fname);
1120 #ifdef __RSXNT__ /* RSXNT/EMX C rtl uses OEM charset */
1121 char *ansi_name = (char *)alloca(strlen(fname) + 1);
1122
1123 INTERN_TO_ISO(fname, ansi_name);
1124 # define Ansi_Fname ansi_name
1125 #else
1126 # define Ansi_Fname fname
1127 #endif
1128
1129 /* open a handle to the file to prepare setting the mod-time stamp */
1130 hFile = CreateFile(Ansi_Fname, GENERIC_WRITE, FILE_SHARE_WRITE, NULL,
1131 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
1132 if ( hFile == INVALID_HANDLE_VALUE ) {
1133 errstat = -1;
1134 } else {
1135 /* convert time_t modtime into WIN32 native 64bit format */
1136 UTIME_BOUNDCHECK_1(modtime)
1137 utime2FileTime(modtime, &Modft);
1138 NT_TZBUG_PRECOMPENSATE(modtime, &Modft)
1139 /* set Access and Modification times of the file to modtime */
1140 if (!SetFileTime(hFile, NULL, &Modft, &Modft)) {
1141 errstat = -1;
1142 }
1143 CloseHandle(hFile);
1144 }
1145
1146 return errstat;
1147
1148 #undef Ansi_Fname
1149 } /* end function stamp_file() */
1150
1151 #endif /* TIMESTAMP */
1152
1153
1154
1155
1156 /***********************/
1157 /* Function isfloppy() */ /* more precisely, is it removable? */
1158 /***********************/
1159
1160 static int isfloppy(int nDrive) /* 1 == A:, 2 == B:, etc. */
1161 {
1162 char rootPathName[4];
1163
1164 rootPathName[0] = (char)('A' + nDrive - 1); /* build the root path */
1165 rootPathName[1] = ':'; /* name, e.g. "A:/" */
1166 rootPathName[2] = '/';
1167 rootPathName[3] = '\0';
1168
1169 return (GetDriveType(rootPathName) == DRIVE_REMOVABLE);
1170
1171 } /* end function isfloppy() */
1172
1173
1174
1175
1176 /*****************************/
1177 /* Function NTQueryVolInfo() */
1178 /*****************************/
1179
1180 /*
1181 * Note: 8.3 limits on filenames apply only to old-style FAT filesystems.
1182 * More recent versions of Windows (Windows NT 3.5 / Windows 4.0)
1183 * can support long filenames (LFN) on FAT filesystems. Check the
1184 * filesystem maximum component length field to detect LFN support.
1185 */
1186
1187 static int NTQueryVolInfo(__GPRO__ const char *name)
1188 {
1189 /* static char lastRootPath[4] = ""; */
1190 /* static int lastVolOldFAT; */
1191 /* static int lastVolLocTim; */
1192 char *tmp0;
1193 char tmp1[MAX_PATH], tmp2[MAX_PATH];
1194 unsigned volSerNo, maxCompLen, fileSysFlags;
1195 #ifdef __RSXNT__ /* RSXNT/EMX C rtl uses OEM charset */
1196 char *ansi_name = (char *)alloca(strlen(name) + 1);
1197
1198 INTERN_TO_ISO(name, ansi_name);
1199 name = ansi_name;
1200 #endif
1201
1202 if ((!strncmp(name, "//", 2) || !strncmp(name,"\\\\", 2)) &&
1203 (name[2] != '\0' && name[2] != '/' && name[2] != '\\')) {
1204 /* GetFullPathname() and GetVolumeInformation() do not work
1205 * on UNC names. For now, we return "error".
1206 * **FIXME**: check if UNC name is mapped to a drive letter
1207 * and use mapped drive for volume info query.
1208 */
1209 return FALSE;
1210 }
1211 if (isalpha((uch)name[0]) && (name[1] == ':'))
1212 tmp0 = (char *)name;
1213 else
1214 {
1215 if (!GetFullPathName(name, MAX_PATH, tmp1, &tmp0))
1216 return FALSE;
1217 tmp0 = &tmp1[0];
1218 }
1219 if (strncmp(G.lastRootPath, tmp0, 2) != 0) {
1220 /* For speed, we skip repeated queries for the same device */
1221 strncpy(G.lastRootPath, tmp0, 2); /* Build the root path name, */
1222 G.lastRootPath[2] = '/'; /* e.g. "A:/" */
1223 G.lastRootPath[3] = '\0';
1224
1225 if (!GetVolumeInformation((LPCTSTR)G.lastRootPath,
1226 (LPTSTR)tmp1, (DWORD)MAX_PATH,
1227 (LPDWORD)&volSerNo, (LPDWORD)&maxCompLen,
1228 (LPDWORD)&fileSysFlags, (LPTSTR)tmp2, (DWORD)MAX_PATH)) {
1229 G.lastRootPath[0] = '\0';
1230 return FALSE;
1231 }
1232
1233 /* LFNs are available if the component length is > 12 */
1234 G.lastVolOldFAT = (maxCompLen <= 12);
1235 /* G.lastVolOldFAT = !strncmp(strupr(tmp2), "FAT", 3); old version */
1236
1237 /* Volumes in (V)FAT and (OS/2) HPFS format store file timestamps in
1238 * local time!
1239 */
1240 G.lastVolLocTim = !strncmp(strupr(tmp2), "VFAT", 4) ||
1241 !strncmp(tmp2, "HPFS", 4) ||
1242 !strncmp(tmp2, "FAT", 3);
1243 }
1244
1245 return TRUE;
1246
1247 } /* end function NTQueryVolInfo() */
1248
1249
1250
1251
1252 /*****************************/
1253 /* Function IsVolumeOldFAT() */
1254 /*****************************/
1255
1256 static int IsVolumeOldFAT(__GPRO__ const char *name)
1257 {
1258 return (NTQueryVolInfo(__G__ name) ? G.lastVolOldFAT : FALSE);
1259 }
1260
1261
1262
1263
1264 #ifndef SFX
1265
1266 /************************/
1267 /* Function do_wild() */ /* identical to OS/2 version */
1268 /************************/
1269
1270 char *do_wild(__G__ wildspec)
1271 __GDEF
1272 char *wildspec; /* only used first time on a given dir */
1273 {
1274 /* static zDIR *wild_dir = NULL; */
1275 /* static char *dirname, *wildname, matchname[FILNAMSIZ]; */
1276 /* static int firstcall=TRUE, have_dirname, dirnamelen; */
1277 char *fnamestart;
1278 struct zdirent *file;
1279
1280 /* Even when we're just returning wildspec, we *always* do so in
1281 * matchname[]--calling routine is allowed to append four characters
1282 * to the returned string, and wildspec may be a pointer to argv[].
1283 */
1284 if (!G.notfirstcall) { /* first call: must initialize everything */
1285 G.notfirstcall = TRUE;
1286
1287 if (!iswild(wildspec)) {
1288 strcpy(G.matchname, wildspec);
1289 G.have_dirname = FALSE;
1290 G.wild_dir = NULL;
1291 return G.matchname;
1292 }
1293
1294 /* break the wildspec into a directory part and a wildcard filename */
1295 if ((G.wildname = strrchr(wildspec, '/')) == NULL &&
1296 (G.wildname = strrchr(wildspec, ':')) == NULL) {
1297 G.dirname = ".";
1298 G.dirnamelen = 1;
1299 G.have_dirname = FALSE;
1300 G.wildname = wildspec;
1301 } else {
1302 ++G.wildname; /* point at character after '/' or ':' */
1303 G.dirnamelen = G.wildname - wildspec;
1304 if ((G.dirname = (char *)malloc(G.dirnamelen+1)) == NULL) {
1305 Info(slide, 1, ((char *)slide,
1306 "warning: cannot allocate wildcard buffers\n"));
1307 strcpy(G.matchname, wildspec);
1308 return G.matchname; /* but maybe filespec was not a wildcard */
1309 }
1310 strncpy(G.dirname, wildspec, G.dirnamelen);
1311 G.dirname[G.dirnamelen] = '\0'; /* terminate for strcpy below */
1312 G.have_dirname = TRUE;
1313 }
1314 Trace((stderr, "do_wild: dirname = [%s]\n", G.dirname));
1315
1316 if ((G.wild_dir = (zvoid *)Opendir(G.dirname)) != NULL) {
1317 if (G.have_dirname) {
1318 strcpy(G.matchname, G.dirname);
1319 fnamestart = G.matchname + G.dirnamelen;
1320 } else
1321 fnamestart = G.matchname;
1322 while ((file = Readdir((zDIR *)G.wild_dir)) != NULL) {
1323 Trace((stderr, "do_wild: Readdir returns %s\n", file->d_name));
1324 strcpy(fnamestart, file->d_name);
1325 if (strrchr(fnamestart, '.') == (char *)NULL)
1326 strcat(fnamestart, ".");
1327 if (match(fnamestart, G.wildname, 1) && /* 1 == ignore case */
1328 /* skip "." and ".." directory entries */
1329 strcmp(fnamestart, ".") && strcmp(fnamestart, "..")) {
1330 Trace((stderr, "do_wild: match() succeeds\n"));
1331 /* remove trailing dot */
1332 fnamestart += strlen(fnamestart) - 1;
1333 if (*fnamestart == '.')
1334 *fnamestart = '\0';
1335 return G.matchname;
1336 }
1337 }
1338 /* if we get to here directory is exhausted, so close it */
1339 Closedir((zDIR *)G.wild_dir);
1340 G.wild_dir = NULL;
1341 }
1342 Trace((stderr, "do_wild: Opendir(%s) returns NULL\n", G.dirname));
1343
1344 /* return the raw wildspec in case that works (e.g., directory not
1345 * searchable, but filespec was not wild and file is readable) */
1346 strcpy(G.matchname, wildspec);
1347 return G.matchname;
1348 }
1349
1350 /* last time through, might have failed opendir but returned raw wildspec */
1351 if (G.wild_dir == NULL) {
1352 G.notfirstcall = FALSE; /* reset for new wildspec */
1353 if (G.have_dirname)
1354 free(G.dirname);
1355 return (char *)NULL;
1356 }
1357
1358 /* If we've gotten this far, we've read and matched at least one entry
1359 * successfully (in a previous call), so dirname has been copied into
1360 * matchname already.
1361 */
1362 if (G.have_dirname) {
1363 /* strcpy(G.matchname, G.dirname); */
1364 fnamestart = G.matchname + G.dirnamelen;
1365 } else
1366 fnamestart = G.matchname;
1367 while ((file = Readdir((zDIR *)G.wild_dir)) != NULL) {
1368 Trace((stderr, "do_wild: readdir returns %s\n", file->d_name));
1369 strcpy(fnamestart, file->d_name);
1370 if (strrchr(fnamestart, '.') == (char *)NULL)
1371 strcat(fnamestart, ".");
1372 if (match(fnamestart, G.wildname, 1)) { /* 1 == ignore case */
1373 Trace((stderr, "do_wild: match() succeeds\n"));
1374 /* remove trailing dot */
1375 fnamestart += strlen(fnamestart) - 1;
1376 if (*fnamestart == '.')
1377 *fnamestart = '\0';
1378 return G.matchname;
1379 }
1380 }
1381
1382 Closedir((zDIR *)G.wild_dir); /* at least one entry read; nothing left */
1383 G.wild_dir = NULL;
1384 G.notfirstcall = FALSE; /* reset for new wildspec */
1385 if (G.have_dirname)
1386 free(G.dirname);
1387 return (char *)NULL;
1388
1389 } /* end function do_wild() */
1390
1391 #endif /* !SFX */
1392
1393
1394
1395 /**********************/
1396 /* Function mapattr() */
1397 /**********************/
1398
1399 /* Identical to MS-DOS, OS/2 versions. However, NT has a lot of extra
1400 * permission stuff, so this function should probably be extended in the
1401 * future. */
1402
1403 int mapattr(__G)
1404 __GDEF
1405 {
1406 /* set archive bit for file entries (file is not backed up): */
1407 G.pInfo->file_attr = ((unsigned)G.crec.external_file_attributes |
1408 (G.crec.external_file_attributes & FILE_ATTRIBUTE_DIRECTORY ?
1409 0 : FILE_ATTRIBUTE_ARCHIVE)) & 0xff;
1410 return 0;
1411
1412 } /* end function mapattr() */
1413
1414
1415
1416
1417 /************************/
1418 /* Function mapname() */
1419 /************************/
1420 /* return 0 if no error, 1 if caution (filename */
1421 int mapname(__G__ renamed) /* truncated), 2 if warning (skip file because */
1422 __GDEF /* dir doesn't exist), 3 if error (skip file), */
1423 int renamed; /* or 10 if out of memory (skip file) */
1424 { /* [also IZ_VOL_LABEL, IZ_CREATED_DIR] */
1425 char pathcomp[FILNAMSIZ]; /* path-component buffer */
1426 char *pp, *cp=NULL; /* character pointers */
1427 char *lastsemi = NULL; /* pointer to last semi-colon in pathcomp */
1428 int error;
1429 register unsigned workch; /* hold the character being tested */
1430
1431
1432 /*---------------------------------------------------------------------------
1433 Initialize various pointers and counters and stuff.
1434 ---------------------------------------------------------------------------*/
1435
1436 /* can create path as long as not just freshening, or if user told us */
1437 G.create_dirs = (!uO.fflag || renamed);
1438
1439 G.created_dir = FALSE; /* not yet */
1440 G.renamed_fullpath = FALSE;
1441 G.fnlen = strlen(G.filename);
1442
1443 if (renamed) {
1444 cp = G.filename - 1; /* point to beginning of renamed name... */
1445 while (*++cp)
1446 if (*cp == '\\') /* convert backslashes to forward */
1447 *cp = '/';
1448 cp = G.filename;
1449 /* use temporary rootpath if user gave full pathname */
1450 if (G.filename[0] == '/') {
1451 G.renamed_fullpath = TRUE;
1452 pathcomp[0] = '/'; /* copy the '/' and terminate */
1453 pathcomp[1] = '\0';
1454 ++cp;
1455 } else if (isalpha((uch)G.filename[0]) && G.filename[1] == ':') {
1456 G.renamed_fullpath = TRUE;
1457 pp = pathcomp;
1458 *pp++ = *cp++; /* copy the "d:" (+ '/', possibly) */
1459 *pp++ = *cp++;
1460 if (*cp == '/')
1461 *pp++ = *cp++; /* otherwise add "./"? */
1462 *pp = '\0';
1463 }
1464 }
1465
1466 /* pathcomp is ignored unless renamed_fullpath is TRUE: */
1467 if ((error = checkdir(__G__ pathcomp, INIT)) != 0) /* init path buffer */
1468 return error; /* ...unless no mem or vol label on hard disk */
1469
1470 *pathcomp = '\0'; /* initialize translation buffer */
1471 pp = pathcomp; /* point to translation buffer */
1472 if (!renamed) { /* cp already set if renamed */
1473 if (uO.jflag) /* junking directories */
1474 cp = (char *)strrchr(G.filename, '/');
1475 if (cp == NULL) /* no '/' or not junking dirs */
1476 cp = G.filename; /* point to internal zipfile-member pathname */
1477 else
1478 ++cp; /* point to start of last component of path */
1479 }
1480
1481 /*---------------------------------------------------------------------------
1482 Begin main loop through characters in filename.
1483 ---------------------------------------------------------------------------*/
1484
1485 while ((workch = (uch)*cp++) != 0) {
1486
1487 switch (workch) {
1488 case '/': /* can assume -j flag not given */
1489 *pp = '\0';
1490 if ((error = checkdir(__G__ pathcomp, APPEND_DIR)) > 1)
1491 return error;
1492 pp = pathcomp; /* reset conversion buffer for next piece */
1493 lastsemi = NULL; /* leave directory semi-colons alone */
1494 break;
1495
1496 case ':': /* drive names not stored in zipfile, */
1497 case '<': /* so no colons allowed */
1498 case '>': /* no redirection symbols allowed either */
1499 case '|': /* no pipe signs allowed */
1500 case '"': /* no double quotes allowed */
1501 case '?': /* no wildcards allowed */
1502 case '*':
1503 *pp++ = '_'; /* these rules apply equally to FAT and NTFS */
1504 break;
1505 case ';': /* start of VMS version? */
1506 lastsemi = pp; /* remove VMS version later... */
1507 *pp++ = ';'; /* but keep semicolon for now */
1508 break;
1509
1510 case ' ': /* keep spaces unless specifically */
1511 /* NT cannot create filenames with spaces on FAT volumes */
1512 if (uO.sflag || IsVolumeOldFAT(__G__ G.filename))
1513 *pp++ = '_';
1514 else
1515 *pp++ = ' ';
1516 break;
1517
1518 default:
1519 /* allow European characters in filenames: */
1520 if (isprint(workch) || workch >= 127)
1521 *pp++ = (char)workch;
1522 } /* end switch */
1523 } /* end while loop */
1524
1525 *pp = '\0'; /* done with pathcomp: terminate it */
1526
1527 /* if not saving them, remove VMS version numbers (appended "###") */
1528 if (!uO.V_flag && lastsemi) {
1529 pp = lastsemi + 1; /* semi-colon was kept: expect #'s after */
1530 while (isdigit((uch)(*pp)))
1531 ++pp;
1532 if (*pp == '\0') /* only digits between ';' and end: nuke */
1533 *lastsemi = '\0';
1534 }
1535
1536 /*---------------------------------------------------------------------------
1537 Report if directory was created (and no file to create: filename ended
1538 in '/'), check name to be sure it exists, and combine path and name be-
1539 fore exiting.
1540 ---------------------------------------------------------------------------*/
1541
1542 if (G.filename[G.fnlen-1] == '/') {
1543 checkdir(__G__ G.filename, GETPATH);
1544 if (G.created_dir) {
1545 #ifdef __RSXNT__ /* RSXNT/EMX C rtl uses OEM charset */
1546 char *ansi_name = (char *)alloca(strlen(G.filename) + 1);
1547
1548 INTERN_TO_ISO(G.filename, ansi_name);
1549 # define Ansi_Fname ansi_name
1550 #else
1551 # define Ansi_Fname G.filename
1552 #endif
1553 if (QCOND2) {
1554 Info(slide, 0, ((char *)slide, " creating: %-22s\n",
1555 FnFilter1(G.filename)));
1556 }
1557
1558 /* set file attributes:
1559 The default for newly created directories is "DIR attribute
1560 flags set", so there is no need to change attributes unless
1561 one of the DOS style attribute flags is set. The readonly
1562 attribute need not be masked, since it does not prevent
1563 modifications in the new directory. */
1564 if(G.pInfo->file_attr & (0x7F & ~FILE_ATTRIBUTE_DIRECTORY)) {
1565 if (!SetFileAttributes(Ansi_Fname, G.pInfo->file_attr & 0x7F))
1566 Info(slide, 1, ((char *)slide,
1567 "\nwarning (%d): could not set file attributes for %s\n",
1568 (int)GetLastError(), G.filename));
1569 }
1570
1571 #ifdef NTSD_EAS
1572 /* set extra fields, both stored-in-zipfile and .LONGNAME flavors */
1573 if (G.extra_field) { /* zipfile e.f. may have extended attribs */
1574 int err = EvalExtraFields(__G__ G.filename, G.extra_field,
1575 G.lrec.extra_field_length);
1576
1577 if (err == IZ_EF_TRUNC) {
1578 if (uO.qflag)
1579 Info(slide, 1, ((char *)slide, "%-22s ",
1580 FnFilter1(G.filename)));
1581 Info(slide, 1, ((char *)slide, LoadFarString(TruncNTSD),
1582 makeword(G.extra_field+2)-10, uO.qflag? "\n":""));
1583 }
1584 }
1585 #endif /* NTSD_EAS */
1586 return IZ_CREATED_DIR; /* set dir time (note trailing '/') */
1587 }
1588 return 2; /* dir existed already; don't look for data to extract */
1589 }
1590
1591 if (*pathcomp == '\0') {
1592 Info(slide, 1, ((char *)slide, "mapname: conversion of %s failed\n",
1593 FnFilter1(G.filename)));
1594 return 3;
1595 }
1596
1597 checkdir(__G__ pathcomp, APPEND_NAME); /* returns 1 if truncated: care? */
1598 checkdir(__G__ G.filename, GETPATH);
1599 Trace((stderr, "mapname returns with filename = [%s] (error = %d)\n\n",
1600 FnFilter1(G.filename), error));
1601
1602 if (G.pInfo->vollabel) { /* set the volume label now */
1603 char drive[4];
1604 #ifdef __RSXNT__ /* RSXNT/EMX C rtl uses OEM charset */
1605 char *ansi_name = (char *)alloca(strlen(G.filename) + 1);
1606 INTERN_TO_ISO(G.filename, ansi_name);
1607 # define Ansi_Fname ansi_name
1608 #else
1609 # define Ansi_Fname G.filename
1610 #endif
1611
1612 /* Build a drive string, e.g. "b:" */
1613 drive[0] = (char)('a' + G.nLabelDrive - 1);
1614 strcpy(drive + 1, ":\\");
1615 if (QCOND2)
1616 Info(slide, 0, ((char *)slide, "labelling %s %-22s\n", drive,
1617 FnFilter1(G.filename)));
1618 if (!SetVolumeLabel(drive, Ansi_Fname)) {
1619 Info(slide, 1, ((char *)slide,
1620 "mapname: error setting volume label\n"));
1621 return 3;
1622 }
1623 return 2; /* success: skip the "extraction" quietly */
1624 #undef Ansi_Fname
1625 }
1626
1627 return error;
1628
1629 } /* end function mapname() */
1630
1631
1632
1633
1634 /**********************/
1635 /* Function map2fat() */ /* Not quite identical to OS/2 version */
1636 /**********************/
1637
1638 static void map2fat(pathcomp, pEndFAT)
1639 char *pathcomp, **pEndFAT;
1640 {
1641 char *ppc = pathcomp; /* variable pointer to pathcomp */
1642 char *pEnd = *pEndFAT; /* variable pointer to buildpathFAT */
1643 char *pBegin = *pEndFAT; /* constant pointer to start of this comp. */
1644 char *last_dot = NULL; /* last dot not converted to underscore */
1645 int dotname = FALSE; /* flag: path component begins with dot */
1646 /* ("." and ".." don't count) */
1647 register unsigned workch; /* hold the character being tested */
1648
1649
1650 /* Only need check those characters which are legal in NTFS but not
1651 * in FAT: to get here, must already have passed through mapname.
1652 * Also must truncate path component to ensure 8.3 compliance.
1653 */
1654 while ((workch = (uch)*ppc++) != 0) {
1655 switch (workch) {
1656 case '[':
1657 case ']':
1658 case '+':
1659 case ',':
1660 case ';':
1661 case '=':
1662 *pEnd++ = '_'; /* convert brackets to underscores */
1663 break;
1664
1665 case '.':
1666 if (pEnd == *pEndFAT) { /* nothing appended yet... */
1667 if (*ppc == '\0') /* don't bother appending a */
1668 break; /* "./" component to the path */
1669 else if (*ppc == '.' && ppc[1] == '\0') { /* "../" */
1670 *pEnd++ = '.'; /* add first dot, unchanged... */
1671 ++ppc; /* skip second dot, since it will */
1672 } else { /* be "added" at end of if-block */
1673 *pEnd++ = '_'; /* FAT doesn't allow null filename */
1674 dotname = TRUE; /* bodies, so map .exrc -> _.exrc */
1675 } /* (extra '_' now, "dot" below) */
1676 } else if (dotname) { /* found a second dot, but still */
1677 dotname = FALSE; /* have extra leading underscore: */
1678 *pEnd = '\0'; /* remove it by shifting chars */
1679 pEnd = *pEndFAT + 1; /* left one space (e.g., .p1.p2: */
1680 while (pEnd[1]) { /* __p1 -> _p1_p2 -> _p1.p2 when */
1681 *pEnd = pEnd[1]; /* finished) [opt.: since first */
1682 ++pEnd; /* two chars are same, can start */
1683 } /* shifting at second position] */
1684 }
1685 last_dot = pEnd; /* point at last dot so far... */
1686 *pEnd++ = '_'; /* convert dot to underscore for now */
1687 break;
1688
1689 default:
1690 *pEnd++ = (char)workch;
1691
1692 } /* end switch */
1693 } /* end while loop */
1694
1695 *pEnd = '\0'; /* terminate buildpathFAT */
1696
1697 /* NOTE: keep in mind that pEnd points to the end of the path
1698 * component, and *pEndFAT still points to the *beginning* of it...
1699 * Also note that the algorithm does not try to get too fancy:
1700 * if there are no dots already, the name either gets truncated
1701 * at 8 characters or the last underscore is converted to a dot
1702 * (only if more characters are saved that way). In no case is
1703 * a dot inserted between existing characters.
1704 */
1705 if (last_dot == NULL) { /* no dots: check for underscores... */
1706 char *plu = strrchr(pBegin, '_'); /* pointer to last underscore */
1707
1708 if (plu == NULL) { /* no dots, no underscores: truncate at 8 chars */
1709 *pEndFAT += 8; /* (or could insert '.' and keep 11...?) */
1710 if (*pEndFAT > pEnd)
1711 *pEndFAT = pEnd; /* oops...didn't have 8 chars to truncate */
1712 else
1713 **pEndFAT = '\0';
1714 } else if (MIN(plu - pBegin, 8) + MIN(pEnd - plu - 1, 3) > 8) {
1715 last_dot = plu; /* be lazy: drop through to next if-blk */
1716 } else if ((pEnd - *pEndFAT) > 8) {
1717 *pEndFAT += 8; /* more fits into just basename than if */
1718 **pEndFAT = '\0'; /* convert last underscore to dot */
1719 } else
1720 *pEndFAT = pEnd; /* whole thing fits into 8 chars or less */
1721 }
1722
1723 if (last_dot != NULL) { /* one dot (or two, in the case of */
1724 *last_dot = '.'; /* "..") is OK: put it back in */
1725
1726 if ((last_dot - pBegin) > 8) {
1727 char *p, *q;
1728 int i;
1729
1730 p = last_dot;
1731 q = last_dot = pBegin + 8;
1732 for (i = 0; (i < 4) && *p; ++i) /* too many chars in basename: */
1733 *q++ = *p++; /* shift .ext left and trun- */
1734 *q = '\0'; /* cate/terminate it */
1735 *pEndFAT = q;
1736 } else if ((pEnd - last_dot) > 4) { /* too many chars in extension */
1737 *pEndFAT = last_dot + 4;
1738 **pEndFAT = '\0';
1739 } else
1740 *pEndFAT = pEnd; /* filename is fine; point at terminating zero */
1741
1742 if ((last_dot - pBegin) > 0 && last_dot[-1] == ' ')
1743 last_dot[-1] = '_'; /* NO blank in front of '.'! */
1744 }
1745 } /* end function map2fat() */
1746
1747
1748
1749
1750 /***********************/ /* Borrowed from os2.c for UnZip 5.1. */
1751 /* Function checkdir() */ /* Difference: no EA stuff */
1752 /***********************/ /* HPFS stuff works on NTFS too */
1753
1754 int checkdir(__G__ pathcomp, flag)
1755 __GDEF
1756 char *pathcomp;
1757 int flag;
1758 /*
1759 * returns: 1 - (on APPEND_NAME) truncated filename
1760 * 2 - path doesn't exist, not allowed to create
1761 * 3 - path doesn't exist, tried to create and failed; or
1762 * path exists and is not a directory, but is supposed to be
1763 * 4 - path is too long
1764 * 10 - can't allocate memory for filename buffers
1765 */
1766 {
1767 /* static int rootlen = 0; */ /* length of rootpath */
1768 /* static char *rootpath; */ /* user's "extract-to" directory */
1769 /* static char *buildpathHPFS; */ /* full path (so far) to extracted file, */
1770 /* static char *buildpathFAT; */ /* both HPFS/EA (main) and FAT versions */
1771 /* static char *endHPFS; */ /* corresponding pointers to end of */
1772 /* static char *endFAT; */ /* buildpath ('\0') */
1773
1774 # define FN_MASK 7
1775 # define FUNCTION (flag & FN_MASK)
1776
1777
1778
1779 /*---------------------------------------------------------------------------
1780 APPEND_DIR: append the path component to the path being built and check
1781 for its existence. If doesn't exist and we are creating directories, do
1782 so for this one; else signal success or error as appropriate.
1783 ---------------------------------------------------------------------------*/
1784
1785 if (FUNCTION == APPEND_DIR) {
1786 char *p = pathcomp;
1787 int too_long=FALSE;
1788
1789 Trace((stderr, "appending dir segment [%s]\n", pathcomp));
1790 while ((*G.endHPFS = *p++) != '\0') /* copy to HPFS filename */
1791 ++G.endHPFS;
1792 if (!IsVolumeOldFAT(__G__ G.buildpathHPFS)) {
1793 p = pathcomp;
1794 while ((*G.endFAT = *p++) != '\0') /* copy to FAT filename, too */
1795 ++G.endFAT;
1796 } else
1797 map2fat(pathcomp, &G.endFAT); /* map into FAT fn, update endFAT */
1798
1799 /* GRR: could do better check, see if overrunning buffer as we go:
1800 * check endHPFS-buildpathHPFS after each append, set warning variable
1801 * if within 20 of FILNAMSIZ; then if var set, do careful check when
1802 * appending. Clear variable when begin new path. */
1803
1804 /* next check: need to append '/', at least one-char name, '\0' */
1805 if ((G.endHPFS-G.buildpathHPFS) > FILNAMSIZ-3)
1806 too_long = TRUE; /* check if extracting dir? */
1807 #ifdef FIX_STAT_BUG
1808 /* Borland C++ 5.0 does not handle a call to stat() well if the
1809 * directory does not exist (it tends to crash in strange places.)
1810 * This is apparently a problem only when compiling for GUI rather
1811 * than console. The code below attempts to work around this problem.
1812 */
1813 if (access(G.buildpathFAT, 0) != 0) {
1814 if (!G.create_dirs) { /* told not to create (freshening) */
1815 free(G.buildpathHPFS);
1816 free(G.buildpathFAT);
1817 return 2; /* path doesn't exist: nothing to do */
1818 }
1819 if (too_long) { /* GRR: should allow FAT extraction w/o EAs */
1820 Info(slide, 1, ((char *)slide,
1821 "checkdir error: path too long: %s\n",
1822 FnFilter1(G.buildpathHPFS)));
1823 free(G.buildpathHPFS);
1824 free(G.buildpathFAT);
1825 return 4; /* no room for filenames: fatal */
1826 }
1827 if (MKDIR(G.buildpathFAT, 0777) == -1) { /* create the directory */
1828 Info(slide, 1, ((char *)slide,
1829 "checkdir error: cannot create %s\n\
1830 unable to process %s.\n",
1831 FnFilter2(G.buildpathFAT), FnFilter1(G.filename)));
1832 free(G.buildpathHPFS);
1833 free(G.buildpathFAT);
1834 return 3; /* path didn't exist, tried to create, failed */
1835 }
1836 G.created_dir = TRUE;
1837 }
1838 #endif /* FIX_STAT_BUG */
1839 if (SSTAT(G.buildpathFAT, &G.statbuf)) /* path doesn't exist */
1840 {
1841 if (!G.create_dirs) { /* told not to create (freshening) */
1842 free(G.buildpathHPFS);
1843 free(G.buildpathFAT);
1844 return 2; /* path doesn't exist: nothing to do */
1845 }
1846 if (too_long) { /* GRR: should allow FAT extraction w/o EAs */
1847 Info(slide, 1, ((char *)slide,
1848 "checkdir error: path too long: %s\n",
1849 FnFilter1(G.buildpathHPFS)));
1850 free(G.buildpathHPFS);
1851 free(G.buildpathFAT);
1852 return 4; /* no room for filenames: fatal */
1853 }
1854 if (MKDIR(G.buildpathFAT, 0777) == -1) { /* create the directory */
1855 Info(slide, 1, ((char *)slide,
1856 "checkdir error: cannot create %s\n\
1857 unable to process %s.\n",
1858 FnFilter2(G.buildpathFAT), FnFilter1(G.filename)));
1859 free(G.buildpathHPFS);
1860 free(G.buildpathFAT);
1861 return 3; /* path didn't exist, tried to create, failed */
1862 }
1863 G.created_dir = TRUE;
1864 } else if (!S_ISDIR(G.statbuf.st_mode)) {
1865 Info(slide, 1, ((char *)slide,
1866 "checkdir error: %s exists but is not directory\n \
1867 unable to process %s.\n",
1868 FnFilter2(G.buildpathFAT), FnFilter1(G.filename)));
1869 free(G.buildpathHPFS);
1870 free(G.buildpathFAT);
1871 return 3; /* path existed but wasn't dir */
1872 }
1873 if (too_long) {
1874 Info(slide, 1, ((char *)slide,
1875 "checkdir error: path too long: %s\n",
1876 FnFilter1(G.buildpathHPFS)));
1877 free(G.buildpathHPFS);
1878 free(G.buildpathFAT);
1879 return 4; /* no room for filenames: fatal */
1880 }
1881 *G.endHPFS++ = '/';
1882 *G.endFAT++ = '/';
1883 *G.endHPFS = *G.endFAT = '\0';
1884 Trace((stderr, "buildpathHPFS now = [%s]\nbuildpathFAT now = [%s]\n",
1885 FnFilter1(G.buildpathHPFS), FnFilter2(G.buildpathFAT)));
1886 return 0;
1887
1888 } /* end if (FUNCTION == APPEND_DIR) */
1889
1890 /*---------------------------------------------------------------------------
1891 GETPATH: copy full FAT path to the string pointed at by pathcomp (want
1892 filename to reflect name used on disk, not EAs; if full path is HPFS,
1893 buildpathFAT and buildpathHPFS will be identical). Also free both paths.
1894 ---------------------------------------------------------------------------*/
1895
1896 if (FUNCTION == GETPATH) {
1897 Trace((stderr, "getting and freeing FAT path [%s]\n",
1898 FnFilter1(G.buildpathFAT)));
1899 Trace((stderr, "freeing HPFS path [%s]\n",
1900 FnFilter1(G.buildpathHPFS)));
1901 strcpy(pathcomp, G.buildpathFAT);
1902 free(G.buildpathFAT);
1903 free(G.buildpathHPFS);
1904 G.buildpathHPFS = G.buildpathFAT = G.endHPFS = G.endFAT = NULL;
1905 return 0;
1906 }
1907
1908 /*---------------------------------------------------------------------------
1909 APPEND_NAME: assume the path component is the filename; append it and
1910 return without checking for existence.
1911 ---------------------------------------------------------------------------*/
1912
1913 if (FUNCTION == APPEND_NAME) {
1914 char *p = pathcomp;
1915 int error = 0;
1916
1917 Trace((stderr, "appending filename [%s]\n", FnFilter1(pathcomp)));
1918 while ((*G.endHPFS = *p++) != '\0') { /* copy to HPFS filename */
1919 ++G.endHPFS;
1920 if ((G.endHPFS-G.buildpathHPFS) >= FILNAMSIZ) {
1921 *--G.endHPFS = '\0';
1922 Info(slide, 1, ((char *)slide,
1923 "checkdir warning: path too long; truncating\n \
1924 %s\n -> %s\n",
1925 FnFilter1(G.filename), FnFilter2(G.buildpathHPFS)));
1926 error = 1; /* filename truncated */
1927 }
1928 }
1929
1930 if ( G.pInfo->vollabel || !IsVolumeOldFAT(__G__ G.buildpathHPFS)) {
1931 p = pathcomp;
1932 while ((*G.endFAT = *p++) != '\0') /* copy to FAT filename, too */
1933 ++G.endFAT;
1934 } else
1935 map2fat(pathcomp, &G.endFAT); /* map into FAT fn, update endFAT */
1936 Trace((stderr, "buildpathHPFS: %s\nbuildpathFAT: %s\n",
1937 FnFilter1(G.buildpathHPFS), FnFilter2(G.buildpathFAT)));
1938
1939 return error; /* could check for existence, prompt for new name... */
1940
1941 } /* end if (FUNCTION == APPEND_NAME) */
1942
1943 /*---------------------------------------------------------------------------
1944 INIT: allocate and initialize buffer space for the file currently being
1945 extracted. If file was renamed with an absolute path, don't prepend the
1946 extract-to path.
1947 ---------------------------------------------------------------------------*/
1948
1949 if (FUNCTION == INIT) {
1950 Trace((stderr, "initializing buildpathHPFS and buildpathFAT to "));
1951 if ((G.buildpathHPFS = (char *)malloc(G.fnlen+G.rootlen+1)) == NULL)
1952 return 10;
1953 if ((G.buildpathFAT = (char *)malloc(G.fnlen+G.rootlen+1)) == NULL) {
1954 free(G.buildpathHPFS);
1955 return 10;
1956 }
1957 if (G.pInfo->vollabel) { /* use root or renamed path, but don't store */
1958 /* GRR: for network drives, do strchr() and return IZ_VOL_LABEL if not [1] */
1959 if (G.renamed_fullpath && pathcomp[1] == ':')
1960 *G.buildpathHPFS = (char)ToLower(*pathcomp);
1961 else if (!G.renamed_fullpath && G.rootlen > 1 &&
1962 G.rootpath[1] == ':')
1963 *G.buildpathHPFS = (char)ToLower(*G.rootpath);
1964 else {
1965 char tmpN[MAX_PATH], *tmpP;
1966 if (GetFullPathName(".", MAX_PATH, tmpN, &tmpP) > MAX_PATH)
1967 { /* by definition of MAX_PATH we should never get here */
1968 Info(slide, 1, ((char *)slide,
1969 "checkdir warning: current dir path too long\n"));
1970 return 1; /* can't get drive letter */
1971 }
1972 G.nLabelDrive = *tmpN - 'a' + 1;
1973 *G.buildpathHPFS = (char)(G.nLabelDrive - 1 + 'a');
1974 }
1975 G.nLabelDrive = *G.buildpathHPFS - 'a' + 1; /* save for mapname() */
1976 if (uO.volflag == 0 || *G.buildpathHPFS < 'a' /* no labels/bogus? */
1977 || (uO.volflag == 1 && !isfloppy(G.nLabelDrive))) { /* !fixed */
1978 free(G.buildpathHPFS);
1979 free(G.buildpathFAT);
1980 return IZ_VOL_LABEL; /* skipping with message */
1981 }
1982 *G.buildpathHPFS = '\0';
1983 } else if (G.renamed_fullpath) /* pathcomp = valid data */
1984 strcpy(G.buildpathHPFS, pathcomp);
1985 else if (G.rootlen > 0)
1986 strcpy(G.buildpathHPFS, G.rootpath);
1987 else
1988 *G.buildpathHPFS = '\0';
1989 G.endHPFS = G.buildpathHPFS;
1990 G.endFAT = G.buildpathFAT;
1991 while ((*G.endFAT = *G.endHPFS) != '\0') {
1992 ++G.endFAT;
1993 ++G.endHPFS;
1994 }
1995 Trace((stderr, "[%s]\n", FnFilter1(G.buildpathHPFS)));
1996 return 0;
1997 }
1998
1999 /*---------------------------------------------------------------------------
2000 ROOT: if appropriate, store the path in rootpath and create it if neces-
2001 sary; else assume it's a zipfile member and return. This path segment
2002 gets used in extracting all members from every zipfile specified on the
2003 command line. Note that under OS/2 and MS-DOS, if a candidate extract-to
2004 directory specification includes a drive letter (leading "x:"), it is
2005 treated just as if it had a trailing '/'--that is, one directory level
2006 will be created if the path doesn't exist, unless this is otherwise pro-
2007 hibited (e.g., freshening).
2008 ---------------------------------------------------------------------------*/
2009
2010 #if (!defined(SFX) || defined(SFX_EXDIR))
2011 if (FUNCTION == ROOT) {
2012 Trace((stderr, "initializing root path to [%s]\n",
2013 FnFilter1(pathcomp)));
2014 if (pathcomp == NULL) {
2015 G.rootlen = 0;
2016 return 0;
2017 }
2018 if ((G.rootlen = strlen(pathcomp)) > 0) {
2019 int had_trailing_pathsep=FALSE, has_drive=FALSE, xtra=2;
2020
2021 if (isalpha((uch)pathcomp[0]) && pathcomp[1] == ':')
2022 has_drive = TRUE; /* drive designator */
2023 if (pathcomp[G.rootlen-1] == '/' || pathcomp[G.rootlen-1] == '\\') {
2024 pathcomp[--G.rootlen] = '\0';
2025 had_trailing_pathsep = TRUE;
2026 }
2027 if (has_drive && (G.rootlen == 2)) {
2028 if (!had_trailing_pathsep) /* i.e., original wasn't "x:/" */
2029 xtra = 3; /* room for '.' + '/' + 0 at end of "x:" */
2030 } else if (G.rootlen > 0) { /* need not check "x:." and "x:/" */
2031 if (SSTAT(pathcomp, &G.statbuf) || !S_ISDIR(G.statbuf.st_mode))
2032 {
2033 /* path does not exist */
2034 if (!G.create_dirs /* || iswild(pathcomp) */ ) {
2035 G.rootlen = 0;
2036 return 2; /* treat as stored file */
2037 }
2038 /* create directory (could add loop here to scan pathcomp
2039 * and create more than one level, but really necessary?) */
2040 if (MKDIR(pathcomp, 0777) == -1) {
2041 Info(slide, 1, ((char *)slide,
2042 "checkdir: cannot create extraction directory: %s\n",
2043 FnFilter1(pathcomp)));
2044 G.rootlen = 0; /* path didn't exist, tried to create, */
2045 return 3; /* failed: file exists, or need 2+ levels */
2046 }
2047 }
2048 }
2049 if ((G.rootpath = (char *)malloc(G.rootlen+xtra)) == NULL) {
2050 G.rootlen = 0;
2051 return 10;
2052 }
2053 strcpy(G.rootpath, pathcomp);
2054 if (xtra == 3) /* had just "x:", make "x:." */
2055 G.rootpath[G.rootlen++] = '.';
2056 G.rootpath[G.rootlen++] = '/';
2057 G.rootpath[G.rootlen] = '\0';
2058 Trace((stderr, "rootpath now = [%s]\n", FnFilter1(G.rootpath)));
2059 }
2060 return 0;
2061 }
2062 #endif /* !SFX || SFX_EXDIR */
2063
2064 /*---------------------------------------------------------------------------
2065 END: free rootpath, immediately prior to program exit.
2066 ---------------------------------------------------------------------------*/
2067
2068 if (FUNCTION == END) {
2069 Trace((stderr, "freeing rootpath\n"));
2070 if (G.rootlen > 0) {
2071 free(G.rootpath);
2072 G.rootlen = 0;
2073 }
2074 return 0;
2075 }
2076
2077 return 99; /* should never reach */
2078
2079 } /* end function checkdir() */
2080
2081
2082
2083
2084
2085 #ifndef SFX
2086 #ifndef WINDLL
2087
2088 /************************/
2089 /* Function version() */
2090 /************************/
2091
2092 void version(__G)
2093 __GDEF
2094 {
2095 int len;
2096 #if (defined(_MSC_VER) || defined(__WATCOMC__) || defined(__DJGPP__))
2097 char buf[80];
2098 #if (defined(_MSC_VER) && (_MSC_VER > 900))
2099 char buf2[80];
2100 #endif
2101 #endif
2102
2103 len = sprintf((char *)slide, CompiledWith,
2104
2105 #if defined(_MSC_VER) /* MSC == VC++, but what about SDK compiler? */
2106 (sprintf(buf, "Microsoft C %d.%02d ", _MSC_VER/100, _MSC_VER%100), buf),
2107 # if (_MSC_VER == 800)
2108 "(Visual C++ v1.1)",
2109 # elif (_MSC_VER == 850)
2110 "(Windows NT v3.5 SDK)",
2111 # elif (_MSC_VER == 900)
2112 "(Visual C++ v2.x)",
2113 # elif (_MSC_VER > 900)
2114 (sprintf(buf2, "(Visual C++ %d.%d)", _MSC_VER/100 - 6, _MSC_VER%100/10),
2115 buf2),
2116 # else
2117 "(bad version)",
2118 # endif
2119 #elif defined(__WATCOMC__)
2120 # if (__WATCOMC__ % 10 > 0)
2121 (sprintf(buf, "Watcom C/C++ %d.%02d", __WATCOMC__ / 100,
2122 __WATCOMC__ % 100), buf), "",
2123 # else
2124 (sprintf(buf, "Watcom C/C++ %d.%d", __WATCOMC__ / 100,
2125 (__WATCOMC__ % 100) / 10), buf), "",
2126 # endif
2127 #elif defined(__BORLANDC__)
2128 "Borland C++",
2129 # if (__BORLANDC__ < 0x0200)
2130 " 1.0",
2131 # elif (__BORLANDC__ == 0x0200)
2132 " 2.0",
2133 # elif (__BORLANDC__ == 0x0400)
2134 " 3.0",
2135 # elif (__BORLANDC__ == 0x0410) /* __BCPLUSPLUS__ = 0x0310 */
2136 " 3.1",
2137 # elif (__BORLANDC__ == 0x0452) /* __BCPLUSPLUS__ = 0x0320 */
2138 " 4.0 or 4.02",
2139 # elif (__BORLANDC__ == 0x0460) /* __BCPLUSPLUS__ = 0x0340 */
2140 " 4.5",
2141 # elif (__BORLANDC__ == 0x0500) /* __BCPLUSPLUS__ = 0x0340 */
2142 " 5.0",
2143 # elif (__BORLANDC__ == 0x0520) /* __BCPLUSPLUS__ = 0x0520 */
2144 " 5.2 (C++ Builder)", /* GRR: assume this will stay sync'd? */
2145 # else
2146 " later than 5.2",
2147 # endif
2148 #elif defined(__LCC__)
2149 "LCC-Win32", "",
2150 #elif defined(__GNUC__)
2151 # if defined(__RSXNT__)
2152 # if (defined(__DJGPP__) && !defined(__EMX__))
2153 (sprintf(buf, "rsxnt(djgpp v%d.%02d) / gcc ",
2154 __DJGPP__, __DJGPP_MINOR__), buf),
2155 # elif defined(__DJGPP__)
2156 (sprintf(buf, "rsxnt(emx+djgpp v%d.%02d) / gcc ",
2157 __DJGPP__, __DJGPP_MINOR__), buf),
2158 # elif (defined(__GO32__) && !defined(__EMX__))
2159 "rsxnt(djgpp v1.x) / gcc ",
2160 # elif defined(__GO32__)
2161 "rsxnt(emx + djgpp v1.x) / gcc ",
2162 # elif defined(__EMX__)
2163 "rsxnt(emx)+gcc ",
2164 # else
2165 "rsxnt(unknown) / gcc ",
2166 # endif
2167 # elif defined(__CYGWIN32__)
2168 "cygnus win32 / gcc ",
2169 # elif defined(__MINGW32__)
2170 "mingw32 / gcc ",
2171 # else
2172 "gcc ",
2173 # endif
2174 __VERSION__,
2175 #else /* !_MSC_VER, !__WATCOMC__, !__BORLANDC__, !__LCC__, !__GNUC__ */
2176 "unknown compiler (SDK?)", "",
2177 #endif /* ?compilers */
2178
2179 "Windows 95 / Windows NT", "\n(32-bit)",
2180
2181 #ifdef __DATE__
2182 " on ", __DATE__
2183 #else
2184 "", ""
2185 #endif
2186 );
2187
2188 (*G.message)((zvoid *)&G, slide, (ulg)len, 0);
2189
2190 return;
2191
2192 } /* end function version() */
2193
2194 #endif /* !WINDLL */
2195 #endif /* !SFX */
2196
2197
2198
2199 #ifdef W32_STAT_BANDAID
2200
2201 /* All currently known variants of WIN32 operating systems (Windows 95/98,
2202 * WinNT 3.x, 4.0, 5.0) have a nasty bug in the OS kernel concerning
2203 * conversions between UTC and local time: In the time conversion functions
2204 * of the Win32 API, the timezone offset (including seasonal daylight saving
2205 * shift) between UTC and local time evaluation is erratically based on the
2206 * current system time. The correct evaluation must determine the offset
2207 * value as it {was/is/will be} for the actual time to be converted.
2208 *
2209 * Some versions of MS C runtime lib's stat() returns utc time-stamps so
2210 * that localtime(timestamp) corresponds to the (potentially false) local
2211 * time shown by the OS' system programs (Explorer, command shell dir, etc.)
2212 * The RSXNT port follows the same strategy, but fails to recognize the
2213 * access-time attribute.
2214 *
2215 * For the NTFS file system (and other filesystems that store time-stamps
2216 * as UTC values), this results in st_mtime (, st_{c|a}time) fields which
2217 * are not stable but vary according to the seasonal change of "daylight
2218 * saving time in effect / not in effect".
2219 *
2220 * Other C runtime libs (CygWin, or the CRT DLLs supplied with Win95/NT
2221 * return the unix-time equivalent of the UTC FILETIME values as got back
2222 * from the Win32 API call. This time, return values from NTFS are correct
2223 * whereas utimes from files on (V)FAT volumes vary according to the DST
2224 * switches.
2225 *
2226 * To achieve timestamp consistency of UTC (UT extra field) values in
2227 * Zip archives, the Info-ZIP programs require work-around code for
2228 * proper time handling in stat() (and other time handling routines).
2229 */
2230 /* stat() functions under Windows95 tend to fail for root directories. *
2231 * Watcom and Borland, at least, are affected by this bug. Watcom made *
2232 * a partial fix for 11.0 but still missed some cases. This substitute *
2233 * detects the case and fills in reasonable values. Otherwise we get *
2234 * effects like failure to extract to a root dir because it's not found. */
2235
2236 int zstat_win32(__W32STAT_GLOBALS__ const char *path, struct stat *buf)
2237 {
2238 if (!stat(path, buf))
2239 {
2240 #ifdef NT_TZBUG_WORKAROUND
2241 /* stat was successful, now redo the time-stamp fetches */
2242 int fs_uses_loctime = FStampIsLocTime(__G__ path);
2243 HANDLE h;
2244 FILETIME Modft, Accft, Creft;
2245 #ifdef __RSXNT__ /* RSXNT/EMX C rtl uses OEM charset */
2246 char *ansi_path = (char *)alloca(strlen(path) + 1);
2247
2248 INTERN_TO_ISO(path, ansi_path);
2249 # define Ansi_Path ansi_path
2250 #else
2251 # define Ansi_Path path
2252 #endif
2253
2254 TTrace((stdout, "stat(%s) finds modtime %08lx\n", path, buf->st_mtime));
2255 h = CreateFile(Ansi_Path, GENERIC_READ,
2256 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
2257 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
2258 if (h != INVALID_HANDLE_VALUE) {
2259 BOOL ftOK = GetFileTime(h, &Creft, &Accft, &Modft);
2260 CloseHandle(h);
2261
2262 if (ftOK) {
2263 FTTrace((stdout, "GetFileTime returned Modft", 0, &Modft));
2264 FTTrace((stdout, "GetFileTime returned Creft", 0, &Creft));
2265 if (!fs_uses_loctime) {
2266 /* On a filesystem that stores UTC timestamps, we refill
2267 * the time fields of the struct stat buffer by directly
2268 * using the UTC values as returned by the Win32
2269 * GetFileTime() API call.
2270 */
2271 FileTime2utime(&Modft, &(buf->st_mtime));
2272 if (Accft.dwLowDateTime != 0 || Accft.dwHighDateTime != 0)
2273 FileTime2utime(&Accft, &(buf->st_atime));
2274 else
2275 buf->st_atime = buf->st_mtime;
2276 if (Creft.dwLowDateTime != 0 || Creft.dwHighDateTime != 0)
2277 FileTime2utime(&Creft, &(buf->st_ctime));
2278 else
2279 buf->st_ctime = buf->st_mtime;
2280 TTrace((stdout,"NTFS, recalculated modtime %08lx\n",
2281 buf->st_mtime));
2282 } else {
2283 /* On VFAT and FAT-like filesystems, the FILETIME values
2284 * are converted back to the stable local time before
2285 * converting them to UTC unix time-stamps.
2286 */
2287 VFatFileTime2utime(&Modft, &(buf->st_mtime));
2288 if (Accft.dwLowDateTime != 0 || Accft.dwHighDateTime != 0)
2289 VFatFileTime2utime(&Accft, &(buf->st_atime));
2290 else
2291 buf->st_atime = buf->st_mtime;
2292 if (Creft.dwLowDateTime != 0 || Creft.dwHighDateTime != 0)
2293 VFatFileTime2utime(&Creft, &(buf->st_ctime));
2294 else
2295 buf->st_ctime = buf->st_mtime;
2296 TTrace((stdout, "VFAT, recalculated modtime %08lx\n",
2297 buf->st_mtime));
2298 }
2299 }
2300 }
2301 # undef Ansi_Path
2302 #endif /* NT_TZBUG_WORKAROUND */
2303 return 0;
2304 }
2305 #ifdef W32_STATROOT_FIX
2306 else
2307 {
2308 DWORD flags;
2309 #ifdef __RSXNT__ /* RSXNT/EMX C rtl uses OEM charset */
2310 char *ansi_path = (char *)alloca(strlen(path) + 1);
2311
2312 INTERN_TO_ISO(path, ansi_path);
2313 # define Ansi_Path ansi_path
2314 #else
2315 # define Ansi_Path path
2316 #endif
2317
2318 flags = GetFileAttributes(Ansi_Path);
2319 if (flags != 0xFFFFFFFF && flags & FILE_ATTRIBUTE_DIRECTORY) {
2320 Trace((stderr, "\nstat(\"%s\",...) failed on existing directory\n",
2321 path));
2322 memset(buf, 0, sizeof(struct stat));
2323 buf->st_atime = buf->st_ctime = buf->st_mtime =
2324 dos_to_unix_time(DOSTIME_MINIMUM); /* 1-1-80 */
2325 buf->st_mode = S_IFDIR | S_IREAD |
2326 ((flags & FILE_ATTRIBUTE_READONLY) ? 0 : S_IWRITE);
2327 return 0;
2328 } /* assumes: stat() won't fail on non-dirs without good reason */
2329 # undef Ansi_Path
2330 }
2331 #endif /* W32_STATROOT_FIX */
2332 return -1;
2333 }
2334
2335 #endif /* W32_STAT_BANDAID */
2336
2337 #endif /* !FUNZIP */
2338
2339
2340
2341 #ifndef WINDLL
2342 /* This replacement getch() function was originally created for Watcom C
2343 * and then additionally used with CYGWIN. Since UnZip 5.4, all other Win32
2344 * ports apply this replacement rather that their supplied getch() (or
2345 * alike) function. There are problems with unabsorbed LF characters left
2346 * over in the keyboard buffer under Win95 (and 98) when ENTER was pressed.
2347 * (Under Win95, ENTER returns two(!!) characters: CR-LF.) This problem
2348 * does not appear when run on a WinNT console prompt!
2349 */
2350
2351 /* Watcom 10.6's getch() does not handle Alt+<digit><digit><digit>. */
2352 /* Note that if PASSWD_FROM_STDIN is defined, the file containing */
2353 /* the password must have a carriage return after the word, not a */
2354 /* Unix-style newline (linefeed only). This discards linefeeds. */
2355
2356 int getch_win32(void)
2357 {
2358 HANDLE stin;
2359 DWORD rc;
2360 unsigned char buf[2];
2361 int ret = -1;
2362 DWORD odemode = ~(DWORD)0;
2363
2364 # ifdef PASSWD_FROM_STDIN
2365 stin = GetStdHandle(STD_INPUT_HANDLE);
2366 # else
2367 stin = CreateFile("CONIN$", GENERIC_READ | GENERIC_WRITE,
2368 FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
2369 if (stin == INVALID_HANDLE_VALUE)
2370 return -1;
2371 # endif
2372 if (GetConsoleMode(stin, &odemode))
2373 SetConsoleMode(stin, ENABLE_PROCESSED_INPUT); /* raw except ^C noticed */
2374 if (ReadFile(stin, &buf, 1, &rc, NULL) && rc == 1)
2375 ret = buf[0];
2376 /* when the user hits return we get CR LF. We discard the LF, not the CR,
2377 * because when we call this for the first time after a previous input
2378 * such as the one for "replace foo? [y]es, ..." the LF may still be in
2379 * the input stream before whatever the user types at our prompt. */
2380 if (ret == '\n')
2381 if (ReadFile(stin, &buf, 1, &rc, NULL) && rc == 1)
2382 ret = buf[0];
2383 if (odemode != ~(DWORD)0)
2384 SetConsoleMode(stin, odemode);
2385 # ifndef PASSWD_FROM_STDIN
2386 CloseHandle(stin);
2387 # endif
2388 return ret;
2389 }
2390 #endif /* !WINDLL */