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
91int 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
476static 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
496int 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
635int 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
663void 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