portability/subprocesses.hpp

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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
#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