// file : libbutl/builtin.mxx -*- C++ -*- // license : MIT; see accompanying LICENSE file #ifndef __cpp_modules_ts #pragma once #endif // C includes. #ifndef __cpp_lib_modules_ts #include #include #include #include #include // size_t #include // move() #include // uint8_t #include #endif // Other includes. #ifdef __cpp_modules_ts export module butl.builtin; #ifdef __cpp_lib_modules_ts import std.core; import std.threading; #endif import butl.path; import butl.fdstream; import butl.timestamp; #else #include #include #include #endif #include LIBBUTL_MODEXPORT namespace butl { // A process/thread-like object representing a running builtin. // // For now, instead of allocating the result storage dynamically, we // expect it to be provided by the caller. // class builtin { public: std::uint8_t wait () {if (t_.joinable ()) t_.join (); return r_;} ~builtin () {wait ();} public: builtin (std::uint8_t& r, std::thread&& t = std::thread ()) : r_ (r), t_ (move (t)) {} builtin (builtin&&) = default; private: std::uint8_t& r_; std::thread t_; }; // Builtin execution callbacks that can be used for checking/handling the // filesystem entries being acted upon (enforcing that they are sub-entries // of some "working" directory, registering cleanups for new entries, etc) // and for providing custom implementations for some functions used by // builtins. // // Note that the filesystem paths passed to the callbacks are absolute and // normalized with directories distinguished from non-directories based on // the lexical representation (presence of the trailing directory separator; // use path::to_directory() to check). // // Also note that builtins catch any exceptions that may be thrown by the // callbacks and, if that's the case, issue diagnostics and exit with the // non-zero status. // struct builtin_callbacks { // If specified, called before (pre is true) and after (pre is false) a // new filesystem entry is created or an existing one is re-created or // updated. // using create_hook = void (const path&, bool pre); std::function create; // If specified, called before (pre is true) and after (pre is false) a // filesystem entry is moved. The force argument is true if the builtin is // executed with the --force option. // using move_hook = void (const path& from, const path& to, bool force, bool pre); std::function move; // If specified, called before (pre is true) and after (pre is false) a // filesystem entry is removed. The force argument is true if the builtin // is executed with the --force option. // using remove_hook = void (const path&, bool force, bool pre); std::function remove; // If specified, called on encountering an unknown option passing the // argument list and the position of the option in question. Return the // number of parsed arguments. // using parse_option_function = std::size_t (const std::vector&, std::size_t); std::function parse_option; // If specified, called by the sleep builtin instead of the default // implementation. // using sleep_function = void (const duration&); std::function sleep; explicit builtin_callbacks (std::function c = {}, std::function m = {}, std::function r = {}, std::function p = {}, std::function s = {}) : create (std::move (c)), move (std::move (m)), remove (std::move (r)), parse_option (std::move (p)), sleep (std::move (s)) {} explicit builtin_callbacks (std::function sl) : sleep (std::move (sl)) {} }; // Start a builtin command. Use the current process' standard streams for // the unopened in, out, and err file descriptors. Use the process' current // working directory unless an alternative is specified. Throw // std::system_error on failure. // // Note that unlike argc/argv, args don't include the program name. // using builtin_function = builtin (std::uint8_t& result, const std::vector& args, auto_fd in, auto_fd out, auto_fd err, const dir_path& cwd, const builtin_callbacks&); // Builtin function and weight. // // The weight between 0 and 2 reflects the builtin's contribution to the // containing script semantics with 0 being the lowest/ignore. Current // mapping is as follows: // // 0 - non-contributing (true, false) // 1 - non-creative (rm, rmdir, sleep, test) // 2 - creative (any builtin that may produce output) // // If the function is NULL, then the builtin has an external implementation // and should be executed by running the program with this name. // struct builtin_info { builtin_function* function; uint8_t weight; }; class builtin_map: public std::map { public: using base = std::map; using base::base; // Return NULL if not a builtin. // const builtin_info* find (const std::string& n) const { auto i (base::find (n)); return i != end () ? &i->second : nullptr; } }; LIBBUTL_SYMEXPORT extern const builtin_map builtins; }