diff third_party/boost/process/operations.hpp @ 0:add35537fdbb tip

Initial import
author irh <ian.r.hobson@gmail.com>
date Thu, 25 Aug 2011 11:05:55 +0100
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/third_party/boost/process/operations.hpp	Thu Aug 25 11:05:55 2011 +0100
@@ -0,0 +1,432 @@
+//
+// Boost.Process
+// ~~~~~~~~~~~~~
+//
+// Copyright (c) 2006, 2007 Julio M. Merino Vidal
+// Copyright (c) 2008 Ilya Sokolov, Boris Schaeling
+// Copyright (c) 2009 Boris Schaeling
+// Copyright (c) 2010 Felipe Tanus, Boris Schaeling
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+/**
+ * \file boost/process/operations.hpp
+ *
+ * Provides miscellaneous free functions.
+ */
+
+#ifndef BOOST_PROCESS_OPERATIONS_HPP
+#define BOOST_PROCESS_OPERATIONS_HPP
+
+#include <boost/process/config.hpp>
+
+#if defined(BOOST_POSIX_API)
+#   include <boost/process/detail/posix_helpers.hpp>
+#   include <utility>
+#   include <cstddef>
+#   include <stdlib.h>
+#   include <unistd.h>
+#   include <fcntl.h>
+#   if defined(__CYGWIN__)
+#       include <boost/scoped_array.hpp>
+#       include <sys/cygwin.h>
+#   endif
+#elif defined(BOOST_WINDOWS_API)
+#   include <boost/process/detail/windows_helpers.hpp>
+#   include <boost/scoped_array.hpp>
+#   include <boost/shared_array.hpp>
+#   include <windows.h>
+#else
+#   error "Unsupported platform."
+#endif
+
+#include <boost/process/child.hpp>
+#include <boost/process/context.hpp>
+#include <boost/process/stream_id.hpp>
+#include <boost/process/stream_ends.hpp>
+#include <boost/process/handle.hpp>
+#include <boost/filesystem/path.hpp>
+#include <boost/algorithm/string/predicate.hpp>
+#include <boost/system/system_error.hpp>
+#include <boost/throw_exception.hpp>
+#include <boost/assert.hpp>
+#include <string>
+#include <vector>
+#include <map>
+#include <utility>
+
+namespace boost {
+namespace process {
+
+/**
+ * Locates the executable program \a file in all the directory components
+ * specified in \a path. If \a path is empty, the value of the PATH
+ * environment variable is used.
+ *
+ * The path variable is interpreted following the same conventions used
+ * to parse the PATH environment variable in the underlying platform.
+ *
+ * \throw boost::filesystem::filesystem_error If the file cannot be found
+ *        in the path.
+ */
+inline std::string find_executable_in_path(const std::string &file,
+    std::string path = "")
+{
+#if defined(BOOST_POSIX_API)
+    BOOST_ASSERT(file.find('/') == std::string::npos);
+#elif defined(BOOST_WINDOWS_API)
+    BOOST_ASSERT(file.find_first_of("\\/") == std::string::npos);
+#endif
+
+    std::string result;
+
+#if defined(BOOST_POSIX_API)
+    if (path.empty())
+    {
+        const char *envpath = getenv("PATH");
+        if (!envpath)
+            boost::throw_exception(boost::filesystem::filesystem_error(
+                BOOST_PROCESS_SOURCE_LOCATION "file not found", file,
+                boost::system::errc::make_error_code(
+                boost::system::errc::no_such_file_or_directory)));
+        path = envpath;
+    }
+    BOOST_ASSERT(!path.empty());
+
+#if defined(__CYGWIN__)
+    if (!cygwin_posix_path_list_p(path.c_str()))
+    {
+        int size = cygwin_win32_to_posix_path_list_buf_size(path.c_str());
+        boost::scoped_array<char> cygpath(new char[size]);
+        cygwin_win32_to_posix_path_list(path.c_str(), cygpath.get());
+        path = cygpath.get();
+    }
+#endif
+
+    std::string::size_type pos1 = 0, pos2;
+    do
+    {
+        pos2 = path.find(':', pos1);
+        std::string dir = (pos2 != std::string::npos) ?
+            path.substr(pos1, pos2 - pos1) : path.substr(pos1);
+        std::string f = dir +
+            (boost::algorithm::ends_with(dir, "/") ? "" : "/") + file;
+        if (!access(f.c_str(), X_OK))
+            result = f;
+        pos1 = pos2 + 1;
+    } while (pos2 != std::string::npos && result.empty());
+#elif defined(BOOST_WINDOWS_API)
+    const char *exts[] = { "", ".exe", ".com", ".bat", NULL };
+    const char **ext = exts;
+    while (*ext)
+    {
+        char buf[MAX_PATH];
+        char *dummy;
+        DWORD size = SearchPathA(path.empty() ? NULL : path.c_str(),
+            file.c_str(), *ext, MAX_PATH, buf, &dummy);
+        BOOST_ASSERT(size < MAX_PATH);
+        if (size > 0)
+        {
+            result = buf;
+            break;
+        }
+        ++ext;
+    }
+#endif
+
+    if (result.empty())
+        boost::throw_exception(boost::filesystem::filesystem_error(
+            BOOST_PROCESS_SOURCE_LOCATION "file not found", file,
+            boost::system::errc::make_error_code(
+            boost::system::errc::no_such_file_or_directory)));
+
+    return result;
+}
+
+/**
+ * Extracts the program name from a given executable.
+ *
+ * \return The program name. On Windows the program name
+ *         is returned without a file extension.
+ */
+inline std::string executable_to_progname(const std::string &exe)
+{
+    std::string::size_type begin = 0;
+    std::string::size_type end = std::string::npos;
+
+#if defined(BOOST_POSIX_API)
+    std::string::size_type slash = exe.rfind('/');
+#elif defined(BOOST_WINDOWS_API)
+    std::string::size_type slash = exe.find_last_of("/\\");
+#endif
+    if (slash != std::string::npos)
+        begin = slash + 1;
+
+#if defined(BOOST_WINDOWS_API)
+    if (exe.size() > 4 && (boost::algorithm::iends_with(exe, ".exe") ||
+        boost::algorithm::iends_with(exe, ".com") ||
+        boost::algorithm::iends_with(exe, ".bat")))
+        end = exe.size() - 4;
+#endif
+
+    return exe.substr(begin, end - begin);
+}
+
+/**
+ * Starts a new child process.
+ *
+ * Launches a new process based on the binary image specified by the
+ * executable, the set of arguments passed to it and the execution context.
+ *
+ * \remark Blocking remarks: This function may block if the device holding the
+ *         executable blocks when loading the image. This might happen if, e.g.,
+ *         the binary is being loaded from a network share.
+ *
+ * \return A handle to the new child process.
+ */
+template <typename Arguments, typename Context>
+inline child create_child(const std::string &executable, Arguments args,
+    Context ctx)
+{
+    typedef std::map<stream_id, stream_ends> handles_t;
+    handles_t handles;
+    typename Context::streams_t::iterator it = ctx.streams.begin();
+    for (; it != ctx.streams.end(); ++it)
+    {
+        if (it->first == stdin_id)
+            handles[it->first] = it->second(input_stream);
+        else if (it->first == stdout_id)
+            handles[it->first] = it->second(output_stream);
+        else if (it->first == stderr_id)
+            handles[it->first] = it->second(output_stream);
+#if defined(BOOST_POSIX_API)
+        else
+            handles[it->first] = it->second(unknown_stream);
+#endif
+    }
+
+    std::string p_name = ctx.process_name.empty() ?
+        executable_to_progname(executable) : ctx.process_name;
+    args.insert(args.begin(), p_name);
+
+#if defined(BOOST_POSIX_API)
+    // Between fork() and execve() only async-signal-safe functions
+    // must be called if multithreaded applications should be supported.
+    // That's why the following code is executed before fork() is called.
+#if defined(F_MAXFD)
+    int maxdescs = fcntl(-1, F_MAXFD, 0);
+    if (maxdescs == -1)
+        maxdescs = sysconf(_SC_OPEN_MAX);
+#else
+    int maxdescs = static_cast<int>(sysconf(_SC_OPEN_MAX));
+#endif
+    if (maxdescs == -1)
+        maxdescs = 1024;
+    std::vector<bool> closeflags(maxdescs, true);
+    std::pair<std::size_t, char**> argv = detail::collection_to_argv(args);
+    std::pair<std::size_t, char**> envp =
+        detail::environment_to_envp(ctx.env);
+
+    const char *work_dir = ctx.work_dir.c_str();
+
+    pid_t pid = fork();
+    if (pid == -1)
+        BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR("fork(2) failed");
+    else if (pid == 0)
+    {
+        if (chdir(work_dir) == -1)
+        {
+            write(STDERR_FILENO, "chdir() failed\n", 15);
+            _exit(127);
+        }
+
+        for (handles_t::iterator it = handles.begin(); it != handles.end();
+            ++it)
+        {
+            if (it->second.child.valid())
+            {
+                handles_t::iterator it2 = it;
+                ++it2;
+                for (; it2 != handles.end(); ++it2)
+                {
+                    if (it2->second.child.native() == it->first)
+                    {
+                        int fd = fcntl(it2->second.child.native(), F_DUPFD,
+                            it->first + 1);
+                        if (fd == -1)
+                        {
+                            write(STDERR_FILENO, "fcntl() failed\n", 15);
+                            _exit(127);
+                        }
+                        it2->second.child = fd;
+                    }
+                }
+
+                if (dup2(it->second.child.native(), it->first) == -1)
+                {
+                    write(STDERR_FILENO, "dup2() failed\n", 14);
+                    _exit(127);
+                }
+                closeflags[it->first] = false;
+            }
+        }
+
+        if (ctx.setup)
+            ctx.setup();
+
+        for (std::size_t i = 0; i < closeflags.size(); ++i)
+        {
+            if (closeflags[i])
+                close(static_cast<int>(i));
+        }
+
+        execve(executable.c_str(), argv.second, envp.second);
+
+        // Actually we should delete argv and envp data. As we must not
+        // call any non-async-signal-safe functions though we simply exit.
+        write(STDERR_FILENO, "execve() failed\n", 16);
+        _exit(127);
+    }
+    else
+    {
+        BOOST_ASSERT(pid > 0);
+
+        for (std::size_t i = 0; i < argv.first; ++i)
+            delete[] argv.second[i];
+        delete[] argv.second;
+
+        for (std::size_t i = 0; i < envp.first; ++i)
+            delete[] envp.second[i];
+        delete[] envp.second;
+
+        std::map<stream_id, handle> parent_ends;
+        for (handles_t::iterator it = handles.begin(); it != handles.end();
+            ++it)
+            parent_ends[it->first] = it->second.parent;
+
+        return child(pid, parent_ends);
+    }
+#elif defined(BOOST_WINDOWS_API)
+    STARTUPINFOA startup_info;
+    ZeroMemory(&startup_info, sizeof(startup_info));
+    startup_info.cb = sizeof(startup_info);
+    startup_info.dwFlags |= STARTF_USESTDHANDLES;
+    startup_info.hStdInput = handles[stdin_id].child.native();
+    startup_info.hStdOutput = handles[stdout_id].child.native();
+    startup_info.hStdError = handles[stderr_id].child.native();
+
+    if (ctx.setup)
+        ctx.setup(startup_info);
+
+    PROCESS_INFORMATION pi;
+    ZeroMemory(&pi, sizeof(pi));
+
+    boost::shared_array<char> cmdline =
+        detail::collection_to_windows_cmdline(args);
+
+    boost::scoped_array<char> exe(new char[executable.size() + 1]);
+#if (BOOST_MSVC >= 1400)
+    strcpy_s(exe.get(), executable.size() + 1, executable.c_str());
+#else
+    strcpy(exe.get(), executable.c_str());
+#endif
+
+    boost::scoped_array<char> workdir(new char[ctx.work_dir.size() + 1]);
+#if (BOOST_MSVC >= 1400)
+    strcpy_s(workdir.get(), ctx.work_dir.size() + 1, ctx.work_dir.c_str());
+#else
+    strcpy(workdir.get(), ctx.work_dir.c_str());
+#endif
+
+    boost::shared_array<char> envstrs =
+        detail::environment_to_windows_strings(ctx.env);
+
+    if (CreateProcessA(exe.get(), cmdline.get(), NULL, NULL, TRUE, 0,
+        envstrs.get(), workdir.get(), &startup_info, &pi) == 0)
+        BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR("CreateProcess() failed");
+
+    handle hprocess(pi.hProcess);
+
+    if (!CloseHandle(pi.hThread))
+        BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR("CloseHandle() failed");
+
+    std::map<stream_id, handle> parent_ends;
+    parent_ends[stdin_id] = handles[stdin_id].parent;
+    parent_ends[stdout_id] = handles[stdout_id].parent;
+    parent_ends[stderr_id] = handles[stderr_id].parent;
+
+    return child(hprocess, parent_ends);
+#endif
+}
+
+/**
+ * \overload
+ */
+inline child create_child(const std::string &executable)
+{
+    return create_child(executable, std::vector<std::string>(), context());
+}
+
+/**
+ * \overload
+ */
+template <typename Arguments>
+inline child create_child(const std::string &executable, Arguments args)
+{
+    return create_child(executable, args, context());
+}
+
+/**
+ * Starts a shell-based command.
+ *
+ * Executes the given command through the default system shell. The
+ * command is subject to pattern expansion, redirection and pipelining.
+ * The shell is launched as described by the parameters in the context.
+ *
+ * This function behaves similarly to the system(3) system call. In a
+ * POSIX system, the command is fed to /bin/sh whereas under a Windows
+ * system, it is fed to cmd.exe. It is difficult to write portable
+ * commands, but this function comes in handy in multiple situations.
+ *
+ * \remark Blocking remarks: This function may block if the device holding the
+ *         executable blocks when loading the image. This might happen if, e.g.,
+ *         the binary is being loaded from a network share.
+ *
+ * \return A handle to the new child process.
+ */
+template <typename Context>
+inline child shell(const std::string &command, Context ctx)
+{
+#if defined(BOOST_POSIX_API)
+    std::string executable = "/bin/sh";
+    std::vector<std::string> args;
+    args.push_back("-c");
+    args.push_back(command);
+#elif defined(BOOST_WINDOWS_API)
+    char sysdir[MAX_PATH];
+    UINT size = GetSystemDirectoryA(sysdir, sizeof(sysdir));
+    if (!size)
+        BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR("GetSystemDirectory() failed");
+    std::string executable = std::string(sysdir) +
+        (sysdir[size - 1] != '\\' ? "\\cmd.exe" : "cmd.exe");
+    std::vector<std::string> args;
+    args.push_back("/c");
+    args.push_back(command);
+#endif
+    return create_child(executable, args, ctx);
+}
+
+/**
+ * \overload
+ */
+inline child shell(const std::string &command)
+{
+    return shell(command, context());
+}
+
+}
+}
+
+#endif