diff options
author | Jean-Baptiste Boric <jblbeurope@gmail.com> | 2021-08-11 18:26:30 +0200 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2021-08-12 00:41:13 +0200 |
commit | 9b2cc9580b8bde12302b54755f4ab67983277b25 (patch) | |
tree | 09f284a2623d72fd943adf4c539248339b69867c | |
parent | 310eb72f729961ff9daa63158c5406b895c34a13 (diff) | |
download | serenity-9b2cc9580b8bde12302b54755f4ab67983277b25.zip |
Userland: Add partial support for complex specifications to tr
-rw-r--r-- | Userland/Utilities/tr.cpp | 95 |
1 files changed, 84 insertions, 11 deletions
diff --git a/Userland/Utilities/tr.cpp b/Userland/Utilities/tr.cpp index fa77a604d6..e9da1461ad 100644 --- a/Userland/Utilities/tr.cpp +++ b/Userland/Utilities/tr.cpp @@ -7,8 +7,86 @@ #include <AK/Optional.h> #include <AK/String.h> #include <LibCore/ArgsParser.h> +#include <ctype.h> #include <stdio.h> +static bool is_octal(int c) +{ + return c == '0' || c == '1' || c == '2' || c == '3' || c == '4' || c == '5' || c == '6' || c == '7'; +} + +static void generate_character_class(Function<int(int)> oracle, StringBuilder& out) +{ + for (int i = 0; i < 128; i++) { + if (oracle(i)) + out.append(static_cast<char>(i)); + } +} + +static String build_set(StringView specification) +{ + StringBuilder out; + GenericLexer lexer(specification); + + while (!lexer.is_eof()) { + if (lexer.consume_specific("[:alnum:]"sv)) + generate_character_class(isalnum, out); + else if (lexer.consume_specific("[:blank:]"sv)) + generate_character_class(isblank, out); + else if (lexer.consume_specific("[:digit:]"sv)) + generate_character_class(isdigit, out); + else if (lexer.consume_specific("[:lower:]"sv)) + generate_character_class(islower, out); + else if (lexer.consume_specific("[:punct:]"sv)) + generate_character_class(ispunct, out); + else if (lexer.consume_specific("[:upper:]"sv)) + generate_character_class(isupper, out); + else if (lexer.consume_specific("[:alpha:]"sv)) + generate_character_class(isalpha, out); + else if (lexer.consume_specific("[:cntrl:]"sv)) + generate_character_class(iscntrl, out); + else if (lexer.consume_specific("[:graph:]"sv)) + generate_character_class(isgraph, out); + else if (lexer.consume_specific("[:print:]"sv)) + generate_character_class(isprint, out); + else if (lexer.consume_specific("[:space:]"sv)) + generate_character_class(isspace, out); + else if (lexer.consume_specific("[:xdigit:]"sv)) + generate_character_class(isxdigit, out); + else if (lexer.consume_specific("\\\\"sv)) + out.append('\\'); + else if (lexer.consume_specific("\\a"sv)) + out.append('\a'); + else if (lexer.consume_specific("\\b"sv)) + out.append('\b'); + else if (lexer.consume_specific("\\f"sv)) + out.append('\f'); + else if (lexer.consume_specific("\\n"sv)) + out.append('\n'); + else if (lexer.consume_specific("\\r"sv)) + out.append('\r'); + else if (lexer.consume_specific("\\t"sv)) + out.append('\t'); + else if (lexer.consume_specific("\\v"sv)) + out.append('\v'); + else if (lexer.next_is('\\') && is_octal(lexer.peek(1))) { + lexer.consume_specific('\\'); + int max_left_over = 3; + auto octal_digits = lexer.consume_while([&](char i) -> bool { + return is_octal(i) && max_left_over--; + }); + + int value = 0; + for (char ch : octal_digits) + value = value * 8 + (ch - '0'); + out.append(static_cast<char>(value)); + } else + out.append(lexer.consume(1)); + } + + return out.to_string(); +} + int main(int argc, char** argv) { bool complement_flag = false; @@ -28,19 +106,14 @@ int main(int argc, char** argv) return 1; } - String from_complement; - StringView from_str; + String from_str = build_set(from_chars); if (complement_flag) { - auto original_set = StringView(from_chars); StringBuilder complement_set; - for (int i = 0; i < 256; i++) { - if (!original_set.contains(i)) - complement_set.append(static_cast<int>(i)); + for (int ch = 0; ch < 256; ch++) { + if (!from_str.contains(static_cast<char>(ch))) + complement_set.append(static_cast<char>(ch)); } - from_complement = complement_set.to_string(); - from_str = from_complement; - } else { - from_str = from_chars; + from_str = complement_set.to_string(); } if (delete_flag) { @@ -52,7 +125,7 @@ int main(int argc, char** argv) putchar(ch); } } else { - auto to_str = AK::StringView(to_chars); + auto to_str = build_set(to_chars); for (;;) { char ch = fgetc(stdin); |