2
Copyright (c) 1990-2002 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
32-bit Windows-specific (NT/95) routines for use with Info-ZIP's UnZip 5.3
16
Contains: GetLoadPath()
20
process_defer_NT() process any deferred items
21
SetSD() set security descriptor on file
22
EvalExtraFields() evaluate and process and extra field NOW
23
IsWinNT() indicate type of WIN32 platform
24
test_NTSD() test integrity of NT security data
49
GetPlatformLocalTimezone()
52
---------------------------------------------------------------------------*/
55
#define UNZIP_INTERNAL
57
#include <windows.h> /* must be AFTER unzip.h to avoid struct G problems */
59
# include "../win32/rsxntwin.h"
61
#include "../win32/nt.h"
63
#ifndef FUNZIP /* most of this file is not used with fUnZip */
65
#if (defined(__EMX__) || defined(__CYGWIN__))
66
# define MKDIR(path,mode) mkdir(path,mode)
68
# define MKDIR(path,mode) mkdir(path)
71
#ifdef HAVE_WORKING_DIRENT_H
72
# undef HAVE_WORKING_DIRENT_H
74
/* The emxrtl dirent support of (__GO32__ || __EMX__) converts to lowercase! */
75
#if defined(__CYGWIN__)
76
# define HAVE_WORKING_DIRENT_H
82
# ifdef HAVE_WORKING_DIRENT_H
83
# include <dirent.h> /* use readdir() */
84
# define zdirent dirent
86
# define Opendir opendir
87
# define Readdir readdir
88
# define Closedir closedir
89
# else /* !HAVE_WORKING_DIRENT_H */
90
typedef struct zdirent {
96
char d_name[MAX_PATH];
101
static zDIR *Opendir (const char *n);
102
static struct zdirent *Readdir (zDIR *d);
103
static void Closedir (zDIR *d);
104
# endif /* ?HAVE_WORKING_DIRENT_H */
109
/* Function prototypes */
111
static int SetSD(__GPRO__ char *path, PVOLUMECAPS VolumeCaps,
112
uch *eb_ptr, unsigned eb_len);
113
static int EvalExtraFields(__GPRO__ char *path, uch *ef_ptr,
117
#if (defined(USE_EF_UT_TIME) || defined(NT_TZBUG_WORKAROUND) || \
119
static void utime2FileTime(time_t ut, FILETIME *pft);
120
static int FStampIsLocTime(__GPRO__ const char *path);
121
#endif /* USE_EF_UT_TIME || NT_TZBUG_WORKAROUND || TIMESTAMP */
122
#ifdef NT_TZBUG_WORKAROUND
123
static int FileTime2utime(const FILETIME *pft, time_t *ut);
124
#ifdef W32_STAT_BANDAID
125
static int VFatFileTime2utime(const FILETIME *pft, time_t *ut);
127
static time_t UTCtime2Localtime(time_t utctime);
128
static void NTtzbugWorkaround(time_t ut, FILETIME *pft);
129
#endif /* NT_TZBUG_WORKAROUND */
132
static int getNTfiletime (__GPRO__ FILETIME *pModFT, FILETIME *pAccFT,
134
static int isfloppy (int nDrive);
135
static int NTQueryVolInfo (__GPRO__ const char *name);
136
static int IsVolumeOldFAT (__GPRO__ const char *name);
137
static void maskDOSdevice (__GPRO__ char *pathcomp);
138
static void map2fat (char *pathcomp, char **pEndFAT);
142
int _CRT_glob = 0; /* suppress command line globbing by C RTL */
145
#ifdef ACORN_FTYPE_NFS
146
/* Acorn bits for NFS filetyping */
156
#endif /* ACORN_FTYPE_NFS */
158
/* static int created_dir; */ /* used by mapname(), checkdir() */
159
/* static int renamed_fullpath; */ /* ditto */
160
/* static int fnlen; */ /* ditto */
161
/* static unsigned nLabelDrive; */ /* ditto */
163
extern char Far TruncNTSD[]; /* in extract.c */
169
/**************************/
170
/* Function GetLoadPath() */
171
/**************************/
173
char *GetLoadPath(__GPRO)
176
extern char *_pgmptr;
179
#else /* use generic API call */
181
GetModuleFileName(NULL, G.filename, FILNAMSIZ-1);
182
_ISO_INTERN(G.filename); /* translate to codepage of C rtl's stdio */
186
} /* end function GetLoadPath() */
194
#ifndef HAVE_WORKING_DIRENT_H
196
/**********************/ /* Borrowed from ZIP 2.0 sources */
197
/* Function Opendir() */ /* Difference: no special handling for */
198
/**********************/ /* hidden or system files. */
200
static zDIR *Opendir(n)
201
const char *n; /* directory to open */
203
zDIR *d; /* malloc'd return value */
204
char *p; /* malloc'd temporary string */
206
extent len = strlen(n);
208
/* Start searching for files in directory n */
210
if ((d = (zDIR *)malloc(sizeof(zDIR))) == NULL ||
211
(p = malloc(strlen(n) + 5)) == NULL)
213
if (d != (zDIR *)NULL)
220
p[len++] = '.'; /* x: => x:. */
221
else if (p[len-1] == '/' || p[len-1] == '\\')
222
--len; /* foo/ => foo */
226
if (INVALID_HANDLE_VALUE == (d->d_hFindFile = FindFirstFile(p, &fd))) {
231
strcpy(d->d_name, fd.cFileName);
237
} /* end of function Opendir() */
242
/**********************/ /* Borrowed from ZIP 2.0 sources */
243
/* Function Readdir() */ /* Difference: no special handling for */
244
/**********************/ /* hidden or system files. */
246
static struct zdirent *Readdir(d)
247
zDIR *d; /* directory stream from which to read */
249
/* Return pointer to first or next directory entry, or NULL if end. */
257
if ( !FindNextFile(d->d_hFindFile, &fd) )
260
ISO_TO_INTERN(fd.cFileName, d->d_name);
262
return (struct zdirent *)d;
264
} /* end of function Readdir() */
269
/***********************/
270
/* Function Closedir() */ /* Borrowed from ZIP 2.0 sources */
271
/***********************/
273
static void Closedir(d)
274
zDIR *d; /* directory stream to close */
276
FindClose(d->d_hFindFile);
280
#endif /* !HAVE_WORKING_DIRENT_H */
288
/*********************************/
289
/* Function process_defer_NT() */
290
/*********************************/
292
void process_defer_NT(__G)
295
/* process deferred items */
298
DWORD dirfail, bytesfail;
300
ProcessDefer(&dir, &bytes, &dirfail, &bytesfail);
302
if (!uO.tflag && (uO.qflag < 2)) {
304
Info(slide, 0, ((char *)slide,
305
" updated: %lu directory entries with %lu bytes security",
306
(ulg)dir, (ulg)bytes));
308
Info(slide, 0, ((char *)slide,
309
" failed: %lu directory entries with %lu bytes security",
310
(ulg)dirfail, (ulg)bytesfail));
316
/**********************/
317
/* Function SetSD() */ /* return almost-PK errors */
318
/**********************/
320
static int SetSD(__G__ path, VolumeCaps, eb_ptr, eb_len)
323
PVOLUMECAPS VolumeCaps;
331
if (eb_ptr == NULL || eb_len < EB_NTSD_L_LEN)
332
return PK_OK; /* not a valid NTSD extra field: assume OK */
334
/* check if we know how to handle this version */
335
if (*(eb_ptr + (EB_HEADSIZE+EB_NTSD_VERSION)) > (uch)EB_NTSD_MAX_VER)
338
ntsd_ucSize = makelong(eb_ptr + (EB_HEADSIZE+EB_UCSIZE_P));
339
if (ntsd_ucSize > 0L && eb_len <= (EB_NTSD_L_LEN + EB_CMPRHEADLEN))
340
return IZ_EF_TRUNC; /* no compressed data! */
342
/* allocate storage for uncompressed data */
343
security_data = (uch *)malloc((extent)ntsd_ucSize);
344
if (security_data == (uch *)NULL)
347
error = memextract(__G__ security_data, ntsd_ucSize,
348
(eb_ptr + (EB_HEADSIZE+EB_NTSD_L_LEN)), (ulg)(eb_len - EB_NTSD_L_LEN));
350
if (error == PK_OK) {
351
if (SecuritySet(path, VolumeCaps, security_data)) {
353
if (!uO.tflag && (uO.qflag < 2) &&
354
(!(VolumeCaps->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)))
355
Info(slide, 0, ((char *)slide, " (%ld bytes security)",
367
/********************************/ /* scan extra fields for something */
368
/* Function EvalExtraFields() */ /* we happen to know */
369
/********************************/
371
static int EvalExtraFields(__G__ path, ef_ptr, ef_len)
380
return PK_OK; /* user said don't process ACLs; for now, no other
381
extra block types are handled here */
383
while (ef_len >= EB_HEADSIZE)
385
unsigned eb_id = makeword(EB_ID + ef_ptr);
386
unsigned eb_len = makeword(EB_LEN + ef_ptr);
388
if (eb_len > (ef_len - EB_HEADSIZE)) {
389
/* discovered some extra field inconsistency! */
391
"EvalExtraFields: block length %u > rest ef_size %u\n", eb_len,
392
ef_len - EB_HEADSIZE));
398
/* process security descriptor extra data if:
400
Target local/remote drive supports acls AND
401
Target file is not a directory (else we defer processing
406
VOLUMECAPS VolumeCaps;
408
/* provide useful input */
409
VolumeCaps.dwFileAttributes = G.pInfo->file_attr;
410
VolumeCaps.bUsePrivileges = (uO.X_flag > 1);
412
/* check target volume capabilities - just fall through
414
if (GetVolumeCaps(G.rootpath, path, &VolumeCaps) &&
415
!(VolumeCaps.dwFileSystemFlags & FS_PERSISTENT_ACLS))
420
rc = SetSD(__G__ path, &VolumeCaps, ef_ptr, eb_len);
426
/* perhaps later we can add support for unzipping OS/2 EAs to NT */
428
rc = SetEAs(__G__ path, ef_ptr);
457
break; /* shut up for other known e.f. blocks */
462
"EvalExtraFields: unknown extra field block, ID=%u\n",
467
ef_ptr += (eb_len + EB_HEADSIZE);
468
ef_len -= (eb_len + EB_HEADSIZE);
482
/**************************/
483
/* Function test_NTSD() */ /* returns PK_WARN when NTSD data is invalid */
484
/**************************/
487
/* Turn off warning about not using all parameters for this function only */
490
int test_NTSD(__G__ eb, eb_size, eb_ucptr, eb_ucsize)
499
if (!ValidateSecurity(eb_ucptr))
503
} /* end function test_NTSD() */
506
#endif /* NTSD_EAS */
511
/**********************/
512
/* Function IsWinNT() */
513
/**********************/
515
int IsWinNT(void) /* returns TRUE if real NT, FALSE if Win95 or Win32s */
517
static DWORD g_PlatformId = 0xFFFFFFFF; /* saved platform indicator */
519
if (g_PlatformId == 0xFFFFFFFF) {
520
/* note: GetVersionEx() doesn't exist on WinNT 3.1 */
521
if (GetVersion() < 0x80000000)
524
g_PlatformId = FALSE;
526
return (int)g_PlatformId;
530
/* DEBUG_TIME insertion: */
532
static int show_NTFileTime(FILE *hdo, char *TTmsg, int isloc, FILETIME *pft);
534
static int show_NTFileTime(FILE *hdo, char *TTmsg, int isloc, FILETIME *pft)
539
rval = FileTimeToSystemTime(pft, &w32tm);
541
fprintf(hdo, "%s\n %08lX,%08lX (%s) -> Conversion failed !!!\n",
542
TTmsg, (ulg)(pft->dwHighDateTime), (ulg)(pft->dwLowDateTime),
543
(isloc ? "local" : "UTC"));
545
fprintf(hdo, "%s\n %08lx,%08lx -> %04u-%02u-%02u, %02u:%02u:%02u %s\n",
546
TTmsg, (ulg)(pft->dwHighDateTime), (ulg)(pft->dwLowDateTime),
547
w32tm.wYear, w32tm.wMonth, w32tm.wDay, w32tm.wHour,
548
w32tm.wMinute, w32tm.wSecond, (isloc ? "local" : "UTC"));
552
#define FTTrace(x) show_NTFileTime x
555
#endif /* DEBUG_TIME */
556
/* end of TIME_DEBUG insertion */
558
#if (defined(USE_EF_UT_TIME) || defined(NT_TZBUG_WORKAROUND) || \
562
# if (defined(__GNUC__) || defined(ULONG_LONG_MAX))
563
typedef long long LLONG64;
564
typedef unsigned long long ULLNG64;
565
# define IZ_USE_INT64
566
# elif (defined(__WATCOMC__) && (__WATCOMC__ >= 1100))
567
typedef __int64 LLONG64;
568
typedef unsigned __int64 ULLNG64;
569
# define IZ_USE_INT64
570
# elif (defined(_MSC_VER) && (_MSC_VER >= 1100))
571
typedef __int64 LLONG64;
572
typedef unsigned __int64 ULLNG64;
573
# define IZ_USE_INT64
574
# elif (defined(__IBMC__) && (__IBMC__ >= 350))
575
typedef __int64 LLONG64;
576
typedef unsigned __int64 ULLNG64;
577
# define IZ_USE_INT64
578
# elif defined(HAVE_INT64)
579
typedef __int64 LLONG64;
580
typedef unsigned __int64 ULLNG64;
581
# define IZ_USE_INT64
585
/*****************************/
586
/* Function utime2FileTime() */ /* convert Unix time_t format into the */
587
/*****************************/ /* form used by SetFileTime() in NT/95 */
589
#define UNIX_TIME_ZERO_HI 0x019DB1DEUL
590
#define UNIX_TIME_ZERO_LO 0xD53E8000UL
591
#define NT_QUANTA_PER_UNIX 10000000L
593
static void utime2FileTime(time_t ut, FILETIME *pft)
598
/* NT_QUANTA_PER_UNIX is small enough so that "ut * NT_QUANTA_PER_UNIX"
599
* cannot overflow in 64-bit signed calculation, regardless wether "ut"
600
* is signed or unsigned. */
601
NTtime = ((LLONG64)ut * NT_QUANTA_PER_UNIX) +
602
((ULLNG64)UNIX_TIME_ZERO_LO + ((ULLNG64)UNIX_TIME_ZERO_HI << 32));
603
pft->dwLowDateTime = (DWORD)NTtime;
604
pft->dwHighDateTime = (DWORD)(NTtime >> 32);
606
#else /* !IZ_USE_INT64 (64-bit integer arithmetics may not be supported) */
607
unsigned int b1, b2, carry = 0;
608
unsigned long r0, r1, r2, r3;
609
long r4; /* signed, to catch environments with signed time_t */
612
b2 = (ut >> 16) & 0xFFFF; /* if ut is over 32 bits, too bad */
613
r1 = b1 * (NT_QUANTA_PER_UNIX & 0xFFFF);
614
r2 = b1 * (NT_QUANTA_PER_UNIX >> 16);
615
r3 = b2 * (NT_QUANTA_PER_UNIX & 0xFFFF);
616
r4 = b2 * (NT_QUANTA_PER_UNIX >> 16);
617
r0 = (r1 + (r2 << 16)) & 0xFFFFFFFFL;
621
r0 = (r0 + (r3 << 16)) & 0xFFFFFFFFL;
624
pft->dwLowDateTime = r0 + UNIX_TIME_ZERO_LO;
625
if (pft->dwLowDateTime < r0)
627
pft->dwHighDateTime = r4 + (r2 >> 16) + (r3 >> 16)
628
+ UNIX_TIME_ZERO_HI + carry;
629
#endif /* ?IZ_USE_INT64 */
631
} /* end function utime2FileTime() */
635
/******************************/
636
/* Function FStampIsLocTime() */
637
/******************************/
639
static int FStampIsLocTime(__GPRO__ const char *path)
641
return (NTQueryVolInfo(__G__ path) ? G.lastVolLocTim : FALSE);
644
#endif /* USE_EF_UT_TIME || NT_TZBUG_WORKAROUND || TIMESTAMP */
648
#ifndef NT_TZBUG_WORKAROUND
649
# define UTIME_BOUNDCHECK_1(utimval) \
650
if (fs_uses_loctime) { \
651
utime_dosmin = dos_to_unix_time(DOSTIME_MINIMUM); \
652
if ((ulg)utimval < (ulg)utime_dosmin) \
653
utimval = utime_dosmin; \
655
# define UTIME_BOUNDCHECK_N(utimval) \
656
if (fs_uses_loctime && ((ulg)utimval < (ulg)utime_dosmin)) \
657
utimval = utime_dosmin;
658
# define NT_TZBUG_PRECOMPENSATE(ut, pft)
660
#else /* NT_TZBUG_WORKAROUND */
661
# define UNIX_TIME_UMAX_HI 0x0236485EUL
662
# define UNIX_TIME_UMAX_LO 0xD4A5E980UL
663
# define UNIX_TIME_SMIN_HI 0x0151669EUL
664
# define UNIX_TIME_SMIN_LO 0xD53E8000UL
665
# define UNIX_TIME_SMAX_HI 0x01E9FD1EUL
666
# define UNIX_TIME_SMAX_LO 0xD4A5E980UL
667
# define UTIME_1980_JAN_01_00_00 315532800L
668
# define UTIME_BOUNDCHECK_1(utimval)
669
# define UTIME_BOUNDCHECK_N(utimval)
670
# define NT_TZBUG_PRECOMPENSATE(ut, pft) \
671
if (fs_uses_loctime) NTtzbugWorkaround(ut, pft);
673
/* nonzero if `y' is a leap year, else zero */
674
# define leap(y) (((y)%4 == 0 && (y)%100 != 0) || (y)%400 == 0)
675
/* number of leap years from 1970 to `y' (not including `y' itself) */
676
# define nleap(y) (((y)-1969)/4 - ((y)-1901)/100 + ((y)-1601)/400)
678
extern ZCONST ush ydays[]; /* defined in fileio.c */
680
/*****************************/
681
/* Function FileTime2utime() */
682
/*****************************/
684
static int FileTime2utime(const FILETIME *pft, time_t *ut)
689
NTtime = ((ULLNG64)pft->dwLowDateTime +
690
((ULLNG64)pft->dwHighDateTime << 32));
692
#ifndef TIME_T_TYPE_DOUBLE
693
/* underflow and overflow handling */
694
#ifdef CHECK_UTIME_SIGNED_UNSIGNED
695
if ((time_t)0x80000000L < (time_t)0L)
697
if (NTtime < ((ULLNG64)UNIX_TIME_SMIN_LO +
698
((ULLNG64)UNIX_TIME_SMIN_HI << 32))) {
699
*ut = (time_t)LONG_MIN;
702
if (NTtime > ((ULLNG64)UNIX_TIME_SMAX_LO +
703
((ULLNG64)UNIX_TIME_SMAX_HI << 32))) {
704
*ut = (time_t)LONG_MAX;
709
#endif /* CHECK_UTIME_SIGNED_UNSIGNED */
711
if (NTtime < ((ULLNG64)UNIX_TIME_ZERO_LO +
712
((ULLNG64)UNIX_TIME_ZERO_HI << 32))) {
716
if (NTtime > ((ULLNG64)UNIX_TIME_UMAX_LO +
717
((ULLNG64)UNIX_TIME_UMAX_HI << 32))) {
718
*ut = (time_t)ULONG_MAX;
722
#endif /* !TIME_T_TYPE_DOUBLE */
724
NTtime -= ((ULLNG64)UNIX_TIME_ZERO_LO +
725
((ULLNG64)UNIX_TIME_ZERO_HI << 32));
726
*ut = (time_t)(NTtime / (unsigned long)NT_QUANTA_PER_UNIX);
728
#else /* !IZ_USE_INT64 (64-bit integer arithmetics may not be supported) */
732
#ifndef TIME_T_TYPE_DOUBLE
733
/* underflow and overflow handling */
734
#ifdef CHECK_UTIME_SIGNED_UNSIGNED
735
if ((time_t)0x80000000L < (time_t)0L)
737
if ((pft->dwHighDateTime < UNIX_TIME_SMIN_HI) ||
738
((pft->dwHighDateTime == UNIX_TIME_SMIN_HI) &&
739
(pft->dwLowDateTime < UNIX_TIME_SMIN_LO))) {
740
*ut = (time_t)LONG_MIN;
742
if ((pft->dwHighDateTime > UNIX_TIME_SMAX_HI) ||
743
((pft->dwHighDateTime == UNIX_TIME_SMAX_HI) &&
744
(pft->dwLowDateTime > UNIX_TIME_SMAX_LO))) {
745
*ut = (time_t)LONG_MAX;
750
#endif /* CHECK_UTIME_SIGNED_UNSIGNED */
752
if ((pft->dwHighDateTime < UNIX_TIME_ZERO_HI) ||
753
((pft->dwHighDateTime == UNIX_TIME_ZERO_HI) &&
754
(pft->dwLowDateTime < UNIX_TIME_ZERO_LO))) {
758
if ((pft->dwHighDateTime > UNIX_TIME_UMAX_HI) ||
759
((pft->dwHighDateTime == UNIX_TIME_UMAX_HI) &&
760
(pft->dwLowDateTime > UNIX_TIME_UMAX_LO))) {
761
*ut = (time_t)ULONG_MAX;
765
#endif /* !TIME_T_TYPE_DOUBLE */
767
FileTimeToSystemTime(pft, &w32tm);
769
/* set `days' to the number of days into the year */
770
days = w32tm.wDay - 1 + ydays[w32tm.wMonth-1] +
771
(w32tm.wMonth > 2 && leap (w32tm.wYear));
773
/* now set `days' to the number of days since 1 Jan 1970 */
774
days += 365 * (time_t)(w32tm.wYear - 1970) +
775
(time_t)(nleap(w32tm.wYear));
777
*ut = (time_t)(86400L * days + 3600L * (time_t)w32tm.wHour +
778
(time_t)(60 * w32tm.wMinute + w32tm.wSecond));
780
#endif /* ?IZ_USE_INT64 */
781
} /* end function FileTime2utime() */
785
#ifdef W32_STAT_BANDAID
786
/*********************************/
787
/* Function VFatFileTime2utime() */
788
/*********************************/
790
static int VFatFileTime2utime(const FILETIME *pft, time_t *ut)
794
WORD wDOSDate, wDOSTime;
800
FileTimeToLocalFileTime(pft, &lft);
801
FTTrace((stdout, "VFatFT2utime, feed for mktime()", 1, &lft));
803
/* This version of the FILETIME-to-UNIXTIME conversion function
804
* uses DOS-DATE-TIME format as intermediate stage. For modification
805
* and access times, this is no problem. But, the extra fine resolution
806
* of the VFAT-stored creation time gets lost.
808
FileTimeToDosDateTime(&lft, &wDOSDate, &wDOSTime);
809
TTrace((stdout,"DosDateTime is %04u-%02u-%02u %02u:%02u:%02u\n",
810
(unsigned)((wDOSDate>>9)&0x7f)+1980,(unsigned)((wDOSDate>>5)&0x0f),
811
(unsigned)(wDOSDate&0x1f),(unsigned)((wDOSTime>>11)&0x1f),
812
(unsigned)((wDOSTime>>5)&0x3f),(unsigned)((wDOSTime<<1)&0x3e)));
813
*ut = dos_to_unix_time(((ulg)wDOSDate << 16) | (ulg)wDOSTime);
815
/* a cheap error check: dos_to_unix_time() only returns an odd time
816
* when clipping at maximum time_t value. DOS_DATE_TIME values have
817
* a resolution of 2 seconds and are therefore even numbers.
819
return (((*ut)&1) == (time_t)0);
820
#else /* HAVE_MKTIME */
821
FileTimeToSystemTime(&lft, &w32tm);
822
#ifndef TIME_T_TYPE_DOUBLE
823
/* underflow and overflow handling */
824
/* TODO: The range checks are not accurate, the actual limits may
825
* be off by one daylight-saving-time shift (typically 1 hour),
826
* depending on the current state of "is_dst".
828
#ifdef CHECK_UTIME_SIGNED_UNSIGNED
829
if ((time_t)0x80000000L < (time_t)0L)
831
if ((pft->dwHighDateTime < UNIX_TIME_SMIN_HI) ||
832
((pft->dwHighDateTime == UNIX_TIME_SMIN_HI) &&
833
(pft->dwLowDateTime < UNIX_TIME_SMIN_LO))) {
834
*ut = (time_t)LONG_MIN;
836
if ((pft->dwHighDateTime > UNIX_TIME_SMAX_HI) ||
837
((pft->dwHighDateTime == UNIX_TIME_SMAX_HI) &&
838
(pft->dwLowDateTime > UNIX_TIME_SMAX_LO))) {
839
*ut = (time_t)LONG_MAX;
844
#endif /* CHECK_UTIME_SIGNED_UNSIGNED */
846
if ((pft->dwHighDateTime < UNIX_TIME_ZERO_HI) ||
847
((pft->dwHighDateTime == UNIX_TIME_ZERO_HI) &&
848
(pft->dwLowDateTime < UNIX_TIME_ZERO_LO))) {
852
if ((pft->dwHighDateTime > UNIX_TIME_UMAX_HI) ||
853
((pft->dwHighDateTime == UNIX_TIME_UMAX_HI) &&
854
(pft->dwLowDateTime > UNIX_TIME_UMAX_LO))) {
855
*ut = (time_t)ULONG_MAX;
859
#endif /* !TIME_T_TYPE_DOUBLE */
860
ltm.tm_year = w32tm.wYear - 1900;
861
ltm.tm_mon = w32tm.wMonth - 1;
862
ltm.tm_mday = w32tm.wDay;
863
ltm.tm_hour = w32tm.wHour;
864
ltm.tm_min = w32tm.wMinute;
865
ltm.tm_sec = w32tm.wSecond;
866
ltm.tm_isdst = -1; /* let mktime determine if DST is in effect */
869
/* a cheap error check: mktime returns "(time_t)-1L" on conversion errors.
870
* Normally, we would have to apply a consistency check because "-1"
871
* could also be a valid time. But, it is quite unlikely to read back odd
872
* time numbers from file systems that store time stamps in DOS format.
873
* (The only known exception is creation time on VFAT partitions.)
875
return (*ut != (time_t)-1L);
876
#endif /* ?HAVE_MKTIME */
878
} /* end function VFatFileTime2utime() */
879
#endif /* W32_STAT_BANDAID */
883
/********************************/
884
/* Function UTCtime2Localtime() */ /* borrowed from Zip's mkgmtime() */
885
/********************************/
887
static time_t UTCtime2Localtime(time_t utctime)
889
time_t utc = utctime;
891
int years, months, days, hours, minutes, seconds;
894
#ifdef __BORLANDC__ /* Borland C++ 5.x crashes when trying to reference tm */
895
if (utc < UTIME_1980_JAN_01_00_00)
896
utc = UTIME_1980_JAN_01_00_00;
898
tm = localtime(&utc);
899
if (tm == (struct tm *)NULL)
900
/* localtime() did not accept given utc time value; as an emergency
901
exit, the unconverted utctime value is returned */
904
years = tm->tm_year + 1900; /* year - 1900 -> year */
905
months = tm->tm_mon; /* 0..11 */
906
days = tm->tm_mday - 1; /* 1..31 -> 0..30 */
907
hours = tm->tm_hour; /* 0..23 */
908
minutes = tm->tm_min; /* 0..59 */
909
seconds = tm->tm_sec; /* 0..61 in ANSI C */
911
/* set `days' to the number of days into the year */
912
days += ydays[months] + (months > 1 && leap(years));
914
/* now set `days' to the number of days since 1 Jan 1970 */
915
days += 365 * (years - 1970) + nleap(years);
917
return (time_t)(86400L * (time_t)days +
918
(time_t)(3600L * hours + (60 * minutes + seconds)));
920
} /* end function UTCtime2Localtime() */
924
/********************************/
925
/* Function NTtzbugWorkaround() */
926
/********************************/
928
static void NTtzbugWorkaround(time_t ut, FILETIME *pft)
930
FILETIME C_RTL_locft, NTAPI_locft;
931
time_t ux_loctime = UTCtime2Localtime(ut);
933
/* This routine is only used when the target file system stores time-
934
* stamps as local time in MSDOS format. Thus we make sure that the
935
* resulting timestamp is within the range of MSDOS date-time values. */
936
if (ux_loctime < UTIME_1980_JAN_01_00_00)
937
ux_loctime = UTIME_1980_JAN_01_00_00;
939
utime2FileTime(ux_loctime, &C_RTL_locft);
940
if (!FileTimeToLocalFileTime(pft, &NTAPI_locft))
943
long time_shift_l, time_shift_h;
946
time_shift_l = C_RTL_locft.dwLowDateTime - NTAPI_locft.dwLowDateTime;
947
if (C_RTL_locft.dwLowDateTime < NTAPI_locft.dwLowDateTime)
949
time_shift_h = C_RTL_locft.dwHighDateTime - NTAPI_locft.dwHighDateTime;
950
pft->dwLowDateTime += time_shift_l;
951
if (pft->dwLowDateTime < (ulg)time_shift_l)
953
pft->dwHighDateTime += time_shift_h + carry;
954
TTrace((stdout, "FileTime shift: %08lx:%08lx\n",
955
time_shift_h+carry,time_shift_l));
957
} /* end function NTtzbugWorkaround() */
959
#endif /* ?NT_TZBUG_WORKAROUND */
963
/****************************/ /* Get the file time in a format that */
964
/* Function getNTfiletime() */ /* can be used by SetFileTime() in NT */
965
/****************************/
967
static int getNTfiletime(__G__ pModFT, pAccFT, pCreFT)
973
#ifdef NT_TZBUG_WORKAROUND
975
#else /* !NT_TZBUG_WORKAROUND */
976
FILETIME locft; /* 64-bit value made up of two 32-bit [low & high] */
977
WORD wDOSDate; /* for converting from DOS date to Windows NT */
979
#endif /* ?NT_TZBUG_WORKAROUND */
980
#ifdef USE_EF_UT_TIME
981
unsigned eb_izux_flg;
982
iztimes z_utime; /* struct for Unix-style actime & modtime, + creatime */
984
#if (defined(USE_EF_UT_TIME) && !defined(NT_TZBUG_WORKAROUND))
987
#if (defined(USE_EF_UT_TIME) || defined(NT_TZBUG_WORKAROUND))
988
int fs_uses_loctime = FStampIsLocTime(__G__ G.filename);
991
/* Copy and/or convert time and date variables, if necessary;
992
* return a flag indicating which time stamps are available. */
993
#ifdef USE_EF_UT_TIME
998
((eb_izux_flg = ef_scan_for_izux(G.extra_field,
999
G.lrec.extra_field_length, 0, G.lrec.last_mod_dos_datetime,
1000
&z_utime, NULL)) & EB_UT_FL_MTIME))
1002
TTrace((stderr, "getNTfiletime: Unix e.f. modif. time = %lu\n",
1004
UTIME_BOUNDCHECK_1(z_utime.mtime)
1005
utime2FileTime(z_utime.mtime, pModFT);
1006
NT_TZBUG_PRECOMPENSATE(z_utime.mtime, pModFT)
1007
if (eb_izux_flg & EB_UT_FL_ATIME) {
1008
UTIME_BOUNDCHECK_N(z_utime.atime)
1009
utime2FileTime(z_utime.atime, pAccFT);
1010
NT_TZBUG_PRECOMPENSATE(z_utime.atime, pAccFT)
1012
if (eb_izux_flg & EB_UT_FL_CTIME) {
1013
UTIME_BOUNDCHECK_N(z_utime.ctime)
1014
utime2FileTime(z_utime.ctime, pCreFT);
1015
NT_TZBUG_PRECOMPENSATE(z_utime.ctime, pCreFT)
1017
return (int)eb_izux_flg;
1019
#endif /* USE_EF_UT_TIME */
1020
#ifdef NT_TZBUG_WORKAROUND
1021
ux_modtime = dos_to_unix_time(G.lrec.last_mod_dos_datetime);
1022
utime2FileTime(ux_modtime, pModFT);
1023
NT_TZBUG_PRECOMPENSATE(ux_modtime, pModFT)
1024
#else /* !NT_TZBUG_WORKAROUND */
1026
wDOSTime = (WORD)(G.lrec.last_mod_dos_datetime);
1027
wDOSDate = (WORD)(G.lrec.last_mod_dos_datetime >> 16);
1029
/* The DosDateTimeToFileTime() function converts a DOS date/time
1030
* into a 64-bit Windows NT file time */
1031
if (!DosDateTimeToFileTime(wDOSDate, wDOSTime, &locft))
1033
Info(slide, 0, ((char *)slide, "DosDateTime failed: %d\n",
1034
(int)GetLastError()));
1037
if (!LocalFileTimeToFileTime(&locft, pModFT))
1039
Info(slide, 0, ((char *)slide, "LocalFileTime failed: %d\n",
1040
(int)GetLastError()));
1043
#endif /* ?NT_TZBUG_WORKAROUND */
1045
return (EB_UT_FL_MTIME | EB_UT_FL_ATIME);
1047
} /* end function getNTfiletime() */
1052
/**************************/
1053
/* Function SetFileSize() */
1054
/**************************/
1056
int SetFileSize(FILE *file, ulg filesize)
1059
/* RSXNT environment lacks a translation function from C file pointer
1060
to Win32-API file handle. So, simply do nothing. */
1062
#else /* !__RSXNT__ */
1063
/* not yet verified, if that really creates an unfragmented file
1068
/* Win9x supports FAT file system, only; presetting file size does
1069
not help to prevent fragmentation. */
1070
if (!IsWinNT()) return 0;
1072
/* Win32-API calls require access to the Win32 file handle.
1073
The interface function used to retrieve the Win32 handle for
1074
a file opened by the C rtl is non-standard and may not be
1075
available for every Win32 compiler environment.
1076
(see also win32/win32.c of the Zip distribution)
1078
os_fh = (HANDLE)_get_osfhandle(fileno(file));
1079
/* move file pointer behind the last byte of the expected file size */
1080
if (SetFilePointer(os_fh, filesize, 0, FILE_BEGIN) == 0xFFFFFFFF)
1082
/* extend/truncate file to the current position */
1083
if (SetEndOfFile(os_fh) == 0)
1085
/* move file position pointer back to the start of the file! */
1086
return (SetFilePointer(os_fh, 0, 0, FILE_BEGIN) == 0xFFFFFFFF) ? -1 : 0;
1087
#endif /* ?__RSXNT__ */
1088
} /* end function SetFileSize() */
1093
/****************************/
1094
/* Function close_outfile() */
1095
/****************************/
1097
void close_outfile(__G)
1100
FILETIME Modft; /* File time type defined in NT, `last modified' time */
1101
FILETIME Accft; /* NT file time type, `last access' time */
1102
FILETIME Creft; /* NT file time type, `file creation' time */
1103
HANDLE hFile; /* File handle defined in NT */
1105
#ifdef __RSXNT__ /* RSXNT/EMX C rtl uses OEM charset */
1106
char *ansi_name = (char *)alloca(strlen(G.filename) + 1);
1108
INTERN_TO_ISO(G.filename, ansi_name);
1109
# define Ansi_Fname ansi_name
1111
# define Ansi_Fname G.filename
1114
/* Close the file and then re-open it using the Win32
1115
* CreateFile call, so that the file can be created
1116
* with GENERIC_WRITE access, otherwise the SetFileTime
1117
* call will fail. */
1120
/* don't set the time stamp and attributes on standard output */
1124
gotTime = getNTfiletime(__G__ &Modft, &Accft, &Creft);
1126
/* open a handle to the file before processing extra fields;
1127
we do this in case new security on file prevents us from updating
1129
hFile = CreateFile(Ansi_Fname, GENERIC_WRITE, FILE_SHARE_WRITE, NULL,
1130
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
1132
/* sfield@microsoft.com: set attributes before time in case we decide to
1133
support other filetime members later. This also allows us to apply
1134
attributes before the security is changed, which may prevent this
1135
from succeeding otherwise. Also, since most files don't have
1136
any interesting attributes, only change them if something other than
1137
FILE_ATTRIBUTE_ARCHIVE appears in the attributes. This works well
1138
as an optimization because FILE_ATTRIBUTE_ARCHIVE gets applied to the
1139
file anyway, when it's created new. */
1140
if((G.pInfo->file_attr & 0x7F) & ~FILE_ATTRIBUTE_ARCHIVE) {
1141
if (!SetFileAttributes(Ansi_Fname, G.pInfo->file_attr & 0x7F))
1142
Info(slide, 1, ((char *)slide,
1143
"\nwarning (%d): could not set file attributes\n",
1144
(int)GetLastError()));
1148
/* set extra fields, both stored-in-zipfile and .LONGNAME flavors */
1149
if (G.extra_field) { /* zipfile extra field may have extended attribs */
1150
int err = EvalExtraFields(__G__ G.filename, G.extra_field,
1151
G.lrec.extra_field_length);
1153
if (err == IZ_EF_TRUNC) {
1155
Info(slide, 1, ((char *)slide, "%-22s ",
1156
FnFilter1(G.filename)));
1157
Info(slide, 1, ((char *)slide, LoadFarString(TruncNTSD),
1158
makeword(G.extra_field+2)-10, uO.qflag? "\n":""));
1161
#endif /* NTSD_EAS */
1163
if ( hFile == INVALID_HANDLE_VALUE )
1164
Info(slide, 1, ((char *)slide,
1165
"\nCreateFile error %d when trying set file time\n",
1166
(int)GetLastError()));
1169
FILETIME *pModft = (gotTime & EB_UT_FL_MTIME) ? &Modft : NULL;
1170
FILETIME *pAccft = (gotTime & EB_UT_FL_ATIME) ? &Accft : NULL;
1171
FILETIME *pCreft = (gotTime & EB_UT_FL_CTIME) ? &Creft : NULL;
1173
if (!SetFileTime(hFile, pCreft, pAccft, pModft))
1174
Info(slide, 0, ((char *)slide, "\nSetFileTime failed: %d\n",
1175
(int)GetLastError()));
1184
} /* end function close_outfile() */
1191
/*************************/
1192
/* Function stamp_file() */
1193
/*************************/
1195
int stamp_file(__GPRO__ ZCONST char *fname, time_t modtime)
1197
FILETIME Modft; /* File time type defined in NT, `last modified' time */
1198
HANDLE hFile; /* File handle defined in NT */
1199
int errstat = 0; /* return status: 0 == "OK", -1 == "Failure" */
1200
#ifndef NT_TZBUG_WORKAROUND
1201
time_t utime_dosmin; /* internal variable for UTIME_BOUNDCHECK_1 */
1203
int fs_uses_loctime = FStampIsLocTime(__G__ fname);
1204
#ifdef __RSXNT__ /* RSXNT/EMX C rtl uses OEM charset */
1205
char *ansi_name = (char *)alloca(strlen(fname) + 1);
1207
INTERN_TO_ISO(fname, ansi_name);
1208
# define Ansi_Fname ansi_name
1210
# define Ansi_Fname fname
1213
/* open a handle to the file to prepare setting the mod-time stamp */
1214
hFile = CreateFile(Ansi_Fname, GENERIC_WRITE, FILE_SHARE_WRITE, NULL,
1215
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
1216
if ( hFile == INVALID_HANDLE_VALUE ) {
1219
/* convert time_t modtime into WIN32 native 64bit format */
1220
UTIME_BOUNDCHECK_1(modtime)
1221
utime2FileTime(modtime, &Modft);
1222
NT_TZBUG_PRECOMPENSATE(modtime, &Modft)
1223
/* set Access and Modification times of the file to modtime */
1224
if (!SetFileTime(hFile, NULL, &Modft, &Modft)) {
1233
} /* end function stamp_file() */
1235
#endif /* TIMESTAMP */
1240
/***********************/
1241
/* Function isfloppy() */ /* more precisely, is it removable? */
1242
/***********************/
1244
static int isfloppy(int nDrive) /* 1 == A:, 2 == B:, etc. */
1246
char rootPathName[4];
1248
rootPathName[0] = (char)('A' + nDrive - 1); /* build the root path */
1249
rootPathName[1] = ':'; /* name, e.g. "A:/" */
1250
rootPathName[2] = '/';
1251
rootPathName[3] = '\0';
1253
return (GetDriveType(rootPathName) == DRIVE_REMOVABLE);
1255
} /* end function isfloppy() */
1260
/*****************************/
1261
/* Function NTQueryVolInfo() */
1262
/*****************************/
1265
* Note: 8.3 limits on filenames apply only to old-style FAT filesystems.
1266
* More recent versions of Windows (Windows NT 3.5 / Windows 4.0)
1267
* can support long filenames (LFN) on FAT filesystems. Check the
1268
* filesystem maximum component length field to detect LFN support.
1271
static int NTQueryVolInfo(__GPRO__ const char *name)
1273
/* static char lastRootPath[4] = ""; */
1274
/* static int lastVolOldFAT; */
1275
/* static int lastVolLocTim; */
1277
char tmp1[MAX_PATH], tmp2[MAX_PATH];
1278
unsigned volSerNo, maxCompLen, fileSysFlags;
1279
#ifdef __RSXNT__ /* RSXNT/EMX C rtl uses OEM charset */
1280
char *ansi_name = (char *)alloca(strlen(name) + 1);
1282
INTERN_TO_ISO(name, ansi_name);
1286
if ((!strncmp(name, "//", 2) || !strncmp(name,"\\\\", 2)) &&
1287
(name[2] != '\0' && name[2] != '/' && name[2] != '\\')) {
1288
/* GetFullPathname() and GetVolumeInformation() do not work
1289
* on UNC names. For now, we return "error".
1290
* **FIXME**: check if UNC name is mapped to a drive letter
1291
* and use mapped drive for volume info query.
1295
if (isalpha((uch)name[0]) && (name[1] == ':'))
1296
tmp0 = (char *)name;
1299
if (!GetFullPathName(name, MAX_PATH, tmp1, &tmp0))
1303
if (strncmp(G.lastRootPath, tmp0, 2) != 0) {
1304
/* For speed, we skip repeated queries for the same device */
1305
strncpy(G.lastRootPath, tmp0, 2); /* Build the root path name, */
1306
G.lastRootPath[2] = '/'; /* e.g. "A:/" */
1307
G.lastRootPath[3] = '\0';
1309
if (!GetVolumeInformation((LPCTSTR)G.lastRootPath,
1310
(LPTSTR)tmp1, (DWORD)MAX_PATH,
1311
(LPDWORD)&volSerNo, (LPDWORD)&maxCompLen,
1312
(LPDWORD)&fileSysFlags, (LPTSTR)tmp2, (DWORD)MAX_PATH)) {
1313
G.lastRootPath[0] = '\0';
1317
/* LFNs are available if the component length is > 12 */
1318
G.lastVolOldFAT = (maxCompLen <= 12);
1319
/* G.lastVolOldFAT = !strncmp(strupr(tmp2), "FAT", 3); old version */
1321
/* Volumes in (V)FAT and (OS/2) HPFS format store file timestamps in
1324
G.lastVolLocTim = !strncmp(strupr(tmp2), "VFAT", 4) ||
1325
!strncmp(tmp2, "HPFS", 4) ||
1326
!strncmp(tmp2, "FAT", 3);
1331
} /* end function NTQueryVolInfo() */
1336
/*****************************/
1337
/* Function IsVolumeOldFAT() */
1338
/*****************************/
1340
static int IsVolumeOldFAT(__GPRO__ const char *name)
1342
return (NTQueryVolInfo(__G__ name) ? G.lastVolOldFAT : FALSE);
1350
/************************/
1351
/* Function do_wild() */ /* identical to OS/2 version */
1352
/************************/
1354
char *do_wild(__G__ wildspec)
1356
ZCONST char *wildspec; /* only used first time on a given dir */
1358
/* these statics are now declared in SYSTEM_SPECIFIC_GLOBALS in w32cfg.h:
1359
static zDIR *wild_dir = NULL;
1360
static ZCONST char *wildname;
1361
static char *dirname, matchname[FILNAMSIZ];
1362
static int notfirstcall=FALSE, have_dirname, dirnamelen;
1365
struct zdirent *file;
1367
/* Even when we're just returning wildspec, we *always* do so in
1368
* matchname[]--calling routine is allowed to append four characters
1369
* to the returned string, and wildspec may be a pointer to argv[].
1371
if (!G.notfirstcall) { /* first call: must initialize everything */
1372
G.notfirstcall = TRUE;
1374
if (!iswild(wildspec)) {
1375
strcpy(G.matchname, wildspec);
1376
G.have_dirname = FALSE;
1381
/* break the wildspec into a directory part and a wildcard filename */
1382
if ((G.wildname = MBSRCHR(wildspec, '/')) == (ZCONST char *)NULL &&
1383
(G.wildname = MBSRCHR(wildspec, ':')) == (ZCONST char *)NULL) {
1386
G.have_dirname = FALSE;
1387
G.wildname = wildspec;
1389
++G.wildname; /* point at character after '/' or ':' */
1390
G.dirnamelen = G.wildname - wildspec;
1391
if ((G.dirname = (char *)malloc(G.dirnamelen+1)) == NULL) {
1392
Info(slide, 1, ((char *)slide,
1393
"warning: cannot allocate wildcard buffers\n"));
1394
strcpy(G.matchname, wildspec);
1395
return G.matchname; /* but maybe filespec was not a wildcard */
1397
strncpy(G.dirname, wildspec, G.dirnamelen);
1398
G.dirname[G.dirnamelen] = '\0'; /* terminate for strcpy below */
1399
G.have_dirname = TRUE;
1401
Trace((stderr, "do_wild: dirname = [%s]\n", FnFilter1(G.dirname)));
1403
if ((G.wild_dir = (zvoid *)Opendir(G.dirname)) != NULL) {
1404
if (G.have_dirname) {
1405
strcpy(G.matchname, G.dirname);
1406
fnamestart = G.matchname + G.dirnamelen;
1408
fnamestart = G.matchname;
1409
while ((file = Readdir((zDIR *)G.wild_dir)) != NULL) {
1410
Trace((stderr, "do_wild: Readdir returns %s\n",
1411
FnFilter1(file->d_name)));
1412
strcpy(fnamestart, file->d_name);
1413
if (MBSRCHR(fnamestart, '.') == (char *)NULL)
1414
strcat(fnamestart, ".");
1415
if (match(fnamestart, G.wildname, 1) && /* 1 == ignore case */
1416
/* skip "." and ".." directory entries */
1417
strcmp(fnamestart, ".") && strcmp(fnamestart, "..")) {
1418
Trace((stderr, "do_wild: match() succeeds\n"));
1419
/* remove trailing dot */
1420
fnamestart = plastchar(fnamestart, strlen(fnamestart));
1421
if (*fnamestart == '.')
1426
/* if we get to here directory is exhausted, so close it */
1427
Closedir((zDIR *)G.wild_dir);
1430
Trace((stderr, "do_wild: Opendir(%s) returns NULL\n",
1431
FnFilter1(G.dirname)));
1433
/* return the raw wildspec in case that works (e.g., directory not
1434
* searchable, but filespec was not wild and file is readable) */
1435
strcpy(G.matchname, wildspec);
1439
/* last time through, might have failed opendir but returned raw wildspec */
1440
if (G.wild_dir == NULL) {
1441
G.notfirstcall = FALSE; /* reset for new wildspec */
1444
return (char *)NULL;
1447
/* If we've gotten this far, we've read and matched at least one entry
1448
* successfully (in a previous call), so dirname has been copied into
1449
* matchname already.
1451
if (G.have_dirname) {
1452
/* strcpy(G.matchname, G.dirname); */
1453
fnamestart = G.matchname + G.dirnamelen;
1455
fnamestart = G.matchname;
1456
while ((file = Readdir((zDIR *)G.wild_dir)) != NULL) {
1457
Trace((stderr, "do_wild: readdir returns %s\n",
1458
FnFilter1(file->d_name)));
1459
strcpy(fnamestart, file->d_name);
1460
if (MBSRCHR(fnamestart, '.') == (char *)NULL)
1461
strcat(fnamestart, ".");
1462
if (match(fnamestart, G.wildname, 1)) { /* 1 == ignore case */
1463
Trace((stderr, "do_wild: match() succeeds\n"));
1464
/* remove trailing dot */
1465
fnamestart = plastchar(fnamestart, strlen(fnamestart));
1466
if (*fnamestart == '.')
1472
Closedir((zDIR *)G.wild_dir); /* at least one entry read; nothing left */
1474
G.notfirstcall = FALSE; /* reset for new wildspec */
1477
return (char *)NULL;
1479
} /* end function do_wild() */
1485
/**********************/
1486
/* Function mapattr() */
1487
/**********************/
1489
/* Identical to MS-DOS, OS/2 versions. However, NT has a lot of extra
1490
* permission stuff, so this function should probably be extended in the
1496
/* set archive bit for file entries (file is not backed up): */
1497
G.pInfo->file_attr = ((unsigned)G.crec.external_file_attributes |
1498
(G.crec.external_file_attributes & FILE_ATTRIBUTE_DIRECTORY ?
1499
0 : FILE_ATTRIBUTE_ARCHIVE)) & 0xff;
1502
} /* end function mapattr() */
1507
/************************/
1508
/* Function mapname() */
1509
/************************/
1511
int mapname(__G__ renamed)
1516
* MPN_OK - no problem detected
1517
* MPN_INF_TRUNC - caution (truncated filename)
1518
* MPN_INF_SKIP - info "skip entry" (dir doesn't exist)
1519
* MPN_ERR_SKIP - error -> skip entry
1520
* MPN_ERR_TOOLONG - error -> path is too long
1521
* MPN_NOMEM - error (memory allocation failed) -> skip entry
1522
* [also MPN_VOL_LABEL, MPN_CREATED_DIR]
1525
char pathcomp[FILNAMSIZ]; /* path-component buffer */
1526
char *pp, *cp=NULL; /* character pointers */
1527
char *lastsemi = NULL; /* pointer to last semi-colon in pathcomp */
1528
#ifdef ACORN_FTYPE_NFS
1529
char *lastcomma=(char *)NULL; /* pointer to last comma in pathcomp */
1530
RO_extra_block *ef_spark; /* pointer Acorn FTYPE ef block */
1532
int killed_ddot = FALSE; /* is set when skipping "../" pathcomp */
1534
register unsigned workch; /* hold the character being tested */
1537
/*---------------------------------------------------------------------------
1538
Initialize various pointers and counters and stuff.
1539
---------------------------------------------------------------------------*/
1541
/* can create path as long as not just freshening, or if user told us */
1542
G.create_dirs = (!uO.fflag || renamed);
1544
G.created_dir = FALSE; /* not yet */
1545
G.renamed_fullpath = FALSE;
1546
G.fnlen = strlen(G.filename);
1549
cp = G.filename; /* point to beginning of renamed name... */
1551
if (*cp == '\\') /* convert backslashes to forward */
1553
} while (*PREINCSTR(cp));
1555
/* use temporary rootpath if user gave full pathname */
1556
if (G.filename[0] == '/') {
1557
G.renamed_fullpath = TRUE;
1558
pathcomp[0] = '/'; /* copy the '/' and terminate */
1561
} else if (isalpha((uch)G.filename[0]) && G.filename[1] == ':') {
1562
G.renamed_fullpath = TRUE;
1564
*pp++ = *cp++; /* copy the "d:" (+ '/', possibly) */
1567
*pp++ = *cp++; /* otherwise add "./"? */
1572
/* pathcomp is ignored unless renamed_fullpath is TRUE: */
1573
if ((error = checkdir(__G__ pathcomp, INIT)) != 0) /* init path buffer */
1574
return error; /* ...unless no mem or vol label on hard disk */
1576
*pathcomp = '\0'; /* initialize translation buffer */
1577
pp = pathcomp; /* point to translation buffer */
1578
if (!renamed) { /* cp already set if renamed */
1579
if (uO.jflag) /* junking directories */
1580
cp = (char *)MBSRCHR(G.filename, '/');
1581
if (cp == NULL) /* no '/' or not junking dirs */
1582
cp = G.filename; /* point to internal zipfile-member pathname */
1584
++cp; /* point to start of last component of path */
1587
/*---------------------------------------------------------------------------
1588
Begin main loop through characters in filename.
1589
---------------------------------------------------------------------------*/
1591
for (; (workch = (uch)*cp) != 0; INCSTR(cp)) {
1594
case '/': /* can assume -j flag not given */
1596
maskDOSdevice(__G__ pathcomp);
1597
if (((error = checkdir(__G__ pathcomp, APPEND_DIR)) & MPN_MASK)
1600
pp = pathcomp; /* reset conversion buffer for next piece */
1601
lastsemi = NULL; /* leave directory semi-colons alone */
1605
if (pp == pathcomp) { /* nothing appended yet... */
1606
if (cp[1] == '/') { /* don't bother appending "./" to */
1607
++cp; /* the path: skip behind the '/' */
1609
} else if (!uO.ddotflag && cp[1] == '.' && cp[2] == '/') {
1610
/* "../" dir traversal detected */
1611
cp += 2; /* skip over behind the '/' */
1612
killed_ddot = TRUE; /* set "show message" flag */
1619
case ':': /* drive names not stored in zipfile, */
1620
case '<': /* so no colons allowed */
1621
case '>': /* no redirection symbols allowed either */
1622
case '|': /* no pipe signs allowed */
1623
case '"': /* no double quotes allowed */
1624
case '?': /* no wildcards allowed */
1626
*pp++ = '_'; /* these rules apply equally to FAT and NTFS */
1628
case ';': /* start of VMS version? */
1629
lastsemi = pp; /* remove VMS version later... */
1630
*pp++ = ';'; /* but keep semicolon for now */
1633
#ifdef ACORN_FTYPE_NFS
1634
case ',': /* NFS filetype extension */
1636
*pp++ = ','; /* keep for now; may need to remove */
1637
break; /* later, if requested */
1640
case ' ': /* keep spaces unless specifically */
1641
/* NT cannot create filenames with spaces on FAT volumes */
1642
if (uO.sflag || IsVolumeOldFAT(__G__ G.filename))
1649
/* allow European characters in filenames: */
1650
if (isprint(workch) || workch >= 127)
1653
memcpy(pp, cp, CLEN(cp));
1657
*pp++ = (char)workch;
1660
} /* end while loop */
1662
/* Show warning when stripping insecure "parent dir" path components */
1663
if (killed_ddot && QCOND2) {
1664
Info(slide, 0, ((char *)slide,
1665
"warning: skipped \"../\" path component(s) in %s\n",
1666
FnFilter1(G.filename)));
1667
if (!(error & ~MPN_MASK))
1668
error = (error & MPN_MASK) | PK_WARN;
1671
/*---------------------------------------------------------------------------
1672
Report if directory was created (and no file to create: filename ended
1673
in '/'), check name to be sure it exists, and combine path and name be-
1675
---------------------------------------------------------------------------*/
1677
if (lastchar(G.filename, G.fnlen) == '/') {
1678
checkdir(__G__ G.filename, GETPATH);
1679
if (G.created_dir) {
1680
#ifdef __RSXNT__ /* RSXNT/EMX C rtl uses OEM charset */
1681
char *ansi_name = (char *)alloca(strlen(G.filename) + 1);
1683
INTERN_TO_ISO(G.filename, ansi_name);
1684
# define Ansi_Fname ansi_name
1686
# define Ansi_Fname G.filename
1689
Info(slide, 0, ((char *)slide, " creating: %-22s\n",
1690
FnFilter1(G.filename)));
1693
/* set file attributes:
1694
The default for newly created directories is "DIR attribute
1695
flags set", so there is no need to change attributes unless
1696
one of the DOS style attribute flags is set. The readonly
1697
attribute need not be masked, since it does not prevent
1698
modifications in the new directory. */
1699
if(G.pInfo->file_attr & (0x7F & ~FILE_ATTRIBUTE_DIRECTORY)) {
1700
if (!SetFileAttributes(Ansi_Fname, G.pInfo->file_attr & 0x7F))
1701
Info(slide, 1, ((char *)slide,
1702
"\nwarning (%d): could not set file attributes for %s\n",
1703
(int)GetLastError(), FnFilter1(G.filename)));
1707
/* set extra fields, both stored-in-zipfile and .LONGNAME flavors */
1708
if (G.extra_field) { /* zipfile e.f. may have extended attribs */
1709
int err = EvalExtraFields(__G__ G.filename, G.extra_field,
1710
G.lrec.extra_field_length);
1712
if (err == IZ_EF_TRUNC) {
1714
Info(slide, 1, ((char *)slide, "%-22s ",
1715
FnFilter1(G.filename)));
1716
Info(slide, 1, ((char *)slide, LoadFarString(TruncNTSD),
1717
makeword(G.extra_field+2)-10, uO.qflag? "\n":""));
1720
#endif /* NTSD_EAS */
1721
/* set dir time (note trailing '/') */
1722
return (error & ~MPN_MASK) | MPN_CREATED_DIR;
1724
/* dir existed already; don't look for data to extract */
1725
return (error & ~MPN_MASK) | MPN_INF_SKIP;
1728
*pp = '\0'; /* done with pathcomp: terminate it */
1730
/* if not saving them, remove VMS version numbers (appended "###") */
1731
if (!uO.V_flag && lastsemi) {
1732
pp = lastsemi + 1; /* semi-colon was kept: expect #'s after */
1733
while (isdigit((uch)(*pp)))
1735
if (*pp == '\0') /* only digits between ';' and end: nuke */
1739
#ifdef ACORN_FTYPE_NFS
1740
/* translate Acorn filetype information if asked to do so */
1741
if (uO.acorn_nfs_ext &&
1742
(ef_spark = (RO_extra_block *)
1743
getRISCOSexfield(G.extra_field, G.lrec.extra_field_length))
1744
!= (RO_extra_block *)NULL)
1746
/* file *must* have a RISC OS extra field */
1747
long ft = (long)makelong((ef_spark->loadaddr);
1751
while (isxdigit((uch)(*pp))) ++pp;
1752
if (pp == lastcomma+4 && *pp == '\0') *lastcomma='\0'; /* nuke */
1754
if ((ft & 1<<31)==0) ft=0x000FFD00;
1755
sprintf(pathcomp+strlen(pathcomp), ",%03x", (int)(ft>>8) & 0xFFF);
1757
#endif /* ACORN_FTYPE_NFS */
1759
maskDOSdevice(__G__ pathcomp);
1761
if (*pathcomp == '\0') {
1762
Info(slide, 1, ((char *)slide, "mapname: conversion of %s failed\n",
1763
FnFilter1(G.filename)));
1764
return (error & ~MPN_MASK) | MPN_ERR_SKIP;
1767
checkdir(__G__ pathcomp, APPEND_NAME); /* returns 1 if truncated: care? */
1768
checkdir(__G__ G.filename, GETPATH);
1770
if (G.pInfo->vollabel) { /* set the volume label now */
1772
#ifdef __RSXNT__ /* RSXNT/EMX C rtl uses OEM charset */
1773
char *ansi_name = (char *)alloca(strlen(G.filename) + 1);
1774
INTERN_TO_ISO(G.filename, ansi_name);
1775
# define Ansi_Fname ansi_name
1777
# define Ansi_Fname G.filename
1780
/* Build a drive string, e.g. "b:" */
1781
drive[0] = (char)('a' + G.nLabelDrive - 1);
1782
strcpy(drive + 1, ":\\");
1784
Info(slide, 0, ((char *)slide, "labelling %s %-22s\n", drive,
1785
FnFilter1(G.filename)));
1786
if (!SetVolumeLabel(drive, Ansi_Fname)) {
1787
Info(slide, 1, ((char *)slide,
1788
"mapname: error setting volume label\n"));
1789
return (error & ~MPN_MASK) | MPN_ERR_SKIP;
1791
/* success: skip the "extraction" quietly */
1792
return (error & ~MPN_MASK) | MPN_INF_SKIP;
1796
Trace((stderr, "mapname returns with filename = [%s] (error = %d)\n\n",
1797
FnFilter1(G.filename), error));
1800
} /* end function mapname() */
1805
/****************************/
1806
/* Function maskDOSdevice() */
1807
/****************************/
1809
static void maskDOSdevice(__G__ pathcomp)
1813
/*---------------------------------------------------------------------------
1814
Put an underscore in front of the file name if the file name is a
1815
DOS/WINDOWS device name like CON.*, AUX.*, PRN.*, etc. Trying to
1816
extract such a file would fail at best and wedge us at worst.
1817
---------------------------------------------------------------------------*/
1818
#if !defined(S_IFCHR) && defined(_S_IFCHR)
1819
# define S_IFCHR _S_IFCHR
1821
#if !defined(S_ISCHR)
1822
# if defined(_S_ISCHR)
1823
# define S_ISCHR(m) _S_ISCHR(m)
1824
# elif defined(S_IFCHR)
1825
# define S_ISCHR(m) ((m) & S_IFCHR)
1830
if (stat(pathcomp, &G.statbuf) == 0) {
1832
"maskDOSdevice() stat(\"%s\", buf) st_mode result: %X, %o\n",
1833
FnFilter1(pathcomp), G.statbuf.st_mode, G.statbuf.st_mode));
1835
Trace((stderr, "maskDOSdevice() stat(\"%s\", buf) failed\n",
1836
FnFilter1(pathcomp)));
1839
if (stat(pathcomp, &G.statbuf) == 0 && S_ISCHR(G.statbuf.st_mode)) {
1842
/* pathcomp contains a name of a DOS character device (builtin or
1843
* installed device driver).
1844
* Prepend a '_' to allow creation of the item in the file system.
1846
for (i = strlen(pathcomp) + 1; i > 0; --i)
1847
pathcomp[i] = pathcomp[i - 1];
1850
} /* end function maskDOSdevice() */
1856
/**********************/
1857
/* Function map2fat() */ /* Not quite identical to OS/2 version */
1858
/**********************/
1860
static void map2fat(pathcomp, pEndFAT)
1861
char *pathcomp, **pEndFAT;
1863
char *ppc = pathcomp; /* variable pointer to pathcomp */
1864
char *pEnd = *pEndFAT; /* variable pointer to buildpathFAT */
1865
char *pBegin = *pEndFAT; /* constant pointer to start of this comp. */
1866
char *last_dot = NULL; /* last dot not converted to underscore */
1867
register unsigned workch; /* hold the character being tested */
1870
/* Only need check those characters which are legal in NTFS but not
1871
* in FAT: to get here, must already have passed through mapname.
1872
* Also must truncate path component to ensure 8.3 compliance.
1874
while ((workch = (uch)*ppc++) != 0) {
1882
*pEnd++ = '_'; /* convert brackets to underscores */
1886
if (pEnd == *pEndFAT) { /* nothing appended yet... */
1887
if (*ppc == '\0') /* don't bother appending a */
1888
break; /* "./" component to the path */
1889
else if (*ppc == '.' && ppc[1] == '\0') { /* "../" */
1890
*pEnd++ = '.'; /* add first dot, */
1891
*pEnd++ = '.'; /* add second dot, and */
1892
++ppc; /* skip over to pathcomp's end */
1893
} else { /* FAT doesn't allow null filename */
1894
*pEnd++ = '_'; /* bodies, so map .exrc -> _exrc */
1895
} /* (_.exr would keep max 3 chars) */
1896
} else { /* found dot within path component */
1897
last_dot = pEnd; /* point at last dot so far... */
1898
*pEnd++ = '_'; /* convert to underscore for now */
1903
*pEnd++ = (char)workch;
1906
} /* end while loop */
1908
*pEnd = '\0'; /* terminate buildpathFAT */
1910
/* NOTE: keep in mind that pEnd points to the end of the path
1911
* component, and *pEndFAT still points to the *beginning* of it...
1912
* Also note that the algorithm does not try to get too fancy:
1913
* if there are no dots already, the name either gets truncated
1914
* at 8 characters or the last underscore is converted to a dot
1915
* (only if more characters are saved that way). In no case is
1916
* a dot inserted between existing characters.
1918
if (last_dot == NULL) { /* no dots: check for underscores... */
1919
char *plu = MBSRCHR(pBegin, '_'); /* pointer to last underscore */
1921
if ((plu != NULL) && /* found underscore: convert to dot? */
1922
(MIN(plu - pBegin, 8) + MIN(pEnd - plu - 1, 3) > 8)) {
1923
last_dot = plu; /* be lazy: drop through to next if-blk */
1924
} else if ((pEnd - *pEndFAT) > 8) {
1925
/* no underscore; or converting underscore to dot would save less
1926
chars than leaving everything in the basename */
1927
*pEndFAT += 8; /* truncate at 8 chars */
1930
*pEndFAT = pEnd; /* whole thing fits into 8 chars or less */
1933
if (last_dot != NULL) { /* one dot is OK: */
1934
*last_dot = '.'; /* put it back in */
1936
if ((last_dot - pBegin) > 8) {
1941
q = last_dot = pBegin + 8;
1942
for (i = 0; (i < 4) && *p; ++i) /* too many chars in basename: */
1943
*q++ = *p++; /* shift .ext left and trun- */
1944
*q = '\0'; /* cate/terminate it */
1946
} else if ((pEnd - last_dot) > 4) { /* too many chars in extension */
1947
*pEndFAT = last_dot + 4;
1950
*pEndFAT = pEnd; /* filename is fine; point at terminating zero */
1952
if ((last_dot - pBegin) > 0 && last_dot[-1] == ' ')
1953
last_dot[-1] = '_'; /* NO blank in front of '.'! */
1955
} /* end function map2fat() */
1960
/***********************/ /* Borrowed from os2.c for UnZip 5.1. */
1961
/* Function checkdir() */ /* Difference: no EA stuff */
1962
/***********************/ /* HPFS stuff works on NTFS too */
1964
int checkdir(__G__ pathcomp, flag)
1970
* MPN_OK - no problem detected
1971
* MPN_INF_TRUNC - (on APPEND_NAME) truncated filename
1972
* MPN_INF_SKIP - path doesn't exist, not allowed to create
1973
* MPN_ERR_SKIP - path doesn't exist, tried to create and failed; or path
1974
* exists and is not a directory, but is supposed to be
1975
* MPN_ERR_TOOLONG - path is too long
1976
* MPN_NOMEM - can't allocate memory for filename buffers
1979
/* static int rootlen = 0; */ /* length of rootpath */
1980
/* static char *rootpath; */ /* user's "extract-to" directory */
1981
/* static char *buildpathHPFS; */ /* full path (so far) to extracted file, */
1982
/* static char *buildpathFAT; */ /* both HPFS/EA (main) and FAT versions */
1983
/* static char *endHPFS; */ /* corresponding pointers to end of */
1984
/* static char *endFAT; */ /* buildpath ('\0') */
1987
# define FUNCTION (flag & FN_MASK)
1991
/*---------------------------------------------------------------------------
1992
APPEND_DIR: append the path component to the path being built and check
1993
for its existence. If doesn't exist and we are creating directories, do
1994
so for this one; else signal success or error as appropriate.
1995
---------------------------------------------------------------------------*/
1997
if (FUNCTION == APPEND_DIR) {
1999
int too_long = FALSE;
2001
Trace((stderr, "appending dir segment [%s]\n", FnFilter1(pathcomp)));
2002
while ((*G.endHPFS = *p++) != '\0') /* copy to HPFS filename */
2004
if (!IsVolumeOldFAT(__G__ G.buildpathHPFS)) {
2006
while ((*G.endFAT = *p++) != '\0') /* copy to FAT filename, too */
2009
map2fat(pathcomp, &G.endFAT); /* map into FAT fn, update endFAT */
2011
/* GRR: could do better check, see if overrunning buffer as we go:
2012
* check endHPFS-buildpathHPFS after each append, set warning variable
2013
* if within 20 of FILNAMSIZ; then if var set, do careful check when
2014
* appending. Clear variable when begin new path. */
2016
/* next check: need to append '/', at least one-char name, '\0' */
2017
if ((G.endHPFS-G.buildpathHPFS) > FILNAMSIZ-3)
2018
too_long = TRUE; /* check if extracting dir? */
2020
/* Borland C++ 5.0 does not handle a call to stat() well if the
2021
* directory does not exist (it tends to crash in strange places.)
2022
* This is apparently a problem only when compiling for GUI rather
2023
* than console. The code below attempts to work around this problem.
2025
if (access(G.buildpathFAT, 0) != 0) {
2026
if (!G.create_dirs) { /* told not to create (freshening) */
2027
free(G.buildpathHPFS);
2028
free(G.buildpathFAT);
2029
/* path doesn't exist: nothing to do */
2030
return MPN_INF_SKIP;
2032
if (too_long) { /* GRR: should allow FAT extraction w/o EAs */
2033
Info(slide, 1, ((char *)slide,
2034
"checkdir error: path too long: %s\n",
2035
FnFilter1(G.buildpathHPFS)));
2036
free(G.buildpathHPFS);
2037
free(G.buildpathFAT);
2038
/* no room for filenames: fatal */
2039
return MPN_ERR_TOOLONG;
2041
if (MKDIR(G.buildpathFAT, 0777) == -1) { /* create the directory */
2042
Info(slide, 1, ((char *)slide,
2043
"checkdir error: cannot create %s\n\
2044
unable to process %s.\n",
2045
FnFilter2(G.buildpathFAT), FnFilter1(G.filename)));
2046
free(G.buildpathHPFS);
2047
free(G.buildpathFAT);
2048
/* path didn't exist, tried to create, failed */
2049
return MPN_ERR_SKIP;
2051
G.created_dir = TRUE;
2053
#endif /* FIX_STAT_BUG */
2054
if (SSTAT(G.buildpathFAT, &G.statbuf)) /* path doesn't exist */
2056
if (!G.create_dirs) { /* told not to create (freshening) */
2057
free(G.buildpathHPFS);
2058
free(G.buildpathFAT);
2059
/* path doesn't exist: nothing to do */
2060
return MPN_INF_SKIP;
2062
if (too_long) { /* GRR: should allow FAT extraction w/o EAs */
2063
Info(slide, 1, ((char *)slide,
2064
"checkdir error: path too long: %s\n",
2065
FnFilter1(G.buildpathHPFS)));
2066
free(G.buildpathHPFS);
2067
free(G.buildpathFAT);
2068
/* no room for filenames: fatal */
2069
return MPN_ERR_TOOLONG;
2071
if (MKDIR(G.buildpathFAT, 0777) == -1) { /* create the directory */
2072
Info(slide, 1, ((char *)slide,
2073
"checkdir error: cannot create %s\n\
2074
unable to process %s.\n",
2075
FnFilter2(G.buildpathFAT), FnFilter1(G.filename)));
2076
free(G.buildpathHPFS);
2077
free(G.buildpathFAT);
2078
/* path didn't exist, tried to create, failed */
2079
return MPN_ERR_SKIP;
2081
G.created_dir = TRUE;
2082
} else if (!S_ISDIR(G.statbuf.st_mode)) {
2083
Info(slide, 1, ((char *)slide,
2084
"checkdir error: %s exists but is not directory\n \
2085
unable to process %s.\n",
2086
FnFilter2(G.buildpathFAT), FnFilter1(G.filename)));
2087
free(G.buildpathHPFS);
2088
free(G.buildpathFAT);
2089
/* path existed but wasn't dir */
2090
return MPN_ERR_SKIP;
2093
Info(slide, 1, ((char *)slide,
2094
"checkdir error: path too long: %s\n",
2095
FnFilter1(G.buildpathHPFS)));
2096
free(G.buildpathHPFS);
2097
free(G.buildpathFAT);
2098
/* no room for filenames: fatal */
2099
return MPN_ERR_TOOLONG;
2103
*G.endHPFS = *G.endFAT = '\0';
2104
Trace((stderr, "buildpathHPFS now = [%s]\nbuildpathFAT now = [%s]\n",
2105
FnFilter1(G.buildpathHPFS), FnFilter2(G.buildpathFAT)));
2108
} /* end if (FUNCTION == APPEND_DIR) */
2110
/*---------------------------------------------------------------------------
2111
GETPATH: copy full FAT path to the string pointed at by pathcomp (want
2112
filename to reflect name used on disk, not EAs; if full path is HPFS,
2113
buildpathFAT and buildpathHPFS will be identical). Also free both paths.
2114
---------------------------------------------------------------------------*/
2116
if (FUNCTION == GETPATH) {
2117
Trace((stderr, "getting and freeing FAT path [%s]\n",
2118
FnFilter1(G.buildpathFAT)));
2119
Trace((stderr, "freeing HPFS path [%s]\n",
2120
FnFilter1(G.buildpathHPFS)));
2121
strcpy(pathcomp, G.buildpathFAT);
2122
free(G.buildpathFAT);
2123
free(G.buildpathHPFS);
2124
G.buildpathHPFS = G.buildpathFAT = G.endHPFS = G.endFAT = NULL;
2128
/*---------------------------------------------------------------------------
2129
APPEND_NAME: assume the path component is the filename; append it and
2130
return without checking for existence.
2131
---------------------------------------------------------------------------*/
2133
if (FUNCTION == APPEND_NAME) {
2137
Trace((stderr, "appending filename [%s]\n", FnFilter1(pathcomp)));
2138
while ((*G.endHPFS = *p++) != '\0') { /* copy to HPFS filename */
2140
if ((G.endHPFS-G.buildpathHPFS) >= FILNAMSIZ) {
2141
*--G.endHPFS = '\0';
2142
Info(slide, 1, ((char *)slide,
2143
"checkdir warning: path too long; truncating\n \
2145
FnFilter1(G.filename), FnFilter2(G.buildpathHPFS)));
2146
error = MPN_INF_TRUNC; /* filename truncated */
2150
if ( G.pInfo->vollabel || !IsVolumeOldFAT(__G__ G.buildpathHPFS)) {
2152
while ((*G.endFAT = *p++) != '\0') /* copy to FAT filename, too */
2155
map2fat(pathcomp, &G.endFAT); /* map into FAT fn, update endFAT */
2156
Trace((stderr, "buildpathHPFS: %s\nbuildpathFAT: %s\n",
2157
FnFilter1(G.buildpathHPFS), FnFilter2(G.buildpathFAT)));
2159
return error; /* could check for existence, prompt for new name... */
2161
} /* end if (FUNCTION == APPEND_NAME) */
2163
/*---------------------------------------------------------------------------
2164
INIT: allocate and initialize buffer space for the file currently being
2165
extracted. If file was renamed with an absolute path, don't prepend the
2167
---------------------------------------------------------------------------*/
2169
if (FUNCTION == INIT) {
2170
Trace((stderr, "initializing buildpathHPFS and buildpathFAT to "));
2171
#ifdef ACORN_FTYPE_NFS
2172
if ((G.buildpathHPFS = (char *)malloc(G.fnlen+G.rootlen+
2173
(uO.acorn_nfs_ext ? 5 : 1)))
2175
if ((G.buildpathHPFS = (char *)malloc(G.fnlen+G.rootlen+1))
2179
#ifdef ACORN_FTYPE_NFS
2180
if ((G.buildpathFAT = (char *)malloc(G.fnlen+G.rootlen+
2181
(uO.acorn_nfs_ext ? 5 : 1)))
2183
if ((G.buildpathFAT = (char *)malloc(G.fnlen+G.rootlen+1))
2186
free(G.buildpathHPFS);
2189
if (G.pInfo->vollabel) { /* use root or renamed path, but don't store */
2190
/* GRR: for network drives, do strchr() and return IZ_VOL_LABEL if not [1] */
2191
if (G.renamed_fullpath && pathcomp[1] == ':')
2192
*G.buildpathHPFS = (char)ToLower(*pathcomp);
2193
else if (!G.renamed_fullpath && G.rootlen > 1 &&
2194
G.rootpath[1] == ':')
2195
*G.buildpathHPFS = (char)ToLower(*G.rootpath);
2197
char tmpN[MAX_PATH], *tmpP;
2198
if (GetFullPathName(".", MAX_PATH, tmpN, &tmpP) > MAX_PATH)
2199
{ /* by definition of MAX_PATH we should never get here */
2200
Info(slide, 1, ((char *)slide,
2201
"checkdir warning: current dir path too long\n"));
2202
return MPN_INF_TRUNC; /* can't get drive letter */
2204
G.nLabelDrive = *tmpN - 'a' + 1;
2205
*G.buildpathHPFS = (char)(G.nLabelDrive - 1 + 'a');
2207
G.nLabelDrive = *G.buildpathHPFS - 'a' + 1; /* save for mapname() */
2208
if (uO.volflag == 0 || *G.buildpathHPFS < 'a' /* no labels/bogus? */
2209
|| (uO.volflag == 1 && !isfloppy(G.nLabelDrive))) { /* !fixed */
2210
free(G.buildpathHPFS);
2211
free(G.buildpathFAT);
2212
return MPN_VOL_LABEL; /* skipping with message */
2214
*G.buildpathHPFS = '\0';
2215
} else if (G.renamed_fullpath) /* pathcomp = valid data */
2216
strcpy(G.buildpathHPFS, pathcomp);
2217
else if (G.rootlen > 0)
2218
strcpy(G.buildpathHPFS, G.rootpath);
2220
*G.buildpathHPFS = '\0';
2221
G.endHPFS = G.buildpathHPFS;
2222
G.endFAT = G.buildpathFAT;
2223
while ((*G.endFAT = *G.endHPFS) != '\0') {
2227
Trace((stderr, "[%s]\n", FnFilter1(G.buildpathHPFS)));
2231
/*---------------------------------------------------------------------------
2232
ROOT: if appropriate, store the path in rootpath and create it if neces-
2233
sary; else assume it's a zipfile member and return. This path segment
2234
gets used in extracting all members from every zipfile specified on the
2235
command line. Note that under OS/2 and MS-DOS, if a candidate extract-to
2236
directory specification includes a drive letter (leading "x:"), it is
2237
treated just as if it had a trailing '/'--that is, one directory level
2238
will be created if the path doesn't exist, unless this is otherwise pro-
2239
hibited (e.g., freshening).
2240
---------------------------------------------------------------------------*/
2242
#if (!defined(SFX) || defined(SFX_EXDIR))
2243
if (FUNCTION == ROOT) {
2244
Trace((stderr, "initializing root path to [%s]\n",
2245
FnFilter1(pathcomp)));
2246
if (pathcomp == NULL) {
2250
if (G.rootlen > 0) /* rootpath was already set, nothing to do */
2252
if ((G.rootlen = strlen(pathcomp)) > 0) {
2253
int had_trailing_pathsep=FALSE, has_drive=FALSE, add_dot=FALSE;
2256
if ((tmproot = (char *)malloc(G.rootlen+3)) == (char *)NULL) {
2260
strcpy(tmproot, pathcomp);
2261
if (isalpha((uch)tmproot[0]) && tmproot[1] == ':')
2262
has_drive = TRUE; /* drive designator */
2263
if (tmproot[G.rootlen-1] == '/' || tmproot[G.rootlen-1] == '\\') {
2264
tmproot[--G.rootlen] = '\0';
2265
had_trailing_pathsep = TRUE;
2267
if (has_drive && (G.rootlen == 2)) {
2268
if (!had_trailing_pathsep) /* i.e., original wasn't "x:/" */
2269
add_dot = TRUE; /* relative path: add '.' before '/' */
2270
} else if (G.rootlen > 0) { /* need not check "x:." and "x:/" */
2271
if (SSTAT(tmproot, &G.statbuf) || !S_ISDIR(G.statbuf.st_mode))
2273
/* path does not exist */
2274
if (!G.create_dirs /* || iswild(tmproot) */ ) {
2277
/* treat as stored file */
2278
return MPN_INF_SKIP;
2280
/* create directory (could add loop here scanning tmproot
2281
* to create more than one level, but really necessary?) */
2282
if (MKDIR(tmproot, 0777) == -1) {
2283
Info(slide, 1, ((char *)slide,
2284
"checkdir: cannot create extraction directory: %s\n",
2285
FnFilter1(tmproot)));
2288
/* path didn't exist, tried to create, failed: */
2289
/* file exists, or need 2+ subdir levels */
2290
return MPN_ERR_SKIP;
2294
if (add_dot) /* had just "x:", make "x:." */
2295
tmproot[G.rootlen++] = '.';
2296
tmproot[G.rootlen++] = '/';
2297
tmproot[G.rootlen] = '\0';
2298
if ((G.rootpath = (char *)realloc(tmproot, G.rootlen+1)) == NULL) {
2303
Trace((stderr, "rootpath now = [%s]\n", FnFilter1(G.rootpath)));
2307
#endif /* !SFX || SFX_EXDIR */
2309
/*---------------------------------------------------------------------------
2310
END: free rootpath, immediately prior to program exit.
2311
---------------------------------------------------------------------------*/
2313
if (FUNCTION == END) {
2314
Trace((stderr, "freeing rootpath\n"));
2315
if (G.rootlen > 0) {
2322
return MPN_INVALID; /* should never reach */
2324
} /* end function checkdir() */
2332
/*************************/
2333
/* Function dateformat() */
2334
/*************************/
2338
TCHAR df[2]; /* LOCALE_IDATE has a maximum value of 2 */
2340
if (GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_IDATE, df, 2) != 0) {
2357
/************************/
2358
/* Function version() */
2359
/************************/
2365
#if (defined(_MSC_VER) || defined(__WATCOMC__) || defined(__DJGPP__))
2367
#if (defined(_MSC_VER) && (_MSC_VER > 900))
2372
len = sprintf((char *)slide, CompiledWith,
2374
#if defined(_MSC_VER) /* MSC == VC++, but what about SDK compiler? */
2375
(sprintf(buf, "Microsoft C %d.%02d ", _MSC_VER/100, _MSC_VER%100), buf),
2376
# if (_MSC_VER == 800)
2377
"(Visual C++ v1.1)",
2378
# elif (_MSC_VER == 850)
2379
"(Windows NT v3.5 SDK)",
2380
# elif (_MSC_VER == 900)
2381
"(Visual C++ v2.x)",
2382
# elif (_MSC_VER > 900)
2383
(sprintf(buf2, "(Visual C++ %d.%d)", _MSC_VER/100 - 6, _MSC_VER%100/10),
2388
#elif defined(__WATCOMC__)
2389
# if (__WATCOMC__ % 10 > 0)
2390
(sprintf(buf, "Watcom C/C++ %d.%02d", __WATCOMC__ / 100,
2391
__WATCOMC__ % 100), buf), "",
2393
(sprintf(buf, "Watcom C/C++ %d.%d", __WATCOMC__ / 100,
2394
(__WATCOMC__ % 100) / 10), buf), "",
2396
#elif defined(__BORLANDC__)
2398
# if (__BORLANDC__ < 0x0200)
2400
# elif (__BORLANDC__ == 0x0200)
2402
# elif (__BORLANDC__ == 0x0400)
2404
# elif (__BORLANDC__ == 0x0410) /* __BCPLUSPLUS__ = 0x0310 */
2406
# elif (__BORLANDC__ == 0x0452) /* __BCPLUSPLUS__ = 0x0320 */
2408
# elif (__BORLANDC__ == 0x0460) /* __BCPLUSPLUS__ = 0x0340 */
2410
# elif (__BORLANDC__ == 0x0500) /* __BCPLUSPLUS__ = 0x0340 */
2412
# elif (__BORLANDC__ == 0x0520) /* __BCPLUSPLUS__ = 0x0520 */
2413
" 5.2 (C++ Builder 1.0)",
2414
# elif (__BORLANDC__ == 0x0530) /* __BCPLUSPLUS__ = 0x0530 */
2415
" 5.3 (C++ Builder 3.0)",
2416
# elif (__BORLANDC__ == 0x0540) /* __BCPLUSPLUS__ = 0x0540 */
2417
" 5.4 (C++ Builder 4.0)",
2418
# elif (__BORLANDC__ == 0x0550) /* __TURBOC__ = 0x0550 */
2419
" 5.5 (C++ Builder 5.0)",
2420
# elif (__BORLANDC__ == 0x0551) /* __TURBOC__ = 0x0551 */
2421
" 5.5.1 (C++ Builder 5.0.1)",
2423
" later than 5.5.1",
2425
#elif defined(__LCC__)
2427
#elif defined(__GNUC__)
2428
# if defined(__RSXNT__)
2429
# if (defined(__DJGPP__) && !defined(__EMX__))
2430
(sprintf(buf, "rsxnt(djgpp v%d.%02d) / gcc ",
2431
__DJGPP__, __DJGPP_MINOR__), buf),
2432
# elif defined(__DJGPP__)
2433
(sprintf(buf, "rsxnt(emx+djgpp v%d.%02d) / gcc ",
2434
__DJGPP__, __DJGPP_MINOR__), buf),
2435
# elif (defined(__GO32__) && !defined(__EMX__))
2436
"rsxnt(djgpp v1.x) / gcc ",
2437
# elif defined(__GO32__)
2438
"rsxnt(emx + djgpp v1.x) / gcc ",
2439
# elif defined(__EMX__)
2442
"rsxnt(unknown) / gcc ",
2444
# elif defined(__CYGWIN__)
2445
"cygnus win32 / gcc ",
2446
# elif defined(__MINGW32__)
2452
#else /* !_MSC_VER, !__WATCOMC__, !__BORLANDC__, !__LCC__, !__GNUC__ */
2453
"unknown compiler (SDK?)", "",
2454
#endif /* ?compilers */
2456
"\nWindows 95 / Windows NT", " (32-bit)",
2465
(*G.message)((zvoid *)&G, slide, (ulg)len, 0);
2469
} /* end function version() */
2471
#endif /* !WINDLL */
2478
int screensize(int *tt_rows, int *tt_cols)
2481
CONSOLE_SCREEN_BUFFER_INFO scr;
2483
hstdout = GetStdHandle(STD_OUTPUT_HANDLE);
2484
GetConsoleScreenBufferInfo(hstdout, &scr);
2485
if (tt_rows != NULL) *tt_rows = scr.srWindow.Bottom - scr.srWindow.Top + 1;
2486
if (tt_cols != NULL) *tt_cols = scr.srWindow.Right - scr.srWindow.Left + 1;
2487
return 0; /* signal success */
2494
#ifdef W32_STAT_BANDAID
2496
/* All currently known variants of WIN32 operating systems (Windows 95/98,
2497
* WinNT 3.x, 4.0, 5.0) have a nasty bug in the OS kernel concerning
2498
* conversions between UTC and local time: In the time conversion functions
2499
* of the Win32 API, the timezone offset (including seasonal daylight saving
2500
* shift) between UTC and local time evaluation is erratically based on the
2501
* current system time. The correct evaluation must determine the offset
2502
* value as it {was/is/will be} for the actual time to be converted.
2504
* Some versions of MS C runtime lib's stat() returns utc time-stamps so
2505
* that localtime(timestamp) corresponds to the (potentially false) local
2506
* time shown by the OS' system programs (Explorer, command shell dir, etc.)
2507
* The RSXNT port follows the same strategy, but fails to recognize the
2508
* access-time attribute.
2510
* For the NTFS file system (and other filesystems that store time-stamps
2511
* as UTC values), this results in st_mtime (, st_{c|a}time) fields which
2512
* are not stable but vary according to the seasonal change of "daylight
2513
* saving time in effect / not in effect".
2515
* Other C runtime libs (CygWin, or the CRT DLLs supplied with Win95/NT
2516
* return the unix-time equivalent of the UTC FILETIME values as got back
2517
* from the Win32 API call. This time, return values from NTFS are correct
2518
* whereas utimes from files on (V)FAT volumes vary according to the DST
2521
* To achieve timestamp consistency of UTC (UT extra field) values in
2522
* Zip archives, the Info-ZIP programs require work-around code for
2523
* proper time handling in stat() (and other time handling routines).
2525
/* stat() functions under Windows95 tend to fail for root directories. *
2526
* Watcom and Borland, at least, are affected by this bug. Watcom made *
2527
* a partial fix for 11.0 but still missed some cases. This substitute *
2528
* detects the case and fills in reasonable values. Otherwise we get *
2529
* effects like failure to extract to a root dir because it's not found. */
2531
int zstat_win32(__W32STAT_GLOBALS__ const char *path, struct stat *buf)
2533
if (!stat(path, buf))
2535
#ifdef NT_TZBUG_WORKAROUND
2536
/* stat was successful, now redo the time-stamp fetches */
2537
int fs_uses_loctime = FStampIsLocTime(__G__ path);
2539
FILETIME Modft, Accft, Creft;
2540
#ifdef __RSXNT__ /* RSXNT/EMX C rtl uses OEM charset */
2541
char *ansi_path = (char *)alloca(strlen(path) + 1);
2543
INTERN_TO_ISO(path, ansi_path);
2544
# define Ansi_Path ansi_path
2546
# define Ansi_Path path
2549
TTrace((stdout, "stat(%s) finds modtime %08lx\n", path, buf->st_mtime));
2550
h = CreateFile(Ansi_Path, GENERIC_READ,
2551
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
2552
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
2553
if (h != INVALID_HANDLE_VALUE) {
2554
BOOL ftOK = GetFileTime(h, &Creft, &Accft, &Modft);
2558
FTTrace((stdout, "GetFileTime returned Modft", 0, &Modft));
2559
FTTrace((stdout, "GetFileTime returned Creft", 0, &Creft));
2560
if (!fs_uses_loctime) {
2561
/* On a filesystem that stores UTC timestamps, we refill
2562
* the time fields of the struct stat buffer by directly
2563
* using the UTC values as returned by the Win32
2564
* GetFileTime() API call.
2566
FileTime2utime(&Modft, &(buf->st_mtime));
2567
if (Accft.dwLowDateTime != 0 || Accft.dwHighDateTime != 0)
2568
FileTime2utime(&Accft, &(buf->st_atime));
2570
buf->st_atime = buf->st_mtime;
2571
if (Creft.dwLowDateTime != 0 || Creft.dwHighDateTime != 0)
2572
FileTime2utime(&Creft, &(buf->st_ctime));
2574
buf->st_ctime = buf->st_mtime;
2575
TTrace((stdout,"NTFS, recalculated modtime %08lx\n",
2578
/* On VFAT and FAT-like filesystems, the FILETIME values
2579
* are converted back to the stable local time before
2580
* converting them to UTC unix time-stamps.
2582
VFatFileTime2utime(&Modft, &(buf->st_mtime));
2583
if (Accft.dwLowDateTime != 0 || Accft.dwHighDateTime != 0)
2584
VFatFileTime2utime(&Accft, &(buf->st_atime));
2586
buf->st_atime = buf->st_mtime;
2587
if (Creft.dwLowDateTime != 0 || Creft.dwHighDateTime != 0)
2588
VFatFileTime2utime(&Creft, &(buf->st_ctime));
2590
buf->st_ctime = buf->st_mtime;
2591
TTrace((stdout, "VFAT, recalculated modtime %08lx\n",
2597
#endif /* NT_TZBUG_WORKAROUND */
2600
#ifdef W32_STATROOT_FIX
2604
#ifdef __RSXNT__ /* RSXNT/EMX C rtl uses OEM charset */
2605
char *ansi_path = (char *)alloca(strlen(path) + 1);
2607
INTERN_TO_ISO(path, ansi_path);
2608
# define Ansi_Path ansi_path
2610
# define Ansi_Path path
2613
flags = GetFileAttributes(Ansi_Path);
2614
if (flags != 0xFFFFFFFF && flags & FILE_ATTRIBUTE_DIRECTORY) {
2615
Trace((stderr, "\nstat(\"%s\",...) failed on existing directory\n",
2617
memset(buf, 0, sizeof(struct stat));
2618
buf->st_atime = buf->st_ctime = buf->st_mtime =
2619
dos_to_unix_time(DOSTIME_MINIMUM); /* 1-1-80 */
2620
buf->st_mode = S_IFDIR | S_IREAD |
2621
((flags & FILE_ATTRIBUTE_READONLY) ? 0 : S_IWRITE);
2623
} /* assumes: stat() won't fail on non-dirs without good reason */
2626
#endif /* W32_STATROOT_FIX */
2630
#endif /* W32_STAT_BANDAID */
2634
#ifdef W32_USE_IZ_TIMEZONE
2635
#include "timezone.h"
2636
#define SECSPERMIN 60
2637
#define MINSPERHOUR 60
2638
#define SECSPERHOUR (SECSPERMIN * MINSPERHOUR)
2639
static void conv_to_rule(LPSYSTEMTIME lpw32tm, struct rule * ZCONST ptrule);
2641
static void conv_to_rule(LPSYSTEMTIME lpw32tm, struct rule * ZCONST ptrule)
2643
if (lpw32tm->wYear != 0) {
2644
ptrule->r_type = JULIAN_DAY;
2645
ptrule->r_day = ydays[lpw32tm->wMonth - 1] + lpw32tm->wDay;
2647
ptrule->r_type = MONTH_NTH_DAY_OF_WEEK;
2648
ptrule->r_mon = lpw32tm->wMonth;
2649
ptrule->r_day = lpw32tm->wDayOfWeek;
2650
ptrule->r_week = lpw32tm->wDay;
2652
ptrule->r_time = (long)lpw32tm->wHour * SECSPERHOUR +
2653
(long)(lpw32tm->wMinute * SECSPERMIN) +
2654
(long)lpw32tm->wSecond;
2657
int GetPlatformLocalTimezone(register struct state * ZCONST sp,
2658
void (*fill_tzstate_from_rules)(struct state * ZCONST sp_res,
2659
ZCONST struct rule * ZCONST start,
2660
ZCONST struct rule * ZCONST end))
2662
TIME_ZONE_INFORMATION tzinfo;
2665
/* read current timezone settings from registry if TZ envvar missing */
2666
res = GetTimeZoneInformation(&tzinfo);
2667
if (res != TIME_ZONE_ID_INVALID)
2669
struct rule startrule, stoprule;
2671
conv_to_rule(&(tzinfo.StandardDate), &stoprule);
2672
conv_to_rule(&(tzinfo.DaylightDate), &startrule);
2674
sp->ttis[0].tt_abbrind = 0;
2676
WideCharToMultiByte(CP_ACP, 0, tzinfo.StandardName, -1,
2677
sp->chars, sizeof(sp->chars), NULL, NULL))
2679
sp->chars[sp->charcnt++] = '\0';
2680
sp->ttis[1].tt_abbrind = sp->charcnt;
2682
WideCharToMultiByte(CP_ACP, 0, tzinfo.DaylightName, -1,
2683
sp->chars + sp->charcnt,
2684
sizeof(sp->chars) - sp->charcnt, NULL, NULL);
2685
if ((sp->charcnt - sp->ttis[1].tt_abbrind) == 0)
2686
sp->chars[sp->charcnt++] = '\0';
2687
sp->ttis[0].tt_gmtoff = - (tzinfo.Bias + tzinfo.StandardBias)
2689
sp->ttis[1].tt_gmtoff = - (tzinfo.Bias + tzinfo.DaylightBias)
2691
sp->ttis[0].tt_isdst = 0;
2692
sp->ttis[1].tt_isdst = 1;
2693
sp->typecnt = (startrule.r_mon == 0 && stoprule.r_mon == 0) ? 1 : 2;
2695
if (sp->typecnt > 1)
2696
(*fill_tzstate_from_rules)(sp, &startrule, &stoprule);
2701
#endif /* W32_USE_IZ_TIMEZONE */
2703
#endif /* !FUNZIP */
2708
/* This replacement getch() function was originally created for Watcom C
2709
* and then additionally used with CYGWIN. Since UnZip 5.4, all other Win32
2710
* ports apply this replacement rather that their supplied getch() (or
2711
* alike) function. There are problems with unabsorbed LF characters left
2712
* over in the keyboard buffer under Win95 (and 98) when ENTER was pressed.
2713
* (Under Win95, ENTER returns two(!!) characters: CR-LF.) This problem
2714
* does not appear when run on a WinNT console prompt!
2717
/* Watcom 10.6's getch() does not handle Alt+<digit><digit><digit>. */
2718
/* Note that if PASSWD_FROM_STDIN is defined, the file containing */
2719
/* the password must have a carriage return after the word, not a */
2720
/* Unix-style newline (linefeed only). This discards linefeeds. */
2722
int getch_win32(void)
2726
unsigned char buf[2];
2728
DWORD odemode = ~(DWORD)0;
2730
# ifdef PASSWD_FROM_STDIN
2731
stin = GetStdHandle(STD_INPUT_HANDLE);
2733
stin = CreateFile("CONIN$", GENERIC_READ | GENERIC_WRITE,
2734
FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
2735
if (stin == INVALID_HANDLE_VALUE)
2738
if (GetConsoleMode(stin, &odemode))
2739
SetConsoleMode(stin, ENABLE_PROCESSED_INPUT); /* raw except ^C noticed */
2740
if (ReadFile(stin, &buf, 1, &rc, NULL) && rc == 1)
2742
/* when the user hits return we get CR LF. We discard the LF, not the CR,
2743
* because when we call this for the first time after a previous input
2744
* such as the one for "replace foo? [y]es, ..." the LF may still be in
2745
* the input stream before whatever the user types at our prompt. */
2747
if (ReadFile(stin, &buf, 1, &rc, NULL) && rc == 1)
2749
if (odemode != ~(DWORD)0)
2750
SetConsoleMode(stin, odemode);
2751
# ifndef PASSWD_FROM_STDIN
2756
#endif /* !WINDLL */
2758
const char *BOINC_RCSID_3ee56ad838 = "$Id: win32.c 4979 2005-01-02 18:29:53Z ballen $";