aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libbutl/command.cxx118
-rw-r--r--libbutl/command.mxx9
-rw-r--r--tests/command/driver.cxx7
-rw-r--r--tests/command/testscript32
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
+ }
+}