xref: /haiku/src/bin/unzip/list.c (revision 17049c451a91f427aec94b944b75876b611103e7)
1 /*
2   Copyright (c) 1990-2001 Info-ZIP.  All rights reserved.
3 
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
8 */
9 /*---------------------------------------------------------------------------
10 
11   list.c
12 
13   This file contains the non-ZipInfo-specific listing routines for UnZip.
14 
15   Contains:  list_files()
16              get_time_stamp()   [optional feature]
17              ratio()
18              fnprint()
19 
20   ---------------------------------------------------------------------------*/
21 
22 
23 #define UNZIP_INTERNAL
24 #include "unzip.h"
25 #ifdef WINDLL
26 #  ifdef POCKET_UNZIP
27 #    include "wince/intrface.h"
28 #  else
29 #    include "windll/windll.h"
30 #  endif
31 #endif
32 
33 
34 #ifdef TIMESTAMP
35    static int  fn_is_dir   OF((__GPRO));
36 #endif
37 
38 #ifndef WINDLL
39    static ZCONST char Far CompFactorStr[] = "%c%d%%";
40    static ZCONST char Far CompFactor100[] = "100%%";
41 
42 #ifdef OS2_EAS
43    static ZCONST char Far HeadersS[]  =
44      "  Length     EAs   ACLs    Date   Time    Name";
45    static ZCONST char Far HeadersS1[] =
46      " --------    ---   ----    ----   ----    ----";
47 #else
48    static ZCONST char Far HeadersS[]  = "  Length     Date   Time    Name";
49    static ZCONST char Far HeadersS1[] = " --------    ----   ----    ----";
50 #endif
51 
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} };
58 
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";
66 #ifdef OS2_EAS
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";
75 #else
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";
80 #endif /* ?OS2_EAS */
81 #endif /* !WINDLL */
82 
83 
84 
85 
86 
87 /*************************/
88 /* Function list_files() */
89 /*************************/
90 
list_files(__G)91 int list_files(__G)    /* return PK-type error code */
92     __GDEF
93 {
94     int do_this_file=FALSE, cfactor, error, error_in_archive=PK_COOL;
95 #ifndef WINDLL
96     char sgn, cfactorstr[10];
97     int longhdr=(uO.vflag>1);
98 #endif
99     int date_format;
100     ulg j, members=0L;
101     unsigned methnum;
102 #ifdef USE_EF_UT_TIME
103     iztimes z_utime;
104     struct tm *t;
105 #endif
106     unsigned yr, mo, dy, hh, mm;
107     ulg csiz, tot_csize=0L, tot_ucsize=0L;
108 #ifdef OS2_EAS
109     ulg ea_size, tot_easize=0L, tot_eafiles=0L;
110     ulg acl_size, tot_aclsize=0L, tot_aclfiles=0L;
111 #endif
112     min_info info;
113     char methbuf[8];
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:###"};
118 
119 
120 
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.
125 
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:
128 
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   ---------------------------------------------------------------------------*/
137 
138     G.pInfo = &info;
139     date_format = DATE_FORMAT;
140 
141 #ifndef WINDLL
142     if (uO.qflag < 2) {
143         if (uO.L_flag)
144             Info(slide, 0, ((char *)slide, LoadFarString(CaseConversion),
145               LoadFarStringSmall(Headers[longhdr][0]),
146               LoadFarStringSmall2(Headers[longhdr][1])));
147         else
148             Info(slide, 0, ((char *)slide, "%s\n%s\n",
149                LoadFarString(Headers[longhdr][0]),
150                LoadFarStringSmall(Headers[longhdr][1])));
151     }
152 #endif /* !WINDLL */
153 
154     for (j = 1L;;j++) {
155 
156         if (readbuf(__G__ G.sig, 4) == 0)
157             return PK_EOF;
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
164                  */
165                 break;
166             } else {
167                 Info(slide, 0x401,
168                      ((char *)slide, LoadFarString(CentSigMsg), j));
169                 Info(slide, 0x401,
170                      ((char *)slide, LoadFarString(ReportMsg)));
171                 return PK_BADERR;   /* sig not found */
172             }
173         }
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 */
177 
178         /*
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.
187          */
188 
189         if ((error = do_string(__G__ G.crec.filename_length, DS_FN)) !=
190              PK_COOL)   /*  ^--(uses pInfo->lcflag) */
191         {
192             error_in_archive = error;
193             if (error > PK_WARN)   /* fatal:  can't continue */
194                 return error;
195         }
196         if (G.extra_field != (uch *)NULL) {
197             free(G.extra_field);
198             G.extra_field = (uch *)NULL;
199         }
200         if ((error = do_string(__G__ G.crec.extra_field_length, EXTRA_FIELD))
201             != 0)
202         {
203             error_in_archive = error;
204             if (error > PK_WARN)      /* fatal */
205                 return error;
206         }
207         if (!G.process_all_files) {   /* check if specified on command line */
208             unsigned i;
209 
210             do_this_file = FALSE;
211             for (i = 0; i < G.filespecs; i++)
212                 if (match(G.filename, G.pfnames[i], uO.C_flag)) {
213                     do_this_file = TRUE;
214                     break;       /* found match, so stop looping */
215                 }
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 */
220                         break;
221                     }
222             }
223         }
224         /*
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.
228          */
229 
230         if (G.process_all_files || do_this_file) {
231 
232 #ifdef OS2DLL
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))
237                     break;
238                 ++members;
239             } else {
240 #endif
241 #ifdef OS2_EAS
242             {
243                 uch *ef_ptr = G.extra_field;
244                 int ef_size, ef_len = G.crec.extra_field_length;
245                 ea_size = acl_size = 0;
246 
247                 while (ef_len >= EB_HEADSIZE) {
248                     ef_size = makeword(&ef_ptr[EB_LEN]);
249                     switch (makeword(&ef_ptr[EB_ID])) {
250                         case EF_OS2:
251                             ea_size = makelong(&ef_ptr[EB_HEADSIZE]);
252                             break;
253                         case EF_ACL:
254                             acl_size = makelong(&ef_ptr[EB_HEADSIZE]);
255                             break;
256                     }
257                     ef_ptr += (ef_size + EB_HEADSIZE);
258                     ef_len -= (ef_size + EB_HEADSIZE);
259                 }
260             }
261 #endif
262 #ifdef USE_EF_UT_TIME
263             if (G.extra_field &&
264 #ifdef IZ_CHECK_TZ
265                 G.tz_is_valid &&
266 #endif
267                 (ef_scan_for_izux(G.extra_field, G.crec.extra_field_length, 1,
268                                   G.crec.last_mod_dos_datetime, &z_utime, NULL)
269                  & EB_UT_FL_MTIME))
270             {
271                 TIMET_TO_NATIVE(z_utime.mtime)   /* NOP unless MSC 7.0, Mac */
272                 t = localtime(&(z_utime.mtime));
273             } else
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);
281             } else
282 #endif /* USE_EF_UT_TIME */
283             {
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);
290             }
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) {
294                 case DF_YMD:
295                     methnum = mo;
296                     mo = yr; yr = dy; dy = methnum;
297                     break;
298                 case DF_DMY:
299                     methnum = mo;
300                     mo = dy; dy = methnum;
301             }
302 
303             csiz = G.crec.csize;
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) {
307 #ifndef WINDLL
308                 sgn = '-';
309 #endif
310                 cfactor = (-cfactor + 5) / 10;
311             } else {
312 #ifndef WINDLL
313                 sgn = ' ';
314 #endif
315                 cfactor = (cfactor + 5) / 10;
316             }
317 
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);
324             }
325 
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)
329                 if (!isprint(*p))
330                     *p = '?';  /* change non-printable chars to '?' */
331 #endif /* DOS_FLX_NLM_OS2_W32 || THEOS || UNIX */
332 #endif /* 0 */
333 
334 #ifdef WINDLL
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' : ' '));
341 #else /* !WINDLL */
342             if (cfactor == 100)
343                 sprintf(cfactorstr, LoadFarString(CompFactor100));
344             else
345                 sprintf(cfactorstr, LoadFarString(CompFactorStr), sgn, cfactor);
346             if (longhdr)
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? '^':' ')));
350             else
351 #ifdef OS2_EAS
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? '^':' ')));
355 #else
356                 Info(slide, 0, ((char *)slide, LoadFarString(ShortHdrStats),
357                   G.crec.ucsize,
358                   mo, dy, yr, hh, mm, (G.pInfo->lcflag? '^':' ')));
359 #endif
360             fnprint(__G);
361 #endif /* ?WINDLL */
362 
363             if ((error = do_string(__G__ G.crec.file_comment_length,
364                                    QCOND? DISPL_8 : SKIP)) != 0)
365             {
366                 error_in_archive = error;  /* might be just warning */
367                 if (error > PK_WARN)       /* fatal */
368                     return error;
369             }
370             tot_ucsize += G.crec.ucsize;
371             tot_csize += csiz;
372             ++members;
373 #ifdef OS2_EAS
374             if (ea_size) {
375                 tot_easize += ea_size;
376                 ++tot_eafiles;
377             }
378             if (acl_size) {
379                 tot_aclsize += acl_size;
380                 ++tot_aclfiles;
381             }
382 #endif
383 #ifdef OS2DLL
384             } /* end of "if (G.processExternally) {...} else {..." */
385 #endif
386         } else {        /* not listing this file */
387             SKIP_(G.crec.file_comment_length)
388         }
389     } /* end for-loop (j: files in central directory) */
390 
391 /*---------------------------------------------------------------------------
392     Print footer line and totals (compressed size, uncompressed size, number
393     of members in zipfile).
394   ---------------------------------------------------------------------------*/
395 
396     if (uO.qflag < 2
397 #ifdef OS2DLL
398                      && !G.processExternally
399 #endif
400                                             ) {
401         if ((cfactor = ratio(tot_ucsize, tot_csize)) < 0) {
402 #ifndef WINDLL
403             sgn = '-';
404 #endif
405             cfactor = (-cfactor + 5) / 10;
406         } else {
407 #ifndef WINDLL
408             sgn = ' ';
409 #endif
410             cfactor = (cfactor + 5) / 10;
411         }
412 #ifdef WINDLL
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;
418 
419 #else /* !WINDLL */
420         if (cfactor == 100)
421             sprintf(cfactorstr, LoadFarString(CompFactor100));
422         else
423             sprintf(cfactorstr, LoadFarString(CompFactorStr), sgn, cfactor);
424         if (longhdr) {
425             Info(slide, 0, ((char *)slide, LoadFarString(LongFileTrailer),
426               tot_ucsize, tot_csize, cfactorstr, members, members==1? "":"s"));
427 #ifdef OS2_EAS
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",
433                   tot_easize));
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",
437                   tot_aclsize));
438 #endif /* OS2_EAS */
439         } else
440 #ifdef OS2_EAS
441             Info(slide, 0, ((char *)slide, LoadFarString(ShortFileTrailer),
442               tot_ucsize, tot_easize, tot_aclsize, members, members == 1?
443               "" : "s"));
444 #else
445             Info(slide, 0, ((char *)slide, LoadFarString(ShortFileTrailer),
446               tot_ucsize, members, members == 1? "" : "s"));
447 #endif /* OS2_EAS */
448 #endif /* ?WINDLL */
449     }
450 
451 /*---------------------------------------------------------------------------
452     Double check that we're back at the end-of-central-directory record.
453   ---------------------------------------------------------------------------*/
454 
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 */
458     }
459     if (members == 0L && error_in_archive <= PK_WARN)
460         error_in_archive = PK_FIND;
461 
462     return error_in_archive;
463 
464 } /* end function list_files() */
465 
466 
467 
468 
469 
470 #ifdef TIMESTAMP
471 
472 /************************/
473 /* Function fn_is_dir() */
474 /************************/
475 
fn_is_dir(__G)476 static int fn_is_dir(__G)    /* returns TRUE if G.filename is directory */
477     __GDEF
478 {
479     extent fn_len = strlen(G.filename);
480     register char   endc;
481 
482     return  fn_len > 0 &&
483             ((endc = lastchar(G.filename, fn_len)) == '/' ||
484              (G.pInfo->hostnum == FS_FAT_ && !MBSCHR(G.filename, '/') &&
485               endc == '\\'));
486 }
487 
488 
489 
490 
491 
492 /*****************************/
493 /* Function get_time_stamp() */
494 /*****************************/
495 
496 int get_time_stamp(__G__ last_modtime, nmember)  /* return PK-type error code */
497     __GDEF
498     time_t *last_modtime;
499     ulg *nmember;
500 {
501     int do_this_file=FALSE, error, error_in_archive=PK_COOL;
502     ulg j;
503 #ifdef USE_EF_UT_TIME
504     iztimes z_utime;
505 #endif
506     min_info info;
507 
508 
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
513     stamp.
514   ---------------------------------------------------------------------------*/
515 
516     *last_modtime = 0L;         /* assuming no zipfile data older than 1970 */
517     *nmember = 0L;
518     G.pInfo = &info;
519 
520     for (j = 1L;; j++) {
521 
522         if (readbuf(__G__ G.sig, 4) == 0)
523             return PK_EOF;
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
530                  */
531                 break;
532             } else {
533                 Info(slide, 0x401,
534                      ((char *)slide, LoadFarString(CentSigMsg), j));
535                 Info(slide, 0x401,
536                      ((char *)slide, LoadFarString(ReportMsg)));
537                 return PK_BADERR;   /* sig not found */
538             }
539         }
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 */
547                 return error;
548         }
549         if (G.extra_field != (uch *)NULL) {
550             free(G.extra_field);
551             G.extra_field = (uch *)NULL;
552         }
553         if ((error = do_string(__G__ G.crec.extra_field_length, EXTRA_FIELD))
554             != 0)
555         {
556             error_in_archive = error;
557             if (error > PK_WARN)      /* fatal */
558                 return error;
559         }
560         if (!G.process_all_files) {   /* check if specified on command line */
561             unsigned i;
562 
563             do_this_file = FALSE;
564             for (i = 0; i < G.filespecs; i++)
565                 if (match(G.filename, G.pfnames[i], uO.C_flag)) {
566                     do_this_file = TRUE;
567                     break;       /* found match, so stop looping */
568                 }
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 */
573                         break;
574                     }
575             }
576         }
577 
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
582          * Zip and PKZIP.
583          */
584         if ((G.process_all_files || do_this_file) && !fn_is_dir(__G)) {
585 #ifdef USE_EF_UT_TIME
586             if (G.extra_field &&
587 #ifdef IZ_CHECK_TZ
588                 G.tz_is_valid &&
589 #endif
590                 (ef_scan_for_izux(G.extra_field, G.crec.extra_field_length, 1,
591                                   G.crec.last_mod_dos_datetime, &z_utime, NULL)
592                  & EB_UT_FL_MTIME))
593             {
594                 if (*last_modtime < z_utime.mtime)
595                     *last_modtime = z_utime.mtime;
596             } else
597 #endif /* USE_EF_UT_TIME */
598             {
599                 time_t modtime = dos_to_unix_time(G.crec.last_mod_dos_datetime);
600 
601                 if (*last_modtime < modtime)
602                     *last_modtime = modtime;
603             }
604             ++*nmember;
605         }
606         SKIP_(G.crec.file_comment_length)
607 
608     } /* end for-loop (j: files in central directory) */
609 
610 /*---------------------------------------------------------------------------
611     Double check that we're back at the end-of-central-directory record.
612   ---------------------------------------------------------------------------*/
613 
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;
617     }
618     if (*nmember == 0L && error_in_archive <= PK_WARN)
619         error_in_archive = PK_FIND;
620 
621     return error_in_archive;
622 
623 } /* end function get_time_stamp() */
624 
625 #endif /* TIMESTAMP */
626 
627 
628 
629 
630 
631 /********************/
632 /* Function ratio() */    /* also used by ZipInfo routines */
633 /********************/
634 
ratio(uc,c)635 int ratio(uc, c)
636     ulg uc, c;
637 {
638     ulg denom;
639 
640     if (uc == 0)
641         return 0;
642     if (uc > 2000000L) {    /* risk signed overflow if multiply numerator */
643         denom = uc / 1000L;
644         return ((uc >= c) ?
645             (int) ((uc-c + (denom>>1)) / denom) :
646           -((int) ((c-uc + (denom>>1)) / denom)));
647     } else {             /* ^^^^^^^^ rounding */
648         denom = uc;
649         return ((uc >= c) ?
650             (int) ((1000L*(uc-c) + (denom>>1)) / denom) :
651           -((int) ((1000L*(c-uc) + (denom>>1)) / denom)));
652     }                            /* ^^^^^^^^ rounding */
653 }
654 
655 
656 
657 
658 
659 /************************/
660 /*  Function fnprint()  */    /* also used by ZipInfo routines */
661 /************************/
662 
fnprint(__G)663 void fnprint(__G)    /* print filename (after filtering) and newline */
664     __GDEF
665 {
666     char *name = fnfilter(G.filename, slide);
667 
668     (*G.message)((zvoid *)&G, (uch *)name, (ulg)strlen(name), 0);
669     (*G.message)((zvoid *)&G, (uch *)"\n", 1L, 0);
670 
671 } /* end function fnprint() */
672