2
Copyright (c) 1990-2003 Info-ZIP. All rights reserved.
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
9
/*---------------------------------------------------------------------------
13
MSDOS-specific routines for use with Info-ZIP's UnZip 5.3 and later.
15
Contains: Opendir() (from zip)
25
volumelabel() (non-djgpp, non-emx)
27
stamp_file() (TIMESTAMP only)
30
zcalloc() (16-bit, only)
31
zcfree() (16-bit, only)
32
_dos_getcountryinfo() (djgpp 1.x, emx)
33
[_dos_getftime() (djgpp 1.x, emx) to be added]
34
_dos_setftime() (djgpp 1.x, emx)
35
_dos_setfileattr() (djgpp 1.x, emx)
36
_dos_getdrive() (djgpp 1.x, emx)
37
_dos_creat() (djgpp 1.x, emx)
38
_dos_close() (djgpp 1.x, emx)
39
volumelabel() (djgpp, emx)
40
_dos_getcountryinfo() (djgpp 2.x)
41
_is_executable() (djgpp 2.x)
42
__crt0_glob_function() (djgpp 2.x)
43
__crt0_load_environment_file() (djgpp 2.x)
44
screensize() (emx, Watcom 32-bit)
45
int86x_realmode() (Watcom 32-bit)
46
stat_bandaid() (Watcom)
48
---------------------------------------------------------------------------*/
52
#define UNZIP_INTERNAL
55
/* fUnZip does not need anything from here except the zcalloc() & zcfree()
56
* function pair (when Deflate64 support is enabled in 16-bit environment).
60
static void maskDOSdevice(__GPRO__ char *pathcomp, char *last_dot);
61
#ifdef MAYBE_PLAIN_FAT
62
static void map2fat OF((char *pathcomp, char *last_dot));
64
static int isfloppy OF((int nDrive));
65
static int z_dos_chmod OF((__GPRO__ ZCONST char *fname, int attributes));
66
static int volumelabel OF((ZCONST char *newlabel));
68
static int created_dir; /* used by mapname(), checkdir() */
69
static int renamed_fullpath; /* ditto */
70
static unsigned nLabelDrive; /* ditto, plus volumelabel() */
74
/*****************************/
75
/* Strings used in msdos.c */
76
/*****************************/
79
static ZCONST char Far CantAllocateWildcard[] =
80
"warning: cannot allocate wildcard buffers\n";
82
static ZCONST char Far WarnDirTraversSkip[] =
83
"warning: skipped \"../\" path component(s) in %s\n";
84
static ZCONST char Far Creating[] = " creating: %s\n";
85
static ZCONST char Far ConversionFailed[] =
86
"mapname: conversion of %s failed\n";
87
static ZCONST char Far Labelling[] = "labelling %c: %-22s\n";
88
static ZCONST char Far ErrSetVolLabel[] =
89
"mapname: error setting volume label\n";
90
static ZCONST char Far PathTooLong[] = "checkdir error: path too long: %s\n";
91
static ZCONST char Far CantCreateDir[] = "checkdir error: cannot create %s\n\
92
unable to process %s.\n";
93
static ZCONST char Far DirIsntDirectory[] =
94
"checkdir error: %s exists but is not directory\n\
95
unable to process %s.\n";
96
static ZCONST char Far PathTooLongTrunc[] =
97
"checkdir warning: path too long; truncating\n %s\n\
99
#if (!defined(SFX) || defined(SFX_EXDIR))
100
static ZCONST char Far CantCreateExtractDir[] =
101
"checkdir: cannot create extraction directory: %s\n";
103
static ZCONST char Far AttribsMayBeWrong[] =
104
"\nwarning: file attributes may not be correct\n";
108
/****************************/
109
/* Macros used in msdos.c */
110
/****************************/
113
# define WREGS(v,r) (v.w.r)
114
# define int86x int386x
115
static int int86x_realmode(int inter_no, union REGS *in,
116
union REGS *out, struct SREGS *seg);
117
# define F_intdosx(ir,or,sr) int86x_realmode(0x21, ir, or, sr)
118
# define XXX__MK_FP_IS_BROKEN
120
# define WREGS(v,r) (v.x.r)
121
# define F_intdosx(ir,or,sr) intdosx(ir, or, sr)
124
#if (defined(__GO32__) || defined(__EMX__))
125
# include <dirent.h> /* use readdir() */
126
# define MKDIR(path,mode) mkdir(path,mode)
127
# define Opendir opendir
128
# define Readdir readdir
129
# define Closedir closedir
130
# define zdirent dirent
134
# define GETDRIVE(d) d = _getdrive()
135
# define FA_LABEL A_LABEL
137
# define GETDRIVE(d) _dos_getdrive(&d)
139
# if defined(_A_SUBDIR) /* MSC dos.h and compatibles */
140
# define FSUBDIR _A_SUBDIR
141
# elif defined(FA_DIREC) /* Borland dos.h and compatible variants */
142
# define FSUBDIR FA_DIREC
143
# elif defined(A_DIR) /* EMX dir.h (and dirent.h) */
144
# define FSUBDIR A_DIR
145
# else /* fallback definition */
146
# define FSUBDIR 0x10
148
# if defined(_A_VOLID) /* MSC dos.h and compatibles */
149
# define FVOLID _A_VOLID
150
# elif defined(FA_LABEL) /* Borland dos.h and compatible variants */
151
# define FVOLID FA_LABEL
152
# elif defined(A_LABEL) /* EMX dir.h (and dirent.h) */
153
# define FVOLID A_LABEL
157
#else /* !(__GO32__ || __EMX__) */
158
# define MKDIR(path,mode) mkdir(path)
160
# define FATTR FA_HIDDEN+FA_SYSTEM+FA_DIREC
161
# define FVOLID FA_LABEL
162
# define FSUBDIR FA_DIREC
163
# define FFIRST(n,d,a) findfirst(n,(struct ffblk *)d,a)
164
# define FNEXT(d) findnext((struct ffblk *)d)
165
# define GETDRIVE(d) d=getdisk()+1
167
# else /* !__TURBOC__ */
168
# define FATTR _A_HIDDEN+_A_SYSTEM+_A_SUBDIR
169
# define FVOLID _A_VOLID
170
# define FSUBDIR _A_SUBDIR
171
# define FFIRST(n,d,a) _dos_findfirst(n,a,(struct find_t *)d)
172
# define FNEXT(d) _dos_findnext((struct find_t *)d)
173
# define GETDRIVE(d) _dos_getdrive(&d)
175
# endif /* ?__TURBOC__ */
176
typedef struct zdirent {
181
zDIR *Opendir OF((const char *));
182
struct zdirent *Readdir OF((zDIR *));
183
# define Closedir free
190
/**********************/ /* Borland C++ 3.x has its own opendir/readdir */
191
/* Function Opendir() */ /* library routines, but earlier versions don't, */
192
/**********************/ /* so use ours regardless */
195
const char *name; /* name of directory to open */
197
zDIR *dirp; /* malloc'd return value */
198
char *nbuf; /* malloc'd temporary string */
199
extent len = strlen(name); /* path length to avoid strlens and strcats */
202
if ((dirp = (zDIR *)malloc(sizeof(zDIR))) == (zDIR *)NULL)
204
if ((nbuf = malloc(len + 6)) == (char *)NULL) {
210
if (nbuf[len-1] == ':') {
212
} else if (nbuf[len-1] == '/' || nbuf[len-1] == '\\')
215
strcpy(nbuf+len, "/*.*");
216
Trace((stderr, "Opendir: nbuf = [%s]\n", FnFilter1(nbuf)));
218
if (FFIRST(nbuf, dirp, FATTR)) {
231
/**********************/
232
/* Function Readdir() */
233
/**********************/
235
struct zdirent *Readdir(d)
236
zDIR *d; /* directory stream from which to read */
238
/* Return pointer to first or next directory entry, or NULL if end. */
244
return (struct zdirent *)NULL;
245
return (struct zdirent *)d;
249
#endif /* ?(__GO32__ || __EMX__) */
257
/************************/
258
/* Function do_wild() */ /* identical to OS/2 version */
259
/************************/
261
char *do_wild(__G__ wildspec)
263
ZCONST char *wildspec; /* only used first time on a given dir */
265
static zDIR *wild_dir = (zDIR *)NULL;
266
static ZCONST char *wildname;
267
static char *dirname, matchname[FILNAMSIZ];
268
static int notfirstcall=FALSE, have_dirname, dirnamelen;
270
struct zdirent *file;
272
/* Even when we're just returning wildspec, we *always* do so in
273
* matchname[]--calling routine is allowed to append four characters
274
* to the returned string, and wildspec may be a pointer to argv[].
276
if (!notfirstcall) { /* first call: must initialize everything */
279
if (!iswild(wildspec)) {
280
strcpy(matchname, wildspec);
281
have_dirname = FALSE;
286
/* break the wildspec into a directory part and a wildcard filename */
287
if ((wildname = strrchr(wildspec, '/')) == (ZCONST char *)NULL &&
288
(wildname = strrchr(wildspec, ':')) == (ZCONST char *)NULL) {
291
have_dirname = FALSE;
294
++wildname; /* point at character after '/' or ':' */
295
dirnamelen = (int)(wildname - wildspec);
296
if ((dirname = (char *)malloc(dirnamelen+1)) == (char *)NULL) {
297
Info(slide, 1, ((char *)slide,
298
LoadFarString(CantAllocateWildcard)));
299
strcpy(matchname, wildspec);
300
return matchname; /* but maybe filespec was not a wildcard */
302
/* GRR: can't strip trailing char for opendir since might be "d:/" or "d:"
303
* (would have to check for "./" at end--let opendir handle it instead) */
304
strncpy(dirname, wildspec, dirnamelen);
305
dirname[dirnamelen] = '\0'; /* terminate for strcpy below */
308
Trace((stderr, "do_wild: dirname = [%s]\n", FnFilter1(dirname)));
310
if ((wild_dir = Opendir(dirname)) != (zDIR *)NULL) {
312
strcpy(matchname, dirname);
313
fnamestart = matchname + dirnamelen;
315
fnamestart = matchname;
316
while ((file = Readdir(wild_dir)) != (struct zdirent *)NULL) {
317
Trace((stderr, "do_wild: readdir returns %s\n",
318
FnFilter1(file->d_name)));
319
strcpy(fnamestart, file->d_name);
320
if (strrchr(fnamestart, '.') == (char *)NULL)
321
strcat(fnamestart, ".");
322
if (match(fnamestart, wildname, 1) && /* 1 == ignore case */
323
/* skip "." and ".." directory entries */
324
strcmp(fnamestart, ".") && strcmp(fnamestart, "..")) {
325
Trace((stderr, "do_wild: match() succeeds\n"));
326
/* remove trailing dot */
327
fnamestart += strlen(fnamestart) - 1;
328
if (*fnamestart == '.')
333
/* if we get to here directory is exhausted, so close it */
335
wild_dir = (zDIR *)NULL;
339
Trace((stderr, "do_wild: Opendir(%s) returns NULL\n",
340
FnFilter1(dirname)));
344
/* return the raw wildspec in case that works (e.g., directory not
345
* searchable, but filespec was not wild and file is readable) */
346
strcpy(matchname, wildspec);
350
/* last time through, might have failed opendir but returned raw wildspec */
351
if (wild_dir == (zDIR *)NULL) {
352
notfirstcall = FALSE; /* nothing left to try--reset for new wildspec */
358
/* If we've gotten this far, we've read and matched at least one entry
359
* successfully (in a previous call), so dirname has been copied into
363
/* strcpy(matchname, dirname); */
364
fnamestart = matchname + dirnamelen;
366
fnamestart = matchname;
367
while ((file = Readdir(wild_dir)) != (struct zdirent *)NULL) {
368
Trace((stderr, "do_wild: readdir returns %s\n",
369
FnFilter1(file->d_name)));
370
strcpy(fnamestart, file->d_name);
371
if (strrchr(fnamestart, '.') == (char *)NULL)
372
strcat(fnamestart, ".");
373
if (match(fnamestart, wildname, 1)) { /* 1 == ignore case */
374
Trace((stderr, "do_wild: match() succeeds\n"));
375
/* remove trailing dot */
376
fnamestart += strlen(fnamestart) - 1;
377
if (*fnamestart == '.')
383
Closedir(wild_dir); /* have read at least one entry; nothing left */
384
wild_dir = (zDIR *)NULL;
385
notfirstcall = FALSE; /* reset for new wildspec */
390
} /* end function do_wild() */
397
/**********************/
398
/* Function mapattr() */
399
/**********************/
404
/* set archive bit for file entries (file is not backed up): */
405
G.pInfo->file_attr = ((unsigned)G.crec.external_file_attributes |
406
(G.crec.external_file_attributes & FSUBDIR ? 0 : 32)) & 0xff;
409
} /* end function mapattr() */
415
/************************/
416
/* Function mapname() */
417
/************************/
419
int mapname(__G__ renamed)
424
* MPN_OK - no problem detected
425
* MPN_INF_TRUNC - caution (truncated filename)
426
* MPN_INF_SKIP - info "skip entry" (dir doesn't exist)
427
* MPN_ERR_SKIP - error -> skip entry
428
* MPN_ERR_TOOLONG - error -> path is too long
429
* MPN_NOMEM - error (memory allocation failed) -> skip entry
430
* [also MPN_VOL_LABEL, MPN_CREATED_DIR]
433
char pathcomp[FILNAMSIZ]; /* path-component buffer */
434
char *pp, *cp=(char *)NULL; /* character pointers */
435
char *lastsemi=(char *)NULL; /* pointer to last semi-colon in pathcomp */
436
#ifdef MAYBE_PLAIN_FAT
437
char *last_dot=(char *)NULL; /* last dot not converted to underscore */
439
int use_lfn = USE_LFN; /* file system supports long filenames? */
442
int killed_ddot = FALSE; /* is set when skipping "../" pathcomp */
444
register unsigned workch; /* hold the character being tested */
447
/*---------------------------------------------------------------------------
448
Initialize various pointers and counters and stuff.
449
---------------------------------------------------------------------------*/
451
/* can create path as long as not just freshening, or if user told us */
452
G.create_dirs = (!uO.fflag || renamed);
454
created_dir = FALSE; /* not yet */
455
renamed_fullpath = FALSE;
458
cp = G.filename - 1; /* point to beginning of renamed name... */
460
if (*cp == '\\') /* convert backslashes to forward */
463
/* use temporary rootpath if user gave full pathname */
464
if (G.filename[0] == '/') {
465
renamed_fullpath = TRUE;
466
pathcomp[0] = '/'; /* copy the '/' and terminate */
469
} else if (isalpha((uch)G.filename[0]) && G.filename[1] == ':') {
470
renamed_fullpath = TRUE;
472
*pp++ = *cp++; /* copy the "d:" (+ '/', possibly) */
475
*pp++ = *cp++; /* otherwise add "./"? */
480
/* pathcomp is ignored unless renamed_fullpath is TRUE: */
481
if ((error = checkdir(__G__ pathcomp, INIT)) != 0) /* initialize path buf */
482
return error; /* ...unless no mem or vol label on hard disk */
484
*pathcomp = '\0'; /* initialize translation buffer */
485
pp = pathcomp; /* point to translation buffer */
486
if (!renamed) { /* cp already set if renamed */
487
if (uO.jflag) /* junking directories */
488
cp = (char *)strrchr(G.filename, '/');
489
if (cp == (char *)NULL) /* no '/' or not junking dirs */
490
cp = G.filename; /* point to internal zipfile-member pathname */
492
++cp; /* point to start of last component of path */
495
/*---------------------------------------------------------------------------
496
Begin main loop through characters in filename.
497
---------------------------------------------------------------------------*/
499
while ((workch = (uch)*cp++) != 0) {
502
case '/': /* can assume -j flag not given */
504
#ifdef MAYBE_PLAIN_FAT
505
maskDOSdevice(__G__ pathcomp, last_dot);
507
maskDOSdevice(__G__ pathcomp, NULL);
509
#ifdef MAYBE_PLAIN_FAT
514
map2fat(pathcomp, last_dot); /* 8.3 trunc. (in place) */
515
last_dot = (char *)NULL;
518
if (strcmp(pathcomp, ".") == 0) {
519
/* don't bother appending "./" to the path */
521
} else if (!uO.ddotflag && strcmp(pathcomp, "..") == 0) {
522
/* "../" dir traversal detected, skip over it */
524
killed_ddot = TRUE; /* set "show message" flag */
526
/* when path component is not empty, append it now */
527
if (*pathcomp != '\0' &&
528
((error = checkdir(__G__ pathcomp, APPEND_DIR))
529
& MPN_MASK) > MPN_INF_TRUNC)
531
pp = pathcomp; /* reset conversion buffer for next piece */
532
lastsemi = (char *)NULL; /* leave direct. semi-colons alone */
535
#ifdef MAYBE_PLAIN_FAT
538
if (use_lfn) { /* LFN filenames may contain many */
539
*pp++ = '.'; /* dots, so simply copy it ... */
542
if (pp == pathcomp && *cp == '.' && cp[1] == '/') {
543
/* nothing appended yet.., and found "../" */
544
*pp++ = '.'; /* add first dot, */
545
*pp++ = '.'; /* second dot, and */
546
++cp; /* skip over to the '/' */
547
} else { /* found dot within path component */
548
last_dot = pp; /* point at last dot so far... */
549
*pp++ = '_'; /* convert to underscore for now */
552
#endif /* MAYBE_PLAIN_FAT */
554
/* drive names are not stored in zipfile, so no colons allowed;
555
* no brackets or most other punctuation either (all of which
556
* can appear in Unix-created archives; backslash is particularly
557
* bad unless all necessary directories exist) */
558
#ifdef MAYBE_PLAIN_FAT
559
case '[': /* these punctuation characters forbidden */
560
case ']': /* only on plain FAT file systems */
566
*pp++ = (char)workch;
572
case ':': /* special shell characters of command.com */
573
case '\\': /* (device and directory limiters, wildcard */
574
case '"': /* characters, stdin/stdout redirection and */
575
case '<': /* pipe indicators and the quote sign) are */
576
case '>': /* never allowed in filenames on (V)FAT */
583
case ';': /* start of VMS version? */
585
#ifdef MAYBE_PLAIN_FAT
588
*pp++ = ';'; /* keep for now; remove VMS ";##" later */
591
*pp++ = ';'; /* keep for now; remove VMS ";##" later */
595
#ifdef MAYBE_PLAIN_FAT
596
case ' ': /* change spaces to underscores */
598
if (!use_lfn && uO.sflag) /* only if requested and NO lfn! */
600
if (uO.sflag) /* only if requested */
604
*pp++ = (char)workch;
606
#endif /* MAYBE_PLAIN_FAT */
609
/* allow ASCII 255 and European characters in filenames: */
610
if (isprint(workch) || workch >= 127)
611
*pp++ = (char)workch;
614
} /* end while loop */
616
/* Show warning when stripping insecure "parent dir" path components */
617
if (killed_ddot && QCOND2) {
618
Info(slide, 0, ((char *)slide, LoadFarString(WarnDirTraversSkip),
619
FnFilter1(G.filename)));
620
if (!(error & ~MPN_MASK))
621
error = (error & MPN_MASK) | PK_WARN;
624
/*---------------------------------------------------------------------------
625
Report if directory was created (and no file to create: filename ended
626
in '/'), check name to be sure it exists, and combine path and name be-
628
---------------------------------------------------------------------------*/
630
if (G.filename[strlen(G.filename) - 1] == '/') {
631
checkdir(__G__ G.filename, GETPATH);
634
Info(slide, 0, ((char *)slide, LoadFarString(Creating),
635
FnFilter1(G.filename)));
638
/* set file attributes: */
639
z_dos_chmod(__G__ G.filename, G.pInfo->file_attr);
641
/* set dir time (note trailing '/') */
642
return (error & ~MPN_MASK) | MPN_CREATED_DIR;
643
} else if (IS_OVERWRT_ALL) {
644
/* overwrite attributes of existing directory on user's request */
646
/* set file attributes: */
647
z_dos_chmod(__G__ G.filename, G.pInfo->file_attr);
649
/* dir existed already; don't look for data to extract */
650
return (error & ~MPN_MASK) | MPN_INF_SKIP;
653
*pp = '\0'; /* done with pathcomp: terminate it */
655
/* if not saving them, remove VMS version numbers (appended ";###") */
656
if (!uO.V_flag && lastsemi) {
657
#ifndef MAYBE_PLAIN_FAT
664
pp = lastsemi; /* semi-colon was omitted: expect all #'s */
666
pp = lastsemi; /* semi-colon was omitted: expect all #'s */
669
while (isdigit((uch)(*pp)))
671
if (*pp == '\0') /* only digits between ';' and end: nuke */
675
#ifdef MAYBE_PLAIN_FAT
676
maskDOSdevice(__G__ pathcomp, last_dot);
678
maskDOSdevice(__G__ pathcomp, NULL);
681
if (G.pInfo->vollabel) {
682
if (strlen(pathcomp) > 11)
685
#ifdef MAYBE_PLAIN_FAT
688
map2fat(pathcomp, last_dot); /* 8.3 truncation (in place) */
690
map2fat(pathcomp, last_dot); /* 8.3 truncation (in place) */
695
if (*pathcomp == '\0') {
696
Info(slide, 1, ((char *)slide, LoadFarString(ConversionFailed),
697
FnFilter1(G.filename)));
698
return (error & ~MPN_MASK) | MPN_ERR_SKIP;
701
checkdir(__G__ pathcomp, APPEND_NAME); /* returns 1 if truncated: care? */
702
checkdir(__G__ G.filename, GETPATH);
704
if (G.pInfo->vollabel) { /* set the volume label now */
706
Info(slide, 0, ((char *)slide, LoadFarString(Labelling),
707
(nLabelDrive + 'a' - 1),
708
FnFilter1(G.filename)));
709
if (volumelabel(G.filename)) {
710
Info(slide, 1, ((char *)slide, LoadFarString(ErrSetVolLabel)));
711
return (error & ~MPN_MASK) | MPN_ERR_SKIP;
713
/* success: skip the "extraction" quietly */
714
return (error & ~MPN_MASK) | MPN_INF_SKIP;
719
} /* end function mapname() */
725
/****************************/
726
/* Function maskDOSdevice() */
727
/****************************/
729
static void maskDOSdevice(__G__ pathcomp, last_dot)
731
char *pathcomp, *last_dot;
733
/*---------------------------------------------------------------------------
734
Put an underscore in front of the file name if the file name is a
735
DOS/WINDOWS device name like CON.*, AUX.*, PRN.*, etc. Trying to
736
extract such a file would fail at best and wedge us at worst.
737
---------------------------------------------------------------------------*/
738
#if !defined(S_IFCHR) && defined(_S_IFCHR)
739
# define S_IFCHR _S_IFCHR
741
#if !defined(S_ISCHR)
742
# if defined(_S_ISCHR)
743
# define S_ISCHR(m) _S_ISCHR(m)
744
# elif defined(S_IFCHR)
745
# define S_ISCHR(m) ((m) & S_IFCHR)
750
if (stat(pathcomp, &G.statbuf) == 0) {
752
"maskDOSdevice() stat(\"%s\", buf) st_mode result: %X, %o\n",
753
FnFilter1(pathcomp), G.statbuf.st_mode, G.statbuf.st_mode));
755
Trace((stderr, "maskDOSdevice() stat(\"%s\", buf) failed\n",
756
FnFilter1(pathcomp)));
759
if (stat(pathcomp, &G.statbuf) == 0 && S_ISCHR(G.statbuf.st_mode)) {
762
/* pathcomp contains a name of a DOS character device (builtin or
763
* installed device driver).
764
* Prepend a '_' to allow creation of the item in the file system.
766
for (i = strlen(pathcomp) + 1; i > 0; --i)
767
pathcomp[i] = pathcomp[i - 1];
769
if (last_dot != (char *)NULL)
772
} /* end function maskDOSdevice() */
778
#ifdef MAYBE_PLAIN_FAT
780
/**********************/
781
/* Function map2fat() */
782
/**********************/
784
static void map2fat(pathcomp, last_dot)
785
char *pathcomp, *last_dot;
787
char *pEnd = pathcomp + strlen(pathcomp);
789
/*---------------------------------------------------------------------------
790
Case 1: filename has no dot, so figure out if we should add one. Note
791
that the algorithm does not try to get too fancy: if there are no dots
792
already, the name either gets truncated at 8 characters or the last un-
793
derscore is converted to a dot (only if more characters are saved that
794
way). In no case is a dot inserted between existing characters.
796
GRR: have problem if filename is volume label??
798
---------------------------------------------------------------------------*/
800
if (last_dot == (char *)NULL) { /* no dots: check for underscores... */
801
char *plu = strrchr(pathcomp, '_'); /* pointer to last underscore */
803
if ((plu != (char *)NULL) && /* found underscore: convert to dot? */
804
(MIN(plu - pathcomp, 8) + MIN(pEnd - plu - 1, 3) > 8)) {
805
last_dot = plu; /* be lazy: drop through to next if-block */
806
} else if ((pEnd - pathcomp) > 8)
807
/* no underscore; or converting underscore to dot would save less
808
chars than leaving everything in the basename */
809
pathcomp[8] = '\0'; /* truncate at 8 chars */
810
/* else whole thing fits into 8 chars or less: no change */
813
/*---------------------------------------------------------------------------
814
Case 2: filename has dot in it, so truncate first half at 8 chars (shift
815
extension if necessary) and second half at three.
816
---------------------------------------------------------------------------*/
818
if (last_dot != (char *)NULL) { /* one dot is OK: */
819
*last_dot = '.'; /* put the last back in */
821
if ((last_dot - pathcomp) > 8) {
826
q = last_dot = pathcomp + 8;
827
for (i = 0; (i < 4) && *p; ++i) /* too many chars in basename: */
828
*q++ = *p++; /* shift extension left and */
829
*q = '\0'; /* truncate/terminate it */
830
} else if ((pEnd - last_dot) > 4)
831
last_dot[4] = '\0'; /* too many chars in extension */
832
/* else filename is fine as is: no change */
834
if ((last_dot - pathcomp) > 0 && last_dot[-1] == ' ')
835
last_dot[-1] = '_'; /* NO blank in front of '.'! */
837
} /* end function map2fat() */
839
#endif /* MAYBE_PLAIN_FAT */
845
/***********************/
846
/* Function checkdir() */
847
/***********************/
849
int checkdir(__G__ pathcomp, flag)
855
* MPN_OK - no problem detected
856
* MPN_INF_TRUNC - (on APPEND_NAME) truncated filename
857
* MPN_INF_SKIP - path doesn't exist, not allowed to create
858
* MPN_ERR_SKIP - path doesn't exist, tried to create and failed; or path
859
* exists and is not a directory, but is supposed to be
860
* MPN_ERR_TOOLONG - path is too long
861
* MPN_NOMEM - can't allocate memory for filename buffers
864
static int rootlen = 0; /* length of rootpath */
865
static char *rootpath; /* user's "extract-to" directory */
866
static char *buildpath; /* full path (so far) to extracted file */
867
static char *end; /* pointer to end of buildpath ('\0') */
869
int attrs; /* work around MSC stat() bug */
873
# define FUNCTION (flag & FN_MASK)
877
/*---------------------------------------------------------------------------
878
APPEND_DIR: append the path component to the path being built and check
879
for its existence. If doesn't exist and we are creating directories, do
880
so for this one; else signal success or error as appropriate.
881
---------------------------------------------------------------------------*/
883
if (FUNCTION == APPEND_DIR) {
884
int too_long = FALSE;
886
Trace((stderr, "appending dir segment [%s]\n", FnFilter1(pathcomp)));
887
while ((*end = *pathcomp++) != '\0')
890
/* GRR: could do better check, see if overrunning buffer as we go:
891
* check end-buildpath after each append, set warning variable if
892
* within 20 of FILNAMSIZ; then if var set, do careful check when
893
* appending. Clear variable when begin new path. */
895
if ((end-buildpath) > FILNAMSIZ-3) /* need '/', one-char name, '\0' */
896
too_long = TRUE; /* check if extracting directory? */
897
#ifdef MSC /* MSC 6.00 bug: stat(non-existent-dir) == 0 [exists!] */
898
if (_dos_getfileattr(buildpath, &attrs) || stat(buildpath, &G.statbuf))
900
if (SSTAT(buildpath, &G.statbuf)) /* path doesn't exist */
903
if (!G.create_dirs) { /* told not to create (freshening) */
905
/* path doesn't exist: nothing to do */
909
Info(slide, 1, ((char *)slide, LoadFarString(PathTooLong),
910
FnFilter1(buildpath)));
912
/* no room for filenames: fatal */
913
return MPN_ERR_TOOLONG;
915
if (MKDIR(buildpath, 0777) == -1) { /* create the directory */
916
Info(slide, 1, ((char *)slide, LoadFarString(CantCreateDir),
917
FnFilter2(buildpath), FnFilter1(G.filename)));
919
/* path didn't exist, tried to create, failed */
923
} else if (!S_ISDIR(G.statbuf.st_mode)) {
924
Info(slide, 1, ((char *)slide, LoadFarString(DirIsntDirectory),
925
FnFilter2(buildpath), FnFilter1(G.filename)));
927
/* path existed but wasn't dir */
931
Info(slide, 1, ((char *)slide, LoadFarString(PathTooLong),
932
FnFilter1(buildpath)));
934
/* no room for filenames: fatal */
935
return MPN_ERR_TOOLONG;
939
Trace((stderr, "buildpath now = [%s]\n", FnFilter1(buildpath)));
942
} /* end if (FUNCTION == APPEND_DIR) */
944
/*---------------------------------------------------------------------------
945
GETPATH: copy full path to the string pointed at by pathcomp, and free
947
---------------------------------------------------------------------------*/
949
if (FUNCTION == GETPATH) {
950
strcpy(pathcomp, buildpath);
951
Trace((stderr, "getting and freeing path [%s]\n",
952
FnFilter1(pathcomp)));
954
buildpath = end = (char *)NULL;
958
/*---------------------------------------------------------------------------
959
APPEND_NAME: assume the path component is the filename; append it and
960
return without checking for existence.
961
---------------------------------------------------------------------------*/
963
if (FUNCTION == APPEND_NAME) {
964
#ifdef NOVELL_BUG_WORKAROUND
965
if (end == buildpath && !G.pInfo->vollabel) {
966
/* work-around for Novell's "overwriting executables" bug:
967
prepend "./" to name when no path component is specified */
971
#endif /* NOVELL_BUG_WORKAROUND */
972
Trace((stderr, "appending filename [%s]\n", FnFilter1(pathcomp)));
973
while ((*end = *pathcomp++) != '\0') {
975
if ((end-buildpath) >= FILNAMSIZ) {
977
Info(slide, 1, ((char *)slide, LoadFarString(PathTooLongTrunc),
978
FnFilter1(G.filename), FnFilter2(buildpath)));
979
return MPN_INF_TRUNC; /* filename truncated */
982
Trace((stderr, "buildpath now = [%s]\n", FnFilter1(buildpath)));
983
/* could check for existence here, prompt for new name... */
987
/*---------------------------------------------------------------------------
988
INIT: allocate and initialize buffer space for the file currently being
989
extracted. If file was renamed with an absolute path, don't prepend the
991
---------------------------------------------------------------------------*/
993
if (FUNCTION == INIT) {
994
Trace((stderr, "initializing buildpath to "));
995
/* allocate space for full filename, root path, and maybe "./" */
996
if ((buildpath = (char *)malloc(strlen(G.filename)+rootlen+3)) ==
999
if (G.pInfo->vollabel) {
1000
/* GRR: for network drives, do strchr() and return IZ_VOL_LABEL if not [1] */
1001
if (renamed_fullpath && pathcomp[1] == ':')
1002
*buildpath = (char)ToLower(*pathcomp);
1003
else if (!renamed_fullpath && rootlen > 1 && rootpath[1] == ':')
1004
*buildpath = (char)ToLower(*rootpath);
1006
GETDRIVE(nLabelDrive); /* assumed that a == 1, b == 2, etc. */
1007
*buildpath = (char)(nLabelDrive - 1 + 'a');
1009
nLabelDrive = *buildpath - 'a' + 1; /* save for mapname() */
1010
if (uO.volflag == 0 || *buildpath < 'a' || /* no label/bogus disk */
1011
(uO.volflag == 1 && !isfloppy(nLabelDrive))) /* -$: no fixed */
1014
return MPN_VOL_LABEL; /* skipping with message */
1018
} else if (renamed_fullpath) { /* pathcomp = valid data */
1020
while ((*end = *pathcomp++) != '\0')
1022
} else if (rootlen > 0) {
1023
strcpy(buildpath, rootpath);
1024
end = buildpath + rootlen;
1029
Trace((stderr, "[%s]\n", FnFilter1(buildpath)));
1033
/*---------------------------------------------------------------------------
1034
ROOT: if appropriate, store the path in rootpath and create it if neces-
1035
sary; else assume it's a zipfile member and return. This path segment
1036
gets used in extracting all members from every zipfile specified on the
1037
command line. Note that under OS/2 and MS-DOS, if a candidate extract-to
1038
directory specification includes a drive letter (leading "x:"), it is
1039
treated just as if it had a trailing '/'--that is, one directory level
1040
will be created if the path doesn't exist, unless this is otherwise pro-
1041
hibited (e.g., freshening).
1042
---------------------------------------------------------------------------*/
1044
#if (!defined(SFX) || defined(SFX_EXDIR))
1045
if (FUNCTION == ROOT) {
1046
Trace((stderr, "initializing root path to [%s]\n",
1047
FnFilter1(pathcomp)));
1048
if (pathcomp == (char *)NULL) {
1052
if (rootlen > 0) /* rootpath was already set, nothing to do */
1054
if ((rootlen = strlen(pathcomp)) > 0) {
1055
int had_trailing_pathsep=FALSE, has_drive=FALSE, add_dot=FALSE;
1058
if ((tmproot = (char *)malloc(rootlen+3)) == (char *)NULL) {
1062
strcpy(tmproot, pathcomp);
1063
if (isalpha((uch)tmproot[0]) && tmproot[1] == ':')
1064
has_drive = TRUE; /* drive designator */
1065
if (tmproot[rootlen-1] == '/' || tmproot[rootlen-1] == '\\') {
1066
tmproot[--rootlen] = '\0';
1067
had_trailing_pathsep = TRUE;
1069
if (has_drive && (rootlen == 2)) {
1070
if (!had_trailing_pathsep) /* i.e., original wasn't "x:/" */
1071
add_dot = TRUE; /* relative path: add '.' before '/' */
1072
} else if (rootlen > 0) { /* need not check "x:." and "x:/" */
1074
/* MSC 6.00 bug: stat(non-existent-dir) == 0 [exists!] */
1075
if (_dos_getfileattr(tmproot, &attrs) ||
1076
SSTAT(tmproot, &G.statbuf) || !S_ISDIR(G.statbuf.st_mode))
1078
if (SSTAT(tmproot, &G.statbuf) || !S_ISDIR(G.statbuf.st_mode))
1081
/* path does not exist */
1082
if (!G.create_dirs /* || iswild(tmproot) */ ) {
1085
/* treat as stored file */
1086
return MPN_INF_SKIP;
1088
/* GRR: scan for wildcard characters? OS-dependent... if find any, return 2:
1089
* treat as stored file(s) */
1090
/* create directory (could add loop here scanning tmproot
1091
* to create more than one level, but really necessary?) */
1092
if (MKDIR(tmproot, 0777) == -1) {
1093
Info(slide, 1, ((char *)slide,
1094
LoadFarString(CantCreateExtractDir),
1095
FnFilter1(tmproot)));
1098
/* path didn't exist, tried to create, failed: */
1099
/* file exists, or need 2+ subdir levels */
1100
return MPN_ERR_SKIP;
1104
if (add_dot) /* had just "x:", make "x:." */
1105
tmproot[rootlen++] = '.';
1106
tmproot[rootlen++] = '/';
1107
tmproot[rootlen] = '\0';
1108
if ((rootpath = (char *)realloc(tmproot, rootlen+1)) == NULL) {
1113
Trace((stderr, "rootpath now = [%s]\n", FnFilter1(rootpath)));
1117
#endif /* !SFX || SFX_EXDIR */
1119
/*---------------------------------------------------------------------------
1120
END: free rootpath, immediately prior to program exit.
1121
---------------------------------------------------------------------------*/
1123
if (FUNCTION == END) {
1124
Trace((stderr, "freeing rootpath\n"));
1132
return MPN_INVALID; /* should never reach */
1134
} /* end function checkdir() */
1141
/***********************/
1142
/* Function isfloppy() */
1143
/***********************/
1145
static int isfloppy(nDrive) /* more precisely, is it removable? */
1152
regs.h.bl = (uch)nDrive;
1154
_int86(0x21, ®s, ®s);
1155
if (WREGS(regs,flags) & 1)
1157
intdos(®s, ®s);
1158
if (WREGS(regs,cflag)) /* error: do default a/b check instead */
1162
"error in DOS function 0x44 (AX = 0x%04x): guessing instead...\n",
1163
(unsigned int)(WREGS(regs,ax))));
1164
return (nDrive == 1 || nDrive == 2)? TRUE : FALSE;
1166
return WREGS(regs,ax)? FALSE : TRUE;
1172
/**************************/
1173
/* Function z_dos_chmod() */
1174
/**************************/
1176
static int z_dos_chmod(__G__ fname, attributes)
1182
unsigned fnamelength;
1185
/* set file attributes:
1186
The DOS `chmod' system call requires to mask out the
1187
directory and volume_label attribute bits.
1188
And, a trailing '/' has to be removed from the directory name,
1189
the DOS `chmod' system call does not accept it. */
1190
fnamelength = strlen(fname);
1191
if (fnamelength > 1 && fname[fnamelength-1] == '/' &&
1192
fname[fnamelength-2] != ':' &&
1193
(name = (char *)malloc(fnamelength)) != (char *)NULL) {
1194
strncpy(name, fname, fnamelength-1);
1195
name[fnamelength-1] = '\0';
1197
name = (char *)fname;
1201
#if defined(__TURBOC__) || (defined(__DJGPP__) && (__DJGPP__ >= 2))
1202
# if (defined(__BORLANDC__) && (__BORLANDC__ >= 0x0452))
1203
# define Chmod _rtl_chmod
1205
# define Chmod _chmod
1207
errv = (Chmod(name, 1, attributes & (~FSUBDIR & ~FVOLID)) !=
1208
(attributes & (~FSUBDIR & ~FVOLID)));
1210
#else /* !(__TURBOC__ || (__DJGPP__ && __DJGPP__ >= 2)) */
1211
errv = (_dos_setfileattr(name, attributes & (~FSUBDIR & ~FVOLID)) != 0);
1212
#endif /* ?(__TURBOC__ || (__DJGPP__ && __DJGPP__ >= 2)) */
1214
Info(slide, 1, ((char *)slide, LoadFarString(AttribsMayBeWrong)));
1216
if (fnamelength > 0)
1219
} /* end function z_dos_chmod() */
1224
#if (!defined(__GO32__) && !defined(__EMX__))
1226
typedef struct dosfcb {
1227
uch flag; /* ff to indicate extended FCB */
1228
char res[5]; /* reserved */
1229
uch vattr; /* attribute */
1230
uch drive; /* drive (1=A, 2=B, ...) */
1231
uch vn[11]; /* file or volume name */
1233
uch nn[11]; /* holds new name if renaming (else reserved) */
1237
/**************************/
1238
/* Function volumelabel() */
1239
/**************************/
1241
static int volumelabel(newlabel)
1242
ZCONST char *newlabel;
1247
int len = strlen(newlabel);
1248
int fcbseg, dtaseg, fcboff, dtaoff, retv;
1249
dos_fcb fcb, dta, far *pfcb=&fcb, far *pdta=&dta;
1254
/*---------------------------------------------------------------------------
1255
Label the diskette specified by nLabelDrive using FCB calls. (Old ver-
1256
sions of MS-DOS and OS/2 DOS boxes can't use DOS function 3Ch to create
1257
labels.) Must use far pointers for MSC FP_* macros to work; must pad
1258
FCB filenames with spaces; and cannot include dot in 8th position. May
1259
or may not need to zero out FCBs before using; do so just in case.
1260
---------------------------------------------------------------------------*/
1265
memset(&sregs, 0, sizeof(sregs));
1266
memset(®s, 0, sizeof(regs));
1267
/* PMODE/W does not support extended versions of any dos FCB functions, */
1268
/* so we have to use brute force, allocating real mode memory for them. */
1270
regs.w.bx = (2 * sizeof(dos_fcb) + 15) >> 4; /* size in paragraphs */
1271
int386(0x31, ®s, ®s); /* DPMI allocate DOS memory */
1273
return DF_MDY; /* no memory, return default */
1274
truseg = regs.w.dx; /* protected mode selector */
1275
dtaseg = regs.w.ax; /* real mode paragraph */
1277
dtaoff = sizeof(dos_fcb);
1278
#ifdef XXX__MK_FP_IS_BROKEN
1279
/* XXX This code may not be trustworthy in general, though it is */
1280
/* valid with DOS/4GW and PMODE/w, which is all we support for now. */
1283
int386(0x31, ®s, ®s); /* convert seg to linear address */
1284
pfcb = (dos_fcb far *) (((ulg) regs.w.cx << 16) | regs.w.dx);
1285
/* pfcb = (dos_fcb far *) ((ulg) dtaseg << 4); */
1288
pfcb = MK_FP(truseg, fcboff);
1289
pdta = MK_FP(truseg, dtaoff);
1291
_fmemset((char far *)pfcb, 0, 2 * sizeof(dos_fcb));
1292
/* we pass the REAL MODE paragraph to the dos interrupts: */
1295
#else /* !WATCOMC_386 */
1297
memset((char *)&dta, 0, sizeof(dos_fcb));
1298
memset((char *)&fcb, 0, sizeof(dos_fcb));
1299
fcbseg = FP_SEG(pfcb);
1300
fcboff = FP_OFF(pfcb);
1301
dtaseg = FP_SEG(pdta);
1302
dtaoff = FP_OFF(pdta);
1303
#endif /* ?WATCOMC_386 */
1306
for (p = (char *)&dta; (p - (char *)&dta) < sizeof(dos_fcb); ++p)
1308
fprintf(stderr, "error: dta[%d] = %x\n", (p - (char *)&dta), *p);
1309
for (p = (char *)&fcb; (p - (char *)&fcb) < sizeof(dos_fcb); ++p)
1311
fprintf(stderr, "error: fcb[%d] = %x\n", (p - (char *)&fcb), *p);
1312
printf("testing pointer macros:\n");
1314
printf("cs = %x, ds = %x, es = %x, ss = %x\n", sregs.cs, sregs.ds, sregs.es,
1320
bdosptr(0x1a, dta, DO_NOT_CARE);
1322
(intdosx method below)
1326
/* set the disk transfer address for subsequent FCB calls */
1328
WREGS(regs,dx) = dtaoff;
1329
Trace((stderr, "segment:offset of pdta = %x:%x\n", dtaseg, dtaoff));
1330
Trace((stderr, "&dta = %lx, pdta = %lx\n", (ulg)&dta, (ulg)pdta));
1332
F_intdosx(®s, ®s, &sregs);
1334
/* fill in the FCB */
1336
WREGS(regs,dx) = fcboff;
1337
pfcb->flag = 0xff; /* extended FCB */
1338
pfcb->vattr = 0x08; /* attribute: disk volume label */
1339
pfcb->drive = (uch)nLabelDrive;
1342
Trace((stderr, "segment:offset of pfcb = %x:%x\n",
1343
(unsigned int)(sregs.ds),
1344
(unsigned int)(WREGS(regs,dx))));
1345
Trace((stderr, "&fcb = %lx, pfcb = %lx\n", (ulg)&fcb, (ulg)pfcb));
1346
Trace((stderr, "(2nd check: labelling drive %c:)\n", pfcb->drive-1+'A'));
1347
if (pfcb->flag != fcb.flag)
1348
fprintf(stderr, "error: pfcb->flag = %d, fcb.flag = %d\n",
1349
pfcb->flag, fcb.flag);
1350
if (pfcb->drive != fcb.drive)
1351
fprintf(stderr, "error: pfcb->drive = %d, fcb.drive = %d\n",
1352
pfcb->drive, fcb.drive);
1353
if (pfcb->vattr != fcb.vattr)
1354
fprintf(stderr, "error: pfcb->vattr = %d, fcb.vattr = %d\n",
1355
pfcb->vattr, fcb.vattr);
1358
/* check for existing label */
1359
Trace((stderr, "searching for existing label via FCBs\n"));
1360
regs.h.ah = 0x11; /* FCB find first */
1362
_fstrncpy((char far *)&pfcb->vn, "???????????", 11);
1364
strncpy((char *)fcb.vn, "???????????", 11); /* i.e., "*.*" */
1365
#endif /* ?WATCOMC_386 */
1366
Trace((stderr, "fcb.vn = %lx\n", (ulg)fcb.vn));
1367
Trace((stderr, "regs.h.ah = %x, regs.x.dx = %04x, sregs.ds = %04x\n",
1368
(unsigned int)(regs.h.ah), (unsigned int)(WREGS(regs,dx)),
1369
(unsigned int)(sregs.ds)));
1370
Trace((stderr, "flag = %x, drive = %d, vattr = %x, vn = %s = %s.\n",
1371
fcb.flag, fcb.drive, fcb.vattr, fcb.vn, pfcb->vn));
1372
F_intdosx(®s, ®s, &sregs);
1374
/*---------------------------------------------------------------------------
1375
If not previously labelled, write a new label. Otherwise just rename,
1376
since MS-DOS 2.x has a bug that damages the FAT when the old label is
1378
---------------------------------------------------------------------------*/
1381
Trace((stderr, "no label found\n\n"));
1382
regs.h.ah = 0x16; /* FCB create file */
1384
_fstrncpy((char far *)pfcb->vn, newlabel, len);
1386
_fstrncpy((char far *)(pfcb->vn+len), " ", 11-len);
1388
strncpy((char *)fcb.vn, newlabel, len);
1389
if (len < 11) /* fill with spaces */
1390
strncpy((char *)(fcb.vn+len), " ", 11-len);
1392
Trace((stderr, "fcb.vn = %lx pfcb->vn = %lx\n", (ulg)fcb.vn,
1394
Trace((stderr, "flag = %x, drive = %d, vattr = %x\n", fcb.flag,
1395
fcb.drive, fcb.vattr));
1396
Trace((stderr, "vn = %s = %s.\n", fcb.vn, pfcb->vn));
1397
F_intdosx(®s, ®s, &sregs);
1398
regs.h.ah = 0x10; /* FCB close file */
1400
Trace((stderr, "unable to write volume name (AL = %x)\n",
1401
(unsigned int)(regs.h.al)));
1402
F_intdosx(®s, ®s, &sregs);
1405
F_intdosx(®s, ®s, &sregs);
1406
Trace((stderr, "new volume label [%s] written\n", newlabel));
1410
Trace((stderr, "found old label [%s]\n\n", dta.vn)); /* not term. */
1411
regs.h.ah = 0x17; /* FCB rename */
1413
_fstrncpy((char far *)pfcb->vn, (char far *)pdta->vn, 11);
1414
_fstrncpy((char far *)pfcb->nn, newlabel, len);
1416
_fstrncpy((char far *)(pfcb->nn+len), " ", 11-len);
1418
strncpy((char *)fcb.vn, (char *)dta.vn, 11);
1419
strncpy((char *)fcb.nn, newlabel, len);
1420
if (len < 11) /* fill with spaces */
1421
strncpy((char *)(fcb.nn+len), " ", 11-len);
1423
Trace((stderr, "fcb.vn = %lx pfcb->vn = %lx\n", (ulg)fcb.vn,
1425
Trace((stderr, "fcb.nn = %lx pfcb->nn = %lx\n", (ulg)fcb.nn,
1427
Trace((stderr, "flag = %x, drive = %d, vattr = %x\n", fcb.flag,
1428
fcb.drive, fcb.vattr));
1429
Trace((stderr, "vn = %s = %s.\n", fcb.vn, pfcb->vn));
1430
Trace((stderr, "nn = %s = %s.\n", fcb.nn, pfcb->nn));
1431
F_intdosx(®s, ®s, &sregs);
1433
Trace((stderr, "Unable to change volume name (AL = %x)\n",
1434
(unsigned int)(regs.h.al)));
1437
Trace((stderr, "volume label changed to [%s]\n", newlabel));
1442
regs.w.ax = 0x0101; /* free dos memory */
1444
int386(0x31, ®s, ®s);
1448
} /* end function volumelabel() */
1450
#endif /* !__GO32__ && !__EMX__ */
1456
#if (defined(USE_EF_UT_TIME) || defined(TIMESTAMP))
1457
/* The following DOS date/time structure is machine-dependent as it
1458
* assumes "little-endian" byte order. For MSDOS-specific code, which
1459
* is run on ix86 CPUs (or emulators), this assumption is valid; but
1460
* care should be taken when using this code as template for other ports.
1465
struct ftime ft; /* system file time record */
1467
struct { /* date and time words */
1468
ush ztime; /* DOS file modification time word */
1469
ush zdate; /* DOS file modification date word */
1471
struct { /* DOS date/time components bitfield */
1480
#endif /* USE_EF_UT_TIME || TIMESTAMP */
1483
/****************************/
1484
/* Function close_outfile() */
1485
/****************************/
1487
void close_outfile(__G)
1492
* Set the output file date/time stamp according to information from the
1493
* zipfile directory record for this member, then close the file and set
1494
* its permissions (archive, hidden, read-only, system). Aside from closing
1495
* the file, this routine is optional (but most compilers support it).
1498
#ifdef USE_EF_UT_TIME
1499
dos_fdatetime dos_dt;
1502
#endif /* USE_EF_UT_TIME */
1505
/*---------------------------------------------------------------------------
1506
Copy and/or convert time and date variables, if necessary; then set the
1507
file time/date. WEIRD BORLAND "BUG": if output is buffered, and if run
1508
under at least some versions of DOS (e.g., 6.0), and if files are smaller
1509
than DOS physical block size (i.e., 512 bytes) (?), then files MAY NOT
1510
get timestamped correctly--apparently setftime() occurs before any data
1511
are written to the file, and when file is closed and buffers are flushed,
1512
timestamp is overwritten with current time. Even with a 32K buffer, this
1513
does not seem to occur with larger files. UnZip output is now unbuffered,
1514
but if it were not, could still avoid problem by adding "fflush(outfile)"
1515
just before setftime() call. Weird, huh?
1516
---------------------------------------------------------------------------*/
1518
#ifdef USE_EF_UT_TIME
1519
if (G.extra_field &&
1523
(ef_scan_for_izux(G.extra_field, G.lrec.extra_field_length, 0,
1524
G.lrec.last_mod_dos_datetime, &z_utime, NULL)
1527
TTrace((stderr, "close_outfile: Unix e.f. modif. time = %ld\n",
1529
/* round up (down if "up" overflows) to even seconds */
1530
if (z_utime.mtime & 1)
1531
z_utime.mtime = (z_utime.mtime + 1 > z_utime.mtime) ?
1532
z_utime.mtime + 1 : z_utime.mtime - 1;
1533
TIMET_TO_NATIVE(z_utime.mtime) /* NOP unless MSC 7.0 or Macintosh */
1534
t = localtime(&(z_utime.mtime));
1536
t = (struct tm *)NULL;
1537
if (t != (struct tm *)NULL) {
1538
if (t->tm_year < 80) {
1539
dos_dt.z_dtf.zt_se = 0;
1540
dos_dt.z_dtf.zt_mi = 0;
1541
dos_dt.z_dtf.zt_hr = 0;
1542
dos_dt.z_dtf.zd_dy = 1;
1543
dos_dt.z_dtf.zd_mo = 1;
1544
dos_dt.z_dtf.zd_yr = 0;
1546
dos_dt.z_dtf.zt_se = t->tm_sec >> 1;
1547
dos_dt.z_dtf.zt_mi = t->tm_min;
1548
dos_dt.z_dtf.zt_hr = t->tm_hour;
1549
dos_dt.z_dtf.zd_dy = t->tm_mday;
1550
dos_dt.z_dtf.zd_mo = t->tm_mon + 1;
1551
dos_dt.z_dtf.zd_yr = t->tm_year - 80;
1554
dos_dt.z_dostime = G.lrec.last_mod_dos_datetime;
1557
setftime(fileno(G.outfile), &dos_dt.ft);
1559
_dos_setftime(fileno(G.outfile), dos_dt.zft.zdate, dos_dt.zft.ztime);
1561
#else /* !USE_EF_UT_TIME */
1563
setftime(fileno(G.outfile),
1564
(struct ftime *)(&(G.lrec.last_mod_dos_datetime)));
1566
_dos_setftime(fileno(G.outfile), (ush)(G.lrec.last_mod_dos_datetime >> 16),
1567
(ush)(G.lrec.last_mod_dos_datetime));
1569
#endif /* ?USE_EF_UT_TIME */
1571
/*---------------------------------------------------------------------------
1572
And finally we can close the file...at least everybody agrees on how to
1573
do *this*. I think... Also change the mode according to the stored file
1574
attributes, since we didn't do that when we opened the dude.
1575
---------------------------------------------------------------------------*/
1579
z_dos_chmod(__G__ G.filename, G.pInfo->file_attr);
1581
} /* end function close_outfile() */
1589
/*************************/
1590
/* Function stamp_file() */
1591
/*************************/
1593
int stamp_file(fname, modtime)
1597
dos_fdatetime dos_dt;
1600
int fd; /* file handle */
1602
/* round up (down if "up" overflows) to even seconds */
1603
t_even = ((modtime + 1 > modtime) ? modtime + 1 : modtime) & (~1);
1604
TIMET_TO_NATIVE(t_even) /* NOP unless MSC 7.0 or Macintosh */
1605
t = localtime(&t_even);
1606
if (t == (struct tm *)NULL)
1607
return -1; /* time conversion error */
1608
if (t->tm_year < 80) {
1609
dos_dt.z_dtf.zt_se = 0;
1610
dos_dt.z_dtf.zt_mi = 0;
1611
dos_dt.z_dtf.zt_hr = 0;
1612
dos_dt.z_dtf.zd_dy = 1;
1613
dos_dt.z_dtf.zd_mo = 1;
1614
dos_dt.z_dtf.zd_yr = 0;
1616
dos_dt.z_dtf.zt_se = t->tm_sec >> 1;
1617
dos_dt.z_dtf.zt_mi = t->tm_min;
1618
dos_dt.z_dtf.zt_hr = t->tm_hour;
1619
dos_dt.z_dtf.zd_dy = t->tm_mday;
1620
dos_dt.z_dtf.zd_mo = t->tm_mon + 1;
1621
dos_dt.z_dtf.zd_yr = t->tm_year - 80;
1623
if (((fd = open((char *)fname, 0)) == -1) ||
1625
(setftime(fd, &dos_dt.ft)))
1627
(_dos_setftime(fd, dos_dt.zft.zdate, dos_dt.zft.ztime)))
1637
} /* end function stamp_file() */
1639
#endif /* TIMESTAMP */
1646
/*************************/
1647
/* Function dateformat() */
1648
/*************************/
1653
/*---------------------------------------------------------------------------
1654
For those operating systems that support it, this function returns a
1655
value that tells how national convention says that numeric dates are
1656
displayed. Return values are DF_YMD, DF_DMY and DF_MDY (the meanings
1657
should be fairly obvious).
1658
---------------------------------------------------------------------------*/
1661
ush CountryInfo[18];
1662
#if (!defined(__GO32__) && !defined(__EMX__))
1663
ush far *_CountryInfo = CountryInfo;
1669
memset(&sregs, 0, sizeof(sregs));
1670
memset(®s, 0, sizeof(regs));
1671
/* PMODE/W does not support an extended version of dos function 38, */
1672
/* so we have to use brute force, allocating real mode memory for it. */
1674
regs.w.bx = 3; /* 36 bytes rounds up to 48 */
1675
int386(0x31, ®s, ®s); /* DPMI allocate DOS memory */
1677
return DF_MDY; /* no memory, return default */
1681
#ifdef XXX__MK_FP_IS_BROKEN
1682
/* XXX This code may not be trustworthy in general, though it is
1683
* valid with DOS/4GW and PMODE/w, which is all we support for now. */
1684
/* _CountryInfo = (ush far *) (para << 4); */ /* works for some extenders */
1687
int386(0x31, ®s, ®s); /* convert seg to linear address */
1688
_CountryInfo = (ush far *) (((ulg) regs.w.cx << 16) | regs.w.dx);
1690
_CountryInfo = (ush far *) MK_FP(seg, 0);
1693
sregs.ds = para; /* real mode paragraph */
1694
regs.w.dx = 0; /* no offset from segment */
1696
int86x_realmode(0x21, ®s, ®s, &sregs);
1697
CountryInfo[0] = regs.w.cflag ? 0 : _CountryInfo[0];
1700
int386(0x31, ®s, ®s); /* DPMI free DOS memory */
1702
#else /* !WATCOMC_386 */
1704
sregs.ds = FP_SEG(_CountryInfo);
1705
regs.x.dx = FP_OFF(_CountryInfo);
1707
intdosx(®s, ®s, &sregs);
1708
#endif /* ?WATCOMC_386 */
1710
#else /* __GO32__ || __EMX__ */
1711
_dos_getcountryinfo(CountryInfo);
1712
#endif /* ?(__GO32__ || __EMX__) */
1714
switch(CountryInfo[0]) {
1722
#endif /* !WINDLL && !WATCOMC_386 */
1724
return DF_MDY; /* default for systems without locale info */
1726
} /* end function dateformat() */
1733
/************************/
1734
/* Function version() */
1735
/************************/
1741
#if defined(__DJGPP__) || defined(__WATCOMC__) || \
1742
(defined(_MSC_VER) && (_MSC_VER != 800))
1746
len = sprintf((char *)slide, LoadFarString(CompiledWith),
1748
#if defined(__GNUC__)
1749
# if defined(__DJGPP__)
1750
(sprintf(buf, "djgpp v%d.%02d / gcc ", __DJGPP__, __DJGPP_MINOR__), buf),
1751
# elif defined(__GO32__) /* __GO32__ is defined as "1" only (sigh) */
1752
"djgpp v1.x / gcc ",
1753
# elif defined(__EMX__) /* ...so is __EMX__ (double sigh) */
1759
#elif defined(__WATCOMC__)
1760
# if (__WATCOMC__ % 10 != 0)
1761
"Watcom C/C++", (sprintf(buf, " %d.%02d", __WATCOMC__ / 100,
1762
__WATCOMC__ % 100), buf),
1764
"Watcom C/C++", (sprintf(buf, " %d.%d", __WATCOMC__ / 100,
1765
(__WATCOMC__ % 100) / 10), buf),
1767
#elif defined(__TURBOC__)
1768
# ifdef __BORLANDC__
1770
# if (__BORLANDC__ < 0x0200)
1772
# elif (__BORLANDC__ == 0x0200) /* James: __TURBOC__ = 0x0297 */
1774
# elif (__BORLANDC__ == 0x0400)
1776
# elif (__BORLANDC__ == 0x0410) /* __BCPLUSPLUS__ = 0x0310 */
1778
# elif (__BORLANDC__ == 0x0452) /* __BCPLUSPLUS__ = 0x0320 */
1780
# elif (__BORLANDC__ == 0x0460) /* __BCPLUSPLUS__ = 0x0340 */
1782
# elif (__BORLANDC__ == 0x0500)
1789
# if (__TURBOC__ > 0x0401) /* Kevin: 3.0 -> 0x0401 */
1790
"++ later than 3.0",
1791
# elif (__TURBOC__ >= 0x0400)
1793
# elif (__TURBOC__ >= 0x0297) /* see remark for Borland C++ 2.0 */
1795
# elif (__TURBOC__ == 0x0296) /* [662] checked by SPC */
1797
# elif (__TURBOC__ == 0x0295) /* [661] vfy'd by Kevin */
1799
# elif (__TURBOC__ == 0x0201) /* Brian: 2.01 -> 0x0201 */
1801
# elif ((__TURBOC__ >= 0x018d) && (__TURBOC__ <= 0x0200)) /* James: 0x0200 */
1803
# elif (__TURBOC__ > 0x0100)
1804
" 1.5", /* James: 0x0105? */
1806
" 1.0", /* James: 0x0100 */
1810
# if defined(_QC) && !defined(_MSC_VER)
1811
"MS Quick C ", "2.0 or earlier", /* _QC is defined as 1 */
1812
# elif defined(_QC) && (_MSC_VER == 600)
1813
"MS Quick C ", "2.5 (MSC 6.00)",
1817
# if (_MSC_VER == 800)
1818
"8.0/8.0c (Visual C++ 1.0/1.5)",
1820
(sprintf(buf, "%d.%02d", _MSC_VER/100, _MSC_VER%100), buf),
1827
"unknown compiler", "",
1828
#endif /* ?compilers */
1832
#if (defined(__GNUC__) || defined(WATCOMC_386))
1835
# if defined(M_I86HM) || defined(__HUGE__)
1837
# elif defined(M_I86LM) || defined(__LARGE__)
1839
# elif defined(M_I86MM) || defined(__MEDIUM__)
1840
" (16-bit, medium)",
1841
# elif defined(M_I86CM) || defined(__COMPACT__)
1842
" (16-bit, compact)",
1843
# elif defined(M_I86SM) || defined(__SMALL__)
1845
# elif defined(M_I86TM) || defined(__TINY__)
1859
(*G.message)((zvoid *)&G, slide, (ulg)len, 0);
1860
/* MSC can't handle huge macro expansion */
1862
/* temporary debugging code for Borland compilers only */
1863
#if (defined(__TURBOC__) && defined(DEBUG))
1864
Info(slide, 0, ((char *)slide, "\tdebug(__TURBOC__ = 0x%04x = %d)\n",
1865
__TURBOC__, __TURBOC__));
1867
Info(slide, 0, ((char *)slide, "\tdebug(__BORLANDC__ = 0x%04x)\n",
1870
Info(slide, 0, ((char *)slide, "\tdebug(__BORLANDC__ not defined)\n"));
1872
#ifdef __TCPLUSPLUS__
1873
Info(slide, 0, ((char *)slide, "\tdebug(__TCPLUSPLUS__ = 0x%04x)\n",
1876
Info(slide, 0, ((char *)slide, "\tdebug(__TCPLUSPLUS__ not defined)\n"));
1878
#ifdef __BCPLUSPLUS__
1879
Info(slide, 0, ((char *)slide, "\tdebug(__BCPLUSPLUS__ = 0x%04x)\n\n",
1882
Info(slide, 0, ((char *)slide, "\tdebug(__BCPLUSPLUS__ not defined)\n\n"));
1884
#endif /* __TURBOC__ && DEBUG */
1886
} /* end function version() */
1888
#endif /* !WINDLL */
1891
#endif /* !FUNZIP */
1897
#ifdef MY_ZCALLOC /* Special zcalloc function for MEMORY16 (MSDOS/OS2) */
1899
#if defined(__TURBOC__) && !defined(OS2)
1901
/* Turbo C malloc() does not allow dynamic allocation of 64K bytes
1902
* and farmalloc(64K) returns a pointer with an offset of 8, so we
1903
* must fix the pointer. Warning: the pointer must be put back to its
1904
* original form in order to free it, use zcfree().
1907
#define MAX_PTR 2 /* reduced from 10 to save space */
1910
static int next_ptr = 0;
1912
typedef struct ptr_table_s {
1917
static ptr_table table[MAX_PTR];
1918
/* This table is used to remember the original form of pointers
1919
* to large buffers (64K). Such pointers are normalized with a zero offset.
1920
* Since MSDOS is not a preemptive multitasking OS, this table is not
1921
* protected from concurrent access. This hack doesn't work anyway on
1922
* a protected system like OS/2. Use Microsoft C instead.
1925
zvoid far *zcalloc(unsigned items, unsigned size)
1928
ulg bsize = (ulg)items*size;
1930
if (bsize < (65536L-16L)) {
1931
buf = farmalloc(bsize);
1932
if (*(ush*)&buf != 0) return buf;
1934
buf = farmalloc(bsize + 16L);
1936
if (buf == NULL || next_ptr >= MAX_PTR) return NULL;
1937
table[next_ptr].org_ptr = buf;
1939
/* Normalize the pointer to seg:0 */
1940
*((ush*)&buf+1) += ((ush)((uch*)buf-NULL) + 15) >> 4;
1942
table[next_ptr++].new_ptr = buf;
1946
zvoid zcfree(zvoid far *ptr)
1949
if (*(ush*)&ptr != 0) { /* object < 64K */
1953
/* Find the original pointer */
1954
for (n = next_ptr - 1; n >= 0; n--) {
1955
if (ptr != table[n].new_ptr) continue;
1957
farfree(table[n].org_ptr);
1958
while (++n < next_ptr) {
1959
table[n-1] = table[n];
1964
Trace((stderr, "zcfree: ptr not found!\n"));
1966
#endif /* __TURBOC__ */
1968
#if defined(MSC) || defined(__WATCOMC__)
1969
#if (!defined(_MSC_VER) || (_MSC_VER < 700))
1970
# define _halloc halloc
1971
# define _hfree hfree
1974
zvoid far *zcalloc(unsigned items, unsigned size)
1976
return (zvoid far *)_halloc((long)items, size);
1979
zvoid zcfree(zvoid far *ptr)
1981
_hfree((void huge *)ptr);
1983
#endif /* MSC || __WATCOMC__ */
1985
#endif /* MY_ZCALLOC */
1990
#if (defined(__GO32__) || defined(__EMX__))
1992
#if (!defined(__DJGPP__) || (__DJGPP__ < 2) || \
1993
((__DJGPP__ == 2) && (__DJGPP_MINOR__ < 2)))
1994
int volatile _doserrno;
1995
#endif /* not "djgpp v2.02 or newer" */
1997
#if (!defined(__DJGPP__) || (__DJGPP__ < 2))
1999
unsigned _dos_getcountryinfo(void *countrybuffer)
2001
asm("movl %0, %%edx": : "g" (countrybuffer));
2002
asm("movl $0x3800, %eax");
2003
asm("int $0x21": : : "%eax", "%ebx", "%ecx", "%edx", "%esi", "%edi");
2006
asm("movl %%eax, %0": "=m" (_doserrno));
2008
return (unsigned)_doserrno;
2011
unsigned _dos_setftime(int fd, unsigned dosdate, unsigned dostime)
2013
asm("movl %0, %%ebx": : "g" (fd));
2014
asm("movl %0, %%ecx": : "g" (dostime));
2015
asm("movl %0, %%edx": : "g" (dosdate));
2016
asm("movl $0x5701, %eax");
2017
asm("int $0x21": : : "%eax", "%ebx", "%ecx", "%edx", "%esi", "%edi");
2020
asm("movl %%eax, %0": "=m" (_doserrno));
2023
return (unsigned)_doserrno;
2026
unsigned _dos_setfileattr(const char *name, unsigned attr)
2028
#if 0 /* stripping of trailing '/' is not needed for unzip-internal use */
2029
unsigned namlen = strlen(name);
2030
char *i_name = alloca(namlen + 1);
2032
strcpy(i_name, name);
2033
if (namlen > 1 && i_name[namlen-1] == '/' && i_name[namlen-2] != ':')
2034
i_name[namlen-1] = '\0';
2035
asm("movl %0, %%edx": : "g" (i_name));
2037
asm("movl %0, %%edx": : "g" (name));
2039
asm("movl %0, %%ecx": : "g" (attr));
2040
asm("movl $0x4301, %eax");
2041
asm("int $0x21": : : "%eax", "%ebx", "%ecx", "%edx", "%esi", "%edi");
2044
asm("movl %%eax, %0": "=m" (_doserrno));
2045
switch (_doserrno) {
2055
return (unsigned)_doserrno;
2058
void _dos_getdrive(unsigned *d)
2060
asm("movl $0x1900, %eax");
2061
asm("int $0x21": : : "%eax", "%ebx", "%ecx", "%edx", "%esi", "%edi");
2062
asm("xorb %ah, %ah");
2064
asm("movl %%eax, %0": "=a" (*d));
2067
unsigned _dos_creat(const char *path, unsigned attr, int *fd)
2069
asm("movl $0x3c00, %eax");
2070
asm("movl %0, %%edx": :"g" (path));
2071
asm("movl %0, %%ecx": :"g" (attr));
2072
asm("int $0x21": : : "%eax", "%ebx", "%ecx", "%edx", "%esi", "%edi");
2073
asm("movl %%eax, %0": "=a" (*fd));
2077
switch (_doserrno) {
2089
return (unsigned)_doserrno;
2092
unsigned _dos_close(int fd)
2094
asm("movl %0, %%ebx": : "g" (fd));
2095
asm("movl $0x3e00, %eax");
2096
asm("int $0x21": : : "%eax", "%ebx", "%ecx", "%edx", "%esi", "%edi");
2099
asm ("movl %%eax, %0": "=m" (_doserrno));
2100
if (_doserrno == 6) {
2104
return (unsigned)_doserrno;
2107
#endif /* !__DJGPP__ || (__DJGPP__ < 2) */
2110
static int volumelabel(ZCONST char *name)
2114
return _dos_creat(name, FA_LABEL, &fd) ? fd : _dos_close(fd);
2118
#if (defined(__DJGPP__) && (__DJGPP__ >= 2))
2120
#include <dpmi.h> /* These includes for the country info */
2122
#include <sys/farptr.h>
2124
/* The above _dos_getcountryinfo function doesn't work with djgpp v2, presumably
2125
* because ds is not set correctly (does it really work at all?). Note that
2126
* this version only sets the date (ie. CountryInfo[0]).
2128
unsigned _dos_getcountryinfo(void *countrybuffer)
2133
regs.x.dx = __tb & 0x0f;
2134
regs.x.ds = (__tb >> 4) & 0xffff;
2135
_doserrno = __dpmi_int(0x21, ®s);
2137
*(ush*)countrybuffer = _farpeekw(_dos_ds, __tb & 0xfffff);
2139
return (unsigned)_doserrno;
2143
/* Disable determination of "x" bit in st_mode field for [f]stat() calls. */
2144
int _is_executable (const char *path, int fhandle, const char *ext)
2149
#ifndef USE_DJGPP_GLOB
2150
/* Prevent globbing of filenames. This gives the same functionality as
2151
* "stubedit <program> globbing=no" did with DJGPP v1.
2153
char **__crt0_glob_function(char *_arg)
2157
#endif /* !USE_DJGPP_GLOB */
2159
#ifndef USE_DJGPP_ENV
2160
/* Reduce the size of the executable and remove the functionality to read
2161
* the program's environment from whatever $DJGPP points to.
2163
void __crt0_load_environment_file(char *_app_name)
2166
#endif /* !USE_DJGPP_ENV */
2168
#endif /* __DJGPP__ >= 2 */
2169
#endif /* __GO32__ || __EMX__ */
2176
/*************************/
2177
/* Function screensize() */
2178
/*************************/
2180
int screensize(int *tt_rows, int *tt_cols)
2182
int scr_dimen[2]; /* scr_dimen[0]: columns, src_dimen[1]: rows */
2184
_scrsize(scr_dimen);
2185
if (tt_rows != NULL) *tt_rows = scr_dimen[1];
2186
if (tt_cols != NULL) *tt_cols = scr_dimen[0];
2191
#endif /* __EMX__ */
2199
/*************************/
2200
/* Function screensize() */
2201
/*************************/
2203
int screensize(int *tt_rows, int *tt_cols)
2205
struct videoconfig vc;
2207
_getvideoconfig(&vc);
2208
if (tt_rows != NULL) *tt_rows = (int)(vc.numtextrows);
2209
if (tt_cols != NULL) *tt_cols = (int)(vc.numtextcols);
2216
static struct RMINFO {
2219
ulg ebx, edx, ecx, eax;
2222
ush ip_ignored,cs_ignored;
2226
/* This function is used to call dos interrupts that may not be supported
2227
* by some particular 32-bit DOS extender. It uses DPMI function 300h to
2228
* simulate a real mode call of the interrupt. The caller is responsible
2229
* for providing real mode addresses of any buffer areas used. The docs
2230
* for PMODE/W imply that this should not be necessary for calling the DOS
2231
* interrupts that it doesn't extend, but it crashes when this isn't used. */
2233
static int int86x_realmode(int inter_no, union REGS *in,
2234
union REGS *out, struct SREGS *seg)
2237
struct SREGS localseg;
2241
rmi.eax = in->x.eax;
2242
rmi.ebx = in->x.ebx;
2243
rmi.ecx = in->x.ecx;
2244
rmi.edx = in->x.edx;
2245
rmi.edi = in->x.edi;
2246
rmi.esi = in->x.esi;
2247
rmi.ebp = rmi.reserved = 0L;
2252
rmi.sp = rmi.ss = rmi.ip_ignored = rmi.cs_ignored = rmi.flags = 0;
2253
memset(&local, 0, sizeof(local));
2254
memset(&localseg, 0, sizeof(localseg));
2255
local.w.ax = 0x0300;
2256
local.h.bl = inter_no;
2259
localseg.es = FP_SEG(&rmi);
2260
local.x.edi = FP_OFF(&rmi);
2261
r = int386x(0x31, &local, &local, &localseg);
2262
out->x.eax = rmi.eax;
2263
out->x.ebx = rmi.ebx;
2264
out->x.ecx = rmi.ecx;
2265
out->x.edx = rmi.edx;
2266
out->x.edi = rmi.edi;
2267
out->x.esi = rmi.esi;
2268
out->x.cflag = rmi.flags & INTR_CF;
2272
#endif /* WATCOMC_386 */
2277
#ifdef DOS_STAT_BANDAID
2279
/* This papers over a bug in Watcom 10.6's standard library...sigh.
2280
* Apparently it applies to both the DOS and Win32 stat()s. */
2282
int stat_bandaid(const char *path, struct stat *buf)
2286
if (!stat(path, buf))
2288
else if (!strcmp(path, ".") || (path[0] && !strcmp(path + 1, ":."))) {
2289
strcpy(newname, path);
2290
newname[strlen(path) - 1] = '\\'; /* stat(".") fails for root! */
2291
return stat(newname, buf);
2296
#endif /* DOS_STAT_BANDAID */
2298
#endif /* !FUNZIP */