From 33413cdd25c58f0e57061e0a3cb7a29d492ba785 Mon Sep 17 00:00:00 2001 From: Rich Felker Date: Thu, 22 Aug 2013 19:44:02 -0400 Subject: simplify strftime and fix integer overflows use a long long value so that even with offsets, values cannot overflow. instead of using different format strings for different numeric formats, simply use a per-format width and %0*lld for all of them. this width specifier is not for use with strftime field widths; that will be a separate step in the caller. --- src/time/strftime.c | 40 ++++++++++++---------------------------- 1 file changed, 12 insertions(+), 28 deletions(-) (limited to 'src') diff --git a/src/time/strftime.c b/src/time/strftime.c index 880e41a1..19ffdbca 100644 --- a/src/time/strftime.c +++ b/src/time/strftime.c @@ -6,8 +6,6 @@ #include #include "libc.h" -// FIXME: integer overflows - const char *__nl_langinfo_l(nl_item, locale_t); static int is_leap(int y) @@ -49,8 +47,9 @@ size_t __strftime_l(char *restrict, size_t, const char *restrict, const struct t const char *__strftime_fmt_1(char (*s)[100], size_t *l, int f, const struct tm *tm, locale_t loc) { nl_item item; - int val; + long long val; const char *fmt; + int width = 2; switch (f) { case 'a': @@ -70,55 +69,45 @@ const char *__strftime_fmt_1(char (*s)[100], size_t *l, int f, const struct tm * item = D_T_FMT; goto nl_strftime; case 'C': - val = (1900+tm->tm_year) / 100; - fmt = "%02d"; + val = (1900LL+tm->tm_year) / 100; goto number; case 'd': val = tm->tm_mday; - fmt = "%02d"; goto number; case 'D': fmt = "%m/%d/%y"; goto recu_strftime; case 'e': val = tm->tm_mday; - fmt = "%2d"; goto number; case 'F': fmt = "%Y-%m-%d"; goto recu_strftime; case 'g': case 'G': - fmt = "%04d"; - val = tm->tm_year + 1900; + val = tm->tm_year + 1900LL; if (tm->tm_yday < 3 && week_num(tm) != 1) val--; else if (tm->tm_yday > 360 && week_num(tm) == 1) val++; - if (f=='g') { - fmt = "%02d"; - val %= 100; - } + if (f=='g') val %= 100; + else width = 4; goto number; case 'H': val = tm->tm_hour; - fmt = "%02d"; goto number; case 'I': val = tm->tm_hour; if (!val) val = 12; else if (val > 12) val -= 12; - fmt = "%02d"; goto number; case 'j': val = tm->tm_yday+1; - fmt = "%03d"; + width = 3; goto number; case 'm': val = tm->tm_mon+1; - fmt = "%02d"; goto number; case 'M': val = tm->tm_min; - fmt = "%02d"; goto number; case 'n': *l = 1; @@ -134,7 +123,6 @@ const char *__strftime_fmt_1(char (*s)[100], size_t *l, int f, const struct tm * goto recu_strftime; case 'S': val = tm->tm_sec; - fmt = "%02d"; goto number; case 't': *l = 1; @@ -144,23 +132,20 @@ const char *__strftime_fmt_1(char (*s)[100], size_t *l, int f, const struct tm * goto recu_strftime; case 'u': val = tm->tm_wday ? tm->tm_wday : 7; - fmt = "%d"; + width = 1; goto number; case 'U': val = (tm->tm_yday + 7 - tm->tm_wday) / 7; - fmt = "%02d"; goto number; case 'W': val = (tm->tm_yday + 7 - (tm->tm_wday+6)%7) / 7; - fmt = "%02d"; goto number; case 'V': val = week_num(tm); - fmt = "%02d"; goto number; case 'w': val = tm->tm_wday; - fmt = "%d"; + width = 1; goto number; case 'x': item = D_FMT; @@ -170,11 +155,10 @@ const char *__strftime_fmt_1(char (*s)[100], size_t *l, int f, const struct tm * goto nl_strftime; case 'y': val = tm->tm_year % 100; - fmt = "%02d"; goto number; case 'Y': - val = tm->tm_year + 1900; - fmt = "%04d"; + val = tm->tm_year + 1900LL; + width = 4; goto number; case 'z': val = -tm->__tm_gmtoff; @@ -190,7 +174,7 @@ const char *__strftime_fmt_1(char (*s)[100], size_t *l, int f, const struct tm * return 0; } number: - *l = snprintf(*s, sizeof *s, fmt, val); + *l = snprintf(*s, sizeof *s, "%0*lld", width, val); return *s; nl_strcat: fmt = __nl_langinfo_l(item, loc); -- cgit v1.2.1