version 0.1.6
[fms.git] / libs / shttpd / compat_wince.c
1 /*
2  vi:ts=8:sw=8:noet
3  * Copyright (c) 2006 Luke Dunstan <infidel@users.sourceforge.net>
4  * Partly based on code by David Kashtan, Validus Medical Systems
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a
7  * copy of this software and associated documentation files (the "Software"),
8  * to deal in the Software without restriction, including without limitation
9  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10  * and/or sell copies of the Software, and to permit persons to whom the
11  * Software is furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included
14  * in all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22  * DEALINGS IN THE SOFTWARE.
23  */
24
25 /*
26  * This file provides various functions that are available on desktop Windows
27  * but not on Windows CE
28  */
29
30 #ifdef _MSC_VER
31 /* Level 4 warnings caused by windows.h */
32 #pragma warning(disable : 4214) // nonstandard extension used : bit field types other than int
33 #pragma warning(disable : 4115) // named type definition in parentheses
34 #pragma warning(disable : 4201) // nonstandard extension used : nameless struct/union
35 #pragma warning(disable : 4514) // unreferenced inline function has been removed
36 #pragma warning(disable : 4244) // conversion from 'int ' to 'unsigned short ', possible loss of data
37 #pragma warning(disable : 4100) // unreferenced formal parameter
38 #endif
39
40 #include <windows.h>
41 #include <stdlib.h>
42
43 #include "compat_wince.h"
44
45
46 static WCHAR *to_wide_string(LPCSTR pStr)
47 {
48         int nwide;
49         WCHAR *buf;
50
51         if(pStr == NULL)
52                 return NULL;
53         nwide = MultiByteToWideChar(CP_ACP, 0, pStr, -1, NULL, 0);
54         if(nwide == 0)
55                 return NULL;
56         buf = malloc(nwide * sizeof(WCHAR));
57         if(buf == NULL) {
58                 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
59                 return NULL;
60         }
61         if(MultiByteToWideChar(CP_ACP, 0, pStr, -1, buf, nwide) == 0) {
62                 free(buf);
63                 return NULL;
64         }
65         return buf;
66 }
67
68 FILE *fdopen(int handle, const char *mode)
69 {
70         WCHAR *wmode = to_wide_string(mode);
71         FILE *result;
72
73         if(wmode != NULL)
74                 result = _wfdopen((void *)handle, wmode);
75         else
76                 result = NULL;
77         free(wmode);
78         return result;
79 }
80
81 /*
82  *      Time conversion constants
83  */
84 #define FT_EPOCH (116444736000000000i64)
85 #define FT_TICKS (10000000i64)
86
87  /*
88  *      Convert a FILETIME to a time_t
89  */
90 static time_t convert_FILETIME_to_time_t(FILETIME *File_Time)
91 {
92         __int64 Temp;
93
94         /*
95          *      Convert the FILETIME structure to 100nSecs since 1601 (as a 64-bit value)
96          */
97         Temp = (((__int64)File_Time->dwHighDateTime) << 32) + (__int64)File_Time->dwLowDateTime;
98         /*
99          *      Convert to seconds from 1970
100          */
101         return((time_t)((Temp - FT_EPOCH) / FT_TICKS));
102 }
103
104 /*
105  *      Convert a FILETIME to a tm structure
106  */
107 static struct tm *Convert_FILETIME_To_tm(FILETIME *File_Time)
108 {
109         SYSTEMTIME System_Time;
110         static struct tm tm = {0};
111         static const short Day_Of_Year_By_Month[12] = {(short)(0),
112                                                        (short)(31),
113                                                        (short)(31 + 28),
114                                                        (short)(31 + 28 + 31),
115                                                        (short)(31 + 28 + 31 + 30),
116                                                        (short)(31 + 28 + 31 + 30 + 31),
117                                                        (short)(31 + 28 + 31 + 30 + 31 + 30),
118                                                        (short)(31 + 28 + 31 + 30 + 31 + 30 + 31),
119                                                        (short)(31 + 28 + 31 + 30 + 31 + 30 + 31 + 31),
120                                                        (short)(31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30),
121                                                        (short)(31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31),
122                                                        (short)(31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30)};
123
124
125         /*
126          *      Turn the FILETIME into a SYSTEMTIME
127          */
128         FileTimeToSystemTime(File_Time, &System_Time);
129         /*
130          *      Use SYSTEMTIME to fill in the tm structure
131          */
132         tm.tm_sec = System_Time.wSecond;
133         tm.tm_min = System_Time.wMinute;
134         tm.tm_hour = System_Time.wHour;
135         tm.tm_mday = System_Time.wDay;
136         tm.tm_mon = System_Time.wMonth - 1;
137         tm.tm_year = System_Time.wYear - 1900;
138         tm.tm_wday = System_Time.wDayOfWeek;
139         tm.tm_yday = Day_Of_Year_By_Month[tm.tm_mon] + tm.tm_mday - 1;
140         if (tm.tm_mon >= 2) {
141                 /*
142                  *      Check for leap year (every 4 years but not every 100 years but every 400 years)
143                  */
144                 if ((System_Time.wYear % 4) == 0) {
145                         /*
146                          *      It Is a 4th year
147                          */
148                         if ((System_Time.wYear % 100) == 0) {
149                                 /*
150                                  *      It is a 100th year
151                                  */
152                                 if ((System_Time.wYear % 400) == 0) {
153                                         /*
154                                          *      It is a 400th year: It is a leap year
155                                          */
156                                         tm.tm_yday++;
157                                 }
158                         } else {
159                                 /*
160                                  *      It is not a 100th year: It is a leap year
161                                  */
162                                 tm.tm_yday++;
163                         }
164                 }
165         }
166         return(&tm);
167 }
168
169 /*
170  *      Convert a time_t to a FILETIME
171  */
172 static void Convert_time_t_To_FILETIME(time_t Time, FILETIME *File_Time)
173 {
174         __int64 Temp;
175
176         /*
177          *      Use 64-bit calculation to convert seconds since 1970 to
178          *      100nSecs since 1601
179          */
180         Temp = ((__int64)Time * FT_TICKS) + FT_EPOCH;
181         /*
182          *      Put it into the FILETIME structure
183          */
184         File_Time->dwLowDateTime = (DWORD)Temp;
185         File_Time->dwHighDateTime = (DWORD)(Temp >> 32);
186 }
187
188 /*
189  *      Convert a tm structure to a FILETIME
190  */
191 static FILETIME *Convert_tm_To_FILETIME(struct tm *tm)
192 {
193         SYSTEMTIME System_Time;
194         static FILETIME File_Time = {0};
195
196         /*
197          *      Use the tm structure to fill in a SYSTEM
198          */
199         System_Time.wYear = tm->tm_year + 1900;
200         System_Time.wMonth = tm->tm_mon + 1;
201         System_Time.wDayOfWeek = tm->tm_wday;
202         System_Time.wDay = tm->tm_mday;
203         System_Time.wHour = tm->tm_hour;
204         System_Time.wMinute = tm->tm_min;
205         System_Time.wSecond = tm->tm_sec;
206         System_Time.wMilliseconds = 0;
207         /*
208          *      Convert it to a FILETIME and return it
209          */
210         SystemTimeToFileTime(&System_Time, &File_Time);
211         return(&File_Time);
212 }
213
214
215 /************************************************************************/
216 /*                                                                      */
217 /*      Errno emulation:  There is no errno on Windows/CE and we need   */
218 /*                        to make it per-thread.  So we have a function */
219 /*                        that returns a pointer to the errno for the   */
220 /*                        current thread.                               */
221 /*                                                                      */
222 /*                        If there is ONLY the main thread then we can  */
223 /*                        quickly return some static storage.           */
224 /*                                                                      */
225 /*                        If we have multiple threads running, we use   */
226 /*                        Thread-Local Storage to hold the pointer      */
227 /*                                                                      */
228 /************************************************************************/
229
230 /*
231  *      Function pointer for returning errno pointer
232  */
233 static int *Initialize_Errno(void);
234 int *(*__WinCE_Errno_Pointer_Function)(void) = Initialize_Errno;
235
236 /*
237  *      Static errno storage for the main thread
238  */
239 static int Errno_Storage = 0;
240
241 /*
242  *      Thread-Local storage slot for errno
243  */
244 static int TLS_Errno_Slot = 0xffffffff;
245
246 /*
247  *      Number of threads we have running and critical section protection
248  *      for manipulating it
249  */
250 static int Number_Of_Threads = 0;
251 static CRITICAL_SECTION Number_Of_Threads_Critical_Section;
252
253 /*
254  *      For the main thread only -- return the errno pointer
255  */
256 static int *Get_Main_Thread_Errno(void)
257 {
258         return &Errno_Storage;
259 }
260
261 /*
262  *      When there is more than one thread -- return the errno pointer
263  */
264 static int *Get_Thread_Errno(void)
265 {
266         return (int *)TlsGetValue(TLS_Errno_Slot);
267 }
268
269 /*
270  *      Initialize a thread's errno
271  */
272 static void Initialize_Thread_Errno(int *Errno_Pointer)
273 {
274         /*
275          *      Make sure we have a slot
276          */
277         if (TLS_Errno_Slot == 0xffffffff) {
278                 /*
279                  *      No: Get one
280                  */
281                 TLS_Errno_Slot = (int)TlsAlloc();
282                 if (TLS_Errno_Slot == 0xffffffff) ExitProcess(3);
283         }
284         /*
285          *      We can safely check for 0 threads, because
286          *      only the main thread will be initializing
287          *      at this point.  Make sure the critical
288          *      section that protects the number of threads
289          *      is initialized
290          */
291         if (Number_Of_Threads == 0)
292                 InitializeCriticalSection(&Number_Of_Threads_Critical_Section);
293         /*
294          *      Store the errno pointer
295          */
296         if (TlsSetValue(TLS_Errno_Slot, (LPVOID)Errno_Pointer) == 0) ExitProcess(3);
297         /*
298          *      Bump the number of threads
299          */
300         EnterCriticalSection(&Number_Of_Threads_Critical_Section);
301         Number_Of_Threads++;
302         if (Number_Of_Threads > 1) {
303                 /*
304                  *      We have threads other than the main thread:
305                  *        Use thread-local storage
306                  */
307                 __WinCE_Errno_Pointer_Function = Get_Thread_Errno;
308         }
309         LeaveCriticalSection(&Number_Of_Threads_Critical_Section);
310 }
311
312 /*
313  *      Initialize errno emulation on Windows/CE (Main thread)
314  */
315 static int *Initialize_Errno(void)
316 {
317         /*
318          *      Initialize the main thread's errno in thread-local storage
319          */
320         Initialize_Thread_Errno(&Errno_Storage);
321         /*
322          *      Set the errno function to be the one that returns the
323          *      main thread's errno
324          */
325         __WinCE_Errno_Pointer_Function = Get_Main_Thread_Errno;
326         /*
327          *      Return the main thread's errno
328          */
329         return &Errno_Storage;
330 }
331
332 /*
333  *      Initialize errno emulation on Windows/CE (New thread)
334  */
335 void __WinCE_Errno_New_Thread(int *Errno_Pointer)
336 {
337         Initialize_Thread_Errno(Errno_Pointer);
338 }
339
340 /*
341  *      Note that a thread has exited
342  */
343 void __WinCE_Errno_Thread_Exit(void)
344 {
345         /*
346          *      Decrease the number of threads
347          */
348         EnterCriticalSection(&Number_Of_Threads_Critical_Section);
349         Number_Of_Threads--;
350         if (Number_Of_Threads <= 1) {
351                 /*
352                  *      We only have the main thread
353                  */
354                 __WinCE_Errno_Pointer_Function = Get_Main_Thread_Errno;
355         }
356         LeaveCriticalSection(&Number_Of_Threads_Critical_Section);
357 }
358
359
360 char *
361 strerror(int errnum)
362 {
363         return "(strerror not implemented)";
364 }
365
366 #define FT_EPOCH (116444736000000000i64)
367 #define FT_TICKS (10000000i64)
368
369 int
370 _wstat(const WCHAR *path, struct _stat *buffer)
371 {
372         WIN32_FIND_DATA data;
373         HANDLE handle;
374         WCHAR *p;
375
376         /* Fail if wildcard characters are specified */
377         if (wcscspn(path, L"?*") != wcslen(path))
378                 return -1;
379
380         handle = FindFirstFile(path, &data);
381         if (handle == INVALID_HANDLE_VALUE) {
382                 errno = GetLastError();
383                 return -1;
384         }
385         FindClose(handle);
386
387         /* Found: Convert the file times */
388         buffer->st_mtime = convert_FILETIME_to_time_t(&data.ftLastWriteTime);
389         if (data.ftLastAccessTime.dwLowDateTime || data.ftLastAccessTime.dwHighDateTime)
390                 buffer->st_atime = convert_FILETIME_to_time_t(&data.ftLastAccessTime);
391         else
392                 buffer->st_atime = buffer->st_mtime;
393         if (data.ftCreationTime.dwLowDateTime || data.ftCreationTime.dwHighDateTime)
394                 buffer->st_ctime = convert_FILETIME_to_time_t(&data.ftCreationTime);
395         else
396                 buffer->st_ctime = buffer->st_mtime;
397
398         /* Convert the file modes */
399         buffer->st_mode = (unsigned short)((data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? (S_IFDIR | S_IEXEC) : S_IFREG);
400         buffer->st_mode |= (data.dwFileAttributes & FILE_ATTRIBUTE_READONLY) ? S_IREAD : (S_IREAD | S_IWRITE);
401         if((p = wcsrchr(path, L'.')) != NULL) {
402                 p++;
403                 if (_wcsicmp(p, L".exe") == 0)
404                         buffer->st_mode |= S_IEXEC;
405         }
406         buffer->st_mode |= (buffer->st_mode & 0700) >> 3;
407         buffer->st_mode |= (buffer->st_mode & 0700) >> 6;
408         /* Set the other information */
409         buffer->st_nlink = 1;
410         buffer->st_size = (unsigned long int)data.nFileSizeLow;
411         buffer->st_uid = 0;
412         buffer->st_gid = 0;
413         buffer->st_ino = 0 /*data.dwOID ?*/;
414         buffer->st_dev = 0;
415
416         return 0;       /* success */
417 }
418
419 /*
420  *      Helper function for cemodule -- do an fstat() operation on a Win32 File Handle
421  */
422 int
423 _fstat(int handle, struct _stat *st)
424 {
425         BY_HANDLE_FILE_INFORMATION Data;
426
427         /*
428          *      Get the file information
429          */
430         if (!GetFileInformationByHandle((HANDLE)handle, &Data)) {
431                 /*
432                  *      Return error
433                  */
434                 errno = GetLastError();
435                 return(-1);
436         }
437         /*
438          *      Found: Convert the file times
439          */
440         st->st_mtime=(time_t)((*(__int64*)&Data.ftLastWriteTime-FT_EPOCH)/FT_TICKS);
441         if(Data.ftLastAccessTime.dwLowDateTime || Data.ftLastAccessTime.dwHighDateTime)
442                 st->st_atime=(time_t)((*(__int64*)&Data.ftLastAccessTime-FT_EPOCH)/FT_TICKS);
443         else
444                 st->st_atime=st->st_mtime ;
445         if(Data.ftCreationTime.dwLowDateTime || Data.ftCreationTime.dwHighDateTime )
446                 st->st_ctime=(time_t)((*(__int64*)&Data.ftCreationTime-FT_EPOCH)/FT_TICKS);
447         else
448                 st->st_ctime=st->st_mtime ;
449         /*
450          *      Convert the file modes
451          */
452         st->st_mode = (unsigned short)((Data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? (S_IFDIR | S_IEXEC) : S_IFREG);
453         st->st_mode |= (Data.dwFileAttributes & FILE_ATTRIBUTE_READONLY) ? S_IREAD : (S_IREAD | S_IWRITE);
454         st->st_mode |= (st->st_mode & 0700) >> 3;
455         st->st_mode |= (st->st_mode & 0700) >> 6;
456         /*
457          *      Set the other information
458          */
459         st->st_nlink=1;
460         st->st_size=(unsigned long int)Data.nFileSizeLow;
461         st->st_uid=0;
462         st->st_gid=0;
463         st->st_ino=0;
464         st->st_dev=0;
465         /*
466          *      Return success
467          */
468         return(0);
469 }
470
471 int _wopen(const wchar_t *filename, int oflag, ...)
472 {
473         DWORD Access, ShareMode, CreationDisposition;
474         HANDLE Handle;
475         static int Modes[4] = {0, (GENERIC_READ | GENERIC_WRITE), GENERIC_READ, GENERIC_WRITE};
476
477         /*
478          *      Calculate the CreateFile arguments
479          */
480         Access = Modes[oflag & O_MODE_MASK];
481         ShareMode = (oflag & O_EXCL) ? 0 : (FILE_SHARE_READ | FILE_SHARE_WRITE);
482         if (oflag & O_TRUNC)
483                 CreationDisposition = (oflag & O_CREAT) ? CREATE_ALWAYS : TRUNCATE_EXISTING;
484         else
485                 CreationDisposition = (oflag & O_CREAT) ? CREATE_NEW : OPEN_EXISTING;
486
487         Handle = CreateFileW(filename, Access, ShareMode, NULL, CreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL);
488
489         /*
490          *      Deal with errors
491          */
492         if (Handle == INVALID_HANDLE_VALUE) {
493                 errno = GetLastError();
494                 if ((errno == ERROR_ALREADY_EXISTS) || (errno == ERROR_FILE_EXISTS))
495                         errno = ERROR_ALREADY_EXISTS;
496                 return -1;
497         }
498         /*
499          *      Return the handle
500          */
501         return (int)Handle;
502 }
503
504 int
505 _close(int handle)
506 {
507         if(CloseHandle((HANDLE)handle))
508                 return 0;
509         errno = GetLastError();
510         return -1;
511 }
512
513 int
514 _write(int handle, const void *buffer, unsigned int count)
515 {
516         DWORD numwritten = 0;
517         if(!WriteFile((HANDLE)handle, buffer, count, &numwritten, NULL)) {
518                 errno = GetLastError();
519                 return -1;
520         }
521         return numwritten;
522 }
523
524 int
525 _read(int handle, void *buffer, unsigned int count)
526 {
527         DWORD numread = 0;
528         if(!ReadFile((HANDLE)handle, buffer, count, &numread, NULL)) {
529                 errno = GetLastError();
530                 return -1;
531         }
532         return numread;
533 }
534
535 long
536 _lseek(int handle, long offset, int origin)
537 {
538         DWORD dwMoveMethod;
539         DWORD result;
540
541         switch(origin) {
542                 default:
543                         errno = EINVAL;
544                         return -1L;
545                 case SEEK_SET:
546                         dwMoveMethod = FILE_BEGIN;
547                         break;
548                 case SEEK_CUR:
549                         dwMoveMethod = FILE_CURRENT;
550                         break;
551                 case SEEK_END:
552                         dwMoveMethod = FILE_END;
553                         break;
554         }
555         result = SetFilePointer((HANDLE)handle, offset, NULL, dwMoveMethod);
556         if(result == 0xFFFFFFFF) {
557                 errno = GetLastError();
558                 return -1;
559         }
560         return (long)result;
561 }
562
563 int
564 _wmkdir(const wchar_t *dirname)
565 {
566         if(!CreateDirectoryW(dirname, NULL)) {
567                 errno = GetLastError();
568                 return -1;
569         }
570         return 0;
571 }
572
573 int
574 _wremove(const wchar_t *filename)
575 {
576         if(!DeleteFileW(filename)) {
577                 errno = GetLastError();
578                 return -1;
579         }
580         return 0;
581 }
582
583 int
584 _wrename(const wchar_t *oldname, const wchar_t *newname)
585 {
586         if(!MoveFileW(oldname, newname)) {
587                 errno = GetLastError();
588                 return -1;
589         }
590         return 0;
591 }
592
593 wchar_t *
594 _wgetcwd(wchar_t *buffer, int maxlen)
595 {
596         wchar_t *result;
597         WCHAR wszPath[MAX_PATH + 1];
598         WCHAR *p;
599
600         if(!GetModuleFileNameW(NULL, wszPath, MAX_PATH + 1)) {
601                 errno = GetLastError();
602                 return NULL;
603         }
604         /* Remove the filename part of the path to leave the directory */
605         p = wcsrchr(wszPath, L'\\');
606         if(p)
607                 *p = L'\0';
608
609         if(buffer == NULL)
610                 result = _wcsdup(wszPath);
611         else if(wcslen(wszPath) + 1 > (size_t)maxlen) {
612                 result = NULL;
613                 errno = ERROR_INSUFFICIENT_BUFFER;
614         } else {
615                 wcsncpy(buffer, wszPath, maxlen);
616                 buffer[maxlen - 1] = L'\0';
617                 result = buffer;
618         }
619         return result;
620 }
621
622 /*
623  *      The missing "C" runtime gmtime() function
624  */
625 struct tm *gmtime(const time_t *TimeP)
626 {
627         FILETIME File_Time;
628
629         /*
630          *      Deal with null time pointer
631          */
632         if (!TimeP) return(NULL);
633         /*
634          *      time_t -> FILETIME -> tm
635          */
636         Convert_time_t_To_FILETIME(*TimeP, &File_Time);
637         return(Convert_FILETIME_To_tm(&File_Time));
638 }
639
640 /*
641  *      The missing "C" runtime localtime() function
642  */
643 struct tm *localtime(const time_t *TimeP)
644 {
645         FILETIME File_Time, Local_File_Time;
646
647         /*
648          *      Deal with null time pointer
649          */
650         if (!TimeP) return(NULL);
651         /*
652          *      time_t -> FILETIME -> Local FILETIME -> tm
653          */
654         Convert_time_t_To_FILETIME(*TimeP, &File_Time);
655         FileTimeToLocalFileTime(&File_Time, &Local_File_Time);
656         return(Convert_FILETIME_To_tm(&Local_File_Time));
657 }
658
659 /*
660  *      The missing "C" runtime mktime() function
661  */
662 time_t mktime(struct tm *tm)
663 {
664         FILETIME *Local_File_Time;
665         FILETIME File_Time;
666
667         /*
668          *      tm -> Local FILETIME -> FILETIME -> time_t
669          */
670         Local_File_Time = Convert_tm_To_FILETIME(tm);
671         LocalFileTimeToFileTime(Local_File_Time, &File_Time);
672         return(convert_FILETIME_to_time_t(&File_Time));
673 }
674
675 /*
676  *      Missing "C" runtime time() function
677  */
678 time_t time(time_t *TimeP)
679 {
680         SYSTEMTIME System_Time;
681         FILETIME File_Time;
682         time_t Result;
683
684         /*
685          *      Get the current system time
686          */
687         GetSystemTime(&System_Time);
688         /*
689          *      SYSTEMTIME -> FILETIME -> time_t
690          */
691         SystemTimeToFileTime(&System_Time, &File_Time);
692         Result = convert_FILETIME_to_time_t(&File_Time);
693         /*
694          *      Return the time_t
695          */
696         if (TimeP) *TimeP = Result;
697         return(Result);
698 }
699
700 static char Standard_Name[32] = "GMT";
701 static char Daylight_Name[32] = "GMT";
702 char *tzname[2] = {Standard_Name, Daylight_Name};
703 long timezone = 0;
704 int daylight = 0;
705
706 void tzset(void)
707 {
708         TIME_ZONE_INFORMATION Info;
709         int Result;
710
711         /*
712          *      Get our current timezone information
713          */
714         Result = GetTimeZoneInformation(&Info);
715         switch(Result) {
716                 /*
717                  *      We are on standard time
718                  */
719                 case TIME_ZONE_ID_STANDARD:
720                         daylight = 0;
721                         break;
722                 /*
723                  *      We are on daylight savings time
724                  */
725                 case TIME_ZONE_ID_DAYLIGHT:
726                         daylight = 1;
727                         break;
728                 /*
729                  *      We don't know the timezone information (leave it GMT)
730                  */
731                 default: return;
732         }
733         /*
734          *      Extract the timezone information
735          */
736         timezone = Info.Bias * 60;
737         if (Info.StandardName[0])
738                 WideCharToMultiByte(CP_ACP, 0, Info.StandardName, -1, Standard_Name, sizeof(Standard_Name) - 1, NULL, NULL);
739         if (Info.DaylightName[0])
740                 WideCharToMultiByte(CP_ACP, 0, Info.DaylightName, -1, Daylight_Name, sizeof(Daylight_Name) - 1, NULL, NULL);
741 }
742
743 /*** strftime() from newlib libc/time/strftime.c ***/
744
745 /*
746  * Sane snprintf(). Acts like snprintf(), but never return -1 or the
747  * value bigger than supplied buffer.
748  */
749 static int
750 Snprintf(char *buf, size_t buflen, const char *fmt, ...)
751 {
752         va_list         ap;
753         int             n;
754
755         if (buflen == 0)
756                 return (0);
757
758         va_start(ap, fmt);
759         n = _vsnprintf(buf, buflen, fmt, ap);
760         va_end(ap);
761
762         if (n < 0 || n > (int) buflen - 1) {
763                 n = buflen - 1;
764         }
765         buf[n] = '\0';
766
767         return (n);
768 }
769
770 #define snprintf Snprintf
771
772 /* from libc/include/_ansi.h */
773 #define _CONST const
774 #define _DEFUN(name, arglist, args)     name(args)
775 #define _AND            ,
776 /* from libc/time/local.h */
777 #define TZ_LOCK
778 #define TZ_UNLOCK
779 #define _tzname tzname
780 #define isleap(y) ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0)
781 #define YEAR_BASE       1900
782 #define SECSPERMIN      60L
783 #define MINSPERHOUR     60L
784 #define HOURSPERDAY     24L
785 #define SECSPERHOUR     (SECSPERMIN * MINSPERHOUR)
786
787 /*
788  * strftime.c
789  * Original Author:     G. Haley
790  * Additions from:      Eric Blake
791  *
792  * Places characters into the array pointed to by s as controlled by the string
793  * pointed to by format. If the total number of resulting characters including
794  * the terminating null character is not more than maxsize, returns the number
795  * of characters placed into the array pointed to by s (not including the
796  * terminating null character); otherwise zero is returned and the contents of
797  * the array indeterminate.
798  */
799
800 /*
801 FUNCTION
802 <<strftime>>---flexible calendar time formatter
803
804 INDEX
805         strftime
806
807 ANSI_SYNOPSIS
808         #include <time.h>
809         size_t strftime(char *<[s]>, size_t <[maxsize]>,
810                         const char *<[format]>, const struct tm *<[timp]>);
811
812 TRAD_SYNOPSIS
813         #include <time.h>
814         size_t strftime(<[s]>, <[maxsize]>, <[format]>, <[timp]>)
815         char *<[s]>;
816         size_t <[maxsize]>;
817         char *<[format]>;
818         struct tm *<[timp]>;
819
820 DESCRIPTION
821 <<strftime>> converts a <<struct tm>> representation of the time (at
822 <[timp]>) into a null-terminated string, starting at <[s]> and occupying
823 no more than <[maxsize]> characters.
824
825 You control the format of the output using the string at <[format]>.
826 <<*<[format]>>> can contain two kinds of specifications: text to be
827 copied literally into the formatted string, and time conversion
828 specifications.  Time conversion specifications are two- and
829 three-character sequences beginning with `<<%>>' (use `<<%%>>' to
830 include a percent sign in the output).  Each defined conversion
831 specification selects only the specified field(s) of calendar time
832 data from <<*<[timp]>>>, and converts it to a string in one of the
833 following ways:
834
835 o+
836 o %a
837 A three-letter abbreviation for the day of the week. [tm_wday]
838
839 o %A
840 The full name for the day of the week, one of `<<Sunday>>',
841 `<<Monday>>', `<<Tuesday>>', `<<Wednesday>>', `<<Thursday>>',
842 `<<Friday>>', or `<<Saturday>>'. [tm_wday]
843
844 o %b
845 A three-letter abbreviation for the month name. [tm_mon]
846
847 o %B
848 The full name of the month, one of `<<January>>', `<<February>>',
849 `<<March>>', `<<April>>', `<<May>>', `<<June>>', `<<July>>',
850 `<<August>>', `<<September>>', `<<October>>', `<<November>>',
851 `<<December>>'. [tm_mon]
852
853 o %c
854 A string representing the complete date and time, in the form
855 `<<"%a %b %e %H:%M:%S %Y">>' (example "Mon Apr 01 13:13:13
856 1992"). [tm_sec, tm_min, tm_hour, tm_mday, tm_mon, tm_year, tm_wday]
857
858 o %C
859 The century, that is, the year divided by 100 then truncated.  For
860 4-digit years, the result is zero-padded and exactly two characters;
861 but for other years, there may a negative sign or more digits.  In
862 this way, `<<%C%y>>' is equivalent to `<<%Y>>'. [tm_year]
863  
864 o %d
865 The day of the month, formatted with two digits (from `<<01>>' to
866 `<<31>>'). [tm_mday]
867
868 o %D
869 A string representing the date, in the form `<<"%m/%d/%y">>'.
870 [tm_mday, tm_mon, tm_year]
871
872 o %e
873 The day of the month, formatted with leading space if single digit
874 (from `<<1>>' to `<<31>>'). [tm_mday]
875
876 o %E<<x>>
877 In some locales, the E modifier selects alternative representations of
878 certain modifiers <<x>>.  But in the "C" locale supported by newlib,
879 it is ignored, and treated as %<<x>>.
880
881 o %F
882 A string representing the ISO 8601:2000 date format, in the form
883 `<<"%Y-%m-%d">>'. [tm_mday, tm_mon, tm_year]
884
885 o %g
886 The last two digits of the week-based year, see specifier %G (from
887 `<<00>>' to `<<99>>'). [tm_year, tm_wday, tm_yday]
888
889 o %G
890 The week-based year. In the ISO 8601:2000 calendar, week 1 of the year
891 includes January 4th, and begin on Mondays. Therefore, if January 1st,
892 2nd, or 3rd falls on a Sunday, that day and earlier belong to the last
893 week of the previous year; and if December 29th, 30th, or 31st falls
894 on Monday, that day and later belong to week 1 of the next year.  For
895 consistency with %Y, it always has at least four characters. 
896 Example: "%G" for Saturday 2nd January 1999 gives "1998", and for
897 Tuesday 30th December 1997 gives "1998". [tm_year, tm_wday, tm_yday]
898
899 o %h
900 A three-letter abbreviation for the month name (synonym for
901 "%b"). [tm_mon]
902
903 o %H
904 The hour (on a 24-hour clock), formatted with two digits (from
905 `<<00>>' to `<<23>>'). [tm_hour]
906
907 o %I
908 The hour (on a 12-hour clock), formatted with two digits (from
909 `<<01>>' to `<<12>>'). [tm_hour]
910
911 o %j
912 The count of days in the year, formatted with three digits
913 (from `<<001>>' to `<<366>>'). [tm_yday]
914
915 o %k
916 The hour (on a 24-hour clock), formatted with leading space if single
917 digit (from `<<0>>' to `<<23>>'). Non-POSIX extension. [tm_hour]
918
919 o %l
920 The hour (on a 12-hour clock), formatted with leading space if single
921 digit (from `<<1>>' to `<<12>>'). Non-POSIX extension. [tm_hour]
922
923 o %m
924 The month number, formatted with two digits (from `<<01>>' to `<<12>>').
925 [tm_mon]
926
927 o %M
928 The minute, formatted with two digits (from `<<00>>' to `<<59>>'). [tm_min]
929
930 o %n
931 A newline character (`<<\n>>').
932
933 o %O<<x>>
934 In some locales, the O modifier selects alternative digit characters
935 for certain modifiers <<x>>.  But in the "C" locale supported by newlib, it
936 is ignored, and treated as %<<x>>.
937
938 o %p
939 Either `<<AM>>' or `<<PM>>' as appropriate. [tm_hour]
940
941 o %r
942 The 12-hour time, to the second.  Equivalent to "%I:%M:%S %p". [tm_sec,
943 tm_min, tm_hour]
944
945 o %R
946 The 24-hour time, to the minute.  Equivalent to "%H:%M". [tm_min, tm_hour]
947
948 o %S
949 The second, formatted with two digits (from `<<00>>' to `<<60>>').  The
950 value 60 accounts for the occasional leap second. [tm_sec]
951
952 o %t
953 A tab character (`<<\t>>').
954
955 o %T
956 The 24-hour time, to the second.  Equivalent to "%H:%M:%S". [tm_sec,
957 tm_min, tm_hour]
958
959 o %u
960 The weekday as a number, 1-based from Monday (from `<<1>>' to
961 `<<7>>'). [tm_wday]
962
963 o %U
964 The week number, where weeks start on Sunday, week 1 contains the first
965 Sunday in a year, and earlier days are in week 0.  Formatted with two
966 digits (from `<<00>>' to `<<53>>').  See also <<%W>>. [tm_wday, tm_yday]
967
968 o %V
969 The week number, where weeks start on Monday, week 1 contains January 4th,
970 and earlier days are in the previous year.  Formatted with two digits
971 (from `<<01>>' to `<<53>>').  See also <<%G>>. [tm_year, tm_wday, tm_yday]
972
973 o %w
974 The weekday as a number, 0-based from Sunday (from `<<0>>' to `<<6>>').
975 [tm_wday]
976
977 o %W
978 The week number, where weeks start on Monday, week 1 contains the first
979 Monday in a year, and earlier days are in week 0.  Formatted with two
980 digits (from `<<00>>' to `<<53>>'). [tm_wday, tm_yday]
981
982 o %x
983 A string representing the complete date, equivalent to "%m/%d/%y".
984 [tm_mon, tm_mday, tm_year]
985
986 o %X
987 A string representing the full time of day (hours, minutes, and
988 seconds), equivalent to "%H:%M:%S". [tm_sec, tm_min, tm_hour]
989
990 o %y
991 The last two digits of the year (from `<<00>>' to `<<99>>'). [tm_year]
992
993 o %Y
994 The full year, equivalent to <<%C%y>>.  It will always have at least four
995 characters, but may have more.  The year is accurate even when tm_year
996 added to the offset of 1900 overflows an int. [tm_year]
997
998 o %z
999 The offset from UTC.  The format consists of a sign (negative is west of
1000 Greewich), two characters for hour, then two characters for minutes
1001 (-hhmm or +hhmm).  If tm_isdst is negative, the offset is unknown and no
1002 output is generated; if it is zero, the offset is the standard offset for
1003 the current time zone; and if it is positive, the offset is the daylight
1004 savings offset for the current timezone. The offset is determined from
1005 the TZ environment variable, as if by calling tzset(). [tm_isdst]
1006
1007 o %Z
1008 The time zone name.  If tm_isdst is negative, no output is generated.
1009 Otherwise, the time zone name is based on the TZ environment variable,
1010 as if by calling tzset(). [tm_isdst]
1011
1012 o %%
1013 A single character, `<<%>>'.
1014 o-
1015
1016 RETURNS
1017 When the formatted time takes up no more than <[maxsize]> characters,
1018 the result is the length of the formatted string.  Otherwise, if the
1019 formatting operation was abandoned due to lack of room, the result is
1020 <<0>>, and the string starting at <[s]> corresponds to just those
1021 parts of <<*<[format]>>> that could be completely filled in within the
1022 <[maxsize]> limit.
1023
1024 PORTABILITY
1025 ANSI C requires <<strftime>>, but does not specify the contents of
1026 <<*<[s]>>> when the formatted string would require more than
1027 <[maxsize]> characters.  Unrecognized specifiers and fields of
1028 <<timp>> that are out of range cause undefined results.  Since some
1029 formats expand to 0 bytes, it is wise to set <<*<[s]>>> to a nonzero
1030 value beforehand to distinguish between failure and an empty string.
1031 This implementation does not support <<s>> being NULL, nor overlapping
1032 <<s>> and <<format>>.
1033
1034 <<strftime>> requires no supporting OS subroutines.
1035 */
1036
1037 static _CONST int dname_len[7] =
1038 {6, 6, 7, 9, 8, 6, 8};
1039
1040 static _CONST char *_CONST dname[7] =
1041 {"Sunday", "Monday", "Tuesday", "Wednesday",
1042  "Thursday", "Friday", "Saturday"};
1043
1044 static _CONST int mname_len[12] =
1045 {7, 8, 5, 5, 3, 4, 4, 6, 9, 7, 8, 8};
1046
1047 static _CONST char *_CONST mname[12] =
1048 {"January", "February", "March", "April",
1049  "May", "June", "July", "August", "September", "October", "November",
1050  "December"};
1051
1052 /* Using the tm_year, tm_wday, and tm_yday components of TIM_P, return
1053    -1, 0, or 1 as the adjustment to add to the year for the ISO week
1054    numbering used in "%g%G%V", avoiding overflow.  */
1055 static int
1056 _DEFUN (iso_year_adjust, (tim_p),
1057         _CONST struct tm *tim_p)
1058 {
1059   /* Account for fact that tm_year==0 is year 1900.  */
1060   int leap = isleap (tim_p->tm_year + (YEAR_BASE
1061                                        - (tim_p->tm_year < 0 ? 0 : 2000)));
1062
1063   /* Pack the yday, wday, and leap year into a single int since there are so
1064      many disparate cases.  */
1065 #define PACK(yd, wd, lp) (((yd) << 4) + (wd << 1) + (lp))
1066   switch (PACK (tim_p->tm_yday, tim_p->tm_wday, leap))
1067     {
1068     case PACK (0, 5, 0): /* Jan 1 is Fri, not leap.  */
1069     case PACK (0, 6, 0): /* Jan 1 is Sat, not leap.  */
1070     case PACK (0, 0, 0): /* Jan 1 is Sun, not leap.  */
1071     case PACK (0, 5, 1): /* Jan 1 is Fri, leap year.  */
1072     case PACK (0, 6, 1): /* Jan 1 is Sat, leap year.  */
1073     case PACK (0, 0, 1): /* Jan 1 is Sun, leap year.  */
1074     case PACK (1, 6, 0): /* Jan 2 is Sat, not leap.  */
1075     case PACK (1, 0, 0): /* Jan 2 is Sun, not leap.  */
1076     case PACK (1, 6, 1): /* Jan 2 is Sat, leap year.  */
1077     case PACK (1, 0, 1): /* Jan 2 is Sun, leap year.  */
1078     case PACK (2, 0, 0): /* Jan 3 is Sun, not leap.  */
1079     case PACK (2, 0, 1): /* Jan 3 is Sun, leap year.  */
1080       return -1; /* Belongs to last week of previous year.  */
1081     case PACK (362, 1, 0): /* Dec 29 is Mon, not leap.  */
1082     case PACK (363, 1, 1): /* Dec 29 is Mon, leap year.  */
1083     case PACK (363, 1, 0): /* Dec 30 is Mon, not leap.  */
1084     case PACK (363, 2, 0): /* Dec 30 is Tue, not leap.  */
1085     case PACK (364, 1, 1): /* Dec 30 is Mon, leap year.  */
1086     case PACK (364, 2, 1): /* Dec 30 is Tue, leap year.  */
1087     case PACK (364, 1, 0): /* Dec 31 is Mon, not leap.  */
1088     case PACK (364, 2, 0): /* Dec 31 is Tue, not leap.  */
1089     case PACK (364, 3, 0): /* Dec 31 is Wed, not leap.  */
1090     case PACK (365, 1, 1): /* Dec 31 is Mon, leap year.  */
1091     case PACK (365, 2, 1): /* Dec 31 is Tue, leap year.  */
1092     case PACK (365, 3, 1): /* Dec 31 is Wed, leap year.  */
1093       return 1; /* Belongs to first week of next year.  */
1094     }
1095   return 0; /* Belongs to specified year.  */
1096 #undef PACK
1097 }
1098
1099 size_t
1100 _DEFUN (strftime, (s, maxsize, format, tim_p),
1101         char *s _AND
1102         size_t maxsize _AND
1103         _CONST char *format _AND
1104         _CONST struct tm *tim_p)
1105 {
1106   size_t count = 0;
1107   int i;
1108
1109   for (;;)
1110     {
1111       while (*format && *format != '%')
1112         {
1113           if (count < maxsize - 1)
1114             s[count++] = *format++;
1115           else
1116             return 0;
1117         }
1118
1119       if (*format == '\0')
1120         break;
1121
1122       format++;
1123       if (*format == 'E' || *format == 'O')
1124         format++;
1125
1126       switch (*format)
1127         {
1128         case 'a':
1129           for (i = 0; i < 3; i++)
1130             {
1131               if (count < maxsize - 1)
1132                 s[count++] =
1133                   dname[tim_p->tm_wday][i];
1134               else
1135                 return 0;
1136             }
1137           break;
1138         case 'A':
1139           for (i = 0; i < dname_len[tim_p->tm_wday]; i++)
1140             {
1141               if (count < maxsize - 1)
1142                 s[count++] =
1143                   dname[tim_p->tm_wday][i];
1144               else
1145                 return 0;
1146             }
1147           break;
1148         case 'b':
1149         case 'h':
1150           for (i = 0; i < 3; i++)
1151             {
1152               if (count < maxsize - 1)
1153                 s[count++] =
1154                   mname[tim_p->tm_mon][i];
1155               else
1156                 return 0;
1157             }
1158           break;
1159         case 'B':
1160           for (i = 0; i < mname_len[tim_p->tm_mon]; i++)
1161             {
1162               if (count < maxsize - 1)
1163                 s[count++] =
1164                   mname[tim_p->tm_mon][i];
1165               else
1166                 return 0;
1167             }
1168           break;
1169         case 'c':
1170           {
1171             /* Length is not known because of %C%y, so recurse. */
1172             size_t adjust = strftime (&s[count], maxsize - count,
1173                                       "%a %b %e %H:%M:%S %C%y", tim_p);
1174             if (adjust > 0)
1175               count += adjust;
1176             else
1177               return 0;
1178           }
1179           break;
1180         case 'C':
1181           {
1182             /* Examples of (tm_year + YEAR_BASE) that show how %Y == %C%y
1183                with 32-bit int.
1184                %Y               %C              %y
1185                2147485547       21474855        47
1186                10000            100             00
1187                9999             99              99
1188                0999             09              99
1189                0099             00              99
1190                0001             00              01
1191                0000             00              00
1192                -001             -0              01
1193                -099             -0              99
1194                -999             -9              99
1195                -1000            -10             00
1196                -10000           -100            00
1197                -2147481748      -21474817       48
1198
1199                Be careful of both overflow and sign adjustment due to the
1200                asymmetric range of years.
1201             */
1202             int neg = tim_p->tm_year < -YEAR_BASE;
1203             int century = tim_p->tm_year >= 0
1204               ? tim_p->tm_year / 100 + YEAR_BASE / 100
1205               : abs (tim_p->tm_year + YEAR_BASE) / 100;
1206             count += snprintf (&s[count], maxsize - count, "%s%.*d",
1207                                neg ? "-" : "", 2 - neg, century);
1208             if (count >= maxsize)
1209               return 0;
1210           }
1211           break;
1212         case 'd':
1213         case 'e':
1214           if (count < maxsize - 2)
1215             {
1216               sprintf (&s[count], *format == 'd' ? "%.2d" : "%2d",
1217                        tim_p->tm_mday);
1218               count += 2;
1219             }
1220           else
1221             return 0;
1222           break;
1223         case 'D':
1224         case 'x':
1225           /* %m/%d/%y */
1226           if (count < maxsize - 8)
1227             {
1228               sprintf (&s[count], "%.2d/%.2d/%.2d",
1229                        tim_p->tm_mon + 1, tim_p->tm_mday,
1230                        tim_p->tm_year >= 0 ? tim_p->tm_year % 100
1231                        : abs (tim_p->tm_year + YEAR_BASE) % 100);
1232               count += 8;
1233             }
1234           else
1235             return 0;
1236           break;
1237         case 'F':
1238           {
1239             /* Length is not known because of %C%y, so recurse. */
1240             size_t adjust = strftime (&s[count], maxsize - count,
1241                                       "%C%y-%m-%d", tim_p);
1242             if (adjust > 0)
1243               count += adjust;
1244             else
1245               return 0;
1246           }
1247           break;
1248         case 'g':
1249           if (count < maxsize - 2)
1250             {
1251               /* Be careful of both overflow and negative years, thanks to
1252                  the asymmetric range of years.  */
1253               int adjust = iso_year_adjust (tim_p);
1254               int year = tim_p->tm_year >= 0 ? tim_p->tm_year % 100
1255                 : abs (tim_p->tm_year + YEAR_BASE) % 100;
1256               if (adjust < 0 && tim_p->tm_year <= -YEAR_BASE)
1257                 adjust = 1;
1258               else if (adjust > 0 && tim_p->tm_year < -YEAR_BASE)
1259                 adjust = -1;
1260               sprintf (&s[count], "%.2d",
1261                        ((year + adjust) % 100 + 100) % 100);
1262               count += 2;
1263             }
1264           else
1265             return 0;
1266           break;
1267         case 'G':
1268           {
1269             /* See the comments for 'C' and 'Y'; this is a variable length
1270                field.  Although there is no requirement for a minimum number
1271                of digits, we use 4 for consistency with 'Y'.  */
1272             int neg = tim_p->tm_year < -YEAR_BASE;
1273             int adjust = iso_year_adjust (tim_p);
1274             int century = tim_p->tm_year >= 0
1275               ? tim_p->tm_year / 100 + YEAR_BASE / 100
1276               : abs (tim_p->tm_year + YEAR_BASE) / 100;
1277             int year = tim_p->tm_year >= 0 ? tim_p->tm_year % 100
1278               : abs (tim_p->tm_year + YEAR_BASE) % 100;
1279             if (adjust < 0 && tim_p->tm_year <= -YEAR_BASE)
1280               neg = adjust = 1;
1281             else if (adjust > 0 && neg)
1282               adjust = -1;
1283             year += adjust;
1284             if (year == -1)
1285               {
1286                 year = 99;
1287                 --century;
1288               }
1289             else if (year == 100)
1290               {
1291                 year = 0;
1292                 ++century;
1293               }
1294             count += snprintf (&s[count], maxsize - count, "%s%.*d%.2d",
1295                                neg ? "-" : "", 2 - neg, century, year);
1296             if (count >= maxsize)
1297               return 0;
1298           }
1299           break;
1300         case 'H':
1301         case 'k':
1302           if (count < maxsize - 2)
1303             {
1304               sprintf (&s[count], *format == 'k' ? "%2d" : "%.2d",
1305                        tim_p->tm_hour);
1306               count += 2;
1307             }
1308           else
1309             return 0;
1310           break;
1311         case 'I':
1312         case 'l':
1313           if (count < maxsize - 2)
1314             {
1315               if (tim_p->tm_hour == 0 ||
1316                   tim_p->tm_hour == 12)
1317                 {
1318                   s[count++] = '1';
1319                   s[count++] = '2';
1320                 }
1321               else
1322                 {
1323                   sprintf (&s[count], *format == 'I' ? "%.2d" : "%2d",
1324                            tim_p->tm_hour % 12);
1325                   count += 2;
1326                 }
1327             }
1328           else
1329             return 0;
1330           break;
1331         case 'j':
1332           if (count < maxsize - 3)
1333             {
1334               sprintf (&s[count], "%.3d",
1335                        tim_p->tm_yday + 1);
1336               count += 3;
1337             }
1338           else
1339             return 0;
1340           break;
1341         case 'm':
1342           if (count < maxsize - 2)
1343             {
1344               sprintf (&s[count], "%.2d",
1345                        tim_p->tm_mon + 1);
1346               count += 2;
1347             }
1348           else
1349             return 0;
1350           break;
1351         case 'M':
1352           if (count < maxsize - 2)
1353             {
1354               sprintf (&s[count], "%.2d",
1355                        tim_p->tm_min);
1356               count += 2;
1357             }
1358           else
1359             return 0;
1360           break;
1361         case 'n':
1362           if (count < maxsize - 1)
1363             s[count++] = '\n';
1364           else
1365             return 0;
1366           break;
1367         case 'p':
1368           if (count < maxsize - 2)
1369             {
1370               if (tim_p->tm_hour < 12)
1371                 s[count++] = 'A';
1372               else
1373                 s[count++] = 'P';
1374
1375               s[count++] = 'M';
1376             }
1377           else
1378             return 0;
1379           break;
1380         case 'r':
1381           if (count < maxsize - 11)
1382             {
1383               if (tim_p->tm_hour == 0 ||
1384                   tim_p->tm_hour == 12)
1385                 {
1386                   s[count++] = '1';
1387                   s[count++] = '2';
1388                 }
1389               else
1390                 {
1391                   sprintf (&s[count], "%.2d", tim_p->tm_hour % 12);
1392                   count += 2;
1393                 }
1394               s[count++] = ':';
1395               sprintf (&s[count], "%.2d",
1396                        tim_p->tm_min);
1397               count += 2;
1398               s[count++] = ':';
1399               sprintf (&s[count], "%.2d",
1400                        tim_p->tm_sec);
1401               count += 2;
1402               s[count++] = ' ';
1403               if (tim_p->tm_hour < 12)
1404                 s[count++] = 'A';
1405               else
1406                 s[count++] = 'P';
1407
1408               s[count++] = 'M';
1409             }
1410           else
1411             return 0;
1412           break;
1413         case 'R':
1414           if (count < maxsize - 5)
1415             {
1416               sprintf (&s[count], "%.2d:%.2d", tim_p->tm_hour, tim_p->tm_min);
1417               count += 5;
1418             }
1419           else
1420             return 0;
1421           break;
1422         case 'S':
1423           if (count < maxsize - 2)
1424             {
1425               sprintf (&s[count], "%.2d",
1426                        tim_p->tm_sec);
1427               count += 2;
1428             }
1429           else
1430             return 0;
1431           break;
1432         case 't':
1433           if (count < maxsize - 1)
1434             s[count++] = '\t';
1435           else
1436             return 0;
1437           break;
1438         case 'T':
1439         case 'X':
1440           if (count < maxsize - 8)
1441             {
1442               sprintf (&s[count], "%.2d:%.2d:%.2d", tim_p->tm_hour,
1443                        tim_p->tm_min, tim_p->tm_sec);
1444               count += 8;
1445             }
1446           else
1447             return 0;
1448           break;
1449         case 'u':
1450           if (count < maxsize - 1)
1451             {
1452               if (tim_p->tm_wday == 0)
1453                 s[count++] = '7';
1454               else
1455                 s[count++] = '0' + tim_p->tm_wday;
1456             }
1457           else
1458             return 0;
1459           break;
1460         case 'U':
1461           if (count < maxsize - 2)
1462             {
1463               sprintf (&s[count], "%.2d",
1464                        (tim_p->tm_yday + 7 -
1465                         tim_p->tm_wday) / 7);
1466               count += 2;
1467             }
1468           else
1469             return 0;
1470           break;
1471         case 'V':
1472           if (count < maxsize - 2)
1473             {
1474               int adjust = iso_year_adjust (tim_p);
1475               int wday = (tim_p->tm_wday) ? tim_p->tm_wday - 1 : 6;
1476               int week = (tim_p->tm_yday + 10 - wday) / 7;
1477               if (adjust > 0)
1478                 week = 1;
1479               else if (adjust < 0)
1480                 /* Previous year has 53 weeks if current year starts on
1481                    Fri, and also if current year starts on Sat and
1482                    previous year was leap year.  */
1483                 week = 52 + (4 >= (wday - tim_p->tm_yday
1484                                    - isleap (tim_p->tm_year
1485                                              + (YEAR_BASE - 1
1486                                                 - (tim_p->tm_year < 0
1487                                                    ? 0 : 2000)))));
1488               sprintf (&s[count], "%.2d", week);
1489               count += 2;
1490             }
1491           else
1492             return 0;
1493           break;
1494         case 'w':
1495           if (count < maxsize - 1)
1496             s[count++] = '0' + tim_p->tm_wday;
1497           else
1498             return 0;
1499           break;
1500         case 'W':
1501           if (count < maxsize - 2)
1502             {
1503               int wday = (tim_p->tm_wday) ? tim_p->tm_wday - 1 : 6;
1504               sprintf (&s[count], "%.2d",
1505                        (tim_p->tm_yday + 7 - wday) / 7);
1506               count += 2;
1507             }
1508           else
1509             return 0;
1510           break;
1511         case 'y':
1512           if (count < maxsize - 2)
1513             {
1514               /* Be careful of both overflow and negative years, thanks to
1515                  the asymmetric range of years.  */
1516               int year = tim_p->tm_year >= 0 ? tim_p->tm_year % 100
1517                 : abs (tim_p->tm_year + YEAR_BASE) % 100;
1518               sprintf (&s[count], "%.2d", year);
1519               count += 2;
1520             }
1521           else
1522             return 0;
1523           break;
1524         case 'Y':
1525           {
1526             /* Length is not known because of %C%y, so recurse. */
1527             size_t adjust = strftime (&s[count], maxsize - count,
1528                                       "%C%y", tim_p);
1529             if (adjust > 0)
1530               count += adjust;
1531             else
1532               return 0;
1533           }
1534           break;
1535         case 'z':
1536 #ifndef _WIN32_WCE
1537           if (tim_p->tm_isdst >= 0)
1538             {
1539               if (count < maxsize - 5)
1540                 {
1541                   long offset;
1542                   __tzinfo_type *tz = __gettzinfo ();
1543                   TZ_LOCK;
1544                   /* The sign of this is exactly opposite the envvar TZ.  We
1545                      could directly use the global _timezone for tm_isdst==0,
1546                      but have to use __tzrule for daylight savings.  */
1547                   offset = -tz->__tzrule[tim_p->tm_isdst > 0].offset;
1548                   TZ_UNLOCK;
1549                   sprintf (&s[count], "%+03ld%.2ld", offset / SECSPERHOUR,
1550                            labs (offset / SECSPERMIN) % 60L);
1551                   count += 5;
1552                 }
1553               else
1554                 return 0;
1555             }
1556           break;
1557 #endif
1558         case 'Z':
1559           if (tim_p->tm_isdst >= 0)
1560             {
1561               int size;
1562               TZ_LOCK;
1563               size = strlen(_tzname[tim_p->tm_isdst > 0]);
1564               for (i = 0; i < size; i++)
1565                 {
1566                   if (count < maxsize - 1)
1567                     s[count++] = _tzname[tim_p->tm_isdst > 0][i];
1568                   else
1569                     {
1570                       TZ_UNLOCK;
1571                       return 0;
1572                     }
1573                 }
1574               TZ_UNLOCK;
1575             }
1576           break;
1577         case '%':
1578           if (count < maxsize - 1)
1579             s[count++] = '%';
1580           else
1581             return 0;
1582           break;
1583         }
1584       if (*format)
1585         format++;
1586       else
1587         break;
1588     }
1589   if (maxsize)
1590     s[count] = '\0';
1591
1592   return count;
1593 }