diff options
Diffstat (limited to 'Build/source/texk/dvisvgm/dvisvgm-src/src/Process.cpp')
-rw-r--r-- | Build/source/texk/dvisvgm/dvisvgm-src/src/Process.cpp | 158 |
1 files changed, 113 insertions, 45 deletions
diff --git a/Build/source/texk/dvisvgm/dvisvgm-src/src/Process.cpp b/Build/source/texk/dvisvgm/dvisvgm-src/src/Process.cpp index f89d5024181..72465c0c73c 100644 --- a/Build/source/texk/dvisvgm/dvisvgm-src/src/Process.cpp +++ b/Build/source/texk/dvisvgm/dvisvgm-src/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 ¶mstr, vector<const char*> ¶ms) { * @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()); |