[refactor] More post-NSS WebCrypto cleanups (utility functions).
[chromium-blink-merge.git] / tools / gn / escape.cc
blob08d893ea18690cac18f916a949005a2627d4ae85
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
3 // found in the LICENSE file.
5 #include "tools/gn/escape.h"
7 #include "base/containers/stack_container.h"
8 #include "base/logging.h"
10 namespace {
12 // A "1" in this lookup table means that char is valid in the Posix shell.
13 const char kShellValid[0x80] = {
14 // 00-1f: all are invalid
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,
17 // ' ' ! " # $ % & ' ( ) * + , - . /
18 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1,
19 // 0 1 2 3 4 5 6 7 8 9 : ; < = > ?
20 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0,
21 // @ A B C D E F G H I J K L M N O
22 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
23 // P Q R S T U V W X Y Z [ \ ] ^ _
24 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
25 // ` a b c d e f g h i j k l m n o
26 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
27 // p q r s t u v w x y z { | } ~
28 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0 };
30 // Append one character to the given string, escaping it for Ninja.
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);
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 (const auto& elem : str)
48 NinjaEscapeChar(elem, dest);
51 template<typename DestString>
52 void EscapeStringToString_NinjaPreformatted(const base::StringPiece& str,
53 DestString* dest) {
54 // Only Ninja-escape $.
55 for (const auto& elem : str) {
56 if (elem == '$')
57 dest->push_back('$');
58 dest->push_back(elem);
62 // Escape for CommandLineToArgvW and additionally escape Ninja characters.
64 // The basic algorithm is if the string doesn't contain any parse-affecting
65 // characters, don't do anything (other than the Ninja processing). If it does,
66 // quote the string, and backslash-escape all quotes and backslashes.
67 // See:
68 // http://blogs.msdn.com/b/twistylittlepassagesallalike/archive/2011/04/23/everyone-quotes-arguments-the-wrong-way.aspx
69 // http://blogs.msdn.com/b/oldnewthing/archive/2010/09/17/10063629.aspx
70 template<typename DestString>
71 void EscapeStringToString_WindowsNinjaFork(const base::StringPiece& str,
72 const EscapeOptions& options,
73 DestString* dest,
74 bool* needed_quoting) {
75 // We assume we don't have any whitespace chars that aren't spaces.
76 DCHECK(str.find_first_of("\r\n\v\t") == std::string::npos);
78 if (str.find_first_of(" \"") == std::string::npos) {
79 // Simple case, don't quote.
80 EscapeStringToString_Ninja(str, options, dest, needed_quoting);
81 } else {
82 if (!options.inhibit_quoting)
83 dest->push_back('"');
85 for (size_t i = 0; i < str.size(); i++) {
86 // Count backslashes in case they're followed by a quote.
87 size_t backslash_count = 0;
88 while (i < str.size() && str[i] == '\\') {
89 i++;
90 backslash_count++;
92 if (i == str.size()) {
93 // Backslashes at end of string. Backslash-escape all of them since
94 // they'll be followed by a quote.
95 dest->append(backslash_count * 2, '\\');
96 } else if (str[i] == '"') {
97 // 0 or more backslashes followed by a quote. Backslash-escape the
98 // backslashes, then backslash-escape the quote.
99 dest->append(backslash_count * 2 + 1, '\\');
100 dest->push_back('"');
101 } else {
102 // Non-special Windows character, just escape for Ninja. Also, add any
103 // backslashes we read previously, these are literals.
104 dest->append(backslash_count, '\\');
105 NinjaEscapeChar(str[i], dest);
109 if (!options.inhibit_quoting)
110 dest->push_back('"');
111 if (needed_quoting)
112 *needed_quoting = true;
116 template<typename DestString>
117 void EscapeStringToString_PosixNinjaFork(const base::StringPiece& str,
118 const EscapeOptions& options,
119 DestString* dest,
120 bool* needed_quoting) {
121 for (const auto& elem : str) {
122 if (elem == '$' || elem == ' ') {
123 // Space and $ are special to both Ninja and the shell. '$' escape for
124 // Ninja, then backslash-escape for the shell.
125 dest->push_back('\\');
126 dest->push_back('$');
127 dest->push_back(elem);
128 } else if (elem == ':') {
129 // Colon is the only other Ninja special char, which is not special to
130 // the shell.
131 dest->push_back('$');
132 dest->push_back(':');
133 } else if (static_cast<unsigned>(elem) >= 0x80 ||
134 !kShellValid[static_cast<int>(elem)]) {
135 // All other invalid shell chars get backslash-escaped.
136 dest->push_back('\\');
137 dest->push_back(elem);
138 } else {
139 // Everything else is a literal.
140 dest->push_back(elem);
145 template<typename DestString>
146 void EscapeStringToString(const base::StringPiece& str,
147 const EscapeOptions& options,
148 DestString* dest,
149 bool* needed_quoting) {
150 switch (options.mode) {
151 case ESCAPE_NONE:
152 dest->append(str.data(), str.size());
153 break;
154 case ESCAPE_NINJA:
155 EscapeStringToString_Ninja(str, options, dest, needed_quoting);
156 break;
157 case ESCAPE_NINJA_COMMAND:
158 switch (options.platform) {
159 case ESCAPE_PLATFORM_CURRENT:
160 #if defined(OS_WIN)
161 EscapeStringToString_WindowsNinjaFork(str, options, dest,
162 needed_quoting);
163 #else
164 EscapeStringToString_PosixNinjaFork(str, options, dest,
165 needed_quoting);
166 #endif
167 break;
168 case ESCAPE_PLATFORM_WIN:
169 EscapeStringToString_WindowsNinjaFork(str, options, dest,
170 needed_quoting);
171 break;
172 case ESCAPE_PLATFORM_POSIX:
173 EscapeStringToString_PosixNinjaFork(str, options, dest,
174 needed_quoting);
175 break;
176 default:
177 NOTREACHED();
179 break;
180 case ESCAPE_NINJA_PREFORMATTED_COMMAND:
181 EscapeStringToString_NinjaPreformatted(str, dest);
182 break;
183 default:
184 NOTREACHED();
188 } // namespace
190 std::string EscapeString(const base::StringPiece& str,
191 const EscapeOptions& options,
192 bool* needed_quoting) {
193 std::string result;
194 result.reserve(str.size() + 4); // Guess we'll add a couple of extra chars.
195 EscapeStringToString(str, options, &result, needed_quoting);
196 return result;
199 void EscapeStringToStream(std::ostream& out,
200 const base::StringPiece& str,
201 const EscapeOptions& options) {
202 base::StackString<256> escaped;
203 EscapeStringToString(str, options, &escaped.container(), nullptr);
204 if (!escaped->empty())
205 out.write(escaped->data(), escaped->size());