0aed8a4664b17a97344c7ee50e164de44e5e1d7a
[libi2ncommon] / src / pipestream.hxx
1  /*
2 The software in this package is distributed under the GNU General
3 Public License version 2 (with a special exception described below).
4
5 A copy of GNU General Public License (GPL) is included in this distribution,
6 in the file COPYING.GPL.
7
8 As a special exception, if other files instantiate templates or use macros
9 or inline functions from this file, or you compile this file and link it
10 with other works to produce a work based on this file, this file
11 does not by itself cause the resulting work to be covered
12 by the GNU General Public License.
13
14 However the source code for this file must still be made available
15 in accordance with section (3) of the GNU General Public License.
16
17 This exception does not invalidate any other reasons why a work based
18 on this file might be covered by the GNU General Public License.
19 */
20 /***************************************************************************
21               inpipestream.hxx  -  C++ streambuffer wrapper 
22                              -------------------
23     begin                : Thu Dec 27 2001
24     copyright            : (C) 2001 by Intra2net AG
25  ***************************************************************************/
26
27 #ifndef _PIPESTREAM
28 #define _PIPESTREAM
29
30 #include <stdio.h>
31
32 #include <cstring>
33 #include <string>
34 #include <streambuf>
35 #include <istream>
36 #include <ostream>
37 #include <vector>
38
39 #include <stringfunc.hxx>
40
41 struct ExecResult
42 {
43     /** if the program exited normally and returned a return code */
44     bool normal_exit;
45
46     /** the real return code of the program, only set when normal_exit true */
47     char return_code;
48
49     /** if the program was terminated by a signal */
50     bool terminated_by_signal;
51
52     /** number of the signal that terminated the program, only valid when terminated_by_signal true */
53     int signal;
54
55     /** errormessage if we have one */
56     std::string error_message;
57
58     inline std::string format (void) const
59     {
60         return std::string ("(")
61             + "(normal_exit " + (this->normal_exit ? "T" : "F") + ") "
62               "(return_code '" + I2n::to_string ((int)this->return_code) + "') "
63               "(signal " + (this->terminated_by_signal
64                             ? strsignal (this->signal)
65                             : "<nil>") + "))"
66             ;
67     };
68 };
69 typedef struct ExecResult ExecResult;
70
71 namespace capture_flag {
72
73     static const int none           =      0;
74     static const int collect_out    = 1 << 0;
75     static const int collect_err    = 1 << 1;
76     static const int search_path    = 1 << 2;
77     static const int env_passthru   = 1 << 3;
78
79     static const int dflt           = collect_out;
80     static const int collect_any    = collect_out | collect_err;
81
82 } /* [namespace capture_flag] */
83
84 std::string capture_exec(const std::string& command, ExecResult &rescode);
85 std::string capture_exec(const char *const *command, ExecResult &rescode,
86                          const int flags=capture_flag::dflt);
87 std::string capture_exec(const std::vector<std::string>& command, ExecResult &rescode,
88                          const int flags=capture_flag::dflt);
89
90 inline std::string capture_exec (const std::string &command)
91 {
92     ExecResult r;
93     return capture_exec(command,r);
94 }
95
96 inline std::string capture_exec(const char *const *command)
97 {
98     ExecResult r;
99     return capture_exec(command,r);
100 }
101
102 inline std::string capture_exec(const std::vector<std::string>& command)
103 {
104     ExecResult r;
105     return capture_exec(command,r);
106 }
107
108 /** @brief runs command and provides buffered input for it through pipe
109  *
110  * opens pipe to command using popen; exit status available after destruction
111  * (use WEXITSTATUS to get the "regular" return code (lowest byte))
112  *
113  * ATTENTION: A lot of mysterious STL bugs occured
114  *            with a "real" buffer (buffer larger than 1 byte and up to 100 bytes)
115  *            -> Keep it slow and working!
116  */
117 class inpipebuf : public std::streambuf
118 {
119 protected:
120     char buffer;
121
122     FILE *pipe;
123     pid_t pid;
124
125     // "callback" variables for destructor to store exit status
126     bool *status_set;
127     int *exit_status;
128
129 public:
130     inpipebuf(const std::string& command, const int flags);
131     inpipebuf(const char *const *command, const int flags);
132     inpipebuf(const std::vector<std::string> &command, const int flags);
133
134     ~inpipebuf();
135
136     void store_exit_status(bool *_status_set, int *_exit_status);
137
138 protected:
139     virtual int_type underflow();
140
141 private:
142     std::pair <pid_t, FILE *>
143     init_without_shell (const char *const *argv, const int flags) const;
144 };
145
146 /** @brief stream around inpipebuf -- see comment there */
147 class inpipestream : public std::istream
148 {
149 protected:
150     inpipebuf buf;
151
152 public:
153     inpipestream(const std::string& command,
154                  const int flags=capture_flag::dflt)
155             : std::istream(&buf), buf(command, flags)
156     {}
157
158     inpipestream(const char *const command[],
159                  const int flags=capture_flag::dflt)
160             : std::istream(&buf), buf(command, flags)
161     {}
162
163     inpipestream(const std::vector<std::string> &command,
164                  const int flags=capture_flag::dflt)
165             : std::istream(&buf), buf(command, flags)
166     {}
167
168     void store_exit_status(bool *_status_set, int *_exit_status)
169     { buf.store_exit_status(_status_set, _exit_status); }
170 };
171
172 /** @brief runs command and provides buffered ouptput from it through pipe
173  *
174  * opens pipe to command using popen; exit status available after destruction
175  * (use WEXITSTATUS to get the "regular" return code (lowest byte))
176  */
177 class outpipebuf : public std::streambuf
178 {
179 protected:
180     FILE *pipe;
181
182     // "callback" variables for destructor to store exit status
183     bool *status_set;
184     int *exit_status;
185
186 public:
187     outpipebuf(const std::string& command);
188
189     ~outpipebuf();
190
191     /** note: exit status only available after destruction */
192     void store_exit_status(bool *_status_set, int *_exit_status);
193
194 protected:
195     virtual int_type overflow(int_type c);
196
197     virtual std::streamsize xsputn(const char* s, std::streamsize num);
198 };
199
200
201 /** @brief stream around outpipebuf -- see comment there */
202 class outpipestream : public std::ostream
203 {
204 protected:
205     outpipebuf buf;
206 public:
207     outpipestream(const std::string& command)
208             : std::ostream(&buf), buf(command)
209     {}
210
211     void store_exit_status(bool *_status_set, int *_exit_status)
212     { buf.store_exit_status(_status_set, _exit_status); }
213 };
214
215 #endif