diff --git a/NEWS b/NEWS index 8e58d6e..8d2d1ed 100644 --- a/NEWS +++ b/NEWS @@ -6,6 +6,7 @@ For more details see the git log. * package name is now libdivvun * normaliser doesn't add subreadings +* pipeline supports sh ## Notable changes in 0.3.10 diff --git a/src/pipeline.cpp b/src/pipeline.cpp index d3d5c12..a9c1dcd 100644 --- a/src/pipeline.cpp +++ b/src/pipeline.cpp @@ -179,6 +179,62 @@ const MsgMap& SuggestCmd::getMsgs() { return suggest->msgs; } +ShCmd::ShCmd( + const string& prog, const std::vector& args, bool verbose) { + argv = (char**)malloc(sizeof(char*) * (args.size() * 2 + 2)); + size_t i = 0; + for (auto arg : args) { + argv[i++] = strdup(arg.c_str()); + } + argv[i] = NULL; +} + + +ShCmd::~ShCmd() { + size_t i = 0; + while (argv[i] != NULL) { + free(argv[i++]); + } + free(argv); +} + +void ShCmd::run(stringstream& input, stringstream& output) const { + int fds[2]; + if (pipe(fds) == -1) { + throw std::runtime_error("pipe failed "); + } + pid_t pid = fork(); + if (pid == -1) { + throw std::runtime_error("fork failed "); + } + else if (pid == 0) { + close(fds[1]); // close write + dup2(fds[0], STDIN_FILENO); // redirect + close(fds[0]); // close read + if (execvp(argv[0], argv) < 0) { + throw std::runtime_error("exec failed "); + } + throw std::runtime_error("child process failed "); + } + else { + close(fds[0]); // close read + char buffin[8192]; + while (input.getline(buffin, sizeof(buffin))) { + size_t readn = input.gcount(); + if (write(fds[1], buffin, readn) == -1) { + throw std::runtime_error("write failed"); + } + } + close(fds[1]); // close write + char buffout[8192]; + ssize_t count; + while ((count = read(fds[1], buffout, sizeof(buffout))) > 0) { + output.write(buffout, count); + } + } + //wait(0); // wait +} + Pipeline::Pipeline(LocalisedPrefs prefs_, vector> cmds_, SuggestCmd* suggestcmd_, bool verbose_, bool trace_) @@ -333,8 +389,14 @@ Pipeline Pipeline::mkPipeline(const unique_ptr& ar_spec, suggestcmd = s; } else if (name == u"sh") { - // const auto& prog = fromUtf8(cmd.attribute("prog").value()); - // cmds.emplace_back(new ShCmd(prog, args, verbose)); + const auto& prog = cmd.attribute("prog").value(); + std::vector argv; + for (const pugi::xml_node& arg : cmd.children()) { + if (strcmp(arg.name(), "arg") == 0) { + argv.push_back(arg.text().get()); + } + } + cmds.emplace_back(new ShCmd(prog, argv, verbose)); } else if (name == u"prefs") { parsePrefs(prefs, cmd); @@ -383,9 +445,9 @@ Pipeline Pipeline::mkPipeline(const unique_ptr& spec, cmd.attribute("max-weight").as_float(5000.0), cmd.attribute("max-unknown-rate").as_float(0.4), verbose)); #else - throw std::runtime_error( - "libdivvun: ERROR: Tried to run pipeline with cgspell, but was " - "compiled without cgspell support!"); + throw std::runtime_error("libdivvun: ERROR: Tried to run " + "pipeline with cgspell, but was " + "compiled without cgspell support!"); #endif } else if ((name == u"normalise") || (name == u"normalize")) { @@ -423,8 +485,15 @@ Pipeline Pipeline::mkPipeline(const unique_ptr& spec, suggestcmd = s; } else if (name == u"sh") { - // const auto& prog = fromUtf8(cmd.attribute("prog").value()); - // cmds.emplace_back(new ShCmd(prog, args, verbose)); + const auto& prog = cmd.attribute("prog").value(); + std::vector argv; + argv.push_back(prog); + for (const pugi::xml_node& arg : cmd.children()) { + if (strcmp(arg.name(), "arg") == 0) { + argv.push_back(arg.text().get()); + } + } + cmds.emplace_back(new ShCmd(prog, argv, verbose)); } else if (name == u"prefs") { parsePrefs(prefs, cmd); @@ -491,5 +560,4 @@ void Pipeline::setIncludes(const std::set& includes) { "a SuggestCmd"); } } - } diff --git a/src/pipeline.hpp b/src/pipeline.hpp index 3d97a4b..2c0e423 100644 --- a/src/pipeline.hpp +++ b/src/pipeline.hpp @@ -245,6 +245,15 @@ class SuggestCmd : public PipeCmd { unique_ptr suggest; }; +class ShCmd : public PipeCmd { +public: + ShCmd(const string& prog, const std::vector& args, bool verbose); + void run(stringstream& input, stringstream& output) const override; + ~ShCmd() override; + +private: + char** argv; +}; inline void parsePrefs(LocalisedPrefs& prefs, const pugi::xml_node& cmd) { for (const pugi::xml_node& pref : cmd.children()) { diff --git a/src/pipespec.cpp b/src/pipespec.cpp index f9fbaad..071fa02 100644 --- a/src/pipespec.cpp +++ b/src/pipespec.cpp @@ -183,8 +183,13 @@ void validatePipespecCmd( } } else if (name == "sh") { - throw std::runtime_error(" command not implemented yet!"); - // const auto& prog = fromUtf8(cmd.attribute("prog").value()); + string prog = cmd.attribute("prog").value(); + // should also hard code list of acceptable commands for security + int rv = std::system((string("which ") + prog + ">/dev/null").c_str()); + if (rv != 0) { + throw std::runtime_error( + "OS is missing a program needed by pipe: " + prog); + } } else if (name == "prefs") { // pass