diff --git a/Userland/Utilities/cut.cpp b/Userland/Utilities/cut.cpp index 53fb5a8ddd..54c7a9061c 100644 --- a/Userland/Utilities/cut.cpp +++ b/Userland/Utilities/cut.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -31,6 +32,11 @@ struct Range { m_from = min(m_from, other.m_from); m_to = max(m_to, other.m_to); } + + bool contains(size_t x) const + { + return m_from <= x && m_to >= x; + } }; static bool expand_list(ByteString& list, Vector& ranges) @@ -134,6 +140,22 @@ static void process_line_bytes(StringView line, Vector const& ranges) outln(); } +static void process_line_characters(StringView line, Vector const& ranges) +{ + for (auto const& range : ranges) { + if (range.m_from >= line.length()) + continue; + + auto s = String::from_utf8(line).release_value_but_fixme_should_propagate_errors(); + size_t i = 1; + for (auto c : s.code_points()) { + if (range.contains(i++)) + out("{}", String::from_code_point(c)); + } + } + outln(); +} + static void process_line_fields(StringView line, Vector const& ranges, char delimiter, bool only_print_delimited_lines) { auto string_split = ByteString(line).split(delimiter, SplitBehavior::KeepEmpty); @@ -157,6 +179,7 @@ static void process_line_fields(StringView line, Vector const& ranges, ch ErrorOr serenity_main(Main::Arguments arguments) { ByteString byte_list = ""; + ByteString character_list = ""; ByteString fields_list = ""; ByteString delimiter = "\t"; bool only_print_delimited_lines = false; @@ -166,24 +189,26 @@ ErrorOr serenity_main(Main::Arguments arguments) Core::ArgsParser args_parser; args_parser.add_positional_argument(files, "file(s) to cut", "file", Core::ArgsParser::Required::No); args_parser.add_option(byte_list, "select only these bytes", "bytes", 'b', "list"); + args_parser.add_option(character_list, "select only these characters", "characters", 'c', "list"); args_parser.add_option(fields_list, "select only these fields", "fields", 'f', "list"); args_parser.add_option(delimiter, "set a custom delimiter", "delimiter", 'd', "delimiter"); args_parser.add_option(only_print_delimited_lines, "suppress lines which don't contain any field delimiter characters", "only-delimited", 's'); args_parser.parse(arguments); - bool selected_bytes = (byte_list != ""); - bool selected_fields = (fields_list != ""); + bool const selected_bytes = (byte_list != ""); + bool const selected_characters = (character_list != ""); + bool const selected_fields = (fields_list != ""); - int selected_options_count = (selected_bytes ? 1 : 0) + (selected_fields ? 1 : 0); + int const selected_options_count = (selected_bytes ? 1 : 0) + (selected_characters ? 1 : 0) + (selected_fields ? 1 : 0); if (selected_options_count == 0) { - warnln("cut: you must specify a list of bytes, or fields"); + warnln("cut: you must specify a list of bytes, characters, or fields"); args_parser.print_usage(stderr, arguments.strings[0]); return 1; } if (selected_options_count > 1) { - warnln("cut: you must specify only one of bytes, or fields"); + warnln("cut: you must specify only one of bytes, characters, or fields"); args_parser.print_usage(stderr, arguments.strings[0]); return 1; } @@ -199,6 +224,8 @@ ErrorOr serenity_main(Main::Arguments arguments) if (selected_bytes) { ranges_list = byte_list; + } else if (selected_characters) { + ranges_list = character_list; } else if (selected_fields) { ranges_list = fields_list; } else { @@ -252,6 +279,8 @@ ErrorOr serenity_main(Main::Arguments arguments) if (selected_bytes) { process_line_bytes(line, disjoint_ranges); + } else if (selected_characters) { + process_line_characters(line, disjoint_ranges); } else if (selected_fields) { process_line_fields(line, disjoint_ranges, delimiter[0], only_print_delimited_lines); } else {