Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(104)

Side by Side Diff: tools/gn/escape.cc

Issue 311733002: Redo escaping in GN (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: review comments Created 6 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "tools/gn/escape.h" 5 #include "tools/gn/escape.h"
6 6
7 #include "base/containers/stack_container.h" 7 #include "base/containers/stack_container.h"
8 #include "base/logging.h"
8 9
9 namespace { 10 namespace {
10 11
11 // A "1" in this lookup table means that char is valid in the shell. 12 // A "1" in this lookup table means that char is valid in the Posix shell.
12 const char kShellValid[0x80] = { 13 const char kShellValid[0x80] = {
13 // 00-1f: all are invalid 14 // 00-1f: all are invalid
14 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
15 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
16 // ' ' ! " # $ % & ' ( ) * + , - . / 17 // ' ' ! " # $ % & ' ( ) * + , - . /
17 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 18 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1,
18 // 0 1 2 3 4 5 6 7 8 9 : ; < = > ? 19 // 0 1 2 3 4 5 6 7 8 9 : ; < = > ?
19 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 20 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0,
20 // @ A B C D E F G H I J K L M N O 21 // @ A B C D E F G H I J K L M N O
21 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 22 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
22 // P Q R S T U V W X Y Z [ \ ] ^ _ 23 // P Q R S T U V W X Y Z [ \ ] ^ _
23 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 24 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
24 // ` a b c d e f g h i j k l m n o 25 // ` a b c d e f g h i j k l m n o
25 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 26 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
26 // p q r s t u v w x y z { | } ~ 27 // p q r s t u v w x y z { | } ~
27 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0 }; 28 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0 };
28 29
30 // Append one character to the given string, escaping it for Ninja.
31 //
32 // Ninja's escaping rules are very simple. We always escape colons even
33 // though they're OK in many places, in case the resulting string is used on
34 // the left-hand-side of a rule.
35 template<typename DestString>
36 inline void NinjaEscapeChar(char ch, DestString* dest) {
37 if (ch == '$' || ch == ' ' || ch == ':')
38 dest->push_back('$');
39 dest->push_back(ch);
40 }
41
42 template<typename DestString>
43 void EscapeStringToString_Ninja(const base::StringPiece& str,
44 const EscapeOptions& options,
45 DestString* dest,
46 bool* needed_quoting) {
47 for (size_t i = 0; i < str.size(); i++)
48 NinjaEscapeChar(str[i], dest);
49 if (needed_quoting)
50 *needed_quoting = false;
51 }
52
53 // Escape for CommandLineToArgvW and additionally escape Ninja characters.
54 //
55 // The basic algorithm is if the string doesn't contain any parse-affecting
56 // characters, don't do anything (other than the Ninja processing). If it does,
57 // quote the string, and backslash-escape all quotes and backslashes.
58 // See:
59 // http://blogs.msdn.com/b/twistylittlepassagesallalike/archive/2011/04/23/eve ryone-quotes-arguments-the-wrong-way.aspx
60 // http://blogs.msdn.com/b/oldnewthing/archive/2010/09/17/10063629.aspx
61 template<typename DestString>
62 void EscapeStringToString_WindowsNinjaFork(const base::StringPiece& str,
63 const EscapeOptions& options,
64 DestString* dest,
65 bool* needed_quoting) {
66 // We assume we don't have any whitespace chars that aren't spaces.
67 DCHECK(str.find_first_of("\r\n\v\t") == std::string::npos);
68
69 if (str.find_first_of(" \"") == std::string::npos) {
70 // Simple case, don't quote.
71 EscapeStringToString_Ninja(str, options, dest, needed_quoting);
72 } else {
73 if (!options.inhibit_quoting)
74 dest->push_back('"');
75
76 for (size_t i = 0; i < str.size(); i++) {
77 // Count backslashes in case they're followed by a quote.
78 size_t backslash_count = 0;
79 while (i < str.size() && str[i] == '\\') {
80 i++;
81 backslash_count++;
82 }
83 if (i == str.size()) {
84 // Backslashes at end of string. Backslash-escape all of them since
85 // they'll be followed by a quote.
86 dest->append(backslash_count * 2, '\\');
87 } else if (str[i] == '"') {
88 // 0 or more backslashes followed by a quote. Backslash-escape the
89 // backslashes, then backslash-escape the quote.
90 dest->append(backslash_count * 2 + 1, '\\');
91 dest->push_back('"');
92 } else {
93 // Non-special Windows character, just escape for Ninja. Also, add any
94 // backslashes we read previously, these are literals.
95 dest->append(backslash_count, '\\');
96 NinjaEscapeChar(str[i], dest);
97 }
98 }
99
100 if (!options.inhibit_quoting)
101 dest->push_back('"');
102 if (needed_quoting)
103 *needed_quoting = true;
104 }
105 }
106
107 template<typename DestString>
108 void EscapeStringToString_PosixNinjaFork(const base::StringPiece& str,
109 const EscapeOptions& options,
110 DestString* dest,
111 bool* needed_quoting) {
112 for (size_t i = 0; i < str.size(); i++) {
113 if (str[i] == '$' || str[i] == ' ') {
114 // Space and $ are special to both Ninja and the shell. '$' escape for
115 // Ninja, then backslash-escape for the shell.
116 dest->push_back('\\');
117 dest->push_back('$');
118 dest->push_back(str[i]);
119 } else if (str[i] == ':') {
120 // Colon is the only other Ninja special char, which is not special to
121 // the shell.
122 dest->push_back('$');
123 dest->push_back(':');
124 } else if (static_cast<unsigned>(str[i]) >= 0x80 ||
125 !kShellValid[static_cast<int>(str[i])]) {
126 // All other invalid shell chars get backslash-escaped.
127 dest->push_back('\\');
128 dest->push_back(str[i]);
129 } else {
130 // Everything else is a literal.
131 dest->push_back(str[i]);
132 }
133 }
134 }
135
29 template<typename DestString> 136 template<typename DestString>
30 void EscapeStringToString(const base::StringPiece& str, 137 void EscapeStringToString(const base::StringPiece& str,
31 const EscapeOptions& options, 138 const EscapeOptions& options,
32 DestString* dest, 139 DestString* dest,
33 bool* needed_quoting) { 140 bool* needed_quoting) {
34 bool used_quotes = false; 141 switch (options.mode) {
35 142 case ESCAPE_NONE:
36 for (size_t i = 0; i < str.size(); i++) { 143 dest->append(str.data(), str.size());
37 if (str[i] == '$' && (options.mode & ESCAPE_NINJA)) { 144 break;
38 // Escape dollars signs since ninja treats these specially. If we're also 145 case ESCAPE_NINJA:
39 // escaping for the shell, we need to backslash-escape that again. 146 EscapeStringToString_Ninja(str, options, dest, needed_quoting);
40 if (options.mode & ESCAPE_SHELL) 147 break;
41 dest->push_back('\\'); 148 case ESCAPE_NINJA_COMMAND:
42 dest->push_back('$'); 149 switch (options.platform) {
43 dest->push_back('$'); 150 case ESCAPE_PLATFORM_CURRENT:
44 } else if (str[i] == ' ') { 151 #if defined(OS_WIN)
45 if (options.mode & ESCAPE_NINJA) { 152 EscapeStringToString_WindowsNinjaFork(str, options, dest,
46 // For Ninja just escape spaces with $. 153 needed_quoting);
47 dest->push_back('$'); 154 #else
155 EscapeStringToString_PosixNinjaFork(str, options, dest,
156 needed_quoting);
157 #endif
158 break;
159 case ESCAPE_PLATFORM_WIN:
160 EscapeStringToString_WindowsNinjaFork(str, options, dest,
161 needed_quoting);
162 break;
163 case ESCAPE_PLATFORM_POSIX:
164 EscapeStringToString_PosixNinjaFork(str, options, dest,
165 needed_quoting);
166 break;
167 default:
168 NOTREACHED();
48 } 169 }
49 if (options.mode & ESCAPE_SHELL) { 170 break;
50 // For the shell, quote the whole string. 171 default:
51 if (needed_quoting) 172 NOTREACHED();
52 *needed_quoting = true;
53 if (!options.inhibit_quoting) {
54 if (!used_quotes) {
55 used_quotes = true;
56 dest->insert(dest->begin(), '"');
57 }
58 }
59 }
60 dest->push_back(' ');
61 } else if (str[i] == '\'' && (options.mode & ESCAPE_JSON)) {
62 dest->push_back('\\');
63 dest->push_back('\'');
64 #if defined(OS_WIN)
65 } else if (str[i] == '/' && options.convert_slashes) {
66 // Convert slashes on Windows if requested.
67 dest->push_back('\\');
68 #else
69 } else if (str[i] == '\\' && (options.mode & ESCAPE_SHELL)) {
70 // For non-Windows shell, escape backslashes.
71 dest->push_back('\\');
72 dest->push_back('\\');
73 #endif
74 } else if (str[i] == '\\' && (options.mode & ESCAPE_JSON)) {
75 dest->push_back('\\');
76 dest->push_back('\\');
77 } else if (str[i] == ':' && (options.mode & ESCAPE_NINJA)) {
78 dest->push_back('$');
79 dest->push_back(':');
80 } else if ((options.mode & ESCAPE_SHELL) &&
81 (static_cast<unsigned>(str[i]) >= 0x80 ||
82 !kShellValid[static_cast<int>(str[i])])) {
83 // All other invalid shell chars get backslash-escaped.
84 dest->push_back('\\');
85 dest->push_back(str[i]);
86 } else {
87 dest->push_back(str[i]);
88 }
89 } 173 }
90
91 if (used_quotes)
92 dest->push_back('"');
93 } 174 }
94 175
95 } // namespace 176 } // namespace
96 177
97 std::string EscapeString(const base::StringPiece& str, 178 std::string EscapeString(const base::StringPiece& str,
98 const EscapeOptions& options, 179 const EscapeOptions& options,
99 bool* needed_quoting) { 180 bool* needed_quoting) {
100 std::string result; 181 std::string result;
101 result.reserve(str.size() + 4); // Guess we'll add a couple of extra chars. 182 result.reserve(str.size() + 4); // Guess we'll add a couple of extra chars.
102 EscapeStringToString(str, options, &result, needed_quoting); 183 EscapeStringToString(str, options, &result, needed_quoting);
103 return result; 184 return result;
104 } 185 }
105 186
106 void EscapeStringToStream(std::ostream& out, 187 void EscapeStringToStream(std::ostream& out,
107 const base::StringPiece& str, 188 const base::StringPiece& str,
108 const EscapeOptions& options) { 189 const EscapeOptions& options) {
109 // Escape to a stack buffer and then write out to the stream. 190 base::StackString<256> escaped;
110 base::StackVector<char, 256> result; 191 EscapeStringToString(str, options, &escaped.container(), NULL);
111 result->reserve(str.size() + 4); // Guess we'll add a couple of extra chars. 192 if (!escaped->empty())
112 EscapeStringToString(str, options, &result.container(), NULL); 193 out.write(escaped->data(), escaped->size());
113 if (!result->empty())
114 out.write(&result[0], result->size());
115 } 194 }
OLDNEW
« no previous file with comments | « tools/gn/escape.h ('k') | tools/gn/escape_unittest.cc » ('j') | tools/gn/escape_unittest.cc » ('J')

Powered by Google App Engine
This is Rietveld 408576698