diff options
Diffstat (limited to 'lib/Support/Windows/Program.inc')
-rw-r--r-- | lib/Support/Windows/Program.inc | 215 |
1 files changed, 83 insertions, 132 deletions
diff --git a/lib/Support/Windows/Program.inc b/lib/Support/Windows/Program.inc index 52921cd6a203..cb68c5b10e52 100644 --- a/lib/Support/Windows/Program.inc +++ b/lib/Support/Windows/Program.inc @@ -16,12 +16,14 @@ #include "llvm/Support/ConvertUTF.h" #include "llvm/Support/Errc.h" #include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" #include "llvm/Support/WindowsError.h" #include "llvm/Support/raw_ostream.h" #include <cstdio> #include <fcntl.h> #include <io.h> #include <malloc.h> +#include <numeric> //===----------------------------------------------------------------------===// //=== WARNING: Implementation here must contain only Win32 specific code @@ -30,7 +32,7 @@ namespace llvm { -ProcessInfo::ProcessInfo() : ProcessHandle(0), Pid(0), ReturnCode(0) {} +ProcessInfo::ProcessInfo() : Pid(0), Process(0), ReturnCode(0) {} ErrorOr<std::string> sys::findProgramByName(StringRef Name, ArrayRef<StringRef> Paths) { @@ -145,112 +147,11 @@ 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; } -/// 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, - const char **Envp, ArrayRef<Optional<StringRef>> Redirects, +static bool Execute(ProcessInfo &PI, StringRef Program, + ArrayRef<StringRef> Args, Optional<ArrayRef<StringRef>> Env, + ArrayRef<Optional<StringRef>> Redirects, unsigned MemoryLimit, std::string *ErrMsg) { if (!sys::fs::can_execute(Program)) { if (ErrMsg) @@ -269,18 +170,18 @@ 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). - std::unique_ptr<char[]> command = flattenArgs(Args); + std::string Command = flattenWindowsCommandLine(Args); // The pointer to the environment block for the new process. std::vector<wchar_t> EnvBlock; - if (Envp) { + if (Env) { // An environment block consists of a null-terminated block of // null-terminated strings. Convert the array of environment variables to // an environment block by concatenating them. - for (unsigned i = 0; Envp[i]; ++i) { + for (const auto E : *Env) { SmallVector<wchar_t, MAX_PATH> EnvString; - if (std::error_code ec = windows::UTF8ToUTF16(Envp[i], EnvString)) { + if (std::error_code ec = windows::UTF8ToUTF16(E, EnvString)) { SetLastError(ec.value()); MakeErrMsg(ErrMsg, "Unable to convert environment variable to UTF-16"); return false; @@ -352,7 +253,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.get(), CommandUtf16)) { + if (std::error_code ec = windows::UTF8ToUTF16(Command, CommandUtf16)) { SetLastError(ec.value()); MakeErrMsg(ErrMsg, std::string("Unable to convert command-line to UTF-16")); @@ -380,7 +281,7 @@ static bool Execute(ProcessInfo &PI, StringRef Program, const char **Args, } PI.Pid = pi.dwProcessId; - PI.ProcessHandle = pi.hProcess; + PI.Process = pi.hProcess; // Make sure these get closed no matter what. ScopedCommonHandle hThread(pi.hThread); @@ -413,11 +314,67 @@ 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 \"&\'()*<>\\`^|"); +} + +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?"); - assert(PI.ProcessHandle && + assert((PI.Process && PI.Process != INVALID_HANDLE_VALUE) && "invalid process handle to wait on, process not started?"); DWORD milliSecondsToWait = 0; if (WaitUntilChildTerminates) @@ -426,20 +383,20 @@ ProcessInfo sys::Wait(const ProcessInfo &PI, unsigned SecondsToWait, milliSecondsToWait = SecondsToWait * 1000; ProcessInfo WaitResult = PI; - DWORD WaitStatus = WaitForSingleObject(PI.ProcessHandle, milliSecondsToWait); + DWORD WaitStatus = WaitForSingleObject(PI.Process, milliSecondsToWait); if (WaitStatus == WAIT_TIMEOUT) { if (SecondsToWait) { - if (!TerminateProcess(PI.ProcessHandle, 1)) { + if (!TerminateProcess(PI.Process, 1)) { if (ErrMsg) MakeErrMsg(ErrMsg, "Failed to terminate timed-out program"); // -2 indicates a crash or timeout as opposed to failure to execute. WaitResult.ReturnCode = -2; - CloseHandle(PI.ProcessHandle); + CloseHandle(PI.Process); return WaitResult; } - WaitForSingleObject(PI.ProcessHandle, INFINITE); - CloseHandle(PI.ProcessHandle); + WaitForSingleObject(PI.Process, INFINITE); + CloseHandle(PI.Process); } else { // Non-blocking wait. return ProcessInfo(); @@ -448,10 +405,10 @@ ProcessInfo sys::Wait(const ProcessInfo &PI, unsigned SecondsToWait, // Get its exit status. DWORD status; - BOOL rc = GetExitCodeProcess(PI.ProcessHandle, &status); + BOOL rc = GetExitCodeProcess(PI.Process, &status); DWORD err = GetLastError(); if (err != ERROR_INVALID_HANDLE) - CloseHandle(PI.ProcessHandle); + CloseHandle(PI.Process); if (!rc) { SetLastError(err); @@ -495,7 +452,7 @@ std::error_code llvm::sys::writeFileWithEncoding(StringRef FileName, StringRef Contents, WindowsEncodingMethod Encoding) { std::error_code EC; - llvm::raw_fd_ostream OS(FileName, EC, llvm::sys::fs::OpenFlags::F_Text); + llvm::raw_fd_ostream OS(FileName, EC, llvm::sys::fs::F_Text); if (EC) return EC; @@ -536,19 +493,13 @@ llvm::sys::writeFileWithEncoding(StringRef FileName, StringRef Contents, } bool llvm::sys::commandLineFitsWithinSystemLimits(StringRef Program, - ArrayRef<const char *> Args) { + ArrayRef<StringRef> Args) { // The documented max length of the command line passed to CreateProcess. static const size_t MaxCommandStringLength = 32768; - // 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; + SmallVector<StringRef, 8> FullArgs; + FullArgs.push_back(Program); + FullArgs.append(Args.begin(), Args.end()); + std::string Result = flattenWindowsCommandLine(FullArgs); + return (Result.size() + 1) <= MaxCommandStringLength; } } |