Correct handling of unescapable entities in quotes

Prior to this fix, if you attempt to complete from inside a quote and the
completion contained an entity that cannot be represented inside quotes
(i.e. \n \r \t \b), the result would be a broken mess of quotes. Rewrite
the implementation so that it exits the quotes, emits the correct unquoted
escape, and then re-enters the quotes.
This commit is contained in:
ridiculousfish 2018-02-17 15:04:31 -08:00
parent 01d87455e1
commit 81b3baaa9c
2 changed files with 26 additions and 17 deletions

View file

@ -371,18 +371,23 @@ static void test_escape_quotes() {
do_test(parse_util_escape_string_with_quote(L"~abc|def", L'\0') == L"\\~abc\\|def");
do_test(parse_util_escape_string_with_quote(L"|abc~def", L'\0') == L"\\|abc\\~def");
do_test(parse_util_escape_string_with_quote(L"|abc~def", L'\0', true) == L"\\|abc~def");
do_test(parse_util_escape_string_with_quote(L"foo\nbar", L'\0') == L"foo\\nbar");
// Note tildes are not expanded inside quotes, so no_tilde is ignored with a quote.
do_test(parse_util_escape_string_with_quote(L"abc", L'\'') == L"abc");
do_test(parse_util_escape_string_with_quote(L"abc\\def", L'\'') == L"abc\\def");
do_test(parse_util_escape_string_with_quote(L"abc\\def", L'\'') == L"abc\\\\def");
do_test(parse_util_escape_string_with_quote(L"abc'def", L'\'') == L"abc\\'def");
do_test(parse_util_escape_string_with_quote(L"~abc'def", L'\'') == L"~abc\\'def");
do_test(parse_util_escape_string_with_quote(L"~abc'def", L'\'', true) == L"~abc\\'def");
do_test(parse_util_escape_string_with_quote(L"foo\nba'r", L'\'') == L"foo'\\n'ba\\'r");
do_test(parse_util_escape_string_with_quote(L"foo\\\\bar", L'\'') == L"foo\\\\\\\\bar");
do_test(parse_util_escape_string_with_quote(L"abc", L'"') == L"abc");
do_test(parse_util_escape_string_with_quote(L"abc\\def", L'"') == L"abc\\def");
do_test(parse_util_escape_string_with_quote(L"abc\\def", L'"') == L"abc\\\\def");
do_test(parse_util_escape_string_with_quote(L"~abc'def", L'"') == L"~abc'def");
do_test(parse_util_escape_string_with_quote(L"~abc'def", L'"', true) == L"~abc'def");
do_test(parse_util_escape_string_with_quote(L"foo\nba'r", L'"') == L"foo\"\\n\"ba'r");
do_test(parse_util_escape_string_with_quote(L"foo\\\\bar", L'"') == L"foo\\\\\\\\bar");
}
static void test_format(void) {

View file

@ -517,29 +517,33 @@ wcstring parse_util_escape_string_with_quote(const wcstring &cmd, wchar_t quote,
escape_flags_t flags = ESCAPE_ALL | ESCAPE_NO_QUOTED | (no_tilde ? ESCAPE_NO_TILDE : 0);
result = escape_string(cmd, flags);
} else {
bool unescapable = false;
for (size_t i = 0; i < cmd.size(); i++) {
wchar_t c = cmd.at(i);
// Here we are going to escape a string with quotes.
// A few characters cannot be represented inside quotes, e.g. newlines. In that case,
// terminate the quote and then re-enter it.
result.reserve(cmd.size());
for (wchar_t c : cmd) {
switch (c) {
case L'\n':
case L'\t':
case L'\b':
case L'\r': {
unescapable = true;
result.append({quote, L'\\', L'n', quote});
break;
}
default: {
case L'\t':
result.append({quote, L'\\', L't', quote});
break;
case L'\b':
result.append({quote, L'\\', L'b', quote});
break;
case L'\r':
result.append({quote, L'\\', L'r', quote});
break;
case L'\\':
result.append({L'\\', L'\\'});
break;
default:
if (c == quote) result.push_back(L'\\');
result.push_back(c);
break;
}
}
}
if (unescapable) {
result = escape_string(cmd, ESCAPE_ALL | ESCAPE_NO_QUOTED);
result.insert(0, &quote, 1);
}
}
return result;
}