1/*
2  Copyright (c) 1990-2002 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  fileio.c
12
13  This file contains routines for doing direct but relatively generic input/
14  output, file-related sorts of things, plus some miscellaneous stuff.  Most
15  of the stuff has to do with opening, closing, reading and/or writing files.
16
17  Contains:  open_input_file()
18             open_outfile()           (not: VMS, AOS/VS, CMSMVS, MACOS, TANDEM)
19             undefer_input()
20             defer_leftover_input()
21             readbuf()
22             readbyte()
23             fillinbuf()
24             seek_zipf()
25             flush()                  (non-VMS)
26             is_vms_varlen_txt()      (non-VMS, VMS_TEXT_CONV only)
27             disk_error()             (non-VMS)
28             UzpMessagePrnt()
29             UzpMessageNull()         (DLL only)
30             UzpInput()
31             UzpMorePause()
32             UzpPassword()            (non-WINDLL)
33             handler()
34             dos_to_unix_time()       (non-VMS, non-VM/CMS, non-MVS)
35             check_for_newer()        (non-VMS, non-OS/2, non-VM/CMS, non-MVS)
36             do_string()
37             makeword()
38             makelong()
39             str2iso()                (CRYPT && NEED_STR2ISO, only)
40             str2oem()                (CRYPT && NEED_STR2OEM, only)
41             memset()                 (ZMEM only)
42             memcpy()                 (ZMEM only)
43             zstrnicmp()              (NO_STRNICMP only)
44             zstat()                  (REGULUS only)
45             plastchar()              (_MBCS only)
46             uzmbschr()               (_MBCS && NEED_UZMBSCHR, only)
47             uzmbsrchr()              (_MBCS && NEED_UZMBSRCHR, only)
48             fLoadFarString()         (SMALL_MEM only)
49             fLoadFarStringSmall()    (SMALL_MEM only)
50             fLoadFarStringSmall2()   (SMALL_MEM only)
51             zfstrcpy()               (SMALL_MEM only)
52
53  ---------------------------------------------------------------------------*/
54
55
56#define __FILEIO_C      /* identifies this source module */
57#define UNZIP_INTERNAL
58#include "unzip.h"
59#ifdef WINDLL
60#  include "windll/windll.h"
61#  include <setjmp.h>
62#endif
63#include "crypt.h"
64#include "ttyio.h"
65
66/* setup of codepage conversion for decryption passwords */
67#if CRYPT
68#  if (defined(CRYP_USES_ISO2OEM) && !defined(IZ_ISO2OEM_ARRAY))
69#    define IZ_ISO2OEM_ARRAY            /* pull in iso2oem[] table */
70#  endif
71#  if (defined(CRYP_USES_OEM2ISO) && !defined(IZ_OEM2ISO_ARRAY))
72#    define IZ_OEM2ISO_ARRAY            /* pull in oem2iso[] table */
73#  endif
74#endif
75#include "ebcdic.h"   /* definition/initialization of ebcdic[] */
76
77
78/*
79   Note: Under Windows, the maximum size of the buffer that can be used
80   with any of the *printf calls is 16,384, so win_fprintf was used to
81   feed the fprintf clone no more than 16K chunks at a time. This should
82   be valid for anything up to 64K (and probably beyond, assuming your
83   buffers are that big).
84*/
85#ifdef WINDLL
86#  define WriteError(buf,len,strm) \
87   (win_fprintf(pG, strm, (extent)len, (char far *)buf) != (int)(len))
88#else /* !WINDLL */
89#  ifdef USE_FWRITE
90#    define WriteError(buf,len,strm) \
91     ((extent)fwrite((char *)(buf),1,(extent)(len),strm) != (extent)(len))
92#  else
93#    define WriteError(buf,len,strm) \
94     ((extent)write(fileno(strm),(char *)(buf),(extent)(len)) != (extent)(len))
95#  endif
96#endif /* ?WINDLL */
97
98#if (defined(USE_DEFLATE64) && defined(__16BIT__))
99static int partflush OF((__GPRO__ uch *rawbuf, ulg size, int unshrink));
100#endif
101#ifdef VMS_TEXT_CONV
102static int is_vms_varlen_txt OF((__GPRO__ uch *ef_buf, unsigned ef_len));
103#endif
104static int disk_error OF((__GPRO));
105
106
107/****************************/
108/* Strings used in fileio.c */
109/****************************/
110
111static ZCONST char Far CannotOpenZipfile[] =
112  "error:  cannot open zipfile [ %s ]\n";
113
114#if (!defined(VMS) && !defined(AOS_VS) && !defined(CMS_MVS) && !defined(MACOS))
115#if (!defined(TANDEM))
116#if (defined(BEO_THS_UNX) || defined(DOS_FLX_NLM_OS2_W32))
117   static ZCONST char Far CannotDeleteOldFile[] =
118     "error:  cannot delete old %s\n";
119#ifdef UNIXBACKUP
120   static ZCONST char Far CannotRenameOldFile[] =
121     "error:  cannot rename old %s\n";
122   static ZCONST char Far BackupSuffix[] = "~";
123#endif
124#endif /* BEO_THS_UNX || DOS_FLX_NLM_OS2_W32 */
125#ifdef NOVELL_BUG_FAILSAFE
126   static ZCONST char Far NovellBug[] =
127     "error:  %s: stat() says does not exist, but fopen() found anyway\n";
128#endif
129   static ZCONST char Far CannotCreateFile[] = "error:  cannot create %s\n";
130#endif /* !TANDEM */
131#endif /* !VMS && !AOS_VS && !CMS_MVS && !MACOS */
132
133static ZCONST char Far ReadError[] = "error:  zipfile read error\n";
134static ZCONST char Far FilenameTooLongTrunc[] =
135  "warning:  filename too long--truncating.\n";
136static ZCONST char Far ExtraFieldTooLong[] =
137  "warning:  extra field too long (%d).  Ignoring...\n";
138
139#ifdef WINDLL
140   static ZCONST char Far DiskFullQuery[] =
141     "%s:  write error (disk full?).\n";
142#else
143   static ZCONST char Far DiskFullQuery[] =
144     "%s:  write error (disk full?).  Continue? (y/n/^C) ";
145   static ZCONST char Far ZipfileCorrupt[] =
146     "error:  zipfile probably corrupt (%s)\n";
147#  ifdef SYMLINKS
148     static ZCONST char Far FileIsSymLink[] =
149       "%s exists and is a symbolic link%s.\n";
150#  endif
151#  ifdef MORE
152     static ZCONST char Far MorePrompt[] = "--More--(%lu)";
153#  endif
154   static ZCONST char Far QuitPrompt[] =
155     "--- Press `Q' to quit, or any other key to continue ---";
156   static ZCONST char Far HidePrompt[] = /* "\r                       \r"; */
157     "\r                                                         \r";
158#  if CRYPT
159#    ifdef MACOS
160       /* SPC: are names on MacOS REALLY so much longer than elsewhere ??? */
161       static ZCONST char Far PasswPrompt[] = "[%s]\n %s password: ";
162#    else
163       static ZCONST char Far PasswPrompt[] = "[%s] %s password: ";
164#    endif
165     static ZCONST char Far PasswPrompt2[] = "Enter password: ";
166     static ZCONST char Far PasswRetry[] = "password incorrect--reenter: ";
167#  endif /* CRYPT */
168#endif /* !WINDLL */
169
170
171
172
173
174/******************************/
175/* Function open_input_file() */
176/******************************/
177
178int open_input_file(__G)    /* return 1 if open failed */
179    __GDEF
180{
181    /*
182     *  open the zipfile for reading and in BINARY mode to prevent cr/lf
183     *  translation, which would corrupt the bitstreams
184     */
185
186#ifdef VMS
187    G.zipfd = open(G.zipfn, O_RDONLY, 0, "ctx=stm");
188#else /* !VMS */
189#ifdef MACOS
190    G.zipfd = open(G.zipfn, 0);
191#else /* !MACOS */
192#ifdef CMS_MVS
193    G.zipfd = vmmvs_open_infile(__G);
194#else /* !CMS_MVS */
195#ifdef USE_STRM_INPUT
196    G.zipfd = fopen(G.zipfn, FOPR);
197#else /* !USE_STRM_INPUT */
198# ifdef O_BINARY
199    G.zipfd = open(G.zipfn, O_RDONLY | O_BINARY);
200# else
201    G.zipfd = open(G.zipfn, O_RDONLY);
202# endif
203#endif /* ?USE_STRM_INPUT */
204#endif /* ?CMS_MVS */
205#endif /* ?MACOS */
206#endif /* ?VMS */
207
208#ifdef USE_STRM_INPUT
209    if (G.zipfd == NULL)
210#else
211    /* if (G.zipfd < 0) */  /* no good for Windows CE port */
212    if (G.zipfd == -1)
213#endif
214    {
215        Info(slide, 0x401, ((char *)slide, LoadFarString(CannotOpenZipfile),
216          G.zipfn));
217        return 1;
218    }
219    return 0;
220
221} /* end function open_input_file() */
222
223
224
225
226#if (!defined(VMS) && !defined(AOS_VS) && !defined(CMS_MVS) && !defined(MACOS))
227#if (!defined(TANDEM))
228
229/***************************/
230/* Function open_outfile() */
231/***************************/
232
233int open_outfile(__G)         /* return 1 if fail */
234    __GDEF
235{
236#ifdef DLL
237    if (G.redirect_data)
238        return (redirect_outfile(__G) == FALSE);
239#endif
240#ifdef QDOS
241    QFilename(__G__ G.filename);
242#endif
243#if (defined(DOS_FLX_NLM_OS2_W32) || defined(BEO_THS_UNX))
244#ifdef BORLAND_STAT_BUG
245    /* Borland 5.0's stat() barfs if the filename has no extension and the
246     * file doesn't exist. */
247    if (access(G.filename, 0) == -1) {
248        FILE *tmp = fopen(G.filename, "wb+");
249
250        /* file doesn't exist, so create a dummy file to keep stat() from
251         * failing (will be over-written anyway) */
252        fputc('0', tmp);  /* just to have something in the file */
253        fclose(tmp);
254    }
255#endif /* BORLAND_STAT_BUG */
256#ifdef SYMLINKS
257    if (SSTAT(G.filename, &G.statbuf) == 0 || lstat(G.filename,&G.statbuf) == 0)
258#else
259    if (SSTAT(G.filename, &G.statbuf) == 0)
260#endif /* ?SYMLINKS */
261    {
262        Trace((stderr, "open_outfile:  stat(%s) returns 0:  file exists\n",
263          FnFilter1(G.filename)));
264#ifdef UNIXBACKUP
265        if (uO.B_flag) {    /* do backup */
266            char *tname;
267            struct stat tmpstat;
268            int blen, flen, tlen;
269
270            blen = strlen(BackupSuffix);
271            flen = strlen(G.filename);
272            tlen = flen + blen + 6;    /* includes space for 5 digits */
273            if (tlen >= FILNAMSIZ) {   /* in case name is too long, truncate */
274                tname = (char *)malloc(FILNAMSIZ);
275                if (tname == NULL)
276                    return 1;                 /* in case we run out of space */
277                tlen = FILNAMSIZ - 1 - blen;
278                strcpy(tname, G.filename);    /* make backup name */
279                tname[tlen] = '\0';
280                if (flen > tlen) flen = tlen;
281                tlen = FILNAMSIZ;
282            } else {
283                tname = (char *)malloc(tlen);
284                if (tname == NULL)
285                    return 1;                 /* in case we run out of space */
286                strcpy(tname, G.filename);    /* make backup name */
287            }
288            strcpy(tname+flen, BackupSuffix);
289
290            if (IS_OVERWRT_ALL) {
291                /* If there is a previous backup file, delete it,
292                 * otherwise the following rename operation may fail.
293                 */
294                if (SSTAT(tname, &tmpstat) == 0)
295                    unlink(tname);
296            } else {
297                /* Check if backupname exists, and, if it's true, try
298                 * appending numbers of up to 5 digits to the BackupSuffix,
299                 * until an unused name is found.
300                 */
301                unsigned maxtail, i;
302                char *numtail = tname + flen + blen;
303
304                maxtail = 65535;
305                switch (tlen - flen - blen - 1) {
306                    case 4: maxtail = 9999; break;
307                    case 3: maxtail = 999; break;
308                    case 2: maxtail = 99; break;
309                    case 1: maxtail = 9; break;
310                    case 0: maxtail = 0; break;
311                }
312                /* while filename exists */
313                for (i = 0; (i <= maxtail) && (SSTAT(tname, &tmpstat) == 0);)
314                    sprintf(numtail,"%u", ++i);
315            }
316
317            if (rename(G.filename, tname) != 0) {   /* move file */
318                Info(slide, 0x401, ((char *)slide,
319                  LoadFarString(CannotRenameOldFile), FnFilter1(G.filename)));
320                free(tname);
321                return 1;
322            }
323            Trace((stderr, "open_outfile:  %s now renamed into %s\n",
324              FnFilter1(G.filename), FnFilter2(tname)));
325            free(tname);
326        } else
327#endif /* UNIXBACKUP */
328        {
329#ifdef DOS_FLX_OS2_W32
330            if (!(G.statbuf.st_mode & S_IWRITE)) {
331                Trace((stderr,
332                  "open_outfile:  existing file %s is read-only\n",
333                  FnFilter1(G.filename)));
334                chmod(G.filename, S_IREAD | S_IWRITE);
335                Trace((stderr, "open_outfile:  %s now writable\n",
336                  FnFilter1(G.filename)));
337            }
338#endif /* DOS_FLX_OS2_W32 */
339#ifdef NLM
340            /* Give the file read/write permission (non-POSIX shortcut) */
341            chmod(G.filename, 0);
342#endif /* NLM */
343            if (unlink(G.filename) != 0) {
344                Info(slide, 0x401, ((char *)slide,
345                  LoadFarString(CannotDeleteOldFile), FnFilter1(G.filename)));
346                return 1;
347            }
348            Trace((stderr, "open_outfile:  %s now deleted\n",
349              FnFilter1(G.filename)));
350        }
351    }
352#endif /* DOS_FLX_NLM_OS2_W32 || BEO_THS_UNX */
353#ifdef RISCOS
354    if (SWI_OS_File_7(G.filename,0xDEADDEAD,0xDEADDEAD,G.lrec.ucsize)!=NULL) {
355        Info(slide, 1, ((char *)slide, LoadFarString(CannotCreateFile),
356          FnFilter1(G.filename)));
357        return 1;
358    }
359#endif /* RISCOS */
360#ifdef TOPS20
361    char *tfilnam;
362
363    if ((tfilnam = (char *)malloc(2*strlen(G.filename)+1)) == (char *)NULL)
364        return 1;
365    strcpy(tfilnam, G.filename);
366    upper(tfilnam);
367    enquote(tfilnam);
368    if ((G.outfile = fopen(tfilnam, FOPW)) == (FILE *)NULL) {
369        Info(slide, 1, ((char *)slide, LoadFarString(CannotCreateFile),
370          tfilnam));
371        free(tfilnam);
372        return 1;
373    }
374    free(tfilnam);
375#else /* !TOPS20 */
376#ifdef MTS
377    if (uO.aflag)
378        G.outfile = fopen(G.filename, FOPWT);
379    else
380        G.outfile = fopen(G.filename, FOPW);
381    if (G.outfile == (FILE *)NULL) {
382        Info(slide, 1, ((char *)slide, LoadFarString(CannotCreateFile),
383          FnFilter1(G.filename)));
384        return 1;
385    }
386#else /* !MTS */
387#ifdef DEBUG
388    Info(slide, 1, ((char *)slide,
389      "open_outfile:  doing fopen(%s) for reading\n", FnFilter1(G.filename)));
390    if ((G.outfile = fopen(G.filename, FOPR)) == (FILE *)NULL)
391        Info(slide, 1, ((char *)slide,
392          "open_outfile:  fopen(%s) for reading failed:  does not exist\n",
393          FnFilter1(G.filename)));
394    else {
395        Info(slide, 1, ((char *)slide,
396          "open_outfile:  fopen(%s) for reading succeeded:  file exists\n",
397          FnFilter1(G.filename)));
398        fclose(G.outfile);
399    }
400#endif /* DEBUG */
401#ifdef NOVELL_BUG_FAILSAFE
402    if (G.dne && ((G.outfile = fopen(G.filename, FOPR)) != (FILE *)NULL)) {
403        Info(slide, 0x401, ((char *)slide, LoadFarString(NovellBug),
404          FnFilter1(G.filename)));
405        fclose(G.outfile);
406        return 1;   /* with "./" fix in checkdir(), should never reach here */
407    }
408#endif /* NOVELL_BUG_FAILSAFE */
409    Trace((stderr, "open_outfile:  doing fopen(%s) for writing\n",
410      FnFilter1(G.filename)));
411    if ((G.outfile = fopen(G.filename, FOPW)) == (FILE *)NULL) {
412        Info(slide, 0x401, ((char *)slide, LoadFarString(CannotCreateFile),
413          FnFilter1(G.filename)));
414        return 1;
415    }
416    Trace((stderr, "open_outfile:  fopen(%s) for writing succeeded\n",
417      FnFilter1(G.filename)));
418#endif /* !MTS */
419#endif /* !TOPS20 */
420
421#ifdef USE_FWRITE
422#ifdef DOS_NLM_OS2_W32
423    /* 16-bit MSC: buffer size must be strictly LESS than 32K (WSIZE):  bogus */
424    setbuf(G.outfile, (char *)NULL);   /* make output unbuffered */
425#else /* !DOS_NLM_OS2_W32 */
426#ifndef RISCOS
427#ifdef _IOFBF  /* make output fully buffered (works just about like write()) */
428    setvbuf(G.outfile, (char *)slide, _IOFBF, WSIZE);
429#else
430    setbuf(G.outfile, (char *)slide);
431#endif
432#endif /* !RISCOS */
433#endif /* ?DOS_NLM_OS2_W32 */
434#endif /* USE_FWRITE */
435#ifdef OS2_W32
436    /* preallocate the final file size to prevent file fragmentation */
437    SetFileSize(G.outfile, G.pInfo->uncompr_size);
438#endif
439    return 0;
440
441} /* end function open_outfile() */
442
443#endif /* !TANDEM */
444#endif /* !VMS && !AOS_VS && !CMS_MVS && !MACOS */
445
446
447
448
449
450/*
451 * These functions allow NEXTBYTE to function without needing two bounds
452 * checks.  Call defer_leftover_input() if you ever have filled G.inbuf
453 * by some means other than readbyte(), and you then want to start using
454 * NEXTBYTE.  When going back to processing bytes without NEXTBYTE, call
455 * undefer_input().  For example, extract_or_test_member brackets its
456 * central section that does the decompression with these two functions.
457 * If you need to check the number of bytes remaining in the current
458 * file while using NEXTBYTE, check (G.csize + G.incnt), not G.csize.
459 */
460
461/****************************/
462/* function undefer_input() */
463/****************************/
464
465void undefer_input(__G)
466    __GDEF
467{
468    if (G.incnt > 0)
469        G.csize += G.incnt;
470    if (G.incnt_leftover > 0) {
471        /* We know that "(G.csize < MAXINT)" so we can cast G.csize to int:
472         * This condition was checked when G.incnt_leftover was set > 0 in
473         * defer_leftover_input(), and it is NOT allowed to touch G.csize
474         * before calling undefer_input() when (G.incnt_leftover > 0)
475         * (single exception: see read_byte()'s  "G.csize <= 0" handling) !!
476         */
477        G.incnt = G.incnt_leftover + (int)G.csize;
478        G.inptr = G.inptr_leftover - (int)G.csize;
479        G.incnt_leftover = 0;
480    } else if (G.incnt < 0)
481        G.incnt = 0;
482} /* end function undefer_input() */
483
484
485
486
487
488/***********************************/
489/* function defer_leftover_input() */
490/***********************************/
491
492void defer_leftover_input(__G)
493    __GDEF
494{
495    if ((long)G.incnt > G.csize) {
496        /* (G.csize < MAXINT), we can safely cast it to int !! */
497        if (G.csize < 0L)
498            G.csize = 0L;
499        G.inptr_leftover = G.inptr + (int)G.csize;
500        G.incnt_leftover = G.incnt - (int)G.csize;
501        G.incnt = (int)G.csize;
502    } else
503        G.incnt_leftover = 0;
504    G.csize -= G.incnt;
505} /* end function defer_leftover_input() */
506
507
508
509
510
511/**********************/
512/* Function readbuf() */
513/**********************/
514
515unsigned readbuf(__G__ buf, size)   /* return number of bytes read into buf */
516    __GDEF
517    char *buf;
518    register unsigned size;
519{
520    register unsigned count;
521    unsigned n;
522
523    n = size;
524    while (size) {
525        if (G.incnt <= 0) {
526            if ((G.incnt = read(G.zipfd, (char *)G.inbuf, INBUFSIZ)) == 0)
527                return (n-size);
528            else if (G.incnt < 0) {
529                /* another hack, but no real harm copying same thing twice */
530                (*G.message)((zvoid *)&G,
531                  (uch *)LoadFarString(ReadError),  /* CANNOT use slide */
532                  (ulg)strlen(LoadFarString(ReadError)), 0x401);
533                return 0;  /* discarding some data; better than lock-up */
534            }
535            /* buffer ALWAYS starts on a block boundary:  */
536            G.cur_zipfile_bufstart += INBUFSIZ;
537            G.inptr = G.inbuf;
538        }
539        count = MIN(size, (unsigned)G.incnt);
540        memcpy(buf, G.inptr, count);
541        buf += count;
542        G.inptr += count;
543        G.incnt -= count;
544        size -= count;
545    }
546    return n;
547
548} /* end function readbuf() */
549
550
551
552
553
554/***********************/
555/* Function readbyte() */
556/***********************/
557
558int readbyte(__G)   /* refill inbuf and return a byte if available, else EOF */
559    __GDEF
560{
561    if (G.mem_mode)
562        return EOF;
563    if (G.csize <= 0) {
564        G.csize--;             /* for tests done after exploding */
565        G.incnt = 0;
566        return EOF;
567    }
568    if (G.incnt <= 0) {
569        if ((G.incnt = read(G.zipfd, (char *)G.inbuf, INBUFSIZ)) == 0) {
570            G.incnt = 0;       /* do not allow negative value to affect stuff */
571            return EOF;
572        } else if (G.incnt < 0) {  /* "fail" (abort, retry, ...) returns this */
573            /* another hack, but no real harm copying same thing twice */
574            (*G.message)((zvoid *)&G,
575              (uch *)LoadFarString(ReadError),
576              (ulg)strlen(LoadFarString(ReadError)), 0x401);
577            echon();
578#ifdef WINDLL
579            longjmp(dll_error_return, 1);
580#else
581            DESTROYGLOBALS();
582            EXIT(PK_BADERR);    /* totally bailing; better than lock-up */
583#endif
584        }
585        G.cur_zipfile_bufstart += INBUFSIZ; /* always starts on block bndry */
586        G.inptr = G.inbuf;
587        defer_leftover_input(__G);           /* decrements G.csize */
588    }
589
590#if CRYPT
591    if (G.pInfo->encrypted) {
592        uch *p;
593        int n;
594
595        /* This was previously set to decrypt one byte beyond G.csize, when
596         * incnt reached that far.  GRR said, "but it's required:  why?"  This
597         * was a bug in fillinbuf() -- was it also a bug here?
598         */
599        for (n = G.incnt, p = G.inptr;  n--;  p++)
600            zdecode(*p);
601    }
602#endif /* CRYPT */
603
604    --G.incnt;
605    return *G.inptr++;
606
607} /* end function readbyte() */
608
609
610
611
612
613#ifdef USE_ZLIB
614
615/************************/
616/* Function fillinbuf() */
617/************************/
618
619int fillinbuf(__G) /* like readbyte() except returns number of bytes in inbuf */
620    __GDEF
621{
622    if (G.mem_mode ||
623                  (G.incnt = read(G.zipfd, (char *)G.inbuf, INBUFSIZ)) <= 0)
624        return 0;
625    G.cur_zipfile_bufstart += INBUFSIZ;  /* always starts on a block boundary */
626    G.inptr = G.inbuf;
627    defer_leftover_input(__G);           /* decrements G.csize */
628
629#if CRYPT
630    if (G.pInfo->encrypted) {
631        uch *p;
632        int n;
633
634        for (n = G.incnt, p = G.inptr;  n--;  p++)
635            zdecode(*p);
636    }
637#endif /* CRYPT */
638
639    return G.incnt;
640
641} /* end function fillinbuf() */
642
643#endif /* USE_ZLIB */
644
645
646
647
648
649/************************/
650/* Function seek_zipf() */
651/************************/
652
653int seek_zipf(__G__ abs_offset)
654    __GDEF
655    LONGINT abs_offset;
656{
657/*
658 *  Seek to the block boundary of the block which includes abs_offset,
659 *  then read block into input buffer and set pointers appropriately.
660 *  If block is already in the buffer, just set the pointers.  This function
661 *  is used by do_seekable (process.c), extract_or_test_entrylist (extract.c)
662 *  and do_string (fileio.c).  Also, a slightly modified version is embedded
663 *  within extract_or_test_entrylist (extract.c).  readbyte() and readbuf()
664 *  (fileio.c) are compatible.  NOTE THAT abs_offset is intended to be the
665 *  "proper offset" (i.e., if there were no extra bytes prepended);
666 *  cur_zipfile_bufstart contains the corrected offset.
667 *
668 *  Since seek_zipf() is never used during decompression, it is safe to
669 *  use the slide[] buffer for the error message.
670 *
671 * returns PK error codes:
672 *  PK_BADERR if effective offset in zipfile is negative
673 *  PK_EOF if seeking past end of zipfile
674 *  PK_OK when seek was successful
675 */
676    LONGINT request = abs_offset + G.extra_bytes;
677    LONGINT inbuf_offset = request % INBUFSIZ;
678    LONGINT bufstart = request - inbuf_offset;
679
680    if (request < 0) {
681        Info(slide, 1, ((char *)slide, LoadFarStringSmall(SeekMsg),
682             G.zipfn, LoadFarString(ReportMsg)));
683        return(PK_BADERR);
684    } else if (bufstart != G.cur_zipfile_bufstart) {
685        Trace((stderr,
686          "fpos_zip: abs_offset = %ld, G.extra_bytes = %ld\n",
687          abs_offset, G.extra_bytes));
688#ifdef USE_STRM_INPUT
689        fseek(G.zipfd, (LONGINT)bufstart, SEEK_SET);
690        G.cur_zipfile_bufstart = ftell(G.zipfd);
691#else /* !USE_STRM_INPUT */
692        G.cur_zipfile_bufstart = lseek(G.zipfd, (LONGINT)bufstart, SEEK_SET);
693#endif /* ?USE_STRM_INPUT */
694        Trace((stderr,
695          "       request = %ld, (abs+extra) = %ld, inbuf_offset = %ld\n",
696          request, (abs_offset+G.extra_bytes), inbuf_offset));
697        Trace((stderr, "       bufstart = %ld, cur_zipfile_bufstart = %ld\n",
698          bufstart, G.cur_zipfile_bufstart));
699        if ((G.incnt = read(G.zipfd, (char *)G.inbuf, INBUFSIZ)) <= 0)
700            return(PK_EOF);
701        G.incnt -= (int)inbuf_offset;
702        G.inptr = G.inbuf + (int)inbuf_offset;
703    } else {
704        G.incnt += (G.inptr-G.inbuf) - (int)inbuf_offset;
705        G.inptr = G.inbuf + (int)inbuf_offset;
706    }
707    return(PK_OK);
708} /* end function seek_zipf() */
709
710
711
712
713
714#ifndef VMS  /* for VMS use code in vms.c */
715
716/********************/
717/* Function flush() */   /* returns PK error codes: */
718/********************/   /* if cflag => always 0; PK_DISK if write error */
719
720int flush(__G__ rawbuf, size, unshrink)
721    __GDEF
722    uch *rawbuf;
723    ulg size;
724    int unshrink;
725#if (defined(USE_DEFLATE64) && defined(__16BIT__))
726{
727    int ret;
728
729    /* On 16-bit systems (MSDOS, OS/2 1.x), the standard C library functions
730     * cannot handle writes of 64k blocks at once.  For these systems, the
731     * blocks to flush are split into pieces of 32k or less.
732     */
733    while (size > 0x8000L) {
734        ret = partflush(__G__ rawbuf, 0x8000L, unshrink);
735        if (ret != PK_OK)
736            return ret;
737        size -= 0x8000L;
738        rawbuf += (unsigned)0x8000;
739    }
740    return partflush(__G__ rawbuf, size, unshrink);
741} /* end function flush() */
742
743
744/************************/
745/* Function partflush() */  /* returns PK error codes: */
746/************************/  /* if cflag => always 0; PK_DISK if write error */
747
748static int partflush(__G__ rawbuf, size, unshrink)
749    __GDEF
750    uch *rawbuf;        /* cannot be ZCONST, gets passed to (*G.message)() */
751    ulg size;
752    int unshrink;
753#endif /* USE_DEFLATE64 && __16BIT__ */
754{
755    register uch *p;
756    register uch *q;
757    uch *transbuf;
758#if (defined(SMALL_MEM) || defined(MED_MEM) || defined(VMS_TEXT_CONV))
759    ulg transbufsiz;
760#endif
761    /* static int didCRlast = FALSE;    moved to globals.h */
762
763
764/*---------------------------------------------------------------------------
765    Compute the CRC first; if testing or if disk is full, that's it.
766  ---------------------------------------------------------------------------*/
767
768    G.crc32val = crc32(G.crc32val, rawbuf, (extent)size);
769
770#ifdef DLL
771    if ((G.statreportcb != NULL) &&
772        (*G.statreportcb)(__G__ UZ_ST_IN_PROGRESS, G.zipfn, G.filename, NULL))
773        return IZ_CTRLC;        /* cancel operation by user request */
774#endif
775
776    if (uO.tflag || size == 0L)  /* testing or nothing to write:  all done */
777        return PK_OK;
778
779    if (G.disk_full)
780        return PK_DISK;         /* disk already full:  ignore rest of file */
781
782/*---------------------------------------------------------------------------
783    Write the bytes rawbuf[0..size-1] to the output device, first converting
784    end-of-lines and ASCII/EBCDIC as needed.  If SMALL_MEM or MED_MEM are NOT
785    defined, outbuf is assumed to be at least as large as rawbuf and is not
786    necessarily checked for overflow.
787  ---------------------------------------------------------------------------*/
788
789    if (!G.pInfo->textmode) {   /* write raw binary data */
790        /* GRR:  note that for standard MS-DOS compilers, size argument to
791         * fwrite() can never be more than 65534, so WriteError macro will
792         * have to be rewritten if size can ever be that large.  For now,
793         * never more than 32K.  Also note that write() returns an int, which
794         * doesn't necessarily limit size to 32767 bytes if write() is used
795         * on 16-bit systems but does make it more of a pain; however, because
796         * at least MSC 5.1 has a lousy implementation of fwrite() (as does
797         * DEC Ultrix cc), write() is used anyway.
798         */
799#ifdef DLL
800        if (G.redirect_data)
801            writeToMemory(__G__ rawbuf, (extent)size);
802        else
803#endif
804        if (!uO.cflag && WriteError(rawbuf, size, G.outfile))
805            return disk_error(__G);
806        else if (uO.cflag && (*G.message)((zvoid *)&G, rawbuf, size, 0))
807            return PK_OK;
808    } else {   /* textmode:  aflag is true */
809        if (unshrink) {
810            /* rawbuf = outbuf */
811            transbuf = G.outbuf2;
812#if (defined(SMALL_MEM) || defined(MED_MEM) || defined(VMS_TEXT_CONV))
813            transbufsiz = TRANSBUFSIZ;
814#endif
815        } else {
816            /* rawbuf = slide */
817            transbuf = G.outbuf;
818#if (defined(SMALL_MEM) || defined(MED_MEM) || defined(VMS_TEXT_CONV))
819            transbufsiz = OUTBUFSIZ;
820            Trace((stderr, "\ntransbufsiz = OUTBUFSIZ = %u\n",
821                   (unsigned)OUTBUFSIZ));
822#endif
823        }
824        if (G.newfile) {
825#ifdef VMS_TEXT_CONV
826            if (G.pInfo->hostnum == VMS_ && G.extra_field &&
827                is_vms_varlen_txt(__G__ G.extra_field,
828                                  G.lrec.extra_field_length))
829                G.VMS_line_state = 0;    /* 0: ready to read line length */
830            else
831                G.VMS_line_state = -1;   /* -1: don't treat as VMS text */
832#endif
833            G.didCRlast = FALSE;         /* no previous buffers written */
834            G.newfile = FALSE;
835        }
836
837#ifdef VMS_TEXT_CONV
838        if (G.VMS_line_state >= 0)
839        {
840            /* GRR: really want to check for actual VMS extra field, and
841             *      ideally for variable-length record format */
842/*
843            printf("\n>>>>>> GRR:  file is VMS text and has an extra field\n");
844 */
845
846            p = rawbuf;
847            q = transbuf;
848            while (p < rawbuf+(unsigned)size) {
849                switch (G.VMS_line_state) {
850
851                    /* 0: ready to read line length */
852                    case 0:
853                        G.VMS_line_length = 0;
854                        if (p == rawbuf+(unsigned)size-1) {    /* last char */
855                            G.VMS_line_length = (unsigned)(*p++);
856                            G.VMS_line_state = 1;
857                        } else {
858                            G.VMS_line_length = makeword(p);
859                            p += 2;
860                            G.VMS_line_state = 2;
861                        }
862                        G.VMS_line_pad =
863                               ((G.VMS_line_length & 1) != 0); /* odd */
864                        break;
865
866                    /* 1: read one byte of length, need second */
867                    case 1:
868                        G.VMS_line_length += ((unsigned)(*p++) << 8);
869                        G.VMS_line_state = 2;
870                        break;
871
872                    /* 2: ready to read VMS_line_length chars */
873                    case 2:
874                        {
875                            extent remaining = rawbuf+(unsigned)size-p;
876                            extent outroom;
877
878                            if (G.VMS_line_length < remaining) {
879                                remaining = G.VMS_line_length;
880                                G.VMS_line_state = 3;
881                            }
882
883                            outroom = transbuf+(unsigned)transbufsiz-q;
884                            if (remaining >= outroom) {
885                                remaining -= outroom;
886                                for (;outroom > 0; p++, outroom--)
887                                    *q++ = native(*p);
888#ifdef DLL
889                                if (G.redirect_data)
890                                    writeToMemory(__G__ transbuf,
891                                      (extent)(q-transbuf));
892                                else
893#endif
894                                if (!uO.cflag && WriteError(transbuf,
895                                    (extent)(q-transbuf), G.outfile))
896                                    return disk_error(__G);
897                                else if (uO.cflag && (*G.message)((zvoid *)&G,
898                                         transbuf, (ulg)(q-transbuf), 0))
899                                    return PK_OK;
900                                q = transbuf;
901                                /* fall through to normal case */
902                            }
903                            G.VMS_line_length -= remaining;
904                            for (;remaining > 0; p++, remaining--)
905                                *q++ = native(*p);
906                        }
907                        break;
908
909                    /* 3: ready to PutNativeEOL */
910                    case 3:
911                        if (q > transbuf+(unsigned)transbufsiz-lenEOL) {
912#ifdef DLL
913                            if (G.redirect_data)
914                                writeToMemory(__G__ transbuf,
915                                  (extent)(q-transbuf));
916                            else
917#endif
918                            if (!uO.cflag &&
919                                WriteError(transbuf, (extent)(q-transbuf),
920                                  G.outfile))
921                                return disk_error(__G);
922                            else if (uO.cflag && (*G.message)((zvoid *)&G,
923                                     transbuf, (ulg)(q-transbuf), 0))
924                                return PK_OK;
925                            q = transbuf;
926                        }
927                        PutNativeEOL
928                        G.VMS_line_state = G.VMS_line_pad ? 4 : 0;
929                        break;
930
931                    /* 4: ready to read pad byte */
932                    case 4:
933                        ++p;
934                        G.VMS_line_state = 0;
935                        break;
936                }
937            } /* end while */
938
939        } else
940#endif /* VMS_TEXT_CONV */
941
942    /*-----------------------------------------------------------------------
943        Algorithm:  CR/LF => native; lone CR => native; lone LF => native.
944        This routine is only for non-raw-VMS, non-raw-VM/CMS files (i.e.,
945        stream-oriented files, not record-oriented).
946      -----------------------------------------------------------------------*/
947
948        /* else not VMS text */ {
949            p = rawbuf;
950            if (*p == LF && G.didCRlast)
951                ++p;
952            G.didCRlast = FALSE;
953            for (q = transbuf;  p < rawbuf+(unsigned)size;  ++p) {
954                if (*p == CR) {           /* lone CR or CR/LF: treat as EOL  */
955                    PutNativeEOL
956                    if (p == rawbuf+(unsigned)size-1) /* last char in buffer */
957                        G.didCRlast = TRUE;
958                    else if (p[1] == LF)  /* get rid of accompanying LF */
959                        ++p;
960                } else if (*p == LF)      /* lone LF */
961                    PutNativeEOL
962                else
963#ifndef DOS_FLX_OS2_W32
964                if (*p != CTRLZ)          /* lose all ^Z's */
965#endif
966                    *q++ = native(*p);
967
968#if (defined(SMALL_MEM) || defined(MED_MEM))
969# if (lenEOL == 1)   /* don't check unshrink:  both buffers small but equal */
970                if (!unshrink)
971# endif
972                    /* check for danger of buffer overflow and flush */
973                    if (q > transbuf+(unsigned)transbufsiz-lenEOL) {
974                        Trace((stderr,
975                          "p - rawbuf = %u   q-transbuf = %u   size = %lu\n",
976                          (unsigned)(p-rawbuf), (unsigned)(q-transbuf), size));
977                        if (!uO.cflag && WriteError(transbuf,
978                            (extent)(q-transbuf), G.outfile))
979                            return disk_error(__G);
980                        else if (uO.cflag && (*G.message)((zvoid *)&G,
981                                 transbuf, (ulg)(q-transbuf), 0))
982                            return PK_OK;
983                        q = transbuf;
984                        continue;
985                    }
986#endif /* SMALL_MEM || MED_MEM */
987            }
988        }
989
990    /*-----------------------------------------------------------------------
991        Done translating:  write whatever we've got to file (or screen).
992      -----------------------------------------------------------------------*/
993
994        Trace((stderr, "p - rawbuf = %u   q-transbuf = %u   size = %lu\n",
995          (unsigned)(p-rawbuf), (unsigned)(q-transbuf), size));
996        if (q > transbuf) {
997#ifdef DLL
998            if (G.redirect_data)
999                writeToMemory(__G__ transbuf, (extent)(q-transbuf));
1000            else
1001#endif
1002            if (!uO.cflag && WriteError(transbuf, (extent)(q-transbuf),
1003                G.outfile))
1004                return disk_error(__G);
1005            else if (uO.cflag && (*G.message)((zvoid *)&G, transbuf,
1006                (ulg)(q-transbuf), 0))
1007                return PK_OK;
1008        }
1009    }
1010
1011    return PK_OK;
1012
1013} /* end function flush() [resp. partflush() for 16-bit Deflate64 support] */
1014
1015
1016
1017
1018
1019#ifdef VMS_TEXT_CONV
1020
1021/********************************/
1022/* Function is_vms_varlen_txt() */
1023/********************************/
1024
1025static int is_vms_varlen_txt(__G__ ef_buf, ef_len)
1026    __GDEF
1027    uch *ef_buf;        /* buffer containing extra field */
1028    unsigned ef_len;    /* total length of extra field */
1029{
1030    unsigned eb_id;
1031    unsigned eb_len;
1032    uch *eb_data;
1033    unsigned eb_datlen;
1034#define VMSREC_C_UNDEF  0
1035#define VMSREC_C_VAR    2
1036    uch vms_rectype = VMSREC_C_UNDEF;
1037    uch vms_fileorg = 0;
1038
1039#define VMSPK_ITEMID            0
1040#define VMSPK_ITEMLEN           2
1041#define VMSPK_ITEMHEADSZ        4
1042
1043#define VMSATR_C_RECATTR        4
1044#define VMS_FABSIG              0x42414656      /* "VFAB" */
1045/* offsets of interesting fields in VMS fabdef structure */
1046#define VMSFAB_B_RFM            31      /* record format byte */
1047#define VMSFAB_B_ORG            29      /* file organization byte */
1048
1049    if (ef_len == 0 || ef_buf == NULL)
1050        return FALSE;
1051
1052    while (ef_len >= EB_HEADSIZE) {
1053        eb_id = makeword(EB_ID + ef_buf);
1054        eb_len = makeword(EB_LEN + ef_buf);
1055
1056        if (eb_len > (ef_len - EB_HEADSIZE)) {
1057            /* discovered some extra field inconsistency! */
1058            Trace((stderr,
1059              "is_vms_varlen_txt: block length %u > rest ef_size %u\n", eb_len,
1060              ef_len - EB_HEADSIZE));
1061            break;
1062        }
1063
1064        switch (eb_id) {
1065          case EF_PKVMS:
1066            /* The PKVMS e.f. raw data part consists of:
1067             * a) 4 bytes CRC checksum
1068             * b) list of uncompressed variable-length data items
1069             * Each data item is introduced by a fixed header
1070             *  - 2 bytes data type ID
1071             *  - 2 bytes <size> of data
1072             *  - <size> bytes of actual attribute data
1073             */
1074
1075            /* get pointer to start of data and its total length */
1076            eb_data = ef_buf+(EB_HEADSIZE+4);
1077            eb_datlen = eb_len-4;
1078
1079            /* test the CRC checksum */
1080            if (makelong(ef_buf+EB_HEADSIZE) !=
1081                crc32(CRCVAL_INITIAL, eb_data, (extent)eb_datlen))
1082            {
1083                Info(slide, 1, ((char *)slide,
1084                  "[Warning: CRC error, discarding PKWARE extra field]\n"));
1085                /* skip over the data analysis code */
1086                break;
1087            }
1088
1089            /* scan through the attribute data items */
1090            while (eb_datlen > 4)
1091            {
1092                unsigned fldsize = makeword(&eb_data[VMSPK_ITEMLEN]);
1093
1094                /* check the item type word */
1095                switch (makeword(&eb_data[VMSPK_ITEMID])) {
1096                  case VMSATR_C_RECATTR:
1097                    /* we have found the (currently only) interesting
1098                     * data item */
1099                    if (fldsize >= 1) {
1100                        vms_rectype = eb_data[VMSPK_ITEMHEADSZ] & 15;
1101                        vms_fileorg = eb_data[VMSPK_ITEMHEADSZ] >> 4;
1102                    }
1103                    break;
1104                  default:
1105                    break;
1106                }
1107                /* skip to next data item */
1108                eb_datlen -= fldsize + VMSPK_ITEMHEADSZ;
1109                eb_data += fldsize + VMSPK_ITEMHEADSZ;
1110            }
1111            break;
1112
1113          case EF_IZVMS:
1114            if (makelong(ef_buf+EB_HEADSIZE) == VMS_FABSIG) {
1115                if ((eb_data = extract_izvms_block(__G__
1116                                                   ef_buf+EB_HEADSIZE, eb_len,
1117                                                   &eb_datlen, NULL, 0))
1118                    != NULL)
1119                {
1120                    if (eb_datlen >= VMSFAB_B_RFM+1) {
1121                        vms_rectype = eb_data[VMSFAB_B_RFM] & 15;
1122                        vms_fileorg = eb_data[VMSFAB_B_ORG] >> 4;
1123                    }
1124                    free(eb_data);
1125                }
1126            }
1127            break;
1128
1129          default:
1130            break;
1131        }
1132
1133        /* Skip this extra field block */
1134        ef_buf += (eb_len + EB_HEADSIZE);
1135        ef_len -= (eb_len + EB_HEADSIZE);
1136    }
1137
1138    return (vms_rectype == VMSREC_C_VAR);
1139
1140} /* end function is_vms_varlen_txtfile() */
1141
1142#endif /* VMS_TEXT_CONV */
1143
1144
1145
1146
1147/*************************/
1148/* Function disk_error() */
1149/*************************/
1150
1151static int disk_error(__G)
1152    __GDEF
1153{
1154    /* OK to use slide[] here because this file is finished regardless */
1155    Info(slide, 0x4a1, ((char *)slide, LoadFarString(DiskFullQuery),
1156      FnFilter1(G.filename)));
1157
1158#ifndef WINDLL
1159    fgets(G.answerbuf, 9, stdin);
1160    if (*G.answerbuf == 'y')   /* stop writing to this file */
1161        G.disk_full = 1;       /*  (outfile bad?), but new OK */
1162    else
1163#endif
1164        G.disk_full = 2;       /* no:  exit program */
1165
1166    return PK_DISK;
1167
1168} /* end function disk_error() */
1169
1170#endif /* !VMS */
1171
1172
1173
1174
1175
1176/*****************************/
1177/* Function UzpMessagePrnt() */
1178/*****************************/
1179
1180int UZ_EXP UzpMessagePrnt(pG, buf, size, flag)
1181    zvoid *pG;   /* globals struct:  always passed */
1182    uch *buf;    /* preformatted string to be printed */
1183    ulg size;    /* length of string (may include nulls) */
1184    int flag;    /* flag bits */
1185{
1186    /* IMPORTANT NOTE:
1187     *    The name of the first parameter of UzpMessagePrnt(), which passes
1188     *    the "Uz_Globs" address, >>> MUST <<< be identical to the string
1189     *    expansion of the __G__ macro in the REENTRANT case (see globals.h).
1190     *    This name identity is mandatory for the LoadFarString() macro
1191     *    (in the SMALL_MEM case) !!!
1192     */
1193    int error;
1194    uch *q=buf, *endbuf=buf+(unsigned)size;
1195#ifdef MORE
1196    uch *p=buf;
1197#if (defined(SCREENWIDTH) && defined(SCREENLWRAP))
1198    int islinefeed = FALSE;
1199#endif
1200#endif
1201    FILE *outfp;
1202
1203
1204/*---------------------------------------------------------------------------
1205    These tests are here to allow fine-tuning of UnZip's output messages,
1206    but none of them will do anything without setting the appropriate bit
1207    in the flag argument of every Info() statement which is to be turned
1208    *off*.  That is, all messages are currently turned on for all ports.
1209    To turn off *all* messages, use the UzpMessageNull() function instead
1210    of this one.
1211  ---------------------------------------------------------------------------*/
1212
1213#if (defined(OS2) && defined(DLL))
1214    if (MSG_NO_DLL2(flag))  /* if OS/2 DLL bit is set, do NOT print this msg */
1215        return 0;
1216#endif
1217#ifdef WINDLL
1218    if (MSG_NO_WDLL(flag))
1219        return 0;
1220#endif
1221#ifdef WINDLL
1222    if (MSG_NO_WGUI(flag))
1223        return 0;
1224#endif
1225/*
1226#ifdef ACORN_GUI
1227    if (MSG_NO_AGUI(flag))
1228        return 0;
1229#endif
1230 */
1231#ifdef DLL                 /* don't display message if data is redirected */
1232    if (((Uz_Globs *)pG)->redirect_data &&
1233        !((Uz_Globs *)pG)->redirect_text)
1234        return 0;
1235#endif
1236
1237    if (MSG_STDERR(flag) && !((Uz_Globs *)pG)->UzO.tflag)
1238        outfp = (FILE *)stderr;
1239    else
1240        outfp = (FILE *)stdout;
1241
1242#ifdef QUERY_TRNEWLN
1243    /* some systems require termination of query prompts with '\n' to force
1244     * immediate display */
1245    if (MSG_MNEWLN(flag)) {   /* assumes writable buffer (e.g., slide[]) */
1246        *endbuf++ = '\n';     /*  with room for one more char at end of buf */
1247        ++size;               /*  (safe assumption:  only used for four */
1248    }                         /*  short queries in extract.c and fileio.c) */
1249#endif
1250
1251    if (MSG_TNEWLN(flag)) {   /* again assumes writable buffer:  fragile... */
1252        if ((!size && !((Uz_Globs *)pG)->sol) ||
1253            (size && (endbuf[-1] != '\n')))
1254        {
1255            *endbuf++ = '\n';
1256            ++size;
1257        }
1258    }
1259
1260#ifdef MORE
1261# ifdef SCREENSIZE
1262    /* room for --More-- and one line of overlap: */
1263#  if (defined(SCREENWIDTH) && defined(SCREENLWRAP))
1264    SCREENSIZE(&((Uz_Globs *)pG)->height, &((Uz_Globs *)pG)->width);
1265#  else
1266    SCREENSIZE(&((Uz_Globs *)pG)->height, (int *)NULL);
1267#  endif
1268    ((Uz_Globs *)pG)->height -= 2;
1269# else
1270    /* room for --More-- and one line of overlap: */
1271    ((Uz_Globs *)pG)->height = SCREENLINES - 2;
1272#  if (defined(SCREENWIDTH) && defined(SCREENLWRAP))
1273    ((Uz_Globs *)pG)->width = SCREENWIDTH;
1274#  endif
1275# endif
1276#endif /* MORE */
1277
1278    if (MSG_LNEWLN(flag) && !((Uz_Globs *)pG)->sol) {
1279        /* not at start of line:  want newline */
1280#ifdef OS2DLL
1281        if (!((Uz_Globs *)pG)->redirect_text) {
1282#endif
1283            putc('\n', outfp);
1284            fflush(outfp);
1285#ifdef MORE
1286            if (((Uz_Globs *)pG)->M_flag)
1287            {
1288#if (defined(SCREENWIDTH) && defined(SCREENLWRAP))
1289                ((Uz_Globs *)pG)->chars = 0;
1290#endif
1291                ++((Uz_Globs *)pG)->numlines;
1292                ++((Uz_Globs *)pG)->lines;
1293                if (((Uz_Globs *)pG)->lines >= ((Uz_Globs *)pG)->height)
1294                    (*((Uz_Globs *)pG)->mpause)((zvoid *)pG,
1295                      LoadFarString(MorePrompt), 1);
1296            }
1297#endif /* MORE */
1298            if (MSG_STDERR(flag) && ((Uz_Globs *)pG)->UzO.tflag &&
1299                !isatty(1) && isatty(2))
1300            {
1301                /* error output from testing redirected:  also send to stderr */
1302                putc('\n', stderr);
1303                fflush(stderr);
1304            }
1305#ifdef OS2DLL
1306        } else
1307           REDIRECTC('\n');
1308#endif
1309        ((Uz_Globs *)pG)->sol = TRUE;
1310    }
1311
1312    /* put zipfile name, filename and/or error/warning keywords here */
1313
1314#ifdef MORE
1315    if (((Uz_Globs *)pG)->M_flag
1316#ifdef OS2DLL
1317         && !((Uz_Globs *)pG)->redirect_text
1318#endif
1319                                                 )
1320    {
1321        while (p < endbuf) {
1322            if (*p == '\n') {
1323#if (defined(SCREENWIDTH) && defined(SCREENLWRAP))
1324                islinefeed = TRUE;
1325            } else if (SCREENLWRAP) {
1326                if (*p == '\r') {
1327                    ((Uz_Globs *)pG)->chars = 0;
1328                } else {
1329#  ifdef TABSIZE
1330                    if (*p == '\t')
1331                        ((Uz_Globs *)pG)->chars +=
1332                            (TABSIZE - (((Uz_Globs *)pG)->chars % TABSIZE));
1333                    else
1334#  endif
1335                        ++((Uz_Globs *)pG)->chars;
1336
1337                    if (((Uz_Globs *)pG)->chars >= ((Uz_Globs *)pG)->width)
1338                        islinefeed = TRUE;
1339                }
1340            }
1341            if (islinefeed) {
1342                islinefeed = FALSE;
1343                ((Uz_Globs *)pG)->chars = 0;
1344#endif /* (SCREENWIDTH && SCREEN_LWRAP) */
1345                ++((Uz_Globs *)pG)->numlines;
1346                ++((Uz_Globs *)pG)->lines;
1347                if (((Uz_Globs *)pG)->lines >= ((Uz_Globs *)pG)->height)
1348                {
1349                    if ((error = WriteError(q, p-q+1, outfp)) != 0)
1350                        return error;
1351                    fflush(outfp);
1352                    ((Uz_Globs *)pG)->sol = TRUE;
1353                    q = p + 1;
1354                    (*((Uz_Globs *)pG)->mpause)((zvoid *)pG,
1355                      LoadFarString(MorePrompt), 1);
1356                }
1357            }
1358            INCSTR(p);
1359        } /* end while */
1360        size = (ulg)(p - q);   /* remaining text */
1361    }
1362#endif /* MORE */
1363
1364    if (size) {
1365#ifdef OS2DLL
1366        if (!((Uz_Globs *)pG)->redirect_text) {
1367#endif
1368            if ((error = WriteError(q, size, outfp)) != 0)
1369                return error;
1370            fflush(outfp);
1371            if (MSG_STDERR(flag) && ((Uz_Globs *)pG)->UzO.tflag &&
1372                !isatty(1) && isatty(2))
1373            {
1374                /* error output from testing redirected:  also send to stderr */
1375                if ((error = WriteError(q, size, stderr)) != 0)
1376                    return error;
1377                fflush(stderr);
1378            }
1379#ifdef OS2DLL
1380        } else {                /* GRR:  this is ugly:  hide with macro */
1381            if ((error = REDIRECTPRINT(q, size)) != 0)
1382                return error;
1383        }
1384#endif /* OS2DLL */
1385        ((Uz_Globs *)pG)->sol = (endbuf[-1] == '\n');
1386    }
1387    return 0;
1388
1389} /* end function UzpMessagePrnt() */
1390
1391
1392
1393
1394
1395#ifdef DLL
1396
1397/*****************************/
1398/* Function UzpMessageNull() */  /* convenience routine for no output at all */
1399/*****************************/
1400
1401int UZ_EXP UzpMessageNull(pG, buf, size, flag)
1402    zvoid *pG;    /* globals struct:  always passed */
1403    uch *buf;     /* preformatted string to be printed */
1404    ulg size;     /* length of string (may include nulls) */
1405    int flag;     /* flag bits */
1406{
1407    return 0;
1408
1409} /* end function UzpMessageNull() */
1410
1411#endif /* DLL */
1412
1413
1414
1415
1416
1417/***********************/
1418/* Function UzpInput() */   /* GRR:  this is a placeholder for now */
1419/***********************/
1420
1421int UZ_EXP UzpInput(pG, buf, size, flag)
1422    zvoid *pG;    /* globals struct:  always passed */
1423    uch *buf;     /* preformatted string to be printed */
1424    int *size;    /* (address of) size of buf and of returned string */
1425    int flag;     /* flag bits (bit 0: no echo) */
1426{
1427    /* tell picky compilers to shut up about "unused variable" warnings */
1428    pG = pG; buf = buf; flag = flag;
1429
1430    *size = 0;
1431    return 0;
1432
1433} /* end function UzpInput() */
1434
1435
1436
1437
1438
1439#if (!defined(WINDLL) && !defined(MACOS))
1440
1441/***************************/
1442/* Function UzpMorePause() */
1443/***************************/
1444
1445void UZ_EXP UzpMorePause(pG, prompt, flag)
1446    zvoid *pG;            /* globals struct:  always passed */
1447    ZCONST char *prompt;  /* "--More--" prompt */
1448    int flag;             /* 0 = any char OK; 1 = accept only '\n', ' ', q */
1449{
1450    uch c;
1451
1452/*---------------------------------------------------------------------------
1453    Print a prompt and wait for the user to press a key, then erase prompt
1454    if possible.
1455  ---------------------------------------------------------------------------*/
1456
1457    if (!((Uz_Globs *)pG)->sol)
1458        fprintf(stderr, "\n");
1459    /* numlines may or may not be used: */
1460    fprintf(stderr, prompt, ((Uz_Globs *)pG)->numlines);
1461    fflush(stderr);
1462    if (flag & 1) {
1463        do {
1464            c = (uch)FGETCH(0);
1465        } while (
1466#ifdef THEOS
1467                 c != 17 &&     /* standard QUIT key */
1468#endif
1469                 c != '\r' && c != '\n' && c != ' ' && c != 'q' && c != 'Q');
1470    } else
1471        c = (uch)FGETCH(0);
1472
1473    /* newline was not echoed, so cover up prompt line */
1474    fprintf(stderr, LoadFarString(HidePrompt));
1475    fflush(stderr);
1476
1477    if (
1478#ifdef THEOS
1479        (c == 17) ||            /* standard QUIT key */
1480#endif
1481        (ToLower(c) == 'q')) {
1482        DESTROYGLOBALS();
1483        EXIT(PK_COOL);
1484    }
1485
1486    ((Uz_Globs *)pG)->sol = TRUE;
1487
1488#ifdef MORE
1489    /* space for another screen, enter for another line. */
1490    if ((flag & 1) && c == ' ')
1491        ((Uz_Globs *)pG)->lines = 0;
1492#endif /* MORE */
1493
1494} /* end function UzpMorePause() */
1495
1496#endif /* !WINDLL && !MACOS */
1497
1498
1499
1500
1501#ifndef WINDLL
1502
1503/**************************/
1504/* Function UzpPassword() */
1505/**************************/
1506
1507int UZ_EXP UzpPassword (pG, rcnt, pwbuf, size, zfn, efn)
1508    zvoid *pG;         /* pointer to UnZip's internal global vars */
1509    int *rcnt;         /* retry counter */
1510    char *pwbuf;       /* buffer for password */
1511    int size;          /* size of password buffer */
1512    ZCONST char *zfn;  /* name of zip archive */
1513    ZCONST char *efn;  /* name of archive entry being processed */
1514{
1515#if CRYPT
1516    int r = IZ_PW_ENTERED;
1517    char *m;
1518    char *prompt;
1519
1520#ifndef REENTRANT
1521    /* tell picky compilers to shut up about "unused variable" warnings */
1522    pG = pG;
1523#endif
1524
1525    if (*rcnt == 0) {           /* First call for current entry */
1526        *rcnt = 2;
1527        if ((prompt = (char *)malloc(2*FILNAMSIZ + 15)) != (char *)NULL) {
1528            sprintf(prompt, LoadFarString(PasswPrompt),
1529                    FnFilter1(zfn), FnFilter2(efn));
1530            m = prompt;
1531        } else
1532            m = (char *)LoadFarString(PasswPrompt2);
1533    } else {                    /* Retry call, previous password was wrong */
1534        (*rcnt)--;
1535        prompt = NULL;
1536        m = (char *)LoadFarString(PasswRetry);
1537    }
1538
1539    m = getp(__G__ m, pwbuf, size);
1540    if (prompt != (char *)NULL) {
1541        free(prompt);
1542    }
1543    if (m == (char *)NULL) {
1544        r = IZ_PW_ERROR;
1545    }
1546    else if (*pwbuf == '\0') {
1547        r = IZ_PW_CANCELALL;
1548    }
1549    return r;
1550
1551#else /* !CRYPT */
1552    /* tell picky compilers to shut up about "unused variable" warnings */
1553    pG = pG; rcnt = rcnt; pwbuf = pwbuf; size = size; zfn = zfn; efn = efn;
1554
1555    return IZ_PW_ERROR;  /* internal error; function should never get called */
1556#endif /* ?CRYPT */
1557
1558} /* end function UzpPassword() */
1559
1560
1561
1562
1563
1564/**********************/
1565/* Function handler() */
1566/**********************/
1567
1568void handler(signal)   /* upon interrupt, turn on echo and exit cleanly */
1569    int signal;
1570{
1571    GETGLOBALS();
1572
1573#if !(defined(SIGBUS) || defined(SIGSEGV))      /* add a newline if not at */
1574    (*G.message)((zvoid *)&G, slide, 0L, 0x41); /*  start of line (to stderr; */
1575#endif                                          /*  slide[] should be safe) */
1576
1577    echon();
1578
1579#ifdef SIGBUS
1580    if (signal == SIGBUS) {
1581        Info(slide, 0x421, ((char *)slide, LoadFarString(ZipfileCorrupt),
1582          "bus error"));
1583        DESTROYGLOBALS();
1584        EXIT(PK_BADERR);
1585    }
1586#endif /* SIGBUS */
1587
1588#ifdef SIGSEGV
1589    if (signal == SIGSEGV) {
1590        Info(slide, 0x421, ((char *)slide, LoadFarString(ZipfileCorrupt),
1591          "segmentation violation"));
1592        DESTROYGLOBALS();
1593        EXIT(PK_BADERR);
1594    }
1595#endif /* SIGSEGV */
1596
1597    /* probably ctrl-C */
1598    DESTROYGLOBALS();
1599#if defined(AMIGA) && defined(__SASC)
1600    _abort();
1601#endif
1602    EXIT(IZ_CTRLC);       /* was EXIT(0), then EXIT(PK_ERR) */
1603}
1604
1605#endif /* !WINDLL */
1606
1607
1608
1609
1610#if (!defined(VMS) && !defined(CMS_MVS))
1611#if (!defined(OS2) || defined(TIMESTAMP))
1612
1613#if (!defined(HAVE_MKTIME) || defined(WIN32))
1614/* also used in amiga/filedate.c and win32/win32.c */
1615ZCONST ush ydays[] =
1616    { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 };
1617#endif
1618
1619/*******************************/
1620/* Function dos_to_unix_time() */ /* used for freshening/updating/timestamps */
1621/*******************************/
1622
1623time_t dos_to_unix_time(dosdatetime)
1624    ulg dosdatetime;
1625{
1626    time_t m_time;
1627
1628#ifdef HAVE_MKTIME
1629
1630    ZCONST time_t now = time(NULL);
1631    struct tm *tm;
1632#   define YRBASE  1900
1633
1634    tm = localtime(&now);
1635    tm->tm_isdst = -1;          /* let mktime determine if DST is in effect */
1636
1637    /* dissect date */
1638    tm->tm_year = ((int)(dosdatetime >> 25) & 0x7f) + (1980 - YRBASE);
1639    tm->tm_mon  = ((int)(dosdatetime >> 21) & 0x0f) - 1;
1640    tm->tm_mday = ((int)(dosdatetime >> 16) & 0x1f);
1641
1642    /* dissect time */
1643    tm->tm_hour = (int)((unsigned)dosdatetime >> 11) & 0x1f;
1644    tm->tm_min  = (int)((unsigned)dosdatetime >> 5) & 0x3f;
1645    tm->tm_sec  = (int)((unsigned)dosdatetime << 1) & 0x3e;
1646
1647    m_time = mktime(tm);
1648    NATIVE_TO_TIMET(m_time)     /* NOP unless MSC 7.0 or Macintosh */
1649    TTrace((stderr, "  final m_time  =       %lu\n", (ulg)m_time));
1650
1651#else /* !HAVE_MKTIME */
1652
1653    int yr, mo, dy, hh, mm, ss;
1654#ifdef TOPS20
1655#   define YRBASE  1900
1656    struct tmx *tmx;
1657    char temp[20];
1658#else /* !TOPS20 */
1659#   define YRBASE  1970
1660    int leap;
1661    unsigned days;
1662    struct tm *tm;
1663#if (!defined(MACOS) && !defined(RISCOS) && !defined(QDOS) && !defined(TANDEM))
1664#ifdef WIN32
1665    TIME_ZONE_INFORMATION tzinfo;
1666    DWORD res;
1667#else /* ! WIN32 */
1668#ifndef BSD4_4   /* GRR:  change to !defined(MODERN) ? */
1669#if (defined(BSD) || defined(MTS) || defined(__GO32__))
1670    struct timeb tbp;
1671#else /* !(BSD || MTS || __GO32__) */
1672#ifdef DECLARE_TIMEZONE
1673    extern time_t timezone;
1674#endif
1675#endif /* ?(BSD || MTS || __GO32__) */
1676#endif /* !BSD4_4 */
1677#endif /* ?WIN32 */
1678#endif /* !MACOS && !RISCOS && !QDOS && !TANDEM */
1679#endif /* ?TOPS20 */
1680
1681
1682    /* dissect date */
1683    yr = ((int)(dosdatetime >> 25) & 0x7f) + (1980 - YRBASE);
1684    mo = ((int)(dosdatetime >> 21) & 0x0f) - 1;
1685    dy = ((int)(dosdatetime >> 16) & 0x1f) - 1;
1686
1687    /* dissect time */
1688    hh = (int)((unsigned)dosdatetime >> 11) & 0x1f;
1689    mm = (int)((unsigned)dosdatetime >> 5) & 0x3f;
1690    ss = (int)((unsigned)dosdatetime & 0x1f) * 2;
1691
1692#ifdef TOPS20
1693    tmx = (struct tmx *)malloc(sizeof(struct tmx));
1694    sprintf (temp, "%02d/%02d/%02d %02d:%02d:%02d", mo+1, dy+1, yr, hh, mm, ss);
1695    time_parse(temp, tmx, (char *)0);
1696    m_time = time_make(tmx);
1697    free(tmx);
1698
1699#else /* !TOPS20 */
1700
1701/*---------------------------------------------------------------------------
1702    Calculate the number of seconds since the epoch, usually 1 January 1970.
1703  ---------------------------------------------------------------------------*/
1704
1705    /* leap = # of leap yrs from YRBASE up to but not including current year */
1706    leap = ((yr + YRBASE - 1) / 4);   /* leap year base factor */
1707
1708    /* calculate days from BASE to this year and add expired days this year */
1709    days = (yr * 365) + (leap - 492) + ydays[mo];
1710
1711    /* if year is a leap year and month is after February, add another day */
1712    if ((mo > 1) && ((yr+YRBASE)%4 == 0) && ((yr+YRBASE) != 2100))
1713        ++days;                 /* OK through 2199 */
1714
1715    /* convert date & time to seconds relative to 00:00:00, 01/01/YRBASE */
1716    m_time = (time_t)(((unsigned long)days + dy) * 86400L +
1717                      (unsigned long)hh * 3600L +
1718                      (unsigned long)(mm * 60 + ss));
1719      /* - 1;   MS-DOS times always rounded up to nearest even second */
1720    TTrace((stderr, "dos_to_unix_time:\n"));
1721    TTrace((stderr, "  m_time before timezone = %lu\n", (ulg)m_time));
1722
1723/*---------------------------------------------------------------------------
1724    Adjust for local standard timezone offset.
1725  ---------------------------------------------------------------------------*/
1726
1727#if (!defined(MACOS) && !defined(RISCOS) && !defined(QDOS) && !defined(TANDEM))
1728#ifdef WIN32
1729    /* account for timezone differences */
1730    res = GetTimeZoneInformation(&tzinfo);
1731    if (res != TIME_ZONE_ID_INVALID)
1732    {
1733    m_time += 60*(tzinfo.Bias);
1734#else /* !WIN32 */
1735#if (defined(BSD) || defined(MTS) || defined(__GO32__))
1736#ifdef BSD4_4
1737    if ( (dosdatetime >= DOSTIME_2038_01_18) &&
1738         (m_time < (time_t)0x70000000L) )
1739        m_time = U_TIME_T_MAX;  /* saturate in case of (unsigned) overflow */
1740    if (m_time < (time_t)0L)    /* a converted DOS time cannot be negative */
1741        m_time = S_TIME_T_MAX;  /*  -> saturate at max signed time_t value */
1742    if ((tm = localtime(&m_time)) != (struct tm *)NULL)
1743        m_time -= tm->tm_gmtoff;                /* sec. EAST of GMT: subtr. */
1744#else /* !(BSD4_4 */
1745    ftime(&tbp);                                /* get `timezone' */
1746    m_time += tbp.timezone * 60L;               /* seconds WEST of GMT:  add */
1747#endif /* ?(BSD4_4 || __EMX__) */
1748#else /* !(BSD || MTS || __GO32__) */
1749    /* tzset was already called at start of process_zipfiles() */
1750    /* tzset(); */              /* set `timezone' variable */
1751#if (!defined(__BEOS__) && !defined(__HAIKU__))                /* BeOS DR8 has no timezones... */
1752    m_time += timezone;         /* seconds WEST of GMT:  add */
1753#endif
1754#endif /* ?(BSD || MTS || __GO32__) */
1755#endif /* ?WIN32 */
1756    TTrace((stderr, "  m_time after timezone =  %lu\n", (ulg)m_time));
1757
1758/*---------------------------------------------------------------------------
1759    Adjust for local daylight savings (summer) time.
1760  ---------------------------------------------------------------------------*/
1761
1762#ifndef BSD4_4  /* (DST already added to tm_gmtoff, so skip tm_isdst) */
1763    if ( (dosdatetime >= DOSTIME_2038_01_18) &&
1764         (m_time < (time_t)0x70000000L) )
1765        m_time = U_TIME_T_MAX;  /* saturate in case of (unsigned) overflow */
1766    if (m_time < (time_t)0L)    /* a converted DOS time cannot be negative */
1767        m_time = S_TIME_T_MAX;  /*  -> saturate at max signed time_t value */
1768    TIMET_TO_NATIVE(m_time)     /* NOP unless MSC 7.0 or Macintosh */
1769    if (((tm = localtime((time_t *)&m_time)) != NULL) && tm->tm_isdst)
1770#ifdef WIN32
1771        m_time += 60L * tzinfo.DaylightBias;    /* adjust with DST bias */
1772    else
1773        m_time += 60L * tzinfo.StandardBias;    /* add StdBias (normally 0) */
1774#else
1775        m_time -= 60L * 60L;    /* adjust for daylight savings time */
1776#endif
1777    NATIVE_TO_TIMET(m_time)     /* NOP unless MSC 7.0 or Macintosh */
1778    TTrace((stderr, "  m_time after DST =       %lu\n", (ulg)m_time));
1779#endif /* !BSD4_4 */
1780#ifdef WIN32
1781    }
1782#endif
1783#endif /* !MACOS && !RISCOS && !QDOS && !TANDEM */
1784#endif /* ?TOPS20 */
1785
1786#endif /* ?HAVE_MKTIME */
1787
1788    if ( (dosdatetime >= DOSTIME_2038_01_18) &&
1789         (m_time < (time_t)0x70000000L) )
1790        m_time = U_TIME_T_MAX;  /* saturate in case of (unsigned) overflow */
1791    if (m_time < (time_t)0L)    /* a converted DOS time cannot be negative */
1792        m_time = S_TIME_T_MAX;  /*  -> saturate at max signed time_t value */
1793
1794    return m_time;
1795
1796} /* end function dos_to_unix_time() */
1797
1798#endif /* !OS2 || TIMESTAMP */
1799#endif /* !VMS && !CMS_MVS */
1800
1801
1802
1803#if (!defined(VMS) && !defined(OS2) && !defined(CMS_MVS))
1804
1805/******************************/
1806/* Function check_for_newer() */  /* used for overwriting/freshening/updating */
1807/******************************/
1808
1809int check_for_newer(__G__ filename)  /* return 1 if existing file is newer */
1810    __GDEF                           /*  or equal; 0 if older; -1 if doesn't */
1811    char *filename;                  /*  exist yet */
1812{
1813    time_t existing, archive;
1814#ifdef USE_EF_UT_TIME
1815    iztimes z_utime;
1816#endif
1817#ifdef AOS_VS
1818    long    dyy, dmm, ddd, dhh, dmin, dss;
1819
1820
1821    dyy = (lrec.last_mod_dos_datetime >> 25) + 1980;
1822    dmm = (lrec.last_mod_dos_datetime >> 21) & 0x0f;
1823    ddd = (lrec.last_mod_dos_datetime >> 16) & 0x1f;
1824    dhh = (lrec.last_mod_dos_datetime >> 11) & 0x1f;
1825    dmin = (lrec.last_mod_dos_datetime >> 5) & 0x3f;
1826    dss = (lrec.last_mod_dos_datetime & 0x1f) * 2;
1827
1828    /* under AOS/VS, file times can only be set at creation time,
1829     * with the info in a special DG format.  Make sure we can create
1830     * it here - we delete it later & re-create it, whether or not
1831     * it exists now.
1832     */
1833    if (!zvs_create(filename, (((ulg)dgdate(dmm, ddd, dyy)) << 16) |
1834        (dhh*1800L + dmin*30L + dss/2L), -1L, -1L, (char *) -1, -1, -1, -1))
1835        return DOES_NOT_EXIST;
1836#endif /* AOS_VS */
1837
1838    Trace((stderr, "check_for_newer:  doing stat(%s)\n", FnFilter1(filename)));
1839    if (SSTAT(filename, &G.statbuf)) {
1840        Trace((stderr,
1841          "check_for_newer:  stat(%s) returns %d:  file does not exist\n",
1842          FnFilter1(filename), SSTAT(filename, &G.statbuf)));
1843#ifdef SYMLINKS
1844        Trace((stderr, "check_for_newer:  doing lstat(%s)\n",
1845          FnFilter1(filename)));
1846        /* GRR OPTION:  could instead do this test ONLY if G.symlnk is true */
1847        if (lstat(filename, &G.statbuf) == 0) {
1848            Trace((stderr,
1849              "check_for_newer:  lstat(%s) returns 0:  symlink does exist\n",
1850              FnFilter1(filename)));
1851            if (QCOND2 && !IS_OVERWRT_ALL)
1852                Info(slide, 0, ((char *)slide, LoadFarString(FileIsSymLink),
1853                  FnFilter1(filename), " with no real file"));
1854            return EXISTS_AND_OLDER;   /* symlink dates are meaningless */
1855        }
1856#endif /* SYMLINKS */
1857        return DOES_NOT_EXIST;
1858    }
1859    Trace((stderr, "check_for_newer:  stat(%s) returns 0:  file exists\n",
1860      FnFilter1(filename)));
1861
1862#ifdef SYMLINKS
1863    /* GRR OPTION:  could instead do this test ONLY if G.symlnk is true */
1864    if (lstat(filename, &G.statbuf) == 0 && S_ISLNK(G.statbuf.st_mode)) {
1865        Trace((stderr, "check_for_newer:  %s is a symbolic link\n",
1866          FnFilter1(filename)));
1867        if (QCOND2 && !IS_OVERWRT_ALL)
1868            Info(slide, 0, ((char *)slide, LoadFarString(FileIsSymLink),
1869              FnFilter1(filename), ""));
1870        return EXISTS_AND_OLDER;   /* symlink dates are meaningless */
1871    }
1872#endif /* SYMLINKS */
1873
1874    NATIVE_TO_TIMET(G.statbuf.st_mtime)   /* NOP unless MSC 7.0 or Macintosh */
1875
1876#ifdef USE_EF_UT_TIME
1877    /* The `Unix extra field mtime' should be used for comparison with the
1878     * time stamp of the existing file >>>ONLY<<< when the EF info is also
1879     * used to set the modification time of the extracted file.
1880     */
1881    if (G.extra_field &&
1882#ifdef IZ_CHECK_TZ
1883        G.tz_is_valid &&
1884#endif
1885        (ef_scan_for_izux(G.extra_field, G.lrec.extra_field_length, 0,
1886                          G.lrec.last_mod_dos_datetime, &z_utime, NULL)
1887         & EB_UT_FL_MTIME))
1888    {
1889        TTrace((stderr, "check_for_newer:  using Unix extra field mtime\n"));
1890        existing = G.statbuf.st_mtime;
1891        archive  = z_utime.mtime;
1892    } else {
1893        /* round up existing filetime to nearest 2 seconds for comparison,
1894         * but saturate in case of arithmetic overflow
1895         */
1896        existing = ((G.statbuf.st_mtime & 1) &&
1897                    (G.statbuf.st_mtime + 1 > G.statbuf.st_mtime)) ?
1898                   G.statbuf.st_mtime + 1 : G.statbuf.st_mtime;
1899        archive  = dos_to_unix_time(G.lrec.last_mod_dos_datetime);
1900    }
1901#else /* !USE_EF_UT_TIME */
1902    /* round up existing filetime to nearest 2 seconds for comparison,
1903     * but saturate in case of arithmetic overflow
1904     */
1905    existing = ((G.statbuf.st_mtime & 1) &&
1906                (G.statbuf.st_mtime + 1 > G.statbuf.st_mtime)) ?
1907               G.statbuf.st_mtime + 1 : G.statbuf.st_mtime;
1908    archive  = dos_to_unix_time(G.lrec.last_mod_dos_datetime);
1909#endif /* ?USE_EF_UT_TIME */
1910
1911    TTrace((stderr, "check_for_newer:  existing %lu, archive %lu, e-a %ld\n",
1912      (ulg)existing, (ulg)archive, (long)(existing-archive)));
1913
1914    return (existing >= archive);
1915
1916} /* end function check_for_newer() */
1917
1918#endif /* !VMS && !OS2 && !CMS_MVS */
1919
1920
1921
1922
1923
1924/************************/
1925/* Function do_string() */
1926/************************/
1927
1928int do_string(__G__ length, option)   /* return PK-type error code */
1929    __GDEF
1930    unsigned int length;        /* without prototype, ush converted to this */
1931    int option;
1932{
1933    unsigned comment_bytes_left;
1934    unsigned int block_len;
1935    int error=PK_OK;
1936#ifdef AMIGA
1937    char tmp_fnote[2 * AMIGA_FILENOTELEN];   /* extra room for squozen chars */
1938#endif
1939
1940
1941/*---------------------------------------------------------------------------
1942    This function processes arbitrary-length (well, usually) strings.  Four
1943    major options are allowed:  SKIP, wherein the string is skipped (pretty
1944    logical, eh?); DISPLAY, wherein the string is printed to standard output
1945    after undergoing any necessary or unnecessary character conversions;
1946    DS_FN, wherein the string is put into the filename[] array after under-
1947    going appropriate conversions (including case-conversion, if that is
1948    indicated: see the global variable pInfo->lcflag); and EXTRA_FIELD,
1949    wherein the `string' is assumed to be an extra field and is copied to
1950    the (freshly malloced) buffer G.extra_field.  The third option should
1951    be OK since filename is dimensioned at 1025, but we check anyway.
1952
1953    The string, by the way, is assumed to start at the current file-pointer
1954    position; its length is given by 'length'.  So start off by checking the
1955    length of the string:  if zero, we're already done.
1956  ---------------------------------------------------------------------------*/
1957
1958    if (!length)
1959        return PK_COOL;
1960
1961    switch (option) {
1962
1963#if (defined(SFX) && defined(CHEAP_SFX_AUTORUN))
1964    /*
1965     * Special case: See if the comment begins with an autorun command line.
1966     * Save that and display (or skip) the remainder.
1967     */
1968
1969    case CHECK_AUTORUN:
1970    case CHECK_AUTORUN_Q:
1971        comment_bytes_left = length;
1972        if (length >= 10)
1973        {
1974            block_len = readbuf(__G__ (char *)G.outbuf, 10);
1975            if (block_len == 0)
1976                return PK_EOF;
1977            comment_bytes_left -= block_len;
1978            G.outbuf[block_len] = '\0';
1979            if (!strcmp((char *)G.outbuf, "$AUTORUN$>")) {
1980                char *eol;
1981                length -= 10;
1982                block_len = readbuf(__G__ G.autorun_command,
1983                                    MIN(length, sizeof(G.autorun_command)-1));
1984                if (block_len == 0)
1985                    return PK_EOF;
1986                comment_bytes_left -= block_len;
1987                G.autorun_command[block_len] = '\0';
1988                A_TO_N(G.autorun_command);
1989                eol = strchr(G.autorun_command, '\n');
1990                if (!eol)
1991                    eol = G.autorun_command + strlen(G.autorun_command) - 1;
1992                length -= eol + 1 - G.autorun_command;
1993                while (eol >= G.autorun_command && isspace(*eol))
1994                    *eol-- = '\0';
1995#ifdef WIN32
1996                /* Win9x console always uses OEM character coding, and
1997                   WinNT console is set to OEM charset by default, too */
1998                INTERN_TO_OEM(G.autorun_command, G.autorun_command);
1999#endif /* WIN32 */
2000            }
2001        }
2002        if (option == CHECK_AUTORUN_Q)  /* don't display the remainder */
2003            length = 0;
2004        /* seek to beginning of remaining part of comment -- rewind if */
2005        /* displaying entire comment, or skip to end if discarding it  */
2006        seek_zipf(__G__ G.cur_zipfile_bufstart - G.extra_bytes +
2007                  (G.inptr - G.inbuf) + comment_bytes_left - length);
2008        if (!length)
2009            break;
2010        /*  FALL THROUGH...  */
2011#endif /* SFX && CHEAP_SFX_AUTORUN */
2012
2013    /*
2014     * First normal case:  print string on standard output.  First set loop
2015     * variables, then loop through the comment in chunks of OUTBUFSIZ bytes,
2016     * converting formats and printing as we go.  The second half of the
2017     * loop conditional was added because the file might be truncated, in
2018     * which case comment_bytes_left will remain at some non-zero value for
2019     * all time.  outbuf and slide are used as scratch buffers because they
2020     * are available (we should be either before or in between any file pro-
2021     * cessing).
2022     */
2023
2024    case DISPLAY:
2025    case DISPL_8:
2026        comment_bytes_left = length;
2027        block_len = OUTBUFSIZ;       /* for the while statement, first time */
2028        while (comment_bytes_left > 0 && block_len > 0) {
2029            register uch *p = G.outbuf;
2030            register uch *q = G.outbuf;
2031
2032            if ((block_len = readbuf(__G__ (char *)G.outbuf,
2033                   MIN((unsigned)OUTBUFSIZ, comment_bytes_left))) == 0)
2034                return PK_EOF;
2035            comment_bytes_left -= block_len;
2036
2037            /* this is why we allocated an extra byte for outbuf:  terminate
2038             *  with zero (ASCIIZ) */
2039            G.outbuf[block_len] = '\0';
2040
2041            /* remove all ASCII carriage returns from comment before printing
2042             * (since used before A_TO_N(), check for CR instead of '\r')
2043             */
2044            while (*p) {
2045                while (*p == CR)
2046                    ++p;
2047                *q++ = *p++;
2048            }
2049            /* could check whether (p - outbuf) == block_len here */
2050            *q = '\0';
2051
2052            if (option == DISPL_8) {
2053                /* translate the text coded in the entry's host-dependent
2054                   "extended ASCII" charset into the compiler's (system's)
2055                   internal text code page */
2056                Ext_ASCII_TO_Native((char *)G.outbuf, G.pInfo->hostnum,
2057                                    G.pInfo->hostver, G.pInfo->HasUxAtt,
2058                                    FALSE);
2059#ifdef WINDLL
2060                /* translate to ANSI (RTL internal codepage may be OEM) */
2061                INTERN_TO_ISO((char *)G.outbuf, (char *)G.outbuf);
2062#else /* !WINDLL */
2063#ifdef WIN32
2064                /* Win9x console always uses OEM character coding, and
2065                   WinNT console is set to OEM charset by default, too */
2066                INTERN_TO_OEM((char *)G.outbuf, (char *)G.outbuf);
2067#endif /* WIN32 */
2068#endif /* ?WINDLL */
2069            } else {
2070                A_TO_N(G.outbuf);   /* translate string to native */
2071            }
2072
2073#ifdef WINDLL
2074            /* ran out of local mem -- had to cheat */
2075            win_fprintf((zvoid *)&G, stdout, length, (char *)G.outbuf);
2076            win_fprintf((zvoid *)&G, stdout, 2, (char *)"\n\n");
2077#else /* !WINDLL */
2078#ifdef NOANSIFILT       /* GRR:  can ANSI be used with EBCDIC? */
2079            (*G.message)((zvoid *)&G, G.outbuf, (ulg)(q-G.outbuf), 0);
2080#else /* ASCII, filter out ANSI escape sequences and handle ^S (pause) */
2081            p = G.outbuf - 1;
2082            q = slide;
2083            while (*++p) {
2084                int pause = FALSE;
2085
2086                if (*p == 0x1B) {          /* ASCII escape char */
2087                    *q++ = '^';
2088                    *q++ = '[';
2089                } else if (*p == 0x13) {   /* ASCII ^S (pause) */
2090                    pause = TRUE;
2091                    if (p[1] == LF)        /* ASCII LF */
2092                        *q++ = *++p;
2093                    else if (p[1] == CR && p[2] == LF) {  /* ASCII CR LF */
2094                        *q++ = *++p;
2095                        *q++ = *++p;
2096                    }
2097                } else
2098                    *q++ = *p;
2099                if ((unsigned)(q-slide) > WSIZE-3 || pause) {   /* flush */
2100                    (*G.message)((zvoid *)&G, slide, (ulg)(q-slide), 0);
2101                    q = slide;
2102                    if (pause && G.extract_flag) /* don't pause for list/test */
2103                        (*G.mpause)((zvoid *)&G, LoadFarString(QuitPrompt), 0);
2104                }
2105            }
2106            (*G.message)((zvoid *)&G, slide, (ulg)(q-slide), 0);
2107#endif /* ?NOANSIFILT */
2108#endif /* ?WINDLL */
2109        }
2110        /* add '\n' if not at start of line */
2111        (*G.message)((zvoid *)&G, slide, 0L, 0x40);
2112        break;
2113
2114    /*
2115     * Second case:  read string into filename[] array.  The filename should
2116     * never ever be longer than FILNAMSIZ-1 (1024), but for now we'll check,
2117     * just to be sure.
2118     */
2119
2120    case DS_FN:
2121    case DS_FN_L:
2122        if (length >= FILNAMSIZ) {
2123            Info(slide, 0x401, ((char *)slide,
2124              LoadFarString(FilenameTooLongTrunc)));
2125            error = PK_WARN;
2126            /* remember excess length in block_len */
2127            block_len = length - (FILNAMSIZ - 1);
2128            length = FILNAMSIZ - 1;
2129        } else
2130            /* no excess size */
2131            block_len = 0;
2132        if (readbuf(__G__ G.filename, length) == 0)
2133            return PK_EOF;
2134        G.filename[length] = '\0';      /* terminate w/zero:  ASCIIZ */
2135
2136        /* translate the Zip entry filename coded in host-dependent "extended
2137           ASCII" into the compiler's (system's) internal text code page */
2138        Ext_ASCII_TO_Native(G.filename, G.pInfo->hostnum, G.pInfo->hostver,
2139                            G.pInfo->HasUxAtt, (option == DS_FN_L));
2140
2141        if (G.pInfo->lcflag)      /* replace with lowercase filename */
2142            STRLOWER(G.filename, G.filename);
2143
2144        if (G.pInfo->vollabel && length > 8 && G.filename[8] == '.') {
2145            char *p = G.filename+8;
2146            while (*p++)
2147                p[-1] = *p;  /* disk label, and 8th char is dot:  remove dot */
2148        }
2149
2150        if (!block_len)         /* no overflow, we're done here */
2151            break;
2152
2153        /*
2154         * We truncated the filename, so print what's left and then fall
2155         * through to the SKIP routine.
2156         */
2157        Info(slide, 0x401, ((char *)slide, "[ %s ]\n", FnFilter1(G.filename)));
2158        length = block_len;     /* SKIP the excess bytes... */
2159        /*  FALL THROUGH...  */
2160
2161    /*
2162     * Third case:  skip string, adjusting readbuf's internal variables
2163     * as necessary (and possibly skipping to and reading a new block of
2164     * data).
2165     */
2166
2167    case SKIP:
2168        /* cur_zipfile_bufstart already takes account of extra_bytes, so don't
2169         * correct for it twice: */
2170        seek_zipf(__G__ G.cur_zipfile_bufstart - G.extra_bytes +
2171                  (G.inptr-G.inbuf) + length);
2172        break;
2173
2174    /*
2175     * Fourth case:  assume we're at the start of an "extra field"; malloc
2176     * storage for it and read data into the allocated space.
2177     */
2178
2179    case EXTRA_FIELD:
2180        if (G.extra_field != (uch *)NULL)
2181            free(G.extra_field);
2182        if ((G.extra_field = (uch *)malloc(length)) == (uch *)NULL) {
2183            Info(slide, 0x401, ((char *)slide, LoadFarString(ExtraFieldTooLong),
2184              length));
2185            /* cur_zipfile_bufstart already takes account of extra_bytes,
2186             * so don't correct for it twice: */
2187            seek_zipf(__G__ G.cur_zipfile_bufstart - G.extra_bytes +
2188                      (G.inptr-G.inbuf) + length);
2189        } else
2190            if (readbuf(__G__ (char *)G.extra_field, length) == 0)
2191                return PK_EOF;
2192        break;
2193
2194#ifdef AMIGA
2195    /*
2196     * Fifth case, for the Amiga only:  take the comment that would ordinarily
2197     * be skipped over, and turn it into a 79 character string that will be
2198     * attached to the file as a "filenote" after it is extracted.
2199     */
2200
2201    case FILENOTE:
2202        if ((block_len = readbuf(__G__ tmp_fnote, (unsigned)
2203                                 MIN(length, 2 * AMIGA_FILENOTELEN - 1))) == 0)
2204            return PK_EOF;
2205        if ((length -= block_len) > 0)  /* treat remainder as in case SKIP: */
2206            seek_zipf(__G__ G.cur_zipfile_bufstart - G.extra_bytes
2207                      + (G.inptr - G.inbuf) + length);
2208        /* convert multi-line text into single line with no ctl-chars: */
2209        tmp_fnote[block_len] = '\0';
2210        while ((short int) --block_len >= 0)
2211            if ((unsigned) tmp_fnote[block_len] < ' ')
2212                if (tmp_fnote[block_len+1] == ' ')     /* no excess */
2213                    strcpy(tmp_fnote+block_len, tmp_fnote+block_len+1);
2214                else
2215                    tmp_fnote[block_len] = ' ';
2216        tmp_fnote[AMIGA_FILENOTELEN - 1] = '\0';
2217        if (G.filenotes[G.filenote_slot])
2218            free(G.filenotes[G.filenote_slot]);     /* should not happen */
2219        G.filenotes[G.filenote_slot] = NULL;
2220        if (tmp_fnote[0]) {
2221            if (!(G.filenotes[G.filenote_slot] = malloc(strlen(tmp_fnote)+1)))
2222                return PK_MEM;
2223            strcpy(G.filenotes[G.filenote_slot], tmp_fnote);
2224        }
2225        break;
2226#endif /* AMIGA */
2227
2228    } /* end switch (option) */
2229
2230    return error;
2231
2232} /* end function do_string() */
2233
2234
2235
2236
2237
2238/***********************/
2239/* Function makeword() */
2240/***********************/
2241
2242ush makeword(b)
2243    ZCONST uch *b;
2244{
2245    /*
2246     * Convert Intel style 'short' integer to non-Intel non-16-bit
2247     * host format.  This routine also takes care of byte-ordering.
2248     */
2249    return (ush)((b[1] << 8) | b[0]);
2250}
2251
2252
2253
2254
2255
2256/***********************/
2257/* Function makelong() */
2258/***********************/
2259
2260ulg makelong(sig)
2261    ZCONST uch *sig;
2262{
2263    /*
2264     * Convert intel style 'long' variable to non-Intel non-16-bit
2265     * host format.  This routine also takes care of byte-ordering.
2266     */
2267    return (((ulg)sig[3]) << 24)
2268        + (((ulg)sig[2]) << 16)
2269        + (((ulg)sig[1]) << 8)
2270        + ((ulg)sig[0]);
2271}
2272
2273
2274
2275#if CRYPT
2276
2277#ifdef NEED_STR2ISO
2278/**********************/
2279/* Function str2iso() */
2280/**********************/
2281
2282char *str2iso(dst, src)
2283    char *dst;                          /* destination buffer */
2284    register ZCONST char *src;          /* source string */
2285{
2286#ifdef INTERN_TO_ISO
2287    INTERN_TO_ISO(src, dst);
2288#else
2289    register uch c;
2290    register char *dstp = dst;
2291
2292    do {
2293        c = (uch)foreign(*src++);
2294        *dstp++ = (char)ASCII2ISO(c);
2295    } while (c != '\0');
2296#endif
2297
2298    return dst;
2299}
2300#endif /* NEED_STR2ISO */
2301
2302
2303#ifdef NEED_STR2OEM
2304/**********************/
2305/* Function str2oem() */
2306/**********************/
2307
2308char *str2oem(dst, src)
2309    char *dst;                          /* destination buffer */
2310    register ZCONST char *src;          /* source string */
2311{
2312#ifdef INTERN_TO_OEM
2313    INTERN_TO_OEM(src, dst);
2314#else
2315    register uch c;
2316    register char *dstp = dst;
2317
2318    do {
2319        c = (uch)foreign(*src++);
2320        *dstp++ = (char)ASCII2OEM(c);
2321    } while (c != '\0');
2322#endif
2323
2324    return dst;
2325}
2326#endif /* NEED_STR2OEM */
2327
2328#endif /* CRYPT */
2329
2330
2331#ifdef ZMEM  /* memset/memcmp/memcpy for systems without either them or */
2332             /* bzero/bcmp/bcopy */
2333             /* (no known systems as of 960211) */
2334
2335/*********************/
2336/* Function memset() */
2337/*********************/
2338
2339zvoid *memset(buf, init, len)
2340    register zvoid *buf;        /* buffer location */
2341    register int init;          /* initializer character */
2342    register unsigned int len;  /* length of the buffer */
2343{
2344    zvoid *start;
2345
2346    start = buf;
2347    while (len--)
2348        *((char *)buf++) = (char)init;
2349    return start;
2350}
2351
2352
2353
2354/*********************/
2355/* Function memcmp() */
2356/*********************/
2357
2358int memcmp(b1, b2, len)
2359    register ZCONST zvoid *b1;
2360    register ZCONST zvoid *b2;
2361    register unsigned int len;
2362{
2363    register int c;
2364
2365    if (len > 0) do {
2366        if ((c = (int)(*((ZCONST unsigned char *)b1)++) -
2367                 (int)(*((ZCONST unsigned char *)b2)++)) != 0)
2368           return c;
2369    } while (--len > 0)
2370    return 0;
2371}
2372
2373
2374
2375/*********************/
2376/* Function memcpy() */
2377/*********************/
2378
2379zvoid *memcpy(dst, src, len)
2380    register zvoid *dst;
2381    register ZCONST zvoid *src;
2382    register unsigned int len;
2383{
2384    zvoid *start;
2385
2386    start = dst;
2387    while (len-- > 0)
2388        *((char *)dst)++ = *((ZCONST char *)src)++;
2389    return start;
2390}
2391
2392#endif /* ZMEM */
2393
2394
2395
2396
2397#ifdef NO_STRNICMP
2398
2399/************************/
2400/* Function zstrnicmp() */
2401/************************/
2402
2403int zstrnicmp(s1, s2, n)
2404    register ZCONST char *s1, *s2;
2405    register unsigned n;
2406{
2407    for (; n > 0;  --n, ++s1, ++s2) {
2408
2409        if (ToLower(*s1) != ToLower(*s2))
2410            /* test includes early termination of one string */
2411            return (ToLower(*s1) < ToLower(*s2))? -1 : 1;
2412
2413        if (*s1 == '\0')   /* both strings terminate early */
2414            return 0;
2415    }
2416    return 0;
2417}
2418
2419#endif /* NO_STRNICMP */
2420
2421
2422
2423
2424#ifdef REGULUS  /* returns the inode number on success(!)...argh argh argh */
2425#  undef stat
2426
2427/********************/
2428/* Function zstat() */
2429/********************/
2430
2431int zstat(p, s)
2432    ZCONST char *p;
2433    struct stat *s;
2434{
2435    return (stat((char *)p,s) >= 0? 0 : (-1));
2436}
2437
2438#endif /* REGULUS */
2439
2440
2441
2442
2443#ifdef _MBCS
2444
2445/* DBCS support for Info-ZIP's zip  (mainly for japanese (-: )
2446 * by Yoshioka Tsuneo (QWF00133@nifty.ne.jp,tsuneo-y@is.aist-nara.ac.jp)
2447 * This code is public domain!   Date: 1998/12/20
2448 */
2449
2450/************************/
2451/* Function plastchar() */
2452/************************/
2453
2454char *plastchar(ptr, len)
2455    ZCONST char *ptr;
2456    extent len;
2457{
2458    unsigned clen;
2459    ZCONST char *oldptr = ptr;
2460    while(*ptr != '\0' && len > 0){
2461        oldptr = ptr;
2462        clen = CLEN(ptr);
2463        ptr += clen;
2464        len -= clen;
2465    }
2466    return (char *)oldptr;
2467}
2468
2469
2470#ifdef NEED_UZMBSCHR
2471/***********************/
2472/* Function uzmbschr() */
2473/***********************/
2474
2475unsigned char *uzmbschr(str, c)
2476    ZCONST unsigned char *str;
2477    unsigned int c;
2478{
2479    while(*str != '\0'){
2480        if (*str == c) {return (unsigned char *)str;}
2481        INCSTR(str);
2482    }
2483    return NULL;
2484}
2485#endif /* NEED_UZMBSCHR */
2486
2487
2488#ifdef NEED_UZMBSRCHR
2489/************************/
2490/* Function uzmbsrchr() */
2491/************************/
2492
2493unsigned char *uzmbsrchr(str, c)
2494    ZCONST unsigned char *str;
2495    unsigned int c;
2496{
2497    unsigned char *match = NULL;
2498    while(*str != '\0'){
2499        if (*str == c) {match = (unsigned char *)str;}
2500        INCSTR(str);
2501    }
2502    return match;
2503}
2504#endif /* NEED_UZMBSRCHR */
2505#endif /* _MBCS */
2506
2507
2508
2509
2510
2511#ifdef SMALL_MEM
2512
2513/*******************************/
2514/*  Function fLoadFarString()  */   /* (and friends...) */
2515/*******************************/
2516
2517char *fLoadFarString(__GPRO__ const char Far *sz)
2518{
2519    (void)zfstrcpy(G.rgchBigBuffer, sz);
2520    return G.rgchBigBuffer;
2521}
2522
2523char *fLoadFarStringSmall(__GPRO__ const char Far *sz)
2524{
2525    (void)zfstrcpy(G.rgchSmallBuffer, sz);
2526    return G.rgchSmallBuffer;
2527}
2528
2529char *fLoadFarStringSmall2(__GPRO__ const char Far *sz)
2530{
2531    (void)zfstrcpy(G.rgchSmallBuffer2, sz);
2532    return G.rgchSmallBuffer2;
2533}
2534
2535
2536
2537
2538#if (!defined(_MSC_VER) || (_MSC_VER < 600))
2539/*************************/
2540/*  Function zfstrcpy()  */   /* portable clone of _fstrcpy() */
2541/*************************/
2542
2543char Far * Far zfstrcpy(char Far *s1, const char Far *s2)
2544{
2545    char Far *p = s1;
2546
2547    while ((*s1++ = *s2++) != '\0');
2548    return p;
2549}
2550#endif /* !_MSC_VER || (_MSC_VER < 600) */
2551
2552#endif /* SMALL_MEM */
2553