summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTavian Barnes <tavianator@tavianator.com>2020-02-13 17:08:23 -0500
committerTavian Barnes <tavianator@tavianator.com>2020-02-13 17:08:23 -0500
commit1ca8b4d9e81bfe7bed51fa6b858d59b721143054 (patch)
tree2f5347c32bb5e9e7685a18bbcbf8ffe4ab3d1ddf
parent95f862fdd82a99e30bbf2c43009ec9a51e416804 (diff)
downloadbfs-1ca8b4d9e81bfe7bed51fa6b858d59b721143054.tar.xz
time: Split out time-related functions from util
-rw-r--r--Makefile1
-rw-r--r--eval.c1
-rw-r--r--main.c1
-rw-r--r--parse.c159
-rw-r--r--printf.c1
-rw-r--r--time.c247
-rw-r--r--time.h86
-rw-r--r--util.c86
-rw-r--r--util.h47
9 files changed, 351 insertions, 278 deletions
diff --git a/Makefile b/Makefile
index f25fd12..d6183ab 100644
--- a/Makefile
+++ b/Makefile
@@ -89,6 +89,7 @@ bfs: \
printf.o \
spawn.o \
stat.o \
+ time.o \
trie.o \
typo.o \
util.o
diff --git a/eval.c b/eval.c
index ab885fc..23c7b1b 100644
--- a/eval.c
+++ b/eval.c
@@ -30,6 +30,7 @@
#include "mtab.h"
#include "printf.h"
#include "stat.h"
+#include "time.h"
#include "trie.h"
#include "util.h"
#include <assert.h>
diff --git a/main.c b/main.c
index abab187..b8d7bae 100644
--- a/main.c
+++ b/main.c
@@ -42,6 +42,7 @@
* - mtab.[ch] (parses the system's mount table)
* - spawn.[ch] (spawns processes)
* - stat.[ch] (wraps stat(), or statx() on Linux)
+ * - time.[ch] (date/time handling utilities)
* - trie.[ch] (a trie set/map implementation)
* - typo.[ch] (fuzzy matching for typos)
* - util.[ch] (everything else)
diff --git a/parse.c b/parse.c
index ada0ec2..509d835 100644
--- a/parse.c
+++ b/parse.c
@@ -34,6 +34,7 @@
#include "printf.h"
#include "spawn.h"
#include "stat.h"
+#include "time.h"
#include "typo.h"
#include "util.h"
#include <assert.h>
@@ -1635,147 +1636,19 @@ static enum bfs_stat_field parse_newerxy_field(char c) {
}
}
-/** Parse some digits from an explicit reference time. */
-static int parse_timestamp_part(const struct parser_state *state, const char **str, size_t n, int *result, int delta) {
- char buf[n + 1];
- for (size_t i = 0; i < n; ++i, ++*str) {
- char c = **str;
- if (c < '0' || c > '9') {
- return -1;
- }
- buf[i] = c;
- }
- buf[n] = '\0';
-
- if (!parse_int(state, buf, result, IF_INT | IF_QUIET)) {
- return -1;
- }
-
- *result += delta;
- return 0;
-}
-
/** Parse an explicit reference timestamp for -newerXt and -*since. */
-static int parse_timestamp(const struct parser_state *state, struct expr *expr) {
- const char *str = expr->sdata;
- struct tm tm = {
- .tm_isdst = -1,
- };
-
- int tz_hour = 0;
- int tz_min = 0;
- bool tz_negative = false;
- bool local = true;
-
- // YYYY
- if (parse_timestamp_part(state, &str, 4, &tm.tm_year, -1900) != 0) {
- goto invalid;
- }
-
- // MM
- if (*str == '-') {
- ++str;
- }
- if (parse_timestamp_part(state, &str, 2, &tm.tm_mon, -1) != 0) {
- goto invalid;
- }
-
- // DD
- if (*str == '-') {
- ++str;
- }
- if (parse_timestamp_part(state, &str, 2, &tm.tm_mday, 0) != 0) {
- goto invalid;
- }
-
- if (!*str) {
- goto end;
- } else if (*str == 'T') {
- ++str;
- }
-
- // hh
- if (parse_timestamp_part(state, &str, 2, &tm.tm_hour, 0) != 0) {
- goto invalid;
- }
-
- // mm
- if (!*str) {
- goto end;
- } else if (*str == ':') {
- ++str;
- }
- if (parse_timestamp_part(state, &str, 2, &tm.tm_min, 0) != 0) {
- goto invalid;
- }
-
- // ss
- if (!*str) {
- goto end;
- } else if (*str == ':') {
- ++str;
- }
- if (parse_timestamp_part(state, &str, 2, &tm.tm_sec, 0) != 0) {
- goto invalid;
- }
-
- if (!*str) {
- goto end;
- } else if (*str == 'Z') {
- local = false;
- ++str;
- } else if (*str == '+' || *str == '-') {
- local = false;
- tz_negative = *str == '-';
- ++str;
-
- // hh
- if (parse_timestamp_part(state, &str, 2, &tz_hour, 0) != 0) {
- goto invalid;
- }
-
- // mm
- if (!*str) {
- goto end;
- } else if (*str == ':') {
- ++str;
- }
- if (parse_timestamp_part(state, &str, 2, &tz_min, 0) != 0) {
- goto invalid;
- }
- } else {
- goto invalid;
- }
-
- if (*str) {
- goto invalid;
- }
-
-end:
- if (local) {
- if (xmktime(&tm, &expr->reftime.tv_sec) != 0) {
- goto error;
- }
- } else {
- if (xtimegm(&tm, &expr->reftime.tv_sec) != 0) {
- goto error;
- }
-
- int offset = 60*tz_hour + tz_min;
- if (tz_negative) {
- expr->reftime.tv_sec -= offset;
- } else {
- expr->reftime.tv_sec += offset;
- }
+static int parse_reftime(const struct parser_state *state, struct expr *expr) {
+ if (parse_timestamp(expr->sdata, &expr->reftime) == 0) {
+ return 0;
+ } else if (errno != EINVAL) {
+ parse_error(state, "%s %s: %m.\n", expr->argv[0], expr->argv[1]);
+ return -1;
}
- expr->reftime.tv_nsec = 0;
- return 0;
-
-invalid:
parse_error(state, "%s %s: Invalid timestamp.\n\n", expr->argv[0], expr->argv[1]);
fprintf(stderr, "Supported timestamp formats are ISO 8601-like, e.g.\n\n");
+ struct tm tm;
if (xlocaltime(&state->now.tv_sec, &tm) != 0) {
perror("xlocaltime()");
return -1;
@@ -1787,12 +1660,12 @@ invalid:
fprintf(stderr, " - %04d-%02d-%02dT%02d:%02d:%02d\n", year, month, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
#if __FreeBSD__
- tz_hour = tm.tm_gmtoff/3600;
- tz_min = (labs(tm.tm_gmtoff)/60)%60;
+ int gmtoff = tm.tm_gmtoff;
#else
- tz_hour = -timezone/3600;
- tz_min = (labs(timezone)/60)%60;
+ int gmtoff = -timezone;
#endif
+ int tz_hour = gmtoff/3600;
+ int tz_min = (labs(gmtoff)/60)%60;
fprintf(stderr, " - %04d-%02d-%02dT%02d:%02d:%02d%+03d:%02d\n",
year, month, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, tz_hour, tz_min);
@@ -1806,10 +1679,6 @@ invalid:
fprintf(stderr, " - %04d-%02d-%02dT%02d:%02d:%02dZ\n", year, month, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
return -1;
-
-error:
- parse_error(state, "%s %s: Error parsing timestamp: %m.\n", expr->argv[0], expr->argv[1]);
- return -1;
}
/**
@@ -1834,7 +1703,7 @@ static struct expr *parse_newerxy(struct parser_state *state, int arg1, int arg2
}
if (arg[7] == 't') {
- if (parse_timestamp(state, expr) != 0) {
+ if (parse_reftime(state, expr) != 0) {
goto fail;
}
} else {
@@ -2408,7 +2277,7 @@ static struct expr *parse_since(struct parser_state *state, int field, int arg2)
return NULL;
}
- if (parse_timestamp(state, expr) != 0) {
+ if (parse_reftime(state, expr) != 0) {
goto fail;
}
diff --git a/printf.c b/printf.c
index 80f54c9..4e7af18 100644
--- a/printf.c
+++ b/printf.c
@@ -22,6 +22,7 @@
#include "expr.h"
#include "mtab.h"
#include "stat.h"
+#include "time.h"
#include "util.h"
#include <assert.h>
#include <errno.h>
diff --git a/time.c b/time.c
new file mode 100644
index 0000000..3575abd
--- /dev/null
+++ b/time.c
@@ -0,0 +1,247 @@
+/****************************************************************************
+ * bfs *
+ * Copyright (C) 2020 Tavian Barnes <tavianator@tavianator.com> *
+ * *
+ * Permission to use, copy, modify, and/or distribute this software for any *
+ * purpose with or without fee is hereby granted. *
+ * *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES *
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF *
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR *
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES *
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN *
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF *
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. *
+ ****************************************************************************/
+
+#include "time.h"
+#include <errno.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+int xlocaltime(const time_t *timep, struct tm *result) {
+ // Should be called before localtime_r() according to POSIX.1-2004
+ tzset();
+
+ if (localtime_r(timep, result)) {
+ return 0;
+ } else {
+ return -1;
+ }
+}
+
+int xgmtime(const time_t *timep, struct tm *result) {
+ // Should be called before gmtime_r() according to POSIX.1-2004
+ tzset();
+
+ if (gmtime_r(timep, result)) {
+ return 0;
+ } else {
+ return -1;
+ }
+}
+
+int xmktime(struct tm *tm, time_t *timep) {
+ *timep = mktime(tm);
+
+ if (*timep == -1) {
+ int error = errno;
+
+ struct tm tmp;
+ if (xlocaltime(timep, &tmp) != 0) {
+ 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;
+ }
+ }
+
+ return 0;
+}
+
+int xtimegm(struct tm *tm, time_t *timep) {
+ // Some man pages for timegm() recommend this as a portable approach
+ int ret = -1;
+ int error;
+
+ char *old_tz = getenv("TZ");
+ if (old_tz) {
+ old_tz = strdup(old_tz);
+ if (!old_tz) {
+ error = errno;
+ goto fail;
+ }
+ }
+
+ if (setenv("TZ", "UTC0", true) != 0) {
+ error = errno;
+ goto fail;
+ }
+
+ ret = xmktime(tm, timep);
+ error = errno;
+
+ if (old_tz) {
+ if (setenv("TZ", old_tz, true) != 0) {
+ ret = -1;
+ error = errno;
+ goto fail;
+ }
+ } else {
+ if (unsetenv("TZ") != 0) {
+ ret = -1;
+ error = errno;
+ goto fail;
+ }
+ }
+
+ tzset();
+fail:
+ free(old_tz);
+ errno = error;
+ return ret;
+}
+
+/** Parse some digits from a timestamp. */
+static int parse_timestamp_part(const char **str, size_t n, int *result) {
+ char buf[n + 1];
+ for (size_t i = 0; i < n; ++i, ++*str) {
+ char c = **str;
+ if (c < '0' || c > '9') {
+ return -1;
+ }
+ buf[i] = c;
+ }
+ buf[n] = '\0';
+
+ *result = atoi(buf);
+ return 0;
+}
+
+int parse_timestamp(const char *str, struct timespec *result) {
+ struct tm tm = {
+ .tm_isdst = -1,
+ };
+
+ int tz_hour = 0;
+ int tz_min = 0;
+ bool tz_negative = false;
+ bool local = true;
+
+ // YYYY
+ if (parse_timestamp_part(&str, 4, &tm.tm_year) != 0) {
+ goto invalid;
+ }
+ tm.tm_year -= 1900;
+
+ // MM
+ if (*str == '-') {
+ ++str;
+ }
+ if (parse_timestamp_part(&str, 2, &tm.tm_mon) != 0) {
+ goto invalid;
+ }
+ tm.tm_mon -= 1;
+
+ // DD
+ if (*str == '-') {
+ ++str;
+ }
+ if (parse_timestamp_part(&str, 2, &tm.tm_mday) != 0) {
+ goto invalid;
+ }
+
+ if (!*str) {
+ goto end;
+ } else if (*str == 'T') {
+ ++str;
+ }
+
+ // hh
+ if (parse_timestamp_part(&str, 2, &tm.tm_hour) != 0) {
+ goto invalid;
+ }
+
+ // mm
+ if (!*str) {
+ goto end;
+ } else if (*str == ':') {
+ ++str;
+ }
+ if (parse_timestamp_part(&str, 2, &tm.tm_min) != 0) {
+ goto invalid;
+ }
+
+ // ss
+ if (!*str) {
+ goto end;
+ } else if (*str == ':') {
+ ++str;
+ }
+ if (parse_timestamp_part(&str, 2, &tm.tm_sec) != 0) {
+ goto invalid;
+ }
+
+ if (!*str) {
+ goto end;
+ } else if (*str == 'Z') {
+ local = false;
+ ++str;
+ } else if (*str == '+' || *str == '-') {
+ local = false;
+ tz_negative = *str == '-';
+ ++str;
+
+ // hh
+ if (parse_timestamp_part(&str, 2, &tz_hour) != 0) {
+ goto invalid;
+ }
+
+ // mm
+ if (!*str) {
+ goto end;
+ } else if (*str == ':') {
+ ++str;
+ }
+ if (parse_timestamp_part(&str, 2, &tz_min) != 0) {
+ goto invalid;
+ }
+ } else {
+ goto invalid;
+ }
+
+ if (*str) {
+ goto invalid;
+ }
+
+end:
+ if (local) {
+ if (xmktime(&tm, &result->tv_sec) != 0) {
+ goto error;
+ }
+ } else {
+ if (xtimegm(&tm, &result->tv_sec) != 0) {
+ goto error;
+ }
+
+ int offset = 60*tz_hour + tz_min;
+ if (tz_negative) {
+ result->tv_sec -= offset;
+ } else {
+ result->tv_sec += offset;
+ }
+ }
+
+ result->tv_nsec = 0;
+ return 0;
+
+invalid:
+ errno = EINVAL;
+error:
+ return -1;
+}
diff --git a/time.h b/time.h
new file mode 100644
index 0000000..0f9adb4
--- /dev/null
+++ b/time.h
@@ -0,0 +1,86 @@
+/****************************************************************************
+ * bfs *
+ * Copyright (C) 2020 Tavian Barnes <tavianator@tavianator.com> *
+ * *
+ * Permission to use, copy, modify, and/or distribute this software for any *
+ * purpose with or without fee is hereby granted. *
+ * *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES *
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF *
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR *
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES *
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN *
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF *
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. *
+ ****************************************************************************/
+
+/**
+ * Date/time handling.
+ */
+
+#ifndef BFS_TIME_H
+#define BFS_TIME_H
+
+#include <time.h>
+
+/**
+ * localtime_r() wrapper that calls tzset() first.
+ *
+ * @param[in] timep
+ * The time_t to convert.
+ * @param[out] result
+ * Buffer to hold the result.
+ * @return
+ * 0 on success, -1 on failure.
+ */
+int xlocaltime(const time_t *timep, struct tm *result);
+
+/**
+ * gmtime_r() wrapper that calls tzset() first.
+ *
+ * @param[in] timep
+ * The time_t to convert.
+ * @param[out] result
+ * Buffer to hold the result.
+ * @return
+ * 0 on success, -1 on failure.
+ */
+int xgmtime(const time_t *timep, struct tm *result);
+
+/**
+ * mktime() wrapper that reports errors more reliably.
+ *
+ * @param[in,out] tm
+ * The struct tm to convert.
+ * @param[out] timep
+ * A pointer to the result.
+ * @return
+ * 0 on success, -1 on failure.
+ */
+int xmktime(struct tm *tm, time_t *timep);
+
+/**
+ * A portable timegm(), the inverse of gmtime().
+ *
+ * @param[in,out] tm
+ * The struct tm to convert.
+ * @param[out] timep
+ * A pointer to the result.
+ * @return
+ * 0 on success, -1 on failure.
+ */
+int xtimegm(struct tm *tm, time_t *timep);
+
+/**
+ * Parse an ISO 8601-style timestamp.
+ *
+ * @param[in] str
+ * The string to parse.
+ * @param[out] result
+ * A pointer to the result.
+ * @return
+ * 0 on success, -1 on failure.
+ */
+int parse_timestamp(const char *str, struct timespec *result);
+
+#endif // BFS_TIME_H
diff --git a/util.c b/util.c
index 6af7d27..ec019ce 100644
--- a/util.c
+++ b/util.c
@@ -163,92 +163,6 @@ char *xregerror(int err, const regex_t *regex) {
return str;
}
-int xlocaltime(const time_t *timep, struct tm *result) {
- // Should be called before localtime_r() according to POSIX.1-2004
- tzset();
-
- if (localtime_r(timep, result)) {
- return 0;
- } else {
- return -1;
- }
-}
-
-int xgmtime(const time_t *timep, struct tm *result) {
- // Should be called before gmtime_r() according to POSIX.1-2004
- tzset();
-
- if (gmtime_r(timep, result)) {
- return 0;
- } else {
- return -1;
- }
-}
-
-int xmktime(struct tm *tm, time_t *timep) {
- *timep = mktime(tm);
-
- if (*timep == -1) {
- int error = errno;
-
- struct tm tmp;
- if (xlocaltime(timep, &tmp) != 0) {
- 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;
- }
- }
-
- return 0;
-}
-
-int xtimegm(struct tm *tm, time_t *timep) {
- // Some man pages for timegm() recommend this as a portable approach
- int ret = -1;
- int error;
-
- char *old_tz = getenv("TZ");
- if (old_tz) {
- old_tz = strdup(old_tz);
- if (!old_tz) {
- error = errno;
- goto fail;
- }
- }
-
- if (setenv("TZ", "UTC0", true) != 0) {
- error = errno;
- goto fail;
- }
-
- ret = xmktime(tm, timep);
- error = errno;
-
- if (old_tz) {
- if (setenv("TZ", old_tz, true) != 0) {
- ret = -1;
- error = errno;
- goto fail;
- }
- } else {
- if (unsetenv("TZ") != 0) {
- ret = -1;
- error = errno;
- goto fail;
- }
- }
-
- tzset();
-fail:
- free(old_tz);
- errno = error;
- return ret;
-}
-
void format_mode(mode_t mode, char str[11]) {
strcpy(str, "----------");
diff --git a/util.h b/util.h
index ae12703..6e7c1f2 100644
--- a/util.h
+++ b/util.h
@@ -27,7 +27,6 @@
#include <regex.h>
#include <stdbool.h>
#include <sys/types.h>
-#include <time.h>
// Some portability concerns
@@ -160,52 +159,6 @@ int pipe_cloexec(int pipefd[2]);
char *xregerror(int err, const regex_t *regex);
/**
- * localtime_r() wrapper that calls tzset() first.
- *
- * @param timep
- * The time_t to convert.
- * @param result
- * Buffer to hold the result.
- * @return 0 on success, -1 on failure.
- */
-int xlocaltime(const time_t *timep, struct tm *result);
-
-/**
- * gmtime_r() wrapper that calls tzset() first.
- *
- * @param timep
- * The time_t to convert.
- * @param result
- * Buffer to hold the result.
- * @return 0 on success, -1 on failure.
- */
-int xgmtime(const time_t *timep, struct tm *result);
-
-/**
- * mktime() wrapper that reports errors more reliably.
- *
- * @param[in,out] tm
- * The struct tm to convert.
- * @param[out] timep
- * Where to store the result.
- * @return
- * 0 on success, -1 on failure.
- */
-int xmktime(struct tm *tm, time_t *timep);
-
-/**
- * A portable timegm(), the inverse of gmtime().
- *
- * @param[in,out] tm
- * The struct tm to convert.
- * @param[out] timep
- * Where to store the result.
- * @return
- * 0 on success, -1 on failure.
- */
-int xtimegm(struct tm *tm, time_t *timep);
-
-/**
* Format a mode like ls -l (e.g. -rw-r--r--).
*
* @param mode