From e0702ee952a727a954fac1f2e1045d730e8add30 Mon Sep 17 00:00:00 2001 From: Dennis Bonke Date: Mon, 10 Jun 2024 00:27:55 +0200 Subject: [PATCH 01/13] options/ansi: Implement %O in strftime Co-authored-by: no92 --- options/ansi/generic/time-stubs.cpp | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/options/ansi/generic/time-stubs.cpp b/options/ansi/generic/time-stubs.cpp index b8c7cf57ac..d788cfddd6 100644 --- a/options/ansi/generic/time-stubs.cpp +++ b/options/ansi/generic/time-stubs.cpp @@ -1,4 +1,5 @@ - +#include +#include #include #include #include @@ -92,6 +93,7 @@ size_t strftime(char *__restrict dest, size_t max_size, const char *__restrict format, const struct tm *__restrict tm) { auto c = format; auto p = dest; + [[maybe_unused]] bool use_alternative_symbols = false; while(*c) { int chunk; @@ -107,6 +109,23 @@ size_t strftime(char *__restrict dest, size_t max_size, continue; } + if(*(c + 1) == 'O') { + std::array valid{{'B', 'b', 'd', 'e', 'H', 'I', 'm', 'M', 'S', 'u', 'U', 'V', 'w', 'W', 'y'}}; + auto next = *(c + 2); + if(std::find(valid.begin(), valid.end(), next) != valid.end()) { + use_alternative_symbols = true; + c++; + } else { + *p = '%'; + p++; + c++; + *p = 'O'; + p++; + c++; + continue; + } + } + switch(*++c) { case 'Y': { chunk = snprintf(p, space, "%d", 1900 + tm->tm_year); From 8da801a3d2006b0cf9a30ce1d4ab7dc621e386d9 Mon Sep 17 00:00:00 2001 From: Dennis Bonke Date: Mon, 10 Jun 2024 00:28:37 +0200 Subject: [PATCH 02/13] options/ansi: Fix bug with years in strftime %D --- options/ansi/generic/time-stubs.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/options/ansi/generic/time-stubs.cpp b/options/ansi/generic/time-stubs.cpp index d788cfddd6..821c5f3a19 100644 --- a/options/ansi/generic/time-stubs.cpp +++ b/options/ansi/generic/time-stubs.cpp @@ -209,7 +209,7 @@ size_t strftime(char *__restrict dest, size_t max_size, break; } case 'D': { - chunk = snprintf(p, space, "%.2d/%.2d/%.2d", tm->tm_mon + 1, tm->tm_mday, tm->tm_year % 100); + chunk = snprintf(p, space, "%.2d/%.2d/%.2d", tm->tm_mon + 1, tm->tm_mday, (tm->tm_year + 1900) % 100); if(chunk >= space) return 0; p += chunk; From 2fd687a60ddd75fbe5e42727013409fb07fa4273 Mon Sep 17 00:00:00 2001 From: Dennis Bonke Date: Mon, 10 Jun 2024 00:29:12 +0200 Subject: [PATCH 03/13] options/ansi: Fix %c in strftime --- options/ansi/generic/time-stubs.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/options/ansi/generic/time-stubs.cpp b/options/ansi/generic/time-stubs.cpp index 821c5f3a19..ce59c1690f 100644 --- a/options/ansi/generic/time-stubs.cpp +++ b/options/ansi/generic/time-stubs.cpp @@ -245,8 +245,16 @@ size_t strftime(char *__restrict dest, size_t max_size, break; } case 'c': { - chunk = snprintf(p, space, "%d/%.2d/%.2d %.2d:%.2d:%.2d", 1900 + tm->tm_year, - tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); + int day = tm->tm_wday; + if(day < 0 || day > 6) + __ensure(!"Day not in bounds."); + + int mon = tm->tm_mon; + if(mon < 0 || mon > 11) + __ensure(!"Month not in bounds."); + + chunk = snprintf(p, space, "%s %s %2d %.2i:%.2i:%.2d %d", mlibc::nl_langinfo(ABDAY_1 + day), + mlibc::nl_langinfo(ABMON_1 + mon), tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, 1900 + tm->tm_year); if(chunk >= space) return 0; p += chunk; From 8251b41c6cfd060578033ab23493d92aaa27cc05 Mon Sep 17 00:00:00 2001 From: Dennis Bonke Date: Mon, 10 Jun 2024 00:29:38 +0200 Subject: [PATCH 04/13] options/ansi: Implement %k in strftime --- options/ansi/generic/time-stubs.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/options/ansi/generic/time-stubs.cpp b/options/ansi/generic/time-stubs.cpp index ce59c1690f..dd43bc66d6 100644 --- a/options/ansi/generic/time-stubs.cpp +++ b/options/ansi/generic/time-stubs.cpp @@ -282,6 +282,14 @@ size_t strftime(char *__restrict dest, size_t max_size, c++; break; } + case 'k': { + chunk = snprintf(p, space, "%2d", tm->tm_hour); + if(chunk >= space) + return 0; + p += chunk; + c++; + break; + } case 'I': { int hour = tm->tm_hour; if(!hour) From 2787ef3282cabd17e973f1eb31cccb1d24690e2c Mon Sep 17 00:00:00 2001 From: Dennis Bonke Date: Mon, 10 Jun 2024 00:29:59 +0200 Subject: [PATCH 05/13] options/ansi: Implement %P in strftime Co-authored-by: no92 --- options/ansi/generic/time-stubs.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/options/ansi/generic/time-stubs.cpp b/options/ansi/generic/time-stubs.cpp index dd43bc66d6..5d2a2adcb2 100644 --- a/options/ansi/generic/time-stubs.cpp +++ b/options/ansi/generic/time-stubs.cpp @@ -311,6 +311,18 @@ size_t strftime(char *__restrict dest, size_t max_size, c++; break; } + case 'P': { + char *str = mlibc::nl_langinfo((tm->tm_hour < 12) ? AM_STR : PM_STR); + for(size_t i = 0; str[i]; i++) + str[i] = tolower(str[i]); + + chunk = snprintf(p, space, "%s", str_lower); + if(chunk >= space) + return 0; + p += chunk; + c++; + break; + } case 'C': { chunk = snprintf(p, space, "%.2d", (1900 + tm->tm_year) / 100); if(chunk >= space) From 8d8aa4a712f4af63d3841d6e4ffe666d2edd3bea Mon Sep 17 00:00:00 2001 From: Dennis Bonke Date: Mon, 10 Jun 2024 00:30:32 +0200 Subject: [PATCH 06/13] options/ansi: Implement %n in strftime --- options/ansi/generic/time-stubs.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/options/ansi/generic/time-stubs.cpp b/options/ansi/generic/time-stubs.cpp index 5d2a2adcb2..e037287abc 100644 --- a/options/ansi/generic/time-stubs.cpp +++ b/options/ansi/generic/time-stubs.cpp @@ -377,6 +377,14 @@ size_t strftime(char *__restrict dest, size_t max_size, c++; break; } + case 'n': { + chunk = snprintf(p, space, "\n"); + if(chunk >= space) + return 0; + p += chunk; + c++; + break; + } case 't': { chunk = snprintf(p, space, "\t"); if(chunk >= space) From d58d9ad5151b56ffc4da7b310ecbfdf39dfeeb6d Mon Sep 17 00:00:00 2001 From: Dennis Bonke Date: Mon, 10 Jun 2024 00:58:24 +0200 Subject: [PATCH 07/13] options/ansi: Hardcode UTC over GMT for strftime %Z --- options/ansi/generic/time-stubs.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/options/ansi/generic/time-stubs.cpp b/options/ansi/generic/time-stubs.cpp index e037287abc..40a616c2a6 100644 --- a/options/ansi/generic/time-stubs.cpp +++ b/options/ansi/generic/time-stubs.cpp @@ -152,7 +152,7 @@ size_t strftime(char *__restrict dest, size_t max_size, break; } case 'Z': { - chunk = snprintf(p, space, "%s", "GMT"); + chunk = snprintf(p, space, "%s", "UTC"); if(chunk >= space) return 0; p += chunk; From f0388a8e8862dc7e26f2492b3978beec9ff677a0 Mon Sep 17 00:00:00 2001 From: Dennis Bonke Date: Mon, 10 Jun 2024 00:58:44 +0200 Subject: [PATCH 08/13] options/internal: Add handling for T_FMT_AMPM to nl_langinfo --- options/internal/generic/locale.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/options/internal/generic/locale.cpp b/options/internal/generic/locale.cpp index 7ba040fb21..f9ff5cc7a4 100644 --- a/options/internal/generic/locale.cpp +++ b/options/internal/generic/locale.cpp @@ -77,6 +77,8 @@ char *nl_langinfo(nl_item item) { return const_cast("%m/%d/%y"); }else if(item == T_FMT) { return const_cast("%H:%M:%S"); + }else if(item == T_FMT_AMPM) { + return const_cast("%I:%M:%S %p"); }else{ mlibc::infoLogger() << "mlibc: nl_langinfo item " << item << " is not implemented properly" << frg::endlog; From 082ab672e6664e1cb7da26e74f91f397dd1f988e Mon Sep 17 00:00:00 2001 From: Dennis Bonke Date: Mon, 10 Jun 2024 01:28:25 +0200 Subject: [PATCH 09/13] options/internal: Add support for D_T_FMT in nl_langinfo --- options/internal/generic/locale.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/options/internal/generic/locale.cpp b/options/internal/generic/locale.cpp index f9ff5cc7a4..55449bb139 100644 --- a/options/internal/generic/locale.cpp +++ b/options/internal/generic/locale.cpp @@ -79,6 +79,8 @@ char *nl_langinfo(nl_item item) { return const_cast("%H:%M:%S"); }else if(item == T_FMT_AMPM) { return const_cast("%I:%M:%S %p"); + }else if(item == D_T_FMT) { + return const_cast("%a %b %e %T %Y"); }else{ mlibc::infoLogger() << "mlibc: nl_langinfo item " << item << " is not implemented properly" << frg::endlog; From 37e716ae70274780d1971f056ec2b795a5a25df9 Mon Sep 17 00:00:00 2001 From: Dennis Bonke Date: Mon, 10 Jun 2024 01:29:26 +0200 Subject: [PATCH 10/13] options/internal: Add support for YESEXPR in nl_langinfo --- options/internal/generic/locale.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/options/internal/generic/locale.cpp b/options/internal/generic/locale.cpp index 55449bb139..7269e6b25c 100644 --- a/options/internal/generic/locale.cpp +++ b/options/internal/generic/locale.cpp @@ -81,6 +81,8 @@ char *nl_langinfo(nl_item item) { return const_cast("%I:%M:%S %p"); }else if(item == D_T_FMT) { return const_cast("%a %b %e %T %Y"); + }else if(item == YESEXPR) { + return const_cast("^[yY]"); }else{ mlibc::infoLogger() << "mlibc: nl_langinfo item " << item << " is not implemented properly" << frg::endlog; From b6d2eb6ac68baade167b00ffee18bd59e714d063 Mon Sep 17 00:00:00 2001 From: Dennis Bonke Date: Mon, 10 Jun 2024 01:29:57 +0200 Subject: [PATCH 11/13] options/internal: Add support for NOEXPR in nl_langinfo --- options/internal/generic/locale.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/options/internal/generic/locale.cpp b/options/internal/generic/locale.cpp index 7269e6b25c..afd676785b 100644 --- a/options/internal/generic/locale.cpp +++ b/options/internal/generic/locale.cpp @@ -83,6 +83,8 @@ char *nl_langinfo(nl_item item) { return const_cast("%a %b %e %T %Y"); }else if(item == YESEXPR) { return const_cast("^[yY]"); + }else if(item == NOEXPR) { + return const_cast("^[nN]"); }else{ mlibc::infoLogger() << "mlibc: nl_langinfo item " << item << " is not implemented properly" << frg::endlog; From 06b14babc2a849046381de43e66fbddf73a1625a Mon Sep 17 00:00:00 2001 From: Dennis Bonke Date: Mon, 10 Jun 2024 13:04:23 +0200 Subject: [PATCH 12/13] options/ansi: Implement %E in strftime --- options/ansi/generic/time-stubs.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/options/ansi/generic/time-stubs.cpp b/options/ansi/generic/time-stubs.cpp index 40a616c2a6..579d88e6fc 100644 --- a/options/ansi/generic/time-stubs.cpp +++ b/options/ansi/generic/time-stubs.cpp @@ -94,6 +94,7 @@ size_t strftime(char *__restrict dest, size_t max_size, auto c = format; auto p = dest; [[maybe_unused]] bool use_alternative_symbols = false; + [[maybe_unused]] bool use_alternative_era_format = false; while(*c) { int chunk; @@ -124,6 +125,21 @@ size_t strftime(char *__restrict dest, size_t max_size, c++; continue; } + } else if(*(c + 1) == 'E') { + std::array valid{{'c', 'C', 'x', 'X', 'y', 'Y'}}; + auto next = *(c + 2); + if(std::find(valid.begin(), valid.end(), next) != valid.end()) { + use_alternative_era_format = true; + c++; + } else { + *p = '%'; + p++; + c++; + *p = 'E'; + p++; + c++; + continue; + } } switch(*++c) { From 38a1df9ea76fd1783a01c917203fb2de3f1f66b8 Mon Sep 17 00:00:00 2001 From: no92 Date: Sat, 6 Jul 2024 18:11:15 +0200 Subject: [PATCH 13/13] tests/ansi: add tests for various new strftime modifiers --- options/ansi/generic/time-stubs.cpp | 6 ++++-- tests/ansi/strftime.c | 22 ++++++++++++++++++++-- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/options/ansi/generic/time-stubs.cpp b/options/ansi/generic/time-stubs.cpp index 579d88e6fc..614be5c4d5 100644 --- a/options/ansi/generic/time-stubs.cpp +++ b/options/ansi/generic/time-stubs.cpp @@ -269,7 +269,7 @@ size_t strftime(char *__restrict dest, size_t max_size, if(mon < 0 || mon > 11) __ensure(!"Month not in bounds."); - chunk = snprintf(p, space, "%s %s %2d %.2i:%.2i:%.2d %d", mlibc::nl_langinfo(ABDAY_1 + day), + chunk = snprintf(p, space, "%s %s %2d %.2i:%.2i:%.2d %d", mlibc::nl_langinfo(ABDAY_1 + day), mlibc::nl_langinfo(ABMON_1 + mon), tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, 1900 + tm->tm_year); if(chunk >= space) return 0; @@ -329,8 +329,10 @@ size_t strftime(char *__restrict dest, size_t max_size, } case 'P': { char *str = mlibc::nl_langinfo((tm->tm_hour < 12) ? AM_STR : PM_STR); + char *str_lower = reinterpret_cast(getAllocator().allocate(strlen(str) + 1)); for(size_t i = 0; str[i]; i++) - str[i] = tolower(str[i]); + str_lower[i] = tolower(str[i]); + str_lower[strlen(str)] = '\0'; chunk = snprintf(p, space, "%s", str_lower); if(chunk >= space) diff --git a/tests/ansi/strftime.c b/tests/ansi/strftime.c index 69ec6427dc..e30df21cb6 100644 --- a/tests/ansi/strftime.c +++ b/tests/ansi/strftime.c @@ -11,8 +11,8 @@ int main() { fputs("strftime testcase could not set locale, errors may be expected!", stderr); } - char timebuf[16]; - char result[16] = " 8"; + char timebuf[32]; + char result[32] = " 8"; struct tm tm; tm.tm_sec = 0; tm.tm_min = 17; @@ -33,6 +33,24 @@ int main() { strftime(timebuf, sizeof(timebuf), "%X", &tm); assert(!strcmp(timebuf, "17:17:00")); + memset(timebuf, 0, sizeof(timebuf)); + strftime(timebuf, sizeof(timebuf), "%D", &tm); + assert(!strcmp(timebuf, "03/08/21")); + + memset(timebuf, 0, sizeof(timebuf)); + strftime(timebuf, sizeof(timebuf), "%OB %Ob", &tm); + assert(!strcmp(timebuf, "March Mar")); + + memset(timebuf, 0, sizeof(timebuf)); + strftime(timebuf, sizeof(timebuf), "%c", &tm); + memset(result, 0, sizeof(result)); + strftime(result, sizeof(result), "%a %b %e %H:%M:%S %Y", &tm); + assert(!strcmp(timebuf, result)); + + memset(timebuf, 0, sizeof(timebuf)); + strftime(timebuf, sizeof(timebuf), "%k %p %P%n", &tm); + assert(!strcmp(timebuf, "17 PM pm\n")); + memset(timebuf, 0, sizeof(timebuf)); strftime(timebuf, sizeof(timebuf), "%a %A", &tm); assert(!strcmp(timebuf, "Tue Tuesday"));