mirror of
https://github.com/klzgrad/naiveproxy.git
synced 2024-12-01 01:36:09 +03:00
203 lines
7.3 KiB
C++
203 lines
7.3 KiB
C++
|
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
|
||
|
// Use of this source code is governed by a BSD-style license that can be
|
||
|
// found in the LICENSE file.
|
||
|
|
||
|
#include "tools/gn/escape.h"
|
||
|
|
||
|
#include <stddef.h>
|
||
|
|
||
|
#include "base/logging.h"
|
||
|
#include "build/build_config.h"
|
||
|
|
||
|
namespace {
|
||
|
|
||
|
// A "1" in this lookup table means that char is valid in the Posix shell.
|
||
|
const char kShellValid[0x80] = {
|
||
|
// 00-1f: all are invalid
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||
|
// ' ' ! " # $ % & ' ( ) * + , - . /
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1,
|
||
|
// 0 1 2 3 4 5 6 7 8 9 : ; < = > ?
|
||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0,
|
||
|
// @ A B C D E F G H I J K L M N O
|
||
|
1, 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, 1,
|
||
|
// ` 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 };
|
||
|
|
||
|
// Append one character to the given string, escaping it for Ninja.
|
||
|
//
|
||
|
// Ninja's escaping rules are very simple. We always escape colons even
|
||
|
// though they're OK in many places, in case the resulting string is used on
|
||
|
// the left-hand-side of a rule.
|
||
|
inline void NinjaEscapeChar(char ch, std::string* dest) {
|
||
|
if (ch == '$' || ch == ' ' || ch == ':')
|
||
|
dest->push_back('$');
|
||
|
dest->push_back(ch);
|
||
|
}
|
||
|
|
||
|
void EscapeStringToString_Ninja(const base::StringPiece& str,
|
||
|
const EscapeOptions& options,
|
||
|
std::string* dest,
|
||
|
bool* needed_quoting) {
|
||
|
for (const auto& elem : str)
|
||
|
NinjaEscapeChar(elem, dest);
|
||
|
}
|
||
|
|
||
|
void EscapeStringToString_NinjaPreformatted(const base::StringPiece& str,
|
||
|
std::string* dest) {
|
||
|
// Only Ninja-escape $.
|
||
|
for (const auto& elem : str) {
|
||
|
if (elem == '$')
|
||
|
dest->push_back('$');
|
||
|
dest->push_back(elem);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Escape for CommandLineToArgvW and additionally escape Ninja characters.
|
||
|
//
|
||
|
// The basic algorithm is if the string doesn't contain any parse-affecting
|
||
|
// characters, don't do anything (other than the Ninja processing). If it does,
|
||
|
// quote the string, and backslash-escape all quotes and backslashes.
|
||
|
// See:
|
||
|
// http://blogs.msdn.com/b/twistylittlepassagesallalike/archive/2011/04/23/everyone-quotes-arguments-the-wrong-way.aspx
|
||
|
// http://blogs.msdn.com/b/oldnewthing/archive/2010/09/17/10063629.aspx
|
||
|
void EscapeStringToString_WindowsNinjaFork(const base::StringPiece& str,
|
||
|
const EscapeOptions& options,
|
||
|
std::string* dest,
|
||
|
bool* needed_quoting) {
|
||
|
// We assume we don't have any whitespace chars that aren't spaces.
|
||
|
DCHECK(str.find_first_of("\r\n\v\t") == std::string::npos);
|
||
|
|
||
|
if (str.find_first_of(" \"") == std::string::npos) {
|
||
|
// Simple case, don't quote.
|
||
|
EscapeStringToString_Ninja(str, options, dest, needed_quoting);
|
||
|
} else {
|
||
|
if (!options.inhibit_quoting)
|
||
|
dest->push_back('"');
|
||
|
|
||
|
for (size_t i = 0; i < str.size(); i++) {
|
||
|
// Count backslashes in case they're followed by a quote.
|
||
|
size_t backslash_count = 0;
|
||
|
while (i < str.size() && str[i] == '\\') {
|
||
|
i++;
|
||
|
backslash_count++;
|
||
|
}
|
||
|
if (i == str.size()) {
|
||
|
// Backslashes at end of string. Backslash-escape all of them since
|
||
|
// they'll be followed by a quote.
|
||
|
dest->append(backslash_count * 2, '\\');
|
||
|
} else if (str[i] == '"') {
|
||
|
// 0 or more backslashes followed by a quote. Backslash-escape the
|
||
|
// backslashes, then backslash-escape the quote.
|
||
|
dest->append(backslash_count * 2 + 1, '\\');
|
||
|
dest->push_back('"');
|
||
|
} else {
|
||
|
// Non-special Windows character, just escape for Ninja. Also, add any
|
||
|
// backslashes we read previously, these are literals.
|
||
|
dest->append(backslash_count, '\\');
|
||
|
NinjaEscapeChar(str[i], dest);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!options.inhibit_quoting)
|
||
|
dest->push_back('"');
|
||
|
if (needed_quoting)
|
||
|
*needed_quoting = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void EscapeStringToString_PosixNinjaFork(const base::StringPiece& str,
|
||
|
const EscapeOptions& options,
|
||
|
std::string* dest,
|
||
|
bool* needed_quoting) {
|
||
|
for (const auto& elem : str) {
|
||
|
if (elem == '$' || elem == ' ') {
|
||
|
// Space and $ are special to both Ninja and the shell. '$' escape for
|
||
|
// Ninja, then backslash-escape for the shell.
|
||
|
dest->push_back('\\');
|
||
|
dest->push_back('$');
|
||
|
dest->push_back(elem);
|
||
|
} else if (elem == ':') {
|
||
|
// Colon is the only other Ninja special char, which is not special to
|
||
|
// the shell.
|
||
|
dest->push_back('$');
|
||
|
dest->push_back(':');
|
||
|
} else if (static_cast<unsigned>(elem) >= 0x80 ||
|
||
|
!kShellValid[static_cast<int>(elem)]) {
|
||
|
// All other invalid shell chars get backslash-escaped.
|
||
|
dest->push_back('\\');
|
||
|
dest->push_back(elem);
|
||
|
} else {
|
||
|
// Everything else is a literal.
|
||
|
dest->push_back(elem);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void EscapeStringToString(const base::StringPiece& str,
|
||
|
const EscapeOptions& options,
|
||
|
std::string* dest,
|
||
|
bool* needed_quoting) {
|
||
|
switch (options.mode) {
|
||
|
case ESCAPE_NONE:
|
||
|
dest->append(str.data(), str.size());
|
||
|
break;
|
||
|
case ESCAPE_NINJA:
|
||
|
EscapeStringToString_Ninja(str, options, dest, needed_quoting);
|
||
|
break;
|
||
|
case ESCAPE_NINJA_COMMAND:
|
||
|
switch (options.platform) {
|
||
|
case ESCAPE_PLATFORM_CURRENT:
|
||
|
#if defined(OS_WIN)
|
||
|
EscapeStringToString_WindowsNinjaFork(str, options, dest,
|
||
|
needed_quoting);
|
||
|
#else
|
||
|
EscapeStringToString_PosixNinjaFork(str, options, dest,
|
||
|
needed_quoting);
|
||
|
#endif
|
||
|
break;
|
||
|
case ESCAPE_PLATFORM_WIN:
|
||
|
EscapeStringToString_WindowsNinjaFork(str, options, dest,
|
||
|
needed_quoting);
|
||
|
break;
|
||
|
case ESCAPE_PLATFORM_POSIX:
|
||
|
EscapeStringToString_PosixNinjaFork(str, options, dest,
|
||
|
needed_quoting);
|
||
|
break;
|
||
|
default:
|
||
|
NOTREACHED();
|
||
|
}
|
||
|
break;
|
||
|
case ESCAPE_NINJA_PREFORMATTED_COMMAND:
|
||
|
EscapeStringToString_NinjaPreformatted(str, dest);
|
||
|
break;
|
||
|
default:
|
||
|
NOTREACHED();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
} // namespace
|
||
|
|
||
|
std::string EscapeString(const base::StringPiece& str,
|
||
|
const EscapeOptions& options,
|
||
|
bool* needed_quoting) {
|
||
|
std::string result;
|
||
|
result.reserve(str.size() + 4); // Guess we'll add a couple of extra chars.
|
||
|
EscapeStringToString(str, options, &result, needed_quoting);
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
void EscapeStringToStream(std::ostream& out,
|
||
|
const base::StringPiece& str,
|
||
|
const EscapeOptions& options) {
|
||
|
std::string escaped;
|
||
|
EscapeStringToString(str, options, &escaped, nullptr);
|
||
|
if (!escaped.empty())
|
||
|
out.write(escaped.data(), escaped.size());
|
||
|
}
|