mirror of
git://git.sv.gnu.org/coreutils.git
synced 2026-04-21 03:12:48 +02:00
Include <stdbool.h>. Use bool where appropriate, instead of int.
(my_strftime): Do not mishandle years close to INT_MAX, by doing the right thing even if adding 1900 would overflow. Similarly for tm_mon + 1 and tm_yday + 1. Make %Y always equivalent to %C%y, and similarly for %G and %g. (DO_NUMBER, DO_NUMBER_SPACEPAD): Set digits to d, not a conditional. (DO_SIGNED_NUMBER): New macro. (my_strftime) [HAVE_TZNAME]: Don't dump core if tp->tm_dst > 1.
This commit is contained in:
158
lib/strftime.c
158
lib/strftime.c
@@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 1991-1999, 2000, 2001, 2003, 2004 Free Software
|
||||
/* Copyright (C) 1991-1999, 2000, 2001, 2003, 2004, 2005 Free Software
|
||||
Foundation, Inc.
|
||||
|
||||
NOTE: The canonical source of this file is maintained with the GNU C Library.
|
||||
@@ -72,6 +72,7 @@ extern char *tzname[];
|
||||
#endif
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@@ -479,16 +480,17 @@ my_strftime (CHAR_T *s, size_t maxsize, const CHAR_T *format,
|
||||
int modifier; /* Field modifier ('E', 'O', or 0). */
|
||||
int digits; /* Max digits for numeric format. */
|
||||
int number_value; /* Numeric value to be printed. */
|
||||
int negative_number; /* 1 if the number is negative. */
|
||||
unsigned int u_number_value; /* (unsigned int) number_value. */
|
||||
bool negative_number; /* 1 if the number is negative. */
|
||||
const CHAR_T *subfmt;
|
||||
CHAR_T *bufp;
|
||||
CHAR_T buf[1 + (sizeof (int) < sizeof (time_t)
|
||||
? INT_STRLEN_BOUND (time_t)
|
||||
: INT_STRLEN_BOUND (int))];
|
||||
int width = -1;
|
||||
int to_lowcase = 0;
|
||||
int to_uppcase = 0;
|
||||
int change_case = 0;
|
||||
bool to_lowcase = false;
|
||||
bool to_uppcase = false;
|
||||
bool change_case = false;
|
||||
int format_char;
|
||||
|
||||
#if DO_MULTIBYTE && !defined COMPILE_WIDE
|
||||
@@ -593,10 +595,10 @@ my_strftime (CHAR_T *s, size_t maxsize, const CHAR_T *format,
|
||||
|
||||
/* This changes textual output. */
|
||||
case L_('^'):
|
||||
to_uppcase = 1;
|
||||
to_uppcase = true;
|
||||
continue;
|
||||
case L_('#'):
|
||||
change_case = 1;
|
||||
change_case = true;
|
||||
continue;
|
||||
|
||||
default:
|
||||
@@ -643,10 +645,14 @@ my_strftime (CHAR_T *s, size_t maxsize, const CHAR_T *format,
|
||||
switch (format_char)
|
||||
{
|
||||
#define DO_NUMBER(d, v) \
|
||||
digits = d > width ? d : width; \
|
||||
digits = d; \
|
||||
number_value = v; goto do_number
|
||||
#define DO_SIGNED_NUMBER(d, negative, v) \
|
||||
digits = d; \
|
||||
negative_number = negative; \
|
||||
u_number_value = v; goto do_signed_number
|
||||
#define DO_NUMBER_SPACEPAD(d, v) \
|
||||
digits = d > width ? d : width; \
|
||||
digits = d; \
|
||||
number_value = v; goto do_number_spacepad
|
||||
|
||||
case L_('%'):
|
||||
@@ -660,8 +666,8 @@ my_strftime (CHAR_T *s, size_t maxsize, const CHAR_T *format,
|
||||
goto bad_format;
|
||||
if (change_case)
|
||||
{
|
||||
to_uppcase = 1;
|
||||
to_lowcase = 0;
|
||||
to_uppcase = true;
|
||||
to_lowcase = false;
|
||||
}
|
||||
#if defined _NL_CURRENT || !HAVE_STRFTIME
|
||||
cpy (aw_len, a_wkday);
|
||||
@@ -675,8 +681,8 @@ my_strftime (CHAR_T *s, size_t maxsize, const CHAR_T *format,
|
||||
goto bad_format;
|
||||
if (change_case)
|
||||
{
|
||||
to_uppcase = 1;
|
||||
to_lowcase = 0;
|
||||
to_uppcase = true;
|
||||
to_lowcase = false;
|
||||
}
|
||||
#if defined _NL_CURRENT || !HAVE_STRFTIME
|
||||
cpy (STRLEN (f_wkday), f_wkday);
|
||||
@@ -689,8 +695,8 @@ my_strftime (CHAR_T *s, size_t maxsize, const CHAR_T *format,
|
||||
case L_('h'):
|
||||
if (change_case)
|
||||
{
|
||||
to_uppcase = 1;
|
||||
to_lowcase = 0;
|
||||
to_uppcase = true;
|
||||
to_lowcase = false;
|
||||
}
|
||||
if (modifier != 0)
|
||||
goto bad_format;
|
||||
@@ -706,8 +712,8 @@ my_strftime (CHAR_T *s, size_t maxsize, const CHAR_T *format,
|
||||
goto bad_format;
|
||||
if (change_case)
|
||||
{
|
||||
to_uppcase = 1;
|
||||
to_lowcase = 0;
|
||||
to_uppcase = true;
|
||||
to_lowcase = false;
|
||||
}
|
||||
#if defined _NL_CURRENT || !HAVE_STRFTIME
|
||||
cpy (STRLEN (f_month), f_month);
|
||||
@@ -807,8 +813,9 @@ my_strftime (CHAR_T *s, size_t maxsize, const CHAR_T *format,
|
||||
}
|
||||
|
||||
{
|
||||
int year = tp->tm_year + TM_YEAR_BASE;
|
||||
DO_NUMBER (1, year / 100 - (year % 100 < 0));
|
||||
int century = tp->tm_year / 100 + TM_YEAR_BASE / 100;
|
||||
century -= tp->tm_year % 100 < 0 && 0 < century;
|
||||
DO_SIGNED_NUMBER (2, tp->tm_year < - TM_YEAR_BASE, century);
|
||||
}
|
||||
|
||||
case L_('x'):
|
||||
@@ -846,8 +853,8 @@ 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 and then
|
||||
jump to one of these two labels. */
|
||||
/* All numeric formats set DIGITS and NUMBER_VALUE (or U_NUMBER_VALUE)
|
||||
and then jump to one of these three labels. */
|
||||
|
||||
do_number_spacepad:
|
||||
/* Force `_' flag unless overridden by `0' or `-' flag. */
|
||||
@@ -855,14 +862,22 @@ my_strftime (CHAR_T *s, size_t maxsize, const CHAR_T *format,
|
||||
pad = L_('_');
|
||||
|
||||
do_number:
|
||||
/* Format the number according to the MODIFIER flag. */
|
||||
/* Format NUMBER_VALUE according to the MODIFIER flag. */
|
||||
negative_number = number_value < 0;
|
||||
u_number_value = number_value;
|
||||
|
||||
if (modifier == L_('O') && 0 <= number_value)
|
||||
do_signed_number:
|
||||
/* 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
|
||||
unsigned int (i.e., modulo (UINT_MAX + 1)) without
|
||||
negating it. */
|
||||
if (modifier == L_('O') && !negative_number)
|
||||
{
|
||||
#ifdef _NL_CURRENT
|
||||
/* Get the locale specific alternate representation of
|
||||
the number NUMBER_VALUE. If none exist NULL is returned. */
|
||||
const CHAR_T *cp = nl_get_alt_digit (number_value
|
||||
the number. If none exist NULL is returned. */
|
||||
const CHAR_T *cp = nl_get_alt_digit (u_number_value
|
||||
HELPER_LOCALE_ARG);
|
||||
|
||||
if (cp != NULL)
|
||||
@@ -880,19 +895,21 @@ my_strftime (CHAR_T *s, size_t maxsize, const CHAR_T *format,
|
||||
# endif
|
||||
#endif
|
||||
}
|
||||
{
|
||||
unsigned int u = number_value;
|
||||
|
||||
bufp = buf + sizeof (buf) / sizeof (buf[0]);
|
||||
negative_number = number_value < 0;
|
||||
bufp = buf + sizeof (buf) / sizeof (buf[0]);
|
||||
|
||||
if (negative_number)
|
||||
u = -u;
|
||||
if (negative_number)
|
||||
u_number_value = - u_number_value;
|
||||
|
||||
do
|
||||
*--bufp = u % 10 + L_('0');
|
||||
while ((u /= 10) != 0);
|
||||
}
|
||||
do
|
||||
{
|
||||
*--bufp = u_number_value % 10 + L_('0');
|
||||
u_number_value /= 10;
|
||||
}
|
||||
while (u_number_value != 0);
|
||||
|
||||
if (digits < width)
|
||||
digits = width;
|
||||
|
||||
do_number_sign_and_padding:
|
||||
if (negative_number)
|
||||
@@ -974,7 +991,7 @@ my_strftime (CHAR_T *s, size_t maxsize, const CHAR_T *format,
|
||||
if (modifier == L_('E'))
|
||||
goto bad_format;
|
||||
|
||||
DO_NUMBER (3, 1 + tp->tm_yday);
|
||||
DO_SIGNED_NUMBER (3, tp->tm_yday < -1, tp->tm_yday + 1U);
|
||||
|
||||
case L_('M'):
|
||||
if (modifier == L_('E'))
|
||||
@@ -986,7 +1003,7 @@ my_strftime (CHAR_T *s, size_t maxsize, const CHAR_T *format,
|
||||
if (modifier == L_('E'))
|
||||
goto bad_format;
|
||||
|
||||
DO_NUMBER (2, tp->tm_mon + 1);
|
||||
DO_SIGNED_NUMBER (2, tp->tm_mon < -1, tp->tm_mon + 1U);
|
||||
|
||||
#ifndef _LIBC
|
||||
case L_('N'): /* GNU extension. */
|
||||
@@ -1010,7 +1027,7 @@ my_strftime (CHAR_T *s, size_t maxsize, const CHAR_T *format,
|
||||
break;
|
||||
|
||||
case L_('P'):
|
||||
to_lowcase = 1;
|
||||
to_lowcase = true;
|
||||
#if !defined _NL_CURRENT && HAVE_STRFTIME
|
||||
format_char = L_('p');
|
||||
#endif
|
||||
@@ -1019,8 +1036,8 @@ my_strftime (CHAR_T *s, size_t maxsize, const CHAR_T *format,
|
||||
case L_('p'):
|
||||
if (change_case)
|
||||
{
|
||||
to_uppcase = 0;
|
||||
to_lowcase = 1;
|
||||
to_uppcase = false;
|
||||
to_lowcase = true;
|
||||
}
|
||||
#if defined _NL_CURRENT || !HAVE_STRFTIME
|
||||
cpy (ap_len, ampm);
|
||||
@@ -1070,20 +1087,7 @@ my_strftime (CHAR_T *s, size_t maxsize, const CHAR_T *format,
|
||||
{
|
||||
int d = t % 10;
|
||||
t /= 10;
|
||||
|
||||
if (negative_number)
|
||||
{
|
||||
d = -d;
|
||||
|
||||
/* Adjust if division truncates to minus infinity. */
|
||||
if (0 < -1 % 10 && d < 0)
|
||||
{
|
||||
t++;
|
||||
d += 10;
|
||||
}
|
||||
}
|
||||
|
||||
*--bufp = d + L_('0');
|
||||
*--bufp = (negative_number ? -d : d) + L_('0');
|
||||
}
|
||||
while (t != 0);
|
||||
|
||||
@@ -1131,14 +1135,22 @@ my_strftime (CHAR_T *s, size_t maxsize, const CHAR_T *format,
|
||||
if (modifier == L_('E'))
|
||||
goto bad_format;
|
||||
{
|
||||
int year = tp->tm_year + TM_YEAR_BASE;
|
||||
/* YEAR is a leap year if and only if (tp->tm_year + TM_YEAR_BASE)
|
||||
is a leap year, except that YEAR and YEAR - 1 both work
|
||||
correctly even when (tp->tm_year + TM_YEAR_BASE) would
|
||||
overflow. */
|
||||
int year = (tp->tm_year
|
||||
+ (tp->tm_year < 0
|
||||
? TM_YEAR_BASE % 400
|
||||
: TM_YEAR_BASE % 400 - 400));
|
||||
int year_adjust = 0;
|
||||
int days = iso_week_days (tp->tm_yday, tp->tm_wday);
|
||||
|
||||
if (days < 0)
|
||||
{
|
||||
/* This ISO week belongs to the previous year. */
|
||||
year--;
|
||||
days = iso_week_days (tp->tm_yday + (365 + __isleap (year)),
|
||||
year_adjust = -1;
|
||||
days = iso_week_days (tp->tm_yday + (365 + __isleap (year - 1)),
|
||||
tp->tm_wday);
|
||||
}
|
||||
else
|
||||
@@ -1148,7 +1160,7 @@ my_strftime (CHAR_T *s, size_t maxsize, const CHAR_T *format,
|
||||
if (0 <= d)
|
||||
{
|
||||
/* This ISO week belongs to the next year. */
|
||||
year++;
|
||||
year_adjust = 1;
|
||||
days = d;
|
||||
}
|
||||
}
|
||||
@@ -1156,10 +1168,19 @@ my_strftime (CHAR_T *s, size_t maxsize, const CHAR_T *format,
|
||||
switch (*f)
|
||||
{
|
||||
case L_('g'):
|
||||
DO_NUMBER (2, (year % 100 + 100) % 100);
|
||||
{
|
||||
int yy = (tp->tm_year % 100 + year_adjust) % 100;
|
||||
DO_NUMBER (2, (0 <= yy
|
||||
? yy
|
||||
: tp->tm_year < -TM_YEAR_BASE - year_adjust
|
||||
? -yy
|
||||
: yy + 100));
|
||||
}
|
||||
|
||||
case L_('G'):
|
||||
DO_NUMBER (1, year);
|
||||
DO_SIGNED_NUMBER (4, tp->tm_year < -TM_YEAR_BASE - year_adjust,
|
||||
(tp->tm_year + (unsigned int) TM_YEAR_BASE
|
||||
+ year_adjust));
|
||||
|
||||
default:
|
||||
DO_NUMBER (2, days / 7 + 1);
|
||||
@@ -1201,7 +1222,8 @@ my_strftime (CHAR_T *s, size_t maxsize, const CHAR_T *format,
|
||||
if (modifier == L_('O'))
|
||||
goto bad_format;
|
||||
else
|
||||
DO_NUMBER (1, tp->tm_year + TM_YEAR_BASE);
|
||||
DO_SIGNED_NUMBER (4, tp->tm_year < -TM_YEAR_BASE,
|
||||
tp->tm_year + (unsigned int) TM_YEAR_BASE);
|
||||
|
||||
case L_('y'):
|
||||
if (modifier == L_('E'))
|
||||
@@ -1220,19 +1242,25 @@ my_strftime (CHAR_T *s, size_t maxsize, const CHAR_T *format,
|
||||
# endif
|
||||
#endif
|
||||
}
|
||||
DO_NUMBER (2, (tp->tm_year % 100 + 100) % 100);
|
||||
|
||||
{
|
||||
int yy = tp->tm_year % 100;
|
||||
if (yy < 0)
|
||||
yy = tp->tm_year < - TM_YEAR_BASE ? -yy : yy + 100;
|
||||
DO_NUMBER (2, yy);
|
||||
}
|
||||
|
||||
case L_('Z'):
|
||||
if (change_case)
|
||||
{
|
||||
to_uppcase = 0;
|
||||
to_lowcase = 1;
|
||||
to_uppcase = false;
|
||||
to_lowcase = true;
|
||||
}
|
||||
|
||||
#if HAVE_TZNAME
|
||||
/* The tzset() call might have changed the value. */
|
||||
if (!(zone && *zone) && tp->tm_isdst >= 0)
|
||||
zone = tzname[tp->tm_isdst];
|
||||
zone = tzname[tp->tm_isdst != 0];
|
||||
#endif
|
||||
if (! zone)
|
||||
zone = "";
|
||||
|
||||
Reference in New Issue
Block a user