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