[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[bug-gnulib] strftime bugs
From: |
Eric Blake |
Subject: |
[bug-gnulib] strftime bugs |
Date: |
Sat, 05 Feb 2005 06:56:16 -0700 |
User-agent: |
Mozilla Thunderbird 1.0 (Windows/20041206) |
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
According to
http://www.opengroup.org/austin/mailarchives/ag/msg07963.html, best
practice for strftime is for "%Y" to always produce at least four
characters, to be strictly equivalent to "%C%y", and to not overflow.
(Note that the email contains one flaw in its examples: the year
- -2147481749 is not possible with 32-bit ints). The gnulib strftime
replacement does not satisfy these properties without my attached patch
(for example, with tm_year == -1901, %y would print 99 instead of 01).
Likewise, there is a potential for a core dump with "%Z" - POSIX allows
tm_isdst to be any positive value, not just 1, and it was blindly being
used as an index into the 2-element array tzname. I did not see a way to
cleanly fix the overflow problems in "%g%G%V", while still staying within
the trivial change limits; if someone else wants to spend time tackling
those, be my guest.
2005-02-05 Eric Blake <address@hidden> (tiny change)
* strftime.c (my_strftime): For 'y', accomodate negative years
with tm_year < -TM_YEAR_BASE. For 'C', avoid overflow and
accomdate negative years, and have 2-character minimum. For
'Y', avoid overflow by using a subformat. For 'Z', avoid
exceeding bounds of tzname. For 'c' and 'F', avoid double
recursion from "%Y". Note that 'V', 'g', and 'G' still suffer
from overflow.
- --
Life is short - so eat dessert first!
Eric Blake address@hidden
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.0 (Cygwin)
Comment: Public key at home.comcast.net/~ericblake/eblake.gpg
Comment: Using GnuPG with Thunderbird - http://enigmail.mozdev.org
iD8DBQFCBNCA84KuGfSFAYARAgfyAJ9xNJDX3XAJWP4kF71YgJrY6MpSTQCg1eq6
DOuc/5Ko7sK8NqgB94i07Gs=
=C/b7
-----END PGP SIGNATURE-----
Index: lib/strftime.c
===================================================================
RCS file: /cvsroot/gnulib/gnulib/lib/strftime.c,v
retrieving revision 1.76
diff -u -p -r1.76 strftime.c
--- lib/strftime.c 11 Nov 2004 05:58:12 -0000 1.76
+++ lib/strftime.c 5 Feb 2005 13:45:46 -0000
@@ -479,7 +479,7 @@ my_strftime (CHAR_T *s, size_t maxsize,
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. */
+ int negative_number = 0; /* 1 if the number is negative. */
const CHAR_T *subfmt;
CHAR_T *bufp;
CHAR_T buf[1 + (sizeof (int) < sizeof (time_t)
@@ -730,7 +730,7 @@ my_strftime (CHAR_T *s, size_t maxsize,
# if HAVE_STRFTIME
goto underlying_strftime;
# else
- subfmt = L_("%a %b %e %H:%M:%S %Y");
+ subfmt = L_("%a %b %e %H:%M:%S %C%y");
# endif
#endif
@@ -805,12 +805,11 @@ my_strftime (CHAR_T *s, size_t maxsize,
# endif
#endif
}
-
- {
- int year = tp->tm_year + TM_YEAR_BASE;
- DO_NUMBER (1, year / 100 - (year % 100 < 0));
- }
-
+ negative_number = tp->tm_year < -TM_YEAR_BASE;
+ DO_NUMBER (2, (tp->tm_year >= 0
+ ? tp->tm_year / 100 + TM_YEAR_BASE / 100
+ : abs (tp->tm_year + TM_YEAR_BASE) / 100));
+
case L_('x'):
if (modifier == L_('O'))
goto bad_format;
@@ -847,7 +846,8 @@ my_strftime (CHAR_T *s, size_t maxsize,
DO_NUMBER_SPACEPAD (2, tp->tm_mday);
/* All numeric formats set DIGITS and NUMBER_VALUE and then
- jump to one of these two labels. */
+ jump to one of these two labels. If NEGATIVE_NUMBER is
+ set then NUMBER_VALUE has already been negated. */
do_number_spacepad:
/* Force `_' flag unless overridden by `0' or `-' flag. */
@@ -884,10 +884,11 @@ my_strftime (CHAR_T *s, size_t maxsize,
unsigned int u = number_value;
bufp = buf + sizeof (buf) / sizeof (buf[0]);
- negative_number = number_value < 0;
-
- if (negative_number)
- u = -u;
+ if (number_value < 0)
+ {
+ negative_number = 1;
+ u = -u;
+ }
do
*--bufp = u % 10 + L_('0');
@@ -943,7 +944,7 @@ my_strftime (CHAR_T *s, size_t maxsize,
case L_('F'):
if (modifier != 0)
goto bad_format;
- subfmt = L_("%Y-%m-%d");
+ subfmt = L_("%C%y-%m-%d");
goto subformat;
case L_('H'):
@@ -1131,6 +1132,8 @@ my_strftime (CHAR_T *s, size_t maxsize,
if (modifier == L_('E'))
goto bad_format;
{
+ /* FIXME: This calculation suffers from overflow if tp->tm_year
+ is close to INT_MIN or INT_MAX. */
int year = tp->tm_year + TM_YEAR_BASE;
int days = iso_week_days (tp->tm_yday, tp->tm_wday);
@@ -1201,7 +1204,10 @@ my_strftime (CHAR_T *s, size_t maxsize,
if (modifier == L_('O'))
goto bad_format;
else
- DO_NUMBER (1, tp->tm_year + TM_YEAR_BASE);
+ {
+ subfmt = L_("%C%y");
+ goto subformat;
+ }
case L_('y'):
if (modifier == L_('E'))
@@ -1220,7 +1226,8 @@ my_strftime (CHAR_T *s, size_t maxsize,
# endif
#endif
}
- DO_NUMBER (2, (tp->tm_year % 100 + 100) % 100);
+ DO_NUMBER (2, (tp->tm_year >= 0 ? tp->tm_year % 100
+ : abs (tp->tm_year + TM_YEAR_BASE) % 100));
case L_('Z'):
if (change_case)
@@ -1232,7 +1239,7 @@ my_strftime (CHAR_T *s, size_t maxsize,
#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 = "";
- [bug-gnulib] strftime bugs,
Eric Blake <=