From a9f3cde30426b546ba6e3172e1a7951213a72049 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Wed, 28 Feb 2024 15:43:44 -0500 Subject: xtime: Fix some xgetdate() bugs And add some more test cases. --- src/xtime.c | 33 +++++++++++++------ tests/xtime.c | 102 ++++++++++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 117 insertions(+), 18 deletions(-) diff --git a/src/xtime.c b/src/xtime.c index e90bdb1..4309289 100644 --- a/src/xtime.c +++ b/src/xtime.c @@ -174,19 +174,29 @@ overflow: return -1; } +/** Parse a decimal digit. */ +static int xgetdigit(char c) { + int ret = c - '0'; + if (ret < 0 || ret > 9) { + return -1; + } else { + return ret; + } +} + /** Parse some digits from a timestamp. */ static int xgetpart(const char **str, size_t n, int *result) { - char buf[n + 1]; + *result = 0; + for (size_t i = 0; i < n; ++i, ++*str) { - char c = **str; - if (c < '0' || c > '9') { + int dig = xgetdigit(**str); + if (dig < 0) { return -1; } - buf[i] = c; + *result *= 10; + *result += dig; } - buf[n] = '\0'; - *result = atoi(buf); return 0; } @@ -239,6 +249,8 @@ int xgetdate(const char *str, struct timespec *result) { goto end; } else if (*str == ':') { ++str; + } else if (xgetdigit(*str) < 0) { + goto zone; } if (xgetpart(&str, 2, &tm.tm_min) != 0) { goto invalid; @@ -249,11 +261,14 @@ int xgetdate(const char *str, struct timespec *result) { goto end; } else if (*str == ':') { ++str; + } else if (xgetdigit(*str) < 0) { + goto zone; } if (xgetpart(&str, 2, &tm.tm_sec) != 0) { goto invalid; } +zone: if (!*str) { goto end; } else if (*str == 'Z') { @@ -296,11 +311,11 @@ end: goto error; } - int offset = 60 * tz_hour + tz_min; + int offset = (tz_hour * 60 + tz_min) * 60; if (tz_negative) { - result->tv_sec -= offset; - } else { result->tv_sec += offset; + } else { + result->tv_sec -= offset; } } diff --git a/tests/xtime.c b/tests/xtime.c index 53ecbc4..c2ee8f3 100644 --- a/tests/xtime.c +++ b/tests/xtime.c @@ -3,7 +3,9 @@ #include "tests.h" #include "../src/xtime.h" +#include "../src/bfstd.h" #include "../src/config.h" +#include #include #include #include @@ -49,22 +51,92 @@ static void tm_print(FILE *file, const struct tm *tm) { tm->tm_isdst ? (tm->tm_isdst < 0 ? " (DST?)" : " (DST)") : ""); } -bool check_xtime(void) { - if (setenv("TZ", "UTC0", true) != 0) { - perror("setenv()"); +/** Check one xgetdate() result. */ +static bool compare_xgetdate(const char *str, int error, time_t expected) { + struct timespec ts; + int ret = xgetdate(str, &ts); + + if (error) { + if (ret != -1 || errno != error) { + fprintf(stderr, "xgetdate('%s'): %s\n", str, xstrerror(errno)); + return false; + } + } else if (ret != 0) { + fprintf(stderr, "xgetdate('%s'): %s\n", str, xstrerror(errno)); + return false; + } else if (ts.tv_sec != expected || ts.tv_nsec != 0) { + fprintf(stderr, "xgetdate('%s'): %jd.%09jd != %jd\n", str, (intmax_t)ts.tv_sec, (intmax_t)ts.tv_nsec, (intmax_t)expected); return false; } + return true; +} + +/** xgetdate() tests. */ +static bool check_xgetdate(void) { + return compare_xgetdate("", EINVAL, 0) + & compare_xgetdate("????", EINVAL, 0) + & compare_xgetdate("1991", EINVAL, 0) + & compare_xgetdate("1991-??", EINVAL, 0) + & compare_xgetdate("1991-12", EINVAL, 0) + & compare_xgetdate("1991-12-", EINVAL, 0) + & compare_xgetdate("1991-12-??", EINVAL, 0) + & compare_xgetdate("1991-12-14", 0, 692668800) + & compare_xgetdate("1991-12-14-", EINVAL, 0) + & compare_xgetdate("1991-12-14T", EINVAL, 0) + & compare_xgetdate("1991-12-14T??", EINVAL, 0) + & compare_xgetdate("1991-12-14T10", 0, 692704800) + & compare_xgetdate("1991-12-14T10:??", EINVAL, 0) + & compare_xgetdate("1991-12-14T10:11", 0, 692705460) + & compare_xgetdate("1991-12-14T10:11:??", EINVAL, 0) + & compare_xgetdate("1991-12-14T10:11:12", 0, 692705472) + & compare_xgetdate("1991-12-14T10Z", 0, 692704800) + & compare_xgetdate("1991-12-14T10:11Z", 0, 692705460) + & compare_xgetdate("1991-12-14T10:11:12Z", 0, 692705472) + & compare_xgetdate("1991-12-14T10:11:12?", EINVAL, 0) + & compare_xgetdate("1991-12-14T02-08", 0, 692704800) + & compare_xgetdate("1991-12-14T06:41-03:30", 0, 692705460) + & compare_xgetdate("1991-12-14T02:11:12-08:00", 0, 692705472) + & compare_xgetdate("19911214 021112-0800", 0, 692705472); +} + +/** xmktime() tests. */ +static bool check_xmktime(void) { + for (time_t time = -10; time <= 10; ++time) { + struct tm tm; + if (xlocaltime(&time, &tm) != 0) { + perror("xlocaltime()"); + return false; + } + + time_t made; + if (xmktime(&tm, &made) != 0) { + perror("xmktime()"); + return false; + } + + if (time != made) { + fprintf(stderr, "Mismatch: %jd != %jd\n", (intmax_t)time, (intmax_t)made); + tm_print(stderr, &tm); + return false; + } + } + + return true; +} + +/** xtimegm() tests. */ +static bool check_xtimegm(void) { struct tm tm = { .tm_isdst = -1, }; - for (tm.tm_year = 10; tm.tm_year <= 200; tm.tm_year += 10) - for (tm.tm_mon = -3; tm.tm_mon <= 15; tm.tm_mon += 3) - for (tm.tm_mday = -31; tm.tm_mday <= 61; tm.tm_mday += 4) - for (tm.tm_hour = -1; tm.tm_hour <= 24; tm.tm_hour += 5) - for (tm.tm_min = -1; tm.tm_min <= 60; tm.tm_min += 31) - for (tm.tm_sec = -60; tm.tm_sec <= 120; tm.tm_sec += 5) { + for (tm.tm_year = 10; tm.tm_year <= 200; tm.tm_year += 10) + for (tm.tm_mon = -3; tm.tm_mon <= 15; tm.tm_mon += 3) + for (tm.tm_mday = -31; tm.tm_mday <= 61; tm.tm_mday += 4) + for (tm.tm_hour = -1; tm.tm_hour <= 24; tm.tm_hour += 5) + for (tm.tm_min = -1; tm.tm_min <= 60; tm.tm_min += 31) + for (tm.tm_sec = -60; tm.tm_sec <= 120; tm.tm_sec += 5) { struct tm tma = tm, tmb = tm; time_t ta, tb; ta = mktime(&tma); @@ -93,3 +165,15 @@ bool check_xtime(void) { return true; } + +bool check_xtime(void) { + if (setenv("TZ", "UTC0", true) != 0) { + perror("setenv()"); + return false; + } + tzset(); + + return check_xgetdate() + & check_xmktime() + & check_xtimegm(); +} -- cgit v1.2.3