From b36422960466777495933ed1eb50befd1c34e9a9 Mon Sep 17 00:00:00 2001 From: Bernhard Reutner-Fischer Date: Tue, 18 Nov 2014 18:27:47 +0100 Subject: [PATCH] locale: Add wcsftime() Signed-off-by: Bernhard Reutner-Fischer --- libc/misc/time/time.c | 144 +++++++++++++++++++++++++++++------------------ test/time/Makefile.in | 7 ++- test/time/tst_wcsftime.c | 70 +++++++++++++++-------- 3 files changed, 143 insertions(+), 78 deletions(-) diff --git a/libc/misc/time/time.c b/libc/misc/time/time.c index 347c8990c..a3fccb251 100644 --- a/libc/misc/time/time.c +++ b/libc/misc/time/time.c @@ -146,8 +146,24 @@ #include #include -#ifdef __UCLIBC_HAS_WCHAR__ +#if defined __UCLIBC_HAS_WCHAR__ && (defined L_wcsftime || defined L_wcsftime_l) #include +# define CHAR_T wchar_t +# define UCHAR_T unsigned int +# ifdef L_wcsftime +# define strftime wcsftime +# define L_strftime +# if defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE) +# define strftime_l wcsftime_l +# endif +# endif +# ifdef L_wcsftime_l +# define strftime_l wcsftime_l +# define L_strftime_l +# endif +#else +# define CHAR_T char +# define UCHAR_T unsigned char #endif #ifndef __isleap @@ -787,12 +803,13 @@ time_t timegm(struct tm *timeptr) #endif /**********************************************************************/ -#if defined(L_strftime) || defined(L_strftime_l) +#if defined(L_strftime) || defined(L_strftime_l) \ + || defined(L_wcsftime) || defined(L_wcsftime_l) #if defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE) -size_t strftime(char *__restrict s, size_t maxsize, - const char *__restrict format, +size_t strftime(CHAR_T *__restrict s, size_t maxsize, + const CHAR_T *__restrict format, const struct tm *__restrict timeptr) { return strftime_l(s, maxsize, format, timeptr, __UCLIBC_CURLOCALE); @@ -990,29 +1007,58 @@ static int load_field(int k, const struct tm *__restrict timeptr) return r; } +#if defined __UCLIBC_HAS_WCHAR__ && (defined L_wcsftime || defined L_wcsftime_l) +static wchar_t* fmt_to_wc_1(const char *src) +{ + mbstate_t mbstate; + size_t src_len = strlen(src); + wchar_t *dest = (wchar_t *)malloc((src_len + 1) * sizeof(wchar_t)); + if (dest == NULL) + return NULL; + mbstate.__mask = 0; + if (mbsrtowcs(dest, &src, src_len + 1, &mbstate) == (size_t) -1) { + free(dest); + return NULL; + } + return dest; +} +# define fmt_to_wc(dest, src) \ + dest = alloc[++allocno] = fmt_to_wc_1(src) +# define to_wc(dest, src) \ + dest = fmt_to_wc_1(src) +#else +# define fmt_to_wc(dest, src) (dest) = (src) +# define to_wc(dest, src) (dest) = (src) +#endif + #define MAX_PUSH 4 #ifdef __UCLIBC_MJN3_ONLY__ #warning TODO: Check multibyte format string validity. #endif -size_t __XL_NPP(strftime)(char *__restrict s, size_t maxsize, - const char *__restrict format, +size_t __XL_NPP(strftime)(CHAR_T *__restrict s, size_t maxsize, + const CHAR_T *__restrict format, const struct tm *__restrict timeptr __LOCALE_PARAM ) { long tzo; - register const char *p; - register const char *o; + register const CHAR_T *p; + const CHAR_T *o; + const char *ccp; #ifndef __UCLIBC_HAS_TM_EXTENSIONS__ const rule_struct *rsp; #endif - const char *stack[MAX_PUSH]; + const CHAR_T *stack[MAX_PUSH]; +#if defined __UCLIBC_HAS_WCHAR__ && (defined L_wcsftime || defined L_wcsftime_l) + const CHAR_T *alloc[MAX_PUSH]; + int allocno = -1; +#endif size_t count; size_t o_count; int field_val = 0, i = 0, j, lvl; int x[3]; /* wday, yday, year */ int isofm, days; - char buf[__UIM_BUFLEN_LONG]; + char buf[__UIM_BUFLEN_LONG] = {0,}; unsigned char mod; unsigned char code; @@ -1037,7 +1083,7 @@ LOOP: } o_count = 1; - if ((*(o = p) == '%') && (*++p != '%')) { + if ((*(o = (CHAR_T *)p) == '%') && (*++p != '%')) { o_count = 2; mod = ILLEGAL_SPEC; if ((*p == 'O') || (*p == 'E')) { /* modifier */ @@ -1062,31 +1108,33 @@ LOOP: } stack[lvl++] = ++p; if ((code &= 0xf) < 8) { - p = ((const char *) spec) + STACKED_STRINGS_START + code; - p += *((unsigned char *)p); + ccp = (const char *)(spec + STACKED_STRINGS_START + code); + ccp += *ccp; + fmt_to_wc(p, ccp); goto LOOP; } - p = ((const char *) spec) + STACKED_STRINGS_NL_ITEM_START - + (code & 7); + ccp = (const char *)spec + STACKED_STRINGS_NL_ITEM_START + (code & 7); + fmt_to_wc(p, ccp); #ifdef ENABLE_ERA_CODE if ((mod & NO_E_MOD) /* Actually, this means E modifier present. */ - && (*(o = __XL_NPP(nl_langinfo)(_NL_ITEM(LC_TIME, + && (*(ccp = __XL_NPP(nl_langinfo)(_NL_ITEM(LC_TIME, (int)(((unsigned char *)p)[4])) __LOCALE_ARG ))) ) { - p = o; + fmt_to_wc(p, ccp); goto LOOP; } #endif - p = __XL_NPP(nl_langinfo)(_NL_ITEM(LC_TIME, + ccp = __XL_NPP(nl_langinfo)(_NL_ITEM(LC_TIME, (int)(*((unsigned char *)p))) __LOCALE_ARG ); + fmt_to_wc(p, ccp); goto LOOP; } - o = ((const char *) spec) + 26; /* set to "????" */ + ccp = (const char *)(spec + 26); /* set to "????" */ if ((code & MASK_SPEC) == CALC_SPEC) { if (*p == 's') { @@ -1101,15 +1149,16 @@ LOOP: goto OUTPUT; } #ifdef TIME_T_IS_UNSIGNED - o = _uintmaxtostr(buf + sizeof(buf) - 1, + ccp = _uintmaxtostr(buf + sizeof(buf) - 1, (uintmax_t) t, 10, __UIM_DECIMAL); #else - o = _uintmaxtostr(buf + sizeof(buf) - 1, + ccp = _uintmaxtostr(buf + sizeof(buf) - 1, (uintmax_t) t, -10, __UIM_DECIMAL); #endif o_count = sizeof(buf); + fmt_to_wc(o, ccp); goto OUTPUT; } else if (((*p) | 0x20) == 'z') { /* 'z' or 'Z' */ @@ -1144,7 +1193,7 @@ LOOP: #endif if (*p == 'Z') { - o = RSP_TZNAME; + ccp = RSP_TZNAME; #ifdef __UCLIBC_HAS_TM_EXTENSIONS__ /* Sigh... blasted glibc extensions. Of course we can't * count on the pointer being valid. Best we can do is @@ -1155,17 +1204,18 @@ LOOP: * case... although it always seems to use the embedded * tm_gmtoff value. What we'll do instead is treat the * timezone name as unknown/invalid and return "???". */ - if (!o) { - o = "???"; + if (!ccp) { + ccp = (const char *)(spec + 27); /* "???" */ } #endif - assert(o != NULL); + assert(ccp != NULL); #if 0 - if (!o) { /* PARANOIA */ - o = spec+30; /* empty string */ + if (!ccp) { /* PARANOIA */ + ccp = spec+30; /* empty string */ } #endif o_count = SIZE_MAX; + fmt_to_wc(o, ccp); #ifdef __UCLIBC_HAS_TM_EXTENSIONS__ goto OUTPUT; #endif @@ -1264,17 +1314,19 @@ ISO_LOOP: if ((code & MASK_SPEC) == STRING_SPEC) { o_count = SIZE_MAX; field_val += spec[STRINGS_NL_ITEM_START + (code & 0xf)]; - o = __XL_NPP(nl_langinfo)(_NL_ITEM(LC_TIME, field_val) __LOCALE_ARG); + ccp = __XL_NPP(nl_langinfo)(_NL_ITEM(LC_TIME, field_val) __LOCALE_ARG); + fmt_to_wc(o, ccp); } else { o_count = ((i >> 1) & 3) + 1; - o = buf + o_count; + ccp = buf + o_count; do { - *(char *)(--o) = '0' + (field_val % 10); + *(char *)(--ccp) = '0' + (field_val % 10); field_val /= 10; - } while (o > buf); + } while (ccp > buf); if (*buf == '0') { *buf = ' ' + (i & 16); } + fmt_to_wc(o, ccp); } } @@ -1285,6 +1337,10 @@ OUTPUT: --o_count; --count; } +#if defined __UCLIBC_HAS_WCHAR__ && (defined L_wcsftime || defined L_wcsftime_l) + if (allocno >= 0) + free((void *)alloc[allocno--]); +#endif goto LOOP; } # ifdef L_strftime_l @@ -2444,31 +2500,9 @@ DONE: #endif /**********************************************************************/ -#if defined(L_wcsftime) || defined(L_wcsftime_l) - -#if defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE) - -size_t wcsftime(wchar_t *__restrict s, size_t maxsize, - const wchar_t *__restrict format, - const struct tm *__restrict timeptr) -{ - return wcsftime_l(s, maxsize, format, timeptr, __UCLIBC_CURLOCALE); -} - -#else /* defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE) */ +#if (defined(L_wcsftime) || defined(L_wcsftime_l)) -size_t __XL_NPP(wcsftime)(wchar_t *__restrict s, size_t maxsize, - const wchar_t *__restrict format, - const struct tm *__restrict timeptr __LOCALE_PARAM ) -{ -#warning wcsftime always fails - return 0; /* always fail */ -} -#ifdef L_wcsftime_l -libc_hidden_def(wcsftime_l) -#endif - -#endif /* defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE) */ +/* Implemented via strftime / strftime_l wchar_t variants */ #endif /**********************************************************************/ diff --git a/test/time/Makefile.in b/test/time/Makefile.in index 02c8d910c..bb11e18bd 100644 --- a/test/time/Makefile.in +++ b/test/time/Makefile.in @@ -5,8 +5,13 @@ TESTS_DISABLED := bug-asctime bug-asctime_r time tst-mktime2 tst-posixtz \ tst-strftime tst-strptime tst-timezone ifneq ($(UCLIBC_HAS_XLOCALE),y) -TESTS_DISABLED += tst-ftime_l tst_wcsftime +TESTS_DISABLED += tst-ftime_l +endif + +ifneq ($(UCLIBC_HAS_WCHAR)$(UCLIBC_HAS_LOCALE),yy) +TESTS_DISABLED += tst_wcsftime endif CFLAGS_tst-strptime2 := -std=c99 DODIFF_futimens1 := 1 +DODIFF_tst_wcsftime := 1 diff --git a/test/time/tst_wcsftime.c b/test/time/tst_wcsftime.c index 6e35f1e6f..5631d952a 100644 --- a/test/time/tst_wcsftime.c +++ b/test/time/tst_wcsftime.c @@ -1,39 +1,65 @@ #include #include #include -#ifdef __UCLIBC_HAS_WCHAR__ #include +#include + +#define NUM_OF_DATES 7 +#define NUM_OF_LOCALES 3 +#define BUF_SIZE 256 int -main (int argc, char *argv[]) +main (void) { - wchar_t buf[200]; - time_t t; + wchar_t buf[BUF_SIZE]; struct tm *tp; - int result = 0; + time_t time_list[NUM_OF_DATES] = { + 500, 68200000, 694223999, + 694224000, 704900000, 705000000, + 705900000 + }; + char *locale_list[NUM_OF_LOCALES] = { + "C", + "fr_FR.ISO-8859-1", + "ja_JP.UTF-8" + }; + int result = 0, ddd, lll; size_t n; - time (&t); - tp = gmtime (&t); + for (lll = 0; lll < NUM_OF_LOCALES; lll++) { + printf ("\nUsing locale: %s\n", locale_list[lll]); + char* set = setlocale(LC_ALL, locale_list[lll]); + if (set == NULL) { + printf ("FAILED!\n\n"); + continue; + } else + printf ("\n"); + for (ddd = 0; ddd < NUM_OF_DATES; ddd++) { + tp = localtime(&time_list[ddd]); + printf ("%ld corresponds to ", time_list[ddd]); - n = wcsftime (buf, sizeof (buf) / sizeof (buf[0]), - L"%H:%M:%S %Y-%m-%d\n", tp); - if (n != 21) - result = 1; + n = wcsftime (buf, sizeof (buf) / sizeof (buf[0]), + L"%H:%M:%S %Y-%m-%d%n", tp); + if (n != 21) { + result = 1; + printf ("FAILED!\n"); + } - wprintf (L"It is now %ls", buf); + printf ("%ls", buf); - wcsftime (buf, sizeof (buf) / sizeof (buf[0]), L"%A\n", tp); + wcsftime (buf, sizeof (buf) / sizeof (buf[0]), + L"%tor, as %%D %%T: %D %T%n", tp); + printf ("%ls", buf); - wprintf (L"The weekday is %ls", buf); + wcsftime (buf, sizeof (buf) / sizeof (buf[0]), L"%A (%a)%n", tp); + printf ("The weekday was %ls", buf); + wcsftime (buf, sizeof (buf) / sizeof (buf[0]), L"%B (%b) %Y%n", tp); + /* glibc bug? forgets aigu from french february février + * See s/printf (/wprintf (L/g */ + //wprintf (L"Month was %ls", buf); + printf ("Month was %ls", buf); + } + } return result; } - -#else -int main(void) -{ - puts("Test requires WCHAR support; skipping"); - return 0; -} -#endif -- 2.11.0