From c4292d57e2e67dfcdac9004f8edb229976f6669a Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Fri, 13 Sep 2019 13:33:02 +0300 Subject: Add builtins support to command running API --- libbutl/command.cxx | 118 ++++++++++++++++++++++++++++++++--------------- libbutl/command.mxx | 9 ++-- tests/command/driver.cxx | 7 ++- 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 #include -#include // ios::failure +#include // ios::failure #include -#include // move() -#include // invalid_argument +#include // move() +#include // invalid_argument +#include #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 #include #include #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 elems ({prog.c_str ()}); + for (const auto& a: args) + elems.push_back (a.c_str ()); + + string rd; + if (redir) { - if (redir) - { - vector 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 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 #include #include -#include // invalid_argument +#include // invalid_argument +#include #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 + } +} -- cgit v1.1