# 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