]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/win32/compat/compat.cpp
6df8dcdab4ea0b2b00d0a235c5a19c8da14a96d3
[bacula/bacula] / bacula / src / win32 / compat / compat.cpp
1 //                              -*- Mode: C++ -*-
2 // compat.cpp -- compatibilty layer to make bacula-fd run
3 //               natively under windows
4 //
5 // Copyright transferred from Raider Solutions, Inc to
6 //   Kern Sibbald and John Walker by express permission.
7 //
8 //  Copyright (C) 2004-2006 Kern Sibbald
9 //
10 //  This program is free software; you can redistribute it and/or
11 //  modify it under the terms of the GNU General Public License
12 //  version 2 as amended with additional clauses defined in the
13 //  file LICENSE in the main source directory.
14 //
15 //  This program is distributed in the hope that it will be useful,
16 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
17 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 //  the file LICENSE for additional details.
19 //
20 // Author          : Christopher S. Hull
21 // Created On      : Sat Jan 31 15:55:00 2004
22 // $Id$
23
24 #include "bacula.h"
25 #include "compat.h"
26 #include "jcr.h"
27
28 #define b_errno_win32 (1<<29)
29
30
31 /* UTF-8 to UCS2 path conversion is expensive,
32    so we cache the conversion. During backup the
33    conversion is called 3 times (lstat, attribs, open),
34    by using the cache this is reduced to 1 time */
35
36 static POOLMEM *g_pWin32ConvUTF8Cache = get_pool_memory (PM_FNAME);
37 static POOLMEM *g_pWin32ConvUCS2Cache = get_pool_memory (PM_FNAME);
38 static DWORD g_dwWin32ConvUTF8strlen = 0;
39 static pthread_mutex_t Win32Convmutex = PTHREAD_MUTEX_INITIALIZER;
40
41 static t_pVSSPathConvert   g_pVSSPathConvert;
42 static t_pVSSPathConvertW  g_pVSSPathConvertW;
43
44 void SetVSSPathConvert(t_pVSSPathConvert pPathConvert, t_pVSSPathConvertW pPathConvertW)
45 {
46    g_pVSSPathConvert = pPathConvert;
47    g_pVSSPathConvertW = pPathConvertW;
48 }
49
50 void Win32ConvCleanupCache()
51 {
52    if (g_pWin32ConvUTF8Cache) {
53       free_pool_memory(g_pWin32ConvUTF8Cache);
54       g_pWin32ConvUTF8Cache = NULL;
55    }
56
57    if (g_pWin32ConvUCS2Cache) {
58       free_pool_memory(g_pWin32ConvUCS2Cache);   
59       g_pWin32ConvUCS2Cache = NULL;
60    }
61
62    g_dwWin32ConvUTF8strlen = 0;
63 }
64
65
66 /* to allow the usage of the original version in this file here */
67 #undef fputs
68
69
70 //#define USE_WIN32_COMPAT_IO 1
71 #define USE_WIN32_32KPATHCONVERSION 1
72
73 extern DWORD   g_platform_id;
74 extern DWORD   g_MinorVersion;
75
76 // from MicroSoft SDK (KES) is the diff between Jan 1 1601 and Jan 1 1970
77 #ifdef HAVE_MINGW
78 #define WIN32_FILETIME_ADJUST 0x19DB1DED53E8000ULL 
79 #else
80 #define WIN32_FILETIME_ADJUST 0x19DB1DED53E8000I64
81 #endif
82
83 #define WIN32_FILETIME_SCALE  10000000             // 100ns/second
84
85 void conv_unix_to_win32_path(const char *name, char *win32_name, DWORD dwSize)
86 {
87     const char *fname = name;
88     char *tname = win32_name;
89
90     Dmsg0(100, "Enter convert_unix_to_win32_path\n");
91
92     if ((name[0] == '/' || name[0] == '\\') &&
93         (name[1] == '/' || name[1] == '\\') &&
94         (name[2] == '.') &&
95         (name[3] == '/' || name[3] == '\\')) {
96
97         *win32_name++ = '\\';
98         *win32_name++ = '\\';
99         *win32_name++ = '.';
100         *win32_name++ = '\\';
101
102         name += 4;
103     } else if (g_platform_id != VER_PLATFORM_WIN32_WINDOWS &&
104                g_pVSSPathConvert == NULL) {
105         /* allow path to be 32767 bytes */
106         *win32_name++ = '\\';
107         *win32_name++ = '\\';
108         *win32_name++ = '?';
109         *win32_name++ = '\\';
110     }
111
112     while (*name) {
113         /* Check for Unix separator and convert to Win32 */
114         if (name[0] == '/' && name[1] == '/') {  /* double slash? */
115            name++;                               /* yes, skip first one */
116         }
117         if (*name == '/') {
118             *win32_name++ = '\\';     /* convert char */
119         /* If Win32 separated that is "quoted", remove quote */
120         } else if (*name == '\\' && name[1] == '\\') {
121             *win32_name++ = '\\';
122             name++;                   /* skip first \ */
123         } else {
124             *win32_name++ = *name;    /* copy character */
125         }
126         name++;
127     }
128     /* Strip any trailing slash, if we stored something */
129     /* but leave "c:\" with backslash (root directory case */
130     if (*fname != 0 && win32_name[-1] == '\\' && strlen (fname) != 3) {
131         win32_name[-1] = 0;
132     } else {
133         *win32_name = 0;
134     }
135
136     /* here we convert to VSS specific file name which
137        can get longer because VSS will make something like
138        \\\\?\\GLOBALROOT\\Device\\HarddiskVolumeShadowCopy1\\bacula\\uninstall.exe
139        from c:\bacula\uninstall.exe
140     */
141     Dmsg1(100, "path=%s\n", tname);
142     if (g_pVSSPathConvert != NULL) {
143        POOLMEM *pszBuf = get_pool_memory (PM_FNAME);
144        pszBuf = check_pool_memory_size(pszBuf, dwSize);
145        bstrncpy(pszBuf, tname, strlen(tname)+1);
146        g_pVSSPathConvert(pszBuf, tname, dwSize);
147        free_pool_memory(pszBuf);
148     }
149
150     Dmsg1(100, "Leave cvt_u_to_win32_path path=%s\n", tname);
151 }
152
153 /* Conversion of a Unix filename to a Win32 filename */
154 void unix_name_to_win32(POOLMEM **win32_name, char *name)
155 {
156    /* One extra byte should suffice, but we double it */
157    /* add MAX_PATH bytes for VSS shadow copy name */
158    DWORD dwSize = 2*strlen(name)+MAX_PATH;
159    *win32_name = check_pool_memory_size(*win32_name, dwSize);
160    conv_unix_to_win32_path(name, *win32_name, dwSize);
161 }
162
163 POOLMEM* 
164 make_wchar_win32_path(POOLMEM *pszUCSPath, BOOL *pBIsRawPath /*= NULL*/)
165 {
166    /* created 02/27/2006 Thorsten Engel
167     * 
168     * This function expects an UCS-encoded standard wchar_t in pszUCSPath and
169     * will complete the input path to an absolue path of the form \\?\c:\path\file
170     * 
171     * With this trick, it is possible to have 32K characters long paths.
172     *
173     * Optionally one can use pBIsRawPath to determine id pszUCSPath contains a path
174     * to a raw windows partition.  
175     */
176
177    Dmsg0(100, "Enter wchar_win32_path\n");
178    if (pBIsRawPath) {
179       *pBIsRawPath = FALSE;              /* Initialize, set later */
180    }
181
182    if (!p_GetCurrentDirectoryW) {
183       Dmsg0(100, "Leave wchar_win32_path no change \n");
184       return pszUCSPath;
185    }
186    
187    wchar_t *name = (wchar_t *)pszUCSPath;
188
189    /* if it has already the desired form, exit without changes */
190    if (wcslen(name) > 3 && wcsncmp(name, L"\\\\?\\", 4) == 0) {
191       Dmsg0(100, "Leave wchar_win32_path no change \n");
192       return pszUCSPath;
193    }
194
195    POOLMEM *pwszBuf = get_pool_memory(PM_FNAME);
196    POOLMEM *pwszCurDirBuf = get_pool_memory(PM_FNAME);
197    DWORD dwCurDirPathSize = 0;
198
199    /* get buffer with enough size (name+max 6. wchars+1 null terminator */
200    DWORD dwBufCharsNeeded = (wcslen(name)+7);
201    pwszBuf = check_pool_memory_size(pwszBuf, dwBufCharsNeeded*sizeof(wchar_t));
202       
203    /* add \\?\ to support 32K long filepaths 
204       it is important to make absolute paths, so we add drive and
205       current path if necessary */
206
207    BOOL bAddDrive = TRUE;
208    BOOL bAddCurrentPath = TRUE;
209    BOOL bAddPrefix = TRUE;
210
211    /* does path begin with drive? if yes, it is absolute */
212    if (wcslen(name) >= 3 && (iswalpha (*name) && *(name+1) == ':'
213        && (*(name+2) == '\\' || *(name+2) == '/'))) {
214       bAddDrive = FALSE;
215       bAddCurrentPath = FALSE;
216    }
217
218    /* is path absolute? */
219    if (*name == '/' || *name == '\\')
220       bAddCurrentPath = FALSE; 
221
222    /* is path relative to itself?, if yes, skip ./ */
223    if (wcslen(name) > 2 && ((wcsncmp(name, L"./", 2) == 0) || (wcsncmp(name, L".\\", 2) == 0))) {
224       name += 2;
225    }
226
227    /* is path of form '//./'? */   
228    if (wcslen(name) > 3 && ((wcsncmp(name, L"//./", 4) == 0) || (wcsncmp(name, L"\\\\.\\", 4) == 0))) {
229       bAddDrive = FALSE;
230       bAddCurrentPath = FALSE;
231       bAddPrefix = FALSE;
232       if (pBIsRawPath) {
233          *pBIsRawPath = TRUE;
234       }
235    }
236
237    int nParseOffset = 0;
238    
239    /* add 4 bytes header */
240    if (bAddPrefix) {
241       nParseOffset = 4;
242       wcscpy((wchar_t *)pwszBuf, L"\\\\?\\");
243    }
244
245    /* get current path if needed */
246    if (bAddDrive || bAddCurrentPath) {
247       dwCurDirPathSize = p_GetCurrentDirectoryW(0, NULL);
248       if (dwCurDirPathSize > 0) {
249          /* get directory into own buffer as it may either return c:\... or \\?\C:\.... */         
250          pwszCurDirBuf = check_pool_memory_size(pwszCurDirBuf, (dwCurDirPathSize+1)*sizeof(wchar_t));
251          p_GetCurrentDirectoryW(dwCurDirPathSize,(wchar_t *)pwszCurDirBuf);
252       } else {
253          /* we have no info for doing so */
254          bAddDrive = FALSE;
255          bAddCurrentPath = FALSE;
256       }
257    }
258       
259
260    /* add drive if needed */
261    if (bAddDrive && !bAddCurrentPath) {
262       wchar_t szDrive[3];
263
264       if (dwCurDirPathSize > 3 && wcsncmp((LPCWSTR)pwszCurDirBuf, L"\\\\?\\", 4) == 0) {
265          /* copy drive character */
266          wcsncpy((wchar_t *)szDrive, (LPCWSTR)pwszCurDirBuf+4, 2);          
267       } else {
268          /* copy drive character */
269          wcsncpy((wchar_t *)szDrive, (LPCWSTR)pwszCurDirBuf, 2);  
270       }
271
272       szDrive[2] = 0;
273             
274       wcscat((wchar_t *)pwszBuf, szDrive);  
275       nParseOffset +=2;
276    }
277
278    /* add path if needed */
279    if (bAddCurrentPath) {
280       /* the 1 add. character is for the eventually added backslash */
281       dwBufCharsNeeded += dwCurDirPathSize+1; 
282       pwszBuf = check_pool_memory_size(pwszBuf, dwBufCharsNeeded*sizeof(wchar_t));
283       /* get directory into own buffer as it may either return c:\... or \\?\C:\.... */
284       
285       if (dwCurDirPathSize > 3 && wcsncmp((LPCWSTR)pwszCurDirBuf, L"\\\\?\\", 4) == 0) {
286          /* copy complete string */
287          wcscpy((wchar_t *)pwszBuf, (LPCWSTR)pwszCurDirBuf);          
288       } else {
289          /* append path  */
290          wcscat((wchar_t *)pwszBuf, (LPCWSTR)pwszCurDirBuf);       
291       }
292
293       nParseOffset = wcslen((LPCWSTR) pwszBuf);
294
295       /* check if path ends with backslash, if not, add one */
296       if (*((wchar_t *)pwszBuf+nParseOffset-1) != L'\\') {
297          wcscat((wchar_t *)pwszBuf, L"\\");
298          nParseOffset++;
299       }      
300    }
301
302
303    wchar_t *win32_name = (wchar_t *)pwszBuf+nParseOffset;
304
305    while (*name) {
306       /* Check for Unix separator and convert to Win32 */
307       if (*name == '/') {
308          *win32_name++ = '\\';     /* convert char */
309          /* If Win32 separated that is "quoted", remove quote */
310 /* HELPME (Thorsten Engel): I don't understand the following part
311  * and it removes a backslash from e.g. "\\.\c:" which I need for 
312  * RAW device access. So I took it out.  
313  */
314 #ifdef needed
315         } else if (*name == '\\' && name[1] == '\\') {
316          *win32_name++ = '\\';
317          name++;                     /* skip first \ */ 
318 #endif
319       } else {
320          *win32_name++ = *name;    /* copy character */
321       }
322       name++;
323    }
324    
325    /* null terminate string */
326    *win32_name = 0;
327
328    /* here we convert to VSS specific file name which
329     * can get longer because VSS will make something like
330     * \\\\?\\GLOBALROOT\\Device\\HarddiskVolumeShadowCopy1\\bacula\\uninstall.exe
331     * from c:\bacula\uninstall.exe
332    */ 
333    if (g_pVSSPathConvertW != NULL) {
334       /* is output buffer large enough? */
335       pwszBuf = check_pool_memory_size(pwszBuf, (dwBufCharsNeeded+MAX_PATH)*sizeof(wchar_t));
336       /* create temp. buffer */
337       POOLMEM* pszBuf = get_pool_memory(PM_FNAME);
338       pszBuf = check_pool_memory_size(pszBuf, (dwBufCharsNeeded+MAX_PATH)*sizeof(wchar_t));
339       if (bAddPrefix)
340          nParseOffset = 4;
341       else
342          nParseOffset = 0; 
343       wcsncpy((wchar_t *)pszBuf, (wchar_t *)pwszBuf+nParseOffset, wcslen((wchar_t *)pwszBuf)+1-nParseOffset);
344       g_pVSSPathConvertW((wchar_t *)pszBuf, (wchar_t *)pwszBuf, dwBufCharsNeeded+MAX_PATH);
345       free_pool_memory(pszBuf);
346    }   
347
348    free_pool_memory(pszUCSPath);
349    free_pool_memory(pwszCurDirBuf);
350
351    Dmsg1(100, "Leave wchar_win32_path=%s\n", pwszBuf);
352    return pwszBuf;
353 }
354
355 int
356 wchar_2_UTF8(char *pszUTF, const wchar_t *pszUCS, int cchChar)
357 {
358    /* the return value is the number of bytes written to the buffer.
359       The number includes the byte for the null terminator. */
360
361    if (p_WideCharToMultiByte) {
362          int nRet = p_WideCharToMultiByte(CP_UTF8,0,pszUCS,-1,pszUTF,cchChar,NULL,NULL);
363          ASSERT (nRet > 0);
364          return nRet;
365       }
366    else
367       return 0;
368 }
369
370 int
371 UTF8_2_wchar(POOLMEM **ppszUCS, const char *pszUTF)
372 {
373    /* the return value is the number of wide characters written to the buffer. */
374    /* convert null terminated string from utf-8 to ucs2, enlarge buffer if necessary */
375
376    if (p_MultiByteToWideChar) {
377       /* strlen of UTF8 +1 is enough */
378       DWORD cchSize = (strlen(pszUTF)+1);
379       *ppszUCS = check_pool_memory_size(*ppszUCS, cchSize*sizeof (wchar_t));
380
381       int nRet = p_MultiByteToWideChar(CP_UTF8, 0, pszUTF, -1, (LPWSTR) *ppszUCS,cchSize);
382       ASSERT (nRet > 0);
383       return nRet;
384    }
385    else
386       return 0;
387 }
388
389
390 void
391 wchar_win32_path(const char *name, wchar_t *win32_name)
392 {
393     const char *fname = name;
394     while (*name) {
395         /* Check for Unix separator and convert to Win32 */
396         if (*name == '/') {
397             *win32_name++ = '\\';     /* convert char */
398         /* If Win32 separated that is "quoted", remove quote */
399         } else if (*name == '\\' && name[1] == '\\') {
400             *win32_name++ = '\\';
401             name++;                   /* skip first \ */
402         } else {
403             *win32_name++ = *name;    /* copy character */
404         }
405         name++;
406     }
407     /* Strip any trailing slash, if we stored something */
408     if (*fname != 0 && win32_name[-1] == '\\') {
409         win32_name[-1] = 0;
410     } else {
411         *win32_name = 0;
412     }
413 }
414
415 int 
416 make_win32_path_UTF8_2_wchar(POOLMEM **pszUCS, const char *pszUTF, BOOL* pBIsRawPath /*= NULL*/)
417 {
418    P(Win32Convmutex);
419    /* if we find the utf8 string in cache, we use the cached ucs2 version.
420       we compare the stringlength first (quick check) and then compare the content.            
421    */
422    if (g_dwWin32ConvUTF8strlen == strlen(pszUTF)) {
423       if (bstrcmp(pszUTF, g_pWin32ConvUTF8Cache)) {
424          int32_t nBufSize = sizeof_pool_memory(g_pWin32ConvUCS2Cache);
425          *pszUCS = check_pool_memory_size(*pszUCS, nBufSize);      
426          wcscpy((LPWSTR) *pszUCS, (LPWSTR) g_pWin32ConvUCS2Cache);
427          V(Win32Convmutex);
428          return nBufSize / sizeof (WCHAR);
429       }
430    }
431
432    /* helper to convert from utf-8 to UCS-2 and to complete a path for 32K path syntax */
433    int nRet = UTF8_2_wchar(pszUCS, pszUTF);
434
435 #ifdef USE_WIN32_32KPATHCONVERSION
436    /* add \\?\ to support 32K long filepaths */
437    *pszUCS = make_wchar_win32_path(*pszUCS, pBIsRawPath);
438 #else
439    if (pBIsRawPath)
440       *pBIsRawPath = FALSE;
441 #endif
442
443    /* populate cache */      
444    g_pWin32ConvUCS2Cache = check_pool_memory_size(g_pWin32ConvUCS2Cache, sizeof_pool_memory(*pszUCS));
445    wcscpy((LPWSTR) g_pWin32ConvUCS2Cache, (LPWSTR) *pszUCS);
446    
447    g_dwWin32ConvUTF8strlen = strlen(pszUTF);
448    g_pWin32ConvUTF8Cache = check_pool_memory_size(g_pWin32ConvUTF8Cache, g_dwWin32ConvUTF8strlen+1);
449    bstrncpy(g_pWin32ConvUTF8Cache, pszUTF, g_dwWin32ConvUTF8strlen+1);
450    V(Win32Convmutex);
451
452    return nRet;
453 }
454
455 #if !defined(_MSC_VER) || (_MSC_VER < 1400) // VC8+
456 int umask(int)
457 {
458    return 0;
459 }
460 #endif
461
462 int fcntl(int fd, int cmd)
463 {
464    return 0;
465 }
466
467 int chmod(const char *, mode_t)
468 {
469    return 0;
470 }
471
472 int chown(const char *k, uid_t, gid_t)
473 {
474    return 0;
475 }
476
477 int lchown(const char *k, uid_t, gid_t)
478 {
479    return 0;
480 }
481
482 long int
483 random(void)
484 {
485     return rand();
486 }
487
488 void
489 srandom(unsigned int seed)
490 {
491    srand(seed);
492 }
493 // /////////////////////////////////////////////////////////////////
494 // convert from Windows concept of time to Unix concept of time
495 // /////////////////////////////////////////////////////////////////
496 void
497 cvt_utime_to_ftime(const time_t  &time, FILETIME &wintime)
498 {
499    uint64_t mstime = time;
500    mstime *= WIN32_FILETIME_SCALE;
501    mstime += WIN32_FILETIME_ADJUST;
502
503 #if defined(_MSC_VER)
504    wintime.dwLowDateTime = (DWORD)(mstime & 0xffffffffI64);
505 #else
506    wintime.dwLowDateTime = (DWORD)(mstime & 0xffffffffUL);
507 #endif
508    wintime.dwHighDateTime = (DWORD) ((mstime>>32)& 0xffffffffUL);
509 }
510
511 time_t
512 cvt_ftime_to_utime(const FILETIME &time)
513 {
514     uint64_t mstime = time.dwHighDateTime;
515     mstime <<= 32;
516     mstime |= time.dwLowDateTime;
517
518     mstime -= WIN32_FILETIME_ADJUST;
519     mstime /= WIN32_FILETIME_SCALE; // convert to seconds.
520
521     return (time_t) (mstime & 0xffffffff);
522 }
523
524 static const char *
525 errorString(void)
526 {
527    LPVOID lpMsgBuf;
528
529    FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
530                  FORMAT_MESSAGE_FROM_SYSTEM |
531                  FORMAT_MESSAGE_IGNORE_INSERTS,
532                  NULL,
533                  GetLastError(),
534                  MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default lang
535                  (LPTSTR) &lpMsgBuf,
536                  0,
537                  NULL);
538
539    /* Strip any \r or \n */
540    char *rval = (char *) lpMsgBuf;
541    char *cp = strchr(rval, '\r');
542    if (cp != NULL) {
543       *cp = 0;
544    } else {
545       cp = strchr(rval, '\n');
546       if (cp != NULL)
547          *cp = 0;
548    }
549    return rval;
550 }
551
552
553 static int
554 statDir(const char *file, struct stat *sb)
555 {
556    WIN32_FIND_DATAW info_w;       // window's file info
557    WIN32_FIND_DATAA info_a;       // window's file info
558
559    // cache some common vars to make code more transparent
560    DWORD* pdwFileAttributes;
561    DWORD* pnFileSizeHigh;
562    DWORD* pnFileSizeLow;
563    FILETIME* pftLastAccessTime;
564    FILETIME* pftLastWriteTime;
565    FILETIME* pftCreationTime;
566
567    if (file[1] == ':' && file[2] == 0) {
568         Dmsg1(99, "faking ROOT attrs(%s).\n", file);
569         sb->st_mode = S_IFDIR;
570         sb->st_mode |= S_IREAD|S_IEXEC|S_IWRITE;
571         time(&sb->st_ctime);
572         time(&sb->st_mtime);
573         time(&sb->st_atime);
574         return 0;
575     }
576
577    HANDLE h = INVALID_HANDLE_VALUE;
578
579    // use unicode or ascii
580    if (p_FindFirstFileW) {
581       POOLMEM* pwszBuf = get_pool_memory (PM_FNAME);
582       make_win32_path_UTF8_2_wchar(&pwszBuf, file);
583
584       h = p_FindFirstFileW((LPCWSTR) pwszBuf, &info_w);
585       free_pool_memory(pwszBuf);
586
587       pdwFileAttributes = &info_w.dwFileAttributes;
588       pnFileSizeHigh    = &info_w.nFileSizeHigh;
589       pnFileSizeLow     = &info_w.nFileSizeLow;
590       pftLastAccessTime = &info_w.ftLastAccessTime;
591       pftLastWriteTime  = &info_w.ftLastWriteTime;
592       pftCreationTime   = &info_w.ftCreationTime;
593    }
594    else if (p_FindFirstFileA) {
595       h = p_FindFirstFileA(file, &info_a);
596
597       pdwFileAttributes = &info_a.dwFileAttributes;
598       pnFileSizeHigh    = &info_a.nFileSizeHigh;
599       pnFileSizeLow     = &info_a.nFileSizeLow;
600       pftLastAccessTime = &info_a.ftLastAccessTime;
601       pftLastWriteTime  = &info_a.ftLastWriteTime;
602       pftCreationTime   = &info_a.ftCreationTime;
603    }
604
605     if (h == INVALID_HANDLE_VALUE) {
606         const char *err = errorString();
607         Dmsg2(99, "FindFirstFile(%s):%s\n", file, err);
608         LocalFree((void *)err);
609         errno = b_errno_win32;
610         return -1;
611     }
612
613     sb->st_mode = 0777;               /* start with everything */
614     if (*pdwFileAttributes & FILE_ATTRIBUTE_READONLY)
615         sb->st_mode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
616     if (*pdwFileAttributes & FILE_ATTRIBUTE_SYSTEM)
617         sb->st_mode &= ~S_IRWXO; /* remove everything for other */
618     if (*pdwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
619         sb->st_mode |= S_ISVTX; /* use sticky bit -> hidden */
620     sb->st_mode |= S_IFDIR;
621
622     sb->st_size = *pnFileSizeHigh;
623     sb->st_size <<= 32;
624     sb->st_size |= *pnFileSizeLow;
625     sb->st_blksize = 4096;
626     sb->st_blocks = (uint32_t)(sb->st_size + 4095)/4096;
627
628     sb->st_atime = cvt_ftime_to_utime(*pftLastAccessTime);
629     sb->st_mtime = cvt_ftime_to_utime(*pftLastWriteTime);
630     sb->st_ctime = cvt_ftime_to_utime(*pftCreationTime);
631     FindClose(h);
632
633     return 0;
634 }
635
636 int
637 fstat(int fd, struct stat *sb)
638 {
639     BY_HANDLE_FILE_INFORMATION info;
640     char tmpbuf[1024];
641
642     if (!GetFileInformationByHandle((HANDLE)fd, &info)) {
643         const char *err = errorString();
644         Dmsg2(99, "GetfileInformationByHandle(%s): %s\n", tmpbuf, err);
645         LocalFree((void *)err);
646         errno = b_errno_win32;
647         return -1;
648     }
649
650     sb->st_dev = info.dwVolumeSerialNumber;
651     sb->st_ino = info.nFileIndexHigh;
652     sb->st_ino <<= 32;
653     sb->st_ino |= info.nFileIndexLow;
654     sb->st_nlink = (short)info.nNumberOfLinks;
655     if (sb->st_nlink > 1) {
656        Dmsg1(99,  "st_nlink=%d\n", sb->st_nlink);
657     }
658
659     sb->st_mode = 0777;               /* start with everything */
660     if (info.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
661         sb->st_mode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
662     if (info.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM)
663         sb->st_mode &= ~S_IRWXO; /* remove everything for other */
664     if (info.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
665         sb->st_mode |= S_ISVTX; /* use sticky bit -> hidden */
666     sb->st_mode |= S_IFREG;
667
668     sb->st_size = info.nFileSizeHigh;
669     sb->st_size <<= 32;
670     sb->st_size |= info.nFileSizeLow;
671     sb->st_blksize = 4096;
672     sb->st_blocks = (uint32_t)(sb->st_size + 4095)/4096;
673     sb->st_atime = cvt_ftime_to_utime(info.ftLastAccessTime);
674     sb->st_mtime = cvt_ftime_to_utime(info.ftLastWriteTime);
675     sb->st_ctime = cvt_ftime_to_utime(info.ftCreationTime);
676
677     return 0;
678 }
679
680 static int
681 stat2(const char *file, struct stat *sb)
682 {
683     HANDLE h;
684     int rval = 0;
685     char tmpbuf[1024];
686     conv_unix_to_win32_path(file, tmpbuf, 1024);
687
688     DWORD attr = (DWORD)-1;
689
690     if (p_GetFileAttributesW) {
691       POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
692       make_win32_path_UTF8_2_wchar(&pwszBuf, tmpbuf);
693
694       attr = p_GetFileAttributesW((LPCWSTR) pwszBuf);
695       free_pool_memory(pwszBuf);
696     } else if (p_GetFileAttributesA) {
697        attr = p_GetFileAttributesA(tmpbuf);
698     }
699
700     if (attr == (DWORD)-1) {
701         const char *err = errorString();
702         Dmsg2(99, "GetFileAttributes(%s): %s\n", tmpbuf, err);
703         LocalFree((void *)err);
704         errno = b_errno_win32;
705         return -1;
706     }
707
708     if (attr & FILE_ATTRIBUTE_DIRECTORY)
709         return statDir(tmpbuf, sb);
710
711     h = CreateFileA(tmpbuf, GENERIC_READ,
712                    FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
713
714     if (h == INVALID_HANDLE_VALUE) {
715         const char *err = errorString();
716         Dmsg2(99, "Cannot open file for stat (%s):%s\n", tmpbuf, err);
717         LocalFree((void *)err);
718         errno = b_errno_win32;
719         return -1;
720     }
721
722     rval = fstat((int)h, sb);
723     CloseHandle(h);
724  
725     return rval;
726 }
727
728 int
729 stat(const char *file, struct stat *sb)
730 {
731    WIN32_FILE_ATTRIBUTE_DATA data;
732    errno = 0;
733
734
735    memset(sb, 0, sizeof(*sb));
736
737    /* why not allow win 95 to use p_GetFileAttributesExA ? 
738     * this function allows _some_ open files to be stat'ed 
739     * if (g_platform_id == VER_PLATFORM_WIN32_WINDOWS) {
740     *    return stat2(file, sb);
741     * }
742     */
743
744    if (p_GetFileAttributesExW) {
745       /* dynamically allocate enough space for UCS2 filename */
746       POOLMEM* pwszBuf = get_pool_memory (PM_FNAME);
747       make_win32_path_UTF8_2_wchar(&pwszBuf, file);
748
749       BOOL b = p_GetFileAttributesExW((LPCWSTR) pwszBuf, GetFileExInfoStandard, &data);
750       free_pool_memory(pwszBuf);
751
752       if (!b) {
753          return stat2(file, sb);
754       }
755    } else if (p_GetFileAttributesExA) {
756       if (!p_GetFileAttributesExA(file, GetFileExInfoStandard, &data)) {
757          return stat2(file, sb);
758        }
759    } else {
760       return stat2(file, sb);
761    }
762
763    sb->st_mode = 0777;               /* start with everything */
764    if (data.dwFileAttributes & FILE_ATTRIBUTE_READONLY) {
765       sb->st_mode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
766    }
767    if (data.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) {
768       sb->st_mode &= ~S_IRWXO; /* remove everything for other */
769    }
770    if (data.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) {
771       sb->st_mode |= S_ISVTX; /* use sticky bit -> hidden */
772    }
773    if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
774       sb->st_mode |= S_IFDIR;
775    } else {
776       sb->st_mode |= S_IFREG;
777    }
778
779    sb->st_nlink = 1;
780    sb->st_size = data.nFileSizeHigh;
781    sb->st_size <<= 32;
782    sb->st_size |= data.nFileSizeLow;
783    sb->st_blksize = 4096;
784    sb->st_blocks = (uint32_t)(sb->st_size + 4095)/4096;
785    sb->st_atime = cvt_ftime_to_utime(data.ftLastAccessTime);
786    sb->st_mtime = cvt_ftime_to_utime(data.ftLastWriteTime);
787    sb->st_ctime = cvt_ftime_to_utime(data.ftCreationTime);
788    return 0;
789 }
790
791 int fcntl(int fd, int cmd, long arg)
792 {
793    int rval = 0;
794
795    switch (cmd) {
796    case F_GETFL:
797       rval = O_NONBLOCK;
798       break;
799
800    case F_SETFL:
801       rval = 0;
802       break;
803
804    default:
805       errno = EINVAL;
806       rval = -1;
807       break;
808    }
809
810    return rval;
811 }
812
813 int
814 lstat(const char *file, struct stat *sb)
815 {
816    return stat(file, sb);
817 }
818
819 void
820 sleep(int sec)
821 {
822    Sleep(sec * 1000);
823 }
824
825 int
826 geteuid(void)
827 {
828    return 0;
829 }
830
831 int
832 execvp(const char *, char *[]) {
833    errno = ENOSYS;
834    return -1;
835 }
836
837
838 int
839 fork(void)
840 {
841    errno = ENOSYS;
842    return -1;
843 }
844
845 int
846 pipe(int[])
847 {
848    errno = ENOSYS;
849    return -1;
850 }
851
852 int
853 waitpid(int, int*, int)
854 {
855    errno = ENOSYS;
856    return -1;
857 }
858
859 int
860 readlink(const char *, char *, int)
861 {
862    errno = ENOSYS;
863    return -1;
864 }
865
866
867 #ifndef HAVE_MINGW
868 int
869 strcasecmp(const char *s1, const char *s2)
870 {
871    register int ch1, ch2;
872
873    if (s1==s2)
874       return 0;       /* strings are equal if same object. */
875    else if (!s1)
876       return -1;
877    else if (!s2)
878       return 1;
879    do {
880       ch1 = *s1;
881       ch2 = *s2;
882       s1++;
883       s2++;
884    } while (ch1 != 0 && tolower(ch1) == tolower(ch2));
885
886    return(ch1 - ch2);
887 }
888 #endif //HAVE_MINGW
889
890 int
891 strncasecmp(const char *s1, const char *s2, int len)
892 {
893    register int ch1 = 0, ch2 = 0;
894
895    if (s1==s2)
896       return 0;       /* strings are equal if same object. */
897    else if (!s1)
898       return -1;
899    else if (!s2)
900       return 1;
901
902    while (len--) {
903       ch1 = *s1;
904       ch2 = *s2;
905       s1++;
906       s2++;
907       if (ch1 == 0 || tolower(ch1) != tolower(ch2)) break;
908    }
909
910    return (ch1 - ch2);
911 }
912
913 int
914 gettimeofday(struct timeval *tv, struct timezone *)
915 {
916     SYSTEMTIME now;
917     FILETIME tmp;
918     GetSystemTime(&now);
919
920     if (tv == NULL) {
921        errno = EINVAL;
922        return -1;
923     }
924     if (!SystemTimeToFileTime(&now, &tmp)) {
925        errno = b_errno_win32;
926        return -1;
927     }
928
929     int64_t _100nsec = tmp.dwHighDateTime;
930     _100nsec <<= 32;
931     _100nsec |= tmp.dwLowDateTime;
932     _100nsec -= WIN32_FILETIME_ADJUST;
933
934     tv->tv_sec =(long) (_100nsec / 10000000);
935     tv->tv_usec = (long) ((_100nsec % 10000000)/10);
936     return 0;
937
938 }
939
940 /* For apcupsd this is in src/lib/wincompat.c */
941 extern "C" void syslog(int type, const char *fmt, ...) 
942 {
943 /*#ifndef HAVE_CONSOLE
944     MessageBox(NULL, msg, "Bacula", MB_OK);
945 #endif*/
946 }
947
948 void
949 closelog()
950 {
951 }
952
953 struct passwd *
954 getpwuid(uid_t)
955 {
956     return NULL;
957 }
958
959 struct group *
960 getgrgid(uid_t)
961 {
962     return NULL;
963 }
964
965 // implement opendir/readdir/closedir on top of window's API
966
967 typedef struct _dir
968 {
969     WIN32_FIND_DATAA data_a;    // window's file info (ansii version)
970     WIN32_FIND_DATAW data_w;    // window's file info (wchar version)
971     const char *spec;           // the directory we're traversing
972     HANDLE      dirh;           // the search handle
973     BOOL        valid_a;        // the info in data_a field is valid
974     BOOL        valid_w;        // the info in data_w field is valid
975     UINT32      offset;         // pseudo offset for d_off
976 } _dir;
977
978 DIR *
979 opendir(const char *path)
980 {
981     /* enough space for VSS !*/
982     int max_len = strlen(path) + MAX_PATH;
983     _dir *rval = NULL;
984     if (path == NULL) {
985        errno = ENOENT;
986        return NULL;
987     }
988
989     Dmsg1(100, "Opendir path=%s\n", path);
990     rval = (_dir *)malloc(sizeof(_dir));
991     memset (rval, 0, sizeof (_dir));
992     if (rval == NULL) return NULL;
993     char *tspec = (char *)malloc(max_len);
994     if (tspec == NULL) return NULL;
995
996     conv_unix_to_win32_path(path, tspec, max_len);
997     Dmsg1(100, "win32 path=%s\n", tspec);
998
999     // add backslash only if there is none yet (think of c:\)
1000     if (tspec[strlen(tspec)-1] != '\\')
1001       bstrncat(tspec, "\\*", max_len);
1002     else
1003       bstrncat(tspec, "*", max_len);
1004
1005     rval->spec = tspec;
1006
1007     // convert to wchar_t
1008     if (p_FindFirstFileW) {
1009       POOLMEM* pwcBuf = get_pool_memory(PM_FNAME);;
1010       make_win32_path_UTF8_2_wchar(&pwcBuf, rval->spec);
1011
1012       rval->dirh = p_FindFirstFileW((LPCWSTR)pwcBuf, &rval->data_w);
1013
1014       free_pool_memory(pwcBuf);
1015
1016       if (rval->dirh != INVALID_HANDLE_VALUE)
1017         rval->valid_w = 1;
1018     } else if (p_FindFirstFileA) {
1019       rval->dirh = p_FindFirstFileA(rval->spec, &rval->data_a);
1020
1021       if (rval->dirh != INVALID_HANDLE_VALUE)
1022         rval->valid_a = 1;
1023     } else goto err;
1024
1025
1026     Dmsg3(99, "opendir(%s)\n\tspec=%s,\n\tFindFirstFile returns %d\n",
1027           path, rval->spec, rval->dirh);
1028
1029     rval->offset = 0;
1030     if (rval->dirh == INVALID_HANDLE_VALUE)
1031         goto err;
1032
1033     if (rval->valid_w) {
1034       Dmsg1(99, "\tFirstFile=%s\n", rval->data_w.cFileName);
1035     }
1036
1037     if (rval->valid_a) {
1038       Dmsg1(99, "\tFirstFile=%s\n", rval->data_a.cFileName);
1039     }
1040
1041     return (DIR *)rval;
1042
1043 err:
1044     free((void *)rval->spec);
1045     free(rval);
1046     errno = b_errno_win32;
1047     return NULL;
1048 }
1049
1050 int
1051 closedir(DIR *dirp)
1052 {
1053     _dir *dp = (_dir *)dirp;
1054     FindClose(dp->dirh);
1055     free((void *)dp->spec);
1056     free((void *)dp);
1057     return 0;
1058 }
1059
1060 /*
1061   typedef struct _WIN32_FIND_DATA {
1062     DWORD dwFileAttributes;
1063     FILETIME ftCreationTime;
1064     FILETIME ftLastAccessTime;
1065     FILETIME ftLastWriteTime;
1066     DWORD nFileSizeHigh;
1067     DWORD nFileSizeLow;
1068     DWORD dwReserved0;
1069     DWORD dwReserved1;
1070     TCHAR cFileName[MAX_PATH];
1071     TCHAR cAlternateFileName[14];
1072 } WIN32_FIND_DATA, *PWIN32_FIND_DATA;
1073 */
1074
1075 static int
1076 copyin(struct dirent &dp, const char *fname)
1077 {
1078     dp.d_ino = 0;
1079     dp.d_reclen = 0;
1080     char *cp = dp.d_name;
1081     while (*fname) {
1082         *cp++ = *fname++;
1083         dp.d_reclen++;
1084     }
1085         *cp = 0;
1086     return dp.d_reclen;
1087 }
1088
1089 int
1090 readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result)
1091 {
1092     _dir *dp = (_dir *)dirp;
1093     if (dp->valid_w || dp->valid_a) {
1094       entry->d_off = dp->offset;
1095
1096       // copy unicode
1097       if (dp->valid_w) {
1098          char szBuf[MAX_PATH_UTF8+1];
1099          wchar_2_UTF8(szBuf,dp->data_w.cFileName);
1100          dp->offset += copyin(*entry, szBuf);
1101       } else if (dp->valid_a) { // copy ansi (only 1 will be valid)
1102          dp->offset += copyin(*entry, dp->data_a.cFileName);
1103       }
1104
1105       *result = entry;              /* return entry address */
1106       Dmsg4(99, "readdir_r(%p, { d_name=\"%s\", d_reclen=%d, d_off=%d\n",
1107             dirp, entry->d_name, entry->d_reclen, entry->d_off);
1108     } else {
1109 //      Dmsg0(99, "readdir_r !valid\n");
1110         errno = b_errno_win32;
1111         return -1;
1112     }
1113
1114     // get next file, try unicode first
1115     if (p_FindNextFileW)
1116        dp->valid_w = p_FindNextFileW(dp->dirh, &dp->data_w);
1117     else if (p_FindNextFileA)
1118        dp->valid_a = p_FindNextFileA(dp->dirh, &dp->data_a);
1119     else {
1120        dp->valid_a = FALSE;
1121        dp->valid_w = FALSE;
1122     }
1123
1124     return 0;
1125 }
1126
1127 /*
1128  * Dotted IP address to network address
1129  *
1130  * Returns 1 if  OK
1131  *         0 on error
1132  */
1133 int
1134 inet_aton(const char *a, struct in_addr *inp)
1135 {
1136    const char *cp = a;
1137    uint32_t acc = 0, tmp = 0;
1138    int dotc = 0;
1139
1140    if (!isdigit(*cp)) {         /* first char must be digit */
1141       return 0;                 /* error */
1142    }
1143    do {
1144       if (isdigit(*cp)) {
1145          tmp = (tmp * 10) + (*cp -'0');
1146       } else if (*cp == '.' || *cp == 0) {
1147          if (tmp > 255) {
1148             return 0;           /* error */
1149          }
1150          acc = (acc << 8) + tmp;
1151          dotc++;
1152          tmp = 0;
1153       } else {
1154          return 0;              /* error */
1155       }
1156    } while (*cp++ != 0);
1157    if (dotc != 4) {              /* want 3 .'s plus EOS */
1158       return 0;                  /* error */
1159    }
1160    inp->s_addr = htonl(acc);     /* store addr in network format */
1161    return 1;
1162 }
1163
1164 int
1165 nanosleep(const struct timespec *req, struct timespec *rem)
1166 {
1167     if (rem)
1168         rem->tv_sec = rem->tv_nsec = 0;
1169     Sleep((req->tv_sec * 1000) + (req->tv_nsec/100000));
1170     return 0;
1171 }
1172
1173 void
1174 init_signals(void terminate(int sig))
1175 {
1176
1177 }
1178
1179 void
1180 init_stack_dump(void)
1181 {
1182
1183 }
1184
1185
1186 long
1187 pathconf(const char *path, int name)
1188 {
1189     switch(name) {
1190     case _PC_PATH_MAX :
1191         if (strncmp(path, "\\\\?\\", 4) == 0)
1192             return 32767;
1193     case _PC_NAME_MAX :
1194         return 255;
1195     }
1196     errno = ENOSYS;
1197     return -1;
1198 }
1199
1200 int
1201 WSA_Init(void)
1202 {
1203     WORD wVersionRequested = MAKEWORD( 1, 1);
1204     WSADATA wsaData;
1205
1206     int err = WSAStartup(wVersionRequested, &wsaData);
1207
1208
1209     if (err != 0) {
1210         printf("Can not start Windows Sockets\n");
1211         errno = ENOSYS;
1212         return -1;
1213     }
1214
1215     return 0;
1216 }
1217
1218
1219 int
1220 win32_chdir(const char *dir)
1221 {
1222    if (p_SetCurrentDirectoryW) {
1223       POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
1224       make_win32_path_UTF8_2_wchar(&pwszBuf, dir);
1225
1226       BOOL b=p_SetCurrentDirectoryW((LPCWSTR)pwszBuf);
1227       
1228       free_pool_memory(pwszBuf);
1229
1230       if (!b) {
1231          errno = b_errno_win32;
1232          return -1;
1233       }
1234    }
1235    else if (p_SetCurrentDirectoryA) {
1236       if (0 == p_SetCurrentDirectoryA(dir)) {
1237          errno = b_errno_win32;
1238          return -1;
1239       }
1240    }
1241    else return -1;
1242
1243    return 0;
1244 }
1245
1246 int
1247 win32_mkdir(const char *dir)
1248 {
1249    if (p_wmkdir){
1250       POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
1251       make_win32_path_UTF8_2_wchar(&pwszBuf, dir);
1252
1253       int n = p_wmkdir((LPCWSTR)pwszBuf);
1254       free_pool_memory(pwszBuf);
1255       return n;
1256    }
1257
1258    return _mkdir(dir);
1259 }
1260
1261
1262 char *
1263 win32_getcwd(char *buf, int maxlen)
1264 {
1265    int n=0;
1266
1267    if (p_GetCurrentDirectoryW) {
1268       POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
1269       pwszBuf = check_pool_memory_size (pwszBuf, maxlen*sizeof(wchar_t));
1270
1271       n = p_GetCurrentDirectoryW(maxlen, (LPWSTR) pwszBuf);
1272       if (n!=0)
1273          n = wchar_2_UTF8 (buf, (wchar_t *)pwszBuf, maxlen)-1;
1274       free_pool_memory(pwszBuf);
1275
1276    } else if (p_GetCurrentDirectoryA)
1277       n = p_GetCurrentDirectoryA(maxlen, buf);
1278
1279    if (n == 0 || n > maxlen) return NULL;
1280
1281    if (n+1 > maxlen) return NULL;
1282    if (n != 3) {
1283        buf[n] = '\\';
1284        buf[n+1] = 0;
1285    }
1286    return buf;
1287 }
1288
1289 int
1290 win32_fputs(const char *string, FILE *stream)
1291 {
1292    /* we use WriteConsoleA / WriteConsoleA
1293       so we can be sure that unicode support works on win32.
1294       with fallback if something fails
1295    */
1296
1297    HANDLE hOut = GetStdHandle (STD_OUTPUT_HANDLE);
1298    if (hOut && (hOut != INVALID_HANDLE_VALUE) && p_WideCharToMultiByte &&
1299        p_MultiByteToWideChar && (stream == stdout)) {
1300
1301       POOLMEM* pwszBuf = get_pool_memory(PM_MESSAGE);
1302
1303       DWORD dwCharsWritten;
1304       DWORD dwChars;
1305
1306       dwChars = UTF8_2_wchar(&pwszBuf, string);
1307
1308       /* try WriteConsoleW */
1309       if (WriteConsoleW (hOut, pwszBuf, dwChars-1, &dwCharsWritten, NULL)) {
1310          free_pool_memory(pwszBuf);
1311          return dwCharsWritten;
1312       }
1313
1314       /* convert to local codepage and try WriteConsoleA */
1315       POOLMEM* pszBuf = get_pool_memory(PM_MESSAGE);
1316       pszBuf = check_pool_memory_size(pszBuf, dwChars+1);
1317
1318       dwChars = p_WideCharToMultiByte(GetConsoleOutputCP(),0,(LPCWSTR) pwszBuf,-1,pszBuf,dwChars,NULL,NULL);
1319       free_pool_memory(pwszBuf);
1320
1321       if (WriteConsoleA (hOut, pszBuf, dwChars-1, &dwCharsWritten, NULL)) {
1322          free_pool_memory(pszBuf);
1323          return dwCharsWritten;
1324       }
1325    }
1326
1327    return fputs(string, stream);
1328 }
1329
1330 char*
1331 win32_cgets (char* buffer, int len)
1332 {
1333    /* we use console ReadConsoleA / ReadConsoleW to be able to read unicode
1334       from the win32 console and fallback if seomething fails */
1335
1336    HANDLE hIn = GetStdHandle (STD_INPUT_HANDLE);
1337    if (hIn && (hIn != INVALID_HANDLE_VALUE) && p_WideCharToMultiByte && p_MultiByteToWideChar) {
1338       DWORD dwRead;
1339       wchar_t wszBuf[1024];
1340       char  szBuf[1024];
1341
1342       /* nt and unicode conversion */
1343       if (ReadConsoleW (hIn, wszBuf, 1024, &dwRead, NULL)) {
1344
1345          /* null terminate at end */
1346          if (wszBuf[dwRead-1] == L'\n') {
1347             wszBuf[dwRead-1] = L'\0';
1348             dwRead --;
1349          }
1350
1351          if (wszBuf[dwRead-1] == L'\r') {
1352             wszBuf[dwRead-1] = L'\0';
1353             dwRead --;
1354          }
1355
1356          wchar_2_UTF8(buffer, wszBuf, len);
1357          return buffer;
1358       }
1359
1360       /* win 9x and unicode conversion */
1361       if (ReadConsoleA (hIn, szBuf, 1024, &dwRead, NULL)) {
1362
1363          /* null terminate at end */
1364          if (szBuf[dwRead-1] == L'\n') {
1365             szBuf[dwRead-1] = L'\0';
1366             dwRead --;
1367          }
1368
1369          if (szBuf[dwRead-1] == L'\r') {
1370             szBuf[dwRead-1] = L'\0';
1371             dwRead --;
1372          }
1373
1374          /* convert from ansii to wchar_t */
1375          p_MultiByteToWideChar(GetConsoleCP(), 0, szBuf, -1, wszBuf,1024);
1376          /* convert from wchar_t to UTF-8 */
1377          if (wchar_2_UTF8(buffer, wszBuf, len))
1378             return buffer;
1379       }
1380    }
1381
1382    /* fallback */
1383    if (fgets(buffer, len, stdin))
1384       return buffer;
1385    else
1386       return NULL;
1387 }
1388
1389 int
1390 win32_unlink(const char *filename)
1391 {
1392    int nRetCode;
1393    if (p_wunlink) {
1394       POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
1395       make_win32_path_UTF8_2_wchar(&pwszBuf, filename);
1396
1397       nRetCode = _wunlink((LPCWSTR) pwszBuf);
1398
1399       /* special case if file is readonly,
1400       we retry but unset attribute before */
1401       if (nRetCode == -1 && errno == EACCES && p_SetFileAttributesW && p_GetFileAttributesW) {
1402          DWORD dwAttr =  p_GetFileAttributesW((LPCWSTR)pwszBuf);
1403          if (dwAttr != INVALID_FILE_ATTRIBUTES) {
1404             if (p_SetFileAttributesW((LPCWSTR)pwszBuf, dwAttr & ~FILE_ATTRIBUTE_READONLY)) {
1405                nRetCode = _wunlink((LPCWSTR) pwszBuf);
1406                /* reset to original if it didn't help */
1407                if (nRetCode == -1)
1408                   p_SetFileAttributesW((LPCWSTR)pwszBuf, dwAttr);
1409             }
1410          }
1411       }
1412       free_pool_memory(pwszBuf);
1413    } else {
1414       nRetCode = _unlink(filename);
1415
1416       /* special case if file is readonly,
1417       we retry but unset attribute before */
1418       if (nRetCode == -1 && errno == EACCES && p_SetFileAttributesA && p_GetFileAttributesA) {
1419          DWORD dwAttr =  p_GetFileAttributesA(filename);
1420          if (dwAttr != INVALID_FILE_ATTRIBUTES) {
1421             if (p_SetFileAttributesA(filename, dwAttr & ~FILE_ATTRIBUTE_READONLY)) {
1422                nRetCode = _unlink(filename);
1423                /* reset to original if it didn't help */
1424                if (nRetCode == -1)
1425                   p_SetFileAttributesA(filename, dwAttr);
1426             }
1427          }
1428       }
1429    }
1430    return nRetCode;
1431 }
1432
1433
1434 #include "mswinver.h"
1435
1436 char WIN_VERSION_LONG[64];
1437 char WIN_VERSION[32];
1438 char WIN_RAWVERSION[32];
1439
1440 class winver {
1441 public:
1442     winver(void);
1443 };
1444
1445 static winver INIT;                     // cause constructor to be called before main()
1446
1447
1448 winver::winver(void)
1449 {
1450     const char *version = "";
1451     const char *platform = "";
1452     OSVERSIONINFO osvinfo;
1453     osvinfo.dwOSVersionInfoSize = sizeof(osvinfo);
1454
1455     // Get the current OS version
1456     if (!GetVersionEx(&osvinfo)) {
1457         version = "Unknown";
1458         platform = "Unknown";
1459     }
1460         const int ver = _mkversion(osvinfo.dwPlatformId,
1461                                    osvinfo.dwMajorVersion,
1462                                    osvinfo.dwMinorVersion);
1463         snprintf(WIN_RAWVERSION, sizeof(WIN_RAWVERSION), "Windows %#08x", ver);
1464          switch (ver)
1465         {
1466         case MS_WINDOWS_95: (version =  "Windows 95"); break;
1467         case MS_WINDOWS_98: (version =  "Windows 98"); break;
1468         case MS_WINDOWS_ME: (version =  "Windows ME"); break;
1469         case MS_WINDOWS_NT4:(version =  "Windows NT 4.0"); platform = "NT"; break;
1470         case MS_WINDOWS_2K: (version =  "Windows 2000");platform = "NT"; break;
1471         case MS_WINDOWS_XP: (version =  "Windows XP");platform = "NT"; break;
1472         case MS_WINDOWS_S2003: (version =  "Windows Server 2003");platform = "NT"; break;
1473         default: version = WIN_RAWVERSION; break;
1474         }
1475
1476     bstrncpy(WIN_VERSION_LONG, version, sizeof(WIN_VERSION_LONG));
1477     snprintf(WIN_VERSION, sizeof(WIN_VERSION), "%s %lu.%lu.%lu",
1478              platform, osvinfo.dwMajorVersion, osvinfo.dwMinorVersion, osvinfo.dwBuildNumber);
1479
1480 #if 0
1481     HANDLE h = CreateFile("G:\\foobar", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
1482     CloseHandle(h);
1483 #endif
1484 #if 0
1485     BPIPE *b = open_bpipe("ls -l", 10, "r");
1486     char buf[1024];
1487     while (!feof(b->rfd)) {
1488         fgets(buf, sizeof(buf), b->rfd);
1489     }
1490     close_bpipe(b);
1491 #endif
1492 }
1493
1494 BOOL CreateChildProcess(VOID);
1495 VOID WriteToPipe(VOID);
1496 VOID ReadFromPipe(VOID);
1497 VOID ErrorExit(LPCSTR);
1498 VOID ErrMsg(LPTSTR, BOOL);
1499
1500 /**
1501  * Check for a quoted path,  if an absolute path name is given and it contains
1502  * spaces it will need to be quoted.  i.e.  "c:/Program Files/foo/bar.exe"
1503  * CreateProcess() says the best way to ensure proper results with executables
1504  * with spaces in path or filename is to quote the string.
1505  */
1506 const char *
1507 getArgv0(const char *cmdline)
1508 {
1509
1510     int inquote = 0;
1511     const char *cp;
1512     for (cp = cmdline; *cp; cp++)
1513     {
1514         if (*cp == '"') {
1515             inquote = !inquote;
1516         }
1517         if (!inquote && isspace(*cp))
1518             break;
1519     }
1520
1521
1522     int len = cp - cmdline;
1523     char *rval = (char *)malloc(len+1);
1524
1525     cp = cmdline;
1526     char *rp = rval;
1527
1528     while (len--)
1529         *rp++ = *cp++;
1530
1531     *rp = 0;
1532     return rval;
1533 }
1534
1535
1536 /**
1537  * OK, so it would seem CreateProcess only handles true executables:
1538  *  .com or .exe files.
1539  * So test to see whether we're getting a .bat file and if so grab
1540  * $COMSPEC value and pass batch file to it.
1541  */
1542 HANDLE
1543 CreateChildProcess(const char *cmdline, HANDLE in, HANDLE out, HANDLE err)
1544 {
1545     static const char *comspec = NULL;
1546     PROCESS_INFORMATION piProcInfo;
1547     STARTUPINFOA siStartInfo;
1548     BOOL bFuncRetn = FALSE;
1549
1550     if (comspec == NULL) {
1551        comspec = getenv("COMSPEC");
1552     }
1553     if (comspec == NULL) // should never happen
1554         return INVALID_HANDLE_VALUE;
1555
1556     // Set up members of the PROCESS_INFORMATION structure.
1557     ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION) );
1558
1559     // Set up members of the STARTUPINFO structure.
1560
1561     ZeroMemory( &siStartInfo, sizeof(STARTUPINFO) );
1562     siStartInfo.cb = sizeof(STARTUPINFO);
1563     // setup new process to use supplied handles for stdin,stdout,stderr
1564     // if supplied handles are not used the send a copy of our STD_HANDLE
1565     // as appropriate
1566     siStartInfo.dwFlags = STARTF_USESTDHANDLES;
1567
1568     if (in != INVALID_HANDLE_VALUE)
1569         siStartInfo.hStdInput = in;
1570     else
1571         siStartInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
1572
1573     if (out != INVALID_HANDLE_VALUE)
1574         siStartInfo.hStdOutput = out;
1575     else
1576         siStartInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
1577     if (err != INVALID_HANDLE_VALUE)
1578         siStartInfo.hStdError = err;
1579     else
1580         siStartInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
1581     // Create the child process.
1582
1583     char exeFile[256];
1584     int cmdLen = strlen(cmdline) + strlen(comspec) + 16;
1585
1586     char *cmdLine = (char *)alloca(cmdLen);
1587
1588     bstrncpy(exeFile, comspec, sizeof(exeFile));
1589     bstrncpy(cmdLine, comspec, cmdLen);
1590     bstrncat(cmdLine, " /c ", cmdLen);
1591     bstrncat(cmdLine, cmdline, cmdLen);
1592
1593     // try to execute program
1594     bFuncRetn = CreateProcessA(exeFile,
1595                               cmdLine, // command line
1596                               NULL, // process security attributes
1597                               NULL, // primary thread security attributes
1598                               TRUE, // handles are inherited
1599                               0, // creation flags
1600                               NULL, // use parent's environment
1601                               NULL, // use parent's current directory
1602                               &siStartInfo, // STARTUPINFO pointer
1603                               &piProcInfo); // receives PROCESS_INFORMATION
1604
1605     if (bFuncRetn == 0) {
1606         ErrorExit("CreateProcess failed\n");
1607         const char *err = errorString();
1608         Dmsg3(99, "CreateProcess(%s, %s, ...)=%s\n", exeFile, cmdLine, err);
1609         LocalFree((void *)err);
1610         return INVALID_HANDLE_VALUE;
1611     }
1612     // we don't need a handle on the process primary thread so we close
1613     // this now.
1614     CloseHandle(piProcInfo.hThread);
1615
1616     return piProcInfo.hProcess;
1617 }
1618
1619
1620 void
1621 ErrorExit (LPCSTR lpszMessage)
1622 {
1623     Dmsg1(0, "%s", lpszMessage);
1624 }
1625
1626
1627 /*
1628 typedef struct s_bpipe {
1629    pid_t worker_pid;
1630    time_t worker_stime;
1631    int wait;
1632    btimer_t *timer_id;
1633    FILE *rfd;
1634    FILE *wfd;
1635 } BPIPE;
1636 */
1637
1638 static void
1639 CloseIfValid(HANDLE handle)
1640 {
1641     if (handle != INVALID_HANDLE_VALUE)
1642         CloseHandle(handle);
1643 }
1644
1645 BPIPE *
1646 open_bpipe(char *prog, int wait, const char *mode)
1647 {
1648     HANDLE hChildStdinRd, hChildStdinWr, hChildStdinWrDup,
1649         hChildStdoutRd, hChildStdoutWr, hChildStdoutRdDup,
1650         hInputFile;
1651
1652     SECURITY_ATTRIBUTES saAttr;
1653
1654     BOOL fSuccess;
1655
1656     hChildStdinRd = hChildStdinWr = hChildStdinWrDup =
1657         hChildStdoutRd = hChildStdoutWr = hChildStdoutRdDup =
1658         hInputFile = INVALID_HANDLE_VALUE;
1659
1660     BPIPE *bpipe = (BPIPE *)malloc(sizeof(BPIPE));
1661     memset((void *)bpipe, 0, sizeof(BPIPE));
1662
1663     int mode_read = (mode[0] == 'r');
1664     int mode_write = (mode[0] == 'w' || mode[1] == 'w');
1665
1666
1667     // Set the bInheritHandle flag so pipe handles are inherited.
1668
1669     saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
1670     saAttr.bInheritHandle = TRUE;
1671     saAttr.lpSecurityDescriptor = NULL;
1672
1673     if (mode_read) {
1674
1675         // Create a pipe for the child process's STDOUT.
1676         if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
1677             ErrorExit("Stdout pipe creation failed\n");
1678             goto cleanup;
1679         }
1680         // Create noninheritable read handle and close the inheritable read
1681         // handle.
1682
1683         fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
1684                                    GetCurrentProcess(), &hChildStdoutRdDup , 0,
1685                                    FALSE,
1686                                    DUPLICATE_SAME_ACCESS);
1687         if ( !fSuccess ) {
1688             ErrorExit("DuplicateHandle failed");
1689             goto cleanup;
1690         }
1691
1692         CloseHandle(hChildStdoutRd);
1693         hChildStdoutRd = INVALID_HANDLE_VALUE;
1694     }
1695
1696     if (mode_write) {
1697
1698         // Create a pipe for the child process's STDIN.
1699
1700         if (!CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
1701             ErrorExit("Stdin pipe creation failed\n");
1702             goto cleanup;
1703         }
1704
1705         // Duplicate the write handle to the pipe so it is not inherited.
1706         fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
1707                                    GetCurrentProcess(), &hChildStdinWrDup,
1708                                    0,
1709                                    FALSE,                  // not inherited
1710                                    DUPLICATE_SAME_ACCESS);
1711         if (!fSuccess) {
1712             ErrorExit("DuplicateHandle failed");
1713             goto cleanup;
1714         }
1715
1716         CloseHandle(hChildStdinWr);
1717         hChildStdinWr = INVALID_HANDLE_VALUE;
1718     }
1719     // spawn program with redirected handles as appropriate
1720     bpipe->worker_pid = (pid_t)
1721         CreateChildProcess(prog,             // commandline
1722                            hChildStdinRd,    // stdin HANDLE
1723                            hChildStdoutWr,   // stdout HANDLE
1724                            hChildStdoutWr);  // stderr HANDLE
1725
1726     if ((HANDLE) bpipe->worker_pid == INVALID_HANDLE_VALUE)
1727         goto cleanup;
1728
1729     bpipe->wait = wait;
1730     bpipe->worker_stime = time(NULL);
1731
1732     if (mode_read) {
1733         CloseHandle(hChildStdoutWr); // close our write side so when
1734                                      // process terminates we can
1735                                      // detect eof.
1736         // ugly but convert WIN32 HANDLE to FILE*
1737         int rfd = _open_osfhandle((long)hChildStdoutRdDup, O_RDONLY);
1738         if (rfd >= 0) {
1739            bpipe->rfd = _fdopen(rfd, "r");
1740         }
1741     }
1742     if (mode_write) {
1743         CloseHandle(hChildStdinRd); // close our read side so as not
1744                                     // to interfre with child's copy
1745         // ugly but convert WIN32 HANDLE to FILE*
1746         int wfd = _open_osfhandle((long)hChildStdinWrDup, O_WRONLY);
1747         if (wfd >= 0) {
1748            bpipe->wfd = _fdopen(wfd, "w");
1749         }
1750     }
1751
1752     if (wait > 0) {
1753         bpipe->timer_id = start_child_timer(bpipe->worker_pid, wait);
1754     }
1755
1756     return bpipe;
1757
1758 cleanup:
1759
1760     CloseIfValid(hChildStdoutRd);
1761     CloseIfValid(hChildStdoutRdDup);
1762     CloseIfValid(hChildStdinWr);
1763     CloseIfValid(hChildStdinWrDup);
1764
1765     free((void *) bpipe);
1766     errno = b_errno_win32;            /* do GetLastError() for error code */
1767     return NULL;
1768 }
1769
1770
1771 int
1772 kill(int pid, int signal)
1773 {
1774    int rval = 0;
1775    if (!TerminateProcess((HANDLE)pid, (UINT) signal)) {
1776       rval = -1;
1777       errno = b_errno_win32;
1778    }
1779    CloseHandle((HANDLE)pid);
1780    return rval;
1781 }
1782
1783
1784 int
1785 close_bpipe(BPIPE *bpipe)
1786 {
1787    int rval = 0;
1788    int32_t remaining_wait = bpipe->wait;
1789
1790    if (remaining_wait == 0) {         /* wait indefinitely */
1791       remaining_wait = INT32_MAX;
1792    }
1793    for ( ;; ) {
1794       DWORD exitCode;
1795       if (!GetExitCodeProcess((HANDLE)bpipe->worker_pid, &exitCode)) {
1796          const char *err = errorString();
1797          rval = b_errno_win32;
1798          Dmsg1(0, "GetExitCode error %s\n", err);
1799          LocalFree((void *)err);
1800          break;
1801       }
1802       if (exitCode == STILL_ACTIVE) {
1803          if (remaining_wait <= 0) {
1804             rval = ETIME;             /* timed out */
1805             break;
1806          }
1807          bmicrosleep(1, 0);           /* wait one second */
1808          remaining_wait--;
1809       } else if (exitCode != 0) {
1810          /* Truncate exit code as it doesn't seem to be correct */
1811          rval = (exitCode & 0xFF) | b_errno_exit;
1812          break;
1813       } else {
1814          break;                       /* Shouldn't get here */
1815       }
1816    }
1817
1818    if (bpipe->timer_id) {
1819        stop_child_timer(bpipe->timer_id);
1820    }
1821    if (bpipe->rfd) fclose(bpipe->rfd);
1822    if (bpipe->wfd) fclose(bpipe->wfd);
1823    free((void *)bpipe);
1824    return rval;
1825 }
1826
1827 int
1828 close_wpipe(BPIPE *bpipe)
1829 {
1830     int stat = 1;
1831
1832     if (bpipe->wfd) {
1833         fflush(bpipe->wfd);
1834         if (fclose(bpipe->wfd) != 0) {
1835             stat = 0;
1836         }
1837         bpipe->wfd = NULL;
1838     }
1839     return stat;
1840 }
1841
1842 #include "findlib/find.h"
1843
1844 int
1845 utime(const char *fname, struct utimbuf *times)
1846 {
1847     FILETIME acc, mod;
1848     char tmpbuf[5000];
1849
1850     conv_unix_to_win32_path(fname, tmpbuf, 5000);
1851
1852     cvt_utime_to_ftime(times->actime, acc);
1853     cvt_utime_to_ftime(times->modtime, mod);
1854
1855     HANDLE h = INVALID_HANDLE_VALUE;
1856
1857     if (p_CreateFileW) {
1858       POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
1859       make_win32_path_UTF8_2_wchar(&pwszBuf, tmpbuf);
1860
1861       h = p_CreateFileW((LPCWSTR)pwszBuf,
1862                         FILE_WRITE_ATTRIBUTES,
1863                         FILE_SHARE_WRITE|FILE_SHARE_READ|FILE_SHARE_DELETE,
1864                         NULL,
1865                         OPEN_EXISTING,
1866                         FILE_FLAG_BACKUP_SEMANTICS, // required for directories
1867                         NULL);
1868
1869       free_pool_memory(pwszBuf);
1870     } else if (p_CreateFileA) {
1871       h = p_CreateFileA(tmpbuf,
1872                         FILE_WRITE_ATTRIBUTES,
1873                         FILE_SHARE_WRITE|FILE_SHARE_READ|FILE_SHARE_DELETE,
1874                         NULL,
1875                         OPEN_EXISTING,
1876                         FILE_FLAG_BACKUP_SEMANTICS, // required for directories
1877                         NULL);
1878     }
1879
1880     if (h == INVALID_HANDLE_VALUE) {
1881         const char *err = errorString();
1882         Dmsg2(99, "Cannot open file \"%s\" for utime(): ERR=%s", tmpbuf, err);
1883         LocalFree((void *)err);
1884         errno = b_errno_win32;
1885         return -1;
1886     }
1887
1888     int rval = SetFileTime(h, NULL, &acc, &mod) ? 0 : -1;
1889     CloseHandle(h);
1890     if (rval == -1) {
1891        errno = b_errno_win32;
1892     }
1893     return rval;
1894 }
1895
1896 #if 0
1897 int
1898 file_open(const char *file, int flags, int mode)
1899 {
1900     DWORD access = 0;
1901     DWORD shareMode = 0;
1902     DWORD create = 0;
1903     DWORD msflags = 0;
1904     HANDLE foo = INVALID_HANDLE_VALUE;
1905     const char *remap = file;
1906
1907     if (flags & O_WRONLY) access = GENERIC_WRITE;
1908     else if (flags & O_RDWR) access = GENERIC_READ|GENERIC_WRITE;
1909     else access = GENERIC_READ;
1910
1911     if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
1912        create = CREATE_NEW;
1913     else if ((flags & (O_CREAT | O_TRUNC)) == (O_CREAT | O_TRUNC))
1914        create = CREATE_ALWAYS;
1915     else if (flags & O_CREAT)
1916        create = OPEN_ALWAYS;
1917     else if (flags & O_TRUNC)
1918        create = TRUNCATE_EXISTING;
1919     else 
1920        create = OPEN_EXISTING;
1921
1922     shareMode = 0;
1923
1924     if (flags & O_APPEND) {
1925         printf("open...APPEND not implemented yet.");
1926         exit(-1);
1927     }
1928
1929     if (p_CreateFileW) {
1930        POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
1931        make_win32_path_UTF8_2_wchar(&pwszBuf, file);
1932
1933        foo = p_CreateFileW((LPCWSTR) pwszBuf, access, shareMode, NULL, create, msflags, NULL);
1934        free_pool_memory(pwszBuf);
1935     } else if (p_CreateFileA)
1936        foo = CreateFile(file, access, shareMode, NULL, create, msflags, NULL);
1937
1938     if (INVALID_HANDLE_VALUE == foo) {
1939         errno = b_errno_win32;
1940         return(int) -1;
1941     }
1942     return (int)foo;
1943
1944 }
1945
1946
1947 int
1948 file_close(int fd)
1949 {
1950     if (!CloseHandle((HANDLE)fd)) {
1951         errno = b_errno_win32;
1952         return -1;
1953     }
1954
1955     return 0;
1956 }
1957
1958 ssize_t
1959 file_write(int fd, const void *data, ssize_t len)
1960 {
1961     BOOL status;
1962     DWORD bwrite;
1963     status = WriteFile((HANDLE)fd, data, len, &bwrite, NULL);
1964     if (status) return bwrite;
1965     errno = b_errno_win32;
1966     return -1;
1967 }
1968
1969
1970 ssize_t
1971 file_read(int fd, void *data, ssize_t len)
1972 {
1973     BOOL status;
1974     DWORD bread;
1975
1976     status = ReadFile((HANDLE)fd, data, len, &bread, NULL);
1977     if (status) return bread;
1978     errno = b_errno_win32;
1979     return -1;
1980 }
1981
1982 off_t
1983 file_seek(int fd, off_t offset, int whence)
1984 {
1985     DWORD method = 0;
1986     DWORD val;
1987     switch (whence) {
1988     case SEEK_SET :
1989         method = FILE_BEGIN;
1990         break;
1991     case SEEK_CUR:
1992         method = FILE_CURRENT;
1993         break;
1994     case SEEK_END:
1995         method = FILE_END;
1996         break;
1997     default:
1998         errno = EINVAL;
1999         return -1;
2000     }
2001
2002     if ((val=SetFilePointer((HANDLE)fd, (DWORD)offset, NULL, method)) == INVALID_SET_FILE_POINTER) {
2003        errno = b_errno_win32;
2004        return -1;
2005     }
2006     /* ***FIXME*** I doubt this works right */
2007     return val;
2008 }
2009
2010 int
2011 file_dup2(int, int)
2012 {
2013     errno = ENOSYS;
2014     return -1;
2015 }
2016 #endif
2017
2018 #ifdef HAVE_MINGW
2019 /* syslog function, added by Nicolas Boichat */
2020 void openlog(const char *ident, int option, int facility) {}  
2021 #endif //HAVE_MINGW