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

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: 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, quote the string, and backslash-escape all quotes and backslashes.
scottmg 2014/06/04 21:21:27 80
57 // See:
58 // http://blogs.msdn.com/b/twistylittlepassagesallalike/archive/2011/04/23/eve ryone-quotes-arguments-the-wrong-way.aspx
59 // http://blogs.msdn.com/b/oldnewthing/archive/2010/09/17/10063629.aspx
60 template<typename DestString>
61 void EscapeStringToString_WindowsNinjaFork(const base::StringPiece& str,
62 const EscapeOptions& options,
63 DestString* dest,
64 bool* needed_quoting) {
65 // We assume we don't have any whitespace chars that aren't spaces.
66 DCHECK(str.find_first_of("\r\n\v") == std::string::npos);
scottmg 2014/06/04 21:21:27 add \t maybe
67
68 if (str.find_first_of(" \"") == std::string::npos) {
69 // Simple case, don't quote.
70 EscapeStringToString_Ninja(str, options, dest, needed_quoting);
71 } else {
72 if (!options.inhibit_quoting)
73 dest->push_back('"');
74
75 for (size_t i = 0; i < str.size(); i++) {
76 // Count backslashes in case they're followed by a quote.
77 size_t backslash_count = 0;
78 while (i < str.size() && str[i] == '\\') {
79 i++;
80 backslash_count++;
81 }
82 if (i == str.size()) {
83 // Backslashes at end of string. Backslash-escape all of them since
84 // they'll be followed by a quote.
85 dest->append(backslash_count * 2, '\\');
86 } else if (str[i] == '"') {
87 // 0 or more backslashes followed by a quote. Backslash-escape the
88 // backslashes, then backslash-escape the quote.
89 dest->append(backslash_count * 2 + 1, '\\');
90 dest->push_back('"');
91 } else {
92 // Non-special Windows character, just escape for Ninja. Also, add any
93 // backslashes we read previously, these are literals.
94 dest->append(backslash_count, '\\');
95 NinjaEscapeChar(str[i], dest);
96 }
97 }
98
99 if (!options.inhibit_quoting)
100 dest->push_back('"');
101 if (needed_quoting)
102 *needed_quoting = true;
103 }
104 }
105
106 template<typename DestString>
107 void EscapeStringToString_PosixNinjaFork(const base::StringPiece& str,
108 const EscapeOptions& options,
109 DestString* dest,
110 bool* needed_quoting) {
111 for (size_t i = 0; i < str.size(); i++) {
112 if (str[i] == '$' || str[i] == ' ') {
113 // Space and $ are special to both Ninja and the shell. '$' escape for
114 // Ninja, then backslash-escape for the shell.
115 dest->push_back('\\');
116 dest->push_back('$');
117 dest->push_back(str[i]);
118 } else if (str[i] == ':') {
119 // Colon is the only other Ninja special char, which is not special to
120 // the shell.
121 dest->push_back('$');
122 dest->push_back(':');
123 } else if (static_cast<unsigned>(str[i]) >= 0x80 ||
124 !kShellValid[static_cast<int>(str[i])]) {
125 // All other invalid shell chars get backslash-escaped.
126 dest->push_back('\\');
127 dest->push_back(str[i]);
128 } else {
129 // Everything else is a literal.
130 dest->push_back(str[i]);
131 }
132 }
133 }
134
29 template<typename DestString> 135 template<typename DestString>
30 void EscapeStringToString(const base::StringPiece& str, 136 void EscapeStringToString(const base::StringPiece& str,
31 const EscapeOptions& options, 137 const EscapeOptions& options,
32 DestString* dest, 138 DestString* dest,
33 bool* needed_quoting) { 139 bool* needed_quoting) {
34 bool used_quotes = false; 140 switch (options.mode) {
35 141 case ESCAPE_NONE:
36 for (size_t i = 0; i < str.size(); i++) { 142 dest->append(str.data(), str.size());
37 if (str[i] == '$' && (options.mode & ESCAPE_NINJA)) { 143 break;
38 // Escape dollars signs since ninja treats these specially. If we're also 144 case ESCAPE_NINJA:
39 // escaping for the shell, we need to backslash-escape that again. 145 EscapeStringToString_Ninja(str, options, dest, needed_quoting);
40 if (options.mode & ESCAPE_SHELL) 146 break;
41 dest->push_back('\\'); 147 case ESCAPE_NINJA_FORK:
42 dest->push_back('$'); 148 switch (options.platform) {
43 dest->push_back('$'); 149 case ESCAPE_PLATFORM_CURRENT:
44 } else if (str[i] == ' ') { 150 #if defined(OS_WIN)
45 if (options.mode & ESCAPE_NINJA) { 151 EscapeStringToString_WindowsNinjaFork(str, options, dest,
46 // For Ninja just escape spaces with $. 152 needed_quoting);
47 dest->push_back('$'); 153 #else
154 EscapeStringToString_PosixNinjaFork(str, options, dest,
155 needed_quoting);
156 #endif
157 break;
158 case ESCAPE_PLATFORM_WIN:
159 EscapeStringToString_WindowsNinjaFork(str, options, dest,
160 needed_quoting);
161 break;
162 case ESCAPE_PLATFORM_POSIX:
163 EscapeStringToString_PosixNinjaFork(str, options, dest,
164 needed_quoting);
165 break;
166 default:
167 NOTREACHED();
48 } 168 }
49 if (options.mode & ESCAPE_SHELL) { 169 break;
50 // For the shell, quote the whole string. 170 default:
51 if (needed_quoting) 171 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 } 172 }
90
91 if (used_quotes)
92 dest->push_back('"');
93 } 173 }
94 174
95 } // namespace 175 } // namespace
96 176
97 std::string EscapeString(const base::StringPiece& str, 177 std::string EscapeString(const base::StringPiece& str,
98 const EscapeOptions& options, 178 const EscapeOptions& options,
99 bool* needed_quoting) { 179 bool* needed_quoting) {
100 std::string result; 180 std::string result;
101 result.reserve(str.size() + 4); // Guess we'll add a couple of extra chars. 181 result.reserve(str.size() + 4); // Guess we'll add a couple of extra chars.
102 EscapeStringToString(str, options, &result, needed_quoting); 182 EscapeStringToString(str, options, &result, needed_quoting);
103 return result; 183 return result;
104 } 184 }
105 185
106 void EscapeStringToStream(std::ostream& out, 186 void EscapeStringToStream(std::ostream& out,
107 const base::StringPiece& str, 187 const base::StringPiece& str,
108 const EscapeOptions& options) { 188 const EscapeOptions& options) {
109 // Escape to a stack buffer and then write out to the stream. 189 base::StackString<256> escaped;
110 base::StackVector<char, 256> result; 190 EscapeStringToString(str, options, &escaped.container(), NULL);
111 result->reserve(str.size() + 4); // Guess we'll add a couple of extra chars. 191 if (!escaped->empty())
112 EscapeStringToString(str, options, &result.container(), NULL); 192 out.write(escaped->data(), escaped->size());
113 if (!result->empty())
114 out.write(&result[0], result->size());
115 } 193 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698