From 2cdc648441794db0f84518f79e8aaf3ead68f110 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Wed, 12 Feb 2020 13:41:46 -0500 Subject: Implement -{a,B,c,m,}since --- bfs.1 | 40 ++++++++++++++++++++++++++++ parse.c | 72 +++++++++++++++++++++++++++++++++++++-------------- tests.sh | 13 ++++++++++ tests/test_asince.out | 3 +++ tests/test_msince.out | 3 +++ 5 files changed, 111 insertions(+), 20 deletions(-) create mode 100644 tests/test_asince.out create mode 100644 tests/test_msince.out diff --git a/bfs.1 b/bfs.1 index 7d58f13..5322f3c 100644 --- a/bfs.1 +++ b/bfs.1 @@ -311,6 +311,23 @@ more recently than was modified. .RE .PP +\fB\-asince \fITIME\fR +.br +\fB\-Bsince \fITIME\fR +.br +\fB\-csince \fITIME\fR +.br +\fB\-msince \fITIME\fR +.RS +Find files +.BR a ccessed/ B irthed/ c hanged/ m odified +more recently than the ISO 8601-style timestamp +.IR TIME . +See +.BI \-newer XY +for examples of the timestamp format. +.RE +.PP \fB\-atime\fR [\fI\-+\fR]\fIN\fR .br \fB\-Btime\fR [\fI\-+\fR]\fIN\fR @@ -425,6 +442,22 @@ and can be any of .RI [ aBcm ] .RI ( a ccess/ B irth/ c hange/ m odification). +.I Y +may also be +.I t +to parse +.I REFERENCE +as an ISO 8601-style timestamp. For example: +.PP +.RS +1991-12-14 +.br +1991-12-14T03:00 +.br +1991-12-14T03:00-07:00 +.br +1991-12-14T10:00Z +.RE .PP .B \-nogroup .br @@ -452,6 +485,13 @@ Find files whose entire path matches the regular expression Find hard links to .IR FILE . .TP +\fB\-since \fITIME\fR +Find files modified since the ISO 8601-style timestamp +.IR TIME . +See +.BI \-newer XY +for examples of the timestamp format. +.TP \fB\-size\fR [\fI\-+\fR]\fIN\fR[\fIcwbkMGTP\fR] Find files with the given size, in 1-byte .IR c haracters, diff --git a/parse.c b/parse.c index 30ed320..6ffe8e7 100644 --- a/parse.c +++ b/parse.c @@ -1636,7 +1636,7 @@ static enum bfs_stat_field parse_newerxy_field(char c) { } /** Parse some digits from an explicit reference time. */ -static int parse_reftime_part(const struct parser_state *state, const char **str, size_t n, int *result, int delta) { +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; @@ -1655,8 +1655,8 @@ static int parse_reftime_part(const struct parser_state *state, const char **str return 0; } -/** Parse an explicit reference time. */ -static int parse_reftime(const struct parser_state *state, struct expr *expr) { +/** 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, @@ -1668,7 +1668,7 @@ static int parse_reftime(const struct parser_state *state, struct expr *expr) { bool local = true; // YYYY - if (parse_reftime_part(state, &str, 4, &tm.tm_year, -1900) != 0) { + if (parse_timestamp_part(state, &str, 4, &tm.tm_year, -1900) != 0) { goto invalid; } @@ -1676,7 +1676,7 @@ static int parse_reftime(const struct parser_state *state, struct expr *expr) { if (*str == '-') { ++str; } - if (parse_reftime_part(state, &str, 2, &tm.tm_mon, -1) != 0) { + if (parse_timestamp_part(state, &str, 2, &tm.tm_mon, -1) != 0) { goto invalid; } @@ -1684,7 +1684,7 @@ static int parse_reftime(const struct parser_state *state, struct expr *expr) { if (*str == '-') { ++str; } - if (parse_reftime_part(state, &str, 2, &tm.tm_mday, 0) != 0) { + if (parse_timestamp_part(state, &str, 2, &tm.tm_mday, 0) != 0) { goto invalid; } @@ -1694,28 +1694,28 @@ static int parse_reftime(const struct parser_state *state, struct expr *expr) { ++str; } - // HH - if (parse_reftime_part(state, &str, 2, &tm.tm_hour, 0) != 0) { + // hh + if (parse_timestamp_part(state, &str, 2, &tm.tm_hour, 0) != 0) { goto invalid; } - // MM + // mm if (!*str) { goto end; } else if (*str == ':') { ++str; } - if (parse_reftime_part(state, &str, 2, &tm.tm_min, 0) != 0) { + if (parse_timestamp_part(state, &str, 2, &tm.tm_min, 0) != 0) { goto invalid; } - // SS + // ss if (!*str) { goto end; } else if (*str == ':') { ++str; } - if (parse_reftime_part(state, &str, 2, &tm.tm_sec, 0) != 0) { + if (parse_timestamp_part(state, &str, 2, &tm.tm_sec, 0) != 0) { goto invalid; } @@ -1729,18 +1729,18 @@ static int parse_reftime(const struct parser_state *state, struct expr *expr) { tz_negative = *str == '-'; ++str; - // HH - if (parse_reftime_part(state, &str, 2, &tz_hour, 0) != 0) { + // hh + if (parse_timestamp_part(state, &str, 2, &tz_hour, 0) != 0) { goto invalid; } - // MM + // mm if (!*str) { goto end; } else if (*str == ':') { ++str; } - if (parse_reftime_part(state, &str, 2, &tz_min, 0) != 0) { + if (parse_timestamp_part(state, &str, 2, &tz_min, 0) != 0) { goto invalid; } } else { @@ -1776,8 +1776,8 @@ end: return 0; invalid: - parse_error(state, "%s %s: Invalid date/time.\n\n", expr->argv[0], expr->argv[1]); - fprintf(stderr, "Supported date/time formats are ISO 8601-like, e.g.\n\n"); + 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"); if (xlocaltime(&state->now.tv_sec, &tm) != 0) { perror("xlocaltime()"); @@ -1833,7 +1833,7 @@ static struct expr *parse_newerxy(struct parser_state *state, int arg1, int arg2 } if (arg[7] == 't') { - if (parse_reftime(state, expr) != 0) { + if (parse_timestamp(state, expr) != 0) { goto fail; } } else { @@ -2398,6 +2398,28 @@ fail_list_strategies: return NULL; } +/** + * Parse -[aBc]?since. + */ +static struct expr *parse_since(struct parser_state *state, int field, int arg2) { + struct expr *expr = parse_unary_test(state, eval_newer); + if (!expr) { + return NULL; + } + + if (parse_timestamp(state, expr) != 0) { + goto fail; + } + + expr->cost = STAT_COST; + expr->stat_field = field; + return expr; + +fail: + free_expr(expr); + return NULL; +} + /** * Parse -size N[cwbkMGTP]?. */ @@ -2817,6 +2839,8 @@ static struct expr *parse_help(struct parser_state *state, int arg1, int arg2) { cfprintf(cout, " ${blu}-${rs}[${blu}aBcm${rs}]${blu}newer${rs} ${bld}FILE${rs}\n"); cfprintf(cout, " Find files ${blu}a${rs}ccessed/${blu}B${rs}irthed/${blu}c${rs}hanged/${blu}m${rs}odified more recently than ${bld}FILE${rs} was\n" " modified\n"); + cfprintf(cout, " ${blu}-${rs}[${blu}aBcm${rs}]${blu}since${rs} ${bld}TIME${rs}\n"); + cfprintf(cout, " Find files ${blu}a${rs}ccessed/${blu}B${rs}irthed/${blu}c${rs}hanged/${blu}m${rs}odified more recently than ${bld}TIME${rs}\n"); cfprintf(cout, " ${blu}-${rs}[${blu}aBcm${rs}]${blu}time${rs} ${bld}[-+]N${rs}\n"); cfprintf(cout, " Find files ${blu}a${rs}ccessed/${blu}B${rs}irthed/${blu}c${rs}hanged/${blu}m${rs}odified ${bld}N${rs} days ago\n"); #if BFS_CAN_CHECK_CAPABILITIES @@ -2867,7 +2891,8 @@ static struct expr *parse_help(struct parser_state *state, int arg1, int arg2) { cfprintf(cout, " ${blu}-newer${rs}${bld}XY${rs} ${bld}REFERENCE${rs}\n"); cfprintf(cout, " Find files whose ${bld}X${rs} time is newer than the ${bld}Y${rs} time of" " ${bld}REFERENCE${rs}. ${bld}X${rs} and ${bld}Y${rs}\n"); - cfprintf(cout, " can be any of [${bld}aBcm${rs}].\n"); + cfprintf(cout, " can be any of [${bld}aBcm${rs}]. ${bld}Y${rs} may also be ${bld}t${rs} to parse ${bld}REFERENCE${rs} an explicit\n"); + cfprintf(cout, " timestamp.\n"); cfprintf(cout, " ${blu}-nogroup${rs}\n"); cfprintf(cout, " ${blu}-nouser${rs}\n"); cfprintf(cout, " Find files owned by nonexistent groups/users\n"); @@ -2880,6 +2905,8 @@ static struct expr *parse_help(struct parser_state *state, int arg1, int arg2) { cfprintf(cout, " Find files whose entire path matches the regular expression ${bld}REGEX${rs}\n"); cfprintf(cout, " ${blu}-samefile${rs} ${bld}FILE${rs}\n"); cfprintf(cout, " Find hard links to ${bld}FILE${rs}\n"); + cfprintf(cout, " ${blu}-since${rs} ${bld}TIME${rs}\n"); + cfprintf(cout, " Find files modified since ${bld}TIME${rs}\n"); cfprintf(cout, " ${blu}-size${rs} ${bld}[-+]N[cwbkMGTP]${rs}\n"); cfprintf(cout, " Find files with the given size, in 1-byte ${bld}c${rs}haracters, 2-byte ${bld}w${rs}ords,\n"); cfprintf(cout, " 512-byte ${bld}b${rs}locks (default), or ${bld}k${rs}iB/${bld}M${rs}iB/${bld}G${rs}iB/${bld}T${rs}iB/${bld}P${rs}iB\n"); @@ -2990,6 +3017,7 @@ static const struct table_entry parse_table[] = { {"--version", parse_version}, {"-Bmin", parse_time, BFS_STAT_BTIME, MINUTES}, {"-Bnewer", parse_newer, BFS_STAT_BTIME}, + {"-Bsince", parse_since, BFS_STAT_BTIME}, {"-Btime", parse_time, BFS_STAT_BTIME, DAYS}, {"-D", parse_debug}, {"-E", parse_regex_extended}, @@ -3004,11 +3032,13 @@ static const struct table_entry parse_table[] = { {"-amin", parse_time, BFS_STAT_ATIME, MINUTES}, {"-and"}, {"-anewer", parse_newer, BFS_STAT_ATIME}, + {"-asince", parse_since, BFS_STAT_ATIME}, {"-atime", parse_time, BFS_STAT_ATIME, DAYS}, {"-capable", parse_capable}, {"-cmin", parse_time, BFS_STAT_CTIME, MINUTES}, {"-cnewer", parse_newer, BFS_STAT_CTIME}, {"-color", parse_color, true}, + {"-csince", parse_since, BFS_STAT_CTIME}, {"-ctime", parse_time, BFS_STAT_CTIME, DAYS}, {"-d", parse_depth}, {"-daystart", parse_daystart}, @@ -3046,6 +3076,7 @@ static const struct table_entry parse_table[] = { {"-mmin", parse_time, BFS_STAT_MTIME, MINUTES}, {"-mnewer", parse_newer, BFS_STAT_MTIME}, {"-mount", parse_mount}, + {"-msince", parse_since, BFS_STAT_MTIME}, {"-mtime", parse_time, BFS_STAT_MTIME, DAYS}, {"-name", parse_name, false}, {"-newer", parse_newer, BFS_STAT_MTIME}, @@ -3075,6 +3106,7 @@ static const struct table_entry parse_table[] = { {"-regextype", parse_regextype}, {"-rm", parse_delete}, {"-samefile", parse_samefile}, + {"-since", parse_since, BFS_STAT_MTIME}, {"-size", parse_size}, {"-sparse", parse_sparse}, {"-true", parse_const, true}, diff --git a/tests.sh b/tests.sh index fb73e4d..3893251 100755 --- a/tests.sh +++ b/tests.sh @@ -280,6 +280,9 @@ bsd_tests=( test_acl test_L_acl + test_anewer + test_asince + test_delete test_depth_maxdepth_1 @@ -331,6 +334,8 @@ bsd_tests=( test_mnewer test_H_mnewer + test_msince + test_name_slash test_name_slashes @@ -1151,6 +1156,10 @@ function test_anewer() { bfs_diff times -anewer times/a } +function test_asince() { + bfs_diff times -asince 1991-12-14T00:01 +} + function test_links() { bfs_diff links -type f -links 2 } @@ -1777,6 +1786,10 @@ function test_H_mnewer() { bfs_diff -H times -mnewer times/l } +function test_msince() { + bfs_diff times -msince 1991-12-14T00:01 +} + function test_size_T() { bfs_diff basic -type f -size 1T } diff --git a/tests/test_asince.out b/tests/test_asince.out new file mode 100644 index 0000000..650e550 --- /dev/null +++ b/tests/test_asince.out @@ -0,0 +1,3 @@ +times +times/c +times/l diff --git a/tests/test_msince.out b/tests/test_msince.out new file mode 100644 index 0000000..650e550 --- /dev/null +++ b/tests/test_msince.out @@ -0,0 +1,3 @@ +times +times/c +times/l -- cgit v1.2.3