Skip to content

Commit 1ef2019

Browse files
committed
Removed exceptions and cleaned up process pipe api
1 parent 45736a1 commit 1ef2019

File tree

4 files changed

+95
-70
lines changed

4 files changed

+95
-70
lines changed

source/matplot/backend/gnuplot.cpp

Lines changed: 17 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -72,16 +72,16 @@ namespace matplot::backend {
7272
// Open the gnuplot pipe_
7373
int perr;
7474
if constexpr (windows_should_persist_by_default) {
75-
perr = popen2("gnuplot --persist", "w", &pipe_);
75+
perr = pipe_open(&pipe_, "gnuplot --persist", "w");
7676
} else {
77-
perr = popen2("gnuplot", "w", &pipe_);
77+
perr = pipe_open(&pipe_, "gnuplot", "w");
7878
}
7979

8080
// Check if everything is OK
81-
if (perr != 0) {
82-
std::cerr << "Opening the gnuplot pipe_ failed: " std::strerror(perr) << std::endl;
83-
std::cerr
84-
<< "Please install gnuplot 5.2.6+: http://www.gnuplot.info"
81+
if (perr != 0 || !pipe_is_valid(pipe_)) {
82+
std::cerr << "Opening the gnuplot pipe_ failed: "
83+
<< std::strerror(perr) << std::endl;
84+
std::cerr << "Please install gnuplot 5.2.6+: http://www.gnuplot.info"
8585
<< std::endl;
8686
}
8787
}
@@ -98,8 +98,8 @@ namespace matplot::backend {
9898
flush_commands();
9999
run_command("exit");
100100
flush_commands();
101-
if (pipe_.file != nullptr) {
102-
pclose2(&pipe_);
101+
if (pipe_is_valid(pipe_)) {
102+
pipe_close(&pipe_);
103103
}
104104
}
105105

@@ -282,7 +282,7 @@ namespace matplot::backend {
282282
if constexpr (dont_let_it_close_too_fast) {
283283
last_flush_ = std::chrono::high_resolution_clock::now();
284284
}
285-
proc_flush(&pipe_, "\n");
285+
pipe_flush(&pipe_, "\n");
286286
if constexpr (trace_commands) {
287287
std::cout << "\n\n\n\n" << std::endl;
288288
}
@@ -294,7 +294,7 @@ namespace matplot::backend {
294294
}
295295

296296
void gnuplot::run_command(const std::string &command) {
297-
if (pipe_.file != nullptr) {
297+
if (!pipe_is_valid(pipe_)) {
298298
return;
299299
}
300300
size_t pipe_capacity = gnuplot_pipe_capacity(pipe_.file);
@@ -303,10 +303,10 @@ namespace matplot::backend {
303303
bytes_in_pipe_ = 0;
304304
}
305305
if (!command.empty()) {
306-
proc_write(&pipe_, command);
306+
pipe_write(&pipe_, command);
307307
}
308308
// proc_write(&pipe_, "; ");
309-
proc_write(&pipe_, "\n");
309+
pipe_write(&pipe_, "\n");
310310
bytes_in_pipe_ += command.size();
311311
if constexpr (trace_commands) {
312312
std::cout << command << std::endl;
@@ -323,11 +323,9 @@ namespace matplot::backend {
323323
static std::string terminal_type;
324324
const bool dont_know_term_type = terminal_type.empty();
325325
if (dont_know_term_type) {
326-
terminal_type =
327-
run_and_get_output("gnuplot -e \"show terminal\" 2>&1");
328-
terminal_type = std::regex_replace(
329-
terminal_type, std::regex("[^]*terminal type is ([^ ]+)[^]*"),
330-
"$1");
326+
terminal_type = run_and_get_output("gnuplot -e \"show terminal\" 2>&1");
327+
terminal_type = std::regex_replace(terminal_type,
328+
std::regex("[^]*terminal type is ([^ ]+)[^]*"), "$1");
331329
const bool still_dont_know_term_type = terminal_type.empty();
332330
if (still_dont_know_term_type) {
333331
terminal_type = "qt";
@@ -337,9 +335,8 @@ namespace matplot::backend {
337335
}
338336

339337
bool gnuplot::terminal_is_available(std::string_view term) {
340-
std::string msg =
341-
run_and_get_output("gnuplot -e \"set terminal " +
342-
std::string(term.data()) + "\" 2>&1");
338+
std::string msg = run_and_get_output("gnuplot -e \"set terminal " +
339+
std::string(term.data()) + "\" 2>&1");
343340
return msg.empty();
344341
}
345342

source/matplot/util/common.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,9 @@ namespace matplot {
5151
}
5252

5353
std::string run_and_get_output(const std::string &cmd) {
54-
return shell_read(cmd);
54+
auto res = std::string{};
55+
shell_read(cmd, res);
56+
return res;
5557
}
5658

5759
std::string escape(std::string_view label) {

source/matplot/util/popen.cpp

Lines changed: 55 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
#include <io.h>
77

8-
int popen2(const char *cmd, const char* mode, ProcPipe *pipe)
8+
int pipe_open(ProcPipe *pipe, const char *cmd, const char *mode)
99
{
1010
if (cmd == nullptr || mode == nullptr || (mode[0] != 'r' && mode[0] != 'w') ||
1111
pipe == nullptr)
@@ -88,11 +88,11 @@ int popen2(const char *cmd, const char* mode, ProcPipe *pipe)
8888
return 0;
8989
}
9090

91-
int pclose2(ProcPipe *pipe, int *exit_code)
91+
int pipe_close(ProcPipe *pipe, int *exit_code)
9292
{
9393
// The following does not work for GetExitCodeProcess:
9494
// HANDLE hFile = (HANDLE)_get_osfhandle(_fileno(file));
95-
if (pipe == nullptr || pipe->file == nullptr)
95+
if (!pipe_is_valid(pipe))
9696
return EINVAL;
9797
// Close the pipe to process:
9898
fclose(pipe->file);
@@ -123,7 +123,7 @@ int pclose2(ProcPipe *pipe, int *exit_code)
123123
#include <cerrno>
124124
#include <sys/wait.h> // waitpid
125125

126-
int popen2(const char *cmd, const char *mode, ProcPipe *p)
126+
int pipe_open(ProcPipe *p, const char *cmd, const char *mode)
127127
{
128128
constexpr auto READ = 0u;
129129
constexpr auto WRITE = 1u;
@@ -161,9 +161,9 @@ int popen2(const char *cmd, const char *mode, ProcPipe *p)
161161
}
162162

163163
/// Closes the pipe opened by popen2 and waits for termination
164-
int pclose2(ProcPipe *pipe, int *exit_code)
164+
int pipe_close(ProcPipe *pipe, int *exit_code)
165165
{
166-
if (pipe == nullptr || pipe->file == nullptr)
166+
if (!proc_is_good(pipe))
167167
return EINVAL;
168168
fclose(pipe->file);
169169
while (waitpid(pipe->pid, exit_code, 0) == -1) {
@@ -172,35 +172,67 @@ int pclose2(ProcPipe *pipe, int *exit_code)
172172
break;
173173
}
174174
}
175+
pipe->file = nullptr;
175176
return 0;
176177
}
177178

178179
#endif // POSIX implementation
179180

180-
void shell_write(const std::string &cmd, std::string_view input)
181+
int pipe_write(ProcPipe *p, std::string_view data)
182+
{
183+
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);
187+
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;
192+
}
193+
if (auto res = std::fflush(p->file); res != 0)
194+
return errno;
195+
return 0;
196+
}
197+
198+
int pipe_flush(ProcPipe *p, std::string_view data)
199+
{
200+
if (!pipe_is_valid(p))
201+
return EINVAL;
202+
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;
207+
return 0;
208+
}
209+
210+
int shell_write(const std::string &cmd, std::string_view data)
181211
{
182212
auto pipe = ProcPipe{};
183-
if (auto e = popen2(cmd.c_str(), "w", &pipe); e != 0)
184-
throw std::system_error{e, std::system_category(), "popen2"};
185-
if (!input.empty())
186-
proc_flush(&pipe, input);
187-
if (auto e = pclose2(&pipe); e != 0)
188-
throw std::system_error{e, std::system_category(), "pclose2"};
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;
189221
}
190222

191-
std::string shell_read(const std::string &cmd)
223+
int shell_read(const std::string &cmd, std::string &data)
192224
{
193225
auto pipe = ProcPipe{};
194-
if (auto e = popen2(cmd.c_str(), "r", &pipe); e != 0)
195-
throw std::system_error{e, std::system_category(), "popen2"};
196-
auto res = std::string{};
226+
if (auto err = pipe_open(&pipe, cmd.c_str(), "r"); err != 0)
227+
return err;
228+
data.clear();
197229
auto buffer = std::array<char, 128>{};
198-
while (!feof(pipe.file) && !ferror(pipe.file)) {
199-
auto count = fread(buffer.data(), sizeof(char), buffer.size(), pipe.file);
230+
while (!std::feof(pipe.file) && !std::ferror(pipe.file)) {
231+
auto count = std::fread(buffer.data(), sizeof(char), buffer.size(), pipe.file);
200232
if (count > 0)
201-
res.append(buffer.data(), count);
233+
data.append(buffer.data(), count);
202234
}
203-
if (auto e = pclose2(&pipe); e != 0)
204-
throw std::system_error{e, std::system_category(), "pclose2"};
205-
return res;
235+
if (auto err = pipe_close(&pipe); err != 0)
236+
return err;
237+
return 0;
206238
}

source/matplot/util/popen.h

Lines changed: 20 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -43,49 +43,43 @@ struct ProcPipe
4343
* @param pipe the piped file and child process handle
4444
* @returns 0 upon success and system error number upon failure
4545
*/
46-
int popen2(const char *command, const char *mode, ProcPipe *pipe);
46+
int pipe_open(ProcPipe *pipe, const char *command, const char *mode);
47+
48+
/// Return true if pipe represents a valid handle
49+
inline bool pipe_is_valid(ProcPipe *pipe)
50+
{
51+
return pipe != nullptr && pipe->file != nullptr;
52+
}
4753

4854
/** Closes the pipe opened by popen2 and waits for the process to terminate.
4955
* @param pipe the handle to close and wait for exit
5056
* @param exit_code to store the process exit code
5157
* @returns 0 upon success and system error number (errno) upon failure
5258
*/
53-
int pclose2(ProcPipe *pipe, int *exit_code = nullptr);
59+
int pipe_close(ProcPipe *pipe, int *exit_code = nullptr);
5460

5561
#ifdef __cplusplus
5662

5763
#include <string>
5864
#include <string_view>
59-
#include <system_error>
6065

61-
/// Writes data into the process pipe without flushing
62-
inline void proc_write(ProcPipe *p, std::string_view data)
66+
/// Return true if pipe represents a valid handle
67+
inline bool pipe_is_valid(ProcPipe &pipe)
6368
{
64-
constexpr auto CSIZE = sizeof(std::string_view::value_type);
65-
if (auto sz = fwrite(data.data(), CSIZE, data.size(), p->file); sz != data.size()) {
66-
if (auto e = ferror(p->file); e != 0)
67-
throw std::runtime_error{"fwrite failed after "+std::to_string(sz)};
68-
if (auto e = feof(p->file); e != 0)
69-
throw std::runtime_error{"fwrite reached end-of-file after "+std::to_string(sz)};
70-
}
71-
if (auto e = fflush(p->file); e != 0)
72-
throw std::system_error{errno, std::system_category(), "fflush"};
69+
return pipe.file != nullptr;
7370
}
7471

75-
/// Writes data into process pipe followed by a flush
76-
inline void proc_flush(ProcPipe *p, std::string_view data = {})
77-
{
78-
if (!data.empty())
79-
proc_write(p, data);
80-
if (auto e = fflush(p->file); e != 0)
81-
throw std::system_error{errno, std::system_category(), "fflush"};
82-
}
72+
/// Writes data into the process pipe without flushing, returns errno if failed
73+
int pipe_write(ProcPipe *pipe, std::string_view data);
74+
75+
/// Writes data into the process pipe followed by a flush, returns errno if failed
76+
int pipe_flush(ProcPipe *pipe, std::string_view data = {});
8377

84-
/// Launches a shell command, writes data to its input stream and waits for completion.
85-
void shell_write(const std::string &cmd, std::string_view data = {});
78+
/// Launches the shell command, writes data to input stream and waits for completion, returns errno if failed
79+
int shell_write(const std::string &command, std::string_view data = {});
8680

87-
/// Launches a shell command, reads data from its output stream and waits for completion.
88-
std::string shell_read(const std::string &cmd);
81+
/// Launches the shell command, reads data from output stream and waits for completion, returns errno if failed
82+
int shell_read(const std::string &command, std::string& data);
8983

9084
#endif // __cplusplus
9185

0 commit comments

Comments
 (0)