]>
Commit | Line | Data |
---|---|---|
1 | /*--------------------------------------------------------------------------- | |
2 | ||
3 | process.c | |
4 | ||
5 | This file contains the top-level routines for processing multiple zipfiles. | |
6 | ||
7 | Contains: process_zipfiles() | |
8 | free_G_buffers() | |
9 | do_seekable() | |
10 | find_ecrec() | |
11 | uz_end_central() | |
12 | process_cdir_file_hdr() | |
13 | get_cdir_ent() | |
14 | process_local_file_hdr() | |
15 | ef_scan_for_izux() | |
16 | ||
17 | ---------------------------------------------------------------------------*/ | |
18 | ||
19 | ||
20 | #define UNZIP_INTERNAL | |
21 | #include "unzip.h" | |
22 | #ifdef WINDLL | |
23 | # ifdef POCKET_UNZIP | |
24 | # include "wince/intrface.h" | |
25 | # else | |
26 | # include "windll/windll.h" | |
27 | # endif | |
28 | #endif | |
29 | ||
30 | static int do_seekable OF((__GPRO__ int lastchance)); | |
31 | static int find_ecrec OF((__GPRO__ long searchlen)); | |
32 | ||
33 | int files = 0; | |
34 | ||
35 | static ZCONST char Far CannotAllocateBuffers[] = | |
36 | "error: cannot allocate unzip buffers\n"; | |
37 | ||
38 | #ifdef SFX | |
39 | static ZCONST char Far CannotFindMyself[] = | |
40 | "unzipsfx: cannot find myself! [%s]\n"; | |
41 | ||
42 | #else /* !SFX */ | |
43 | /* process_zipfiles() strings */ | |
44 | # if (defined(IZ_CHECK_TZ) && defined(USE_EF_UT_TIME)) | |
45 | static ZCONST char Far WarnInvalidTZ[] = | |
46 | "Warning: TZ environment variable not found, cannot use UTC times!!\n"; | |
47 | # endif | |
48 | static ZCONST char Far FilesProcessOK[] = | |
49 | "%d archive%s successfully processed.\n"; | |
50 | static ZCONST char Far ArchiveWarning[] = | |
51 | "%d archive%s had warnings but no fatal errors.\n"; | |
52 | static ZCONST char Far ArchiveFatalError[] = | |
53 | "%d archive%s had fatal errors.\n"; | |
54 | static ZCONST char Far FileHadNoZipfileDir[] = | |
55 | "%d file%s had no zipfile directory.\n"; | |
56 | static ZCONST char Far ZipfileWasDir[] = "1 \"zipfile\" was a directory.\n"; | |
57 | static ZCONST char Far ManyZipfilesWereDir[] = | |
58 | "%d \"zipfiles\" were directories.\n"; | |
59 | static ZCONST char Far NoZipfileFound[] = "No zipfiles found.\n"; | |
60 | ||
61 | /* do_seekable() strings */ | |
62 | # ifdef UNIX | |
63 | static ZCONST char Far CannotFindZipfileDirMsg[] = | |
64 | "%s: cannot find zipfile directory in one of %s or\n\ | |
65 | %s%s.zip, and cannot find %s, period.\n"; | |
66 | static ZCONST char Far CannotFindEitherZipfile[] = | |
67 | "%s: cannot find %s, %s.zip or %s.\n"; /* ", so there" removed 970918 */ | |
68 | # else /* !UNIX */ | |
69 | # ifndef AMIGA | |
70 | static ZCONST char Far CannotFindWildcardMatch[] = | |
71 | "%s: cannot find any matches for wildcard specification \"%s\".\n"; | |
72 | # endif /* !AMIGA */ | |
73 | static ZCONST char Far CannotFindZipfileDirMsg[] = | |
74 | "%s: cannot find zipfile directory in %s,\n\ | |
75 | %sand cannot find %s, period.\n"; | |
76 | static ZCONST char Far CannotFindEitherZipfile[] = | |
77 | "%s: cannot find either %s or %s.\n"; /* ", so there" removed 970918 */ | |
78 | # endif /* ?UNIX */ | |
79 | extern ZCONST char Far Zipnfo[]; /* in unzip.c */ | |
80 | #ifndef WINDLL | |
81 | static ZCONST char Far Unzip[] = "unzip"; | |
82 | #else | |
83 | static ZCONST char Far Unzip[] = "UnZip DLL"; | |
84 | #endif | |
85 | static ZCONST char Far MaybeExe[] = | |
86 | "note: %s may be a plain executable, not an archive\n"; | |
87 | static ZCONST char Far CentDirNotInZipMsg[] = "\n\ | |
88 | [%s]:\n\ | |
89 | Zipfile is disk %u of a multi-disk archive, and this is not the disk on\n\ | |
90 | which the central zipfile directory begins (disk %u).\n"; | |
91 | static ZCONST char Far EndCentDirBogus[] = | |
92 | "\nwarning [%s]: end-of-central-directory record claims this\n\ | |
93 | is disk %u but that the central directory starts on disk %u; this is a\n\ | |
94 | contradiction. Attempting to process anyway.\n"; | |
95 | # ifdef NO_MULTIPART | |
96 | static ZCONST char Far NoMultiDiskArcSupport[] = | |
97 | "\nerror [%s]: zipfile is part of multi-disk archive\n\ | |
98 | (sorry, not yet supported).\n"; | |
99 | static ZCONST char Far MaybePakBug[] = "warning [%s]:\ | |
100 | zipfile claims to be 2nd disk of a 2-part archive;\n\ | |
101 | attempting to process anyway. If no further errors occur, this archive\n\ | |
102 | was probably created by PAK v2.51 or earlier. This bug was reported to\n\ | |
103 | NoGate in March 1991 and was supposed to have been fixed by mid-1991; as\n\ | |
104 | of mid-1992 it still hadn't been. (If further errors do occur, archive\n\ | |
105 | was probably created by PKZIP 2.04c or later; UnZip does not yet support\n\ | |
106 | multi-part archives.)\n"; | |
107 | # else | |
108 | static ZCONST char Far MaybePakBug[] = "warning [%s]:\ | |
109 | zipfile claims to be last disk of a multi-part archive;\n\ | |
110 | attempting to process anyway, assuming all parts have been concatenated\n\ | |
111 | together in order. Expect \"errors\" and warnings...true multi-part support\ | |
112 | \n doesn't exist yet (coming soon).\n"; | |
113 | # endif | |
114 | static ZCONST char Far ExtraBytesAtStart[] = | |
115 | "warning [%s]: %ld extra byte%s at beginning or within zipfile\n\ | |
116 | (attempting to process anyway)\n"; | |
117 | #endif /* ?SFX */ | |
118 | ||
119 | static ZCONST char Far MissingBytes[] = | |
120 | "error [%s]: missing %ld bytes in zipfile\n\ | |
121 | (attempting to process anyway)\n"; | |
122 | static ZCONST char Far NullCentDirOffset[] = | |
123 | "error [%s]: NULL central directory offset\n\ | |
124 | (attempting to process anyway)\n"; | |
125 | static ZCONST char Far ZipfileEmpty[] = "warning [%s]: zipfile is empty\n"; | |
126 | static ZCONST char Far CentDirStartNotFound[] = | |
127 | "error [%s]: start of central directory not found;\n\ | |
128 | zipfile corrupt.\n%s"; | |
129 | #ifndef SFX | |
130 | static ZCONST char Far CentDirTooLong[] = | |
131 | "error [%s]: reported length of central directory is\n\ | |
132 | %ld bytes too long (Atari STZip zipfile? J.H.Holm ZIPSPLIT 1.1\n\ | |
133 | zipfile?). Compensating...\n"; | |
134 | static ZCONST char Far CentDirEndSigNotFound[] = "\ | |
135 | End-of-central-directory signature not found. Either this file is not\n\ | |
136 | a zipfile, or it constitutes one disk of a multi-part archive. In the\n\ | |
137 | latter case the central directory and zipfile comment will be found on\n\ | |
138 | the last disk(s) of this archive.\n"; | |
139 | #else /* SFX */ | |
140 | static ZCONST char Far CentDirEndSigNotFound[] = | |
141 | " End-of-central-directory signature not found.\n"; | |
142 | #endif /* ?SFX */ | |
143 | static ZCONST char Far ZipfileCommTrunc1[] = | |
144 | "\ncaution: zipfile comment truncated\n"; | |
145 | ||
146 | ||
147 | ||
148 | ||
149 | /*******************************/ | |
150 | /* Function process_zipfiles() */ | |
151 | /*******************************/ | |
152 | ||
153 | int process_zipfiles(__G) /* return PK-type error code */ | |
154 | __GDEF | |
155 | { | |
156 | #ifndef SFX | |
157 | char *lastzipfn = (char *)NULL; | |
158 | int NumWinFiles, NumLoseFiles, NumWarnFiles; | |
159 | int NumMissDirs, NumMissFiles; | |
160 | #endif | |
161 | int error=0, error_in_archive=0; | |
162 | ||
163 | ||
164 | /*--------------------------------------------------------------------------- | |
165 | Start by allocating buffers and (re)constructing the various PK signature | |
166 | strings. | |
167 | ---------------------------------------------------------------------------*/ | |
168 | ||
169 | G.inbuf = (uch *)malloc(INBUFSIZ + 4); /* 4 extra for hold[] (below) */ | |
170 | G.outbuf = (uch *)malloc(OUTBUFSIZ + 1); /* 1 extra for string term. */ | |
171 | ||
172 | if ((G.inbuf == (uch *)NULL) || (G.outbuf == (uch *)NULL)) { | |
173 | Info(slide, 0x401, ((char *)slide, | |
174 | LoadFarString(CannotAllocateBuffers))); | |
175 | return(PK_MEM); | |
176 | } | |
177 | G.hold = G.inbuf + INBUFSIZ; /* to check for boundary-spanning sigs */ | |
178 | #ifndef VMS /* VMS uses its own buffer scheme for textmode flush(). */ | |
179 | #ifdef SMALL_MEM | |
180 | G.outbuf2 = G.outbuf+RAWBUFSIZ; /* never changes */ | |
181 | #endif | |
182 | #endif /* !VMS */ | |
183 | ||
184 | #if 0 /* CRC_32_TAB has been NULLified by CONSTRUCTGLOBALS !!!! */ | |
185 | /* allocate the CRC table only later when we know we have a zipfile */ | |
186 | CRC_32_TAB = NULL; | |
187 | #endif /* 0 */ | |
188 | ||
189 | /* finish up initialization of magic signature strings */ | |
190 | local_hdr_sig[0] /* = extd_local_sig[0] */ = 0x50; /* ASCII 'P', */ | |
191 | central_hdr_sig[0] = end_central_sig[0] = 0x50; /* not EBCDIC */ | |
192 | ||
193 | local_hdr_sig[1] /* = extd_local_sig[1] */ = 0x4B; /* ASCII 'K', */ | |
194 | central_hdr_sig[1] = end_central_sig[1] = 0x4B; /* not EBCDIC */ | |
195 | ||
196 | /*--------------------------------------------------------------------------- | |
197 | Make sure timezone info is set correctly; localtime() returns GMT on | |
198 | some OSes (e.g., Solaris 2.x) if this isn't done first. The ifdefs were | |
199 | initially copied from dos_to_unix_time() in fileio.c. probably, they are | |
200 | still too strict; any listed OS that supplies tzset(), regardless of | |
201 | whether the function does anything, should be removed from the ifdefs. | |
202 | ---------------------------------------------------------------------------*/ | |
203 | ||
204 | #if (defined(IZ_CHECK_TZ) && defined(USE_EF_UT_TIME)) | |
205 | # ifndef VALID_TIMEZONE | |
206 | # define VALID_TIMEZONE(tmp) \ | |
207 | (((tmp = getenv("TZ")) != NULL) && (*tmp != '\0')) | |
208 | # endif | |
209 | { | |
210 | char *p; | |
211 | G.tz_is_valid = VALID_TIMEZONE(p); | |
212 | # ifndef SFX | |
213 | if (!G.tz_is_valid) { | |
214 | Info(slide, 0x401, ((char *)slide, LoadFarString(WarnInvalidTZ))); | |
215 | error_in_archive = error = PK_WARN; | |
216 | } | |
217 | # endif /* !SFX */ | |
218 | } | |
219 | #endif /* IZ_CHECK_TZ && USE_EF_UT_TIME */ | |
220 | ||
221 | /* For systems that do not have tzset() but supply this function using another | |
222 | name (_tzset() or something similar), an appropiate "#define tzset ..." | |
223 | should be added to the system specifc configuration section. */ | |
224 | #if (!defined(T20_VMS) && !defined(MACOS) && !defined(RISCOS) && !defined(QDOS)) | |
225 | #if (!defined(BSD) && !defined(MTS) && !defined(CMS_MVS) && !defined(TANDEM)) | |
226 | tzset(); | |
227 | #endif | |
228 | #endif | |
229 | ||
230 | /*--------------------------------------------------------------------------- | |
231 | Match (possible) wildcard zipfile specification with existing files and | |
232 | attempt to process each. If no hits, try again after appending ".zip" | |
233 | suffix. If still no luck, give up. | |
234 | ---------------------------------------------------------------------------*/ | |
235 | ||
236 | #ifdef SFX | |
237 | if ((error = do_seekable(__G__ 0)) == PK_NOZIP) { | |
238 | #ifdef EXE_EXTENSION | |
239 | int len=strlen(G.argv0); | |
240 | ||
241 | /* append .exe if appropriate; also .sfx? */ | |
242 | if ( (G.zipfn = (char *)malloc(len+sizeof(EXE_EXTENSION))) != | |
243 | (char *)NULL ) { | |
244 | strcpy(G.zipfn, G.argv0); | |
245 | strcpy(G.zipfn+len, EXE_EXTENSION); | |
246 | error = do_seekable(__G__ 0); | |
247 | free(G.zipfn); | |
248 | G.zipfn = G.argv0; /* for "cannot find myself" message only */ | |
249 | } | |
250 | #endif /* EXE_EXTENSION */ | |
251 | #ifdef WIN32 | |
252 | G.zipfn = G.argv0; /* for "cannot find myself" message only */ | |
253 | #endif | |
254 | } | |
255 | if (error) { | |
256 | if (error == IZ_DIR) | |
257 | error_in_archive = PK_NOZIP; | |
258 | else | |
259 | error_in_archive = error; | |
260 | if (error == PK_NOZIP) | |
261 | Info(slide, 1, ((char *)slide, LoadFarString(CannotFindMyself), | |
262 | G.zipfn)); | |
263 | } | |
264 | ||
265 | #else /* !SFX */ | |
266 | NumWinFiles = NumLoseFiles = NumWarnFiles = 0; | |
267 | files = NumMissDirs = NumMissFiles = 0; | |
268 | ||
269 | while ((G.zipfn = do_wild(__G__ G.wildzipfn)) != (char *)NULL) { | |
270 | Trace((stderr, "do_wild( %s ) returns %s\n", G.wildzipfn, G.zipfn)); | |
271 | ||
272 | lastzipfn = G.zipfn; | |
273 | ||
274 | /* print a blank line between the output of different zipfiles */ | |
275 | if (!uO.qflag && error != PK_NOZIP && error != IZ_DIR | |
276 | #ifdef TIMESTAMP | |
277 | && (!uO.T_flag || uO.zipinfo_mode) | |
278 | #endif | |
279 | && (NumWinFiles+NumLoseFiles+NumWarnFiles+NumMissFiles) > 0) | |
280 | (*G.message)((zvoid *)&G, (uch *)"\n", 1L, 0); | |
281 | ||
282 | if ((error = do_seekable(__G__ 0)) == PK_WARN) | |
283 | ++NumWarnFiles; | |
284 | else if (error == IZ_DIR) | |
285 | ++NumMissDirs; | |
286 | else if (error == PK_NOZIP) | |
287 | ++NumMissFiles; | |
288 | else if (error) | |
289 | ++NumLoseFiles; | |
290 | else | |
291 | ++NumWinFiles; | |
292 | ||
293 | if(!uO.vflag) | |
294 | files = NumWinFiles; | |
295 | ||
296 | if (error != IZ_DIR && error > error_in_archive) | |
297 | error_in_archive = error; | |
298 | Trace((stderr, "do_seekable(0) returns %d\n", error)); | |
299 | #ifdef WINDLL | |
300 | if (error == IZ_CTRLC) { | |
301 | free_G_buffers(__G); | |
302 | return error; | |
303 | } | |
304 | #endif | |
305 | ||
306 | } /* end while-loop (wildcard zipfiles) */ | |
307 | ||
308 | if ((NumWinFiles + NumWarnFiles + NumLoseFiles) == 0 && | |
309 | (NumMissDirs + NumMissFiles) == 1 && lastzipfn != (char *)NULL) | |
310 | { | |
311 | NumMissDirs = NumMissFiles = 0; | |
312 | if (error_in_archive == PK_NOZIP) | |
313 | error_in_archive = PK_COOL; | |
314 | ||
315 | #if (!defined(UNIX) && !defined(AMIGA)) /* filenames with wildcard characters */ | |
316 | if (iswild(G.wildzipfn)) | |
317 | Info(slide, 0x401, ((char *)slide, | |
318 | LoadFarString(CannotFindWildcardMatch), uO.zipinfo_mode? | |
319 | LoadFarStringSmall(Zipnfo) : LoadFarStringSmall(Unzip), | |
320 | G.wildzipfn)); | |
321 | else | |
322 | #endif | |
323 | { | |
324 | char *p = lastzipfn + strlen(lastzipfn); | |
325 | ||
326 | G.zipfn = lastzipfn; | |
327 | strcpy(p, ZSUFX); | |
328 | ||
329 | #if defined(UNIX) || defined(QDOS) | |
330 | /* only Unix has case-sensitive filesystems */ | |
331 | /* Well FlexOS (sometimes) also has them, but support is per media */ | |
332 | /* and a pig to code for, so treat as case insensitive for now */ | |
333 | /* we do this under QDOS to check for .zip as well as _zip */ | |
334 | if ((error = do_seekable(__G__ 0)) == PK_NOZIP || error == IZ_DIR) { | |
335 | if (error == IZ_DIR) | |
336 | ++NumMissDirs; | |
337 | strcpy(p, ALT_ZSUFX); | |
338 | error = do_seekable(__G__ 1); | |
339 | } | |
340 | #else | |
341 | error = do_seekable(__G__ 1); | |
342 | #endif | |
343 | if (error == PK_WARN) /* GRR: make this a switch/case stmt ... */ | |
344 | ++NumWarnFiles; | |
345 | else if (error == IZ_DIR) | |
346 | ++NumMissDirs; | |
347 | else if (error == PK_NOZIP) | |
348 | /* increment again => bug: "1 file had no zipfile directory." */ | |
349 | /* ++NumMissFiles */ ; | |
350 | else if (error) | |
351 | ++NumLoseFiles; | |
352 | else | |
353 | ++NumWinFiles; | |
354 | ||
355 | if (error > error_in_archive) | |
356 | error_in_archive = error; | |
357 | Trace((stderr, "do_seekable(1) returns %d\n", error)); | |
358 | #ifdef WINDLL | |
359 | if (error == IZ_CTRLC) { | |
360 | free_G_buffers(__G); | |
361 | return error; | |
362 | } | |
363 | #endif | |
364 | } | |
365 | } | |
366 | #endif /* ?SFX */ | |
367 | ||
368 | /*--------------------------------------------------------------------------- | |
369 | Print summary of all zipfiles, assuming zipfile spec was a wildcard (no | |
370 | need for a summary if just one zipfile). | |
371 | ---------------------------------------------------------------------------*/ | |
372 | ||
373 | #ifndef SFX | |
374 | if (iswild(G.wildzipfn) && uO.qflag < 3 | |
375 | #ifdef TIMESTAMP | |
376 | && !(uO.T_flag && uO.qflag && !uO.zipinfo_mode) | |
377 | #endif | |
378 | ) | |
379 | { | |
380 | if ((NumMissFiles + NumLoseFiles + NumWarnFiles > 0 || NumWinFiles != 1) | |
381 | #ifdef TIMESTAMP | |
382 | && !(uO.T_flag && !uO.zipinfo_mode) | |
383 | #endif | |
384 | && !(uO.tflag && uO.qflag > 1)) | |
385 | (*G.message)((zvoid *)&G, (uch *)"\n", 1L, 0x401); | |
386 | if ((NumWinFiles > 1) || (NumWinFiles == 1 && | |
387 | NumMissDirs + NumMissFiles + NumLoseFiles + NumWarnFiles > 0)) | |
388 | Info(slide, 0x401, ((char *)slide, LoadFarString(FilesProcessOK), | |
389 | NumWinFiles, (NumWinFiles == 1)? " was" : "s were")); | |
390 | if (NumWarnFiles > 0) | |
391 | Info(slide, 0x401, ((char *)slide, LoadFarString(ArchiveWarning), | |
392 | NumWarnFiles, (NumWarnFiles == 1)? "" : "s")); | |
393 | if (NumLoseFiles > 0) | |
394 | Info(slide, 0x401, ((char *)slide, LoadFarString(ArchiveFatalError), | |
395 | NumLoseFiles, (NumLoseFiles == 1)? "" : "s")); | |
396 | if (NumMissFiles > 0) | |
397 | Info(slide, 0x401, ((char *)slide, | |
398 | LoadFarString(FileHadNoZipfileDir), NumMissFiles, | |
399 | (NumMissFiles == 1)? "" : "s")); | |
400 | if (NumMissDirs == 1) | |
401 | Info(slide, 0x401, ((char *)slide, LoadFarString(ZipfileWasDir))); | |
402 | else if (NumMissDirs > 0) | |
403 | Info(slide, 0x401, ((char *)slide, | |
404 | LoadFarString(ManyZipfilesWereDir), NumMissDirs)); | |
405 | if (NumWinFiles + NumLoseFiles + NumWarnFiles == 0) | |
406 | Info(slide, 0x401, ((char *)slide, LoadFarString(NoZipfileFound))); | |
407 | } | |
408 | #endif /* !SFX */ | |
409 | ||
410 | /* free allocated memory */ | |
411 | free_G_buffers(__G); | |
412 | ||
413 | return error_in_archive; | |
414 | ||
415 | } /* end function process_zipfiles() */ | |
416 | ||
417 | ||
418 | ||
419 | ||
420 | ||
421 | /*****************************/ | |
422 | /* Function free_G_buffers() */ | |
423 | /*****************************/ | |
424 | ||
425 | void free_G_buffers(__G) /* releases all memory allocated in global vars */ | |
426 | __GDEF | |
427 | { | |
428 | inflate_free(__G); | |
429 | checkdir(__G__ (char *)NULL, END); | |
430 | ||
431 | #ifdef DYNALLOC_CRCTAB | |
432 | if (CRC_32_TAB) { | |
433 | free_crc_table(); | |
434 | CRC_32_TAB = NULL; | |
435 | } | |
436 | #endif | |
437 | ||
438 | if (G.key != (char *)NULL) { | |
439 | free(G.key); | |
440 | G.key = (char *)NULL; | |
441 | } | |
442 | ||
443 | #if (!defined(VMS) && !defined(SMALL_MEM)) | |
444 | /* VMS uses its own buffer scheme for textmode flush() */ | |
445 | if (G.outbuf2) { | |
446 | free(G.outbuf2); /* malloc'd ONLY if unshrink and -a */ | |
447 | G.outbuf2 = (uch *)NULL; | |
448 | } | |
449 | #endif | |
450 | ||
451 | if (G.outbuf) | |
452 | free(G.outbuf); | |
453 | if (G.inbuf) | |
454 | free(G.inbuf); | |
455 | G.inbuf = G.outbuf = (uch *)NULL; | |
456 | ||
457 | #ifdef MALLOC_WORK | |
458 | if (G.area.Slide) { | |
459 | free(G.area.Slide); | |
460 | G.area.Slide = (uch *)NULL; | |
461 | } | |
462 | #endif | |
463 | ||
464 | } /* end function free_G_buffers() */ | |
465 | ||
466 | ||
467 | ||
468 | ||
469 | ||
470 | /**************************/ | |
471 | /* Function do_seekable() */ | |
472 | /**************************/ | |
473 | ||
474 | static int do_seekable(__G__ lastchance) /* return PK-type error code */ | |
475 | __GDEF | |
476 | int lastchance; | |
477 | { | |
478 | #ifndef SFX | |
479 | /* static int no_ecrec = FALSE; SKM: moved to globals.h */ | |
480 | int maybe_exe=FALSE; | |
481 | int too_weird_to_continue=FALSE; | |
482 | #ifdef TIMESTAMP | |
483 | time_t uxstamp; | |
484 | unsigned nmember = 0; | |
485 | #endif | |
486 | #endif | |
487 | int error=0, error_in_archive; | |
488 | ||
489 | ||
490 | /*--------------------------------------------------------------------------- | |
491 | Open the zipfile for reading in BINARY mode to prevent CR/LF translation, | |
492 | which would corrupt the bit streams. | |
493 | ---------------------------------------------------------------------------*/ | |
494 | ||
495 | if (SSTAT(G.zipfn, &G.statbuf) || | |
496 | (error = S_ISDIR(G.statbuf.st_mode)) != 0) | |
497 | { | |
498 | #ifndef SFX | |
499 | if (lastchance) { | |
500 | #if defined(UNIX) || defined(QDOS) | |
501 | if (G.no_ecrec) | |
502 | Info(slide, 1, ((char *)slide, | |
503 | LoadFarString(CannotFindZipfileDirMsg), uO.zipinfo_mode? | |
504 | LoadFarStringSmall(Zipnfo) : LoadFarStringSmall(Unzip), | |
505 | G.wildzipfn, uO.zipinfo_mode? " " : "", G.wildzipfn, | |
506 | G.zipfn)); | |
507 | else | |
508 | Info(slide, 1, ((char *)slide, | |
509 | LoadFarString(CannotFindEitherZipfile), uO.zipinfo_mode? | |
510 | LoadFarStringSmall(Zipnfo) : LoadFarStringSmall(Unzip), | |
511 | G.wildzipfn, G.wildzipfn, G.zipfn)); | |
512 | #else /* !UNIX */ | |
513 | if (G.no_ecrec) | |
514 | Info(slide, 0x401, ((char *)slide, | |
515 | LoadFarString(CannotFindZipfileDirMsg), uO.zipinfo_mode? | |
516 | LoadFarStringSmall(Zipnfo) : LoadFarStringSmall(Unzip), | |
517 | G.wildzipfn, uO.zipinfo_mode? " " : "", G.zipfn)); | |
518 | else | |
519 | Info(slide, 0x401, ((char *)slide, | |
520 | LoadFarString(CannotFindEitherZipfile), uO.zipinfo_mode? | |
521 | LoadFarStringSmall(Zipnfo) : LoadFarStringSmall(Unzip), | |
522 | G.wildzipfn, G.zipfn)); | |
523 | #endif /* ?UNIX */ | |
524 | } | |
525 | #endif /* !SFX */ | |
526 | return error? IZ_DIR : PK_NOZIP; | |
527 | } | |
528 | G.ziplen = G.statbuf.st_size; | |
529 | ||
530 | #ifndef SFX | |
531 | #if defined(UNIX) || defined(DOS_OS2_W32) | |
532 | if (G.statbuf.st_mode & S_IEXEC) /* no extension on Unix exes: might */ | |
533 | maybe_exe = TRUE; /* find unzip, not unzip.zip; etc. */ | |
534 | #endif | |
535 | #endif /* !SFX */ | |
536 | ||
537 | #ifdef VMS | |
538 | if (check_format(__G)) /* check for variable-length format */ | |
539 | return PK_ERR; | |
540 | #endif | |
541 | ||
542 | if (open_input_file(__G)) /* this should never happen, given */ | |
543 | return PK_NOZIP; /* the stat() test above, but... */ | |
544 | ||
545 | /*--------------------------------------------------------------------------- | |
546 | Find and process the end-of-central-directory header. UnZip need only | |
547 | check last 65557 bytes of zipfile: comment may be up to 65535, end-of- | |
548 | central-directory record is 18 bytes, and signature itself is 4 bytes; | |
549 | add some to allow for appended garbage. Since ZipInfo is often used as | |
550 | a debugging tool, search the whole zipfile if zipinfo_mode is true. | |
551 | ---------------------------------------------------------------------------*/ | |
552 | ||
553 | /* initialize the CRC table pointer (once) */ | |
554 | if (CRC_32_TAB == NULL) { | |
555 | if ((CRC_32_TAB = get_crc_table()) == NULL) | |
556 | return PK_MEM; | |
557 | } | |
558 | ||
559 | #if (!defined(SFX) || defined(SFX_EXDIR)) | |
560 | /* check out if specified extraction root directory exists */ | |
561 | if (uO.exdir != (char *)NULL && G.extract_flag) { | |
562 | G.create_dirs = !uO.fflag; | |
563 | if ((error = checkdir(__G__ uO.exdir, ROOT)) > 2) | |
564 | return error; /* out of memory, or file in way */ | |
565 | } | |
566 | #endif /* !SFX || SFX_EXDIR */ | |
567 | ||
568 | G.cur_zipfile_bufstart = 0; | |
569 | G.inptr = G.inbuf; | |
570 | ||
571 | #if (!defined(WINDLL) && !defined(SFX)) | |
572 | #ifdef TIMESTAMP | |
573 | if (!uO.zipinfo_mode && !uO.qflag && !uO.T_flag) | |
574 | #else | |
575 | if (!uO.zipinfo_mode && !uO.qflag) | |
576 | #endif | |
577 | #ifdef WIN32 /* Win32 console may require codepage conversion for G.zipfn */ | |
578 | Info(slide, 0, ((char *)slide, "Archive: %s\n", FnFilter1(G.zipfn))); | |
579 | #else | |
580 | Info(slide, 0, ((char *)slide, "Archive: %s\n", G.zipfn)); | |
581 | #endif | |
582 | #endif /* !WINDLL && !SFX */ | |
583 | ||
584 | if (( | |
585 | #ifndef NO_ZIPINFO | |
586 | uO.zipinfo_mode && | |
587 | ((error_in_archive = find_ecrec(__G__ G.ziplen)) != 0 || | |
588 | (error_in_archive = zi_end_central(__G)) > PK_WARN)) | |
589 | || (!uO.zipinfo_mode && | |
590 | #endif | |
591 | ((error_in_archive = find_ecrec(__G__ MIN(G.ziplen,66000L))) != 0 || | |
592 | (error_in_archive = uz_end_central(__G)) > PK_WARN))) | |
593 | { | |
594 | CLOSE_INFILE(); | |
595 | ||
596 | #ifdef SFX | |
597 | ++lastchance; /* avoid picky compiler warnings */ | |
598 | return error_in_archive; | |
599 | #else | |
600 | if (maybe_exe) | |
601 | Info(slide, 0x401, ((char *)slide, LoadFarString(MaybeExe), | |
602 | G.zipfn)); | |
603 | if (lastchance) | |
604 | return error_in_archive; | |
605 | else { | |
606 | G.no_ecrec = TRUE; /* assume we found wrong file: e.g., */ | |
607 | return PK_NOZIP; /* unzip instead of unzip.zip */ | |
608 | } | |
609 | #endif /* ?SFX */ | |
610 | } | |
611 | ||
612 | if ((uO.zflag > 0) && !uO.zipinfo_mode) { /* unzip: zflag = comment ONLY */ | |
613 | CLOSE_INFILE(); | |
614 | return error_in_archive; | |
615 | } | |
616 | ||
617 | /*--------------------------------------------------------------------------- | |
618 | Test the end-of-central-directory info for incompatibilities (multi-disk | |
619 | archives) or inconsistencies (missing or extra bytes in zipfile). | |
620 | ---------------------------------------------------------------------------*/ | |
621 | ||
622 | #ifdef NO_MULTIPART | |
623 | error = !uO.zipinfo_mode && (G.ecrec.number_this_disk == 1) && | |
624 | (G.ecrec.num_disk_start_cdir == 1); | |
625 | #else | |
626 | error = !uO.zipinfo_mode && (G.ecrec.number_this_disk != 0); | |
627 | #endif | |
628 | ||
629 | #ifndef SFX | |
630 | if (uO.zipinfo_mode && | |
631 | G.ecrec.number_this_disk != G.ecrec.num_disk_start_cdir) | |
632 | { | |
633 | if (G.ecrec.number_this_disk > G.ecrec.num_disk_start_cdir) { | |
634 | Info(slide, 0x401, ((char *)slide, | |
635 | LoadFarString(CentDirNotInZipMsg), G.zipfn, | |
636 | G.ecrec.number_this_disk, G.ecrec.num_disk_start_cdir)); | |
637 | error_in_archive = PK_FIND; | |
638 | too_weird_to_continue = TRUE; | |
639 | } else { | |
640 | Info(slide, 0x401, ((char *)slide, | |
641 | LoadFarString(EndCentDirBogus), G.zipfn, | |
642 | G.ecrec.number_this_disk, G.ecrec.num_disk_start_cdir)); | |
643 | error_in_archive = PK_WARN; | |
644 | } | |
645 | #ifdef NO_MULTIPART /* concatenation of multiple parts works in some cases */ | |
646 | } else if (!uO.zipinfo_mode && !error && G.ecrec.number_this_disk != 0) { | |
647 | Info(slide, 0x401, ((char *)slide, LoadFarString(NoMultiDiskArcSupport), | |
648 | G.zipfn)); | |
649 | error_in_archive = PK_FIND; | |
650 | too_weird_to_continue = TRUE; | |
651 | #endif | |
652 | } | |
653 | ||
654 | if (!too_weird_to_continue) { /* (relatively) normal zipfile: go for it */ | |
655 | if (error) { | |
656 | Info(slide, 0x401, ((char *)slide, LoadFarString(MaybePakBug), | |
657 | G.zipfn)); | |
658 | error_in_archive = PK_WARN; | |
659 | } | |
660 | #endif /* !SFX */ | |
661 | if ((G.extra_bytes = G.real_ecrec_offset-G.expect_ecrec_offset) < | |
662 | (LONGINT)0) | |
663 | { | |
664 | Info(slide, 0x401, ((char *)slide, LoadFarString(MissingBytes), | |
665 | G.zipfn, (long)(-G.extra_bytes))); | |
666 | error_in_archive = PK_ERR; | |
667 | } else if (G.extra_bytes > 0) { | |
668 | if ((G.ecrec.offset_start_central_directory == 0) && | |
669 | (G.ecrec.size_central_directory != 0)) /* zip 1.5 -go bug */ | |
670 | { | |
671 | Info(slide, 0x401, ((char *)slide, | |
672 | LoadFarString(NullCentDirOffset), G.zipfn)); | |
673 | G.ecrec.offset_start_central_directory = G.extra_bytes; | |
674 | G.extra_bytes = 0; | |
675 | error_in_archive = PK_ERR; | |
676 | } | |
677 | #ifndef SFX | |
678 | else { | |
679 | Info(slide, 0x401, ((char *)slide, | |
680 | LoadFarString(ExtraBytesAtStart), G.zipfn, | |
681 | (long)G.extra_bytes, (G.extra_bytes == 1)? "":"s")); | |
682 | error_in_archive = PK_WARN; | |
683 | } | |
684 | #endif /* !SFX */ | |
685 | } | |
686 | ||
687 | /*----------------------------------------------------------------------- | |
688 | Check for empty zipfile and exit now if so. | |
689 | -----------------------------------------------------------------------*/ | |
690 | ||
691 | if (G.expect_ecrec_offset==0L && G.ecrec.size_central_directory==0) { | |
692 | if (uO.zipinfo_mode) | |
693 | Info(slide, 0, ((char *)slide, "%sEmpty zipfile.\n", | |
694 | uO.lflag>9? "\n " : "")); | |
695 | else | |
696 | Info(slide, 0x401, ((char *)slide, LoadFarString(ZipfileEmpty), | |
697 | G.zipfn)); | |
698 | CLOSE_INFILE(); | |
699 | return (error_in_archive > PK_WARN)? error_in_archive : PK_WARN; | |
700 | } | |
701 | ||
702 | /*----------------------------------------------------------------------- | |
703 | Compensate for missing or extra bytes, and seek to where the start | |
704 | of central directory should be. If header not found, uncompensate | |
705 | and try again (necessary for at least some Atari archives created | |
706 | with STZip, as well as archives created by J.H. Holm's ZIPSPLIT 1.1). | |
707 | -----------------------------------------------------------------------*/ | |
708 | ||
709 | ZLSEEK( G.ecrec.offset_start_central_directory ) | |
710 | #ifdef OLD_SEEK_TEST | |
711 | if (readbuf(G.sig, 4) == 0) { | |
712 | CLOSE_INFILE(); | |
713 | return PK_ERR; /* file may be locked, or possibly disk error(?) */ | |
714 | } | |
715 | if (strncmp(G.sig, central_hdr_sig, 4)) | |
716 | #else | |
717 | if ((readbuf(__G__ G.sig, 4) == 0) || | |
718 | strncmp(G.sig, central_hdr_sig, 4)) | |
719 | #endif | |
720 | { | |
721 | #ifndef SFX | |
722 | long tmp = G.extra_bytes; | |
723 | #endif | |
724 | ||
725 | G.extra_bytes = 0; | |
726 | ZLSEEK( G.ecrec.offset_start_central_directory ) | |
727 | if ((readbuf(__G__ G.sig, 4) == 0) || | |
728 | strncmp(G.sig, central_hdr_sig, 4)) | |
729 | { | |
730 | Info(slide, 0x401, ((char *)slide, | |
731 | LoadFarString(CentDirStartNotFound), G.zipfn, | |
732 | LoadFarStringSmall(ReportMsg))); | |
733 | CLOSE_INFILE(); | |
734 | return PK_BADERR; | |
735 | } | |
736 | #ifndef SFX | |
737 | Info(slide, 0x401, ((char *)slide, LoadFarString(CentDirTooLong), | |
738 | G.zipfn, -tmp)); | |
739 | #endif | |
740 | error_in_archive = PK_ERR; | |
741 | } | |
742 | ||
743 | /*----------------------------------------------------------------------- | |
744 | Seek to the start of the central directory one last time, since we | |
745 | have just read the first entry's signature bytes; then list, extract | |
746 | or test member files as instructed, and close the zipfile. | |
747 | -----------------------------------------------------------------------*/ | |
748 | ||
749 | Trace((stderr, "about to extract/list files (error = %d)\n", | |
750 | error_in_archive)); | |
751 | ||
752 | ZLSEEK( G.ecrec.offset_start_central_directory ) | |
753 | ||
754 | #ifdef DLL | |
755 | /* G.fValidate is used only to look at an archive to see if | |
756 | it appears to be a valid archive. There is no interest | |
757 | in what the archive contains, nor in validating that the | |
758 | entries in the archive are in good condition. This is | |
759 | currently used only in the Windows DLLs for purposes of | |
760 | checking archives within an archive to determine whether | |
761 | or not to display the inner archives. | |
762 | */ | |
763 | if (!G.fValidate) | |
764 | #endif | |
765 | { | |
766 | #ifndef NO_ZIPINFO | |
767 | if (uO.zipinfo_mode) | |
768 | error = zipinfo(__G); /* ZIPINFO 'EM */ | |
769 | else | |
770 | #endif | |
771 | #ifndef SFX | |
772 | #ifdef TIMESTAMP | |
773 | if (uO.T_flag) | |
774 | error = get_time_stamp(__G__ &uxstamp, &nmember); | |
775 | else | |
776 | #endif | |
777 | if (uO.vflag && !uO.tflag && !uO.cflag) | |
778 | error = list_files(__G); /* LIST 'EM */ | |
779 | else | |
780 | #endif /* !SFX */ | |
781 | error = extract_or_test_files(__G); /* EXTRACT OR TEST 'EM */ | |
782 | ||
783 | Trace((stderr, "done with extract/list files (error = %d)\n", | |
784 | error)); | |
785 | } | |
786 | ||
787 | if (error > error_in_archive) /* don't overwrite stronger error */ | |
788 | error_in_archive = error; /* with (for example) a warning */ | |
789 | #ifndef SFX | |
790 | } /* end if (!too_weird_to_continue) */ | |
791 | #endif | |
792 | ||
793 | CLOSE_INFILE(); | |
794 | ||
795 | #ifdef TIMESTAMP | |
796 | if (uO.T_flag && !uO.zipinfo_mode && (nmember > 0)) { | |
797 | # ifdef WIN32 | |
798 | if (stamp_file(__G__ G.zipfn, uxstamp)) { /* TIME-STAMP 'EM */ | |
799 | # else | |
800 | if (stamp_file(G.zipfn, uxstamp)) { /* TIME-STAMP 'EM */ | |
801 | # endif | |
802 | Info(slide, 0x201, ((char *)slide, | |
803 | "warning: cannot set time for %s\n", G.zipfn)); | |
804 | if (error_in_archive < PK_WARN) | |
805 | error_in_archive = PK_WARN; | |
806 | } | |
807 | } | |
808 | #endif | |
809 | return error_in_archive; | |
810 | ||
811 | } /* end function do_seekable() */ | |
812 | ||
813 | ||
814 | ||
815 | ||
816 | ||
817 | /*************************/ | |
818 | /* Function find_ecrec() */ | |
819 | /*************************/ | |
820 | ||
821 | static int find_ecrec(__G__ searchlen) /* return PK-class error */ | |
822 | __GDEF | |
823 | long searchlen; | |
824 | { | |
825 | int i, numblks, found=FALSE; | |
826 | LONGINT tail_len; | |
827 | ec_byte_rec byterec; | |
828 | ||
829 | ||
830 | /*--------------------------------------------------------------------------- | |
831 | Treat case of short zipfile separately. | |
832 | ---------------------------------------------------------------------------*/ | |
833 | ||
834 | if (G.ziplen <= INBUFSIZ) { | |
835 | lseek(G.zipfd, 0L, SEEK_SET); | |
836 | if ((G.incnt = read(G.zipfd,(char *)G.inbuf,(unsigned int)G.ziplen)) | |
837 | == (int)G.ziplen) | |
838 | ||
839 | /* 'P' must be at least 22 bytes from end of zipfile */ | |
840 | for (G.inptr = G.inbuf+(int)G.ziplen-22; G.inptr >= G.inbuf; | |
841 | --G.inptr) | |
842 | if ((native(*G.inptr) == 'P') && | |
843 | !strncmp((char *)G.inptr, end_central_sig, 4)) { | |
844 | G.incnt -= (int)(G.inptr - G.inbuf); | |
845 | found = TRUE; | |
846 | break; | |
847 | } | |
848 | ||
849 | /*--------------------------------------------------------------------------- | |
850 | Zipfile is longer than INBUFSIZ: may need to loop. Start with short | |
851 | block at end of zipfile (if not TOO short). | |
852 | ---------------------------------------------------------------------------*/ | |
853 | ||
854 | } else { | |
855 | if ((tail_len = G.ziplen % INBUFSIZ) > ECREC_SIZE) { | |
856 | #ifdef USE_STRM_INPUT | |
857 | fseek((FILE *)G.zipfd, G.ziplen-tail_len, SEEK_SET); | |
858 | G.cur_zipfile_bufstart = ftell((FILE *)G.zipfd); | |
859 | #else /* !USE_STRM_INPUT */ | |
860 | G.cur_zipfile_bufstart = lseek(G.zipfd, G.ziplen-tail_len, | |
861 | SEEK_SET); | |
862 | #endif /* ?USE_STRM_INPUT */ | |
863 | if ((G.incnt = read(G.zipfd, (char *)G.inbuf, | |
864 | (unsigned int)tail_len)) != (int)tail_len) | |
865 | goto fail; /* it's expedient... */ | |
866 | ||
867 | /* 'P' must be at least 22 bytes from end of zipfile */ | |
868 | for (G.inptr = G.inbuf+(int)tail_len-22; G.inptr >= G.inbuf; | |
869 | --G.inptr) | |
870 | if ((native(*G.inptr) == 'P') && | |
871 | !strncmp((char *)G.inptr, end_central_sig, 4)) { | |
872 | G.incnt -= (int)(G.inptr - G.inbuf); | |
873 | found = TRUE; | |
874 | break; | |
875 | } | |
876 | /* sig may span block boundary: */ | |
877 | strncpy((char *)G.hold, (char *)G.inbuf, 3); | |
878 | } else | |
879 | G.cur_zipfile_bufstart = G.ziplen - tail_len; | |
880 | ||
881 | /*----------------------------------------------------------------------- | |
882 | Loop through blocks of zipfile data, starting at the end and going | |
883 | toward the beginning. In general, need not check whole zipfile for | |
884 | signature, but may want to do so if testing. | |
885 | -----------------------------------------------------------------------*/ | |
886 | ||
887 | numblks = (int)((searchlen - tail_len + (INBUFSIZ-1)) / INBUFSIZ); | |
888 | /* ==amount= ==done== ==rounding== =blksiz= */ | |
889 | ||
890 | for (i = 1; !found && (i <= numblks); ++i) { | |
891 | G.cur_zipfile_bufstart -= INBUFSIZ; | |
892 | lseek(G.zipfd, G.cur_zipfile_bufstart, SEEK_SET); | |
893 | if ((G.incnt = read(G.zipfd,(char *)G.inbuf,INBUFSIZ)) | |
894 | != INBUFSIZ) | |
895 | break; /* fall through and fail */ | |
896 | ||
897 | for (G.inptr = G.inbuf+INBUFSIZ-1; G.inptr >= G.inbuf; | |
898 | --G.inptr) | |
899 | if ((native(*G.inptr) == 'P') && | |
900 | !strncmp((char *)G.inptr, end_central_sig, 4)) { | |
901 | G.incnt -= (int)(G.inptr - G.inbuf); | |
902 | found = TRUE; | |
903 | break; | |
904 | } | |
905 | /* sig may span block boundary: */ | |
906 | strncpy((char *)G.hold, (char *)G.inbuf, 3); | |
907 | } | |
908 | } /* end if (ziplen > INBUFSIZ) */ | |
909 | ||
910 | /*--------------------------------------------------------------------------- | |
911 | Searched through whole region where signature should be without finding | |
912 | it. Print informational message and die a horrible death. | |
913 | ---------------------------------------------------------------------------*/ | |
914 | ||
915 | fail: | |
916 | if (!found) { | |
917 | if (uO.qflag || uO.zipinfo_mode) | |
918 | Info(slide, 0x401, ((char *)slide, "[%s]\n", G.zipfn)); | |
919 | Info(slide, 0x401, ((char *)slide, | |
920 | LoadFarString(CentDirEndSigNotFound))); | |
921 | return PK_ERR; /* failed */ | |
922 | } | |
923 | ||
924 | /*--------------------------------------------------------------------------- | |
925 | Found the signature, so get the end-central data before returning. Do | |
926 | any necessary machine-type conversions (byte ordering, structure padding | |
927 | compensation) by reading data into character array and copying to struct. | |
928 | ---------------------------------------------------------------------------*/ | |
929 | ||
930 | G.real_ecrec_offset = G.cur_zipfile_bufstart + (G.inptr-G.inbuf); | |
931 | #ifdef TEST | |
932 | pipeit("\n found end-of-central-dir signature at offset %ld (%.8lXh)\n", | |
933 | G.real_ecrec_offset, G.real_ecrec_offset); | |
934 | pipeit(" from beginning of file; offset %d (%.4Xh) within block\n", | |
935 | G.inptr-G.inbuf, G.inptr-G.inbuf); | |
936 | #endif | |
937 | ||
938 | if (readbuf(__G__ (char *)byterec, ECREC_SIZE+4) == 0) | |
939 | return PK_EOF; | |
940 | ||
941 | G.ecrec.number_this_disk = | |
942 | makeword(&byterec[NUMBER_THIS_DISK]); | |
943 | G.ecrec.num_disk_start_cdir = | |
944 | makeword(&byterec[NUM_DISK_WITH_START_CENTRAL_DIR]); | |
945 | G.ecrec.num_entries_centrl_dir_ths_disk = | |
946 | makeword(&byterec[NUM_ENTRIES_CENTRL_DIR_THS_DISK]); | |
947 | G.ecrec.total_entries_central_dir = | |
948 | makeword(&byterec[TOTAL_ENTRIES_CENTRAL_DIR]); | |
949 | G.ecrec.size_central_directory = | |
950 | makelong(&byterec[SIZE_CENTRAL_DIRECTORY]); | |
951 | G.ecrec.offset_start_central_directory = | |
952 | makelong(&byterec[OFFSET_START_CENTRAL_DIRECTORY]); | |
953 | G.ecrec.zipfile_comment_length = | |
954 | makeword(&byterec[ZIPFILE_COMMENT_LENGTH]); | |
955 | ||
956 | G.expect_ecrec_offset = G.ecrec.offset_start_central_directory + | |
957 | G.ecrec.size_central_directory; | |
958 | return PK_COOL; | |
959 | ||
960 | } /* end function find_ecrec() */ | |
961 | ||
962 | ||
963 | ||
964 | ||
965 | ||
966 | /*****************************/ | |
967 | /* Function uz_end_central() */ | |
968 | /*****************************/ | |
969 | ||
970 | int uz_end_central(__G) /* return PK-type error code */ | |
971 | __GDEF | |
972 | { | |
973 | int error = PK_COOL; | |
974 | ||
975 | ||
976 | /*--------------------------------------------------------------------------- | |
977 | Get the zipfile comment (up to 64KB long), if any, and print it out. | |
978 | Then position the file pointer to the beginning of the central directory | |
979 | and fill buffer. | |
980 | ---------------------------------------------------------------------------*/ | |
981 | ||
982 | #ifdef WINDLL | |
983 | /* for comment button: */ | |
984 | if ((!G.fValidate) && (G.lpUserFunctions != NULL)) | |
985 | G.lpUserFunctions->cchComment = G.ecrec.zipfile_comment_length; | |
986 | if (G.ecrec.zipfile_comment_length && (uO.zflag > 0)) | |
987 | #else /* !WINDLL */ | |
988 | if (G.ecrec.zipfile_comment_length && (uO.zflag > 0 || | |
989 | (uO.zflag == 0 && | |
990 | #ifdef TIMESTAMP | |
991 | !uO.T_flag && | |
992 | #endif | |
993 | !uO.qflag))) | |
994 | #endif /* ?WINDLL */ | |
995 | { | |
996 | if (do_string(__G__ G.ecrec.zipfile_comment_length, DISPLAY)) { | |
997 | Info(slide, 0x401, ((char *)slide, | |
998 | LoadFarString(ZipfileCommTrunc1))); | |
999 | error = PK_WARN; | |
1000 | } | |
1001 | } | |
1002 | return error; | |
1003 | ||
1004 | } /* end function uz_end_central() */ | |
1005 | ||
1006 | ||
1007 | ||
1008 | ||
1009 | ||
1010 | /************************************/ | |
1011 | /* Function process_cdir_file_hdr() */ | |
1012 | /************************************/ | |
1013 | ||
1014 | int process_cdir_file_hdr(__G) /* return PK-type error code */ | |
1015 | __GDEF | |
1016 | { | |
1017 | int error; | |
1018 | ||
1019 | ||
1020 | /*--------------------------------------------------------------------------- | |
1021 | Get central directory info, save host and method numbers, and set flag | |
1022 | for lowercase conversion of filename, depending on the OS from which the | |
1023 | file is coming. | |
1024 | ---------------------------------------------------------------------------*/ | |
1025 | ||
1026 | if ((error = get_cdir_ent(__G)) != 0) | |
1027 | return error; | |
1028 | ||
1029 | G.pInfo->hostnum = MIN(G.crec.version_made_by[1], NUM_HOSTS); | |
1030 | /* extnum = MIN(crec.version_needed_to_extract[1], NUM_HOSTS); */ | |
1031 | ||
1032 | G.pInfo->lcflag = 0; | |
1033 | if (uO.L_flag) /* user specified case-conversion */ | |
1034 | switch (G.pInfo->hostnum) { | |
1035 | case FS_FAT_: /* PKZIP and zip -k store in uppercase */ | |
1036 | case CPM_: /* like MS-DOS, right? */ | |
1037 | case VM_CMS_: /* all caps? */ | |
1038 | case MVS_: /* all caps? */ | |
1039 | case TANDEM_: | |
1040 | case TOPS20_: | |
1041 | case VMS_: /* our Zip uses lowercase, but ASi's doesn't */ | |
1042 | /* case Z_SYSTEM_: ? */ | |
1043 | /* case QDOS_: ? */ | |
1044 | G.pInfo->lcflag = 1; /* convert filename to lowercase */ | |
1045 | break; | |
1046 | ||
1047 | default: /* AMIGA_, FS_HPFS_, FS_NTFS_, MAC_, UNIX_, ATARI_, */ | |
1048 | break; /* FS_VFAT_, BEOS_ (Z_SYSTEM_): no conversion */ | |
1049 | } | |
1050 | ||
1051 | /* do Amigas (AMIGA_) also have volume labels? */ | |
1052 | if (IS_VOLID(G.crec.external_file_attributes) && | |
1053 | (G.pInfo->hostnum == FS_FAT_ || G.pInfo->hostnum == FS_HPFS_ || | |
1054 | G.pInfo->hostnum == FS_NTFS_ || G.pInfo->hostnum == ATARI_)) | |
1055 | { | |
1056 | G.pInfo->vollabel = TRUE; | |
1057 | G.pInfo->lcflag = 0; /* preserve case of volume labels */ | |
1058 | } else | |
1059 | G.pInfo->vollabel = FALSE; | |
1060 | ||
1061 | return PK_COOL; | |
1062 | ||
1063 | } /* end function process_cdir_file_hdr() */ | |
1064 | ||
1065 | ||
1066 | ||
1067 | ||
1068 | ||
1069 | /***************************/ | |
1070 | /* Function get_cdir_ent() */ | |
1071 | /***************************/ | |
1072 | ||
1073 | int get_cdir_ent(__G) /* return PK-type error code */ | |
1074 | __GDEF | |
1075 | { | |
1076 | cdir_byte_hdr byterec; | |
1077 | ||
1078 | ||
1079 | /*--------------------------------------------------------------------------- | |
1080 | Read the next central directory entry and do any necessary machine-type | |
1081 | conversions (byte ordering, structure padding compensation--do so by | |
1082 | copying the data from the array into which it was read (byterec) to the | |
1083 | usable struct (crec)). | |
1084 | ---------------------------------------------------------------------------*/ | |
1085 | ||
1086 | if (readbuf(__G__ (char *)byterec, CREC_SIZE) == 0) | |
1087 | return PK_EOF; | |
1088 | ||
1089 | G.crec.version_made_by[0] = byterec[C_VERSION_MADE_BY_0]; | |
1090 | G.crec.version_made_by[1] = byterec[C_VERSION_MADE_BY_1]; | |
1091 | G.crec.version_needed_to_extract[0] = | |
1092 | byterec[C_VERSION_NEEDED_TO_EXTRACT_0]; | |
1093 | G.crec.version_needed_to_extract[1] = | |
1094 | byterec[C_VERSION_NEEDED_TO_EXTRACT_1]; | |
1095 | ||
1096 | G.crec.general_purpose_bit_flag = | |
1097 | makeword(&byterec[C_GENERAL_PURPOSE_BIT_FLAG]); | |
1098 | G.crec.compression_method = | |
1099 | makeword(&byterec[C_COMPRESSION_METHOD]); | |
1100 | G.crec.last_mod_dos_datetime = | |
1101 | makelong(&byterec[C_LAST_MOD_DOS_DATETIME]); | |
1102 | G.crec.crc32 = | |
1103 | makelong(&byterec[C_CRC32]); | |
1104 | G.crec.csize = | |
1105 | makelong(&byterec[C_COMPRESSED_SIZE]); | |
1106 | G.crec.ucsize = | |
1107 | makelong(&byterec[C_UNCOMPRESSED_SIZE]); | |
1108 | G.crec.filename_length = | |
1109 | makeword(&byterec[C_FILENAME_LENGTH]); | |
1110 | G.crec.extra_field_length = | |
1111 | makeword(&byterec[C_EXTRA_FIELD_LENGTH]); | |
1112 | G.crec.file_comment_length = | |
1113 | makeword(&byterec[C_FILE_COMMENT_LENGTH]); | |
1114 | G.crec.disk_number_start = | |
1115 | makeword(&byterec[C_DISK_NUMBER_START]); | |
1116 | G.crec.internal_file_attributes = | |
1117 | makeword(&byterec[C_INTERNAL_FILE_ATTRIBUTES]); | |
1118 | G.crec.external_file_attributes = | |
1119 | makelong(&byterec[C_EXTERNAL_FILE_ATTRIBUTES]); /* LONG, not word! */ | |
1120 | G.crec.relative_offset_local_header = | |
1121 | makelong(&byterec[C_RELATIVE_OFFSET_LOCAL_HEADER]); | |
1122 | ||
1123 | return PK_COOL; | |
1124 | ||
1125 | } /* end function get_cdir_ent() */ | |
1126 | ||
1127 | ||
1128 | ||
1129 | ||
1130 | ||
1131 | /*************************************/ | |
1132 | /* Function process_local_file_hdr() */ | |
1133 | /*************************************/ | |
1134 | ||
1135 | int process_local_file_hdr(__G) /* return PK-type error code */ | |
1136 | __GDEF | |
1137 | { | |
1138 | local_byte_hdr byterec; | |
1139 | ||
1140 | ||
1141 | /*--------------------------------------------------------------------------- | |
1142 | Read the next local file header and do any necessary machine-type con- | |
1143 | versions (byte ordering, structure padding compensation--do so by copy- | |
1144 | ing the data from the array into which it was read (byterec) to the | |
1145 | usable struct (lrec)). | |
1146 | ---------------------------------------------------------------------------*/ | |
1147 | ||
1148 | if (readbuf(__G__ (char *)byterec, LREC_SIZE) == 0) | |
1149 | return PK_EOF; | |
1150 | ||
1151 | G.lrec.version_needed_to_extract[0] = | |
1152 | byterec[L_VERSION_NEEDED_TO_EXTRACT_0]; | |
1153 | G.lrec.version_needed_to_extract[1] = | |
1154 | byterec[L_VERSION_NEEDED_TO_EXTRACT_1]; | |
1155 | ||
1156 | G.lrec.general_purpose_bit_flag = | |
1157 | makeword(&byterec[L_GENERAL_PURPOSE_BIT_FLAG]); | |
1158 | G.lrec.compression_method = makeword(&byterec[L_COMPRESSION_METHOD]); | |
1159 | G.lrec.last_mod_dos_datetime = makelong(&byterec[L_LAST_MOD_DOS_DATETIME]); | |
1160 | G.lrec.crc32 = makelong(&byterec[L_CRC32]); | |
1161 | G.lrec.csize = makelong(&byterec[L_COMPRESSED_SIZE]); | |
1162 | G.lrec.ucsize = makelong(&byterec[L_UNCOMPRESSED_SIZE]); | |
1163 | G.lrec.filename_length = makeword(&byterec[L_FILENAME_LENGTH]); | |
1164 | G.lrec.extra_field_length = makeword(&byterec[L_EXTRA_FIELD_LENGTH]); | |
1165 | ||
1166 | G.csize = (long) G.lrec.csize; | |
1167 | G.ucsize = (long) G.lrec.ucsize; | |
1168 | ||
1169 | if ((G.lrec.general_purpose_bit_flag & 8) != 0) { | |
1170 | /* can't trust local header, use central directory: */ | |
1171 | G.lrec.crc32 = G.pInfo->crc; | |
1172 | G.csize = (long)(G.lrec.csize = G.pInfo->compr_size); | |
1173 | G.ucsize = (long)(G.lrec.ucsize = G.pInfo->uncompr_size); | |
1174 | } | |
1175 | ||
1176 | return PK_COOL; | |
1177 | ||
1178 | } /* end function process_local_file_hdr() */ | |
1179 | ||
1180 | ||
1181 | #ifdef USE_EF_UT_TIME | |
1182 | ||
1183 | /*******************************/ | |
1184 | /* Function ef_scan_for_izux() */ | |
1185 | /*******************************/ | |
1186 | ||
1187 | unsigned ef_scan_for_izux(ef_buf, ef_len, ef_is_c, dos_mdatetime, | |
1188 | z_utim, z_uidgid) | |
1189 | uch *ef_buf; /* buffer containing extra field */ | |
1190 | unsigned ef_len; /* total length of extra field */ | |
1191 | int ef_is_c; /* flag indicating "is central extra field" */ | |
1192 | ulg dos_mdatetime; /* last_mod_file_date_time in DOS format */ | |
1193 | iztimes *z_utim; /* return storage: atime, mtime, ctime */ | |
1194 | ush *z_uidgid; /* return storage: uid and gid */ | |
1195 | { | |
1196 | unsigned flags = 0; | |
1197 | unsigned eb_id; | |
1198 | unsigned eb_len; | |
1199 | int have_new_type_eb = FALSE; | |
1200 | int ut_zip_unzip_compatible = FALSE; | |
1201 | ||
1202 | /*--------------------------------------------------------------------------- | |
1203 | This function scans the extra field for EF_TIME, EF_IZUNIX2, EF_IZUNIX, or | |
1204 | EF_PKUNIX blocks containing Unix-style time_t (GMT) values for the entry's | |
1205 | access, creation, and modification time. | |
1206 | If a valid block is found, the time stamps are copied to the iztimes | |
1207 | structure (provided the z_utim pointer is not NULL). | |
1208 | If a IZUNIX2 block is found or the IZUNIX block contains UID/GID fields, | |
1209 | and the z_uidgid array pointer is valid (!= NULL), the owner info is | |
1210 | transfered as well. | |
1211 | The presence of an EF_TIME or EF_IZUNIX2 block results in ignoring all | |
1212 | data from probably present obsolete EF_IZUNIX blocks. | |
1213 | If multiple blocks of the same type are found, only the information from | |
1214 | the last block is used. | |
1215 | The return value is a combination of the EF_TIME Flags field with an | |
1216 | additional flag bit indicating the presence of valid UID/GID info, | |
1217 | or 0 in case of failure. | |
1218 | ---------------------------------------------------------------------------*/ | |
1219 | ||
1220 | if (ef_len == 0 || ef_buf == NULL || (z_utim == 0 && z_uidgid == NULL)) | |
1221 | return 0; | |
1222 | ||
1223 | TTrace((stderr,"\nef_scan_for_izux: scanning extra field of length %u\n", | |
1224 | ef_len)); | |
1225 | ||
1226 | while (ef_len >= EB_HEADSIZE) { | |
1227 | eb_id = makeword(EB_ID + ef_buf); | |
1228 | eb_len = makeword(EB_LEN + ef_buf); | |
1229 | ||
1230 | if (eb_len > (ef_len - EB_HEADSIZE)) { | |
1231 | /* discovered some extra field inconsistency! */ | |
1232 | TTrace((stderr, | |
1233 | "ef_scan_for_izux: block length %u > rest ef_size %u\n", eb_len, | |
1234 | ef_len - EB_HEADSIZE)); | |
1235 | break; | |
1236 | } | |
1237 | ||
1238 | switch (eb_id) { | |
1239 | case EF_TIME: | |
1240 | flags &= ~0x0ff; /* ignore previous IZUNIX or EF_TIME fields */ | |
1241 | have_new_type_eb = TRUE; | |
1242 | if ( eb_len >= EB_UT_MINLEN && z_utim != NULL) { | |
1243 | unsigned eb_idx = EB_UT_TIME1; | |
1244 | TTrace((stderr,"ef_scan_for_izux: found TIME extra field\n")); | |
1245 | flags |= (ef_buf[EB_HEADSIZE+EB_UT_FLAGS] & 0x0ff); | |
1246 | if ((flags & EB_UT_FL_MTIME)) { | |
1247 | if ((eb_idx+4) <= eb_len) { | |
1248 | z_utim->mtime = makelong((EB_HEADSIZE+eb_idx) + ef_buf); | |
1249 | eb_idx += 4; | |
1250 | TTrace((stderr," UT e.f. modification time = %ld\n", | |
1251 | z_utim->mtime)); | |
1252 | ||
1253 | if ((ulg)(z_utim->mtime) & (ulg)(0x80000000L)) { | |
1254 | ut_zip_unzip_compatible = | |
1255 | ((time_t)0x80000000L < (time_t)0L) | |
1256 | ? (dos_mdatetime == DOSTIME_MINIMUM) | |
1257 | : (dos_mdatetime >= DOSTIME_2038_01_18); | |
1258 | if (!ut_zip_unzip_compatible) { | |
1259 | /* UnZip interpretes mtime differently than Zip; | |
1260 | without modtime: ignore complete UT field */ | |
1261 | flags &= ~0x0ff; /* no time_t times available */ | |
1262 | TTrace((stderr, | |
1263 | " UT modtime range error; ignore e.f.!\n")); | |
1264 | break; /* stop scanning this field */ | |
1265 | } | |
1266 | } else { | |
1267 | /* cannot determine, safe assumption is FALSE */ | |
1268 | ut_zip_unzip_compatible = FALSE; | |
1269 | } | |
1270 | } else { | |
1271 | flags &= ~EB_UT_FL_MTIME; | |
1272 | TTrace((stderr," UT e.f. truncated; no modtime\n")); | |
1273 | } | |
1274 | } | |
1275 | if (ef_is_c) { | |
1276 | break; /* central version of TIME field ends here */ | |
1277 | } | |
1278 | ||
1279 | if (flags & EB_UT_FL_ATIME) { | |
1280 | if ((eb_idx+4) <= eb_len) { | |
1281 | z_utim->atime = makelong((EB_HEADSIZE+eb_idx) + ef_buf); | |
1282 | eb_idx += 4; | |
1283 | TTrace((stderr," UT e.f. access time = %ld\n", | |
1284 | z_utim->atime)); | |
1285 | if (((ulg)(z_utim->atime) & (ulg)(0x80000000L)) && | |
1286 | !ut_zip_unzip_compatible) { | |
1287 | flags &= ~EB_UT_FL_ATIME; | |
1288 | TTrace((stderr, | |
1289 | " UT access time range error: skip time!\n")); | |
1290 | } | |
1291 | } else { | |
1292 | flags &= ~EB_UT_FL_ATIME; | |
1293 | } | |
1294 | } | |
1295 | if (flags & EB_UT_FL_CTIME) { | |
1296 | if ((eb_idx+4) <= eb_len) { | |
1297 | z_utim->ctime = makelong((EB_HEADSIZE+eb_idx) + ef_buf); | |
1298 | TTrace((stderr," UT e.f. creation time = %ld\n", | |
1299 | z_utim->ctime)); | |
1300 | if (((ulg)(z_utim->ctime) & (ulg)(0x80000000L)) && | |
1301 | !ut_zip_unzip_compatible) { | |
1302 | flags &= ~EB_UT_FL_CTIME; | |
1303 | TTrace((stderr, | |
1304 | " UT creation time range error: skip time!\n")); | |
1305 | } | |
1306 | } else { | |
1307 | flags &= ~EB_UT_FL_CTIME; | |
1308 | } | |
1309 | } | |
1310 | } | |
1311 | break; | |
1312 | ||
1313 | case EF_IZUNIX2: | |
1314 | if (!have_new_type_eb) { | |
1315 | flags &= ~0x0ff; /* ignore any previous IZUNIX field */ | |
1316 | have_new_type_eb = TRUE; | |
1317 | } | |
1318 | if (eb_len >= EB_UX2_MINLEN && z_uidgid != NULL) { | |
1319 | z_uidgid[0] = makeword((EB_HEADSIZE+EB_UX2_UID) + ef_buf); | |
1320 | z_uidgid[1] = makeword((EB_HEADSIZE+EB_UX2_GID) + ef_buf); | |
1321 | flags |= EB_UX2_VALID; /* signal success */ | |
1322 | } | |
1323 | break; | |
1324 | ||
1325 | case EF_IZUNIX: | |
1326 | case EF_PKUNIX: /* PKUNIX e.f. layout is identical to IZUNIX */ | |
1327 | if (eb_len >= EB_UX_MINLEN) { | |
1328 | TTrace((stderr,"ef_scan_for_izux: found %s extra field\n", | |
1329 | (eb_id == EF_IZUNIX ? "IZUNIX" : "PKUNIX"))); | |
1330 | if (have_new_type_eb) { | |
1331 | break; /* Ignore IZUNIX extra field block ! */ | |
1332 | } | |
1333 | if (z_utim != NULL) { | |
1334 | z_utim->atime = makelong((EB_HEADSIZE+EB_UX_ATIME)+ef_buf); | |
1335 | z_utim->mtime = makelong((EB_HEADSIZE+EB_UX_MTIME)+ef_buf); | |
1336 | TTrace((stderr," Unix EF actime = %ld\n", z_utim->atime)); | |
1337 | TTrace((stderr," Unix EF modtime = %ld\n", z_utim->mtime)); | |
1338 | flags |= (EB_UT_FL_MTIME | EB_UT_FL_ATIME); | |
1339 | if ((ulg)(z_utim->mtime) & (ulg)(0x80000000L)) { | |
1340 | ut_zip_unzip_compatible = | |
1341 | ((time_t)0x80000000L < (time_t)0L) | |
1342 | ? (dos_mdatetime == DOSTIME_MINIMUM) | |
1343 | : (dos_mdatetime >= DOSTIME_2038_01_18); | |
1344 | if (!ut_zip_unzip_compatible) { | |
1345 | /* UnZip interpretes mtime differently than Zip; | |
1346 | without modtime: ignore complete UT field */ | |
1347 | flags &= ~0x0ff; /* no time_t times available */ | |
1348 | TTrace((stderr, | |
1349 | " UX modtime range error: ignore e.f.!\n")); | |
1350 | } | |
1351 | } else { | |
1352 | /* cannot determine, safe assumption is FALSE */ | |
1353 | ut_zip_unzip_compatible = FALSE; | |
1354 | } | |
1355 | if ((ulg)(z_utim->atime) & (ulg)(0x80000000L) && | |
1356 | !ut_zip_unzip_compatible && (flags & 0x0ff)) { | |
1357 | /* atime not in range of UnZip's time_t */ | |
1358 | flags &= ~EB_UT_FL_ATIME; | |
1359 | TTrace((stderr, | |
1360 | " UX access time range error: skip time!\n")); | |
1361 | } | |
1362 | } | |
1363 | if (eb_len >= EB_UX_FULLSIZE && z_uidgid != NULL) { | |
1364 | z_uidgid[0] = makeword((EB_HEADSIZE+EB_UX_UID) + ef_buf); | |
1365 | z_uidgid[1] = makeword((EB_HEADSIZE+EB_UX_GID) + ef_buf); | |
1366 | flags |= EB_UX2_VALID; | |
1367 | } | |
1368 | } | |
1369 | break; | |
1370 | ||
1371 | default: | |
1372 | break; | |
1373 | } | |
1374 | ||
1375 | /* Skip this extra field block */ | |
1376 | ef_buf += (eb_len + EB_HEADSIZE); | |
1377 | ef_len -= (eb_len + EB_HEADSIZE); | |
1378 | } | |
1379 | ||
1380 | return flags; | |
1381 | } | |
1382 | ||
1383 | #endif /* USE_EF_UT_TIME */ |