OSDN Git Service

locale: Add wcsftime()
[uclinux-h8/uClibc.git] / libc / misc / time / time.c
index 347c899..a3fccb2 100644 (file)
 #include <bits/uClibc_uintmaxtostr.h>
 #include <bits/uClibc_mutex.h>
 
-#ifdef __UCLIBC_HAS_WCHAR__
+#if defined __UCLIBC_HAS_WCHAR__ && (defined L_wcsftime || defined L_wcsftime_l)
 #include <wchar.h>
+# 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
 /**********************************************************************/