mirror of
https://github.com/RGBCube/serenity
synced 2025-07-27 14:07:45 +00:00
Userland: Add partial support for complex specifications to tr
This commit is contained in:
parent
310eb72f72
commit
9b2cc9580b
1 changed files with 84 additions and 11 deletions
|
@ -7,8 +7,86 @@
|
||||||
#include <AK/Optional.h>
|
#include <AK/Optional.h>
|
||||||
#include <AK/String.h>
|
#include <AK/String.h>
|
||||||
#include <LibCore/ArgsParser.h>
|
#include <LibCore/ArgsParser.h>
|
||||||
|
#include <ctype.h>
|
||||||
#include <stdio.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)
|
int main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
bool complement_flag = false;
|
bool complement_flag = false;
|
||||||
|
@ -28,19 +106,14 @@ int main(int argc, char** argv)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
String from_complement;
|
String from_str = build_set(from_chars);
|
||||||
StringView from_str;
|
|
||||||
if (complement_flag) {
|
if (complement_flag) {
|
||||||
auto original_set = StringView(from_chars);
|
|
||||||
StringBuilder complement_set;
|
StringBuilder complement_set;
|
||||||
for (int i = 0; i < 256; i++) {
|
for (int ch = 0; ch < 256; ch++) {
|
||||||
if (!original_set.contains(i))
|
if (!from_str.contains(static_cast<char>(ch)))
|
||||||
complement_set.append(static_cast<int>(i));
|
complement_set.append(static_cast<char>(ch));
|
||||||
}
|
}
|
||||||
from_complement = complement_set.to_string();
|
from_str = complement_set.to_string();
|
||||||
from_str = from_complement;
|
|
||||||
} else {
|
|
||||||
from_str = from_chars;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (delete_flag) {
|
if (delete_flag) {
|
||||||
|
@ -52,7 +125,7 @@ int main(int argc, char** argv)
|
||||||
putchar(ch);
|
putchar(ch);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
auto to_str = AK::StringView(to_chars);
|
auto to_str = build_set(to_chars);
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
char ch = fgetc(stdin);
|
char ch = fgetc(stdin);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue