diff options
author | Ali Mohammad Pur <ali.mpfard@gmail.com> | 2021-05-10 11:09:40 +0430 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2021-05-10 10:43:23 +0200 |
commit | 417910fd28147987fd09f4ec535aa2b759dce968 (patch) | |
tree | e05a84df40bedf304c1b04eade2cd98eb81463a5 /Userland/Shell/Shell.cpp | |
parent | 22b244df4580ae9e8c35899a513dd0eb7bff5fd0 (diff) | |
download | serenity-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.cpp | 70 |
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() } } } - } |