diff options
-rw-r--r-- | lib/strftime.c | 190 |
1 files changed, 138 insertions, 52 deletions
diff --git a/lib/strftime.c b/lib/strftime.c index 94eb4ab0f..d740ae018 100644 --- a/lib/strftime.c +++ b/lib/strftime.c @@ -155,7 +155,24 @@ extern char *tzname[]; #endif -#ifdef COMPILE_WIDE +#ifndef FPRINTFTIME +# define FPRINTFTIME 0 +#endif + +#if FPRINTFTIME +# define STREAM_OR_CHAR_T FILE +# define STRFTIME_ARG(x) /* empty */ +#else +# define STREAM_OR_CHAR_T CHAR_T +# define STRFTIME_ARG(x) x, +#endif + +#if FPRINTFTIME +# define memset_byte(P, Len, Byte) \ + do { size_t _i; for (_i = 0; _i < Len; _i++) fputc (Byte, P); } while (0) +# define memset_space(P, Len) memset_byte (P, Len, ' ') +# define memset_zero(P, Len) memset_byte (P, Len, '0') +#elif defined COMPILE_WIDE # define memset_space(P, Len) (wmemset (P, L' ', Len), (P) += (Len)) # define memset_zero(P, Len) (wmemset (P, L'0', Len), (P) += (Len)) #else @@ -173,7 +190,7 @@ extern char *tzname[]; return 0; \ if (p) \ { \ - if (_delta > 0) \ + if (digits == 0 && _delta > 0) \ { \ if (pad == L_('0')) \ memset_zero (p, _delta); \ @@ -181,12 +198,28 @@ extern char *tzname[]; memset_space (p, _delta); \ } \ f; \ - p += _n; \ + p += FPRINTFTIME ? 0 : _n; \ } \ i += _incr; \ } while (0) -#define cpy(n, s) \ +#if FPRINTFTIME +# define add1(C) add (1, fputc (C, p)) +#else +# define add1(C) add (1, *p = C) +#endif + +#if FPRINTFTIME +# define cpy(n, s) \ + add ((n), \ + if (to_lowcase) \ + fwrite_lowcase (p, (s), _n); \ + else if (to_uppcase) \ + fwrite_uppcase (p, (s), _n); \ + else \ + fwrite ((s), _n, 1, p)) +#else +# define cpy(n, s) \ add ((n), \ if (to_lowcase) \ memcpy_lowcase (p, (s), _n LOCALE_ARG); \ @@ -194,6 +227,7 @@ extern char *tzname[]; memcpy_uppcase (p, (s), _n LOCALE_ARG); \ else \ MEMCPY ((void *) p, (void const *) (s), _n)) +#endif #ifdef COMPILE_WIDE # ifndef USE_IN_EXTENDED_LOCALE_MODEL @@ -263,6 +297,27 @@ extern char *tzname[]; more reliable way to accept other sets of digits. */ #define ISDIGIT(Ch) ((unsigned int) (Ch) - L_('0') <= 9) +#if FPRINTFTIME +static void +fwrite_lowcase (FILE *fp, const CHAR_T *src, size_t len) +{ + while (len-- > 0) + { + fputc (TOLOWER ((UCHAR_T) *src, loc), fp); + ++src; + } +} + +static void +fwrite_uppcase (FILE *fp, const CHAR_T *src, size_t len) +{ + while (len-- > 0) + { + fputc (TOUPPER ((UCHAR_T) *src, loc), fp); + ++src; + } +} +#else static CHAR_T * memcpy_lowcase (CHAR_T *dest, const CHAR_T *src, size_t len LOCALE_PARAM_PROTO) @@ -280,6 +335,7 @@ memcpy_uppcase (CHAR_T *dest, const CHAR_T *src, dest[len] = TOUPPER ((UCHAR_T) src[len], loc); return dest; } +#endif #if ! HAVE_TM_GMTOFF @@ -355,11 +411,16 @@ static CHAR_T const month_name[][10] = # define my_strftime nstrftime #endif +#if FPRINTFTIME +# undef my_strftime +# define my_strftime fprintftime +#endif + #ifdef my_strftime # define extra_args , ut, ns # define extra_args_spec , int ut, int ns #else -# ifdef COMPILE_WIDE +# if defined COMPILE_WIDE # define my_strftime wcsftime # define nl_get_alt_digit _nl_get_walt_digit # else @@ -374,19 +435,20 @@ static CHAR_T const month_name[][10] = #endif -/* Write information from TP into S according to the format - string FORMAT, writing no more that MAXSIZE characters - (including the terminating '\0') and returning number of - characters written. If S is NULL, nothing will be written - anywhere, so to determine how many characters would be - written, use NULL for S and (size_t) -1 for MAXSIZE. */ -size_t -my_strftime (CHAR_T *s, size_t maxsize, const CHAR_T *format, - const struct tm *tp extra_args_spec LOCALE_PARAM_PROTO) +/* Just like my_strftime, below, but with one more parameter, UPCASE, + to indicate that the result should be converted to upper case. */ +static size_t +strftime_case_ (bool upcase, STREAM_OR_CHAR_T *s, + STRFTIME_ARG (size_t maxsize) + const CHAR_T *format, + const struct tm *tp extra_args_spec LOCALE_PARAM_PROTO) { #if defined _LIBC && defined USE_IN_EXTENDED_LOCALE_MODEL struct locale_data *const current = loc->__locales[LC_TIME]; #endif +#if FPRINTFTIME + size_t maxsize = (size_t) -1; +#endif int hour12 = tp->tm_hour; #ifdef _NL_CURRENT @@ -426,7 +488,7 @@ my_strftime (CHAR_T *s, size_t maxsize, const CHAR_T *format, #endif const char *zone; size_t i = 0; - CHAR_T *p = s; + STREAM_OR_CHAR_T *p = s; const CHAR_T *f; #if DO_MULTIBYTE && !defined COMPILE_WIDE const char *format_end = NULL; @@ -477,7 +539,7 @@ my_strftime (CHAR_T *s, size_t maxsize, const CHAR_T *format, { int pad = 0; /* Padding for number ('-', '_', or 0). */ int modifier; /* Field modifier ('E', 'O', or 0). */ - int digits; /* Max digits for numeric format. */ + int digits = 0; /* Max digits for numeric format. */ int number_value; /* Numeric value to be printed. */ unsigned int u_number_value; /* (unsigned int) number_value. */ bool negative_number; /* The number is negative. */ @@ -486,12 +548,14 @@ my_strftime (CHAR_T *s, size_t maxsize, const CHAR_T *format, const CHAR_T *subfmt; CHAR_T sign_char; CHAR_T *bufp; - CHAR_T buf[1 + (sizeof (int) < sizeof (time_t) - ? INT_STRLEN_BOUND (time_t) - : INT_STRLEN_BOUND (int))]; + CHAR_T buf[1 + + 2 /* for the two colons in a %::z or %:::z time zone */ + + (sizeof (int) < sizeof (time_t) + ? INT_STRLEN_BOUND (time_t) + : INT_STRLEN_BOUND (int))]; int width = -1; bool to_lowcase = false; - bool to_uppcase = false; + bool to_uppcase = upcase; size_t colons; bool change_case = false; int format_char; @@ -527,7 +591,7 @@ my_strftime (CHAR_T *s, size_t maxsize, const CHAR_T *format, be in the basic execution character set. None of these characters can start a multibyte sequence, so they need not be analyzed further. */ - add (1, *p = *f); + add1 (*f); continue; default: @@ -578,7 +642,7 @@ my_strftime (CHAR_T *s, size_t maxsize, const CHAR_T *format, or this is the wide character version. */ if (*f != L_('%')) { - add (1, *p = *f); + add1 (*f); continue; } @@ -654,6 +718,10 @@ my_strftime (CHAR_T *s, size_t maxsize, const CHAR_T *format, digits = d; \ negative_number = negative; \ u_number_value = v; goto do_signed_number + + /* The mask is not what you might think. + When the ordinal i'th bit is set, insert a colon + before the i'th digit of the time zone representation. */ #define DO_TZ_OFFSET(d, negative, mask, v) \ digits = d; \ negative_number = negative; \ @@ -666,7 +734,7 @@ my_strftime (CHAR_T *s, size_t maxsize, const CHAR_T *format, case L_('%'): if (modifier != 0) goto bad_format; - add (1, *p = *f); + add1 (*f); break; case L_('a'): @@ -750,18 +818,14 @@ my_strftime (CHAR_T *s, size_t maxsize, const CHAR_T *format, subformat: { - CHAR_T *old_start = p; - size_t len = my_strftime (NULL, (size_t) -1, subfmt, - tp extra_args LOCALE_ARG); - add (len, my_strftime (p, maxsize - i, subfmt, - tp extra_args LOCALE_ARG)); - - if (to_uppcase) - while (old_start < p) - { - *old_start = TOUPPER ((UCHAR_T) *old_start, loc); - ++old_start; - } + size_t len = strftime_case_ (to_uppcase, + NULL, STRFTIME_ARG ((size_t) -1) + subfmt, + tp extra_args LOCALE_ARG); + add (len, strftime_case_ (to_uppcase, p, + STRFTIME_ARG (maxsize - i) + subfmt, + tp extra_args LOCALE_ARG)); } break; @@ -864,7 +928,7 @@ my_strftime (CHAR_T *s, size_t maxsize, const CHAR_T *format, DO_NUMBER_SPACEPAD (2, tp->tm_mday); /* All numeric formats set DIGITS and NUMBER_VALUE (or U_NUMBER_VALUE) - and then jump to one of these three labels. */ + and then jump to one of these labels. */ do_tz_offset: always_output_a_sign = true; @@ -937,13 +1001,15 @@ my_strftime (CHAR_T *s, size_t maxsize, const CHAR_T *format, : always_output_a_sign ? L_('+') : 0); - if (sign_char) - *--bufp = sign_char; - - if (pad != L_('-')) + if (pad == L_('-')) + { + if (sign_char) + add1 (sign_char); + } + else { int padding = digits - (buf + (sizeof (buf) / sizeof (buf[0])) - - bufp); + - bufp) - !!sign_char; if (padding > 0) { @@ -956,6 +1022,8 @@ my_strftime (CHAR_T *s, size_t maxsize, const CHAR_T *format, memset_space (p, padding); i += padding; width = width > padding ? width - padding : 0; + if (sign_char) + add1 (sign_char); } else { @@ -963,13 +1031,7 @@ my_strftime (CHAR_T *s, size_t maxsize, const CHAR_T *format, return 0; if (sign_char) - { - ++bufp; - - if (p) - *p++ = sign_char; - ++i; - } + add1 (sign_char); if (p) memset_zero (p, padding); @@ -977,6 +1039,11 @@ my_strftime (CHAR_T *s, size_t maxsize, const CHAR_T *format, width = 0; } } + else + { + if (sign_char) + add1 (sign_char); + } } cpy (buf + sizeof (buf) / sizeof (buf[0]) - bufp, bufp); @@ -1050,7 +1117,7 @@ my_strftime (CHAR_T *s, size_t maxsize, const CHAR_T *format, #endif case L_('n'): - add (1, *p = L_('\n')); + add1 (L_('\n')); break; case L_('P'): @@ -1145,7 +1212,7 @@ my_strftime (CHAR_T *s, size_t maxsize, const CHAR_T *format, goto subformat; case L_('t'): - add (1, *p = L_('\t')); + add1 (L_('\t')); break; case L_('u'): @@ -1413,16 +1480,35 @@ my_strftime (CHAR_T *s, size_t maxsize, const CHAR_T *format, } } +#if ! FPRINTFTIME if (p && maxsize != 0) *p = L_('\0'); +#endif + return i; } -#ifdef _LIBC + +/* Write information from TP into S according to the format + string FORMAT, writing no more that MAXSIZE characters + (including the terminating '\0') and returning number of + characters written. If S is NULL, nothing will be written + anywhere, so to determine how many characters would be + written, use NULL for S and (size_t) -1 for MAXSIZE. */ +size_t +my_strftime (STREAM_OR_CHAR_T *s, STRFTIME_ARG (size_t maxsize) + const CHAR_T *format, + const struct tm *tp extra_args_spec LOCALE_PARAM_PROTO) +{ + return strftime_case_ (false, s, STRFTIME_ARG (maxsize) + format, tp extra_args LOCALE_ARG); +} + +#if defined _LIBC && ! FPRINTFTIME libc_hidden_def (my_strftime) #endif -#ifdef emacs +#if defined emacs && ! FPRINTFTIME /* For Emacs we have a separate interface which corresponds to the normal strftime function plus the ut argument, but without the ns argument. */ size_t |