summaryrefslogtreecommitdiff
path: root/Userland/Shell/Shell.cpp
diff options
context:
space:
mode:
authorAli Mohammad Pur <ali.mpfard@gmail.com>2021-05-10 11:09:40 +0430
committerAndreas Kling <kling@serenityos.org>2021-05-10 10:43:23 +0200
commit417910fd28147987fd09f4ec535aa2b759dce968 (patch)
treee05a84df40bedf304c1b04eade2cd98eb81463a5 /Userland/Shell/Shell.cpp
parent22b244df4580ae9e8c35899a513dd0eb7bff5fd0 (diff)
downloadserenity-417910fd28147987fd09f4ec535aa2b759dce968.zip
Shell: Make escaping more intelligent
Instead of the previous only-escape-with-backslashes, extend the escaping to one of: - No escape - Escape with backslash - Escape with "\xhh" if control character that isn't easily represented as \X - Escape with "\uhhhhhhhh" if unicode character that is too big to represent as "\xhh". Fixes #6986.
Diffstat (limited to 'Userland/Shell/Shell.cpp')
-rw-r--r--Userland/Shell/Shell.cpp70
1 files changed, 58 insertions, 12 deletions
diff --git a/Userland/Shell/Shell.cpp b/Userland/Shell/Shell.cpp
index c0e2b5c998..0dc3caae62 100644
--- a/Userland/Shell/Shell.cpp
+++ b/Userland/Shell/Shell.cpp
@@ -1142,9 +1142,9 @@ String Shell::escape_token_for_double_quotes(const String& token)
return builder.build();
}
-bool Shell::is_special(char c)
+Shell::SpecialCharacterEscapeMode Shell::special_character_escape_mode(u32 code_point)
{
- switch (c) {
+ switch (code_point) {
case '\'':
case '"':
case '$':
@@ -1156,25 +1156,72 @@ bool Shell::is_special(char c)
case '{':
case '}':
case '&':
+ case ';':
case '\\':
case ' ':
- return true;
+ return SpecialCharacterEscapeMode::Escaped;
+ case '\n':
+ case '\t':
+ case '\r':
+ return SpecialCharacterEscapeMode::QuotedAsEscape;
default:
- return false;
+ // FIXME: Should instead use unicode's "graphic" property (categories L, M, N, P, S, Zs)
+ if (code_point < NumericLimits<i32>::max()) {
+ if (isascii(static_cast<i32>(code_point)))
+ return isprint(static_cast<i32>(code_point)) ? SpecialCharacterEscapeMode::Untouched : SpecialCharacterEscapeMode::QuotedAsHex;
+ }
+ return SpecialCharacterEscapeMode::Untouched;
}
}
String Shell::escape_token(const String& token)
{
- StringBuilder builder;
+ auto do_escape = [](auto& token) {
+ StringBuilder builder;
+ for (auto c : token) {
+ static_assert(sizeof(c) == sizeof(u32) || sizeof(c) == sizeof(u8));
+ switch (special_character_escape_mode(c)) {
+ case SpecialCharacterEscapeMode::Untouched:
+ if constexpr (sizeof(c) == sizeof(u8))
+ builder.append(c);
+ else
+ builder.append(Utf32View { &c, 1 });
+ break;
+ case SpecialCharacterEscapeMode::Escaped:
+ builder.append('\\');
+ builder.append(c);
+ break;
+ case SpecialCharacterEscapeMode::QuotedAsEscape:
+ switch (c) {
+ case '\n':
+ builder.append(R"("\n")");
+ break;
+ case '\t':
+ builder.append(R"("\t")");
+ break;
+ case '\r':
+ builder.append(R"("\r")");
+ break;
+ default:
+ VERIFY_NOT_REACHED();
+ }
+ break;
+ case SpecialCharacterEscapeMode::QuotedAsHex:
+ if (c <= NumericLimits<u8>::max())
+ builder.appendff(R"("\x{:0>2x}")", static_cast<u8>(c));
+ else
+ builder.appendff(R"("\u{:0>8x}")", static_cast<u32>(c));
+ break;
+ }
+ }
- for (auto c : token) {
- if (is_special(c))
- builder.append('\\');
- builder.append(c);
- }
+ return builder.build();
+ };
- return builder.build();
+ Utf8View view { token };
+ if (view.validate())
+ return do_escape(view);
+ return do_escape(token);
}
String Shell::unescape_token(const String& token)
@@ -2057,5 +2104,4 @@ SavedFileDescriptors::~SavedFileDescriptors()
}
}
}
-
}