X-Git-Url: http://nsz.repo.hu/git/?a=blobdiff_plain;f=src%2Ftime%2Fstrftime.c;h=cc53d5369ca717900ef5816ed7bdc400d149dc6b;hb=8ef9d46f4d0ff4f0073da6bee7ed0cb5f9035ead;hp=d16e81343891dc15159bd69f8d63a7d58d61c4b3;hpb=1cc81f5cb0df2b66a795ff0c26d7bbc4d16e13c6;p=musl diff --git a/src/time/strftime.c b/src/time/strftime.c index d16e8134..cc53d536 100644 --- a/src/time/strftime.c +++ b/src/time/strftime.c @@ -1,12 +1,12 @@ #include #include +#include #include +#include #include #include - -// FIXME: integer overflows - -const char *__langinfo(nl_item); +#include "locale_impl.h" +#include "time_impl.h" static int is_leap(int y) { @@ -18,194 +18,264 @@ static int is_leap(int y) static int week_num(const struct tm *tm) { - int val = (tm->tm_yday + 7 - (tm->tm_wday+6)%7) / 7; + int val = (tm->tm_yday + 7U - (tm->tm_wday+6U)%7) / 7; /* If 1 Jan is just 1-3 days past Monday, * the previous week is also in this year. */ - if ((tm->tm_wday - tm->tm_yday - 2 + 371) % 7 <= 2) + if ((tm->tm_wday + 371U - tm->tm_yday - 2) % 7 <= 2) val++; if (!val) { val = 52; /* If 31 December of prev year a Thursday, * or Friday of a leap year, then the * prev year has 53 weeks. */ - int dec31 = (tm->tm_wday - tm->tm_yday - 1 + 7) % 7; + int dec31 = (tm->tm_wday + 7U - tm->tm_yday - 1) % 7; if (dec31 == 4 || (dec31 == 5 && is_leap(tm->tm_year%400-1))) val++; } else if (val == 53) { /* If 1 January is not a Thursday, and not * a Wednesday of a leap year, then this * year has only 52 weeks. */ - int jan1 = (tm->tm_wday - tm->tm_yday + 371) % 7; + int jan1 = (tm->tm_wday + 371U - tm->tm_yday) % 7; if (jan1 != 4 && (jan1 != 3 || !is_leap(tm->tm_year))) val = 1; } return val; } -size_t strftime(char *restrict s, size_t n, const char *restrict f, const struct tm *restrict tm) +const char *__strftime_fmt_1(char (*s)[100], size_t *l, int f, const struct tm *tm, locale_t loc, int pad) { nl_item item; - int val; - const char *fmt; - size_t l; - for (l=0; *f && ltm_wday; - goto nl_strcat; - case 'A': - item = DAY_1 + tm->tm_wday; - goto nl_strcat; - case 'h': - case 'b': - item = ABMON_1 + tm->tm_mon; - goto nl_strcat; - case 'B': - item = MON_1 + tm->tm_mon; - goto nl_strcat; - case 'c': - item = D_T_FMT; - goto nl_strftime; - case 'C': - val = (1900+tm->tm_year) / 100; - fmt = "%02d"; - 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; - 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; - } - 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"; - 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': - s[l++] = '\n'; - continue; - case 'p': - item = tm->tm_hour >= 12 ? PM_STR : AM_STR; - goto nl_strcat; - case 'r': - item = T_FMT_AMPM; - goto nl_strftime; - case 'R': - fmt = "%H:%M"; - goto recu_strftime; - case 'S': - val = tm->tm_sec; - fmt = "%02d"; - goto number; - case 't': - s[l++] = '\t'; - continue; - case 'T': - fmt = "%H:%M:%S"; - goto recu_strftime; - case 'u': - val = tm->tm_wday ? tm->tm_wday : 7; - fmt = "%d"; - 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"; - goto number; - case 'x': - item = D_FMT; - goto nl_strftime; - case 'X': - item = T_FMT; - goto nl_strftime; - case 'y': - val = tm->tm_year % 100; - fmt = "%02d"; - goto number; - case 'Y': - val = tm->tm_year + 1900; - fmt = "%04d"; - goto number; - case 'z': - val = -tm->__tm_gmtoff; - l += snprintf(s+l, n-l, "%+.2d%.2d", val/3600, abs(val%3600)/60); - continue; - case 'Z': - l += snprintf(s+l, n-l, "%s", tm->__tm_zone); - continue; - default: - return 0; + long long val; + const char *fmt = "-"; + int width = 2, def_pad = '0'; + + switch (f) { + case 'a': + if (tm->tm_wday > 6U) goto string; + item = ABDAY_1 + tm->tm_wday; + goto nl_strcat; + case 'A': + if (tm->tm_wday > 6U) goto string; + item = DAY_1 + tm->tm_wday; + goto nl_strcat; + case 'h': + case 'b': + if (tm->tm_mon > 11U) goto string; + item = ABMON_1 + tm->tm_mon; + goto nl_strcat; + case 'B': + if (tm->tm_mon > 11U) goto string; + item = MON_1 + tm->tm_mon; + goto nl_strcat; + case 'c': + item = D_T_FMT; + goto nl_strftime; + case 'C': + val = (1900LL+tm->tm_year) / 100; + goto number; + case 'e': + def_pad = '_'; + case 'd': + val = tm->tm_mday; + goto number; + case 'D': + fmt = "%m/%d/%y"; + goto recu_strftime; + case 'F': + fmt = "%Y-%m-%d"; + goto recu_strftime; + case 'g': + case 'G': + 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') val %= 100; + else width = 4; + goto number; + case 'H': + val = tm->tm_hour; + goto number; + case 'I': + val = tm->tm_hour; + if (!val) val = 12; + else if (val > 12) val -= 12; + goto number; + case 'j': + val = tm->tm_yday+1; + width = 3; + goto number; + case 'm': + val = tm->tm_mon+1; + goto number; + case 'M': + val = tm->tm_min; + goto number; + case 'n': + *l = 1; + return "\n"; + case 'p': + item = tm->tm_hour >= 12 ? PM_STR : AM_STR; + goto nl_strcat; + case 'r': + item = T_FMT_AMPM; + goto nl_strftime; + case 'R': + fmt = "%H:%M"; + goto recu_strftime; + case 's': + val = __tm_to_secs(tm) - tm->__tm_gmtoff; + width = 1; + goto number; + case 'S': + val = tm->tm_sec; + goto number; + case 't': + *l = 1; + return "\t"; + case 'T': + fmt = "%H:%M:%S"; + goto recu_strftime; + case 'u': + val = tm->tm_wday ? tm->tm_wday : 7; + width = 1; + goto number; + case 'U': + val = (tm->tm_yday + 7U - tm->tm_wday) / 7; + goto number; + case 'W': + val = (tm->tm_yday + 7U - (tm->tm_wday+6U)%7) / 7; + goto number; + case 'V': + val = week_num(tm); + goto number; + case 'w': + val = tm->tm_wday; + width = 1; + goto number; + case 'x': + item = D_FMT; + goto nl_strftime; + case 'X': + item = T_FMT; + goto nl_strftime; + case 'y': + val = (tm->tm_year + 1900LL) % 100; + if (val < 0) val = -val; + goto number; + case 'Y': + val = tm->tm_year + 1900LL; + if (val >= 10000) { + *l = snprintf(*s, sizeof *s, "+%lld", val); + return *s; } + width = 4; + goto number; + case 'z': + if (tm->tm_isdst < 0) { + *l = 0; + return ""; } -literal: - s[l++] = *f; - continue; + *l = snprintf(*s, sizeof *s, "%+.4ld", + tm->__tm_gmtoff/3600*100 + tm->__tm_gmtoff%3600/60); + return *s; + case 'Z': + if (tm->tm_isdst < 0) { + *l = 0; + return ""; + } + fmt = __tm_to_tzname(tm); + goto string; + case '%': + *l = 1; + return "%"; + default: + return 0; + } number: - l += snprintf(s+l, n-l, fmt, val); - continue; + switch (pad ? pad : def_pad) { + case '-': *l = snprintf(*s, sizeof *s, "%lld", val); break; + case '_': *l = snprintf(*s, sizeof *s, "%*lld", width, val); break; + case '0': + default: *l = snprintf(*s, sizeof *s, "%0*lld", width, val); break; + } + return *s; nl_strcat: - l += snprintf(s+l, n-l, "%s", __langinfo(item)); - continue; + fmt = __nl_langinfo_l(item, loc); +string: + *l = strlen(fmt); + return fmt; nl_strftime: - fmt = __langinfo(item); + fmt = __nl_langinfo_l(item, loc); recu_strftime: - l += strftime(s+l, n-l, fmt, tm); + *l = __strftime_l(*s, sizeof *s, fmt, tm, loc); + if (!*l) return 0; + return *s; +} + +size_t __strftime_l(char *restrict s, size_t n, const char *restrict f, const struct tm *restrict tm, locale_t loc) +{ + size_t l, k; + char buf[100]; + char *p; + const char *t; + int pad, plus; + unsigned long width; + for (l=0; ltm_year < -1900) { + s[l++] = '-'; + width--; + } else if (plus && d+(width-k) >= (*p=='C'?3:5)) { + s[l++] = '+'; + width--; + } + for (; width > k && l < n; width--) + s[l++] = '0'; + } + if (k > n-l) k = n-l; + memcpy(s+l, t, k); + l += k; } - if (l >= n) return 0; - s[l] = 0; - return l; + if (n) { + if (l==n) l=n-1; + s[l] = 0; + } + return 0; +} + +size_t strftime(char *restrict s, size_t n, const char *restrict f, const struct tm *restrict tm) +{ + return __strftime_l(s, n, f, tm, CURRENT_LOCALE); } + +weak_alias(__strftime_l, strftime_l);