summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTavian Barnes <tavianator@tavianator.com>2020-02-13 16:30:05 -0500
committerTavian Barnes <tavianator@tavianator.com>2020-02-13 16:39:30 -0500
commit95f862fdd82a99e30bbf2c43009ec9a51e416804 (patch)
tree0edafaf7a69935d56dc4ce8b442325e12dfd88ff
parent2cdc648441794db0f84518f79e8aaf3ead68f110 (diff)
downloadbfs-95f862fdd82a99e30bbf2c43009ec9a51e416804.tar.xz
parse: Handle 1969-12-31T23:59:59Z
mktime() returns -1 on error, but also for one second before the epoch. Compare the input against localtime(-1) to distinguish those cases.
-rw-r--r--parse.c27
-rwxr-xr-xtests.sh6
-rw-r--r--tests/test_newermt_epoch_minus_one.out5
-rw-r--r--util.c27
-rw-r--r--util.h20
5 files changed, 66 insertions, 19 deletions
diff --git a/parse.c b/parse.c
index 6ffe8e7..ada0ec2 100644
--- a/parse.c
+++ b/parse.c
@@ -1129,9 +1129,9 @@ static struct expr *parse_daystart(struct parser_state *state, int arg1, int arg
tm.tm_min = 0;
tm.tm_sec = 0;
- time_t time = mktime(&tm);
- if (time == -1) {
- perror("mktime()");
+ time_t time;
+ if (xmktime(&tm, &time) != 0) {
+ perror("xmktime()");
return NULL;
}
@@ -1753,23 +1753,20 @@ static int parse_timestamp(const struct parser_state *state, struct expr *expr)
end:
if (local) {
- expr->reftime.tv_sec = mktime(&tm);
- if (expr->reftime.tv_sec == -1) {
- perror("mktime()");
- return -1;
+ if (xmktime(&tm, &expr->reftime.tv_sec) != 0) {
+ goto error;
}
} else {
- expr->reftime.tv_sec = xtimegm(&tm);
- if (expr->reftime.tv_sec == -1) {
- perror("xtimegm()");
- return -1;
+ if (xtimegm(&tm, &expr->reftime.tv_sec) != 0) {
+ goto error;
}
int offset = 60*tz_hour + tz_min;
if (tz_negative) {
- offset = -offset;
+ expr->reftime.tv_sec -= offset;
+ } else {
+ expr->reftime.tv_sec += offset;
}
- expr->reftime.tv_sec += offset;
}
expr->reftime.tv_nsec = 0;
@@ -1809,6 +1806,10 @@ 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;
}
/**
diff --git a/tests.sh b/tests.sh
index 3893251..d1c5413 100755
--- a/tests.sh
+++ b/tests.sh
@@ -343,6 +343,7 @@ bsd_tests=(
test_newerma
test_newermt
+ test_newermt_epoch_minus_one
test_nogroup
test_nogroup_ulimit
@@ -492,6 +493,7 @@ gnu_tests=(
test_newerma
test_newermt
+ test_newermt_epoch_minus_one
test_nogroup
test_nogroup_ulimit
@@ -1364,6 +1366,10 @@ function test_newermt() {
bfs_diff times -newermt 1991-12-14T00:01
}
+function test_newermt_epoch_minus_one() {
+ bfs_diff times -newermt 1969-12-31T23:59:59Z
+}
+
function test_size() {
bfs_diff basic -type f -size 0
}
diff --git a/tests/test_newermt_epoch_minus_one.out b/tests/test_newermt_epoch_minus_one.out
new file mode 100644
index 0000000..f7f63b0
--- /dev/null
+++ b/tests/test_newermt_epoch_minus_one.out
@@ -0,0 +1,5 @@
+times
+times/a
+times/b
+times/c
+times/l
diff --git a/util.c b/util.c
index 88f90aa..6af7d27 100644
--- a/util.c
+++ b/util.c
@@ -185,9 +185,30 @@ int xgmtime(const time_t *timep, struct tm *result) {
}
}
-time_t xtimegm(struct tm *tm) {
+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
- time_t ret = -1;
+ int ret = -1;
int error;
char *old_tz = getenv("TZ");
@@ -204,7 +225,7 @@ time_t xtimegm(struct tm *tm) {
goto fail;
}
- ret = mktime(tm);
+ ret = xmktime(tm, timep);
error = errno;
if (old_tz) {
diff --git a/util.h b/util.h
index ef546d4..ae12703 100644
--- a/util.h
+++ b/util.h
@@ -182,14 +182,28 @@ int xlocaltime(const time_t *timep, struct tm *result);
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 tm
+ * @param[in,out] tm
* The struct tm to convert.
+ * @param[out] timep
+ * Where to store the result.
* @return
- * The converted time on success, or -1 on failure.
+ * 0 on success, -1 on failure.
*/
-time_t xtimegm(struct tm *tm);
+int xtimegm(struct tm *tm, time_t *timep);
/**
* Format a mode like ls -l (e.g. -rw-r--r--).