summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTavian Barnes <tavianator@tavianator.com>2017-11-05 12:03:31 -0500
committerTavian Barnes <tavianator@tavianator.com>2017-11-05 12:04:17 -0500
commit6e9f52c9a8d51cac7db3b62e799fc32072c86443 (patch)
tree2cf2e0acb149421e710ea6b285fdbe7b69d516ba
parent7f8bacca4c2b1d35bb65ddf8cbf70fadf1adf66e (diff)
downloadbfs-6e9f52c9a8d51cac7db3b62e799fc32072c86443.tar.xz
Add support for file birth/creation times on platforms that have it
Fixes #19
-rw-r--r--bfs.121
-rw-r--r--eval.c56
-rw-r--r--eval.h4
-rw-r--r--expr.h2
-rw-r--r--parse.c105
-rw-r--r--printf.c12
-rw-r--r--util.h5
7 files changed, 145 insertions, 60 deletions
diff --git a/bfs.1 b/bfs.1
index f49e505..a6ff153 100644
--- a/bfs.1
+++ b/bfs.1
@@ -391,8 +391,8 @@ time of
and
.I Y
can be any of
-.RI [ acm ]
-.RI ( a ccess/ c hange/ m odification).
+.RI [ aBcm ]
+.RI ( a ccess/ B irth/ c hange/ m odification).
.TP
\fB\-regex \fIREGEX\fR
Find files whose entire path matches the regular expression
@@ -523,6 +523,23 @@ Treat
as a path to search (useful if it begins with a dash).
.LP
Tests:
+.PP
+\fB\-Bmin\fR [\fI\-+\fR]\fIN\fR
+.br
+\fB\-Btime\fR [\fI\-+\fR]\fIN\fR
+.RS
+Find files
+.BR B irthed
+.I N
+minutes/days ago.
+.RE
+.TP
+\fB\-Bnewer \fIFILE\fR
+Find files
+.BR B irthed
+more recently than
+.I FILE
+was modified.
.TP
\fB\-depth\fR [\fI\-+\fR]\fIN\fR
Find files with depth
diff --git a/eval.c b/eval.c
index 62dbf55..fe89225 100644
--- a/eval.c
+++ b/eval.c
@@ -135,15 +135,15 @@ bool eval_access(const struct expr *expr, struct eval_state *state) {
}
/**
- * -[acm]{min,time} tests.
+ * -[aBcm]?newer tests.
*/
-bool eval_acmtime(const struct expr *expr, struct eval_state *state) {
+bool eval_newer(const struct expr *expr, struct eval_state *state) {
const struct stat *statbuf = fill_statbuf(state);
if (!statbuf) {
return false;
}
- const struct timespec *time = NULL;
+ const struct timespec *time;
switch (expr->time_field) {
case ATIME:
time = &statbuf->st_atim;
@@ -154,32 +154,32 @@ bool eval_acmtime(const struct expr *expr, struct eval_state *state) {
case MTIME:
time = &statbuf->st_mtim;
break;
- }
- assert(time);
- time_t diff = timespec_diff(&expr->reftime, time);
- switch (expr->time_unit) {
- case MINUTES:
- diff /= 60;
- break;
- case DAYS:
- diff /= 60*60*24;
+#if BFS_HAVE_ST_BIRTHTIM
+ case BTIME:
+ time = &statbuf->st_birthtim;
break;
+#endif
+
+ default:
+ assert(false);
+ return false;
}
- return expr_cmp(expr, diff);
+ return time->tv_sec > expr->reftime.tv_sec
+ || (time->tv_sec == expr->reftime.tv_sec && time->tv_nsec > expr->reftime.tv_nsec);
}
/**
- * -[ac]?newer tests.
+ * -[aBcm]{min,time} tests.
*/
-bool eval_acnewer(const struct expr *expr, struct eval_state *state) {
+bool eval_time(const struct expr *expr, struct eval_state *state) {
const struct stat *statbuf = fill_statbuf(state);
if (!statbuf) {
return false;
}
- const struct timespec *time = NULL;
+ const struct timespec *time;
switch (expr->time_field) {
case ATIME:
time = &statbuf->st_atim;
@@ -190,11 +190,29 @@ bool eval_acnewer(const struct expr *expr, struct eval_state *state) {
case MTIME:
time = &statbuf->st_mtim;
break;
+
+#if BFS_HAVE_ST_BIRTHTIM
+ case BTIME:
+ time = &statbuf->st_birthtim;
+ break;
+#endif
+
+ default:
+ assert(false);
+ return false;
}
- assert(time);
- return time->tv_sec > expr->reftime.tv_sec
- || (time->tv_sec == expr->reftime.tv_sec && time->tv_nsec > expr->reftime.tv_nsec);
+ time_t diff = timespec_diff(&expr->reftime, time);
+ switch (expr->time_unit) {
+ case MINUTES:
+ diff /= 60;
+ break;
+ case DAYS:
+ diff /= 60*60*24;
+ break;
+ }
+
+ return expr_cmp(expr, diff);
}
/**
diff --git a/eval.h b/eval.h
index 356f332..f190703 100644
--- a/eval.h
+++ b/eval.h
@@ -26,8 +26,8 @@ bool eval_false(const struct expr *expr, struct eval_state *state);
bool eval_access(const struct expr *expr, struct eval_state *state);
bool eval_perm(const struct expr *expr, struct eval_state *state);
-bool eval_acmtime(const struct expr *expr, struct eval_state *state);
-bool eval_acnewer(const struct expr *expr, struct eval_state *state);
+bool eval_newer(const struct expr *expr, struct eval_state *state);
+bool eval_time(const struct expr *expr, struct eval_state *state);
bool eval_used(const struct expr *expr, struct eval_state *state);
bool eval_gid(const struct expr *expr, struct eval_state *state);
diff --git a/expr.h b/expr.h
index 0eba0da..5581bf4 100644
--- a/expr.h
+++ b/expr.h
@@ -82,6 +82,8 @@ enum time_field {
CTIME,
/** Modification time. */
MTIME,
+ /** Birth time. */
+ BTIME,
};
/**
diff --git a/parse.c b/parse.c
index 6492412..9d533ab 100644
--- a/parse.c
+++ b/parse.c
@@ -893,38 +893,57 @@ static struct expr *parse_access(struct parser_state *state, int flag, int arg2)
}
/**
- * Parse -[acm]{min,time}.
+ * Parse -[aBcm]?newer.
*/
-static struct expr *parse_acmtime(struct parser_state *state, int field, int unit) {
- struct expr *expr = parse_test_icmp(state, eval_acmtime);
- if (expr) {
- expr->cost = STAT_COST;
- expr->reftime = state->now;
- expr->time_field = field;
- expr->time_unit = unit;
+static struct expr *parse_newer(struct parser_state *state, int field, int arg2) {
+ struct expr *expr = parse_unary_test(state, eval_newer);
+ if (!expr) {
+ return NULL;
+ }
+
+#if !BFS_HAVE_ST_BIRTHTIM
+ if (field == BTIME) {
+ cfprintf(state->cmdline->cerr, "%{er}error: %s: File birth times are not supported on this platform.%{rs}\n", expr->argv[0]);
+ goto fail;
+ }
+#endif
+
+ struct stat sb;
+ if (stat_arg(state, expr, &sb) != 0) {
+ goto fail;
}
+
+ expr->cost = STAT_COST;
+ expr->reftime = sb.st_mtim;
+ expr->time_field = field;
return expr;
+
+fail:
+ free_expr(expr);
+ return NULL;
}
/**
- * Parse -[ac]?newer.
+ * Parse -[aBcm]{min,time}.
*/
-static struct expr *parse_acnewer(struct parser_state *state, int field, int arg2) {
- struct expr *expr = parse_unary_test(state, eval_acnewer);
+static struct expr *parse_time(struct parser_state *state, int field, int unit) {
+ struct expr *expr = parse_test_icmp(state, eval_time);
if (!expr) {
return NULL;
}
- struct stat sb;
- if (stat_arg(state, expr, &sb) != 0) {
+#if !BFS_HAVE_ST_BIRTHTIM
+ if (field == BTIME) {
+ cfprintf(state->cmdline->cerr, "%{er}error: %s: File birth times are not supported on this platform.%{rs}\n", expr->argv[0]);
free_expr(expr);
return NULL;
}
+#endif
expr->cost = STAT_COST;
- expr->reftime = sb.st_mtim;
+ expr->reftime = state->now;
expr->time_field = field;
-
+ expr->time_unit = unit;
return expr;
}
@@ -1445,13 +1464,15 @@ static struct expr *parse_lname(struct parser_state *state, int casefold, int ar
* Parse -newerXY.
*/
static struct expr *parse_newerxy(struct parser_state *state, int arg1, int arg2) {
+ CFILE *cerr = state->cmdline->cerr;
+
const char *arg = state->argv[0];
if (strlen(arg) != 8) {
- cfprintf(state->cmdline->cerr, "%{er}error: Expected -newerXY; found %s.%{rs}\n", arg);
+ cfprintf(cerr, "%{er}error: Expected -newerXY; found %s.%{rs}\n", arg);
return NULL;
}
- struct expr *expr = parse_unary_test(state, eval_acnewer);
+ struct expr *expr = parse_unary_test(state, eval_newer);
if (!expr) {
goto fail;
}
@@ -1468,16 +1489,21 @@ static struct expr *parse_newerxy(struct parser_state *state, int arg1, int arg2
break;
case 'B':
- cfprintf(state->cmdline->cerr, "%{er}error: %s: File birth times ('B') are not supported.%{rs}\n", arg);
+#if BFS_HAVE_ST_BIRTHTIM
+ expr->time_field = BTIME;
+ break;
+#else
+ cfprintf(cerr, "%{er}error: %s: File birth times ('B') are not supported on this platform.%{rs}\n", arg);
goto fail;
+#endif
default:
- cfprintf(state->cmdline->cerr, "%{er}error: %s: For -newerXY, X should be 'a', 'c', 'm', or 'B'.%{rs}\n", arg);
+ cfprintf(cerr, "%{er}error: %s: For -newerXY, X should be 'a', 'c', 'm', or 'B'.%{rs}\n", arg);
goto fail;
}
if (arg[7] == 't') {
- cfprintf(state->cmdline->cerr, "%{er}error: %s: Explicit reference times ('t') are not supported.%{rs}\n", arg);
+ cfprintf(cerr, "%{er}error: %s: Explicit reference times ('t') are not supported.%{rs}\n", arg);
goto fail;
} else {
struct stat sb;
@@ -1497,12 +1523,15 @@ static struct expr *parse_newerxy(struct parser_state *state, int arg1, int arg2
break;
case 'B':
- cfprintf(state->cmdline->cerr, "%{er}error: %s: File birth times ('B') are not supported.%{rs}\n", arg);
+#if BFS_HAVE_ST_BIRTHTIM
+ expr->reftime = sb.st_birthtim;
+#else
+ cfprintf(cerr, "%{er}error: %s: File birth times ('B') are not supported on this platform.%{rs}\n", arg);
goto fail;
+#endif
default:
- cfprintf(state->cmdline->cerr,
- "%{er}error: %s: For -newerXY, Y should be 'a', 'c', 'm', 'B', or 't'.%{rs}\n", arg);
+ cfprintf(cerr, "%{er}error: %s: For -newerXY, Y should be 'a', 'c', 'm', 'B', or 't'.%{rs}\n", arg);
goto fail;
}
}
@@ -2310,7 +2339,7 @@ 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 [acm].\n");
+ cfprintf(cout, " can be any of [aBcm].\n");
cfprintf(cout, " %{blu}-regex%{rs} %{bld}REGEX%{rs}\n");
cfprintf(cout, " Find files whose entire path matches the regular expression %{bld}REGEX%{rs}\n");
cfprintf(cout, " %{blu}-samefile%{rs} %{bld}FILE%{rs}\n");
@@ -2375,6 +2404,11 @@ static struct expr *parse_help(struct parser_state *state, int arg1, int arg2) {
cfprintf(cout, " %{cyn}-f%{rs} %{mag}PATH%{rs}\n");
cfprintf(cout, " Treat %{mag}PATH%{rs} as a path to search (useful if begins with a dash)\n\n");
+ cfprintf(cout, " %{blu}-Bmin%{rs} %{bld}[-+]N%{rs}\n");
+ cfprintf(cout, " %{blu}-Btime%{rs} %{bld}[-+]N%{rs}\n");
+ cfprintf(cout, " Find files Birthed %{bld}N%{rs} minutes/days ago\n");
+ cfprintf(cout, " %{blu}-Bnewer%{rs} %{bld}FILE%{rs}\n");
+ cfprintf(cout, " Find files Birthed more recently than %{bld}FILEa%{rs} was modified\n");
cfprintf(cout, " %{blu}-depth%{rs} %{bld}[-+]N%{rs}\n");
cfprintf(cout, " Find files with depth %{bld}N%{rs}\n");
cfprintf(cout, " %{blu}-gid%{rs} %{bld}NAME%{rs}\n");
@@ -2451,6 +2485,9 @@ struct table_entry {
* The parse table for literals.
*/
static const struct table_entry parse_table[] = {
+ {"-Bmin", false, parse_time, BTIME, MINUTES},
+ {"-Bnewer", false, parse_newer, BTIME},
+ {"-Btime", false, parse_time, BTIME, DAYS},
{"-D", false, parse_debug},
{"-E", false, parse_regex_extended},
{"-O", true, parse_optlevel},
@@ -2459,13 +2496,13 @@ static const struct table_entry parse_table[] = {
{"-L", false, parse_follow, BFTW_LOGICAL | BFTW_DETECT_CYCLES, false},
{"-X", false, parse_xargs_safe},
{"-a"},
- {"-amin", false, parse_acmtime, ATIME, MINUTES},
+ {"-amin", false, parse_time, ATIME, MINUTES},
{"-and"},
- {"-atime", false, parse_acmtime, ATIME, DAYS},
- {"-anewer", false, parse_acnewer, ATIME},
- {"-cmin", false, parse_acmtime, CTIME, MINUTES},
- {"-ctime", false, parse_acmtime, CTIME, DAYS},
- {"-cnewer", false, parse_acnewer, CTIME},
+ {"-anewer", false, parse_newer, ATIME},
+ {"-atime", false, parse_time, ATIME, DAYS},
+ {"-cmin", false, parse_time, CTIME, MINUTES},
+ {"-cnewer", false, parse_newer, CTIME},
+ {"-ctime", false, parse_time, CTIME, DAYS},
{"-color", false, parse_color, true},
{"-d", false, parse_depth},
{"-daystart", false, parse_daystart},
@@ -2500,12 +2537,12 @@ static const struct table_entry parse_table[] = {
{"-ls", false, parse_ls},
{"-maxdepth", false, parse_depth_limit, false},
{"-mindepth", false, parse_depth_limit, true},
- {"-mmin", false, parse_acmtime, MTIME, MINUTES},
- {"-mnewer", false, parse_acnewer, MTIME},
+ {"-mmin", false, parse_time, MTIME, MINUTES},
+ {"-mnewer", false, parse_newer, MTIME},
{"-mount", false, parse_mount},
- {"-mtime", false, parse_acmtime, MTIME, DAYS},
+ {"-mtime", false, parse_time, MTIME, DAYS},
{"-name", false, parse_name, false},
- {"-newer", false, parse_acnewer, MTIME},
+ {"-newer", false, parse_newer, MTIME},
{"-newer", true, parse_newerxy},
{"-nocolor", false, parse_color, false},
{"-nogroup", false, parse_nogroup},
diff --git a/printf.c b/printf.c
index 3c32108..9157ccb 100644
--- a/printf.c
+++ b/printf.c
@@ -87,10 +87,16 @@ static const struct timespec *get_time_field(const struct stat *statbuf, enum ti
return &statbuf->st_ctim;
case MTIME:
return &statbuf->st_mtim;
- }
- assert(false);
- return NULL;
+#ifdef BFS_HAVE_ST_BIRTHTIM
+ case BTIME:
+ return &statbuf->st_birthtim;
+#endif
+
+ default:
+ assert(false);
+ return NULL;
+ }
}
/** %c, %c, and %t: ctime() */
diff --git a/util.h b/util.h
index 8f285d3..50f476e 100644
--- a/util.h
+++ b/util.h
@@ -32,6 +32,11 @@
# define st_atim st_atimespec
# define st_ctim st_ctimespec
# define st_mtim st_mtimespec
+# define st_birthtim st_birthtimespec
+#endif
+
+#if __APPLE__ || __FreeBSD__ || __NetBSD__
+# define BFS_HAVE_ST_BIRTHTIM true
#endif
#if !defined(FNM_CASEFOLD) && defined(FNM_IGNORECASE)