1 /*
2   Copyright (c) 1990-2009 Info-ZIP.  All rights reserved.
3 
4   See the accompanying file LICENSE, version 2009-Jan-02 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 // File:        INTRFACE.CPP
12 //
13 // Description: This module acts as the interface between the Info-ZIP code and
14 //              our Windows code in WINMAIN.CPP.  We expose the needed
15 //              functions to query a file list, test file(s), extract file(s),
16 //              and display a zip file comment.  The windows code is never
17 //              bothered with understanding the Globals structure "Uz_Globs".
18 //
19 //              This module also catches all the callbacks from the Info-ZIP
20 //              code, cleans up the data provided in the callback, and then
21 //              forwards the information to the appropriate function in the
22 //              windows code.  These callbacks include status messages, file
23 //              lists, comments, password prompt, and file overwrite prompts.
24 //
25 //              Finally, this module implements the few functions that the
26 //              Info-ZIP code expects the port to implement. These functions are
27 //              OS dependent and are mostly related to validating file names and
28 //              directories, and setting file attributes and dates of saved files.
29 //
30 // Copyright:   All the source files for Pocket UnZip, except for components
31 //              written by the Info-ZIP group, are copyrighted 1997 by Steve P.
32 //              Miller.  As of June 1999, Steve P. Miller has agreed to apply
33 //              the Info-ZIP License (see citation on top of this module)
34 //              to his work.  See the contents of this License for terms
35 //              and conditon of using the product "Pocket UnZip".
36 //
37 // Disclaimer:  All project files are provided "as is" with no guarantee of
38 //              their correctness.  The authors are not liable for any outcome
39 //              that is the result of using this source.  The source for Pocket
40 //              UnZip has been placed in the public domain to help provide an
41 //              understanding of its implementation.  You are hereby granted
42 //              full permission to use this source in any way you wish, except
43 //              to alter Pocket UnZip itself.  For comments, suggestions, and
44 //              bug reports, please write to stevemil@pobox.com or the Info-ZIP
45 //              mailing list Zip-Bugs@lists.wku.edu.
46 //
47 // Functions:   DoListFiles
48 //              DoExtractOrTestFiles
49 //              DoGetComment
50 //              SetExtractToDirectory
51 //              InitGlobals
52 //              FreeGlobals
53 //              ExtractOrTestFilesThread
54 //              IsFileOrDirectory
55 //              SmartCreateDirectory
56 //              CheckForAbort2
57 //              SetCurrentFile
58 //              UzpMessagePrnt2
59 //              UzpInput2
60 //              UzpMorePause
61 //              UzpPassword
62 //              UzpReplace
63 //              UzpSound
64 //              SendAppMsg
65 //              win_fprintf
66 //              test_NT
67 //              utimeToFileTime
68 //              GetFileTimes
69 //              IsOldFileSystem
70 //              SetFileSize
71 //              close_outfile
72 //              do_wild
73 //              mapattr
74 //              mapname
75 //              checkdir
76 //              match
77 //              iswild
78 //              conv_to_rule
79 //              GetPlatformLocalTimezone
80 //              wide_to_local_string
81 //
82 //
83 // Date      Name          History
84 // --------  ------------  -----------------------------------------------------
85 // 02/01/97  Steve Miller  Created (Version 1.0 using Info-ZIP UnZip 5.30)
86 // 08/01/99  Johnny Lee, Christian Spieler, Steve Miller, and others
87 //                         Adapted to UnZip 5.41 (Version 1.1)
88 // 12/01/02  Chr. Spieler  Updated interface for UnZip 5.50
89 // 02/23/05  Chr. Spieler  Modified and optimized utimeToFileTime() to support
90 //                         the NO_W32TIMES_IZFIX compilation option
91 // 11/01/09  Chr. Spieler  Added wide_to_local_string() conversion function
92 //                         from win32.c, which is currently needed for the
93 //                         new UTF-8 names support (until we manage to port
94 //                         the complete UnZip code to native wide-char support).
95 //
96 //*****************************************************************************
97 
98 
99 //*****************************************************************************
100 // The following information and structure are here just for reference
101 //*****************************************************************************
102 //
103 // The Windows CE version of Unzip builds with the following defines set:
104 //
105 //
106 //    WIN32
107 //    _WINDOWS
108 //    UNICODE
109 //    _UNICODE
110 //    WIN32_LEAN_AND_MEAN
111 //    STRICT
112 //
113 //    POCKET_UNZIP         (Main define - Always set)
114 //
115 //    UNZIP_INTERNAL
116 //    WINDLL
117 //    DLL
118 //    REENTRANT
119 //    USE_EF_UT_TIME
120 //    NO_ZIPINFO
121 //    NO_STDDEF_H
122 //    NO_NTSD_EAS
123 //
124 //    USE_SMITH_CODE       (optional - See INSTALL document)
125 //    LZW_CLEAN            (optional - See INSTALL document)
126 //    NO_W32TIMES_IZFIX    (optional - See INSTALL document)
127 //
128 //    DEBUG                (When building for Debug)
129 //    _DEBUG               (When building for Debug)
130 //    NDEBUG               (When building for Retail)
131 //    _NDEBUG              (When building for Retail)
132 //
133 //    _WIN32_WCE=100       (When building for Windows CE native)
134 //
135 //****************************************************************************/
136 
137 extern "C" {
138 #define __INTRFACE_CPP__
139 #define UNZIP_INTERNAL
140 #include "unzip.h"
141 #include "crypt.h"     // Needed to pick up CRYPT define
142 #include <commctrl.h>
143 #include "intrface.h"
144 #include "winmain.h"
145 
146 #ifndef _WIN32_WCE
147 #include <process.h>   // _beginthreadex() and _endthreadex()
148 #endif
149 
150 }
151 #include <tchar.h> // Must be outside of extern "C" block
152 
153 #ifdef POCKET_UNZIP
154 
155 //******************************************************************************
156 //***** "Local" Global Variables
157 //******************************************************************************
158 
159 static USERFUNCTIONS  g_uf;
160 static EXTRACT_INFO  *g_pExtractInfo = NULL;
161 static FILE_NODE     *g_pFileLast    = NULL;
162 static CHAR           g_szExtractToDirectory[_MAX_PATH];
163 static BOOL           g_fOutOfMemory;
164 
165 //******************************************************************************
166 //***** Local Function Prototypes
167 //******************************************************************************
168 
169 // Internal functions of the GUI interface.
170 static Uz_Globs* InitGlobals(LPCSTR szZipFile);
171 static void FreeGlobals(Uz_Globs *pG);
172 
173 #ifdef _WIN32_WCE
174 static DWORD WINAPI ExtractOrTestFilesThread(LPVOID lpv);
175 #else
176 static unsigned __stdcall ExtractOrTestFilesThread(void *lpv);
177 #endif
178 
179 static void SetCurrentFile(__GPRO);
180 
181 #endif // POCKET_UNZIP
182 
183 // Internal helper functions for the UnZip core.
184 static int IsFileOrDirectory(LPCTSTR szPath);
185 static BOOL SmartCreateDirectory(__GPRO__ LPCSTR szDirectory, BOOL *pNewDir);
186 static void utimeToFileTime(time_t ut, FILETIME *pft, BOOL fOldFileSystem);
187 static int GetFileTimes(Uz_Globs *pG, FILETIME *pftCreated,
188                         FILETIME *pftAccessed, FILETIME *pftModified);
189 
190 // Check for FAT, VFAT, HPFS, etc.
191 static BOOL IsOldFileSystem(char *szPath);
192 
193 #ifdef POCKET_UNZIP
194 
195 extern "C" {
196 
197 // Local variants of callbacks from Info-ZIP code.
198 // (These functions are not referenced by name outside this source module.)
199 int UZ_EXP UzpMessagePrnt2(zvoid *pG, uch *buffer, ulg size, int flag);
200 int UZ_EXP UzpInput2(zvoid *pG, uch *buffer, int *size, int flag);
201 int UZ_EXP CheckForAbort2(zvoid *pG, int fnflag, ZCONST char *zfn,
202                           ZCONST char *efn, ZCONST zvoid *details);
203 int WINAPI UzpReplace(LPSTR szFile, unsigned nbufsiz);
204 void WINAPI UzpSound(void);
205 #ifdef Z_UINT8_DEFINED
206 void WINAPI SendAppMsg(z_uint8 uzSize, z_uint8 uzCompressedSize,
207 #else
208 void WINAPI SendAppMsg(ulg uzSize, ulg uzCompressedSize,
209 #endif
210                        unsigned ratio,
211                        unsigned month, unsigned day, unsigned year,
212                        unsigned hour, unsigned minute, char uppercase,
213                        LPCSTR szPath, LPCSTR szMethod, ulg dwCRC,
214                        char chCrypt);
215 
216 } // extern "C"
217 
218 
219 //******************************************************************************
220 //***** Our exposed interface functions to the Info-ZIP core
221 //******************************************************************************
222 
DoListFiles(LPCSTR szZipFile)223 int DoListFiles(LPCSTR szZipFile) {
224 
225    int result;
226 
227    // Create our Globals struct and fill it in whith some default values.
228    Uz_Globs *pG = InitGlobals(szZipFile);
229    if (!pG) {
230       return PK_MEM;
231    }
232 
233    pG->UzO.vflag = 1; // verbosely: list directory (for WIN32 it is 0 or 1)
234    pG->process_all_files = TRUE; // improves speed
235 
236    g_pFileLast = NULL;
237    g_fOutOfMemory = FALSE;
238 
239    // We wrap some exception handling around the entire Info-ZIP engine to be
240    // safe.  Since we are running on a device with tight memory configurations,
241    // all sorts of problems can arise when we run out of memory.
242    __try {
243 
244       // Call the unzip routine.  We will catch the file information in a
245       // callback to SendAppMsg().
246       result = process_zipfiles(pG);
247 
248       // Make sure we didn't run out of memory in the process.
249       if (g_fOutOfMemory) {
250          result = PK_MEM;
251       }
252 
253    } __except(EXCEPTION_EXECUTE_HANDLER) {
254 
255       // Catch any exception here.
256       DebugOut(TEXT("Exception 0x%08X occurred in DoListFiles()"),
257                GetExceptionCode());
258       result = PK_EXCEPTION;
259    }
260 
261    g_pFileLast = NULL;
262 
263    // It is possible that the ZIP engine change the file name a bit (like adding
264    // a ".zip" if needed).  If so, we will pick up the new name.
265    if ((result != PK_EXCEPTION) && pG->zipfn && *pG->zipfn) {
266       strcpy(g_szZipFile, pG->zipfn);
267    }
268 
269    // Free our globals.
270    FreeGlobals(pG);
271 
272    return result;
273 }
274 
275 //******************************************************************************
DoExtractOrTestFiles(LPCSTR szZipFile,EXTRACT_INFO * pei)276 BOOL DoExtractOrTestFiles(LPCSTR szZipFile, EXTRACT_INFO *pei) {
277 
278    // WARNING!!!  This functions hands the EXTRACT_INFO structure of to a thread
279    // to perform the actual extraction/test.  When the thread is done, it will
280    // send a message to the progress dialog.  The calling function must not
281    // delete the EXTRACT_INFO structure until it receives the message.  Currently,
282    // this is not a problem for us since the structure lives on the stack of the
283    // calling thread.  The calling thread then displays a dialog that blocks the
284    // calling thread from clearing the stack until the dialog is dismissed, which
285    // occurs when the dialog receives the message.
286 
287    // Create our globals so we can store the file name.
288    Uz_Globs *pG = InitGlobals(szZipFile);
289    if (!pG) {
290       pei->result = PK_MEM;
291       SendMessage(g_hDlgProgress, WM_PRIVATE, MSG_OPERATION_COMPLETE, (LPARAM)pei);
292       return FALSE;
293    }
294 
295    // Store a global pointer to the Extract structure so it can be reached from
296    // our thread and callback functions.
297    g_pExtractInfo = pei;
298 
299    // Spawn our thread
300    DWORD dwThreadId;
301    HANDLE hThread;
302 
303 #ifdef _WIN32_WCE
304 
305    // On CE, we use good old CreateThread() since the WinCE CRT does not
306    // allocate per-thread storage.
307    hThread = CreateThread(NULL, 0, ExtractOrTestFilesThread, pG, 0,
308                           &dwThreadId);
309 
310 #else
311 
312    // On NT, we need use the CRT's thread function so that we don't leak any
313    // CRT allocated memory when the thread exits.
314    hThread = (HANDLE)_beginthreadex(NULL, 0, ExtractOrTestFilesThread, pG, 0,
315                                     (unsigned*)&dwThreadId);
316 
317 #endif
318 
319    // Bail out if our thread failed to create.
320    if (!hThread) {
321 
322       DebugOut(TEXT("CreateThread() failed [%u]"), GetLastError());
323 
324       // Set our error as a memory error.
325       g_pExtractInfo->result = PK_MEM;
326 
327       // Free our globals.
328       FreeGlobals(pG);
329 
330       // Tell the progress dialog that we are done.
331       SendMessage(g_hDlgProgress, WM_PRIVATE, MSG_OPERATION_COMPLETE, (LPARAM)pei);
332 
333       g_pExtractInfo = NULL;
334       return FALSE;
335    }
336 
337    // Close our thread handle since we have no use for it.
338    CloseHandle(hThread);
339    return TRUE;
340 }
341 
342 //******************************************************************************
DoGetComment(LPCSTR szZipFile)343 int DoGetComment(LPCSTR szZipFile) {
344 
345    int result;
346 
347    // Create our Globals struct and fill it in with some default values.
348    Uz_Globs *pG = InitGlobals(szZipFile);
349    if (!pG) {
350       return PK_MEM;
351    }
352 
353    pG->UzO.zflag = TRUE; // display the zipfile comment
354 
355    // We wrap some exception handling around the entire Info-ZIP engine to be
356    // safe.  Since we are running on a device with tight memory configurations,
357    // all sorts of problems can arise when we run out of memory.
358    __try {
359 
360       // Call the unzip routine.  We will catch the comment string in a callback
361       // to win_fprintf().
362       result = process_zipfiles(pG);
363 
364    } __except(EXCEPTION_EXECUTE_HANDLER) {
365 
366       // Catch any exception here.
367       DebugOut(TEXT("Exception 0x%08X occurred in DoGetComment()"),
368                GetExceptionCode());
369       result = PK_EXCEPTION;
370    }
371 
372    // Free our globals.
373    FreeGlobals(pG);
374 
375    return result;
376 }
377 
378 //******************************************************************************
SetExtractToDirectory(LPTSTR szDirectory)379 BOOL SetExtractToDirectory(LPTSTR szDirectory) {
380 
381    BOOL fNeedToAddWack = FALSE;
382 
383    // Remove any trailing wack from the path.
384    int length = _tcslen(szDirectory);
385    if ((length > 0) && (szDirectory[length - 1] == TEXT('\\'))) {
386       szDirectory[--length] = TEXT('\0');
387       fNeedToAddWack = TRUE;
388    }
389 
390 #ifndef _WIN32_WCE
391 
392    // Check to see if a root directory was specified.
393    if ((length == 2) && isalpha(szDirectory[0]) && (szDirectory[1] == ':')) {
394 
395       // If just a root is specified, we need to only verify the drive letter.
396       if (!(GetLogicalDrives() & (1 << (tolower(szDirectory[0]) - (int)'a')))) {
397 
398          // This drive does not exist.  Bail out with a failure.
399          return FALSE;
400       }
401 
402    } else
403 
404 #endif
405 
406    // We only verify path if length is >0 since we know "\" is valid.
407    if (length > 0) {
408 
409       // Verify the the path exists and that it is a directory.
410       if (IsFileOrDirectory(szDirectory) != 2) {
411          return FALSE;
412       }
413    }
414 
415    // Store the directory for when we do an extract.
416    TSTRTOMBS(g_szExtractToDirectory, szDirectory, countof(g_szExtractToDirectory));
417 
418    // We always want a wack at the end of our path.
419    strcat(g_szExtractToDirectory, "\\");
420 
421    // Add the wack back to the end of the path.
422    if (fNeedToAddWack) {
423       _tcscat(szDirectory, TEXT("\\"));
424    }
425 
426    return TRUE;
427 }
428 
429 //******************************************************************************
430 //***** Internal functions
431 //******************************************************************************
432 
InitGlobals(LPCSTR szZipFile)433 static Uz_Globs* InitGlobals(LPCSTR szZipFile)
434 {
435    // Create our global structure - pG
436    CONSTRUCTGLOBALS();
437 
438    // Bail out if we failed to allocate our Globals structure.
439    if (!pG) {
440       return NULL;
441    }
442 
443    // Clear our USERFUNCTIONS structure
444    ZeroMemory(&g_uf, sizeof(g_uf));
445 
446    // Initialize a global pointer to our USERFUNCTIONS structure that is
447    // used by WINMAIN.CPP to access it (without using the pG construction).
448    lpUserFunctions = &g_uf;
449 
450    // Store a global pointer to our USERFUNCTIONS structure in pG so that
451    // the generic Info-ZIP code LIST.C and PROCESS.C can access it.
452    pG->lpUserFunctions = &g_uf;
453 
454    // Fill in all our callback functions.
455    pG->message      = UzpMessagePrnt2;
456    pG->input        = UzpInput2;
457    pG->mpause       = UzpMorePause;
458    pG->statreportcb = CheckForAbort2;
459    pG->lpUserFunctions->replace                = UzpReplace;
460    pG->lpUserFunctions->sound                  = UzpSound;
461    pG->lpUserFunctions->SendApplicationMessage = SendAppMsg;
462    pG->lpUserFunctions->SendApplicationMessage_i32 = NULL;
463 
464 #if CRYPT
465    pG->decr_passwd = UzpPassword;
466 #endif
467 
468    // Match filenames case-sensitively.  We can do this since we can guarantee
469    // exact case because the user can only select files via our UI.
470    pG->UzO.C_flag = FALSE;
471 
472    // Allocate and store the ZIP file name in pG->zipfn
473    if (!(pG->zipfnPtr = new char[FILNAMSIZ])) {
474       FreeGlobals(pG);
475       return NULL;
476    }
477    pG->zipfn = pG->zipfnPtr;
478    strcpy(pG->zipfn, szZipFile);
479 
480    // Allocate and store the ZIP file name in pG->zipfn.  This needs to done
481    // so that do_wild() does not wind up clearing out the zip file name when
482    // it returns in process.c
483    if (!(pG->wildzipfnPtr = new char[FILNAMSIZ])) {
484       FreeGlobals(pG);
485       return NULL;
486    }
487    pG->wildzipfn = pG->wildzipfnPtr;
488    strcpy(pG->wildzipfn, szZipFile);
489 
490    return pG;
491 }
492 
493 //******************************************************************************
FreeGlobals(Uz_Globs * pG)494 static void FreeGlobals(Uz_Globs *pG)
495 {
496    // Free our ZIP file name
497    if (pG->zipfnPtr) {
498       delete[] pG->zipfnPtr;
499       pG->zipfnPtr = pG->zipfn = NULL;
500    }
501 
502    // Free our wild name buffer
503    if (pG->wildzipfnPtr) {
504       delete[] pG->wildzipfnPtr;
505       pG->wildzipfnPtr = pG->wildzipfn = NULL;
506    }
507 
508    // Free everything else.
509    DESTROYGLOBALS();
510 }
511 
512 //******************************************************************************
513 #ifdef _WIN32_WCE
514 
515 // On WinCE, we declare our thread function the way CreateThread() likes it.
ExtractOrTestFilesThread(LPVOID lpv)516 static DWORD WINAPI ExtractOrTestFilesThread(LPVOID lpv)
517 
518 #else
519 
520 // On WinNT, we declare our thread function the way _beginthreadex likes it.
521 static unsigned __stdcall ExtractOrTestFilesThread(void *lpv)
522 
523 #endif
524 {
525    Uz_Globs *pG = (Uz_Globs*)lpv;
526 
527    if (g_pExtractInfo->fExtract) {
528 
529       pG->extract_flag = TRUE;
530 
531       switch (g_pExtractInfo->overwriteMode) {
532 
533          case OM_NEWER:         // Update (extract only newer/brand-new files)
534             pG->UzO.uflag = TRUE;
535             break;
536 
537          case OM_ALWAYS:        // OK to overwrite files without prompting
538             pG->UzO.overwrite_all = TRUE;
539             break;
540 
541          case OM_NEVER:         // Never overwrite files (no prompting)
542             pG->UzO.overwrite_none = TRUE;
543             break;
544 
545          default:               // Force a prompt
546             pG->UzO.overwrite_all = FALSE;
547             pG->UzO.overwrite_none = FALSE;
548             pG->UzO.uflag = FALSE;
549             break;
550       }
551 
552       // Throw away paths if requested.
553       pG->UzO.jflag = !g_pExtractInfo->fRestorePaths;
554 
555    } else {
556       pG->UzO.tflag = TRUE;
557    }
558 
559    if (g_pExtractInfo->szFileList) {
560       pG->filespecs = g_pExtractInfo->dwFileCount;
561       pG->pfnames = g_pExtractInfo->szFileList;
562    } else {
563       // Improves performance if all files are being extracted.
564       pG->process_all_files = TRUE;
565    }
566 
567    // Invalidate our file offset to show that we are starting a new operation.
568    g_pExtractInfo->uzFileOffset = ~(zusz_t)0;
569 
570    // We wrap some exception handling around the entire Info-ZIP engine to be
571    // safe.  Since we are running on a device with tight memory configurations,
572    // all sorts of problems can arise when we run out of memory.
573    __try {
574 
575       // Put a jump marker on our stack so the user can abort.
576       int error = setjmp(dll_error_return);
577 
578       // If setjmp() returns 0, then we just set our jump marker and we can
579       // continue with the operation.  If setjmp() returned something else,
580       // then we reached this point because the operation was aborted and
581       // set our instruction pointer back here.
582 
583       if (error > 0) {
584          // We already called process_zipfiles() and were thrown back here.
585          g_pExtractInfo->result = (error == 1) ? PK_BADERR : error;
586 
587       } else {
588          // Entering Info-ZIP... close your eyes.
589          g_pExtractInfo->result = process_zipfiles(pG);
590       }
591 
592    } __except(EXCEPTION_EXECUTE_HANDLER) {
593 
594       // Catch any exception here.
595       DebugOut(TEXT("Exception 0x%08X occurred in ExtractOrTestFilesThread()"),
596                GetExceptionCode());
597       g_pExtractInfo->result = PK_EXCEPTION;
598    }
599 
600    // Free our globals.
601    FreeGlobals(pG);
602 
603    // Tell the progress dialog that we are done.
604    SendMessage(g_hDlgProgress, WM_PRIVATE, MSG_OPERATION_COMPLETE,
605                (LPARAM)g_pExtractInfo);
606 
607    // Clear our global pointer as we are done with it.
608    g_pExtractInfo = NULL;
609 
610 #ifndef _WIN32_WCE
611    // On NT, we need to free any CRT allocated memory.
612    _endthreadex(0);
613 #endif
614 
615    return 0;
616 }
617 
618 //******************************************************************************
SetCurrentFile(__GPRO)619 static void SetCurrentFile(__GPRO)
620 {
621    // Reset all our counters as we about to process a new file.
622    g_pExtractInfo->uzFileOffset = (zusz_t)G.pInfo->offset;
623    g_pExtractInfo->dwFile++;
624    g_pExtractInfo->uzBytesWrittenThisFile = 0;
625    g_pExtractInfo->uzBytesWrittenPreviousFiles += g_pExtractInfo->uzBytesTotalThisFile;
626    g_pExtractInfo->uzBytesTotalThisFile = G.lrec.ucsize;
627    g_pExtractInfo->szFile = G.filename;
628    g_pExtractInfo->fNewLineOfText = TRUE;
629 
630    // Pass control to our GUI thread to do a full update our progress dialog.
631    SendMessage(g_hWndMain, WM_PRIVATE, MSG_UPDATE_PROGRESS_COMPLETE,
632                (LPARAM)g_pExtractInfo);
633 
634    // Check our abort flag.
635 }
636 #endif // POCKET_UNZIP
637 
638 //******************************************************************************
IsFileOrDirectory(LPCTSTR szPath)639 static int IsFileOrDirectory(LPCTSTR szPath)
640 {
641    // Geth the attributes of the item.
642    DWORD dwAttribs = GetFileAttributes(szPath);
643 
644    // Bail out now if we could not find the path at all.
645    if (dwAttribs == 0xFFFFFFFF) {
646       return 0;
647    }
648 
649    // Return 1 for file and 2 for directory.
650    return ((dwAttribs & FILE_ATTRIBUTE_DIRECTORY) ? 2 : 1);
651 }
652 
653 //******************************************************************************
SmartCreateDirectory(__GPRO__ LPCSTR szDirectory,BOOL * pNewDir)654 static BOOL SmartCreateDirectory(__GPRO__ LPCSTR szDirectory, BOOL *pNewDir)
655 {
656    // Copy path to a UNICODE buffer.
657    TCHAR szBuffer[_MAX_PATH];
658    MBSTOTSTR(szBuffer, szDirectory, countof(szBuffer));
659 
660    switch (IsFileOrDirectory(szBuffer)) {
661       case 0:
662          // Create the directory if it does not exist.
663          if (!CreateDirectory(szBuffer, NULL)) {
664             Info(slide, 1, ((char *)slide, "error creating directory: %s\n",
665               FnFilter1( szDirectory)));
666             return FALSE;
667          }
668          if (pNewDir != NULL) *pNewDir = TRUE;
669          break;
670 
671       case 1:
672          // If there is a file with the same name, then display an error.
673          Info(slide, 1, ((char *)slide,
674               "cannot create %s as a file with same name already exists.\n",
675               FnFilter1(szDirectory)));
676          return FALSE;
677    }
678 
679    // If the directory already exists or was created, then return success.
680    return TRUE;
681 }
682 
683 
684 #ifdef POCKET_UNZIP
685 //******************************************************************************
686 //***** Callbacks from Info-ZIP code.
687 //******************************************************************************
688 
UzpMessagePrnt2(zvoid * pG,uch * buffer,ulg size,int flag)689 int UZ_EXP UzpMessagePrnt2(zvoid *pG, uch *buffer, ulg size, int flag)
690 {
691 
692    // Some ZIP files cause us to get called during DoListFiles(). We only handle
693    // messages while processing DoExtractFiles().
694    if (!g_pExtractInfo) {
695       if (g_hWndEdit) {
696          SendMessage(g_hWndMain, WM_PRIVATE, MSG_ADD_TEXT_TO_EDIT,
697                      (LPARAM)buffer);
698       } else {
699 #ifdef UNICODE
700          DebugOut(TEXT("Unhandled call to UzpMessagePrnt2(\"%S\")"), buffer);
701 #else
702          DebugOut(TEXT("Unhandled call to UzpMessagePrnt2(\"%s\")"), buffer);
703 #endif
704       }
705       return 0;
706    }
707 
708    // When extracting, mapname() will get called for every file which in turn
709    // will call SetCurrentFile().  For testing though, mapname() never gets
710    // called so we need to be on the lookout for a new file.
711    if (g_pExtractInfo->uzFileOffset != (zusz_t)((Uz_Globs*)pG)->pInfo->offset) {
712       SetCurrentFile((Uz_Globs*)pG);
713    }
714 
715    // Make sure this message was intended for us to display.
716    if (!MSG_NO_WGUI(flag) && !MSG_NO_WDLL(flag)) {
717 
718       // Insert a leading newline if requested to do so.
719       if (MSG_LNEWLN(flag) && !g_pExtractInfo->fNewLineOfText) {
720          SendMessage(g_hWndMain, WM_PRIVATE, MSG_ADD_TEXT_TO_EDIT, (LPARAM)"\n");
721          g_pExtractInfo->fNewLineOfText = TRUE;
722       }
723 
724       // Since we use a proportional font, we need to do a little cleanup of the
725       // text we are passed since it assumes a fixed font and adds padding to try
726       // to line things up.  We remove leading whitespace on any new line of text.
727       if (g_pExtractInfo->fNewLineOfText) {
728          while (*buffer == ' ') {
729             buffer++;
730          }
731       }
732 
733       // We always remove trailing whitespace.
734       LPSTR psz = (LPSTR)buffer;
735       LPSTR pszn;
736       while ((pszn = MBSCHR(psz, ' ')) != NULL) {
737          for (psz = pszn+1; *psz == ' '; psz++);
738          if (*psz == '\0') {
739             *pszn = '\0';
740             break;
741          }
742       }
743 
744 
745       // Determine if the next line of text will be a new line of text.
746       g_pExtractInfo->fNewLineOfText = ((*psz == '\r') || (*psz == '\n'));
747 
748       // Change all forward slashes to back slashes in the buffer
749       ForwardSlashesToBackSlashesA((LPSTR)buffer);
750 
751       // Add the cleaned-up text to our extraction log edit control.
752       SendMessage(g_hWndMain, WM_PRIVATE, MSG_ADD_TEXT_TO_EDIT, (LPARAM)buffer);
753 
754       // Append a trailing newline if requested to do so.
755       if (MSG_TNEWLN(flag) || MSG_MNEWLN(flag) && !g_pExtractInfo->fNewLineOfText) {
756          SendMessage(g_hWndMain, WM_PRIVATE, MSG_ADD_TEXT_TO_EDIT, (LPARAM)"\n");
757          g_pExtractInfo->fNewLineOfText = TRUE;
758       }
759    }
760 
761    return 0;
762 }
763 
764 //******************************************************************************
UzpInput2(zvoid * pG,uch * buffer,int * size,int flag)765 int UZ_EXP UzpInput2(zvoid *pG, uch *buffer, int *size, int flag)
766 {
767    DebugOut(TEXT("WARNING: UzpInput2(...) called"));
768    return 0;
769 }
770 
771 //******************************************************************************
UzpMorePause(zvoid * pG,const char * szPrompt,int flag)772 void UZ_EXP UzpMorePause(zvoid *pG, const char *szPrompt, int flag)
773 {
774    DebugOut(TEXT("WARNING: UzpMorePause(...) called"));
775 }
776 
777 //******************************************************************************
UzpPassword(zvoid * pG,int * pcRetry,char * szPassword,int nSize,const char * szZipFile,const char * szFile)778 int UZ_EXP UzpPassword(zvoid *pG, int *pcRetry, char *szPassword, int nSize,
779                        const char *szZipFile, const char *szFile)
780 {
781    // Return Values:
782    //    IZ_PW_ENTERED    got some PWD string, use/try it
783    //    IZ_PW_CANCEL     no password available (for this entry)
784    //    IZ_PW_CANCELALL  no password, skip any further PWD request
785    //    IZ_PW_ERROR      failure (no mem, no tty, ...)
786 
787 #if CRYPT
788 
789    // Build the data structure for our dialog.
790    DECRYPT_INFO di;
791    di.retry      = *pcRetry;
792    di.szPassword = szPassword;
793    di.nSize      = nSize;
794    di.szFile     = szFile;
795 
796    // Clear the password to be safe.
797    *di.szPassword = '\0';
798 
799    // On our first call for a file, *pcRetry == 0.  If we would like to allow
800    // for retries, then we set the value of *pcRetry to the number of retries we
801    // are willing to allow.  We will be recalled as neccessary, each time with
802    // *pcRetry being decremented once.  1 is the last retry we will get.
803    *pcRetry = (*pcRetry == 0) ? MAX_PASSWORD_RETRIES : (*pcRetry - 1);
804 
805    // Pass control to our GUI thread which will prompt the user for a password.
806    return SendMessage(g_hWndMain, WM_PRIVATE, MSG_PROMPT_FOR_PASSWORD, (LPARAM)&di);
807 
808 #else
809    return IZ_PW_CANCELALL;
810 #endif
811 }
812 
813 //******************************************************************************
CheckForAbort2(zvoid * pG,int fnflag,ZCONST char * zfn,ZCONST char * efn,ZCONST zvoid * details)814 int UZ_EXP CheckForAbort2(zvoid *pG, int fnflag, ZCONST char *zfn,
815                     ZCONST char *efn, ZCONST zvoid *details)
816 {
817    int rval = UZ_ST_CONTINUE;
818 
819    if (g_pExtractInfo->fAbort) {
820 
821       // Add a newline to our log if we are in the middle of a line of text.
822       if (!g_pExtractInfo->fNewLineOfText) {
823          SendMessage(g_hWndMain, WM_PRIVATE, MSG_ADD_TEXT_TO_EDIT, (LPARAM)"\n");
824       }
825 
826       // Make sure whatever file we are currently processing gets closed.
827       if (((int)((Uz_Globs *)pG)->outfile != 0) &&
828           ((int)((Uz_Globs *)pG)->outfile != -1)) {
829          if (g_pExtractInfo->fExtract && *efn) {
830 
831             // Make sure the user is aware that this file is screwed.
832             SendMessage(g_hWndMain, WM_PRIVATE, MSG_ADD_TEXT_TO_EDIT,
833                         (LPARAM)"warning: ");
834             SendMessage(g_hWndMain, WM_PRIVATE, MSG_ADD_TEXT_TO_EDIT,
835                         (LPARAM)efn);
836             SendMessage(g_hWndMain, WM_PRIVATE, MSG_ADD_TEXT_TO_EDIT,
837                         (LPARAM)" is probably truncated.\n");
838          }
839       }
840 
841       // Display an aborted message in the log
842       SendMessage(g_hWndMain, WM_PRIVATE, MSG_ADD_TEXT_TO_EDIT,
843                   (LPARAM)"Operation aborted by user.\n");
844 
845       // Signal "Immediate Cancel" back to the UnZip engine.
846       rval = UZ_ST_BREAK;
847    }
848 
849    return rval;
850 }
851 
852 //******************************************************************************
UzpReplace(LPSTR szFile,unsigned nbufsiz)853 int WINAPI UzpReplace(LPSTR szFile, unsigned nbufsiz) {
854    // Pass control to our GUI thread which will prompt the user to overwrite.
855    // The nbufsiz parameter is not needed here, because this program does not
856    // (yet?) contain support for renaming the extraction target.
857    return SendMessage(g_hWndMain, WM_PRIVATE, MSG_PROMPT_TO_REPLACE,
858                       (LPARAM)szFile);
859 }
860 
861 //******************************************************************************
UzpSound(void)862 void WINAPI UzpSound(void) {
863    // Do nothing.
864 }
865 
866 //******************************************************************************
867 // Called from LIST.C
868 #ifdef Z_UINT8_DEFINED
SendAppMsg(z_uint8 uzSize,z_uint8 uzCompressedSize,unsigned ratio,unsigned month,unsigned day,unsigned year,unsigned hour,unsigned minute,char uppercase,LPCSTR szPath,LPCSTR szMethod,ulg dwCRC,char chCrypt)869 void WINAPI SendAppMsg(z_uint8 uzSize, z_uint8 uzCompressedSize,
870 #else
871 void WINAPI SendAppMsg(ulg uzSize, ulg uzCompressedSize,
872 #endif
873                        unsigned ratio,
874                        unsigned month, unsigned day, unsigned year,
875                        unsigned hour, unsigned minute, char uppercase,
876                        LPCSTR szPath, LPCSTR szMethod, ulg dwCRC,
877                        char chCrypt)
878 {
879    // If we are out of memory, then just bail since we will only make things worse.
880    if (g_fOutOfMemory) {
881       return;
882    }
883 
884    // We get our Globals structure.
885    GETGLOBALS();
886 
887    // Allocate a FILE_NODE large enough to hold this file.
888    int length = strlen(szPath) + strlen(szMethod);
889    g_pFileLast = (FILE_NODE*)new BYTE[sizeof(FILE_NODE) +
890                                       (sizeof(CHAR) * length)];
891 
892    // Bail out if we failed to allocate the node.
893    if (!g_pFileLast) {
894 #ifdef UNICODE
895       DebugOut(TEXT("Failed to create a FILE_NODE for \"%S\"."), szPath);
896 #else
897       DebugOut(TEXT("Failed to create a FILE_NODE for \"%s\"."), szPath);
898 #endif
899       g_fOutOfMemory = TRUE;
900       return;
901    }
902 
903    // Fill in our node.
904    g_pFileLast->uzSize           = (zusz_t)uzSize;
905    g_pFileLast->uzCompressedSize = (zusz_t)uzCompressedSize;
906    g_pFileLast->dwCRC            = dwCRC;
907    g_pFileLast->szComment        = NULL;
908    g_pFileLast->szType           = NULL;
909 
910    // Fix the year value to contain the real year.
911    year += 1900;
912 
913    // Year:   0 - 4095 (12) 1111 1111 1111 0000 0000 0000 0000 0000 (0xFFF00000)
914    // Month:  1 -   12 ( 4) 0000 0000 0000 1111 0000 0000 0000 0000 (0x000F0000)
915    // Day:    1 -   31 ( 5) 0000 0000 0000 0000 1111 1000 0000 0000 (0x0000F800)
916    // Hour:   0 -   23 ( 5) 0000 0000 0000 0000 0000 0111 1100 0000 (0x000007C0)
917    // Minute: 0 -   59 ( 6) 0000 0000 0000 0000 0000 0000 0011 1111 (0x0000003F)
918 
919    // Do some bit shifting to make the date and time fit in a DWORD.
920    g_pFileLast->dwModified = (((DWORD)(year   & 0x0FFF) << 20) |
921                               ((DWORD)(month  & 0x000F) << 16) |
922                               ((DWORD)(day    & 0x001F) << 11) |
923                               ((DWORD)(hour   & 0x001F) <<  6) |
924                               ((DWORD)(minute & 0x003F)));
925 
926    // We need to get our globals structure to determine our attributes and
927    // encryption information.
928    g_pFileLast->dwAttributes = (pG->crec.external_file_attributes & 0xFF);
929    if (chCrypt == 'E') {
930       g_pFileLast->dwAttributes |= ZFILE_ATTRIBUTE_ENCRYPTED;
931    }
932 
933    // Store the path and method in our string buffer.
934    strcpy(g_pFileLast->szPathAndMethod, szPath);
935    strcpy(g_pFileLast->szPathAndMethod + strlen(szPath) + 1, szMethod);
936 
937    // Pass the file object to our windows code to have it added to our list.
938    AddFileToListView(g_pFileLast);
939 }
940 
941 //******************************************************************************
win_fprintf(zvoid * pG,FILE * file,unsigned int dwCount,char far * buffer)942 int win_fprintf(zvoid *pG, FILE *file, unsigned int dwCount, char far *buffer)
943 {
944 
945    // win_fprintf() is used within Info-ZIP to write to a file as well as log
946    // information.  If the "file" is a real file handle (not stdout or stderr),
947    // then we write the data to the file and return.
948 
949    if ((file != stdout) && (file != stderr)) {
950 
951       DWORD dwBytesWritten = 0;
952 #if (defined(_WIN32_WCE) && (_WIN32_WCE < 211))
953       // On WinCE all FILEs are really HANDLEs.  See WINCE.CPP for more info.
954       WriteFile((HANDLE)file, buffer, dwCount, &dwBytesWritten, NULL);
955 #else
956       dwBytesWritten = fwrite(buffer, 1, dwCount, file);
957 #endif
958 
959       // Update our bytes written count.
960       g_pExtractInfo->uzBytesWrittenThisFile += dwBytesWritten;
961 
962       // Pass control to our GUI thread to do a partial update our progress dialog.
963       SendMessage(g_hWndMain, WM_PRIVATE, MSG_UPDATE_PROGRESS_PARTIAL,
964                   (LPARAM)g_pExtractInfo);
965 
966       return dwBytesWritten;
967    }
968 
969    // Check to see if we are expecting a extraction progress string
970    if (g_pExtractInfo) {
971 
972       // Most of our progress strings come to our UzpMessagePrnt2() callback,
973       // but we occasionally get one here.  We will just forward it to
974       // UzpMessagePrnt2() as if it never came here.
975       UzpMessagePrnt2(pG, (uch*)buffer, dwCount, 0);
976       return dwCount;
977    }
978 
979    // Check to see if we are expecting a zip file comment string.
980    if (g_hWndEdit) {
981 
982       // Change all forward slashes to back slashes in the buffer
983       ForwardSlashesToBackSlashesA((LPSTR)buffer);
984 
985       SendMessage(g_hWndMain, WM_PRIVATE, MSG_ADD_TEXT_TO_EDIT, (LPARAM)buffer);
986       return dwCount;
987    }
988 
989    // Check to see if we are expecting a compressed file comment string.
990    if (g_pFileLast) {
991       char *p1, *p2;
992 
993       // Calcalute the size of the buffer we will need to store this comment.
994       // We are going to convert all ASC values 0 - 31 (except tab, new line,
995       // and CR) to ^char.
996       int size = 1;
997       for (p1 = buffer; *p1; INCSTR(p1)) {
998          size += ((*p1 >= 32) || (*p1 == '\t') ||
999                   (*p1 == '\r') || (*p1 == '\n')) ? CLEN(p1) : 2;
1000       }
1001 
1002       // Allocate a comment buffer and assign it to the last file node we saw.
1003       if (g_pFileLast->szComment = new CHAR[size]) {
1004 
1005          // Copy while formatting.
1006          for (p1 = buffer, p2 = (char*)g_pFileLast->szComment; *p1; INCSTR(p1)) {
1007             if ((*p1 >= 32) || (*p1 == '\t') ||
1008                 (*p1 == '\r') || (*p1 == '\n')) {
1009                memcpy(p2, p1, CLEN(p1));
1010                p2 += CLEN(p1);
1011             } else {
1012                *(p2++) = '^';
1013                *(p2++) = 64 + *p1;
1014             }
1015          }
1016          *p2 = '\0';
1017       }
1018 
1019       // Update the attributes of the file node to include the comment attribute.
1020       g_pFileLast->dwAttributes |= ZFILE_ATTRIBUTE_COMMENT;
1021 
1022       // Clear the file node so we don't try to add another bogus comment to it.
1023       g_pFileLast = NULL;
1024 
1025       return dwCount;
1026    }
1027 
1028    if (dwCount >= _MAX_PATH) {
1029       buffer[_MAX_PATH] = '\0';
1030    }
1031 #ifdef UNICODE
1032    DebugOut(TEXT("Unhandled call to win_fprintf(\"%S\")"), buffer);
1033 #else
1034    DebugOut(TEXT("Unhandled call to win_fprintf(\"%S\")"), buffer);
1035 #endif
1036    return dwCount;
1037 }
1038 
1039 //******************************************************************************
Wiz_NoPrinting(int f)1040 void WINAPI Wiz_NoPrinting(int f) {
1041    // Do nothing.
1042 }
1043 
1044 #endif // POCKET_UNZIP
1045 
1046 //******************************************************************************
1047 //***** Functions that Info-ZIP expects the port to write and export.
1048 //***** Some of this code was stolen from the WIN32 port and highly modified.
1049 //******************************************************************************
1050 
1051 #ifdef NTSD_EAS
1052 #ifndef SFX
1053 //******************************************************************************
1054 // Called from EXTRACT.C
test_NTSD(__GPRO__ uch * eb,unsigned eb_size)1055 int test_NTSD(__GPRO__ uch *eb, unsigned eb_size) {
1056    // This function is called when an NT security descriptor is found in the
1057    // extra field.  We have nothing to do, so we just return success.
1058    return PK_OK;
1059 }
1060 #endif /* !SFX */
1061 #endif /* NTSD_EAS */
1062 
utimeToFileTime(time_t ut,FILETIME * pft,BOOL fOldFileSystem)1063 static void utimeToFileTime(time_t ut, FILETIME *pft, BOOL fOldFileSystem)
1064 {
1065 
1066    // time_t    is a 32-bit value for the seconds since January 1, 1970
1067    // FILETIME  is a 64-bit value for the number of 100-nanosecond intervals
1068    //           since January 1, 1601
1069    // DWORDLONG is a 64-bit unsigned int that we can use to perform large math
1070    //           operations.
1071 
1072 
1073    // time_t has minimum of 1/1/1970.  Many file systems, such as FAT, have a
1074    // minimum date of 1/1/1980.  If extracting to one of those file systems and
1075    // out time_t is less than 1980, then we make it 1/1/1980.
1076    // (365 days/yr * 10 yrs + 3 leap yr days) * (60 secs * 60 mins * 24 hrs).
1077    if (fOldFileSystem && (ut < 0x12CFF780)) {
1078       ut = 0x12CFF780;
1079    }
1080 
1081 #ifndef NO_W32TIMES_IZFIX
1082    // Now for the next fix for old file systems.  If we are in Daylight Savings
1083    // Time (DST) and the file is not in DST, then we need subtract off the DST
1084    // bias from the filetime.  This is due to a bug in Windows (NT, CE, and 95)
1085    // that causes the DST bias to be added to all file times when the system
1086    // is in DST, even if the file is not in DST.  This only effects old file
1087    // systems since they store local times instead of UTC times.  Newer file
1088    // systems like NTFS and CEFS store UTC times.
1089    if (fOldFileSystem)
1090 #endif
1091    {
1092       // We use the CRT's localtime() and Win32's LocalTimeToFileTime()
1093       // functions to compute a FILETIME value that always shows the correct
1094       // local time in Windows' file listings.  This works because localtime()
1095       // correctly adds the DST bias only if the file time is in DST.
1096       // FileTimeToLocalTime() always adds the DST bias to the time.
1097       // Therefore, if the functions return different results, we know we
1098       // are dealing with a non-DST file during a system DST.
1099 
1100       FILETIME lftCRT;
1101 
1102       // Get the CRT result - result is a "tm" struct.
1103       struct tm *ptmCRT = localtime(&ut);
1104 
1105       // Check if localtime() returned something useful; continue with the
1106       // "NewFileSystem" code in case of an error. This failsafe method
1107       // should give an "almost" correct filetime result.
1108       if (ptmCRT != (struct tm *)NULL) {
1109          // Convert the "tm" struct to a FILETIME.
1110          SYSTEMTIME stCRT;
1111          ZeroMemory(&stCRT, sizeof(stCRT));
1112          if (fOldFileSystem && (ptmCRT->tm_year < 80)) {
1113             stCRT.wYear   = 1980;
1114             stCRT.wMonth  = 1;
1115             stCRT.wDay    = 1;
1116             stCRT.wHour   = 0;
1117             stCRT.wMinute = 0;
1118             stCRT.wSecond = 0;
1119          } else {
1120             stCRT.wYear   = ptmCRT->tm_year + 1900;
1121             stCRT.wMonth  = ptmCRT->tm_mon + 1;
1122             stCRT.wDay    = ptmCRT->tm_mday;
1123             stCRT.wHour   = ptmCRT->tm_hour;
1124             stCRT.wMinute = ptmCRT->tm_min;
1125             stCRT.wSecond = ptmCRT->tm_sec;
1126          }
1127          SystemTimeToFileTime(&stCRT, &lftCRT);
1128          LocalFileTimeToFileTime(&lftCRT, pft);
1129          // we are finished!
1130          return;
1131       }
1132    }
1133    // For "Modern" file system that stores timestamps in UTC (or as second
1134    // chance in case of localtime() errors) the conversion of time_t into
1135    // 64-bit FILETIME is a simple arithmetic rescaling calculation.
1136    // Compute the FILETIME for the given time_t.
1137    DWORDLONG dwl = ((DWORDLONG)116444736000000000 +
1138                    ((DWORDLONG)ut * (DWORDLONG)10000000));
1139 
1140    // Store the return value.
1141    *pft = *(FILETIME*)&dwl;
1142 
1143 }
1144 
1145 //******************************************************************************
GetFileTimes(__GPRO__ FILETIME * pftCreated,FILETIME * pftAccessed,FILETIME * pftModified)1146 static int GetFileTimes(__GPRO__ FILETIME *pftCreated,
1147                         FILETIME *pftAccessed, FILETIME *pftModified)
1148 {
1149    // We need to check to see if this file system is limited.  This includes
1150    // FAT, VFAT, and HPFS.  It does not include NTFS and CEFS.  The limited
1151    // file systems can not support dates < 1980 and they store file local times
1152    // for files as opposed to UTC times.
1153    BOOL fOldFileSystem = IsOldFileSystem(G.filename);
1154 
1155 #ifdef USE_EF_UT_TIME  // Always true for WinCE build
1156 
1157 #ifdef IZ_CHECK_TZ
1158    if (G.extra_field && G.tz_is_valid) {
1159 #else
1160    if (G.extra_field) {
1161 #endif
1162 
1163       // Structure for Unix style actime, modtime, creatime
1164       iztimes z_utime;
1165 
1166       // Get any date/time we can.  This can return 0 to 3 unix time fields.
1167       unsigned eb_izux_flg = ef_scan_for_izux(G.extra_field,
1168                                               G.lrec.extra_field_length, 0,
1169                                               G.lrec.last_mod_dos_datetime,
1170                                               &z_utime, NULL);
1171 
1172       // We require at least a modified time.
1173       if (eb_izux_flg & EB_UT_FL_MTIME) {
1174 
1175          // We know we have a modified time, so get it first.
1176          utimeToFileTime(z_utime.mtime, pftModified, fOldFileSystem);
1177 
1178          // Get the accessed time if we have one.
1179          if (eb_izux_flg & EB_UT_FL_ATIME) {
1180             utimeToFileTime(z_utime.atime, pftAccessed, fOldFileSystem);
1181          }
1182 
1183          // Get the created time if we have one.
1184          if (eb_izux_flg & EB_UT_FL_CTIME) {
1185             utimeToFileTime(z_utime.ctime, pftCreated, fOldFileSystem);
1186          }
1187 
1188          // Return our flags.
1189          return (int)eb_izux_flg;
1190       }
1191    }
1192 
1193 #endif // USE_EF_UT_TIME
1194 
1195    // If all else fails, we can resort to using the DOS date and time data.
1196    time_t ux_modtime = dos_to_unix_time(G.lrec.last_mod_dos_datetime);
1197    utimeToFileTime(ux_modtime, pftModified, fOldFileSystem);
1198 
1199    *pftAccessed = *pftModified;
1200 
1201    return (EB_UT_FL_MTIME | EB_UT_FL_ATIME);
1202 }
1203 
1204 //******************************************************************************
1205 //***** Functions to correct time stamp bugs on old file systems.
1206 //******************************************************************************
1207 
1208 //******************************************************************************
1209 // Borrowed/Modified from win32.c
1210 static BOOL IsOldFileSystem(char *szPath) {
1211 
1212 #ifdef _WIN32_WCE
1213 
1214    char szRoot[10];
1215 
1216    // Get the first nine characters of the path.
1217    strncpy(szRoot, szPath, 9);
1218    szRoot[9] = '\0';
1219 
1220    // Convert to uppercase to help with compare.
1221    _strupr(szRoot);
1222 
1223    // PC Cards are mounted off the root in a directory called "\PC Cards".
1224    // PC Cards are FAT, no CEOS.  We need to check if the file is being
1225    // extracted to the PC card.
1226    return !strcmp(szRoot, "\\PC CARD\\");
1227 
1228 #else
1229 
1230    char szRoot[_MAX_PATH] = "\0\0\0", szFS[64];
1231 
1232    // Check to see if our path contains a drive letter.
1233    if (isalpha(szPath[0]) && (szPath[1] == ':') && (szPath[2] == '\\')) {
1234 
1235       // If so, then just copy the drive letter, colon, and wack to our root path.
1236       strncpy(szRoot, szPath, 3);
1237 
1238    } else {
1239 
1240       // Expand the path so we can get a drive letter.
1241       GetFullPathNameA(szPath, sizeof(szRoot), szRoot, NULL);
1242 
1243       // Make sure we actually got a drive letter back in our root path buffer..
1244       if (!isalpha(szRoot[0]) || (szRoot[1] != ':') || (szRoot[2] != '\\')) {
1245 
1246          // When in doubt, return TRUE.
1247          return TRUE;
1248       }
1249    }
1250 
1251    // NULL terminate after the wack to ensure we have just the root path.
1252    szRoot[3] = '\0';
1253 
1254    // Get the file system type string.
1255    GetVolumeInformationA(szRoot, NULL, 0, NULL, NULL, NULL, szFS, sizeof(szFS));
1256 
1257    // Ensure that the file system type string is uppercase.
1258    _strupr(szFS);
1259 
1260    // Return true for (V)FAT and (OS/2) HPFS format.
1261    return !strncmp(szFS, "FAT",  3) ||
1262           !strncmp(szFS, "VFAT", 4) ||
1263           !strncmp(szFS, "HPFS", 4);
1264 
1265 #endif // _WIN32_WCE
1266 }
1267 
1268 //******************************************************************************
1269 int SetFileSize(FILE *file, zusz_t filesize)
1270 {
1271 #if (defined(_WIN32_WCE) || defined(__RSXNT__))
1272     // For native Windows CE, it is not known whether the API supports
1273     // presetting a file's size.
1274     // RSXNT environment lacks a translation function from C file pointer
1275     // to Win32-API file handle.
1276     // So, simply do nothing.
1277     return 0;
1278 #else /* !(_WIN32_WCE || __RSXNT__) */
1279     /* not yet verified, if that really creates an unfragmented file
1280       rommel@ars.de
1281      */
1282     HANDLE os_fh;
1283 #ifdef Z_UINT8_DEFINED
1284     LARGE_INTEGER fsbuf;
1285 #endif
1286 
1287     /* Win9x supports FAT file system, only; presetting file size does
1288        not help to prevent fragmentation. */
1289     if ((long)GetVersion() < 0) return 0;
1290 
1291     /* Win32-API calls require access to the Win32 file handle.
1292        The interface function used to retrieve the Win32 handle for
1293        a file opened by the C rtl is non-standard and may not be
1294        available for every Win32 compiler environment.
1295        (see also win32/win32.c of the Zip distribution)
1296      */
1297     os_fh = (HANDLE)_get_osfhandle(fileno(file));
1298     /* move file pointer behind the last byte of the expected file size */
1299 #ifdef Z_UINT8_DEFINED
1300     fsbuf.QuadPart = filesize;
1301     if ((SetFilePointer(os_fh, fsbuf.LowPart, &fsbuf.HighPart, FILE_BEGIN)
1302          == 0xFFFFFFFF) && GetLastError() != NO_ERROR)
1303 #else
1304     if (SetFilePointer(os_fh, filesize, 0, FILE_BEGIN) == 0xFFFFFFFF)
1305 #endif
1306         return -1;
1307     /* extend/truncate file to the current position */
1308     if (SetEndOfFile(os_fh) == 0)
1309         return -1;
1310     /* move file position pointer back to the start of the file! */
1311     return (SetFilePointer(os_fh, 0, 0, FILE_BEGIN) == 0xFFFFFFFF) ? -1 : 0;
1312 #endif /* ?(_WIN32_WCE || __RSXNT__) */
1313 } /* end function SetFileSize() */
1314 
1315 //******************************************************************************
1316 void close_outfile(__GPRO)
1317 {
1318    HANDLE hFile;
1319 
1320    TCHAR szFile[_MAX_PATH];
1321    MBSTOTSTR(szFile, G.filename, countof(szFile));
1322 
1323    /* skip restoring time stamps on user's request */
1324    if (uO.D_flag <= 1) {
1325       // Get the 3 time stamps for the file.
1326       FILETIME ftCreated, ftAccessed, ftModified;
1327       int timeFlags = GetFileTimes(__G__ &ftCreated, &ftAccessed, &ftModified);
1328 
1329 #if (defined(_WIN32_WCE) && (_WIN32_WCE < 211))
1330 
1331       // Cast the outfile to a HANDLE (since that is really what it is), and
1332       // flush the file.  We need to flush, because any unsaved data that is
1333       // written to the file during CloseHandle() will step on the work done
1334       // by SetFileTime().
1335       hFile = (HANDLE)G.outfile;
1336       FlushFileBuffers(hFile);
1337 
1338 #else
1339 
1340       // Close the file and then re-open it using the Win32 CreateFile() call.
1341       // SetFileTime() requires a Win32 file HANDLE created with GENERIC_WRITE
1342       // access.
1343       fclose(G.outfile);
1344       hFile = CreateFile(szFile, GENERIC_WRITE, FILE_SHARE_WRITE, NULL,
1345                          OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
1346 
1347 #endif
1348 
1349       // Set the file's date and time.
1350       if (hFile != INVALID_HANDLE_VALUE) {
1351 
1352          // Make sure we retrieved some valid time stamp(s)
1353          if (timeFlags) {
1354 
1355             // Set the various date and time fields.
1356             if (!SetFileTime(hFile,
1357                     (timeFlags & EB_UT_FL_CTIME) ? &ftCreated  : NULL,
1358                     (timeFlags & EB_UT_FL_ATIME) ? &ftAccessed : NULL,
1359                     (timeFlags & EB_UT_FL_MTIME) ? &ftModified : NULL))
1360             {
1361                DebugOut(TEXT("SetFileTime() failed [%u]"), GetLastError());
1362             }
1363 
1364          } else {
1365             DebugOut(TEXT("GetFileTimes() failed"));
1366          }
1367 
1368          // Close out file.
1369          CloseHandle(hFile);
1370 
1371       } else {
1372          DebugOut(TEXT("CreateFile() failed [%u]"), GetLastError());
1373       }
1374    }
1375 
1376    // If the file was successfully written, then set the attributes.
1377 #ifdef POCKET_UNZIP
1378    if (!G.disk_full && !g_pExtractInfo->fAbort) {
1379 #else
1380    if (!G.disk_full) {
1381 #endif
1382       if (!SetFileAttributes(szFile, G.pInfo->file_attr & 0x7F)) {
1383          DebugOut(TEXT("SetFileAttributes() failed [%u]"), GetLastError());
1384       }
1385    }
1386 
1387    // Clear outfile so we know it is closed.
1388    G.outfile = 0;
1389 
1390    return;
1391 }
1392 
1393 //******************************************************************************
1394 // Called by PROCESS.C
1395 char* do_wild(__GPRO__ ZCONST char *wildspec)
1396 {
1397    // This is a very slimmed down version of do_wild() taken from WIN32.C.
1398    // Since we don't support wildcards, we basically just return the wildspec
1399    // passed in as the filename.
1400 
1401 #ifndef POCKET_UNZIP
1402    // Delete allocated storage for the match name
1403    if (G.matchname != NULL)
1404    {
1405       delete G.matchname;
1406       G.matchname = NULL;
1407    }
1408 #endif
1409 
1410    // First call - must initialize everything.
1411    if (!G.notfirstcall) {
1412       G.notfirstcall = TRUE;
1413 #ifdef POCKET_UNZIP
1414       return strcpy(G.matchname, wildspec);
1415 #else
1416       // allocate some storage for the match name
1417       G.matchname = new char[strlen(wildspec) + 1];
1418       if (G.matchname != NULL)
1419          return strcpy(G.matchname, wildspec);
1420 #endif
1421    }
1422 
1423    // Last time through - reset for new wildspec.
1424    G.notfirstcall = FALSE;
1425 
1426    return (char *)NULL;
1427 }
1428 
1429 //******************************************************************************
1430 // Called from EXTRACT.C
1431 int mapattr(__GPRO)
1432 {
1433 #ifdef POCKET_UNZIP
1434    // Check to see if we are extracting this file for viewing.  Currently, we do
1435    // this by checking the szMappedPath member of our extract info stucture
1436    // since we know OnActionView() is the only one who sets this member.
1437 
1438    if (g_pExtractInfo && g_pExtractInfo->szMappedPath) {
1439 
1440       // If we are extracting for view only, then we ignore the file's real
1441       // attributes and force the file to create as read-only.  We make the file
1442       // read-only to help prevent the user from making changes to the temporary
1443       // file and then trying to save the changes back to a file that we will
1444       // eventually delete.
1445       G.pInfo->file_attr = FILE_ATTRIBUTE_READONLY;
1446 
1447    } else
1448 #endif
1449    {
1450       /* set archive bit for file entries (file is not backed up): */
1451       G.pInfo->file_attr = ((unsigned)G.crec.external_file_attributes |
1452         (G.crec.external_file_attributes & FILE_ATTRIBUTE_DIRECTORY ?
1453          0 : FILE_ATTRIBUTE_ARCHIVE)) & 0xff;
1454    }
1455    return 0;
1456 } /* end function mapattr() */
1457 
1458 //******************************************************************************
1459 // Called from EXTRACT.C
1460 //
1461 // returns:
1462 //  MPN_OK          - no problem detected
1463 //  MPN_INF_TRUNC   - (on APPEND_NAME) truncated filename
1464 //  MPN_INF_SKIP    - path doesn't exist, not allowed to create
1465 //  MPN_ERR_SKIP    - path doesn't exist, tried to create and failed; or path
1466 //                    exists and is not a directory, but is supposed to be
1467 //  MPN_ERR_TOOLONG - path is too long
1468 //  MPN_NOMEM       - can't allocate memory for filename buffers
1469 //
1470 //  MPN_VOL_LABEL   - Path was a volume label, skip it.
1471 //  MPN_CREATED_DIR - Created a directory.
1472 //
1473 int mapname(__GPRO__ int renamed)
1474 {
1475     int error = MPN_OK;
1476     CHAR szBuffer[countof(G.filename)] = "";
1477     CHAR *pIn = NULL, *pOut, *pLastSemi = NULL;
1478     CHAR *pPathComp, workch;
1479     BOOL killed_ddot = FALSE, renamed_fullpath = FALSE, created_dir = FALSE;
1480 
1481 #ifdef POCKET_UNZIP
1482     // mapname() is a great place to reset all our status counters for the next
1483     // file to be processed since it is called for every zip file member before
1484     // any work is done with that member.
1485     SetCurrentFile(__G);
1486 #endif
1487 
1488     // If Volume Label, skip the "extraction" quietly
1489     if (G.pInfo->vollabel) {
1490        return MPN_VOL_LABEL;
1491     }
1492 
1493 #ifndef POCKET_UNZIP // The GUI interface does not support renaming...
1494     if (renamed) {
1495         pIn = G.filename;   // point to beginning of renamed name...
1496         if (*pIn) do {
1497             if (*pIn == '\\')   // convert backslashes to forward
1498                 *pIn = '/';
1499         } while (*PREINCSTR(pIn));
1500         pIn = G.filename;
1501         // use temporary rootpath if user gave full pathname
1502         if (G.filename[0] == '/') {
1503             renamed_fullpath = TRUE;
1504             szBuffer[0] = '\\'; // copy the '/' and terminate
1505             szBuffer[1] = '\0';
1506             ++pIn;
1507         } else if (isalpha((uch)G.filename[0]) && G.filename[1] == ':') {
1508             renamed_fullpath = TRUE;
1509             pOut = szBuffer;
1510             *pOut++ = *pIn++;   // copy the "d:" (+ '/', possibly)
1511             *pOut++ = *pIn++;
1512             if (*pIn == '/') {
1513                 *pOut++ = '\\';
1514                 pIn++;          // otherwise add "./"?
1515             }
1516             *pOut = '\0';
1517         }
1518     }
1519 #endif
1520 
1521     // Initialize file path buffer with our "extract to" path.
1522     if (!renamed_fullpath) {
1523 #ifdef POCKET_UNZIP
1524         strcpy(szBuffer, g_szExtractToDirectory);
1525 #else
1526         strcpy(szBuffer, G.rootpath);
1527 #endif
1528         pOut = szBuffer + strlen(szBuffer);
1529     }
1530     pPathComp = pOut;
1531 
1532     if (!renamed) {
1533         // Point pIn to beginning of our internal pathname.
1534         // If we are junking paths, then locate the file portion of the path.
1535         if (uO.jflag)
1536             pIn = (CHAR*)MBSRCHR(G.filename, '/');
1537         if (pIn == NULL)
1538             pIn = G.filename;
1539         else
1540             ++pIn;
1541     }
1542 
1543     // Begin main loop through characters in filename.
1544     for ( ; (workch = *pIn) != '\0'; INCSTR(pIn)) {
1545 
1546         // Make sure we don't overflow our output buffer.
1547         if (pOut >= (szBuffer + countof(szBuffer) - 2)) {
1548             Info(slide, 1, ((char*)slide, "path too long: %s\n",
1549               FnFilter1(G.filename)));
1550             return MPN_ERR_TOOLONG;
1551         }
1552 
1553         // Examine the next character in our input buffer.
1554         switch (workch) {
1555 
1556           // Check for a directory wack.
1557           case '/':
1558             *pOut = '\0';
1559             // Skip dir traversals unless they are explicitly allowed.
1560             if (strcmp(pPathComp, ".") == 0) {
1561                 // don't bother appending "./" to the path
1562                 *pPathComp = '\0';
1563             } else if (!uO.ddotflag && strcmp(pPathComp, "..") == 0) {
1564                 // "../" dir traversal detected, skip over it
1565                 *pPathComp = '\0';
1566                 killed_ddot = TRUE;     // set "show message" flag
1567             }
1568             // When path component is not empty, append it now.
1569             if (*pPathComp == '\0') {
1570                 // Reset insert pos to start of path component.
1571                 pOut = pPathComp;
1572             } else {
1573                 if (!SmartCreateDirectory(__G__ szBuffer, &created_dir)) {
1574                    Info(slide, 1, ((char*)slide, "failure extracting: %s\n",
1575                         FnFilter1(G.filename)));
1576                    return MPN_ERR_SKIP;
1577                 }
1578                 *(pOut++) = '\\';
1579                 pPathComp = pOut;  // Remember start pos of new path component
1580             }
1581             pLastSemi = NULL;  // Leave any directory semi-colons alone
1582             break;
1583 
1584           // Check for illegal characters and replace with underscore.
1585           case ':':
1586           case '\\':
1587           case '*':
1588           case '?':
1589           case '"':
1590           case '<':
1591           case '>':
1592           case '|':
1593             *(pOut++) = '_';
1594             break;
1595 
1596           // Check for start of VMS version.
1597           case ';':
1598             pLastSemi = pOut;  // Make note as to where we are.
1599             *(pOut++) = ';';   // Leave the semi-colon alone for now.
1600             break;
1601 
1602           default:
1603             // Allow European characters and spaces in filenames.
1604 #ifdef _MBCS
1605             if ((UCHAR)workch >= 0x20) {
1606                 memcpy(pOut, pIn, CLEN(pIn));
1607                 INCSTR(pOut);
1608             } else {
1609                 *(pOut++) = '_';
1610             }
1611 #else
1612             *(pOut++) = (((UCHAR)workch >= 0x20) ? workch : '_');
1613 #endif
1614        }
1615     }
1616 
1617     // Show warning when stripping insecure "parent dir" path components
1618     if (killed_ddot && QCOND2) {
1619         Info(slide, 0, ((char *)slide,
1620           "warning:  skipped \"../\" path component(s) in %s\n",
1621           FnFilter1(G.filename)));
1622         if (!(error & ~MPN_MASK))
1623             error = (error & MPN_MASK) | PK_WARN;
1624     }
1625 
1626     // Done with output buffer, terminate it.
1627     *pOut = '\0';
1628 
1629     // Remove any VMS version numbers if found (appended ";###").
1630     if (pLastSemi) {
1631 
1632         // Walk over all digits following the semi-colon.
1633         for (pOut = pLastSemi + 1; (*pOut >= '0') && (*pOut <= '9'); pOut++);
1634 
1635         // If we reached the end, then nuke the semi-colon and digits.
1636         if (!*pOut)
1637            *pLastSemi = '\0';
1638     }
1639 
1640     // Copy the mapped name back to the internal path buffer
1641     strcpy(G.filename, szBuffer);
1642 
1643 #ifdef POCKET_UNZIP
1644     // Fill in the mapped name buffer if the original caller requested us to.
1645     if (g_pExtractInfo->szMappedPath) {
1646         strcpy(g_pExtractInfo->szMappedPath, szBuffer);
1647     }
1648 #endif
1649 
1650     // If it is a directory, then display the "creating" status text.
1651     if ((pOut > szBuffer) && (lastchar(szBuffer, pOut-szBuffer) == '\\')) {
1652         if (created_dir) {
1653 #ifdef UNICODE
1654             TCHAR szFile[_MAX_PATH];
1655 
1656             MBSTOTSTR(szFile, G.filename, countof(szFile));
1657 #           define T_Fname  szFile
1658 #else
1659 #           define T_Fname  G.filename
1660 #endif
1661             if (QCOND2) {
1662                 Info(slide, 0, ((char *)slide, "   creating: %-22s\n",
1663                   FnFilter1(G.filename)));
1664             }
1665 
1666             // set file attributes:
1667             // The default for newly created directories is "DIR attribute
1668             // flags set", so there is no need to change attributes unless
1669             // one of the DOS style attribute flags is set. There is no need
1670             // to mask the readonly attribute, because it does not prevent
1671             // modifications in the new directory.
1672             if(G.pInfo->file_attr & (0x7F & ~FILE_ATTRIBUTE_DIRECTORY)) {
1673                 if (!SetFileAttributes(T_Fname, G.pInfo->file_attr & 0x7F))
1674                     Info(slide, 1, ((char *)slide,
1675                       "\nwarning (%d): could not set file attributes for %s\n",
1676                       (int)GetLastError(), FnFilter1(G.filename)));
1677             }
1678 
1679             /* set dir time (note trailing '/') */
1680             return (error & ~MPN_MASK) | MPN_CREATED_DIR;
1681         }
1682         /* dir existed already; don't look for data to extract */
1683         return (error & ~MPN_MASK) | MPN_INF_SKIP;
1684     }
1685 
1686     return error;
1687 }
1688 
1689 //******************************************************************************
1690 // Called from PROCESS.C
1691 int checkdir(__GPRO__ char *pathcomp, int flag) {
1692 #ifdef POCKET_UNZIP
1693 
1694     // This function is only called by free_G_buffers() from PROCESS.C with the
1695     // flag set to END.  We have nothing to do, so we just return success.
1696     return MPN_OK;
1697 
1698 #else // !POCKET_UNZIP
1699 
1700 #   define FN_MASK 7
1701 #   define FUNCTION (flag & FN_MASK)
1702     int rc = MPN_OK;
1703 
1704     switch (FUNCTION) {
1705     case ROOT:
1706       {
1707         // User specified a root path. save the root without separator
1708         char* pathcompStart;
1709 
1710         if (pathcomp == NULL) {
1711             G.rootlen = 0;      // trivial NULL clause...
1712             break;
1713         }
1714         if (G.rootlen > 0)
1715             break;              // nothing to do, rootpath was already set
1716 
1717         G.rootlen = strlen(pathcomp);
1718         pathcompStart = pathcomp;
1719         // Strip the drive if given. CE does not support Drive
1720         if (pathcomp[1] == ':') {
1721             G.rootlen -= 2;
1722             pathcompStart += 2;
1723         }
1724         // Check for trailing separator, Strip if given
1725         // accomodate it if required.
1726         if (pathcomp[G.rootlen - 1] == '/' || pathcomp[G.rootlen - 1] == '\\')
1727             G.rootlen--;
1728         // Save the root
1729         memcpy(G.rootpath, pathcompStart, G.rootlen);
1730         G.rootpath[G.rootlen] = '\0';
1731 
1732         // Check if directory exists and try to create when neccessary.
1733         if (!SmartCreateDirectory(__G__ G.rootpath, NULL)) {
1734             rc = MPN_ERR_SKIP; // Create directory failed
1735         }
1736 
1737         // Add trailing path separator
1738         G.rootpath[G.rootlen++] = '\\';
1739         G.rootpath[G.rootlen] = '\0';
1740         break;
1741      }
1742 
1743     case END:
1744         Trace((stderr, "freeing rootpath\n"));
1745         if (G.rootlen > 0) {
1746             G.rootlen = 0;
1747             G.rootpath[0] = '\0';
1748         }
1749         break;
1750 
1751     default:
1752         rc = MPN_INVALID;       /* should never reach */
1753         break;
1754     }
1755     return rc;
1756 
1757 #endif // !POCKET_UNZIP
1758 } /* end function checkdir() */
1759 
1760 #ifdef POCKET_UNZIP
1761 //******************************************************************************
1762 // Called from EXTRACT.C and LIST.C
1763 int match(ZCONST char *string, ZCONST char *pattern, int ignore_case __WDLPRO)
1764 {
1765    // match() for the other ports compares a file in the Zip file with some
1766    // command line file pattern.  In our case, we always pass in exact matches,
1767    // so we can simply do a string compare to see if we have a match.
1768    return (strcmp(string, pattern) == 0);
1769 }
1770 
1771 //******************************************************************************
1772 // Called from PROCESS.C
1773 int iswild(ZCONST char *pattern) {
1774    // Our file patterns never contain wild characters.  They are always exact
1775    // matches of file names in our Zip file.
1776    return FALSE;
1777 }
1778 
1779 #else // !POCKET_UNZIP
1780 
1781 /************************/
1782 /*  Function version()  */
1783 /************************/
1784 
1785 void version(__GPRO)
1786 {
1787     // Dummy function, does nothing.
1788 }
1789 
1790 #ifndef WINDLL
1791 /* Console input not supported on CE so just return -1 */
1792 int getch_win32(void)
1793 {
1794   return -1;
1795 }
1796 #endif /* !WINDLL */
1797 #endif // !POCKET_UNZIP
1798 
1799 #if (defined(UNICODE_SUPPORT))
1800 /* convert wide character string to multi-byte character string */
1801 char *wide_to_local_string(ZCONST zwchar *wide_string,
1802                            int escape_all)
1803 {
1804   int i;
1805   wchar_t wc;
1806   int bytes_char;
1807   int default_used;
1808   int wsize = 0;
1809   int max_bytes = 9;
1810   char buf[9];
1811   char *buffer = NULL;
1812   char *local_string = NULL;
1813 
1814   for (wsize = 0; wide_string[wsize]; wsize++) ;
1815 
1816   if (max_bytes < MB_CUR_MAX)
1817     max_bytes = MB_CUR_MAX;
1818 
1819   if ((buffer = (char *)malloc(wsize * max_bytes + 1)) == NULL) {
1820     return NULL;
1821   }
1822 
1823   /* convert it */
1824   buffer[0] = '\0';
1825   for (i = 0; i < wsize; i++) {
1826     if (sizeof(wchar_t) < 4 && wide_string[i] > 0xFFFF) {
1827       /* wchar_t probably 2 bytes */
1828       /* could do surrogates if state_dependent and wctomb can do */
1829       wc = zwchar_to_wchar_t_default_char;
1830     } else {
1831       wc = (wchar_t)wide_string[i];
1832     }
1833     /* The C-RTL under WinCE does not support the generic C-style
1834      * Wide-to-MultiByte conversion functions (like wctomb() et. al.).
1835      * Therefore, we have to fall back to the underlying WinCE-API call to
1836      * get WCHAR-to-ANSI translation done.
1837      */
1838     bytes_char = WideCharToMultiByte(
1839                           CP_ACP, WC_COMPOSITECHECK,
1840                           &wc, 1,
1841                           (LPSTR)buf, sizeof(buf),
1842                           NULL, &default_used);
1843     if (default_used)
1844       bytes_char = -1;
1845     if (escape_all) {
1846       if (bytes_char == 1 && (uch)buf[0] <= 0x7f) {
1847         /* ASCII */
1848         strncat(buffer, buf, 1);
1849       } else {
1850         /* use escape for wide character */
1851         char *escape_string = wide_to_escape_string(wide_string[i]);
1852         strcat(buffer, escape_string);
1853         free(escape_string);
1854       }
1855     } else if (bytes_char > 0) {
1856       /* multi-byte char */
1857       strncat(buffer, buf, bytes_char);
1858     } else {
1859       /* no MB for this wide */
1860       /* use escape for wide character */
1861       char *escape_string = wide_to_escape_string(wide_string[i]);
1862       strcat(buffer, escape_string);
1863       free(escape_string);
1864     }
1865   }
1866   if ((local_string = (char *)realloc(buffer, strlen(buffer) + 1)) == NULL) {
1867     free(buffer);
1868     return NULL;
1869   }
1870 
1871   return local_string;
1872 }
1873 #endif /* UNICODE_SUPPORT */
1874