Skip to content

Commit 315f488

Browse files
committed
preliminary <sh> implementation
1 parent b6a1388 commit 315f488

File tree

4 files changed

+93
-10
lines changed

4 files changed

+93
-10
lines changed

Diff for: NEWS

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ For more details see the git log.
66

77
* package name is now libdivvun
88
* normaliser doesn't add subreadings
9+
* pipeline supports sh
910

1011
## Notable changes in 0.3.10
1112

Diff for: src/pipeline.cpp

+76-8
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,62 @@ const MsgMap& SuggestCmd::getMsgs() {
179179
return suggest->msgs;
180180
}
181181

182+
ShCmd::ShCmd(
183+
const string& prog, const std::vector<string>& args, bool verbose) {
184+
argv = (char**)malloc(sizeof(char*) * (args.size() * 2 + 2));
185+
size_t i = 0;
186+
for (auto arg : args) {
187+
argv[i++] = strdup(arg.c_str());
188+
}
189+
argv[i] = NULL;
190+
}
191+
192+
193+
ShCmd::~ShCmd() {
194+
size_t i = 0;
195+
while (argv[i] != NULL) {
196+
free(argv[i++]);
197+
}
198+
free(argv);
199+
}
200+
201+
void ShCmd::run(stringstream& input, stringstream& output) const {
202+
int fds[2];
203+
if (pipe(fds) == -1) {
204+
throw std::runtime_error("pipe failed ");
205+
}
206+
pid_t pid = fork();
207+
if (pid == -1) {
208+
throw std::runtime_error("fork failed ");
209+
}
210+
else if (pid == 0) {
211+
close(fds[1]); // close write
212+
dup2(fds[0], STDIN_FILENO); // redirect
213+
close(fds[0]); // close read
214+
if (execvp(argv[0], argv) < 0) {
215+
throw std::runtime_error("exec failed ");
216+
}
217+
throw std::runtime_error("child process failed ");
218+
}
219+
else {
220+
close(fds[0]); // close read
221+
char buffin[8192];
222+
while (input.getline(buffin, sizeof(buffin))) {
223+
size_t readn = input.gcount();
224+
if (write(fds[1], buffin, readn) == -1) {
225+
throw std::runtime_error("write failed");
226+
}
227+
}
228+
close(fds[1]); // close write
229+
char buffout[8192];
230+
ssize_t count;
231+
while ((count = read(fds[1], buffout, sizeof(buffout))) > 0) {
232+
output.write(buffout, count);
233+
}
234+
}
235+
//wait(0); // wait
236+
}
237+
182238

183239
Pipeline::Pipeline(LocalisedPrefs prefs_, vector<unique_ptr<PipeCmd>> cmds_,
184240
SuggestCmd* suggestcmd_, bool verbose_, bool trace_)
@@ -333,8 +389,14 @@ Pipeline Pipeline::mkPipeline(const unique_ptr<ArPipeSpec>& ar_spec,
333389
suggestcmd = s;
334390
}
335391
else if (name == u"sh") {
336-
// const auto& prog = fromUtf8(cmd.attribute("prog").value());
337-
// cmds.emplace_back(new ShCmd(prog, args, verbose));
392+
const auto& prog = cmd.attribute("prog").value();
393+
std::vector<string> argv;
394+
for (const pugi::xml_node& arg : cmd.children()) {
395+
if (strcmp(arg.name(), "arg") == 0) {
396+
argv.push_back(arg.text().get());
397+
}
398+
}
399+
cmds.emplace_back(new ShCmd(prog, argv, verbose));
338400
}
339401
else if (name == u"prefs") {
340402
parsePrefs(prefs, cmd);
@@ -383,9 +445,9 @@ Pipeline Pipeline::mkPipeline(const unique_ptr<PipeSpec>& spec,
383445
cmd.attribute("max-weight").as_float(5000.0),
384446
cmd.attribute("max-unknown-rate").as_float(0.4), verbose));
385447
#else
386-
throw std::runtime_error(
387-
"libdivvun: ERROR: Tried to run pipeline with cgspell, but was "
388-
"compiled without cgspell support!");
448+
throw std::runtime_error("libdivvun: ERROR: Tried to run "
449+
"pipeline with cgspell, but was "
450+
"compiled without cgspell support!");
389451
#endif
390452
}
391453
else if ((name == u"normalise") || (name == u"normalize")) {
@@ -423,8 +485,15 @@ Pipeline Pipeline::mkPipeline(const unique_ptr<PipeSpec>& spec,
423485
suggestcmd = s;
424486
}
425487
else if (name == u"sh") {
426-
// const auto& prog = fromUtf8(cmd.attribute("prog").value());
427-
// cmds.emplace_back(new ShCmd(prog, args, verbose));
488+
const auto& prog = cmd.attribute("prog").value();
489+
std::vector<string> argv;
490+
argv.push_back(prog);
491+
for (const pugi::xml_node& arg : cmd.children()) {
492+
if (strcmp(arg.name(), "arg") == 0) {
493+
argv.push_back(arg.text().get());
494+
}
495+
}
496+
cmds.emplace_back(new ShCmd(prog, argv, verbose));
428497
}
429498
else if (name == u"prefs") {
430499
parsePrefs(prefs, cmd);
@@ -491,5 +560,4 @@ void Pipeline::setIncludes(const std::set<ErrId>& includes) {
491560
"a SuggestCmd");
492561
}
493562
}
494-
495563
}

Diff for: src/pipeline.hpp

+9
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,15 @@ class SuggestCmd : public PipeCmd {
245245
unique_ptr<Suggest> suggest;
246246
};
247247

248+
class ShCmd : public PipeCmd {
249+
public:
250+
ShCmd(const string& prog, const std::vector<string>& args, bool verbose);
251+
void run(stringstream& input, stringstream& output) const override;
252+
~ShCmd() override;
253+
254+
private:
255+
char** argv;
256+
};
248257

249258
inline void parsePrefs(LocalisedPrefs& prefs, const pugi::xml_node& cmd) {
250259
for (const pugi::xml_node& pref : cmd.children()) {

Diff for: src/pipespec.cpp

+7-2
Original file line numberDiff line numberDiff line change
@@ -183,8 +183,13 @@ void validatePipespecCmd(
183183
}
184184
}
185185
else if (name == "sh") {
186-
throw std::runtime_error("<sh> command not implemented yet!");
187-
// const auto& prog = fromUtf8(cmd.attribute("prog").value());
186+
string prog = cmd.attribute("prog").value();
187+
// should also hard code list of acceptable commands for security
188+
int rv = std::system((string("which ") + prog + ">/dev/null").c_str());
189+
if (rv != 0) {
190+
throw std::runtime_error(
191+
"OS is missing a program needed by pipe: " + prog);
192+
}
188193
}
189194
else if (name == "prefs") {
190195
// pass

0 commit comments

Comments
 (0)