Do not backslash-escape non-ASCII UTF-8 bytes on POSIX Previously, kShellValid marked bytes >= 0x80 as invalid (0), causing EscapeStringToString_PosixNinjaFork to insert an ASCII backslash (0x5c) before each individual byte of multi-byte UTF-8 sequences. This broke multi-byte UTF-8 characters apart and produced invalid UTF-8 in generated .ninja files. Mark bytes 0x80-0xff as valid in kShellValid since non-ASCII bytes are not special characters in POSIX shells. Bug: 579608206 Change-Id: I117a6f377b72b6b6646af89bbb4db0d4fd857846 Reviewed-on: https://gn-review.googlesource.com/c/gn/+/28280 Commit-Queue: Takuto Ikuta <tikuta@google.com> Reviewed-by: Matt Stark <msta@google.com> Commit-Queue: Matt Stark <msta@google.com>
diff --git a/src/gn/escape.cc b/src/gn/escape.cc index 8d902d4..a3c139a 100644 --- a/src/gn/escape.cc +++ b/src/gn/escape.cc
@@ -39,7 +39,16 @@ // ` a b c d e f g h i j k l m n o 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // p q r s t u v w x y z { | } ~ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0}; + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, +// 80-ff: non-ASCII bytes (e.g. UTF-8 multi-byte sequences) are valid + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; // clang-format on constexpr std::array<char, 256> PosixEscapesTo(const char (&entries)[256]) {
diff --git a/src/gn/escape_unittest.cc b/src/gn/escape_unittest.cc index 62fd2e2..5d501d6 100644 --- a/src/gn/escape_unittest.cc +++ b/src/gn/escape_unittest.cc
@@ -66,6 +66,11 @@ // Curly braces must be escaped to avoid brace expansion on systems using // bash as default shell.. EXPECT_EQ("\\{a,b\\}\\{c,d\\}", EscapeString("{a,b}{c,d}", opts, nullptr)); + + // Non-ASCII UTF-8 bytes (>= 0x80) are not special to POSIX shells and must + // not be backslash-escaped byte-by-byte. + EXPECT_EQ("föo\\$ bär\\$ \\<あ\\>", + EscapeString("föo bär <あ>", opts, nullptr)); } TEST(Escape, NinjaPreformatted) {