diff --git a/libc/include/locale.h b/libc/include/locale.h index e0740303..45493c6a 100644 --- a/libc/include/locale.h +++ b/libc/include/locale.h @@ -5,6 +5,28 @@ #include +struct lconv +{ + char* decimal_point; + char* thousands_sep; + char* grouping; + char* int_curr_symbol; + char* currency_symbol; + char* mon_decimal_point; + char* mon_thousands_sep; + char* mon_grouping; + char* positive_sign; + char* negative_sign; + char int_frac_digits; + char frac_digits; + char p_cs_precedes; + char p_sep_by_space; + char n_cs_precedes; + char n_sep_by_space; + char p_sign_posn; + char n_sign_posn; +}; + #ifdef __cplusplus extern "C" { @@ -13,6 +35,9 @@ extern "C" // Query or set the current locale. char* setlocale(int category, const char* locale); + // Query formatting information for the current locale. + struct lconv* localeconv(void); + #ifdef __cplusplus } #endif diff --git a/libc/include/string.h b/libc/include/string.h index 800c3947..6b87f20e 100644 --- a/libc/include/string.h +++ b/libc/include/string.h @@ -98,6 +98,9 @@ extern "C" /* Compare two null-terminated strings according to the current locale. */ int strcoll(const char* a, const char* b); + /* Transform a string according to the current locale. */ + size_t strxfrm(char* dest, const char* src, size_t n); + #ifdef __cplusplus } #endif diff --git a/libc/include/time.h b/libc/include/time.h index 8f4291aa..3e725293 100644 --- a/libc/include/time.h +++ b/libc/include/time.h @@ -7,6 +7,10 @@ #include #include +typedef long int clock_t; + +#define CLOCKS_PER_SEC 1000000 + #ifdef __cplusplus extern "C" { @@ -42,9 +46,18 @@ extern "C" /* Build a string representation of UNIX time. */ char* ctime_r(const time_t* tp, char buf[26]); + /* Convert broken-down time back into UNIX time. */ + time_t mktime(struct tm* tm); + /* Format a string representation of broken-down time using a format string. */ size_t strftime(char* buf, size_t max, const char* format, const struct tm* tm); + /* Return the difference in seconds between two times. */ + double difftime(time_t a, time_t b); + + /* Estimate the CPU time used by a process. */ + clock_t clock(void); + #ifdef __cplusplus } #endif diff --git a/libc/src/locale.cpp b/libc/src/locale.cpp index 19c508ab..1db886a4 100644 --- a/libc/src/locale.cpp +++ b/libc/src/locale.cpp @@ -1,6 +1,30 @@ +#include #include static char s_default_locale[] = "C"; +static char s_empty_string[] = ""; +static char s_decimal_point[] = "."; + +static struct lconv s_lconv = { + .decimal_point = s_decimal_point, + .thousands_sep = s_empty_string, + .grouping = s_empty_string, + .int_curr_symbol = s_empty_string, + .currency_symbol = s_empty_string, + .mon_decimal_point = s_empty_string, + .mon_thousands_sep = s_empty_string, + .mon_grouping = s_empty_string, + .positive_sign = s_empty_string, + .negative_sign = s_empty_string, + .int_frac_digits = CHAR_MAX, + .frac_digits = CHAR_MAX, + .p_cs_precedes = CHAR_MAX, + .p_sep_by_space = CHAR_MAX, + .n_cs_precedes = CHAR_MAX, + .n_sep_by_space = CHAR_MAX, + .p_sign_posn = CHAR_MAX, + .n_sign_posn = CHAR_MAX, +}; extern "C" { @@ -9,4 +33,9 @@ extern "C" // FIXME: Set the current locale if is not NULL. return s_default_locale; } + + struct lconv* localeconv(void) + { + return &s_lconv; + } } diff --git a/libc/src/string.cpp b/libc/src/string.cpp index 7aa55706..026b1953 100644 --- a/libc/src/string.cpp +++ b/libc/src/string.cpp @@ -15,6 +15,12 @@ extern "C" return strcmp(a, b); } + size_t strxfrm(char* dest, const char* src, size_t n) + { + strncpy(dest, src, n); + return n; + } + char* strerror(int errnum) { return const_cast(error_string(errnum)); diff --git a/libc/src/time.cpp b/libc/src/time.cpp index 33c6a22e..7f13646a 100644 --- a/libc/src/time.cpp +++ b/libc/src/time.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -31,6 +32,13 @@ static int day_of_week(int year, int mon, int day) return (year + year / 4 - year / 100 + year / 400 + t[mon - 1] + day) % 7; } +// https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_16 +static constexpr u64 broken_down_to_unix(u64 year, u64 yday, u64 hour, u64 min, u64 sec) +{ + return sec + min * 60 + hour * 3600 + yday * 86400 + (year - 70) * 31536000 + ((year - 69) / 4) * 86400 - + ((year - 1) / 100) * 86400 + ((year + 299) / 400) * 86400; +} + static void time_to_struct_tm(time_t time, struct tm* result) { result->tm_isdst = 0; // No DST/timezone support for now. @@ -167,4 +175,29 @@ extern "C" return 0; } + + time_t mktime(struct tm* tm) + { + // FIXME: Check if the tm structure is valid. + time_t result = broken_down_to_unix(tm->tm_year, make_yday(tm->tm_year, tm->tm_mon + 1) + (tm->tm_mday - 1), + tm->tm_hour, tm->tm_min, tm->tm_sec); + + tm->tm_yday = make_yday(tm->tm_year + 1900, tm->tm_mon + 1) + (tm->tm_mday - 1); + tm->tm_wday = day_of_week(tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday); + + return result; + } + + double difftime(time_t a, time_t b) + { + return (double)(a - b); + } + + clock_t clock(void) + { + struct rusage ru; + if (getrusage(RUSAGE_SELF, &ru) < 0) return (clock_t)-1; + + return ru.ru_utime.tv_sec * CLOCKS_PER_SEC + ru.ru_utime.tv_usec; + } }