summaryrefslogtreecommitdiff
path: root/lib/System/Unix/Program.inc
blob: b8ef55925d63b5d6d1ba65b442086e9e83973558 (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
//===- llvm/System/Unix/Program.cpp -----------------------------*- C++ -*-===//
// 
//                     The LLVM Compiler Infrastructure
//
// This file was developed by Reid Spencer and is distributed under the 
// University of Illinois Open Source License. See LICENSE.TXT for details.
// 
//===----------------------------------------------------------------------===//
//
// This file implements the Unix specific portion of the Program class.
//
//===----------------------------------------------------------------------===//

//===----------------------------------------------------------------------===//
//=== WARNING: Implementation here must contain only generic UNIX code that
//===          is guaranteed to work on *all* UNIX variants.
//===----------------------------------------------------------------------===//

#include <llvm/Config/config.h>
#include "Unix.h"
#include <sys/stat.h>
#include <fcntl.h>
#ifdef HAVE_SYS_WAIT_H
#include <sys/wait.h>
#endif

extern char** environ;

namespace llvm {
using namespace sys;

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

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

  // At this point, the file name is valid and its not executable
 
  // Get the path. If its empty, we can't do anything to find it.
  const char *PathStr = getenv("PATH");
  if (PathStr == 0) 
    return Program();

  // Now we have a colon separated list of directories to search; try them.
  unsigned PathLen = strlen(PathStr);
  while (PathLen) {
    // Find the first colon...
    const char *Colon = std::find(PathStr, PathStr+PathLen, ':');

    // Check to see if this first directory contains the executable...
    Program FilePath;
    if (FilePath.set_directory(std::string(PathStr,Colon))) {
      FilePath.append_file(progName);
      if (FilePath.executable())
        return FilePath;                    // Found the executable!
    }

    // Nope it wasn't in this directory, check the next path in the list!
    PathLen -= Colon-PathStr;
    PathStr = Colon;

    // Advance past duplicate colons
    while (*PathStr == ':') {
      PathStr++;
      PathLen--;
    }
  }
  return Program();
}

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

#ifdef HAVE_SYS_WAIT_H
  // Create local versions of the parameters that can be passed into execve()
  // without creating const problems.
  const char* argv[ args.size() + 2 ];
  unsigned index = 0;
  std::string progname(this->getLast());
  argv[index++] = progname.c_str();
  for (unsigned i = 0; i < args.size(); i++)
    argv[index++] = args[i].c_str();
  argv[index] = 0;

  // Create a child process.
  switch (fork()) {
    // An error occured:  Return to the caller.
    case -1:
      ThrowErrno(std::string("Couldn't execute program '") + get() + "'");
      break;

    // Child process: Execute the program.
    case 0:
      execve (get().c_str(), (char** const)argv, environ);
      // If the execve() failed, we should exit and let the parent pick up
      // our non-zero exit status.
      exit (errno);

    // Parent process: Break out of the switch to do our processing.
    default:
      break;
  }

  // Parent process: Wait for the child process to terminate.
  int status;
  if ((::wait (&status)) == -1)
    ThrowErrno(std::string("Failed waiting for program '") + get() + "'");

  // If the program exited normally with a zero exit status, return success!
  if (WIFEXITED (status))
    return WEXITSTATUS(status);
  else if (WIFSIGNALED(status))
    throw std::string("Program '") + get() + "' received terminating signal.";
  else
    return 0;
    
#else
  throw std::string("Program::ExecuteAndWait not implemented on this platform!\n");
#endif

}

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