summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJean-Baptiste Boric <jblbeurope@gmail.com>2021-08-11 18:26:30 +0200
committerAndreas Kling <kling@serenityos.org>2021-08-12 00:41:13 +0200
commit9b2cc9580b8bde12302b54755f4ab67983277b25 (patch)
tree09f284a2623d72fd943adf4c539248339b69867c
parent310eb72f729961ff9daa63158c5406b895c34a13 (diff)
downloadserenity-9b2cc9580b8bde12302b54755f4ab67983277b25.zip
Userland: Add partial support for complex specifications to tr
-rw-r--r--Userland/Utilities/tr.cpp95
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);