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:###"};
113 /*---------------------------------------------------------------------------
114 Unlike extract_or_test_files(), this routine confines itself to the cen-
115 tral directory. Thus its structure is somewhat simpler, since we can do
116 just a single loop through the entire directory, listing files as we go.
118 So to start off, print the heading line and then begin main loop through
119 the central directory. The results will look vaguely like the following:
121 Length Method Size Ratio Date Time CRC-32 Name ("^" ==> case
122 -------- ------ ------- ----- ---- ---- ------ ---- conversion)
123 44004 Implode 13041 71% 11-02-89 19:34 8b4207f7 Makefile.UNIX
124 3438 Shrunk 2209 36% 09-15-90 14:07 a2394fd8 ^dos-file.ext
125 16717 Defl:X 5252 69% 11-03-97 06:40 1ce0f189 WHERE
126 -------- ------- --- -------
127 64159 20502 68% 3 files
128 ---------------------------------------------------------------------------*/
132 date_format
= DATE_FORMAT
;
139 Info(slide
, 0, ((char *)slide
, LoadFarString(CaseConversion
),
140 LoadFarStringSmall(Headers
[longhdr
][0]),
141 LoadFarStringSmall2(Headers
[longhdr
][1])));
143 Info(slide
, 0, ((char *)slide
, "%s\n%s\n",
144 LoadFarString(Headers
[longhdr
][0]),
145 LoadFarStringSmall(Headers
[longhdr
][1])));
149 for (j
= 0; j
++ < (unsigned)G
.ecrec
.total_entries_central_dir
;) {
151 if (readbuf(__G__ G
.sig
, 4) == 0)
153 if (strncmp(G
.sig
, central_hdr_sig
, 4)) { /* just to make sure */
154 Info(slide
, 0x401, ((char *)slide
, LoadFarString(CentSigMsg
), j
));
155 Info(slide
, 0x401, ((char *)slide
, LoadFarString(ReportMsg
)));
156 return PK_BADERR
; /* sig not found */
158 /* process_cdir_file_hdr() sets pInfo->hostnum, pInfo->lcflag, ...: */
159 if ((error
= process_cdir_file_hdr(__G
)) != PK_COOL
)
160 return error
; /* only PK_EOF defined */
163 * We could DISPLAY the filename instead of storing (and possibly trun-
164 * cating, in the case of a very long name) and printing it, but that
165 * has the disadvantage of not allowing case conversion--and it's nice
166 * to be able to see in the listing precisely how you have to type each
167 * filename in order for unzip to consider it a match. Speaking of
168 * which, if member names were specified on the command line, check in
169 * with match() to see if the current file is one of them, and make a
170 * note of it if it is.
173 if ((error
= do_string(__G__ G
.crec
.filename_length
, DS_FN
)) !=
174 PK_COOL
) /* ^--(uses pInfo->lcflag) */
176 error_in_archive
= error
;
177 if (error
> PK_WARN
) /* fatal: can't continue */
180 if (G
.extra_field
!= (uch
*)NULL
) {
182 G
.extra_field
= (uch
*)NULL
;
184 if ((error
= do_string(__G__ G
.crec
.extra_field_length
, EXTRA_FIELD
))
187 error_in_archive
= error
;
188 if (error
> PK_WARN
) /* fatal */
191 if (!G
.process_all_files
) { /* check if specified on command line */
194 do_this_file
= FALSE
;
195 for (i
= 0; i
< G
.filespecs
; i
++)
196 if (match(G
.filename
, G
.pfnames
[i
], uO
.C_flag
)) {
198 break; /* found match, so stop looping */
200 if (do_this_file
) { /* check if this is an excluded file */
201 for (i
= 0; i
< G
.xfilespecs
; i
++)
202 if (match(G
.filename
, G
.pxnames
[i
], uO
.C_flag
)) {
203 do_this_file
= FALSE
; /* ^-- ignore case in match */
209 * If current file was specified on command line, or if no names were
210 * specified, do the listing for this file. Otherwise, get rid of the
211 * file comment and go back for the next file.
214 if (G
.process_all_files
|| do_this_file
) {
217 /* this is used by UzpFileTree() to allow easy processing of lists
218 * of zip directory contents */
219 if (G
.processExternally
) {
220 if ((G
.processExternally
)(G
.filename
, &G
.crec
))
227 uch
*ef_ptr
= G
.extra_field
;
228 int ef_size
, ef_len
= G
.crec
.extra_field_length
;
229 ea_size
= acl_size
= 0;
231 while (ef_len
>= EB_HEADSIZE
) {
232 ef_size
= makeword(&ef_ptr
[EB_LEN
]);
233 switch (makeword(&ef_ptr
[EB_ID
])) {
235 ea_size
= makelong(&ef_ptr
[EB_HEADSIZE
]);
238 acl_size
= makelong(&ef_ptr
[EB_HEADSIZE
]);
241 ef_ptr
+= (ef_size
+ EB_HEADSIZE
);
242 ef_len
-= (ef_size
+ EB_HEADSIZE
);
246 #ifdef USE_EF_UT_TIME
251 (ef_scan_for_izux(G
.extra_field
, G
.crec
.extra_field_length
, 1,
252 G
.crec
.last_mod_dos_datetime
, &z_utime
, NULL
)
255 TIMET_TO_NATIVE(z_utime
.mtime
) /* NOP unless MSC 7.0, Mac */
256 t
= localtime(&(z_utime
.mtime
));
258 t
= (struct tm
*)NULL
;
259 if (t
!= (struct tm
*)NULL
) {
260 mo
= (unsigned)(t
->tm_mon
+ 1);
261 dy
= (unsigned)(t
->tm_mday
);
262 yr
= (unsigned)(t
->tm_year
% 100);
263 hh
= (unsigned)(t
->tm_hour
);
264 mm
= (unsigned)(t
->tm_min
);
266 #endif /* USE_EF_UT_TIME */
268 yr
= ((((unsigned)(G
.crec
.last_mod_dos_datetime
>> 25) & 0x7f)
269 + 80) % (unsigned)100);
270 mo
= ((unsigned)(G
.crec
.last_mod_dos_datetime
>> 21) & 0x0f);
271 dy
= ((unsigned)(G
.crec
.last_mod_dos_datetime
>> 16) & 0x1f);
272 hh
= (((unsigned)G
.crec
.last_mod_dos_datetime
>> 11) & 0x1f);
273 mm
= (((unsigned)G
.crec
.last_mod_dos_datetime
>> 5) & 0x3f);
275 /* permute date so it displays according to nat'l convention
276 * ('methnum' is not yet set, it is used as temporary buffer) */
277 switch (date_format
) {
279 methnum
= (unsigned)mo
;
280 mo
= yr
; yr
= dy
; dy
= (ush
)methnum
;
283 methnum
= (unsigned)mo
;
284 mo
= dy
; dy
= (ush
)methnum
;
288 if (G
.crec
.general_purpose_bit_flag
& 1)
289 csiz
-= 12; /* if encrypted, don't count encryption header */
290 if ((cfactor
= ratio(G
.crec
.ucsize
, csiz
)) < 0) {
294 cfactor
= (-cfactor
+ 5) / 10;
299 cfactor
= (cfactor
+ 5) / 10;
302 methnum
= MIN(G
.crec
.compression_method
, NUM_METHODS
);
303 zfstrcpy(methbuf
, method
[methnum
]);
304 if (methnum
== DEFLATED
) {
305 methbuf
[5] = dtype
[(G
.crec
.general_purpose_bit_flag
>>1) & 3];
306 } else if (methnum
>= NUM_METHODS
) {
307 sprintf(&methbuf
[4], "%03u", G
.crec
.compression_method
);
310 #if 0 /* GRR/Euro: add this? */
311 #if defined(DOS_FLX_OS2_W32) || defined(UNIX)
312 for (p
= G
.filename
; *p
; ++p
)
314 *p
= '?'; /* change non-printable chars to '?' */
315 #endif /* DOS_FLX_OS2_W32 || UNIX */
319 /* send data to application for formatting and printing */
320 (*G
.lpUserFunctions
->SendApplicationMessage
)(G
.crec
.ucsize
, csiz
,
321 (ush
)cfactor
, mo
, dy
, yr
, hh
, mm
,
322 (char)(G
.pInfo
->lcflag
? '^' : ' '),
323 (LPSTR
)fnfilter(G
.filename
, slide
), (LPSTR
)methbuf
, G
.crec
.crc32
,
324 (char)((G
.crec
.general_purpose_bit_flag
& 1) ? 'E' : ' '));
327 sprintf(cfactorstr
, LoadFarString(CompFactor100
));
329 sprintf(cfactorstr
, LoadFarString(CompFactorStr
), sgn
, cfactor
);
331 Info(slide
, 0, ((char *)slide
, LoadFarString(LongHdrStats
),
332 G
.crec
.ucsize
, methbuf
, csiz
, cfactorstr
, mo
, dy
,
333 yr
, hh
, mm
, G
.crec
.crc32
, (G
.pInfo
->lcflag
? '^':' ')));
336 Info(slide
, 0, ((char *)slide
, LoadFarString(ShortHdrStats
),
337 G
.crec
.ucsize
, ea_size
, acl_size
,
338 mo
, dy
, yr
, hh
, mm
, (G
.pInfo
->lcflag
? '^':' ')));
340 Info(slide
, 0, ((char *)slide
, LoadFarString(ShortHdrStats
),
342 mo
, dy
, yr
, hh
, mm
, (G
.pInfo
->lcflag
? '^':' ')));
348 if ((error
= do_string(__G__ G
.crec
.file_comment_length
,
349 QCOND
? DISPL_8
: SKIP
)) != 0)
351 error_in_archive
= error
; /* might be just warning */
352 if (error
> PK_WARN
) /* fatal */
355 tot_ucsize
+= G
.crec
.ucsize
;
360 tot_easize
+= ea_size
;
364 tot_aclsize
+= acl_size
;
369 } /* end of "if (G.processExternally) {...} else {..." */
371 } else { /* not listing this file */
372 SKIP_(G
.crec
.file_comment_length
)
374 } /* end for-loop (j: files in central directory) */
376 /*---------------------------------------------------------------------------
377 Print footer line and totals (compressed size, uncompressed size, number
378 of members in zipfile).
379 ---------------------------------------------------------------------------*/
383 && !G
.processExternally
386 if ((cfactor
= ratio(tot_ucsize
, tot_csize
)) < 0) {
390 cfactor
= (-cfactor
+ 5) / 10;
395 cfactor
= (cfactor
+ 5) / 10;
398 /* pass the totals back to the calling application */
399 G
.lpUserFunctions
->TotalSizeComp
= tot_csize
;
400 G
.lpUserFunctions
->TotalSize
= tot_ucsize
;
401 G
.lpUserFunctions
->CompFactor
= cfactor
;
402 G
.lpUserFunctions
->NumMembers
= members
;
406 sprintf(cfactorstr
, LoadFarString(CompFactor100
));
408 sprintf(cfactorstr
, LoadFarString(CompFactorStr
), sgn
, cfactor
);
410 Info(slide
, 0, ((char *)slide
, LoadFarString(LongFileTrailer
),
411 tot_ucsize
, tot_csize
, cfactorstr
, members
, members
==1? "":"s"));
413 if (tot_easize
|| tot_aclsize
)
414 Info(slide
, 0, ((char *)slide
, "\n"));
415 if (tot_eafiles
&& tot_easize
)
416 Info(slide
, 0, ((char *)slide
, LoadFarString(OS2ExtAttrTrailer
),
417 tot_eafiles
, tot_eafiles
== 1? " has" : "s have a total of",
419 if (tot_aclfiles
&& tot_aclsize
)
420 Info(slide
, 0, ((char *)slide
, LoadFarString(OS2ACLTrailer
),
421 tot_aclfiles
, tot_aclfiles
== 1? " has" : "s have a total of",
426 Info(slide
, 0, ((char *)slide
, LoadFarString(ShortFileTrailer
),
427 tot_ucsize
, tot_easize
, tot_aclsize
, members
, members
== 1?
430 Info(slide
, 0, ((char *)slide
, LoadFarString(ShortFileTrailer
),
431 tot_ucsize
, members
, members
== 1? "" : "s"));
436 /*---------------------------------------------------------------------------
437 Double check that we're back at the end-of-central-directory record.
438 ---------------------------------------------------------------------------*/
440 if (readbuf(__G__ G
.sig
, 4) == 0) /* disk error? */
442 if (strncmp(G
.sig
, end_central_sig
, 4)) { /* just to make sure again */
443 Info(slide
, 0x401, ((char *)slide
, LoadFarString(EndSigMsg
)));
444 error_in_archive
= PK_WARN
; /* didn't find sig */
446 if (members
== 0 && error_in_archive
<= PK_WARN
)
447 error_in_archive
= PK_FIND
;
449 return error_in_archive
;
451 } /* end function list_files() */
459 /************************/
460 /* Function fn_is_dir() */
461 /************************/
463 static int fn_is_dir(__G
) /* returns TRUE if G.filename is directory */
466 extent fn_len
= strlen(G
.filename
);
470 ((endc
= G
.filename
[fn_len
-1]) == '/' ||
471 (G
.pInfo
->hostnum
== FS_FAT_
&& !strchr(G
.filename
, '/') &&
479 /*****************************/
480 /* Function get_time_stamp() */
481 /*****************************/
483 int get_time_stamp(__G__ last_modtime
, nmember
) /* return PK-type error code */
485 time_t *last_modtime
;
488 int do_this_file
=FALSE
, error
, error_in_archive
=PK_COOL
;
490 #ifdef USE_EF_UT_TIME
496 /*---------------------------------------------------------------------------
497 Unlike extract_or_test_files() but like list_files(), this function works
498 on information in the central directory alone. Thus we have a single,
499 large loop through the entire directory, searching for the latest time
501 ---------------------------------------------------------------------------*/
503 *last_modtime
= 0L; /* assuming no zipfile data older than 1970 */
507 for (j
= 0; j
++ < (unsigned)G
.ecrec
.total_entries_central_dir
;) {
509 if (readbuf(__G__ G
.sig
, 4) == 0)
511 if (strncmp(G
.sig
, central_hdr_sig
, 4)) { /* just to make sure */
512 Info(slide
, 0x401, ((char *)slide
, LoadFarString(CentSigMsg
), j
));
513 Info(slide
, 0x401, ((char *)slide
, LoadFarString(ReportMsg
)));
516 /* process_cdir_file_hdr() sets pInfo->lcflag: */
517 if ((error
= process_cdir_file_hdr(__G
)) != PK_COOL
)
518 return error
; /* only PK_EOF defined */
519 if ((error
= do_string(__G__ G
.crec
.filename_length
, DS_FN
)) != PK_OK
)
520 { /* ^-- (uses pInfo->lcflag) */
521 error_in_archive
= error
;
522 if (error
> PK_WARN
) /* fatal: can't continue */
525 if (G
.extra_field
!= (uch
*)NULL
) {
527 G
.extra_field
= (uch
*)NULL
;
529 if ((error
= do_string(__G__ G
.crec
.extra_field_length
, EXTRA_FIELD
))
532 error_in_archive
= error
;
533 if (error
> PK_WARN
) /* fatal */
536 if (!G
.process_all_files
) { /* check if specified on command line */
539 do_this_file
= FALSE
;
540 for (i
= 0; i
< G
.filespecs
; i
++)
541 if (match(G
.filename
, G
.pfnames
[i
], uO
.C_flag
)) {
543 break; /* found match, so stop looping */
545 if (do_this_file
) { /* check if this is an excluded file */
546 for (i
= 0; i
< G
.xfilespecs
; i
++)
547 if (match(G
.filename
, G
.pxnames
[i
], uO
.C_flag
)) {
548 do_this_file
= FALSE
; /* ^-- ignore case in match */
554 /* If current file was specified on command line, or if no names were
555 * specified, check the time for this file. Either way, get rid of the
556 * file comment and go back for the next file.
557 * Directory entries are always ignored, to stay compatible with both
560 if ((G
.process_all_files
|| do_this_file
) && !fn_is_dir(__G
)) {
561 #ifdef USE_EF_UT_TIME
566 (ef_scan_for_izux(G
.extra_field
, G
.crec
.extra_field_length
, 1,
567 G
.crec
.last_mod_dos_datetime
, &z_utime
, NULL
)
570 if (*last_modtime
< z_utime
.mtime
)
571 *last_modtime
= z_utime
.mtime
;
573 #endif /* USE_EF_UT_TIME */
575 time_t modtime
= dos_to_unix_time(G
.crec
.last_mod_dos_datetime
);
577 if (*last_modtime
< modtime
)
578 *last_modtime
= modtime
;
582 SKIP_(G
.crec
.file_comment_length
)
584 } /* end for-loop (j: files in central directory) */
586 /*---------------------------------------------------------------------------
587 Double check that we're back at the end-of-central-directory record.
588 ---------------------------------------------------------------------------*/
590 if (readbuf(__G__ G
.sig
, 4) == 0)
592 if (strncmp(G
.sig
, end_central_sig
, 4)) { /* just to make sure again */
593 Info(slide
, 0x401, ((char *)slide
, LoadFarString(EndSigMsg
)));
594 error_in_archive
= PK_WARN
;
596 if (*nmember
== 0 && error_in_archive
<= PK_WARN
)
597 error_in_archive
= PK_FIND
;
599 return error_in_archive
;
601 } /* end function get_time_stamp() */
603 #endif /* TIMESTAMP */
609 /********************/
610 /* Function ratio() */ /* also used by ZipInfo routines */
611 /********************/
620 if (uc
> 2000000L) { /* risk signed overflow if multiply numerator */
623 (int) ((uc
-c
+ (denom
>>1)) / denom
) :
624 -((int) ((c
-uc
+ (denom
>>1)) / denom
)));
625 } else { /* ^^^^^^^^ rounding */
628 (int) ((1000L*(uc
-c
) + (denom
>>1)) / denom
) :
629 -((int) ((1000L*(c
-uc
) + (denom
>>1)) / denom
)));
630 } /* ^^^^^^^^ rounding */
637 /************************/
638 /* Function fnprint() */ /* also used by ZipInfo routines */
639 /************************/
641 void fnprint(__G
) /* print filename (after filtering) and newline */
644 char *name
= fnfilter(G
.filename
, slide
);
646 (*G
.message
)((zvoid
*)&G
, (uch
*)name
, (ulg
)strlen(name
), 0);
647 (*G
.message
)((zvoid
*)&G
, (uch
*)"\n", 1L, 0);
649 } /* end function fnprint() */