portability/subprocesses.hpp
1: #ifndef STLPLUS_SUBPROCESSES
2: #define STLPLUS_SUBPROCESSES
3: ////////////////////////////////////////////////////////////////////////////////
4:
5: // Author: Andy Rushton
6: // Copyright: (c) Southampton University 1999-2004
7: // (c) Andy Rushton 2004-2009
8: // License: BSD License, see ../docs/license.html
9:
10: // Platform-independent wrapper around the very platform-specific handling of
11: // subprocesses. Uses the C++ convention that all resources must be contained in
12: // an object so that when a subprocess object goes out of scope the subprocess
13: // itself gets closed down.
14:
15: ////////////////////////////////////////////////////////////////////////////////
16: #include "portability_fixes.hpp"
17: #ifdef MSWINDOWS
18: #include <windows.h>
19: #endif
20: #include <stdexcept>
21: #include <vector>
22: #include <string>
23: #include <map> // for std::pair - why is this not defined separately?
24:
25: ////////////////////////////////////////////////////////////////////////////////
26:
27: namespace stlplus
28: {
29:
30: ////////////////////////////////////////////////////////////////////////////////
31: // Argument vector class
32: // allows manipulation of argv-like vectors
33: // includes splitting of command lines into argvectors as per the shell
34: // (removing quotes) and the reverse conversion (adding quotes where necessary)
35:
36: class arg_vector
37: {
38: private:
39: char** m_argv;
40:
41: public:
42: // create an empty vector
43: arg_vector (void);
44:
45: // copy constructor (yes it copies)
46: arg_vector (const arg_vector&);
47:
48: // construct from an argv
49: arg_vector (char**);
50:
51: // construct from a command-line string
52: // includes de-quoting of values
53: arg_vector (const std::string&);
54: arg_vector (const char*);
55:
56: ~arg_vector (void);
57:
58: // assignment operators are compatible with the constructors
59: arg_vector& operator = (const arg_vector&);
60: arg_vector& operator = (char**);
61: arg_vector& operator = (const std::string&);
62: arg_vector& operator = (const char*);
63:
64: // add an argument to the vector
65: arg_vector& operator += (const std::string&);
66: arg_vector& operator -= (const std::string&);
67:
68: // insert/clear an argument at a certain index
69: // adding is like the other array classes - it moves the current item at index
70: // up one (and all subsequent values) to make room
71: void insert (unsigned index, const std::string&) throw(std::out_of_range);
72: void clear (unsigned index) throw(std::out_of_range);
73: void clear (void);
74:
75: // number of values in the vector (including argv[0], the command itself
76: unsigned size (void) const;
77:
78: // type conversion to the argv type
79: operator char** (void) const;
80: // function-based version of the above for people who don't like type conversions
81: char** argv (void) const;
82:
83: // access individual values in the vector
84: char* operator [] (unsigned index) const throw(std::out_of_range);
85:
86: // special-case access of the command name (e.g. to do path lookup on the command)
87: char* argv0 (void) const throw(std::out_of_range);
88:
89: // get the command-line string represented by this vector
90: // includes escaping of special characters and quoting
91: std::string image (void) const;
92: };
93:
94: ////////////////////////////////////////////////////////////////////////////////
95: // Environment class
96: // Allows manipulation of an environment vector
97: // This is typically used to create an environment to be used by a subprocess
98: // It does NOT modify the environment of the current process
99:
100: #ifdef MSWINDOWS
101: #define ENVIRON_TYPE char*
102: #else
103: #define ENVIRON_TYPE char**
104: #endif
105:
106: class env_vector
107: {
108: private:
109: ENVIRON_TYPE m_env;
110:
111: public:
112: // create an env_vector vector from the current process
113: env_vector (void);
114: env_vector (const env_vector&);
115: ~env_vector (void);
116:
117: env_vector& operator = (const env_vector&);
118:
119: void clear (void);
120:
121: // manipulate the env_vector by adding or removing variables
122: // adding a name that already exists replaces its value
123: void add (const std::string& name, const std::string& value);
124: bool remove (const std::string& name);
125:
126: // get the value associated with a name
127: // the first uses an indexed notation (e.g. env["PATH"] )
128: // the second is a function based form (e.g. env.get("PATH"))
129: std::string operator [] (const std::string& name) const;
130: std::string get (const std::string& name) const;
131:
132: // number of name=value pairs in the env_vector
133: unsigned size (void) const;
134:
135: // get the name=value pairs by index (in the range 0 to size()-1)
136: std::pair<std::string,std::string> operator [] (unsigned index) const throw(std::out_of_range);
137: std::pair<std::string,std::string> get (unsigned index) const throw(std::out_of_range);
138:
139: // access the env_vector as an envp type - used for passing to subprocesses
140: ENVIRON_TYPE envp (void) const;
141: };
142:
143: ////////////////////////////////////////////////////////////////////////////////
144:
145: #ifdef MSWINDOWS
146: #define PID_TYPE PROCESS_INFORMATION
147: #define PIPE_TYPE HANDLE
148: #else
149: #define PID_TYPE int
150: #define PIPE_TYPE int
151: #endif
152:
153: ////////////////////////////////////////////////////////////////////////////////
154: // Synchronous subprocess
155:
156: class subprocess
157: {
158: private:
159:
160: PID_TYPE m_pid;
161: #ifdef MSWINDOWS
162: HANDLE m_job;
163: #endif
164: PIPE_TYPE m_child_in;
165: PIPE_TYPE m_child_out;
166: PIPE_TYPE m_child_err;
167: env_vector m_env;
168: int m_err;
169: int m_status;
170:
171: public:
172: subprocess(void);
173: virtual ~subprocess(void);
174:
175: void add_variable(const std::string& name, const std::string& value);
176: bool remove_variable(const std::string& name);
177:
178: bool spawn(const std::string& path, const arg_vector& argv,
179: bool connect_stdin = false, bool connect_stdout = false, bool connect_stderr = false);
180: bool spawn(const std::string& command_line,
181: bool connect_stdin = false, bool connect_stdout = false, bool connect_stderr = false);
182:
183: virtual bool callback(void);
184: bool kill(void);
185:
186: int write_stdin(std::string& buffer);
187: int read_stdout(std::string& buffer);
188: int read_stderr(std::string& buffer);
189:
190: void close_stdin(void);
191: void close_stdout(void);
192: void close_stderr(void);
193:
194: bool error(void) const;
195: int error_number(void) const;
196: std::string error_text(void) const;
197:
198: int exit_status(void) const;
199:
200: private:
201: // disallow copying
202: subprocess(const subprocess&);
203: subprocess& operator=(const subprocess&);
204: };
205:
206: ////////////////////////////////////////////////////////////////////////////////
207: // Preconfigured subprocess which executes a command and captures its output
208:
209: class backtick_subprocess : public subprocess
210: {
211: private:
212: std::string m_text;
213: public:
214: backtick_subprocess(void);
215: virtual bool callback(void);
216: bool spawn(const std::string& path, const arg_vector& argv);
217: bool spawn(const std::string& command_line);
218: std::vector<std::string> text(void) const;
219: };
220:
221: std::vector<std::string> backtick(const std::string& path, const arg_vector& argv);
222: std::vector<std::string> backtick(const std::string& command_line);
223:
224: ////////////////////////////////////////////////////////////////////////////////
225: // Asynchronous subprocess
226:
227: class async_subprocess
228: {
229: private:
230: PID_TYPE m_pid;
231: #ifdef MSWINDOWS
232: HANDLE m_job;
233: #endif
234: PIPE_TYPE m_child_in;
235: PIPE_TYPE m_child_out;
236: PIPE_TYPE m_child_err;
237: env_vector m_env;
238: int m_err;
239: int m_status;
240: void set_error(int);
241:
242: public:
243: async_subprocess(void);
244: virtual ~async_subprocess(void);
245:
246: void add_variable(const std::string& name, const std::string& value);
247: bool remove_variable(const std::string& name);
248:
249: bool spawn(const std::string& path, const arg_vector& argv,
250: bool connect_stdin = false, bool connect_stdout = false, bool connect_stderr = false);
251: bool spawn(const std::string& command_line,
252: bool connect_stdin = false, bool connect_stdout = false, bool connect_stderr = false);
253:
254: virtual bool callback(void);
255: bool tick(void);
256: bool kill(void);
257:
258: int write_stdin(std::string& buffer);
259: int read_stdout(std::string& buffer);
260: int read_stderr(std::string& buffer);
261:
262: void close_stdin(void);
263: void close_stdout(void);
264: void close_stderr(void);
265:
266: bool error(void) const;
267: int error_number(void) const;
268: std::string error_text(void) const;
269:
270: int exit_status(void) const;
271:
272: private:
273: // disallow copying
274: async_subprocess(const async_subprocess&);
275: async_subprocess& operator=(const async_subprocess&);
276: };
277:
278: ////////////////////////////////////////////////////////////////////////////////
279:
280: } // end namespace stlplus
281:
282: #endif