Eliminate tabs
[oota-llvm.git] / lib / System / Win32 / Program.inc
1 //===- Win32/Program.cpp - Win32 Program Implementation ------- -*- C++ -*-===//
2 // 
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file was developed by Jeff Cohen and is distributed under the 
6 // University of Illinois Open Source License. See LICENSE.TXT for details.
7 // 
8 //===----------------------------------------------------------------------===//
9 //
10 // This file provides the Win32 specific implementation of the Program class.
11 //
12 //===----------------------------------------------------------------------===//
13
14 #include "Win32.h"
15 #include <malloc.h>
16 #include <io.h>
17
18 //===----------------------------------------------------------------------===//
19 //=== WARNING: Implementation here must contain only Win32 specific code 
20 //===          and must not be UNIX code
21 //===----------------------------------------------------------------------===//
22
23 namespace llvm {
24 using namespace sys;
25
26 // This function just uses the PATH environment variable to find the program.
27 Path
28 Program::FindProgramByName(const std::string& progName) {
29
30   // Check some degenerate cases
31   if (progName.length() == 0) // no program
32     return Path();
33   Path temp;
34   if (!temp.setFile(progName)) // invalid name
35     return Path();
36   if (temp.executable()) // already executable as is
37     return temp;
38
39   // At this point, the file name is valid and its not executable.
40   // Let Windows search for it.
41   char buffer[MAX_PATH];
42   char *dummy = NULL;
43   DWORD len = SearchPath(NULL, progName.c_str(), ".exe", MAX_PATH,
44                          buffer, &dummy);
45
46   // See if it wasn't found.
47   if (len == 0)
48     return Path();
49
50   // See if we got the entire path.
51   if (len < MAX_PATH)
52     return Path(buffer);
53
54   // Buffer was too small; grow and retry.
55   while (true) {
56     char *b = reinterpret_cast<char *>(_alloca(len+1));
57     DWORD len2 = SearchPath(NULL, progName.c_str(), ".exe", len+1, b, &dummy);
58
59     // It is unlikely the search failed, but it's always possible some file
60     // was added or removed since the last search, so be paranoid...
61     if (len2 == 0)
62       return Path();
63     else if (len2 <= len)
64       return Path(b);
65
66     len = len2;
67   }
68 }
69
70 static HANDLE RedirectIO(const Path *path, int fd) {
71   HANDLE h;
72   if (path == 0) {
73     DuplicateHandle(GetCurrentProcess(), (HANDLE)_get_osfhandle(fd),
74                     GetCurrentProcess(), &h,
75                     0, TRUE, DUPLICATE_SAME_ACCESS);
76     return h;
77   }
78
79   const char *fname = path->toString().c_str();
80   if (*fname == 0)
81     fname = "NUL";
82
83   SECURITY_ATTRIBUTES sa;
84   sa.nLength = sizeof(sa);
85   sa.lpSecurityDescriptor = 0;
86   sa.bInheritHandle = TRUE;
87
88   h = CreateFile(fname, fd ? GENERIC_WRITE : GENERIC_READ, FILE_SHARE_READ,
89                  &sa, fd == 0 ? OPEN_EXISTING : CREATE_ALWAYS,
90                  FILE_ATTRIBUTE_NORMAL, NULL);
91   if (h == INVALID_HANDLE_VALUE) {
92     ThrowError(std::string(fname) + ": Can't open file for " +
93         (fd ? "input: " : "output: "));
94   }
95   return h;
96 }
97
98 int 
99 Program::ExecuteAndWait(const Path& path, 
100                         const char** args,
101                         const char** envp,
102                         const Path** redirects,
103                         unsigned secondsToWait) {
104   if (!path.executable())
105     throw path.toString() + " is not executable"; 
106
107   // Windows wants a command line, not an array of args, to pass to the new
108   // process.  We have to concatenate them all, while quoting the args that
109   // have embedded spaces.
110
111   // First, determine the length of the command line.
112   unsigned len = 0;
113   for (unsigned i = 0; args[i]; i++) {
114     len += strlen(args[i]) + 1;
115     if (strchr(args[i], ' '))
116       len += 2;
117   }
118
119   // Now build the command line.
120   char *command = reinterpret_cast<char *>(_alloca(len));
121   char *p = command;
122
123   for (unsigned i = 0; args[i]; i++) {
124     const char *arg = args[i];
125     size_t len = strlen(arg);
126     bool needsQuoting = strchr(arg, ' ') != 0;
127     if (needsQuoting)
128       *p++ = '"';
129     memcpy(p, arg, len);
130     p += len;
131     if (needsQuoting)
132       *p++ = '"';
133     *p++ = ' ';
134   }
135
136   *p = 0;
137
138   // Create a child process.
139   STARTUPINFO si;
140   memset(&si, 0, sizeof(si));
141   si.cb = sizeof(si);
142   si.hStdInput = INVALID_HANDLE_VALUE;
143   si.hStdOutput = INVALID_HANDLE_VALUE;
144   si.hStdError = INVALID_HANDLE_VALUE;
145
146   if (redirects) {
147     si.dwFlags = STARTF_USESTDHANDLES;
148     
149     try {
150       si.hStdInput = RedirectIO(redirects[0], 0);
151       si.hStdOutput = RedirectIO(redirects[1], 1);
152       if (redirects[1] && redirects[2] && *(redirects[1]) != *(redirects[2])) {
153         si.hStdError = RedirectIO(redirects[2], 2);
154       } else {
155         DuplicateHandle(GetCurrentProcess(), si.hStdOutput,
156                         GetCurrentProcess(), &si.hStdError,
157                         0, TRUE, DUPLICATE_SAME_ACCESS);
158       }
159     } catch (...) {
160       CloseHandle(si.hStdInput);
161       CloseHandle(si.hStdOutput);
162       CloseHandle(si.hStdError);
163       throw;
164     }
165   }
166
167   PROCESS_INFORMATION pi;
168   memset(&pi, 0, sizeof(pi));
169
170   fflush(stdout);
171   fflush(stderr);
172   BOOL rc = CreateProcess(path.c_str(), command, NULL, NULL, FALSE, 0,
173                           envp, NULL, &si, &pi);
174   DWORD err = GetLastError();
175
176   // Regardless of whether the process got created or not, we are done with
177   // the handles we created for it to inherit.
178   CloseHandle(si.hStdInput);
179   CloseHandle(si.hStdOutput);
180   CloseHandle(si.hStdError);
181
182   // Now throw an error if the process didn't get created.
183   if (!rc)
184   {
185     SetLastError(err);
186     ThrowError(std::string("Couldn't execute program '") + 
187                path.toString() + "'");
188   }
189
190   // Wait for it to terminate.
191   DWORD millisecondsToWait = INFINITE;
192   if (secondsToWait > 0)
193     millisecondsToWait = secondsToWait * 1000;
194
195   if (WaitForSingleObject(pi.hProcess, millisecondsToWait) == WAIT_TIMEOUT) {
196     if (!TerminateProcess(pi.hProcess, 1)) {
197       ThrowError(std::string("Failed to terminate timed-out program '") + 
198                  path.toString() + "'");
199     }
200     WaitForSingleObject(pi.hProcess, INFINITE);
201   }
202   
203   // Get its exit status.
204   DWORD status;
205   rc = GetExitCodeProcess(pi.hProcess, &status);
206   err = GetLastError();
207
208   // Done with the handles; go close them.
209   CloseHandle(pi.hProcess);
210   CloseHandle(pi.hThread);
211
212   if (!rc) {
213     SetLastError(err);
214     ThrowError(std::string("Failed getting status for program '") + 
215                path.toString() + "'");
216   }
217
218   return status;
219 }
220
221 }
222 // vim: sw=2 smartindent smarttab tw=80 autoindent expandtab