1
1
#include < matplot/util/popen.h>
2
2
#include < array>
3
+ #include < system_error>
3
4
4
5
#ifdef _WIN32
5
6
6
7
#include < io.h>
7
8
8
- int pipe_open (ProcPipe *pipe, const char * cmd, const char * mode)
9
+ int common_pipe::open ( const std::string& cmd, char mode)
9
10
{
10
- if (cmd == nullptr || mode == nullptr || (mode[0 ] != ' r' && mode[0 ] != ' w' ) ||
11
- pipe == nullptr )
12
- return EINVAL;
11
+ if (opened ())
12
+ close (); // prevent resource leak
13
13
HANDLE hChildStdinRd, hChildStdinWr, hStdin, hStdout;
14
14
SECURITY_ATTRIBUTES saAttr{};
15
15
// Set up security attributes for inheritable handles
@@ -19,7 +19,7 @@ int pipe_open(ProcPipe *pipe, const char *cmd, const char *mode)
19
19
20
20
// Create a pipe for the child process's input
21
21
if (!CreatePipe (&hChildStdinRd, &hChildStdinWr, &saAttr, 0 ))
22
- return GetLastError ();
22
+ return error ( GetLastError (), " CreatePipe " );
23
23
24
24
// Ensure the write handle to the pipe is not inherited by child
25
25
// processes
@@ -58,7 +58,7 @@ int pipe_open(ProcPipe *pipe, const char *cmd, const char *mode)
58
58
PROCESS_INFORMATION pi;
59
59
60
60
// Create the child process, while hiding the window
61
- if (!CreateProcess (NULL , const_cast <char *>(cmd), NULL , NULL , TRUE ,
61
+ if (!CreateProcess (NULL , const_cast <char *>(cmd. c_str () ), NULL , NULL , TRUE ,
62
62
CREATE_NO_WINDOW, NULL , NULL , &si, &pi)) {
63
63
auto err = GetLastError ();
64
64
CloseHandle (hChildStdinRd);
@@ -67,17 +67,20 @@ int pipe_open(ProcPipe *pipe, const char *cmd, const char *mode)
67
67
CloseHandle (hStdout);
68
68
return err;
69
69
}
70
- pipe-> hProcess = pi.hProcess ;
71
- pipe-> hThread = pi.hThread ;
70
+ hProcess = pi.hProcess ;
71
+ hThread = pi.hThread ;
72
72
73
73
// Close unnecessary handles
74
74
CloseHandle (hChildStdinRd);
75
75
CloseHandle (hStdout);
76
76
77
77
// Create a FILE pointer from the write handle to the child process's
78
78
// input
79
- pipe->file = _fdopen (_open_osfhandle ((intptr_t )hChildStdinWr, 0 ), mode);
80
- if (pipe->file == nullptr ) {
79
+ if (mode == ' r' )
80
+ file_ = _fdopen (_open_osfhandle ((intptr_t )hChildStdinWr, 0 ), " r" );
81
+ else
82
+ file_ = _fdopen (_open_osfhandle ((intptr_t )hChildStdinWr, 0 ), " w" );
83
+ if (file_ == nullptr ) {
81
84
auto err = GetLastError ();
82
85
CloseHandle (hChildStdinWr);
83
86
CloseHandle (hStdin);
@@ -88,32 +91,30 @@ int pipe_open(ProcPipe *pipe, const char *cmd, const char *mode)
88
91
return 0 ;
89
92
}
90
93
91
- int pipe_close (ProcPipe *pipe, int *exit_code)
94
+ int common_pipe::close ( int *exit_code)
92
95
{
93
- // The following does not work for GetExitCodeProcess:
94
- // HANDLE hFile = (HANDLE)_get_osfhandle(_fileno(file));
95
- if (!pipe_is_valid (pipe))
96
- return EINVAL;
96
+ if (!opened ())
97
+ return error (EINVAL, " common_pipe::close" );
97
98
// Close the pipe to process:
98
- fclose (pipe-> file );
99
- pipe-> file = nullptr ;
99
+ fclose (file_ );
100
+ file_ = nullptr ;
100
101
// Wait for the process to finish
101
- if (auto r = WaitForSingleObject (pipe-> hProcess , INFINITE); r != WAIT_OBJECT_0) {
102
- CloseHandle (pipe-> hThread );
103
- CloseHandle (pipe-> hProcess );
102
+ if (auto r = WaitForSingleObject (hProcess, INFINITE); r != WAIT_OBJECT_0) {
103
+ CloseHandle (hThread);
104
+ CloseHandle (hProcess);
104
105
return ECHILD;
105
106
}
106
107
// Retrieve the exit code
107
108
if (exit_code != nullptr ) {
108
- if (BOOL r = GetExitCodeProcess (pipe-> hProcess , (LPDWORD)exit_code); !r) {
109
+ if (BOOL r = GetExitCodeProcess (hProcess, (LPDWORD)exit_code); !r) {
109
110
auto err = GetLastError ();
110
- CloseHandle (pipe-> hThread );
111
- CloseHandle (pipe-> hProcess );
111
+ CloseHandle (hThread);
112
+ CloseHandle (hProcess);
112
113
return err;
113
114
}
114
115
}
115
- CloseHandle (pipe-> hThread );
116
- CloseHandle (pipe-> hProcess );
116
+ CloseHandle (hThread);
117
+ CloseHandle (hProcess);
117
118
return 0 ;
118
119
}
119
120
#endif // _WIN32 implementtion
@@ -123,116 +124,128 @@ int pipe_close(ProcPipe *pipe, int *exit_code)
123
124
#include < cerrno>
124
125
#include < sys/wait.h> // waitpid
125
126
126
- int pipe_open (ProcPipe *p, const char * cmd, const char * mode)
127
+ int common_pipe::open ( const std::string & cmd, char mode)
127
128
{
128
129
constexpr auto READ = 0u ;
129
130
constexpr auto WRITE = 1u ;
130
- if (cmd == nullptr || mode == nullptr || (mode[0 ] != ' r' && mode[0 ] != ' w' ) || p == nullptr )
131
- return EINVAL;
132
-
133
131
int fd[2 ];
134
132
if (pipe (fd) == -1 )
135
- return errno;
136
- if ((p-> pid = fork ()) == -1 )
137
- return errno;
133
+ return error ( errno, " pipe " ) ;
134
+ if ((pid = fork ()) == -1 )
135
+ return error ( errno, " fork " ) ;
138
136
139
- if (p-> pid == 0 ) { // child process
140
- if (mode[ 0 ] == ' r' ) {
137
+ if (pid == 0 ) { // child process
138
+ if (mode == ' r' ) {
141
139
close (fd[READ]); // Close the READ end of the pipe
142
140
dup2 (fd[WRITE], 1 ); // Redirect stdout to pipe
143
- } else { // (mode[0] == 'w')
141
+ } else { // (mode == 'w')
144
142
close (fd[WRITE]); // Close the WRITE end of the pipe
145
143
dup2 (fd[READ], 0 ); // Redirect stdin to pipe
146
144
}
147
- setpgid (p->pid , p->pid ); // Needed so negative PIDs can kill children of /bin/sh
148
- execl (" /bin/sh" , " /bin/sh" , " -c" , cmd, nullptr ); // returns only upon errors
149
- return errno;
145
+ setpgid (pid, pid); // Needed so negative PIDs can kill children of /bin/sh
146
+ execl (" /bin/sh" , " /bin/sh" , " -c" , cmd.c_str (), nullptr );
147
+ // execl returns only upon error
148
+ std::exit (EXIT_FAILURE);
150
149
} else {
151
- if (mode[ 0 ] == ' r' ) {
150
+ if (mode == ' r' ) {
152
151
close (fd[WRITE]); // Close the WRITE end of the pipe since parent's
153
152
// fd is read-only
154
- } else { // (mode[0] == 'w')
153
+ } else { // (mode == 'w')
155
154
close (fd[READ]); // Close the READ end of the pipe since parent's fd
156
155
// is write-only
157
156
}
158
157
}
159
- p->file = (mode[0 ] == ' r' ) ? fdopen (fd[READ], " r" ) : fdopen (fd[WRITE], " w" );
158
+ if (mode == ' r' )
159
+ file_ = fdopen (fd[READ], " r" );
160
+ else
161
+ file_ = fdopen (fd[WRITE], " w" );
162
+ if (file_ == nullptr )
163
+ return error (errno, " fdopen" );
160
164
return 0 ;
161
165
}
162
166
163
167
// / Closes the pipe opened by popen2 and waits for termination
164
- int pipe_close (ProcPipe *pipe, int *exit_code)
168
+ int common_pipe::close ( int *exit_code)
165
169
{
166
- if (!proc_is_good (pipe))
167
- return EINVAL;
168
- fclose (pipe->file );
169
- while (waitpid (pipe->pid , exit_code, 0 ) == -1 ) {
170
+ if (!valid ())
171
+ return 0 ;
172
+ fclose (file_);
173
+ file_ = nullptr ;
174
+ while (waitpid (pid, exit_code, 0 ) == -1 ) {
170
175
if (errno != EINTR) {
171
- *exit_code = -1 ;
176
+ if (exit_code != nullptr )
177
+ *exit_code = -1 ;
172
178
break ;
173
179
}
174
180
}
175
- pipe->file = nullptr ;
176
181
return 0 ;
177
182
}
178
183
179
184
#endif // POSIX implementation
180
185
181
- int pipe_write (ProcPipe *p, std::string_view data)
186
+ inline int common_pipe::error (int code, const std::string& what) const
187
+ {
188
+ if (exceptions_)
189
+ throw std::system_error{code, std::generic_category (), what};
190
+ return code;
191
+ }
192
+
193
+ int opipe::write (std::string_view data)
182
194
{
183
195
constexpr auto CSIZE = sizeof (std::string_view::value_type);
184
- if (!pipe_is_valid (p ))
185
- return EINVAL;
186
- if (auto sz = std::fwrite (data.data (), CSIZE, data.length (), p-> file );
196
+ if (!opened ( ))
197
+ return error ( EINVAL, " opipe::write " ) ;
198
+ if (auto sz = std::fwrite (data.data (), CSIZE, data.length (), file_ );
187
199
sz != data.size ()) {
188
- if (auto err = std::ferror (p-> file ); err != 0 )
189
- return EIO;
190
- if (auto err = std::feof (p-> file ); err != 0 )
191
- return EIO;
200
+ if (auto err = std::ferror (file_ ); err != 0 )
201
+ return error ( EIO, " fwrite error " ) ;
202
+ if (auto err = std::feof (file_ ); err != 0 )
203
+ return error ( EIO, " fwrite eof " ) ;
192
204
}
193
- if (auto res = std::fflush (p->file ); res != 0 )
194
- return errno;
195
205
return 0 ;
196
206
}
197
207
198
- int pipe_flush (ProcPipe *p, std::string_view data)
208
+ int opipe::flush ( std::string_view data)
199
209
{
200
- if (!pipe_is_valid (p ))
201
- return EINVAL;
210
+ if (!opened ( ))
211
+ return error ( EINVAL, " opipe::flush " ) ;
202
212
if (!data.empty ())
203
- if (auto err = pipe_write (p, data); err != 0 )
204
- return err;
205
- if (auto res = std::fflush (p-> file ); res != 0 )
206
- return errno;
213
+ if (auto err = write ( data); err != 0 )
214
+ return error ( err, " opipe::write " ) ;
215
+ if (auto res = std::fflush (file_ ); res != 0 )
216
+ return error ( errno, " fflush " ) ;
207
217
return 0 ;
208
218
}
209
219
210
- int shell_write ( const std::string &cmd, std::string_view data)
220
+ int ipipe::read ( std::string &data)
211
221
{
212
- auto pipe = ProcPipe{};
213
- if (auto err = pipe_open (&pipe, cmd.c_str (), " w" ); err != 0 )
214
- return err;
215
- if (!data.empty ())
216
- if (auto err = pipe_flush (&pipe, data); err != 0 )
217
- return err;
218
- if (auto err = pipe_close (&pipe); err != 0 )
219
- return err;
220
- return 0 ;
221
- }
222
-
223
- int shell_read (const std::string &cmd, std::string &data)
224
- {
225
- auto pipe = ProcPipe{};
226
- if (auto err = pipe_open (&pipe, cmd.c_str (), " r" ); err != 0 )
227
- return err;
222
+ if (!opened ())
223
+ return error (EINVAL, " ipipe::read" );
228
224
data.clear ();
229
225
auto buffer = std::array<char , 128 >{};
230
- while (!std::feof (pipe.file ) && !std::ferror (pipe.file )) {
231
- auto count = std::fread (buffer.data (), sizeof (char ), buffer.size (), pipe.file );
226
+ while (!std::feof (file_) && !std::ferror (file_)) {
227
+ auto count =
228
+ std::fread (buffer.data (), sizeof (char ), buffer.size (), file_);
232
229
if (count > 0 )
233
230
data.append (buffer.data (), count);
234
231
}
235
- if (auto err = pipe_close (&pipe); err != 0 )
236
- return err ;
232
+ if (std::ferror (file_) )
233
+ return error (EIO, " fread " ) ;
237
234
return 0 ;
238
235
}
236
+
237
+ int shell_write (const std::string &cmd, std::string &data)
238
+ {
239
+ auto pipe = opipe{};
240
+ if (auto err = pipe.open (cmd); err != 0 )
241
+ return err;
242
+ return pipe.write (data);
243
+ }
244
+
245
+ int shell_read (const std::string &cmd, std::string &data)
246
+ {
247
+ auto pipe = ipipe{};
248
+ if (auto err = pipe.open (cmd); err != 0 )
249
+ return err;
250
+ return pipe.read (data);
251
+ }
0 commit comments