summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTavian Barnes <tavianator@tavianator.com>2016-02-01 17:41:55 -0500
committerTavian Barnes <tavianator@tavianator.com>2016-02-02 10:35:52 -0500
commitb997ddf3df6535765f7eab0683eae4703933a703 (patch)
treef49da0ca0bb1d84d6a0eb4494dc52e1b911aaf74
parente641450d7d5dd86139b92fe6f69801b16dd3eeed (diff)
downloadbfs-b997ddf3df6535765f7eab0683eae4703933a703.tar.xz
Add -[acm]{min,time} support.
-rw-r--r--bfs.h39
-rw-r--r--eval.c141
-rw-r--r--parse.c92
3 files changed, 242 insertions, 30 deletions
diff --git a/bfs.h b/bfs.h
index 441993b..e4dffdd 100644
--- a/bfs.h
+++ b/bfs.h
@@ -15,6 +15,7 @@
#include "color.h"
#include <stdbool.h>
#include <stddef.h>
+#include <time.h>
/**
* The parsed command line.
@@ -64,8 +65,23 @@ struct cmdline {
/** The command line expression. */
expression *expr;
+
+ /** The current time. */
+ struct timespec now;
};
+/**
+ * Possible types of numeric comparison.
+ */
+typedef enum {
+ /** Exactly n. */
+ CMP_EXACT,
+ /** Less than n. */
+ CMP_LESS,
+ /** Greater than n. */
+ CMP_GREATER,
+} cmpflag;
+
struct expression {
/** The left hand side of the expression. */
expression *lhs;
@@ -73,6 +89,8 @@ struct expression {
expression *rhs;
/** The function that evaluates this expression. */
eval_fn *eval;
+ /** The comparison flag. */
+ cmpflag cmp;
/** Optional integer data for this expression. */
int idata;
/** Optional string data for this expression. */
@@ -95,20 +113,31 @@ int eval_cmdline(cmdline *cl);
void free_cmdline(cmdline *cl);
// Predicate evaluation functions
+bool eval_true(const expression *expr, eval_state *state);
+bool eval_false(const expression *expr, eval_state *state);
+
bool eval_access(const expression *expr, eval_state *state);
-bool eval_delete(const expression *expr, eval_state *state);
+
+bool eval_amin(const expression *expr, eval_state *state);
+bool eval_atime(const expression *expr, eval_state *state);
+bool eval_cmin(const expression *expr, eval_state *state);
+bool eval_ctime(const expression *expr, eval_state *state);
+bool eval_mmin(const expression *expr, eval_state *state);
+bool eval_mtime(const expression *expr, eval_state *state);
+
bool eval_empty(const expression *expr, eval_state *state);
-bool eval_false(const expression *expr, eval_state *state);
bool eval_hidden(const expression *expr, eval_state *state);
+bool eval_type(const expression *expr, eval_state *state);
+
bool eval_name(const expression *expr, eval_state *state);
-bool eval_nohidden(const expression *expr, eval_state *state);
bool eval_path(const expression *expr, eval_state *state);
+
+bool eval_delete(const expression *expr, eval_state *state);
+bool eval_nohidden(const expression *expr, eval_state *state);
bool eval_print(const expression *expr, eval_state *state);
bool eval_print0(const expression *expr, eval_state *state);
bool eval_prune(const expression *expr, eval_state *state);
bool eval_quit(const expression *expr, eval_state *state);
-bool eval_true(const expression *expr, eval_state *state);
-bool eval_type(const expression *expr, eval_state *state);
// Operator evaluation functions
bool eval_not(const expression *expr, eval_state *state);
diff --git a/eval.c b/eval.c
index 417bfc3..243546e 100644
--- a/eval.c
+++ b/eval.c
@@ -8,6 +8,7 @@
#include <string.h>
#include <sys/resource.h>
#include <sys/stat.h>
+#include <time.h>
#include <unistd.h>
struct eval_state {
@@ -24,17 +25,57 @@ struct eval_state {
/**
* Perform a stat() call if necessary.
*/
-static void fill_statbuf(eval_state *state) {
+static const struct stat *fill_statbuf(eval_state *state) {
struct BFTW *ftwbuf = state->ftwbuf;
- if (ftwbuf->statbuf) {
- return;
+ if (!ftwbuf->statbuf) {
+ if (fstatat(ftwbuf->at_fd, ftwbuf->at_path, &state->statbuf, AT_SYMLINK_NOFOLLOW) == 0) {
+ ftwbuf->statbuf = &state->statbuf;
+ } else {
+ perror("fstatat()");
+ }
}
+ return ftwbuf->statbuf;
+}
- if (fstatat(ftwbuf->at_fd, ftwbuf->at_path, &state->statbuf, AT_SYMLINK_NOFOLLOW) == 0) {
- ftwbuf->statbuf = &state->statbuf;
- } else {
- perror("fstatat()");
+/**
+ * Get the difference (in seconds) between two struct timespecs.
+ */
+static time_t timespec_diff(const struct timespec *lhs, const struct timespec *rhs) {
+ time_t ret = lhs->tv_sec - rhs->tv_sec;
+ if (lhs->tv_nsec < rhs->tv_nsec) {
+ --ret;
+ }
+ return ret;
+}
+
+/**
+ * Convert a second count to minutes.
+ */
+static time_t to_minutes(time_t seconds) {
+ return seconds/60;
+}
+
+/**
+ * Convert a second count to days.
+ */
+static time_t to_days(time_t seconds) {
+ return seconds/60/60/24;
+}
+
+/**
+ * Perform a comparison.
+ */
+static bool do_cmp(const expression *expr, int n) {
+ switch (expr->cmp) {
+ case CMP_EXACT:
+ return n == expr->idata;
+ case CMP_LESS:
+ return n < expr->idata;
+ case CMP_GREATER:
+ return n > expr->idata;
}
+
+ return false;
}
/**
@@ -52,7 +93,7 @@ bool eval_false(const expression *expr, eval_state *state) {
}
/**
- * -executable, -readable, -writable action.
+ * -executable, -readable, -writable tests.
*/
bool eval_access(const expression *expr, eval_state *state) {
struct BFTW *ftwbuf = state->ftwbuf;
@@ -60,6 +101,84 @@ bool eval_access(const expression *expr, eval_state *state) {
}
/**
+ * -amin test.
+ */
+bool eval_amin(const expression *expr, eval_state *state) {
+ const struct stat *statbuf = fill_statbuf(state);
+ if (!statbuf) {
+ return false;
+ }
+
+ time_t diff = timespec_diff(&state->cl->now, &statbuf->st_atim);
+ return do_cmp(expr, to_minutes(diff));
+}
+
+/**
+ * -atime test.
+ */
+bool eval_atime(const expression *expr, eval_state *state) {
+ const struct stat *statbuf = fill_statbuf(state);
+ if (!statbuf) {
+ return false;
+ }
+
+ time_t diff = timespec_diff(&state->cl->now, &statbuf->st_atim);
+ return do_cmp(expr, to_days(diff));
+}
+
+/**
+ * -cmin test.
+ */
+bool eval_cmin(const expression *expr, eval_state *state) {
+ const struct stat *statbuf = fill_statbuf(state);
+ if (!statbuf) {
+ return false;
+ }
+
+ time_t diff = timespec_diff(&state->cl->now, &statbuf->st_ctim);
+ return do_cmp(expr, to_minutes(diff));
+}
+
+/**
+ * -ctime test.
+ */
+bool eval_ctime(const expression *expr, eval_state *state) {
+ const struct stat *statbuf = fill_statbuf(state);
+ if (!statbuf) {
+ return false;
+ }
+
+ time_t diff = timespec_diff(&state->cl->now, &statbuf->st_ctim);
+ return do_cmp(expr, to_days(diff));
+}
+
+/**
+ * -mmin test.
+ */
+bool eval_mmin(const expression *expr, eval_state *state) {
+ const struct stat *statbuf = fill_statbuf(state);
+ if (!statbuf) {
+ return false;
+ }
+
+ time_t diff = timespec_diff(&state->cl->now, &statbuf->st_mtim);
+ return do_cmp(expr, to_minutes(diff));
+}
+
+/**
+ * -mtime test.
+ */
+bool eval_mtime(const expression *expr, eval_state *state) {
+ const struct stat *statbuf = fill_statbuf(state);
+ if (!statbuf) {
+ return false;
+ }
+
+ time_t diff = timespec_diff(&state->cl->now, &statbuf->st_mtim);
+ return do_cmp(expr, to_days(diff));
+}
+
+/**
* -delete action.
*/
bool eval_delete(const expression *expr, eval_state *state) {
@@ -111,9 +230,9 @@ bool eval_empty(const expression *expr, eval_state *state) {
closedir(dir);
} else {
- fill_statbuf(state);
- if (ftwbuf->statbuf) {
- ret = ftwbuf->statbuf->st_size == 0;
+ const struct stat *statbuf = fill_statbuf(state);
+ if (statbuf) {
+ ret = statbuf->st_size == 0;
}
}
diff --git a/parse.c b/parse.c
index 5425afa..6983f16 100644
--- a/parse.c
+++ b/parse.c
@@ -18,6 +18,7 @@ static expression *new_expression(eval_fn *eval) {
expr->lhs = NULL;
expr->rhs = NULL;
expr->eval = eval;
+ expr->cmp = CMP_EXACT;
expr->idata = 0;
expr->sdata = NULL;
return expr;
@@ -164,6 +165,50 @@ static const char *skip_paths(parser_state *state) {
}
/**
+ * Parse an integer.
+ */
+static bool parse_int(const char *str, int *value) {
+ char *endptr;
+ long result = strtol(str, &endptr, 10);
+
+ if (*str == '\0' || *endptr != '\0') {
+ goto bad;
+ }
+
+ if (result < INT_MIN || result > INT_MAX) {
+ goto bad;
+ }
+
+ *value = result;
+ return true;
+
+bad:
+ fprintf(stderr, "'%s' is not a valid integer.\n", str);
+ return false;
+}
+
+/**
+ * Parse an integer and a comparison flag.
+ */
+static bool parse_icmp(const char *str, expression *expr) {
+ switch (str[0]) {
+ case '-':
+ expr->cmp = CMP_LESS;
+ ++str;
+ break;
+ case '+':
+ expr->cmp = CMP_GREATER;
+ ++str;
+ break;
+ default:
+ expr->cmp = CMP_EXACT;
+ break;
+ }
+
+ return parse_int(str, &expr->idata);
+}
+
+/**
* Create a new option expression.
*/
static expression *new_option(parser_state *state, const char *option) {
@@ -228,22 +273,25 @@ static expression *new_action(parser_state *state, eval_fn *eval) {
}
/**
- * Parse an integer.
+ * Parse a test expression with integer data and a comparison flag.
*/
-static bool parse_int(const char *str, int *value) {
- char *endptr;
- long result = strtol(str, &endptr, 10);
-
- if (*str == '\0' || *endptr != '\0') {
- return false;
+static expression *parse_test_icmp(parser_state *state, const char *test, eval_fn *eval) {
+ const char *arg = state->argv[state->i];
+ if (!arg) {
+ fprintf(stderr, "%s needs a value.\n", test);
+ return NULL;
}
- if (result < INT_MIN || result > INT_MAX) {
- return false;
- }
+ ++state->i;
- *value = result;
- return true;
+ expression *expr = new_test(state, eval);
+ if (expr) {
+ if (!parse_icmp(arg, expr)) {
+ free_expression(expr);
+ expr = NULL;
+ }
+ }
+ return expr;
}
/**
@@ -274,7 +322,6 @@ static expression *parse_depth(parser_state *state, const char *option, int *dep
++state->i;
if (!parse_int(arg, depth)) {
- fprintf(stderr, "%s is not a valid integer.\n", arg);
return NULL;
}
@@ -336,7 +383,15 @@ static expression *parse_literal(parser_state *state) {
// Paths are already skipped at this point
const char *arg = state->argv[state->i++];
- if (strcmp(arg, "-color") == 0) {
+ if (strcmp(arg, "-amin") == 0) {
+ return parse_test_icmp(state, arg, eval_amin);
+ } else if (strcmp(arg, "-atime") == 0) {
+ return parse_test_icmp(state, arg, eval_atime);
+ } else if (strcmp(arg, "-cmin") == 0) {
+ return parse_test_icmp(state, arg, eval_cmin);
+ } else if (strcmp(arg, "-ctime") == 0) {
+ return parse_test_icmp(state, arg, eval_ctime);
+ } else if (strcmp(arg, "-color") == 0) {
state->cl->color = true;
return new_option(state, arg);
} else if (strcmp(arg, "-nocolor") == 0) {
@@ -362,6 +417,10 @@ static expression *parse_literal(parser_state *state) {
return parse_depth(state, arg, &state->cl->mindepth);
} else if (strcmp(arg, "-maxdepth") == 0) {
return parse_depth(state, arg, &state->cl->maxdepth);
+ } else if (strcmp(arg, "-mmin") == 0) {
+ return parse_test_icmp(state, arg, eval_mmin);
+ } else if (strcmp(arg, "-mtime") == 0) {
+ return parse_test_icmp(state, arg, eval_mtime);
} else if (strcmp(arg, "-name") == 0) {
return parse_test_sdata(state, arg, eval_name);
} else if (strcmp(arg, "-path") == 0 || strcmp(arg, "-wholename") == 0) {
@@ -613,6 +672,11 @@ cmdline *parse_cmdline(int argc, char *argv[]) {
cl->flags = BFTW_RECOVER;
cl->expr = &expr_true;
+ if (clock_gettime(CLOCK_REALTIME, &cl->now) != 0) {
+ perror("clock_gettime()");
+ goto fail;
+ }
+
parser_state state = {
.cl = cl,
.argv = argv,