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, ®s, ®s);
1174 if (WREGS(regs,flags) & 1)
1175 #else
1176 intdos(®s, ®s);
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(®s, 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, ®s, ®s); /* 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, ®s, ®s); /* 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(®s, ®s, &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(®s, ®s, &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(®s, ®s, &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(®s, ®s, &sregs);
1422 retv = 1;
1423 } else {
1424 F_intdosx(®s, ®s, &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(®s, ®s, &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, ®s, ®s);
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(®s, 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, ®s, ®s); /* 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, ®s, ®s); /* 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, ®s, ®s, &sregs);
1761 CountryInfo[0] = regs.w.cflag ? 0 : _CountryInfo[0];
1762 regs.w.ax = 0x0101;
1763 regs.w.dx = seg;
1764 int386(0x31, ®s, ®s); /* 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(®s, ®s, &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, ®s);
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, ®s, ®s);
2290 if (WREGS(regs,flags) & 1)
2291 #else
2292 intdos(®s, ®s);
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