LLVM API Documentation

Win32/Program.inc

Go to the documentation of this file.
00001 //===- Win32/Program.cpp - Win32 Program Implementation ------- -*- C++ -*-===//
00002 // 
00003 //                     The LLVM Compiler Infrastructure
00004 //
00005 // This file was developed by Jeff Cohen and is distributed under the 
00006 // University of Illinois Open Source License. See LICENSE.TXT for details.
00007 // 
00008 //===----------------------------------------------------------------------===//
00009 //
00010 // This file provides the Win32 specific implementation of the Program class.
00011 //
00012 //===----------------------------------------------------------------------===//
00013 
00014 #include "Win32.h"
00015 #include <cstdio>
00016 #include <malloc.h>
00017 #include <io.h>
00018 #include <fcntl.h>
00019 
00020 //===----------------------------------------------------------------------===//
00021 //=== WARNING: Implementation here must contain only Win32 specific code 
00022 //===          and must not be UNIX code
00023 //===----------------------------------------------------------------------===//
00024 
00025 namespace llvm {
00026 using namespace sys;
00027 
00028 // This function just uses the PATH environment variable to find the program.
00029 Path
00030 Program::FindProgramByName(const std::string& progName) {
00031 
00032   // Check some degenerate cases
00033   if (progName.length() == 0) // no program
00034     return Path();
00035   Path temp;
00036   if (!temp.set(progName)) // invalid name
00037     return Path();
00038   if (temp.canExecute()) // already executable as is
00039     return temp;
00040 
00041   // At this point, the file name is valid and its not executable.
00042   // Let Windows search for it.
00043   char buffer[MAX_PATH];
00044   char *dummy = NULL;
00045   DWORD len = SearchPath(NULL, progName.c_str(), ".exe", MAX_PATH,
00046                          buffer, &dummy);
00047 
00048   // See if it wasn't found.
00049   if (len == 0)
00050     return Path();
00051 
00052   // See if we got the entire path.
00053   if (len < MAX_PATH)
00054     return Path(buffer);
00055 
00056   // Buffer was too small; grow and retry.
00057   while (true) {
00058     char *b = reinterpret_cast<char *>(_alloca(len+1));
00059     DWORD len2 = SearchPath(NULL, progName.c_str(), ".exe", len+1, b, &dummy);
00060 
00061     // It is unlikely the search failed, but it's always possible some file
00062     // was added or removed since the last search, so be paranoid...
00063     if (len2 == 0)
00064       return Path();
00065     else if (len2 <= len)
00066       return Path(b);
00067 
00068     len = len2;
00069   }
00070 }
00071 
00072 static HANDLE RedirectIO(const Path *path, int fd) {
00073   HANDLE h;
00074   if (path == 0) {
00075     DuplicateHandle(GetCurrentProcess(), (HANDLE)_get_osfhandle(fd),
00076                     GetCurrentProcess(), &h,
00077                     0, TRUE, DUPLICATE_SAME_ACCESS);
00078     return h;
00079   }
00080 
00081   const char *fname = path->toString().c_str();
00082   if (*fname == 0)
00083     fname = "NUL";
00084 
00085   SECURITY_ATTRIBUTES sa;
00086   sa.nLength = sizeof(sa);
00087   sa.lpSecurityDescriptor = 0;
00088   sa.bInheritHandle = TRUE;
00089 
00090   h = CreateFile(fname, fd ? GENERIC_WRITE : GENERIC_READ, FILE_SHARE_READ,
00091                  &sa, fd == 0 ? OPEN_EXISTING : CREATE_ALWAYS,
00092                  FILE_ATTRIBUTE_NORMAL, NULL);
00093   if (h == INVALID_HANDLE_VALUE) {
00094     ThrowError(std::string(fname) + ": Can't open file for " +
00095         (fd ? "input: " : "output: "));
00096   }
00097   return h;
00098 }
00099 
00100 int 
00101 Program::ExecuteAndWait(const Path& path, 
00102                         const char** args,
00103                         const char** envp,
00104                         const Path** redirects,
00105                         unsigned secondsToWait) {
00106   if (!path.canExecute())
00107     throw path.toString() + " is not executable"; 
00108 
00109   // Windows wants a command line, not an array of args, to pass to the new
00110   // process.  We have to concatenate them all, while quoting the args that
00111   // have embedded spaces.
00112 
00113   // First, determine the length of the command line.
00114   unsigned len = 0;
00115   for (unsigned i = 0; args[i]; i++) {
00116     len += strlen(args[i]) + 1;
00117     if (strchr(args[i], ' '))
00118       len += 2;
00119   }
00120 
00121   // Now build the command line.
00122   char *command = reinterpret_cast<char *>(_alloca(len));
00123   char *p = command;
00124 
00125   for (unsigned i = 0; args[i]; i++) {
00126     const char *arg = args[i];
00127     size_t len = strlen(arg);
00128     bool needsQuoting = strchr(arg, ' ') != 0;
00129     if (needsQuoting)
00130       *p++ = '"';
00131     memcpy(p, arg, len);
00132     p += len;
00133     if (needsQuoting)
00134       *p++ = '"';
00135     *p++ = ' ';
00136   }
00137 
00138   *p = 0;
00139 
00140   // Create a child process.
00141   STARTUPINFO si;
00142   memset(&si, 0, sizeof(si));
00143   si.cb = sizeof(si);
00144   si.hStdInput = INVALID_HANDLE_VALUE;
00145   si.hStdOutput = INVALID_HANDLE_VALUE;
00146   si.hStdError = INVALID_HANDLE_VALUE;
00147 
00148   if (redirects) {
00149     si.dwFlags = STARTF_USESTDHANDLES;
00150     
00151     try {
00152       si.hStdInput = RedirectIO(redirects[0], 0);
00153       si.hStdOutput = RedirectIO(redirects[1], 1);
00154       if (redirects[1] && redirects[2] && *(redirects[1]) != *(redirects[2])) {
00155         si.hStdError = RedirectIO(redirects[2], 2);
00156       } else {
00157         DuplicateHandle(GetCurrentProcess(), si.hStdOutput,
00158                         GetCurrentProcess(), &si.hStdError,
00159                         0, TRUE, DUPLICATE_SAME_ACCESS);
00160       }
00161     } catch (...) {
00162       CloseHandle(si.hStdInput);
00163       CloseHandle(si.hStdOutput);
00164       CloseHandle(si.hStdError);
00165       throw;
00166     }
00167   }
00168 
00169   PROCESS_INFORMATION pi;
00170   memset(&pi, 0, sizeof(pi));
00171 
00172   fflush(stdout);
00173   fflush(stderr);
00174   BOOL rc = CreateProcess(path.c_str(), command, NULL, NULL, FALSE, 0,
00175                           envp, NULL, &si, &pi);
00176   DWORD err = GetLastError();
00177 
00178   // Regardless of whether the process got created or not, we are done with
00179   // the handles we created for it to inherit.
00180   CloseHandle(si.hStdInput);
00181   CloseHandle(si.hStdOutput);
00182   CloseHandle(si.hStdError);
00183 
00184   // Now throw an error if the process didn't get created.
00185   if (!rc)
00186   {
00187     SetLastError(err);
00188     ThrowError(std::string("Couldn't execute program '") + 
00189                path.toString() + "'");
00190   }
00191 
00192   // Wait for it to terminate.
00193   DWORD millisecondsToWait = INFINITE;
00194   if (secondsToWait > 0)
00195     millisecondsToWait = secondsToWait * 1000;
00196 
00197   if (WaitForSingleObject(pi.hProcess, millisecondsToWait) == WAIT_TIMEOUT) {
00198     if (!TerminateProcess(pi.hProcess, 1)) {
00199       ThrowError(std::string("Failed to terminate timed-out program '") + 
00200                  path.toString() + "'");
00201     }
00202     WaitForSingleObject(pi.hProcess, INFINITE);
00203   }
00204   
00205   // Get its exit status.
00206   DWORD status;
00207   rc = GetExitCodeProcess(pi.hProcess, &status);
00208   err = GetLastError();
00209 
00210   // Done with the handles; go close them.
00211   CloseHandle(pi.hProcess);
00212   CloseHandle(pi.hThread);
00213 
00214   if (!rc) {
00215     SetLastError(err);
00216     ThrowError(std::string("Failed getting status for program '") + 
00217                path.toString() + "'");
00218   }
00219 
00220   return status;
00221 }
00222 
00223 void Program::ChangeStdinToBinary(){
00224   int result = _setmode( _fileno(stdin), _O_BINARY );
00225   if( result == -1 )
00226     throw std::string("Cannot set input mode on stdin to binary.");
00227 }
00228 
00229 void Program::ChangeStdoutToBinary(){
00230   int result = _setmode( _fileno(stdout), _O_BINARY );
00231   if( result == -1 )
00232     throw std::string("Cannot set output mode on stdout to binary.");
00233 }
00234 
00235 }