1/*
2  Copyright (c) 1990-2008 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  msdos.c
12
13  MSDOS-specific routines for use with Info-ZIP's UnZip 5.3 and later.
14
15  Contains:  Opendir()                      (from zip)
16             Readdir()                      (from zip)
17             do_wild()
18             mapattr()
19             mapname()
20             maskDOSdevice()
21             map2fat()
22             checkdir()
23             isfloppy()
24             z_dos_chmod()
25             volumelabel()                  (non-djgpp, non-emx)
26             close_outfile()
27             stamp_file()                   (TIMESTAMP only)
28             prepare_ISO_OEM_translat()
29             dateformat()
30             version()
31             zcalloc()                      (16-bit, only)
32             zcfree()                       (16-bit, only)
33             _dos_getcountryinfo()          (djgpp 1.x, emx)
34            [_dos_getftime()                (djgpp 1.x, emx)   to be added]
35             _dos_setftime()                (djgpp 1.x, emx)
36             _dos_setfileattr()             (djgpp 1.x, emx)
37             _dos_getdrive()                (djgpp 1.x, emx)
38             _dos_creat()                   (djgpp 1.x, emx)
39             _dos_close()                   (djgpp 1.x, emx)
40             volumelabel()                  (djgpp, emx)
41             _dos_getcountryinfo()          (djgpp 2.x)
42             _is_executable()               (djgpp 2.x)
43             __crt0_glob_function()         (djgpp 2.x)
44             __crt0_load_environment_file() (djgpp 2.x)
45             dos_getcodepage()              (all, ASM system call)
46             screensize()                   (emx, Watcom 32-bit)
47             int86x_realmode()              (Watcom 32-bit)
48             stat_bandaid()                 (Watcom)
49
50  ---------------------------------------------------------------------------*/
51
52
53
54#define UNZIP_INTERNAL
55#include "unzip.h"
56
57/* fUnZip does not need anything from here except the zcalloc() & zcfree()
58 * function pair (when Deflate64 support is enabled in 16-bit environment).
59 */
60#ifndef FUNZIP
61
62static void maskDOSdevice(__GPRO__ char *pathcomp, char *last_dot);
63#ifdef MAYBE_PLAIN_FAT
64   static void map2fat OF((char *pathcomp, char *last_dot));
65#endif
66static int isfloppy OF((int nDrive));
67static int z_dos_chmod OF((__GPRO__ ZCONST char *fname, int attributes));
68static int volumelabel OF((ZCONST char *newlabel));
69#if (!defined(SFX) && !defined(WINDLL))
70   static int is_running_on_windows OF((void));
71#endif
72static int getdoscodepage OF((void));
73
74static int created_dir;        /* used by mapname(), checkdir() */
75static int renamed_fullpath;   /* ditto */
76static unsigned nLabelDrive;   /* ditto, plus volumelabel() */
77
78
79
80/*****************************/
81/*  Strings used in msdos.c  */
82/*****************************/
83
84#ifndef SFX
85  static ZCONST char Far CantAllocateWildcard[] =
86    "warning:  cannot allocate wildcard buffers\n";
87#endif
88static ZCONST char Far WarnDirTraversSkip[] =
89  "warning:  skipped \"../\" path component(s) in %s\n";
90static ZCONST char Far Creating[] = "   creating: %s\n";
91static ZCONST char Far ConversionFailed[] =
92  "mapname:  conversion of %s failed\n";
93static ZCONST char Far Labelling[] = "labelling %c: %-22s\n";
94static ZCONST char Far ErrSetVolLabel[] =
95  "mapname:  error setting volume label\n";
96static ZCONST char Far PathTooLong[] = "checkdir error:  path too long: %s\n";
97static ZCONST char Far CantCreateDir[] = "checkdir error:  cannot create %s\n\
98                 unable to process %s.\n";
99static ZCONST char Far DirIsntDirectory[] =
100  "checkdir error:  %s exists but is not directory\n\
101                 unable to process %s.\n";
102static ZCONST char Far PathTooLongTrunc[] =
103  "checkdir warning:  path too long; truncating\n                   %s\n\
104                -> %s\n";
105#if (!defined(SFX) || defined(SFX_EXDIR))
106   static ZCONST char Far CantCreateExtractDir[] =
107     "checkdir:  cannot create extraction directory: %s\n";
108#endif
109static ZCONST char Far AttribsMayBeWrong[] =
110  "\nwarning:  file attributes may not be correct\n";
111#if (!defined(SFX) && !defined(WINDLL))
112   static ZCONST char Far WarnUsedOnWindows[] =
113     "\n%s warning: You are using the MSDOS version on Windows.\n"
114     "Please try the native Windows version before reporting any problems.\n";
115#endif
116
117
118
119/****************************/
120/*  Macros used in msdos.c  */
121/****************************/
122
123#ifdef WATCOMC_386
124#  define WREGS(v,r) (v.w.r)
125#  define int86x int386x
126   static int int86x_realmode(int inter_no, union REGS *in,
127                              union REGS *out, struct SREGS *seg);
128#  define F_intdosx(ir,or,sr) int86x_realmode(0x21, ir, or, sr)
129#  define XXX__MK_FP_IS_BROKEN
130#else
131#  if (defined(__DJGPP__) && (__DJGPP__ >= 2))
132#   define WREGS(v,r) (v.w.r)
133#  else
134#   define WREGS(v,r) (v.x.r)
135#  endif
136#  define F_intdosx(ir,or,sr) intdosx(ir, or, sr)
137#endif
138
139#if (defined(__GO32__) || defined(__EMX__))
140#  include <dirent.h>        /* use readdir() */
141#  define MKDIR(path,mode)   mkdir(path,mode)
142#  define Opendir  opendir
143#  define Readdir  readdir
144#  define Closedir closedir
145#  define zdirent  dirent
146#  define zDIR     DIR
147#  ifdef __EMX__
148#    include <dos.h>
149#    define GETDRIVE(d)      d = _getdrive()
150#    define FA_LABEL         A_LABEL
151#  else
152#    define GETDRIVE(d)      _dos_getdrive(&d)
153#  endif
154#  if defined(_A_SUBDIR)     /* MSC dos.h and compatibles */
155#    define FSUBDIR          _A_SUBDIR
156#  elif defined(FA_DIREC)    /* Borland dos.h and compatible variants */
157#    define FSUBDIR          FA_DIREC
158#  elif defined(A_DIR)       /* EMX dir.h (and dirent.h) */
159#    define FSUBDIR          A_DIR
160#  else                      /* fallback definition */
161#    define FSUBDIR          0x10
162#  endif
163#  if defined(_A_VOLID)      /* MSC dos.h and compatibles */
164#    define FVOLID           _A_VOLID
165#  elif defined(FA_LABEL)    /* Borland dos.h and compatible variants */
166#    define FVOLID           FA_LABEL
167#  elif defined(A_LABEL)     /* EMX dir.h (and dirent.h) */
168#    define FVOLID           A_LABEL
169#  else
170#    define FVOLID           0x08
171#  endif
172#else /* !(__GO32__ || __EMX__) */
173#  define MKDIR(path,mode)   mkdir(path)
174#  ifdef __TURBOC__
175#    define FATTR            FA_HIDDEN+FA_SYSTEM+FA_DIREC
176#    define FVOLID           FA_LABEL
177#    define FSUBDIR          FA_DIREC
178#    define FFIRST(n,d,a)    findfirst(n,(struct ffblk *)d,a)
179#    define FNEXT(d)         findnext((struct ffblk *)d)
180#    define GETDRIVE(d)      d=getdisk()+1
181#    include <dir.h>
182#  else /* !__TURBOC__ */
183#    define FATTR            _A_HIDDEN+_A_SYSTEM+_A_SUBDIR
184#    define FVOLID           _A_VOLID
185#    define FSUBDIR          _A_SUBDIR
186#    define FFIRST(n,d,a)    _dos_findfirst(n,a,(struct find_t *)d)
187#    define FNEXT(d)         _dos_findnext((struct find_t *)d)
188#    define GETDRIVE(d)      _dos_getdrive(&d)
189#    include <direct.h>
190#  endif /* ?__TURBOC__ */
191   typedef struct zdirent {
192       char d_reserved[30];
193       char d_name[13];
194       int d_first;
195   } zDIR;
196   zDIR *Opendir OF((const char *));
197   struct zdirent *Readdir OF((zDIR *));
198#  define Closedir free
199
200
201
202
203#ifndef SFX
204
205/**********************/   /* Borland C++ 3.x has its own opendir/readdir */
206/* Function Opendir() */   /*  library routines, but earlier versions don't, */
207/**********************/   /*  so use ours regardless */
208
209zDIR *Opendir(name)
210    const char *name;           /* name of directory to open */
211{
212    zDIR *dirp;                 /* malloc'd return value */
213    char *nbuf;                 /* malloc'd temporary string */
214    extent len = strlen(name);  /* path length to avoid strlens and strcats */
215
216
217    if ((dirp = (zDIR *)malloc(sizeof(zDIR))) == (zDIR *)NULL)
218        return (zDIR *)NULL;
219    if ((nbuf = malloc(len + 6)) == (char *)NULL) {
220        free(dirp);
221        return (zDIR *)NULL;
222    }
223    strcpy(nbuf, name);
224    if (len > 0) {
225        if (nbuf[len-1] == ':') {
226            nbuf[len++] = '.';
227        } else if (nbuf[len-1] == '/' || nbuf[len-1] == '\\')
228            --len;
229    }
230    strcpy(nbuf+len, "/*.*");
231    Trace((stderr, "Opendir:  nbuf = [%s]\n", FnFilter1(nbuf)));
232
233    if (FFIRST(nbuf, dirp, FATTR)) {
234        free((zvoid *)nbuf);
235        return (zDIR *)NULL;
236    }
237    free((zvoid *)nbuf);
238    dirp->d_first = 1;
239    return dirp;
240}
241
242
243
244
245
246/**********************/
247/* Function Readdir() */
248/**********************/
249
250struct zdirent *Readdir(d)
251    zDIR *d;        /* directory stream from which to read */
252{
253    /* Return pointer to first or next directory entry, or NULL if end. */
254
255    if (d->d_first)
256        d->d_first = 0;
257    else
258        if (FNEXT(d))
259            return (struct zdirent *)NULL;
260    return (struct zdirent *)d;
261}
262
263#endif /* !SFX */
264#endif /* ?(__GO32__ || __EMX__) */
265
266
267
268
269
270#ifndef SFX
271
272/************************/
273/*  Function do_wild()  */   /* identical to OS/2 version */
274/************************/
275
276char *do_wild(__G__ wildspec)
277    __GDEF
278    ZCONST char *wildspec;   /* only used first time on a given dir */
279{
280    static zDIR *wild_dir = (zDIR *)NULL;
281    static ZCONST char *wildname;
282    static char *dirname, matchname[FILNAMSIZ];
283    static int notfirstcall=FALSE, have_dirname, dirnamelen;
284    char *fnamestart;
285    struct zdirent *file;
286
287    /* Even when we're just returning wildspec, we *always* do so in
288     * matchname[]--calling routine is allowed to append four characters
289     * to the returned string, and wildspec may be a pointer to argv[].
290     */
291    if (!notfirstcall) {    /* first call:  must initialize everything */
292        notfirstcall = TRUE;
293
294        if (!iswild(wildspec)) {
295            strncpy(matchname, wildspec, FILNAMSIZ);
296            matchname[FILNAMSIZ-1] = '\0';
297            have_dirname = FALSE;
298            wild_dir = NULL;
299            return matchname;
300        }
301
302        /* break the wildspec into a directory part and a wildcard filename */
303        if ((wildname = strrchr(wildspec, '/')) == (ZCONST char *)NULL &&
304            (wildname = strrchr(wildspec, ':')) == (ZCONST char *)NULL) {
305            dirname = ".";
306            dirnamelen = 1;
307            have_dirname = FALSE;
308            wildname = wildspec;
309        } else {
310            ++wildname;     /* point at character after '/' or ':' */
311            dirnamelen = (int)(wildname - wildspec);
312            if ((dirname = (char *)malloc(dirnamelen+1)) == (char *)NULL) {
313                Info(slide, 1, ((char *)slide,
314                  LoadFarString(CantAllocateWildcard)));
315                strncpy(matchname, wildspec, FILNAMSIZ);
316                matchname[FILNAMSIZ-1] = '\0';
317                return matchname;   /* but maybe filespec was not a wildcard */
318            }
319/* GRR:  can't strip trailing char for opendir since might be "d:/" or "d:"
320 *       (would have to check for "./" at end--let opendir handle it instead) */
321            strncpy(dirname, wildspec, dirnamelen);
322            dirname[dirnamelen] = '\0';   /* terminate for strcpy below */
323            have_dirname = TRUE;
324        }
325        Trace((stderr, "do_wild:  dirname = [%s]\n", FnFilter1(dirname)));
326
327        if ((wild_dir = Opendir(dirname)) != (zDIR *)NULL) {
328            if (have_dirname) {
329                strcpy(matchname, dirname);
330                fnamestart = matchname + dirnamelen;
331            } else
332                fnamestart = matchname;
333            while ((file = Readdir(wild_dir)) != (struct zdirent *)NULL) {
334                Trace((stderr, "do_wild:  readdir returns %s\n",
335                  FnFilter1(file->d_name)));
336                strcpy(fnamestart, file->d_name);
337                if (strrchr(fnamestart, '.') == (char *)NULL)
338                    strcat(fnamestart, ".");
339                /* 1 == ignore case (for case-insensitive DOS-FS) */
340                if (match(fnamestart, wildname, 1 WISEP) &&
341                    /* skip "." and ".." directory entries */
342                    strcmp(fnamestart, ".") && strcmp(fnamestart, "..")) {
343                    Trace((stderr, "do_wild:  match() succeeds\n"));
344                    /* remove trailing dot */
345                    fnamestart += strlen(fnamestart) - 1;
346                    if (*fnamestart == '.')
347                        *fnamestart = '\0';
348                    return matchname;
349                }
350            }
351            /* if we get to here directory is exhausted, so close it */
352            Closedir(wild_dir);
353            wild_dir = (zDIR *)NULL;
354        }
355#ifdef DEBUG
356        else {
357            Trace((stderr, "do_wild:  Opendir(%s) returns NULL\n",
358               FnFilter1(dirname)));
359        }
360#endif /* DEBUG */
361
362        /* return the raw wildspec in case that works (e.g., directory not
363         * searchable, but filespec was not wild and file is readable) */
364        strncpy(matchname, wildspec, FILNAMSIZ);
365        matchname[FILNAMSIZ-1] = '\0';
366        return matchname;
367    }
368
369    /* last time through, might have failed opendir but returned raw wildspec */
370    if (wild_dir == (zDIR *)NULL) {
371        notfirstcall = FALSE; /* nothing left to try--reset for new wildspec */
372        if (have_dirname)
373            free(dirname);
374        return (char *)NULL;
375    }
376
377    /* If we've gotten this far, we've read and matched at least one entry
378     * successfully (in a previous call), so dirname has been copied into
379     * matchname already.
380     */
381    if (have_dirname) {
382        /* strcpy(matchname, dirname); */
383        fnamestart = matchname + dirnamelen;
384    } else
385        fnamestart = matchname;
386    while ((file = Readdir(wild_dir)) != (struct zdirent *)NULL) {
387        Trace((stderr, "do_wild:  readdir returns %s\n",
388          FnFilter1(file->d_name)));
389        strcpy(fnamestart, file->d_name);
390        if (strrchr(fnamestart, '.') == (char *)NULL)
391            strcat(fnamestart, ".");
392        if (match(fnamestart, wildname, 1 WISEP)) { /* 1 == ignore case */
393            Trace((stderr, "do_wild:  match() succeeds\n"));
394            /* remove trailing dot */
395            fnamestart += strlen(fnamestart) - 1;
396            if (*fnamestart == '.')
397                *fnamestart = '\0';
398            return matchname;
399        }
400    }
401
402    Closedir(wild_dir);     /* have read at least one entry; nothing left */
403    wild_dir = (zDIR *)NULL;
404    notfirstcall = FALSE;   /* reset for new wildspec */
405    if (have_dirname)
406        free(dirname);
407    return (char *)NULL;
408
409} /* end function do_wild() */
410
411#endif /* !SFX */
412
413
414
415
416/**********************/
417/* Function mapattr() */
418/**********************/
419
420int mapattr(__G)
421    __GDEF
422{
423    /* set archive bit for file entries (file is not backed up): */
424    G.pInfo->file_attr = ((unsigned)G.crec.external_file_attributes |
425      (G.crec.external_file_attributes & FSUBDIR ? 0 : 32)) & 0xff;
426    return 0;
427
428} /* end function mapattr() */
429
430
431
432
433
434/************************/
435/*  Function mapname()  */
436/************************/
437
438int mapname(__G__ renamed)
439    __GDEF
440    int renamed;
441/*
442 * returns:
443 *  MPN_OK          - no problem detected
444 *  MPN_INF_TRUNC   - caution (truncated filename)
445 *  MPN_INF_SKIP    - info "skip entry" (dir doesn't exist)
446 *  MPN_ERR_SKIP    - error -> skip entry
447 *  MPN_ERR_TOOLONG - error -> path is too long
448 *  MPN_NOMEM       - error (memory allocation failed) -> skip entry
449 *  [also MPN_VOL_LABEL, MPN_CREATED_DIR]
450 */
451{
452    char pathcomp[FILNAMSIZ];      /* path-component buffer */
453    char *pp, *cp=(char *)NULL;    /* character pointers */
454    char *lastsemi=(char *)NULL;   /* pointer to last semi-colon in pathcomp */
455#ifdef MAYBE_PLAIN_FAT
456    char *last_dot=(char *)NULL;   /* last dot not converted to underscore */
457# ifdef USE_LFN
458    int use_lfn = USE_LFN;         /* file system supports long filenames? */
459# endif
460#endif
461    int killed_ddot = FALSE;       /* is set when skipping "../" pathcomp */
462    int error = MPN_OK;
463    register unsigned workch;      /* hold the character being tested */
464
465
466/*---------------------------------------------------------------------------
467    Initialize various pointers and counters and stuff.
468  ---------------------------------------------------------------------------*/
469
470    /* can create path as long as not just freshening, or if user told us */
471    G.create_dirs = (!uO.fflag || renamed);
472
473    created_dir = FALSE;        /* not yet */
474    renamed_fullpath = FALSE;
475
476    if (renamed) {
477        cp = G.filename - 1;    /* point to beginning of renamed name... */
478        while (*++cp)
479            if (*cp == '\\')    /* convert backslashes to forward */
480                *cp = '/';
481        cp = G.filename;
482        /* use temporary rootpath if user gave full pathname */
483        if (G.filename[0] == '/') {
484            renamed_fullpath = TRUE;
485            pathcomp[0] = '/';  /* copy the '/' and terminate */
486            pathcomp[1] = '\0';
487            ++cp;
488        } else if (isalpha((uch)G.filename[0]) && G.filename[1] == ':') {
489            renamed_fullpath = TRUE;
490            pp = pathcomp;
491            *pp++ = *cp++;      /* copy the "d:" (+ '/', possibly) */
492            *pp++ = *cp++;
493            if (*cp == '/')
494                *pp++ = *cp++;  /* otherwise add "./"? */
495            *pp = '\0';
496        }
497    }
498
499    /* pathcomp is ignored unless renamed_fullpath is TRUE: */
500    if ((error = checkdir(__G__ pathcomp, INIT)) != 0) /* initialize path buf */
501        return error;           /* ...unless no mem or vol label on hard disk */
502
503    *pathcomp = '\0';           /* initialize translation buffer */
504    pp = pathcomp;              /* point to translation buffer */
505    if (!renamed) {             /* cp already set if renamed */
506        if (uO.jflag)           /* junking directories */
507            cp = (char *)strrchr(G.filename, '/');
508        if (cp == (char *)NULL) /* no '/' or not junking dirs */
509            cp = G.filename;    /* point to internal zipfile-member pathname */
510        else
511            ++cp;               /* point to start of last component of path */
512    }
513
514/*---------------------------------------------------------------------------
515    Begin main loop through characters in filename.
516  ---------------------------------------------------------------------------*/
517
518    while ((workch = (uch)*cp++) != 0) {
519
520        switch (workch) {
521            case '/':             /* can assume -j flag not given */
522                *pp = '\0';
523#ifdef MAYBE_PLAIN_FAT
524                maskDOSdevice(__G__ pathcomp, last_dot);
525#else
526                maskDOSdevice(__G__ pathcomp, NULL);
527#endif
528#ifdef MAYBE_PLAIN_FAT
529# ifdef USE_LFN
530                if (!use_lfn)
531# endif
532                {
533                    map2fat(pathcomp, last_dot);   /* 8.3 trunc. (in place) */
534                    last_dot = (char *)NULL;
535                }
536#endif
537                if (strcmp(pathcomp, ".") == 0) {
538                    /* don't bother appending "./" to the path */
539                    *pathcomp = '\0';
540                } else if (!uO.ddotflag && strcmp(pathcomp, "..") == 0) {
541                    /* "../" dir traversal detected, skip over it */
542                    *pathcomp = '\0';
543                    killed_ddot = TRUE;     /* set "show message" flag */
544                }
545                /* when path component is not empty, append it now */
546                if (*pathcomp != '\0' &&
547                    ((error = checkdir(__G__ pathcomp, APPEND_DIR))
548                     & MPN_MASK) > MPN_INF_TRUNC)
549                    return error;
550                pp = pathcomp;    /* reset conversion buffer for next piece */
551                lastsemi = (char *)NULL; /* leave direct. semi-colons alone */
552                break;
553
554#ifdef MAYBE_PLAIN_FAT
555            case '.':
556# ifdef USE_LFN
557                if (use_lfn) {          /* LFN filenames may contain many */
558                    *pp++ = '.';        /*  dots, so simply copy it ... */
559                } else
560# endif
561                if (pp == pathcomp && *cp == '.' && cp[1] == '/') {
562                    /* nothing appended yet.., and found "../" */
563                    *pp++ = '.';        /*  add first dot, */
564                    *pp++ = '.';        /*  second dot, and */
565                    ++cp;               /*  skip over to the '/' */
566                } else {                /* found dot within path component */
567                    last_dot = pp;      /*  point at last dot so far... */
568                    *pp++ = '_';        /*  convert to underscore for now */
569                }
570                break;
571#endif /* MAYBE_PLAIN_FAT */
572
573            /* drive names are not stored in zipfile, so no colons allowed;
574             *  no brackets or most other punctuation either (all of which
575             *  can appear in Unix-created archives; backslash is particularly
576             *  bad unless all necessary directories exist) */
577#ifdef MAYBE_PLAIN_FAT
578            case '[':          /* these punctuation characters forbidden */
579            case ']':          /*  only on plain FAT file systems */
580            case '+':
581            case ',':
582            case '=':
583# ifdef USE_LFN
584                if (use_lfn)
585                    *pp++ = (char)workch;
586                else
587                    *pp++ = '_';
588                break;
589# endif
590#endif
591            case ':':           /* special shell characters of command.com */
592            case '\\':          /*  (device and directory limiters, wildcard */
593            case '"':           /*  characters, stdin/stdout redirection and */
594            case '<':           /*  pipe indicators and the quote sign) are */
595            case '>':           /*  never allowed in filenames on (V)FAT */
596            case '|':
597            case '*':
598            case '?':
599                *pp++ = '_';
600                break;
601
602            case ';':             /* start of VMS version? */
603                lastsemi = pp;
604#ifdef MAYBE_PLAIN_FAT
605# ifdef USE_LFN
606                if (use_lfn)
607                    *pp++ = ';';  /* keep for now; remove VMS ";##" later */
608# endif
609#else
610                *pp++ = ';';      /* keep for now; remove VMS ";##" later */
611#endif
612                break;
613
614#ifdef MAYBE_PLAIN_FAT
615            case ' ':                      /* change spaces to underscores */
616# ifdef USE_LFN
617                if (!use_lfn && uO.sflag)  /*  only if requested and NO lfn! */
618# else
619                if (uO.sflag)              /*  only if requested */
620# endif
621                    *pp++ = '_';
622                else
623                    *pp++ = (char)workch;
624                break;
625#endif /* MAYBE_PLAIN_FAT */
626
627            default:
628                /* allow ASCII 255 and European characters in filenames: */
629                if (isprint(workch) || workch >= 127)
630                    *pp++ = (char)workch;
631
632        } /* end switch */
633    } /* end while loop */
634
635    /* Show warning when stripping insecure "parent dir" path components */
636    if (killed_ddot && QCOND2) {
637        Info(slide, 0, ((char *)slide, LoadFarString(WarnDirTraversSkip),
638          FnFilter1(G.filename)));
639        if (!(error & ~MPN_MASK))
640            error = (error & MPN_MASK) | PK_WARN;
641    }
642
643/*---------------------------------------------------------------------------
644    Report if directory was created (and no file to create:  filename ended
645    in '/'), check name to be sure it exists, and combine path and name be-
646    fore exiting.
647  ---------------------------------------------------------------------------*/
648
649    if (G.filename[strlen(G.filename) - 1] == '/') {
650        checkdir(__G__ G.filename, GETPATH);
651        if (created_dir) {
652            if (QCOND2) {
653                Info(slide, 0, ((char *)slide, LoadFarString(Creating),
654                  FnFilter1(G.filename)));
655            }
656
657            /* set file attributes: */
658            z_dos_chmod(__G__ G.filename, G.pInfo->file_attr);
659
660            /* set dir time (note trailing '/') */
661            return (error & ~MPN_MASK) | MPN_CREATED_DIR;
662        } else if (IS_OVERWRT_ALL) {
663            /* overwrite attributes of existing directory on user's request */
664
665            /* set file attributes: */
666            z_dos_chmod(__G__ G.filename, G.pInfo->file_attr);
667        }
668        /* dir existed already; don't look for data to extract */
669        return (error & ~MPN_MASK) | MPN_INF_SKIP;
670    }
671
672    *pp = '\0';                   /* done with pathcomp:  terminate it */
673
674    /* if not saving them, remove VMS version numbers (appended ";###") */
675    if (!uO.V_flag && lastsemi) {
676#ifndef MAYBE_PLAIN_FAT
677        pp = lastsemi + 1;
678#else
679# ifdef USE_LFN
680        if (use_lfn)
681            pp = lastsemi + 1;
682        else
683            pp = lastsemi;        /* semi-colon was omitted:  expect all #'s */
684# else
685        pp = lastsemi;            /* semi-colon was omitted:  expect all #'s */
686# endif
687#endif
688        while (isdigit((uch)(*pp)))
689            ++pp;
690        if (*pp == '\0')          /* only digits between ';' and end:  nuke */
691            *lastsemi = '\0';
692    }
693
694#ifdef MAYBE_PLAIN_FAT
695    maskDOSdevice(__G__ pathcomp, last_dot);
696#else
697    maskDOSdevice(__G__ pathcomp, NULL);
698#endif
699
700    if (G.pInfo->vollabel) {
701        if (strlen(pathcomp) > 11)
702            pathcomp[11] = '\0';
703    } else {
704#ifdef MAYBE_PLAIN_FAT
705# ifdef USE_LFN
706        if (!use_lfn)
707            map2fat(pathcomp, last_dot);  /* 8.3 truncation (in place) */
708# else
709        map2fat(pathcomp, last_dot);  /* 8.3 truncation (in place) */
710# endif
711#endif
712    }
713
714    if (*pathcomp == '\0') {
715        Info(slide, 1, ((char *)slide, LoadFarString(ConversionFailed),
716          FnFilter1(G.filename)));
717        return (error & ~MPN_MASK) | MPN_ERR_SKIP;
718    }
719
720    checkdir(__G__ pathcomp, APPEND_NAME);  /* returns 1 if truncated: care? */
721    checkdir(__G__ G.filename, GETPATH);
722
723    if (G.pInfo->vollabel) {    /* set the volume label now */
724        if (QCOND2)
725            Info(slide, 0, ((char *)slide, LoadFarString(Labelling),
726              (nLabelDrive + 'a' - 1),
727              FnFilter1(G.filename)));
728        if (volumelabel(G.filename)) {
729            Info(slide, 1, ((char *)slide, LoadFarString(ErrSetVolLabel)));
730            return (error & ~MPN_MASK) | MPN_ERR_SKIP;
731        }
732        /* success:  skip the "extraction" quietly */
733        return (error & ~MPN_MASK) | MPN_INF_SKIP;
734    }
735
736    return error;
737
738} /* end function mapname() */
739
740
741
742
743
744/****************************/
745/* Function maskDOSdevice() */
746/****************************/
747
748static void maskDOSdevice(__G__ pathcomp, last_dot)
749    __GDEF
750    char *pathcomp, *last_dot;
751{
752/*---------------------------------------------------------------------------
753    Put an underscore in front of the file name if the file name is a
754    DOS/WINDOWS device name like CON.*, AUX.*, PRN.*, etc. Trying to
755    extract such a file would fail at best and wedge us at worst.
756  ---------------------------------------------------------------------------*/
757#if !defined(S_IFCHR) && defined(_S_IFCHR)
758#  define S_IFCHR _S_IFCHR
759#endif
760#if !defined(S_ISCHR)
761# if defined(_S_ISCHR)
762#  define S_ISCHR(m) _S_ISCHR(m)
763# elif defined(S_IFCHR)
764#  define S_ISCHR(m) ((m) & S_IFCHR)
765# endif
766#endif
767
768#ifdef DEBUG
769    if (stat(pathcomp, &G.statbuf) == 0) {
770        Trace((stderr,
771               "maskDOSdevice() stat(\"%s\", buf) st_mode result: %X, %o\n",
772               FnFilter1(pathcomp), G.statbuf.st_mode, G.statbuf.st_mode));
773    } else {
774        Trace((stderr, "maskDOSdevice() stat(\"%s\", buf) failed\n",
775               FnFilter1(pathcomp)));
776    }
777#endif
778    if (stat(pathcomp, &G.statbuf) == 0 && S_ISCHR(G.statbuf.st_mode)) {
779        extent i;
780
781        /* pathcomp contains a name of a DOS character device (builtin or
782         * installed device driver).
783         * Prepend a '_' to allow creation of the item in the file system.
784         */
785        for (i = strlen(pathcomp) + 1; i > 0; --i)
786            pathcomp[i] = pathcomp[i - 1];
787        pathcomp[0] = '_';
788        if (last_dot != (char *)NULL)
789            last_dot++;
790    }
791} /* end function maskDOSdevice() */
792
793
794
795
796
797#ifdef MAYBE_PLAIN_FAT
798
799/**********************/
800/* Function map2fat() */
801/**********************/
802
803static void map2fat(pathcomp, last_dot)
804    char *pathcomp, *last_dot;
805{
806    char *pEnd = pathcomp + strlen(pathcomp);
807
808/*---------------------------------------------------------------------------
809    Case 1:  filename has no dot, so figure out if we should add one.  Note
810    that the algorithm does not try to get too fancy:  if there are no dots
811    already, the name either gets truncated at 8 characters or the last un-
812    derscore is converted to a dot (only if more characters are saved that
813    way).  In no case is a dot inserted between existing characters.
814
815              GRR:  have problem if filename is volume label??
816
817  ---------------------------------------------------------------------------*/
818
819    if (last_dot == (char *)NULL) {   /* no dots:  check for underscores... */
820        char *plu = strrchr(pathcomp, '_');   /* pointer to last underscore */
821
822        if ((plu != (char *)NULL) &&    /* found underscore: convert to dot? */
823            (MIN(plu - pathcomp, 8) + MIN(pEnd - plu - 1, 3) > 8)) {
824            last_dot = plu;       /* be lazy:  drop through to next if-block */
825        } else if ((pEnd - pathcomp) > 8)
826            /* no underscore; or converting underscore to dot would save less
827               chars than leaving everything in the basename */
828            pathcomp[8] = '\0';     /* truncate at 8 chars */
829        /* else whole thing fits into 8 chars or less:  no change */
830    }
831
832/*---------------------------------------------------------------------------
833    Case 2:  filename has dot in it, so truncate first half at 8 chars (shift
834    extension if necessary) and second half at three.
835  ---------------------------------------------------------------------------*/
836
837    if (last_dot != (char *)NULL) {     /* one dot is OK: */
838        *last_dot = '.';                /* put the last back in */
839
840        if ((last_dot - pathcomp) > 8) {
841            char *p, *q;
842            int i;
843
844            p = last_dot;
845            q = last_dot = pathcomp + 8;
846            for (i = 0;  (i < 4) && *p;  ++i) /* too many chars in basename: */
847                *q++ = *p++;                  /*  shift extension left and */
848            *q = '\0';                        /*  truncate/terminate it */
849        } else if ((pEnd - last_dot) > 4)
850            last_dot[4] = '\0';               /* too many chars in extension */
851        /* else filename is fine as is:  no change */
852
853        if ((last_dot - pathcomp) > 0 && last_dot[-1] == ' ')
854            last_dot[-1] = '_';               /* NO blank in front of '.'! */
855    }
856} /* end function map2fat() */
857
858#endif /* MAYBE_PLAIN_FAT */
859
860
861
862
863
864/***********************/
865/* Function checkdir() */
866/***********************/
867
868int checkdir(__G__ pathcomp, flag)
869    __GDEF
870    char *pathcomp;
871    int flag;
872/*
873 * returns:
874 *  MPN_OK          - no problem detected
875 *  MPN_INF_TRUNC   - (on APPEND_NAME) truncated filename
876 *  MPN_INF_SKIP    - path doesn't exist, not allowed to create
877 *  MPN_ERR_SKIP    - path doesn't exist, tried to create and failed; or path
878 *                    exists and is not a directory, but is supposed to be
879 *  MPN_ERR_TOOLONG - path is too long
880 *  MPN_NOMEM       - can't allocate memory for filename buffers
881 */
882{
883    static int rootlen = 0;   /* length of rootpath */
884    static char *rootpath;    /* user's "extract-to" directory */
885    static char *buildpath;   /* full path (so far) to extracted file */
886    static char *end;         /* pointer to end of buildpath ('\0') */
887#ifdef MSC
888    int attrs;                /* work around MSC stat() bug */
889#endif
890
891#   define FN_MASK   7
892#   define FUNCTION  (flag & FN_MASK)
893
894
895
896/*---------------------------------------------------------------------------
897    APPEND_DIR:  append the path component to the path being built and check
898    for its existence.  If doesn't exist and we are creating directories, do
899    so for this one; else signal success or error as appropriate.
900  ---------------------------------------------------------------------------*/
901
902    if (FUNCTION == APPEND_DIR) {
903        int too_long = FALSE;
904
905        Trace((stderr, "appending dir segment [%s]\n", FnFilter1(pathcomp)));
906        while ((*end = *pathcomp++) != '\0')
907            ++end;
908
909        /* GRR:  could do better check, see if overrunning buffer as we go:
910         * check end-buildpath after each append, set warning variable if
911         * within 20 of FILNAMSIZ; then if var set, do careful check when
912         * appending.  Clear variable when begin new path. */
913
914        if ((end-buildpath) > FILNAMSIZ-3)  /* need '/', one-char name, '\0' */
915            too_long = TRUE;                /* check if extracting directory? */
916#ifdef MSC /* MSC 6.00 bug:  stat(non-existent-dir) == 0 [exists!] */
917        if (_dos_getfileattr(buildpath, &attrs) || stat(buildpath, &G.statbuf))
918#else
919        if (SSTAT(buildpath, &G.statbuf))   /* path doesn't exist */
920#endif
921        {
922            if (!G.create_dirs) { /* told not to create (freshening) */
923                free(buildpath);
924                /* path doesn't exist:  nothing to do */
925                return MPN_INF_SKIP;
926            }
927            if (too_long) {
928                Info(slide, 1, ((char *)slide, LoadFarString(PathTooLong),
929                  FnFilter1(buildpath)));
930                free(buildpath);
931                /* no room for filenames:  fatal */
932                return MPN_ERR_TOOLONG;
933            }
934            if (MKDIR(buildpath, 0777) == -1) {   /* create the directory */
935                Info(slide, 1, ((char *)slide, LoadFarString(CantCreateDir),
936                  FnFilter2(buildpath), FnFilter1(G.filename)));
937                free(buildpath);
938                /* path didn't exist, tried to create, failed */
939                return MPN_ERR_SKIP;
940            }
941            created_dir = TRUE;
942        } else if (!S_ISDIR(G.statbuf.st_mode)) {
943            Info(slide, 1, ((char *)slide, LoadFarString(DirIsntDirectory),
944              FnFilter2(buildpath), FnFilter1(G.filename)));
945            free(buildpath);
946            /* path existed but wasn't dir */
947            return MPN_ERR_SKIP;
948        }
949        if (too_long) {
950            Info(slide, 1, ((char *)slide, LoadFarString(PathTooLong),
951              FnFilter1(buildpath)));
952            free(buildpath);
953            /* no room for filenames:  fatal */
954            return MPN_ERR_TOOLONG;
955        }
956        *end++ = '/';
957        *end = '\0';
958        Trace((stderr, "buildpath now = [%s]\n", FnFilter1(buildpath)));
959        return MPN_OK;
960
961    } /* end if (FUNCTION == APPEND_DIR) */
962
963/*---------------------------------------------------------------------------
964    GETPATH:  copy full path to the string pointed at by pathcomp, and free
965    buildpath.
966  ---------------------------------------------------------------------------*/
967
968    if (FUNCTION == GETPATH) {
969        strcpy(pathcomp, buildpath);
970        Trace((stderr, "getting and freeing path [%s]\n",
971          FnFilter1(pathcomp)));
972        free(buildpath);
973        buildpath = end = (char *)NULL;
974        return MPN_OK;
975    }
976
977/*---------------------------------------------------------------------------
978    APPEND_NAME:  assume the path component is the filename; append it and
979    return without checking for existence.
980  ---------------------------------------------------------------------------*/
981
982    if (FUNCTION == APPEND_NAME) {
983#ifdef NOVELL_BUG_WORKAROUND
984        if (end == buildpath && !G.pInfo->vollabel) {
985            /* work-around for Novell's "overwriting executables" bug:
986               prepend "./" to name when no path component is specified */
987            *end++ = '.';
988            *end++ = '/';
989        }
990#endif /* NOVELL_BUG_WORKAROUND */
991        Trace((stderr, "appending filename [%s]\n", FnFilter1(pathcomp)));
992        while ((*end = *pathcomp++) != '\0') {
993            ++end;
994            if ((end-buildpath) >= FILNAMSIZ) {
995                *--end = '\0';
996                Info(slide, 1, ((char *)slide, LoadFarString(PathTooLongTrunc),
997                  FnFilter1(G.filename), FnFilter2(buildpath)));
998                return MPN_INF_TRUNC;   /* filename truncated */
999            }
1000        }
1001        Trace((stderr, "buildpath now = [%s]\n", FnFilter1(buildpath)));
1002        /* could check for existence here, prompt for new name... */
1003        return MPN_OK;
1004    }
1005
1006/*---------------------------------------------------------------------------
1007    INIT:  allocate and initialize buffer space for the file currently being
1008    extracted.  If file was renamed with an absolute path, don't prepend the
1009    extract-to path.
1010  ---------------------------------------------------------------------------*/
1011
1012    if (FUNCTION == INIT) {
1013        Trace((stderr, "initializing buildpath to "));
1014        /* allocate space for full filename, root path, and maybe "./" */
1015        if ((buildpath = (char *)malloc(strlen(G.filename)+rootlen+3)) ==
1016            (char *)NULL)
1017            return MPN_NOMEM;
1018        if (G.pInfo->vollabel) {
1019/* GRR:  for network drives, do strchr() and return IZ_VOL_LABEL if not [1] */
1020            if (renamed_fullpath && pathcomp[1] == ':')
1021                *buildpath = (char)ToLower(*pathcomp);
1022            else if (!renamed_fullpath && rootlen > 1 && rootpath[1] == ':')
1023                *buildpath = (char)ToLower(*rootpath);
1024            else {
1025                GETDRIVE(nLabelDrive);   /* assumed that a == 1, b == 2, etc. */
1026                *buildpath = (char)(nLabelDrive - 1 + 'a');
1027            }
1028            nLabelDrive = *buildpath - 'a' + 1;        /* save for mapname() */
1029            if (uO.volflag == 0 || *buildpath < 'a' || /* no label/bogus disk */
1030               (uO.volflag == 1 && !isfloppy(nLabelDrive))) /* -$:  no fixed */
1031            {
1032                free(buildpath);
1033                return MPN_VOL_LABEL;    /* skipping with message */
1034            }
1035            *buildpath = '\0';
1036            end = buildpath;
1037        } else if (renamed_fullpath) {   /* pathcomp = valid data */
1038            end = buildpath;
1039            while ((*end = *pathcomp++) != '\0')
1040                ++end;
1041        } else if (rootlen > 0) {
1042            strcpy(buildpath, rootpath);
1043            end = buildpath + rootlen;
1044        } else {
1045            *buildpath = '\0';
1046            end = buildpath;
1047        }
1048        Trace((stderr, "[%s]\n", FnFilter1(buildpath)));
1049        return MPN_OK;
1050    }
1051
1052/*---------------------------------------------------------------------------
1053    ROOT:  if appropriate, store the path in rootpath and create it if neces-
1054    sary; else assume it's a zipfile member and return.  This path segment
1055    gets used in extracting all members from every zipfile specified on the
1056    command line.  Note that under OS/2 and MS-DOS, if a candidate extract-to
1057    directory specification includes a drive letter (leading "x:"), it is
1058    treated just as if it had a trailing '/'--that is, one directory level
1059    will be created if the path doesn't exist, unless this is otherwise pro-
1060    hibited (e.g., freshening).
1061  ---------------------------------------------------------------------------*/
1062
1063#if (!defined(SFX) || defined(SFX_EXDIR))
1064    if (FUNCTION == ROOT) {
1065        Trace((stderr, "initializing root path to [%s]\n",
1066          FnFilter1(pathcomp)));
1067        if (pathcomp == (char *)NULL) {
1068            rootlen = 0;
1069            return MPN_OK;
1070        }
1071        if (rootlen > 0)        /* rootpath was already set, nothing to do */
1072            return MPN_OK;
1073        if ((rootlen = strlen(pathcomp)) > 0) {
1074            int had_trailing_pathsep=FALSE, has_drive=FALSE, add_dot=FALSE;
1075            char *tmproot;
1076
1077            if ((tmproot = (char *)malloc(rootlen+3)) == (char *)NULL) {
1078                rootlen = 0;
1079                return MPN_NOMEM;
1080            }
1081            strcpy(tmproot, pathcomp);
1082            if (isalpha((uch)tmproot[0]) && tmproot[1] == ':')
1083                has_drive = TRUE;   /* drive designator */
1084            if (tmproot[rootlen-1] == '/' || tmproot[rootlen-1] == '\\') {
1085                tmproot[--rootlen] = '\0';
1086                had_trailing_pathsep = TRUE;
1087            }
1088            if (has_drive && (rootlen == 2)) {
1089                if (!had_trailing_pathsep)   /* i.e., original wasn't "x:/" */
1090                    add_dot = TRUE;    /* relative path: add '.' before '/' */
1091            } else if (rootlen > 0) {     /* need not check "x:." and "x:/" */
1092#ifdef MSC
1093                /* MSC 6.00 bug:  stat(non-existent-dir) == 0 [exists!] */
1094                if (_dos_getfileattr(tmproot, &attrs) ||
1095                    SSTAT(tmproot, &G.statbuf) || !S_ISDIR(G.statbuf.st_mode))
1096#else
1097                if (SSTAT(tmproot, &G.statbuf) || !S_ISDIR(G.statbuf.st_mode))
1098#endif
1099                {
1100                    /* path does not exist */
1101                    if (!G.create_dirs /* || iswild(tmproot) */ ) {
1102                        free(tmproot);
1103                        rootlen = 0;
1104                        /* treat as stored file */
1105                        return MPN_INF_SKIP;
1106                    }
1107/* GRR:  scan for wildcard characters?  OS-dependent...  if find any, return 2:
1108 * treat as stored file(s) */
1109                    /* create directory (could add loop here scanning tmproot
1110                     * to create more than one level, but really necessary?) */
1111                    if (MKDIR(tmproot, 0777) == -1) {
1112                        Info(slide, 1, ((char *)slide,
1113                          LoadFarString(CantCreateExtractDir),
1114                          FnFilter1(tmproot)));
1115                        free(tmproot);
1116                        rootlen = 0;
1117                        /* path didn't exist, tried to create, failed: */
1118                        /* file exists, or need 2+ subdir levels */
1119                        return MPN_ERR_SKIP;
1120                    }
1121                }
1122            }
1123            if (add_dot)                    /* had just "x:", make "x:." */
1124                tmproot[rootlen++] = '.';
1125            tmproot[rootlen++] = '/';
1126            tmproot[rootlen] = '\0';
1127            if ((rootpath = (char *)realloc(tmproot, rootlen+1)) == NULL) {
1128                free(tmproot);
1129                rootlen = 0;
1130                return MPN_NOMEM;
1131            }
1132            Trace((stderr, "rootpath now = [%s]\n", FnFilter1(rootpath)));
1133        }
1134        return MPN_OK;
1135    }
1136#endif /* !SFX || SFX_EXDIR */
1137
1138/*---------------------------------------------------------------------------
1139    END:  free rootpath, immediately prior to program exit.
1140  ---------------------------------------------------------------------------*/
1141
1142    if (FUNCTION == END) {
1143        Trace((stderr, "freeing rootpath\n"));
1144        if (rootlen > 0) {
1145            free(rootpath);
1146            rootlen = 0;
1147        }
1148        return MPN_OK;
1149    }
1150
1151    return MPN_INVALID; /* should never reach */
1152
1153} /* end function checkdir() */
1154
1155
1156
1157
1158
1159
1160/***********************/
1161/* Function isfloppy() */
1162/***********************/
1163
1164static int isfloppy(nDrive)  /* more precisely, is it removable? */
1165    int nDrive;
1166{
1167    union REGS regs;
1168
1169    regs.h.ah = 0x44;
1170    regs.h.al = 0x08;
1171    regs.h.bl = (uch)nDrive;
1172#ifdef __EMX__
1173    _int86(0x21, &regs, &regs);
1174    if (WREGS(regs,flags) & 1)
1175#else
1176    intdos(&regs, &regs);
1177    if (WREGS(regs,cflag))        /* error:  do default a/b check instead */
1178#endif
1179    {
1180        Trace((stderr,
1181          "error in DOS function 0x44 (AX = 0x%04x):  guessing instead...\n",
1182          (unsigned int)(WREGS(regs,ax))));
1183        return (nDrive == 1 || nDrive == 2)? TRUE : FALSE;
1184    } else
1185        return WREGS(regs,ax)? FALSE : TRUE;
1186}
1187
1188
1189
1190
1191/**************************/
1192/* Function z_dos_chmod() */
1193/**************************/
1194
1195static int z_dos_chmod(__G__ fname, attributes)
1196    __GDEF
1197    ZCONST char *fname;
1198    int attributes;
1199{
1200    char *name;
1201    unsigned fnamelength;
1202    int errv;
1203
1204    /* set file attributes:
1205       The DOS `chmod' system call requires to mask out the
1206       directory and volume_label attribute bits.
1207       And, a trailing '/' has to be removed from the directory name,
1208       the DOS `chmod' system call does not accept it. */
1209    fnamelength = strlen(fname);
1210    if (fnamelength > 1 && fname[fnamelength-1] == '/' &&
1211        fname[fnamelength-2] != ':' &&
1212        (name = (char *)malloc(fnamelength)) != (char *)NULL) {
1213        strncpy(name, fname, fnamelength-1);
1214        name[fnamelength-1] = '\0';
1215    } else {
1216        name = (char *)fname;
1217        fnamelength = 0;
1218    }
1219
1220#if defined(__TURBOC__) || (defined(__DJGPP__) && (__DJGPP__ >= 2))
1221#   if (defined(__BORLANDC__) && (__BORLANDC__ >= 0x0452))
1222#     define Chmod  _rtl_chmod
1223#   else
1224#     define Chmod  _chmod
1225#   endif
1226    errv = (Chmod(name, 1, attributes & (~FSUBDIR & ~FVOLID)) !=
1227            (attributes & (~FSUBDIR & ~FVOLID)));
1228#   undef Chmod
1229#else /* !(__TURBOC__ || (__DJGPP__ && __DJGPP__ >= 2)) */
1230    errv = (_dos_setfileattr(name, attributes & (~FSUBDIR & ~FVOLID)) != 0);
1231#endif /* ?(__TURBOC__ || (__DJGPP__ && __DJGPP__ >= 2)) */
1232    if (errv)
1233        Info(slide, 1, ((char *)slide, LoadFarString(AttribsMayBeWrong)));
1234
1235    if (fnamelength > 0)
1236        free(name);
1237    return errv;
1238} /* end function z_dos_chmod() */
1239
1240
1241
1242
1243#if (!defined(__GO32__) && !defined(__EMX__))
1244
1245typedef struct dosfcb {
1246    uch  flag;        /* ff to indicate extended FCB */
1247    char res[5];      /* reserved */
1248    uch  vattr;       /* attribute */
1249    uch  drive;       /* drive (1=A, 2=B, ...) */
1250    uch  vn[11];      /* file or volume name */
1251    char dmmy[5];
1252    uch  nn[11];      /* holds new name if renaming (else reserved) */
1253    char dmmy2[9];
1254} dos_fcb;
1255
1256/**************************/
1257/* Function volumelabel() */
1258/**************************/
1259
1260static int volumelabel(newlabel)
1261    ZCONST char *newlabel;
1262{
1263#ifdef DEBUG
1264    char *p;
1265#endif
1266    int len = strlen(newlabel);
1267    int fcbseg, dtaseg, fcboff, dtaoff, retv;
1268    dos_fcb  fcb, dta, far *pfcb=&fcb, far *pdta=&dta;
1269    struct SREGS sregs;
1270    union REGS regs;
1271
1272
1273/*---------------------------------------------------------------------------
1274    Label the diskette specified by nLabelDrive using FCB calls.  (Old ver-
1275    sions of MS-DOS and OS/2 DOS boxes can't use DOS function 3Ch to create
1276    labels.)  Must use far pointers for MSC FP_* macros to work; must pad
1277    FCB filenames with spaces; and cannot include dot in 8th position.  May
1278    or may not need to zero out FCBs before using; do so just in case.
1279  ---------------------------------------------------------------------------*/
1280
1281#ifdef WATCOMC_386
1282    int truseg;
1283
1284    memset(&sregs, 0, sizeof(sregs));
1285    memset(&regs, 0, sizeof(regs));
1286    /* PMODE/W does not support extended versions of any dos FCB functions, */
1287    /* so we have to use brute force, allocating real mode memory for them. */
1288    regs.w.ax = 0x0100;
1289    regs.w.bx = (2 * sizeof(dos_fcb) + 15) >> 4;   /* size in paragraphs */
1290    int386(0x31, &regs, &regs);            /* DPMI allocate DOS memory */
1291    if (regs.w.cflag)
1292        return DF_MDY;                     /* no memory, return default */
1293    truseg = regs.w.dx;                    /* protected mode selector */
1294    dtaseg = regs.w.ax;                    /* real mode paragraph */
1295    fcboff = 0;
1296    dtaoff = sizeof(dos_fcb);
1297#ifdef XXX__MK_FP_IS_BROKEN
1298    /* XXX  This code may not be trustworthy in general, though it is   */
1299    /* valid with DOS/4GW and PMODE/w, which is all we support for now. */
1300    regs.w.ax = 6;
1301    regs.w.bx = truseg;
1302    int386(0x31, &regs, &regs);            /* convert seg to linear address */
1303    pfcb = (dos_fcb far *) (((ulg) regs.w.cx << 16) | regs.w.dx);
1304    /* pfcb = (dos_fcb far *) ((ulg) dtaseg << 4); */
1305    pdta = pfcb + 1;
1306#else
1307    pfcb = MK_FP(truseg, fcboff);
1308    pdta = MK_FP(truseg, dtaoff);
1309#endif
1310    _fmemset((char far *)pfcb, 0, 2 * sizeof(dos_fcb));
1311    /* we pass the REAL MODE paragraph to the dos interrupts: */
1312    fcbseg = dtaseg;
1313
1314#else /* !WATCOMC_386 */
1315
1316    memset((char *)&dta, 0, sizeof(dos_fcb));
1317    memset((char *)&fcb, 0, sizeof(dos_fcb));
1318    fcbseg = FP_SEG(pfcb);
1319    fcboff = FP_OFF(pfcb);
1320    dtaseg = FP_SEG(pdta);
1321    dtaoff = FP_OFF(pdta);
1322#endif /* ?WATCOMC_386 */
1323
1324#ifdef DEBUG
1325    for (p = (char *)&dta; (p - (char *)&dta) < sizeof(dos_fcb); ++p)
1326        if (*p)
1327            fprintf(stderr, "error:  dta[%d] = %x\n", (p - (char *)&dta), *p);
1328    for (p = (char *)&fcb; (p - (char *)&fcb) < sizeof(dos_fcb); ++p)
1329        if (*p)
1330            fprintf(stderr, "error:  fcb[%d] = %x\n", (p - (char *)&fcb), *p);
1331    printf("testing pointer macros:\n");
1332    segread(&sregs);
1333    printf("cs = %x, ds = %x, es = %x, ss = %x\n", sregs.cs, sregs.ds, sregs.es,
1334      sregs.ss);
1335#endif /* DEBUG */
1336
1337#if 0
1338#ifdef __TURBOC__
1339    bdosptr(0x1a, dta, DO_NOT_CARE);
1340#else
1341    (intdosx method below)
1342#endif
1343#endif /* 0 */
1344
1345    /* set the disk transfer address for subsequent FCB calls */
1346    sregs.ds = dtaseg;
1347    WREGS(regs,dx) = dtaoff;
1348    Trace((stderr, "segment:offset of pdta = %x:%x\n", dtaseg, dtaoff));
1349    Trace((stderr, "&dta = %lx, pdta = %lx\n", (ulg)&dta, (ulg)pdta));
1350    regs.h.ah = 0x1a;
1351    F_intdosx(&regs, &regs, &sregs);
1352
1353    /* fill in the FCB */
1354    sregs.ds = fcbseg;
1355    WREGS(regs,dx) = fcboff;
1356    pfcb->flag = 0xff;          /* extended FCB */
1357    pfcb->vattr = 0x08;         /* attribute:  disk volume label */
1358    pfcb->drive = (uch)nLabelDrive;
1359
1360#ifdef DEBUG
1361    Trace((stderr, "segment:offset of pfcb = %x:%x\n",
1362      (unsigned int)(sregs.ds),
1363      (unsigned int)(WREGS(regs,dx))));
1364    Trace((stderr, "&fcb = %lx, pfcb = %lx\n", (ulg)&fcb, (ulg)pfcb));
1365    Trace((stderr, "(2nd check:  labelling drive %c:)\n", pfcb->drive-1+'A'));
1366    if (pfcb->flag != fcb.flag)
1367        fprintf(stderr, "error:  pfcb->flag = %d, fcb.flag = %d\n",
1368          pfcb->flag, fcb.flag);
1369    if (pfcb->drive != fcb.drive)
1370        fprintf(stderr, "error:  pfcb->drive = %d, fcb.drive = %d\n",
1371          pfcb->drive, fcb.drive);
1372    if (pfcb->vattr != fcb.vattr)
1373        fprintf(stderr, "error:  pfcb->vattr = %d, fcb.vattr = %d\n",
1374          pfcb->vattr, fcb.vattr);
1375#endif /* DEBUG */
1376
1377    /* check for existing label */
1378    Trace((stderr, "searching for existing label via FCBs\n"));
1379    regs.h.ah = 0x11;      /* FCB find first */
1380#ifdef WATCOMC_386
1381    _fstrncpy((char far *)&pfcb->vn, "???????????", 11);
1382#else
1383    strncpy((char *)fcb.vn, "???????????", 11);   /* i.e., "*.*" */
1384#endif /* ?WATCOMC_386 */
1385    Trace((stderr, "fcb.vn = %lx\n", (ulg)fcb.vn));
1386    Trace((stderr, "regs.h.ah = %x, regs.x.dx = %04x, sregs.ds = %04x\n",
1387      (unsigned int)(regs.h.ah), (unsigned int)(WREGS(regs,dx)),
1388      (unsigned int)(sregs.ds)));
1389    Trace((stderr, "flag = %x, drive = %d, vattr = %x, vn = %s = %s.\n",
1390      fcb.flag, fcb.drive, fcb.vattr, fcb.vn, pfcb->vn));
1391    F_intdosx(&regs, &regs, &sregs);
1392
1393/*---------------------------------------------------------------------------
1394    If not previously labelled, write a new label.  Otherwise just rename,
1395    since MS-DOS 2.x has a bug that damages the FAT when the old label is
1396    deleted.
1397  ---------------------------------------------------------------------------*/
1398
1399    if (regs.h.al) {
1400        Trace((stderr, "no label found\n\n"));
1401        regs.h.ah = 0x16;                 /* FCB create file */
1402#ifdef WATCOMC_386
1403        _fstrncpy((char far *)pfcb->vn, newlabel, len);
1404        if (len < 11)
1405            _fstrncpy((char far *)(pfcb->vn+len), "           ", 11-len);
1406#else
1407        strncpy((char *)fcb.vn, newlabel, len);
1408        if (len < 11)   /* fill with spaces */
1409            strncpy((char *)(fcb.vn+len), "           ", 11-len);
1410#endif
1411        Trace((stderr, "fcb.vn = %lx  pfcb->vn = %lx\n", (ulg)fcb.vn,
1412          (ulg)pfcb->vn));
1413        Trace((stderr, "flag = %x, drive = %d, vattr = %x\n", fcb.flag,
1414          fcb.drive, fcb.vattr));
1415        Trace((stderr, "vn = %s = %s.\n", fcb.vn, pfcb->vn));
1416        F_intdosx(&regs, &regs, &sregs);
1417        regs.h.ah = 0x10;                 /* FCB close file */
1418        if (regs.h.al) {
1419            Trace((stderr, "unable to write volume name (AL = %x)\n",
1420              (unsigned int)(regs.h.al)));
1421            F_intdosx(&regs, &regs, &sregs);
1422            retv = 1;
1423        } else {
1424            F_intdosx(&regs, &regs, &sregs);
1425            Trace((stderr, "new volume label [%s] written\n", newlabel));
1426            retv = 0;
1427        }
1428    } else {
1429        Trace((stderr, "found old label [%s]\n\n", dta.vn));  /* not term. */
1430        regs.h.ah = 0x17;                 /* FCB rename */
1431#ifdef WATCOMC_386
1432        _fstrncpy((char far *)pfcb->vn, (char far *)pdta->vn, 11);
1433        _fstrncpy((char far *)pfcb->nn, newlabel, len);
1434        if (len < 11)
1435            _fstrncpy((char far *)(pfcb->nn+len), "           ", 11-len);
1436#else
1437        strncpy((char *)fcb.vn, (char *)dta.vn, 11);
1438        strncpy((char *)fcb.nn, newlabel, len);
1439        if (len < 11)                     /* fill with spaces */
1440            strncpy((char *)(fcb.nn+len), "           ", 11-len);
1441#endif
1442        Trace((stderr, "fcb.vn = %lx  pfcb->vn = %lx\n", (ulg)fcb.vn,
1443          (ulg)pfcb->vn));
1444        Trace((stderr, "fcb.nn = %lx  pfcb->nn = %lx\n", (ulg)fcb.nn,
1445          (ulg)pfcb->nn));
1446        Trace((stderr, "flag = %x, drive = %d, vattr = %x\n", fcb.flag,
1447          fcb.drive, fcb.vattr));
1448        Trace((stderr, "vn = %s = %s.\n", fcb.vn, pfcb->vn));
1449        Trace((stderr, "nn = %s = %s.\n", fcb.nn, pfcb->nn));
1450        F_intdosx(&regs, &regs, &sregs);
1451        if (regs.h.al) {
1452            Trace((stderr, "Unable to change volume name (AL = %x)\n",
1453              (unsigned int)(regs.h.al)));
1454            retv = 1;
1455        } else {
1456            Trace((stderr, "volume label changed to [%s]\n", newlabel));
1457            retv = 0;
1458        }
1459    }
1460#ifdef WATCOMC_386
1461    regs.w.ax = 0x0101;                    /* free dos memory */
1462    regs.w.dx = truseg;
1463    int386(0x31, &regs, &regs);
1464#endif
1465    return retv;
1466
1467} /* end function volumelabel() */
1468
1469#endif /* !__GO32__ && !__EMX__ */
1470
1471
1472
1473
1474
1475#if (defined(USE_EF_UT_TIME) || defined(TIMESTAMP))
1476/* The following DOS date/time structure is machine-dependent as it
1477 * assumes "little-endian" byte order.  For MSDOS-specific code, which
1478 * is run on ix86 CPUs (or emulators), this assumption is valid; but
1479 * care should be taken when using this code as template for other ports.
1480 */
1481typedef union {
1482    ulg z_dostime;
1483# ifdef __TURBOC__
1484    struct ftime ft;            /* system file time record */
1485# endif
1486    struct {                    /* date and time words */
1487        ush ztime;              /* DOS file modification time word */
1488        ush zdate;              /* DOS file modification date word */
1489    } zft;
1490    struct {                    /* DOS date/time components bitfield */
1491        unsigned zt_se : 5;
1492        unsigned zt_mi : 6;
1493        unsigned zt_hr : 5;
1494        unsigned zd_dy : 5;
1495        unsigned zd_mo : 4;
1496        unsigned zd_yr : 7;
1497    } z_dtf;
1498} dos_fdatetime;
1499#endif /* USE_EF_UT_TIME || TIMESTAMP */
1500
1501
1502/****************************/
1503/* Function close_outfile() */
1504/****************************/
1505
1506void close_outfile(__G)
1507    __GDEF
1508 /*
1509  * MS-DOS VERSION
1510  *
1511  * Set the output file date/time stamp according to information from the
1512  * zipfile directory record for this member, then close the file and set
1513  * its permissions (archive, hidden, read-only, system).  Aside from closing
1514  * the file, this routine is optional (but most compilers support it).
1515  */
1516{
1517    /* skip restoring time stamps on user's request */
1518    if (uO.D_flag <= 1) {
1519#ifdef USE_EF_UT_TIME
1520        dos_fdatetime dos_dt;
1521        iztimes z_utime;
1522        struct tm *t;
1523#endif /* USE_EF_UT_TIME */
1524
1525
1526/*---------------------------------------------------------------------------
1527        Copy and/or convert time and date variables, if necessary; then set
1528        the file time/date.  WEIRD BORLAND "BUG":  if output is buffered,
1529        and if run under at least some versions of DOS (e.g., 6.0), and if
1530        files are smaller than DOS physical block size (i.e., 512 bytes) (?),
1531        then files MAY NOT get timestamped correctly--apparently setftime()
1532        occurs before any data are written to the file, and when file is
1533        closed and buffers are flushed, timestamp is overwritten with
1534        current time.  Even with a 32K buffer, this does not seem to occur
1535        with larger files.  UnZip output is now unbuffered, but if it were
1536        not, could still avoid problem by adding "fflush(outfile)" just
1537        before setftime() call.  Weird, huh?
1538  ---------------------------------------------------------------------------*/
1539
1540#ifdef USE_EF_UT_TIME
1541        if (G.extra_field &&
1542#ifdef IZ_CHECK_TZ
1543            G.tz_is_valid &&
1544#endif
1545            (ef_scan_for_izux(G.extra_field, G.lrec.extra_field_length, 0,
1546                              G.lrec.last_mod_dos_datetime, &z_utime, NULL)
1547             & EB_UT_FL_MTIME))
1548        {
1549            TTrace((stderr, "close_outfile:  Unix e.f. modif. time = %ld\n",
1550              z_utime.mtime));
1551            /* round up (down if "up" overflows) to even seconds */
1552            if (z_utime.mtime & 1)
1553                z_utime.mtime = (z_utime.mtime + 1 > z_utime.mtime) ?
1554                                 z_utime.mtime + 1 : z_utime.mtime - 1;
1555            TIMET_TO_NATIVE(z_utime.mtime) /* NOP unless MSC 7 or Macintosh */
1556            t = localtime(&(z_utime.mtime));
1557        } else
1558            t = (struct tm *)NULL;
1559        if (t != (struct tm *)NULL) {
1560            if (t->tm_year < 80) {
1561                dos_dt.z_dtf.zt_se = 0;
1562                dos_dt.z_dtf.zt_mi = 0;
1563                dos_dt.z_dtf.zt_hr = 0;
1564                dos_dt.z_dtf.zd_dy = 1;
1565                dos_dt.z_dtf.zd_mo = 1;
1566                dos_dt.z_dtf.zd_yr = 0;
1567            } else {
1568                dos_dt.z_dtf.zt_se = t->tm_sec >> 1;
1569                dos_dt.z_dtf.zt_mi = t->tm_min;
1570                dos_dt.z_dtf.zt_hr = t->tm_hour;
1571                dos_dt.z_dtf.zd_dy = t->tm_mday;
1572                dos_dt.z_dtf.zd_mo = t->tm_mon + 1;
1573                dos_dt.z_dtf.zd_yr = t->tm_year - 80;
1574            }
1575        } else {
1576            dos_dt.z_dostime = G.lrec.last_mod_dos_datetime;
1577        }
1578# ifdef __TURBOC__
1579        setftime(fileno(G.outfile), &dos_dt.ft);
1580# else
1581        _dos_setftime(fileno(G.outfile), dos_dt.zft.zdate, dos_dt.zft.ztime);
1582# endif
1583#else /* !USE_EF_UT_TIME */
1584# ifdef __TURBOC__
1585        setftime(fileno(G.outfile),
1586                 (struct ftime *)(&(G.lrec.last_mod_dos_datetime)));
1587# else
1588        _dos_setftime(fileno(G.outfile),
1589                      (ush)(G.lrec.last_mod_dos_datetime >> 16),
1590                      (ush)(G.lrec.last_mod_dos_datetime));
1591# endif
1592#endif /* ?USE_EF_UT_TIME */
1593    }
1594
1595/*---------------------------------------------------------------------------
1596    And finally we can close the file...at least everybody agrees on how to
1597    do *this*.  I think...  Also change the mode according to the stored file
1598    attributes, since we didn't do that when we opened the dude.
1599  ---------------------------------------------------------------------------*/
1600
1601    fclose(G.outfile);
1602
1603    z_dos_chmod(__G__ G.filename, G.pInfo->file_attr);
1604
1605} /* end function close_outfile() */
1606
1607
1608
1609
1610
1611#ifdef TIMESTAMP
1612
1613/*************************/
1614/* Function stamp_file() */
1615/*************************/
1616
1617int stamp_file(fname, modtime)
1618    ZCONST char *fname;
1619    time_t modtime;
1620{
1621    dos_fdatetime dos_dt;
1622    time_t t_even;
1623    struct tm *t;
1624    int fd;                             /* file handle */
1625
1626    /* round up (down if "up" overflows) to even seconds */
1627    t_even = ((modtime + 1 > modtime) ? modtime + 1 : modtime) & (~1);
1628    TIMET_TO_NATIVE(t_even)             /* NOP unless MSC 7.0 or Macintosh */
1629    t = localtime(&t_even);
1630    if (t == (struct tm *)NULL)
1631        return -1;                      /* time conversion error */
1632    if (t->tm_year < 80) {
1633        dos_dt.z_dtf.zt_se = 0;
1634        dos_dt.z_dtf.zt_mi = 0;
1635        dos_dt.z_dtf.zt_hr = 0;
1636        dos_dt.z_dtf.zd_dy = 1;
1637        dos_dt.z_dtf.zd_mo = 1;
1638        dos_dt.z_dtf.zd_yr = 0;
1639    } else {
1640        dos_dt.z_dtf.zt_se = t->tm_sec >> 1;
1641        dos_dt.z_dtf.zt_mi = t->tm_min;
1642        dos_dt.z_dtf.zt_hr = t->tm_hour;
1643        dos_dt.z_dtf.zd_dy = t->tm_mday;
1644        dos_dt.z_dtf.zd_mo = t->tm_mon + 1;
1645        dos_dt.z_dtf.zd_yr = t->tm_year - 80;
1646    }
1647    if (((fd = open((char *)fname, 0)) == -1) ||
1648# ifdef __TURBOC__
1649        (setftime(fd, &dos_dt.ft)))
1650# else
1651        (_dos_setftime(fd, dos_dt.zft.zdate, dos_dt.zft.ztime)))
1652# endif
1653    {
1654        if (fd != -1)
1655            close(fd);
1656        return -1;
1657    }
1658    close(fd);
1659    return 0;
1660
1661} /* end function stamp_file() */
1662
1663#endif /* TIMESTAMP */
1664
1665
1666
1667
1668void prepare_ISO_OEM_translat(__G)
1669   __GDEF
1670{
1671    switch (getdoscodepage()) {
1672    case 437:
1673    case 850:
1674    case 858:
1675#ifdef IZ_ISO2OEM_ARRAY
1676        iso2oem = iso2oem_850;
1677#endif
1678#ifdef IZ_OEM2ISO_ARRAY
1679        oem2iso = oem2iso_850;
1680#endif
1681
1682    case 932:   /* Japanese */
1683    case 949:   /* Korean */
1684    case 936:   /* Chinese, simple */
1685    case 950:   /* Chinese, traditional */
1686    case 874:   /* Thai */
1687    case 1258:  /* Vietnamese */
1688#ifdef IZ_ISO2OEM_ARRAY
1689        iso2oem = NULL;
1690#endif
1691#ifdef IZ_OEM2ISO_ARRAY
1692        oem2iso = NULL;
1693#endif
1694
1695    default:
1696#ifdef IZ_ISO2OEM_ARRAY
1697       iso2oem = NULL;
1698#endif
1699#ifdef IZ_OEM2ISO_ARRAY
1700       oem2iso = NULL;
1701#endif
1702    }
1703} /* end function prepare_ISO_OEM_translat() */
1704
1705
1706
1707
1708#ifndef SFX
1709
1710/*************************/
1711/* Function dateformat() */
1712/*************************/
1713
1714int dateformat()
1715{
1716
1717/*---------------------------------------------------------------------------
1718    For those operating systems that support it, this function returns a
1719    value that tells how national convention says that numeric dates are
1720    displayed.  Return values are DF_YMD, DF_DMY and DF_MDY (the meanings
1721    should be fairly obvious).
1722  ---------------------------------------------------------------------------*/
1723
1724#ifndef WINDLL
1725    ush CountryInfo[18];
1726#if (!defined(__GO32__) && !defined(__EMX__))
1727    ush far *_CountryInfo = CountryInfo;
1728    struct SREGS sregs;
1729    union REGS regs;
1730#ifdef WATCOMC_386
1731    ush seg, para;
1732
1733    memset(&sregs, 0, sizeof(sregs));
1734    memset(&regs, 0, sizeof(regs));
1735    /* PMODE/W does not support an extended version of dos function 38,   */
1736    /* so we have to use brute force, allocating real mode memory for it. */
1737    regs.w.ax = 0x0100;
1738    regs.w.bx = 3;                         /* 36 bytes rounds up to 48 */
1739    int386(0x31, &regs, &regs);            /* DPMI allocate DOS memory */
1740    if (regs.w.cflag)
1741        return DF_MDY;                     /* no memory, return default */
1742    seg = regs.w.dx;
1743    para = regs.w.ax;
1744
1745#ifdef XXX__MK_FP_IS_BROKEN
1746    /* XXX  This code may not be trustworthy in general, though it is
1747     * valid with DOS/4GW and PMODE/w, which is all we support for now. */
1748 /* _CountryInfo = (ush far *) (para << 4); */ /* works for some extenders */
1749    regs.w.ax = 6;
1750    regs.w.bx = seg;
1751    int386(0x31, &regs, &regs);            /* convert seg to linear address */
1752    _CountryInfo = (ush far *) (((ulg) regs.w.cx << 16) | regs.w.dx);
1753#else
1754    _CountryInfo = (ush far *) MK_FP(seg, 0);
1755#endif
1756
1757    sregs.ds = para;                       /* real mode paragraph */
1758    regs.w.dx = 0;                         /* no offset from segment */
1759    regs.w.ax = 0x3800;
1760    int86x_realmode(0x21, &regs, &regs, &sregs);
1761    CountryInfo[0] = regs.w.cflag ? 0 : _CountryInfo[0];
1762    regs.w.ax = 0x0101;
1763    regs.w.dx = seg;
1764    int386(0x31, &regs, &regs);              /* DPMI free DOS memory */
1765
1766#else /* !WATCOMC_386 */
1767
1768    sregs.ds  = FP_SEG(_CountryInfo);
1769    regs.x.dx = FP_OFF(_CountryInfo);
1770    regs.x.ax = 0x3800;
1771    intdosx(&regs, &regs, &sregs);
1772#endif /* ?WATCOMC_386 */
1773
1774#else /* __GO32__ || __EMX__ */
1775    _dos_getcountryinfo(CountryInfo);
1776#endif /* ?(__GO32__ || __EMX__) */
1777
1778    switch(CountryInfo[0]) {
1779        case 0:
1780            return DF_MDY;
1781        case 1:
1782            return DF_DMY;
1783        case 2:
1784            return DF_YMD;
1785    }
1786#endif /* !WINDLL */
1787
1788    return DF_MDY;   /* default for systems without locale info */
1789
1790} /* end function dateformat() */
1791
1792
1793
1794
1795#ifndef WINDLL
1796
1797/**************************************/
1798/*  Function is_running_on_windows()  */
1799/**************************************/
1800
1801static int is_running_on_windows(void)
1802{
1803    char *var = getenv("OS");
1804
1805    /* if the OS env.var says 'Windows_NT' then */
1806    /* we're likely running on a variant of WinNT */
1807    if ((var != NULL) && (strcmp("Windows_NT", var) == 0))
1808        return TRUE;
1809
1810    /* if the windir env.var is non-null then */
1811    /* we're likely running on a variant of Win9x */
1812    /* DOS mode of Win9x doesn't define windir, only winbootdir */
1813    /* NT's command.com can't see lowercase env. vars */
1814    var = getenv("windir");
1815    if ((var != NULL) && (var[0] != '\0'))
1816        return TRUE;
1817
1818    return FALSE;
1819}
1820
1821
1822/**********************************/
1823/*  Function check_for_windows()  */
1824/**********************************/
1825
1826void check_for_windows(ZCONST char *app)
1827{
1828#ifdef SMALL_MEM
1829    char msg_str[160];          /* enough space for two 79-char-lines  */
1830
1831    (void)zfstrcpy(msg_buf, WarnUsedOnWindows)
1832#else
1833#   define msg_str WarnUsedOnWindows
1834#endif
1835    /* Print a warning for users running under Windows */
1836    /* to reduce bug reports due to running DOS version */
1837    /* under Windows, when Windows version usually works correctly */
1838    if (is_running_on_windows())
1839        printf(msg_str, app);
1840} /* end function check_for_windows() */
1841
1842
1843/************************/
1844/*  Function version()  */
1845/************************/
1846
1847void version(__G)
1848    __GDEF
1849{
1850    int len;
1851#if defined(__DJGPP__) || defined(__WATCOMC__) || \
1852    (defined(_MSC_VER) && (_MSC_VER != 800))
1853    char buf[80];
1854#endif
1855
1856    len = sprintf((char *)slide, LoadFarString(CompiledWith),
1857
1858#if defined(__GNUC__)
1859#  if defined(__DJGPP__)
1860      (sprintf(buf, "djgpp v%d.%02d / gcc ", __DJGPP__, __DJGPP_MINOR__), buf),
1861#  elif defined(__GO32__)         /* __GO32__ is defined as "1" only (sigh) */
1862      "djgpp v1.x / gcc ",
1863#  elif defined(__EMX__)          /* ...so is __EMX__ (double sigh) */
1864      "emx+gcc ",
1865#  else
1866      "gcc ",
1867#  endif
1868      __VERSION__,
1869#elif defined(__WATCOMC__)
1870#  if (__WATCOMC__ % 10 != 0)
1871      "Watcom C/C++", (sprintf(buf, " %d.%02d", __WATCOMC__ / 100,
1872                               __WATCOMC__ % 100), buf),
1873#  else
1874      "Watcom C/C++", (sprintf(buf, " %d.%d", __WATCOMC__ / 100,
1875                               (__WATCOMC__ % 100) / 10), buf),
1876#  endif
1877#elif defined(__TURBOC__)
1878#  ifdef __BORLANDC__
1879      "Borland C++",
1880#    if (__BORLANDC__ < 0x0200)
1881        " 1.0",
1882#    elif (__BORLANDC__ == 0x0200)   /* James:  __TURBOC__ = 0x0297 */
1883        " 2.0",
1884#    elif (__BORLANDC__ == 0x0400)
1885        " 3.0",
1886#    elif (__BORLANDC__ == 0x0410)   /* __BCPLUSPLUS__ = 0x0310 */
1887        " 3.1",
1888#    elif (__BORLANDC__ == 0x0452)   /* __BCPLUSPLUS__ = 0x0320 */
1889        " 4.0 or 4.02",
1890#    elif (__BORLANDC__ == 0x0460)   /* __BCPLUSPLUS__ = 0x0340 */
1891        " 4.5",
1892#    elif (__BORLANDC__ == 0x0500)
1893        " 5.0",
1894#    else
1895        " later than 5.0",
1896#    endif
1897#  else
1898      "Turbo C",
1899#    if (__TURBOC__ > 0x0401)        /* Kevin:  3.0 -> 0x0401 */
1900        "++ later than 3.0",
1901#    elif (__TURBOC__ >= 0x0400)
1902        "++ 3.0",
1903#    elif (__TURBOC__ >= 0x0297)     /* see remark for Borland C++ 2.0 */
1904        "++ 2.0",
1905#    elif (__TURBOC__ == 0x0296)     /* [662] checked by SPC */
1906        "++ 1.01",
1907#    elif (__TURBOC__ == 0x0295)     /* [661] vfy'd by Kevin */
1908        "++ 1.0",
1909#    elif (__TURBOC__ == 0x0201)     /* Brian:  2.01 -> 0x0201 */
1910        " 2.01",
1911#    elif ((__TURBOC__ >= 0x018d) && (__TURBOC__ <= 0x0200)) /* James: 0x0200 */
1912        " 2.0",
1913#    elif (__TURBOC__ > 0x0100)
1914        " 1.5",                      /* James:  0x0105? */
1915#    else
1916        " 1.0",                      /* James:  0x0100 */
1917#    endif
1918#  endif
1919#elif defined(MSC)
1920#  if defined(_QC) && !defined(_MSC_VER)
1921      "MS Quick C ", "2.0 or earlier",      /* _QC is defined as 1 */
1922#  elif defined(_QC) && (_MSC_VER == 600)
1923      "MS Quick C ", "2.5 (MSC 6.00)",
1924#  else
1925      "Microsoft C ",
1926#    ifdef _MSC_VER
1927#      if (_MSC_VER == 800)
1928        "8.0/8.0c (Visual C++ 1.0/1.5)",
1929#      else
1930        (sprintf(buf, "%d.%02d", _MSC_VER/100, _MSC_VER%100), buf),
1931#      endif
1932#    else
1933      "5.1 or earlier",
1934#    endif
1935#  endif
1936#else
1937      "unknown compiler", "",
1938#endif /* ?compilers */
1939
1940      "\nMS-DOS",
1941
1942#if (defined(__GNUC__) || defined(WATCOMC_386))
1943      " (32-bit)",
1944#else
1945#  if defined(M_I86HM) || defined(__HUGE__)
1946      " (16-bit, huge)",
1947#  elif defined(M_I86LM) || defined(__LARGE__)
1948      " (16-bit, large)",
1949#  elif defined(M_I86MM) || defined(__MEDIUM__)
1950      " (16-bit, medium)",
1951#  elif defined(M_I86CM) || defined(__COMPACT__)
1952      " (16-bit, compact)",
1953#  elif defined(M_I86SM) || defined(__SMALL__)
1954      " (16-bit, small)",
1955#  elif defined(M_I86TM) || defined(__TINY__)
1956      " (16-bit, tiny)",
1957#  else
1958      " (16-bit)",
1959#  endif
1960#endif
1961
1962#ifdef __DATE__
1963      " on ", __DATE__
1964#else
1965      "", ""
1966#endif
1967    );
1968
1969    (*G.message)((zvoid *)&G, slide, (ulg)len, 0);
1970                                /* MSC can't handle huge macro expansion */
1971
1972    /* temporary debugging code for Borland compilers only */
1973#if (defined(__TURBOC__) && defined(DEBUG))
1974    Info(slide, 0, ((char *)slide, "\tdebug(__TURBOC__ = 0x%04x = %d)\n",
1975      __TURBOC__, __TURBOC__));
1976#ifdef __BORLANDC__
1977    Info(slide, 0, ((char *)slide, "\tdebug(__BORLANDC__ = 0x%04x)\n",
1978      __BORLANDC__));
1979#else
1980    Info(slide, 0, ((char *)slide, "\tdebug(__BORLANDC__ not defined)\n"));
1981#endif
1982#ifdef __TCPLUSPLUS__
1983    Info(slide, 0, ((char *)slide, "\tdebug(__TCPLUSPLUS__ = 0x%04x)\n",
1984      __TCPLUSPLUS__));
1985#else
1986    Info(slide, 0, ((char *)slide, "\tdebug(__TCPLUSPLUS__ not defined)\n"));
1987#endif
1988#ifdef __BCPLUSPLUS__
1989    Info(slide, 0, ((char *)slide, "\tdebug(__BCPLUSPLUS__ = 0x%04x)\n\n",
1990      __BCPLUSPLUS__));
1991#else
1992    Info(slide, 0, ((char *)slide, "\tdebug(__BCPLUSPLUS__ not defined)\n\n"));
1993#endif
1994#endif /* __TURBOC__ && DEBUG */
1995
1996} /* end function version() */
1997
1998#endif /* !WINDLL */
1999#endif /* !SFX */
2000
2001#endif /* !FUNZIP */
2002
2003
2004
2005
2006
2007#ifdef MY_ZCALLOC       /* Special zcalloc function for MEMORY16 (MSDOS/OS2) */
2008
2009#if defined(__TURBOC__) && !defined(OS2)
2010#include <alloc.h>
2011/* Turbo C malloc() does not allow dynamic allocation of 64K bytes
2012 * and farmalloc(64K) returns a pointer with an offset of 8, so we
2013 * must fix the pointer. Warning: the pointer must be put back to its
2014 * original form in order to free it, use zcfree().
2015 */
2016
2017#define MAX_PTR 2       /* reduced from 10 to save space */
2018/* 10*64K = 640K */
2019
2020static int next_ptr = 0;
2021
2022typedef struct ptr_table_s {
2023    zvoid far *org_ptr;
2024    zvoid far *new_ptr;
2025} ptr_table;
2026
2027static ptr_table table[MAX_PTR];
2028/* This table is used to remember the original form of pointers
2029 * to large buffers (64K). Such pointers are normalized with a zero offset.
2030 * Since MSDOS is not a preemptive multitasking OS, this table is not
2031 * protected from concurrent access. This hack doesn't work anyway on
2032 * a protected system like OS/2. Use Microsoft C instead.
2033 */
2034
2035zvoid far *zcalloc(unsigned items, unsigned size)
2036{
2037    zvoid far *buf;
2038    ulg bsize = (ulg)items*size;
2039
2040    if (bsize < (65536L-16L)) {
2041        buf = farmalloc(bsize);
2042        if (*(ush*)&buf != 0) return buf;
2043    } else {
2044        buf = farmalloc(bsize + 16L);
2045    }
2046    if (buf == NULL || next_ptr >= MAX_PTR) return NULL;
2047    table[next_ptr].org_ptr = buf;
2048
2049    /* Normalize the pointer to seg:0 */
2050    *((ush*)&buf+1) += ((ush)((uch*)buf-NULL) + 15) >> 4;
2051    *(ush*)&buf = 0;
2052    table[next_ptr++].new_ptr = buf;
2053    return buf;
2054}
2055
2056zvoid zcfree(zvoid far *ptr)
2057{
2058    int n;
2059    if (*(ush*)&ptr != 0) { /* object < 64K */
2060        farfree(ptr);
2061        return;
2062    }
2063    /* Find the original pointer */
2064    for (n = next_ptr - 1; n >= 0; n--) {
2065        if (ptr != table[n].new_ptr) continue;
2066
2067        farfree(table[n].org_ptr);
2068        while (++n < next_ptr) {
2069            table[n-1] = table[n];
2070        }
2071        next_ptr--;
2072        return;
2073    }
2074    Trace((stderr, "zcfree: ptr not found!\n"));
2075}
2076#endif /* __TURBOC__ */
2077
2078#if defined(MSC) || defined(__WATCOMC__)
2079#if (!defined(_MSC_VER) || (_MSC_VER < 700))
2080#  define _halloc  halloc
2081#  define _hfree   hfree
2082#endif
2083
2084zvoid far *zcalloc(unsigned items, unsigned size)
2085{
2086    return (zvoid far *)_halloc((long)items, size);
2087}
2088
2089zvoid zcfree(zvoid far *ptr)
2090{
2091    _hfree((void huge *)ptr);
2092}
2093#endif /* MSC || __WATCOMC__ */
2094
2095#endif /* MY_ZCALLOC */
2096
2097
2098#ifndef FUNZIP
2099
2100#if (defined(__GO32__) || defined(__EMX__))
2101
2102#if (!defined(__DJGPP__) || (__DJGPP__ < 2) || \
2103     ((__DJGPP__ == 2) && (__DJGPP_MINOR__ < 2)))
2104int volatile _doserrno;
2105#endif /* not "djgpp v2.02 or newer" */
2106
2107#if (!defined(__DJGPP__) || (__DJGPP__ < 2))
2108
2109unsigned _dos_getcountryinfo(void *countrybuffer)
2110{
2111    asm("movl %0, %%edx": : "g" (countrybuffer));
2112    asm("movl $0x3800, %eax");
2113    asm("int $0x21": : : "%eax", "%ebx", "%ecx", "%edx", "%esi", "%edi");
2114    _doserrno = 0;
2115    asm("jnc 1f");
2116    asm("movl %%eax, %0": "=m" (_doserrno));
2117    asm("1:");
2118    return (unsigned)_doserrno;
2119}
2120
2121unsigned _dos_setftime(int fd, unsigned dosdate, unsigned dostime)
2122{
2123    asm("movl %0, %%ebx": : "g" (fd));
2124    asm("movl %0, %%ecx": : "g" (dostime));
2125    asm("movl %0, %%edx": : "g" (dosdate));
2126    asm("movl $0x5701, %eax");
2127    asm("int $0x21": : : "%eax", "%ebx", "%ecx", "%edx", "%esi", "%edi");
2128    _doserrno = 0;
2129    asm("jnc 1f");
2130    asm("movl %%eax, %0": "=m" (_doserrno));
2131    errno = EBADF;
2132    asm("1:");
2133    return (unsigned)_doserrno;
2134}
2135
2136unsigned _dos_setfileattr(const char *name, unsigned attr)
2137{
2138#if 0   /* stripping of trailing '/' is not needed for unzip-internal use */
2139    unsigned namlen = strlen(name);
2140    char *i_name = alloca(namlen + 1);
2141
2142    strcpy(i_name, name);
2143    if (namlen > 1 && i_name[namlen-1] == '/' && i_name[namlen-2] != ':')
2144        i_name[namlen-1] = '\0';
2145    asm("movl %0, %%edx": : "g" (i_name));
2146#else
2147    asm("movl %0, %%edx": : "g" (name));
2148#endif
2149    asm("movl %0, %%ecx": : "g" (attr));
2150    asm("movl $0x4301, %eax");
2151    asm("int $0x21": : : "%eax", "%ebx", "%ecx", "%edx", "%esi", "%edi");
2152    _doserrno = 0;
2153    asm("jnc 1f");
2154    asm("movl %%eax, %0": "=m" (_doserrno));
2155    switch (_doserrno) {
2156    case 2:
2157    case 3:
2158           errno = ENOENT;
2159           break;
2160    case 5:
2161           errno = EACCES;
2162           break;
2163    }
2164    asm("1:");
2165    return (unsigned)_doserrno;
2166}
2167
2168void _dos_getdrive(unsigned *d)
2169{
2170    asm("movl $0x1900, %eax");
2171    asm("int $0x21": : : "%eax", "%ebx", "%ecx", "%edx", "%esi", "%edi");
2172    asm("xorb %ah, %ah");
2173    asm("incb %al");
2174    asm("movl %%eax, %0": "=a" (*d));
2175}
2176
2177unsigned _dos_creat(const char *path, unsigned attr, int *fd)
2178{
2179    asm("movl $0x3c00, %eax");
2180    asm("movl %0, %%edx": :"g" (path));
2181    asm("movl %0, %%ecx": :"g" (attr));
2182    asm("int $0x21": : : "%eax", "%ebx", "%ecx", "%edx", "%esi", "%edi");
2183    asm("movl %%eax, %0": "=a" (*fd));
2184    _doserrno = 0;
2185    asm("jnc 1f");
2186    _doserrno = *fd;
2187    switch (_doserrno) {
2188    case 3:
2189           errno = ENOENT;
2190           break;
2191    case 4:
2192           errno = EMFILE;
2193           break;
2194    case 5:
2195           errno = EACCES;
2196           break;
2197    }
2198    asm("1:");
2199    return (unsigned)_doserrno;
2200}
2201
2202unsigned _dos_close(int fd)
2203{
2204    asm("movl %0, %%ebx": : "g" (fd));
2205    asm("movl $0x3e00, %eax");
2206    asm("int $0x21": : : "%eax", "%ebx", "%ecx", "%edx", "%esi", "%edi");
2207    _doserrno = 0;
2208    asm("jnc 1f");
2209    asm ("movl %%eax, %0": "=m" (_doserrno));
2210    if (_doserrno == 6) {
2211          errno = EBADF;
2212    }
2213    asm("1:");
2214    return (unsigned)_doserrno;
2215}
2216
2217#endif /* !__DJGPP__ || (__DJGPP__ < 2) */
2218
2219
2220static int volumelabel(ZCONST char *name)
2221{
2222    int fd;
2223
2224    return _dos_creat(name, FA_LABEL, &fd) ? fd : _dos_close(fd);
2225}
2226
2227
2228#if (defined(__DJGPP__) && (__DJGPP__ >= 2))
2229
2230#include <dpmi.h>               /* These includes for the country info */
2231#include <go32.h>
2232#include <sys/farptr.h>
2233
2234/* The above _dos_getcountryinfo function doesn't work with djgpp v2, presumably
2235 * because ds is not set correctly (does it really work at all?). Note that
2236 * this version only sets the date (ie. CountryInfo[0]).
2237 */
2238unsigned _dos_getcountryinfo(void *countrybuffer)
2239{
2240   __dpmi_regs regs;
2241
2242   regs.x.ax = 0x3800;
2243   regs.x.dx = __tb & 0x0f;
2244   regs.x.ds = (__tb >> 4) & 0xffff;
2245   _doserrno = __dpmi_int(0x21, &regs);
2246
2247   *(ush*)countrybuffer = _farpeekw(_dos_ds, __tb & 0xfffff);
2248
2249   return (unsigned)_doserrno;
2250}
2251
2252
2253/* Disable determination of "x" bit in st_mode field for [f]stat() calls. */
2254int _is_executable (const char *path, int fhandle, const char *ext)
2255{
2256    return 0;
2257}
2258
2259#ifndef USE_DJGPP_GLOB
2260/* Prevent globbing of filenames.  This gives the same functionality as
2261 * "stubedit <program> globbing=no" did with DJGPP v1.
2262 */
2263char **__crt0_glob_function(char *_arg)
2264{
2265    return NULL;
2266}
2267#endif /* !USE_DJGPP_GLOB */
2268
2269#ifndef USE_DJGPP_ENV
2270/* Reduce the size of the executable and remove the functionality to read
2271 * the program's environment from whatever $DJGPP points to.
2272 */
2273void __crt0_load_environment_file(char *_app_name)
2274{
2275}
2276#endif /* !USE_DJGPP_ENV */
2277
2278#endif /* __DJGPP__ >= 2 */
2279#endif /* __GO32__ || __EMX__ */
2280
2281
2282
2283static int getdoscodepage(void)
2284{
2285    union REGS regs;
2286
2287    WREGS(regs,ax) = 0x6601;
2288#ifdef __EMX__
2289    _int86(0x21, &regs, &regs);
2290    if (WREGS(regs,flags) & 1)
2291#else
2292    intdos(&regs, &regs);
2293    if (WREGS(regs,cflag))
2294#endif
2295    {
2296        Trace((stderr,
2297          "error in DOS function 0x66 (AX = 0x%04x): default to 850...\n",
2298          (unsigned int)(WREGS(regs,ax))));
2299        return 858;
2300    } else
2301        return WREGS(regs,bx);
2302}
2303
2304
2305
2306#ifdef __EMX__
2307#ifdef MORE
2308
2309/*************************/
2310/* Function screensize() */
2311/*************************/
2312
2313int screensize(int *tt_rows, int *tt_cols)
2314{
2315    int scr_dimen[2];           /* scr_dimen[0]: columns, src_dimen[1]: rows */
2316
2317    _scrsize(scr_dimen);
2318    if (tt_rows != NULL) *tt_rows = scr_dimen[1];
2319    if (tt_cols != NULL) *tt_cols = scr_dimen[0];
2320    return 0;
2321}
2322
2323#endif /* MORE */
2324#endif /* __EMX__ */
2325
2326
2327
2328#ifdef WATCOMC_386
2329#ifdef MORE
2330#include <graph.h>
2331
2332/*************************/
2333/* Function screensize() */
2334/*************************/
2335
2336int screensize(int *tt_rows, int *tt_cols)
2337{
2338    struct videoconfig vc;
2339
2340    _getvideoconfig(&vc);
2341    if (tt_rows != NULL) *tt_rows = (int)(vc.numtextrows);
2342    if (tt_cols != NULL) *tt_cols = (int)(vc.numtextcols);
2343    return 0;
2344}
2345
2346#endif /* MORE */
2347
2348
2349static struct RMINFO {
2350    ulg edi, esi, ebp;
2351    ulg reserved;
2352    ulg ebx, edx, ecx, eax;
2353    ush flags;
2354    ush es,ds,fs,gs;
2355    ush ip_ignored,cs_ignored;
2356    ush sp,ss;
2357};
2358
2359/* This function is used to call dos interrupts that may not be supported
2360 * by some particular 32-bit DOS extender.  It uses DPMI function 300h to
2361 * simulate a real mode call of the interrupt.  The caller is responsible
2362 * for providing real mode addresses of any buffer areas used.  The docs
2363 * for PMODE/W imply that this should not be necessary for calling the DOS
2364 * interrupts that it doesn't extend, but it crashes when this isn't used. */
2365
2366static int int86x_realmode(int inter_no, union REGS *in,
2367                           union REGS *out, struct SREGS *seg)
2368{
2369    union REGS local;
2370    struct SREGS localseg;
2371    struct RMINFO rmi;
2372    int r;
2373
2374    rmi.eax = in->x.eax;
2375    rmi.ebx = in->x.ebx;
2376    rmi.ecx = in->x.ecx;
2377    rmi.edx = in->x.edx;
2378    rmi.edi = in->x.edi;
2379    rmi.esi = in->x.esi;
2380    rmi.ebp = rmi.reserved = 0L;
2381    rmi.es = seg->es;
2382    rmi.ds = seg->ds;
2383    rmi.fs = seg->fs;
2384    rmi.gs = seg->gs;
2385    rmi.sp = rmi.ss = rmi.ip_ignored = rmi.cs_ignored = rmi.flags = 0;
2386    memset(&local, 0, sizeof(local));
2387    memset(&localseg, 0, sizeof(localseg));
2388    local.w.ax = 0x0300;
2389    local.h.bl = inter_no;
2390    local.h.bh = 0;
2391    local.w.cx = 0;
2392    localseg.es = FP_SEG(&rmi);
2393    local.x.edi = FP_OFF(&rmi);
2394    r = int386x(0x31, &local, &local, &localseg);
2395    out->x.eax = rmi.eax;
2396    out->x.ebx = rmi.ebx;
2397    out->x.ecx = rmi.ecx;
2398    out->x.edx = rmi.edx;
2399    out->x.edi = rmi.edi;
2400    out->x.esi = rmi.esi;
2401    out->x.cflag = rmi.flags & INTR_CF;
2402    return r;
2403}
2404
2405#endif /* WATCOMC_386 */
2406
2407
2408
2409
2410#ifdef DOS_STAT_BANDAID
2411
2412/* This papers over a bug in Watcom 10.6's standard library...sigh.
2413 * Apparently it applies to both the DOS and Win32 stat()s. */
2414
2415int stat_bandaid(const char *path, struct stat *buf)
2416{
2417    char newname[4];
2418
2419    if (!stat(path, buf))
2420        return 0;
2421    else if (!strcmp(path, ".") || (path[0] && !strcmp(path + 1, ":."))) {
2422        strcpy(newname, path);
2423        newname[strlen(path) - 1] = '\\';   /* stat(".") fails for root! */
2424        return stat(newname, buf);
2425    } else
2426        return -1;
2427}
2428
2429#endif /* DOS_STAT_BANDAID */
2430
2431#endif /* !FUNZIP */
2432