X-Git-Url: http://erislabs.net/gitweb/?a=blobdiff_plain;f=lib%2Fstrftime.c;h=7d64c15264634bf6df2eea42aa0a34b3af14711d;hb=964a52336aad0cf4a4d6c08c128560c87742c921;hp=8085a59925515f3286a1ff97540157791036c9f0;hpb=eef56b544e01755a52fdfbf204e6f2d5800efd21;p=gnulib.git diff --git a/lib/strftime.c b/lib/strftime.c index 8085a5992..7d64c1526 100644 --- a/lib/strftime.c +++ b/lib/strftime.c @@ -1,73 +1,62 @@ -/* Copyright (C) 1991-1999, 2000, 2001, 2003, 2004, 2005 Free Software +/* Copyright (C) 1991-1999, 2000, 2001, 2003, 2004, 2005, 2006, 2007, 2009 Free Software Foundation, Inc. NOTE: The canonical source of this file is maintained with the GNU C Library. Bugs can be reported to bug-glibc@prep.ai.mit.edu. - This program is free software; you can redistribute it and/or modify + This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, - Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ - -#ifdef HAVE_CONFIG_H -# include -#endif + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ #ifdef _LIBC -# define HAVE_MBLEN 1 -# define HAVE_MBRLEN 1 # define HAVE_STRUCT_ERA_ENTRY 1 # define HAVE_TM_GMTOFF 1 # define HAVE_TM_ZONE 1 # define HAVE_TZNAME 1 # define HAVE_TZSET 1 -# define MULTIBYTE_IS_FORMAT_SAFE 1 # include "../locale/localeinfo.h" -#endif - -#include -#include /* Some systems define `time_t' here. */ - -#ifdef TIME_WITH_SYS_TIME -# include -# include #else -# ifdef HAVE_SYS_TIME_H -# include +# include +# if FPRINTFTIME +# include "fprintftime.h" # else -# include +# include "strftime.h" # endif +# include "ignore-value.h" #endif -#if HAVE_TZNAME + +#include +#include + +#if HAVE_TZNAME && !HAVE_DECL_TZNAME extern char *tzname[]; #endif /* Do multibyte processing if multibytes are supported, unless multibyte sequences are safe in formats. Multibyte sequences are safe if they cannot contain byte sequences that look like format - conversion specifications. The GNU C Library uses UTF8 multibyte - encoding, which is safe for formats, but strftime.c can be used - with other C libraries that use unsafe encodings. */ -#define DO_MULTIBYTE (HAVE_MBLEN && HAVE_WCHAR_H && ! MULTIBYTE_IS_FORMAT_SAFE) + conversion specifications. The multibyte encodings used by the + C library on the various platforms (UTF-8, GB2312, GBK, CP936, + GB18030, EUC-TW, BIG5, BIG5-HKSCS, CP950, EUC-JP, EUC-KR, CP949, + SHIFT_JIS, CP932, JOHAB) are safe for formats, because the byte '%' + cannot occur in a multibyte character except in the first byte. + But this does not hold for the DEC-HANYU encoding used on OSF/1. */ +#if !defined __osf__ +# define MULTIBYTE_IS_FORMAT_SAFE 1 +#endif +#define DO_MULTIBYTE (! MULTIBYTE_IS_FORMAT_SAFE) #if DO_MULTIBYTE -# if HAVE_MBRLEN -# include -# else - /* Simulate mbrlen with mblen as best we can. */ -# define mbstate_t int -# define mbrlen(s, n, ps) mblen (s, n) -# define mbsinit(ps) (*(ps) == 0) -# endif +# include static const mbstate_t mbstate_zero; #endif @@ -96,13 +85,6 @@ extern char *tzname[]; # define MEMCPY(d, s, n) memcpy (d, s, n) # define STRLEN(s) strlen (s) -# ifdef _LIBC -# define MEMPCPY(d, s, n) __mempcpy (d, s, n) -# else -# ifndef HAVE_MEMPCPY -# define MEMPCPY(d, s, n) ((void *) ((char *) memcpy (d, s, n) + (n))) -# endif -# endif #endif /* Shift A right by B bits portably, by dividing A by 2**B and @@ -120,7 +102,7 @@ extern char *tzname[]; ? (a) >> (b) \ : (a) / (1 << (b)) - ((a) % (1 << (b)) < 0)) -/* Bound on length of the string representing an integer value or type T. +/* Bound on length of the string representing an integer type or expression T. Subtract 1 for the sign bit if t is signed; log10 (2.0) < 146/485; add 1 for integer division truncation; add 1 more for a minus sign if needed. */ @@ -143,11 +125,10 @@ extern char *tzname[]; #endif #if !HAVE_TM_GMTOFF -/* Portable standalone applications should supply a "time_r.h" that +/* Portable standalone applications should supply a "time.h" that declares a POSIX-compliant localtime_r, for the benefit of older implementations that lack localtime_r or have a nonstandard one. See the gnulib time_r module for one way to implement this. */ -# include "time_r.h" # undef __gmtime_r # undef __localtime_r # define __gmtime_r gmtime_r @@ -155,7 +136,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 @@ -163,6 +161,12 @@ extern char *tzname[]; # define memset_zero(P, Len) (memset (P, '0', Len), (P) += (Len)) #endif +#if FPRINTFTIME +# define advance(P, N) +#else +# define advance(P, N) ((P) += (N)) +#endif + #define add(n, f) \ do \ { \ @@ -173,7 +177,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 +185,41 @@ extern char *tzname[]; memset_space (p, _delta); \ } \ f; \ - p += _n; \ + advance (p, _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), \ + do \ + { \ + if (to_lowcase) \ + fwrite_lowcase (p, (s), _n); \ + else if (to_uppcase) \ + fwrite_uppcase (p, (s), _n); \ + else \ + { \ + /* We are ignoring the value of fwrite here, in spite of the \ + fact that technically, that may not be valid: the fwrite \ + specification in POSIX 2008 defers to that of fputc, which \ + is intended to be consistent with the one from ISO C, \ + which permits failure due to ENOMEM *without* setting the \ + stream's error indicator. */ \ + ignore_value (fwrite ((s), _n, 1, p)); \ + } \ + } \ + while (0) \ + ) +#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 @@ -244,17 +278,12 @@ extern char *tzname[]; # define TOLOWER(Ch, L) towlower (Ch) # endif #else -# ifdef _LIBC -# ifdef USE_IN_EXTENDED_LOCALE_MODEL -# define TOUPPER(Ch, L) __toupper_l (Ch, L) -# define TOLOWER(Ch, L) __tolower_l (Ch, L) -# else -# define TOUPPER(Ch, L) toupper (Ch) -# define TOLOWER(Ch, L) tolower (Ch) -# endif +# ifdef USE_IN_EXTENDED_LOCALE_MODEL +# define TOUPPER(Ch, L) __toupper_l (Ch, L) +# define TOLOWER(Ch, L) __tolower_l (Ch, L) # else -# define TOUPPER(Ch, L) (islower (Ch) ? toupper (Ch) : (Ch)) -# define TOLOWER(Ch, L) (isupper (Ch) ? tolower (Ch) : (Ch)) +# define TOUPPER(Ch, L) toupper (Ch) +# define TOLOWER(Ch, L) tolower (Ch) # endif #endif /* We don't use `isdigit' here since the locale dependent @@ -263,6 +292,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 +330,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 @@ -331,21 +382,6 @@ iso_week_days (int yday, int wday) } -#if !(defined _NL_CURRENT || HAVE_STRFTIME) -static CHAR_T const weekday_name[][10] = - { - L_("Sunday"), L_("Monday"), L_("Tuesday"), L_("Wednesday"), - L_("Thursday"), L_("Friday"), L_("Saturday") - }; -static CHAR_T const month_name[][10] = - { - L_("January"), L_("February"), L_("March"), L_("April"), L_("May"), - L_("June"), L_("July"), L_("August"), L_("September"), L_("October"), - L_("November"), L_("December") - }; -#endif - - /* When compiling this file, GNU applications can #define my_strftime to a symbol (typically nstrftime) to get an extended strftime with extra arguments UT and NS. Emacs is a special case for now, but @@ -355,11 +391,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 @@ -373,27 +414,21 @@ static CHAR_T const month_name[][10] = # define ns 0 #endif -#if ! defined _LIBC && ! HAVE_RUN_TZSET_TEST -/* Solaris 2.5.x and 2.6 tzset sometimes modify the storage returned - by localtime. On such systems, we must use the tzset and localtime - wrappers to work around the bug. */ -"you must run the autoconf test for a working tzset function" -#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) UINT_MAX 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 @@ -418,27 +453,24 @@ my_strftime (CHAR_T *s, size_t maxsize, const CHAR_T *format, # define aw_len STRLEN (a_wkday) # define am_len STRLEN (a_month) # define ap_len STRLEN (ampm) -#else -# if !HAVE_STRFTIME -# define f_wkday (weekday_name[tp->tm_wday]) -# define f_month (month_name[tp->tm_mon]) -# define a_wkday f_wkday -# define a_month f_month -# define ampm (L_("AMPM") + 2 * (tp->tm_hour > 11)) - - size_t aw_len = 3; - size_t am_len = 3; - size_t ap_len = 2; -# endif #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; #endif +#if ! defined _LIBC && ! HAVE_RUN_TZSET_TEST + /* Solaris 2.5.x and 2.6 tzset sometimes modify the storage returned + by localtime. On such systems, we must either use the tzset and + localtime wrappers to work around the bug (which sets + HAVE_RUN_TZSET_TEST) or make a copy of the structure. */ + struct tm copy = *tp; + tp = © +#endif + zone = NULL; #if HAVE_TM_ZONE /* The POSIX test suite assumes that setting @@ -475,18 +507,24 @@ 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; /* 1 if the number is negative. */ + bool negative_number; /* The number is negative. */ + bool always_output_a_sign; /* +/- should always be output. */ + int tz_colon_mask; /* Bitmask of where ':' should appear. */ 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; @@ -521,7 +559,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: @@ -572,7 +610,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; } @@ -648,6 +686,15 @@ 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; \ + tz_colon_mask = mask; \ + u_number_value = v; goto do_tz_offset #define DO_NUMBER_SPACEPAD(d, v) \ digits = d; \ number_value = v; goto do_number_spacepad @@ -655,7 +702,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'): @@ -666,7 +713,7 @@ my_strftime (CHAR_T *s, size_t maxsize, const CHAR_T *format, to_uppcase = true; to_lowcase = false; } -#if defined _NL_CURRENT || !HAVE_STRFTIME +#ifdef _NL_CURRENT cpy (aw_len, a_wkday); break; #else @@ -681,7 +728,7 @@ my_strftime (CHAR_T *s, size_t maxsize, const CHAR_T *format, to_uppcase = true; to_lowcase = false; } -#if defined _NL_CURRENT || !HAVE_STRFTIME +#ifdef _NL_CURRENT cpy (STRLEN (f_wkday), f_wkday); break; #else @@ -697,7 +744,7 @@ my_strftime (CHAR_T *s, size_t maxsize, const CHAR_T *format, } if (modifier != 0) goto bad_format; -#if defined _NL_CURRENT || !HAVE_STRFTIME +#ifdef _NL_CURRENT cpy (am_len, a_month); break; #else @@ -712,7 +759,7 @@ my_strftime (CHAR_T *s, size_t maxsize, const CHAR_T *format, to_uppcase = true; to_lowcase = false; } -#if defined _NL_CURRENT || !HAVE_STRFTIME +#ifdef _NL_CURRENT cpy (STRLEN (f_month), f_month); break; #else @@ -730,36 +777,28 @@ my_strftime (CHAR_T *s, size_t maxsize, const CHAR_T *format, != '\0'))) subfmt = (const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(D_T_FMT)); #else -# if HAVE_STRFTIME goto underlying_strftime; -# else - subfmt = L_("%a %b %e %H:%M:%S %Y"); -# endif #endif 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; -#if HAVE_STRFTIME && ! (defined _NL_CURRENT && HAVE_STRUCT_ERA_ENTRY) +#if !(defined _NL_CURRENT && HAVE_STRUCT_ERA_ENTRY) underlying_strftime: { /* The relevant information is available only via the underlying strftime implementation, so use that. */ - char ufmt[4]; + char ufmt[5]; char *u = ufmt; char ubuf[1024]; /* enough for any single format in practice */ size_t len; @@ -771,15 +810,17 @@ my_strftime (CHAR_T *s, size_t maxsize, const CHAR_T *format, size_t strftime (); # endif + /* The space helps distinguish strftime failure from empty + output. */ + *u++ = ' '; *u++ = '%'; if (modifier != 0) *u++ = modifier; *u++ = format_char; *u = '\0'; len = strftime (ubuf, sizeof ubuf, ufmt, tp); - if (len == 0 && ubuf[0] != '\0') - return 0; - cpy (len, ubuf); + if (len != 0) + cpy (len - 1, ubuf + 1); } break; #endif @@ -803,9 +844,7 @@ my_strftime (CHAR_T *s, size_t maxsize, const CHAR_T *format, break; } #else -# if HAVE_STRFTIME goto underlying_strftime; -# endif #endif } @@ -826,11 +865,7 @@ my_strftime (CHAR_T *s, size_t maxsize, const CHAR_T *format, subfmt = (const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(D_FMT)); goto subformat; #else -# if HAVE_STRFTIME goto underlying_strftime; -# else - /* Fall through. */ -# endif #endif case L_('D'): if (modifier != 0) @@ -851,7 +886,11 @@ 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; + goto do_number_body; do_number_spacepad: /* Force `_' flag unless overridden by `0' or `-' flag. */ @@ -864,6 +903,10 @@ my_strftime (CHAR_T *s, size_t maxsize, const CHAR_T *format, u_number_value = number_value; do_signed_number: + always_output_a_sign = false; + tz_colon_mask = 0; + + do_number_body: /* Format U_NUMBER_VALUE according to the MODIFIER flag. NEGATIVE_NUMBER is nonzero if the original number was negative; in this case it was converted directly to @@ -887,9 +930,7 @@ my_strftime (CHAR_T *s, size_t maxsize, const CHAR_T *format, } } #else -# if HAVE_STRFTIME goto underlying_strftime; -# endif #endif } @@ -900,22 +941,31 @@ my_strftime (CHAR_T *s, size_t maxsize, const CHAR_T *format, do { + if (tz_colon_mask & 1) + *--bufp = ':'; + tz_colon_mask >>= 1; *--bufp = u_number_value % 10 + L_('0'); u_number_value /= 10; } - while (u_number_value != 0); + while (u_number_value != 0 || tz_colon_mask != 0); + do_number_sign_and_padding: if (digits < width) digits = width; - do_number_sign_and_padding: - if (negative_number) - *--bufp = L_('-'); + sign_char = (negative_number ? L_('-') + : always_output_a_sign ? L_('+') + : 0); - 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) { @@ -928,20 +978,16 @@ 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 { if ((size_t) digits >= maxsize - i) return 0; - if (negative_number) - { - ++bufp; - - if (p) - *p++ = L_('-'); - ++i; - } + if (sign_char) + add1 (sign_char); if (p) memset_zero (p, padding); @@ -949,6 +995,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); @@ -1008,7 +1059,9 @@ my_strftime (CHAR_T *s, size_t maxsize, const CHAR_T *format, goto bad_format; number_value = ns; - if (width != -1) + if (width == -1) + width = 9; + else { /* Take an explicit width less than 9 as a precision. */ int j; @@ -1016,16 +1069,16 @@ my_strftime (CHAR_T *s, size_t maxsize, const CHAR_T *format, number_value /= 10; } - DO_NUMBER (9, number_value); + DO_NUMBER (width, number_value); #endif case L_('n'): - add (1, *p = L_('\n')); + add1 (L_('\n')); break; case L_('P'): to_lowcase = true; -#if !defined _NL_CURRENT && HAVE_STRFTIME +#ifndef _NL_CURRENT format_char = L_('p'); #endif /* FALLTHROUGH */ @@ -1036,7 +1089,7 @@ my_strftime (CHAR_T *s, size_t maxsize, const CHAR_T *format, to_uppcase = false; to_lowcase = true; } -#if defined _NL_CURRENT || !HAVE_STRFTIME +#ifdef _NL_CURRENT cpy (ap_len, ampm); break; #else @@ -1048,16 +1101,14 @@ my_strftime (CHAR_T *s, size_t maxsize, const CHAR_T *format, goto subformat; case L_('r'): -#if !defined _NL_CURRENT && HAVE_STRFTIME - goto underlying_strftime; -#else -# ifdef _NL_CURRENT +#ifdef _NL_CURRENT if (*(subfmt = (const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(T_FMT_AMPM))) == L_('\0')) -# endif subfmt = L_("%I:%M:%S %p"); goto subformat; +#else + goto underlying_strftime; #endif case L_('S'): @@ -1089,6 +1140,7 @@ my_strftime (CHAR_T *s, size_t maxsize, const CHAR_T *format, while (t != 0); digits = 1; + always_output_a_sign = false; goto do_number_sign_and_padding; } @@ -1103,18 +1155,14 @@ my_strftime (CHAR_T *s, size_t maxsize, const CHAR_T *format, subfmt = (const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(T_FMT)); goto subformat; #else -# if HAVE_STRFTIME goto underlying_strftime; -# else - /* Fall through. */ -# endif #endif case L_('T'): subfmt = L_("%H:%M:%S"); goto subformat; case L_('t'): - add (1, *p = L_('\t')); + add1 (L_('\t')); break; case L_('u'): @@ -1211,9 +1259,7 @@ my_strftime (CHAR_T *s, size_t maxsize, const CHAR_T *format, goto subformat; } #else -# if HAVE_STRFTIME goto underlying_strftime; -# endif #endif } if (modifier == L_('O')) @@ -1234,9 +1280,7 @@ my_strftime (CHAR_T *s, size_t maxsize, const CHAR_T *format, + delta * era->absolute_direction)); } #else -# if HAVE_STRFTIME goto underlying_strftime; -# endif #endif } @@ -1276,12 +1320,28 @@ my_strftime (CHAR_T *s, size_t maxsize, const CHAR_T *format, #endif break; + case L_(':'): + /* :, ::, and ::: are valid only just before 'z'. + :::: etc. are rejected later. */ + for (colons = 1; f[colons] == L_(':'); colons++) + continue; + if (f[colons] != L_('z')) + goto bad_format; + f += colons; + goto do_z_conversion; + case L_('z'): + colons = 0; + + do_z_conversion: if (tp->tm_isdst < 0) break; { int diff; + int hour_diff; + int min_diff; + int sec_diff; #if HAVE_TM_GMTOFF diff = tp->tm_gmtoff; #else @@ -1320,16 +1380,32 @@ my_strftime (CHAR_T *s, size_t maxsize, const CHAR_T *format, } #endif - if (diff < 0) + hour_diff = diff / 60 / 60; + min_diff = diff / 60 % 60; + sec_diff = diff % 60; + + switch (colons) { - add (1, *p = L_('-')); - diff = -diff; - } - else - add (1, *p = L_('+')); + case 0: /* +hhmm */ + DO_TZ_OFFSET (5, diff < 0, 0, hour_diff * 100 + min_diff); + + case 1: tz_hh_mm: /* +hh:mm */ + DO_TZ_OFFSET (6, diff < 0, 04, hour_diff * 100 + min_diff); + + case 2: tz_hh_mm_ss: /* +hh:mm:ss */ + DO_TZ_OFFSET (9, diff < 0, 024, + hour_diff * 10000 + min_diff * 100 + sec_diff); - diff /= 60; - DO_NUMBER (4, (diff / 60) * 100 + diff % 60); + case 3: /* +hh if possible, else +hh:mm, else +hh:mm:ss */ + if (sec_diff != 0) + goto tz_hh_mm_ss; + if (min_diff != 0) + goto tz_hh_mm; + DO_TZ_OFFSET (3, diff < 0, 0, hour_diff); + + default: + goto bad_format; + } } case L_('\0'): /* GNU extension: % at end of format. */ @@ -1350,16 +1426,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