summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTavian Barnes <tavianator@tavianator.com>2023-07-05 10:45:56 -0400
committerTavian Barnes <tavianator@tavianator.com>2023-07-06 11:06:22 -0400
commita215ab6de0ae34db7311136404d0f9feab34ef04 (patch)
tree51470bb6b2859ee7b139b8fb81decd007944e7a8
parent66264f1518d1e19123cca23ec92eace4c7982731 (diff)
downloadbfs-a215ab6de0ae34db7311136404d0f9feab34ef04.tar.xz
Use strcmp() instead of fnmatch() if possible
-rw-r--r--src/eval.c21
-rw-r--r--src/expr.h10
-rw-r--r--src/parse.c17
3 files changed, 39 insertions, 9 deletions
diff --git a/src/eval.c b/src/eval.c
index 5f27681..56753af 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -547,6 +547,20 @@ bool eval_links(const struct bfs_expr *expr, struct bfs_eval *state) {
return bfs_expr_cmp(expr, statbuf->nlink);
}
+/** Common code for fnmatch() tests. */
+static bool eval_fnmatch(const struct bfs_expr *expr, const char *str) {
+ if (expr->literal) {
+#ifdef FNM_CASEFOLD
+ if (expr->fnm_flags & FNM_CASEFOLD) {
+ return strcasecmp(expr->pattern, str) == 0;
+ }
+#endif
+ return strcmp(expr->pattern, str) == 0;
+ } else {
+ return fnmatch(expr->pattern, str, expr->fnm_flags) == 0;
+ }
+}
+
/**
* -i?lname test.
*/
@@ -568,7 +582,7 @@ bool eval_lname(const struct bfs_expr *expr, struct bfs_eval *state) {
goto done;
}
- ret = fnmatch(expr->argv[1], name, expr->num) == 0;
+ ret = eval_fnmatch(expr, name);
done:
free(name);
@@ -589,7 +603,7 @@ bool eval_name(const struct bfs_expr *expr, struct bfs_eval *state) {
name = copy = xbasename(name);
}
- bool ret = fnmatch(expr->argv[1], name, expr->num) == 0;
+ bool ret = eval_fnmatch(expr, name);
free(copy);
return ret;
}
@@ -598,8 +612,7 @@ bool eval_name(const struct bfs_expr *expr, struct bfs_eval *state) {
* -i?path test.
*/
bool eval_path(const struct bfs_expr *expr, struct bfs_eval *state) {
- const struct BFTW *ftwbuf = state->ftwbuf;
- return fnmatch(expr->argv[1], ftwbuf->path, expr->num) == 0;
+ return eval_fnmatch(expr, state->ftwbuf->path);
}
/**
diff --git a/src/expr.h b/src/expr.h
index 356689d..95118b9 100644
--- a/src/expr.h
+++ b/src/expr.h
@@ -143,6 +143,16 @@ struct bfs_expr {
};
};
+ /** String comparisons. */
+ struct {
+ /** String pattern. */
+ const char *pattern;
+ /** fnmatch() flags. */
+ int fnm_flags;
+ /** Whether strcmp() can be used instead of fnmatch(). */
+ bool literal;
+ };
+
/** Printing actions. */
struct {
/** The output stream. */
diff --git a/src/parse.c b/src/parse.c
index 623a528..c225a5b 100644
--- a/src/parse.c
+++ b/src/parse.c
@@ -1676,16 +1676,18 @@ static struct bfs_expr *parse_fnmatch(const struct parser_state *state, struct b
return NULL;
}
+ expr->pattern = expr->argv[1];
+
if (casefold) {
#ifdef FNM_CASEFOLD
- expr->num = FNM_CASEFOLD;
+ expr->fnm_flags = FNM_CASEFOLD;
#else
parse_expr_error(state, expr, "Missing platform support.\n");
bfs_expr_free(expr);
return NULL;
#endif
} else {
- expr->num = 0;
+ expr->fnm_flags = 0;
}
// POSIX says, about fnmatch():
@@ -1694,10 +1696,9 @@ static struct bfs_expr *parse_fnmatch(const struct parser_state *state, struct b
// return a non-zero value (indicating either no match or an error).
//
// But not all implementations obey this, so check for it ourselves.
- const char *pattern = expr->argv[1];
- size_t i, len = strlen(pattern);
+ size_t i, len = strlen(expr->pattern);
for (i = 0; i < len; ++i) {
- if (pattern[len - i - 1] != '\\') {
+ if (expr->pattern[len - i - 1] != '\\') {
break;
}
}
@@ -1707,6 +1708,12 @@ static struct bfs_expr *parse_fnmatch(const struct parser_state *state, struct b
return expr;
}
+ // strcmp() can be much faster than fnmatch() since it doesn't have to
+ // parse the pattern, so special-case patterns with no wildcards.
+ //
+ // https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_13_01
+ expr->literal = strcspn(expr->pattern, "?*\\[") == len;
+
return expr;
}