878 строки
27 KiB
C++
878 строки
27 KiB
C++
/*============================================================================
|
|
CMake - Cross Platform Makefile Generator
|
|
Copyright 2000-2009 Kitware, Inc., Insight Software Consortium
|
|
|
|
Distributed under the OSI-approved BSD License (the "License");
|
|
see accompanying file Copyright.txt for details.
|
|
|
|
This software is distributed WITHOUT ANY WARRANTY; without even the
|
|
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
See the License for more information.
|
|
============================================================================*/
|
|
#include "cmOutputConverter.h"
|
|
|
|
#include "cmAlgorithms.h"
|
|
#include "cmake.h"
|
|
|
|
#include <assert.h>
|
|
|
|
#include <ctype.h> /* isalpha */
|
|
#include <string.h> /* strlen */
|
|
|
|
cmOutputConverter::cmOutputConverter(cmState::Snapshot snapshot)
|
|
: StateSnapshot(snapshot)
|
|
, LinkScriptShell(false)
|
|
{
|
|
assert(this->StateSnapshot.IsValid());
|
|
}
|
|
|
|
std::string cmOutputConverter::ConvertToOutputForExistingCommon(
|
|
const std::string& remote, std::string const& result,
|
|
OutputFormat format) const
|
|
{
|
|
// If this is a windows shell, the result has a space, and the path
|
|
// already exists, we can use a short-path to reference it without a
|
|
// space.
|
|
if (this->GetState()->UseWindowsShell() && result.find(' ') != result.npos &&
|
|
cmSystemTools::FileExists(remote.c_str())) {
|
|
std::string tmp;
|
|
if (cmSystemTools::GetShortPath(remote, tmp)) {
|
|
return this->ConvertToOutputFormat(tmp, format);
|
|
}
|
|
}
|
|
|
|
// Otherwise, leave it unchanged.
|
|
return result;
|
|
}
|
|
|
|
std::string cmOutputConverter::ConvertToOutputForExisting(
|
|
const std::string& remote, RelativeRoot local, OutputFormat format) const
|
|
{
|
|
static_cast<void>(local);
|
|
|
|
// Perform standard conversion.
|
|
std::string result = this->ConvertToOutputFormat(remote, format);
|
|
|
|
// Consider short-path.
|
|
return this->ConvertToOutputForExistingCommon(remote, result, format);
|
|
}
|
|
|
|
std::string cmOutputConverter::ConvertToOutputForExisting(
|
|
RelativeRoot remote, const std::string& local, OutputFormat format) const
|
|
{
|
|
// Perform standard conversion.
|
|
std::string result = this->Convert(remote, local, format, true);
|
|
|
|
// Consider short-path.
|
|
const char* remotePath = this->GetRelativeRootPath(remote);
|
|
return this->ConvertToOutputForExistingCommon(remotePath, result, format);
|
|
}
|
|
|
|
const char* cmOutputConverter::GetRelativeRootPath(RelativeRoot relroot) const
|
|
{
|
|
switch (relroot) {
|
|
case HOME:
|
|
return this->GetState()->GetSourceDirectory();
|
|
case START:
|
|
return this->StateSnapshot.GetDirectory().GetCurrentSource();
|
|
case HOME_OUTPUT:
|
|
return this->GetState()->GetBinaryDirectory();
|
|
case START_OUTPUT:
|
|
return this->StateSnapshot.GetDirectory().GetCurrentBinary();
|
|
default:
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
std::string cmOutputConverter::Convert(const std::string& source,
|
|
RelativeRoot relative,
|
|
OutputFormat output) const
|
|
{
|
|
// Convert the path to a relative path.
|
|
std::string result = source;
|
|
|
|
switch (relative) {
|
|
case HOME:
|
|
result = this->ConvertToRelativePath(
|
|
this->GetState()->GetSourceDirectoryComponents(), result);
|
|
break;
|
|
case START:
|
|
result = this->ConvertToRelativePath(
|
|
this->StateSnapshot.GetDirectory().GetCurrentSourceComponents(),
|
|
result);
|
|
break;
|
|
case HOME_OUTPUT:
|
|
result = this->ConvertToRelativePath(
|
|
this->GetState()->GetBinaryDirectoryComponents(), result);
|
|
break;
|
|
case START_OUTPUT:
|
|
result = this->ConvertToRelativePath(
|
|
this->StateSnapshot.GetDirectory().GetCurrentBinaryComponents(),
|
|
result);
|
|
break;
|
|
case FULL:
|
|
result = cmSystemTools::CollapseFullPath(result);
|
|
break;
|
|
case NONE:
|
|
break;
|
|
}
|
|
return this->ConvertToOutputFormat(result, output);
|
|
}
|
|
|
|
std::string cmOutputConverter::ConvertToOutputFormat(const std::string& source,
|
|
OutputFormat output) const
|
|
{
|
|
std::string result = source;
|
|
// Convert it to an output path.
|
|
if (output == MAKERULE) {
|
|
result = cmSystemTools::ConvertToOutputPath(result.c_str());
|
|
} else if (output == SHELL || output == WATCOMQUOTE) {
|
|
result = this->ConvertDirectorySeparatorsForShell(source);
|
|
result = this->EscapeForShell(result, true, false, output == WATCOMQUOTE);
|
|
} else if (output == RESPONSE) {
|
|
result = this->EscapeForShell(result, false, false, false);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
std::string cmOutputConverter::ConvertDirectorySeparatorsForShell(
|
|
const std::string& source) const
|
|
{
|
|
std::string result = source;
|
|
// For the MSYS shell convert drive letters to posix paths, so
|
|
// that c:/some/path becomes /c/some/path. This is needed to
|
|
// avoid problems with the shell path translation.
|
|
if (this->GetState()->UseMSYSShell() && !this->LinkScriptShell) {
|
|
if (result.size() > 2 && result[1] == ':') {
|
|
result[1] = result[0];
|
|
result[0] = '/';
|
|
}
|
|
}
|
|
if (this->GetState()->UseWindowsShell()) {
|
|
std::replace(result.begin(), result.end(), '/', '\\');
|
|
}
|
|
return result;
|
|
}
|
|
|
|
std::string cmOutputConverter::Convert(RelativeRoot remote,
|
|
const std::string& local,
|
|
OutputFormat output,
|
|
bool optional) const
|
|
{
|
|
const char* remotePath = this->GetRelativeRootPath(remote);
|
|
|
|
// The relative root must have a path (i.e. not FULL or NONE)
|
|
assert(remotePath != 0);
|
|
|
|
if (!local.empty() && !optional) {
|
|
std::vector<std::string> components;
|
|
cmSystemTools::SplitPath(local, components);
|
|
std::string result = this->ConvertToRelativePath(components, remotePath);
|
|
return this->ConvertToOutputFormat(result, output);
|
|
}
|
|
|
|
return this->ConvertToOutputFormat(remotePath, output);
|
|
}
|
|
|
|
static bool cmOutputConverterNotAbove(const char* a, const char* b)
|
|
{
|
|
return (cmSystemTools::ComparePath(a, b) ||
|
|
cmSystemTools::IsSubDirectory(a, b));
|
|
}
|
|
|
|
std::string cmOutputConverter::ConvertToRelativePath(
|
|
const std::vector<std::string>& local, const std::string& in_remote,
|
|
bool force) const
|
|
{
|
|
// The path should never be quoted.
|
|
assert(in_remote[0] != '\"');
|
|
|
|
// The local path should never have a trailing slash.
|
|
assert(!local.empty() && !(local[local.size() - 1] == ""));
|
|
|
|
// If the path is already relative then just return the path.
|
|
if (!cmSystemTools::FileIsFullPath(in_remote.c_str())) {
|
|
return in_remote;
|
|
}
|
|
|
|
if (!force) {
|
|
// Skip conversion if the path and local are not both in the source
|
|
// or both in the binary tree.
|
|
std::string local_path = cmSystemTools::JoinPath(local);
|
|
if (!((cmOutputConverterNotAbove(
|
|
local_path.c_str(),
|
|
this->StateSnapshot.GetDirectory().GetRelativePathTopBinary()) &&
|
|
cmOutputConverterNotAbove(
|
|
in_remote.c_str(),
|
|
this->StateSnapshot.GetDirectory().GetRelativePathTopBinary())) ||
|
|
(cmOutputConverterNotAbove(
|
|
local_path.c_str(),
|
|
this->StateSnapshot.GetDirectory().GetRelativePathTopSource()) &&
|
|
cmOutputConverterNotAbove(in_remote.c_str(),
|
|
this->StateSnapshot.GetDirectory()
|
|
.GetRelativePathTopSource())))) {
|
|
return in_remote;
|
|
}
|
|
}
|
|
|
|
// Identify the longest shared path component between the remote
|
|
// path and the local path.
|
|
std::vector<std::string> remote;
|
|
cmSystemTools::SplitPath(in_remote, remote);
|
|
unsigned int common = 0;
|
|
while (common < remote.size() && common < local.size() &&
|
|
cmSystemTools::ComparePath(remote[common], local[common])) {
|
|
++common;
|
|
}
|
|
|
|
// If no part of the path is in common then return the full path.
|
|
if (common == 0) {
|
|
return in_remote;
|
|
}
|
|
|
|
// If the entire path is in common then just return a ".".
|
|
if (common == remote.size() && common == local.size()) {
|
|
return ".";
|
|
}
|
|
|
|
// If the entire path is in common except for a trailing slash then
|
|
// just return a "./".
|
|
if (common + 1 == remote.size() && remote[common].empty() &&
|
|
common == local.size()) {
|
|
return "./";
|
|
}
|
|
|
|
// Construct the relative path.
|
|
std::string relative;
|
|
|
|
// First add enough ../ to get up to the level of the shared portion
|
|
// of the path. Leave off the trailing slash. Note that the last
|
|
// component of local will never be empty because local should never
|
|
// have a trailing slash.
|
|
for (unsigned int i = common; i < local.size(); ++i) {
|
|
relative += "..";
|
|
if (i < local.size() - 1) {
|
|
relative += "/";
|
|
}
|
|
}
|
|
|
|
// Now add the portion of the destination path that is not included
|
|
// in the shared portion of the path. Add a slash the first time
|
|
// only if there was already something in the path. If there was a
|
|
// trailing slash in the input then the last iteration of the loop
|
|
// will add a slash followed by an empty string which will preserve
|
|
// the trailing slash in the output.
|
|
|
|
if (!relative.empty() && !remote.empty()) {
|
|
relative += "/";
|
|
}
|
|
relative += cmJoin(cmMakeRange(remote).advance(common), "/");
|
|
|
|
// Finally return the path.
|
|
return relative;
|
|
}
|
|
|
|
static bool cmOutputConverterIsShellOperator(const std::string& str)
|
|
{
|
|
static std::set<std::string> shellOperators;
|
|
if (shellOperators.empty()) {
|
|
shellOperators.insert("<");
|
|
shellOperators.insert(">");
|
|
shellOperators.insert("<<");
|
|
shellOperators.insert(">>");
|
|
shellOperators.insert("|");
|
|
shellOperators.insert("||");
|
|
shellOperators.insert("&&");
|
|
shellOperators.insert("&>");
|
|
shellOperators.insert("1>");
|
|
shellOperators.insert("2>");
|
|
shellOperators.insert("2>&1");
|
|
shellOperators.insert("1>&2");
|
|
}
|
|
return shellOperators.count(str) > 0;
|
|
}
|
|
|
|
std::string cmOutputConverter::EscapeForShell(const std::string& str,
|
|
bool makeVars, bool forEcho,
|
|
bool useWatcomQuote) const
|
|
{
|
|
// Do not escape shell operators.
|
|
if (cmOutputConverterIsShellOperator(str)) {
|
|
return str;
|
|
}
|
|
|
|
// Compute the flags for the target shell environment.
|
|
int flags = 0;
|
|
if (this->GetState()->UseWindowsVSIDE()) {
|
|
flags |= Shell_Flag_VSIDE;
|
|
} else if (!this->LinkScriptShell) {
|
|
flags |= Shell_Flag_Make;
|
|
}
|
|
if (makeVars) {
|
|
flags |= Shell_Flag_AllowMakeVariables;
|
|
}
|
|
if (forEcho) {
|
|
flags |= Shell_Flag_EchoWindows;
|
|
}
|
|
if (useWatcomQuote) {
|
|
flags |= Shell_Flag_WatcomQuote;
|
|
}
|
|
if (this->GetState()->UseWatcomWMake()) {
|
|
flags |= Shell_Flag_WatcomWMake;
|
|
}
|
|
if (this->GetState()->UseMinGWMake()) {
|
|
flags |= Shell_Flag_MinGWMake;
|
|
}
|
|
if (this->GetState()->UseNMake()) {
|
|
flags |= Shell_Flag_NMake;
|
|
}
|
|
|
|
// Compute the buffer size needed.
|
|
int size = (this->GetState()->UseWindowsShell()
|
|
? Shell_GetArgumentSizeForWindows(str.c_str(), flags)
|
|
: Shell_GetArgumentSizeForUnix(str.c_str(), flags));
|
|
|
|
// Compute the shell argument itself.
|
|
std::vector<char> arg(size);
|
|
if (this->GetState()->UseWindowsShell()) {
|
|
Shell_GetArgumentForWindows(str.c_str(), &arg[0], flags);
|
|
} else {
|
|
Shell_GetArgumentForUnix(str.c_str(), &arg[0], flags);
|
|
}
|
|
return std::string(&arg[0]);
|
|
}
|
|
|
|
std::string cmOutputConverter::EscapeForCMake(const std::string& str)
|
|
{
|
|
// Always double-quote the argument to take care of most escapes.
|
|
std::string result = "\"";
|
|
for (const char* c = str.c_str(); *c; ++c) {
|
|
if (*c == '"') {
|
|
// Escape the double quote to avoid ending the argument.
|
|
result += "\\\"";
|
|
} else if (*c == '$') {
|
|
// Escape the dollar to avoid expanding variables.
|
|
result += "\\$";
|
|
} else if (*c == '\\') {
|
|
// Escape the backslash to avoid other escapes.
|
|
result += "\\\\";
|
|
} else {
|
|
// Other characters will be parsed correctly.
|
|
result += *c;
|
|
}
|
|
}
|
|
result += "\"";
|
|
return result;
|
|
}
|
|
|
|
std::string cmOutputConverter::EscapeWindowsShellArgument(const char* arg,
|
|
int shell_flags)
|
|
{
|
|
char local_buffer[1024];
|
|
char* buffer = local_buffer;
|
|
int size = Shell_GetArgumentSizeForWindows(arg, shell_flags);
|
|
if (size > 1024) {
|
|
buffer = new char[size];
|
|
}
|
|
Shell_GetArgumentForWindows(arg, buffer, shell_flags);
|
|
std::string result(buffer);
|
|
if (buffer != local_buffer) {
|
|
delete[] buffer;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
cmOutputConverter::FortranFormat cmOutputConverter::GetFortranFormat(
|
|
const char* value)
|
|
{
|
|
FortranFormat format = FortranFormatNone;
|
|
if (value && *value) {
|
|
std::vector<std::string> fmt;
|
|
cmSystemTools::ExpandListArgument(value, fmt);
|
|
for (std::vector<std::string>::iterator fi = fmt.begin(); fi != fmt.end();
|
|
++fi) {
|
|
if (*fi == "FIXED") {
|
|
format = FortranFormatFixed;
|
|
}
|
|
if (*fi == "FREE") {
|
|
format = FortranFormatFree;
|
|
}
|
|
}
|
|
}
|
|
return format;
|
|
}
|
|
|
|
void cmOutputConverter::SetLinkScriptShell(bool linkScriptShell)
|
|
{
|
|
this->LinkScriptShell = linkScriptShell;
|
|
}
|
|
|
|
cmState* cmOutputConverter::GetState() const
|
|
{
|
|
return this->StateSnapshot.GetState();
|
|
}
|
|
|
|
/*
|
|
|
|
Notes:
|
|
|
|
Make variable replacements open a can of worms. Sometimes they should
|
|
be quoted and sometimes not. Sometimes their replacement values are
|
|
already quoted.
|
|
|
|
VS variables cause problems. In order to pass the referenced value
|
|
with spaces the reference must be quoted. If the variable value ends
|
|
in a backslash then it will escape the ending quote! In order to make
|
|
the ending backslash appear we need this:
|
|
|
|
"$(InputDir)\"
|
|
|
|
However if there is not a trailing backslash then this will put a
|
|
quote in the value so we need:
|
|
|
|
"$(InputDir)"
|
|
|
|
Make variable references are platform specific so we should probably
|
|
just NOT quote them and let the listfile author deal with it.
|
|
|
|
*/
|
|
|
|
/*
|
|
TODO: For windows echo:
|
|
|
|
To display a pipe (|) or redirection character (< or >) when using the
|
|
echo command, use a caret character immediately before the pipe or
|
|
redirection character (for example, ^>, ^<, or ^| ). If you need to
|
|
use the caret character itself (^), use two in a row (^^).
|
|
*/
|
|
|
|
int cmOutputConverter::Shell__CharIsWhitespace(char c)
|
|
{
|
|
return ((c == ' ') || (c == '\t'));
|
|
}
|
|
|
|
int cmOutputConverter::Shell__CharNeedsQuotesOnUnix(char c)
|
|
{
|
|
return ((c == '\'') || (c == '`') || (c == ';') || (c == '#') ||
|
|
(c == '&') || (c == '$') || (c == '(') || (c == ')') || (c == '~') ||
|
|
(c == '<') || (c == '>') || (c == '|') || (c == '*') || (c == '^') ||
|
|
(c == '\\'));
|
|
}
|
|
|
|
int cmOutputConverter::Shell__CharNeedsQuotesOnWindows(char c)
|
|
{
|
|
return ((c == '\'') || (c == '#') || (c == '&') || (c == '<') ||
|
|
(c == '>') || (c == '|') || (c == '^'));
|
|
}
|
|
|
|
int cmOutputConverter::Shell__CharNeedsQuotes(char c, int isUnix, int flags)
|
|
{
|
|
/* On Windows the built-in command shell echo never needs quotes. */
|
|
if (!isUnix && (flags & Shell_Flag_EchoWindows)) {
|
|
return 0;
|
|
}
|
|
|
|
/* On all platforms quotes are needed to preserve whitespace. */
|
|
if (Shell__CharIsWhitespace(c)) {
|
|
return 1;
|
|
}
|
|
|
|
if (isUnix) {
|
|
/* On UNIX several special characters need quotes to preserve them. */
|
|
if (Shell__CharNeedsQuotesOnUnix(c)) {
|
|
return 1;
|
|
}
|
|
} else {
|
|
/* On Windows several special characters need quotes to preserve them. */
|
|
if (Shell__CharNeedsQuotesOnWindows(c)) {
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int cmOutputConverter::Shell__CharIsMakeVariableName(char c)
|
|
{
|
|
return c && (c == '_' || isalpha(((int)c)));
|
|
}
|
|
|
|
const char* cmOutputConverter::Shell__SkipMakeVariables(const char* c)
|
|
{
|
|
while (*c == '$' && *(c + 1) == '(') {
|
|
const char* skip = c + 2;
|
|
while (Shell__CharIsMakeVariableName(*skip)) {
|
|
++skip;
|
|
}
|
|
if (*skip == ')') {
|
|
c = skip + 1;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
return c;
|
|
}
|
|
|
|
/*
|
|
Allowing make variable replacements opens a can of worms. Sometimes
|
|
they should be quoted and sometimes not. Sometimes their replacement
|
|
values are already quoted or contain escapes.
|
|
|
|
Some Visual Studio variables cause problems. In order to pass the
|
|
referenced value with spaces the reference must be quoted. If the
|
|
variable value ends in a backslash then it will escape the ending
|
|
quote! In order to make the ending backslash appear we need this:
|
|
|
|
"$(InputDir)\"
|
|
|
|
However if there is not a trailing backslash then this will put a
|
|
quote in the value so we need:
|
|
|
|
"$(InputDir)"
|
|
|
|
This macro decides whether we quote an argument just because it
|
|
contains a make variable reference. This should be replaced with a
|
|
flag later when we understand applications of this better.
|
|
*/
|
|
#define KWSYS_SYSTEM_SHELL_QUOTE_MAKE_VARIABLES 0
|
|
|
|
int cmOutputConverter::Shell__ArgumentNeedsQuotes(const char* in, int isUnix,
|
|
int flags)
|
|
{
|
|
/* The empty string needs quotes. */
|
|
if (!*in) {
|
|
return 1;
|
|
}
|
|
|
|
/* Scan the string for characters that require quoting. */
|
|
{
|
|
const char* c;
|
|
for (c = in; *c; ++c) {
|
|
/* Look for $(MAKEVAR) syntax if requested. */
|
|
if (flags & Shell_Flag_AllowMakeVariables) {
|
|
#if KWSYS_SYSTEM_SHELL_QUOTE_MAKE_VARIABLES
|
|
const char* skip = Shell__SkipMakeVariables(c);
|
|
if (skip != c) {
|
|
/* We need to quote make variable references to preserve the
|
|
string with contents substituted in its place. */
|
|
return 1;
|
|
}
|
|
#else
|
|
/* Skip over the make variable references if any are present. */
|
|
c = Shell__SkipMakeVariables(c);
|
|
|
|
/* Stop if we have reached the end of the string. */
|
|
if (!*c) {
|
|
break;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/* Check whether this character needs quotes. */
|
|
if (Shell__CharNeedsQuotes(*c, isUnix, flags)) {
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* On Windows some single character arguments need quotes. */
|
|
if (!isUnix && *in && !*(in + 1)) {
|
|
char c = *in;
|
|
if ((c == '?') || (c == '&') || (c == '^') || (c == '|') || (c == '#')) {
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int cmOutputConverter::Shell__GetArgumentSize(const char* in, int isUnix,
|
|
int flags)
|
|
{
|
|
/* Start with the length of the original argument, plus one for
|
|
either a terminating null or a separating space. */
|
|
int size = (int)strlen(in) + 1;
|
|
|
|
/* String iterator. */
|
|
const char* c;
|
|
|
|
/* Keep track of how many backslashes have been encountered in a row. */
|
|
int windows_backslashes = 0;
|
|
|
|
/* Scan the string for characters that require escaping or quoting. */
|
|
for (c = in; *c; ++c) {
|
|
/* Look for $(MAKEVAR) syntax if requested. */
|
|
if (flags & Shell_Flag_AllowMakeVariables) {
|
|
/* Skip over the make variable references if any are present. */
|
|
c = Shell__SkipMakeVariables(c);
|
|
|
|
/* Stop if we have reached the end of the string. */
|
|
if (!*c) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Check whether this character needs escaping for the shell. */
|
|
if (isUnix) {
|
|
/* On Unix a few special characters need escaping even inside a
|
|
quoted argument. */
|
|
if (*c == '\\' || *c == '"' || *c == '`' || *c == '$') {
|
|
/* This character needs a backslash to escape it. */
|
|
++size;
|
|
}
|
|
} else if (flags & Shell_Flag_EchoWindows) {
|
|
/* On Windows the built-in command shell echo never needs escaping. */
|
|
} else {
|
|
/* On Windows only backslashes and double-quotes need escaping. */
|
|
if (*c == '\\') {
|
|
/* Found a backslash. It may need to be escaped later. */
|
|
++windows_backslashes;
|
|
} else if (*c == '"') {
|
|
/* Found a double-quote. We need to escape it and all
|
|
immediately preceding backslashes. */
|
|
size += windows_backslashes + 1;
|
|
windows_backslashes = 0;
|
|
} else {
|
|
/* Found another character. This eliminates the possibility
|
|
that any immediately preceding backslashes will be
|
|
escaped. */
|
|
windows_backslashes = 0;
|
|
}
|
|
}
|
|
|
|
/* Check whether this character needs escaping for a make tool. */
|
|
if (*c == '$') {
|
|
if (flags & Shell_Flag_Make) {
|
|
/* In Makefiles a dollar is written $$ so we need one extra
|
|
character. */
|
|
++size;
|
|
} else if (flags & Shell_Flag_VSIDE) {
|
|
/* In a VS IDE a dollar is written "$" so we need two extra
|
|
characters. */
|
|
size += 2;
|
|
}
|
|
} else if (*c == '#') {
|
|
if ((flags & Shell_Flag_Make) && (flags & Shell_Flag_WatcomWMake)) {
|
|
/* In Watcom WMake makefiles a pound is written $# so we need
|
|
one extra character. */
|
|
++size;
|
|
}
|
|
} else if (*c == '%') {
|
|
if ((flags & Shell_Flag_VSIDE) ||
|
|
((flags & Shell_Flag_Make) &&
|
|
((flags & Shell_Flag_MinGWMake) || (flags & Shell_Flag_NMake)))) {
|
|
/* In the VS IDE, NMake, or MinGW make a percent is written %%
|
|
so we need one extra characters. */
|
|
size += 1;
|
|
}
|
|
} else if (*c == ';') {
|
|
if (flags & Shell_Flag_VSIDE) {
|
|
/* In a VS IDE a semicolon is written ";" so we need two extra
|
|
characters. */
|
|
size += 2;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Check whether the argument needs surrounding quotes. */
|
|
if (Shell__ArgumentNeedsQuotes(in, isUnix, flags)) {
|
|
/* Surrounding quotes are needed. Allocate space for them. */
|
|
if ((flags & Shell_Flag_WatcomQuote) && (isUnix)) {
|
|
size += 2;
|
|
}
|
|
size += 2;
|
|
|
|
/* We must escape all ending backslashes when quoting on windows. */
|
|
size += windows_backslashes;
|
|
}
|
|
|
|
return size;
|
|
}
|
|
|
|
char* cmOutputConverter::Shell__GetArgument(const char* in, char* out,
|
|
int isUnix, int flags)
|
|
{
|
|
/* String iterator. */
|
|
const char* c;
|
|
|
|
/* Keep track of how many backslashes have been encountered in a row. */
|
|
int windows_backslashes = 0;
|
|
|
|
/* Whether the argument must be quoted. */
|
|
int needQuotes = Shell__ArgumentNeedsQuotes(in, isUnix, flags);
|
|
if (needQuotes) {
|
|
/* Add the opening quote for this argument. */
|
|
if (flags & Shell_Flag_WatcomQuote) {
|
|
if (isUnix) {
|
|
*out++ = '"';
|
|
}
|
|
*out++ = '\'';
|
|
} else {
|
|
*out++ = '"';
|
|
}
|
|
}
|
|
|
|
/* Scan the string for characters that require escaping or quoting. */
|
|
for (c = in; *c; ++c) {
|
|
/* Look for $(MAKEVAR) syntax if requested. */
|
|
if (flags & Shell_Flag_AllowMakeVariables) {
|
|
const char* skip = Shell__SkipMakeVariables(c);
|
|
if (skip != c) {
|
|
/* Copy to the end of the make variable references. */
|
|
while (c != skip) {
|
|
*out++ = *c++;
|
|
}
|
|
|
|
/* The make variable reference eliminates any escaping needed
|
|
for preceding backslashes. */
|
|
windows_backslashes = 0;
|
|
|
|
/* Stop if we have reached the end of the string. */
|
|
if (!*c) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Check whether this character needs escaping for the shell. */
|
|
if (isUnix) {
|
|
/* On Unix a few special characters need escaping even inside a
|
|
quoted argument. */
|
|
if (*c == '\\' || *c == '"' || *c == '`' || *c == '$') {
|
|
/* This character needs a backslash to escape it. */
|
|
*out++ = '\\';
|
|
}
|
|
} else if (flags & Shell_Flag_EchoWindows) {
|
|
/* On Windows the built-in command shell echo never needs escaping. */
|
|
} else {
|
|
/* On Windows only backslashes and double-quotes need escaping. */
|
|
if (*c == '\\') {
|
|
/* Found a backslash. It may need to be escaped later. */
|
|
++windows_backslashes;
|
|
} else if (*c == '"') {
|
|
/* Found a double-quote. Escape all immediately preceding
|
|
backslashes. */
|
|
while (windows_backslashes > 0) {
|
|
--windows_backslashes;
|
|
*out++ = '\\';
|
|
}
|
|
|
|
/* Add the backslash to escape the double-quote. */
|
|
*out++ = '\\';
|
|
} else {
|
|
/* We encountered a normal character. This eliminates any
|
|
escaping needed for preceding backslashes. */
|
|
windows_backslashes = 0;
|
|
}
|
|
}
|
|
|
|
/* Check whether this character needs escaping for a make tool. */
|
|
if (*c == '$') {
|
|
if (flags & Shell_Flag_Make) {
|
|
/* In Makefiles a dollar is written $$. The make tool will
|
|
replace it with just $ before passing it to the shell. */
|
|
*out++ = '$';
|
|
*out++ = '$';
|
|
} else if (flags & Shell_Flag_VSIDE) {
|
|
/* In a VS IDE a dollar is written "$". If this is written in
|
|
an un-quoted argument it starts a quoted segment, inserts
|
|
the $ and ends the segment. If it is written in a quoted
|
|
argument it ends quoting, inserts the $ and restarts
|
|
quoting. Either way the $ is isolated from surrounding
|
|
text to avoid looking like a variable reference. */
|
|
*out++ = '"';
|
|
*out++ = '$';
|
|
*out++ = '"';
|
|
} else {
|
|
/* Otherwise a dollar is written just $. */
|
|
*out++ = '$';
|
|
}
|
|
} else if (*c == '#') {
|
|
if ((flags & Shell_Flag_Make) && (flags & Shell_Flag_WatcomWMake)) {
|
|
/* In Watcom WMake makefiles a pound is written $#. The make
|
|
tool will replace it with just # before passing it to the
|
|
shell. */
|
|
*out++ = '$';
|
|
*out++ = '#';
|
|
} else {
|
|
/* Otherwise a pound is written just #. */
|
|
*out++ = '#';
|
|
}
|
|
} else if (*c == '%') {
|
|
if ((flags & Shell_Flag_VSIDE) ||
|
|
((flags & Shell_Flag_Make) &&
|
|
((flags & Shell_Flag_MinGWMake) || (flags & Shell_Flag_NMake)))) {
|
|
/* In the VS IDE, NMake, or MinGW make a percent is written %%. */
|
|
*out++ = '%';
|
|
*out++ = '%';
|
|
} else {
|
|
/* Otherwise a percent is written just %. */
|
|
*out++ = '%';
|
|
}
|
|
} else if (*c == ';') {
|
|
if (flags & Shell_Flag_VSIDE) {
|
|
/* In a VS IDE a semicolon is written ";". If this is written
|
|
in an un-quoted argument it starts a quoted segment,
|
|
inserts the ; and ends the segment. If it is written in a
|
|
quoted argument it ends quoting, inserts the ; and restarts
|
|
quoting. Either way the ; is isolated. */
|
|
*out++ = '"';
|
|
*out++ = ';';
|
|
*out++ = '"';
|
|
} else {
|
|
/* Otherwise a semicolon is written just ;. */
|
|
*out++ = ';';
|
|
}
|
|
} else {
|
|
/* Store this character. */
|
|
*out++ = *c;
|
|
}
|
|
}
|
|
|
|
if (needQuotes) {
|
|
/* Add enough backslashes to escape any trailing ones. */
|
|
while (windows_backslashes > 0) {
|
|
--windows_backslashes;
|
|
*out++ = '\\';
|
|
}
|
|
|
|
/* Add the closing quote for this argument. */
|
|
if (flags & Shell_Flag_WatcomQuote) {
|
|
*out++ = '\'';
|
|
if (isUnix) {
|
|
*out++ = '"';
|
|
}
|
|
} else {
|
|
*out++ = '"';
|
|
}
|
|
}
|
|
|
|
/* Store a terminating null without incrementing. */
|
|
*out = 0;
|
|
|
|
return out;
|
|
}
|
|
|
|
char* cmOutputConverter::Shell_GetArgumentForWindows(const char* in, char* out,
|
|
int flags)
|
|
{
|
|
return Shell__GetArgument(in, out, 0, flags);
|
|
}
|
|
|
|
char* cmOutputConverter::Shell_GetArgumentForUnix(const char* in, char* out,
|
|
int flags)
|
|
{
|
|
return Shell__GetArgument(in, out, 1, flags);
|
|
}
|
|
|
|
int cmOutputConverter::Shell_GetArgumentSizeForWindows(const char* in,
|
|
int flags)
|
|
{
|
|
return Shell__GetArgumentSize(in, 0, flags);
|
|
}
|
|
|
|
int cmOutputConverter::Shell_GetArgumentSizeForUnix(const char* in, int flags)
|
|
{
|
|
return Shell__GetArgumentSize(in, 1, flags);
|
|
}
|