From 912d2b94cf6ff0871c07325af5ed520a2bc97722 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Wed, 20 Mar 2024 10:44:34 -0400 Subject: Implement -limit N Closes: https://github.com/tavianator/bfs/issues/133 --- completions/bfs.fish | 1 + 1 file changed, 1 insertion(+) (limited to 'completions/bfs.fish') diff --git a/completions/bfs.fish b/completions/bfs.fish index 3f399e7..1303639 100644 --- a/completions/bfs.fish +++ b/completions/bfs.fish @@ -133,6 +133,7 @@ complete -c bfs -o fls -d "Like -ls, but write to specified file" -F complete -c bfs -o fprint -d "Like -print, but write to specified file" -F complete -c bfs -o fprint0 -d "Like -print0, but write to specified file" -F complete -c bfs -o fprintf -d "Like -printf, but write to specified file" -F +complete -c bfs -o limit -d "Limit the number of results" -x complete -c bfs -o ls -d "List files like ls -dils" complete -c bfs -o print -d "Print the path to the found file" complete -c bfs -o print0 -d "Like -print, but use the null character as a separator rather than newlines" -- cgit v1.2.3 From dc885d2e4cb12d3a3444cea7f754a691f6d200c4 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Mon, 25 Mar 2024 13:28:06 -0400 Subject: completions: Add -j --- completions/bfs.bash | 1 + completions/bfs.fish | 1 + completions/bfs.zsh | 9 +++++---- 3 files changed, 7 insertions(+), 4 deletions(-) (limited to 'completions/bfs.fish') diff --git a/completions/bfs.bash b/completions/bfs.bash index db582da..816f1ec 100644 --- a/completions/bfs.bash +++ b/completions/bfs.bash @@ -18,6 +18,7 @@ _bfs() { -fstype -gid -group + -j -ok -okdir -regextype diff --git a/completions/bfs.fish b/completions/bfs.fish index 1303639..0c58ef4 100644 --- a/completions/bfs.fish +++ b/completions/bfs.fish @@ -20,6 +20,7 @@ complete -c bfs -o f -d "Treat specified path as a path to search" -a "(__fish_c complete -c bfs -o D -d "Turn on a debugging flag" -a $debug_flag_comp -x complete -c bfs -s O -d "Enable specified optimization level" -a $optimization_comp -x complete -c bfs -o S -d "Choose the search strategy" -a $strategy_comp -x +complete -c bfs -s j -d "Use this many threads" -x # Operators diff --git a/completions/bfs.zsh b/completions/bfs.zsh index 51b5029..07db456 100644 --- a/completions/bfs.zsh +++ b/completions/bfs.zsh @@ -10,7 +10,7 @@ args=( '-D[print diagnostics]:debug option:(cost exec opt rates search stat time tree all help)' '-E[use extended regular expressions with -regex/-iregex]' '-f[specify file hierarchy to traverse]:path:_directories' - '-O+[enable query optimisation]:level:(1 2 3)' + '-O+[enable query optimisation]:level:(0 1 2 3 4 fast)' '-s[traverse directories in sorted order]' '-X[warn if filename contains characters special to xargs]' "-x[don't span filesystems]" @@ -19,6 +19,7 @@ args=( '(-L -P)-H[only follow symlinks when resolving command-line arguments]' "-S[select search method]:value:(bfs dfs ids eds)" '-f[treat path as path to search]:path:_files -/' + '-j+[use this many threads]:threads:' # Operators '*-and' @@ -81,7 +82,7 @@ args=( '*-false[always false]' '*-true[always true]' '*-fstype[find files on file systems with the given type]:file system type:_file_systems' - + '*-gid[find files owned by group ID N]:numeric group ID:' '*-group[find files owned by group NAME]:group:_groups' '*-uid[find files owned by user ID N]:numeric user ID' @@ -117,7 +118,7 @@ args=( '*-xattr[find files with extended attributes]' '*-xattrname[find files with extended attribute NAME]:name:' '*-xtype[find files of the given type following links when -type would not, and vice versa]:file type:((b\:block\ device c\:character\ device d\:directory p\:named\ pipe f\:normal\ file l\:symbolic\ link s\:socket w\:whiteout D\:Door))' - + # Actions '*-delete[delete any found files (-implies -depth)]' '*-rm[delete any found files (-implies -depth)]' @@ -126,7 +127,7 @@ args=( '*-execdir[execute a command in the same directory as the found files]:program: _command_names -e:*(\;|+)::program arguments: _normal' '*-ok[prompt the user whether to execute a command]:program: _command_names -e:*(\;|+)::program arguments: _normal' '*-okdir[prompt the user whether to execute a command in the same directory as the found files]:program: _command_names -e:*(\;|+)::program arguments: _normal' - + '-exit[exit with status if found, default 0]' '*-fls[list files like ls -dils, but write to FILE instead of standard output]:output file:_files' '*-fprint[print the path to the found file, but write to FILE instead of standard output]:output file:_files' -- cgit v1.2.3 From 8f6b0c1b360f2fea3f7f6563808513cbdd51df80 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Wed, 10 Apr 2024 10:10:51 -0400 Subject: Implement -context Closes: https://github.com/tavianator/bfs/issues/27 --- completions/bfs.bash | 1 + completions/bfs.fish | 1 + completions/bfs.zsh | 1 + docs/bfs.1 | 4 ++ src/eval.c | 43 ++++++++++++------- src/eval.h | 1 + src/parse.c | 114 +++++++++++++++++++++++++++++---------------------- 7 files changed, 103 insertions(+), 62 deletions(-) (limited to 'completions/bfs.fish') diff --git a/completions/bfs.bash b/completions/bfs.bash index 816f1ec..6fd82c8 100644 --- a/completions/bfs.bash +++ b/completions/bfs.bash @@ -32,6 +32,7 @@ _bfs() { # (e.g. because they are numeric, glob, regexp, time, etc.) local nocomp=( -{a,B,c,m}{min,since,time} + -context -ilname -iname -inum diff --git a/completions/bfs.fish b/completions/bfs.fish index 0c58ef4..24b0ad9 100644 --- a/completions/bfs.fish +++ b/completions/bfs.fish @@ -71,6 +71,7 @@ complete -c bfs -o Btime -d "Find files birthed specified number of days ago" -x complete -c bfs -o ctime -d "Find files changed specified number of days ago" -x complete -c bfs -o mtime -d "Find files modified specified number of days ago" -x complete -c bfs -o capable -d "Find files with capabilities set" +complete -c bfs -o context -d "Find files by SELinux context" -x complete -c bfs -o depth -d "Find files with specified number of depth" -x complete -c bfs -o empty -d "Find empty files/directories" complete -c bfs -o executable -d "Find files the current user can execute" diff --git a/completions/bfs.zsh b/completions/bfs.zsh index 07db456..432ab8c 100644 --- a/completions/bfs.zsh +++ b/completions/bfs.zsh @@ -74,6 +74,7 @@ args=( '*-mtime[find files modified N days ago]:modification time (days):->times' '*-capable[find files with POSIX.1e capabilities set]' + '*-context[find files by SELinux context]:pattern' # -depth without parameters exist above. I don't know how to handle this gracefully '*-empty[find empty files/directories]' '*-executable[find files the current user can execute]' diff --git a/docs/bfs.1 b/docs/bfs.1 index 3a4f15a..54166ab 100644 --- a/docs/bfs.1 +++ b/docs/bfs.1 @@ -452,6 +452,10 @@ Find files with POSIX.1e .BR capabilities (7) set. .TP +\fB\-context \fIGLOB\fR +Find files whose SELinux context matches the +.IR GLOB . +.TP \fB\-depth\fR [\fI\-+\fR]\fIN\fR Find files with depth .IR N . diff --git a/src/eval.c b/src/eval.c index 2f06858..d0112c2 100644 --- a/src/eval.c +++ b/src/eval.c @@ -145,6 +145,20 @@ bool bfs_expr_cmp(const struct bfs_expr *expr, long long n) { return false; } +/** 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; + } +} + /** * -true test. */ @@ -193,6 +207,21 @@ bool eval_capable(const struct bfs_expr *expr, struct bfs_eval *state) { } } +/** + * -context test. + */ +bool eval_context(const struct bfs_expr *expr, struct bfs_eval *state) { + char *con = bfs_getfilecon(state->ftwbuf); + if (!con) { + eval_report_error(state); + return false; + } + + bool ret = eval_fnmatch(expr, con); + bfs_freecon(con); + return ret; +} + /** * Get the given timespec field out of a stat buffer. */ @@ -546,20 +575,6 @@ 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. */ diff --git a/src/eval.h b/src/eval.h index f7f6c77..ae43628 100644 --- a/src/eval.h +++ b/src/eval.h @@ -49,6 +49,7 @@ bool eval_false(const struct bfs_expr *expr, struct bfs_eval *state); bool eval_access(const struct bfs_expr *expr, struct bfs_eval *state); bool eval_acl(const struct bfs_expr *expr, struct bfs_eval *state); bool eval_capable(const struct bfs_expr *expr, struct bfs_eval *state); +bool eval_context(const struct bfs_expr *expr, struct bfs_eval *state); bool eval_perm(const struct bfs_expr *expr, struct bfs_eval *state); bool eval_xattr(const struct bfs_expr *expr, struct bfs_eval *state); bool eval_xattrname(const struct bfs_expr *expr, struct bfs_eval *state); diff --git a/src/parse.c b/src/parse.c index 38ebf3f..a3e32fe 100644 --- a/src/parse.c +++ b/src/parse.c @@ -1075,6 +1075,67 @@ static struct bfs_expr *parse_color(struct bfs_parser *parser, int color, int ar return expr; } +/** + * Common code for fnmatch() tests. + */ +static struct bfs_expr *parse_fnmatch(const struct bfs_parser *parser, struct bfs_expr *expr, bool casefold) { + if (!expr) { + return NULL; + } + + expr->pattern = expr->argv[1]; + + if (casefold) { +#ifdef FNM_CASEFOLD + expr->fnm_flags = FNM_CASEFOLD; +#else + parse_expr_error(parser, expr, "Missing platform support.\n"); + return NULL; +#endif + } else { + expr->fnm_flags = 0; + } + + // POSIX says, about fnmatch(): + // + // If pattern ends with an unescaped , fnmatch() shall + // return a non-zero value (indicating either no match or an error). + // + // But not all implementations obey this, so check for it ourselves. + size_t i, len = strlen(expr->pattern); + for (i = 0; i < len; ++i) { + if (expr->pattern[len - i - 1] != '\\') { + break; + } + } + if (i % 2 != 0) { + parse_expr_warning(parser, expr, "Unescaped trailing backslash.\n\n"); + expr->eval_fn = eval_false; + 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; +} + +/** + * Parse -context. + */ +static struct bfs_expr *parse_context(struct bfs_parser *parser, int flag, int arg2) { +#if BFS_CAN_CHECK_CONTEXT + struct bfs_expr *expr = parse_unary_test(parser, eval_context); + return parse_fnmatch(parser, expr, false); +#else + parse_error(parser, "Missing platform support.\n"); + return NULL; +#endif +} + /** * Parse -{false,true}. */ @@ -1631,54 +1692,6 @@ static struct bfs_expr *parse_mount(struct bfs_parser *parser, int arg1, int arg return expr; } -/** - * Common code for fnmatch() tests. - */ -static struct bfs_expr *parse_fnmatch(const struct bfs_parser *parser, struct bfs_expr *expr, bool casefold) { - if (!expr) { - return NULL; - } - - expr->pattern = expr->argv[1]; - - if (casefold) { -#ifdef FNM_CASEFOLD - expr->fnm_flags = FNM_CASEFOLD; -#else - parse_expr_error(parser, expr, "Missing platform support.\n"); - return NULL; -#endif - } else { - expr->fnm_flags = 0; - } - - // POSIX says, about fnmatch(): - // - // If pattern ends with an unescaped , fnmatch() shall - // return a non-zero value (indicating either no match or an error). - // - // But not all implementations obey this, so check for it ourselves. - size_t i, len = strlen(expr->pattern); - for (i = 0; i < len; ++i) { - if (expr->pattern[len - i - 1] != '\\') { - break; - } - } - if (i % 2 != 0) { - parse_expr_warning(parser, expr, "Unescaped trailing backslash.\n\n"); - expr->eval_fn = eval_false; - 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; -} - /** * Parse -i?name. */ @@ -2767,6 +2780,10 @@ static struct bfs_expr *parse_help(struct bfs_parser *parser, int arg1, int arg2 #if BFS_CAN_CHECK_CAPABILITIES cfprintf(cout, " ${blu}-capable${rs}\n"); cfprintf(cout, " Find files with POSIX.1e capabilities set\n"); +#endif +#if BFS_CAN_CHECK_CONTEXT + cfprintf(cout, " ${blu}-context${rs} ${bld}GLOB${rs}\n"); + cfprintf(cout, " Find files with SELinux context matching a glob pattern\n"); #endif cfprintf(cout, " ${blu}-depth${rs} ${bld}[-+]N${rs}\n"); cfprintf(cout, " Find files with depth ${bld}N${rs}\n"); @@ -2961,6 +2978,7 @@ static const struct table_entry parse_table[] = { {"-cmin", T_TEST, parse_min, BFS_STAT_CTIME}, {"-cnewer", T_TEST, parse_newer, BFS_STAT_CTIME}, {"-color", T_OPTION, parse_color, true}, + {"-context", T_TEST, parse_context, true}, {"-csince", T_TEST, parse_since, BFS_STAT_CTIME}, {"-ctime", T_TEST, parse_time, BFS_STAT_CTIME}, {"-d", T_FLAG, parse_depth}, -- cgit v1.2.3