2 Copyright (c) 1990-2001 Info-ZIP. All rights reserved.
4 See the accompanying file LICENSE, version 2000-Apr-09 or later
5 (the contents of which are also included in unzip.h) for terms of use.
6 If, for some reason, all these files are missing, the Info-ZIP license
7 also may be found at: ftp://ftp.info-zip.org/pub/infozip/license.html
9 /*---------------------------------------------------------------------------
13 This file contains the non-ZipInfo-specific listing routines for UnZip.
15 Contains: list_files()
16 get_time_stamp() [optional feature]
20 ---------------------------------------------------------------------------*/
23 #define UNZIP_INTERNAL
27 # include "wince/intrface.h"
29 # include "windll/windll.h"
35 static int fn_is_dir
OF((__GPRO
));
39 static ZCONST
char Far CompFactorStr
[] = "%c%d%%";
40 static ZCONST
char Far CompFactor100
[] = "100%%";
43 static ZCONST
char Far HeadersS
[] =
44 " Length EAs ACLs Date Time Name";
45 static ZCONST
char Far HeadersS1
[] =
46 " -------- --- ---- ---- ---- ----";
48 static ZCONST
char Far HeadersS
[] = " Length Date Time Name";
49 static ZCONST
char Far HeadersS1
[] = " -------- ---- ---- ----";
52 static ZCONST
char Far HeadersL
[] =
53 " Length Method Size Ratio Date Time CRC-32 Name";
54 static ZCONST
char Far HeadersL1
[] =
55 "-------- ------ ------- ----- ---- ---- ------ ----";
56 static ZCONST
char Far
*Headers
[][2] =
57 { {HeadersS
, HeadersS1
}, {HeadersL
, HeadersL1
} };
59 static ZCONST
char Far CaseConversion
[] =
60 "%s (\"^\" ==> case\n%s conversion)\n";
61 static ZCONST
char Far LongHdrStats
[] =
62 "%8lu %-7s%8lu %4s %02u-%02u-%02u %02u:%02u %08lx %c";
63 static ZCONST
char Far LongFileTrailer
[] =
64 "-------- ------- --- \
65 -------\n%8lu %8lu %4s %lu file%s\n";
67 static ZCONST
char Far ShortHdrStats
[] =
68 "%9lu %6lu %6lu %02u-%02u-%02u %02u:%02u %c";
69 static ZCONST
char Far ShortFileTrailer
[] = " -------- ----- ----- \
70 -------\n%9lu %6lu %6lu %lu file%s\n";
71 static ZCONST
char Far OS2ExtAttrTrailer
[] =
72 "%lu file%s %lu bytes of OS/2 extended attributes attached.\n";
73 static ZCONST
char Far OS2ACLTrailer
[] =
74 "%lu file%s %lu bytes of access control lists attached.\n";
76 static ZCONST
char Far ShortHdrStats
[] =
77 "%9lu %02u-%02u-%02u %02u:%02u %c";
78 static ZCONST
char Far ShortFileTrailer
[] = " -------- \
79 -------\n%9lu %lu file%s\n";
87 /*************************/
88 /* Function list_files() */
89 /*************************/
91 int list_files(__G
) /* return PK-type error code */
94 int do_this_file
=FALSE
, cfactor
, error
, error_in_archive
=PK_COOL
;
96 char sgn
, cfactorstr
[10];
97 int longhdr
=(uO
.vflag
>1);
102 #ifdef USE_EF_UT_TIME
106 unsigned yr
, mo
, dy
, hh
, mm
;
107 ulg csiz
, tot_csize
=0L, tot_ucsize
=0L;
109 ulg ea_size
, tot_easize
=0L, tot_eafiles
=0L;
110 ulg acl_size
, tot_aclsize
=0L, tot_aclfiles
=0L;
114 static ZCONST
char dtype
[]="NXFS"; /* see zi_short() */
115 static ZCONST
char Far method
[NUM_METHODS
+1][8] =
116 {"Stored", "Shrunk", "Reduce1", "Reduce2", "Reduce3", "Reduce4",
117 "Implode", "Token", "Defl:#", "Def64#", "ImplDCL", "Unk:###"};
121 /*---------------------------------------------------------------------------
122 Unlike extract_or_test_files(), this routine confines itself to the cen-
123 tral directory. Thus its structure is somewhat simpler, since we can do
124 just a single loop through the entire directory, listing files as we go.
126 So to start off, print the heading line and then begin main loop through
127 the central directory. The results will look vaguely like the following:
129 Length Method Size Ratio Date Time CRC-32 Name ("^" ==> case
130 -------- ------ ------- ----- ---- ---- ------ ---- conversion)
131 44004 Implode 13041 71% 11-02-89 19:34 8b4207f7 Makefile.UNIX
132 3438 Shrunk 2209 36% 09-15-90 14:07 a2394fd8 ^dos-file.ext
133 16717 Defl:X 5252 69% 11-03-97 06:40 1ce0f189 WHERE
134 -------- ------- --- -------
135 64159 20502 68% 3 files
136 ---------------------------------------------------------------------------*/
139 date_format
= DATE_FORMAT
;
144 Info(slide
, 0, ((char *)slide
, LoadFarString(CaseConversion
),
145 LoadFarStringSmall(Headers
[longhdr
][0]),
146 LoadFarStringSmall2(Headers
[longhdr
][1])));
148 Info(slide
, 0, ((char *)slide
, "%s\n%s\n",
149 LoadFarString(Headers
[longhdr
][0]),
150 LoadFarStringSmall(Headers
[longhdr
][1])));
156 if (readbuf(__G__ G
.sig
, 4) == 0)
158 if (strncmp(G
.sig
, central_hdr_sig
, 4)) { /* is it a CentDir entry? */
159 if (((unsigned)(j
- 1) & (unsigned)0xFFFF) ==
160 (unsigned)G
.ecrec
.total_entries_central_dir
) {
161 /* "j modulus 64k" matches the reported 16-bit-unsigned
162 * number of directory entries -> probably, the regular
163 * end of the central directory has been reached
168 ((char *)slide
, LoadFarString(CentSigMsg
), j
));
170 ((char *)slide
, LoadFarString(ReportMsg
)));
171 return PK_BADERR
; /* sig not found */
174 /* process_cdir_file_hdr() sets pInfo->hostnum, pInfo->lcflag, ...: */
175 if ((error
= process_cdir_file_hdr(__G
)) != PK_COOL
)
176 return error
; /* only PK_EOF defined */
179 * We could DISPLAY the filename instead of storing (and possibly trun-
180 * cating, in the case of a very long name) and printing it, but that
181 * has the disadvantage of not allowing case conversion--and it's nice
182 * to be able to see in the listing precisely how you have to type each
183 * filename in order for unzip to consider it a match. Speaking of
184 * which, if member names were specified on the command line, check in
185 * with match() to see if the current file is one of them, and make a
186 * note of it if it is.
189 if ((error
= do_string(__G__ G
.crec
.filename_length
, DS_FN
)) !=
190 PK_COOL
) /* ^--(uses pInfo->lcflag) */
192 error_in_archive
= error
;
193 if (error
> PK_WARN
) /* fatal: can't continue */
196 if (G
.extra_field
!= (uch
*)NULL
) {
198 G
.extra_field
= (uch
*)NULL
;
200 if ((error
= do_string(__G__ G
.crec
.extra_field_length
, EXTRA_FIELD
))
203 error_in_archive
= error
;
204 if (error
> PK_WARN
) /* fatal */
207 if (!G
.process_all_files
) { /* check if specified on command line */
210 do_this_file
= FALSE
;
211 for (i
= 0; i
< G
.filespecs
; i
++)
212 if (match(G
.filename
, G
.pfnames
[i
], uO
.C_flag
)) {
214 break; /* found match, so stop looping */
216 if (do_this_file
) { /* check if this is an excluded file */
217 for (i
= 0; i
< G
.xfilespecs
; i
++)
218 if (match(G
.filename
, G
.pxnames
[i
], uO
.C_flag
)) {
219 do_this_file
= FALSE
; /* ^-- ignore case in match */
225 * If current file was specified on command line, or if no names were
226 * specified, do the listing for this file. Otherwise, get rid of the
227 * file comment and go back for the next file.
230 if (G
.process_all_files
|| do_this_file
) {
233 /* this is used by UzpFileTree() to allow easy processing of lists
234 * of zip directory contents */
235 if (G
.processExternally
) {
236 if ((G
.processExternally
)(G
.filename
, &G
.crec
))
243 uch
*ef_ptr
= G
.extra_field
;
244 int ef_size
, ef_len
= G
.crec
.extra_field_length
;
245 ea_size
= acl_size
= 0;
247 while (ef_len
>= EB_HEADSIZE
) {
248 ef_size
= makeword(&ef_ptr
[EB_LEN
]);
249 switch (makeword(&ef_ptr
[EB_ID
])) {
251 ea_size
= makelong(&ef_ptr
[EB_HEADSIZE
]);
254 acl_size
= makelong(&ef_ptr
[EB_HEADSIZE
]);
257 ef_ptr
+= (ef_size
+ EB_HEADSIZE
);
258 ef_len
-= (ef_size
+ EB_HEADSIZE
);
262 #ifdef USE_EF_UT_TIME
267 (ef_scan_for_izux(G
.extra_field
, G
.crec
.extra_field_length
, 1,
268 G
.crec
.last_mod_dos_datetime
, &z_utime
, NULL
)
271 TIMET_TO_NATIVE(z_utime
.mtime
) /* NOP unless MSC 7.0, Mac */
272 t
= localtime(&(z_utime
.mtime
));
274 t
= (struct tm
*)NULL
;
275 if (t
!= (struct tm
*)NULL
) {
276 mo
= (unsigned)(t
->tm_mon
+ 1);
277 dy
= (unsigned)(t
->tm_mday
);
278 yr
= (unsigned)(t
->tm_year
% 100);
279 hh
= (unsigned)(t
->tm_hour
);
280 mm
= (unsigned)(t
->tm_min
);
282 #endif /* USE_EF_UT_TIME */
284 yr
= ((((unsigned)(G
.crec
.last_mod_dos_datetime
>> 25) & 0x7f)
285 + 80) % (unsigned)100);
286 mo
= ((unsigned)(G
.crec
.last_mod_dos_datetime
>> 21) & 0x0f);
287 dy
= ((unsigned)(G
.crec
.last_mod_dos_datetime
>> 16) & 0x1f);
288 hh
= (((unsigned)G
.crec
.last_mod_dos_datetime
>> 11) & 0x1f);
289 mm
= (((unsigned)G
.crec
.last_mod_dos_datetime
>> 5) & 0x3f);
291 /* permute date so it displays according to nat'l convention
292 * ('methnum' is not yet set, it is used as temporary buffer) */
293 switch (date_format
) {
296 mo
= yr
; yr
= dy
; dy
= methnum
;
300 mo
= dy
; dy
= methnum
;
304 if (G
.crec
.general_purpose_bit_flag
& 1)
305 csiz
-= 12; /* if encrypted, don't count encryption header */
306 if ((cfactor
= ratio(G
.crec
.ucsize
, csiz
)) < 0) {
310 cfactor
= (-cfactor
+ 5) / 10;
315 cfactor
= (cfactor
+ 5) / 10;
318 methnum
= MIN(G
.crec
.compression_method
, NUM_METHODS
);
319 zfstrcpy(methbuf
, method
[methnum
]);
320 if (methnum
== DEFLATED
|| methnum
== ENHDEFLATED
) {
321 methbuf
[5] = dtype
[(G
.crec
.general_purpose_bit_flag
>>1) & 3];
322 } else if (methnum
>= NUM_METHODS
) {
323 sprintf(&methbuf
[4], "%03u", G
.crec
.compression_method
);
326 #if 0 /* GRR/Euro: add this? */
327 #if defined(DOS_FLX_NLM_OS2_W32) || defined(THEOS) || defined(UNIX)
328 for (p
= G
.filename
; *p
; ++p
)
330 *p
= '?'; /* change non-printable chars to '?' */
331 #endif /* DOS_FLX_NLM_OS2_W32 || THEOS || UNIX */
335 /* send data to application for formatting and printing */
336 (*G
.lpUserFunctions
->SendApplicationMessage
)(G
.crec
.ucsize
, csiz
,
337 (unsigned)cfactor
, mo
, dy
, yr
, hh
, mm
,
338 (char)(G
.pInfo
->lcflag
? '^' : ' '),
339 (LPSTR
)fnfilter(G
.filename
, slide
), (LPSTR
)methbuf
, G
.crec
.crc32
,
340 (char)((G
.crec
.general_purpose_bit_flag
& 1) ? 'E' : ' '));
343 sprintf(cfactorstr
, LoadFarString(CompFactor100
));
345 sprintf(cfactorstr
, LoadFarString(CompFactorStr
), sgn
, cfactor
);
347 Info(slide
, 0, ((char *)slide
, LoadFarString(LongHdrStats
),
348 G
.crec
.ucsize
, methbuf
, csiz
, cfactorstr
, mo
, dy
,
349 yr
, hh
, mm
, G
.crec
.crc32
, (G
.pInfo
->lcflag
? '^':' ')));
352 Info(slide
, 0, ((char *)slide
, LoadFarString(ShortHdrStats
),
353 G
.crec
.ucsize
, ea_size
, acl_size
,
354 mo
, dy
, yr
, hh
, mm
, (G
.pInfo
->lcflag
? '^':' ')));
356 Info(slide
, 0, ((char *)slide
, LoadFarString(ShortHdrStats
),
358 mo
, dy
, yr
, hh
, mm
, (G
.pInfo
->lcflag
? '^':' ')));
363 if ((error
= do_string(__G__ G
.crec
.file_comment_length
,
364 QCOND
? DISPL_8
: SKIP
)) != 0)
366 error_in_archive
= error
; /* might be just warning */
367 if (error
> PK_WARN
) /* fatal */
370 tot_ucsize
+= G
.crec
.ucsize
;
375 tot_easize
+= ea_size
;
379 tot_aclsize
+= acl_size
;
384 } /* end of "if (G.processExternally) {...} else {..." */
386 } else { /* not listing this file */
387 SKIP_(G
.crec
.file_comment_length
)
389 } /* end for-loop (j: files in central directory) */
391 /*---------------------------------------------------------------------------
392 Print footer line and totals (compressed size, uncompressed size, number
393 of members in zipfile).
394 ---------------------------------------------------------------------------*/
398 && !G
.processExternally
401 if ((cfactor
= ratio(tot_ucsize
, tot_csize
)) < 0) {
405 cfactor
= (-cfactor
+ 5) / 10;
410 cfactor
= (cfactor
+ 5) / 10;
413 /* pass the totals back to the calling application */
414 G
.lpUserFunctions
->TotalSizeComp
= tot_csize
;
415 G
.lpUserFunctions
->TotalSize
= tot_ucsize
;
416 G
.lpUserFunctions
->CompFactor
= (ulg
)cfactor
;
417 G
.lpUserFunctions
->NumMembers
= members
;
421 sprintf(cfactorstr
, LoadFarString(CompFactor100
));
423 sprintf(cfactorstr
, LoadFarString(CompFactorStr
), sgn
, cfactor
);
425 Info(slide
, 0, ((char *)slide
, LoadFarString(LongFileTrailer
),
426 tot_ucsize
, tot_csize
, cfactorstr
, members
, members
==1? "":"s"));
428 if (tot_easize
|| tot_aclsize
)
429 Info(slide
, 0, ((char *)slide
, "\n"));
430 if (tot_eafiles
&& tot_easize
)
431 Info(slide
, 0, ((char *)slide
, LoadFarString(OS2ExtAttrTrailer
),
432 tot_eafiles
, tot_eafiles
== 1? " has" : "s have a total of",
434 if (tot_aclfiles
&& tot_aclsize
)
435 Info(slide
, 0, ((char *)slide
, LoadFarString(OS2ACLTrailer
),
436 tot_aclfiles
, tot_aclfiles
== 1? " has" : "s have a total of",
441 Info(slide
, 0, ((char *)slide
, LoadFarString(ShortFileTrailer
),
442 tot_ucsize
, tot_easize
, tot_aclsize
, members
, members
== 1?
445 Info(slide
, 0, ((char *)slide
, LoadFarString(ShortFileTrailer
),
446 tot_ucsize
, members
, members
== 1? "" : "s"));
451 /*---------------------------------------------------------------------------
452 Double check that we're back at the end-of-central-directory record.
453 ---------------------------------------------------------------------------*/
455 if (strncmp(G
.sig
, end_central_sig
, 4)) { /* just to make sure again */
456 Info(slide
, 0x401, ((char *)slide
, LoadFarString(EndSigMsg
)));
457 error_in_archive
= PK_WARN
; /* didn't find sig */
459 if (members
== 0L && error_in_archive
<= PK_WARN
)
460 error_in_archive
= PK_FIND
;
462 return error_in_archive
;
464 } /* end function list_files() */
472 /************************/
473 /* Function fn_is_dir() */
474 /************************/
476 static int fn_is_dir(__G
) /* returns TRUE if G.filename is directory */
479 extent fn_len
= strlen(G
.filename
);
483 ((endc
= lastchar(G
.filename
, fn_len
)) == '/' ||
484 (G
.pInfo
->hostnum
== FS_FAT_
&& !MBSCHR(G
.filename
, '/') &&
492 /*****************************/
493 /* Function get_time_stamp() */
494 /*****************************/
496 int get_time_stamp(__G__ last_modtime
, nmember
) /* return PK-type error code */
498 time_t *last_modtime
;
501 int do_this_file
=FALSE
, error
, error_in_archive
=PK_COOL
;
503 #ifdef USE_EF_UT_TIME
509 /*---------------------------------------------------------------------------
510 Unlike extract_or_test_files() but like list_files(), this function works
511 on information in the central directory alone. Thus we have a single,
512 large loop through the entire directory, searching for the latest time
514 ---------------------------------------------------------------------------*/
516 *last_modtime
= 0L; /* assuming no zipfile data older than 1970 */
522 if (readbuf(__G__ G
.sig
, 4) == 0)
524 if (strncmp(G
.sig
, central_hdr_sig
, 4)) { /* is it a CentDir entry? */
525 if (((unsigned)(j
- 1) & (unsigned)0xFFFF) ==
526 (unsigned)G
.ecrec
.total_entries_central_dir
) {
527 /* "j modulus 64k" matches the reported 16-bit-unsigned
528 * number of directory entries -> probably, the regular
529 * end of the central directory has been reached
534 ((char *)slide
, LoadFarString(CentSigMsg
), j
));
536 ((char *)slide
, LoadFarString(ReportMsg
)));
537 return PK_BADERR
; /* sig not found */
540 /* process_cdir_file_hdr() sets pInfo->lcflag: */
541 if ((error
= process_cdir_file_hdr(__G
)) != PK_COOL
)
542 return error
; /* only PK_EOF defined */
543 if ((error
= do_string(__G__ G
.crec
.filename_length
, DS_FN
)) != PK_OK
)
544 { /* ^-- (uses pInfo->lcflag) */
545 error_in_archive
= error
;
546 if (error
> PK_WARN
) /* fatal: can't continue */
549 if (G
.extra_field
!= (uch
*)NULL
) {
551 G
.extra_field
= (uch
*)NULL
;
553 if ((error
= do_string(__G__ G
.crec
.extra_field_length
, EXTRA_FIELD
))
556 error_in_archive
= error
;
557 if (error
> PK_WARN
) /* fatal */
560 if (!G
.process_all_files
) { /* check if specified on command line */
563 do_this_file
= FALSE
;
564 for (i
= 0; i
< G
.filespecs
; i
++)
565 if (match(G
.filename
, G
.pfnames
[i
], uO
.C_flag
)) {
567 break; /* found match, so stop looping */
569 if (do_this_file
) { /* check if this is an excluded file */
570 for (i
= 0; i
< G
.xfilespecs
; i
++)
571 if (match(G
.filename
, G
.pxnames
[i
], uO
.C_flag
)) {
572 do_this_file
= FALSE
; /* ^-- ignore case in match */
578 /* If current file was specified on command line, or if no names were
579 * specified, check the time for this file. Either way, get rid of the
580 * file comment and go back for the next file.
581 * Directory entries are always ignored, to stay compatible with both
584 if ((G
.process_all_files
|| do_this_file
) && !fn_is_dir(__G
)) {
585 #ifdef USE_EF_UT_TIME
590 (ef_scan_for_izux(G
.extra_field
, G
.crec
.extra_field_length
, 1,
591 G
.crec
.last_mod_dos_datetime
, &z_utime
, NULL
)
594 if (*last_modtime
< z_utime
.mtime
)
595 *last_modtime
= z_utime
.mtime
;
597 #endif /* USE_EF_UT_TIME */
599 time_t modtime
= dos_to_unix_time(G
.crec
.last_mod_dos_datetime
);
601 if (*last_modtime
< modtime
)
602 *last_modtime
= modtime
;
606 SKIP_(G
.crec
.file_comment_length
)
608 } /* end for-loop (j: files in central directory) */
610 /*---------------------------------------------------------------------------
611 Double check that we're back at the end-of-central-directory record.
612 ---------------------------------------------------------------------------*/
614 if (strncmp(G
.sig
, end_central_sig
, 4)) { /* just to make sure again */
615 Info(slide
, 0x401, ((char *)slide
, LoadFarString(EndSigMsg
)));
616 error_in_archive
= PK_WARN
;
618 if (*nmember
== 0L && error_in_archive
<= PK_WARN
)
619 error_in_archive
= PK_FIND
;
621 return error_in_archive
;
623 } /* end function get_time_stamp() */
625 #endif /* TIMESTAMP */
631 /********************/
632 /* Function ratio() */ /* also used by ZipInfo routines */
633 /********************/
642 if (uc
> 2000000L) { /* risk signed overflow if multiply numerator */
645 (int) ((uc
-c
+ (denom
>>1)) / denom
) :
646 -((int) ((c
-uc
+ (denom
>>1)) / denom
)));
647 } else { /* ^^^^^^^^ rounding */
650 (int) ((1000L*(uc
-c
) + (denom
>>1)) / denom
) :
651 -((int) ((1000L*(c
-uc
) + (denom
>>1)) / denom
)));
652 } /* ^^^^^^^^ rounding */
659 /************************/
660 /* Function fnprint() */ /* also used by ZipInfo routines */
661 /************************/
663 void fnprint(__G
) /* print filename (after filtering) and newline */
666 char *name
= fnfilter(G
.filename
, slide
);
668 (*G
.message
)((zvoid
*)&G
, (uch
*)name
, (ulg
)strlen(name
), 0);
669 (*G
.message
)((zvoid
*)&G
, (uch
*)"\n", 1L, 0);
671 } /* end function fnprint() */