1 /*---------------------------------------------------------------------------
5 This file contains the non-ZipInfo-specific listing routines for UnZip.
8 get_time_stamp() [optional feature]
12 ---------------------------------------------------------------------------*/
15 #define UNZIP_INTERNAL
19 # include "wince/intrface.h"
21 # include "windll/windll.h"
27 static int fn_is_dir
OF((__GPRO
));
31 static ZCONST
char Far CompFactorStr
[] = "%c%d%%";
32 static ZCONST
char Far CompFactor100
[] = "100%%";
35 static ZCONST
char Far HeadersS
[] =
36 " Length EAs ACLs Date Time Name";
37 static ZCONST
char Far HeadersS1
[] =
38 " -------- --- ---- ---- ---- ----";
40 static ZCONST
char Far HeadersS
[] = " Length Date Time Name";
41 static ZCONST
char Far HeadersS1
[] = " -------- ---- ---- ----";
44 static ZCONST
char Far HeadersL
[] =
45 " Length Method Size Ratio Date Time CRC-32 Name";
46 static ZCONST
char Far HeadersL1
[] =
47 "-------- ------ ------- ----- ---- ---- ------ ----";
48 static ZCONST
char Far
*Headers
[][2] =
49 { {HeadersS
, HeadersS1
}, {HeadersL
, HeadersL1
} };
51 static ZCONST
char Far CaseConversion
[] =
52 "%s (\"^\" ==> case\n%s conversion)\n";
53 static ZCONST
char Far LongHdrStats
[] =
54 "%8lu %-7s%8lu %4s %02u-%02u-%02u %02u:%02u %08lx %c";
55 static ZCONST
char Far LongFileTrailer
[] =
56 "-------- ------- --- \
57 -------\n%8lu %8lu %4s %u file%s\n";
59 static ZCONST
char Far ShortHdrStats
[] =
60 "%9lu %6lu %6lu %02u-%02u-%02u %02u:%02u %c";
61 static ZCONST
char Far ShortFileTrailer
[] = " -------- ----- ----- \
62 -------\n%9lu %6lu %6lu %u file%s\n";
63 static ZCONST
char Far OS2ExtAttrTrailer
[] =
64 "%ld file%s %ld bytes of OS/2 extended attributes attached.\n";
65 static ZCONST
char Far OS2ACLTrailer
[] =
66 "%ld file%s %ld bytes of access control lists attached.\n";
68 static ZCONST
char Far ShortHdrStats
[] =
69 "%9lu %02u-%02u-%02u %02u:%02u %c";
70 static ZCONST
char Far ShortFileTrailer
[] =
71 " -------- -------\n%9lu %u file%s\n";
79 /*************************/
80 /* Function list_files() */
81 /*************************/
83 int list_files(__G
) /* return PK-type error code */
86 int do_this_file
=FALSE
, cfactor
, error
, error_in_archive
=PK_COOL
;
88 char sgn
, cfactorstr
[10];
89 int longhdr
=(uO
.vflag
>1);
92 unsigned j
, methnum
, members
=0;
97 unsigned yr
, mo
, dy
, hh
, mm
;
98 ulg csiz
, tot_csize
=0L, tot_ucsize
=0L;
100 ulg ea_size
, tot_easize
=0L, tot_eafiles
=0L;
101 ulg acl_size
, tot_aclsize
=0L, tot_aclfiles
=0L;
105 static ZCONST
char dtype
[]="NXFS"; /* see zi_short() */
106 static ZCONST
char Far method
[NUM_METHODS
+1][8] =
107 {"Stored", "Shrunk", "Reduce1", "Reduce2", "Reduce3", "Reduce4",
108 "Implode", "Token", "Defl:#", "EnhDefl", "ImplDCL", "Unk:###"};
111 /*---------------------------------------------------------------------------
112 Unlike extract_or_test_files(), this routine confines itself to the cen-
113 tral directory. Thus its structure is somewhat simpler, since we can do
114 just a single loop through the entire directory, listing files as we go.
116 So to start off, print the heading line and then begin main loop through
117 the central directory. The results will look vaguely like the following:
119 Length Method Size Ratio Date Time CRC-32 Name ("^" ==> case
120 -------- ------ ------- ----- ---- ---- ------ ---- conversion)
121 44004 Implode 13041 71% 11-02-89 19:34 8b4207f7 Makefile.UNIX
122 3438 Shrunk 2209 36% 09-15-90 14:07 a2394fd8 ^dos-file.ext
123 16717 Defl:X 5252 69% 11-03-97 06:40 1ce0f189 WHERE
124 -------- ------- --- -------
125 64159 20502 68% 3 files
126 ---------------------------------------------------------------------------*/
130 date_format
= DATE_FORMAT
;
135 Info(slide
, 0, ((char *)slide
, LoadFarString(CaseConversion
),
136 LoadFarStringSmall(Headers
[longhdr
][0]),
137 LoadFarStringSmall2(Headers
[longhdr
][1])));
139 Info(slide
, 0, ((char *)slide
, "%s\n%s\n",
140 LoadFarString(Headers
[longhdr
][0]),
141 LoadFarStringSmall(Headers
[longhdr
][1])));
145 for (j
= 0; j
++ < (unsigned)G
.ecrec
.total_entries_central_dir
;) {
147 if (readbuf(__G__ G
.sig
, 4) == 0)
149 if (strncmp(G
.sig
, central_hdr_sig
, 4)) { /* just to make sure */
150 Info(slide
, 0x401, ((char *)slide
, LoadFarString(CentSigMsg
), j
));
151 Info(slide
, 0x401, ((char *)slide
, LoadFarString(ReportMsg
)));
152 return PK_BADERR
; /* sig not found */
154 /* process_cdir_file_hdr() sets pInfo->hostnum, pInfo->lcflag, ...: */
155 if ((error
= process_cdir_file_hdr(__G
)) != PK_COOL
)
156 return error
; /* only PK_EOF defined */
159 * We could DISPLAY the filename instead of storing (and possibly trun-
160 * cating, in the case of a very long name) and printing it, but that
161 * has the disadvantage of not allowing case conversion--and it's nice
162 * to be able to see in the listing precisely how you have to type each
163 * filename in order for unzip to consider it a match. Speaking of
164 * which, if member names were specified on the command line, check in
165 * with match() to see if the current file is one of them, and make a
166 * note of it if it is.
169 if ((error
= do_string(__G__ G
.crec
.filename_length
, DS_FN
)) !=
170 PK_COOL
) /* ^--(uses pInfo->lcflag) */
172 error_in_archive
= error
;
173 if (error
> PK_WARN
) /* fatal: can't continue */
176 if (G
.extra_field
!= (uch
*)NULL
) {
178 G
.extra_field
= (uch
*)NULL
;
180 if ((error
= do_string(__G__ G
.crec
.extra_field_length
, EXTRA_FIELD
))
183 error_in_archive
= error
;
184 if (error
> PK_WARN
) /* fatal */
187 if (!G
.process_all_files
) { /* check if specified on command line */
190 do_this_file
= FALSE
;
191 for (i
= 0; i
< G
.filespecs
; i
++)
192 if (match(G
.filename
, G
.pfnames
[i
], uO
.C_flag
)) {
194 break; /* found match, so stop looping */
196 if (do_this_file
) { /* check if this is an excluded file */
197 for (i
= 0; i
< G
.xfilespecs
; i
++)
198 if (match(G
.filename
, G
.pxnames
[i
], uO
.C_flag
)) {
199 do_this_file
= FALSE
; /* ^-- ignore case in match */
205 * If current file was specified on command line, or if no names were
206 * specified, do the listing for this file. Otherwise, get rid of the
207 * file comment and go back for the next file.
210 if (G
.process_all_files
|| do_this_file
) {
213 /* this is used by UzpFileTree() to allow easy processing of lists
214 * of zip directory contents */
215 if (G
.processExternally
) {
216 if ((G
.processExternally
)(G
.filename
, &G
.crec
))
223 uch
*ef_ptr
= G
.extra_field
;
224 int ef_size
, ef_len
= G
.crec
.extra_field_length
;
225 ea_size
= acl_size
= 0;
227 while (ef_len
>= EB_HEADSIZE
) {
228 ef_size
= makeword(&ef_ptr
[EB_LEN
]);
229 switch (makeword(&ef_ptr
[EB_ID
])) {
231 ea_size
= makelong(&ef_ptr
[EB_HEADSIZE
]);
234 acl_size
= makelong(&ef_ptr
[EB_HEADSIZE
]);
237 ef_ptr
+= (ef_size
+ EB_HEADSIZE
);
238 ef_len
-= (ef_size
+ EB_HEADSIZE
);
242 #ifdef USE_EF_UT_TIME
247 (ef_scan_for_izux(G
.extra_field
, G
.crec
.extra_field_length
, 1,
248 G
.crec
.last_mod_dos_datetime
, &z_utime
, NULL
)
251 TIMET_TO_NATIVE(z_utime
.mtime
) /* NOP unless MSC 7.0, Mac */
252 t
= localtime(&(z_utime
.mtime
));
254 t
= (struct tm
*)NULL
;
255 if (t
!= (struct tm
*)NULL
) {
256 mo
= (unsigned)(t
->tm_mon
+ 1);
257 dy
= (unsigned)(t
->tm_mday
);
258 yr
= (unsigned)(t
->tm_year
% 100);
259 hh
= (unsigned)(t
->tm_hour
);
260 mm
= (unsigned)(t
->tm_min
);
262 #endif /* USE_EF_UT_TIME */
264 yr
= ((((unsigned)(G
.crec
.last_mod_dos_datetime
>> 25) & 0x7f)
265 + 80) % (unsigned)100);
266 mo
= ((unsigned)(G
.crec
.last_mod_dos_datetime
>> 21) & 0x0f);
267 dy
= ((unsigned)(G
.crec
.last_mod_dos_datetime
>> 16) & 0x1f);
268 hh
= (((unsigned)G
.crec
.last_mod_dos_datetime
>> 11) & 0x1f);
269 mm
= (((unsigned)G
.crec
.last_mod_dos_datetime
>> 5) & 0x3f);
271 /* permute date so it displays according to nat'l convention
272 * ('methnum' is not yet set, it is used as temporary buffer) */
273 switch (date_format
) {
275 methnum
= (unsigned)mo
;
276 mo
= yr
; yr
= dy
; dy
= (ush
)methnum
;
279 methnum
= (unsigned)mo
;
280 mo
= dy
; dy
= (ush
)methnum
;
284 if (G
.crec
.general_purpose_bit_flag
& 1)
285 csiz
-= 12; /* if encrypted, don't count encryption header */
286 if ((cfactor
= ratio(G
.crec
.ucsize
, csiz
)) < 0) {
290 cfactor
= (-cfactor
+ 5) / 10;
295 cfactor
= (cfactor
+ 5) / 10;
298 methnum
= MIN(G
.crec
.compression_method
, NUM_METHODS
);
299 zfstrcpy(methbuf
, method
[methnum
]);
300 if (methnum
== DEFLATED
) {
301 methbuf
[5] = dtype
[(G
.crec
.general_purpose_bit_flag
>>1) & 3];
302 } else if (methnum
>= NUM_METHODS
) {
303 sprintf(&methbuf
[4], "%03u", G
.crec
.compression_method
);
306 #if 0 /* GRR/Euro: add this? */
307 #if defined(DOS_FLX_OS2_W32) || defined(UNIX)
308 for (p
= G
.filename
; *p
; ++p
)
310 *p
= '?'; /* change non-printable chars to '?' */
311 #endif /* DOS_FLX_OS2_W32 || UNIX */
315 /* send data to application for formatting and printing */
316 (*G
.lpUserFunctions
->SendApplicationMessage
)(G
.crec
.ucsize
, csiz
,
317 (ush
)cfactor
, mo
, dy
, yr
, hh
, mm
,
318 (char)(G
.pInfo
->lcflag
? '^' : ' '),
319 (LPSTR
)fnfilter(G
.filename
, slide
), (LPSTR
)methbuf
, G
.crec
.crc32
,
320 (char)((G
.crec
.general_purpose_bit_flag
& 1) ? 'E' : ' '));
323 sprintf(cfactorstr
, LoadFarString(CompFactor100
));
325 sprintf(cfactorstr
, LoadFarString(CompFactorStr
), sgn
, cfactor
);
327 Info(slide
, 0, ((char *)slide
, LoadFarString(LongHdrStats
),
328 G
.crec
.ucsize
, methbuf
, csiz
, cfactorstr
, mo
, dy
,
329 yr
, hh
, mm
, G
.crec
.crc32
, (G
.pInfo
->lcflag
? '^':' ')));
332 Info(slide
, 0, ((char *)slide
, LoadFarString(ShortHdrStats
),
333 G
.crec
.ucsize
, ea_size
, acl_size
,
334 mo
, dy
, yr
, hh
, mm
, (G
.pInfo
->lcflag
? '^':' ')));
336 Info(slide
, 0, ((char *)slide
, LoadFarString(ShortHdrStats
),
338 mo
, dy
, yr
, hh
, mm
, (G
.pInfo
->lcflag
? '^':' ')));
343 if ((error
= do_string(__G__ G
.crec
.file_comment_length
,
344 QCOND
? DISPL_8
: SKIP
)) != 0)
346 error_in_archive
= error
; /* might be just warning */
347 if (error
> PK_WARN
) /* fatal */
350 tot_ucsize
+= G
.crec
.ucsize
;
355 tot_easize
+= ea_size
;
359 tot_aclsize
+= acl_size
;
364 } /* end of "if (G.processExternally) {...} else {..." */
366 } else { /* not listing this file */
367 SKIP_(G
.crec
.file_comment_length
)
369 } /* end for-loop (j: files in central directory) */
371 /*---------------------------------------------------------------------------
372 Print footer line and totals (compressed size, uncompressed size, number
373 of members in zipfile).
374 ---------------------------------------------------------------------------*/
378 && !G
.processExternally
381 if ((cfactor
= ratio(tot_ucsize
, tot_csize
)) < 0) {
385 cfactor
= (-cfactor
+ 5) / 10;
390 cfactor
= (cfactor
+ 5) / 10;
393 /* pass the totals back to the calling application */
394 G
.lpUserFunctions
->TotalSizeComp
= tot_csize
;
395 G
.lpUserFunctions
->TotalSize
= tot_ucsize
;
396 G
.lpUserFunctions
->CompFactor
= cfactor
;
397 G
.lpUserFunctions
->NumMembers
= members
;
401 sprintf(cfactorstr
, LoadFarString(CompFactor100
));
403 sprintf(cfactorstr
, LoadFarString(CompFactorStr
), sgn
, cfactor
);
405 Info(slide
, 0, ((char *)slide
, LoadFarString(LongFileTrailer
),
406 tot_ucsize
, tot_csize
, cfactorstr
, members
, members
==1? "":"s"));
408 if (tot_easize
|| tot_aclsize
)
409 Info(slide
, 0, ((char *)slide
, "\n"));
410 if (tot_eafiles
&& tot_easize
)
411 Info(slide
, 0, ((char *)slide
, LoadFarString(OS2ExtAttrTrailer
),
412 tot_eafiles
, tot_eafiles
== 1? " has" : "s have a total of",
414 if (tot_aclfiles
&& tot_aclsize
)
415 Info(slide
, 0, ((char *)slide
, LoadFarString(OS2ACLTrailer
),
416 tot_aclfiles
, tot_aclfiles
== 1? " has" : "s have a total of",
421 Info(slide
, 0, ((char *)slide
, LoadFarString(ShortFileTrailer
),
422 tot_ucsize
, tot_easize
, tot_aclsize
, members
, members
== 1?
425 Info(slide
, 0, ((char *)slide
, LoadFarString(ShortFileTrailer
),
426 tot_ucsize
, members
, members
== 1? "" : "s"));
431 /*---------------------------------------------------------------------------
432 Double check that we're back at the end-of-central-directory record.
433 ---------------------------------------------------------------------------*/
435 if (readbuf(__G__ G
.sig
, 4) == 0) /* disk error? */
437 if (strncmp(G
.sig
, end_central_sig
, 4)) { /* just to make sure again */
438 Info(slide
, 0x401, ((char *)slide
, LoadFarString(EndSigMsg
)));
439 error_in_archive
= PK_WARN
; /* didn't find sig */
441 if (members
== 0 && error_in_archive
<= PK_WARN
)
442 error_in_archive
= PK_FIND
;
444 return error_in_archive
;
446 } /* end function list_files() */
454 /************************/
455 /* Function fn_is_dir() */
456 /************************/
458 static int fn_is_dir(__G
) /* returns TRUE if G.filename is directory */
461 extent fn_len
= strlen(G
.filename
);
465 ((endc
= G
.filename
[fn_len
-1]) == '/' ||
466 (G
.pInfo
->hostnum
== FS_FAT_
&& !strchr(G
.filename
, '/') &&
474 /*****************************/
475 /* Function get_time_stamp() */
476 /*****************************/
478 int get_time_stamp(__G__ last_modtime
, nmember
) /* return PK-type error code */
480 time_t *last_modtime
;
483 int do_this_file
=FALSE
, error
, error_in_archive
=PK_COOL
;
485 #ifdef USE_EF_UT_TIME
491 /*---------------------------------------------------------------------------
492 Unlike extract_or_test_files() but like list_files(), this function works
493 on information in the central directory alone. Thus we have a single,
494 large loop through the entire directory, searching for the latest time
496 ---------------------------------------------------------------------------*/
498 *last_modtime
= 0L; /* assuming no zipfile data older than 1970 */
502 for (j
= 0; j
++ < (unsigned)G
.ecrec
.total_entries_central_dir
;) {
504 if (readbuf(__G__ G
.sig
, 4) == 0)
506 if (strncmp(G
.sig
, central_hdr_sig
, 4)) { /* just to make sure */
507 Info(slide
, 0x401, ((char *)slide
, LoadFarString(CentSigMsg
), j
));
508 Info(slide
, 0x401, ((char *)slide
, LoadFarString(ReportMsg
)));
511 /* process_cdir_file_hdr() sets pInfo->lcflag: */
512 if ((error
= process_cdir_file_hdr(__G
)) != PK_COOL
)
513 return error
; /* only PK_EOF defined */
514 if ((error
= do_string(__G__ G
.crec
.filename_length
, DS_FN
)) != PK_OK
)
515 { /* ^-- (uses pInfo->lcflag) */
516 error_in_archive
= error
;
517 if (error
> PK_WARN
) /* fatal: can't continue */
520 if (G
.extra_field
!= (uch
*)NULL
) {
522 G
.extra_field
= (uch
*)NULL
;
524 if ((error
= do_string(__G__ G
.crec
.extra_field_length
, EXTRA_FIELD
))
527 error_in_archive
= error
;
528 if (error
> PK_WARN
) /* fatal */
531 if (!G
.process_all_files
) { /* check if specified on command line */
534 do_this_file
= FALSE
;
535 for (i
= 0; i
< G
.filespecs
; i
++)
536 if (match(G
.filename
, G
.pfnames
[i
], uO
.C_flag
)) {
538 break; /* found match, so stop looping */
540 if (do_this_file
) { /* check if this is an excluded file */
541 for (i
= 0; i
< G
.xfilespecs
; i
++)
542 if (match(G
.filename
, G
.pxnames
[i
], uO
.C_flag
)) {
543 do_this_file
= FALSE
; /* ^-- ignore case in match */
549 /* If current file was specified on command line, or if no names were
550 * specified, check the time for this file. Either way, get rid of the
551 * file comment and go back for the next file.
552 * Directory entries are always ignored, to stay compatible with both
555 if ((G
.process_all_files
|| do_this_file
) && !fn_is_dir(__G
)) {
556 #ifdef USE_EF_UT_TIME
561 (ef_scan_for_izux(G
.extra_field
, G
.crec
.extra_field_length
, 1,
562 G
.crec
.last_mod_dos_datetime
, &z_utime
, NULL
)
565 if (*last_modtime
< z_utime
.mtime
)
566 *last_modtime
= z_utime
.mtime
;
568 #endif /* USE_EF_UT_TIME */
570 time_t modtime
= dos_to_unix_time(G
.crec
.last_mod_dos_datetime
);
572 if (*last_modtime
< modtime
)
573 *last_modtime
= modtime
;
577 SKIP_(G
.crec
.file_comment_length
)
579 } /* end for-loop (j: files in central directory) */
581 /*---------------------------------------------------------------------------
582 Double check that we're back at the end-of-central-directory record.
583 ---------------------------------------------------------------------------*/
585 if (readbuf(__G__ G
.sig
, 4) == 0)
587 if (strncmp(G
.sig
, end_central_sig
, 4)) { /* just to make sure again */
588 Info(slide
, 0x401, ((char *)slide
, LoadFarString(EndSigMsg
)));
589 error_in_archive
= PK_WARN
;
591 if (*nmember
== 0 && error_in_archive
<= PK_WARN
)
592 error_in_archive
= PK_FIND
;
594 return error_in_archive
;
596 } /* end function get_time_stamp() */
598 #endif /* TIMESTAMP */
604 /********************/
605 /* Function ratio() */ /* also used by ZipInfo routines */
606 /********************/
615 if (uc
> 2000000L) { /* risk signed overflow if multiply numerator */
618 (int) ((uc
-c
+ (denom
>>1)) / denom
) :
619 -((int) ((c
-uc
+ (denom
>>1)) / denom
)));
620 } else { /* ^^^^^^^^ rounding */
623 (int) ((1000L*(uc
-c
) + (denom
>>1)) / denom
) :
624 -((int) ((1000L*(c
-uc
) + (denom
>>1)) / denom
)));
625 } /* ^^^^^^^^ rounding */
632 /************************/
633 /* Function fnprint() */ /* also used by ZipInfo routines */
634 /************************/
636 void fnprint(__G
) /* print filename (after filtering) and newline */
639 char *name
= fnfilter(G
.filename
, slide
);
641 (*G
.message
)((zvoid
*)&G
, (uch
*)name
, (ulg
)strlen(name
), 0);
642 (*G
.message
)((zvoid
*)&G
, (uch
*)"\n", 1L, 0);
644 } /* end function fnprint() */