X-Git-Url: https://git.pterodactylus.net/?a=blobdiff_plain;f=libs%2Fshttpd%2Fcompat_wince.c;fp=libs%2Fshttpd%2Fcompat_wince.c;h=36702f6d4bc7e5aa0ed57a84664c09071ea4d89c;hb=d8ccfe2b3944adf07d35534459cdda19d15217c8;hp=0000000000000000000000000000000000000000;hpb=21f835f30b4e092c847bf4569a00995774f7330e;p=fms.git diff --git a/libs/shttpd/compat_wince.c b/libs/shttpd/compat_wince.c new file mode 100644 index 0000000..36702f6 --- /dev/null +++ b/libs/shttpd/compat_wince.c @@ -0,0 +1,1593 @@ +/* + 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; +}