1/*
2  win32/win32.c - Zip 3
3
4  Copyright (c) 1990-2008 Info-ZIP.  All rights reserved.
5
6  See the accompanying file LICENSE, version 2007-Mar-4 or later
7  (the contents of which are also included in zip.h) for terms of use.
8  If, for some reason, all these files are missing, the Info-ZIP license
9  also may be found at:  ftp://ftp.info-zip.org/pub/infozip/license.html
10*/
11
12/*
13 * WIN32 specific functions for ZIP.
14 *
15 * The WIN32 version of ZIP heavily relies on the MSDOS and OS2 versions,
16 * since we have to do similar things to switch between NTFS, HPFS and FAT.
17 */
18
19
20#include "../zip.h"
21
22#include <stdlib.h>
23#include <stdio.h>
24#include <limits.h>
25#include <time.h>
26#include <ctype.h>
27#define WIN32_LEAN_AND_MEAN
28#include <windows.h>
29/* for LARGE_FILE_SUPPORT but may not be needed */
30#include <io.h>
31
32#ifdef __RSXNT__
33#  include <alloca.h>
34#  include "../win32/rsxntwin.h"
35#endif
36#include "../win32/win32zip.h"
37
38#define A_RONLY    0x01
39#define A_HIDDEN   0x02
40#define A_SYSTEM   0x04
41#define A_LABEL    0x08
42#define A_DIR      0x10
43#define A_ARCHIVE  0x20
44
45
46#define EAID     0x0009
47
48#if (defined(__MINGW32__) && !defined(USE_MINGW_GLOBBING))
49   int _CRT_glob = 0;   /* suppress command line globbing by C RTL */
50#endif
51
52#ifndef UTIL
53
54extern int noisy;
55
56#ifdef NT_TZBUG_WORKAROUND
57local int FSusesLocalTime(const char *path);
58#ifdef UNICODE_SUPPORt
59local int FSusesLocalTimeW(const wchar_t *path);
60#endif
61#endif
62#if (defined(USE_EF_UT_TIME) || defined(NT_TZBUG_WORKAROUND))
63local int FileTime2utime(FILETIME *pft, time_t *ut);
64#endif
65#if (defined(NT_TZBUG_WORKAROUND) && defined(W32_STAT_BANDAID))
66local int VFatFileTime2utime(const FILETIME *pft, time_t *ut);
67#endif
68
69
70/* FAT / HPFS detection */
71
72int IsFileSystemOldFAT(char *dir)
73{
74  static char lastDrive = '\0';    /* cached drive of last GetVolumeInformation call */
75  static int lastDriveOldFAT = 0;  /* cached OldFAT value of last GetVolumeInformation call */
76  char root[4];
77  DWORD vfnsize;
78  DWORD vfsflags;
79
80    /*
81     * We separate FAT and HPFS+other file systems here.
82     * I consider other systems to be similar to HPFS/NTFS, i.e.
83     * support for long file names and being case sensitive to some extent.
84     */
85
86    strncpy(root, dir, 3);
87    if ( isalpha((uch)root[0]) && (root[1] == ':') ) {
88      root[0] = to_up(dir[0]);
89      root[2] = '\\';
90      root[3] = 0;
91    }
92    else {
93      root[0] = '\\';
94      root[1] = 0;
95    }
96    if (lastDrive == root[0]) {
97      return lastDriveOldFAT;
98    }
99
100    if ( !GetVolumeInformation(root, NULL, 0,
101                               NULL, &vfnsize, &vfsflags,
102                               NULL, 0)) {
103        fprintf(mesg, "zip diagnostic: GetVolumeInformation failed\n");
104        return(FALSE);
105    }
106
107    lastDrive = root[0];
108    lastDriveOldFAT = vfnsize <= 12;
109
110    return lastDriveOldFAT;
111}
112
113#ifdef UNICODE_SUPPORT
114int IsFileSystemOldFATW(wchar_t *dir)
115{
116  static wchar_t lastDrive = (wchar_t)'\0';    /* cached drive of last GetVolumeInformation call */
117  static int lastDriveOldFAT = 0;  /* cached OldFAT value of last GetVolumeInformation call */
118  wchar_t root[4];
119  DWORD vfnsize;
120  DWORD vfsflags;
121
122    /*
123     * We separate FAT and HPFS+other file systems here.
124     * I consider other systems to be similar to HPFS/NTFS, i.e.
125     * support for long file names and being case sensitive to some extent.
126     */
127
128    wcsncpy(root, dir, 3);
129    if ( iswalpha(root[0]) && (root[1] == (wchar_t)':') ) {
130      root[0] = towupper(dir[0]);
131      root[2] = (wchar_t)'\\';
132      root[3] = 0;
133    }
134    else {
135      root[0] = (wchar_t)'\\';
136      root[1] = 0;
137    }
138    if (lastDrive == root[0]) {
139      return lastDriveOldFAT;
140    }
141
142    if ( !GetVolumeInformationW(root, NULL, 0,
143                                NULL, &vfnsize, &vfsflags,
144                                NULL, 0)) {
145        fprintf(mesg, "zip diagnostic: GetVolumeInformation failed\n");
146        return(FALSE);
147    }
148
149    lastDrive = root[0];
150    lastDriveOldFAT = vfnsize <= 12;
151
152    return lastDriveOldFAT;
153}
154#endif
155
156
157/* access mode bits and time stamp */
158
159int GetFileMode(char *name)
160{
161DWORD dwAttr;
162#if defined(__RSXNT__)  /* RSXNT/EMX C rtl uses OEM charset */
163  char *ansi_name = (char *)alloca(strlen(name) + 1);
164
165  OemToAnsi(name, ansi_name);
166  name = ansi_name;
167#endif
168
169  dwAttr = GetFileAttributes(name);
170  if ( dwAttr == 0xFFFFFFFF ) {
171    zipwarn("reading file attributes failed: ", name);
172    /*
173    fprintf(mesg, "zip diagnostic: GetFileAttributes failed");
174    fflush();
175    */
176    return(0x20); /* the most likely, though why the error? security? */
177  }
178  return(
179          (dwAttr&FILE_ATTRIBUTE_READONLY  ? A_RONLY   :0)
180        | (dwAttr&FILE_ATTRIBUTE_HIDDEN    ? A_HIDDEN  :0)
181        | (dwAttr&FILE_ATTRIBUTE_SYSTEM    ? A_SYSTEM  :0)
182        | (dwAttr&FILE_ATTRIBUTE_DIRECTORY ? A_DIR     :0)
183        | (dwAttr&FILE_ATTRIBUTE_ARCHIVE   ? A_ARCHIVE :0));
184}
185
186#ifdef UNICODE_SUPPORT
187int GetFileModeW(wchar_t *namew)
188{
189DWORD dwAttr;
190#if defined(__RSXNT__)  /* RSXNT/EMX C rtl uses OEM charset */
191  wchar_t *ansi_namew = (wchar_t *)alloca((wcslen(namew) + 1) * sizeof(wchar_t));
192
193  CharToAnsiW(namew, ansi_namew);
194  namew = ansi_namew;
195#endif
196
197  dwAttr = GetFileAttributesW(namew);
198  if ( dwAttr == 0xFFFFFFFF ) {
199    char *name = wchar_to_local_string(namew);
200    zipwarn("reading file attributes failed: ", name);
201    free(name);
202    return(0x20); /* the most likely, though why the error? security? */
203  }
204  return(
205          (dwAttr&FILE_ATTRIBUTE_READONLY  ? A_RONLY   :0)
206        | (dwAttr&FILE_ATTRIBUTE_HIDDEN    ? A_HIDDEN  :0)
207        | (dwAttr&FILE_ATTRIBUTE_SYSTEM    ? A_SYSTEM  :0)
208        | (dwAttr&FILE_ATTRIBUTE_DIRECTORY ? A_DIR     :0)
209        | (dwAttr&FILE_ATTRIBUTE_ARCHIVE   ? A_ARCHIVE :0));
210}
211#endif
212
213
214int ClearArchiveBitW(wchar_t *namew)
215{
216DWORD dwAttr;
217  dwAttr = GetFileAttributesW(namew);
218  if ( dwAttr == 0xFFFFFFFF ) {
219    fprintf(mesg, "zip diagnostic: GetFileAttributes failed\n");
220    return(0);
221  }
222
223  if (!SetFileAttributesW(namew, (DWORD)(dwAttr & ~FILE_ATTRIBUTE_ARCHIVE))) {
224    fprintf(mesg, "zip diagnostic: SetFileAttributes failed\n");
225    perror("SetFileAttributes");
226    return(0);
227  }
228  return(1);
229}
230
231int ClearArchiveBit(char *name)
232{
233DWORD dwAttr;
234#if defined(__RSXNT__)  /* RSXNT/EMX C rtl uses OEM charset */
235  char *ansi_name = (char *)alloca(strlen(name) + 1);
236
237  OemToAnsi(name, ansi_name);
238  name = ansi_name;
239#endif
240
241  dwAttr = GetFileAttributes(name);
242  if ( dwAttr == 0xFFFFFFFF ) {
243    fprintf(mesg, "zip diagnostic: GetFileAttributes failed\n");
244    return(0);
245  }
246
247  if (!SetFileAttributes(name, (DWORD)(dwAttr & ~FILE_ATTRIBUTE_ARCHIVE))) {
248    fprintf(mesg, "zip diagnostic: SetFileAttributes failed\n");
249    perror("SetFileAttributes");
250    return(0);
251  }
252  return(1);
253}
254
255
256#ifdef NT_TZBUG_WORKAROUND
257local int FSusesLocalTime(const char *path)
258{
259    char  *tmp0;
260    char   rootPathName[4];
261    char   tmp1[MAX_PATH], tmp2[MAX_PATH];
262    DWORD  volSerNo, maxCompLen, fileSysFlags;
263#if defined(__RSXNT__)  /* RSXNT/EMX C rtl uses OEM charset */
264    char *ansi_path = (char *)alloca(strlen(path) + 1);
265
266    OemToAnsi(path, ansi_path);
267    path = ansi_path;
268#endif
269
270    if (isalpha((uch)path[0]) && (path[1] == ':'))
271        tmp0 = (char *)path;
272    else
273    {
274        GetFullPathName(path, MAX_PATH, tmp1, &tmp0);
275        tmp0 = &tmp1[0];
276    }
277    strncpy(rootPathName, tmp0, 3);   /* Build the root path name, */
278    rootPathName[3] = '\0';           /* e.g. "A:/"                */
279
280    GetVolumeInformation((LPCTSTR)rootPathName, (LPTSTR)tmp1, (DWORD)MAX_PATH,
281                         &volSerNo, &maxCompLen, &fileSysFlags,
282                         (LPTSTR)tmp2, (DWORD)MAX_PATH);
283
284    /* Volumes in (V)FAT and (OS/2) HPFS format store file timestamps in
285     * local time!
286     */
287    return !strncmp(strupr(tmp2), "FAT", 3) ||
288           !strncmp(tmp2, "VFAT", 4) ||
289           !strncmp(tmp2, "HPFS", 4);
290
291} /* end function FSusesLocalTime() */
292
293# ifdef UNICODE_SUPPORT
294local int FSusesLocalTimeW(const wchar_t *path)
295{
296    wchar_t  *tmp0;
297    wchar_t   rootPathName[4];
298    wchar_t   tmp1[MAX_PATH], tmp2[MAX_PATH];
299    DWORD  volSerNo, maxCompLen, fileSysFlags;
300#if defined(__RSXNT__)  /* RSXNT/EMX C rtl uses OEM charset */
301    wchar_t *ansi_path = (wchar_t *)alloca((wcslen(path) + 1) * sizeof(wchar_t));
302
303    CharToAnsiW(path, ansi_path);
304    path = ansi_path;
305#endif
306
307    if (iswalpha(path[0]) && (path[1] == (wchar_t)':'))
308        tmp0 = (wchar_t *)path;
309    else
310    {
311        GetFullPathNameW(path, MAX_PATH, tmp1, &tmp0);
312        tmp0 = &tmp1[0];
313    }
314    wcsncpy(rootPathName, tmp0, 3);   /* Build the root path name, */
315    rootPathName[3] = (wchar_t)'\0';           /* e.g. "A:/"                */
316
317    GetVolumeInformationW(rootPathName, tmp1, (DWORD)MAX_PATH,
318                         &volSerNo, &maxCompLen, &fileSysFlags,
319                         tmp2, (DWORD)MAX_PATH);
320
321    /* Volumes in (V)FAT and (OS/2) HPFS format store file timestamps in
322     * local time!
323     */
324    return !wcsncmp(_wcsupr(tmp2), L"FAT", 3) ||
325           !wcsncmp(tmp2, L"VFAT", 4) ||
326           !wcsncmp(tmp2, L"HPFS", 4);
327
328} /* end function FSusesLocalTimeW() */
329# endif
330
331#endif /* NT_TZBUG_WORKAROUND */
332
333
334#if (defined(USE_EF_UT_TIME) || defined(NT_TZBUG_WORKAROUND))
335
336#if (defined(__GNUC__) || defined(ULONG_LONG_MAX))
337   typedef long long            LLONG64;
338   typedef unsigned long long   ULLNG64;
339#elif (defined(__WATCOMC__) && (__WATCOMC__ >= 1100))
340   typedef __int64              LLONG64;
341   typedef unsigned __int64     ULLNG64;
342#elif (defined(_MSC_VER) && (_MSC_VER >= 1100))
343   typedef __int64              LLONG64;
344   typedef unsigned __int64     ULLNG64;
345#elif (defined(__IBMC__) && (__IBMC__ >= 350))
346   typedef __int64              LLONG64;
347   typedef unsigned __int64     ULLNG64;
348#else
349#  define NO_INT64
350#endif
351
352#  define UNIX_TIME_ZERO_HI  0x019DB1DEUL
353#  define UNIX_TIME_ZERO_LO  0xD53E8000UL
354#  define NT_QUANTA_PER_UNIX 10000000L
355#  define FTQUANTA_PER_UT_L  (NT_QUANTA_PER_UNIX & 0xFFFF)
356#  define FTQUANTA_PER_UT_H  (NT_QUANTA_PER_UNIX >> 16)
357#  define UNIX_TIME_UMAX_HI  0x0236485EUL
358#  define UNIX_TIME_UMAX_LO  0xD4A5E980UL
359#  define UNIX_TIME_SMIN_HI  0x0151669EUL
360#  define UNIX_TIME_SMIN_LO  0xD53E8000UL
361#  define UNIX_TIME_SMAX_HI  0x01E9FD1EUL
362#  define UNIX_TIME_SMAX_LO  0xD4A5E980UL
363
364local int FileTime2utime(FILETIME *pft, time_t *ut)
365{
366#ifndef NO_INT64
367    ULLNG64 NTtime;
368
369    NTtime = ((ULLNG64)pft->dwLowDateTime +
370              ((ULLNG64)pft->dwHighDateTime << 32));
371
372    /* underflow and overflow handling */
373#ifdef CHECK_UTIME_SIGNED_UNSIGNED
374    if ((time_t)0x80000000L < (time_t)0L)
375    {
376        if (NTtime < ((ULLNG64)UNIX_TIME_SMIN_LO +
377                      ((ULLNG64)UNIX_TIME_SMIN_HI << 32))) {
378            *ut = (time_t)LONG_MIN;
379            return FALSE;
380        }
381        if (NTtime > ((ULLNG64)UNIX_TIME_SMAX_LO +
382                      ((ULLNG64)UNIX_TIME_SMAX_HI << 32))) {
383            *ut = (time_t)LONG_MAX;
384            return FALSE;
385        }
386    }
387    else
388#endif /* CHECK_UTIME_SIGNED_UNSIGNED */
389    {
390        if (NTtime < ((ULLNG64)UNIX_TIME_ZERO_LO +
391                      ((ULLNG64)UNIX_TIME_ZERO_HI << 32))) {
392            *ut = (time_t)0;
393            return FALSE;
394        }
395        if (NTtime > ((ULLNG64)UNIX_TIME_UMAX_LO +
396                      ((ULLNG64)UNIX_TIME_UMAX_HI << 32))) {
397            *ut = (time_t)ULONG_MAX;
398            return FALSE;
399        }
400    }
401
402    NTtime -= ((ULLNG64)UNIX_TIME_ZERO_LO +
403               ((ULLNG64)UNIX_TIME_ZERO_HI << 32));
404    *ut = (time_t)(NTtime / (unsigned long)NT_QUANTA_PER_UNIX);
405    return TRUE;
406#else /* NO_INT64 (64-bit integer arithmetics may not be supported) */
407    /* nonzero if `y' is a leap year, else zero */
408#   define leap(y) (((y)%4 == 0 && (y)%100 != 0) || (y)%400 == 0)
409    /* number of leap years from 1970 to `y' (not including `y' itself) */
410#   define nleap(y) (((y)-1969)/4 - ((y)-1901)/100 + ((y)-1601)/400)
411    /* daycount at the end of month[m-1] */
412    static ZCONST ush ydays[] =
413      { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 };
414
415    time_t days;
416    SYSTEMTIME w32tm;
417
418    /* underflow and overflow handling */
419#ifdef CHECK_UTIME_SIGNED_UNSIGNED
420    if ((time_t)0x80000000L < (time_t)0L)
421    {
422        if ((pft->dwHighDateTime < UNIX_TIME_SMIN_HI) ||
423            ((pft->dwHighDateTime == UNIX_TIME_SMIN_HI) &&
424             (pft->dwLowDateTime < UNIX_TIME_SMIN_LO))) {
425            *ut = (time_t)LONG_MIN;
426            return FALSE;
427        if ((pft->dwHighDateTime > UNIX_TIME_SMAX_HI) ||
428            ((pft->dwHighDateTime == UNIX_TIME_SMAX_HI) &&
429             (pft->dwLowDateTime > UNIX_TIME_SMAX_LO))) {
430            *ut = (time_t)LONG_MAX;
431            return FALSE;
432        }
433    }
434    else
435#endif /* CHECK_UTIME_SIGNED_UNSIGNED */
436    {
437        if ((pft->dwHighDateTime < UNIX_TIME_ZERO_HI) ||
438            ((pft->dwHighDateTime == UNIX_TIME_ZERO_HI) &&
439             (pft->dwLowDateTime < UNIX_TIME_ZERO_LO))) {
440            *ut = (time_t)0;
441            return FALSE;
442        }
443        if ((pft->dwHighDateTime > UNIX_TIME_UMAX_HI) ||
444            ((pft->dwHighDateTime == UNIX_TIME_UMAX_HI) &&
445             (pft->dwLowDateTime > UNIX_TIME_UMAX_LO))) {
446            *ut = (time_t)ULONG_MAX;
447            return FALSE;
448        }
449    }
450
451    FileTimeToSystemTime(pft, &w32tm);
452
453    /* set `days' to the number of days into the year */
454    days = w32tm.wDay - 1 + ydays[w32tm.wMonth-1] +
455           (w32tm.wMonth > 2 && leap (w32tm.wYear));
456
457    /* now set `days' to the number of days since 1 Jan 1970 */
458    days += 365 * (time_t)(w32tm.wYear - 1970) +
459            (time_t)(nleap(w32tm.wYear));
460
461    *ut = (time_t)(86400L * days + 3600L * (time_t)w32tm.wHour +
462                   (time_t)(60 * w32tm.wMinute + w32tm.wSecond));
463    return TRUE;
464#endif /* ?NO_INT64 */
465} /* end function FileTime2utime() */
466#endif /* USE_EF_UT_TIME || NT_TZBUG_WORKAROUND */
467
468
469#if (defined(NT_TZBUG_WORKAROUND) && defined(W32_STAT_BANDAID))
470
471local int VFatFileTime2utime(const FILETIME *pft, time_t *ut)
472{
473    FILETIME lft;
474    SYSTEMTIME w32tm;
475    struct tm ltm;
476
477    FileTimeToLocalFileTime(pft, &lft);
478    FileTimeToSystemTime(&lft, &w32tm);
479    /* underflow and overflow handling */
480    /* TODO: The range checks are not accurate, the actual limits may
481     *       be off by one daylight-saving-time shift (typically 1 hour),
482     *       depending on the current state of "is_dst".
483     */
484#ifdef CHECK_UTIME_SIGNED_UNSIGNED
485    if ((time_t)0x80000000L < (time_t)0L)
486    {
487        if ((pft->dwHighDateTime < UNIX_TIME_SMIN_HI) ||
488            ((pft->dwHighDateTime == UNIX_TIME_SMIN_HI) &&
489             (pft->dwLowDateTime < UNIX_TIME_SMIN_LO))) {
490            *ut = (time_t)LONG_MIN;
491            return FALSE;
492        if ((pft->dwHighDateTime > UNIX_TIME_SMAX_HI) ||
493            ((pft->dwHighDateTime == UNIX_TIME_SMAX_HI) &&
494             (pft->dwLowDateTime > UNIX_TIME_SMAX_LO))) {
495            *ut = (time_t)LONG_MAX;
496            return FALSE;
497        }
498    }
499    else
500#endif /* CHECK_UTIME_SIGNED_UNSIGNED */
501    {
502        if ((pft->dwHighDateTime < UNIX_TIME_ZERO_HI) ||
503            ((pft->dwHighDateTime == UNIX_TIME_ZERO_HI) &&
504             (pft->dwLowDateTime < UNIX_TIME_ZERO_LO))) {
505            *ut = (time_t)0;
506            return FALSE;
507        }
508        if ((pft->dwHighDateTime > UNIX_TIME_UMAX_HI) ||
509            ((pft->dwHighDateTime == UNIX_TIME_UMAX_HI) &&
510             (pft->dwLowDateTime > UNIX_TIME_UMAX_LO))) {
511            *ut = (time_t)ULONG_MAX;
512            return FALSE;
513        }
514    }
515    ltm.tm_year = w32tm.wYear - 1900;
516    ltm.tm_mon = w32tm.wMonth - 1;
517    ltm.tm_mday = w32tm.wDay;
518    ltm.tm_hour = w32tm.wHour;
519    ltm.tm_min = w32tm.wMinute;
520    ltm.tm_sec = w32tm.wSecond;
521    ltm.tm_isdst = -1;  /* let mktime determine if DST is in effect */
522    *ut = mktime(&ltm);
523
524    /* a cheap error check: mktime returns "(time_t)-1L" on conversion errors.
525     * Normally, we would have to apply a consistency check because "-1"
526     * could also be a valid time. But, it is quite unlikely to read back odd
527     * time numbers from file systems that store time stamps in DOS format.
528     * (The only known exception is creation time on VFAT partitions.)
529     */
530    return (*ut != (time_t)-1L);
531
532} /* end function VFatFileTime2utime() */
533#endif /* NT_TZBUG_WORKAROUND && W32_STAT_BANDAID */
534
535
536#if 0           /* Currently, this is not used at all */
537
538long GetTheFileTime(char *name, iztimes *z_ut)
539{
540HANDLE h;
541FILETIME Modft, Accft, Creft, lft;
542WORD dh, dl;
543#ifdef __RSXNT__        /* RSXNT/EMX C rtl uses OEM charset */
544  char *ansi_name = (char *)alloca(strlen(name) + 1);
545
546  OemToAnsi(name, ansi_name);
547  name = ansi_name;
548#endif
549
550  h = CreateFile(name, FILE_READ_ATTRIBUTES, FILE_SHARE_READ,
551                 NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
552  if ( h != INVALID_HANDLE_VALUE ) {
553    BOOL ftOK = GetFileTime(h, &Creft, &Accft, &Modft);
554    CloseHandle(h);
555#ifdef USE_EF_UT_TIME
556    if (ftOK && (z_ut != NULL)) {
557      FileTime2utime(&Modft, &(z_ut->mtime));
558      if (Accft.dwLowDateTime != 0 || Accft.dwHighDateTime != 0)
559          FileTime2utime(&Accft, &(z_ut->atime));
560      else
561          z_ut->atime = z_ut->mtime;
562      if (Creft.dwLowDateTime != 0 || Creft.dwHighDateTime != 0)
563          FileTime2utime(&Creft, &(z_ut->ctime));
564      else
565          z_ut->ctime = z_ut->mtime;
566    }
567#endif
568    FileTimeToLocalFileTime(&ft, &lft);
569    FileTimeToDosDateTime(&lft, &dh, &dl);
570    return(dh<<16) | dl;
571  }
572  else
573    return 0L;
574}
575
576#endif /* never */
577
578
579void ChangeNameForFAT(char *name)
580{
581  char *src, *dst, *next, *ptr, *dot, *start;
582  static char invalid[] = ":;,=+\"[]<>| \t";
583
584  if ( isalpha((uch)name[0]) && (name[1] == ':') )
585    start = name + 2;
586  else
587    start = name;
588
589  src = dst = start;
590  if ( (*src == '/') || (*src == '\\') )
591    src++, dst++;
592
593  while ( *src )
594  {
595    for ( next = src; *next && (*next != '/') && (*next != '\\'); next++ );
596
597    for ( ptr = src, dot = NULL; ptr < next; ptr++ )
598      if ( *ptr == '.' )
599      {
600        dot = ptr; /* remember last dot */
601        *ptr = '_';
602      }
603
604    if ( dot == NULL )
605      for ( ptr = src; ptr < next; ptr++ )
606        if ( *ptr == '_' )
607          dot = ptr; /* remember last _ as if it were a dot */
608
609    if ( dot && (dot > src) &&
610         ((next - dot <= 4) ||
611          ((next - src > 8) && (dot - src > 3))) )
612    {
613      if ( dot )
614        *dot = '.';
615
616      for ( ptr = src; (ptr < dot) && ((ptr - src) < 8); ptr++ )
617        *dst++ = *ptr;
618
619      for ( ptr = dot; (ptr < next) && ((ptr - dot) < 4); ptr++ )
620        *dst++ = *ptr;
621    }
622    else
623    {
624      if ( dot && (next - src == 1) )
625        *dot = '.';           /* special case: "." as a path component */
626
627      for ( ptr = src; (ptr < next) && ((ptr - src) < 8); ptr++ )
628        *dst++ = *ptr;
629    }
630
631    *dst++ = *next; /* either '/' or 0 */
632
633    if ( *next )
634    {
635      src = next + 1;
636
637      if ( *src == 0 ) /* handle trailing '/' on dirs ! */
638        *dst = 0;
639    }
640    else
641      break;
642  }
643
644  for ( src = start; *src != 0; ++src )
645    if ( (strchr(invalid, *src) != NULL) || (*src == ' ') )
646      *src = '_';
647}
648
649char *GetLongPathEA(char *name)
650{
651    return(NULL); /* volunteers ? */
652}
653
654#ifdef UNICODE_SUPPORT
655wchar_t *GetLongPathEAW(wchar_t *name)
656{
657    return(NULL); /* volunteers ? */
658}
659#endif
660
661
662int IsFileNameValid(x)
663char *x;
664{
665    WIN32_FIND_DATA fd;
666    HANDLE h;
667#if defined(__RSXNT__)  /* RSXNT/EMX C rtl uses OEM charset */
668    char *ansi_name = (char *)alloca(strlen(x) + 1);
669
670    OemToAnsi(x, ansi_name);
671    x = ansi_name;
672#endif
673
674    if ((h = FindFirstFile(x, &fd)) == INVALID_HANDLE_VALUE)
675        return FALSE;
676    FindClose(h);
677    return TRUE;
678}
679
680char *getVolumeLabel(drive, vtime, vmode, vutim)
681  int drive;    /* drive name: 'A' .. 'Z' or '\0' for current drive */
682  ulg *vtime;   /* volume label creation time (DOS format) */
683  ulg *vmode;   /* volume label file mode */
684  time_t *vutim;/* volume label creationtime (UNIX format) */
685
686/* If a volume label exists for the given drive, return its name and
687   pretend to set its time and mode. The returned name is static data. */
688{
689  char rootpath[4];
690  static char vol[14];
691  DWORD fnlen, flags;
692
693  *vmode = A_ARCHIVE | A_LABEL;           /* this is what msdos returns */
694  *vtime = dostime(1980, 1, 1, 0, 0, 0);  /* no true date info available */
695  *vutim = dos2unixtime(*vtime);
696  strcpy(rootpath, "x:\\");
697  rootpath[0] = (char)drive;
698  if (GetVolumeInformation(drive ? rootpath : NULL, vol, 13, NULL,
699                           &fnlen, &flags, NULL, 0))
700#if defined(__RSXNT__)  /* RSXNT/EMX C rtl uses OEM charset */
701    return (AnsiToOem(vol, vol), vol);
702#else
703    return vol;
704#endif
705  else
706    return NULL;
707}
708
709#endif /* !UTIL */
710
711
712
713int ZipIsWinNT(void)    /* returns TRUE if real NT, FALSE if Win95 or Win32s */
714{
715    static DWORD g_PlatformId = 0xFFFFFFFF; /* saved platform indicator */
716
717    if (g_PlatformId == 0xFFFFFFFF) {
718        /* note: GetVersionEx() doesn't exist on WinNT 3.1 */
719        if (GetVersion() < 0x80000000)
720            g_PlatformId = TRUE;
721        else
722            g_PlatformId = FALSE;
723    }
724    return (int)g_PlatformId;
725}
726
727
728#ifndef UTIL
729#ifdef __WATCOMC__
730#  include <io.h>
731#  define _get_osfhandle _os_handle
732/* gaah -- Watcom's docs claim that _get_osfhandle exists, but it doesn't.  */
733#endif
734
735#ifdef HAVE_FSEEKABLE
736/*
737 * return TRUE if file is seekable
738 */
739int fseekable(fp)
740FILE *fp;
741{
742    return GetFileType((HANDLE)_get_osfhandle(fileno(fp))) == FILE_TYPE_DISK;
743}
744#endif /* HAVE_FSEEKABLE */
745#endif /* !UTIL */
746
747
748#if 0 /* seems to be never used; try it out... */
749char *StringLower(char *szArg)
750{
751  char *szPtr;
752/*  unsigned char *szPtr; */
753  for ( szPtr = szArg; *szPtr; szPtr++ )
754    *szPtr = lower[*szPtr];
755  return szArg;
756}
757#endif /* never */
758
759
760
761#ifdef W32_STAT_BANDAID
762
763/* All currently known variants of WIN32 operating systems (Windows 95/98,
764 * WinNT 3.x, 4.0, 5.0) have a nasty bug in the OS kernel concerning
765 * conversions between UTC and local time: In the time conversion functions
766 * of the Win32 API, the timezone offset (including seasonal daylight saving
767 * shift) between UTC and local time evaluation is erratically based on the
768 * current system time. The correct evaluation must determine the offset
769 * value as it {was/is/will be} for the actual time to be converted.
770 *
771 * The C runtime lib's stat() returns utc time-stamps so that
772 * localtime(timestamp) corresponds to the (potentially false) local
773 * time shown by the OS' system programs (Explorer, command shell dir, etc.)
774 *
775 * For the NTFS file system (and other filesystems that store time-stamps
776 * as UTC values), this results in st_mtime (, st_{c|a}time) fields which
777 * are not stable but vary according to the seasonal change of "daylight
778 * saving time in effect / not in effect".
779 *
780 * To achieve timestamp consistency of UTC (UT extra field) values in
781 * Zip archives, the Info-ZIP programs require work-around code for
782 * proper time handling in stat() (and other time handling routines).
783 */
784/* stat() functions under Windows95 tend to fail for root directories.   *
785 * Watcom and Borland, at least, are affected by this bug.  Watcom made  *
786 * a partial fix for 11.0 but still missed some cases.  This substitute  *
787 * detects the case and fills in reasonable values.  Otherwise we get    *
788 * effects like failure to extract to a root dir because it's not found. */
789
790#ifdef LARGE_FILE_SUPPORT         /* E. Gordon 9/12/03 */
791 int zstat_zipwin32(const char *path, z_stat *buf)
792#else
793 int zstat_zipwin32(const char *path, struct stat *buf)
794#endif
795{
796# ifdef LARGE_FILE_SUPPORT         /* E. Gordon 9/12/03 */
797    if (!zstat(path, buf))
798# else
799    if (!stat(path, buf))
800# endif
801    {
802#ifdef NT_TZBUG_WORKAROUND
803        /* stat was successful, now redo the time-stamp fetches */
804        int fs_uses_loctime = FSusesLocalTime(path);
805        HANDLE h;
806        FILETIME Modft, Accft, Creft;
807#if defined(__RSXNT__)  /* RSXNT/EMX C rtl uses OEM charset */
808        char *ansi_path = (char *)alloca(strlen(path) + 1);
809
810        OemToAnsi(path, ansi_path);
811#       define Ansi_Path  ansi_path
812#else
813#       define Ansi_Path  path
814#endif
815
816        Trace((stdout, "stat(%s) finds modtime %08lx\n", path, buf->st_mtime));
817        h = CreateFile(Ansi_Path, FILE_READ_ATTRIBUTES, FILE_SHARE_READ,
818                       NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
819        if (h != INVALID_HANDLE_VALUE) {
820            BOOL ftOK = GetFileTime(h, &Creft, &Accft, &Modft);
821            CloseHandle(h);
822
823            if (ftOK) {
824                if (!fs_uses_loctime) {
825                    /*  On a filesystem that stores UTC timestamps, we refill
826                     *  the time fields of the struct stat buffer by directly
827                     *  using the UTC values as returned by the Win32
828                     *  GetFileTime() API call.
829                     */
830                    FileTime2utime(&Modft, &(buf->st_mtime));
831                    if (Accft.dwLowDateTime != 0 || Accft.dwHighDateTime != 0)
832                        FileTime2utime(&Accft, &(buf->st_atime));
833                    else
834                        buf->st_atime = buf->st_mtime;
835                    if (Creft.dwLowDateTime != 0 || Creft.dwHighDateTime != 0)
836                        FileTime2utime(&Creft, &(buf->st_ctime));
837                    else
838                        buf->st_ctime = buf->st_mtime;
839                    Tracev((stdout,"NTFS, recalculated modtime %08lx\n",
840                            buf->st_mtime));
841                } else {
842                    /*  On VFAT and FAT-like filesystems, the FILETIME values
843                     *  are converted back to the stable local time before
844                     *  converting them to UTC unix time-stamps.
845                     */
846                    VFatFileTime2utime(&Modft, &(buf->st_mtime));
847                    if (Accft.dwLowDateTime != 0 || Accft.dwHighDateTime != 0)
848                        VFatFileTime2utime(&Accft, &(buf->st_atime));
849                    else
850                        buf->st_atime = buf->st_mtime;
851                    if (Creft.dwLowDateTime != 0 || Creft.dwHighDateTime != 0)
852                        VFatFileTime2utime(&Creft, &(buf->st_ctime));
853                    else
854                        buf->st_ctime = buf->st_mtime;
855                    Tracev((stdout, "VFAT, recalculated modtime %08lx\n",
856                            buf->st_mtime));
857                }
858            }
859        }
860#       undef Ansi_Path
861#endif /* NT_TZBUG_WORKAROUND */
862        return 0;
863    }
864#ifdef W32_STATROOT_FIX
865    else
866    {
867        DWORD flags;
868#if defined(__RSXNT__)  /* RSXNT/EMX C rtl uses OEM charset */
869        char *ansi_path = (char *)alloca(strlen(path) + 1);
870
871        OemToAnsi(path, ansi_path);
872#       define Ansi_Path  ansi_path
873#else
874#       define Ansi_Path  path
875#endif
876
877        flags = GetFileAttributes(Ansi_Path);
878        if (flags != 0xFFFFFFFF && flags & FILE_ATTRIBUTE_DIRECTORY) {
879            Trace((stderr, "\nstat(\"%s\",...) failed on existing directory\n",
880                   path));
881#ifdef LARGE_FILE_SUPPORT         /* E. Gordon 9/12/03 */
882            memset(buf, 0, sizeof(z_stat));
883#else
884            memset(buf, 0, sizeof(struct stat));
885#endif
886            buf->st_atime = buf->st_ctime = buf->st_mtime =
887              dos2unixtime(DOSTIME_MINIMUM);
888            /* !!!   you MUST NOT add a cast to the type of "st_mode" here;
889             * !!!   different compilers do not agree on the "st_mode" size!
890             * !!!   (And, some compiler may not declare the "mode_t" type
891             * !!!   identifier, so you cannot use it, either.)
892             */
893            buf->st_mode = S_IFDIR | S_IREAD |
894                           ((flags & FILE_ATTRIBUTE_READONLY) ? 0 : S_IWRITE);
895            return 0;
896        } /* assumes: stat() won't fail on non-dirs without good reason */
897#       undef Ansi_Path
898    }
899#endif /* W32_STATROOT_FIX */
900    return -1;
901}
902
903
904# ifdef UNICODE_SUPPORT
905
906int zstat_zipwin32w(const wchar_t *pathw, zw_stat *buf)
907{
908    if (!zwstat(pathw, buf))
909    {
910#ifdef NT_TZBUG_WORKAROUND
911        /* stat was successful, now redo the time-stamp fetches */
912        int fs_uses_loctime = FSusesLocalTimeW(pathw);
913        HANDLE h;
914        FILETIME Modft, Accft, Creft;
915#if defined(__RSXNT__)  /* RSXNT/EMX C rtl uses OEM charset */
916        char *ansi_path = (char *)alloca(strlen(pathw) + 1);
917
918        OemToAnsi(path, ansi_path);
919#       define Ansi_Path  ansi_path
920#else
921#       define Ansi_Path  pathw
922#endif
923
924        Trace((stdout, "stat(%s) finds modtime %08lx\n", pathw, buf->st_mtime));
925        h = CreateFileW(Ansi_Path, FILE_READ_ATTRIBUTES, FILE_SHARE_READ,
926                       NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
927        if (h != INVALID_HANDLE_VALUE) {
928            BOOL ftOK = GetFileTime(h, &Creft, &Accft, &Modft);
929            CloseHandle(h);
930
931            if (ftOK) {
932                if (!fs_uses_loctime) {
933                    /*  On a filesystem that stores UTC timestamps, we refill
934                     *  the time fields of the struct stat buffer by directly
935                     *  using the UTC values as returned by the Win32
936                     *  GetFileTime() API call.
937                     */
938                    FileTime2utime(&Modft, &(buf->st_mtime));
939                    if (Accft.dwLowDateTime != 0 || Accft.dwHighDateTime != 0)
940                        FileTime2utime(&Accft, &(buf->st_atime));
941                    else
942                        buf->st_atime = buf->st_mtime;
943                    if (Creft.dwLowDateTime != 0 || Creft.dwHighDateTime != 0)
944                        FileTime2utime(&Creft, &(buf->st_ctime));
945                    else
946                        buf->st_ctime = buf->st_mtime;
947                    Tracev((stdout,"NTFS, recalculated modtime %08lx\n",
948                            buf->st_mtime));
949                } else {
950                    /*  On VFAT and FAT-like filesystems, the FILETIME values
951                     *  are converted back to the stable local time before
952                     *  converting them to UTC unix time-stamps.
953                     */
954                    VFatFileTime2utime(&Modft, &(buf->st_mtime));
955                    if (Accft.dwLowDateTime != 0 || Accft.dwHighDateTime != 0)
956                        VFatFileTime2utime(&Accft, &(buf->st_atime));
957                    else
958                        buf->st_atime = buf->st_mtime;
959                    if (Creft.dwLowDateTime != 0 || Creft.dwHighDateTime != 0)
960                        VFatFileTime2utime(&Creft, &(buf->st_ctime));
961                    else
962                        buf->st_ctime = buf->st_mtime;
963                    Tracev((stdout, "VFAT, recalculated modtime %08lx\n",
964                            buf->st_mtime));
965                }
966            }
967        }
968#       undef Ansi_Path
969#endif /* NT_TZBUG_WORKAROUND */
970        return 0;
971    }
972#ifdef W32_STATROOT_FIX
973    else
974    {
975        DWORD flags;
976#if defined(__RSXNT__)  /* RSXNT/EMX C rtl uses OEM charset */
977        char *ansi_path = (char *)alloca(strlen(pathw) + 1);
978
979        OemToAnsi(path, ansi_path);
980#       define Ansi_Path  ansi_path
981#else
982#       define Ansi_Path  pathw
983#endif
984
985        flags = GetFileAttributesW(Ansi_Path);
986        if (flags != 0xFFFFFFFF && flags & FILE_ATTRIBUTE_DIRECTORY) {
987            Trace((stderr, "\nstat(\"%s\",...) failed on existing directory\n",
988                   pathw));
989#ifdef LARGE_FILE_SUPPORT         /* E. Gordon 9/12/03 */
990            memset(buf, 0, sizeof(z_stat));
991#else
992            memset(buf, 0, sizeof(struct stat));
993#endif
994            buf->st_atime = buf->st_ctime = buf->st_mtime =
995              dos2unixtime(DOSTIME_MINIMUM);
996            /* !!!   you MUST NOT add a cast to the type of "st_mode" here;
997             * !!!   different compilers do not agree on the "st_mode" size!
998             * !!!   (And, some compiler may not declare the "mode_t" type
999             * !!!   identifier, so you cannot use it, either.)
1000             */
1001            buf->st_mode = S_IFDIR | S_IREAD |
1002                           ((flags & FILE_ATTRIBUTE_READONLY) ? 0 : S_IWRITE);
1003            return 0;
1004        } /* assumes: stat() won't fail on non-dirs without good reason */
1005#       undef Ansi_Path
1006    }
1007#endif /* W32_STATROOT_FIX */
1008    return -1;
1009}
1010
1011# endif
1012
1013
1014#endif /* W32_STAT_BANDAID */
1015
1016
1017
1018#ifdef W32_USE_IZ_TIMEZONE
1019#include "timezone.h"
1020#define SECSPERMIN      60
1021#define MINSPERHOUR     60
1022#define SECSPERHOUR     (SECSPERMIN * MINSPERHOUR)
1023static void conv_to_rule(LPSYSTEMTIME lpw32tm, struct rule * ZCONST ptrule);
1024
1025static void conv_to_rule(LPSYSTEMTIME lpw32tm, struct rule * ZCONST ptrule)
1026{
1027    if (lpw32tm->wYear != 0) {
1028        ptrule->r_type = JULIAN_DAY;
1029        ptrule->r_day = ydays[lpw32tm->wMonth - 1] + lpw32tm->wDay;
1030    } else {
1031        ptrule->r_type = MONTH_NTH_DAY_OF_WEEK;
1032        ptrule->r_mon = lpw32tm->wMonth;
1033        ptrule->r_day = lpw32tm->wDayOfWeek;
1034        ptrule->r_week = lpw32tm->wDay;
1035    }
1036    ptrule->r_time = (long)lpw32tm->wHour * SECSPERHOUR +
1037                     (long)(lpw32tm->wMinute * SECSPERMIN) +
1038                     (long)lpw32tm->wSecond;
1039}
1040
1041int GetPlatformLocalTimezone(register struct state * ZCONST sp,
1042        void (*fill_tzstate_from_rules)(struct state * ZCONST sp_res,
1043                                        ZCONST struct rule * ZCONST start,
1044                                        ZCONST struct rule * ZCONST end))
1045{
1046    TIME_ZONE_INFORMATION tzinfo;
1047    DWORD res;
1048
1049    /* read current timezone settings from registry if TZ envvar missing */
1050    res = GetTimeZoneInformation(&tzinfo);
1051    if (res != TIME_ZONE_ID_INVALID)
1052    {
1053        struct rule startrule, stoprule;
1054
1055        conv_to_rule(&(tzinfo.StandardDate), &stoprule);
1056        conv_to_rule(&(tzinfo.DaylightDate), &startrule);
1057        sp->timecnt = 0;
1058        sp->ttis[0].tt_abbrind = 0;
1059        if ((sp->charcnt =
1060             WideCharToMultiByte(CP_ACP, 0, tzinfo.StandardName, -1,
1061                                 sp->chars, sizeof(sp->chars), NULL, NULL))
1062            == 0)
1063            sp->chars[sp->charcnt++] = '\0';
1064        sp->ttis[1].tt_abbrind = sp->charcnt;
1065        sp->charcnt +=
1066            WideCharToMultiByte(CP_ACP, 0, tzinfo.DaylightName, -1,
1067                                sp->chars + sp->charcnt,
1068                                sizeof(sp->chars) - sp->charcnt, NULL, NULL);
1069        if ((sp->charcnt - sp->ttis[1].tt_abbrind) == 0)
1070            sp->chars[sp->charcnt++] = '\0';
1071        sp->ttis[0].tt_gmtoff = - (tzinfo.Bias + tzinfo.StandardBias)
1072                                * MINSPERHOUR;
1073        sp->ttis[1].tt_gmtoff = - (tzinfo.Bias + tzinfo.DaylightBias)
1074                                * MINSPERHOUR;
1075        sp->ttis[0].tt_isdst = 0;
1076        sp->ttis[1].tt_isdst = 1;
1077        sp->typecnt = (startrule.r_mon == 0 && stoprule.r_mon == 0) ? 1 : 2;
1078
1079        if (sp->typecnt > 1)
1080            (*fill_tzstate_from_rules)(sp, &startrule, &stoprule);
1081        return TRUE;
1082    }
1083    return FALSE;
1084}
1085#endif /* W32_USE_IZ_TIMEZONE */
1086
1087
1088
1089#ifndef WINDLL
1090/* This replacement getch() function was originally created for Watcom C
1091 * and then additionally used with CYGWIN. Since UnZip 5.4, all other Win32
1092 * ports apply this replacement rather that their supplied getch() (or
1093 * alike) function.  There are problems with unabsorbed LF characters left
1094 * over in the keyboard buffer under Win95 (and 98) when ENTER was pressed.
1095 * (Under Win95, ENTER returns two(!!) characters: CR-LF.)  This problem
1096 * does not appear when run on a WinNT console prompt!
1097 */
1098
1099/* Watcom 10.6's getch() does not handle Alt+<digit><digit><digit>. */
1100/* Note that if PASSWD_FROM_STDIN is defined, the file containing   */
1101/* the password must have a carriage return after the word, not a   */
1102/* Unix-style newline (linefeed only).  This discards linefeeds.    */
1103
1104int getch_win32(void)
1105{
1106  HANDLE stin;
1107  DWORD rc;
1108  unsigned char buf[2];
1109  int ret = -1;
1110  DWORD odemode = ~(DWORD)0;
1111
1112#  ifdef PASSWD_FROM_STDIN
1113  stin = GetStdHandle(STD_INPUT_HANDLE);
1114#  else
1115  stin = CreateFile("CONIN$", GENERIC_READ | GENERIC_WRITE,
1116                    FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
1117  if (stin == INVALID_HANDLE_VALUE)
1118    return -1;
1119#  endif
1120  if (GetConsoleMode(stin, &odemode))
1121    SetConsoleMode(stin, ENABLE_PROCESSED_INPUT);  /* raw except ^C noticed */
1122  if (ReadFile(stin, &buf, 1, &rc, NULL) && rc == 1)
1123    ret = buf[0];
1124  /* when the user hits return we get CR LF.  We discard the LF, not the CR,
1125   * because when we call this for the first time after a previous input
1126   * such as the one for "replace foo? [y]es, ..." the LF may still be in
1127   * the input stream before whatever the user types at our prompt. */
1128  if (ret == '\n')
1129    if (ReadFile(stin, &buf, 1, &rc, NULL) && rc == 1)
1130      ret = buf[0];
1131  if (odemode != ~(DWORD)0)
1132    SetConsoleMode(stin, odemode);
1133#  ifndef PASSWD_FROM_STDIN
1134  CloseHandle(stin);
1135#  endif
1136  return ret;
1137}
1138
1139
1140
1141/******************************/
1142/*  Function version_local()  */
1143/******************************/
1144
1145void version_local()
1146{
1147    static ZCONST char CompiledWith[] = "Compiled with %s%s for %s%s%s.\n\n";
1148#if (defined(_MSC_VER) || defined(__WATCOMC__) || defined(__DJGPP__))
1149    char buf[80];
1150#if (defined(_MSC_VER) && (_MSC_VER > 900))
1151    char buf2[80];
1152#endif
1153#endif
1154
1155/* Define the compiler name and version strings */
1156#if defined(_MSC_VER)  /* MSC == MSVC++, including the SDK compiler */
1157    sprintf(buf, "Microsoft C %d.%02d ", _MSC_VER/100, _MSC_VER%100);
1158#  define COMPILER_NAME1        buf
1159#  if (_MSC_VER == 800)
1160#    define COMPILER_NAME2      "(Visual C++ v1.1)"
1161#  elif (_MSC_VER == 850)
1162#    define COMPILER_NAME2      "(Windows NT v3.5 SDK)"
1163#  elif (_MSC_VER == 900)
1164#    define COMPILER_NAME2      "(Visual C++ v2.x)"
1165#  elif (_MSC_VER > 900)
1166    sprintf(buf2, "(Visual C++ v%d.%d)", _MSC_VER/100 - 6, _MSC_VER%100/10);
1167#    define COMPILER_NAME2      buf2
1168#  else
1169#    define COMPILER_NAME2      "(bad version)"
1170#  endif
1171#elif defined(__WATCOMC__)
1172#  if (__WATCOMC__ % 10 > 0)
1173/* We do this silly test because __WATCOMC__ gives two digits for the  */
1174/* minor version, but Watcom packaging prefers to show only one digit. */
1175    sprintf(buf, "Watcom C/C++ %d.%02d", __WATCOMC__ / 100,
1176            __WATCOMC__ % 100);
1177#  else
1178    sprintf(buf, "Watcom C/C++ %d.%d", __WATCOMC__ / 100,
1179            (__WATCOMC__ % 100) / 10);
1180#  endif /* __WATCOMC__ % 10 > 0 */
1181#  define COMPILER_NAME1        buf
1182#  define COMPILER_NAME2        ""
1183#elif defined(__TURBOC__)
1184#  ifdef __BORLANDC__
1185#    define COMPILER_NAME1      "Borland C++"
1186#    if (__BORLANDC__ == 0x0452)   /* __BCPLUSPLUS__ = 0x0320 */
1187#      define COMPILER_NAME2    " 4.0 or 4.02"
1188#    elif (__BORLANDC__ == 0x0460)   /* __BCPLUSPLUS__ = 0x0340 */
1189#      define COMPILER_NAME2    " 4.5"
1190#    elif (__BORLANDC__ == 0x0500)   /* __TURBOC__ = 0x0500 */
1191#      define COMPILER_NAME2    " 5.0"
1192#    elif (__BORLANDC__ == 0x0520)   /* __TURBOC__ = 0x0520 */
1193#      define COMPILER_NAME2    " 5.2 (C++ Builder 1.0)"
1194#    elif (__BORLANDC__ == 0x0530)   /* __BCPLUSPLUS__ = 0x0530 */
1195#      define COMPILER_NAME2    " 5.3 (C++ Builder 3.0)"
1196#    elif (__BORLANDC__ == 0x0540)   /* __BCPLUSPLUS__ = 0x0540 */
1197#      define COMPILER_NAME2    " 5.4 (C++ Builder 4.0)"
1198#    elif (__BORLANDC__ == 0x0550)   /* __BCPLUSPLUS__ = 0x0550 */
1199#      define COMPILER_NAME2    " 5.5 (C++ Builder 5.0)"
1200#    elif (__BORLANDC__ == 0x0551)   /* __BCPLUSPLUS__ = 0x0551 */
1201#      define COMPILER_NAME2    " 5.5.1 (C++ Builder 5.0.1)"
1202#    elif (__BORLANDC__ == 0x0560)   /* __BCPLUSPLUS__ = 0x0560 */
1203#      define COMPILER_NAME2    " 5.6 (C++ Builder 6)"
1204#    else
1205#      define COMPILER_NAME2    " later than 5.6"
1206#    endif
1207#  else /* !__BORLANDC__ */
1208#    define COMPILER_NAME1      "Turbo C"
1209#    if (__TURBOC__ >= 0x0400)     /* Kevin:  3.0 -> 0x0401 */
1210#      define COMPILER_NAME2    "++ 3.0 or later"
1211#    elif (__TURBOC__ == 0x0295)     /* [661] vfy'd by Kevin */
1212#      define COMPILER_NAME2    "++ 1.0"
1213#    endif
1214#  endif /* __BORLANDC__ */
1215#elif defined(__GNUC__)
1216#  ifdef __RSXNT__
1217#    if (defined(__DJGPP__) && !defined(__EMX__))
1218    sprintf(buf, "rsxnt(djgpp v%d.%02d) / gcc ",
1219            __DJGPP__, __DJGPP_MINOR__);
1220#      define COMPILER_NAME1    buf
1221#    elif defined(__DJGPP__)
1222   sprintf(buf, "rsxnt(emx+djgpp v%d.%02d) / gcc ",
1223            __DJGPP__, __DJGPP_MINOR__);
1224#      define COMPILER_NAME1    buf
1225#    elif (defined(__GO32__) && !defined(__EMX__))
1226#      define COMPILER_NAME1    "rsxnt(djgpp v1.x) / gcc "
1227#    elif defined(__GO32__)
1228#      define COMPILER_NAME1    "rsxnt(emx + djgpp v1.x) / gcc "
1229#    elif defined(__EMX__)
1230#      define COMPILER_NAME1    "rsxnt(emx)+gcc "
1231#    else
1232#      define COMPILER_NAME1    "rsxnt(unknown) / gcc "
1233#    endif
1234#  elif defined(__CYGWIN__)
1235#      define COMPILER_NAME1    "Cygnus win32 / gcc "
1236#  elif defined(__MINGW32__)
1237#      define COMPILER_NAME1    "mingw32 / gcc "
1238#  else
1239#      define COMPILER_NAME1    "gcc "
1240#  endif
1241#  define COMPILER_NAME2        __VERSION__
1242#elif defined(__LCC__)
1243#  define COMPILER_NAME1        "LCC-Win32"
1244#  define COMPILER_NAME2        ""
1245#else
1246#  define COMPILER_NAME1        "unknown compiler (SDK?)"
1247#  define COMPILER_NAME2        ""
1248#endif
1249
1250/* Define the compile date string */
1251#ifdef __DATE__
1252#  define COMPILE_DATE " on " __DATE__
1253#else
1254#  define COMPILE_DATE ""
1255#endif
1256
1257    printf(CompiledWith, COMPILER_NAME1, COMPILER_NAME2,
1258           "\nWindows 9x / Windows NT", " (32-bit)", COMPILE_DATE);
1259
1260    return;
1261
1262} /* end function version_local() */
1263#endif /* !WINDLL */
1264
1265
1266/* --------------------------------------------------- */
1267/* Large File Support
1268 *
1269 * Moved to Win32i64.c to avoid conflicts in same name functions
1270 * in WiZ using UnZip and Zip libraries.
1271 * 9/25/2003
1272 */
1273
1274
1275/* --------------------------------------------------- */
1276/* Unicode Support for Win32
1277 *
1278 */
1279
1280#ifdef UNICODE_SUPPORT
1281# if 0
1282
1283  /* get the wide command line and convert to argvw */
1284  /* windows ignores argv and gets argvw separately */
1285  zchar **get_wide_argv(argv)
1286    char **argv;
1287  {
1288    int i;
1289    int argc;
1290    int size;
1291    zchar **argvw = NULL;
1292    zchar *commandline = NULL;
1293    zchar **a = NULL;
1294
1295    commandline = GetCommandLineW();
1296    a = CommandLineToArgvW(commandline, &argc);
1297
1298    if (a == NULL) {
1299      /* failed */
1300      ZIPERR(ZE_COMPERR, "get_wide_argv");
1301    }
1302
1303    /* copy args so can use free_args() */
1304    if ((argvw = (zchar **)malloc((argc + 1) * sizeof(zchar *))) == NULL) {
1305      ZIPERR(ZE_MEM, "get_wide_argv");
1306    }
1307    for (i = 0; i < argc; i++) {
1308      size = zstrlen(a[i]) + 1;
1309      if ((argvw[i] = (zchar *)malloc(size * sizeof(zchar))) == NULL) {
1310        ZIPERR(ZE_MEM, "get_wide_argv");
1311      }
1312      if ((argvw[i] = copy_zstring(a[i])) == NULL) {
1313        ZIPERR(ZE_MEM, "get_wide_argv");
1314      }
1315    }
1316    argvw[argc] = L'\0';
1317
1318    /* free original argvw */
1319    LocalFree(a);
1320
1321    return argvw;
1322  }
1323# endif
1324
1325
1326/* convert wide character string to multi-byte character string */
1327/* win32 version */
1328char *wide_to_local_string(wide_string)
1329  zwchar *wide_string;
1330{
1331  int i;
1332  wchar_t wc;
1333  int bytes_char;
1334  int default_used;
1335  int wsize = 0;
1336  int max_bytes = 9;
1337  char buf[9];
1338  char *buffer = NULL;
1339  char *local_string = NULL;
1340
1341  if (wide_string == NULL)
1342    return NULL;
1343
1344  for (wsize = 0; wide_string[wsize]; wsize++) ;
1345
1346  if (max_bytes < MB_CUR_MAX)
1347    max_bytes = MB_CUR_MAX;
1348
1349  if ((buffer = (char *)malloc(wsize * max_bytes + 1)) == NULL) {
1350    ZIPERR(ZE_MEM, "wide_to_local_string");
1351  }
1352
1353  /* convert it */
1354  buffer[0] = '\0';
1355  for (i = 0; i < wsize; i++) {
1356    if (sizeof(wchar_t) < 4 && wide_string[i] > 0xFFFF) {
1357      /* wchar_t probably 2 bytes */
1358      /* could do surrogates if state_dependent and wctomb can do */
1359      wc = zwchar_to_wchar_t_default_char;
1360    } else {
1361      wc = (wchar_t)wide_string[i];
1362    }
1363    /* Unter some vendor's C-RTL, the Wide-to-MultiByte conversion functions
1364     * (like wctomb() et. al.) do not use the same codepage as the other
1365     * string arguments I/O functions (fopen, mkdir, rmdir etc.).
1366     * Therefore, we have to fall back to the underlying Win32-API call to
1367     * achieve a consistent behaviour for all supported compiler environments.
1368     * Failing RTLs are for example:
1369     *   Borland (locale uses OEM-CP as default, but I/O functions expect ANSI
1370     *            names)
1371     *   Watcom  (only "C" locale, wctomb() always uses OEM CP)
1372     * (in other words: all supported environments except the Microsoft RTLs)
1373     */
1374    bytes_char = WideCharToMultiByte(
1375                          CP_ACP, WC_COMPOSITECHECK,
1376                          &wc, 1,
1377                          (LPSTR)buf, sizeof(buf),
1378                          NULL, &default_used);
1379    if (default_used)
1380      bytes_char = -1;
1381    if (unicode_escape_all) {
1382      if (bytes_char == 1 && (uch)buf[0] <= 0x7f) {
1383        /* ASCII */
1384        strncat(buffer, buf, 1);
1385      } else {
1386        /* use escape for wide character */
1387        char *e = wide_char_to_escape_string(wide_string[i]);
1388        strcat(buffer, e);
1389        free(e);
1390      }
1391    } else if (bytes_char > 0) {
1392      /* multi-byte char */
1393      strncat(buffer, buf, bytes_char);
1394    } else {
1395      /* no MB for this wide */
1396      if (use_wide_to_mb_default) {
1397        /* default character */
1398        strcat(buffer, wide_to_mb_default_string);
1399      } else {
1400        /* use escape for wide character */
1401        char *e = wide_char_to_escape_string(wide_string[i]);
1402        strcat(buffer, e);
1403        free(e);
1404      }
1405    }
1406  }
1407  if ((local_string = (char *)realloc(buffer, strlen(buffer) + 1)) == NULL) {
1408    free(buffer);
1409    ZIPERR(ZE_MEM, "wide_to_local_string");
1410  }
1411
1412  return local_string;
1413}
1414
1415/* convert multi-byte character string to wide character string */
1416/* win32 version */
1417zwchar *local_to_wide_string(local_string)
1418  char *local_string;
1419{
1420  int wsize;
1421  wchar_t *wc_string;
1422  zwchar *wide_string;
1423
1424  /* for now try to convert as string - fails if a bad char in string */
1425  wsize = MultiByteToWideChar(CP_ACP, 0,
1426                        local_string, -1, NULL, 0);
1427  if (wsize == (size_t)-1) {
1428    /* could not convert */
1429    return NULL;
1430  }
1431
1432  /* convert it */
1433  if ((wc_string = (wchar_t *)malloc((wsize + 1) * sizeof(wchar_t))) == NULL) {
1434    ZIPERR(ZE_MEM, "local_to_wide_string");
1435  }
1436  wsize = MultiByteToWideChar(CP_ACP, 0,
1437           local_string, -1,
1438           wc_string, wsize + 1);
1439  wc_string[wsize] = (wchar_t) 0;
1440
1441  /* in case wchar_t is not zwchar */
1442  if ((wide_string = (zwchar *)malloc((wsize + 1) * sizeof(zwchar))) == NULL) {
1443    free(wc_string);
1444    ZIPERR(ZE_MEM, "local_to_wide_string");
1445  }
1446  for (wsize = 0; (wide_string[wsize] = (zwchar)wc_string[wsize]); wsize++) ;
1447  wide_string[wsize] = (zwchar)0;
1448  free(wc_string);
1449
1450  return wide_string;
1451}
1452#endif /* UNICODE_SUPPORT */
1453
1454
1455/*
1456# if defined(UNICODE_SUPPORT) || defined(WIN32_OEM)
1457*/
1458/* convert oem to ansi character string */
1459char *oem_to_local_string(local_string, oem_string)
1460  char *local_string;
1461  char *oem_string;
1462{
1463  /* convert OEM to ANSI character set */
1464  OemToChar(oem_string, local_string);
1465
1466  return local_string;
1467}
1468/*
1469# endif
1470*/
1471
1472
1473/*
1474#if defined(UNICODE_SUPPORT) || defined(WIN32_OEM)
1475*/
1476/* convert local to oem character string */
1477char *local_to_oem_string(oem_string, local_string)
1478  char *oem_string;
1479  char *local_string;
1480{
1481  /* convert to OEM display character set */
1482  CharToOem(local_string, oem_string);
1483  return oem_string;
1484}
1485/*
1486#endif
1487*/
1488
1489