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