summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTavian Barnes <tavianator@tavianator.com>2024-05-06 16:04:05 -0400
committerTavian Barnes <tavianator@tavianator.com>2024-05-06 16:04:05 -0400
commitf976c98d334dce9ba30aa7da4427bb530aeea536 (patch)
treeff48eba03fc0ce3a3497118142b87ebaa6865bf7
parentc74947f39063218f3cbd884e4ebaafe8dfc9302c (diff)
downloadbfs-f976c98d334dce9ba30aa7da4427bb530aeea536.tar.xz
xtime: Use the libc's timegm() if present
-rw-r--r--build/has/timegm.c9
-rw-r--r--build/header.mk1
-rw-r--r--src/xtime.c36
-rw-r--r--tests/xtime.c12
4 files changed, 51 insertions, 7 deletions
diff --git a/build/has/timegm.c b/build/has/timegm.c
new file mode 100644
index 0000000..6e2d155
--- /dev/null
+++ b/build/has/timegm.c
@@ -0,0 +1,9 @@
+// Copyright © Tavian Barnes <tavianator@tavianator.com>
+// SPDX-License-Identifier: 0BSD
+
+#include <time.h>
+
+int main(void) {
+ struct tm tm = {0};
+ return (int)timegm(&tm);
+}
diff --git a/build/header.mk b/build/header.mk
index a9157ad..d235fd0 100644
--- a/build/header.mk
+++ b/build/header.mk
@@ -40,6 +40,7 @@ HEADERS := \
gen/has/strerror-l.h \
gen/has/strerror-r-gnu.h \
gen/has/strerror-r-posix.h \
+ gen/has/timegm.h \
gen/has/tm-gmtoff.h \
gen/has/uselocale.h
diff --git a/src/xtime.c b/src/xtime.c
index 91ed915..c3537e7 100644
--- a/src/xtime.c
+++ b/src/xtime.c
@@ -12,13 +12,13 @@
#include <unistd.h>
int xmktime(struct tm *tm, time_t *timep) {
- *timep = mktime(tm);
+ time_t time = mktime(tm);
- if (*timep == -1) {
+ if (time == -1) {
int error = errno;
struct tm tmp;
- if (!localtime_r(timep, &tmp)) {
+ if (!localtime_r(&time, &tmp)) {
bfs_bug("localtime_r(-1): %s", xstrerror(errno));
return -1;
}
@@ -30,9 +30,37 @@ int xmktime(struct tm *tm, time_t *timep) {
}
}
+ *timep = time;
+ return 0;
+}
+
+#if BFS_HAS_TIMEGM
+
+int xtimegm(struct tm *tm, time_t *timep) {
+ time_t time = timegm(tm);
+
+ if (time == -1) {
+ int error = errno;
+
+ struct tm tmp;
+ if (!gmtime_r(&time, &tmp)) {
+ bfs_bug("gmtime_r(-1): %s", xstrerror(errno));
+ return -1;
+ }
+
+ if (tm->tm_year != tmp.tm_year || tm->tm_yday != tmp.tm_yday
+ || tm->tm_hour != tmp.tm_hour || tm->tm_min != tmp.tm_min || tm->tm_sec != tmp.tm_sec) {
+ errno = error;
+ return -1;
+ }
+ }
+
+ *timep = time;
return 0;
}
+#else
+
static int safe_add(int *value, int delta) {
if (*value >= 0) {
if (delta > INT_MAX - *value) {
@@ -147,6 +175,8 @@ overflow:
return -1;
}
+#endif // !BFS_HAS_TIMEGM
+
/** Parse a decimal digit. */
static int xgetdigit(char c) {
int ret = c - '0';
diff --git a/tests/xtime.c b/tests/xtime.c
index a7c63d2..d9d6c5c 100644
--- a/tests/xtime.c
+++ b/tests/xtime.c
@@ -137,6 +137,7 @@ static bool check_one_xtimegm(const struct tm *tm) {
return ret;
}
+#if !BFS_HAS_TIMEGM
/** Check an overflowing xtimegm() call. */
static bool check_xtimegm_overflow(const struct tm *tm) {
struct tm copy = *tm;
@@ -154,6 +155,7 @@ static bool check_xtimegm_overflow(const struct tm *tm) {
return ret;
}
+#endif
/** xtimegm() tests. */
static bool check_xtimegm(void) {
@@ -173,11 +175,13 @@ static bool check_xtimegm(void) {
ret &= check_one_xtimegm(&tm);
}
+#if !BFS_HAS_TIMEGM
// Check integer overflow cases
- check_xtimegm_overflow(&(struct tm) { .tm_sec = INT_MAX, .tm_min = INT_MAX });
- check_xtimegm_overflow(&(struct tm) { .tm_min = INT_MAX, .tm_hour = INT_MAX });
- check_xtimegm_overflow(&(struct tm) { .tm_hour = INT_MAX, .tm_mday = INT_MAX });
- check_xtimegm_overflow(&(struct tm) { .tm_mon = INT_MAX, .tm_year = INT_MAX });
+ ret &= check_xtimegm_overflow(&(struct tm) { .tm_sec = INT_MAX, .tm_min = INT_MAX });
+ ret &= check_xtimegm_overflow(&(struct tm) { .tm_min = INT_MAX, .tm_hour = INT_MAX });
+ ret &= check_xtimegm_overflow(&(struct tm) { .tm_hour = INT_MAX, .tm_mday = INT_MAX });
+ ret &= check_xtimegm_overflow(&(struct tm) { .tm_mon = INT_MAX, .tm_year = INT_MAX });
+#endif
return ret;
}