bug-gnulib
[Top][All Lists]
Advanced

[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 = "";

reply via email to

[Prev in Thread] Current Thread [Next in Thread]