diff options
author | Zachary Turner <zturner@google.com> | 2018-06-10 03:16:25 +0000 |
---|---|---|
committer | Zachary Turner <zturner@google.com> | 2018-06-10 03:16:25 +0000 |
commit | 071a09053ad2e5cc6e7f9fcace738193528c07b5 (patch) | |
tree | 5d6cbdcca6b42edd6c6d31289e6a545fa760b854 /llvm/lib/Support/Windows | |
parent | 5e119768a1ecf9d6f8ad5b38de7497cc334b69d9 (diff) | |
download | bcm5719-llvm-071a09053ad2e5cc6e7f9fcace738193528c07b5.tar.gz bcm5719-llvm-071a09053ad2e5cc6e7f9fcace738193528c07b5.zip |
Revert "Resubmit "[Support] Expose flattenWindowsCommandLine.""
This reverts commit 65243b6d19143cb7a03f68df0169dcb63e8b4632.
Seems like it's not a flake. It might have something to do with
the '*' character being in a command line.
llvm-svn: 334356
Diffstat (limited to 'llvm/lib/Support/Windows')
-rw-r--r-- | llvm/lib/Support/Windows/Path.inc | 2 | ||||
-rw-r--r-- | llvm/lib/Support/Windows/Program.inc | 187 |
2 files changed, 116 insertions, 73 deletions
diff --git a/llvm/lib/Support/Windows/Path.inc b/llvm/lib/Support/Windows/Path.inc index 7ef7468867d..9173206d816 100644 --- a/llvm/lib/Support/Windows/Path.inc +++ b/llvm/lib/Support/Windows/Path.inc @@ -1197,7 +1197,7 @@ Expected<file_t> openNativeFileForRead(const Twine &Name, OpenFlags Flags, if (Result && RealPath) realPathFromHandle(*Result, *RealPath); - return Result; + return std::move(Result); } void closeFile(file_t &F) { diff --git a/llvm/lib/Support/Windows/Program.inc b/llvm/lib/Support/Windows/Program.inc index ef1da602897..183b66ce2c0 100644 --- a/llvm/lib/Support/Windows/Program.inc +++ b/llvm/lib/Support/Windows/Program.inc @@ -23,7 +23,6 @@ #include <fcntl.h> #include <io.h> #include <malloc.h> -#include <numeric> //===----------------------------------------------------------------------===// //=== WARNING: Implementation here must contain only Win32 specific code @@ -32,7 +31,7 @@ namespace llvm { -ProcessInfo::ProcessInfo() : Pid(0), Process(0), ReturnCode(0) {} +ProcessInfo::ProcessInfo() : Process(0), Pid(0), ReturnCode(0) {} ErrorOr<std::string> sys::findProgramByName(StringRef Name, ArrayRef<StringRef> Paths) { @@ -147,13 +146,108 @@ static HANDLE RedirectIO(Optional<StringRef> Path, int fd, return h; } +/// ArgNeedsQuotes - Check whether argument needs to be quoted when calling +/// CreateProcess. +static bool ArgNeedsQuotes(const char *Str) { + return Str[0] == '\0' || strpbrk(Str, "\t \"&\'()*<>\\`^|") != 0; } -static SmallVector<StringRef, 8> buildArgVector(const char **Args) { - SmallVector<StringRef, 8> Result; - for (unsigned I = 0; Args[I]; ++I) - Result.push_back(StringRef(Args[I])); - return Result; +/// CountPrecedingBackslashes - Returns the number of backslashes preceding Cur +/// in the C string Start. +static unsigned int CountPrecedingBackslashes(const char *Start, + const char *Cur) { + unsigned int Count = 0; + --Cur; + while (Cur >= Start && *Cur == '\\') { + ++Count; + --Cur; + } + return Count; +} + +/// EscapePrecedingEscapes - Append a backslash to Dst for every backslash +/// preceding Cur in the Start string. Assumes Dst has enough space. +static char *EscapePrecedingEscapes(char *Dst, const char *Start, + const char *Cur) { + unsigned PrecedingEscapes = CountPrecedingBackslashes(Start, Cur); + while (PrecedingEscapes > 0) { + *Dst++ = '\\'; + --PrecedingEscapes; + } + return Dst; +} + +/// ArgLenWithQuotes - Check whether argument needs to be quoted when calling +/// CreateProcess and returns length of quoted arg with escaped quotes +static unsigned int ArgLenWithQuotes(const char *Str) { + const char *Start = Str; + bool Quoted = ArgNeedsQuotes(Str); + unsigned int len = Quoted ? 2 : 0; + + while (*Str != '\0') { + if (*Str == '\"') { + // We need to add a backslash, but ensure that it isn't escaped. + unsigned PrecedingEscapes = CountPrecedingBackslashes(Start, Str); + len += PrecedingEscapes + 1; + } + // Note that we *don't* need to escape runs of backslashes that don't + // precede a double quote! See MSDN: + // http://msdn.microsoft.com/en-us/library/17w5ykft%28v=vs.85%29.aspx + + ++len; + ++Str; + } + + if (Quoted) { + // Make sure the closing quote doesn't get escaped by a trailing backslash. + unsigned PrecedingEscapes = CountPrecedingBackslashes(Start, Str); + len += PrecedingEscapes + 1; + } + + return len; +} + +} + +static std::unique_ptr<char[]> flattenArgs(const char **Args) { + // First, determine the length of the command line. + unsigned len = 0; + for (unsigned i = 0; Args[i]; i++) { + len += ArgLenWithQuotes(Args[i]) + 1; + } + + // Now build the command line. + std::unique_ptr<char[]> command(new char[len+1]); + char *p = command.get(); + + for (unsigned i = 0; Args[i]; i++) { + const char *arg = Args[i]; + const char *start = arg; + + bool needsQuoting = ArgNeedsQuotes(arg); + if (needsQuoting) + *p++ = '"'; + + while (*arg != '\0') { + if (*arg == '\"') { + // Escape all preceding escapes (if any), and then escape the quote. + p = EscapePrecedingEscapes(p, start, arg); + *p++ = '\\'; + } + + *p++ = *arg++; + } + + if (needsQuoting) { + // Make sure our quote doesn't get escaped by a trailing backslash. + p = EscapePrecedingEscapes(p, start, arg); + *p++ = '"'; + } + *p++ = ' '; + } + + *p = 0; + return command; } static bool Execute(ProcessInfo &PI, StringRef Program, const char **Args, @@ -176,8 +270,7 @@ static bool Execute(ProcessInfo &PI, StringRef Program, const char **Args, // Windows wants a command line, not an array of args, to pass to the new // process. We have to concatenate them all, while quoting the args that // have embedded spaces (or are empty). - auto ArgVector = buildArgVector(Args); - std::string Command = flattenWindowsCommandLine(ArgVector); + std::unique_ptr<char[]> command = flattenArgs(Args); // The pointer to the environment block for the new process. std::vector<wchar_t> EnvBlock; @@ -260,7 +353,7 @@ static bool Execute(ProcessInfo &PI, StringRef Program, const char **Args, } SmallVector<wchar_t, MAX_PATH> CommandUtf16; - if (std::error_code ec = windows::UTF8ToUTF16(Command, CommandUtf16)) { + if (std::error_code ec = windows::UTF8ToUTF16(command.get(), CommandUtf16)) { SetLastError(ec.value()); MakeErrMsg(ErrMsg, std::string("Unable to convert command-line to UTF-16")); @@ -321,63 +414,7 @@ static bool Execute(ProcessInfo &PI, StringRef Program, const char **Args, return true; } -static bool argNeedsQuotes(StringRef Arg) { - if (Arg.empty()) - return true; - return StringRef::npos != Arg.find_first_of(" \t\n\v\""); -} - -static std::string quoteSingleArg(StringRef Arg) { - std::string Result; - Result.push_back('"'); - - while (!Arg.empty()) { - size_t FirstNonBackslash = Arg.find_first_not_of('\\'); - size_t BackslashCount = FirstNonBackslash; - if (FirstNonBackslash == StringRef::npos) { - // The entire remainder of the argument is backslashes. Escape all of - // them and just early out. - BackslashCount = Arg.size(); - Result.append(BackslashCount * 2, '\\'); - break; - } - - if (Arg[FirstNonBackslash] == '\"') { - // This is an embedded quote. Escape all preceding backslashes, then - // add one additional backslash to escape the quote. - Result.append(BackslashCount * 2 + 1, '\\'); - Result.push_back('\"'); - } else { - // This is just a normal character. Don't escape any of the preceding - // backslashes, just append them as they are and then append the - // character. - Result.append(BackslashCount, '\\'); - Result.push_back(Arg[FirstNonBackslash]); - } - - // Drop all the backslashes, plus the following character. - Arg = Arg.drop_front(FirstNonBackslash + 1); - } - - Result.push_back('"'); - return Result; -} - namespace llvm { -std::string sys::flattenWindowsCommandLine(ArrayRef<StringRef> Args) { - std::string Command; - for (StringRef Arg : Args) { - if (argNeedsQuotes(Arg)) - Command += quoteSingleArg(Arg); - else - Command += Arg; - - Command.push_back(' '); - } - - return Command; -} - ProcessInfo sys::Wait(const ProcessInfo &PI, unsigned SecondsToWait, bool WaitUntilChildTerminates, std::string *ErrMsg) { assert(PI.Pid && "invalid pid to wait on, process not started?"); @@ -500,13 +537,19 @@ llvm::sys::writeFileWithEncoding(StringRef FileName, StringRef Contents, } bool llvm::sys::commandLineFitsWithinSystemLimits(StringRef Program, - ArrayRef<StringRef> Args) { + ArrayRef<const char *> Args) { // The documented max length of the command line passed to CreateProcess. static const size_t MaxCommandStringLength = 32768; - SmallVector<StringRef, 8> FullArgs; - FullArgs.push_back(Program); - FullArgs.append(Args.begin(), Args.end()); - std::string Result = flattenWindowsCommandLine(FullArgs); - return (Result.size() + 1) <= MaxCommandStringLength; + // Account for the trailing space for the program path and the + // trailing NULL of the last argument. + size_t ArgLength = ArgLenWithQuotes(Program.str().c_str()) + 2; + for (const char* Arg : Args) { + // Account for the trailing space for every arg + ArgLength += ArgLenWithQuotes(Arg) + 1; + if (ArgLength > MaxCommandStringLength) { + return false; + } + } + return true; } } |