diff options
-rw-r--r-- | libbutl/command.cxx | 118 | ||||
-rw-r--r-- | libbutl/command.mxx | 9 | ||||
-rw-r--r-- | tests/command/driver.cxx | 7 | ||||
-rw-r--r-- | tests/command/testscript | 32 |
4 files changed, 124 insertions, 42 deletions
diff --git a/libbutl/command.cxx b/libbutl/command.cxx index 923d595..a344790 100644 --- a/libbutl/command.cxx +++ b/libbutl/command.cxx @@ -14,10 +14,11 @@ #include <cstddef> #include <functional> -#include <ios> // ios::failure +#include <ios> // ios::failure #include <vector> -#include <utility> // move() -#include <stdexcept> // invalid_argument +#include <utility> // move() +#include <stdexcept> // invalid_argument +#include <system_error> #endif // Other includes. @@ -35,9 +36,11 @@ import butl.process; import butl.optional; #endif +import butl.builtin; import butl.fdstream; import butl.string_parser; #else +#include <libbutl/builtin.mxx> #include <libbutl/fdstream.mxx> #include <libbutl/string-parser.mxx> #endif @@ -187,12 +190,6 @@ namespace butl } } - // Prepare the process environment. - // - // Note: cwd passed to process_env() may not be a temporary object. - // - process_env pe (prog, cwd, env ? env->vars : nullptr); - // Open the redirect file descriptor, if specified. // // Intercept the exception to make the error description more informative. @@ -231,39 +228,88 @@ namespace butl msg.c_str ()); } - // Finally, run the process. - // - // If the callback is specified, then intercept its call, appending the - // stdout redirect to the arguments list, if present. - // - return process_run_callback ( - [&callback, &redir, redir_append] (const char* const args[], size_t n) + builtin_function* bf (builtins.find (prog)); + + if (bf != nullptr) // Execute the builtin. + { + if (callback) { - if (callback) + // Build the complete arguments list, appending the stdout redirect, + // if present. + // + vector<const char*> elems ({prog.c_str ()}); + for (const auto& a: args) + elems.push_back (a.c_str ()); + + string rd; + if (redir) { - if (redir) - { - vector<const char*> elems (args, args + n); - string rd ((redir_append ? ">>" : ">") + redir->string ()); + rd = (redir_append ? ">>" : ">"); + rd += redir->string (); - // Inject the redirect prior to the trailing NULL. - // - assert (n > 0); + elems.push_back (rd.c_str ()); + } - elems.insert (elems.begin () + n - 1, rd.c_str ()); + elems.push_back (nullptr); - callback (elems.data (), elems.size ()); - } - else + callback (elems.data (), elems.size ()); + } + + // Finally, run the builtin. + // + uint8_t r; // Storage. + builtin_callbacks cb; + + builtin b (bf (r, + args, + nullfd /* stdin */, + move (rd) /* stdout */, + nullfd /* stderr */, + cwd, + cb)); + + return process_exit (b.wait ()); + } + else // Execute the program. + { + // Prepare the process environment. + // + // Note: cwd passed to process_env() may not be a temporary object. + // + process_env pe (prog, cwd, env ? env->vars : nullptr); + + // Finally, run the process. + // + // If the callback is specified, then intercept its call, appending the + // stdout redirect to the arguments list, if present. + // + return process_run_callback ( + [&callback, &redir, redir_append] (const char* const args[], size_t n) + { + if (callback) { - callback (args, n); + if (redir) + { + vector<const char*> elems (args, args + n); + string rd ((redir_append ? ">>" : ">") + redir->string ()); + + // Inject the redirect prior to the trailing NULL. + // + assert (n > 0); + + elems.insert (elems.begin () + n - 1, rd.c_str ()); + + callback (elems.data (), elems.size ()); + } + else + callback (args, n); } - } - }, - 0 /* stdin */, - redir ? rd.get () : 1 /* stdout */, - 2 /* stderr */, - pe, - args); + }, + 0 /* stdin */, + redir ? rd.get () : 1 /* stdout */, + 2 /* stderr */, + pe, + args); + } } } diff --git a/libbutl/command.mxx b/libbutl/command.mxx index e2a9737..0e6617b 100644 --- a/libbutl/command.mxx +++ b/libbutl/command.mxx @@ -31,10 +31,11 @@ import butl.optional; LIBBUTL_MODEXPORT namespace butl { - // Run a process, interpreting the command line as whitespace-separated, - // potentially quoted program path, arguments, and redirects. Throw - // std::invalid_argument on the parsing error, ios::failure on the - // underlying OS error, and process_error on the process running error. + // Run a process or a builtin, interpreting the command line as + // whitespace-separated, potentially quoted program path/builtin name, + // arguments, and redirects. Throw std::invalid_argument on the parsing + // error, ios::failure on the underlying OS error, process_error on the + // process running error and std::system_error on the builtin running error. // // The process environment path is unused and must point to the empty // process path. diff --git a/tests/command/driver.cxx b/tests/command/driver.cxx index 39d38aa..ef07962 100644 --- a/tests/command/driver.cxx +++ b/tests/command/driver.cxx @@ -9,7 +9,8 @@ #include <string> #include <vector> #include <iostream> -#include <stdexcept> // invalid_argument +#include <stdexcept> // invalid_argument +#include <system_error> #endif // Other includes. @@ -245,7 +246,9 @@ main (int argc, const char* argv[]) cerr << e << endl; return 1; } - catch (const process_error& e) + // Also handles process_error exception (derived from system_error). + // + catch (const system_error& e) { cerr << e << endl; return 1; diff --git a/tests/command/testscript b/tests/command/testscript index bffe621..db9bb5c 100644 --- a/tests/command/testscript +++ b/tests/command/testscript @@ -153,3 +153,35 @@ end : $* "'$0' -C -S 10" 2>/~'%.+ exited with code 10%' == 10 } + +: builtin +: +{ + : no-cwd + : + { + $* 'touch a' &a; + test -f a + } + + : cwd + : + { + mkdir a; + $* -d a 'touch b' &a/b; + test -f a/b + } + + : redirect + : + { + $* 'echo abc >a' &a; + cat a >'abc' + } + + : callback + : + { + $* -p 'echo abc >a' >'echo abc >a' &a + } +} |