summaryrefslogtreecommitdiff
path: root/lib/System/Win32/Program.inc
blob: a83f0e84f8e8eb99a68a5765ca5c926453b98c43 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
//===- Win32/Program.cpp - Win32 Program Implementation ------- -*- C++ -*-===//
// 
//                     The LLVM Compiler Infrastructure
//
// This file was developed by Jeff Cohen and 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 the generic Unix implementation
#include "Win32.h"
#include "llvm/System/Program.h"
#include <malloc.h>

//===----------------------------------------------------------------------===//
//=== WARNING: Implementation here must contain only Win32 specific code 
//===          and must not be UNIX code
//===----------------------------------------------------------------------===//

namespace llvm {
using namespace sys;

// This function just uses the PATH environment variable to find the program.
Path
Program::FindProgramByName(const std::string& progName) {

  // Check some degenerate cases
  if (progName.length() == 0) // no program
    return Path();
  Path temp;
  if (!temp.set_file(progName)) // invalid name
    return Path();
  if (temp.executable()) // already executable as is
    return temp;

  // At this point, the file name is valid and its not executable.
  // Let Windows search for it.
  char buffer[MAX_PATH];
  char *dummy = NULL;
  DWORD len = SearchPath(NULL, progName.c_str(), ".exe", MAX_PATH,
                         buffer, &dummy);

  // See if it wasn't found.
  if (len == 0)
    return Path();

  // See if we got the entire path.
  if (len < MAX_PATH)
    return Path(buffer);

  // Buffer was too small; grow and retry.
  while (true) {
    char *b = reinterpret_cast<char *>(_alloca(len+1));
    DWORD len2 = SearchPath(NULL, progName.c_str(), ".exe", len+1, b, &dummy);

    // It is unlikely the search failed, but it's always possible some file
    // was added or removed since the last search, so be paranoid...
    if (len2 == 0)
      return Path();
    else if (len2 <= len)
      return Path(b);

    len = len2;
  }
}

//
int 
Program::ExecuteAndWait(const Path& path, 
                        const std::vector<std::string>& args) {
  if (!path.executable())
    throw path.get() + " is not executable"; 

  // 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.

  // First, determine the length of the command line.
  std::string progname(path.getLast());
  unsigned len = progname.length() + 1;
  if (progname.find(' ') != std::string::npos)
    len += 2;

  for (unsigned i = 0; i < args.size(); i++) {
    len += args[i].length() + 1;
    if (args[i].find(' ') != std::string::npos)
      len += 2;
  }

  // Now build the command line.
  char *command = reinterpret_cast<char *>(_alloca(len));
  char *p = command;

  bool needsQuoting = progname.find(' ') != std::string::npos;
  if (needsQuoting)
    *p++ = '"';
  memcpy(p, progname.c_str(), progname.length());
  p += progname.length();
  if (needsQuoting)
    *p++ = '"';
  *p++ = ' ';

  for (unsigned i = 0; i < args.size(); i++) {
    const std::string& arg = args[i];
    needsQuoting = arg.find(' ') != std::string::npos;
    if (needsQuoting)
      *p++ = '"';
    memcpy(p, arg.c_str(), arg.length());
    p += arg.length();
    if (needsQuoting)
      *p++ = '"';
    *p++ = ' ';
  }

  *p = 0;

  // Create a child process.
  STARTUPINFO si;
  memset(&si, 0, sizeof(si));
  si.cb = sizeof(si);

  PROCESS_INFORMATION pi;
  memset(&pi, 0, sizeof(pi));

  if (!CreateProcess(path.get().c_str(), command, NULL, NULL, FALSE, 0,
                     NULL, NULL, &si, &pi))
  {
    ThrowError(std::string("Couldn't execute program '") + path.get() + "'");
  }

  // Wait for it to terminate.
  WaitForSingleObject(pi.hProcess, INFINITE);
  
  // Get its exit status.
  DWORD status;
  BOOL rc = GetExitCodeProcess(pi.hProcess, &status);

  // Done with the handles; go close them.
  CloseHandle(pi.hProcess);
  CloseHandle(pi.hThread);

  if (!rc)
    ThrowError(std::string("Failed getting status for program '") + path.get() + "'");

  return status;
}

}
// vim: sw=2 smartindent smarttab tw=80 autoindent expandtab