From 0dc65cd835a3232aa0220675eb7e107b9ab89a8d Mon Sep 17 00:00:00 2001 From: Eli Youngs Date: Sun, 18 Dec 2022 22:29:10 -0800 Subject: [PATCH] sed: Write substitution output to a file with "/w" A substitution command like "s/x/y/wabc" will now write all substituted lines to a file called "abc". Note that this is in addition to writing to stdout. --- Userland/Utilities/sed.cpp | 58 ++++++++++++++++++++++++++++++++++---- 1 file changed, 52 insertions(+), 6 deletions(-) diff --git a/Userland/Utilities/sed.cpp b/Userland/Utilities/sed.cpp index ebb814bd520..c4811391611 100644 --- a/Userland/Utilities/sed.cpp +++ b/Userland/Utilities/sed.cpp @@ -4,6 +4,7 @@ * SPDX-License-Identifier: BSD-2-Clause */ +#include #include #include #include @@ -17,8 +18,31 @@ struct SubstitutionCommand { Regex regex; StringView replacement; PosixOptions options; + Optional output_filepath; }; +static Vector split_flags(StringView const& input) +{ + Vector flags; + + auto lexer = GenericLexer(input); + while (!lexer.is_eof()) { + StringView flag; + + if (lexer.next_is(is_ascii_digit)) { + flag = lexer.consume_while(is_ascii_digit); + } else if (lexer.peek() == 'w') { + flag = lexer.consume_all(); + } else { + flag = lexer.consume(1); + } + + flags.append(flag); + } + + return flags; +} + static ErrorOr parse_command(StringView command) { auto generic_error_message = "Incomplete substitution command"sv; @@ -53,18 +77,30 @@ static ErrorOr parse_command(StringView command) if (!lexer.consume_specific(delimiter)) return Error::from_string_literal("The substitution command was not properly terminated."); - PosixOptions const options = PosixOptions(PosixFlags::Global | PosixFlags::SingleMatch); + PosixOptions options = PosixOptions(PosixFlags::Global | PosixFlags::SingleMatch); + Optional output_filepath; - auto flags = lexer.consume_all(); - if (!flags.is_empty()) - warnln("sed: Flags are currently ignored"); + auto flags = split_flags(lexer.consume_all()); + for (auto const& flag : flags) { + if (flag.starts_with('w')) { + auto flag_filepath = flag.substring_view(1).trim_whitespace(); + if (flag_filepath.is_empty()) + return Error::from_string_literal("No filepath was provided for the 'w' flag."); + output_filepath = flag_filepath; + } else if (flag == "g"sv) { + // Allow multiple matches per line by un-setting the SingleMatch flag + options &= ~PosixFlags::SingleMatch; + } else { + warnln("sed: Unsupported flag: {}", flag); + } + } - return SubstitutionCommand { Regex { pattern }, replacement, options }; + return SubstitutionCommand { Regex { pattern }, replacement, options, output_filepath }; } ErrorOr serenity_main(Main::Arguments args) { - TRY(Core::System::pledge("stdio rpath")); + TRY(Core::System::pledge("stdio cpath rpath wpath")); Core::ArgsParser args_parser; @@ -78,6 +114,10 @@ ErrorOr serenity_main(Main::Arguments args) auto command = TRY(parse_command(command_input)); + Optional> maybe_output_file; + if (command.output_filepath.has_value()) + maybe_output_file = TRY(Core::Stream::File::open_file_or_standard_stream(command.output_filepath.release_value(), Core::Stream::OpenMode::Write)); + if (filepaths.is_empty()) filepaths = { "-"sv }; @@ -96,6 +136,12 @@ ErrorOr serenity_main(Main::Arguments args) auto result = command.regex.replace(line, command.replacement, command.options); outln(result); + + if (maybe_output_file.has_value()) { + auto const& output_file = maybe_output_file.value(); + TRY(output_file->write(result.bytes())); + TRY(output_file->write("\n"sv.bytes())); + } } }