diff options
author | Corinna Vinschen <corinna@vinschen.de> | 2010-02-26 09:41:44 +0000 |
---|---|---|
committer | Corinna Vinschen <corinna@vinschen.de> | 2010-02-26 09:41:44 +0000 |
commit | be7f7a7503ef2dc45243bac4843ead3a599e9674 (patch) | |
tree | b109420fff9e3d64e801bc60c01f8529444111ad | |
parent | d91ab868e147a93e28a30a08beb9275ae7e1d37b (diff) | |
download | cygnal-be7f7a7503ef2dc45243bac4843ead3a599e9674.tar.gz cygnal-be7f7a7503ef2dc45243bac4843ead3a599e9674.tar.bz2 cygnal-be7f7a7503ef2dc45243bac4843ead3a599e9674.zip |
* libc/time/strftime.c: Add support for era and alt_digits data from
LC_TIME locale category. Conditionalize using _WANT_C99_TIME_FORMATS
flag.
(STRTOUL): Define differently for building strftime or wcsftime.
(STRCPY): Ditto.
(STRCHR): Ditto.
(STRLEN): Ditto.
(CHECK_LENGTH): Define to simplify code.
(era_info_t): New type to store era info.
(get_era_info): New function to fetch era info matching incoming
struct tm.
(free_era_info): New function to free era info.
(alt_digits_t): New type to store alternative digits.
(get_alt_digits): New function to convert alt_digits string into
alt_digits_t structure.
(free_alt_digits): New function to free alt_digits info.
(conv_to_alt_digits): New function to convert unsigned value into
alternative digits.
(strftime): Conditionalize on _WANT_C99_TIME_FORMATS. If
_WANT_C99_TIME_FORMATS is defined, define as just a wrapper function
providing era_info and alt_digits pointers and call ...
(__strftime): Rename from strftime and make static if
_WANT_C99_TIME_FORMATS is defined. Add parameters for era_info and
alt_digits pointers. Handle conversion modifiers according to
POSIX-1.2008. Redefine %F and %Y according to POSIX. Add default case
to allow to bail out on invalid conversion specifiers.
* libc/include/sys/config.h: Move Cygwin build flags to Cygwin's
config.h.
* libc/include/stdio.h: Remove __CYGWIN_USE_BIG_TYPES__ condition.
-rw-r--r-- | newlib/ChangeLog | 33 | ||||
-rw-r--r-- | newlib/libc/include/stdio.h | 4 | ||||
-rw-r--r-- | newlib/libc/include/sys/config.h | 3 | ||||
-rw-r--r-- | newlib/libc/time/strftime.c | 707 |
4 files changed, 644 insertions, 103 deletions
diff --git a/newlib/ChangeLog b/newlib/ChangeLog index 486da7e4b..0be03a0de 100644 --- a/newlib/ChangeLog +++ b/newlib/ChangeLog @@ -1,3 +1,36 @@ +2010-02-26 Corinna Vinschen <corinna@vinschen.de> + + * libc/time/strftime.c: Add support for era and alt_digits data from + LC_TIME locale category. Conditionalize using _WANT_C99_TIME_FORMATS + flag. + (STRTOUL): Define differently for building strftime or wcsftime. + (STRCPY): Ditto. + (STRCHR): Ditto. + (STRLEN): Ditto. + (CHECK_LENGTH): Define to simplify code. + (era_info_t): New type to store era info. + (get_era_info): New function to fetch era info matching incoming + struct tm. + (free_era_info): New function to free era info. + (alt_digits_t): New type to store alternative digits. + (get_alt_digits): New function to convert alt_digits string into + alt_digits_t structure. + (free_alt_digits): New function to free alt_digits info. + (conv_to_alt_digits): New function to convert unsigned value into + alternative digits. + (strftime): Conditionalize on _WANT_C99_TIME_FORMATS. If + _WANT_C99_TIME_FORMATS is defined, define as just a wrapper function + providing era_info and alt_digits pointers and call ... + (__strftime): Rename from strftime and make static if + _WANT_C99_TIME_FORMATS is defined. Add parameters for era_info and + alt_digits pointers. Handle conversion modifiers according to + POSIX-1.2008. Redefine %F and %Y according to POSIX. Add default case + to allow to bail out on invalid conversion specifiers. + * libc/include/sys/config.h: Move Cygwin build flags to Cygwin's + config.h. + + * libc/include/stdio.h: Remove __CYGWIN_USE_BIG_TYPES__ condition. + 2010-02-25 Corinna Vinschen <corinna@vinschen.de> * libc/locale/locale.c (loadlocale): Fix typo in comment. diff --git a/newlib/libc/include/stdio.h b/newlib/libc/include/stdio.h index 8389449c2..2912eafa9 100644 --- a/newlib/libc/include/stdio.h +++ b/newlib/libc/include/stdio.h @@ -50,13 +50,9 @@ _BEGIN_STD_C typedef __FILE FILE; #ifdef __CYGWIN__ -#ifdef __CYGWIN_USE_BIG_TYPES__ typedef _fpos64_t fpos_t; #else typedef _fpos_t fpos_t; -#endif -#else -typedef _fpos_t fpos_t; #ifdef __LARGE64_FILES typedef _fpos64_t fpos64_t; #endif diff --git a/newlib/libc/include/sys/config.h b/newlib/libc/include/sys/config.h index 64a3fe638..49a3d8ac7 100644 --- a/newlib/libc/include/sys/config.h +++ b/newlib/libc/include/sys/config.h @@ -188,9 +188,6 @@ #if defined(__CYGWIN__) #include <cygwin/config.h> -#define __LINUX_ERRNO_EXTENSIONS__ 1 -#define _MB_EXTENDED_CHARSETS_ALL 1 -#define __HAVE_LOCALE_INFO__ 1 #if !defined (__STRICT_ANSI__) || (__STDC_VERSION__ >= 199901L) #define __USE_XOPEN2K 1 #endif diff --git a/newlib/libc/time/strftime.c b/newlib/libc/time/strftime.c index 95fb3019c..1df353abf 100644 --- a/newlib/libc/time/strftime.c +++ b/newlib/libc/time/strftime.c @@ -284,6 +284,10 @@ the "C" locale settings. # define SFLG /* %s flag (null for normal char) */ # define _ctloc(x) (ctloclen = strlen (ctloc = _CurrentTimeLocale->x), ctloc) # define TOLOWER(c) tolower((int)(unsigned char)(c)) +# define STRTOUL(c,p,b) strtoul((c),(p),(b)) +# define STRCPY(a,b) strcpy((a),(b)) +# define STRCHR(a,b) strchr((a),(b)) +# define STRLEN(a) strlen(a) # else # define strftime wcsftime /* Alternate function name */ # define CHAR wchar_t /* string type basis */ @@ -291,6 +295,10 @@ the "C" locale settings. # define snprintf swprintf /* wide-char equivalent function name */ # define strncmp wcsncmp /* wide-char equivalent function name */ # define TOLOWER(c) towlower((wint_t)(c)) +# define STRTOUL(c,p,b) wcstoul((c),(p),(b)) +# define STRCPY(a,b) wcscpy((a),(b)) +# define STRCHR(a,b) wcschr((a),(b)) +# define STRLEN(a) wcslen(a) # define SFLG "l" /* %s flag (l for wide char) */ # define CTLOCBUFLEN 256 /* Arbitrary big buffer size */ const wchar_t * @@ -306,6 +314,9 @@ the "C" locale settings. &ctloclen)) #endif /* MAKE_WCSFTIME */ +#define CHECK_LENGTH() if (len < 0 || (count += len) >= maxsize) \ + return 0 + /* Enforce the coding assumptions that YEAR_BASE is positive. (%C, %Y, etc.) */ #if YEAR_BASE < 0 # error "YEAR_BASE < 0" @@ -361,12 +372,288 @@ _DEFUN (iso_year_adjust, (tim_p), #undef PACK } +#ifdef _WANT_C99_TIME_FORMATS +typedef struct { + int year; + CHAR *era_C; + CHAR *era_Y; +} era_info_t; + +static era_info_t * +get_era_info (const struct tm *tim_p, const char *era) +{ + char *c; + const char *dir; + long offset; + struct tm stm, etm; + era_info_t *ei; + + ei = (era_info_t *) calloc (1, sizeof (era_info_t)); + if (!ei) + return NULL; + + stm.tm_isdst = etm.tm_isdst = 0; + while (era) + { + dir = era; + era += 2; + offset = strtol (era, &c, 10); + era = c + 1; + stm.tm_year = strtol (era, &c, 10) - YEAR_BASE; + /* Adjust offset for negative gregorian dates. */ + if (stm.tm_year <= -YEAR_BASE) + ++stm.tm_year; + stm.tm_mon = strtol (c + 1, &c, 10); + stm.tm_mday = strtol (c + 1, &c, 10); + stm.tm_hour = stm.tm_min = stm.tm_sec = 0; + era = c + 1; + if (era[0] == '-' && era[1] == '*') + { + etm = stm; + stm.tm_year = INT_MIN; + stm.tm_mon = stm.tm_mday = stm.tm_hour = stm.tm_min = stm.tm_sec = 0; + era += 3; + } + else if (era[0] == '+' && era[1] == '*') + { + etm.tm_year = INT_MAX; + etm.tm_mon = 12; + etm.tm_mday = 31; + etm.tm_hour = 23; + etm.tm_min = etm.tm_sec = 59; + era += 3; + } + else + { + etm.tm_year = strtol (era, &c, 10) - YEAR_BASE; + /* Adjust offset for negative gregorian dates. */ + if (etm.tm_year <= -YEAR_BASE) + ++etm.tm_year; + etm.tm_mon = strtol (c + 1, &c, 10); + etm.tm_mday = strtol (c + 1, &c, 10); + etm.tm_mday = 31; + etm.tm_hour = 23; + etm.tm_min = etm.tm_sec = 59; + era = c + 1; + } + if ((tim_p->tm_year > stm.tm_year + || (tim_p->tm_year == stm.tm_year + && (tim_p->tm_mon > stm.tm_mon + || (tim_p->tm_mon == stm.tm_mon + && tim_p->tm_mday >= stm.tm_mday)))) + && (tim_p->tm_year < etm.tm_year + || (tim_p->tm_year == etm.tm_year + && (tim_p->tm_mon < etm.tm_mon + || (tim_p->tm_mon == etm.tm_mon + && tim_p->tm_mday <= etm.tm_mday))))) + { + /* Gotcha */ + size_t len; + + /* year */ + if (*dir == '+' && stm.tm_year != INT_MIN) + ei->year = tim_p->tm_year - stm.tm_year + offset; + else + ei->year = etm.tm_year - tim_p->tm_year + offset; + /* era_C */ + c = strchr (era, ':'); +#ifdef MAKE_WCSFTIME + len = mbsnrtowcs (NULL, &era, c - era, 0, NULL); + if (len == (size_t) -1) + { + free (ei); + return NULL; + } +#else + len = c - era; +#endif + ei->era_C = (CHAR *) malloc ((len + 1) * sizeof (CHAR)); + if (!ei->era_C) + { + free (ei); + return NULL; + } +#ifdef MAKE_WCSFTIME + len = mbsnrtowcs (ei->era_C, &era, c - era, len + 1, NULL); +#else + strncpy (ei->era_C, era, len); + era += len; +#endif + ei->era_C[len] = CQ('\0'); + /* era_Y */ + ++era; + c = strchr (era, ';'); + if (!c) + c = strchr (era, '\0'); +#ifdef MAKE_WCSFTIME + len = mbsnrtowcs (NULL, &era, c - era, 0, NULL); + if (len == (size_t) -1) + { + free (ei->era_C); + free (ei); + return NULL; + } +#else + len = c - era; +#endif + ei->era_Y = (CHAR *) malloc ((len + 1) * sizeof (CHAR)); + if (!ei->era_Y) + { + free (ei->era_C); + free (ei); + return NULL; + } +#ifdef MAKE_WCSFTIME + len = mbsnrtowcs (ei->era_Y, &era, c - era, len + 1, NULL); +#else + strncpy (ei->era_Y, era, len); + era += len; +#endif + ei->era_Y[len] = CQ('\0'); + return ei; + } + else + era = strchr (era, ';'); + if (era) + ++era; + } + return NULL; +} + +static void +free_era_info (era_info_t *ei) +{ + free (ei->era_C); + free (ei->era_Y); + free (ei); +} + +typedef struct { + size_t num; + CHAR **digit; + CHAR *buffer; +} alt_digits_t; + +static alt_digits_t * +get_alt_digits (const char *alt_digits) +{ + alt_digits_t *adi; + const char *a, *e; + CHAR *aa, *ae; + size_t len; + + adi = (alt_digits_t *) calloc (1, sizeof (alt_digits_t)); + if (!adi) + return NULL; + + /* Compute number of alt_digits. */ + adi->num = 1; + for (a = alt_digits; (e = strchr (a, ';')) != NULL; a = e + 1) + ++adi->num; + /* Allocate the `digit' array, which is an array of `num' pointers into + `buffer'. */ + adi->digit = (CHAR **) calloc (adi->num, sizeof (CHAR **)); + if (!adi->digit) + { + free (adi); + return NULL; + } + /* Compute memory required for `buffer'. */ +#ifdef MAKE_WCSFTIME + len = mbstowcs (NULL, alt_digits, 0); + if (len == (size_t) -1) + { + free (adi->digit); + free (adi); + return NULL; + } +#else + len = strlen (alt_digits); +#endif + /* Allocate it. */ + adi->buffer = (CHAR *) malloc ((len + 1) * sizeof (CHAR)); + if (!adi->buffer) + { + free (adi->digit); + free (adi); + return NULL; + } + /* Store digits in it. */ +#ifdef MAKE_WCSFTIME + mbstowcs (adi->buffer, alt_digits, len + 1); +#else + strcpy (adi->buffer, alt_digits); +#endif + /* Store the pointers into `buffer' into the appropriate `digit' slot. */ + for (len = 0, aa = adi->buffer; (ae = STRCHR (aa, CQ(';'))) != NULL; + ++len, aa = ae + 1) + { + *ae = '\0'; + adi->digit[len] = aa; + } + adi->digit[len] = aa; + return adi; +} + +static void +free_alt_digits (alt_digits_t *adi) +{ + free (adi->digit); + free (adi->buffer); + free (adi); +} + +/* Return 0 if no alt_digit is available for a number. + Return -1 if buffer size isn't sufficient to hold alternative digit. + Return length of new digit otherwise. */ +static int +conv_to_alt_digits (CHAR *buf, size_t bufsiz, unsigned num, alt_digits_t *adi) +{ + if (num < adi->num) + { + size_t len = STRLEN (adi->digit[num]); + if (bufsiz < len) + return -1; + STRCPY (buf, adi->digit[num]); + return (int) len; + } + return 0; +} + +static size_t __strftime (CHAR *, size_t, const CHAR *, const struct tm *, + era_info_t **, alt_digits_t **); + +size_t +_DEFUN (strftime, (s, maxsize, format, tim_p), + CHAR *s _AND + size_t maxsize _AND + _CONST CHAR *format _AND + _CONST struct tm *tim_p) +{ + era_info_t *era_info = NULL; + alt_digits_t *alt_digits = NULL; + size_t ret = __strftime (s, maxsize, format, tim_p, &era_info, &alt_digits); + if (era_info) + free_era_info (era_info); + if (alt_digits) + free_alt_digits (alt_digits); + return ret; +} + +static size_t +__strftime (CHAR *s, size_t maxsize, const CHAR *format, + const struct tm *tim_p, era_info_t **era_info, + alt_digits_t **alt_digits) +#else /* !_WANT_C99_TIME_FORMATS */ +# define __strftime(s,m,f,t,e,a) strftime((s),(m),(f),(t)) + size_t _DEFUN (strftime, (s, maxsize, format, tim_p), CHAR *s _AND size_t maxsize _AND _CONST CHAR *format _AND _CONST struct tm *tim_p) +#endif /* !_WANT_C99_TIME_FORMATS */ { size_t count = 0; int i, len; @@ -375,6 +662,9 @@ _DEFUN (strftime, (s, maxsize, format, tim_p), CHAR ctlocbuf[CTLOCBUFLEN]; #endif size_t ctloclen; + CHAR alt; + CHAR pad; + unsigned long width; struct lc_time_T *_CurrentTimeLocale = __get_current_time_locale (); for (;;) @@ -386,13 +676,42 @@ _DEFUN (strftime, (s, maxsize, format, tim_p), else return 0; } - if (*format == CQ('\0')) break; - format++; - if (*format == CQ('E') || *format == CQ('O')) - format++; + pad = '\0'; + width = 0; + + /* POSIX-1.2008 feature: '0' and '+' modifiers require 0-padding with + slightly different semantics. */ + if (*format == CQ('0') || *format == CQ('+')) + pad = *format++; + + /* POSIX-1.2008 feature: A minimum field width can be specified. */ + if (*format >= CQ('1') && *format <= CQ('9')) + { + CHAR *fp; + width = STRTOUL (format, &fp, 10); + format = fp; + } + + alt = CQ('\0'); + if (*format == CQ('E')) + { + alt = *format++; +#ifdef _WANT_C99_TIME_FORMATS + if (!*era_info && *_CurrentTimeLocale->era) + *era_info = get_era_info (tim_p, _CurrentTimeLocale->era); +#endif /* _WANT_C99_TIME_FORMATS */ + } + else if (*format == CQ('O')) + { + alt = *format++; +#ifdef _WANT_C99_TIME_FORMATS + if (!*alt_digits && *_CurrentTimeLocale->alt_digits) + *alt_digits = get_alt_digits (_CurrentTimeLocale->alt_digits); +#endif /* _WANT_C99_TIME_FORMATS */ + } switch (*format) { @@ -438,24 +757,39 @@ _DEFUN (strftime, (s, maxsize, format, tim_p), } break; case CQ('c'): - _ctloc (c_fmt); +#ifdef _WANT_C99_TIME_FORMATS + if (alt == 'E' && *era_info && *_CurrentTimeLocale->era_d_t_fmt) + _ctloc (era_d_t_fmt); + else +#endif /* _WANT_C99_TIME_FORMATS */ + _ctloc (c_fmt); goto recurse; case CQ('r'): _ctloc (ampm_fmt); goto recurse; case CQ('x'): - _ctloc (x_fmt); +#ifdef _WANT_C99_TIME_FORMATS + if (alt == 'E' && *era_info && *_CurrentTimeLocale->era_d_fmt) + _ctloc (era_d_fmt); + else +#endif /* _WANT_C99_TIME_FORMATS */ + _ctloc (x_fmt); goto recurse; case CQ('X'): - _ctloc (X_fmt); +#ifdef _WANT_C99_TIME_FORMATS + if (alt == 'E' && *era_info && *_CurrentTimeLocale->era_t_fmt) + _ctloc (era_t_fmt); + else +#endif /* _WANT_C99_TIME_FORMATS */ + _ctloc (X_fmt); recurse: if (*ctloc) { /* Recurse to avoid need to replicate %Y formation. */ - size_t adjust = strftime (&s[count], maxsize - count, ctloc, - tim_p); - if (adjust > 0) - count += adjust; + len = __strftime (&s[count], maxsize - count, ctloc, tim_p, + era_info, alt_digits); + if (len > 0) + count += len; else return 0; } @@ -482,38 +816,97 @@ recurse: Be careful of both overflow and sign adjustment due to the asymmetric range of years. */ - int neg = tim_p->tm_year < -YEAR_BASE; - int century = tim_p->tm_year >= 0 - ? tim_p->tm_year / 100 + YEAR_BASE / 100 - : abs (tim_p->tm_year + YEAR_BASE) / 100; - len = snprintf (&s[count], maxsize - count, CQ("%s%.*d"), - neg ? CQ("-") : CQ(""), 2 - neg, century); - if (len < 0 || (count+=len) >= maxsize) return 0; +#ifdef _WANT_C99_TIME_FORMATS + if (alt == 'E' && *era_info) + len = snprintf (&s[count], maxsize - count, CQ("%" SFLG "s"), + (*era_info)->era_C); + else +#endif /* _WANT_C99_TIME_FORMATS */ + { + CHAR *fmt = CQ("%s%.*d"); + char *pos = ""; + int neg = tim_p->tm_year < -YEAR_BASE; + int century = tim_p->tm_year >= 0 + ? tim_p->tm_year / 100 + YEAR_BASE / 100 + : abs (tim_p->tm_year + YEAR_BASE) / 100; + if (pad) /* '0' or '+' */ + { + fmt = CQ("%s%0.*d"); + if (century >= 100 && pad == CQ('+')) + pos = "+"; + } + if (width < 2) + width = 2; + len = snprintf (&s[count], maxsize - count, fmt, + neg ? "-" : pos, width - neg, century); + } + CHECK_LENGTH (); } break; case CQ('d'): case CQ('e'): +#ifdef _WANT_C99_TIME_FORMATS + if (alt == CQ('O') && *alt_digits) + { + if (tim_p->tm_mday < 10) + { + if (*format == CQ('d')) + { + if (maxsize - count < 2) return 0; + len = conv_to_alt_digits (&s[count], maxsize - count, + 0, *alt_digits); + CHECK_LENGTH (); + } + if (*format == CQ('e') || len == 0) + s[count++] = CQ(' '); + } + len = conv_to_alt_digits (&s[count], maxsize - count, + tim_p->tm_mday, *alt_digits); + CHECK_LENGTH (); + if (len > 0) + break; + } +#endif /* _WANT_C99_TIME_FORMATS */ len = snprintf (&s[count], maxsize - count, - *format == CQ('d') ? CQ("%.2d") : CQ("%2d"), - tim_p->tm_mday); - if (len < 0 || (count+=len) >= maxsize) return 0; + *format == CQ('d') ? CQ("%.2d") : CQ("%2d"), + tim_p->tm_mday); + CHECK_LENGTH (); break; case CQ('D'): /* %m/%d/%y */ len = snprintf (&s[count], maxsize - count, - CQ("%.2d/%.2d/%.2d"), - tim_p->tm_mon + 1, tim_p->tm_mday, - tim_p->tm_year >= 0 ? tim_p->tm_year % 100 - : abs (tim_p->tm_year + YEAR_BASE) % 100); - if (len < 0 || (count+=len) >= maxsize) return 0; + CQ("%.2d/%.2d/%.2d"), + tim_p->tm_mon + 1, tim_p->tm_mday, + tim_p->tm_year >= 0 ? tim_p->tm_year % 100 + : abs (tim_p->tm_year + YEAR_BASE) % 100); + CHECK_LENGTH (); break; case CQ('F'): - { /* %F is equivalent to "%Y-%m-%d" */ - /* Recurse to avoid need to replicate %Y formation. */ - size_t adjust = strftime (&s[count], maxsize - count, - CQ("%Y-%m-%d"), tim_p); - if (adjust > 0) - count += adjust; + { /* %F is equivalent to "%+4Y-%m-%d", flags and width can change + that. Recurse to avoid need to replicate %Y formation. */ + CHAR fmtbuf[32], *fmt = fmtbuf; + + *fmt++ = CQ('%'); + if (pad) /* '0' or '+' */ + *fmt++ = pad; + else + *fmt++ = '+'; + if (!pad) + width = 10; + if (width < 6) + width = 6; + width -= 6; + if (width) + { + len = snprintf (fmt, fmtbuf + 32 - fmt, CQ("%lu"), width); + if (len > 0) + fmt += len; + } + STRCPY (fmt, CQ("Y-%m-%d")); + len = __strftime (&s[count], maxsize - count, fmtbuf, tim_p, + era_info, alt_digits); + if (len > 0) + count += len; else return 0; } @@ -530,8 +923,8 @@ recurse: else if (adjust > 0 && tim_p->tm_year < -YEAR_BASE) adjust = -1; len = snprintf (&s[count], maxsize - count, CQ("%.2d"), - ((year + adjust) % 100 + 100) % 100); - if (len < 0 || (count+=len) >= maxsize) return 0; + ((year + adjust) % 100 + 100) % 100); + CHECK_LENGTH (); } break; case CQ('G'): @@ -539,7 +932,7 @@ recurse: /* See the comments for 'C' and 'Y'; this is a variable length field. Although there is no requirement for a minimum number of digits, we use 4 for consistency with 'Y'. */ - int neg = tim_p->tm_year < -YEAR_BASE; + int sign = tim_p->tm_year < -YEAR_BASE; int adjust = iso_year_adjust (tim_p); int century = tim_p->tm_year >= 0 ? tim_p->tm_year / 100 + YEAR_BASE / 100 @@ -547,8 +940,8 @@ recurse: int year = tim_p->tm_year >= 0 ? tim_p->tm_year % 100 : abs (tim_p->tm_year + YEAR_BASE) % 100; if (adjust < 0 && tim_p->tm_year <= -YEAR_BASE) - neg = adjust = 1; - else if (adjust > 0 && neg) + sign = adjust = 1; + else if (adjust > 0 && sign) adjust = -1; year += adjust; if (year == -1) @@ -561,45 +954,88 @@ recurse: year = 0; ++century; } - len = snprintf (&s[count], maxsize - count, CQ("%s%.*d%.2d"), - neg ? CQ("-") : CQ(""), 2 - neg, century, year); + CHAR fmtbuf[10], *fmt = fmtbuf; + /* int potentially overflows, so use unsigned instead. */ + unsigned p_year = century * 100 + year; + if (sign) + *fmt++ = CQ('-'); + else if (pad == CQ('+') && p_year >= 10000) + { + *fmt++ = CQ('+'); + sign = 1; + } + if (width && sign) + --width; + *fmt++ = CQ('%'); + if (pad) + *fmt++ = CQ('0'); + STRCPY (fmt, CQ(".*u")); + len = snprintf (&s[count], maxsize - count, fmtbuf, width, p_year); if (len < 0 || (count+=len) >= maxsize) return 0; } break; case CQ('H'): +#ifdef _WANT_C99_TIME_FORMATS + if (alt == CQ('O') && *alt_digits) + { + len = conv_to_alt_digits (&s[count], maxsize - count, + tim_p->tm_hour, *alt_digits); + CHECK_LENGTH (); + if (len > 0) + break; + } +#endif /* _WANT_C99_TIME_FORMATS */ + /*FALLTHRU*/ case CQ('k'): /* newlib extension */ len = snprintf (&s[count], maxsize - count, - *format == CQ('k') ? CQ("%2d") : CQ("%.2d"), - tim_p->tm_hour); - if (len < 0 || (count+=len) >= maxsize) return 0; + *format == CQ('k') ? CQ("%2d") : CQ("%.2d"), + tim_p->tm_hour); + CHECK_LENGTH (); break; - case CQ('I'): case CQ('l'): /* newlib extension */ + if (alt == CQ('O')) + alt = CQ('\0'); + /*FALLTHRU*/ + case CQ('I'): { register int h12; h12 = (tim_p->tm_hour == 0 || tim_p->tm_hour == 12) ? 12 : tim_p->tm_hour % 12; - len = snprintf (&s[count], maxsize - count, - *format == CQ('I') ? CQ("%.2d") : CQ("%2d"), - h12); - if (len < 0 || (count+=len) >= maxsize) return 0; +#ifdef _WANT_C99_TIME_FORMATS + if (alt != CQ('O') || !*alt_digits + || !(len = conv_to_alt_digits (&s[count], maxsize - count, + h12, *alt_digits))) +#endif /* _WANT_C99_TIME_FORMATS */ + len = snprintf (&s[count], maxsize - count, + *format == CQ('I') ? CQ("%.2d") : CQ("%2d"), h12); + CHECK_LENGTH (); } break; case CQ('j'): len = snprintf (&s[count], maxsize - count, CQ("%.3d"), - tim_p->tm_yday + 1); - if (len < 0 || (count+=len) >= maxsize) return 0; + tim_p->tm_yday + 1); + CHECK_LENGTH (); break; case CQ('m'): - len = snprintf (&s[count], maxsize - count, CQ("%.2d"), - tim_p->tm_mon + 1); - if (len < 0 || (count+=len) >= maxsize) return 0; +#ifdef _WANT_C99_TIME_FORMATS + if (alt != CQ('O') || !*alt_digits + || !(len = conv_to_alt_digits (&s[count], maxsize - count, + tim_p->tm_mon + 1, *alt_digits))) +#endif /* _WANT_C99_TIME_FORMATS */ + len = snprintf (&s[count], maxsize - count, CQ("%.2d"), + tim_p->tm_mon + 1); + CHECK_LENGTH (); break; case CQ('M'): - len = snprintf (&s[count], maxsize - count, CQ("%.2d"), - tim_p->tm_min); - if (len < 0 || (count+=len) >= maxsize) return 0; +#ifdef _WANT_C99_TIME_FORMATS + if (alt != CQ('O') || !*alt_digits + || !(len = conv_to_alt_digits (&s[count], maxsize - count, + tim_p->tm_min, *alt_digits))) +#endif /* _WANT_C99_TIME_FORMATS */ + len = snprintf (&s[count], maxsize - count, CQ("%.2d"), + tim_p->tm_min); + CHECK_LENGTH (); break; case CQ('n'): if (count < maxsize - 1) @@ -621,13 +1057,18 @@ recurse: break; case CQ('R'): len = snprintf (&s[count], maxsize - count, CQ("%.2d:%.2d"), - tim_p->tm_hour, tim_p->tm_min); - if (len < 0 || (count+=len) >= maxsize) return 0; + tim_p->tm_hour, tim_p->tm_min); + CHECK_LENGTH (); break; case CQ('S'): - len = snprintf (&s[count], maxsize - count, CQ("%.2d"), - tim_p->tm_sec); - if (len < 0 || (count+=len) >= maxsize) return 0; +#ifdef _WANT_C99_TIME_FORMATS + if (alt != CQ('O') || !*alt_digits + || !(len = conv_to_alt_digits (&s[count], maxsize - count, + tim_p->tm_sec, *alt_digits))) +#endif /* _WANT_C99_TIME_FORMATS */ + len = snprintf (&s[count], maxsize - count, CQ("%.2d"), + tim_p->tm_sec); + CHECK_LENGTH (); break; case CQ('t'): if (count < maxsize - 1) @@ -637,10 +1078,22 @@ recurse: break; case CQ('T'): len = snprintf (&s[count], maxsize - count, CQ("%.2d:%.2d:%.2d"), - tim_p->tm_hour, tim_p->tm_min, tim_p->tm_sec); - if (len < 0 || (count+=len) >= maxsize) return 0; + tim_p->tm_hour, tim_p->tm_min, tim_p->tm_sec); + CHECK_LENGTH (); break; case CQ('u'): +#ifdef _WANT_C99_TIME_FORMATS + if (alt == CQ('O') && *alt_digits) + { + len = conv_to_alt_digits (&s[count], maxsize - count, + tim_p->tm_wday == 0 ? 7 + : tim_p->tm_wday, + *alt_digits); + CHECK_LENGTH (); + if (len > 0) + break; + } +#endif /* _WANT_C99_TIME_FORMATS */ if (count < maxsize - 1) { if (tim_p->tm_wday == 0) @@ -652,10 +1105,17 @@ recurse: return 0; break; case CQ('U'): - len = snprintf (&s[count], maxsize - count, CQ("%.2d"), - (tim_p->tm_yday + 7 - - tim_p->tm_wday) / 7); - if (len < 0 || (count+=len) >= maxsize) return 0; +#ifdef _WANT_C99_TIME_FORMATS + if (alt != CQ('O') || !*alt_digits + || !(len = conv_to_alt_digits (&s[count], maxsize - count, + (tim_p->tm_yday + 7 - + tim_p->tm_wday) / 7, + *alt_digits))) +#endif /* _WANT_C99_TIME_FORMATS */ + len = snprintf (&s[count], maxsize - count, CQ("%.2d"), + (tim_p->tm_yday + 7 - + tim_p->tm_wday) / 7); + CHECK_LENGTH (); break; case CQ('V'): { @@ -673,11 +1133,26 @@ recurse: + (YEAR_BASE - 1 - (tim_p->tm_year < 0 ? 0 : 2000))))); - len = snprintf (&s[count], maxsize - count, CQ("%.2d"), week); - if (len < 0 || (count+=len) >= maxsize) return 0; +#ifdef _WANT_C99_TIME_FORMATS + if (alt != CQ('O') || !*alt_digits + || !(len = conv_to_alt_digits (&s[count], maxsize - count, + week, *alt_digits))) +#endif /* _WANT_C99_TIME_FORMATS */ + len = snprintf (&s[count], maxsize - count, CQ("%.2d"), week); + CHECK_LENGTH (); } break; case CQ('w'): +#ifdef _WANT_C99_TIME_FORMATS + if (alt == CQ('O') && *alt_digits) + { + len = conv_to_alt_digits (&s[count], maxsize - count, + tim_p->tm_wday, *alt_digits); + CHECK_LENGTH (); + if (len > 0) + break; + } +#endif /* _WANT_C99_TIME_FORMATS */ if (count < maxsize - 1) s[count++] = CQ('0') + tim_p->tm_wday; else @@ -686,37 +1161,75 @@ recurse: case CQ('W'): { int wday = (tim_p->tm_wday) ? tim_p->tm_wday - 1 : 6; - len = snprintf (&s[count], maxsize - count, CQ("%.2d"), - (tim_p->tm_yday + 7 - wday) / 7); - if (len < 0 || (count+=len) >= maxsize) return 0; + wday = (tim_p->tm_yday + 7 - wday) / 7; +#ifdef _WANT_C99_TIME_FORMATS + if (alt != CQ('O') || !*alt_digits + || !(len = conv_to_alt_digits (&s[count], maxsize - count, + wday, *alt_digits))) +#endif /* _WANT_C99_TIME_FORMATS */ + len = snprintf (&s[count], maxsize - count, CQ("%.2d"), wday); + CHECK_LENGTH (); } break; case CQ('y'): { - /* Be careful of both overflow and negative years, thanks to - the asymmetric range of years. */ - int year = tim_p->tm_year >= 0 ? tim_p->tm_year % 100 - : abs (tim_p->tm_year + YEAR_BASE) % 100; - len = snprintf (&s[count], maxsize - count, CQ("%.2d"), year); - if (len < 0 || (count+=len) >= maxsize) return 0; +#ifdef _WANT_C99_TIME_FORMATS + if (alt == 'E' && *era_info) + len = snprintf (&s[count], maxsize - count, CQ("%d"), + (*era_info)->year); + else +#endif /* _WANT_C99_TIME_FORMATS */ + { + /* Be careful of both overflow and negative years, thanks to + the asymmetric range of years. */ + int year = tim_p->tm_year >= 0 ? tim_p->tm_year % 100 + : abs (tim_p->tm_year + YEAR_BASE) % 100; +#ifdef _WANT_C99_TIME_FORMATS + if (alt != CQ('O') || !*alt_digits + || !(len = conv_to_alt_digits (&s[count], maxsize - count, + year, *alt_digits))) +#endif /* _WANT_C99_TIME_FORMATS */ + len = snprintf (&s[count], maxsize - count, CQ("%.2d"), + year); + } + CHECK_LENGTH (); } break; case CQ('Y'): - /* An implementation choice is to have %Y match %C%y, so that it - * gives at least 4 digits, with leading zeros as needed. */ - if(tim_p->tm_year <= INT_MAX-YEAR_BASE) { - /* For normal, non-overflow case. */ - len = snprintf (&s[count], maxsize - count, CQ("%04d"), - tim_p->tm_year + YEAR_BASE); - } - else { - /* int would overflow, so use unsigned instead. */ - register unsigned year; - year = (unsigned) tim_p->tm_year + (unsigned) YEAR_BASE; - len = snprintf (&s[count], maxsize - count, CQ("%04u"), - tim_p->tm_year + YEAR_BASE); - } - if (len < 0 || (count+=len) >= maxsize) return 0; +#ifdef _WANT_C99_TIME_FORMATS + if (alt == 'E' && *era_info) + { + ctloc = (*era_info)->era_Y; + goto recurse; + } + else +#endif /* _WANT_C99_TIME_FORMATS */ + { + CHAR fmtbuf[10], *fmt = fmtbuf; + int sign = tim_p->tm_year < -YEAR_BASE; + /* int potentially overflows, so use unsigned instead. */ + register unsigned year = (unsigned) tim_p->tm_year + + (unsigned) YEAR_BASE; + if (sign) + { + *fmt++ = CQ('-'); + year = UINT_MAX - year + 1; + } + else if (pad == CQ('+') && year >= 10000) + { + *fmt++ = CQ('+'); + sign = 1; + } + if (width && sign) + --width; + *fmt++ = CQ('%'); + if (pad) + *fmt++ = CQ('0'); + STRCPY (fmt, CQ(".*u")); + len = snprintf (&s[count], maxsize - count, fmtbuf, width, + year); + CHECK_LENGTH (); + } break; case CQ('z'): if (tim_p->tm_isdst >= 0) @@ -730,9 +1243,9 @@ recurse: offset = -tz->__tzrule[tim_p->tm_isdst > 0].offset; TZ_UNLOCK; len = snprintf (&s[count], maxsize - count, CQ("%+03ld%.2ld"), - offset / SECSPERHOUR, - labs (offset / SECSPERMIN) % 60L); - if (len < 0 || (count+=len) >= maxsize) return 0; + offset / SECSPERHOUR, + labs (offset / SECSPERMIN) % 60L); + CHECK_LENGTH (); } break; case CQ('Z'): @@ -760,6 +1273,8 @@ recurse: else return 0; break; + default: + return 0; } if (*format) format++; @@ -771,7 +1286,7 @@ recurse: return count; } - + /* The remainder of this file can serve as a regression test. Compile * with -D_REGRESSION_TEST. */ #if defined(_REGRESSION_TEST) /* [Test code: */ |