/* vi:ts=8:sw=8:noet * Copyright (c) 2006 Luke Dunstan * Partly based on code by David Kashtan, Validus Medical Systems * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ /* * This file provides various functions that are available on desktop Windows * but not on Windows CE */ #ifdef _MSC_VER /* Level 4 warnings caused by windows.h */ #pragma warning(disable : 4214) // nonstandard extension used : bit field types other than int #pragma warning(disable : 4115) // named type definition in parentheses #pragma warning(disable : 4201) // nonstandard extension used : nameless struct/union #pragma warning(disable : 4514) // unreferenced inline function has been removed #pragma warning(disable : 4244) // conversion from 'int ' to 'unsigned short ', possible loss of data #pragma warning(disable : 4100) // unreferenced formal parameter #endif #include #include #include "compat_wince.h" static WCHAR *to_wide_string(LPCSTR pStr) { int nwide; WCHAR *buf; if(pStr == NULL) return NULL; nwide = MultiByteToWideChar(CP_ACP, 0, pStr, -1, NULL, 0); if(nwide == 0) return NULL; buf = malloc(nwide * sizeof(WCHAR)); if(buf == NULL) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); return NULL; } if(MultiByteToWideChar(CP_ACP, 0, pStr, -1, buf, nwide) == 0) { free(buf); return NULL; } return buf; } FILE *fdopen(int handle, const char *mode) { WCHAR *wmode = to_wide_string(mode); FILE *result; if(wmode != NULL) result = _wfdopen((void *)handle, wmode); else result = NULL; free(wmode); return result; } /* * Time conversion constants */ #define FT_EPOCH (116444736000000000i64) #define FT_TICKS (10000000i64) /* * Convert a FILETIME to a time_t */ static time_t convert_FILETIME_to_time_t(FILETIME *File_Time) { __int64 Temp; /* * Convert the FILETIME structure to 100nSecs since 1601 (as a 64-bit value) */ Temp = (((__int64)File_Time->dwHighDateTime) << 32) + (__int64)File_Time->dwLowDateTime; /* * Convert to seconds from 1970 */ return((time_t)((Temp - FT_EPOCH) / FT_TICKS)); } /* * Convert a FILETIME to a tm structure */ static struct tm *Convert_FILETIME_To_tm(FILETIME *File_Time) { SYSTEMTIME System_Time; static struct tm tm = {0}; static const short Day_Of_Year_By_Month[12] = {(short)(0), (short)(31), (short)(31 + 28), (short)(31 + 28 + 31), (short)(31 + 28 + 31 + 30), (short)(31 + 28 + 31 + 30 + 31), (short)(31 + 28 + 31 + 30 + 31 + 30), (short)(31 + 28 + 31 + 30 + 31 + 30 + 31), (short)(31 + 28 + 31 + 30 + 31 + 30 + 31 + 31), (short)(31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30), (short)(31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31), (short)(31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30)}; /* * Turn the FILETIME into a SYSTEMTIME */ FileTimeToSystemTime(File_Time, &System_Time); /* * Use SYSTEMTIME to fill in the tm structure */ tm.tm_sec = System_Time.wSecond; tm.tm_min = System_Time.wMinute; tm.tm_hour = System_Time.wHour; tm.tm_mday = System_Time.wDay; tm.tm_mon = System_Time.wMonth - 1; tm.tm_year = System_Time.wYear - 1900; tm.tm_wday = System_Time.wDayOfWeek; tm.tm_yday = Day_Of_Year_By_Month[tm.tm_mon] + tm.tm_mday - 1; if (tm.tm_mon >= 2) { /* * Check for leap year (every 4 years but not every 100 years but every 400 years) */ if ((System_Time.wYear % 4) == 0) { /* * It Is a 4th year */ if ((System_Time.wYear % 100) == 0) { /* * It is a 100th year */ if ((System_Time.wYear % 400) == 0) { /* * It is a 400th year: It is a leap year */ tm.tm_yday++; } } else { /* * It is not a 100th year: It is a leap year */ tm.tm_yday++; } } } return(&tm); } /* * Convert a time_t to a FILETIME */ static void Convert_time_t_To_FILETIME(time_t Time, FILETIME *File_Time) { __int64 Temp; /* * Use 64-bit calculation to convert seconds since 1970 to * 100nSecs since 1601 */ Temp = ((__int64)Time * FT_TICKS) + FT_EPOCH; /* * Put it into the FILETIME structure */ File_Time->dwLowDateTime = (DWORD)Temp; File_Time->dwHighDateTime = (DWORD)(Temp >> 32); } /* * Convert a tm structure to a FILETIME */ static FILETIME *Convert_tm_To_FILETIME(struct tm *tm) { SYSTEMTIME System_Time; static FILETIME File_Time = {0}; /* * Use the tm structure to fill in a SYSTEM */ System_Time.wYear = tm->tm_year + 1900; System_Time.wMonth = tm->tm_mon + 1; System_Time.wDayOfWeek = tm->tm_wday; System_Time.wDay = tm->tm_mday; System_Time.wHour = tm->tm_hour; System_Time.wMinute = tm->tm_min; System_Time.wSecond = tm->tm_sec; System_Time.wMilliseconds = 0; /* * Convert it to a FILETIME and return it */ SystemTimeToFileTime(&System_Time, &File_Time); return(&File_Time); } /************************************************************************/ /* */ /* Errno emulation: There is no errno on Windows/CE and we need */ /* to make it per-thread. So we have a function */ /* that returns a pointer to the errno for the */ /* current thread. */ /* */ /* If there is ONLY the main thread then we can */ /* quickly return some static storage. */ /* */ /* If we have multiple threads running, we use */ /* Thread-Local Storage to hold the pointer */ /* */ /************************************************************************/ /* * Function pointer for returning errno pointer */ static int *Initialize_Errno(void); int *(*__WinCE_Errno_Pointer_Function)(void) = Initialize_Errno; /* * Static errno storage for the main thread */ static int Errno_Storage = 0; /* * Thread-Local storage slot for errno */ static int TLS_Errno_Slot = 0xffffffff; /* * Number of threads we have running and critical section protection * for manipulating it */ static int Number_Of_Threads = 0; static CRITICAL_SECTION Number_Of_Threads_Critical_Section; /* * For the main thread only -- return the errno pointer */ static int *Get_Main_Thread_Errno(void) { return &Errno_Storage; } /* * When there is more than one thread -- return the errno pointer */ static int *Get_Thread_Errno(void) { return (int *)TlsGetValue(TLS_Errno_Slot); } /* * Initialize a thread's errno */ static void Initialize_Thread_Errno(int *Errno_Pointer) { /* * Make sure we have a slot */ if (TLS_Errno_Slot == 0xffffffff) { /* * No: Get one */ TLS_Errno_Slot = (int)TlsAlloc(); if (TLS_Errno_Slot == 0xffffffff) ExitProcess(3); } /* * We can safely check for 0 threads, because * only the main thread will be initializing * at this point. Make sure the critical * section that protects the number of threads * is initialized */ if (Number_Of_Threads == 0) InitializeCriticalSection(&Number_Of_Threads_Critical_Section); /* * Store the errno pointer */ if (TlsSetValue(TLS_Errno_Slot, (LPVOID)Errno_Pointer) == 0) ExitProcess(3); /* * Bump the number of threads */ EnterCriticalSection(&Number_Of_Threads_Critical_Section); Number_Of_Threads++; if (Number_Of_Threads > 1) { /* * We have threads other than the main thread: * Use thread-local storage */ __WinCE_Errno_Pointer_Function = Get_Thread_Errno; } LeaveCriticalSection(&Number_Of_Threads_Critical_Section); } /* * Initialize errno emulation on Windows/CE (Main thread) */ static int *Initialize_Errno(void) { /* * Initialize the main thread's errno in thread-local storage */ Initialize_Thread_Errno(&Errno_Storage); /* * Set the errno function to be the one that returns the * main thread's errno */ __WinCE_Errno_Pointer_Function = Get_Main_Thread_Errno; /* * Return the main thread's errno */ return &Errno_Storage; } /* * Initialize errno emulation on Windows/CE (New thread) */ void __WinCE_Errno_New_Thread(int *Errno_Pointer) { Initialize_Thread_Errno(Errno_Pointer); } /* * Note that a thread has exited */ void __WinCE_Errno_Thread_Exit(void) { /* * Decrease the number of threads */ EnterCriticalSection(&Number_Of_Threads_Critical_Section); Number_Of_Threads--; if (Number_Of_Threads <= 1) { /* * We only have the main thread */ __WinCE_Errno_Pointer_Function = Get_Main_Thread_Errno; } LeaveCriticalSection(&Number_Of_Threads_Critical_Section); } char * strerror(int errnum) { return "(strerror not implemented)"; } #define FT_EPOCH (116444736000000000i64) #define FT_TICKS (10000000i64) int _wstat(const WCHAR *path, struct _stat *buffer) { WIN32_FIND_DATA data; HANDLE handle; WCHAR *p; /* Fail if wildcard characters are specified */ if (wcscspn(path, L"?*") != wcslen(path)) return -1; handle = FindFirstFile(path, &data); if (handle == INVALID_HANDLE_VALUE) { errno = GetLastError(); return -1; } FindClose(handle); /* Found: Convert the file times */ buffer->st_mtime = convert_FILETIME_to_time_t(&data.ftLastWriteTime); if (data.ftLastAccessTime.dwLowDateTime || data.ftLastAccessTime.dwHighDateTime) buffer->st_atime = convert_FILETIME_to_time_t(&data.ftLastAccessTime); else buffer->st_atime = buffer->st_mtime; if (data.ftCreationTime.dwLowDateTime || data.ftCreationTime.dwHighDateTime) buffer->st_ctime = convert_FILETIME_to_time_t(&data.ftCreationTime); else buffer->st_ctime = buffer->st_mtime; /* Convert the file modes */ buffer->st_mode = (unsigned short)((data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? (S_IFDIR | S_IEXEC) : S_IFREG); buffer->st_mode |= (data.dwFileAttributes & FILE_ATTRIBUTE_READONLY) ? S_IREAD : (S_IREAD | S_IWRITE); if((p = wcsrchr(path, L'.')) != NULL) { p++; if (_wcsicmp(p, L".exe") == 0) buffer->st_mode |= S_IEXEC; } buffer->st_mode |= (buffer->st_mode & 0700) >> 3; buffer->st_mode |= (buffer->st_mode & 0700) >> 6; /* Set the other information */ buffer->st_nlink = 1; buffer->st_size = (unsigned long int)data.nFileSizeLow; buffer->st_uid = 0; buffer->st_gid = 0; buffer->st_ino = 0 /*data.dwOID ?*/; buffer->st_dev = 0; return 0; /* success */ } /* * Helper function for cemodule -- do an fstat() operation on a Win32 File Handle */ int _fstat(int handle, struct _stat *st) { BY_HANDLE_FILE_INFORMATION Data; /* * Get the file information */ if (!GetFileInformationByHandle((HANDLE)handle, &Data)) { /* * Return error */ errno = GetLastError(); return(-1); } /* * Found: Convert the file times */ st->st_mtime=(time_t)((*(__int64*)&Data.ftLastWriteTime-FT_EPOCH)/FT_TICKS); if(Data.ftLastAccessTime.dwLowDateTime || Data.ftLastAccessTime.dwHighDateTime) st->st_atime=(time_t)((*(__int64*)&Data.ftLastAccessTime-FT_EPOCH)/FT_TICKS); else st->st_atime=st->st_mtime ; if(Data.ftCreationTime.dwLowDateTime || Data.ftCreationTime.dwHighDateTime ) st->st_ctime=(time_t)((*(__int64*)&Data.ftCreationTime-FT_EPOCH)/FT_TICKS); else st->st_ctime=st->st_mtime ; /* * Convert the file modes */ st->st_mode = (unsigned short)((Data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? (S_IFDIR | S_IEXEC) : S_IFREG); st->st_mode |= (Data.dwFileAttributes & FILE_ATTRIBUTE_READONLY) ? S_IREAD : (S_IREAD | S_IWRITE); st->st_mode |= (st->st_mode & 0700) >> 3; st->st_mode |= (st->st_mode & 0700) >> 6; /* * Set the other information */ st->st_nlink=1; st->st_size=(unsigned long int)Data.nFileSizeLow; st->st_uid=0; st->st_gid=0; st->st_ino=0; st->st_dev=0; /* * Return success */ return(0); } int _wopen(const wchar_t *filename, int oflag, ...) { DWORD Access, ShareMode, CreationDisposition; HANDLE Handle; static int Modes[4] = {0, (GENERIC_READ | GENERIC_WRITE), GENERIC_READ, GENERIC_WRITE}; /* * Calculate the CreateFile arguments */ Access = Modes[oflag & O_MODE_MASK]; ShareMode = (oflag & O_EXCL) ? 0 : (FILE_SHARE_READ | FILE_SHARE_WRITE); if (oflag & O_TRUNC) CreationDisposition = (oflag & O_CREAT) ? CREATE_ALWAYS : TRUNCATE_EXISTING; else CreationDisposition = (oflag & O_CREAT) ? CREATE_NEW : OPEN_EXISTING; Handle = CreateFileW(filename, Access, ShareMode, NULL, CreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL); /* * Deal with errors */ if (Handle == INVALID_HANDLE_VALUE) { errno = GetLastError(); if ((errno == ERROR_ALREADY_EXISTS) || (errno == ERROR_FILE_EXISTS)) errno = ERROR_ALREADY_EXISTS; return -1; } /* * Return the handle */ return (int)Handle; } int _close(int handle) { if(CloseHandle((HANDLE)handle)) return 0; errno = GetLastError(); return -1; } int _write(int handle, const void *buffer, unsigned int count) { DWORD numwritten = 0; if(!WriteFile((HANDLE)handle, buffer, count, &numwritten, NULL)) { errno = GetLastError(); return -1; } return numwritten; } int _read(int handle, void *buffer, unsigned int count) { DWORD numread = 0; if(!ReadFile((HANDLE)handle, buffer, count, &numread, NULL)) { errno = GetLastError(); return -1; } return numread; } long _lseek(int handle, long offset, int origin) { DWORD dwMoveMethod; DWORD result; switch(origin) { default: errno = EINVAL; return -1L; case SEEK_SET: dwMoveMethod = FILE_BEGIN; break; case SEEK_CUR: dwMoveMethod = FILE_CURRENT; break; case SEEK_END: dwMoveMethod = FILE_END; break; } result = SetFilePointer((HANDLE)handle, offset, NULL, dwMoveMethod); if(result == 0xFFFFFFFF) { errno = GetLastError(); return -1; } return (long)result; } int _wmkdir(const wchar_t *dirname) { if(!CreateDirectoryW(dirname, NULL)) { errno = GetLastError(); return -1; } return 0; } int _wremove(const wchar_t *filename) { if(!DeleteFileW(filename)) { errno = GetLastError(); return -1; } return 0; } int _wrename(const wchar_t *oldname, const wchar_t *newname) { if(!MoveFileW(oldname, newname)) { errno = GetLastError(); return -1; } return 0; } wchar_t * _wgetcwd(wchar_t *buffer, int maxlen) { wchar_t *result; WCHAR wszPath[MAX_PATH + 1]; WCHAR *p; if(!GetModuleFileNameW(NULL, wszPath, MAX_PATH + 1)) { errno = GetLastError(); return NULL; } /* Remove the filename part of the path to leave the directory */ p = wcsrchr(wszPath, L'\\'); if(p) *p = L'\0'; if(buffer == NULL) result = _wcsdup(wszPath); else if(wcslen(wszPath) + 1 > (size_t)maxlen) { result = NULL; errno = ERROR_INSUFFICIENT_BUFFER; } else { wcsncpy(buffer, wszPath, maxlen); buffer[maxlen - 1] = L'\0'; result = buffer; } return result; } /* * The missing "C" runtime gmtime() function */ struct tm *gmtime(const time_t *TimeP) { FILETIME File_Time; /* * Deal with null time pointer */ if (!TimeP) return(NULL); /* * time_t -> FILETIME -> tm */ Convert_time_t_To_FILETIME(*TimeP, &File_Time); return(Convert_FILETIME_To_tm(&File_Time)); } /* * The missing "C" runtime localtime() function */ struct tm *localtime(const time_t *TimeP) { FILETIME File_Time, Local_File_Time; /* * Deal with null time pointer */ if (!TimeP) return(NULL); /* * time_t -> FILETIME -> Local FILETIME -> tm */ Convert_time_t_To_FILETIME(*TimeP, &File_Time); FileTimeToLocalFileTime(&File_Time, &Local_File_Time); return(Convert_FILETIME_To_tm(&Local_File_Time)); } /* * The missing "C" runtime mktime() function */ time_t mktime(struct tm *tm) { FILETIME *Local_File_Time; FILETIME File_Time; /* * tm -> Local FILETIME -> FILETIME -> time_t */ Local_File_Time = Convert_tm_To_FILETIME(tm); LocalFileTimeToFileTime(Local_File_Time, &File_Time); return(convert_FILETIME_to_time_t(&File_Time)); } /* * Missing "C" runtime time() function */ time_t time(time_t *TimeP) { SYSTEMTIME System_Time; FILETIME File_Time; time_t Result; /* * Get the current system time */ GetSystemTime(&System_Time); /* * SYSTEMTIME -> FILETIME -> time_t */ SystemTimeToFileTime(&System_Time, &File_Time); Result = convert_FILETIME_to_time_t(&File_Time); /* * Return the time_t */ if (TimeP) *TimeP = Result; return(Result); } static char Standard_Name[32] = "GMT"; static char Daylight_Name[32] = "GMT"; char *tzname[2] = {Standard_Name, Daylight_Name}; long timezone = 0; int daylight = 0; void tzset(void) { TIME_ZONE_INFORMATION Info; int Result; /* * Get our current timezone information */ Result = GetTimeZoneInformation(&Info); switch(Result) { /* * We are on standard time */ case TIME_ZONE_ID_STANDARD: daylight = 0; break; /* * We are on daylight savings time */ case TIME_ZONE_ID_DAYLIGHT: daylight = 1; break; /* * We don't know the timezone information (leave it GMT) */ default: return; } /* * Extract the timezone information */ timezone = Info.Bias * 60; if (Info.StandardName[0]) WideCharToMultiByte(CP_ACP, 0, Info.StandardName, -1, Standard_Name, sizeof(Standard_Name) - 1, NULL, NULL); if (Info.DaylightName[0]) WideCharToMultiByte(CP_ACP, 0, Info.DaylightName, -1, Daylight_Name, sizeof(Daylight_Name) - 1, NULL, NULL); } /*** strftime() from newlib libc/time/strftime.c ***/ /* * Sane snprintf(). Acts like snprintf(), but never return -1 or the * value bigger than supplied buffer. */ static int Snprintf(char *buf, size_t buflen, const char *fmt, ...) { va_list ap; int n; if (buflen == 0) return (0); va_start(ap, fmt); n = _vsnprintf(buf, buflen, fmt, ap); va_end(ap); if (n < 0 || n > (int) buflen - 1) { n = buflen - 1; } buf[n] = '\0'; return (n); } #define snprintf Snprintf /* from libc/include/_ansi.h */ #define _CONST const #define _DEFUN(name, arglist, args) name(args) #define _AND , /* from libc/time/local.h */ #define TZ_LOCK #define TZ_UNLOCK #define _tzname tzname #define isleap(y) ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0) #define YEAR_BASE 1900 #define SECSPERMIN 60L #define MINSPERHOUR 60L #define HOURSPERDAY 24L #define SECSPERHOUR (SECSPERMIN * MINSPERHOUR) /* * strftime.c * Original Author: G. Haley * Additions from: Eric Blake * * Places characters into the array pointed to by s as controlled by the string * pointed to by format. If the total number of resulting characters including * the terminating null character is not more than maxsize, returns the number * of characters placed into the array pointed to by s (not including the * terminating null character); otherwise zero is returned and the contents of * the array indeterminate. */ /* FUNCTION <>---flexible calendar time formatter INDEX strftime ANSI_SYNOPSIS #include size_t strftime(char *<[s]>, size_t <[maxsize]>, const char *<[format]>, const struct tm *<[timp]>); TRAD_SYNOPSIS #include size_t strftime(<[s]>, <[maxsize]>, <[format]>, <[timp]>) char *<[s]>; size_t <[maxsize]>; char *<[format]>; struct tm *<[timp]>; DESCRIPTION <> converts a <> representation of the time (at <[timp]>) into a null-terminated string, starting at <[s]> and occupying no more than <[maxsize]> characters. You control the format of the output using the string at <[format]>. <<*<[format]>>> can contain two kinds of specifications: text to be copied literally into the formatted string, and time conversion specifications. Time conversion specifications are two- and three-character sequences beginning with `<<%>>' (use `<<%%>>' to include a percent sign in the output). Each defined conversion specification selects only the specified field(s) of calendar time data from <<*<[timp]>>>, and converts it to a string in one of the following ways: o+ o %a A three-letter abbreviation for the day of the week. [tm_wday] o %A The full name for the day of the week, one of `<>', `<>', `<>', `<>', `<>', `<>', or `<>'. [tm_wday] o %b A three-letter abbreviation for the month name. [tm_mon] o %B The full name of the month, one of `<>', `<>', `<>', `<>', `<>', `<>', `<>', `<>', `<>', `<>', `<>', `<>'. [tm_mon] o %c A string representing the complete date and time, in the form `<<"%a %b %e %H:%M:%S %Y">>' (example "Mon Apr 01 13:13:13 1992"). [tm_sec, tm_min, tm_hour, tm_mday, tm_mon, tm_year, tm_wday] o %C The century, that is, the year divided by 100 then truncated. For 4-digit years, the result is zero-padded and exactly two characters; but for other years, there may a negative sign or more digits. In this way, `<<%C%y>>' is equivalent to `<<%Y>>'. [tm_year] o %d The day of the month, formatted with two digits (from `<<01>>' to `<<31>>'). [tm_mday] o %D A string representing the date, in the form `<<"%m/%d/%y">>'. [tm_mday, tm_mon, tm_year] o %e The day of the month, formatted with leading space if single digit (from `<<1>>' to `<<31>>'). [tm_mday] o %E<> In some locales, the E modifier selects alternative representations of certain modifiers <>. But in the "C" locale supported by newlib, it is ignored, and treated as %<>. o %F A string representing the ISO 8601:2000 date format, in the form `<<"%Y-%m-%d">>'. [tm_mday, tm_mon, tm_year] o %g The last two digits of the week-based year, see specifier %G (from `<<00>>' to `<<99>>'). [tm_year, tm_wday, tm_yday] o %G The week-based year. In the ISO 8601:2000 calendar, week 1 of the year includes January 4th, and begin on Mondays. Therefore, if January 1st, 2nd, or 3rd falls on a Sunday, that day and earlier belong to the last week of the previous year; and if December 29th, 30th, or 31st falls on Monday, that day and later belong to week 1 of the next year. For consistency with %Y, it always has at least four characters. Example: "%G" for Saturday 2nd January 1999 gives "1998", and for Tuesday 30th December 1997 gives "1998". [tm_year, tm_wday, tm_yday] o %h A three-letter abbreviation for the month name (synonym for "%b"). [tm_mon] o %H The hour (on a 24-hour clock), formatted with two digits (from `<<00>>' to `<<23>>'). [tm_hour] o %I The hour (on a 12-hour clock), formatted with two digits (from `<<01>>' to `<<12>>'). [tm_hour] o %j The count of days in the year, formatted with three digits (from `<<001>>' to `<<366>>'). [tm_yday] o %k The hour (on a 24-hour clock), formatted with leading space if single digit (from `<<0>>' to `<<23>>'). Non-POSIX extension. [tm_hour] o %l The hour (on a 12-hour clock), formatted with leading space if single digit (from `<<1>>' to `<<12>>'). Non-POSIX extension. [tm_hour] o %m The month number, formatted with two digits (from `<<01>>' to `<<12>>'). [tm_mon] o %M The minute, formatted with two digits (from `<<00>>' to `<<59>>'). [tm_min] o %n A newline character (`<<\n>>'). o %O<> In some locales, the O modifier selects alternative digit characters for certain modifiers <>. But in the "C" locale supported by newlib, it is ignored, and treated as %<>. o %p Either `<>' or `<>' as appropriate. [tm_hour] o %r The 12-hour time, to the second. Equivalent to "%I:%M:%S %p". [tm_sec, tm_min, tm_hour] o %R The 24-hour time, to the minute. Equivalent to "%H:%M". [tm_min, tm_hour] o %S The second, formatted with two digits (from `<<00>>' to `<<60>>'). The value 60 accounts for the occasional leap second. [tm_sec] o %t A tab character (`<<\t>>'). o %T The 24-hour time, to the second. Equivalent to "%H:%M:%S". [tm_sec, tm_min, tm_hour] o %u The weekday as a number, 1-based from Monday (from `<<1>>' to `<<7>>'). [tm_wday] o %U The week number, where weeks start on Sunday, week 1 contains the first Sunday in a year, and earlier days are in week 0. Formatted with two digits (from `<<00>>' to `<<53>>'). See also <<%W>>. [tm_wday, tm_yday] o %V The week number, where weeks start on Monday, week 1 contains January 4th, and earlier days are in the previous year. Formatted with two digits (from `<<01>>' to `<<53>>'). See also <<%G>>. [tm_year, tm_wday, tm_yday] o %w The weekday as a number, 0-based from Sunday (from `<<0>>' to `<<6>>'). [tm_wday] o %W The week number, where weeks start on Monday, week 1 contains the first Monday in a year, and earlier days are in week 0. Formatted with two digits (from `<<00>>' to `<<53>>'). [tm_wday, tm_yday] o %x A string representing the complete date, equivalent to "%m/%d/%y". [tm_mon, tm_mday, tm_year] o %X A string representing the full time of day (hours, minutes, and seconds), equivalent to "%H:%M:%S". [tm_sec, tm_min, tm_hour] o %y The last two digits of the year (from `<<00>>' to `<<99>>'). [tm_year] o %Y The full year, equivalent to <<%C%y>>. It will always have at least four characters, but may have more. The year is accurate even when tm_year added to the offset of 1900 overflows an int. [tm_year] o %z The offset from UTC. The format consists of a sign (negative is west of Greewich), two characters for hour, then two characters for minutes (-hhmm or +hhmm). If tm_isdst is negative, the offset is unknown and no output is generated; if it is zero, the offset is the standard offset for the current time zone; and if it is positive, the offset is the daylight savings offset for the current timezone. The offset is determined from the TZ environment variable, as if by calling tzset(). [tm_isdst] o %Z The time zone name. If tm_isdst is negative, no output is generated. Otherwise, the time zone name is based on the TZ environment variable, as if by calling tzset(). [tm_isdst] o %% A single character, `<<%>>'. o- RETURNS When the formatted time takes up no more than <[maxsize]> characters, the result is the length of the formatted string. Otherwise, if the formatting operation was abandoned due to lack of room, the result is <<0>>, and the string starting at <[s]> corresponds to just those parts of <<*<[format]>>> that could be completely filled in within the <[maxsize]> limit. PORTABILITY ANSI C requires <>, but does not specify the contents of <<*<[s]>>> when the formatted string would require more than <[maxsize]> characters. Unrecognized specifiers and fields of <> that are out of range cause undefined results. Since some formats expand to 0 bytes, it is wise to set <<*<[s]>>> to a nonzero value beforehand to distinguish between failure and an empty string. This implementation does not support <> being NULL, nor overlapping <> and <>. <> requires no supporting OS subroutines. */ static _CONST int dname_len[7] = {6, 6, 7, 9, 8, 6, 8}; static _CONST char *_CONST dname[7] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}; static _CONST int mname_len[12] = {7, 8, 5, 5, 3, 4, 4, 6, 9, 7, 8, 8}; static _CONST char *_CONST mname[12] = {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"}; /* Using the tm_year, tm_wday, and tm_yday components of TIM_P, return -1, 0, or 1 as the adjustment to add to the year for the ISO week numbering used in "%g%G%V", avoiding overflow. */ static int _DEFUN (iso_year_adjust, (tim_p), _CONST struct tm *tim_p) { /* Account for fact that tm_year==0 is year 1900. */ int leap = isleap (tim_p->tm_year + (YEAR_BASE - (tim_p->tm_year < 0 ? 0 : 2000))); /* Pack the yday, wday, and leap year into a single int since there are so many disparate cases. */ #define PACK(yd, wd, lp) (((yd) << 4) + (wd << 1) + (lp)) switch (PACK (tim_p->tm_yday, tim_p->tm_wday, leap)) { case PACK (0, 5, 0): /* Jan 1 is Fri, not leap. */ case PACK (0, 6, 0): /* Jan 1 is Sat, not leap. */ case PACK (0, 0, 0): /* Jan 1 is Sun, not leap. */ case PACK (0, 5, 1): /* Jan 1 is Fri, leap year. */ case PACK (0, 6, 1): /* Jan 1 is Sat, leap year. */ case PACK (0, 0, 1): /* Jan 1 is Sun, leap year. */ case PACK (1, 6, 0): /* Jan 2 is Sat, not leap. */ case PACK (1, 0, 0): /* Jan 2 is Sun, not leap. */ case PACK (1, 6, 1): /* Jan 2 is Sat, leap year. */ case PACK (1, 0, 1): /* Jan 2 is Sun, leap year. */ case PACK (2, 0, 0): /* Jan 3 is Sun, not leap. */ case PACK (2, 0, 1): /* Jan 3 is Sun, leap year. */ return -1; /* Belongs to last week of previous year. */ case PACK (362, 1, 0): /* Dec 29 is Mon, not leap. */ case PACK (363, 1, 1): /* Dec 29 is Mon, leap year. */ case PACK (363, 1, 0): /* Dec 30 is Mon, not leap. */ case PACK (363, 2, 0): /* Dec 30 is Tue, not leap. */ case PACK (364, 1, 1): /* Dec 30 is Mon, leap year. */ case PACK (364, 2, 1): /* Dec 30 is Tue, leap year. */ case PACK (364, 1, 0): /* Dec 31 is Mon, not leap. */ case PACK (364, 2, 0): /* Dec 31 is Tue, not leap. */ case PACK (364, 3, 0): /* Dec 31 is Wed, not leap. */ case PACK (365, 1, 1): /* Dec 31 is Mon, leap year. */ case PACK (365, 2, 1): /* Dec 31 is Tue, leap year. */ case PACK (365, 3, 1): /* Dec 31 is Wed, leap year. */ return 1; /* Belongs to first week of next year. */ } return 0; /* Belongs to specified year. */ #undef PACK } size_t _DEFUN (strftime, (s, maxsize, format, tim_p), char *s _AND size_t maxsize _AND _CONST char *format _AND _CONST struct tm *tim_p) { size_t count = 0; int i; for (;;) { while (*format && *format != '%') { if (count < maxsize - 1) s[count++] = *format++; else return 0; } if (*format == '\0') break; format++; if (*format == 'E' || *format == 'O') format++; switch (*format) { case 'a': for (i = 0; i < 3; i++) { if (count < maxsize - 1) s[count++] = dname[tim_p->tm_wday][i]; else return 0; } break; case 'A': for (i = 0; i < dname_len[tim_p->tm_wday]; i++) { if (count < maxsize - 1) s[count++] = dname[tim_p->tm_wday][i]; else return 0; } break; case 'b': case 'h': for (i = 0; i < 3; i++) { if (count < maxsize - 1) s[count++] = mname[tim_p->tm_mon][i]; else return 0; } break; case 'B': for (i = 0; i < mname_len[tim_p->tm_mon]; i++) { if (count < maxsize - 1) s[count++] = mname[tim_p->tm_mon][i]; else return 0; } break; case 'c': { /* Length is not known because of %C%y, so recurse. */ size_t adjust = strftime (&s[count], maxsize - count, "%a %b %e %H:%M:%S %C%y", tim_p); if (adjust > 0) count += adjust; else return 0; } break; case 'C': { /* Examples of (tm_year + YEAR_BASE) that show how %Y == %C%y with 32-bit int. %Y %C %y 2147485547 21474855 47 10000 100 00 9999 99 99 0999 09 99 0099 00 99 0001 00 01 0000 00 00 -001 -0 01 -099 -0 99 -999 -9 99 -1000 -10 00 -10000 -100 00 -2147481748 -21474817 48 Be careful of both overflow and sign adjustment due to the asymmetric range of years. */ int neg = tim_p->tm_year < -YEAR_BASE; int century = tim_p->tm_year >= 0 ? tim_p->tm_year / 100 + YEAR_BASE / 100 : abs (tim_p->tm_year + YEAR_BASE) / 100; count += snprintf (&s[count], maxsize - count, "%s%.*d", neg ? "-" : "", 2 - neg, century); if (count >= maxsize) return 0; } break; case 'd': case 'e': if (count < maxsize - 2) { sprintf (&s[count], *format == 'd' ? "%.2d" : "%2d", tim_p->tm_mday); count += 2; } else return 0; break; case 'D': case 'x': /* %m/%d/%y */ if (count < maxsize - 8) { sprintf (&s[count], "%.2d/%.2d/%.2d", tim_p->tm_mon + 1, tim_p->tm_mday, tim_p->tm_year >= 0 ? tim_p->tm_year % 100 : abs (tim_p->tm_year + YEAR_BASE) % 100); count += 8; } else return 0; break; case 'F': { /* Length is not known because of %C%y, so recurse. */ size_t adjust = strftime (&s[count], maxsize - count, "%C%y-%m-%d", tim_p); if (adjust > 0) count += adjust; else return 0; } break; case 'g': if (count < maxsize - 2) { /* Be careful of both overflow and negative years, thanks to the asymmetric range of years. */ int adjust = iso_year_adjust (tim_p); int year = tim_p->tm_year >= 0 ? tim_p->tm_year % 100 : abs (tim_p->tm_year + YEAR_BASE) % 100; if (adjust < 0 && tim_p->tm_year <= -YEAR_BASE) adjust = 1; else if (adjust > 0 && tim_p->tm_year < -YEAR_BASE) adjust = -1; sprintf (&s[count], "%.2d", ((year + adjust) % 100 + 100) % 100); count += 2; } else return 0; break; case 'G': { /* See the comments for 'C' and 'Y'; this is a variable length field. Although there is no requirement for a minimum number of digits, we use 4 for consistency with 'Y'. */ int neg = tim_p->tm_year < -YEAR_BASE; int adjust = iso_year_adjust (tim_p); int century = tim_p->tm_year >= 0 ? tim_p->tm_year / 100 + YEAR_BASE / 100 : abs (tim_p->tm_year + YEAR_BASE) / 100; int year = tim_p->tm_year >= 0 ? tim_p->tm_year % 100 : abs (tim_p->tm_year + YEAR_BASE) % 100; if (adjust < 0 && tim_p->tm_year <= -YEAR_BASE) neg = adjust = 1; else if (adjust > 0 && neg) adjust = -1; year += adjust; if (year == -1) { year = 99; --century; } else if (year == 100) { year = 0; ++century; } count += snprintf (&s[count], maxsize - count, "%s%.*d%.2d", neg ? "-" : "", 2 - neg, century, year); if (count >= maxsize) return 0; } break; case 'H': case 'k': if (count < maxsize - 2) { sprintf (&s[count], *format == 'k' ? "%2d" : "%.2d", tim_p->tm_hour); count += 2; } else return 0; break; case 'I': case 'l': if (count < maxsize - 2) { if (tim_p->tm_hour == 0 || tim_p->tm_hour == 12) { s[count++] = '1'; s[count++] = '2'; } else { sprintf (&s[count], *format == 'I' ? "%.2d" : "%2d", tim_p->tm_hour % 12); count += 2; } } else return 0; break; case 'j': if (count < maxsize - 3) { sprintf (&s[count], "%.3d", tim_p->tm_yday + 1); count += 3; } else return 0; break; case 'm': if (count < maxsize - 2) { sprintf (&s[count], "%.2d", tim_p->tm_mon + 1); count += 2; } else return 0; break; case 'M': if (count < maxsize - 2) { sprintf (&s[count], "%.2d", tim_p->tm_min); count += 2; } else return 0; break; case 'n': if (count < maxsize - 1) s[count++] = '\n'; else return 0; break; case 'p': if (count < maxsize - 2) { if (tim_p->tm_hour < 12) s[count++] = 'A'; else s[count++] = 'P'; s[count++] = 'M'; } else return 0; break; case 'r': if (count < maxsize - 11) { if (tim_p->tm_hour == 0 || tim_p->tm_hour == 12) { s[count++] = '1'; s[count++] = '2'; } else { sprintf (&s[count], "%.2d", tim_p->tm_hour % 12); count += 2; } s[count++] = ':'; sprintf (&s[count], "%.2d", tim_p->tm_min); count += 2; s[count++] = ':'; sprintf (&s[count], "%.2d", tim_p->tm_sec); count += 2; s[count++] = ' '; if (tim_p->tm_hour < 12) s[count++] = 'A'; else s[count++] = 'P'; s[count++] = 'M'; } else return 0; break; case 'R': if (count < maxsize - 5) { sprintf (&s[count], "%.2d:%.2d", tim_p->tm_hour, tim_p->tm_min); count += 5; } else return 0; break; case 'S': if (count < maxsize - 2) { sprintf (&s[count], "%.2d", tim_p->tm_sec); count += 2; } else return 0; break; case 't': if (count < maxsize - 1) s[count++] = '\t'; else return 0; break; case 'T': case 'X': if (count < maxsize - 8) { sprintf (&s[count], "%.2d:%.2d:%.2d", tim_p->tm_hour, tim_p->tm_min, tim_p->tm_sec); count += 8; } else return 0; break; case 'u': if (count < maxsize - 1) { if (tim_p->tm_wday == 0) s[count++] = '7'; else s[count++] = '0' + tim_p->tm_wday; } else return 0; break; case 'U': if (count < maxsize - 2) { sprintf (&s[count], "%.2d", (tim_p->tm_yday + 7 - tim_p->tm_wday) / 7); count += 2; } else return 0; break; case 'V': if (count < maxsize - 2) { int adjust = iso_year_adjust (tim_p); int wday = (tim_p->tm_wday) ? tim_p->tm_wday - 1 : 6; int week = (tim_p->tm_yday + 10 - wday) / 7; if (adjust > 0) week = 1; else if (adjust < 0) /* Previous year has 53 weeks if current year starts on Fri, and also if current year starts on Sat and previous year was leap year. */ week = 52 + (4 >= (wday - tim_p->tm_yday - isleap (tim_p->tm_year + (YEAR_BASE - 1 - (tim_p->tm_year < 0 ? 0 : 2000))))); sprintf (&s[count], "%.2d", week); count += 2; } else return 0; break; case 'w': if (count < maxsize - 1) s[count++] = '0' + tim_p->tm_wday; else return 0; break; case 'W': if (count < maxsize - 2) { int wday = (tim_p->tm_wday) ? tim_p->tm_wday - 1 : 6; sprintf (&s[count], "%.2d", (tim_p->tm_yday + 7 - wday) / 7); count += 2; } else return 0; break; case 'y': if (count < maxsize - 2) { /* Be careful of both overflow and negative years, thanks to the asymmetric range of years. */ int year = tim_p->tm_year >= 0 ? tim_p->tm_year % 100 : abs (tim_p->tm_year + YEAR_BASE) % 100; sprintf (&s[count], "%.2d", year); count += 2; } else return 0; break; case 'Y': { /* Length is not known because of %C%y, so recurse. */ size_t adjust = strftime (&s[count], maxsize - count, "%C%y", tim_p); if (adjust > 0) count += adjust; else return 0; } break; case 'z': #ifndef _WIN32_WCE if (tim_p->tm_isdst >= 0) { if (count < maxsize - 5) { long offset; __tzinfo_type *tz = __gettzinfo (); TZ_LOCK; /* The sign of this is exactly opposite the envvar TZ. We could directly use the global _timezone for tm_isdst==0, but have to use __tzrule for daylight savings. */ offset = -tz->__tzrule[tim_p->tm_isdst > 0].offset; TZ_UNLOCK; sprintf (&s[count], "%+03ld%.2ld", offset / SECSPERHOUR, labs (offset / SECSPERMIN) % 60L); count += 5; } else return 0; } break; #endif case 'Z': if (tim_p->tm_isdst >= 0) { int size; TZ_LOCK; size = strlen(_tzname[tim_p->tm_isdst > 0]); for (i = 0; i < size; i++) { if (count < maxsize - 1) s[count++] = _tzname[tim_p->tm_isdst > 0][i]; else { TZ_UNLOCK; return 0; } } TZ_UNLOCK; } break; case '%': if (count < maxsize - 1) s[count++] = '%'; else return 0; break; } if (*format) format++; else break; } if (maxsize) s[count] = '\0'; return count; }