From c006ebe5a301738e45d00bb378fa6bccab31af84 Mon Sep 17 00:00:00 2001 From: Tim Ledbetter Date: Mon, 18 Sep 2023 17:13:12 +0100 Subject: [PATCH] find: Add the `-path` and `-ipath` options These options behave the same way as `-name` and `-iname` but match the full file path instead of just the basename. --- Base/usr/share/man/man1/find.md | 13 +++++++++++++ Userland/Utilities/find.cpp | 30 +++++++++++++++++++++++++----- 2 files changed, 38 insertions(+), 5 deletions(-) diff --git a/Base/usr/share/man/man1/find.md b/Base/usr/share/man/man1/find.md index 9686139319..46a172c508 100644 --- a/Base/usr/share/man/man1/find.md +++ b/Base/usr/share/man/man1/find.md @@ -54,6 +54,19 @@ specified commands, a `-print` command is implicitly appended. * `M`: mebibytes (1024 kibibytes) * `G`: gibibytes (1024 mebibytes) +* `-path pattern`: Checks if the full file path matches the given global-style + pattern. This check matches against the full file name, starting from one of + the start points given on the command line. This means that using an absolute + path only makes sense in the case where the start point given on the command + line is an absolute path. For example, the following command will never match + anything: + + `find bar -ipath '/foo/bar/test_file' -print` + + The given path is compared against the current directory concatenated with the + basename of the current file. Because such a concatenation can never end in a + '/', specifying path argument that ends with a '/' will never match anything. +* `-ipath pattern`: Functions identically to `-path` but is case-insensitive. * `-name pattern`: Checks if the file name matches the given global-style pattern (case sensitive). * `-empty`: File is either an empty regular file or a directory containing no diff --git a/Userland/Utilities/find.cpp b/Userland/Utilities/find.cpp index fa5e51641d..4e3f870dc4 100644 --- a/Userland/Utilities/find.cpp +++ b/Userland/Utilities/find.cpp @@ -413,22 +413,34 @@ private: } }; -class NameCommand : public Command { +class PathCommand : public Command { public: - NameCommand(char const* pattern, CaseSensitivity case_sensitivity) + enum class PathPart { + FullPath, + Basename + }; + + PathCommand(char const* pattern, CaseSensitivity case_sensitivity, PathPart path_part) : m_pattern(pattern, strlen(pattern)) , m_case_sensitivity(case_sensitivity) + , m_path_part(path_part) { + if (path_part == PathPart::FullPath && m_pattern.ends_with('/')) + warnln("find: warning: path command will not match anything because it ends with '/'."); } private: virtual bool evaluate(FileData& file_data) const override { - return file_data.relative_path.basename().matches(m_pattern, m_case_sensitivity); + if (m_path_part == PathPart::Basename) + return file_data.relative_path.basename().matches(m_pattern, m_case_sensitivity); + + return file_data.full_path().matches(m_pattern, m_case_sensitivity); } StringView m_pattern; CaseSensitivity m_case_sensitivity { CaseSensitivity::CaseSensitive }; + PathPart m_path_part { PathPart::FullPath }; }; class RegexCommand : public Command { @@ -766,14 +778,22 @@ static OwnPtr parse_simple_command(Vector& args) return make(args.take_first()); } else if (arg == "-empty") { return make(); + } else if (arg == "-path") { + if (args.is_empty()) + fatal_error("-path: requires additional arguments"); + return make(args.take_first(), CaseSensitivity::CaseSensitive, PathCommand::PathPart::FullPath); + } else if (arg == "-ipath") { + if (args.is_empty()) + fatal_error("-ipath: requires additional arguments"); + return make(args.take_first(), CaseSensitivity::CaseInsensitive, PathCommand::PathPart::FullPath); } else if (arg == "-name") { if (args.is_empty()) fatal_error("-name: requires additional arguments"); - return make(args.take_first(), CaseSensitivity::CaseSensitive); + return make(args.take_first(), CaseSensitivity::CaseSensitive, PathCommand::PathPart::Basename); } else if (arg == "-iname") { if (args.is_empty()) fatal_error("-iname: requires additional arguments"); - return make(args.take_first(), CaseSensitivity::CaseInsensitive); + return make(args.take_first(), CaseSensitivity::CaseInsensitive, PathCommand::PathPart::Basename); } else if (arg == "-regex") { if (args.is_empty()) fatal_error("-regex: requires additional arguments");