summaryrefslogtreecommitdiff
path: root/dviware/dvisvgm/src/Process.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dviware/dvisvgm/src/Process.cpp')
-rw-r--r--dviware/dvisvgm/src/Process.cpp158
1 files changed, 113 insertions, 45 deletions
diff --git a/dviware/dvisvgm/src/Process.cpp b/dviware/dvisvgm/src/Process.cpp
index f89d502418..72465c0c73 100644
--- a/dviware/dvisvgm/src/Process.cpp
+++ b/dviware/dvisvgm/src/Process.cpp
@@ -2,7 +2,7 @@
** Process.cpp **
** **
** This file is part of dvisvgm -- a fast DVI to SVG converter **
-** Copyright (C) 2005-2022 Martin Gieseking <martin.gieseking@uos.de> **
+** Copyright (C) 2005-2023 Martin Gieseking <martin.gieseking@uos.de> **
** **
** This program is free software; you can redistribute it and/or **
** modify it under the terms of the GNU General Public License as **
@@ -29,6 +29,7 @@
#endif
#include <cstdlib>
+#include <regex>
#include "FileSystem.hpp"
#include "Process.hpp"
#include "SignalHandler.hpp"
@@ -43,25 +44,60 @@ class Subprocess {
enum class State {RUNNING, FINISHED, FAILED};
public:
- Subprocess () =default;
+ Subprocess () : _rdbuf(4096) {}
Subprocess (const Subprocess&) =delete;
Subprocess (Subprocess&&) =delete;
~Subprocess ();
- bool run (const string &cmd, string params);
- bool readFromPipe (string &out);
+ bool run (const string &cmd, string params, Process::PipeFlags flags);
+ bool readFromPipe (string &out, const SearchPattern &pattern);
State state ();
+ protected:
+ string read (const SearchPattern &pattern, bool *error=nullptr);
+ size_t readFromPipe (char *buf, size_t size, bool *error=nullptr) const;
+
private:
+ vector<char> _rdbuf;
+ size_t _bufstartpos = 0;
#ifdef _WIN32
- HANDLE _pipeReadHandle=NULL; ///< handle of read end of pipe
- HANDLE _childProcHandle=NULL; ///< handle of child process
+ HANDLE _pipeReadHandle = nullptr; ///< handle of read end of pipe
+ HANDLE _childProcHandle = nullptr; ///< handle of child process
#else
- int _readfd=-1; ///< file descriptor of read end of pipe
- pid_t _pid=-1; ///< PID of the subprocess
+ int _readfd = -1; ///< file descriptor of read end of pipe
+ pid_t _pid = -1; ///< PID of the subprocess
#endif
};
+string Subprocess::read (const SearchPattern &pattern, bool *error) {
+ string result;
+ const size_t bytesRead = readFromPipe(&_rdbuf[_bufstartpos], _rdbuf.size()-_bufstartpos, error);
+ const size_t bufendpos = _bufstartpos + bytesRead;
+ smatch matches;
+ size_t matchendpos= bufendpos; // buffer position after last match
+ if (pattern.search.empty())
+ result.assign(_rdbuf.data(), matchendpos);
+ else {
+ bool matched=false;
+ regex re(pattern.search);
+ for (auto it = cregex_iterator(&_rdbuf[0], &_rdbuf[bufendpos], re); it != cregex_iterator(); ++it) {
+ result += it->format(pattern.replace, regex_constants::format_no_copy);
+ matchendpos = it->position() + it->length();
+ matched = true;
+ }
+ const size_t MAX_OVERLAP=50;
+ size_t overlappos;
+ if (matched)
+ overlappos = max(bufendpos - min(bufendpos, MAX_OVERLAP), matchendpos);
+ else
+ overlappos = matchendpos - min(MAX_OVERLAP, matchendpos);
+ copy(_rdbuf.begin()+overlappos, _rdbuf.begin()+bufendpos, _rdbuf.begin());
+ _bufstartpos = bufendpos-overlappos;
+ }
+ return result;
+}
+
+
Process::Process (string cmd, string paramstr)
: _cmd(std::move(cmd)), _paramstr(std::move(paramstr))
{
@@ -72,14 +108,20 @@ Process::Process (string cmd, string paramstr)
* @param[out] out takes the output written to stdout by the executed subprocess
* @return true if process terminated properly
* @throw SignalException if CTRL-C was pressed during execution */
-bool Process::run (string *out) {
+
+bool Process::run (string *out, PipeFlags flags) {
+ return run(out, SearchPattern(), flags);
+}
+
+
+bool Process::run (string *out, const SearchPattern &pattern, PipeFlags flags) {
Subprocess subprocess;
- if (!subprocess.run(_cmd, _paramstr))
+ if (!subprocess.run(_cmd, _paramstr, flags))
return false;
for (;;) {
if (out) {
out->clear();
- subprocess.readFromPipe(*out);
+ subprocess.readFromPipe(*out, pattern);
}
Subprocess::State state = subprocess.state();
if (state != Subprocess::State::RUNNING)
@@ -94,11 +136,11 @@ bool Process::run (string *out) {
* @param[out] out takes the output written to stdout by the executed process
* @return true if process terminated properly
* @throw SignalException if CTRL-C was pressed during execution */
-bool Process::run (const string &dir, string *out) {
+bool Process::run (const string &dir, string *out, PipeFlags flags) {
bool ret = false;
string cwd = FileSystem::getcwd();
if (FileSystem::chdir(dir)) {
- ret = run(out);
+ ret = run(out, flags);
ret &= FileSystem::chdir(cwd);
}
return ret;
@@ -110,46 +152,61 @@ bool Process::run (const string &dir, string *out) {
static inline void close_and_zero_handle (HANDLE &handle) {
CloseHandle(handle);
- handle = NULL;
+ handle = nullptr;
}
Subprocess::~Subprocess () {
- if (_pipeReadHandle != NULL)
+ if (_pipeReadHandle != nullptr)
CloseHandle(_pipeReadHandle);
- if (_childProcHandle != NULL) {
+ if (_childProcHandle != nullptr) {
TerminateProcess(_childProcHandle, 1);
CloseHandle(_childProcHandle);
}
}
-/** Retrieves output generated by child process.
+/** Retrieves output generated by the child process and optionally filters
+ * the lines by a regular expression and replaces them. If the search pattern
+ * is not empty and the line currently processed doesn't match, the line isn't
+ * appended to the output string.
* @param[out] out read output is appended to this string
+ * @param[in] searchPattern regex pattern applied to each line
+ * @param[in] replacePattern replacement for all lines matching the search pattern
* @returns false on errors */
-bool Subprocess::readFromPipe (string &out) {
+bool Subprocess::readFromPipe (string &out, const SearchPattern &pattern) {
if (!_pipeReadHandle)
return false;
-
- bool success=false;
+ bool finished=false;
bool processExited=false;
DWORD len=0;
- while (PeekNamedPipe(_pipeReadHandle, NULL, 0, NULL, &len, NULL)) { // prevent blocking
+ while (PeekNamedPipe(_pipeReadHandle, nullptr, 0, nullptr, &len, nullptr)) { // prevent blocking
if (len == 0) {
if (processExited)
break;
// process still busy
processExited = (!_childProcHandle || WaitForSingleObject(_childProcHandle, 100) != WAIT_TIMEOUT);
}
- else {
- char buf[4096];
- success = ReadFile(_pipeReadHandle, buf, sizeof(buf), &len, NULL);
- if (!success || len == 0)
- break;
- out.append(buf, len);
- }
+ string buf = read(pattern, &finished);
+ if (buf.empty())
+ break;
+ out.append(buf);
}
- return success;
+ return !finished;
+}
+
+
+/** Reads a sequence of bytes from the pipe into a buffer. The function stops reading
+ * when either all bytes have been read from the pipe or the buffer is completely filled.
+ * @param[in] buf pointer to start of the buffer
+ * @param[in] size maximal number of bytes that can be stored in the buffer
+ * @return number of bytes read */
+size_t Subprocess::readFromPipe (char *buf, size_t size, bool *error) const {
+ DWORD bytesRead;
+ bool success = ReadFile(_pipeReadHandle, buf, size, &bytesRead, nullptr);
+ if (error)
+ *error = !success;
+ return success ? bytesRead : 0;
}
@@ -157,7 +214,7 @@ bool Subprocess::readFromPipe (string &out) {
* @param[in] cmd name of command to execute
* @param[in] paramstr parameters required by command
* @returns true if child process started properly */
-bool Subprocess::run (const string &cmd, string paramstr) {
+bool Subprocess::run (const string &cmd, string paramstr, Process::PipeFlags flags) {
SECURITY_ATTRIBUTES securityAttribs;
ZeroMemory(&securityAttribs, sizeof(SECURITY_ATTRIBUTES));
securityAttribs.nLength = sizeof(SECURITY_ATTRIBUTES);
@@ -168,7 +225,7 @@ bool Subprocess::run (const string &cmd, string paramstr) {
return false;
SetHandleInformation(_pipeReadHandle, HANDLE_FLAG_INHERIT, 0);
- HANDLE nullFile = CreateFile("NUL", GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, &securityAttribs, OPEN_EXISTING, 0, NULL);
+ HANDLE nullFile = CreateFile("NUL", GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, &securityAttribs, OPEN_EXISTING, 0, nullptr);
bool success = false;
if (nullFile != INVALID_HANDLE_VALUE) {
STARTUPINFO startupInfo;
@@ -176,15 +233,15 @@ bool Subprocess::run (const string &cmd, string paramstr) {
startupInfo.cb = sizeof(STARTUPINFO);
startupInfo.dwFlags = STARTF_USESTDHANDLES;
startupInfo.hStdInput = nullFile;
- startupInfo.hStdOutput = pipeWriteHandle;
- startupInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
+ startupInfo.hStdOutput = (flags & Process::PF_STDOUT) ? pipeWriteHandle : nullFile;
+ startupInfo.hStdError = (flags & Process::PF_STDERR) ? pipeWriteHandle : nullFile;
PROCESS_INFORMATION processInfo;
ZeroMemory(&processInfo, sizeof(PROCESS_INFORMATION));
string cmdline = cmd + " " + paramstr;
// put subprocess in separate process group to prevent its termination in case of CTRL-C
- success = CreateProcess(NULL, (LPSTR)cmdline.c_str(), NULL, NULL, true, CREATE_NEW_PROCESS_GROUP, NULL, NULL, &startupInfo, &processInfo);
+ success = CreateProcess(nullptr, (LPSTR)cmdline.c_str(), nullptr, nullptr, true, CREATE_NEW_PROCESS_GROUP, nullptr, nullptr, &startupInfo, &processInfo);
if (success) {
_childProcHandle = processInfo.hProcess;
CloseHandle(processInfo.hThread);
@@ -222,22 +279,31 @@ Subprocess::~Subprocess () {
/** Retrieves output generated by child process.
* @param[out] out read output is appended to this string
* @returns false on errors */
-bool Subprocess::readFromPipe (string &out) {
+bool Subprocess::readFromPipe (string &out, const SearchPattern &pattern) {
if (_readfd < 0 || _pid < 0)
return false;
-
- char buf[1024];
- ssize_t len;
- while ((len = read(_readfd, buf, sizeof(buf))) > 0)
- out.append(buf, len);
- if (len < 0) {
+ bool finished=false;
+ for (;;) {
+ string buf = read(pattern, &finished);
+ if (buf.empty())
+ break;
+ out.append(buf);
+ }
+ if (finished) {
close(_readfd);
_readfd = -1;
}
- return len >= 0;
+ return !finished;
}
+size_t Subprocess::readFromPipe (char *buf, size_t size, bool *error) const {
+ auto len = ::read(_readfd, buf, size);
+ if (error)
+ *error = len < 0;
+ return len >= 0 ? size_t(len) : 0;
+}
+
/** Extracts whitespace-separated parameters from a string.
* @param[in,out] paramstr the parameter string
* @param[out] params vector holding pointers to the extracted parameters */
@@ -272,7 +338,7 @@ static void split_paramstr (string &paramstr, vector<const char*> &params) {
* @param[in] cmd name of command to execute or absolute path to executable
* @param[in] paramstr parameters required by the command
* @returns true if child process started properly */
-bool Subprocess::run (const string &cmd, string paramstr) {
+bool Subprocess::run (const string &cmd, string paramstr, Process::PipeFlags flags) {
int pipefd[2];
if (cmd.empty() || pipe(pipefd) < 0)
return false;
@@ -284,10 +350,12 @@ bool Subprocess::run (const string &cmd, string paramstr) {
return false;
}
if (_pid == 0) { // child process
- dup2(pipefd[1], STDOUT_FILENO); // redirect stdout to the pipe
- dup2(pipefd[1], STDERR_FILENO); // redirect stderr to the pipe
+ int devnull = open("/dev/null", O_WRONLY);
+ dup2((flags & Process::PF_STDOUT) ? pipefd[1] : devnull, STDOUT_FILENO); // redirect stdout to the pipe
+ dup2((flags & Process::PF_STDERR) ? pipefd[1] : devnull, STDERR_FILENO); // redirect stdout to the pipe
close(pipefd[0]);
close(pipefd[1]);
+ close(devnull);
vector<const char*> params;
params.push_back(cmd.c_str());