# # # patch "cmd.hh" # from [56b25612d3b308b4da739a813764fef755332a33] # to [2492f631b8bc163a25cd6dfe03f267cfdaad908f] # # patch "cmd_automate.cc" # from [1328e65fc98bd88779b701ef3423d33ac246150e] # to [dda99f14f877d817666d59e6e8fde7791f275751] # # patch "commands.cc" # from [4c4a322cca8c3eb6760a289639a4b9a6f6819bf7] # to [01718bbf05632ce901bf2e521b386b1a450b70a5] # # patch "lua_hooks.cc" # from [1d95c581d5880c74fd9c941fd69e2564d624d2e2] # to [33536067d47c7ff32a84f75600765aef0ccb6e7c] # # patch "lua_hooks.hh" # from [0968f4973ed14ccd60fb411de351f30008f7da7e] # to [0f79afa4f23d64a221b23ef4b39766528fec8a14] # # patch "monotone.cc" # from [424f3537c4ae32633fe643e0e82bbb3b252722c7] # to [e8cf166e90778a1f4a46089915617dab0537e9fc] # ============================================================ --- cmd.hh 56b25612d3b308b4da739a813764fef755332a33 +++ cmd.hh 2492f631b8bc163a25cd6dfe03f267cfdaad908f @@ -67,6 +67,7 @@ namespace commands utf8 const & primary_name(void) const; names_set const & names(void) const; + void add_alias(const utf8 &new_name); command * parent(void) const; bool hidden(void) const; virtual std::string params(void) const; @@ -86,6 +87,7 @@ namespace commands bool has_name(utf8 const & name) const; command const * find_command(command_id const & id) const; + command * find_command(command_id const & id); std::set< command_id > complete_command(command_id const & id, command_id completed = command_id(), @@ -115,6 +117,11 @@ namespace commands void exec(app_state & app, command_id const & execid, + args_vector const & args, + std::ostream & output) const; + + void exec(app_state & app, + command_id const & execid, args_vector const & args) const; }; }; @@ -320,6 +327,7 @@ CMD_FWD_DECL(workspace); CMD_FWD_DECL(tree); CMD_FWD_DECL(variables); CMD_FWD_DECL(workspace); +CMD_FWD_DECL(user); // Local Variables: // mode: C++ ============================================================ --- cmd_automate.cc 1328e65fc98bd88779b701ef3423d33ac246150e +++ cmd_automate.cc dda99f14f877d817666d59e6e8fde7791f275751 @@ -9,15 +9,19 @@ #include "base.hh" #include +#include #include #include "cmd.hh" #include "app_state.hh" +#include "lua.hh" +#include "lua_hooks.hh" using std::istream; using std::make_pair; using std::map; using std::ostream; +using std::ostringstream; using std::pair; using std::set; using std::string; @@ -41,11 +45,20 @@ namespace commands { void automate::exec(app_state & app, command_id const & execid, - args_vector const & args) const + args_vector const & args, + std::ostream & output) const { make_io_binary(); - exec_from_automate(args, execid, app, std::cout); + exec_from_automate(args, execid, app, output); } + + void + automate::exec(app_state & app, + command_id const & execid, + args_vector const & args) const + { + exec(app, execid, args, std::cout); + } } static string const interface_version = "5.0"; @@ -315,7 +328,6 @@ struct automate_ostream : public std::os { _M_autobuf.end_cmd(); } }; - CMD_AUTOMATE(stdio, "", N_("Automates several commands in one run"), "", @@ -397,6 +409,76 @@ CMD_AUTOMATE(stdio, "", } } +LUAEXT(mtn_automate, ) +{ + args_vector args; + std::stringstream output; + app_state* app_p = get_app_state(L); + I(app_p != NULL); + I(app_p->lua.check_lua_state(L)); + + // automate_ostream os(output, app_p->opts.automate_stdio_size); + std::stringstream & os = output; + + int n = lua_gettop(L); + + E(n > 1, F("Bad input to mtn_automate() lua extension: command name is missing")); + + app_p->db.ensure_open(); + + for (int i=1; i<=n; i++) + args.push_back(arg_type(luaL_checkstring(L, i))); + + try + { + commands::command_id id; + for (args_vector::const_iterator iter = args.begin(); + iter != args.end(); iter++) + id.push_back(utf8((*iter)())); + + if (!id.empty()) + { + I(!args.empty()); + + set< commands::command_id > matches = + CMD_REF(automate)->complete_command(id); + + if (matches.size() == 0) + { + N(false, F("no completions for this command")); + } + else if (matches.size() > 1) + { + N(false, F("multiple completions possible for this command")); + } + + id = *matches.begin(); + + I(args.size() >= id.size()); + for (commands::command_id::size_type i = 0; i < id.size(); i++) + args.erase(args.begin()); + + commands::command const * cmd = CMD_REF(automate)->find_command(id); + I(cmd != NULL); + commands::automate const * acmd = reinterpret_cast< commands::automate const * >(cmd); + + acmd->exec(*app_p, id, args, os); + } + } + catch(informative_failure & f) + { + //os.set_err(2); + //Do this instead of printing f.what directly so the output + //will be split into properly-sized blocks automatically. + os<find_command(remaining); + } + else + cmd = NULL; + } + + return cmd; + } + + command * + command::find_command(command_id const & id) + { + command * cmd; + + if (id.empty()) + cmd = this; + else + { + utf8 component = *(id.begin()); command * match = find_child_by_name(component); if (match != NULL) ============================================================ --- lua_hooks.cc 1d95c581d5880c74fd9c941fd69e2564d624d2e2 +++ lua_hooks.cc 33536067d47c7ff32a84f75600765aef0ccb6e7c @@ -16,6 +16,7 @@ #include #include #include +#include #include "lua.hh" @@ -27,6 +28,8 @@ #include "transforms.hh" #include "paths.hh" #include "uri.hh" +#include "cmd.hh" +#include "commands.hh" // defined in {std,test}_hooks.lua, converted to {std,test}_hooks.c respectively extern char const std_hooks_constant[]; @@ -73,6 +76,16 @@ extern "C" } } +app_state* +get_app_state(lua_State *L) +{ + map::iterator i = map_of_lua_to_app.find(L); + if (i != map_of_lua_to_app.end()) + return i->second; + else + return NULL; +} + lua_hooks::lua_hooks() { st = luaL_newstate(); @@ -115,8 +128,12 @@ lua_hooks::set_app(app_state *_app) map_of_lua_to_app.insert(make_pair(st, _app)); } +bool +lua_hooks::check_lua_state(lua_State * p_st) const +{ + return (p_st == st); +} - #ifdef BUILD_UNIT_TESTS void lua_hooks::add_test_hooks() @@ -937,15 +954,92 @@ lua_hooks::hook_note_mtn_startup(args_ve ll.func("note_mtn_startup"); - int n=0; - for (args_vector::const_iterator i = args.begin(); i != args.end(); ++i, ++n) + for (args_vector::const_iterator i = args.begin(); i != args.end(); ++i) ll.push_str((*i)()); - ll.call(n, 0); + ll.call(args.size(), 0); return ll.ok(); } +namespace commands { + class cmd_lua : public command + { + lua_State *st; + std::string const f_name; + public: + cmd_lua(std::string const & primary_name, + std::string const & abstract, + std::string const & desc, + lua_State *L_st, + std::string const & func_name) : + command(primary_name, "", CMD_REF(user), false, "", + abstract, desc, true, options::options_type() | options::opts::none, true), + st(L_st), f_name(func_name) + { + // because user commands are inserted after the normal initialisation process + CMD_REF(user)->children().insert(this); + } + void exec(app_state & app, command_id const & execid, args_vector const & args) const; + }; +} + +void commands::cmd_lua::exec(app_state & app, + command_id const & execid, + args_vector const & args) const +{ + I(st); + I(app.lua.check_lua_state(st)); + + app_state* app_p = get_app_state(st); + I(app_p == & app); + + Lua ll(st); + ll.func(f_name); + + for (args_vector::const_iterator it = args.begin(); it != args.end(); it++) + ll.push_str((*it)()); + + ll.call(args.size(),0); + + E(ll.ok(), F("Call to user command %s (lua command: %s) failed.") % primary_name() % f_name); +} + +LUAEXT(alias_command, ) +{ + const char *old_cmd = luaL_checkstring(L, -2); + const char *new_cmd = luaL_checkstring(L, -1); + N(old_cmd && new_cmd, + F("%s called with an invalid parameter") % "alias_command"); + + args_vector args; + args.push_back(arg_type(old_cmd)); + commands::command_id id = commands::complete_command(args); + commands::command *old_cmd_p = CMD_REF(__root__)->find_command(id); + + old_cmd_p->add_alias(utf8(new_cmd)); + + lua_pushboolean(L, true); + return 1; +} + + +LUAEXT(register_command, ) +{ + const char *cmd_name = luaL_checkstring(L, -4); + const char *cmd_abstract = luaL_checkstring(L, -3); + const char *cmd_desc = luaL_checkstring(L, -2); + const char *cmd_func = luaL_checkstring(L, -1); + + N(cmd_name && cmd_abstract && cmd_desc && cmd_func, + F("%s called with an invalid parameter") % "register_command"); + + new commands::cmd_lua(cmd_name, cmd_abstract, cmd_desc, L, cmd_func); // leak this - commands can't be removed anyway + + lua_pushboolean(L, true); + return 1; +} + // Local Variables: // mode: C++ // fill-column: 76 ============================================================ --- lua_hooks.hh 0968f4973ed14ccd60fb411de351f30008f7da7e +++ lua_hooks.hh 0f79afa4f23d64a221b23ef4b39766528fec8a14 @@ -24,6 +24,8 @@ struct lua_State; class app_state; struct lua_State; +extern app_state* get_app_state(lua_State *L); + class lua_hooks { struct lua_State * st; @@ -35,6 +37,7 @@ public: void add_test_hooks(); #endif void set_app(app_state *_app); + bool check_lua_state(lua_State * st) const; void add_std_hooks(); void workspace_rcfilename(bookkeeping_path & file); void default_rcfilename(system_path & file); ============================================================ --- monotone.cc 424f3537c4ae32633fe643e0e82bbb3b252722c7 +++ monotone.cc e8cf166e90778a1f4a46089915617dab0537e9fc @@ -118,15 +118,21 @@ void localize_monotone() } } -// read command-line options and return the command name -commands::command_id read_options(options & opts, args_vector args) +option::concrete_option_set +read_global_options(options & opts, args_vector & args) { - commands::command_id cmd; - option::concrete_option_set optset = options::opts::all_options().instantiate(&opts); optset.from_command_line(args); + + return optset; +} +// read command-line options and return the command name +commands::command_id read_options(option::concrete_option_set & optset, options & opts, args_vector & args) +{ + commands::command_id cmd; + if (!opts.args.empty()) { // There are some arguments remaining in the command line. Try first @@ -207,7 +213,10 @@ cpp_main(int argc, char ** argv) app_state app; try { - commands::command_id cmd = read_options(app.opts, args); + // read global options first + // commad specific options will be read below + args_vector opt_args(args); + option::concrete_option_set optset = read_global_options(app.opts, opt_args); if (app.opts.version_given) { @@ -227,12 +236,6 @@ cpp_main(int argc, char ** argv) app.keys.set_key_dir(app.opts.key_dir); } - // stop here if they asked for help - if (app.opts.help) - { - throw usage(cmd); - } - // at this point we allow a workspace (meaning search for it // and if found read _MTN/options, but don't use the data quite // yet, and read all the monotonercs). Processing the data @@ -241,11 +244,21 @@ cpp_main(int argc, char ** argv) // if we didn't find one at this point. app.allow_workspace(); + // now grab any command specific options and parse the command + // this needs to happen after the monotonercs have been read + commands::command_id cmd = read_options(optset, app.opts, opt_args); + if (!app.found_workspace && global_sanity.filename.empty()) global_sanity.filename = (app.opts.conf_dir / "dump").as_external(); app.lua.hook_note_mtn_startup(args); + // stop here if they asked for help + if (app.opts.help) + { + throw usage(cmd); + } + // main options processed, now invoke the // sub-command w/ remaining args if (cmd.empty())