diff --git a/src/fish_tests.cpp b/src/fish_tests.cpp index f6385a561..ef14f9ede 100644 --- a/src/fish_tests.cpp +++ b/src/fish_tests.cpp @@ -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) { diff --git a/src/parse_util.cpp b/src/parse_util.cpp index 6dbcc783c..92e74dac1 100644 --- a/src/parse_util.cpp +++ b/src/parse_util.cpp @@ -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, "e, 1); - } } return result; }