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