| //===- Win32/Program.cpp - Win32 Program Implementation ------- -*- C++ -*-===// | 
 | // | 
 | //                     The LLVM Compiler Infrastructure | 
 | // | 
 | // This file is distributed under the University of Illinois Open Source | 
 | // License. See LICENSE.TXT for details. | 
 | // | 
 | //===----------------------------------------------------------------------===// | 
 | // | 
 | // This file provides the Win32 specific implementation of the Program class. | 
 | // | 
 | //===----------------------------------------------------------------------===// | 
 |  | 
 | #include "WindowsSupport.h" | 
 | #include "llvm/ADT/StringExtras.h" | 
 | #include "llvm/Support/ConvertUTF.h" | 
 | #include "llvm/Support/Errc.h" | 
 | #include "llvm/Support/FileSystem.h" | 
 | #include "llvm/Support/WindowsError.h" | 
 | #include "llvm/Support/raw_ostream.h" | 
 | #include <cstdio> | 
 | #include <fcntl.h> | 
 | #include <io.h> | 
 | #include <malloc.h> | 
 |  | 
 | //===----------------------------------------------------------------------===// | 
 | //=== WARNING: Implementation here must contain only Win32 specific code | 
 | //===          and must not be UNIX code | 
 | //===----------------------------------------------------------------------===// | 
 |  | 
 | namespace llvm { | 
 | using namespace sys; | 
 |  | 
 | ProcessInfo::ProcessInfo() : ProcessHandle(0), Pid(0), ReturnCode(0) {} | 
 |  | 
 | ErrorOr<std::string> sys::findProgramByName(StringRef Name, | 
 |                                             ArrayRef<StringRef> Paths) { | 
 |   assert(!Name.empty() && "Must have a name!"); | 
 |  | 
 |   if (Name.find_first_of("/\\") != StringRef::npos) | 
 |     return std::string(Name); | 
 |  | 
 |   const wchar_t *Path = nullptr; | 
 |   std::wstring PathStorage; | 
 |   if (!Paths.empty()) { | 
 |     PathStorage.reserve(Paths.size() * MAX_PATH); | 
 |     for (unsigned i = 0; i < Paths.size(); ++i) { | 
 |       if (i) | 
 |         PathStorage.push_back(L';'); | 
 |       StringRef P = Paths[i]; | 
 |       SmallVector<wchar_t, MAX_PATH> TmpPath; | 
 |       if (std::error_code EC = windows::UTF8ToUTF16(P, TmpPath)) | 
 |         return EC; | 
 |       PathStorage.append(TmpPath.begin(), TmpPath.end()); | 
 |     } | 
 |     Path = PathStorage.c_str(); | 
 |   } | 
 |  | 
 |   SmallVector<wchar_t, MAX_PATH> U16Name; | 
 |   if (std::error_code EC = windows::UTF8ToUTF16(Name, U16Name)) | 
 |     return EC; | 
 |  | 
 |   SmallVector<StringRef, 12> PathExts; | 
 |   PathExts.push_back(""); | 
 |   PathExts.push_back(".exe"); // FIXME: This must be in %PATHEXT%. | 
 |   if (const char *PathExtEnv = std::getenv("PATHEXT")) | 
 |     SplitString(PathExtEnv, PathExts, ";"); | 
 |  | 
 |   SmallVector<wchar_t, MAX_PATH> U16Result; | 
 |   DWORD Len = MAX_PATH; | 
 |   for (StringRef Ext : PathExts) { | 
 |     SmallVector<wchar_t, MAX_PATH> U16Ext; | 
 |     if (std::error_code EC = windows::UTF8ToUTF16(Ext, U16Ext)) | 
 |       return EC; | 
 |  | 
 |     do { | 
 |       U16Result.reserve(Len); | 
 |       // Lets attach the extension manually. That is needed for files | 
 |       // with a point in name like aaa.bbb. SearchPathW will not add extension | 
 |       // from its argument to such files because it thinks they already had one. | 
 |       SmallVector<wchar_t, MAX_PATH> U16NameExt; | 
 |       if (std::error_code EC = | 
 |               windows::UTF8ToUTF16(Twine(Name + Ext).str(), U16NameExt)) | 
 |         return EC; | 
 |  | 
 |       Len = ::SearchPathW(Path, c_str(U16NameExt), nullptr, | 
 |                           U16Result.capacity(), U16Result.data(), nullptr); | 
 |     } while (Len > U16Result.capacity()); | 
 |  | 
 |     if (Len != 0) | 
 |       break; // Found it. | 
 |   } | 
 |  | 
 |   if (Len == 0) | 
 |     return mapWindowsError(::GetLastError()); | 
 |  | 
 |   U16Result.set_size(Len); | 
 |  | 
 |   SmallVector<char, MAX_PATH> U8Result; | 
 |   if (std::error_code EC = | 
 |           windows::UTF16ToUTF8(U16Result.data(), U16Result.size(), U8Result)) | 
 |     return EC; | 
 |  | 
 |   return std::string(U8Result.begin(), U8Result.end()); | 
 | } | 
 |  | 
 | static HANDLE RedirectIO(const StringRef *path, int fd, std::string* ErrMsg) { | 
 |   HANDLE h; | 
 |   if (path == 0) { | 
 |     if (!DuplicateHandle(GetCurrentProcess(), (HANDLE)_get_osfhandle(fd), | 
 |                          GetCurrentProcess(), &h, | 
 |                          0, TRUE, DUPLICATE_SAME_ACCESS)) | 
 |       return INVALID_HANDLE_VALUE; | 
 |     return h; | 
 |   } | 
 |  | 
 |   std::string fname; | 
 |   if (path->empty()) | 
 |     fname = "NUL"; | 
 |   else | 
 |     fname = *path; | 
 |  | 
 |   SECURITY_ATTRIBUTES sa; | 
 |   sa.nLength = sizeof(sa); | 
 |   sa.lpSecurityDescriptor = 0; | 
 |   sa.bInheritHandle = TRUE; | 
 |  | 
 |   SmallVector<wchar_t, 128> fnameUnicode; | 
 |   if (path->empty()) { | 
 |     // Don't play long-path tricks on "NUL". | 
 |     if (windows::UTF8ToUTF16(fname, fnameUnicode)) | 
 |       return INVALID_HANDLE_VALUE; | 
 |   } else { | 
 |     if (path::widenPath(fname, fnameUnicode)) | 
 |       return INVALID_HANDLE_VALUE; | 
 |   } | 
 |   h = CreateFileW(fnameUnicode.data(), fd ? GENERIC_WRITE : GENERIC_READ, | 
 |                   FILE_SHARE_READ, &sa, fd == 0 ? OPEN_EXISTING : CREATE_ALWAYS, | 
 |                   FILE_ATTRIBUTE_NORMAL, NULL); | 
 |   if (h == INVALID_HANDLE_VALUE) { | 
 |     MakeErrMsg(ErrMsg, fname + ": Can't open file for " + | 
 |         (fd ? "input" : "output")); | 
 |   } | 
 |  | 
 |   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, const StringRef **redirects, | 
 |                     unsigned memoryLimit, std::string *ErrMsg) { | 
 |   if (!sys::fs::can_execute(Program)) { | 
 |     if (ErrMsg) | 
 |       *ErrMsg = "program not executable"; | 
 |     return false; | 
 |   } | 
 |  | 
 |   // can_execute may succeed by looking at Program + ".exe". CreateProcessW | 
 |   // will implicitly add the .exe if we provide a command line without an | 
 |   // executable path, but since we use an explicit executable, we have to add | 
 |   // ".exe" ourselves. | 
 |   SmallString<64> ProgramStorage; | 
 |   if (!sys::fs::exists(Program)) | 
 |     Program = Twine(Program + ".exe").toStringRef(ProgramStorage); | 
 |  | 
 |   // 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); | 
 |  | 
 |   // The pointer to the environment block for the new process. | 
 |   std::vector<wchar_t> EnvBlock; | 
 |  | 
 |   if (envp) { | 
 |     // 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) { | 
 |       SmallVector<wchar_t, MAX_PATH> EnvString; | 
 |       if (std::error_code ec = windows::UTF8ToUTF16(envp[i], EnvString)) { | 
 |         SetLastError(ec.value()); | 
 |         MakeErrMsg(ErrMsg, "Unable to convert environment variable to UTF-16"); | 
 |         return false; | 
 |       } | 
 |  | 
 |       EnvBlock.insert(EnvBlock.end(), EnvString.begin(), EnvString.end()); | 
 |       EnvBlock.push_back(0); | 
 |     } | 
 |     EnvBlock.push_back(0); | 
 |   } | 
 |  | 
 |   // Create a child process. | 
 |   STARTUPINFOW si; | 
 |   memset(&si, 0, sizeof(si)); | 
 |   si.cb = sizeof(si); | 
 |   si.hStdInput = INVALID_HANDLE_VALUE; | 
 |   si.hStdOutput = INVALID_HANDLE_VALUE; | 
 |   si.hStdError = INVALID_HANDLE_VALUE; | 
 |  | 
 |   if (redirects) { | 
 |     si.dwFlags = STARTF_USESTDHANDLES; | 
 |  | 
 |     si.hStdInput = RedirectIO(redirects[0], 0, ErrMsg); | 
 |     if (si.hStdInput == INVALID_HANDLE_VALUE) { | 
 |       MakeErrMsg(ErrMsg, "can't redirect stdin"); | 
 |       return false; | 
 |     } | 
 |     si.hStdOutput = RedirectIO(redirects[1], 1, ErrMsg); | 
 |     if (si.hStdOutput == INVALID_HANDLE_VALUE) { | 
 |       CloseHandle(si.hStdInput); | 
 |       MakeErrMsg(ErrMsg, "can't redirect stdout"); | 
 |       return false; | 
 |     } | 
 |     if (redirects[1] && redirects[2] && *(redirects[1]) == *(redirects[2])) { | 
 |       // If stdout and stderr should go to the same place, redirect stderr | 
 |       // to the handle already open for stdout. | 
 |       if (!DuplicateHandle(GetCurrentProcess(), si.hStdOutput, | 
 |                            GetCurrentProcess(), &si.hStdError, | 
 |                            0, TRUE, DUPLICATE_SAME_ACCESS)) { | 
 |         CloseHandle(si.hStdInput); | 
 |         CloseHandle(si.hStdOutput); | 
 |         MakeErrMsg(ErrMsg, "can't dup stderr to stdout"); | 
 |         return false; | 
 |       } | 
 |     } else { | 
 |       // Just redirect stderr | 
 |       si.hStdError = RedirectIO(redirects[2], 2, ErrMsg); | 
 |       if (si.hStdError == INVALID_HANDLE_VALUE) { | 
 |         CloseHandle(si.hStdInput); | 
 |         CloseHandle(si.hStdOutput); | 
 |         MakeErrMsg(ErrMsg, "can't redirect stderr"); | 
 |         return false; | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   PROCESS_INFORMATION pi; | 
 |   memset(&pi, 0, sizeof(pi)); | 
 |  | 
 |   fflush(stdout); | 
 |   fflush(stderr); | 
 |  | 
 |   SmallVector<wchar_t, MAX_PATH> ProgramUtf16; | 
 |   if (std::error_code ec = path::widenPath(Program, ProgramUtf16)) { | 
 |     SetLastError(ec.value()); | 
 |     MakeErrMsg(ErrMsg, | 
 |                std::string("Unable to convert application name to UTF-16")); | 
 |     return false; | 
 |   } | 
 |  | 
 |   SmallVector<wchar_t, MAX_PATH> 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")); | 
 |     return false; | 
 |   } | 
 |  | 
 |   BOOL rc = CreateProcessW(ProgramUtf16.data(), CommandUtf16.data(), 0, 0, | 
 |                            TRUE, CREATE_UNICODE_ENVIRONMENT, | 
 |                            EnvBlock.empty() ? 0 : EnvBlock.data(), 0, &si, | 
 |                            &pi); | 
 |   DWORD err = GetLastError(); | 
 |  | 
 |   // Regardless of whether the process got created or not, we are done with | 
 |   // the handles we created for it to inherit. | 
 |   CloseHandle(si.hStdInput); | 
 |   CloseHandle(si.hStdOutput); | 
 |   CloseHandle(si.hStdError); | 
 |  | 
 |   // Now return an error if the process didn't get created. | 
 |   if (!rc) { | 
 |     SetLastError(err); | 
 |     MakeErrMsg(ErrMsg, std::string("Couldn't execute program '") + | 
 |                Program.str() + "'"); | 
 |     return false; | 
 |   } | 
 |  | 
 |   PI.Pid = pi.dwProcessId; | 
 |   PI.ProcessHandle = pi.hProcess; | 
 |  | 
 |   // Make sure these get closed no matter what. | 
 |   ScopedCommonHandle hThread(pi.hThread); | 
 |  | 
 |   // Assign the process to a job if a memory limit is defined. | 
 |   ScopedJobHandle hJob; | 
 |   if (memoryLimit != 0) { | 
 |     hJob = CreateJobObjectW(0, 0); | 
 |     bool success = false; | 
 |     if (hJob) { | 
 |       JOBOBJECT_EXTENDED_LIMIT_INFORMATION jeli; | 
 |       memset(&jeli, 0, sizeof(jeli)); | 
 |       jeli.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_PROCESS_MEMORY; | 
 |       jeli.ProcessMemoryLimit = uintptr_t(memoryLimit) * 1048576; | 
 |       if (SetInformationJobObject(hJob, JobObjectExtendedLimitInformation, | 
 |                                   &jeli, sizeof(jeli))) { | 
 |         if (AssignProcessToJobObject(hJob, pi.hProcess)) | 
 |           success = true; | 
 |       } | 
 |     } | 
 |     if (!success) { | 
 |       SetLastError(GetLastError()); | 
 |       MakeErrMsg(ErrMsg, std::string("Unable to set memory limit")); | 
 |       TerminateProcess(pi.hProcess, 1); | 
 |       WaitForSingleObject(pi.hProcess, INFINITE); | 
 |       return false; | 
 |     } | 
 |   } | 
 |  | 
 |   return true; | 
 | } | 
 |  | 
 | namespace llvm { | 
 | 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 && | 
 |          "invalid process handle to wait on, process not started?"); | 
 |   DWORD milliSecondsToWait = 0; | 
 |   if (WaitUntilChildTerminates) | 
 |     milliSecondsToWait = INFINITE; | 
 |   else if (SecondsToWait > 0) | 
 |     milliSecondsToWait = SecondsToWait * 1000; | 
 |  | 
 |   ProcessInfo WaitResult = PI; | 
 |   DWORD WaitStatus = WaitForSingleObject(PI.ProcessHandle, milliSecondsToWait); | 
 |   if (WaitStatus == WAIT_TIMEOUT) { | 
 |     if (SecondsToWait) { | 
 |       if (!TerminateProcess(PI.ProcessHandle, 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); | 
 |         return WaitResult; | 
 |       } | 
 |       WaitForSingleObject(PI.ProcessHandle, INFINITE); | 
 |       CloseHandle(PI.ProcessHandle); | 
 |     } else { | 
 |       // Non-blocking wait. | 
 |       return ProcessInfo(); | 
 |     } | 
 |   } | 
 |  | 
 |   // Get its exit status. | 
 |   DWORD status; | 
 |   BOOL rc = GetExitCodeProcess(PI.ProcessHandle, &status); | 
 |   DWORD err = GetLastError(); | 
 |   if (err != ERROR_INVALID_HANDLE) | 
 |     CloseHandle(PI.ProcessHandle); | 
 |  | 
 |   if (!rc) { | 
 |     SetLastError(err); | 
 |     if (ErrMsg) | 
 |       MakeErrMsg(ErrMsg, "Failed getting status for program"); | 
 |  | 
 |     // -2 indicates a crash or timeout as opposed to failure to execute. | 
 |     WaitResult.ReturnCode = -2; | 
 |     return WaitResult; | 
 |   } | 
 |  | 
 |   if (!status) | 
 |     return WaitResult; | 
 |  | 
 |   // Pass 10(Warning) and 11(Error) to the callee as negative value. | 
 |   if ((status & 0xBFFF0000U) == 0x80000000U) | 
 |     WaitResult.ReturnCode = static_cast<int>(status); | 
 |   else if (status & 0xFF) | 
 |     WaitResult.ReturnCode = status & 0x7FFFFFFF; | 
 |   else | 
 |     WaitResult.ReturnCode = 1; | 
 |  | 
 |   return WaitResult; | 
 | } | 
 |  | 
 | std::error_code sys::ChangeStdinToBinary() { | 
 |   int result = _setmode(_fileno(stdin), _O_BINARY); | 
 |   if (result == -1) | 
 |     return std::error_code(errno, std::generic_category()); | 
 |   return std::error_code(); | 
 | } | 
 |  | 
 | std::error_code sys::ChangeStdoutToBinary() { | 
 |   int result = _setmode(_fileno(stdout), _O_BINARY); | 
 |   if (result == -1) | 
 |     return std::error_code(errno, std::generic_category()); | 
 |   return std::error_code(); | 
 | } | 
 |  | 
 | 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); | 
 |   if (EC) | 
 |     return EC; | 
 |  | 
 |   if (Encoding == WEM_UTF8) { | 
 |     OS << Contents; | 
 |   } else if (Encoding == WEM_CurrentCodePage) { | 
 |     SmallVector<wchar_t, 1> ArgsUTF16; | 
 |     SmallVector<char, 1> ArgsCurCP; | 
 |  | 
 |     if ((EC = windows::UTF8ToUTF16(Contents, ArgsUTF16))) | 
 |       return EC; | 
 |  | 
 |     if ((EC = windows::UTF16ToCurCP( | 
 |              ArgsUTF16.data(), ArgsUTF16.size(), ArgsCurCP))) | 
 |       return EC; | 
 |  | 
 |     OS.write(ArgsCurCP.data(), ArgsCurCP.size()); | 
 |   } else if (Encoding == WEM_UTF16) { | 
 |     SmallVector<wchar_t, 1> ArgsUTF16; | 
 |  | 
 |     if ((EC = windows::UTF8ToUTF16(Contents, ArgsUTF16))) | 
 |       return EC; | 
 |  | 
 |     // Endianness guessing | 
 |     char BOM[2]; | 
 |     uint16_t src = UNI_UTF16_BYTE_ORDER_MARK_NATIVE; | 
 |     memcpy(BOM, &src, 2); | 
 |     OS.write(BOM, 2); | 
 |     OS.write((char *)ArgsUTF16.data(), ArgsUTF16.size() << 1); | 
 |   } else { | 
 |     llvm_unreachable("Unknown encoding"); | 
 |   } | 
 |  | 
 |   if (OS.has_error()) | 
 |     return make_error_code(errc::io_error); | 
 |  | 
 |   return EC; | 
 | } | 
 |  | 
 | bool llvm::sys::commandLineFitsWithinSystemLimits(StringRef Program, ArrayRef<const char*> 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 (ArrayRef<const char*>::iterator I = Args.begin(), E = Args.end(); | 
 |        I != E; ++I) { | 
 |     // Account for the trailing space for every arg | 
 |     ArgLength += ArgLenWithQuotes(*I) + 1; | 
 |     if (ArgLength > MaxCommandStringLength) { | 
 |       return false; | 
 |     } | 
 |   } | 
 |   return true; | 
 | } | 
 | } |