Skip to content

Commit

Permalink
refactor: popen and pclose error reporting
Browse files Browse the repository at this point in the history
* Set errno for popen and pclose for WIN32 just like POSIX does
(at least mingw64+wine seems to decode the error message without doing anything extra)

* A work around for popen/pclose: complicated but works

* Refactored popen/pclose into custom implementation and added c++ wrappers

* Removed exceptions and cleaned up process pipe api

* Refactored popen/pclose into C++ class

* Fixed trivial issues for Linux

* Simplified the move semantics by avoiding virtual functions

* Moved error reporting into common_pipe

* Simplified open and close in case the pipe is already opened/closed

* Fixed error handling for MSVC, which very weird:
MSVC rejects strerror, suggest to use "secure" strerror_s, but then does not supply strerrorlen_s
GCC does not provide strerror_s, looks like strerror is good enough there.

* MSVC is not following any standards
  • Loading branch information
mikucionisaau authored Oct 23, 2024
1 parent e4ce497 commit 4afebf1
Show file tree
Hide file tree
Showing 5 changed files with 354 additions and 144 deletions.
47 changes: 21 additions & 26 deletions source/matplot/backend/gnuplot.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@

#include "gnuplot.h"

#include <cstdlib>
#include <iostream>
#include <matplot/util/common.h>
#include <matplot/util/popen.h>
#include <iostream>
#include <regex>
#include <thread>
#include <cstring>
#include <cstdlib>

#ifdef __has_include
#if __has_include(<filesystem>)
Expand Down Expand Up @@ -70,17 +71,18 @@ namespace matplot::backend {
}

// Open the gnuplot pipe_
int perr;
if constexpr (windows_should_persist_by_default) {
pipe_ = POPEN("gnuplot --persist", "w");
perr = pipe_.open("gnuplot --persist");
} else {
pipe_ = POPEN("gnuplot", "w");
perr = pipe_.open("gnuplot");
}

// Check if everything is OK
if (!pipe_) {
std::cerr << "Opening the gnuplot pipe_ failed!" << std::endl;
std::cerr
<< "Please install gnuplot 5.2.6+: http://www.gnuplot.info"
if (perr != 0 || !pipe_.opened()) {
std::cerr << "Opening the gnuplot failed: ";
std::cerr << pipe_.error() << std::endl;
std::cerr << "Please install gnuplot 5.2.6+: http://www.gnuplot.info"
<< std::endl;
}
}
Expand All @@ -97,9 +99,6 @@ namespace matplot::backend {
flush_commands();
run_command("exit");
flush_commands();
if (pipe_) {
PCLOSE(pipe_);
}
}

bool gnuplot::is_interactive() { return output_.empty(); }
Expand Down Expand Up @@ -281,8 +280,7 @@ namespace matplot::backend {
if constexpr (dont_let_it_close_too_fast) {
last_flush_ = std::chrono::high_resolution_clock::now();
}
fputs("\n", pipe_);
fflush(pipe_);
pipe_.flush("\n");
if constexpr (trace_commands) {
std::cout << "\n\n\n\n" << std::endl;
}
Expand All @@ -294,19 +292,19 @@ namespace matplot::backend {
}

void gnuplot::run_command(const std::string &command) {
if (!pipe_) {
if (!pipe_.opened()) {
return;
}
size_t pipe_capacity = gnuplot_pipe_capacity(pipe_);
size_t pipe_capacity = gnuplot_pipe_capacity(pipe_.file());
if (command.size() + bytes_in_pipe_ > pipe_capacity) {
flush_commands();
bytes_in_pipe_ = 0;
}
if (!command.empty()) {
fputs(command.c_str(), pipe_);
pipe_.write(command);
}
// fputs("; ", pipe_);
fputs("\n", pipe_);
// proc_write(&pipe_, "; ");
pipe_.write("\n");
bytes_in_pipe_ += command.size();
if constexpr (trace_commands) {
std::cout << command << std::endl;
Expand All @@ -323,11 +321,9 @@ namespace matplot::backend {
static std::string terminal_type;
const bool dont_know_term_type = terminal_type.empty();
if (dont_know_term_type) {
terminal_type =
run_and_get_output("gnuplot -e \"show terminal\" 2>&1");
terminal_type = std::regex_replace(
terminal_type, std::regex("[^]*terminal type is ([^ ]+)[^]*"),
"$1");
terminal_type = run_and_get_output("gnuplot -e \"show terminal\" 2>&1");
terminal_type = std::regex_replace(terminal_type,
std::regex("[^]*terminal type is ([^ ]+)[^]*"), "$1");
const bool still_dont_know_term_type = terminal_type.empty();
if (still_dont_know_term_type) {
terminal_type = "qt";
Expand All @@ -337,9 +333,8 @@ namespace matplot::backend {
}

bool gnuplot::terminal_is_available(std::string_view term) {
std::string msg =
run_and_get_output("gnuplot -e \"set terminal " +
std::string(term.data()) + "\" 2>&1");
std::string msg = run_and_get_output("gnuplot -e \"set terminal " +
std::string(term.data()) + "\" 2>&1");
return msg.empty();
}

Expand Down
5 changes: 3 additions & 2 deletions source/matplot/backend/gnuplot.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

#include <matplot/detail/config.h>
#include <matplot/backend/backend_interface.h>
#include <matplot/util/popen.h>
#include <array>
#include <chrono>
#include <tuple>
Expand Down Expand Up @@ -115,8 +116,8 @@ namespace matplot::backend {
}

private:
// Pipe to gnuplot process
FILE *pipe_;
// Process pipe to gnuplot
opipe pipe_;

// How many bytes we put in the pipe
size_t bytes_in_pipe_{0};
Expand Down
22 changes: 3 additions & 19 deletions source/matplot/util/common.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,26 +50,10 @@ namespace matplot {
iequals(str, "no");
}

struct pipe_deleter {
int operator()(FILE* pipe) const {
if (int status = PCLOSE(pipe); status != -1)
return status;
throw std::system_error{errno, std::system_category(), "pclose"};
}
};

std::string run_and_get_output(const std::string &cmd) {
std::unique_ptr<FILE, pipe_deleter> pipe(POPEN(cmd.c_str(), "r"));
if (!pipe) {
throw std::system_error{errno, std::system_category(), cmd};
}
std::array<char, 128> buffer{};
std::string result;
while (fgets(buffer.data(), static_cast<int>(buffer.size()),
pipe.get()) != nullptr) {
result += buffer.data();
}
return result;
auto res = std::string{};
shell_read(cmd, res);
return res;
}

std::string escape(std::string_view label) {
Expand Down
Loading

0 comments on commit 4afebf1

Please sign in to comment.