]>
git.saurik.com Git - wxWidgets.git/blob - utils/Install/sfxzip/win32.c
1 /*---------------------------------------------------------------------------
5 32-bit Windows-specific (NT/95) routines for use with Info-ZIP's UnZip 5.3
8 Contains: GetLoadPath()
12 process_defer_NT() process any deferred items
13 SetSD() set security descriptor on file
14 EvalExtraFields() evaluate and process and extra field NOW
15 IsWinNT() indicate type of WIN32 platform
16 test_NTSD() test integrity of NT security data
38 ---------------------------------------------------------------------------*/
41 #define UNZIP_INTERNAL
43 #include <windows.h> /* must be AFTER unzip.h to avoid struct G problems */
45 # include "rsxntwin.h"
49 #ifndef FUNZIP /* most of this file is not used with fUnZip */
51 #if (defined(__GO32__) || defined(__EMX__) || defined(__CYGWIN32__))
52 # define MKDIR(path,mode) mkdir(path,mode)
54 # define MKDIR(path,mode) mkdir(path)
57 #ifdef HAVE_WORKING_DIRENT_H
58 # undef HAVE_WORKING_DIRENT_H
60 /* The emxrtl dirent support of (__GO32__ || __EMX__) converts to lowercase! */
61 #if defined(__CYGWIN32__)
62 # define HAVE_WORKING_DIRENT_H
66 # ifdef HAVE_WORKING_DIRENT_H
67 # include <dirent.h> /* use readdir() */
68 # define zdirent dirent
70 # define Opendir opendir
71 # define Readdir readdir
72 # define Closedir closedir
73 # else /* !HAVE_WORKING_DIRENT_H */
74 typedef struct zdirent
{
80 char d_name
[MAX_PATH
];
85 static zDIR
*Opendir (const char *n
);
86 static struct zdirent
*Readdir (zDIR
*d
);
87 static void Closedir (zDIR
*d
);
88 # endif /* ?HAVE_WORKING_DIRENT_H */
92 /* Function prototypes */
94 static int SetSD(__GPRO__
char *path
, PVOLUMECAPS VolumeCaps
,
95 uch
*eb_ptr
, unsigned eb_len
);
96 static int EvalExtraFields(__GPRO__
char *path
, uch
*ef_ptr
,
100 #if (defined(USE_EF_UT_TIME) || defined(NT_TZBUG_WORKAROUND) || \
102 static void utime2FileTime(time_t ut
, FILETIME
*pft
);
103 static int FStampIsLocTime(__GPRO__
const char *path
);
104 #endif /* USE_EF_UT_TIME || NT_TZBUG_WORKAROUND || TIMESTAMP */
105 #ifdef NT_TZBUG_WORKAROUND
106 static int FileTime2utime(const FILETIME
*pft
, time_t *ut
);
107 #ifdef W32_STAT_BANDAID
108 static int VFatFileTime2utime(const FILETIME
*pft
, time_t *ut
);
110 static time_t UTCtime2Localtime(time_t utctime
);
111 static void NTtzbugWorkaround(time_t ut
, FILETIME
*pft
);
112 #endif /* NT_TZBUG_WORKAROUND */
114 static int getNTfiletime (__GPRO__ FILETIME
*pModFT
, FILETIME
*pAccFT
,
116 static int isfloppy (int nDrive
);
117 static int NTQueryVolInfo (__GPRO__
const char *name
);
118 static int IsVolumeOldFAT (__GPRO__
const char *name
);
119 static void map2fat (char *pathcomp
, char **pEndFAT
);
123 int _CRT_glob
= 0; /* suppress command line globbing by C RTL */
126 /* static int created_dir; */ /* used by mapname(), checkdir() */
127 /* static int renamed_fullpath; */ /* ditto */
128 /* static int fnlen; */ /* ditto */
129 /* static unsigned nLabelDrive; */ /* ditto */
131 extern char Far TruncNTSD
[]; /* in extract.c */
137 /**************************/
138 /* Function GetLoadPath() */
139 /**************************/
141 char *GetLoadPath(__GPRO
)
144 extern char *_pgmptr
;
147 #else /* use generic API call */
149 GetModuleFileName(NULL
, G
.filename
, FILNAMSIZ
-1);
150 _ISO_INTERN(G
.filename
); /* translate to codepage of C rtl's stdio */
154 } /* end function GetLoadPath() */
162 #ifndef HAVE_WORKING_DIRENT_H
164 /**********************/ /* Borrowed from ZIP 2.0 sources */
165 /* Function Opendir() */ /* Difference: no special handling for */
166 /**********************/ /* hidden or system files. */
168 static zDIR
*Opendir(n
)
169 const char *n
; /* directory to open */
171 zDIR
*d
; /* malloc'd return value */
172 char *p
; /* malloc'd temporary string */
174 extent len
= strlen(n
);
176 /* Start searching for files in directory n */
178 if ((d
= (zDIR
*)malloc(sizeof(zDIR
))) == NULL
||
179 (p
= malloc(strlen(n
) + 5)) == NULL
)
181 if (d
!= (zDIR
*)NULL
)
188 p
[len
++] = '.'; /* x: => x:. */
189 else if (p
[len
-1] == '/' || p
[len
-1] == '\\')
190 --len
; /* foo/ => foo */
194 if (INVALID_HANDLE_VALUE
== (d
->d_hFindFile
= FindFirstFile(p
, &fd
))) {
199 strcpy(d
->d_name
, fd
.cFileName
);
205 } /* end of function Opendir() */
210 /**********************/ /* Borrowed from ZIP 2.0 sources */
211 /* Function Readdir() */ /* Difference: no special handling for */
212 /**********************/ /* hidden or system files. */
214 static struct zdirent
*Readdir(d
)
215 zDIR
*d
; /* directory stream from which to read */
217 /* Return pointer to first or next directory entry, or NULL if end. */
225 if ( !FindNextFile(d
->d_hFindFile
, &fd
) )
228 ISO_TO_INTERN(fd
.cFileName
, d
->d_name
);
230 return (struct zdirent
*)d
;
232 } /* end of function Readdir() */
237 /***********************/
238 /* Function Closedir() */ /* Borrowed from ZIP 2.0 sources */
239 /***********************/
241 static void Closedir(d
)
242 zDIR
*d
; /* directory stream to close */
244 FindClose(d
->d_hFindFile
);
248 #endif /* !HAVE_WORKING_DIRENT_H */
256 /*********************************/
257 /* Function process_defer_NT() */
258 /*********************************/
260 void process_defer_NT(__G
)
263 /* process deferred items */
266 DWORD dirfail
, bytesfail
;
268 ProcessDefer(&dir
, &bytes
, &dirfail
, &bytesfail
);
270 if (!uO
.tflag
&& (uO
.qflag
< 2)) {
272 Info(slide
, 0, ((char *)slide
,
273 " updated: %lu directory entries with %lu bytes security",
274 (ulg
)dir
, (ulg
)bytes
));
276 Info(slide
, 0, ((char *)slide
,
277 " failed: %lu directory entries with %lu bytes security",
278 (ulg
)dirfail
, (ulg
)bytesfail
));
284 /**********************/
285 /* Function SetSD() */ /* return almost-PK errors */
286 /**********************/
288 static int SetSD(__G__ path
, VolumeCaps
, eb_ptr
, eb_len
)
291 PVOLUMECAPS VolumeCaps
;
299 if (eb_ptr
== NULL
|| eb_len
< EB_NTSD_L_LEN
)
300 return PK_OK
; /* not a valid NTSD extra field: assume OK */
302 /* check if we know how to handle this version */
303 if (*(eb_ptr
+ (EB_HEADSIZE
+EB_NTSD_VERSION
)) > (uch
)EB_NTSD_MAX_VER
)
306 ntsd_ucSize
= makelong(eb_ptr
+ (EB_HEADSIZE
+EB_UCSIZE_P
));
307 if (ntsd_ucSize
> 0L && eb_len
<= (EB_NTSD_L_LEN
+ EB_CMPRHEADLEN
))
308 return IZ_EF_TRUNC
; /* no compressed data! */
310 /* allocate storage for uncompressed data */
311 security_data
= (uch
*)malloc((extent
)ntsd_ucSize
);
312 if (security_data
== (uch
*)NULL
)
315 error
= memextract(__G__ security_data
, ntsd_ucSize
,
316 (eb_ptr
+ (EB_HEADSIZE
+EB_NTSD_L_LEN
)), (ulg
)(eb_len
- EB_NTSD_L_LEN
));
318 if (error
== PK_OK
) {
319 if (SecuritySet(path
, VolumeCaps
, security_data
)) {
321 if (!uO
.tflag
&& (uO
.qflag
< 2) &&
322 (!(VolumeCaps
->dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)))
323 Info(slide
, 0, ((char *)slide
, " (%ld bytes security)",
335 /********************************/ /* scan extra fields for something */
336 /* Function EvalExtraFields() */ /* we happen to know */
337 /********************************/
339 static int EvalExtraFields(__G__ path
, ef_ptr
, ef_len
)
348 return PK_OK
; /* user said don't process ACLs; for now, no other
349 extra block types are handled here */
351 while (ef_len
>= EB_HEADSIZE
)
353 unsigned eb_id
= makeword(EB_ID
+ ef_ptr
);
354 unsigned eb_len
= makeword(EB_LEN
+ ef_ptr
);
356 if (eb_len
> (ef_len
- EB_HEADSIZE
)) {
357 /* discovered some extra field inconsistency! */
359 "EvalExtraFields: block length %u > rest ef_size %u\n", eb_len
,
360 ef_len
- EB_HEADSIZE
));
366 /* process security descriptor extra data if:
368 Target local/remote drive supports acls AND
369 Target file is not a directory (else we defer processing
374 VOLUMECAPS VolumeCaps
;
376 /* provide useful input */
377 VolumeCaps
.dwFileAttributes
= G
.pInfo
->file_attr
;
378 VolumeCaps
.bUsePrivileges
= (uO
.X_flag
> 1);
380 /* check target volume capabilities - just fall through
382 if (GetVolumeCaps(G
.rootpath
, path
, &VolumeCaps
) &&
383 !(VolumeCaps
.dwFileSystemFlags
& FS_PERSISTENT_ACLS
))
388 rc
= SetSD(__G__ path
, &VolumeCaps
, ef_ptr
, eb_len
);
394 /* perhaps later we can add support for unzipping OS/2 EAs to NT */
396 rc
= SetEAs(__G__ path
, ef_ptr
);
403 break; /* handled elsewhere */
427 break; /* shut up for other known e.f. blocks */
433 "EvalExtraFields: unknown extra field block, ID=%u\n",
438 ef_ptr
+= (eb_len
+ EB_HEADSIZE
);
439 ef_len
-= (eb_len
+ EB_HEADSIZE
);
453 /**************************/
454 /* Function test_NTSD() */ /* returns PK_WARN when NTSD data is invalid */
455 /**************************/
458 /* Turn off warning about not using all parameters for this function only */
461 int test_NTSD(__G__ eb
, eb_size
, eb_ucptr
, eb_ucsize
)
470 if (!ValidateSecurity(eb_ucptr
))
474 } /* end function test_NTSD() */
477 #endif /* NTSD_EAS */
482 /**********************/
483 /* Function IsWinNT() */
484 /**********************/
486 int IsWinNT(void) /* returns TRUE if real NT, FALSE if Win95 or Win32s */
488 static DWORD g_PlatformId
= 0xFFFFFFFF; /* saved platform indicator */
490 if (g_PlatformId
== 0xFFFFFFFF) {
491 /* note: GetVersionEx() doesn't exist on WinNT 3.1 */
492 if (GetVersion() < 0x80000000)
495 g_PlatformId
= FALSE
;
497 return (int)g_PlatformId
;
501 /* DEBUG_TIME insertion: */
503 static int show_NTFileTime(FILE *hdo
, char *TTmsg
, int isloc
, FILETIME
*pft
);
505 static int show_NTFileTime(FILE *hdo
, char *TTmsg
, int isloc
, FILETIME
*pft
)
510 rval
= FileTimeToSystemTime(pft
, &w32tm
);
512 fprintf(hdo
, "%s\n %08lX,%08lX (%s) -> Conversion failed !!!\n",
513 TTmsg
, (ulg
)(pft
->dwHighDateTime
), (ulg
)(pft
->dwLowDateTime
),
514 (isloc
? "local" : "UTC"));
516 fprintf(hdo
, "%s\n %08lx,%08lx -> %04u-%02u-%02u, %02u:%02u:%02u %s\n",
517 TTmsg
, (ulg
)(pft
->dwHighDateTime
), (ulg
)(pft
->dwLowDateTime
),
518 w32tm
.wYear
, w32tm
.wMonth
, w32tm
.wDay
, w32tm
.wHour
,
519 w32tm
.wMinute
, w32tm
.wSecond
, (isloc
? "local" : "UTC"));
523 #define FTTrace(x) show_NTFileTime x
526 #endif /* DEBUG_TIME */
527 /* end of TIME_DEBUG insertion */
529 #if (defined(USE_EF_UT_TIME) || defined(NT_TZBUG_WORKAROUND) || \
532 #if ((defined(__GNUC__) || defined(ULONG_LONG_MAX)) && !defined(HAVE_INT64))
533 typedef long long LLONG64
;
534 typedef unsigned long long ULLNG64
;
537 #if (defined(__WATCOMC__) && (__WATCOMC__ >= 1100) && !defined(HAVE_INT64))
538 typedef __int64 LLONG64
;
539 typedef unsigned __int64 ULLNG64
;
542 #if (defined(_MSC_VER) && (_MSC_VER >= 1100) && !defined(HAVE_INT64))
543 typedef __int64 LLONG64
;
544 typedef unsigned __int64 ULLNG64
;
548 /*****************************/
549 /* Function utime2FileTime() */ /* convert Unix time_t format into the */
550 /*****************************/ /* form used by SetFileTime() in NT/95 */
552 #define UNIX_TIME_ZERO_HI 0x019DB1DEUL
553 #define UNIX_TIME_ZERO_LO 0xD53E8000UL
554 #define NT_QUANTA_PER_UNIX 10000000L
556 static void utime2FileTime(time_t ut
, FILETIME
*pft
)
561 /* NT_QUANTA_PER_UNIX is small enough so that "ut * NT_QUANTA_PER_UNIX"
562 * cannot overflow in 64-bit signed calculation, regardless wether "ut"
563 * is signed or unsigned. */
564 NTtime
= ((LLONG64
)ut
* NT_QUANTA_PER_UNIX
) +
565 ((ULLNG64
)UNIX_TIME_ZERO_LO
+ ((ULLNG64
)UNIX_TIME_ZERO_HI
<< 32));
566 pft
->dwLowDateTime
= (DWORD
)NTtime
;
567 pft
->dwHighDateTime
= (DWORD
)(NTtime
>> 32);
569 #else /* !HAVE_INT64 (64-bit integer arithmetics may not be supported) */
570 unsigned int b1
, b2
, carry
= 0;
571 unsigned long r0
, r1
, r2
, r3
;
572 long r4
; /* signed, to catch environments with signed time_t */
575 b2
= (ut
>> 16) & 0xFFFF; /* if ut is over 32 bits, too bad */
576 r1
= b1
* (NT_QUANTA_PER_UNIX
& 0xFFFF);
577 r2
= b1
* (NT_QUANTA_PER_UNIX
>> 16);
578 r3
= b2
* (NT_QUANTA_PER_UNIX
& 0xFFFF);
579 r4
= b2
* (NT_QUANTA_PER_UNIX
>> 16);
580 r0
= (r1
+ (r2
<< 16)) & 0xFFFFFFFFL
;
584 r0
= (r0
+ (r3
<< 16)) & 0xFFFFFFFFL
;
587 pft
->dwLowDateTime
= r0
+ UNIX_TIME_ZERO_LO
;
588 if (pft
->dwLowDateTime
< r0
)
590 pft
->dwHighDateTime
= r4
+ (r2
>> 16) + (r3
>> 16)
591 + UNIX_TIME_ZERO_HI
+ carry
;
592 #endif /* ?HAVE_INT64 */
594 } /* end function utime2FileTime() */
598 /******************************/
599 /* Function FStampIsLocTime() */
600 /******************************/
602 static int FStampIsLocTime(__GPRO__
const char *path
)
604 return (NTQueryVolInfo(__G__ path
) ? G
.lastVolLocTim
: FALSE
);
607 #endif /* USE_EF_UT_TIME || NT_TZBUG_WORKAROUND || TIMESTAMP */
611 #ifndef NT_TZBUG_WORKAROUND
612 # define UTIME_BOUNDCHECK_1(utimval) \
613 if (fs_uses_loctime) { \
614 utime_dosmin = dos_to_unix_time(DOSTIME_MINIMUM); \
615 if ((ulg)utimval < (ulg)utime_dosmin) \
616 utimval = utime_dosmin; \
618 # define UTIME_BOUNDCHECK_N(utimval) \
619 if (fs_uses_loctime && ((ulg)utimval < (ulg)utime_dosmin)) \
620 utimval = utime_dosmin;
621 # define NT_TZBUG_PRECOMPENSATE(ut, pft)
623 #else /* NT_TZBUG_WORKAROUND */
624 # define UNIX_TIME_UMAX_HI 0x0236485EUL
625 # define UNIX_TIME_UMAX_LO 0xD4A5E980UL
626 # define UNIX_TIME_SMIN_HI 0x0151669EUL
627 # define UNIX_TIME_SMIN_LO 0xD53E8000UL
628 # define UNIX_TIME_SMAX_HI 0x01E9FD1EUL
629 # define UNIX_TIME_SMAX_LO 0xD4A5E980UL
630 # define UTIME_1980_JAN_01_00_00 315532800L
631 # define UTIME_BOUNDCHECK_1(utimval)
632 # define UTIME_BOUNDCHECK_N(utimval)
633 # define NT_TZBUG_PRECOMPENSATE(ut, pft) \
634 if (fs_uses_loctime) NTtzbugWorkaround(ut, pft);
636 /* nonzero if `y' is a leap year, else zero */
637 # define leap(y) (((y)%4 == 0 && (y)%100 != 0) || (y)%400 == 0)
638 /* number of leap years from 1970 to `y' (not including `y' itself) */
639 # define nleap(y) (((y)-1969)/4 - ((y)-1901)/100 + ((y)-1601)/400)
641 extern ZCONST ush ydays
[]; /* defined in fileio.c */
643 /*****************************/
644 /* Function FileTime2utime() */
645 /*****************************/
647 static int FileTime2utime(const FILETIME
*pft
, time_t *ut
)
652 NTtime
= ((ULLNG64
)pft
->dwLowDateTime
+
653 ((ULLNG64
)pft
->dwHighDateTime
<< 32));
655 /* underflow and overflow handling */
656 #ifdef CHECK_UTIME_SIGNED_UNSIGNED
657 if ((time_t)0x80000000L
< (time_t)0L)
659 if (NTtime
< ((ULLNG64
)UNIX_TIME_SMIN_LO
+
660 ((ULLNG64
)UNIX_TIME_SMIN_HI
<< 32))) {
661 *ut
= (time_t)LONG_MIN
;
664 if (NTtime
> ((ULLNG64
)UNIX_TIME_SMAX_LO
+
665 ((ULLNG64
)UNIX_TIME_SMAX_HI
<< 32))) {
666 *ut
= (time_t)LONG_MAX
;
671 #endif /* CHECK_UTIME_SIGNED_UNSIGNED */
673 if (NTtime
< ((ULLNG64
)UNIX_TIME_ZERO_LO
+
674 ((ULLNG64
)UNIX_TIME_ZERO_HI
<< 32))) {
678 if (NTtime
> ((ULLNG64
)UNIX_TIME_UMAX_LO
+
679 ((ULLNG64
)UNIX_TIME_UMAX_HI
<< 32))) {
680 *ut
= (time_t)ULONG_MAX
;
685 NTtime
-= ((ULLNG64
)UNIX_TIME_ZERO_LO
+
686 ((ULLNG64
)UNIX_TIME_ZERO_HI
<< 32));
687 *ut
= (time_t)(NTtime
/ (unsigned long)NT_QUANTA_PER_UNIX
);
689 #else /* !HAVE_INT64 (64-bit integer arithmetics may not be supported) */
693 /* underflow and overflow handling */
694 #ifdef CHECK_UTIME_SIGNED_UNSIGNED
695 if ((time_t)0x80000000L
< (time_t)0L)
697 if ((pft
->dwHighDateTime
< UNIX_TIME_SMIN_HI
) ||
698 ((pft
->dwHighDateTime
== UNIX_TIME_SMIN_HI
) &&
699 (pft
->dwLowDateTime
< UNIX_TIME_SMIN_LO
))) {
700 *ut
= (time_t)LONG_MIN
;
702 if ((pft
->dwHighDateTime
> UNIX_TIME_SMAX_HI
) ||
703 ((pft
->dwHighDateTime
== UNIX_TIME_SMAX_HI
) &&
704 (pft
->dwLowDateTime
> UNIX_TIME_SMAX_LO
))) {
705 *ut
= (time_t)LONG_MAX
;
710 #endif /* CHECK_UTIME_SIGNED_UNSIGNED */
712 if ((pft
->dwHighDateTime
< UNIX_TIME_ZERO_HI
) ||
713 ((pft
->dwHighDateTime
== UNIX_TIME_ZERO_HI
) &&
714 (pft
->dwLowDateTime
< UNIX_TIME_ZERO_LO
))) {
718 if ((pft
->dwHighDateTime
> UNIX_TIME_UMAX_HI
) ||
719 ((pft
->dwHighDateTime
== UNIX_TIME_UMAX_HI
) &&
720 (pft
->dwLowDateTime
> UNIX_TIME_UMAX_LO
))) {
721 *ut
= (time_t)ULONG_MAX
;
726 FileTimeToSystemTime(pft
, &w32tm
);
728 /* set `days' to the number of days into the year */
729 days
= w32tm
.wDay
- 1 + ydays
[w32tm
.wMonth
-1] +
730 (w32tm
.wMonth
> 2 && leap (w32tm
.wYear
));
732 /* now set `days' to the number of days since 1 Jan 1970 */
733 days
+= 365 * (time_t)(w32tm
.wYear
- 1970) +
734 (time_t)(nleap(w32tm
.wYear
));
736 *ut
= (time_t)(86400L * days
+ 3600L * (time_t)w32tm
.wHour
+
737 (time_t)(60 * w32tm
.wMinute
+ w32tm
.wSecond
));
739 #endif /* ?HAVE_INT64 */
740 } /* end function FileTime2utime() */
744 #ifdef W32_STAT_BANDAID
745 /*********************************/
746 /* Function VFatFileTime2utime() */
747 /*********************************/
749 static int VFatFileTime2utime(const FILETIME
*pft
, time_t *ut
)
753 WORD wDOSDate
, wDOSTime
;
759 FileTimeToLocalFileTime(pft
, &lft
);
760 FTTrace((stdout
, "VFatFT2utime, feed for mktime()", 1, &lft
));
762 /* This version of the FILETIME-to-UNIXTIME conversion function
763 * uses DOS-DATE-TIME format as intermediate stage. For modification
764 * and access times, this is no problem. But, the extra fine resolution
765 * of the VFAT-stored creation time gets lost.
767 FileTimeToDosDateTime(&lft
, &wDOSDate
, &wDOSTime
);
768 TTrace((stdout
,"DosDateTime is %04u-%02u-%02u %02u:%02u:%02u\n",
769 (unsigned)((wDOSDate
>>9)&0x7f)+1980,(unsigned)((wDOSDate
>>5)&0x0f),
770 (unsigned)(wDOSDate
&0x1f),(unsigned)((wDOSTime
>>11)&0x1f),
771 (unsigned)((wDOSTime
>>5)&0x3f),(unsigned)((wDOSTime
<<1)&0x3e)));
772 *ut
= dos_to_unix_time(((ulg
)wDOSDate
<< 16) | (ulg
)wDOSTime
);
774 /* a cheap error check: dos_to_unix_time() only returns an odd time
775 * when clipping at maximum time_t value. DOS_DATE_TIME values have
776 * a resolution of 2 seconds and are therefore even numbers.
778 return (((*ut
)&1) == (time_t)0);
779 #else /* HAVE_MKTIME */
780 FileTimeToSystemTime(&lft
, &w32tm
);
781 /* underflow and overflow handling */
782 /* TODO: The range checks are not accurate, the actual limits may
783 * be off by one daylight-saving-time shift (typically 1 hour),
784 * depending on the current state of "is_dst".
786 #ifdef CHECK_UTIME_SIGNED_UNSIGNED
787 if ((time_t)0x80000000L
< (time_t)0L)
789 if ((pft
->dwHighDateTime
< UNIX_TIME_SMIN_HI
) ||
790 ((pft
->dwHighDateTime
== UNIX_TIME_SMIN_HI
) &&
791 (pft
->dwLowDateTime
< UNIX_TIME_SMIN_LO
))) {
792 *ut
= (time_t)LONG_MIN
;
794 if ((pft
->dwHighDateTime
> UNIX_TIME_SMAX_HI
) ||
795 ((pft
->dwHighDateTime
== UNIX_TIME_SMAX_HI
) &&
796 (pft
->dwLowDateTime
> UNIX_TIME_SMAX_LO
))) {
797 *ut
= (time_t)LONG_MAX
;
802 #endif /* CHECK_UTIME_SIGNED_UNSIGNED */
804 if ((pft
->dwHighDateTime
< UNIX_TIME_ZERO_HI
) ||
805 ((pft
->dwHighDateTime
== UNIX_TIME_ZERO_HI
) &&
806 (pft
->dwLowDateTime
< UNIX_TIME_ZERO_LO
))) {
810 if ((pft
->dwHighDateTime
> UNIX_TIME_UMAX_HI
) ||
811 ((pft
->dwHighDateTime
== UNIX_TIME_UMAX_HI
) &&
812 (pft
->dwLowDateTime
> UNIX_TIME_UMAX_LO
))) {
813 *ut
= (time_t)ULONG_MAX
;
817 ltm
.tm_year
= w32tm
.wYear
- 1900;
818 ltm
.tm_mon
= w32tm
.wMonth
- 1;
819 ltm
.tm_mday
= w32tm
.wDay
;
820 ltm
.tm_hour
= w32tm
.wHour
;
821 ltm
.tm_min
= w32tm
.wMinute
;
822 ltm
.tm_sec
= w32tm
.wSecond
;
823 ltm
.tm_isdst
= -1; /* let mktime determine if DST is in effect */
826 /* a cheap error check: mktime returns "(time_t)-1L" on conversion errors.
827 * Normally, we would have to apply a consistency check because "-1"
828 * could also be a valid time. But, it is quite unlikely to read back odd
829 * time numbers from file systems that store time stamps in DOS format.
830 * (The only known exception is creation time on VFAT partitions.)
832 return (*ut
!= (time_t)-1L);
833 #endif /* ?HAVE_MKTIME */
835 } /* end function VFatFileTime2utime() */
836 #endif /* W32_STAT_BANDAID */
840 /********************************/
841 /* Function UTCtime2Localtime() */ /* borrowed from Zip's mkgmtime() */
842 /********************************/
844 static time_t UTCtime2Localtime(time_t utctime
)
846 time_t utc
= utctime
;
848 unsigned years
, months
, days
, hours
, minutes
, seconds
;
851 #ifdef __BORLANDC__ /* Borland C++ 5.x crashes when trying to reference tm */
852 if (utc
< UTIME_1980_JAN_01_00_00
)
853 utc
= UTIME_1980_JAN_01_00_00
;
855 tm
= localtime(&utc
);
856 if (tm
== (struct tm
*)NULL
)
857 /* localtime() did not accept given utc time value; as an emergency
858 exit, the unconverted utctime value is returned */
861 years
= tm
->tm_year
+ 1900; /* year - 1900 -> year */
862 months
= tm
->tm_mon
; /* 0..11 */
863 days
= tm
->tm_mday
- 1; /* 1..31 -> 0..30 */
864 hours
= tm
->tm_hour
; /* 0..23 */
865 minutes
= tm
->tm_min
; /* 0..59 */
866 seconds
= tm
->tm_sec
; /* 0..61 in ANSI C */
868 /* set `days' to the number of days into the year */
869 days
+= ydays
[months
] + (months
> 1 && leap(years
));
871 /* now set `days' to the number of days since 1 Jan 1970 */
872 days
+= 365 * (years
- 1970) + nleap(years
);
874 return (time_t)(86400L * (ulg
)days
+ 3600L * (ulg
)hours
+
875 (ulg
)(60 * minutes
+ seconds
));
877 } /* end function UTCtime2Localtime() */
881 /********************************/
882 /* Function NTtzbugWorkaround() */
883 /********************************/
885 static void NTtzbugWorkaround(time_t ut
, FILETIME
*pft
)
887 FILETIME C_RTL_locft
, NTAPI_locft
;
888 time_t ux_loctime
= UTCtime2Localtime(ut
);
890 /* This routine is only used when the target file system stores time-
891 * stamps as local time in MSDOS format. Thus we make sure that the
892 * resulting timestamp is within the range of MSDOS date-time values. */
893 if (ux_loctime
< UTIME_1980_JAN_01_00_00
)
894 ux_loctime
= UTIME_1980_JAN_01_00_00
;
896 utime2FileTime(ux_loctime
, &C_RTL_locft
);
897 if (!FileTimeToLocalFileTime(pft
, &NTAPI_locft
))
900 long time_shift_l
, time_shift_h
;
903 time_shift_l
= C_RTL_locft
.dwLowDateTime
- NTAPI_locft
.dwLowDateTime
;
904 if (C_RTL_locft
.dwLowDateTime
< NTAPI_locft
.dwLowDateTime
)
906 time_shift_h
= C_RTL_locft
.dwHighDateTime
- NTAPI_locft
.dwHighDateTime
;
907 pft
->dwLowDateTime
+= time_shift_l
;
908 if (pft
->dwLowDateTime
< (ulg
)time_shift_l
)
910 pft
->dwHighDateTime
+= time_shift_h
+ carry
;
911 TTrace((stdout
, "FileTime shift: %08lx:%08lx\n",
912 time_shift_h
+carry
,time_shift_l
));
914 } /* end function NTtzbugWorkaround() */
916 #endif /* ?NT_TZBUG_WORKAROUND */
920 /****************************/ /* Get the file time in a format that */
921 /* Function getNTfiletime() */ /* can be used by SetFileTime() in NT */
922 /****************************/
924 static int getNTfiletime(__G__ pModFT
, pAccFT
, pCreFT
)
930 #ifdef NT_TZBUG_WORKAROUND
932 #else /* !NT_TZBUG_WORKAROUND */
933 FILETIME locft
; /* 64-bit value made up of two 32-bit [low & high] */
934 WORD wDOSDate
; /* for converting from DOS date to Windows NT */
936 #endif /* ?NT_TZBUG_WORKAROUND */
937 #ifdef USE_EF_UT_TIME
938 unsigned eb_izux_flg
;
939 iztimes z_utime
; /* struct for Unix-style actime & modtime, + creatime */
941 #if (defined(USE_EF_UT_TIME) && !defined(NT_TZBUG_WORKAROUND))
944 #if (defined(USE_EF_UT_TIME) || defined(NT_TZBUG_WORKAROUND))
945 int fs_uses_loctime
= FStampIsLocTime(__G__ G
.filename
);
948 /* Copy and/or convert time and date variables, if necessary;
949 * return a flag indicating which time stamps are available. */
950 #ifdef USE_EF_UT_TIME
955 ((eb_izux_flg
= ef_scan_for_izux(G
.extra_field
,
956 G
.lrec
.extra_field_length
, 0, G
.lrec
.last_mod_dos_datetime
,
957 &z_utime
, NULL
)) & EB_UT_FL_MTIME
))
959 TTrace((stderr
, "getNTfiletime: Unix e.f. modif. time = %lu\n",
961 UTIME_BOUNDCHECK_1(z_utime
.mtime
)
962 utime2FileTime(z_utime
.mtime
, pModFT
);
963 NT_TZBUG_PRECOMPENSATE(z_utime
.mtime
, pModFT
)
964 if (eb_izux_flg
& EB_UT_FL_ATIME
) {
965 UTIME_BOUNDCHECK_N(z_utime
.atime
)
966 utime2FileTime(z_utime
.atime
, pAccFT
);
967 NT_TZBUG_PRECOMPENSATE(z_utime
.atime
, pAccFT
)
969 if (eb_izux_flg
& EB_UT_FL_CTIME
) {
970 UTIME_BOUNDCHECK_N(z_utime
.ctime
)
971 utime2FileTime(z_utime
.ctime
, pCreFT
);
972 NT_TZBUG_PRECOMPENSATE(z_utime
.ctime
, pCreFT
)
974 return (int)eb_izux_flg
;
976 #endif /* USE_EF_UT_TIME */
977 #ifdef NT_TZBUG_WORKAROUND
978 ux_modtime
= dos_to_unix_time(G
.lrec
.last_mod_dos_datetime
);
979 utime2FileTime(ux_modtime
, pModFT
);
980 NT_TZBUG_PRECOMPENSATE(ux_modtime
, pModFT
)
981 #else /* !NT_TZBUG_WORKAROUND */
983 wDOSTime
= (WORD
)(G
.lrec
.last_mod_dos_datetime
);
984 wDOSDate
= (WORD
)(G
.lrec
.last_mod_dos_datetime
>> 16);
986 /* The DosDateTimeToFileTime() function converts a DOS date/time
987 * into a 64-bit Windows NT file time */
988 if (!DosDateTimeToFileTime(wDOSDate
, wDOSTime
, &locft
))
990 Info(slide
, 0, ((char *)slide
, "DosDateTime failed: %d\n",
991 (int)GetLastError()));
994 if (!LocalFileTimeToFileTime(&locft
, pModFT
))
996 Info(slide
, 0, ((char *)slide
, "LocalFileTime failed: %d\n",
997 (int)GetLastError()));
1000 #endif /* ?NT_TZBUG_WORKAROUND */
1002 return (EB_UT_FL_MTIME
| EB_UT_FL_ATIME
);
1004 } /* end function getNTfiletime() */
1009 /****************************/
1010 /* Function close_outfile() */
1011 /****************************/
1013 void close_outfile(__G
)
1016 FILETIME Modft
; /* File time type defined in NT, `last modified' time */
1017 FILETIME Accft
; /* NT file time type, `last access' time */
1018 FILETIME Creft
; /* NT file time type, `file creation' time */
1019 HANDLE hFile
; /* File handle defined in NT */
1021 #ifdef __RSXNT__ /* RSXNT/EMX C rtl uses OEM charset */
1022 char *ansi_name
= (char *)alloca(strlen(G
.filename
) + 1);
1024 INTERN_TO_ISO(G
.filename
, ansi_name
);
1025 # define Ansi_Fname ansi_name
1027 # define Ansi_Fname G.filename
1030 /* Close the file and then re-open it using the Win32
1031 * CreateFile call, so that the file can be created
1032 * with GENERIC_WRITE access, otherwise the SetFileTime
1033 * call will fail. */
1036 /* don't set the time stamp and attributes on standard output */
1040 gotTime
= getNTfiletime(__G__
&Modft
, &Accft
, &Creft
);
1042 /* open a handle to the file before processing extra fields;
1043 we do this in case new security on file prevents us from updating
1045 hFile
= CreateFile(Ansi_Fname
, GENERIC_WRITE
, FILE_SHARE_WRITE
, NULL
,
1046 OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, NULL
);
1048 /* sfield@microsoft.com: set attributes before time in case we decide to
1049 support other filetime members later. This also allows us to apply
1050 attributes before the security is changed, which may prevent this
1051 from succeeding otherwise. Also, since most files don't have
1052 any interesting attributes, only change them if something other than
1053 FILE_ATTRIBUTE_ARCHIVE appears in the attributes. This works well
1054 as an optimization because FILE_ATTRIBUTE_ARCHIVE gets applied to the
1055 file anyway, when it's created new. */
1056 if((G
.pInfo
->file_attr
& 0x7F) & ~FILE_ATTRIBUTE_ARCHIVE
) {
1057 if (!SetFileAttributes(Ansi_Fname
, G
.pInfo
->file_attr
& 0x7F))
1058 Info(slide
, 1, ((char *)slide
,
1059 "\nwarning (%d): could not set file attributes\n",
1060 (int)GetLastError()));
1064 /* set extra fields, both stored-in-zipfile and .LONGNAME flavors */
1065 if (G
.extra_field
) { /* zipfile extra field may have extended attribs */
1066 int err
= EvalExtraFields(__G__ G
.filename
, G
.extra_field
,
1067 G
.lrec
.extra_field_length
);
1069 if (err
== IZ_EF_TRUNC
) {
1071 Info(slide
, 1, ((char *)slide
, "%-22s ",
1072 FnFilter1(G
.filename
)));
1073 Info(slide
, 1, ((char *)slide
, LoadFarString(TruncNTSD
),
1074 makeword(G
.extra_field
+2)-10, uO
.qflag
? "\n":""));
1077 #endif /* NTSD_EAS */
1079 if ( hFile
== INVALID_HANDLE_VALUE
)
1080 Info(slide
, 1, ((char *)slide
,
1081 "\nCreateFile error %d when trying set file time\n",
1082 (int)GetLastError()));
1085 FILETIME
*pModft
= (gotTime
& EB_UT_FL_MTIME
) ? &Modft
: NULL
;
1086 FILETIME
*pAccft
= (gotTime
& EB_UT_FL_ATIME
) ? &Accft
: NULL
;
1087 FILETIME
*pCreft
= (gotTime
& EB_UT_FL_CTIME
) ? &Creft
: NULL
;
1089 if (!SetFileTime(hFile
, pCreft
, pAccft
, pModft
))
1090 Info(slide
, 0, ((char *)slide
, "\nSetFileTime failed: %d\n",
1091 (int)GetLastError()));
1100 } /* end function close_outfile() */
1107 /*************************/
1108 /* Function stamp_file() */
1109 /*************************/
1111 int stamp_file(__GPRO__ ZCONST
char *fname
, time_t modtime
)
1113 FILETIME Modft
; /* File time type defined in NT, `last modified' time */
1114 HANDLE hFile
; /* File handle defined in NT */
1115 int errstat
= 0; /* return status: 0 == "OK", -1 == "Failure" */
1116 #ifndef NT_TZBUG_WORKAROUND
1117 time_t utime_dosmin
; /* internal variable for UTIME_BOUNDCHECK_1 */
1119 int fs_uses_loctime
= FStampIsLocTime(__G__ fname
);
1120 #ifdef __RSXNT__ /* RSXNT/EMX C rtl uses OEM charset */
1121 char *ansi_name
= (char *)alloca(strlen(fname
) + 1);
1123 INTERN_TO_ISO(fname
, ansi_name
);
1124 # define Ansi_Fname ansi_name
1126 # define Ansi_Fname fname
1129 /* open a handle to the file to prepare setting the mod-time stamp */
1130 hFile
= CreateFile(Ansi_Fname
, GENERIC_WRITE
, FILE_SHARE_WRITE
, NULL
,
1131 OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, NULL
);
1132 if ( hFile
== INVALID_HANDLE_VALUE
) {
1135 /* convert time_t modtime into WIN32 native 64bit format */
1136 UTIME_BOUNDCHECK_1(modtime
)
1137 utime2FileTime(modtime
, &Modft
);
1138 NT_TZBUG_PRECOMPENSATE(modtime
, &Modft
)
1139 /* set Access and Modification times of the file to modtime */
1140 if (!SetFileTime(hFile
, NULL
, &Modft
, &Modft
)) {
1149 } /* end function stamp_file() */
1151 #endif /* TIMESTAMP */
1156 /***********************/
1157 /* Function isfloppy() */ /* more precisely, is it removable? */
1158 /***********************/
1160 static int isfloppy(int nDrive
) /* 1 == A:, 2 == B:, etc. */
1162 char rootPathName
[4];
1164 rootPathName
[0] = (char)('A' + nDrive
- 1); /* build the root path */
1165 rootPathName
[1] = ':'; /* name, e.g. "A:/" */
1166 rootPathName
[2] = '/';
1167 rootPathName
[3] = '\0';
1169 return (GetDriveType(rootPathName
) == DRIVE_REMOVABLE
);
1171 } /* end function isfloppy() */
1176 /*****************************/
1177 /* Function NTQueryVolInfo() */
1178 /*****************************/
1181 * Note: 8.3 limits on filenames apply only to old-style FAT filesystems.
1182 * More recent versions of Windows (Windows NT 3.5 / Windows 4.0)
1183 * can support long filenames (LFN) on FAT filesystems. Check the
1184 * filesystem maximum component length field to detect LFN support.
1187 static int NTQueryVolInfo(__GPRO__
const char *name
)
1189 /* static char lastRootPath[4] = ""; */
1190 /* static int lastVolOldFAT; */
1191 /* static int lastVolLocTim; */
1193 char tmp1
[MAX_PATH
], tmp2
[MAX_PATH
];
1194 unsigned volSerNo
, maxCompLen
, fileSysFlags
;
1195 #ifdef __RSXNT__ /* RSXNT/EMX C rtl uses OEM charset */
1196 char *ansi_name
= (char *)alloca(strlen(name
) + 1);
1198 INTERN_TO_ISO(name
, ansi_name
);
1202 if ((!strncmp(name
, "//", 2) || !strncmp(name
,"\\\\", 2)) &&
1203 (name
[2] != '\0' && name
[2] != '/' && name
[2] != '\\')) {
1204 /* GetFullPathname() and GetVolumeInformation() do not work
1205 * on UNC names. For now, we return "error".
1206 * **FIXME**: check if UNC name is mapped to a drive letter
1207 * and use mapped drive for volume info query.
1211 if (isalpha((uch
)name
[0]) && (name
[1] == ':'))
1212 tmp0
= (char *)name
;
1215 if (!GetFullPathName(name
, MAX_PATH
, tmp1
, &tmp0
))
1219 if (strncmp(G
.lastRootPath
, tmp0
, 2) != 0) {
1220 /* For speed, we skip repeated queries for the same device */
1221 strncpy(G
.lastRootPath
, tmp0
, 2); /* Build the root path name, */
1222 G
.lastRootPath
[2] = '/'; /* e.g. "A:/" */
1223 G
.lastRootPath
[3] = '\0';
1225 if (!GetVolumeInformation((LPCTSTR
)G
.lastRootPath
,
1226 (LPTSTR
)tmp1
, (DWORD
)MAX_PATH
,
1227 (LPDWORD
)&volSerNo
, (LPDWORD
)&maxCompLen
,
1228 (LPDWORD
)&fileSysFlags
, (LPTSTR
)tmp2
, (DWORD
)MAX_PATH
)) {
1229 G
.lastRootPath
[0] = '\0';
1233 /* LFNs are available if the component length is > 12 */
1234 G
.lastVolOldFAT
= (maxCompLen
<= 12);
1235 /* G.lastVolOldFAT = !strncmp(strupr(tmp2), "FAT", 3); old version */
1237 /* Volumes in (V)FAT and (OS/2) HPFS format store file timestamps in
1240 G
.lastVolLocTim
= !strncmp(strupr(tmp2
), "VFAT", 4) ||
1241 !strncmp(tmp2
, "HPFS", 4) ||
1242 !strncmp(tmp2
, "FAT", 3);
1247 } /* end function NTQueryVolInfo() */
1252 /*****************************/
1253 /* Function IsVolumeOldFAT() */
1254 /*****************************/
1256 static int IsVolumeOldFAT(__GPRO__
const char *name
)
1258 return (NTQueryVolInfo(__G__ name
) ? G
.lastVolOldFAT
: FALSE
);
1266 /************************/
1267 /* Function do_wild() */ /* identical to OS/2 version */
1268 /************************/
1270 char *do_wild(__G__ wildspec
)
1272 char *wildspec
; /* only used first time on a given dir */
1274 /* static zDIR *wild_dir = NULL; */
1275 /* static char *dirname, *wildname, matchname[FILNAMSIZ]; */
1276 /* static int firstcall=TRUE, have_dirname, dirnamelen; */
1278 struct zdirent
*file
;
1280 /* Even when we're just returning wildspec, we *always* do so in
1281 * matchname[]--calling routine is allowed to append four characters
1282 * to the returned string, and wildspec may be a pointer to argv[].
1284 if (!G
.notfirstcall
) { /* first call: must initialize everything */
1285 G
.notfirstcall
= TRUE
;
1287 if (!iswild(wildspec
)) {
1288 strcpy(G
.matchname
, wildspec
);
1289 G
.have_dirname
= FALSE
;
1294 /* break the wildspec into a directory part and a wildcard filename */
1295 if ((G
.wildname
= strrchr(wildspec
, '/')) == NULL
&&
1296 (G
.wildname
= strrchr(wildspec
, ':')) == NULL
) {
1299 G
.have_dirname
= FALSE
;
1300 G
.wildname
= wildspec
;
1302 ++G
.wildname
; /* point at character after '/' or ':' */
1303 G
.dirnamelen
= G
.wildname
- wildspec
;
1304 if ((G
.dirname
= (char *)malloc(G
.dirnamelen
+1)) == NULL
) {
1305 Info(slide
, 1, ((char *)slide
,
1306 "warning: cannot allocate wildcard buffers\n"));
1307 strcpy(G
.matchname
, wildspec
);
1308 return G
.matchname
; /* but maybe filespec was not a wildcard */
1310 strncpy(G
.dirname
, wildspec
, G
.dirnamelen
);
1311 G
.dirname
[G
.dirnamelen
] = '\0'; /* terminate for strcpy below */
1312 G
.have_dirname
= TRUE
;
1314 Trace((stderr
, "do_wild: dirname = [%s]\n", G
.dirname
));
1316 if ((G
.wild_dir
= (zvoid
*)Opendir(G
.dirname
)) != NULL
) {
1317 if (G
.have_dirname
) {
1318 strcpy(G
.matchname
, G
.dirname
);
1319 fnamestart
= G
.matchname
+ G
.dirnamelen
;
1321 fnamestart
= G
.matchname
;
1322 while ((file
= Readdir((zDIR
*)G
.wild_dir
)) != NULL
) {
1323 Trace((stderr
, "do_wild: Readdir returns %s\n", file
->d_name
));
1324 strcpy(fnamestart
, file
->d_name
);
1325 if (strrchr(fnamestart
, '.') == (char *)NULL
)
1326 strcat(fnamestart
, ".");
1327 if (match(fnamestart
, G
.wildname
, 1) && /* 1 == ignore case */
1328 /* skip "." and ".." directory entries */
1329 strcmp(fnamestart
, ".") && strcmp(fnamestart
, "..")) {
1330 Trace((stderr
, "do_wild: match() succeeds\n"));
1331 /* remove trailing dot */
1332 fnamestart
+= strlen(fnamestart
) - 1;
1333 if (*fnamestart
== '.')
1338 /* if we get to here directory is exhausted, so close it */
1339 Closedir((zDIR
*)G
.wild_dir
);
1342 Trace((stderr
, "do_wild: Opendir(%s) returns NULL\n", G
.dirname
));
1344 /* return the raw wildspec in case that works (e.g., directory not
1345 * searchable, but filespec was not wild and file is readable) */
1346 strcpy(G
.matchname
, wildspec
);
1350 /* last time through, might have failed opendir but returned raw wildspec */
1351 if (G
.wild_dir
== NULL
) {
1352 G
.notfirstcall
= FALSE
; /* reset for new wildspec */
1355 return (char *)NULL
;
1358 /* If we've gotten this far, we've read and matched at least one entry
1359 * successfully (in a previous call), so dirname has been copied into
1360 * matchname already.
1362 if (G
.have_dirname
) {
1363 /* strcpy(G.matchname, G.dirname); */
1364 fnamestart
= G
.matchname
+ G
.dirnamelen
;
1366 fnamestart
= G
.matchname
;
1367 while ((file
= Readdir((zDIR
*)G
.wild_dir
)) != NULL
) {
1368 Trace((stderr
, "do_wild: readdir returns %s\n", file
->d_name
));
1369 strcpy(fnamestart
, file
->d_name
);
1370 if (strrchr(fnamestart
, '.') == (char *)NULL
)
1371 strcat(fnamestart
, ".");
1372 if (match(fnamestart
, G
.wildname
, 1)) { /* 1 == ignore case */
1373 Trace((stderr
, "do_wild: match() succeeds\n"));
1374 /* remove trailing dot */
1375 fnamestart
+= strlen(fnamestart
) - 1;
1376 if (*fnamestart
== '.')
1382 Closedir((zDIR
*)G
.wild_dir
); /* at least one entry read; nothing left */
1384 G
.notfirstcall
= FALSE
; /* reset for new wildspec */
1387 return (char *)NULL
;
1389 } /* end function do_wild() */
1395 /**********************/
1396 /* Function mapattr() */
1397 /**********************/
1399 /* Identical to MS-DOS, OS/2 versions. However, NT has a lot of extra
1400 * permission stuff, so this function should probably be extended in the
1406 /* set archive bit for file entries (file is not backed up): */
1407 G
.pInfo
->file_attr
= ((unsigned)G
.crec
.external_file_attributes
|
1408 (G
.crec
.external_file_attributes
& FILE_ATTRIBUTE_DIRECTORY
?
1409 0 : FILE_ATTRIBUTE_ARCHIVE
)) & 0xff;
1412 } /* end function mapattr() */
1417 /************************/
1418 /* Function mapname() */
1419 /************************/
1420 /* return 0 if no error, 1 if caution (filename */
1421 int mapname(__G__ renamed
) /* truncated), 2 if warning (skip file because */
1422 __GDEF
/* dir doesn't exist), 3 if error (skip file), */
1423 int renamed
; /* or 10 if out of memory (skip file) */
1424 { /* [also IZ_VOL_LABEL, IZ_CREATED_DIR] */
1425 char pathcomp
[FILNAMSIZ
]; /* path-component buffer */
1426 char *pp
, *cp
=NULL
; /* character pointers */
1427 char *lastsemi
= NULL
; /* pointer to last semi-colon in pathcomp */
1429 register unsigned workch
; /* hold the character being tested */
1432 /*---------------------------------------------------------------------------
1433 Initialize various pointers and counters and stuff.
1434 ---------------------------------------------------------------------------*/
1436 /* can create path as long as not just freshening, or if user told us */
1437 G
.create_dirs
= (!uO
.fflag
|| renamed
);
1439 G
.created_dir
= FALSE
; /* not yet */
1440 G
.renamed_fullpath
= FALSE
;
1441 G
.fnlen
= strlen(G
.filename
);
1444 cp
= G
.filename
- 1; /* point to beginning of renamed name... */
1446 if (*cp
== '\\') /* convert backslashes to forward */
1449 /* use temporary rootpath if user gave full pathname */
1450 if (G
.filename
[0] == '/') {
1451 G
.renamed_fullpath
= TRUE
;
1452 pathcomp
[0] = '/'; /* copy the '/' and terminate */
1455 } else if (isalpha((uch
)G
.filename
[0]) && G
.filename
[1] == ':') {
1456 G
.renamed_fullpath
= TRUE
;
1458 *pp
++ = *cp
++; /* copy the "d:" (+ '/', possibly) */
1461 *pp
++ = *cp
++; /* otherwise add "./"? */
1466 /* pathcomp is ignored unless renamed_fullpath is TRUE: */
1467 if ((error
= checkdir(__G__ pathcomp
, INIT
)) != 0) /* init path buffer */
1468 return error
; /* ...unless no mem or vol label on hard disk */
1470 *pathcomp
= '\0'; /* initialize translation buffer */
1471 pp
= pathcomp
; /* point to translation buffer */
1472 if (!renamed
) { /* cp already set if renamed */
1473 if (uO
.jflag
) /* junking directories */
1474 cp
= (char *)strrchr(G
.filename
, '/');
1475 if (cp
== NULL
) /* no '/' or not junking dirs */
1476 cp
= G
.filename
; /* point to internal zipfile-member pathname */
1478 ++cp
; /* point to start of last component of path */
1481 /*---------------------------------------------------------------------------
1482 Begin main loop through characters in filename.
1483 ---------------------------------------------------------------------------*/
1485 while ((workch
= (uch
)*cp
++) != 0) {
1488 case '/': /* can assume -j flag not given */
1490 if ((error
= checkdir(__G__ pathcomp
, APPEND_DIR
)) > 1)
1492 pp
= pathcomp
; /* reset conversion buffer for next piece */
1493 lastsemi
= NULL
; /* leave directory semi-colons alone */
1496 case ':': /* drive names not stored in zipfile, */
1497 case '<': /* so no colons allowed */
1498 case '>': /* no redirection symbols allowed either */
1499 case '|': /* no pipe signs allowed */
1500 case '"': /* no double quotes allowed */
1501 case '?': /* no wildcards allowed */
1503 *pp
++ = '_'; /* these rules apply equally to FAT and NTFS */
1505 case ';': /* start of VMS version? */
1506 lastsemi
= pp
; /* remove VMS version later... */
1507 *pp
++ = ';'; /* but keep semicolon for now */
1510 case ' ': /* keep spaces unless specifically */
1511 /* NT cannot create filenames with spaces on FAT volumes */
1512 if (uO
.sflag
|| IsVolumeOldFAT(__G__ G
.filename
))
1519 /* allow European characters in filenames: */
1520 if (isprint(workch
) || workch
>= 127)
1521 *pp
++ = (char)workch
;
1523 } /* end while loop */
1525 *pp
= '\0'; /* done with pathcomp: terminate it */
1527 /* if not saving them, remove VMS version numbers (appended "###") */
1528 if (!uO
.V_flag
&& lastsemi
) {
1529 pp
= lastsemi
+ 1; /* semi-colon was kept: expect #'s after */
1530 while (isdigit((uch
)(*pp
)))
1532 if (*pp
== '\0') /* only digits between ';' and end: nuke */
1536 /*---------------------------------------------------------------------------
1537 Report if directory was created (and no file to create: filename ended
1538 in '/'), check name to be sure it exists, and combine path and name be-
1540 ---------------------------------------------------------------------------*/
1542 if (G
.filename
[G
.fnlen
-1] == '/') {
1543 checkdir(__G__ G
.filename
, GETPATH
);
1544 if (G
.created_dir
) {
1545 #ifdef __RSXNT__ /* RSXNT/EMX C rtl uses OEM charset */
1546 char *ansi_name
= (char *)alloca(strlen(G
.filename
) + 1);
1548 INTERN_TO_ISO(G
.filename
, ansi_name
);
1549 # define Ansi_Fname ansi_name
1551 # define Ansi_Fname G.filename
1554 Info(slide
, 0, ((char *)slide
, " creating: %-22s\n",
1555 FnFilter1(G
.filename
)));
1558 /* set file attributes:
1559 The default for newly created directories is "DIR attribute
1560 flags set", so there is no need to change attributes unless
1561 one of the DOS style attribute flags is set. The readonly
1562 attribute need not be masked, since it does not prevent
1563 modifications in the new directory. */
1564 if(G
.pInfo
->file_attr
& (0x7F & ~FILE_ATTRIBUTE_DIRECTORY
)) {
1565 if (!SetFileAttributes(Ansi_Fname
, G
.pInfo
->file_attr
& 0x7F))
1566 Info(slide
, 1, ((char *)slide
,
1567 "\nwarning (%d): could not set file attributes for %s\n",
1568 (int)GetLastError(), G
.filename
));
1572 /* set extra fields, both stored-in-zipfile and .LONGNAME flavors */
1573 if (G
.extra_field
) { /* zipfile e.f. may have extended attribs */
1574 int err
= EvalExtraFields(__G__ G
.filename
, G
.extra_field
,
1575 G
.lrec
.extra_field_length
);
1577 if (err
== IZ_EF_TRUNC
) {
1579 Info(slide
, 1, ((char *)slide
, "%-22s ",
1580 FnFilter1(G
.filename
)));
1581 Info(slide
, 1, ((char *)slide
, LoadFarString(TruncNTSD
),
1582 makeword(G
.extra_field
+2)-10, uO
.qflag
? "\n":""));
1585 #endif /* NTSD_EAS */
1586 return IZ_CREATED_DIR
; /* set dir time (note trailing '/') */
1588 return 2; /* dir existed already; don't look for data to extract */
1591 if (*pathcomp
== '\0') {
1592 Info(slide
, 1, ((char *)slide
, "mapname: conversion of %s failed\n",
1593 FnFilter1(G
.filename
)));
1597 checkdir(__G__ pathcomp
, APPEND_NAME
); /* returns 1 if truncated: care? */
1598 checkdir(__G__ G
.filename
, GETPATH
);
1599 Trace((stderr
, "mapname returns with filename = [%s] (error = %d)\n\n",
1600 FnFilter1(G
.filename
), error
));
1602 if (G
.pInfo
->vollabel
) { /* set the volume label now */
1604 #ifdef __RSXNT__ /* RSXNT/EMX C rtl uses OEM charset */
1605 char *ansi_name
= (char *)alloca(strlen(G
.filename
) + 1);
1606 INTERN_TO_ISO(G
.filename
, ansi_name
);
1607 # define Ansi_Fname ansi_name
1609 # define Ansi_Fname G.filename
1612 /* Build a drive string, e.g. "b:" */
1613 drive
[0] = (char)('a' + G
.nLabelDrive
- 1);
1614 strcpy(drive
+ 1, ":\\");
1616 Info(slide
, 0, ((char *)slide
, "labelling %s %-22s\n", drive
,
1617 FnFilter1(G
.filename
)));
1618 if (!SetVolumeLabel(drive
, Ansi_Fname
)) {
1619 Info(slide
, 1, ((char *)slide
,
1620 "mapname: error setting volume label\n"));
1623 return 2; /* success: skip the "extraction" quietly */
1629 } /* end function mapname() */
1634 /**********************/
1635 /* Function map2fat() */ /* Not quite identical to OS/2 version */
1636 /**********************/
1638 static void map2fat(pathcomp
, pEndFAT
)
1639 char *pathcomp
, **pEndFAT
;
1641 char *ppc
= pathcomp
; /* variable pointer to pathcomp */
1642 char *pEnd
= *pEndFAT
; /* variable pointer to buildpathFAT */
1643 char *pBegin
= *pEndFAT
; /* constant pointer to start of this comp. */
1644 char *last_dot
= NULL
; /* last dot not converted to underscore */
1645 int dotname
= FALSE
; /* flag: path component begins with dot */
1646 /* ("." and ".." don't count) */
1647 register unsigned workch
; /* hold the character being tested */
1650 /* Only need check those characters which are legal in NTFS but not
1651 * in FAT: to get here, must already have passed through mapname.
1652 * Also must truncate path component to ensure 8.3 compliance.
1654 while ((workch
= (uch
)*ppc
++) != 0) {
1662 *pEnd
++ = '_'; /* convert brackets to underscores */
1666 if (pEnd
== *pEndFAT
) { /* nothing appended yet... */
1667 if (*ppc
== '\0') /* don't bother appending a */
1668 break; /* "./" component to the path */
1669 else if (*ppc
== '.' && ppc
[1] == '\0') { /* "../" */
1670 *pEnd
++ = '.'; /* add first dot, unchanged... */
1671 ++ppc
; /* skip second dot, since it will */
1672 } else { /* be "added" at end of if-block */
1673 *pEnd
++ = '_'; /* FAT doesn't allow null filename */
1674 dotname
= TRUE
; /* bodies, so map .exrc -> _.exrc */
1675 } /* (extra '_' now, "dot" below) */
1676 } else if (dotname
) { /* found a second dot, but still */
1677 dotname
= FALSE
; /* have extra leading underscore: */
1678 *pEnd
= '\0'; /* remove it by shifting chars */
1679 pEnd
= *pEndFAT
+ 1; /* left one space (e.g., .p1.p2: */
1680 while (pEnd
[1]) { /* __p1 -> _p1_p2 -> _p1.p2 when */
1681 *pEnd
= pEnd
[1]; /* finished) [opt.: since first */
1682 ++pEnd
; /* two chars are same, can start */
1683 } /* shifting at second position] */
1685 last_dot
= pEnd
; /* point at last dot so far... */
1686 *pEnd
++ = '_'; /* convert dot to underscore for now */
1690 *pEnd
++ = (char)workch
;
1693 } /* end while loop */
1695 *pEnd
= '\0'; /* terminate buildpathFAT */
1697 /* NOTE: keep in mind that pEnd points to the end of the path
1698 * component, and *pEndFAT still points to the *beginning* of it...
1699 * Also note that the algorithm does not try to get too fancy:
1700 * if there are no dots already, the name either gets truncated
1701 * at 8 characters or the last underscore is converted to a dot
1702 * (only if more characters are saved that way). In no case is
1703 * a dot inserted between existing characters.
1705 if (last_dot
== NULL
) { /* no dots: check for underscores... */
1706 char *plu
= strrchr(pBegin
, '_'); /* pointer to last underscore */
1708 if (plu
== NULL
) { /* no dots, no underscores: truncate at 8 chars */
1709 *pEndFAT
+= 8; /* (or could insert '.' and keep 11...?) */
1710 if (*pEndFAT
> pEnd
)
1711 *pEndFAT
= pEnd
; /* oops...didn't have 8 chars to truncate */
1714 } else if (MIN(plu
- pBegin
, 8) + MIN(pEnd
- plu
- 1, 3) > 8) {
1715 last_dot
= plu
; /* be lazy: drop through to next if-blk */
1716 } else if ((pEnd
- *pEndFAT
) > 8) {
1717 *pEndFAT
+= 8; /* more fits into just basename than if */
1718 **pEndFAT
= '\0'; /* convert last underscore to dot */
1720 *pEndFAT
= pEnd
; /* whole thing fits into 8 chars or less */
1723 if (last_dot
!= NULL
) { /* one dot (or two, in the case of */
1724 *last_dot
= '.'; /* "..") is OK: put it back in */
1726 if ((last_dot
- pBegin
) > 8) {
1731 q
= last_dot
= pBegin
+ 8;
1732 for (i
= 0; (i
< 4) && *p
; ++i
) /* too many chars in basename: */
1733 *q
++ = *p
++; /* shift .ext left and trun- */
1734 *q
= '\0'; /* cate/terminate it */
1736 } else if ((pEnd
- last_dot
) > 4) { /* too many chars in extension */
1737 *pEndFAT
= last_dot
+ 4;
1740 *pEndFAT
= pEnd
; /* filename is fine; point at terminating zero */
1742 if ((last_dot
- pBegin
) > 0 && last_dot
[-1] == ' ')
1743 last_dot
[-1] = '_'; /* NO blank in front of '.'! */
1745 } /* end function map2fat() */
1750 /***********************/ /* Borrowed from os2.c for UnZip 5.1. */
1751 /* Function checkdir() */ /* Difference: no EA stuff */
1752 /***********************/ /* HPFS stuff works on NTFS too */
1754 int checkdir(__G__ pathcomp
, flag
)
1759 * returns: 1 - (on APPEND_NAME) truncated filename
1760 * 2 - path doesn't exist, not allowed to create
1761 * 3 - path doesn't exist, tried to create and failed; or
1762 * path exists and is not a directory, but is supposed to be
1763 * 4 - path is too long
1764 * 10 - can't allocate memory for filename buffers
1767 /* static int rootlen = 0; */ /* length of rootpath */
1768 /* static char *rootpath; */ /* user's "extract-to" directory */
1769 /* static char *buildpathHPFS; */ /* full path (so far) to extracted file, */
1770 /* static char *buildpathFAT; */ /* both HPFS/EA (main) and FAT versions */
1771 /* static char *endHPFS; */ /* corresponding pointers to end of */
1772 /* static char *endFAT; */ /* buildpath ('\0') */
1775 # define FUNCTION (flag & FN_MASK)
1779 /*---------------------------------------------------------------------------
1780 APPEND_DIR: append the path component to the path being built and check
1781 for its existence. If doesn't exist and we are creating directories, do
1782 so for this one; else signal success or error as appropriate.
1783 ---------------------------------------------------------------------------*/
1785 if (FUNCTION
== APPEND_DIR
) {
1789 Trace((stderr
, "appending dir segment [%s]\n", pathcomp
));
1790 while ((*G
.endHPFS
= *p
++) != '\0') /* copy to HPFS filename */
1792 if (!IsVolumeOldFAT(__G__ G
.buildpathHPFS
)) {
1794 while ((*G
.endFAT
= *p
++) != '\0') /* copy to FAT filename, too */
1797 map2fat(pathcomp
, &G
.endFAT
); /* map into FAT fn, update endFAT */
1799 /* GRR: could do better check, see if overrunning buffer as we go:
1800 * check endHPFS-buildpathHPFS after each append, set warning variable
1801 * if within 20 of FILNAMSIZ; then if var set, do careful check when
1802 * appending. Clear variable when begin new path. */
1804 /* next check: need to append '/', at least one-char name, '\0' */
1805 if ((G
.endHPFS
-G
.buildpathHPFS
) > FILNAMSIZ
-3)
1806 too_long
= TRUE
; /* check if extracting dir? */
1808 /* Borland C++ 5.0 does not handle a call to stat() well if the
1809 * directory does not exist (it tends to crash in strange places.)
1810 * This is apparently a problem only when compiling for GUI rather
1811 * than console. The code below attempts to work around this problem.
1813 if (access(G
.buildpathFAT
, 0) != 0) {
1814 if (!G
.create_dirs
) { /* told not to create (freshening) */
1815 free(G
.buildpathHPFS
);
1816 free(G
.buildpathFAT
);
1817 return 2; /* path doesn't exist: nothing to do */
1819 if (too_long
) { /* GRR: should allow FAT extraction w/o EAs */
1820 Info(slide
, 1, ((char *)slide
,
1821 "checkdir error: path too long: %s\n",
1822 FnFilter1(G
.buildpathHPFS
)));
1823 free(G
.buildpathHPFS
);
1824 free(G
.buildpathFAT
);
1825 return 4; /* no room for filenames: fatal */
1827 if (MKDIR(G
.buildpathFAT
, 0777) == -1) { /* create the directory */
1828 Info(slide
, 1, ((char *)slide
,
1829 "checkdir error: cannot create %s\n\
1830 unable to process %s.\n",
1831 FnFilter2(G
.buildpathFAT
), FnFilter1(G
.filename
)));
1832 free(G
.buildpathHPFS
);
1833 free(G
.buildpathFAT
);
1834 return 3; /* path didn't exist, tried to create, failed */
1836 G
.created_dir
= TRUE
;
1838 #endif /* FIX_STAT_BUG */
1839 if (SSTAT(G
.buildpathFAT
, &G
.statbuf
)) /* path doesn't exist */
1841 if (!G
.create_dirs
) { /* told not to create (freshening) */
1842 free(G
.buildpathHPFS
);
1843 free(G
.buildpathFAT
);
1844 return 2; /* path doesn't exist: nothing to do */
1846 if (too_long
) { /* GRR: should allow FAT extraction w/o EAs */
1847 Info(slide
, 1, ((char *)slide
,
1848 "checkdir error: path too long: %s\n",
1849 FnFilter1(G
.buildpathHPFS
)));
1850 free(G
.buildpathHPFS
);
1851 free(G
.buildpathFAT
);
1852 return 4; /* no room for filenames: fatal */
1854 if (MKDIR(G
.buildpathFAT
, 0777) == -1) { /* create the directory */
1855 Info(slide
, 1, ((char *)slide
,
1856 "checkdir error: cannot create %s\n\
1857 unable to process %s.\n",
1858 FnFilter2(G
.buildpathFAT
), FnFilter1(G
.filename
)));
1859 free(G
.buildpathHPFS
);
1860 free(G
.buildpathFAT
);
1861 return 3; /* path didn't exist, tried to create, failed */
1863 G
.created_dir
= TRUE
;
1864 } else if (!S_ISDIR(G
.statbuf
.st_mode
)) {
1865 Info(slide
, 1, ((char *)slide
,
1866 "checkdir error: %s exists but is not directory\n \
1867 unable to process %s.\n",
1868 FnFilter2(G
.buildpathFAT
), FnFilter1(G
.filename
)));
1869 free(G
.buildpathHPFS
);
1870 free(G
.buildpathFAT
);
1871 return 3; /* path existed but wasn't dir */
1874 Info(slide
, 1, ((char *)slide
,
1875 "checkdir error: path too long: %s\n",
1876 FnFilter1(G
.buildpathHPFS
)));
1877 free(G
.buildpathHPFS
);
1878 free(G
.buildpathFAT
);
1879 return 4; /* no room for filenames: fatal */
1883 *G
.endHPFS
= *G
.endFAT
= '\0';
1884 Trace((stderr
, "buildpathHPFS now = [%s]\nbuildpathFAT now = [%s]\n",
1885 FnFilter1(G
.buildpathHPFS
), FnFilter2(G
.buildpathFAT
)));
1888 } /* end if (FUNCTION == APPEND_DIR) */
1890 /*---------------------------------------------------------------------------
1891 GETPATH: copy full FAT path to the string pointed at by pathcomp (want
1892 filename to reflect name used on disk, not EAs; if full path is HPFS,
1893 buildpathFAT and buildpathHPFS will be identical). Also free both paths.
1894 ---------------------------------------------------------------------------*/
1896 if (FUNCTION
== GETPATH
) {
1897 Trace((stderr
, "getting and freeing FAT path [%s]\n",
1898 FnFilter1(G
.buildpathFAT
)));
1899 Trace((stderr
, "freeing HPFS path [%s]\n",
1900 FnFilter1(G
.buildpathHPFS
)));
1901 strcpy(pathcomp
, G
.buildpathFAT
);
1902 free(G
.buildpathFAT
);
1903 free(G
.buildpathHPFS
);
1904 G
.buildpathHPFS
= G
.buildpathFAT
= G
.endHPFS
= G
.endFAT
= NULL
;
1908 /*---------------------------------------------------------------------------
1909 APPEND_NAME: assume the path component is the filename; append it and
1910 return without checking for existence.
1911 ---------------------------------------------------------------------------*/
1913 if (FUNCTION
== APPEND_NAME
) {
1917 Trace((stderr
, "appending filename [%s]\n", FnFilter1(pathcomp
)));
1918 while ((*G
.endHPFS
= *p
++) != '\0') { /* copy to HPFS filename */
1920 if ((G
.endHPFS
-G
.buildpathHPFS
) >= FILNAMSIZ
) {
1921 *--G
.endHPFS
= '\0';
1922 Info(slide
, 1, ((char *)slide
,
1923 "checkdir warning: path too long; truncating\n \
1925 FnFilter1(G
.filename
), FnFilter2(G
.buildpathHPFS
)));
1926 error
= 1; /* filename truncated */
1930 if ( G
.pInfo
->vollabel
|| !IsVolumeOldFAT(__G__ G
.buildpathHPFS
)) {
1932 while ((*G
.endFAT
= *p
++) != '\0') /* copy to FAT filename, too */
1935 map2fat(pathcomp
, &G
.endFAT
); /* map into FAT fn, update endFAT */
1936 Trace((stderr
, "buildpathHPFS: %s\nbuildpathFAT: %s\n",
1937 FnFilter1(G
.buildpathHPFS
), FnFilter2(G
.buildpathFAT
)));
1939 return error
; /* could check for existence, prompt for new name... */
1941 } /* end if (FUNCTION == APPEND_NAME) */
1943 /*---------------------------------------------------------------------------
1944 INIT: allocate and initialize buffer space for the file currently being
1945 extracted. If file was renamed with an absolute path, don't prepend the
1947 ---------------------------------------------------------------------------*/
1949 if (FUNCTION
== INIT
) {
1950 Trace((stderr
, "initializing buildpathHPFS and buildpathFAT to "));
1951 if ((G
.buildpathHPFS
= (char *)malloc(G
.fnlen
+G
.rootlen
+1)) == NULL
)
1953 if ((G
.buildpathFAT
= (char *)malloc(G
.fnlen
+G
.rootlen
+1)) == NULL
) {
1954 free(G
.buildpathHPFS
);
1957 if (G
.pInfo
->vollabel
) { /* use root or renamed path, but don't store */
1958 /* GRR: for network drives, do strchr() and return IZ_VOL_LABEL if not [1] */
1959 if (G
.renamed_fullpath
&& pathcomp
[1] == ':')
1960 *G
.buildpathHPFS
= (char)ToLower(*pathcomp
);
1961 else if (!G
.renamed_fullpath
&& G
.rootlen
> 1 &&
1962 G
.rootpath
[1] == ':')
1963 *G
.buildpathHPFS
= (char)ToLower(*G
.rootpath
);
1965 char tmpN
[MAX_PATH
], *tmpP
;
1966 if (GetFullPathName(".", MAX_PATH
, tmpN
, &tmpP
) > MAX_PATH
)
1967 { /* by definition of MAX_PATH we should never get here */
1968 Info(slide
, 1, ((char *)slide
,
1969 "checkdir warning: current dir path too long\n"));
1970 return 1; /* can't get drive letter */
1972 G
.nLabelDrive
= *tmpN
- 'a' + 1;
1973 *G
.buildpathHPFS
= (char)(G
.nLabelDrive
- 1 + 'a');
1975 G
.nLabelDrive
= *G
.buildpathHPFS
- 'a' + 1; /* save for mapname() */
1976 if (uO
.volflag
== 0 || *G
.buildpathHPFS
< 'a' /* no labels/bogus? */
1977 || (uO
.volflag
== 1 && !isfloppy(G
.nLabelDrive
))) { /* !fixed */
1978 free(G
.buildpathHPFS
);
1979 free(G
.buildpathFAT
);
1980 return IZ_VOL_LABEL
; /* skipping with message */
1982 *G
.buildpathHPFS
= '\0';
1983 } else if (G
.renamed_fullpath
) /* pathcomp = valid data */
1984 strcpy(G
.buildpathHPFS
, pathcomp
);
1985 else if (G
.rootlen
> 0)
1986 strcpy(G
.buildpathHPFS
, G
.rootpath
);
1988 *G
.buildpathHPFS
= '\0';
1989 G
.endHPFS
= G
.buildpathHPFS
;
1990 G
.endFAT
= G
.buildpathFAT
;
1991 while ((*G
.endFAT
= *G
.endHPFS
) != '\0') {
1995 Trace((stderr
, "[%s]\n", FnFilter1(G
.buildpathHPFS
)));
1999 /*---------------------------------------------------------------------------
2000 ROOT: if appropriate, store the path in rootpath and create it if neces-
2001 sary; else assume it's a zipfile member and return. This path segment
2002 gets used in extracting all members from every zipfile specified on the
2003 command line. Note that under OS/2 and MS-DOS, if a candidate extract-to
2004 directory specification includes a drive letter (leading "x:"), it is
2005 treated just as if it had a trailing '/'--that is, one directory level
2006 will be created if the path doesn't exist, unless this is otherwise pro-
2007 hibited (e.g., freshening).
2008 ---------------------------------------------------------------------------*/
2010 #if (!defined(SFX) || defined(SFX_EXDIR))
2011 if (FUNCTION
== ROOT
) {
2012 Trace((stderr
, "initializing root path to [%s]\n",
2013 FnFilter1(pathcomp
)));
2014 if (pathcomp
== NULL
) {
2018 if ((G
.rootlen
= strlen(pathcomp
)) > 0) {
2019 int had_trailing_pathsep
=FALSE
, has_drive
=FALSE
, xtra
=2;
2021 if (isalpha((uch
)pathcomp
[0]) && pathcomp
[1] == ':')
2022 has_drive
= TRUE
; /* drive designator */
2023 if (pathcomp
[G
.rootlen
-1] == '/' || pathcomp
[G
.rootlen
-1] == '\\') {
2024 pathcomp
[--G
.rootlen
] = '\0';
2025 had_trailing_pathsep
= TRUE
;
2027 if (has_drive
&& (G
.rootlen
== 2)) {
2028 if (!had_trailing_pathsep
) /* i.e., original wasn't "x:/" */
2029 xtra
= 3; /* room for '.' + '/' + 0 at end of "x:" */
2030 } else if (G
.rootlen
> 0) { /* need not check "x:." and "x:/" */
2031 if (SSTAT(pathcomp
, &G
.statbuf
) || !S_ISDIR(G
.statbuf
.st_mode
))
2033 /* path does not exist */
2034 if (!G
.create_dirs
/* || iswild(pathcomp) */ ) {
2036 return 2; /* treat as stored file */
2038 /* create directory (could add loop here to scan pathcomp
2039 * and create more than one level, but really necessary?) */
2040 if (MKDIR(pathcomp
, 0777) == -1) {
2041 Info(slide
, 1, ((char *)slide
,
2042 "checkdir: cannot create extraction directory: %s\n",
2043 FnFilter1(pathcomp
)));
2044 G
.rootlen
= 0; /* path didn't exist, tried to create, */
2045 return 3; /* failed: file exists, or need 2+ levels */
2049 if ((G
.rootpath
= (char *)malloc(G
.rootlen
+xtra
)) == NULL
) {
2053 strcpy(G
.rootpath
, pathcomp
);
2054 if (xtra
== 3) /* had just "x:", make "x:." */
2055 G
.rootpath
[G
.rootlen
++] = '.';
2056 G
.rootpath
[G
.rootlen
++] = '/';
2057 G
.rootpath
[G
.rootlen
] = '\0';
2058 Trace((stderr
, "rootpath now = [%s]\n", FnFilter1(G
.rootpath
)));
2062 #endif /* !SFX || SFX_EXDIR */
2064 /*---------------------------------------------------------------------------
2065 END: free rootpath, immediately prior to program exit.
2066 ---------------------------------------------------------------------------*/
2068 if (FUNCTION
== END
) {
2069 Trace((stderr
, "freeing rootpath\n"));
2070 if (G
.rootlen
> 0) {
2077 return 99; /* should never reach */
2079 } /* end function checkdir() */
2088 /************************/
2089 /* Function version() */
2090 /************************/
2096 #if (defined(_MSC_VER) || defined(__WATCOMC__) || defined(__DJGPP__))
2098 #if (defined(_MSC_VER) && (_MSC_VER > 900))
2103 len
= sprintf((char *)slide
, CompiledWith
,
2105 #if defined(_MSC_VER) /* MSC == VC++, but what about SDK compiler? */
2106 (sprintf(buf
, "Microsoft C %d.%02d ", _MSC_VER
/100, _MSC_VER%100
), buf
),
2107 # if (_MSC_VER == 800)
2108 "(Visual C++ v1.1)",
2109 # elif (_MSC_VER == 850)
2110 "(Windows NT v3.5 SDK)",
2111 # elif (_MSC_VER == 900)
2112 "(Visual C++ v2.x)",
2113 # elif (_MSC_VER > 900)
2114 (sprintf(buf2
, "(Visual C++ %d.%d)", _MSC_VER
/100 - 6, _MSC_VER%100
/10),
2119 #elif defined(__WATCOMC__)
2120 # if (__WATCOMC__ % 10 > 0)
2121 (sprintf(buf
, "Watcom C/C++ %d.%02d", __WATCOMC__
/ 100,
2122 __WATCOMC__
% 100), buf
), "",
2124 (sprintf(buf
, "Watcom C/C++ %d.%d", __WATCOMC__
/ 100,
2125 (__WATCOMC__
% 100) / 10), buf
), "",
2127 #elif defined(__BORLANDC__)
2129 # if (__BORLANDC__ < 0x0200)
2131 # elif (__BORLANDC__ == 0x0200)
2133 # elif (__BORLANDC__ == 0x0400)
2135 # elif (__BORLANDC__ == 0x0410) /* __BCPLUSPLUS__ = 0x0310 */
2137 # elif (__BORLANDC__ == 0x0452) /* __BCPLUSPLUS__ = 0x0320 */
2139 # elif (__BORLANDC__ == 0x0460) /* __BCPLUSPLUS__ = 0x0340 */
2141 # elif (__BORLANDC__ == 0x0500) /* __BCPLUSPLUS__ = 0x0340 */
2143 # elif (__BORLANDC__ == 0x0520) /* __BCPLUSPLUS__ = 0x0520 */
2144 " 5.2 (C++ Builder)", /* GRR: assume this will stay sync'd? */
2148 #elif defined(__LCC__)
2150 #elif defined(__GNUC__)
2151 # if defined(__RSXNT__)
2152 # if (defined(__DJGPP__) && !defined(__EMX__))
2153 (sprintf(buf
, "rsxnt(djgpp v%d.%02d) / gcc ",
2154 __DJGPP__
, __DJGPP_MINOR__
), buf
),
2155 # elif defined(__DJGPP__)
2156 (sprintf(buf
, "rsxnt(emx+djgpp v%d.%02d) / gcc ",
2157 __DJGPP__
, __DJGPP_MINOR__
), buf
),
2158 # elif (defined(__GO32__) && !defined(__EMX__))
2159 "rsxnt(djgpp v1.x) / gcc ",
2160 # elif defined(__GO32__)
2161 "rsxnt(emx + djgpp v1.x) / gcc ",
2162 # elif defined(__EMX__)
2165 "rsxnt(unknown) / gcc ",
2167 # elif defined(__CYGWIN32__)
2168 "cygnus win32 / gcc ",
2169 # elif defined(__MINGW32__)
2175 #else /* !_MSC_VER, !__WATCOMC__, !__BORLANDC__, !__LCC__, !__GNUC__ */
2176 "unknown compiler (SDK?)", "",
2177 #endif /* ?compilers */
2179 "Windows 95 / Windows NT", "\n(32-bit)",
2188 (*G
.message
)((zvoid
*)&G
, slide
, (ulg
)len
, 0);
2192 } /* end function version() */
2194 #endif /* !WINDLL */
2199 #ifdef W32_STAT_BANDAID
2201 /* All currently known variants of WIN32 operating systems (Windows 95/98,
2202 * WinNT 3.x, 4.0, 5.0) have a nasty bug in the OS kernel concerning
2203 * conversions between UTC and local time: In the time conversion functions
2204 * of the Win32 API, the timezone offset (including seasonal daylight saving
2205 * shift) between UTC and local time evaluation is erratically based on the
2206 * current system time. The correct evaluation must determine the offset
2207 * value as it {was/is/will be} for the actual time to be converted.
2209 * Some versions of MS C runtime lib's stat() returns utc time-stamps so
2210 * that localtime(timestamp) corresponds to the (potentially false) local
2211 * time shown by the OS' system programs (Explorer, command shell dir, etc.)
2212 * The RSXNT port follows the same strategy, but fails to recognize the
2213 * access-time attribute.
2215 * For the NTFS file system (and other filesystems that store time-stamps
2216 * as UTC values), this results in st_mtime (, st_{c|a}time) fields which
2217 * are not stable but vary according to the seasonal change of "daylight
2218 * saving time in effect / not in effect".
2220 * Other C runtime libs (CygWin, or the CRT DLLs supplied with Win95/NT
2221 * return the unix-time equivalent of the UTC FILETIME values as got back
2222 * from the Win32 API call. This time, return values from NTFS are correct
2223 * whereas utimes from files on (V)FAT volumes vary according to the DST
2226 * To achieve timestamp consistency of UTC (UT extra field) values in
2227 * Zip archives, the Info-ZIP programs require work-around code for
2228 * proper time handling in stat() (and other time handling routines).
2230 /* stat() functions under Windows95 tend to fail for root directories. *
2231 * Watcom and Borland, at least, are affected by this bug. Watcom made *
2232 * a partial fix for 11.0 but still missed some cases. This substitute *
2233 * detects the case and fills in reasonable values. Otherwise we get *
2234 * effects like failure to extract to a root dir because it's not found. */
2236 int zstat_win32(__W32STAT_GLOBALS__
const char *path
, struct stat
*buf
)
2238 if (!stat(path
, buf
))
2240 #ifdef NT_TZBUG_WORKAROUND
2241 /* stat was successful, now redo the time-stamp fetches */
2242 int fs_uses_loctime
= FStampIsLocTime(__G__ path
);
2244 FILETIME Modft
, Accft
, Creft
;
2245 #ifdef __RSXNT__ /* RSXNT/EMX C rtl uses OEM charset */
2246 char *ansi_path
= (char *)alloca(strlen(path
) + 1);
2248 INTERN_TO_ISO(path
, ansi_path
);
2249 # define Ansi_Path ansi_path
2251 # define Ansi_Path path
2254 TTrace((stdout
, "stat(%s) finds modtime %08lx\n", path
, buf
->st_mtime
));
2255 h
= CreateFile(Ansi_Path
, GENERIC_READ
,
2256 FILE_SHARE_READ
| FILE_SHARE_WRITE
, NULL
,
2257 OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, NULL
);
2258 if (h
!= INVALID_HANDLE_VALUE
) {
2259 BOOL ftOK
= GetFileTime(h
, &Creft
, &Accft
, &Modft
);
2263 FTTrace((stdout
, "GetFileTime returned Modft", 0, &Modft
));
2264 FTTrace((stdout
, "GetFileTime returned Creft", 0, &Creft
));
2265 if (!fs_uses_loctime
) {
2266 /* On a filesystem that stores UTC timestamps, we refill
2267 * the time fields of the struct stat buffer by directly
2268 * using the UTC values as returned by the Win32
2269 * GetFileTime() API call.
2271 FileTime2utime(&Modft
, &(buf
->st_mtime
));
2272 if (Accft
.dwLowDateTime
!= 0 || Accft
.dwHighDateTime
!= 0)
2273 FileTime2utime(&Accft
, &(buf
->st_atime
));
2275 buf
->st_atime
= buf
->st_mtime
;
2276 if (Creft
.dwLowDateTime
!= 0 || Creft
.dwHighDateTime
!= 0)
2277 FileTime2utime(&Creft
, &(buf
->st_ctime
));
2279 buf
->st_ctime
= buf
->st_mtime
;
2280 TTrace((stdout
,"NTFS, recalculated modtime %08lx\n",
2283 /* On VFAT and FAT-like filesystems, the FILETIME values
2284 * are converted back to the stable local time before
2285 * converting them to UTC unix time-stamps.
2287 VFatFileTime2utime(&Modft
, &(buf
->st_mtime
));
2288 if (Accft
.dwLowDateTime
!= 0 || Accft
.dwHighDateTime
!= 0)
2289 VFatFileTime2utime(&Accft
, &(buf
->st_atime
));
2291 buf
->st_atime
= buf
->st_mtime
;
2292 if (Creft
.dwLowDateTime
!= 0 || Creft
.dwHighDateTime
!= 0)
2293 VFatFileTime2utime(&Creft
, &(buf
->st_ctime
));
2295 buf
->st_ctime
= buf
->st_mtime
;
2296 TTrace((stdout
, "VFAT, recalculated modtime %08lx\n",
2302 #endif /* NT_TZBUG_WORKAROUND */
2305 #ifdef W32_STATROOT_FIX
2309 #ifdef __RSXNT__ /* RSXNT/EMX C rtl uses OEM charset */
2310 char *ansi_path
= (char *)alloca(strlen(path
) + 1);
2312 INTERN_TO_ISO(path
, ansi_path
);
2313 # define Ansi_Path ansi_path
2315 # define Ansi_Path path
2318 flags
= GetFileAttributes(Ansi_Path
);
2319 if (flags
!= 0xFFFFFFFF && flags
& FILE_ATTRIBUTE_DIRECTORY
) {
2320 Trace((stderr
, "\nstat(\"%s\",...) failed on existing directory\n",
2322 memset(buf
, 0, sizeof(struct stat
));
2323 buf
->st_atime
= buf
->st_ctime
= buf
->st_mtime
=
2324 dos_to_unix_time(DOSTIME_MINIMUM
); /* 1-1-80 */
2325 buf
->st_mode
= S_IFDIR
| S_IREAD
|
2326 ((flags
& FILE_ATTRIBUTE_READONLY
) ? 0 : S_IWRITE
);
2328 } /* assumes: stat() won't fail on non-dirs without good reason */
2331 #endif /* W32_STATROOT_FIX */
2335 #endif /* W32_STAT_BANDAID */
2337 #endif /* !FUNZIP */
2342 /* This replacement getch() function was originally created for Watcom C
2343 * and then additionally used with CYGWIN. Since UnZip 5.4, all other Win32
2344 * ports apply this replacement rather that their supplied getch() (or
2345 * alike) function. There are problems with unabsorbed LF characters left
2346 * over in the keyboard buffer under Win95 (and 98) when ENTER was pressed.
2347 * (Under Win95, ENTER returns two(!!) characters: CR-LF.) This problem
2348 * does not appear when run on a WinNT console prompt!
2351 /* Watcom 10.6's getch() does not handle Alt+<digit><digit><digit>. */
2352 /* Note that if PASSWD_FROM_STDIN is defined, the file containing */
2353 /* the password must have a carriage return after the word, not a */
2354 /* Unix-style newline (linefeed only). This discards linefeeds. */
2356 int getch_win32(void)
2360 unsigned char buf
[2];
2362 DWORD odemode
= ~(DWORD
)0;
2364 # ifdef PASSWD_FROM_STDIN
2365 stin
= GetStdHandle(STD_INPUT_HANDLE
);
2367 stin
= CreateFile("CONIN$", GENERIC_READ
| GENERIC_WRITE
,
2368 FILE_SHARE_READ
, NULL
, OPEN_EXISTING
, 0, NULL
);
2369 if (stin
== INVALID_HANDLE_VALUE
)
2372 if (GetConsoleMode(stin
, &odemode
))
2373 SetConsoleMode(stin
, ENABLE_PROCESSED_INPUT
); /* raw except ^C noticed */
2374 if (ReadFile(stin
, &buf
, 1, &rc
, NULL
) && rc
== 1)
2376 /* when the user hits return we get CR LF. We discard the LF, not the CR,
2377 * because when we call this for the first time after a previous input
2378 * such as the one for "replace foo? [y]es, ..." the LF may still be in
2379 * the input stream before whatever the user types at our prompt. */
2381 if (ReadFile(stin
, &buf
, 1, &rc
, NULL
) && rc
== 1)
2383 if (odemode
!= ~(DWORD
)0)
2384 SetConsoleMode(stin
, odemode
);
2385 # ifndef PASSWD_FROM_STDIN
2390 #endif /* !WINDLL */