1 /*
2   Copyright (c) 1990-2008 Info-ZIP.  All rights reserved.
3 
4   See the accompanying file LICENSE, version 2007-Mar-04 or later
5   (the contents of which are also included in unzip.h) for terms of use.
6   If, for some reason, all these files are missing, the Info-ZIP license
7   also may be found at:  ftp://ftp.info-zip.org/pub/infozip/license.html
8 */
9 /*---------------------------------------------------------------------------
10 
11   win32.c
12 
13   32-bit Windows-specific (NT/9x) routines for use with Info-ZIP's UnZip 5.3
14   and later.
15 
16   Contains:  GetLoadPath()
17              Opendir()
18              Readdir()
19              Closedir()
20              SetSD()              set security descriptor on file
21              FindSDExtraField()   extract SD e.f. block from extra field
22              IsWinNT()            indicate type of WIN32 platform
23              test_NTSD()          test integrity of NT security data
24              utime2NtfsFileTime()
25              utime2VFatFileTime()
26              FStampIsLocTime()
27              NtfsFileTime2utime()
28              VFatFileTime2utime()
29              getNTfiletime()
30              SetFileSize()
31              close_outfile()
32              defer_dir_attribs()
33              set_direc_attribs()
34              stamp_file()
35              isfloppy()
36              NTQueryVolInfo()
37              IsVolumeOldFAT()
38              do_wild()
39              mapattr()
40              mapname()
41              maskDOSdevice()
42              map2fat()
43              checkdir()
44              dateformat()
45              dateseparator()
46              version()
47              screensize()
48              zstat_win32()
49              conv_to_rule()
50              GetPlatformLocalTimezone()
51              getch_win32()
52 
53   ---------------------------------------------------------------------------*/
54 
55 
56 #define UNZIP_INTERNAL
57 #include "../unzip.h"
58 #include <windows.h>    /* must be AFTER unzip.h to avoid struct G problems */
59 #ifdef __RSXNT__
60 #  include "../win32/rsxntwin.h"
61 #endif
62 #include "../win32/nt.h"
63 
64 #ifndef FUNZIP          /* most of this file is not used with fUnZip */
65 
66 /* some non-MS runtime headers (e.g. lcc) may miss this definition */
67 #ifndef FILE_WRITE_ATTRIBUTES
68 #  define FILE_WRITE_ATTRIBUTES 0x0100
69 #endif
70 
71 #if (defined(__EMX__) || defined(__CYGWIN__))
72 #  define MKDIR(path,mode)   mkdir(path,mode)
73 #else
74 #  define MKDIR(path,mode)   mkdir(path)
75 #endif
76 
77 #ifdef HAVE_WORKING_DIRENT_H
78 #  undef HAVE_WORKING_DIRENT_H
79 #endif
80 /* The emxrtl dirent support of (__GO32__ || __EMX__) converts to lowercase! */
81 #if defined(__CYGWIN__)
82 #  define HAVE_WORKING_DIRENT_H
83 #endif
84 
85 #ifndef SFX
86 #  ifdef HAVE_WORKING_DIRENT_H
87 #    include <dirent.h>         /* use readdir() */
88 #    define zdirent  dirent
89 #    define zDIR     DIR
90 #    define Opendir  opendir
91 #    define Readdir  readdir
92 #    define Closedir closedir
93 #  else /* !HAVE_WORKING_DIRENT_H */
94      typedef struct zdirent {
95          char    reserved [21];
96          char    ff_attrib;
97          short   ff_ftime;
98          short   ff_fdate;
99          long    size;
100          char    d_name[MAX_PATH];
101          int     d_first;
102          HANDLE  d_hFindFile;
103      } zDIR;
104 
105      static zDIR           *Opendir  (const char *n);
106      static struct zdirent *Readdir  (zDIR *d);
107      static void            Closedir (zDIR *d);
108 #  endif /* ?HAVE_WORKING_DIRENT_H */
109 #endif /* !SFX */
110 
111 #ifdef SET_DIR_ATTRIB
112 typedef struct NTdirattr {      /* struct for holding unix style directory */
113     struct NTdirattr *next;     /*  info until can be sorted and set at end */
114     char *fn;                   /* filename of directory */
115     FILETIME Modft;    /* File time type defined in NT, `last modified' time */
116     FILETIME Accft;    /* NT file time type, `last access' time */
117     FILETIME Creft;    /* NT file time type, `file creation' time */
118     int gotTime;
119     unsigned perms;             /* same as min_info.file_attr */
120 #ifdef NTSD_EAS
121     unsigned SDlen;             /* length of SD data in buf */
122 #endif
123     char buf[1];                /* buffer stub for directory SD and name */
124 } NTdirattr;
125 #define NtAtt(d)  ((NTdirattr *)d)    /* typecast shortcut */
126 #endif /* SET_DIR_ATTRIB */
127 
128 
129 /* Function prototypes */
130 #ifdef NTSD_EAS
131    static int  SetSD(__GPRO__ char *path, unsigned fperms,
132                      uch *eb_ptr, unsigned eb_len);
133    static int  FindSDExtraField(__GPRO__
134                                 uch *ef_ptr, unsigned ef_len,
135                                 uch **p_ebSD_ptr, unsigned *p_ebSD_len);
136 #endif
137 
138 #ifndef NO_W32TIMES_IZFIX
139    static void utime2NtfsFileTime(time_t ut, FILETIME *pft);
140 #endif
141 static void utime2VFatFileTime(time_t ut, FILETIME *pft, int clipDosMin);
142 #if (defined(W32_STAT_BANDAID) && !defined(NO_W32TIMES_IZFIX))
143    static int NtfsFileTime2utime(const FILETIME *pft, time_t *ut);
144 #endif
145 #ifdef W32_STAT_BANDAID
146    static int VFatFileTime2utime(const FILETIME *pft, time_t *ut);
147 #endif
148 static int FStampIsLocTime(__GPRO__ const char *path);
149 
150 
151 static int  getNTfiletime   (__GPRO__ FILETIME *pModFT, FILETIME *pAccFT,
152                              FILETIME *pCreFT);
153 static int  isfloppy        (int nDrive);
154 static int  NTQueryVolInfo  (__GPRO__ const char *name);
155 static int  IsVolumeOldFAT  (__GPRO__ const char *name);
156 static void maskDOSdevice   (__GPRO__ char *pathcomp);
157 static void map2fat         (char *pathcomp, char **pEndFAT);
158 
159 
160 #if (defined(__MINGW32__) && !defined(USE_MINGW_GLOBBING))
161    int _CRT_glob = 0;   /* suppress command line globbing by C RTL */
162 #endif
163 
164 #ifdef ACORN_FTYPE_NFS
165 /* Acorn bits for NFS filetyping */
166 typedef struct {
167   uch ID[2];
168   uch size[2];
169   uch ID_2[4];
170   uch loadaddr[4];
171   uch execaddr[4];
172   uch attr[4];
173 } RO_extra_block;
174 
175 #endif /* ACORN_FTYPE_NFS */
176 
177 /* static int created_dir;      */     /* used by mapname(), checkdir() */
178 /* static int renamed_fullpath; */     /* ditto */
179 /* static int fnlen;            */     /* ditto */
180 /* static unsigned nLabelDrive; */     /* ditto */
181 
182 extern char Far TruncNTSD[];    /* in extract.c */
183 
184 
185 
186 #ifdef SFX
187 
188 /**************************/
189 /* Function GetLoadPath() */
190 /**************************/
191 
GetLoadPath(__GPRO)192 char *GetLoadPath(__GPRO)
193 {
194 #ifdef MSC
195     extern char *_pgmptr;
196     return _pgmptr;
197 
198 #else    /* use generic API call */
199 
200     GetModuleFileName(NULL, G.filename, FILNAMSIZ);
201     _ISO_INTERN(G.filename);    /* translate to codepage of C rtl's stdio */
202     return G.filename;
203 #endif
204 
205 } /* end function GetLoadPath() */
206 
207 
208 
209 
210 
211 #else /* !SFX */
212 
213 #ifndef HAVE_WORKING_DIRENT_H
214 
215 /**********************/        /* Borrowed from ZIP 2.0 sources            */
216 /* Function Opendir() */        /* Difference: no special handling for      */
217 /**********************/        /*             hidden or system files.      */
218 
Opendir(n)219 static zDIR *Opendir(n)
220     const char *n;          /* directory to open */
221 {
222     zDIR *d;                /* malloc'd return value */
223     char *p;                /* malloc'd temporary string */
224     WIN32_FIND_DATAA fd;
225     extent len = strlen(n);
226 
227     /* Start searching for files in directory n */
228 
229     if ((d = (zDIR *)malloc(sizeof(zDIR))) == NULL ||
230         (p = malloc(strlen(n) + 5)) == NULL)
231     {
232         if (d != (zDIR *)NULL)
233             free((void *)d);
234         return (zDIR *)NULL;
235     }
236     INTERN_TO_ISO(n, p);
237     if (len > 0) {
238         if (p[len-1] == ':')
239             p[len++] = '.';     /* x: => x:. */
240         else if (p[len-1] == '/' || p[len-1] == '\\')
241             --len;              /* foo/ => foo */
242     }
243     strcpy(p+len, "/*");
244 
245     if (INVALID_HANDLE_VALUE == (d->d_hFindFile = FindFirstFileA(p, &fd))) {
246         free((zvoid *)d);
247         free((zvoid *)p);
248         return NULL;
249     }
250     strcpy(d->d_name, fd.cFileName);
251 
252     free((zvoid *)p);
253     d->d_first = 1;
254     return d;
255 
256 } /* end of function Opendir() */
257 
258 
259 
260 
261 /**********************/        /* Borrowed from ZIP 2.0 sources            */
262 /* Function Readdir() */        /* Difference: no special handling for      */
263 /**********************/        /*             hidden or system files.      */
264 
Readdir(d)265 static struct zdirent *Readdir(d)
266     zDIR *d;                    /* directory stream from which to read */
267 {
268     /* Return pointer to first or next directory entry, or NULL if end. */
269 
270     if ( d->d_first )
271         d->d_first = 0;
272     else
273     {
274         WIN32_FIND_DATAA fd;
275 
276         if ( !FindNextFileA(d->d_hFindFile, &fd) )
277             return NULL;
278 
279         ISO_TO_INTERN(fd.cFileName, d->d_name);
280     }
281     return (struct zdirent *)d;
282 
283 } /* end of function Readdir() */
284 
285 
286 
287 
288 /***********************/
289 /* Function Closedir() */       /* Borrowed from ZIP 2.0 sources */
290 /***********************/
291 
Closedir(d)292 static void Closedir(d)
293     zDIR *d;                    /* directory stream to close */
294 {
295     FindClose(d->d_hFindFile);
296     free(d);
297 }
298 
299 #endif /* !HAVE_WORKING_DIRENT_H */
300 #endif /* ?SFX */
301 
302 
303 
304 
305 #ifdef NTSD_EAS
306 
307 /**********************/
308 /*  Function SetSD()  */   /* return almost-PK errors */
309 /**********************/
310 
311 static int SetSD(__G__ path, fperms, eb_ptr, eb_len)
312     __GDEF
313     char *path;
314     unsigned fperms;
315     uch *eb_ptr;
316     unsigned eb_len;
317 {
318     ulg ntsd_ucSize;
319     VOLUMECAPS VolumeCaps;
320     uch *security_data;
321     int error;
322 
323     ntsd_ucSize = makelong(eb_ptr + (EB_HEADSIZE+EB_UCSIZE_P));
324     if (ntsd_ucSize > 0L && eb_len <= (EB_NTSD_L_LEN + EB_CMPRHEADLEN))
325         return IZ_EF_TRUNC;               /* no compressed data! */
326 
327     /* provide useful input */
328     VolumeCaps.dwFileAttributes = fperms;
329     VolumeCaps.bUsePrivileges = (uO.X_flag > 1);
330 
331     /* check target volume capabilities - just fall through
332      * and try if fail */
333     if (GetVolumeCaps(G.rootpath, path, &VolumeCaps) &&
334         !(VolumeCaps.dwFileSystemFlags & FS_PERSISTENT_ACLS))
335         return PK_OK;
336 
337     /* allocate storage for uncompressed data */
338     security_data = (uch *)malloc((extent)ntsd_ucSize);
339     if (security_data == (uch *)NULL)
340         return PK_MEM4;
341 
342     error = memextract(__G__ security_data, ntsd_ucSize,
343       (eb_ptr + (EB_HEADSIZE+EB_NTSD_L_LEN)), (ulg)(eb_len - EB_NTSD_L_LEN));
344 
345     if (error == PK_OK) {
346         if (SecuritySet(path, &VolumeCaps, security_data)) {
347             error = PK_COOL;
348             if (!uO.tflag && QCOND2)
349                 Info(slide, 0, ((char *)slide, " (%ld bytes security)",
350                   ntsd_ucSize));
351         }
352     }
353 
354     free(security_data);
355     return error;
356 }
357 
358 
359 
360 
361 /********************************/   /* scan extra fields for something */
362 /*  Function FindSDExtraField() */   /*  we happen to know */
363 /********************************/
364 /* Returns TRUE when a valid NTFS SD block is found.
365  * Address and size of the NTSD e.f. block are passed up to the caller.
366  * In case of more than one valid NTSD block in the e.f., the last block
367  * found is passed up.
368  * Returns FALSE and leaves the content of the ebSD_ptr and ebSD_len
369  * parameters untouched when no valid NTFS SD block is found. */
FindSDExtraField(__GPRO__ uch * ef_ptr,unsigned ef_len,uch ** p_ebSD_ptr,unsigned * p_ebSD_len)370 static int FindSDExtraField(__GPRO__
371                             uch *ef_ptr, unsigned ef_len,
372                             uch **p_ebSD_ptr, unsigned *p_ebSD_len)
373 {
374     int rc = FALSE;
375 
376     if (!uO.X_flag)
377         return FALSE;  /* user said don't process ACLs; for now, no other
378                           extra block types are handled here */
379 
380     while (ef_len >= EB_HEADSIZE)
381     {
382         unsigned eb_id = makeword(EB_ID + ef_ptr);
383         unsigned eb_len = makeword(EB_LEN + ef_ptr);
384 
385         if (eb_len > (ef_len - EB_HEADSIZE)) {
386             /* discovered some extra field inconsistency! */
387             Trace((stderr,
388               "FindSDExtraField: block length %u > rest ef_size %u\n", eb_len,
389               ef_len - EB_HEADSIZE));
390             break;
391         }
392 
393         switch (eb_id)
394         {
395             /* process security descriptor extra data if:
396                  Caller is WinNT AND
397                  Target local/remote drive supports acls AND
398                  Target file is not a directory (else we defer processing
399                    until later)
400              */
401             case EF_NTSD:
402                 if (!IsWinNT())
403                     break; /* OS not capable of handling NTFS attributes */
404 
405                 if (eb_len < EB_NTSD_L_LEN)
406                     break; /* not a valid NTSD extra field */
407 
408                 /* check if we know how to handle this version */
409                 if (*(ef_ptr + (EB_HEADSIZE+EB_NTSD_VERSION))
410                     > (uch)EB_NTSD_MAX_VER)
411                     break;
412 
413                 *p_ebSD_ptr = ef_ptr;
414                 *p_ebSD_len = eb_len;
415                 rc = TRUE;
416                 break;
417 
418 #ifdef DEBUG
419             case EF_OS2:
420             case EF_AV:
421             case EF_PKVMS:
422             case EF_PKW32:
423             case EF_PKUNIX:
424             case EF_IZVMS:
425             case EF_IZUNIX:
426             case EF_IZUNIX2:
427             case EF_TIME:
428             case EF_MAC3:
429             case EF_JLMAC:
430             case EF_ZIPIT:
431             case EF_VMCMS:
432             case EF_MVS:
433             case EF_ACL:
434             case EF_ATHEOS:
435             case EF_BEOS:
436             case EF_QDOS:
437             case EF_AOSVS:
438             case EF_SPARK:
439             case EF_MD5:
440             case EF_ASIUNIX:
441                 break;          /* shut up for other known e.f. blocks  */
442 #endif /* DEBUG */
443 
444             default:
445                 Trace((stderr,
446                   "FindSDExtraField: unknown extra field block, ID=%u\n",
447                   eb_id));
448                 break;
449         }
450 
451         ef_ptr += (eb_len + EB_HEADSIZE);
452         ef_len -= (eb_len + EB_HEADSIZE);
453     }
454 
455     return rc;
456 }
457 
458 
459 
460 
461 #ifndef SFX
462 
463 /**************************/
464 /*  Function test_NTSD()  */   /*  returns PK_WARN when NTSD data is invalid */
465 /**************************/
466 
467 #ifdef __BORLANDC__
468 /* Turn off warning about not using all parameters for this function only */
469 #pragma argsused
470 #endif
471 int test_NTSD(__G__ eb, eb_size, eb_ucptr, eb_ucsize)
472     __GDEF
473     uch *eb;
474     unsigned eb_size;
475     uch *eb_ucptr;
476     ulg eb_ucsize;
477 {
478     return (ValidateSecurity(eb_ucptr) ? PK_OK : PK_WARN);
479 } /* end function test_NTSD() */
480 
481 #endif /* !SFX */
482 #endif /* NTSD_EAS */
483 
484 
485 
486 
487 /**********************/
488 /* Function IsWinNT() */
489 /**********************/
490 
IsWinNT(void)491 int IsWinNT(void)       /* returns TRUE if real NT, FALSE if Win9x or Win32s */
492 {
493     static DWORD g_PlatformId = 0xFFFFFFFF; /* saved platform indicator */
494 
495     if (g_PlatformId == 0xFFFFFFFF) {
496         /* note: GetVersionEx() doesn't exist on WinNT 3.1 */
497         if (GetVersion() < 0x80000000)
498             g_PlatformId = TRUE;
499         else
500             g_PlatformId = FALSE;
501     }
502     return (int)g_PlatformId;
503 }
504 
505 
506 /* DEBUG_TIME insertion: */
507 #ifdef DEBUG_TIME
508 static int show_NTFileTime(FILE *hdo, char *TTmsg, int isloc, FILETIME *pft);
509 
show_NTFileTime(FILE * hdo,char * TTmsg,int isloc,FILETIME * pft)510 static int show_NTFileTime(FILE *hdo, char *TTmsg, int isloc, FILETIME *pft)
511 {
512     SYSTEMTIME w32tm;
513     int rval;
514 
515     rval = FileTimeToSystemTime(pft, &w32tm);
516     if (!rval) {
517         fprintf(hdo, "%s\n %08lX,%08lX (%s) -> Conversion failed !!!\n",
518                 TTmsg, (ulg)(pft->dwHighDateTime), (ulg)(pft->dwLowDateTime),
519                 (isloc ? "local" : "UTC"));
520     } else {
521         fprintf(hdo, "%s\n %08lx,%08lx -> %04u-%02u-%02u, %02u:%02u:%02u %s\n",
522                 TTmsg, (ulg)(pft->dwHighDateTime), (ulg)(pft->dwLowDateTime),
523                 w32tm.wYear, w32tm.wMonth, w32tm.wDay, w32tm.wHour,
524                 w32tm.wMinute, w32tm.wSecond, (isloc ? "local" : "UTC"));
525     }
526     return rval;
527 }
528 #define FTTrace(x)   show_NTFileTime x
529 #else
530 #define FTTrace(x)
531 #endif /* DEBUG_TIME */
532 /* end of DEBUG_TIME insertion */
533 
534 #ifndef IZ_USE_INT64
535 #  if (defined(__GNUC__) || defined(ULONG_LONG_MAX))
536      typedef long long            LLONG64;
537      typedef unsigned long long   ULLNG64;
538 #    define IZ_USE_INT64
539 #  elif (defined(__WATCOMC__) && (__WATCOMC__ >= 1100))
540      typedef __int64              LLONG64;
541      typedef unsigned __int64     ULLNG64;
542 #    define IZ_USE_INT64
543 #  elif (defined(_MSC_VER) && (_MSC_VER >= 1100))
544      typedef __int64              LLONG64;
545      typedef unsigned __int64     ULLNG64;
546 #    define IZ_USE_INT64
547 #  elif (defined(__IBMC__) && (__IBMC__ >= 350))
548      typedef __int64              LLONG64;
549      typedef unsigned __int64     ULLNG64;
550 #    define IZ_USE_INT64
551 #  elif defined(HAVE_INT64)
552      typedef __int64              LLONG64;
553      typedef unsigned __int64     ULLNG64;
554 #    define IZ_USE_INT64
555 #  endif
556 #endif
557 
558 /* scale factor and offset for conversion time_t -> FILETIME */
559 #define NT_QUANTA_PER_UNIX 10000000L
560 #define UNIX_TIME_ZERO_HI  0x019DB1DEUL
561 #define UNIX_TIME_ZERO_LO  0xD53E8000UL
562 /* special FILETIME values for bound-checks */
563 #define UNIX_TIME_UMAX_HI  0x0236485EUL
564 #define UNIX_TIME_UMAX_LO  0xD4A5E980UL
565 #define UNIX_TIME_SMIN_HI  0x0151669EUL
566 #define UNIX_TIME_SMIN_LO  0xD53E8000UL
567 #define UNIX_TIME_SMAX_HI  0x01E9FD1EUL
568 #define UNIX_TIME_SMAX_LO  0xD4A5E980UL
569 #define DOSTIME_MIN_FT_HI  0x01A8E79FUL
570 #define DOSTIME_MIN_FT_LO  0xE1D58000UL
571 /* time_t equivalent of DOSTIME_MINIMUM */
572 #define UTIME_1980_JAN_01_00_00   315532800L
573 
574 
575 #ifndef NO_W32TIMES_IZFIX
576 /*********************************/
577 /* Function utime2NtfsFileTime() */ /* convert Unix time_t format into the */
578 /*********************************/ /* form used by SetFileTime() in NT/9x */
579 
utime2NtfsFileTime(time_t ut,FILETIME * pft)580 static void utime2NtfsFileTime(time_t ut, FILETIME *pft)
581 {
582 #ifdef IZ_USE_INT64
583     ULLNG64 NTtime;
584 
585     /* NT_QUANTA_PER_UNIX is small enough so that "ut * NT_QUANTA_PER_UNIX"
586      * cannot overflow in 64-bit signed calculation, regardless whether "ut"
587      * is signed or unsigned.  */
588     NTtime = ((LLONG64)ut * NT_QUANTA_PER_UNIX) +
589              ((ULLNG64)UNIX_TIME_ZERO_LO + ((ULLNG64)UNIX_TIME_ZERO_HI << 32));
590     pft->dwLowDateTime = (DWORD)NTtime;
591     pft->dwHighDateTime = (DWORD)(NTtime >> 32);
592 
593 #else /* !IZ_USE_INT64 (64-bit integer arithmetics may not be supported) */
594     unsigned int b1, b2, carry = 0;
595     unsigned long r0, r1, r2, r3;
596     long r4;            /* signed, to catch environments with signed time_t */
597 
598     b1 = ut & 0xFFFF;
599     b2 = (ut >> 16) & 0xFFFF;       /* if ut is over 32 bits, too bad */
600     r1 = b1 * (NT_QUANTA_PER_UNIX & 0xFFFF);
601     r2 = b1 * (NT_QUANTA_PER_UNIX >> 16);
602     r3 = b2 * (NT_QUANTA_PER_UNIX & 0xFFFF);
603     r4 = b2 * (NT_QUANTA_PER_UNIX >> 16);
604     r0 = (r1 + (r2 << 16)) & 0xFFFFFFFFL;
605     if (r0 < r1)
606         carry++;
607     r1 = r0;
608     r0 = (r0 + (r3 << 16)) & 0xFFFFFFFFL;
609     if (r0 < r1)
610         carry++;
611     pft->dwLowDateTime = r0 + UNIX_TIME_ZERO_LO;
612     if (pft->dwLowDateTime < r0)
613         carry++;
614     pft->dwHighDateTime = r4 + (r2 >> 16) + (r3 >> 16)
615                             + UNIX_TIME_ZERO_HI + carry;
616 #endif /* ?IZ_USE_INT64 */
617 
618 } /* end function utime2NtfsFileTime() */
619 #endif /* !NO_W32TIMES_IZFIX */
620 
621 
622 
623 /*********************************/
624 /* Function utime2VFatFileTime() */ /* convert Unix time_t format into the */
625 /*********************************/ /* form used by SetFileTime() in NT/9x */
626 
utime2VFatFileTime(time_t ut,FILETIME * pft,int clipDosMin)627 static void utime2VFatFileTime(time_t ut, FILETIME *pft, int clipDosMin)
628 {
629     time_t utc = ut;
630     struct tm *ltm;
631     SYSTEMTIME w32tm;
632     FILETIME lft;
633 
634     /* The milliseconds field gets always initialized to 0. */
635     w32tm.wMilliseconds = 0;
636 
637 #ifdef __BORLANDC__   /* Borland C++ 5.x crashes when trying to reference tm */
638     if (utc < UTIME_1980_JAN_01_00_00)
639         utc = UTIME_1980_JAN_01_00_00;
640 #endif
641     ltm = localtime(&utc);
642     if (ltm == (struct tm *)NULL)
643         /* localtime() did not accept given utc time value; try to use
644            the UTC value */
645         ltm = gmtime(&utc);
646     if (ltm == (struct tm *)NULL) {
647         if (ut <= (UTIME_1980_JAN_01_00_00 + 86400)) {
648             /* use DOSTIME_MINIMUM date instead of "early" failure dates */
649             w32tm.wYear = 1980;
650             w32tm.wMonth = 1;
651             w32tm.wDay = 1;
652             w32tm.wHour = 0;
653             w32tm.wMinute = 0;
654             w32tm.wSecond = 0;
655         } else {
656             /* as a last resort, use the current system time */
657             GetLocalTime(&w32tm);
658         }
659     } else if (clipDosMin && (ltm->tm_year < 80)) {
660         w32tm.wYear = 1980;
661         w32tm.wMonth = 1;
662         w32tm.wDay = 1;
663         w32tm.wHour = 0;
664         w32tm.wMinute = 0;
665         w32tm.wSecond = 0;
666     } else {
667         w32tm.wYear = ltm->tm_year + 1900; /* year + 1900 -> year */
668         w32tm.wMonth = ltm->tm_mon + 1;    /* 0..11 -> 1..12 */
669         w32tm.wDay = ltm->tm_mday;         /* 1..31 */
670         w32tm.wHour = ltm->tm_hour;        /* 0..23 */
671         w32tm.wMinute = ltm->tm_min;       /* 0..59 */
672         w32tm.wSecond = ltm->tm_sec;       /* 0..61 in ANSI C */
673     }
674 
675     SystemTimeToFileTime(&w32tm, &lft);
676     LocalFileTimeToFileTime(&lft, pft);
677 
678 } /* end function utime2VFatFileTime() */
679 
680 
681 
682  /* nonzero if `y' is a leap year, else zero */
683 #define leap(y) (((y)%4 == 0 && (y)%100 != 0) || (y)%400 == 0)
684  /* number of leap years from 1970 to `y' (not including `y' itself) */
685 #define nleap(y) (((y)-1969)/4 - ((y)-1901)/100 + ((y)-1601)/400)
686 
687 extern ZCONST ush ydays[];              /* defined in fileio.c */
688 
689 #if (defined(W32_STAT_BANDAID) && !defined(NO_W32TIMES_IZFIX))
690 /*********************************/
691 /* Function NtfsFileTime2utime() */
692 /*********************************/
693 
NtfsFileTime2utime(const FILETIME * pft,time_t * ut)694 static int NtfsFileTime2utime(const FILETIME *pft, time_t *ut)
695 {
696 #ifdef IZ_USE_INT64
697     ULLNG64 NTtime;
698 
699     NTtime = ((ULLNG64)pft->dwLowDateTime +
700               ((ULLNG64)pft->dwHighDateTime << 32));
701 
702 #ifndef TIME_T_TYPE_DOUBLE
703     /* underflow and overflow handling */
704 #ifdef CHECK_UTIME_SIGNED_UNSIGNED
705     if ((time_t)0x80000000L < (time_t)0L)
706     {
707         if (NTtime < ((ULLNG64)UNIX_TIME_SMIN_LO +
708                       ((ULLNG64)UNIX_TIME_SMIN_HI << 32))) {
709             *ut = (time_t)LONG_MIN;
710             return FALSE;
711         }
712         if (NTtime > ((ULLNG64)UNIX_TIME_SMAX_LO +
713                       ((ULLNG64)UNIX_TIME_SMAX_HI << 32))) {
714             *ut = (time_t)LONG_MAX;
715             return FALSE;
716         }
717     }
718     else
719 #endif /* CHECK_UTIME_SIGNED_UNSIGNED */
720     {
721         if (NTtime < ((ULLNG64)UNIX_TIME_ZERO_LO +
722                       ((ULLNG64)UNIX_TIME_ZERO_HI << 32))) {
723             *ut = (time_t)0;
724             return FALSE;
725         }
726         if (NTtime > ((ULLNG64)UNIX_TIME_UMAX_LO +
727                       ((ULLNG64)UNIX_TIME_UMAX_HI << 32))) {
728             *ut = (time_t)ULONG_MAX;
729             return FALSE;
730         }
731     }
732 #endif /* !TIME_T_TYPE_DOUBLE */
733 
734     NTtime -= ((ULLNG64)UNIX_TIME_ZERO_LO +
735                ((ULLNG64)UNIX_TIME_ZERO_HI << 32));
736     *ut = (time_t)(NTtime / (unsigned long)NT_QUANTA_PER_UNIX);
737     return TRUE;
738 #else /* !IZ_USE_INT64 (64-bit integer arithmetics may not be supported) */
739     time_t days;
740     SYSTEMTIME w32tm;
741 
742 #ifndef TIME_T_TYPE_DOUBLE
743     /* underflow and overflow handling */
744 #ifdef CHECK_UTIME_SIGNED_UNSIGNED
745     if ((time_t)0x80000000L < (time_t)0L)
746     {
747         if ((pft->dwHighDateTime < UNIX_TIME_SMIN_HI) ||
748             ((pft->dwHighDateTime == UNIX_TIME_SMIN_HI) &&
749              (pft->dwLowDateTime < UNIX_TIME_SMIN_LO))) {
750             *ut = (time_t)LONG_MIN;
751             return FALSE;
752         if ((pft->dwHighDateTime > UNIX_TIME_SMAX_HI) ||
753             ((pft->dwHighDateTime == UNIX_TIME_SMAX_HI) &&
754              (pft->dwLowDateTime > UNIX_TIME_SMAX_LO))) {
755             *ut = (time_t)LONG_MAX;
756             return FALSE;
757         }
758     }
759     else
760 #endif /* CHECK_UTIME_SIGNED_UNSIGNED */
761     {
762         if ((pft->dwHighDateTime < UNIX_TIME_ZERO_HI) ||
763             ((pft->dwHighDateTime == UNIX_TIME_ZERO_HI) &&
764              (pft->dwLowDateTime < UNIX_TIME_ZERO_LO))) {
765             *ut = (time_t)0;
766             return FALSE;
767         }
768         if ((pft->dwHighDateTime > UNIX_TIME_UMAX_HI) ||
769             ((pft->dwHighDateTime == UNIX_TIME_UMAX_HI) &&
770              (pft->dwLowDateTime > UNIX_TIME_UMAX_LO))) {
771             *ut = (time_t)ULONG_MAX;
772             return FALSE;
773         }
774     }
775 #endif /* !TIME_T_TYPE_DOUBLE */
776 
777     FileTimeToSystemTime(pft, &w32tm);
778 
779     /* set `days' to the number of days into the year */
780     days = w32tm.wDay - 1 + ydays[w32tm.wMonth-1] +
781            (w32tm.wMonth > 2 && leap (w32tm.wYear));
782 
783     /* now set `days' to the number of days since 1 Jan 1970 */
784     days += 365 * (time_t)(w32tm.wYear - 1970) +
785             (time_t)(nleap(w32tm.wYear));
786 
787     *ut = (time_t)(86400L * days + 3600L * (time_t)w32tm.wHour +
788                    (time_t)(60 * w32tm.wMinute + w32tm.wSecond));
789     return TRUE;
790 #endif /* ?IZ_USE_INT64 */
791 } /* end function NtfsFileTime2utime() */
792 #endif /* W32_STAT_BANDAID && !NO_W32TIMES_IZFIX */
793 
794 
795 
796 #ifdef W32_STAT_BANDAID
797 /*********************************/
798 /* Function VFatFileTime2utime() */
799 /*********************************/
800 
801 static int VFatFileTime2utime(const FILETIME *pft, time_t *ut)
802 {
803     FILETIME lft;
804 #ifndef HAVE_MKTIME
805     WORD wDOSDate, wDOSTime;
806 #else
807     SYSTEMTIME w32tm;
808     struct tm ltm;
809 #endif
810 
811     if (!FileTimeToLocalFileTime(pft, &lft)) {
812         /* if pft cannot be converted to local time, set ut to current time */
813         time(ut);
814         return FALSE;
815     }
816     FTTrace((stdout, "VFatFT2utime, feed for mktime()", 1, &lft));
817 #ifndef HAVE_MKTIME
818     /* This version of the FILETIME-to-UNIXTIME conversion function
819      * uses DOS-DATE-TIME format as intermediate stage. For modification
820      * and access times, this is no problem. But, the extra fine resolution
821      * of the VFAT-stored creation time gets lost.
822      */
823     if (!FileTimeToDosDateTime(&lft, &wDOSDate, &wDOSTime)) {
824         static const FILETIME dosmin_ft =
825                 {DOSTIME_MIN_FT_LO, DOSTIME_MIN_FT_HI};
826         if (CompareFileTime(&lft, &dosmin_ft) <= 0) {
827             /* underflow -> set to minimum DOS time */
828             wDOSDate = (WORD)((DWORD)DOSTIME_MINIMUM >> 16);
829             wDOSTime = (WORD)DOSTIME_MINIMUM;
830         } else {
831             /* overflow -> set to maximum DOS time */
832             wDOSDate = (WORD)0xFF9F;    /* 2107-12-31 */
833             wDOSTime = (WORD)0xBF7D;    /* 23:59:58 */
834         }
835     }
836     TTrace((stdout,"DosDateTime is %04u-%02u-%02u %02u:%02u:%02u\n",
837       (unsigned)((wDOSDate>>9)&0x7f)+1980,(unsigned)((wDOSDate>>5)&0x0f),
838       (unsigned)(wDOSDate&0x1f),(unsigned)((wDOSTime>>11)&0x1f),
839       (unsigned)((wDOSTime>>5)&0x3f),(unsigned)((wDOSTime<<1)&0x3e)));
840     *ut = dos_to_unix_time(((ulg)wDOSDate << 16) | (ulg)wDOSTime);
841 
842     /* a cheap error check: dos_to_unix_time() only returns an odd time
843      * when clipping at maximum time_t value. DOS_DATE_TIME values have
844      * a resolution of 2 seconds and are therefore even numbers.
845      */
846     return (((*ut)&1) == (time_t)0);
847 #else /* HAVE_MKTIME */
848     FileTimeToSystemTime(&lft, &w32tm);
849 #ifndef TIME_T_TYPE_DOUBLE
850     /* underflow and overflow handling */
851     /* TODO: The range checks are not accurate, the actual limits may
852      *       be off by one daylight-saving-time shift (typically 1 hour),
853      *       depending on the current state of "is_dst".
854      */
855 #ifdef CHECK_UTIME_SIGNED_UNSIGNED
856     if ((time_t)0x80000000L < (time_t)0L)
857     {
858         if ((pft->dwHighDateTime < UNIX_TIME_SMIN_HI) ||
859             ((pft->dwHighDateTime == UNIX_TIME_SMIN_HI) &&
860              (pft->dwLowDateTime < UNIX_TIME_SMIN_LO))) {
861             *ut = (time_t)LONG_MIN;
862             return FALSE;
863         if ((pft->dwHighDateTime > UNIX_TIME_SMAX_HI) ||
864             ((pft->dwHighDateTime == UNIX_TIME_SMAX_HI) &&
865              (pft->dwLowDateTime > UNIX_TIME_SMAX_LO))) {
866             *ut = (time_t)LONG_MAX;
867             return FALSE;
868         }
869     }
870     else
871 #endif /* CHECK_UTIME_SIGNED_UNSIGNED */
872     {
873         if ((pft->dwHighDateTime < UNIX_TIME_ZERO_HI) ||
874             ((pft->dwHighDateTime == UNIX_TIME_ZERO_HI) &&
875              (pft->dwLowDateTime < UNIX_TIME_ZERO_LO))) {
876             *ut = (time_t)0;
877             return FALSE;
878         }
879         if ((pft->dwHighDateTime > UNIX_TIME_UMAX_HI) ||
880             ((pft->dwHighDateTime == UNIX_TIME_UMAX_HI) &&
881              (pft->dwLowDateTime > UNIX_TIME_UMAX_LO))) {
882             *ut = (time_t)ULONG_MAX;
883             return FALSE;
884         }
885     }
886 #endif /* !TIME_T_TYPE_DOUBLE */
887     ltm.tm_year = w32tm.wYear - 1900;
888     ltm.tm_mon = w32tm.wMonth - 1;
889     ltm.tm_mday = w32tm.wDay;
890     ltm.tm_hour = w32tm.wHour;
891     ltm.tm_min = w32tm.wMinute;
892     ltm.tm_sec = w32tm.wSecond;
893     ltm.tm_isdst = -1;  /* let mktime determine if DST is in effect */
894     *ut = mktime(&ltm);
895 
896     /* a cheap error check: mktime returns "(time_t)-1L" on conversion errors.
897      * Normally, we would have to apply a consistency check because "-1"
898      * could also be a valid time. But, it is quite unlikely to read back odd
899      * time numbers from file systems that store time stamps in DOS format.
900      * (The only known exception is creation time on VFAT partitions.)
901      */
902     return (*ut != (time_t)-1L);
903 #endif /* ?HAVE_MKTIME */
904 
905 } /* end function VFatFileTime2utime() */
906 #endif /* W32_STAT_BANDAID */
907 
908 
909 
910 /******************************/
911 /* Function FStampIsLocTime() */
912 /******************************/
913 
914 static int FStampIsLocTime(__GPRO__ const char *path)
915 {
916     return (NTQueryVolInfo(__G__ path) ? G.lastVolLocTim : FALSE);
917 }
918 
919 
920 
921 #ifndef NO_W32TIMES_IZFIX
922 # define UTIME_2_IZFILETIME(ut, pft) \
923    if (fs_uses_loctime) {utime2VFatFileTime(ut, pft, TRUE);} \
924    else {utime2NtfsFileTime(ut, pft);}
925 #else
926 # define UTIME_2_IZFILETIME(ut, pft) \
927    utime2VFatFileTime(ut, pft, fs_uses_loctime);
928 #endif
929 
930 
931 
932 /****************************/      /* Get the file time in a format that */
933 /* Function getNTfiletime() */      /*  can be used by SetFileTime() in NT */
934 /****************************/
935 
936 static int getNTfiletime(__G__ pModFT, pAccFT, pCreFT)
937     __GDEF
938     FILETIME *pModFT;
939     FILETIME *pAccFT;
940     FILETIME *pCreFT;
941 {
942 #ifdef USE_EF_UT_TIME
943     unsigned eb_izux_flg;
944     iztimes z_utime;   /* struct for Unix-style actime & modtime, + creatime */
945 #endif
946     int fs_uses_loctime = FStampIsLocTime(__G__ G.filename);
947 
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
951     if (G.extra_field &&
952 #ifdef IZ_CHECK_TZ
953         G.tz_is_valid &&
954 #endif
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))
958     {
959         TTrace((stderr, "getNTfiletime:  Unix e.f. modif. time = %lu\n",
960           z_utime.mtime));
961         UTIME_2_IZFILETIME(z_utime.mtime, pModFT)
962         if (eb_izux_flg & EB_UT_FL_ATIME) {
963             UTIME_2_IZFILETIME(z_utime.atime, pAccFT)
964         }
965         if (eb_izux_flg & EB_UT_FL_CTIME) {
966             UTIME_2_IZFILETIME(z_utime.ctime, pCreFT)
967         }
968         return (int)eb_izux_flg;
969     }
970 #endif /* USE_EF_UT_TIME */
971 #ifndef NO_W32TIMES_IZFIX
972     if (!fs_uses_loctime) {
973         time_t ux_modtime;
974 
975         ux_modtime = dos_to_unix_time(G.lrec.last_mod_dos_datetime);
976         utime2NtfsFileTime(ux_modtime, pModFT);
977     } else
978 #endif /* NO_W32TIMES_IZFIX */
979     {
980         FILETIME lft;
981 
982         DosDateTimeToFileTime((WORD)(G.lrec.last_mod_dos_datetime >> 16),
983                               (WORD)(G.lrec.last_mod_dos_datetime & 0xFFFFL),
984                               &lft);
985         LocalFileTimeToFileTime(&lft, pModFT);
986     }
987     *pAccFT = *pModFT;
988     return (EB_UT_FL_MTIME | EB_UT_FL_ATIME);
989 
990 } /* end function getNTfiletime() */
991 
992 
993 
994 
995 /**************************/
996 /* Function SetFileSize() */
997 /**************************/
998 
999 int SetFileSize(FILE *file, zusz_t filesize)
1000 {
1001 #ifdef __RSXNT__
1002     /* RSXNT environment lacks a translation function from C file pointer
1003        to Win32-API file handle. So, simply do nothing. */
1004     return 0;
1005 #else /* !__RSXNT__ */
1006     /* not yet verified, if that really creates an unfragmented file
1007       rommel@ars.de
1008      */
1009     HANDLE os_fh;
1010 #ifdef Z_UINT8_DEFINED
1011     LARGE_INTEGER fsbuf;
1012 #endif
1013 
1014     /* Win9x supports FAT file system, only; presetting file size does
1015        not help to prevent fragmentation. */
1016     if (!IsWinNT()) return 0;
1017 
1018     /* Win32-API calls require access to the Win32 file handle.
1019        The interface function used to retrieve the Win32 handle for
1020        a file opened by the C rtl is non-standard and may not be
1021        available for every Win32 compiler environment.
1022        (see also win32/win32.c of the Zip distribution)
1023      */
1024     os_fh = (HANDLE)_get_osfhandle(fileno(file));
1025     /* move file pointer behind the last byte of the expected file size */
1026 #ifdef Z_UINT8_DEFINED
1027     fsbuf.QuadPart = filesize;
1028     if ((SetFilePointer(os_fh, fsbuf.LowPart, &fsbuf.HighPart, FILE_BEGIN)
1029          == 0xFFFFFFFF) && GetLastError() != NO_ERROR)
1030 #else
1031     if (SetFilePointer(os_fh, (ulg)filesize, 0, FILE_BEGIN) == 0xFFFFFFFF)
1032 #endif
1033         return -1;
1034     /* extend/truncate file to the current position */
1035     if (SetEndOfFile(os_fh) == 0)
1036         return -1;
1037     /* move file position pointer back to the start of the file! */
1038     return (SetFilePointer(os_fh, 0, 0, FILE_BEGIN) == 0xFFFFFFFF) ? -1 : 0;
1039 #endif /* ?__RSXNT__ */
1040 } /* end function SetFileSize() */
1041 
1042 
1043 
1044 
1045 /****************************/
1046 /* Function close_outfile() */
1047 /****************************/
1048 
1049 void close_outfile(__G)
1050     __GDEF
1051 {
1052     FILETIME Modft;    /* File time type defined in NT, `last modified' time */
1053     FILETIME Accft;    /* NT file time type, `last access' time */
1054     FILETIME Creft;    /* NT file time type, `file creation' time */
1055     HANDLE hFile = INVALID_HANDLE_VALUE;        /* File handle defined in NT */
1056     int gotTime;
1057 #ifdef NTSD_EAS
1058     uch *ebSDptr;
1059     unsigned ebSDlen;
1060 #endif
1061 #ifdef __RSXNT__        /* RSXNT/EMX C rtl uses OEM charset */
1062     char *ansi_name = (char *)alloca(strlen(G.filename) + 1);
1063 
1064     INTERN_TO_ISO(G.filename, ansi_name);
1065 #   define Ansi_Fname  ansi_name
1066 #else
1067 #   define Ansi_Fname  G.filename
1068 #endif
1069 
1070 #ifndef __RSXNT__
1071     if (IsWinNT()) {
1072         /* Truncate the file to the current position.
1073          * This is needed to remove excess allocation in case the
1074          * extraction has failed or stopped prematurely. */
1075         SetEndOfFile((HANDLE)_get_osfhandle(fileno(G.outfile)));
1076     }
1077 #endif
1078 
1079     /* Close the file and then re-open it using the Win32
1080      * CreateFile call, so that the file can be created
1081      * with GENERIC_WRITE access, otherwise the SetFileTime
1082      * call will fail. */
1083     fclose(G.outfile);
1084 
1085     /* don't set the time stamp and attributes on standard output */
1086     if (uO.cflag)
1087         return;
1088 
1089     /* skip restoring time stamps on user's request */
1090     if (uO.D_flag <= 1) {
1091         gotTime = getNTfiletime(__G__ &Modft, &Accft, &Creft);
1092 
1093         /* open a handle to the file before processing extra fields;
1094            we do this in case new security on file prevents us from updating
1095            time stamps */
1096         hFile = CreateFileA(Ansi_Fname, GENERIC_WRITE, FILE_SHARE_WRITE, NULL,
1097              OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
1098     } else {
1099         gotTime = 0;
1100     }
1101 
1102     /* sfield@microsoft.com: set attributes before time in case we decide to
1103        support other filetime members later.  This also allows us to apply
1104        attributes before the security is changed, which may prevent this
1105        from succeeding otherwise.  Also, since most files don't have
1106        any interesting attributes, only change them if something other than
1107        FILE_ATTRIBUTE_ARCHIVE appears in the attributes.  This works well
1108        as an optimization because FILE_ATTRIBUTE_ARCHIVE gets applied to the
1109        file anyway, when it's created new. */
1110     if ((G.pInfo->file_attr & 0x7F) & ~FILE_ATTRIBUTE_ARCHIVE) {
1111         if (!SetFileAttributesA(Ansi_Fname, G.pInfo->file_attr & 0x7F))
1112             Info(slide, 1, ((char *)slide,
1113               "\nwarning (%d): could not set file attributes\n",
1114               (int)GetLastError()));
1115     }
1116 
1117 #ifdef NTSD_EAS
1118     /* set NTFS SD extra fields */
1119     if (G.extra_field &&    /* zipfile extra field may have extended attribs */
1120         FindSDExtraField(__G__ G.extra_field, G.lrec.extra_field_length,
1121                          &ebSDptr, &ebSDlen))
1122     {
1123         int err = SetSD(__G__ Ansi_Fname, G.pInfo->file_attr,
1124                         ebSDptr, ebSDlen);
1125 
1126         if (err == IZ_EF_TRUNC) {
1127             if (uO.qflag)
1128                 Info(slide, 1, ((char *)slide, "%-22s ",
1129                   FnFilter1(G.filename)));
1130             Info(slide, 1, ((char *)slide, LoadFarString(TruncNTSD),
1131               ebSDlen-(EB_NTSD_L_LEN+EB_CMPRHEADLEN), uO.qflag? "\n":""));
1132         }
1133     }
1134 #endif /* NTSD_EAS */
1135 
1136     /* skip restoring time stamps on user's request */
1137     if (uO.D_flag <= 1) {
1138         if ( hFile == INVALID_HANDLE_VALUE )
1139             Info(slide, 1, ((char *)slide,
1140               "\nCreateFile() error %d when trying set file time\n",
1141               (int)GetLastError()));
1142         else {
1143             if (gotTime) {
1144                 FILETIME *pModft = (gotTime & EB_UT_FL_MTIME) ? &Modft : NULL;
1145                 FILETIME *pAccft = (gotTime & EB_UT_FL_ATIME) ? &Accft : NULL;
1146                 FILETIME *pCreft = (gotTime & EB_UT_FL_CTIME) ? &Creft : NULL;
1147 
1148                 if (!SetFileTime(hFile, pCreft, pAccft, pModft))
1149                     Info(slide, 0, ((char *)slide,
1150                       "\nSetFileTime failed: %d\n", (int)GetLastError()));
1151             }
1152             CloseHandle(hFile);
1153         }
1154     }
1155 
1156     return;
1157 
1158 #undef Ansi_Fname
1159 
1160 } /* end function close_outfile() */
1161 
1162 
1163 
1164 
1165 #ifdef SET_DIR_ATTRIB
1166 
1167 int defer_dir_attribs(__G__ pd)
1168     __GDEF
1169     direntry **pd;
1170 {
1171     NTdirattr *d_entry;
1172 #ifdef NTSD_EAS
1173     uch *ebSDptr;
1174     unsigned ebSDlen;
1175 #endif
1176 
1177     /* Win9x does not support setting directory time stamps. */
1178     if (!IsWinNT()) {
1179         *pd = (direntry *)NULL;
1180         return PK_OK;
1181     }
1182 
1183 #ifdef NTSD_EAS
1184     /* set extended attributes from extra fields */
1185     if (G.extra_field &&  /* zipfile e.f. may have extended attribs */
1186         FindSDExtraField(__G__ G.extra_field, G.lrec.extra_field_length,
1187                          &ebSDptr, &ebSDlen)) {
1188         /* ebSDlen contains the payload size of the e.f. block, but
1189            we store it including the e.b. header. */
1190         ebSDlen += EB_HEADSIZE;
1191     } else {
1192         /* no NTSD e.f. block -> no space needed to allocate */
1193         ebSDlen = 0;
1194     }
1195 #endif /* NTSD_EAS */
1196 
1197     d_entry = (NTdirattr *)malloc(sizeof(NTdirattr)
1198 #ifdef NTSD_EAS
1199                                   + ebSDlen
1200 #endif
1201                                   + strlen(G.filename));
1202     *pd = (direntry *)d_entry;
1203     if (d_entry == (NTdirattr *)NULL) {
1204         return PK_MEM;
1205     }
1206 #ifdef NTSD_EAS
1207     if (ebSDlen > 0)
1208         memcpy(d_entry->buf, ebSDptr, ebSDlen);
1209     d_entry->SDlen = ebSDlen;
1210     d_entry->fn = d_entry->buf + ebSDlen;
1211 #else
1212     d_entry->fn = d_entry->buf;
1213 #endif
1214 
1215     strcpy(d_entry->fn, G.filename);
1216 
1217     d_entry->perms = G.pInfo->file_attr;
1218 
1219     d_entry->gotTime = (uO.D_flag <= 0
1220                         ? getNTfiletime(__G__ &(d_entry->Modft),
1221                                         &(d_entry->Accft), &(d_entry->Creft))
1222                         : 0);
1223     return PK_OK;
1224 } /* end function defer_dir_attribs() */
1225 
1226 
1227 int set_direc_attribs(__G__ d)
1228     __GDEF
1229     direntry *d;
1230 {
1231     int errval;
1232     HANDLE hFile = INVALID_HANDLE_VALUE;        /* File handle defined in NT */
1233 #ifdef __RSXNT__
1234     char *ansi_name;
1235 #endif
1236 
1237     /* Win9x does not support setting directory time stamps. */
1238     if (!IsWinNT())
1239         return PK_OK;
1240 
1241     errval = PK_OK;
1242 #ifdef __RSXNT__        /* RSXNT/EMX C rtl uses OEM charset */
1243     ansi_name = (char *)alloca(strlen(d->fn) + 1);
1244     INTERN_TO_ISO(d->fn, ansi_name);
1245 #   define Ansi_Dirname  ansi_name
1246 #else
1247 #   define Ansi_Dirname  d->fn
1248 #endif
1249 
1250     /* Skip restoring directory time stamps on user' request. */
1251     if (uO.D_flag <= 0) {
1252         /* Open a handle to the directory before processing extra fields;
1253            we do this in case new security on file prevents us from updating
1254            time stamps.
1255            Although the WIN32 documentation recommends to use GENERIC_WRITE
1256            access flag to create the handle for SetFileTime(), this is too
1257            demanding for directories with the "read-only" attribute bit set.
1258            So we use the more specific flag FILE_WRITE_ATTRIBUTES here to
1259            request the minimum required access rights. (This problem is a
1260            Windows bug that has been silently fixed in Windows XP SP2.) */
1261         hFile = CreateFileA(Ansi_Dirname, FILE_WRITE_ATTRIBUTES,
1262                             FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
1263                             OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
1264     }
1265 
1266 #ifdef NTSD_EAS
1267     if (NtAtt(d)->SDlen > 0) {
1268         int err;
1269 
1270         if (QCOND2) {
1271             Info(slide, 1, ((char *)slide, " set attrib: %-22s  ",
1272               FnFilter1(d->fn)));
1273         }
1274 
1275         /* set NTFS SD extra fields */
1276         err = SetSD(__G__ Ansi_Dirname, NtAtt(d)->perms,
1277                         NtAtt(d)->buf, NtAtt(d)->SDlen - EB_HEADSIZE);
1278         if (err == IZ_EF_TRUNC) {
1279             if (!QCOND2)
1280                 Info(slide, 1, ((char *)slide, "%-22s  ",
1281                   FnFilter1(d->fn)));
1282             Info(slide, 1, ((char *)slide, LoadFarString(TruncNTSD),
1283               NtAtt(d)->SDlen-(EB_NTSD_L_LEN+EB_CMPRHEADLEN), "\n"));
1284         } else if (QCOND2) {
1285             Info(slide, 0, ((char *)slide, "\n"));
1286         }
1287         if (errval < err)
1288             errval = err;
1289     }
1290 #endif /* NTSD_EAS */
1291 
1292     /* Skip restoring directory time stamps on user' request. */
1293     if (uO.D_flag <= 0) {
1294         if (hFile == INVALID_HANDLE_VALUE) {
1295             Info(slide, 1, ((char *)slide,
1296               "warning: CreateFile() error %d (set file times for %s)\n",
1297               (int)GetLastError(), FnFilter1(d->fn)));
1298             if (!errval)
1299                 errval = PK_WARN;
1300         } else {
1301             if (NtAtt(d)->gotTime) {
1302                 FILETIME *pModft = (NtAtt(d)->gotTime & EB_UT_FL_MTIME)
1303                                   ? &(NtAtt(d)->Modft) : NULL;
1304                 FILETIME *pAccft = (NtAtt(d)->gotTime & EB_UT_FL_ATIME)
1305                                   ? &(NtAtt(d)->Accft) : NULL;
1306                 FILETIME *pCreft = (NtAtt(d)->gotTime & EB_UT_FL_CTIME)
1307                                   ? &(NtAtt(d)->Creft) : NULL;
1308 
1309                 if (!SetFileTime(hFile, pCreft, pAccft, pModft)) {
1310                     Info(slide, 0, ((char *)slide,
1311                       "warning:  SetFileTime() for %s error %d\n",
1312                       FnFilter1(d->fn), (int)GetLastError()));
1313                     if (!errval)
1314                         errval = PK_WARN;
1315                 }
1316             }
1317             CloseHandle(hFile);
1318         }
1319     }
1320 
1321     return errval;
1322 } /* end function set_direc_attribs() */
1323 
1324 #endif /* SET_DIR_ATTRIB */
1325 
1326 
1327 
1328 
1329 #ifdef TIMESTAMP
1330 
1331 /*************************/
1332 /* Function stamp_file() */
1333 /*************************/
1334 
1335 int stamp_file(__GPRO__ ZCONST char *fname, time_t modtime)
1336 {
1337     FILETIME Modft;    /* File time type defined in NT, `last modified' time */
1338     HANDLE hFile;      /* File handle defined in NT    */
1339     int errstat = 0;   /* return status: 0 == "OK", -1 == "Failure" */
1340     int fs_uses_loctime = FStampIsLocTime(__G__ fname);
1341 #ifdef __RSXNT__        /* RSXNT/EMX C rtl uses OEM charset */
1342     char *ansi_name = (char *)alloca(strlen(fname) + 1);
1343 
1344     INTERN_TO_ISO(fname, ansi_name);
1345 #   define Ansi_Fname  ansi_name
1346 #else
1347 #   define Ansi_Fname  fname
1348 #endif
1349 
1350     /* open a handle to the file to prepare setting the mod-time stamp */
1351     hFile = CreateFileA(Ansi_Fname, GENERIC_WRITE, FILE_SHARE_WRITE, NULL,
1352          OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
1353     if ( hFile == INVALID_HANDLE_VALUE ) {
1354         errstat = -1;
1355     } else {
1356         /* convert time_t modtime into WIN32 native 64bit format */
1357         UTIME_2_IZFILETIME(modtime, &Modft)
1358         /* set Access and Modification times of the file to modtime */
1359         if (!SetFileTime(hFile, NULL, &Modft, &Modft)) {
1360             errstat = -1;
1361         }
1362         CloseHandle(hFile);
1363     }
1364 
1365     return errstat;
1366 
1367 #undef Ansi_Fname
1368 } /* end function stamp_file() */
1369 
1370 #endif /* TIMESTAMP */
1371 
1372 
1373 
1374 
1375 /***********************/
1376 /* Function isfloppy() */   /* more precisely, is it removable? */
1377 /***********************/
1378 
1379 static int isfloppy(int nDrive)   /* 1 == A:, 2 == B:, etc. */
1380 {
1381     char rootPathName[4];
1382 
1383     rootPathName[0] = (char)('A' + nDrive - 1);   /* build the root path */
1384     rootPathName[1] = ':';                        /*  name, e.g. "A:/" */
1385     rootPathName[2] = '/';
1386     rootPathName[3] = '\0';
1387 
1388     return (GetDriveTypeA(rootPathName) == DRIVE_REMOVABLE);
1389 
1390 } /* end function isfloppy() */
1391 
1392 
1393 
1394 
1395 /*****************************/
1396 /* Function NTQueryVolInfo() */
1397 /*****************************/
1398 
1399 /*
1400  * Note:  8.3 limits on filenames apply only to old-style FAT filesystems.
1401  *        More recent versions of Windows (Windows NT 3.5 / Windows 4.0)
1402  *        can support long filenames (LFN) on FAT filesystems.  Check the
1403  *        filesystem maximum component length field to detect LFN support.
1404  */
1405 
1406 static int NTQueryVolInfo(__GPRO__ const char *name)
1407 {
1408  /* static char lastRootPath[4] = ""; */
1409  /* static int lastVolOldFAT; */
1410  /* static int lastVolLocTim; */
1411     char *tmp0;
1412     char tmp1[MAX_PATH], tmp2[MAX_PATH];
1413     DWORD volSerNo, maxCompLen, fileSysFlags;
1414 #ifdef __RSXNT__        /* RSXNT/EMX C rtl uses OEM charset */
1415     char *ansi_name = (char *)alloca(strlen(name) + 1);
1416 
1417     INTERN_TO_ISO(name, ansi_name);
1418     name = ansi_name;
1419 #endif
1420 
1421     if ((!strncmp(name, "//", 2) || !strncmp(name, "\\\\", 2)) &&
1422         (name[2] != '\0' && name[2] != '/' && name[2] != '\\')) {
1423         /* GetFullPathname() and GetVolumeInformation() do not work
1424          * on UNC names. For now, we return "error".
1425          * **FIXME**: check if UNC name is mapped to a drive letter
1426          *            and use mapped drive for volume info query.
1427          */
1428         return FALSE;
1429     }
1430     if (isalpha((uch)name[0]) && (name[1] == ':'))
1431         tmp0 = (char *)name;
1432     else
1433     {
1434         if (!GetFullPathNameA(name, MAX_PATH, tmp1, &tmp0))
1435             return FALSE;
1436         tmp0 = &tmp1[0];
1437     }
1438     if (strncmp(G.lastRootPath, tmp0, 2) != 0) {
1439         /* For speed, we skip repeated queries for the same device */
1440         strncpy(G.lastRootPath, tmp0, 2);   /* Build the root path name, */
1441         G.lastRootPath[2] = '/';            /* e.g. "A:/"                */
1442         G.lastRootPath[3] = '\0';
1443 
1444         if (!GetVolumeInformationA((LPCSTR)G.lastRootPath,
1445               (LPSTR)tmp1, (DWORD)MAX_PATH,
1446               &volSerNo, &maxCompLen, &fileSysFlags,
1447               (LPSTR)tmp2, (DWORD)MAX_PATH)) {
1448             G.lastRootPath[0] = '\0';
1449             return FALSE;
1450         }
1451 
1452         /*  LFNs are available if the component length is > 12 */
1453         G.lastVolOldFAT = (maxCompLen <= 12);
1454 /*      G.lastVolOldFAT = !strncmp(strupr(tmp2), "FAT", 3);   old version */
1455 
1456         /* Volumes in (V)FAT and (OS/2) HPFS format store file timestamps in
1457          * local time!
1458          */
1459         G.lastVolLocTim = !strncmp(strupr(tmp2), "VFAT", 4) ||
1460                           !strncmp(tmp2, "HPFS", 4) ||
1461                           !strncmp(tmp2, "FAT", 3);
1462     }
1463 
1464     return TRUE;
1465 
1466 } /* end function NTQueryVolInfo() */
1467 
1468 
1469 
1470 
1471 /*****************************/
1472 /* Function IsVolumeOldFAT() */
1473 /*****************************/
1474 
1475 static int IsVolumeOldFAT(__GPRO__ const char *name)
1476 {
1477     return (NTQueryVolInfo(__G__ name) ? G.lastVolOldFAT : FALSE);
1478 }
1479 
1480 
1481 
1482 
1483 #ifndef SFX
1484 
1485 /************************/
1486 /*  Function do_wild()  */   /* identical to OS/2 version */
1487 /************************/
1488 
1489 char *do_wild(__G__ wildspec)
1490     __GDEF
1491     ZCONST char *wildspec;  /* only used first time on a given dir */
1492 {
1493 /* these statics are now declared in SYSTEM_SPECIFIC_GLOBALS in w32cfg.h:
1494     static zDIR *wild_dir = NULL;
1495     static ZCONST char *wildname;
1496     static char *dirname, matchname[FILNAMSIZ];
1497     static int notfirstcall=FALSE, have_dirname, dirnamelen;
1498 */
1499     char *fnamestart;
1500     struct zdirent *file;
1501 
1502     /* Even when we're just returning wildspec, we *always* do so in
1503      * matchname[]--calling routine is allowed to append four characters
1504      * to the returned string, and wildspec may be a pointer to argv[].
1505      */
1506     if (!G.notfirstcall) {  /* first call:  must initialize everything */
1507         G.notfirstcall = TRUE;
1508 
1509         if (!iswild(wildspec)) {
1510             strncpy(G.matchname, wildspec, FILNAMSIZ);
1511             G.matchname[FILNAMSIZ-1] = '\0';
1512             G.have_dirname = FALSE;
1513             G.wild_dir = NULL;
1514             return G.matchname;
1515         }
1516 
1517         /* break the wildspec into a directory part and a wildcard filename */
1518         if ((G.wildname = MBSRCHR(wildspec, '/')) == (ZCONST char *)NULL &&
1519             (G.wildname = MBSRCHR(wildspec, ':')) == (ZCONST char *)NULL) {
1520             G.dirname = ".";
1521             G.dirnamelen = 1;
1522             G.have_dirname = FALSE;
1523             G.wildname = wildspec;
1524         } else {
1525             ++G.wildname;     /* point at character after '/' or ':' */
1526             G.dirnamelen = G.wildname - wildspec;
1527             if ((G.dirname = (char *)malloc(G.dirnamelen+1)) == NULL) {
1528                 Info(slide, 1, ((char *)slide,
1529                   "warning:  cannot allocate wildcard buffers\n"));
1530                 strncpy(G.matchname, wildspec, FILNAMSIZ);
1531                 G.matchname[FILNAMSIZ-1] = '\0';
1532                 return G.matchname; /* but maybe filespec was not a wildcard */
1533             }
1534             strncpy(G.dirname, wildspec, G.dirnamelen);
1535             G.dirname[G.dirnamelen] = '\0';   /* terminate for strcpy below */
1536             G.have_dirname = TRUE;
1537         }
1538         Trace((stderr, "do_wild:  dirname = [%s]\n", FnFilter1(G.dirname)));
1539 
1540         if ((G.wild_dir = (zvoid *)Opendir(G.dirname)) != NULL) {
1541             if (G.have_dirname) {
1542                 strcpy(G.matchname, G.dirname);
1543                 fnamestart = G.matchname + G.dirnamelen;
1544             } else
1545                 fnamestart = G.matchname;
1546             while ((file = Readdir((zDIR *)G.wild_dir)) != NULL) {
1547                 Trace((stderr, "do_wild:  Readdir returns %s\n",
1548                   FnFilter1(file->d_name)));
1549                 strcpy(fnamestart, file->d_name);
1550                 if (MBSRCHR(fnamestart, '.') == (char *)NULL)
1551                     strcat(fnamestart, ".");
1552                 if (match(fnamestart, G.wildname, TRUE WISEP) &&
1553                     /* skip "." and ".." directory entries */
1554                     strcmp(fnamestart, ".") && strcmp(fnamestart, "..")) {
1555                     Trace((stderr, "do_wild:  match() succeeds\n"));
1556                     /* remove trailing dot */
1557                     fnamestart = plastchar(fnamestart, strlen(fnamestart));
1558                     if (*fnamestart == '.')
1559                         *fnamestart = '\0';
1560                     return G.matchname;
1561                 }
1562             }
1563             /* if we get to here directory is exhausted, so close it */
1564             Closedir((zDIR *)G.wild_dir);
1565             G.wild_dir = NULL;
1566         }
1567         Trace((stderr, "do_wild:  Opendir(%s) returns NULL\n",
1568           FnFilter1(G.dirname)));
1569 
1570         /* return the raw wildspec in case that works (e.g., directory not
1571          * searchable, but filespec was not wild and file is readable) */
1572         strncpy(G.matchname, wildspec, FILNAMSIZ);
1573         G.matchname[FILNAMSIZ-1] = '\0';
1574         return G.matchname;
1575     }
1576 
1577     /* last time through, might have failed opendir but returned raw wildspec */
1578     if (G.wild_dir == NULL) {
1579         G.notfirstcall = FALSE;    /* reset for new wildspec */
1580         if (G.have_dirname)
1581             free(G.dirname);
1582         return (char *)NULL;
1583     }
1584 
1585     /* If we've gotten this far, we've read and matched at least one entry
1586      * successfully (in a previous call), so dirname has been copied into
1587      * matchname already.
1588      */
1589     if (G.have_dirname) {
1590         /* strcpy(G.matchname, G.dirname); */
1591         fnamestart = G.matchname + G.dirnamelen;
1592     } else
1593         fnamestart = G.matchname;
1594     while ((file = Readdir((zDIR *)G.wild_dir)) != NULL) {
1595         Trace((stderr, "do_wild:  readdir returns %s\n",
1596           FnFilter1(file->d_name)));
1597         strcpy(fnamestart, file->d_name);
1598         if (MBSRCHR(fnamestart, '.') == (char *)NULL)
1599             strcat(fnamestart, ".");
1600         if (match(fnamestart, G.wildname, TRUE WISEP)) {
1601             Trace((stderr, "do_wild:  match() succeeds\n"));
1602             /* remove trailing dot */
1603             fnamestart = plastchar(fnamestart, strlen(fnamestart));
1604             if (*fnamestart == '.')
1605                 *fnamestart = '\0';
1606             return G.matchname;
1607         }
1608     }
1609 
1610     Closedir((zDIR *)G.wild_dir);  /* at least one entry read; nothing left */
1611     G.wild_dir = NULL;
1612     G.notfirstcall = FALSE;        /* reset for new wildspec */
1613     if (G.have_dirname)
1614         free(G.dirname);
1615     return (char *)NULL;
1616 
1617 } /* end function do_wild() */
1618 
1619 #endif /* !SFX */
1620 
1621 
1622 
1623 /**********************/
1624 /* Function mapattr() */
1625 /**********************/
1626 
1627 /* Identical to MS-DOS, OS/2 versions.  However, NT has a lot of extra
1628  * permission stuff, so this function should probably be extended in the
1629  * future. */
1630 
1631 int mapattr(__G)
1632     __GDEF
1633 {
1634     /* set archive bit for file entries (file is not backed up): */
1635     G.pInfo->file_attr = ((unsigned)G.crec.external_file_attributes |
1636       (G.crec.external_file_attributes & FILE_ATTRIBUTE_DIRECTORY ?
1637        0 : FILE_ATTRIBUTE_ARCHIVE)) & 0xff;
1638     return 0;
1639 
1640 } /* end function mapattr() */
1641 
1642 
1643 
1644 
1645 /************************/
1646 /*  Function mapname()  */
1647 /************************/
1648 
1649 int mapname(__G__ renamed)
1650     __GDEF
1651     int renamed;
1652 /*
1653  * returns:
1654  *  MPN_OK          - no problem detected
1655  *  MPN_INF_TRUNC   - caution (truncated filename)
1656  *  MPN_INF_SKIP    - info "skip entry" (dir doesn't exist)
1657  *  MPN_ERR_SKIP    - error -> skip entry
1658  *  MPN_ERR_TOOLONG - error -> path is too long
1659  *  MPN_NOMEM       - error (memory allocation failed) -> skip entry
1660  *  [also MPN_VOL_LABEL, MPN_CREATED_DIR]
1661  */
1662 {
1663     char pathcomp[FILNAMSIZ];   /* path-component buffer */
1664     char *pp, *cp=NULL;         /* character pointers */
1665     char *lastsemi = NULL;      /* pointer to last semi-colon in pathcomp */
1666 #ifdef ACORN_FTYPE_NFS
1667     char *lastcomma=(char *)NULL;  /* pointer to last comma in pathcomp */
1668     RO_extra_block *ef_spark;      /* pointer Acorn FTYPE ef block */
1669 #endif
1670     int killed_ddot = FALSE;    /* is set when skipping "../" pathcomp */
1671     int error;
1672     register unsigned workch;   /* hold the character being tested */
1673 
1674 
1675 /*---------------------------------------------------------------------------
1676     Initialize various pointers and counters and stuff.
1677   ---------------------------------------------------------------------------*/
1678 
1679     /* can create path as long as not just freshening, or if user told us */
1680     G.create_dirs = (!uO.fflag || renamed);
1681 
1682     G.created_dir = FALSE;      /* not yet */
1683     G.renamed_fullpath = FALSE;
1684     G.fnlen = strlen(G.filename);
1685 
1686     if (renamed) {
1687         cp = G.filename;    /* point to beginning of renamed name... */
1688         if (*cp) do {
1689             if (*cp == '\\')    /* convert backslashes to forward */
1690                 *cp = '/';
1691         } while (*PREINCSTR(cp));
1692         cp = G.filename;
1693         /* use temporary rootpath if user gave full pathname */
1694         if (G.filename[0] == '/') {
1695             G.renamed_fullpath = TRUE;
1696             pathcomp[0] = '/';  /* copy the '/' and terminate */
1697             pathcomp[1] = '\0';
1698             ++cp;
1699         } else if (isalpha((uch)G.filename[0]) && G.filename[1] == ':') {
1700             G.renamed_fullpath = TRUE;
1701             pp = pathcomp;
1702             *pp++ = *cp++;      /* copy the "d:" (+ '/', possibly) */
1703             *pp++ = *cp++;
1704             if (*cp == '/')
1705                 *pp++ = *cp++;  /* otherwise add "./"? */
1706             *pp = '\0';
1707         }
1708     }
1709 
1710     /* pathcomp is ignored unless renamed_fullpath is TRUE: */
1711     if ((error = checkdir(__G__ pathcomp, INIT)) != 0)    /* init path buffer */
1712         return error;           /* ...unless no mem or vol label on hard disk */
1713 
1714     *pathcomp = '\0';           /* initialize translation buffer */
1715     pp = pathcomp;              /* point to translation buffer */
1716     if (!renamed) {             /* cp already set if renamed */
1717         if (uO.jflag)           /* junking directories */
1718             cp = (char *)MBSRCHR(G.filename, '/');
1719         if (cp == NULL)         /* no '/' or not junking dirs */
1720             cp = G.filename;    /* point to internal zipfile-member pathname */
1721         else
1722             ++cp;               /* point to start of last component of path */
1723     }
1724 
1725 /*---------------------------------------------------------------------------
1726     Begin main loop through characters in filename.
1727   ---------------------------------------------------------------------------*/
1728 
1729     for (; (workch = (uch)*cp) != 0; INCSTR(cp)) {
1730 
1731         switch (workch) {
1732             case '/':             /* can assume -j flag not given */
1733                 *pp = '\0';
1734                 maskDOSdevice(__G__ pathcomp);
1735                 if (strcmp(pathcomp, ".") == 0) {
1736                     /* don't bother appending "./" to the path */
1737                     *pathcomp = '\0';
1738                 } else if (!uO.ddotflag && strcmp(pathcomp, "..") == 0) {
1739                     /* "../" dir traversal detected, skip over it */
1740                     *pathcomp = '\0';
1741                     killed_ddot = TRUE;     /* set "show message" flag */
1742                 }
1743                 /* when path component is not empty, append it now */
1744                 if (*pathcomp != '\0' &&
1745                     ((error = checkdir(__G__ pathcomp, APPEND_DIR))
1746                      & MPN_MASK) > MPN_INF_TRUNC)
1747                     return error;
1748                 pp = pathcomp;    /* reset conversion buffer for next piece */
1749                 lastsemi = (char *)NULL; /* leave direct. semi-colons alone */
1750                 break;
1751 
1752             case ':':             /* drive spec not stored, so no colon allowed */
1753             case '\\':            /* '\\' may come as normal filename char (not */
1754             case '<':             /*  dir sep char!) from unix-like file system */
1755             case '>':             /* no redirection symbols allowed either */
1756             case '|':             /* no pipe signs allowed */
1757             case '"':             /* no double quotes allowed */
1758             case '?':             /* no wildcards allowed */
1759             case '*':
1760                 *pp++ = '_';      /* these rules apply equally to FAT and NTFS */
1761                 break;
1762             case ';':             /* start of VMS version? */
1763                 lastsemi = pp;    /* remove VMS version later... */
1764                 *pp++ = ';';      /*  but keep semicolon for now */
1765                 break;
1766 
1767 #ifdef ACORN_FTYPE_NFS
1768             case ',':             /* NFS filetype extension */
1769                 lastcomma = pp;
1770                 *pp++ = ',';      /* keep for now; may need to remove */
1771                 break;            /*  later, if requested */
1772 #endif
1773 
1774             case ' ':             /* keep spaces unless specifically */
1775                 /* NT cannot create filenames with spaces on FAT volumes */
1776                 if (uO.sflag || IsVolumeOldFAT(__G__ G.filename))
1777                     *pp++ = '_';
1778                 else
1779                     *pp++ = ' ';
1780                 break;
1781 
1782             default:
1783                 /* allow European characters in filenames: */
1784                 if (isprint(workch) || workch >= 127)
1785 #ifdef _MBCS
1786                 {
1787                     memcpy(pp, cp, CLEN(cp));
1788                     INCSTR(pp);
1789                 }
1790 #else
1791                     *pp++ = (char)workch;
1792 #endif
1793         } /* end switch */
1794 
1795     } /* end while loop */
1796 
1797     /* Show warning when stripping insecure "parent dir" path components */
1798     if (killed_ddot && QCOND2) {
1799         Info(slide, 0, ((char *)slide,
1800           "warning:  skipped \"../\" path component(s) in %s\n",
1801           FnFilter1(G.filename)));
1802         if (!(error & ~MPN_MASK))
1803             error = (error & MPN_MASK) | PK_WARN;
1804     }
1805 
1806 /*---------------------------------------------------------------------------
1807     Report if directory was created (and no file to create:  filename ended
1808     in '/'), check name to be sure it exists, and combine path and name be-
1809     fore exiting.
1810   ---------------------------------------------------------------------------*/
1811 
1812     if (lastchar(G.filename, G.fnlen) == '/') {
1813 #ifdef __RSXNT__        /* RSXNT/EMX C rtl uses OEM charset */
1814         char *ansi_name = (char *)alloca(strlen(G.filename) + 1);
1815 
1816         INTERN_TO_ISO(G.filename, ansi_name);
1817 #       define Ansi_Fname  ansi_name
1818 #else
1819 #       define Ansi_Fname  G.filename
1820 #endif
1821         checkdir(__G__ G.filename, GETPATH);
1822         if (G.created_dir) {
1823             if (QCOND2) {
1824                 Info(slide, 0, ((char *)slide, "   creating: %-22s\n",
1825                   FnFilter1(G.filename)));
1826             }
1827 
1828             /* set file attributes:
1829                The default for newly created directories is "DIR attribute
1830                flags set", so there is no need to change attributes unless
1831                one of the DOS style attribute flags is set. The readonly
1832                attribute need not be masked, since it does not prevent
1833                modifications in the new directory. */
1834             if(G.pInfo->file_attr & (0x7F & ~FILE_ATTRIBUTE_DIRECTORY)) {
1835                 if (!SetFileAttributesA(Ansi_Fname, G.pInfo->file_attr & 0x7F))
1836                     Info(slide, 1, ((char *)slide,
1837                       "\nwarning (%d): could not set file attributes for %s\n",
1838                       (int)GetLastError(), FnFilter1(G.filename)));
1839             }
1840 
1841             /* set dir time (note trailing '/') */
1842             return (error & ~MPN_MASK) | MPN_CREATED_DIR;
1843         } else if (IS_OVERWRT_ALL) {
1844             /* overwrite attributes of existing directory on user's request */
1845 
1846             /* set file attributes: */
1847             if(G.pInfo->file_attr & (0x7F & ~FILE_ATTRIBUTE_DIRECTORY)) {
1848                 if (!SetFileAttributesA(Ansi_Fname, G.pInfo->file_attr & 0x7F))
1849                     Info(slide, 1, ((char *)slide,
1850                       "\nwarning (%d): could not set file attributes for %s\n",
1851                       (int)GetLastError(), FnFilter1(G.filename)));
1852             }
1853         }
1854         /* dir existed already; don't look for data to extract */
1855         return (error & ~MPN_MASK) | MPN_INF_SKIP;
1856     }
1857 
1858     *pp = '\0';                   /* done with pathcomp:  terminate it */
1859 
1860     /* if not saving them, remove VMS version numbers (appended "###") */
1861     if (!uO.V_flag && lastsemi) {
1862         pp = lastsemi + 1;        /* semi-colon was kept:  expect #'s after */
1863         while (isdigit((uch)(*pp)))
1864             ++pp;
1865         if (*pp == '\0')          /* only digits between ';' and end:  nuke */
1866             *lastsemi = '\0';
1867     }
1868 
1869 #ifdef ACORN_FTYPE_NFS
1870     /* translate Acorn filetype information if asked to do so */
1871     if (uO.acorn_nfs_ext &&
1872         (ef_spark = (RO_extra_block *)
1873                     getRISCOSexfield(G.extra_field, G.lrec.extra_field_length))
1874         != (RO_extra_block *)NULL)
1875     {
1876         /* file *must* have a RISC OS extra field */
1877         long ft = (long)makelong(ef_spark->loadaddr);
1878         /*32-bit*/
1879         if (lastcomma) {
1880             pp = lastcomma + 1;
1881             while (isxdigit((uch)(*pp))) ++pp;
1882             if (pp == lastcomma+4 && *pp == '\0') *lastcomma='\0'; /* nuke */
1883         }
1884         if ((ft & 1<<31)==0) ft=0x000FFD00;
1885         sprintf(pathcomp+strlen(pathcomp), ",%03x", (int)(ft>>8) & 0xFFF);
1886     }
1887 #endif /* ACORN_FTYPE_NFS */
1888 
1889     maskDOSdevice(__G__ pathcomp);
1890 
1891     if (*pathcomp == '\0') {
1892         Info(slide, 1, ((char *)slide, "mapname:  conversion of %s failed\n",
1893           FnFilter1(G.filename)));
1894         return (error & ~MPN_MASK) | MPN_ERR_SKIP;
1895     }
1896 
1897     checkdir(__G__ pathcomp, APPEND_NAME);  /* returns 1 if truncated: care? */
1898     checkdir(__G__ G.filename, GETPATH);
1899 
1900     if (G.pInfo->vollabel) {    /* set the volume label now */
1901         char drive[4];
1902 #ifdef __RSXNT__        /* RSXNT/EMX C rtl uses OEM charset */
1903         char *ansi_name = (char *)alloca(strlen(G.filename) + 1);
1904         INTERN_TO_ISO(G.filename, ansi_name);
1905 #       define Ansi_Fname  ansi_name
1906 #else
1907 #       define Ansi_Fname  G.filename
1908 #endif
1909 
1910         /* Build a drive string, e.g. "b:" */
1911         drive[0] = (char)('a' + G.nLabelDrive - 1);
1912         strcpy(drive + 1, ":\\");
1913         if (QCOND2)
1914             Info(slide, 0, ((char *)slide, "labelling %s %-22s\n", drive,
1915               FnFilter1(G.filename)));
1916         if (!SetVolumeLabelA(drive, Ansi_Fname)) {
1917             Info(slide, 1, ((char *)slide,
1918               "mapname:  error setting volume label\n"));
1919             return (error & ~MPN_MASK) | MPN_ERR_SKIP;
1920         }
1921         /* success:  skip the "extraction" quietly */
1922         return (error & ~MPN_MASK) | MPN_INF_SKIP;
1923 #undef Ansi_Fname
1924     }
1925 
1926     Trace((stderr, "mapname returns with filename = [%s] (error = %d)\n\n",
1927       FnFilter1(G.filename), error));
1928     return error;
1929 
1930 } /* end function mapname() */
1931 
1932 
1933 
1934 
1935 /****************************/
1936 /* Function maskDOSdevice() */
1937 /****************************/
1938 
1939 static void maskDOSdevice(__G__ pathcomp)
1940     __GDEF
1941     char *pathcomp;
1942 {
1943 /*---------------------------------------------------------------------------
1944     Put an underscore in front of the file name if the file name is a
1945     DOS/WINDOWS device name like CON.*, AUX.*, PRN.*, etc. Trying to
1946     extract such a file would fail at best and wedge us at worst.
1947   ---------------------------------------------------------------------------*/
1948 #if !defined(S_IFCHR) && defined(_S_IFCHR)
1949 #  define S_IFCHR _S_IFCHR
1950 #endif
1951 #if !defined(S_ISCHR)
1952 # if defined(_S_ISCHR)
1953 #  define S_ISCHR(m) _S_ISCHR(m)
1954 # elif defined(S_IFCHR)
1955 #  define S_ISCHR(m) ((m) & S_IFCHR)
1956 # endif
1957 #endif
1958 
1959 #ifdef DEBUG
1960     if (zstat(pathcomp, &G.statbuf) == 0) {
1961         Trace((stderr,
1962                "maskDOSdevice() stat(\"%s\", buf) st_mode result: %X, %o\n",
1963                FnFilter1(pathcomp), G.statbuf.st_mode, G.statbuf.st_mode));
1964     } else {
1965         Trace((stderr, "maskDOSdevice() stat(\"%s\", buf) failed\n",
1966                FnFilter1(pathcomp)));
1967     }
1968 #endif
1969     if (zstat(pathcomp, &G.statbuf) == 0 && S_ISCHR(G.statbuf.st_mode)) {
1970         extent i;
1971 
1972         /* pathcomp contains a name of a DOS character device (builtin or
1973          * installed device driver).
1974          * Prepend a '_' to allow creation of the item in the file system.
1975          */
1976         for (i = strlen(pathcomp) + 1; i > 0; --i)
1977             pathcomp[i] = pathcomp[i - 1];
1978         pathcomp[0] = '_';
1979     }
1980 } /* end function maskDOSdevice() */
1981 
1982 
1983 
1984 
1985 
1986 /**********************/
1987 /* Function map2fat() */        /* Not quite identical to OS/2 version */
1988 /**********************/
1989 
1990 static void map2fat(pathcomp, pEndFAT)
1991     char *pathcomp, **pEndFAT;
1992 {
1993     char *ppc = pathcomp;       /* variable pointer to pathcomp */
1994     char *pEnd = *pEndFAT;      /* variable pointer to buildpathFAT */
1995     char *pBegin = *pEndFAT;    /* constant pointer to start of this comp. */
1996     char *last_dot = NULL;      /* last dot not converted to underscore */
1997     register unsigned workch;   /* hold the character being tested */
1998 
1999 
2000     /* Only need check those characters which are legal in NTFS but not
2001      * in FAT:  to get here, must already have passed through mapname.
2002      * Also must truncate path component to ensure 8.3 compliance.
2003      */
2004     while ((workch = (uch)*ppc++) != 0) {
2005         switch (workch) {
2006             case '[':
2007             case ']':
2008             case '+':
2009             case ',':
2010             case ';':
2011             case '=':
2012                 *pEnd++ = '_';      /* convert brackets to underscores */
2013                 break;
2014 
2015             case '.':
2016                 if (pEnd == *pEndFAT) {   /* nothing appended yet... */
2017                     if (*ppc == '\0')     /* don't bother appending a */
2018                         break;            /*  "./" component to the path */
2019                     else if (*ppc == '.' && ppc[1] == '\0') {   /* "../" */
2020                         *pEnd++ = '.';    /*  add first dot, */
2021                         *pEnd++ = '.';    /*  add second dot, and */
2022                         ++ppc;            /*  skip over to pathcomp's end */
2023                     } else {              /* FAT doesn't allow null filename */
2024                         *pEnd++ = '_';    /*  bodies, so map .exrc -> _exrc */
2025                     }                     /*  (_.exr would keep max 3 chars) */
2026                 } else {                  /* found dot within path component */
2027                     last_dot = pEnd;      /*  point at last dot so far... */
2028                     *pEnd++ = '_';        /*  convert to underscore for now */
2029                 }
2030                 break;
2031 
2032             default:
2033                 *pEnd++ = (char)workch;
2034 
2035         } /* end switch */
2036     } /* end while loop */
2037 
2038     *pEnd = '\0';                 /* terminate buildpathFAT */
2039 
2040     /* NOTE:  keep in mind that pEnd points to the end of the path
2041      * component, and *pEndFAT still points to the *beginning* of it...
2042      * Also note that the algorithm does not try to get too fancy:
2043      * if there are no dots already, the name either gets truncated
2044      * at 8 characters or the last underscore is converted to a dot
2045      * (only if more characters are saved that way).  In no case is
2046      * a dot inserted between existing characters.
2047      */
2048     if (last_dot == NULL) {       /* no dots:  check for underscores... */
2049         char *plu = MBSRCHR(pBegin, '_');   /* pointer to last underscore */
2050 
2051         if ((plu != NULL) &&      /* found underscore: convert to dot? */
2052             (MIN(plu - pBegin, 8) + MIN(pEnd - plu - 1, 3) > 8)) {
2053             last_dot = plu;       /* be lazy:  drop through to next if-blk */
2054         } else if ((pEnd - *pEndFAT) > 8) {
2055             /* no underscore; or converting underscore to dot would save less
2056                chars than leaving everything in the basename */
2057             *pEndFAT += 8;        /* truncate at 8 chars */
2058             **pEndFAT = '\0';
2059         } else
2060             *pEndFAT = pEnd;      /* whole thing fits into 8 chars or less */
2061     }
2062 
2063     if (last_dot != NULL) {       /* one dot is OK: */
2064         *last_dot = '.';          /* put it back in */
2065 
2066         if ((last_dot - pBegin) > 8) {
2067             char *p, *q;
2068             int i;
2069 
2070             p = last_dot;
2071             q = last_dot = pBegin + 8;
2072             for (i = 0;  (i < 4) && *p;  ++i)  /* too many chars in basename: */
2073                 *q++ = *p++;                   /*  shift .ext left and trun- */
2074             *q = '\0';                         /*  cate/terminate it */
2075             *pEndFAT = q;
2076         } else if ((pEnd - last_dot) > 4) {    /* too many chars in extension */
2077             *pEndFAT = last_dot + 4;
2078             **pEndFAT = '\0';
2079         } else
2080             *pEndFAT = pEnd;   /* filename is fine; point at terminating zero */
2081 
2082         if ((last_dot - pBegin) > 0 && last_dot[-1] == ' ')
2083             last_dot[-1] = '_';                /* NO blank in front of '.'! */
2084     }
2085 } /* end function map2fat() */
2086 
2087 
2088 
2089 
2090 /***********************/       /* Borrowed from os2.c for UnZip 5.1.        */
2091 /* Function checkdir() */       /* Difference: no EA stuff                   */
2092 /***********************/       /*             HPFS stuff works on NTFS too  */
2093 
2094 int checkdir(__G__ pathcomp, flag)
2095     __GDEF
2096     char *pathcomp;
2097     int flag;
2098 /*
2099  * returns:
2100  *  MPN_OK          - no problem detected
2101  *  MPN_INF_TRUNC   - (on APPEND_NAME) truncated filename
2102  *  MPN_INF_SKIP    - path doesn't exist, not allowed to create
2103  *  MPN_ERR_SKIP    - path doesn't exist, tried to create and failed; or path
2104  *                    exists and is not a directory, but is supposed to be
2105  *  MPN_ERR_TOOLONG - path is too long
2106  *  MPN_NOMEM       - can't allocate memory for filename buffers
2107  */
2108 {
2109  /* static int rootlen = 0;     */   /* length of rootpath */
2110  /* static char *rootpath;      */   /* user's "extract-to" directory */
2111  /* static char *buildpathHPFS; */   /* full path (so far) to extracted file, */
2112  /* static char *buildpathFAT;  */   /*  both HPFS/EA (main) and FAT versions */
2113  /* static char *endHPFS;       */   /* corresponding pointers to end of */
2114  /* static char *endFAT;        */   /*  buildpath ('\0') */
2115 
2116 #   define FN_MASK   7
2117 #   define FUNCTION  (flag & FN_MASK)
2118 
2119 
2120 
2121 /*---------------------------------------------------------------------------
2122     APPEND_DIR:  append the path component to the path being built and check
2123     for its existence.  If doesn't exist and we are creating directories, do
2124     so for this one; else signal success or error as appropriate.
2125   ---------------------------------------------------------------------------*/
2126 
2127     if (FUNCTION == APPEND_DIR) {
2128         char *p = pathcomp;
2129         int too_long = FALSE;
2130 
2131         Trace((stderr, "appending dir segment [%s]\n", FnFilter1(pathcomp)));
2132         while ((*G.endHPFS = *p++) != '\0')     /* copy to HPFS filename */
2133             ++G.endHPFS;
2134         if (!IsVolumeOldFAT(__G__ G.buildpathHPFS)) {
2135             p = pathcomp;
2136             while ((*G.endFAT = *p++) != '\0')  /* copy to FAT filename, too */
2137                 ++G.endFAT;
2138         } else
2139             map2fat(pathcomp, &G.endFAT);   /* map into FAT fn, update endFAT */
2140 
2141         /* GRR:  could do better check, see if overrunning buffer as we go:
2142          * check endHPFS-buildpathHPFS after each append, set warning variable
2143          * if within 20 of FILNAMSIZ; then if var set, do careful check when
2144          * appending.  Clear variable when begin new path. */
2145 
2146         /* next check:  need to append '/', at least one-char name, '\0' */
2147         if ((G.endHPFS-G.buildpathHPFS) > FILNAMSIZ-3)
2148             too_long = TRUE;                    /* check if extracting dir? */
2149 #ifdef FIX_STAT_BUG
2150         /* Borland C++ 5.0 does not handle a call to stat() well if the
2151          * directory does not exist (it tends to crash in strange places.)
2152          * This is apparently a problem only when compiling for GUI rather
2153          * than console. The code below attempts to work around this problem.
2154          */
2155         if (access(G.buildpathFAT, 0) != 0) {
2156             if (!G.create_dirs) { /* told not to create (freshening) */
2157                 free(G.buildpathHPFS);
2158                 free(G.buildpathFAT);
2159                 /* path doesn't exist:  nothing to do */
2160                 return MPN_INF_SKIP;
2161             }
2162             if (too_long) {   /* GRR:  should allow FAT extraction w/o EAs */
2163                 Info(slide, 1, ((char *)slide,
2164                   "checkdir error:  path too long: %s\n",
2165                   FnFilter1(G.buildpathHPFS)));
2166                 free(G.buildpathHPFS);
2167                 free(G.buildpathFAT);
2168                 /* no room for filenames:  fatal */
2169                 return MPN_ERR_TOOLONG;
2170             }
2171             if (MKDIR(G.buildpathFAT, 0777) == -1) { /* create the directory */
2172                 Info(slide, 1, ((char *)slide,
2173                   "checkdir error:  cannot create %s\n\
2174                  %s\n\
2175                  unable to process %s.\n",
2176                   FnFilter2(G.buildpathFAT),
2177                   strerror(errno),
2178                   FnFilter1(G.filename)));
2179                 free(G.buildpathHPFS);
2180                 free(G.buildpathFAT);
2181                 /* path didn't exist, tried to create, failed */
2182                 return MPN_ERR_SKIP;
2183             }
2184             G.created_dir = TRUE;
2185         }
2186 #endif /* FIX_STAT_BUG */
2187         if (SSTAT(G.buildpathFAT, &G.statbuf))   /* path doesn't exist */
2188         {
2189             if (!G.create_dirs) { /* told not to create (freshening) */
2190                 free(G.buildpathHPFS);
2191                 free(G.buildpathFAT);
2192                 /* path doesn't exist:  nothing to do */
2193                 return MPN_INF_SKIP;
2194             }
2195             if (too_long) {   /* GRR:  should allow FAT extraction w/o EAs */
2196                 Info(slide, 1, ((char *)slide,
2197                   "checkdir error:  path too long: %s\n",
2198                   FnFilter1(G.buildpathHPFS)));
2199                 free(G.buildpathHPFS);
2200                 free(G.buildpathFAT);
2201                 /* no room for filenames:  fatal */
2202                 return MPN_ERR_TOOLONG;
2203             }
2204             if (MKDIR(G.buildpathFAT, 0777) == -1) { /* create the directory */
2205                 Info(slide, 1, ((char *)slide,
2206                   "checkdir error:  cannot create %s\n\
2207                  %s\n\
2208                  unable to process %s.\n",
2209                   FnFilter2(G.buildpathFAT),
2210                   strerror(errno),
2211                   FnFilter1(G.filename)));
2212                 free(G.buildpathHPFS);
2213                 free(G.buildpathFAT);
2214                 /* path didn't exist, tried to create, failed */
2215                 return MPN_ERR_SKIP;
2216             }
2217             G.created_dir = TRUE;
2218         } else if (!S_ISDIR(G.statbuf.st_mode)) {
2219             Info(slide, 1, ((char *)slide,
2220               "checkdir error:  %s exists but is not directory\n\
2221                  unable to process %s.\n",
2222               FnFilter2(G.buildpathFAT), FnFilter1(G.filename)));
2223             free(G.buildpathHPFS);
2224             free(G.buildpathFAT);
2225             /* path existed but wasn't dir */
2226             return MPN_ERR_SKIP;
2227         }
2228         if (too_long) {
2229             Info(slide, 1, ((char *)slide,
2230               "checkdir error:  path too long: %s\n",
2231               FnFilter1(G.buildpathHPFS)));
2232             free(G.buildpathHPFS);
2233             free(G.buildpathFAT);
2234             /* no room for filenames:  fatal */
2235             return MPN_ERR_TOOLONG;
2236         }
2237         *G.endHPFS++ = '/';
2238         *G.endFAT++ = '/';
2239         *G.endHPFS = *G.endFAT = '\0';
2240         Trace((stderr, "buildpathHPFS now = [%s]\nbuildpathFAT now =  [%s]\n",
2241           FnFilter1(G.buildpathHPFS), FnFilter2(G.buildpathFAT)));
2242         return MPN_OK;
2243 
2244     } /* end if (FUNCTION == APPEND_DIR) */
2245 
2246 /*---------------------------------------------------------------------------
2247     GETPATH:  copy full FAT path to the string pointed at by pathcomp (want
2248     filename to reflect name used on disk, not EAs; if full path is HPFS,
2249     buildpathFAT and buildpathHPFS will be identical).  Also free both paths.
2250   ---------------------------------------------------------------------------*/
2251 
2252     if (FUNCTION == GETPATH) {
2253         Trace((stderr, "getting and freeing FAT path [%s]\n",
2254           FnFilter1(G.buildpathFAT)));
2255         Trace((stderr, "freeing HPFS path [%s]\n",
2256           FnFilter1(G.buildpathHPFS)));
2257         strcpy(pathcomp, G.buildpathFAT);
2258         free(G.buildpathFAT);
2259         free(G.buildpathHPFS);
2260         G.buildpathHPFS = G.buildpathFAT = G.endHPFS = G.endFAT = NULL;
2261         return MPN_OK;
2262     }
2263 
2264 /*---------------------------------------------------------------------------
2265     APPEND_NAME:  assume the path component is the filename; append it and
2266     return without checking for existence.
2267   ---------------------------------------------------------------------------*/
2268 
2269     if (FUNCTION == APPEND_NAME) {
2270         char *p = pathcomp;
2271         int error = MPN_OK;
2272 
2273         Trace((stderr, "appending filename [%s]\n", FnFilter1(pathcomp)));
2274         /* The buildpathHPFS buffer has been allocated large enough to
2275          * hold the complete combined name, so there is no need to check
2276          * for OS filename size limit overflow within the copy loop.
2277          */
2278         while ((*G.endHPFS = *p++) != '\0') {   /* copy to HPFS filename */
2279             ++G.endHPFS;
2280         }
2281         /* Now, check for OS filename size overflow.  When detected, the
2282          * mapped HPFS name is truncated and a warning message is shown.
2283          */
2284         if ((G.endHPFS-G.buildpathHPFS) >= FILNAMSIZ) {
2285             G.buildpathHPFS[FILNAMSIZ-1] = '\0';
2286             Info(slide, 1, ((char *)slide,
2287               "checkdir warning:  path too long; truncating\n \
2288               %s\n                -> %s\n",
2289               FnFilter1(G.filename), FnFilter2(G.buildpathHPFS)));
2290             error = MPN_INF_TRUNC;  /* filename truncated */
2291         }
2292 
2293         /* The buildpathFAT buffer has the same allocated size as the
2294          * buildpathHPFS buffer, so there is no need for an overflow check
2295          * within the following copy loop, either.
2296          */
2297         if (G.pInfo->vollabel || !IsVolumeOldFAT(__G__ G.buildpathHPFS)) {
2298             /* copy to FAT filename, too */
2299             p = pathcomp;
2300             while ((*G.endFAT = *p++) != '\0')
2301                 ++G.endFAT;
2302         } else
2303             /* map into FAT fn, update endFAT */
2304             map2fat(pathcomp, &G.endFAT);
2305 
2306         /* Check that the FAT path does not exceed the FILNAMSIZ limit, and
2307          * truncate when neccessary.
2308          * Note that truncation can only happen when the HPFS path (which is
2309          * never shorter than the FAT path) has been already truncated.
2310          * So, emission of the warning message and setting the error code
2311          * has already happened.
2312          */
2313         if ((G.endFAT-G.buildpathFAT) >= FILNAMSIZ)
2314             G.buildpathFAT[FILNAMSIZ-1] = '\0';
2315         Trace((stderr, "buildpathHPFS: %s\nbuildpathFAT:  %s\n",
2316           FnFilter1(G.buildpathHPFS), FnFilter2(G.buildpathFAT)));
2317 
2318         return error;  /* could check for existence, prompt for new name... */
2319 
2320     } /* end if (FUNCTION == APPEND_NAME) */
2321 
2322 /*---------------------------------------------------------------------------
2323     INIT:  allocate and initialize buffer space for the file currently being
2324     extracted.  If file was renamed with an absolute path, don't prepend the
2325     extract-to path.
2326   ---------------------------------------------------------------------------*/
2327 
2328     if (FUNCTION == INIT) {
2329         Trace((stderr, "initializing buildpathHPFS and buildpathFAT to "));
2330 #ifdef ACORN_FTYPE_NFS
2331         if ((G.buildpathHPFS = (char *)malloc(G.fnlen+G.rootlen+
2332                                               (uO.acorn_nfs_ext ? 5 : 1)))
2333 #else
2334         if ((G.buildpathHPFS = (char *)malloc(G.fnlen+G.rootlen+1))
2335 #endif
2336             == NULL)
2337             return MPN_NOMEM;
2338 #ifdef ACORN_FTYPE_NFS
2339         if ((G.buildpathFAT = (char *)malloc(G.fnlen+G.rootlen+
2340                                              (uO.acorn_nfs_ext ? 5 : 1)))
2341 #else
2342         if ((G.buildpathFAT = (char *)malloc(G.fnlen+G.rootlen+1))
2343 #endif
2344             == NULL) {
2345             free(G.buildpathHPFS);
2346             return MPN_NOMEM;
2347         }
2348         if (G.pInfo->vollabel) { /* use root or renamed path, but don't store */
2349 /* GRR:  for network drives, do strchr() and return IZ_VOL_LABEL if not [1] */
2350             if (G.renamed_fullpath && pathcomp[1] == ':')
2351                 *G.buildpathHPFS = (char)ToLower(*pathcomp);
2352             else if (!G.renamed_fullpath && G.rootlen > 1 &&
2353                      G.rootpath[1] == ':')
2354                 *G.buildpathHPFS = (char)ToLower(*G.rootpath);
2355             else {
2356                 char tmpN[MAX_PATH], *tmpP;
2357                 if (GetFullPathNameA(".", MAX_PATH, tmpN, &tmpP) > MAX_PATH)
2358                 { /* by definition of MAX_PATH we should never get here */
2359                     Info(slide, 1, ((char *)slide,
2360                       "checkdir warning: current dir path too long\n"));
2361                     return MPN_INF_TRUNC;   /* can't get drive letter */
2362                 }
2363                 G.nLabelDrive = *tmpN - 'a' + 1;
2364                 *G.buildpathHPFS = (char)(G.nLabelDrive - 1 + 'a');
2365             }
2366             G.nLabelDrive = *G.buildpathHPFS - 'a' + 1; /* save for mapname() */
2367             if (uO.volflag == 0 || *G.buildpathHPFS < 'a' /* no labels/bogus? */
2368                 || (uO.volflag == 1 && !isfloppy(G.nLabelDrive))) { /* !fixed */
2369                 free(G.buildpathHPFS);
2370                 free(G.buildpathFAT);
2371                 return MPN_VOL_LABEL;  /* skipping with message */
2372             }
2373             *G.buildpathHPFS = '\0';
2374         } else if (G.renamed_fullpath) /* pathcomp = valid data */
2375             strcpy(G.buildpathHPFS, pathcomp);
2376         else if (G.rootlen > 0)
2377             strcpy(G.buildpathHPFS, G.rootpath);
2378         else
2379             *G.buildpathHPFS = '\0';
2380         G.endHPFS = G.buildpathHPFS;
2381         G.endFAT = G.buildpathFAT;
2382         while ((*G.endFAT = *G.endHPFS) != '\0') {
2383             ++G.endFAT;
2384             ++G.endHPFS;
2385         }
2386         Trace((stderr, "[%s]\n", FnFilter1(G.buildpathHPFS)));
2387         return MPN_OK;
2388     }
2389 
2390 /*---------------------------------------------------------------------------
2391     ROOT:  if appropriate, store the path in rootpath and create it if neces-
2392     sary; else assume it's a zipfile member and return.  This path segment
2393     gets used in extracting all members from every zipfile specified on the
2394     command line.  Note that under OS/2 and MS-DOS, if a candidate extract-to
2395     directory specification includes a drive letter (leading "x:"), it is
2396     treated just as if it had a trailing '/'--that is, one directory level
2397     will be created if the path doesn't exist, unless this is otherwise pro-
2398     hibited (e.g., freshening).
2399   ---------------------------------------------------------------------------*/
2400 
2401 #if (!defined(SFX) || defined(SFX_EXDIR))
2402     if (FUNCTION == ROOT) {
2403         Trace((stderr, "initializing root path to [%s]\n",
2404           FnFilter1(pathcomp)));
2405         if (pathcomp == NULL) {
2406             G.rootlen = 0;
2407             return MPN_OK;
2408         }
2409         if (G.rootlen > 0)      /* rootpath was already set, nothing to do */
2410             return MPN_OK;
2411         if ((G.rootlen = strlen(pathcomp)) > 0) {
2412             int had_trailing_pathsep=FALSE, has_drive=FALSE, add_dot=FALSE;
2413             char *tmproot;
2414 
2415             if ((tmproot = (char *)malloc(G.rootlen+3)) == (char *)NULL) {
2416                 G.rootlen = 0;
2417                 return MPN_NOMEM;
2418             }
2419             strcpy(tmproot, pathcomp);
2420             if (isalpha((uch)tmproot[0]) && tmproot[1] == ':')
2421                 has_drive = TRUE;   /* drive designator */
2422             if (tmproot[G.rootlen-1] == '/' || tmproot[G.rootlen-1] == '\\') {
2423                 tmproot[--G.rootlen] = '\0';
2424                 had_trailing_pathsep = TRUE;
2425             }
2426             if (has_drive && (G.rootlen == 2)) {
2427                 if (!had_trailing_pathsep)   /* i.e., original wasn't "x:/" */
2428                     add_dot = TRUE;    /* relative path: add '.' before '/' */
2429             } else if (G.rootlen > 0) {   /* need not check "x:." and "x:/" */
2430                 if (SSTAT(tmproot, &G.statbuf) || !S_ISDIR(G.statbuf.st_mode))
2431                 {
2432                     /* path does not exist */
2433                     if (!G.create_dirs /* || iswild(tmproot) */ ) {
2434                         free(tmproot);
2435                         G.rootlen = 0;
2436                         /* treat as stored file */
2437                         return MPN_INF_SKIP;
2438                     }
2439                     /* create directory (could add loop here scanning tmproot
2440                      * to create more than one level, but really necessary?) */
2441                     if (MKDIR(tmproot, 0777) == -1) {
2442                         Info(slide, 1, ((char *)slide,
2443                           "checkdir:  cannot create extraction directory: %s\n",
2444                           FnFilter1(tmproot)));
2445                         free(tmproot);
2446                         G.rootlen = 0;
2447                         /* path didn't exist, tried to create, failed: */
2448                         /* file exists, or need 2+ subdir levels */
2449                         return MPN_ERR_SKIP;
2450                     }
2451                 }
2452             }
2453             if (add_dot)                    /* had just "x:", make "x:." */
2454                 tmproot[G.rootlen++] = '.';
2455             tmproot[G.rootlen++] = '/';
2456             tmproot[G.rootlen] = '\0';
2457             if ((G.rootpath = (char *)realloc(tmproot, G.rootlen+1)) == NULL) {
2458                 free(tmproot);
2459                 G.rootlen = 0;
2460                 return MPN_NOMEM;
2461             }
2462             Trace((stderr, "rootpath now = [%s]\n", FnFilter1(G.rootpath)));
2463         }
2464         return MPN_OK;
2465     }
2466 #endif /* !SFX || SFX_EXDIR */
2467 
2468 /*---------------------------------------------------------------------------
2469     END:  free rootpath, immediately prior to program exit.
2470   ---------------------------------------------------------------------------*/
2471 
2472     if (FUNCTION == END) {
2473         Trace((stderr, "freeing rootpath\n"));
2474         if (G.rootlen > 0) {
2475             free(G.rootpath);
2476             G.rootlen = 0;
2477         }
2478         return MPN_OK;
2479     }
2480 
2481     return MPN_INVALID; /* should never reach */
2482 
2483 } /* end function checkdir() */
2484 
2485 
2486 
2487 
2488 
2489 #ifndef SFX
2490 
2491 /*************************/
2492 /* Function dateformat() */
2493 /*************************/
2494 
2495 int dateformat()
2496 {
2497   char df[2];   /* LOCALE_IDATE has a maximum value of 2 */
2498 
2499   if (GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_IDATE, df, 2) != 0) {
2500     switch (df[0])
2501     {
2502       case '0':
2503         return DF_MDY;
2504       case '1':
2505         return DF_DMY;
2506       case '2':
2507         return DF_YMD;
2508     }
2509   }
2510   return DF_MDY;
2511 }
2512 
2513 
2514 /****************************/
2515 /* Function dateseparator() */
2516 /****************************/
2517 
2518 char dateseparator()
2519 {
2520   char df[2];   /* use only if it is one character */
2521 
2522   if ((GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_SDATE, df, 2) != 0) &&
2523       (df[0] != '\0'))
2524     return df[0];
2525   else
2526     return '-';
2527 }
2528 
2529 
2530 #ifndef WINDLL
2531 
2532 /************************/
2533 /*  Function version()  */
2534 /************************/
2535 
2536 void version(__G)
2537     __GDEF
2538 {
2539     int len;
2540 #if (defined(_MSC_VER) || defined(__WATCOMC__) || defined(__DJGPP__))
2541     char buf[80];
2542 #if (defined(_MSC_VER) && (_MSC_VER > 900))
2543     char buf2[80];
2544 #endif
2545 #endif
2546 
2547     len = sprintf((char *)slide, CompiledWith,
2548 
2549 #if defined(_MSC_VER)  /* MSC == VC++, but what about SDK compiler? */
2550       (sprintf(buf, "Microsoft C %d.%02d ", _MSC_VER/100, _MSC_VER%100), buf),
2551 #  if (_MSC_VER == 800)
2552       "(Visual C++ v1.1)",
2553 #  elif (_MSC_VER == 850)
2554       "(Windows NT v3.5 SDK)",
2555 #  elif (_MSC_VER == 900)
2556       "(Visual C++ v2.x)",
2557 #  elif (_MSC_VER > 900)
2558       (sprintf(buf2, "(Visual C++ %d.%d)", _MSC_VER/100 - 6, _MSC_VER%100/10),
2559         buf2),
2560 #  else
2561       "(bad version)",
2562 #  endif
2563 #elif defined(__WATCOMC__)
2564 #  if (__WATCOMC__ % 10 > 0)
2565       (sprintf(buf, "Watcom C/C++ %d.%02d", __WATCOMC__ / 100,
2566        __WATCOMC__ % 100), buf), "",
2567 #  else
2568       (sprintf(buf, "Watcom C/C++ %d.%d", __WATCOMC__ / 100,
2569        (__WATCOMC__ % 100) / 10), buf), "",
2570 #  endif
2571 #elif defined(__BORLANDC__)
2572       "Borland C++",
2573 #  if (__BORLANDC__ < 0x0200)
2574       " 1.0",
2575 #  elif (__BORLANDC__ == 0x0200)
2576       " 2.0",
2577 #  elif (__BORLANDC__ == 0x0400)
2578       " 3.0",
2579 #  elif (__BORLANDC__ == 0x0410)   /* __TURBOC__ = 0x0310 */
2580       " 3.1",
2581 #  elif (__BORLANDC__ == 0x0452)   /* __TURBOC__ = 0x0320 */
2582       " 4.0 or 4.02",
2583 #  elif (__BORLANDC__ == 0x0460)   /* __TURBOC__ = 0x0340 */
2584       " 4.5",
2585 #  elif (__BORLANDC__ == 0x0500)   /* __TURBOC__ = 0x0340 */
2586       " 5.0",
2587 #  elif (__BORLANDC__ == 0x0520)   /* __TURBOC__ = 0x0520 */
2588       " 5.2 (C++ Builder 1.0)",
2589 #  elif (__BORLANDC__ == 0x0530)   /* __TURBOC__ = 0x0530 */
2590       " 5.3 (C++ Builder 3.0)",
2591 #  elif (__BORLANDC__ == 0x0540)   /* __TURBOC__ = 0x0540 */
2592       " 5.4 (C++ Builder 4.0)",
2593 #  elif (__BORLANDC__ == 0x0550)   /* __TURBOC__ = 0x0550 */
2594       " 5.5 (C++ Builder 5.0)",
2595 #  elif (__BORLANDC__ == 0x0551)   /* __TURBOC__ = 0x0551 */
2596       " 5.5.1 (C++ Builder 5.0.1)",
2597 #  elif (__BORLANDC__ == 0x0560)   /* __TURBOC__ = 0x0560 */
2598       " 6.0 (C++ Builder 6.0)",
2599 #  else
2600       " later than 6.0",
2601 #  endif
2602 #elif defined(__LCC__)
2603       "LCC-Win32", "",
2604 #elif defined(__GNUC__)
2605 #  if defined(__RSXNT__)
2606 #    if (defined(__DJGPP__) && !defined(__EMX__))
2607       (sprintf(buf, "rsxnt(djgpp v%d.%02d) / gcc ",
2608         __DJGPP__, __DJGPP_MINOR__), buf),
2609 #    elif defined(__DJGPP__)
2610       (sprintf(buf, "rsxnt(emx+djgpp v%d.%02d) / gcc ",
2611         __DJGPP__, __DJGPP_MINOR__), buf),
2612 #    elif (defined(__GO32__) && !defined(__EMX__))
2613       "rsxnt(djgpp v1.x) / gcc ",
2614 #    elif defined(__GO32__)
2615       "rsxnt(emx + djgpp v1.x) / gcc ",
2616 #    elif defined(__EMX__)
2617       "rsxnt(emx)+gcc ",
2618 #    else
2619       "rsxnt(unknown) / gcc ",
2620 #    endif
2621 #  elif defined(__CYGWIN__)
2622       "cygnus win32 / gcc ",
2623 #  elif defined(__MINGW32__)
2624       "mingw32 / gcc ",
2625 #  else
2626       "gcc ",
2627 #  endif
2628       __VERSION__,
2629 #else /* !_MSC_VER, !__WATCOMC__, !__BORLANDC__, !__LCC__, !__GNUC__ */
2630       "unknown compiler (SDK?)", "",
2631 #endif /* ?compilers */
2632 
2633       "\nWindows 9x / Windows NT/2K/XP/2K3", " (32-bit)",
2634 
2635 #ifdef __DATE__
2636       " on ", __DATE__
2637 #else
2638       "", ""
2639 #endif
2640     );
2641 
2642     (*G.message)((zvoid *)&G, slide, (ulg)len, 0);
2643 
2644     return;
2645 
2646 } /* end function version() */
2647 
2648 #endif /* !WINDLL */
2649 #endif /* !SFX */
2650 
2651 
2652 
2653 #ifdef MORE
2654 
2655 int screensize(int *tt_rows, int *tt_cols)
2656 {
2657     HANDLE hstdout;
2658     CONSOLE_SCREEN_BUFFER_INFO scr;
2659 
2660     hstdout = GetStdHandle(STD_OUTPUT_HANDLE);
2661     GetConsoleScreenBufferInfo(hstdout, &scr);
2662     if (tt_rows != NULL) *tt_rows = scr.srWindow.Bottom - scr.srWindow.Top + 1;
2663     if (tt_cols != NULL) *tt_cols = scr.srWindow.Right - scr.srWindow.Left + 1;
2664     return 0;           /* signal success */
2665 }
2666 
2667 #endif /* MORE */
2668 
2669 
2670 
2671 #ifdef W32_STAT_BANDAID
2672 
2673 /* All currently known variants of WIN32 operating systems (Windows 95/98,
2674  * WinNT 3.x, 4.0, 5.x) have a nasty bug in the OS kernel concerning
2675  * conversions between UTC and local time: In the time conversion functions
2676  * of the Win32 API, the timezone offset (including seasonal daylight saving
2677  * shift) between UTC and local time evaluation is erratically based on the
2678  * current system time. The correct evaluation must determine the offset
2679  * value as it {was/is/will be} for the actual time to be converted.
2680  *
2681  * Newer versions of MS C runtime lib's stat() returns utc time-stamps so
2682  * that localtime(timestamp) corresponds to the (potentially false) local
2683  * time shown by the OS' system programs (Explorer, command shell dir, etc.)
2684  * The RSXNT port follows the same strategy, but fails to recognize the
2685  * access-time attribute.
2686  *
2687  * For the NTFS file system (and other filesystems that store time-stamps
2688  * as UTC values), this results in st_mtime (, st_{c|a}time) fields which
2689  * are not stable but vary according to the seasonal change of "daylight
2690  * saving time in effect / not in effect".
2691  *
2692  * Other C runtime libs (CygWin), or the crtdll.dll supplied with Win9x/NT
2693  * return the unix-time equivalent of the UTC FILETIME values as got back
2694  * from the Win32 API call. This time, return values from NTFS are correct
2695  * whereas utimes from files on (V)FAT volumes vary according to the DST
2696  * switches.
2697  *
2698  * To achieve timestamp consistency of UTC (UT extra field) values in
2699  * Zip archives, the Info-ZIP programs require work-around code for
2700  * proper time handling in stat() (and other time handling routines).
2701  *
2702  * However, nowadays most other programs on Windows systems use the
2703  * time conversion strategy of Microsofts C runtime lib "msvcrt.dll".
2704  * To improve interoperability in environments where a "consistent" (but
2705  * false) "UTC<-->LocalTime" conversion is preferred over "stable" time
2706  * stamps, the Info-ZIP specific time conversion handling can be
2707  * deactivated by defining the preprocessor flag NO_W32TIMES_IZFIX.
2708  */
2709 /* stat() functions under Windows95 tend to fail for root directories.   *
2710  * Watcom and Borland, at least, are affected by this bug.  Watcom made  *
2711  * a partial fix for 11.0 but still missed some cases.  This substitute  *
2712  * detects the case and fills in reasonable values.  Otherwise we get    *
2713  * effects like failure to extract to a root dir because it's not found. */
2714 
2715 int zstat_win32(__W32STAT_GLOBALS__ const char *path, z_stat *buf)
2716 {
2717     if (!zstat(path, buf))
2718     {
2719         /* stat was successful, now redo the time-stamp fetches */
2720 #ifndef NO_W32TIMES_IZFIX
2721         int fs_uses_loctime = FStampIsLocTime(__G__ path);
2722 #endif
2723         HANDLE h;
2724         FILETIME Modft, Accft, Creft;
2725 #ifdef __RSXNT__        /* RSXNT/EMX C rtl uses OEM charset */
2726         char *ansi_path = (char *)alloca(strlen(path) + 1);
2727 
2728         INTERN_TO_ISO(path, ansi_path);
2729 #       define Ansi_Path  ansi_path
2730 #else
2731 #       define Ansi_Path  path
2732 #endif
2733 
2734         TTrace((stdout, "stat(%s) finds modtime %08lx\n", path, buf->st_mtime));
2735         h = CreateFileA(Ansi_Path, GENERIC_READ,
2736                         FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
2737                         OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
2738         if (h != INVALID_HANDLE_VALUE) {
2739             BOOL ftOK = GetFileTime(h, &Creft, &Accft, &Modft);
2740             CloseHandle(h);
2741 
2742             if (ftOK) {
2743                 FTTrace((stdout, "GetFileTime returned Modft", 0, &Modft));
2744                 FTTrace((stdout, "GetFileTime returned Creft", 0, &Creft));
2745 #ifndef NO_W32TIMES_IZFIX
2746                 if (!fs_uses_loctime) {
2747                     /*  On a filesystem that stores UTC timestamps, we refill
2748                      *  the time fields of the struct stat buffer by directly
2749                      *  using the UTC values as returned by the Win32
2750                      *  GetFileTime() API call.
2751                      */
2752                     NtfsFileTime2utime(&Modft, &(buf->st_mtime));
2753                     if (Accft.dwLowDateTime != 0 || Accft.dwHighDateTime != 0)
2754                         NtfsFileTime2utime(&Accft, &(buf->st_atime));
2755                     else
2756                         buf->st_atime = buf->st_mtime;
2757                     if (Creft.dwLowDateTime != 0 || Creft.dwHighDateTime != 0)
2758                         NtfsFileTime2utime(&Creft, &(buf->st_ctime));
2759                     else
2760                         buf->st_ctime = buf->st_mtime;
2761                     TTrace((stdout,"NTFS, recalculated modtime %08lx\n",
2762                             buf->st_mtime));
2763                 } else
2764 #endif /* NO_W32TIMES_IZFIX */
2765                 {
2766                     /*  On VFAT and FAT-like filesystems, the FILETIME values
2767                      *  are converted back to the stable local time before
2768                      *  converting them to UTC unix time-stamps.
2769                      */
2770                     VFatFileTime2utime(&Modft, &(buf->st_mtime));
2771                     if (Accft.dwLowDateTime != 0 || Accft.dwHighDateTime != 0)
2772                         VFatFileTime2utime(&Accft, &(buf->st_atime));
2773                     else
2774                         buf->st_atime = buf->st_mtime;
2775                     if (Creft.dwLowDateTime != 0 || Creft.dwHighDateTime != 0)
2776                         VFatFileTime2utime(&Creft, &(buf->st_ctime));
2777                     else
2778                         buf->st_ctime = buf->st_mtime;
2779                     TTrace((stdout, "VFAT, recalculated modtime %08lx\n",
2780                             buf->st_mtime));
2781                 }
2782             }
2783         }
2784 #       undef Ansi_Path
2785         return 0;
2786     }
2787 #ifdef W32_STATROOT_FIX
2788     else
2789     {
2790         DWORD flags;
2791 #ifdef __RSXNT__        /* RSXNT/EMX C rtl uses OEM charset */
2792         char *ansi_path = (char *)alloca(strlen(path) + 1);
2793 
2794         INTERN_TO_ISO(path, ansi_path);
2795 #       define Ansi_Path  ansi_path
2796 #else
2797 #       define Ansi_Path  path
2798 #endif
2799 
2800         flags = GetFileAttributesA(Ansi_Path);
2801         if (flags != 0xFFFFFFFF && flags & FILE_ATTRIBUTE_DIRECTORY) {
2802             Trace((stderr, "\nstat(\"%s\",...) failed on existing directory\n",
2803                    FnFilter1(path)));
2804             memset(buf, 0, sizeof(z_stat));
2805             buf->st_atime = buf->st_ctime = buf->st_mtime =
2806               dos_to_unix_time(DOSTIME_MINIMUM);        /* 1-1-80 */
2807             buf->st_mode = S_IFDIR | S_IREAD |
2808                            ((flags & FILE_ATTRIBUTE_READONLY) ? 0 : S_IWRITE);
2809             return 0;
2810         } /* assumes: stat() won't fail on non-dirs without good reason */
2811 #       undef Ansi_Path
2812     }
2813 #endif /* W32_STATROOT_FIX */
2814     return -1;
2815 }
2816 
2817 #endif /* W32_STAT_BANDAID */
2818 
2819 
2820 
2821 #ifdef W32_USE_IZ_TIMEZONE
2822 #include "timezone.h"
2823 #define SECSPERMIN      60
2824 #define MINSPERHOUR     60
2825 #define SECSPERHOUR     (SECSPERMIN * MINSPERHOUR)
2826 static void conv_to_rule(LPSYSTEMTIME lpw32tm, struct rule * ZCONST ptrule);
2827 
2828 static void conv_to_rule(LPSYSTEMTIME lpw32tm, struct rule * ZCONST ptrule)
2829 {
2830     if (lpw32tm->wYear != 0) {
2831         ptrule->r_type = JULIAN_DAY;
2832         ptrule->r_day = ydays[lpw32tm->wMonth - 1] + lpw32tm->wDay;
2833     } else {
2834         ptrule->r_type = MONTH_NTH_DAY_OF_WEEK;
2835         ptrule->r_mon = lpw32tm->wMonth;
2836         ptrule->r_day = lpw32tm->wDayOfWeek;
2837         ptrule->r_week = lpw32tm->wDay;
2838     }
2839     ptrule->r_time = (long)lpw32tm->wHour * SECSPERHOUR +
2840                      (long)(lpw32tm->wMinute * SECSPERMIN) +
2841                      (long)lpw32tm->wSecond;
2842 }
2843 
2844 int GetPlatformLocalTimezone(register struct state * ZCONST sp,
2845         void (*fill_tzstate_from_rules)(struct state * ZCONST sp_res,
2846                                         ZCONST struct rule * ZCONST start,
2847                                         ZCONST struct rule * ZCONST end))
2848 {
2849     TIME_ZONE_INFORMATION tzinfo;
2850     DWORD res;
2851 
2852     /* read current timezone settings from registry if TZ envvar missing */
2853     res = GetTimeZoneInformation(&tzinfo);
2854     if (res != TIME_ZONE_ID_INVALID)
2855     {
2856         struct rule startrule, stoprule;
2857 
2858         conv_to_rule(&(tzinfo.StandardDate), &stoprule);
2859         conv_to_rule(&(tzinfo.DaylightDate), &startrule);
2860         sp->timecnt = 0;
2861         sp->ttis[0].tt_abbrind = 0;
2862         if ((sp->charcnt =
2863              WideCharToMultiByte(CP_ACP, 0, tzinfo.StandardName, -1,
2864                                  sp->chars, sizeof(sp->chars), NULL, NULL))
2865             == 0)
2866             sp->chars[sp->charcnt++] = '\0';
2867         sp->ttis[1].tt_abbrind = sp->charcnt;
2868         sp->charcnt +=
2869             WideCharToMultiByte(CP_ACP, 0, tzinfo.DaylightName, -1,
2870                                 sp->chars + sp->charcnt,
2871                                 sizeof(sp->chars) - sp->charcnt, NULL, NULL);
2872         if ((sp->charcnt - sp->ttis[1].tt_abbrind) == 0)
2873             sp->chars[sp->charcnt++] = '\0';
2874         sp->ttis[0].tt_gmtoff = - (tzinfo.Bias + tzinfo.StandardBias)
2875                                 * MINSPERHOUR;
2876         sp->ttis[1].tt_gmtoff = - (tzinfo.Bias + tzinfo.DaylightBias)
2877                                 * MINSPERHOUR;
2878         sp->ttis[0].tt_isdst = 0;
2879         sp->ttis[1].tt_isdst = 1;
2880         sp->typecnt = (startrule.r_mon == 0 && stoprule.r_mon == 0) ? 1 : 2;
2881 
2882         if (sp->typecnt > 1)
2883             (*fill_tzstate_from_rules)(sp, &startrule, &stoprule);
2884         return TRUE;
2885     }
2886     return FALSE;
2887 }
2888 #endif /* W32_USE_IZ_TIMEZONE */
2889 
2890 #endif /* !FUNZIP */
2891 
2892 
2893 
2894 #ifndef WINDLL
2895 /* This replacement getch() function was originally created for Watcom C
2896  * and then additionally used with CYGWIN. Since UnZip 5.4, all other Win32
2897  * ports apply this replacement rather that their supplied getch() (or
2898  * alike) function.  There are problems with unabsorbed LF characters left
2899  * over in the keyboard buffer under Win95 (and 98) when ENTER was pressed.
2900  * (Under Win95, ENTER returns two(!!) characters: CR-LF.)  This problem
2901  * does not appear when run on a WinNT console prompt!
2902  */
2903 
2904 /* Watcom 10.6's getch() does not handle Alt+<digit><digit><digit>. */
2905 /* Note that if PASSWD_FROM_STDIN is defined, the file containing   */
2906 /* the password must have a carriage return after the word, not a   */
2907 /* Unix-style newline (linefeed only).  This discards linefeeds.    */
2908 
2909 int getch_win32(void)
2910 {
2911   HANDLE stin;
2912   DWORD rc;
2913   unsigned char buf[2];
2914   int ret = -1;
2915   DWORD odemode = ~(DWORD)0;
2916 
2917 #  ifdef PASSWD_FROM_STDIN
2918   stin = GetStdHandle(STD_INPUT_HANDLE);
2919 #  else
2920   stin = CreateFileA("CONIN$", GENERIC_READ | GENERIC_WRITE,
2921                      FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
2922   if (stin == INVALID_HANDLE_VALUE)
2923     return -1;
2924 #  endif
2925   if (GetConsoleMode(stin, &odemode))
2926     SetConsoleMode(stin, ENABLE_PROCESSED_INPUT);  /* raw except ^C noticed */
2927   if (ReadFile(stin, &buf, 1, &rc, NULL) && rc == 1)
2928     ret = buf[0];
2929   /* when the user hits return we get CR LF.  We discard the LF, not the CR,
2930    * because when we call this for the first time after a previous input
2931    * such as the one for "replace foo? [y]es, ..." the LF may still be in
2932    * the input stream before whatever the user types at our prompt. */
2933   if (ret == '\n')
2934     if (ReadFile(stin, &buf, 1, &rc, NULL) && rc == 1)
2935       ret = buf[0];
2936   if (odemode != ~(DWORD)0)
2937     SetConsoleMode(stin, odemode);
2938 #  ifndef PASSWD_FROM_STDIN
2939   CloseHandle(stin);
2940 #  endif
2941   return ret;
2942 }
2943 #endif /* !WINDLL */
2944 
2945 
2946 
2947 #if (defined(UNICODE_SUPPORT) && !defined(FUNZIP))
2948 /* convert wide character string to multi-byte character string */
2949 char *wide_to_local_string(wide_string, escape_all)
2950   ZCONST zwchar *wide_string;
2951   int escape_all;
2952 {
2953   int i;
2954   wchar_t wc;
2955   int bytes_char;
2956   int default_used;
2957   int wsize = 0;
2958   int max_bytes = 9;
2959   char buf[9];
2960   char *buffer = NULL;
2961   char *local_string = NULL;
2962 
2963   for (wsize = 0; wide_string[wsize]; wsize++) ;
2964 
2965   if (max_bytes < MB_CUR_MAX)
2966     max_bytes = MB_CUR_MAX;
2967 
2968   if ((buffer = (char *)malloc(wsize * max_bytes + 1)) == NULL) {
2969     return NULL;
2970   }
2971 
2972   /* convert it */
2973   buffer[0] = '\0';
2974   for (i = 0; i < wsize; i++) {
2975     if (sizeof(wchar_t) < 4 && wide_string[i] > 0xFFFF) {
2976       /* wchar_t probably 2 bytes */
2977       /* could do surrogates if state_dependent and wctomb can do */
2978       wc = zwchar_to_wchar_t_default_char;
2979     } else {
2980       wc = (wchar_t)wide_string[i];
2981     }
2982     /* Unter some vendor's C-RTL, the Wide-to-MultiByte conversion functions
2983      * (like wctomb() et. al.) do not use the same codepage as the other
2984      * string arguments I/O functions (fopen, mkdir, rmdir etc.).
2985      * Therefore, we have to fall back to the underlying Win32-API call to
2986      * achieve a consistent behaviour for all supported compiler environments.
2987      * Failing RTLs are for example:
2988      *   Borland (locale uses OEM-CP as default, but I/O functions expect ANSI
2989      *            names)
2990      *   Watcom  (only "C" locale, wctomb() always uses OEM CP)
2991      * (in other words: all supported environments except the Microsoft RTLs)
2992      */
2993     bytes_char = WideCharToMultiByte(
2994                           CP_ACP, WC_COMPOSITECHECK,
2995                           &wc, 1,
2996                           (LPSTR)buf, sizeof(buf),
2997                           NULL, &default_used);
2998     if (default_used)
2999       bytes_char = -1;
3000     if (escape_all) {
3001       if (bytes_char == 1 && (uch)buf[0] <= 0x7f) {
3002         /* ASCII */
3003         strncat(buffer, buf, 1);
3004       } else {
3005         /* use escape for wide character */
3006         char *escape_string = wide_to_escape_string(wide_string[i]);
3007         strcat(buffer, escape_string);
3008         free(escape_string);
3009       }
3010     } else if (bytes_char > 0) {
3011       /* multi-byte char */
3012       strncat(buffer, buf, bytes_char);
3013     } else {
3014       /* no MB for this wide */
3015       /* use escape for wide character */
3016       char *escape_string = wide_to_escape_string(wide_string[i]);
3017       strcat(buffer, escape_string);
3018       free(escape_string);
3019     }
3020   }
3021   if ((local_string = (char *)realloc(buffer, strlen(buffer) + 1)) == NULL) {
3022     free(buffer);
3023     return NULL;
3024   }
3025 
3026   return local_string;
3027 }
3028 
3029 
3030 #if 0
3031 wchar_t *utf8_to_wchar_string(utf8_string)
3032   char *utf8_string;       /* path to get utf-8 name for */
3033 {
3034   wchar_t  *qw;
3035   int       ulen;
3036   int       ulenw;
3037 
3038   if (utf8_string == NULL)
3039     return NULL;
3040 
3041     /* get length */
3042     ulenw = MultiByteToWideChar(
3043                 CP_UTF8,           /* UTF-8 code page */
3044                 0,                 /* flags for character-type options */
3045                 utf8_string,       /* string to convert */
3046                 -1,                /* string length (-1 = NULL terminated) */
3047                 NULL,              /* buffer */
3048                 0 );               /* buffer length (0 = return length) */
3049     if (ulenw == 0) {
3050       /* failed */
3051       return NULL;
3052     }
3053     ulenw++;
3054     /* get length in bytes */
3055     ulen = sizeof(wchar_t) * (ulenw + 1);
3056     if ((qw = (wchar_t *)malloc(ulen + 1)) == NULL) {
3057       return NULL;
3058     }
3059     /* convert multibyte to wide */
3060     ulen = MultiByteToWideChar(
3061                CP_UTF8,           /* UTF-8 code page */
3062                0,                 /* flags for character-type options */
3063                utf8_string,       /* string to convert */
3064                -1,                /* string length (-1 = NULL terminated) */
3065                qw,                /* buffer */
3066                ulenw);            /* buffer length (0 = return length) */
3067     if (ulen == 0) {
3068       /* failed */
3069       free(qw);
3070       return NULL;
3071     }
3072 
3073   return qw;
3074 }
3075 
3076 wchar_t *local_to_wchar_string(local_string)
3077   char *local_string;       /* path of local name */
3078 {
3079   wchar_t  *qw;
3080   int       ulen;
3081   int       ulenw;
3082 
3083   if (local_string == NULL)
3084     return NULL;
3085 
3086     /* get length */
3087     ulenw = MultiByteToWideChar(
3088                 CP_ACP,            /* ANSI code page */
3089                 0,                 /* flags for character-type options */
3090                 local_string,      /* string to convert */
3091                 -1,                /* string length (-1 = NULL terminated) */
3092                 NULL,              /* buffer */
3093                 0 );               /* buffer length (0 = return length) */
3094     if (ulenw == 0) {
3095       /* failed */
3096       return NULL;
3097     }
3098     ulenw++;
3099     /* get length in bytes */
3100     ulen = sizeof(wchar_t) * (ulenw + 1);
3101     if ((qw = (wchar_t *)malloc(ulen + 1)) == NULL) {
3102       return NULL;
3103     }
3104     /* convert multibyte to wide */
3105     ulen = MultiByteToWideChar(
3106                CP_ACP,            /* ANSI code page */
3107                0,                 /* flags for character-type options */
3108                local_string,      /* string to convert */
3109                -1,                /* string length (-1 = NULL terminated) */
3110                qw,                /* buffer */
3111                ulenw);            /* buffer length (0 = return length) */
3112     if (ulen == 0) {
3113       /* failed */
3114       free(qw);
3115       return NULL;
3116     }
3117 
3118   return qw;
3119 }
3120 #endif /* 0 */
3121 #endif /* UNICODE_SUPPORT && !FUNZIP */
3122 
3123 
3124 
3125 /* --------------------------------------------------- */
3126 /* Large File Support
3127  *
3128  * Initial functions by E. Gordon and R. Nausedat
3129  * 9/10/2003
3130  * Lifted from Zip 3b, win32.c and place here by Myles Bennett
3131  * 7/6/2004
3132  *
3133  * These implement 64-bit file support for Windows.  The
3134  * defines and headers are in win32/w32cfg.h.
3135  *
3136  * Moved to win32i64.c by Mike White to avoid conflicts in
3137  * same name functions in WiZ using UnZip and Zip libraries.
3138  * 9/25/2003
3139  */
3140