diff --git a/Base/usr/share/man/man1/find.md b/Base/usr/share/man/man1/find.md index 1689f66988c..56457f9fe6e 100644 --- a/Base/usr/share/man/man1/find.md +++ b/Base/usr/share/man/man1/find.md @@ -78,6 +78,7 @@ by the current user. The commands can be combined to form complex expressions using the following operators: +* `! command`: Logical NOT. * `command1 -o command2`: Logical OR. * `command1 -a command2`, `command1 command2`: Logical AND. * `( command )`: Groups commands together for operator priority purposes. diff --git a/Userland/Utilities/find.cpp b/Userland/Utilities/find.cpp index c646a60f98d..78cf0537c3f 100644 --- a/Userland/Utilities/find.cpp +++ b/Userland/Utilities/find.cpp @@ -512,6 +512,22 @@ private: NonnullOwnPtr m_rhs; }; +class NotCommand final : public Command { +public: + NotCommand(NonnullOwnPtr&& operand) + : m_operand(move(operand)) + { + } + +private: + virtual bool evaluate(FileData& file_data) const override + { + return !m_operand->evaluate(file_data); + } + + NonnullOwnPtr m_operand; +}; + static OwnPtr parse_complex_command(Vector& args); // Parse a simple command starting at optind; leave optind at its the last @@ -529,6 +545,12 @@ static OwnPtr parse_simple_command(Vector& args) if (command && !args.is_empty() && StringView { args.first(), strlen(args.first()) } == ")") return command; fatal_error("Unmatched \033[1m("); + } else if (arg == "!") { + if (args.is_empty()) + fatal_error("Expected an expression after '!'"); + + auto command = parse_simple_command(args).release_nonnull(); + return make(move(command)); } else if (arg == "-type") { if (args.is_empty()) fatal_error("-type: requires additional arguments"); @@ -746,12 +768,11 @@ ErrorOr serenity_main(Main::Arguments arguments) StringView arg { raw_arg, strlen(raw_arg) }; if (arg == "-L") { g_follow_symlinks = true; - } else if (!arg.starts_with('-')) { - paths.append(LexicalPath(arg)); - } else { - // No special case, so add back the argument and try to parse a command. + } else if (arg.starts_with('-') || arg == "!"sv) { args.prepend(raw_arg); command = parse_all_commands(args); + } else { + paths.append(LexicalPath(arg)); } }