From a95c2ed9784be8dc089735bab6efc5a4f2fd8a2b Mon Sep 17 00:00:00 2001 From: Tim Ledbetter Date: Sun, 10 Sep 2023 00:08:34 +0100 Subject: [PATCH] find: Add the `-maxdepth` and `-mindepth` options The `-maxdepth` option limits the number of levels `find` will descend into the file system for each given starting point. The `-mindepth` option causes commands not to be evaluated until the specified depth is reached. --- Base/usr/share/man/man1/find.md | 6 +++ Userland/Utilities/find.cpp | 66 +++++++++++++++++++++++++++++++-- 2 files changed, 69 insertions(+), 3 deletions(-) diff --git a/Base/usr/share/man/man1/find.md b/Base/usr/share/man/man1/find.md index 572850552b0..4f29f0ee36e 100644 --- a/Base/usr/share/man/man1/find.md +++ b/Base/usr/share/man/man1/find.md @@ -24,6 +24,12 @@ specified commands, a `-print` command is implicitly appended. ## Commands +* `-maxdepth n`: Do not descend more than `n` levels below each path given on + the command line. Specifying `-maxdepth 0` has the effect of only evaluating + each command line argument. +* `-mindepth n`: Descend `n` levels below each path given on the command line + before executing any commands. Specifying `-mindepth 1` has the effect of + processing all files except the command line arguments. * `-type t`: Checks if the file is of the specified type, which must be one of `b` (for block device), `c` (character device), `d` (directory), `l` (symbolic link), `p` (FIFO), `f` (regular file), and `s` (socket). diff --git a/Userland/Utilities/find.cpp b/Userland/Utilities/find.cpp index 48975828395..a94050cf717 100644 --- a/Userland/Utilities/find.cpp +++ b/Userland/Utilities/find.cpp @@ -33,6 +33,8 @@ bool g_follow_symlinks = false; bool g_there_was_an_error = false; bool g_have_seen_action_command = false; bool g_print_hyperlinks = false; +Optional g_max_depth = {}; +Optional g_min_depth = {}; template [[noreturn]] static void fatal_error(CheckedFormatString&& fmtstr, Parameters const&... parameters) @@ -167,6 +169,42 @@ private: } }; +class MaxDepthCommand : public Command { +public: + MaxDepthCommand(char const* arg) + { + auto max_depth_string = StringView { arg, strlen(arg) }; + auto maybe_max_depth = max_depth_string.to_uint(); + if (!maybe_max_depth.has_value()) + fatal_error("-maxdepth: '{}' is not a valid non-negative integer", arg); + + g_max_depth = maybe_max_depth.value(); + } + + virtual bool evaluate(FileData&) const override + { + return true; + } +}; + +class MinDepthCommand : public Command { +public: + MinDepthCommand(char const* arg) + { + auto min_depth_string = StringView { arg, strlen(arg) }; + auto maybe_min_depth = min_depth_string.to_uint(); + if (!maybe_min_depth.has_value()) + fatal_error("-mindepth: '{}' is not a valid non-negative integer", arg); + + g_min_depth = maybe_min_depth.value(); + } + + virtual bool evaluate(FileData&) const override + { + return true; + } +}; + class TypeCommand final : public Command { public: TypeCommand(char const* arg) @@ -605,6 +643,14 @@ static OwnPtr parse_simple_command(Vector& args) auto command = parse_simple_command(args).release_nonnull(); return make(move(command)); + } else if (arg == "-maxdepth"sv) { + if (args.is_empty()) + fatal_error("-maxdepth: requires additional arguments"); + return make(args.take_first()); + } else if (arg == "-mindepth"sv) { + if (args.is_empty()) + fatal_error("-mindepth: requires additional arguments"); + return make(args.take_first()); } else if (arg == "-type") { if (args.is_empty()) fatal_error("-type: requires additional arguments"); @@ -746,9 +792,10 @@ static NonnullOwnPtr parse_all_commands(Vector& args) return make(command.release_nonnull(), make()); } -static void walk_tree(FileData& root_data, Command& command) +static void walk_tree(FileData& root_data, Command& command, u32 depth = 0) { - command.evaluate(root_data); + if (!g_min_depth.has_value() || g_min_depth.value() <= depth) + command.evaluate(root_data); // We should try to read directory entries if either: // * This is a directory. @@ -798,7 +845,20 @@ static void walk_tree(FileData& root_data, Command& command) false, dirent->d_type, }; - walk_tree(file_data, command); + + bool should_increase_depth = false; + if (g_max_depth.has_value() || g_min_depth.has_value()) { + if (g_max_depth.has_value() && depth >= g_max_depth.value()) + return; + + if (file_data.d_type == DT_UNKNOWN) + file_data.ensure_stat(); + + if (file_data.d_type == DT_DIR) + should_increase_depth = true; + } + + walk_tree(file_data, command, should_increase_depth ? depth + 1 : depth); } if (errno != 0) {