portability/subprocesses.hpp
#ifndef STLPLUS_SUBPROCESSES
#define STLPLUS_SUBPROCESSES
////////////////////////////////////////////////////////////////////////////////
// Author: Andy Rushton
// Copyright: (c) Southampton University 1999-2004
// (c) Andy Rushton 2004 onwards
// License: BSD License, see ../docs/license.html
// Platform-independent wrapper around the very platform-specific handling of
// subprocesses. Uses the C++ convention that all resources must be contained in
// an object so that when a subprocess object goes out of scope the subprocess
// itself gets closed down.
////////////////////////////////////////////////////////////////////////////////
#include "portability_fixes.hpp"
#ifdef MSWINDOWS
#include <windows.h>
#endif
#include <stdexcept>
#include <vector>
#include <string>
#include <map> // for std::pair - why is this not defined separately?
////////////////////////////////////////////////////////////////////////////////
namespace stlplus
{
////////////////////////////////////////////////////////////////////////////////
// Argument vector class
// allows manipulation of argv-like vectors
// includes splitting of command lines into argvectors as per the shell
// (removing quotes) and the reverse conversion (adding quotes where necessary)
class arg_vector
{
private:
char** m_argv;
public:
// create an empty vector
arg_vector (void);
// copy constructor (yes it copies)
arg_vector (const arg_vector&);
// construct from an argv
arg_vector (char**);
// construct from a command-line string
// includes de-quoting of values
arg_vector (const std::string&);
arg_vector (const char*);
~arg_vector (void);
// assignment operators are compatible with the constructors
arg_vector& operator = (const arg_vector&);
arg_vector& operator = (char**);
arg_vector& operator = (const std::string&);
arg_vector& operator = (const char*);
// add an argument to the vector
arg_vector& operator += (const std::string&);
arg_vector& operator -= (const std::string&);
// insert/clear an argument at a certain index
// adding is like the other array classes - it moves the current item at index
// up one (and all subsequent values) to make room
// exceptions: std::out_of_range
void insert (unsigned index, const std::string&) ;
// exceptions: std::out_of_range
void clear (unsigned index) ;
void clear (void);
// number of values in the vector (including argv[0], the command itself
unsigned size (void) const;
// type conversion to the argv type
operator char** (void) const;
// function-based version of the above for people who don't like type conversions
char** argv (void) const;
// access individual values in the vector
// exceptions: std::out_of_range
char* operator [] (unsigned index) const ;
// special-case access of the command name (e.g. to do path lookup on the command)
// exceptions: std::out_of_range
char* argv0 (void) const ;
// get the command-line string represented by this vector
// includes escaping of special characters and quoting
std::string image (void) const;
};
////////////////////////////////////////////////////////////////////////////////
// Environment class
// Allows manipulation of an environment vector
// This is typically used to create an environment to be used by a subprocess
// It does NOT modify the environment of the current process
#ifdef MSWINDOWS
#define ENVIRON_TYPE char*
#else
#define ENVIRON_TYPE char**
#endif
class env_vector
{
private:
ENVIRON_TYPE m_env;
public:
// access the env_vector as an envp type - used for passing to subprocesses
ENVIRON_TYPE envp (void) const;
// create an env_vector vector from the current process
env_vector (void);
env_vector (const env_vector&);
~env_vector (void);
env_vector& operator = (const env_vector&);
// manipulate the env_vector by adding or removing variables
// adding a name that already exists replaces its value
void add (const std::string& name, const std::string& value);
bool remove (const std::string& name);
void clear (void);
// get the value associated with a name
// the first uses an indexed notation (e.g. env["PATH"] )
// the second is a function based form (e.g. env.get("PATH"))
bool present(const std::string& name) const;
std::string operator [] (const std::string& name) const;
std::string get (const std::string& name) const;
// number of name=value pairs in the env_vector
unsigned size (void) const;
// get the name=value pairs by index (in the range 0 to size()-1)
// exceptions: std::out_of_range
std::pair<std::string,std::string> operator [] (unsigned index) const ;
// exceptions: std::out_of_range
std::pair<std::string,std::string> get (unsigned index) const ;
};
////////////////////////////////////////////////////////////////////////////////
#ifdef MSWINDOWS
#define PID_TYPE PROCESS_INFORMATION
#define PIPE_TYPE HANDLE
#else
#define PID_TYPE int
#define PIPE_TYPE int
#endif
////////////////////////////////////////////////////////////////////////////////
// Synchronous subprocess
class subprocess
{
protected:
PID_TYPE m_pid;
#ifdef MSWINDOWS
HANDLE m_job;
#endif
PIPE_TYPE m_child_in;
PIPE_TYPE m_child_out;
PIPE_TYPE m_child_err;
env_vector m_env;
int m_err;
int m_status;
void set_error(int);
public:
subprocess(void);
virtual ~subprocess(void);
void add_variable(const std::string& name, const std::string& value);
bool remove_variable(const std::string& name);
const env_vector& get_variables(void) const;
bool spawn(const std::string& path, const arg_vector& argv,
bool connect_stdin = false, bool connect_stdout = false, bool connect_stderr = false);
bool spawn(const std::string& command_line,
bool connect_stdin = false, bool connect_stdout = false, bool connect_stderr = false);
virtual bool callback(void);
bool kill(void);
int write_stdin(std::string& buffer);
int read_stdout(std::string& buffer);
int read_stderr(std::string& buffer);
void close_stdin(void);
void close_stdout(void);
void close_stderr(void);
bool error(void) const;
int error_number(void) const;
std::string error_text(void) const;
int exit_status(void) const;
private:
// disallow copying
subprocess(const subprocess&);
subprocess& operator=(const subprocess&);
};
////////////////////////////////////////////////////////////////////////////////
// Preconfigured subprocess which executes a command and captures its output
class backtick_subprocess : public subprocess
{
protected:
std::string m_text;
public:
backtick_subprocess(void);
virtual bool callback(void);
bool spawn(const std::string& path, const arg_vector& argv);
bool spawn(const std::string& command_line);
std::vector<std::string> text(void) const;
};
std::vector<std::string> backtick(const std::string& path, const arg_vector& argv);
std::vector<std::string> backtick(const std::string& command_line);
////////////////////////////////////////////////////////////////////////////////
// Asynchronous subprocess
class async_subprocess
{
protected:
PID_TYPE m_pid;
#ifdef MSWINDOWS
HANDLE m_job;
#endif
PIPE_TYPE m_child_in;
PIPE_TYPE m_child_out;
PIPE_TYPE m_child_err;
env_vector m_env;
int m_err;
int m_status;
void set_error(int);
public:
async_subprocess(void);
virtual ~async_subprocess(void);
void add_variable(const std::string& name, const std::string& value);
bool remove_variable(const std::string& name);
const env_vector& get_variables(void) const;
bool spawn(const std::string& path, const arg_vector& argv,
bool connect_stdin = false, bool connect_stdout = false, bool connect_stderr = false);
bool spawn(const std::string& command_line,
bool connect_stdin = false, bool connect_stdout = false, bool connect_stderr = false);
virtual bool callback(void);
bool tick(void);
bool kill(void);
int write_stdin(std::string& buffer);
int read_stdout(std::string& buffer);
int read_stderr(std::string& buffer);
void close_stdin(void);
void close_stdout(void);
void close_stderr(void);
bool error(void) const;
int error_number(void) const;
std::string error_text(void) const;
int exit_status(void) const;
private:
// disallow copying
async_subprocess(const async_subprocess&);
async_subprocess& operator=(const async_subprocess&);
};
////////////////////////////////////////////////////////////////////////////////
} // end namespace stlplus
#endif