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 
62 static 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
66 static int isfloppy OF((int nDrive));
67 static int z_dos_chmod OF((__GPRO__ ZCONST char *fname, int attributes));
68 static int volumelabel OF((ZCONST char *newlabel));
69 #if (!defined(SFX) && !defined(WINDLL))
70    static int is_running_on_windows OF((void));
71 #endif
72 static int getdoscodepage OF((void));
73 
74 static int created_dir;        /* used by mapname(), checkdir() */
75 static int renamed_fullpath;   /* ditto */
76 static 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
88 static ZCONST char Far WarnDirTraversSkip[] =
89   "warning:  skipped \"../\" path component(s) in %s\n";
90 static ZCONST char Far Creating[] = "   creating: %s\n";
91 static ZCONST char Far ConversionFailed[] =
92   "mapname:  conversion of %s failed\n";
93 static ZCONST char Far Labelling[] = "labelling %c: %-22s\n";
94 static ZCONST char Far ErrSetVolLabel[] =
95   "mapname:  error setting volume label\n";
96 static ZCONST char Far PathTooLong[] = "checkdir error:  path too long: %s\n";
97 static ZCONST char Far CantCreateDir[] = "checkdir error:  cannot create %s\n\
98                  unable to process %s.\n";
99 static ZCONST char Far DirIsntDirectory[] =
100   "checkdir error:  %s exists but is not directory\n\
101                  unable to process %s.\n";
102 static 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
109 static 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 
Opendir(name)209 zDIR *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 
Readdir(d)250 struct 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 
276 char *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 
mapattr(__G)420 int 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 
438 int 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 
748 static 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 
map2fat(pathcomp,last_dot)803 static 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 
868 int 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 
isfloppy(nDrive)1164 static 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 
1195 static 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 
1245 typedef 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 
volumelabel(newlabel)1260 static 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  */
1481 typedef 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 
close_outfile(__G)1506 void 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 
stamp_file(fname,modtime)1617 int 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 
prepare_ISO_OEM_translat(__G)1668 void 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 
dateformat()1714 int 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 
is_running_on_windows(void)1801 static 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 
check_for_windows(ZCONST char * app)1826 void 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 
version(__G)1847 void 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 
2020 static int next_ptr = 0;
2021 
2022 typedef struct ptr_table_s {
2023     zvoid far *org_ptr;
2024     zvoid far *new_ptr;
2025 } ptr_table;
2026 
2027 static 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 
zcalloc(unsigned items,unsigned size)2035 zvoid 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 
zcfree(zvoid far * ptr)2056 zvoid 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 
zcalloc(unsigned items,unsigned size)2084 zvoid far *zcalloc(unsigned items, unsigned size)
2085 {
2086     return (zvoid far *)_halloc((long)items, size);
2087 }
2088 
zcfree(zvoid far * ptr)2089 zvoid 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)))
2104 int volatile _doserrno;
2105 #endif /* not "djgpp v2.02 or newer" */
2106 
2107 #if (!defined(__DJGPP__) || (__DJGPP__ < 2))
2108 
_dos_getcountryinfo(void * countrybuffer)2109 unsigned _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 
_dos_setftime(int fd,unsigned dosdate,unsigned dostime)2121 unsigned _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 
_dos_setfileattr(const char * name,unsigned attr)2136 unsigned _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 
_dos_getdrive(unsigned * d)2168 void _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 
_dos_creat(const char * path,unsigned attr,int * fd)2177 unsigned _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 
_dos_close(int fd)2202 unsigned _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 
volumelabel(ZCONST char * name)2220 static 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  */
_dos_getcountryinfo(void * countrybuffer)2238 unsigned _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. */
_is_executable(const char * path,int fhandle,const char * ext)2254 int _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  */
__crt0_glob_function(char * _arg)2263 char **__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  */
__crt0_load_environment_file(char * _app_name)2273 void __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 
getdoscodepage(void)2283 static 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 
screensize(int * tt_rows,int * tt_cols)2313 int 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 
screensize(int * tt_rows,int * tt_cols)2336 int 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 
2349 static 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 
int86x_realmode(int inter_no,union REGS * in,union REGS * out,struct SREGS * seg)2366 static 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 
stat_bandaid(const char * path,struct stat * buf)2415 int 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