]>
Commit | Line | Data |
---|---|---|
f6bcfd97 BP |
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(<m); | |
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 */ |