Initial commit of an platform-indepdendent implementation for the
"Program" operating system concept (find and execute programs).


git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@16084 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/System/Unix/Program.cpp b/lib/System/Unix/Program.cpp
new file mode 100644
index 0000000..6943d52
--- /dev/null
+++ b/lib/System/Unix/Program.cpp
@@ -0,0 +1,133 @@
+//===- 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 "Unix.h"
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <Config/config.h>
+#ifdef HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+
+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