* 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.
This commit is contained in:
Corinna Vinschen 2010-02-26 09:41:44 +00:00
parent d91ab868e1
commit be7f7a7503
4 changed files with 644 additions and 103 deletions

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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: */