mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-09-30 00:31:14 +00:00
Shell: Allow completing StringLiterals as paths
This auto-escapes the token as well :^)
This commit is contained in:
parent
118590325a
commit
0ea775f257
Notes:
sideshowbarker
2024-07-17 17:52:59 +09:00
Author: https://github.com/alimpfard Commit: https://github.com/SerenityOS/serenity/commit/0ea775f257 Pull-request: https://github.com/SerenityOS/serenity/pull/12903
|
@ -335,17 +335,40 @@ Vector<Line::CompletionSuggestion> Node::complete_for_editor(Shell& shell, size_
|
|||
{
|
||||
auto matching_node = hit_test_result.matching_node;
|
||||
if (matching_node) {
|
||||
if (matching_node->is_bareword()) {
|
||||
auto* node = static_cast<BarewordLiteral*>(matching_node.ptr());
|
||||
auto corrected_offset = find_offset_into_node(node->text(), offset - matching_node->position().start_offset);
|
||||
auto kind = matching_node->kind();
|
||||
StringLiteral::EnclosureType enclosure_type = StringLiteral::EnclosureType::None;
|
||||
if (kind == Kind::StringLiteral)
|
||||
enclosure_type = static_cast<StringLiteral*>(matching_node.ptr())->enclosure_type();
|
||||
|
||||
if (corrected_offset > node->text().length())
|
||||
auto set_results_trivia = [enclosure_type](Vector<Line::CompletionSuggestion>&& suggestions) {
|
||||
if (enclosure_type != StringLiteral::EnclosureType::None) {
|
||||
for (auto& entry : suggestions)
|
||||
entry.trailing_trivia = { static_cast<u32>(enclosure_type == StringLiteral::EnclosureType::SingleQuotes ? '\'' : '"') };
|
||||
}
|
||||
return suggestions;
|
||||
};
|
||||
if (kind == Kind::BarewordLiteral || kind == Kind::StringLiteral) {
|
||||
Shell::EscapeMode escape_mode;
|
||||
StringView text;
|
||||
size_t corrected_offset;
|
||||
if (kind == Kind::BarewordLiteral) {
|
||||
auto* node = static_cast<BarewordLiteral*>(matching_node.ptr());
|
||||
text = node->text();
|
||||
escape_mode = Shell::EscapeMode::Bareword;
|
||||
corrected_offset = find_offset_into_node(text, offset - matching_node->position().start_offset, escape_mode);
|
||||
} else {
|
||||
auto* node = static_cast<StringLiteral*>(matching_node.ptr());
|
||||
text = node->text();
|
||||
escape_mode = enclosure_type == StringLiteral::EnclosureType::SingleQuotes ? Shell::EscapeMode::SingleQuotedString : Shell::EscapeMode::DoubleQuotedString;
|
||||
corrected_offset = find_offset_into_node(text, offset - matching_node->position().start_offset + 1, escape_mode);
|
||||
}
|
||||
|
||||
if (corrected_offset > text.length())
|
||||
return {};
|
||||
auto& text = node->text();
|
||||
|
||||
// If the literal isn't an option, treat it as a path.
|
||||
if (!(text.starts_with("-") || text == "--" || text == "-"))
|
||||
return shell.complete_path("", text, corrected_offset, Shell::ExecutableOnly::No);
|
||||
return set_results_trivia(shell.complete_path("", text, corrected_offset, Shell::ExecutableOnly::No, escape_mode));
|
||||
|
||||
// If the literal is an option, we have to know the program name
|
||||
// should we have no way to get that, bail early.
|
||||
|
@ -363,7 +386,7 @@ Vector<Line::CompletionSuggestion> Node::complete_for_editor(Shell& shell, size_
|
|||
else
|
||||
program_name = static_cast<StringLiteral*>(program_name_node.ptr())->text();
|
||||
|
||||
return shell.complete_option(program_name, text, corrected_offset);
|
||||
return set_results_trivia(shell.complete_option(program_name, text, corrected_offset));
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
@ -3096,9 +3119,10 @@ void StringLiteral::highlight_in_editor(Line::Editor& editor, Shell&, HighlightM
|
|||
editor.stylize({ m_position.start_offset, m_position.end_offset }, move(style));
|
||||
}
|
||||
|
||||
StringLiteral::StringLiteral(Position position, String text)
|
||||
StringLiteral::StringLiteral(Position position, String text, EnclosureType enclosure_type)
|
||||
: Node(move(position))
|
||||
, m_text(move(text))
|
||||
, m_enclosure_type(enclosure_type)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -1344,11 +1344,18 @@ private:
|
|||
|
||||
class StringLiteral final : public Node {
|
||||
public:
|
||||
StringLiteral(Position, String);
|
||||
enum class EnclosureType {
|
||||
None,
|
||||
SingleQuotes,
|
||||
DoubleQuotes,
|
||||
};
|
||||
|
||||
StringLiteral(Position, String, EnclosureType);
|
||||
virtual ~StringLiteral();
|
||||
virtual void visit(NodeVisitor& visitor) override { visitor.visit(this); }
|
||||
|
||||
const String& text() const { return m_text; }
|
||||
EnclosureType enclosure_type() const { return m_enclosure_type; }
|
||||
|
||||
private:
|
||||
NODE(StringLiteral);
|
||||
|
@ -1358,6 +1365,7 @@ private:
|
|||
virtual RefPtr<Node> leftmost_trivial_literal() const override { return this; };
|
||||
|
||||
String m_text;
|
||||
EnclosureType m_enclosure_type;
|
||||
};
|
||||
|
||||
class StringPartCompose final : public Node {
|
||||
|
|
|
@ -228,7 +228,7 @@ RefPtr<AST::Node> Shell::immediate_regex_replace(AST::ImmediateExpression& invok
|
|||
Regex<PosixExtendedParser> re { pattern->resolve_as_list(this).first() };
|
||||
auto result = re.replace(value->resolve_as_list(this)[0], replacement->resolve_as_list(this)[0], PosixFlags::Global | PosixFlags::Multiline | PosixFlags::Unicode);
|
||||
|
||||
return AST::make_ref_counted<AST::StringLiteral>(invoking_node.position(), move(result));
|
||||
return AST::make_ref_counted<AST::StringLiteral>(invoking_node.position(), move(result), AST::StringLiteral::EnclosureType::None);
|
||||
}
|
||||
|
||||
RefPtr<AST::Node> Shell::immediate_remove_suffix(AST::ImmediateExpression& invoking_node, const NonnullRefPtrVector<AST::Node>& arguments)
|
||||
|
@ -256,7 +256,7 @@ RefPtr<AST::Node> Shell::immediate_remove_suffix(AST::ImmediateExpression& invok
|
|||
|
||||
if (value_str.ends_with(suffix_str))
|
||||
removed = removed.substring_view(0, value_str.length() - suffix_str.length());
|
||||
nodes.append(AST::make_ref_counted<AST::StringLiteral>(invoking_node.position(), removed));
|
||||
nodes.append(AST::make_ref_counted<AST::StringLiteral>(invoking_node.position(), removed, AST::StringLiteral::EnclosureType::None));
|
||||
}
|
||||
|
||||
return AST::make_ref_counted<AST::ListConcatenate>(invoking_node.position(), move(nodes));
|
||||
|
@ -287,7 +287,7 @@ RefPtr<AST::Node> Shell::immediate_remove_prefix(AST::ImmediateExpression& invok
|
|||
|
||||
if (value_str.starts_with(prefix_str))
|
||||
removed = removed.substring_view(prefix_str.length());
|
||||
nodes.append(AST::make_ref_counted<AST::StringLiteral>(invoking_node.position(), removed));
|
||||
nodes.append(AST::make_ref_counted<AST::StringLiteral>(invoking_node.position(), removed, AST::StringLiteral::EnclosureType::None));
|
||||
}
|
||||
|
||||
return AST::make_ref_counted<AST::ListConcatenate>(invoking_node.position(), move(nodes));
|
||||
|
@ -375,7 +375,7 @@ RefPtr<AST::Node> Shell::immediate_concat_lists(AST::ImmediateExpression& invoki
|
|||
} else {
|
||||
auto values = list_of_values->resolve_as_list(this);
|
||||
for (auto& entry : values)
|
||||
result.append(AST::make_ref_counted<AST::StringLiteral>(argument.position(), entry));
|
||||
result.append(AST::make_ref_counted<AST::StringLiteral>(argument.position(), entry, AST::StringLiteral::EnclosureType::None));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -333,7 +333,7 @@ RefPtr<AST::Node> Parser::parse_variable_decls()
|
|||
if (!expression) {
|
||||
if (is_whitespace(peek())) {
|
||||
auto string_start = push_start();
|
||||
expression = create<AST::StringLiteral>("");
|
||||
expression = create<AST::StringLiteral>("", AST::StringLiteral::EnclosureType::None);
|
||||
} else {
|
||||
restore_to(pos_before_name.offset, pos_before_name.line);
|
||||
return nullptr;
|
||||
|
@ -1276,7 +1276,7 @@ RefPtr<AST::Node> Parser::parse_string()
|
|||
bool is_error = false;
|
||||
if (!expect('\''))
|
||||
is_error = true;
|
||||
auto result = create<AST::StringLiteral>(move(text)); // String Literal
|
||||
auto result = create<AST::StringLiteral>(move(text), AST::StringLiteral::EnclosureType::SingleQuotes); // String Literal
|
||||
if (is_error)
|
||||
result->set_is_syntax_error(*create<AST::SyntaxError>("Expected a terminating single quote", true));
|
||||
return result;
|
||||
|
@ -1358,7 +1358,7 @@ RefPtr<AST::Node> Parser::parse_string_inner(StringEndCondition condition)
|
|||
continue;
|
||||
}
|
||||
if (peek() == '$') {
|
||||
auto string_literal = create<AST::StringLiteral>(builder.to_string()); // String Literal
|
||||
auto string_literal = create<AST::StringLiteral>(builder.to_string(), AST::StringLiteral::EnclosureType::DoubleQuotes); // String Literal
|
||||
auto read_concat = [&](auto&& node) {
|
||||
auto inner = create<AST::StringPartCompose>(
|
||||
move(string_literal),
|
||||
|
@ -1384,7 +1384,7 @@ RefPtr<AST::Node> Parser::parse_string_inner(StringEndCondition condition)
|
|||
builder.append(consume());
|
||||
}
|
||||
|
||||
return create<AST::StringLiteral>(builder.to_string()); // String Literal
|
||||
return create<AST::StringLiteral>(builder.to_string(), AST::StringLiteral::EnclosureType::DoubleQuotes); // String Literal
|
||||
}
|
||||
|
||||
RefPtr<AST::Node> Parser::parse_variable()
|
||||
|
@ -1923,7 +1923,7 @@ RefPtr<AST::Node> Parser::parse_brace_expansion_spec()
|
|||
|
||||
if (next_is(",")) {
|
||||
// Note that we don't consume the ',' here.
|
||||
subexpressions.append(create<AST::StringLiteral>(""));
|
||||
subexpressions.append(create<AST::StringLiteral>("", AST::StringLiteral::EnclosureType::None));
|
||||
} else {
|
||||
auto start_expr = parse_expression();
|
||||
if (start_expr) {
|
||||
|
@ -1948,7 +1948,7 @@ RefPtr<AST::Node> Parser::parse_brace_expansion_spec()
|
|||
if (expr) {
|
||||
subexpressions.append(expr.release_nonnull());
|
||||
} else {
|
||||
subexpressions.append(create<AST::StringLiteral>(""));
|
||||
subexpressions.append(create<AST::StringLiteral>("", AST::StringLiteral::EnclosureType::None));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2061,7 +2061,7 @@ bool Parser::parse_heredoc_entries()
|
|||
if (!last_line_offset.has_value())
|
||||
last_line_offset = current_position();
|
||||
// Now just wrap it in a StringLiteral and set it as the node's contents
|
||||
auto node = create<AST::StringLiteral>(m_input.substring_view(rule_start->offset, last_line_offset->offset - rule_start->offset));
|
||||
auto node = create<AST::StringLiteral>(m_input.substring_view(rule_start->offset, last_line_offset->offset - rule_start->offset), AST::StringLiteral::EnclosureType::None);
|
||||
if (!found_key)
|
||||
node->set_is_syntax_error(*create<AST::SyntaxError>(String::formatted("Expected to find the heredoc key '{}', but found Eof", record.end), true));
|
||||
record.node->set_contents(move(node));
|
||||
|
@ -2110,7 +2110,7 @@ bool Parser::parse_heredoc_entries()
|
|||
}
|
||||
|
||||
if (!expr && found_key) {
|
||||
expr = create<AST::StringLiteral>("");
|
||||
expr = create<AST::StringLiteral>("", AST::StringLiteral::EnclosureType::None);
|
||||
} else if (!expr) {
|
||||
expr = create<AST::SyntaxError>(String::formatted("Expected to find a valid string inside a heredoc (with end key '{}')", record.end), true);
|
||||
} else if (!found_key) {
|
||||
|
|
|
@ -1146,12 +1146,19 @@ String Shell::escape_token_for_double_quotes(StringView token)
|
|||
return builder.build();
|
||||
}
|
||||
|
||||
Shell::SpecialCharacterEscapeMode Shell::special_character_escape_mode(u32 code_point)
|
||||
Shell::SpecialCharacterEscapeMode Shell::special_character_escape_mode(u32 code_point, EscapeMode mode)
|
||||
{
|
||||
switch (code_point) {
|
||||
case '\'':
|
||||
if (mode == EscapeMode::DoubleQuotedString)
|
||||
return SpecialCharacterEscapeMode::Untouched;
|
||||
return SpecialCharacterEscapeMode::Escaped;
|
||||
case '"':
|
||||
case '$':
|
||||
case '\\':
|
||||
if (mode == EscapeMode::SingleQuotedString)
|
||||
return SpecialCharacterEscapeMode::Untouched;
|
||||
return SpecialCharacterEscapeMode::Escaped;
|
||||
case '|':
|
||||
case '>':
|
||||
case '<':
|
||||
|
@ -1161,8 +1168,9 @@ Shell::SpecialCharacterEscapeMode Shell::special_character_escape_mode(u32 code_
|
|||
case '}':
|
||||
case '&':
|
||||
case ';':
|
||||
case '\\':
|
||||
case ' ':
|
||||
if (mode == EscapeMode::SingleQuotedString || mode == EscapeMode::DoubleQuotedString)
|
||||
return SpecialCharacterEscapeMode::Untouched;
|
||||
return SpecialCharacterEscapeMode::Escaped;
|
||||
case '\n':
|
||||
case '\t':
|
||||
|
@ -1176,13 +1184,13 @@ Shell::SpecialCharacterEscapeMode Shell::special_character_escape_mode(u32 code_
|
|||
}
|
||||
}
|
||||
|
||||
String Shell::escape_token(StringView token)
|
||||
String Shell::escape_token(StringView token, EscapeMode escape_mode)
|
||||
{
|
||||
auto do_escape = [](auto& token) {
|
||||
auto do_escape = [escape_mode](auto& token) {
|
||||
StringBuilder builder;
|
||||
for (auto c : token) {
|
||||
static_assert(sizeof(c) == sizeof(u32) || sizeof(c) == sizeof(u8));
|
||||
switch (special_character_escape_mode(c)) {
|
||||
switch (special_character_escape_mode(c, escape_mode)) {
|
||||
case SpecialCharacterEscapeMode::Untouched:
|
||||
if constexpr (sizeof(c) == sizeof(u8))
|
||||
builder.append(c);
|
||||
|
@ -1190,29 +1198,51 @@ String Shell::escape_token(StringView token)
|
|||
builder.append(Utf32View { &c, 1 });
|
||||
break;
|
||||
case SpecialCharacterEscapeMode::Escaped:
|
||||
if (escape_mode == EscapeMode::SingleQuotedString)
|
||||
builder.append("'");
|
||||
builder.append('\\');
|
||||
builder.append(c);
|
||||
if (escape_mode == EscapeMode::SingleQuotedString)
|
||||
builder.append("'");
|
||||
break;
|
||||
case SpecialCharacterEscapeMode::QuotedAsEscape:
|
||||
if (escape_mode == EscapeMode::SingleQuotedString)
|
||||
builder.append("'");
|
||||
if (escape_mode != EscapeMode::DoubleQuotedString)
|
||||
builder.append("\"");
|
||||
switch (c) {
|
||||
case '\n':
|
||||
builder.append(R"("\n")");
|
||||
builder.append(R"(\n)");
|
||||
break;
|
||||
case '\t':
|
||||
builder.append(R"("\t")");
|
||||
builder.append(R"(\t)");
|
||||
break;
|
||||
case '\r':
|
||||
builder.append(R"("\r")");
|
||||
builder.append(R"(\r)");
|
||||
break;
|
||||
default:
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
if (escape_mode != EscapeMode::DoubleQuotedString)
|
||||
builder.append("\"");
|
||||
if (escape_mode == EscapeMode::SingleQuotedString)
|
||||
builder.append("'");
|
||||
break;
|
||||
case SpecialCharacterEscapeMode::QuotedAsHex:
|
||||
if (escape_mode == EscapeMode::SingleQuotedString)
|
||||
builder.append("'");
|
||||
if (escape_mode != EscapeMode::DoubleQuotedString)
|
||||
builder.append("\"");
|
||||
|
||||
if (c <= NumericLimits<u8>::max())
|
||||
builder.appendff(R"("\x{:0>2x}")", static_cast<u8>(c));
|
||||
builder.appendff(R"(\x{:0>2x})", static_cast<u8>(c));
|
||||
else
|
||||
builder.appendff(R"("\u{:0>8x}")", static_cast<u32>(c));
|
||||
builder.appendff(R"(\u{:0>8x})", static_cast<u32>(c));
|
||||
|
||||
if (escape_mode != EscapeMode::DoubleQuotedString)
|
||||
builder.append("\"");
|
||||
if (escape_mode == EscapeMode::SingleQuotedString)
|
||||
builder.append("'");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -1372,8 +1402,7 @@ Vector<Line::CompletionSuggestion> Shell::complete()
|
|||
return ast->complete_for_editor(*this, line.length());
|
||||
}
|
||||
|
||||
Vector<Line::CompletionSuggestion> Shell::complete_path(StringView base,
|
||||
StringView part, size_t offset, ExecutableOnly executable_only)
|
||||
Vector<Line::CompletionSuggestion> Shell::complete_path(StringView base, StringView part, size_t offset, ExecutableOnly executable_only, EscapeMode escape_mode)
|
||||
{
|
||||
auto token = offset ? part.substring_view(0, offset) : "";
|
||||
String path;
|
||||
|
@ -1415,7 +1444,7 @@ Vector<Line::CompletionSuggestion> Shell::complete_path(StringView base,
|
|||
// e. in `cd /foo/bar', 'bar' is the invariant
|
||||
// since we are not suggesting anything starting with
|
||||
// `/foo/', but rather just `bar...'
|
||||
auto token_length = escape_token(token).length();
|
||||
auto token_length = escape_token(token, escape_mode).length();
|
||||
size_t static_offset = last_slash + 1;
|
||||
auto invariant_offset = token_length;
|
||||
if (m_editor)
|
||||
|
@ -1435,11 +1464,11 @@ Vector<Line::CompletionSuggestion> Shell::complete_path(StringView base,
|
|||
int stat_error = stat(file_path.characters(), &program_status);
|
||||
if (!stat_error && (executable_only == ExecutableOnly::No || access(file_path.characters(), X_OK) == 0)) {
|
||||
if (S_ISDIR(program_status.st_mode)) {
|
||||
suggestions.append({ escape_token(file), "/" });
|
||||
suggestions.append({ escape_token(file, escape_mode), "/" });
|
||||
} else {
|
||||
if (!allow_direct_children && !file.contains("/"))
|
||||
continue;
|
||||
suggestions.append({ escape_token(file), " " });
|
||||
suggestions.append({ escape_token(file, escape_mode), " " });
|
||||
}
|
||||
suggestions.last().input_offset = token_length;
|
||||
suggestions.last().invariant_offset = invariant_offset;
|
||||
|
@ -1451,7 +1480,7 @@ Vector<Line::CompletionSuggestion> Shell::complete_path(StringView base,
|
|||
return suggestions;
|
||||
}
|
||||
|
||||
Vector<Line::CompletionSuggestion> Shell::complete_program_name(StringView name, size_t offset)
|
||||
Vector<Line::CompletionSuggestion> Shell::complete_program_name(StringView name, size_t offset, EscapeMode escape_mode)
|
||||
{
|
||||
auto match = binary_search(
|
||||
cached_path.span(),
|
||||
|
@ -1465,10 +1494,10 @@ Vector<Line::CompletionSuggestion> Shell::complete_program_name(StringView name,
|
|||
});
|
||||
|
||||
if (!match)
|
||||
return complete_path("", name, offset, ExecutableOnly::Yes);
|
||||
return complete_path("", name, offset, ExecutableOnly::Yes, escape_mode);
|
||||
|
||||
String completion = *match;
|
||||
auto token_length = escape_token(name).length();
|
||||
auto token_length = escape_token(name, escape_mode).length();
|
||||
auto invariant_offset = token_length;
|
||||
size_t static_offset = 0;
|
||||
if (m_editor)
|
||||
|
|
|
@ -156,9 +156,14 @@ public:
|
|||
[[nodiscard]] Frame push_frame(String name);
|
||||
void pop_frame();
|
||||
|
||||
enum class EscapeMode {
|
||||
Bareword,
|
||||
SingleQuotedString,
|
||||
DoubleQuotedString,
|
||||
};
|
||||
static String escape_token_for_double_quotes(StringView token);
|
||||
static String escape_token_for_single_quotes(StringView token);
|
||||
static String escape_token(StringView token);
|
||||
static String escape_token(StringView token, EscapeMode = EscapeMode::Bareword);
|
||||
static String unescape_token(StringView token);
|
||||
enum class SpecialCharacterEscapeMode {
|
||||
Untouched,
|
||||
|
@ -166,7 +171,7 @@ public:
|
|||
QuotedAsEscape,
|
||||
QuotedAsHex,
|
||||
};
|
||||
static SpecialCharacterEscapeMode special_character_escape_mode(u32 c);
|
||||
static SpecialCharacterEscapeMode special_character_escape_mode(u32 c, EscapeMode);
|
||||
|
||||
static bool is_glob(StringView);
|
||||
static Vector<StringView> split_path(StringView);
|
||||
|
@ -178,8 +183,8 @@ public:
|
|||
|
||||
void highlight(Line::Editor&) const;
|
||||
Vector<Line::CompletionSuggestion> complete();
|
||||
Vector<Line::CompletionSuggestion> complete_path(StringView base, StringView, size_t offset, ExecutableOnly executable_only);
|
||||
Vector<Line::CompletionSuggestion> complete_program_name(StringView, size_t offset);
|
||||
Vector<Line::CompletionSuggestion> complete_path(StringView base, StringView, size_t offset, ExecutableOnly executable_only, EscapeMode = EscapeMode::Bareword);
|
||||
Vector<Line::CompletionSuggestion> complete_program_name(StringView, size_t offset, EscapeMode = EscapeMode::Bareword);
|
||||
Vector<Line::CompletionSuggestion> complete_variable(StringView, size_t offset);
|
||||
Vector<Line::CompletionSuggestion> complete_user(StringView, size_t offset);
|
||||
Vector<Line::CompletionSuggestion> complete_option(StringView, StringView, size_t offset);
|
||||
|
@ -378,7 +383,7 @@ private:
|
|||
return c == '_' || (c <= 'Z' && c >= 'A') || (c <= 'z' && c >= 'a') || (c <= '9' && c >= '0');
|
||||
}
|
||||
|
||||
inline size_t find_offset_into_node(StringView unescaped_text, size_t escaped_offset)
|
||||
inline size_t find_offset_into_node(StringView unescaped_text, size_t escaped_offset, Shell::EscapeMode escape_mode)
|
||||
{
|
||||
size_t unescaped_offset = 0;
|
||||
size_t offset = 0;
|
||||
|
@ -387,20 +392,41 @@ inline size_t find_offset_into_node(StringView unescaped_text, size_t escaped_of
|
|||
if (offset == escaped_offset)
|
||||
return unescaped_offset;
|
||||
|
||||
switch (Shell::special_character_escape_mode(c)) {
|
||||
switch (Shell::special_character_escape_mode(c, escape_mode)) {
|
||||
case Shell::SpecialCharacterEscapeMode::Untouched:
|
||||
break;
|
||||
case Shell::SpecialCharacterEscapeMode::Escaped:
|
||||
++offset; // X -> \X
|
||||
break;
|
||||
case Shell::SpecialCharacterEscapeMode::QuotedAsEscape:
|
||||
offset += 3; // X -> "\Y"
|
||||
switch (escape_mode) {
|
||||
case Shell::EscapeMode::Bareword:
|
||||
offset += 3; // X -> "\Y"
|
||||
break;
|
||||
case Shell::EscapeMode::SingleQuotedString:
|
||||
offset += 5; // X -> '"\Y"'
|
||||
break;
|
||||
case Shell::EscapeMode::DoubleQuotedString:
|
||||
offset += 1; // X -> \Y
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case Shell::SpecialCharacterEscapeMode::QuotedAsHex:
|
||||
switch (escape_mode) {
|
||||
case Shell::EscapeMode::Bareword:
|
||||
offset += 2; // X -> "\..."
|
||||
break;
|
||||
case Shell::EscapeMode::SingleQuotedString:
|
||||
offset += 4; // X -> '"\..."'
|
||||
break;
|
||||
case Shell::EscapeMode::DoubleQuotedString:
|
||||
// X -> \...
|
||||
break;
|
||||
}
|
||||
if (c > NumericLimits<u8>::max())
|
||||
offset += 11; // X -> "\uhhhhhhhh"
|
||||
offset += 8; // X -> "\uhhhhhhhh"
|
||||
else
|
||||
offset += 5; // X -> "\xhh"
|
||||
offset += 3; // X -> "\xhh"
|
||||
break;
|
||||
}
|
||||
++offset;
|
||||
|
|
Loading…
Reference in a new issue