From bb749f6b02fb68002205c0412698adbf93356ffd Mon Sep 17 00:00:00 2001 From: =?utf8?q?Petr=20P=C3=ADsa=C5=99?= Date: Sun, 12 May 2024 10:59:14 +0200 Subject: [PATCH] Fix converting YYYY-DDD date string on musl isds-datestring2tm test failed on musl C library: FAIL: isds-datestring2tm ======================== Testing unit: ISO date string to tm conversion 2001-02-03: passed 20010203: passed 2001-34: failed reason: Returned struct tm differs in tm_mon: expected=1, got=34 2001-02-03T05:06: passed foo bar: passed Empty input: passed NULL input pointer: passed NULL output pointer: passed Test results: unit = ISO date string to tm conversion, passed = 7, failed = 1, skipped = 0 The cause was that strptime() does not populate tm.tm_mon and tm.tm_mday when parsing "%Y-%j" on musl, in contrast to glibc. The musl behavior seems to be in-line with a drafted POSIX update . Since there seems to be no standard interface for computing those fields from a day of year, this patch resorts to an libisds implementation. Because there was already one in win32 backend, this patch moves the implementation from yday2mday() into _isds_yday2mday(). Implementation note: It turns a modifiable heap array into a stack one to improve thread safety. One could use two constant arrays and switch between them with a pointer. But I prefer the stack aproach to increase locality. It's faster according my benchmarks. https://www.openwall.com/lists/musl/2024/05/11/5 https://bugs.gentoo.org/show_bug.cgi?id=928107 --- src/unix.c | 6 +++++- src/utils.c | 25 +++++++++++++++++++++++++ src/utils.h | 6 ++++++ src/win32.c | 20 +------------------- 4 files changed, 37 insertions(+), 20 deletions(-) diff --git a/src/unix.c b/src/unix.c index 63a6f7b..35ed187 100644 --- a/src/unix.c +++ b/src/unix.c @@ -15,6 +15,8 @@ _hidden isds_error _isds_datestring2tm(const xmlChar *string, struct tm *time) { char *offset; if (!string || !time) return IE_INVAL; + memset(time, 0, sizeof(*time)); + /* xsd:date is ISO 8601 string, thus ASCII */ offset = strptime((char*)string, "%Y-%m-%d", time); if (offset && *offset == '\0') @@ -25,8 +27,10 @@ _hidden isds_error _isds_datestring2tm(const xmlChar *string, struct tm *time) { return IE_SUCCESS; offset = strptime((char*)string, "%Y-%j", time); - if (offset && *offset == '\0') + if (offset && *offset == '\0') { + _isds_yday2mday(time); return IE_SUCCESS; + } return IE_NOTSUP; } diff --git a/src/utils.c b/src/utils.c index d4a4c83..7eeca00 100644 --- a/src/utils.c +++ b/src/utils.c @@ -330,8 +330,33 @@ _hidden int _isds_hex2i(char digit) { return -1; } +#if HAVE_LIBCURL +/* Convert struct tm with tm_year and tm_yday to tm_year, tm_mon, and tm_mday. + * Neither strptime(), nor mktime() do it. + */ +_hidden void _isds_yday2mday(struct tm *time) { + int mtab[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + int year = 1900 + time->tm_year; + int i; + + if (!(year % 4) && ((year % 100) || !(year % 400))) + mtab[1] = 29; + + time->tm_mday = time->tm_yday + 1; + for (i = 0; i < 12; i++) { + if (time->tm_mday > mtab[i]) { + time->tm_mday -= mtab[i]; + } else { + break; + } + } + + time->tm_mon = i; +} +#endif /* Convert size_t to int. */ _hidden int _isds_sizet2int(size_t val) { return (val <= INT_MAX) ? (int) val : -1; } + diff --git a/src/utils.h b/src/utils.h index aa1fa84..edeaa46 100644 --- a/src/utils.h +++ b/src/utils.h @@ -98,6 +98,12 @@ int _isds_hex2i(char digit); * @return (time_t) -1 in case of error */ time_t _isds_timegm(struct tm *broken_utc); +#if HAVE_LIBCURL +/* Convert struct tm with tm_year and tm_yday to tm_year, tm_mon, and tm_mday. + */ +void _isds_yday2mday(struct tm *time); +#endif + /* Convert size_t to int. * @val Value to be converted to int. * @return value converted to int or -1 when the supplied value is too large diff --git a/src/win32.c b/src/win32.c index 8492cdc..91e90a9 100644 --- a/src/win32.c +++ b/src/win32.c @@ -6,24 +6,6 @@ #include "win32.h" #if HAVE_LIBCURL -static void yday2mday(struct tm *time) { - static int mtab[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; - int i, year = 1900 + time->tm_year; - - mtab[1] = (!(year % 4) && ((year % 100) || !(year %400))) ? 29 : 28; - time->tm_mday = time->tm_yday + 1; - - for (i = 0; i < 12; i++) { - if (time->tm_mday > mtab[i]) { - time->tm_mday -= mtab[i]; - } else { - break; - } - } - - time->tm_mon = i; -} - /* Convert UTF-8 @string representation of ISO 8601 date to @time. * XXX: Not all ISO formats are supported */ isds_error _isds_datestring2tm(const xmlChar *string, struct tm *time) { @@ -48,7 +30,7 @@ isds_error _isds_datestring2tm(const xmlChar *string, struct tm *time) { && tmp == strlen((const char*)string)) { time->tm_yday--; time->tm_year -= 1900; - yday2mday(time); + _isds_yday2mday(time); return IE_SUCCESS; } -- 2.11.4.GIT