# # # add_file "cmd.cc" # content [7e2e64e4e7b59b0f4075a863c25e8e1838caa920] # # patch "Makefile.am" # from [c8088e39b68df0a3fb16fdecfaa4ced7ee3e1aa5] # to [b24cf0cbefbe70213ceef8da1a78b05c523f59fb] # # patch "app_state.cc" # from [2a1ed84ea7cc9ac7793a92001d4b7f5a28b11587] # to [26132b37eb0b496baa8eacd0a116a4561b782106] # # patch "app_state.hh" # from [757e3a3d3587f8328a8b917753270103054617a1] # to [f8efec246414ce2bd299f51084645d5c7a2366cc] # # patch "cmd.hh" # from [eca7fe262e914baf949f98fa7001e468ac9f9b38] # to [b1068b8f8f0fe3b691a0a2b5624527d37adc4e48] # # patch "commands.cc" # from [2d1d23d1778deeeaadbb0ad338ae7fa21ed754ab] # to [6d1d665d00d93e1195d9cbf8d87bb27f4effc8a0] # # patch "database.cc" # from [42c0bb65733e5abf6e7629114487789542b6f0f6] # to [7edf90a2cf52fa99c7b17cc65a0fc5be6bc51526] # # patch "lua_hooks.cc" # from [246a3c65f82857649c0cb86e225af19717cd60a0] # to [5e3cb56505d5dce87f7e76bfe18793abd97e40a1] # # patch "monotone.cc" # from [aeec6948097f979d4899e92bff62c619bce878ae] # to [afde49109945b4b1f47ac59d839cd5578968189b] # # patch "mt_version.cc" # from [cf7b64d81f3e592d5601c042863c6f76817ce5ee] # to [0a802f72a076bf65eacea18438acb73a3a44bb2e] # # patch "option.cc" # from [c7f033f0f2e4251e7fba65d99983d01a3cf18b53] # to [706f424f7b995dbc6db012e814b57adcc8f642a1] # # patch "option.hh" # from [82dad5ae3a07d89df3078564498e9b49d4cc092a] # to [65d3756b0f12e7741a308536a0d51cee021189c1] # # patch "paths.cc" # from [ffe8973a49302cc9e5fc2dbf74927dd2c00bf458] # to [12ae002c3b8ada76d9367e7fb89db07de44eddb3] # # patch "project.cc" # from [1ca851ed8ce7b4acb24a0b0d7c5200359aa55619] # to [ceed59b027d62e68f882420c3323ac8e48a5f7da] # # patch "project.hh" # from [b0977a2c8ba8a5ccd65e07087c8bcf689fbc6b21] # to [f67968192cfc5e513d2281f384c29b76eefdd5e7] # # patch "tester.cc" # from [d879cd83eb36960f1eb5dc12211c95bebaac7ee3] # to [3c883424385bb3a897f0c382910b392875eab3f4] # # patch "transforms.cc" # from [627ab523898554957d55dc2e9138bfaadf8bdec5] # to [21d1f172b5413b4123358f3c44135b6cd44ae5d5] # # patch "ui.cc" # from [646fea920cb0481004997c15fe10f4a829faf7d2] # to [cbee345d5859b445b47730c1cb779cc02da6d9a1] # # patch "ui.hh" # from [ed0c01eb8218bb8dabd9a999aabfe1c50d54b085] # to [2ca5d4fa5a7b2c36a89fa5f84c36603b9818d861] # # patch "unit_tests.cc" # from [5baf8acaed8b1a76321a7b8e95de47414bf97de7] # to [475fa100e0921123927e14b005c6d83735057980] # # patch "xdelta.cc" # from [6c75c90672859828c596acd6329f5c18d0bec659] # to [3bfd03cf01ed8e07764ce268820407d13a583c22] # ============================================================ --- cmd.cc 7e2e64e4e7b59b0f4075a863c25e8e1838caa920 +++ cmd.cc 7e2e64e4e7b59b0f4075a863c25e8e1838caa920 @@ -0,0 +1,520 @@ +// Copyright (C) 2002 Graydon Hoare +// Copyright (C) 2007 Julio M. Merino Vidal +// +// This program is made available under the GNU GPL version 2.0 or +// greater. See the accompanying file COPYING for details. +// +// This program is distributed WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. + +#include "base.hh" +#include "cmd.hh" +#include "lua.hh" +#include "app_state.hh" +#include "work.hh" +#include "ui.hh" +#include "mt_version.hh" +#include "charset.hh" +#include "simplestring_xform.hh" +#include "vocab_cast.hh" + +#ifndef _WIN32 +#include +#endif + +using std::string; +using std::vector; +using std::ostream; + +// +// Definition of top-level commands, used to classify the real commands +// in logical groups. +// +// These top level commands, while part of the final identifiers and defined +// as regular command groups, are handled separately. The user should not +// see them except through the help command. +// +// XXX This is to easily maintain compatibilty with older versions. But +// maybe this should be revised, because exposing the top level category +// (being optional, of course), may not be a bad idea. +// + +CMD_GROUP(__root__, "__root__", "", NULL, "", ""); + +CMD_GROUP_NO_COMPLETE(automation, "automation", "", CMD_REF(__root__), + N_("Commands that aid in scripted execution"), + ""); +CMD_GROUP(database, "database", "", CMD_REF(__root__), + N_("Commands that manipulate the database"), + ""); +CMD_GROUP(debug, "debug", "", CMD_REF(__root__), + N_("Commands that aid in program debugging"), + ""); +CMD_GROUP(informative, "informative", "", CMD_REF(__root__), + N_("Commands for information retrieval"), + ""); +CMD_GROUP(key_and_cert, "key_and_cert", "", CMD_REF(__root__), + N_("Commands to manage keys and certificates"), + ""); +CMD_GROUP(network, "network", "", CMD_REF(__root__), + N_("Commands that access the network"), + ""); +CMD_GROUP(packet_io, "packet_io", "", CMD_REF(__root__), + N_("Commands for packet reading and writing"), + ""); +CMD_GROUP(rcs, "rcs", "", CMD_REF(__root__), + N_("Commands for interaction with RCS and CVS"), + ""); +CMD_GROUP(review, "review", "", CMD_REF(__root__), + N_("Commands to review revisions"), + ""); +CMD_GROUP(tree, "tree", "", CMD_REF(__root__), + N_("Commands to manipulate the tree"), + ""); +CMD_GROUP(variables, "variables", "", CMD_REF(__root__), + N_("Commands to manage persistent variables"), + ""); +CMD_GROUP(workspace, "workspace", "", CMD_REF(__root__), + N_("Commands that deal with the workspace"), + ""); +CMD_GROUP(user, "user", "", CMD_REF(__root__), + N_("Commands defined by the user"), + ""); + +namespace commands { + + // monotone.cc calls this function after option processing. + void process(app_state & app, command_id const & ident, + args_vector const & args) + { + command const * cmd = CMD_REF(__root__)->find_command(ident); + + string visibleid = join_words(vector< utf8 >(ident.begin() + 1, + ident.end()))(); + + I(cmd->is_leaf() || cmd->is_group()); + E(!(cmd->is_group() && cmd->parent() == CMD_REF(__root__)), + origin::user, + F("command '%s' is invalid; it is a group") % join_words(ident)); + + E(!(!cmd->is_leaf() && args.empty()), origin::user, + F("no subcommand specified for '%s'") % visibleid); + + E(!(!cmd->is_leaf() && !args.empty()), origin::user, + F("could not match '%s' to a subcommand of '%s'") % + join_words(args) % visibleid); + + L(FL("executing command '%s'") % visibleid); + + // at this point we process the data from _MTN/options if + // the command needs it. + if (cmd->use_workspace_options()) + { + workspace::check_ws_format(); + workspace::get_ws_options(app.opts); + } + + cmd->exec(app, ident, args); + } + + // Prints the abstract description of the given command or command group + // properly indented. The tag starts at column two. The description has + // to start, at the very least, two spaces after the tag's end position; + // this is given by the colabstract parameter. + static void describe(const string & tag, const string & abstract, + const string & subcommands, size_t colabstract, + ostream & out) + { + I(colabstract > 0); + + size_t col = 0; + out << " " << tag << " "; + col += display_width(utf8(tag + " ", origin::internal)); + + out << string(colabstract - col, ' '); + col = colabstract; + string desc(abstract); + if (!subcommands.empty()) + { + desc += " (" + subcommands + ')'; + } + out << format_text(desc, colabstract, col) << '\n'; + } + + static void explain_children(command::children_set const & children, + ostream & out) + { + I(!children.empty()); + + vector< command const * > sorted; + + size_t colabstract = 0; + for (command::children_set::const_iterator i = children.begin(); + i != children.end(); i++) + { + command const * child = *i; + + if (child->hidden()) + continue; + + size_t len = display_width(join_words(child->names(), ", ")) + + display_width(utf8(" ")); + if (colabstract < len) + colabstract = len; + + sorted.push_back(child); + } + + sort(sorted.begin(), sorted.end(), std::greater< command const * >()); + + for (vector< command const * >::const_iterator i = sorted.begin(); + i != sorted.end(); i++) + { + command const * child = *i; + describe(join_words(child->names(), ", ")(), child->abstract(), + join_words(child->subcommands(), ", ")(), + colabstract, out); + } + } + + static command const * + find_command(command_id const & ident) + { + command const * cmd = CMD_REF(__root__)->find_command(ident); + + // This function is only used internally with an identifier returned + // by complete_command. Therefore, it must always exist. + I(cmd != NULL); + + return cmd; + } + + static void explain_cmd_usage(command_id const & ident, ostream & out) + { + I(ident.size() >= 1); + + vector< string > lines; + command const * cmd = find_command(ident); + + string visibleid = join_words(vector< utf8 >(ident.begin() + 1, + ident.end()))(); + + // Print command parameters. + string params = cmd->params(); + split_into_lines(params, lines); + + if (visibleid.empty()) + out << format_text(F("Commands in group '%s':") % + join_words(ident)()) + << "\n\n"; + else + { + if (!cmd->children().empty()) + out << format_text(F("Subcommands of '%s %s':") % + ui.prog_name % visibleid) + << "\n\n"; + else if (!lines.empty()) + out << format_text(F("Syntax specific to '%s %s':") % + ui.prog_name % visibleid) + << "\n\n"; + } + + // lines might be empty, but only when specific syntax is to be + // displayed, not in the other cases. + if (!lines.empty()) + { + for (vector::const_iterator j = lines.begin(); + j != lines.end(); ++j) + out << " " << visibleid << ' ' << *j << '\n'; + out << '\n'; + } + + // Explain children, if any. + if (!cmd->is_leaf()) + { + explain_children(cmd->children(), out); + out << '\n'; + } + + // Print command description. + if (visibleid.empty()) + out << format_text(F("Purpose of group '%s':") % + join_words(ident)()) + << "\n\n"; + else + out << format_text(F("Description for '%s %s':") % + ui.prog_name % visibleid) + << "\n\n"; + out << format_text(cmd->desc(), 2) << "\n\n"; + + // Print all available aliases. + if (cmd->names().size() > 1) + { + command::names_set othernames = cmd->names(); + othernames.erase(ident[ident.size() - 1]); + out << format_text(F("Aliases: %s.") % + join_words(othernames, ", ")(), 2) + << '\n'; + } + } + + void explain_usage(command_id const & ident, ostream & out) + { + command const * cmd = find_command(ident); + + if (ident.empty()) + { + out << format_text(F("Command groups:")) << "\n\n"; + explain_children(CMD_REF(__root__)->children(), out); + out << '\n' + << format_text(F("For information on a specific command, type " + "'mtn help [subcommand_name ...]'.")) + << "\n\n" + << format_text(F("To see more details about the commands of a " + "particular group, type 'mtn help '.")) + << "\n\n" + << format_text(F("Note that you can always abbreviate a command " + "name as long as it does not conflict with other " + "names.")) + << "\n"; + } + else + explain_cmd_usage(ident, out); + } + + options::options_type command_options(command_id const & ident) + { + command const * cmd = find_command(ident); + return cmd->opts(); + } + + // Lua-defined user 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 & params, + std::string const & abstract, + std::string const & desc, + lua_State *L_st, + std::string const & func_name) : + command(primary_name, "", CMD_REF(user), false, false, params, + 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 + { + 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)()); + + app.mtn_automate_allowed = true; + + ll.call(args.size(),0); + + app.mtn_automate_allowed = false; + + E(ll.ok(), origin::user, + F("Call to user command %s (lua command: %s) failed.") + % primary_name() % f_name); + } + }; +} + +LUAEXT(alias_command, ) +{ + const char *old_cmd = luaL_checkstring(LS, -2); + const char *new_cmd = luaL_checkstring(LS, -1); + E(old_cmd && new_cmd, origin::user, + F("%s called with an invalid parameter") % "alias_command"); + + args_vector args; + args.push_back(arg_type(old_cmd, origin::user)); + 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(LS, true); + return 1; +} + + +LUAEXT(register_command, ) +{ + const char *cmd_name = luaL_checkstring(LS, -5); + const char *cmd_params = luaL_checkstring(LS, -4); + const char *cmd_abstract = luaL_checkstring(LS, -3); + const char *cmd_desc = luaL_checkstring(LS, -2); + const char *cmd_func = luaL_checkstring(LS, -1); + + E(cmd_name && cmd_params && cmd_abstract && cmd_desc && cmd_func, + origin::user, + F("%s called with an invalid parameter") % "register_command"); + + // leak this - commands can't be removed anyway + new commands::cmd_lua(cmd_name, cmd_params, cmd_abstract, cmd_desc, + LS, cmd_func); + + lua_pushboolean(LS, true); + return 1; +} + +// Miscellaneous commands and related functions for which there is no +// better file. + +CMD_NO_WORKSPACE(help, "help", "", CMD_REF(informative), + N_("command [ARGS...]"), + N_("Displays help about commands and options"), + "", + options::opts::none) +{ + if (args.size() < 1) + { + app.opts.help = true; + throw usage(command_id()); + } + + command_id id = commands::complete_command(args); + app.opts.help = true; + throw usage(id); +} + +CMD_NO_WORKSPACE(version, "version", "", CMD_REF(informative), "", + N_("Shows the program version"), + "", + options::opts::full) +{ + E(args.empty(), origin::user, + F("no arguments allowed")); + + if (app.opts.full) + print_full_version(); + else + print_version(); +} + +CMD_HIDDEN(crash, "crash", "", CMD_REF(debug), + "{ N | E | I | exception | signal }", + N_("Triggers the specified kind of crash"), + "", + options::opts::none) +{ + if (args.size() != 1) + throw usage(execid); + bool spoon_exists(false); + if (idx(args,0)() == "N") + E(spoon_exists, origin::user, i18n_format("There is no spoon.")); + else if (idx(args,0)() == "E") + E(spoon_exists, origin::system, i18n_format("There is no spoon.")); + else if (idx(args,0)() == "I") + { + I(spoon_exists); + } +#define maybe_throw(ex) if(idx(args,0)()==#ex) throw ex("There is no spoon.") +#define maybe_throw_bare(ex) if(idx(args,0)()==#ex) throw ex() + else maybe_throw_bare(std::bad_alloc); + else maybe_throw_bare(std::bad_cast); + else maybe_throw_bare(std::bad_typeid); + else maybe_throw_bare(std::bad_exception); + else maybe_throw_bare(std::exception); + else maybe_throw(std::domain_error); + else maybe_throw(std::invalid_argument); + else maybe_throw(std::length_error); + else maybe_throw(std::out_of_range); + else maybe_throw(std::range_error); + else maybe_throw(std::overflow_error); + else maybe_throw(std::underflow_error); + else maybe_throw(std::logic_error); + else maybe_throw(std::runtime_error); + else + { +#ifndef _WIN32 + try + { + int signo = boost::lexical_cast(idx(args,0)()); + if (0 < signo && signo <= 15) + { + raise(signo); + // control should not get here... + I(!"crash: raise returned"); + } + } + catch (boost::bad_lexical_cast&) + { // fall through and throw usage + } +#endif + throw usage(execid); + } +#undef maybe_throw +#undef maybe_throw_bare +} + +// There isn't really a better place for this function. + +void +process_commit_message_args(options const & opts, + bool & given, + utf8 & log_message, + utf8 const & message_prefix) +{ + // can't have both a --message and a --message-file ... + E(!opts.message_given || !opts.msgfile_given, origin::user, + F("--message and --message-file are mutually exclusive")); + + if (opts.message_given) + { + string msg; + join_lines(opts.message, msg); + log_message = utf8(msg, origin::user); + if (!opts.no_prefix && message_prefix().length() != 0) + log_message = utf8(message_prefix() + "\n\n" + log_message(), + origin::user); + given = true; + } + else if (opts.msgfile_given) + { + data dat; + read_data_for_command_line(opts.msgfile, dat); + external dat2 = typecast_vocab(dat); + system_to_utf8(dat2, log_message); + if (!opts.no_prefix && message_prefix().length() != 0) + log_message = utf8(message_prefix() + "\n\n" + log_message(), + origin::user); + given = true; + } + else if (message_prefix().length() != 0) + { + log_message = message_prefix; + given = true; + } + else + given = false; +} + +// Local Variables: +// mode: C++ +// fill-column: 76 +// c-file-style: "gnu" +// indent-tabs-mode: nil +// End: +// vim: et:sw=2:sts=2:ts=2:cino=>2s,{s,\:s,+s,t0,g0,^-2,e-2,n-2,p2s,(0,=s: ============================================================ --- Makefile.am c8088e39b68df0a3fb16fdecfaa4ced7ee3e1aa5 +++ Makefile.am b24cf0cbefbe70213ceef8da1a78b05c523f59fb @@ -2,9 +2,10 @@ CMD_SOURCES = \ ACLOCAL_AMFLAGS = -I m4 CMD_SOURCES = \ - cmd.hh cmd_netsync.cc cmd_list.cc cmd_packet.cc cmd_key_cert.cc \ - cmd_merging.cc cmd_db.cc cmd_diff_log.cc cmd_ws_commit.cc \ - cmd_othervcs.cc cmd_automate.cc cmd_files.cc cmd_conflicts.cc + cmd.hh cmd.cc cmd_netsync.cc cmd_list.cc cmd_packet.cc \ + cmd_key_cert.cc cmd_merging.cc cmd_db.cc cmd_diff_log.cc \ + cmd_ws_commit.cc cmd_othervcs.cc cmd_automate.cc cmd_files.cc \ + cmd_conflicts.cc SANITY_CORE_SOURCES = \ sanity.cc sanity.hh quick_alloc.hh vector.hh base.hh \ @@ -81,7 +82,6 @@ MOST_SOURCES = \ \ cleanup.hh unit_tests.hh \ cycle_detector.hh randomfile.hh adler32.hh \ - randomizer.cc randomizer.hh \ netio.hh gettext.h \ package_revision.c package_revision.h \ package_full_revision.c package_full_revision.h \ @@ -302,31 +302,26 @@ UNIT_TEST_SOURCES = \ netxx_pipe.cc numeric_vocab.cc option.cc outdated_indicator.cc \ packet.cc paths.cc refiner.cc restrictions.cc rev_height.cc \ revision.cc roster.cc roster_merge.cc simplestring_xform.cc \ - string_queue.cc transforms.cc unit_tests.cc uri.cc vocab.cc \ - xdelta.cc + string_queue.cc transforms.cc uri.cc vocab.cc xdelta.cc # these files do not contain unit tests, but are required for unit testing # and must be recompiled for that purpose UNIT_TEST_SRC_SUPPORT = \ - roster_delta.cc + roster_delta.cc randomizer.cc randomizer.hh unit_tests.cc # these files do not contain unit tests; they are required for unit # testing, but can be used "as is" from the main build. (many of # these _should_ have unit tests, but they haven't been written yet.) UNIT_TEST_OBJ_SUPPORT = \ - mtn-app_state.$(OBJEXT) mtn-cert.$(OBJEXT) \ - mtn-constants.$(OBJEXT) mtn-database.$(OBJEXT) \ - mtn-epoch.$(OBJEXT) mtn-file_io.$(OBJEXT) mtn-gzip.$(OBJEXT) \ - mtn-hmac.$(OBJEXT) mtn-inodeprint.$(OBJEXT) \ - mtn-key_store.$(OBJEXT) mtn-keys.$(OBJEXT) mtn-lcs.$(OBJEXT) \ - mtn-legacy.$(OBJEXT) mtn-lua.$(OBJEXT) mtn-lua_hooks.$(OBJEXT) \ - mtn-merkle_tree.$(OBJEXT) mtn-mt_version.$(OBJEXT) \ - mtn-mtn-sanity.$(OBJEXT) mtn-options.$(OBJEXT) \ - mtn-package_full_revision.$(OBJEXT) \ - mtn-package_revision.$(OBJEXT) mtn-pcrewrap.$(OBJEXT) \ - mtn-project.$(OBJEXT) mtn-randomizer.$(OBJEXT) \ - mtn-sanity.$(OBJEXT) mtn-schema.$(OBJEXT) \ - mtn-schema_migration.$(OBJEXT) \ + mtn-cert.$(OBJEXT) mtn-constants.$(OBJEXT) \ + mtn-database.$(OBJEXT) mtn-epoch.$(OBJEXT) \ + mtn-file_io.$(OBJEXT) mtn-gzip.$(OBJEXT) mtn-hmac.$(OBJEXT) \ + mtn-inodeprint.$(OBJEXT) mtn-key_store.$(OBJEXT) \ + mtn-keys.$(OBJEXT) mtn-lcs.$(OBJEXT) mtn-legacy.$(OBJEXT) \ + mtn-lua.$(OBJEXT) mtn-lua_hooks.$(OBJEXT) \ + mtn-merkle_tree.$(OBJEXT) mtn-pcrewrap.$(OBJEXT) \ + mtn-project.$(OBJEXT) mtn-sanity.$(OBJEXT) \ + mtn-schema.$(OBJEXT) mtn-schema_migration.$(OBJEXT) \ mtn-specialized_lexical_cast.$(OBJEXT) mtn-ssh_agent.$(OBJEXT) \ mtn-std_hooks.$(OBJEXT) mtn-ui.$(OBJEXT) mtn-work.$(OBJEXT) \ mtn-work_migration.$(OBJEXT) ============================================================ --- app_state.cc 2a1ed84ea7cc9ac7793a92001d4b7f5a28b11587 +++ app_state.cc 26132b37eb0b496baa8eacd0a116a4561b782106 @@ -9,30 +9,15 @@ #include "base.hh" #include "app_state.hh" -#include "database.hh" -#include - -class app_state_private -{ -public: - std::map > databases; -}; - app_state::app_state() - : _hidden(new app_state_private()), lua(this), mtn_automate_allowed(false), + : lua(this), mtn_automate_allowed(false), rng(Botan::RandomNumberGenerator::make_rng()) {} app_state::~app_state() {} -boost::shared_ptr & -app_state::lookup_db(system_path const & f) -{ - return _hidden->databases[f]; -} - // Local Variables: // mode: C++ // fill-column: 76 ============================================================ --- app_state.hh 757e3a3d3587f8328a8b917753270103054617a1 +++ app_state.hh f8efec246414ce2bd299f51084645d5c7a2366cc @@ -20,22 +20,18 @@ // commands, or be accessible to the lua hooks (which includes anything // needed by mtn_automate()). -class app_state_private; -class database_impl; +struct database_cache; class app_state { - boost::shared_ptr _hidden; public: explicit app_state(); ~app_state(); - boost::shared_ptr & - lookup_db(system_path const & f); - options opts; lua_hooks lua; bool mtn_automate_allowed; boost::shared_ptr rng; + boost::shared_ptr dbcache; }; // Local Variables: ============================================================ --- cmd.hh eca7fe262e914baf949f98fa7001e468ac9f9b38 +++ cmd.hh b1068b8f8f0fe3b691a0a2b5624527d37adc4e48 @@ -16,12 +16,8 @@ #include "commands.hh" #include "selectors.hh" #include "options.hh" -#include "sanity.hh" class app_state; -class database; -class project_t; -struct workspace; namespace commands { @@ -132,7 +128,7 @@ namespace commands command_id const & execid, args_vector const & args) const; }; -}; +} inline std::vector args_to_paths(args_vector const & args) @@ -155,14 +151,7 @@ args_to_paths(args_vector const & args) return paths; } -std::string -describe_revision(project_t & project, revision_id const & id); - void -notify_if_multiple_heads(project_t & project, branch_name const & branchname, - bool ignore_suspend_certs); - -void process_commit_message_args(options const & opts, bool & given, utf8 & log_message, ============================================================ --- commands.cc 2d1d23d1778deeeaadbb0ad338ae7fa21ed754ab +++ commands.cc 6d1d665d00d93e1195d9cbf8d87bb27f4effc8a0 @@ -9,103 +9,18 @@ // PURPOSE. #include "base.hh" -#include -#include -#include - -#include "transforms.hh" +#include "cmd.hh" #include "simplestring_xform.hh" -#include "file_io.hh" -#include "charset.hh" -#include "diff_patch.hh" -#include "inodeprint.hh" -#include "cert.hh" -#include "ui.hh" -#include "cmd.hh" -#include "constants.hh" -#include "app_state.hh" -#include "project.hh" -#include "work.hh" -#include "vocab_cast.hh" -#ifndef _WIN32 -#include "lexical_cast.hh" -#include -#endif - -using std::cin; -using std::make_pair; using std::map; -using std::ostream; -using std::pair; using std::set; using std::string; -using std::strlen; using std::vector; -CMD_GROUP(__root__, "__root__", "", NULL, "", ""); +// This file defines the logic behind the CMD() family of macros and handles +// command completion. Note that commands::process is in cmd.cc mainly for +// better encapsulation of functions not needed in the unit tester. -// -// Definition of top-level commands, used to classify the real commands -// in logical groups. -// -// These top level commands, while part of the final identifiers and defined -// as regular command groups, are handled separately. The user should not -// see them except through the help command. -// -// XXX This is to easily maintain compatibilty with older versions. But -// maybe this should be revised, because exposing the top level category -// (being optional, of course), may not be a bad idea. -// -CMD_GROUP_NO_COMPLETE(automation, "automation", "", CMD_REF(__root__), - N_("Commands that aid in scripted execution"), - ""); -CMD_GROUP(database, "database", "", CMD_REF(__root__), - N_("Commands that manipulate the database"), - ""); -CMD_GROUP(debug, "debug", "", CMD_REF(__root__), - N_("Commands that aid in program debugging"), - ""); -CMD_GROUP(informative, "informative", "", CMD_REF(__root__), - N_("Commands for information retrieval"), - ""); -CMD_GROUP(key_and_cert, "key_and_cert", "", CMD_REF(__root__), - N_("Commands to manage keys and certificates"), - ""); -CMD_GROUP(network, "network", "", CMD_REF(__root__), - N_("Commands that access the network"), - ""); -CMD_GROUP(packet_io, "packet_io", "", CMD_REF(__root__), - N_("Commands for packet reading and writing"), - ""); -CMD_GROUP(rcs, "rcs", "", CMD_REF(__root__), - N_("Commands for interaction with RCS and CVS"), - ""); -CMD_GROUP(review, "review", "", CMD_REF(__root__), - N_("Commands to review revisions"), - ""); -CMD_GROUP(tree, "tree", "", CMD_REF(__root__), - N_("Commands to manipulate the tree"), - ""); -CMD_GROUP(variables, "variables", "", CMD_REF(__root__), - N_("Commands to manage persistent variables"), - ""); -CMD_GROUP(workspace, "workspace", "", CMD_REF(__root__), - N_("Commands that deal with the workspace"), - ""); -CMD_GROUP(user, "user", "", CMD_REF(__root__), - N_("Commands defined by the user"), - ""); - -// this file defines the task-oriented "top level" commands which can be -// issued as part of a monotone command line. the command line can only -// have one such command on it, followed by a vector of strings which are its -// arguments. all --options will be processed by the main program *before* -// calling a command -// -// we might expose this blunt command interface to scripting someday. but -// not today. - namespace commands { const char * safe_gettext(const char * msgid) @@ -141,12 +56,10 @@ namespace commands } } } -} -// -// Implementation of the commands::command class. -// -namespace commands { + // + // Implementation of the commands::command class. + // command::command(std::string const & primary_name, std::string const & other_names, command * parent, @@ -579,379 +492,33 @@ namespace commands return id; } - static command const * - find_command(command_id const & ident) - { - command const * cmd = CMD_REF(__root__)->find_command(ident); - - // This function is only used internally with an identifier returned - // by complete_command. Therefore, it must always exist. - I(cmd != NULL); - - return cmd; - } - - // Prints the abstract description of the given command or command group - // properly indented. The tag starts at column two. The description has - // to start, at the very least, two spaces after the tag's end position; - // this is given by the colabstract parameter. - static void describe(const string & tag, const string & abstract, - const string & subcommands, size_t colabstract, - ostream & out) - { - I(colabstract > 0); - - size_t col = 0; - out << " " << tag << " "; - col += display_width(utf8(tag + " ", origin::internal)); - - out << string(colabstract - col, ' '); - col = colabstract; - string desc(abstract); - if (!subcommands.empty()) - { - desc += " (" + subcommands + ')'; - } - out << format_text(desc, colabstract, col) << '\n'; - } - - static void explain_children(command::children_set const & children, - ostream & out) - { - I(!children.empty()); - - vector< command const * > sorted; - - size_t colabstract = 0; - for (command::children_set::const_iterator i = children.begin(); - i != children.end(); i++) - { - command const * child = *i; - - if (child->hidden()) - continue; - - size_t len = display_width(join_words(child->names(), ", ")) + - display_width(utf8(" ")); - if (colabstract < len) - colabstract = len; - - sorted.push_back(child); - } - - sort(sorted.begin(), sorted.end(), std::greater< command * >()); - - for (vector< command const * >::const_iterator i = sorted.begin(); - i != sorted.end(); i++) - { - command const * child = *i; - describe(join_words(child->names(), ", ")(), child->abstract(), - join_words(child->subcommands(), ", ")(), - colabstract, out); - } - } - - static void explain_cmd_usage(command_id const & ident, ostream & out) - { - I(ident.size() >= 1); - - vector< string > lines; - command const * cmd = find_command(ident); - - string visibleid = join_words(vector< utf8 >(ident.begin() + 1, - ident.end()))(); - - // Print command parameters. - string params = cmd->params(); - split_into_lines(params, lines); - - if (visibleid.empty()) - out << format_text(F("Commands in group '%s':") % - join_words(ident)()) - << "\n\n"; - else - { - if (!cmd->children().empty()) - out << format_text(F("Subcommands of '%s %s':") % - ui.prog_name % visibleid) - << "\n\n"; - else if (!lines.empty()) - out << format_text(F("Syntax specific to '%s %s':") % - ui.prog_name % visibleid) - << "\n\n"; - } - - // lines might be empty, but only when specific syntax is to be - // displayed, not in the other cases. - if (!lines.empty()) - { - for (vector::const_iterator j = lines.begin(); - j != lines.end(); ++j) - out << " " << visibleid << ' ' << *j << '\n'; - out << '\n'; - } - - // Explain children, if any. - if (!cmd->is_leaf()) - { - explain_children(cmd->children(), out); - out << '\n'; - } - - // Print command description. - if (visibleid.empty()) - out << format_text(F("Purpose of group '%s':") % - join_words(ident)()) - << "\n\n"; - else - out << format_text(F("Description for '%s %s':") % - ui.prog_name % visibleid) - << "\n\n"; - out << format_text(cmd->desc(), 2) << "\n\n"; - - // Print all available aliases. - if (cmd->names().size() > 1) - { - command::names_set othernames = cmd->names(); - othernames.erase(ident[ident.size() - 1]); - out << format_text(F("Aliases: %s.") % - join_words(othernames, ", ")(), 2) - << '\n'; - } - } - command_id make_command_id(std::string const & path) { return split_into_words(utf8(path, origin::user)); } - - void explain_usage(command_id const & ident, ostream & out) - { - command const * cmd = find_command(ident); - - if (ident.empty()) - { - out << format_text(F("Command groups:")) << "\n\n"; - explain_children(CMD_REF(__root__)->children(), out); - out << '\n' - << format_text(F("For information on a specific command, type " - "'mtn help [subcommand_name ...]'.")) - << "\n\n" - << format_text(F("To see more details about the commands of a " - "particular group, type 'mtn help '.")) - << "\n\n" - << format_text(F("Note that you can always abbreviate a command " - "name as long as it does not conflict with other " - "names.")) - << "\n"; - } - else - explain_cmd_usage(ident, out); - } - - void process(app_state & app, command_id const & ident, - args_vector const & args) - { - command const * cmd = CMD_REF(__root__)->find_command(ident); - - string visibleid = join_words(vector< utf8 >(ident.begin() + 1, - ident.end()))(); - - I(cmd->is_leaf() || cmd->is_group()); - E(!(cmd->is_group() && cmd->parent() == CMD_REF(__root__)), - origin::user, - F("command '%s' is invalid; it is a group") % join_words(ident)); - - E(!(!cmd->is_leaf() && args.empty()), origin::user, - F("no subcommand specified for '%s'") % visibleid); - - E(!(!cmd->is_leaf() && !args.empty()), origin::user, - F("could not match '%s' to a subcommand of '%s'") % - join_words(args) % visibleid); - - L(FL("executing command '%s'") % visibleid); - - // at this point we process the data from _MTN/options if - // the command needs it. - if (cmd->use_workspace_options()) - { - workspace::check_ws_format(); - workspace::get_ws_options(app.opts); - } - - cmd->exec(app, ident, args); - } - - options::options_type command_options(command_id const & ident) - { - command const * cmd = find_command(ident); - return cmd->opts(); - } } -//////////////////////////////////////////////////////////////////////// -CMD(help, "help", "", CMD_REF(informative), N_("command [ARGS...]"), - N_("Displays help about commands and options"), - "", - options::opts::none) -{ - if (args.size() < 1) - { - app.opts.help = true; - throw usage(command_id()); - } +#ifdef BUILD_UNIT_TESTS +#include "unit_tests.hh" - command_id id = commands::complete_command(args); - app.opts.help = true; - throw usage(id); -} +// by duplicating these definitions from options.cc we avoid dragging +// that file and all its dependencies into the unit tester -CMD_HIDDEN(crash, "crash", "", CMD_REF(debug), - "{ N | E | I | exception | signal }", - N_("Triggers the specified kind of crash"), - "", - options::opts::none) +option::option_set +operator | (option::option_set const & opts, + option::option_set const & (*fun)()) { - if (args.size() != 1) - throw usage(execid); - bool spoon_exists(false); - if (idx(args,0)() == "N") - E(spoon_exists, origin::user, i18n_format("There is no spoon.")); - else if (idx(args,0)() == "E") - E(spoon_exists, origin::system, i18n_format("There is no spoon.")); - else if (idx(args,0)() == "I") - { - I(spoon_exists); - } -#define maybe_throw(ex) if(idx(args,0)()==#ex) throw ex("There is no spoon.") -#define maybe_throw_bare(ex) if(idx(args,0)()==#ex) throw ex() - else maybe_throw_bare(std::bad_alloc); - else maybe_throw_bare(std::bad_cast); - else maybe_throw_bare(std::bad_typeid); - else maybe_throw_bare(std::bad_exception); - else maybe_throw_bare(std::exception); - else maybe_throw(std::domain_error); - else maybe_throw(std::invalid_argument); - else maybe_throw(std::length_error); - else maybe_throw(std::out_of_range); - else maybe_throw(std::range_error); - else maybe_throw(std::overflow_error); - else maybe_throw(std::underflow_error); - else maybe_throw(std::logic_error); - else maybe_throw(std::runtime_error); - else - { -#ifndef _WIN32 - try - { - int signo = boost::lexical_cast(idx(args,0)()); - if (0 < signo && signo <= 15) - { - raise(signo); - // control should not get here... - I(!"crash: raise returned"); - } - } - catch (boost::bad_lexical_cast&) - { // fall through and throw usage - } -#endif - throw usage(execid); - } -#undef maybe_throw -#undef maybe_throw_bare + return opts | fun(); } -string -describe_revision(project_t & project, revision_id const & id) +options::options_type const & options::opts::none() { - cert_name author_name(author_cert_name); - cert_name date_name(date_cert_name); - - string description; - - description += encode_hexenc(id.inner()(), id.inner().made_from); - - // append authors and date of this revision - vector< revision > tmp; - project.get_revision_certs_by_name(id, author_name, tmp); - for (vector< revision >::const_iterator i = tmp.begin(); - i != tmp.end(); ++i) - { - description += " "; - description += i->inner().value(); - } - project.get_revision_certs_by_name(id, date_name, tmp); - for (vector< revision >::const_iterator i = tmp.begin(); - i != tmp.end(); ++i) - { - description += " "; - description += i->inner().value(); - } - - return description; + static options::options_type val; + return val; } -void -notify_if_multiple_heads(project_t & project, - branch_name const & branchname, - bool ignore_suspend_certs) -{ - set heads; - project.get_branch_heads(branchname, heads, ignore_suspend_certs); - if (heads.size() > 1) { - string prefixedline; - prefix_lines_with(_("note: "), - _("branch '%s' has multiple heads\n" - "perhaps consider '%s merge'"), - prefixedline); - P(i18n_format(prefixedline) % branchname % ui.prog_name); - } -} +CMD_GROUP(__root__, "__root__", "", NULL, "", ""); -void -process_commit_message_args(options const & opts, - bool & given, - utf8 & log_message, - utf8 const & message_prefix) -{ - // can't have both a --message and a --message-file ... - E(!opts.message_given || !opts.msgfile_given, origin::user, - F("--message and --message-file are mutually exclusive")); - - if (opts.message_given) - { - string msg; - join_lines(opts.message, msg); - log_message = utf8(msg, origin::user); - if (!opts.no_prefix && message_prefix().length() != 0) - log_message = utf8(message_prefix() + "\n\n" + log_message(), - origin::user); - given = true; - } - else if (opts.msgfile_given) - { - data dat; - read_data_for_command_line(opts.msgfile, dat); - external dat2 = typecast_vocab(dat); - system_to_utf8(dat2, log_message); - if (!opts.no_prefix && message_prefix().length() != 0) - log_message = utf8(message_prefix() + "\n\n" + log_message(), - origin::user); - given = true; - } - else if (message_prefix().length() != 0) - { - log_message = message_prefix; - given = true; - } - else - given = false; -} - -#ifdef BUILD_UNIT_TESTS -#include "unit_tests.hh" - CMD_GROUP(top, "top", "", CMD_REF(__root__), "", ""); CMD(test, "test", "", CMD_REF(top), ============================================================ --- database.cc 42c0bb65733e5abf6e7629114487789542b6f0f6 +++ database.cc 7edf90a2cf52fa99c7b17cc65a0fc5be6bc51526 @@ -415,14 +415,21 @@ database_impl::~database_impl() close(); } +struct database_cache +{ + map > dbs; +}; + database::database(app_state & app) : lua(app.lua), rng(app.rng) { - boost::shared_ptr & i = app.lookup_db(app.opts.dbname); + if (!app.dbcache) + app.dbcache.reset(new database_cache); + + boost::shared_ptr & i = app.dbcache->dbs[app.opts.dbname]; if (!i) - { - i.reset(new database_impl(app.opts.dbname)); - } + i.reset(new database_impl(app.opts.dbname)); + imp = i; } ============================================================ --- lua_hooks.cc 246a3c65f82857649c0cb86e225af19717cd60a0 +++ lua_hooks.cc 5e3cb56505d5dce87f7e76bfe18793abd97e40a1 @@ -1138,93 +1138,6 @@ lua_hooks::hook_note_mtn_startup(args_ve 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 & params, - std::string const & abstract, - std::string const & desc, - lua_State *L_st, - std::string const & func_name) : - command(primary_name, "", CMD_REF(user), false, false, params, - 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)()); - - app.mtn_automate_allowed = true; - - ll.call(args.size(),0); - - app.mtn_automate_allowed = false; - - E(ll.ok(), origin::user, - F("Call to user command %s (lua command: %s) failed.") % primary_name() % f_name); -} - -LUAEXT(alias_command, ) -{ - const char *old_cmd = luaL_checkstring(LS, -2); - const char *new_cmd = luaL_checkstring(LS, -1); - E(old_cmd && new_cmd, origin::user, - F("%s called with an invalid parameter") % "alias_command"); - - args_vector args; - args.push_back(arg_type(old_cmd, origin::user)); - 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(LS, true); - return 1; -} - - -LUAEXT(register_command, ) -{ - const char *cmd_name = luaL_checkstring(LS, -5); - const char *cmd_params = luaL_checkstring(LS, -4); - const char *cmd_abstract = luaL_checkstring(LS, -3); - const char *cmd_desc = luaL_checkstring(LS, -2); - const char *cmd_func = luaL_checkstring(LS, -1); - - E(cmd_name && cmd_params && cmd_abstract && cmd_desc && cmd_func, - origin::user, - F("%s called with an invalid parameter") % "register_command"); - - new commands::cmd_lua(cmd_name, cmd_params, cmd_abstract, cmd_desc, LS, cmd_func); // leak this - commands can't be removed anyway - - lua_pushboolean(LS, true); - return 1; -} - // Local Variables: // mode: C++ // fill-column: 76 ============================================================ --- monotone.cc aeec6948097f979d4899e92bff62c619bce878ae +++ monotone.cc afde49109945b4b1f47ac59d839cd5578968189b @@ -147,11 +147,20 @@ commands::command_id read_options(option return cmd; } +string +get_usage_str(options::options_type const & optset, options & opts) +{ + vector names; + vector descriptions; + unsigned int maxnamelen; + + optset.instantiate(&opts).get_usage_strings(names, descriptions, maxnamelen); + return format_usage_strings(names, descriptions, maxnamelen); +} + int cpp_main(int argc, char ** argv) { - int ret = 0; - // go-go gadget i18n localize_monotone(); @@ -209,7 +218,8 @@ cpp_main(int argc, char ** argv) // read global options first // command specific options will be read below args_vector opt_args(args); - option::concrete_option_set optset = read_global_options(app.opts, opt_args); + option::concrete_option_set optset + = read_global_options(app.opts, opt_args); if (app.opts.version_given) { @@ -240,7 +250,8 @@ cpp_main(int argc, char ** argv) // check if the user specified default arguments for this command args_vector default_args; - if (!cmd.empty() && app.lua.hook_get_default_command_options(cmd, default_args)) + if (!cmd.empty() + && app.lua.hook_get_default_command_options(cmd, default_args)) optset.from_command_line(default_args, false); if (workspace::found) @@ -262,29 +273,20 @@ cpp_main(int argc, char ** argv) // stop here if they asked for help if (app.opts.help) - { - throw usage(cmd); - } + throw usage(cmd); // main options processed, now invoke the // sub-command w/ remaining args if (cmd.empty()) - { - throw usage(commands::command_id()); - } - else - { - commands::process(app, cmd, app.opts.args); - // The command will raise any problems itself through - // exceptions. If we reach this point, it is because it - // worked correctly. - return 0; - } + throw usage(commands::command_id()); + + + commands::process(app, cmd, app.opts.args); + // The command will raise any problems itself through + // exceptions. If we reach this point, it is because it + // worked correctly. + return 0; } - catch (option::option_error const & e) - { - E(false, origin::user, i18n_format("%s") % e.what()); - } catch (usage & u) { // we send --help output to stdout, so that "mtn --help | less" works @@ -302,10 +304,7 @@ cpp_main(int argc, char ** argv) ui.prog_name << "\n\n"; if (u.which.empty()) - { - usage_stream << options::opts::globals().instantiate(&app.opts). - get_usage_str() << '\n'; - } + usage_stream << get_usage_str(options::opts::globals(), app.opts); // Make sure to hide documentation that's not part of // the current command. @@ -314,11 +313,11 @@ cpp_main(int argc, char ** argv) if (!cmd_options.empty()) { usage_stream - << F("Options specific to '%s %s' (run '%s help' to see global options):") + << F("Options specific to '%s %s' " + "(run '%s help' to see global options):") % ui.prog_name % visibleid % ui.prog_name << "\n\n"; - usage_stream << cmd_options.instantiate(&app.opts). - get_usage_str() << '\n'; + usage_stream << get_usage_str(cmd_options, app.opts); } commands::explain_usage(u.which, usage_stream); @@ -326,9 +325,13 @@ cpp_main(int argc, char ** argv) return 0; else return 2; - } } + catch (option::option_error const & inf) + { + ui.inform(inf.what()); + return 2; + } catch (recoverable_failure & inf) { ui.inform(inf.what()); ============================================================ --- mt_version.cc cf7b64d81f3e592d5601c042863c6f76817ce5ee +++ mt_version.cc 0a802f72a076bf65eacea18438acb73a3a44bb2e @@ -13,14 +13,12 @@ #include "base.hh" -#include -#include #include #include +#include #include "app_state.hh" -#include "cmd.hh" #include "platform.hh" #include "mt_version.hh" #include "package_revision.h" @@ -28,25 +26,8 @@ using std::cout; #include "sanity.hh" using std::cout; -using std::ostringstream; using std::string; -CMD_NO_WORKSPACE(version, "version", "", CMD_REF(informative), "", - N_("Shows the program version"), - "", - options::opts::full) -{ - E(args.empty(), origin::user, - F("no arguments allowed")); - - string version; - if (app.opts.full) - get_full_version(version); - else - get_version(version); - cout << version << '\n'; -} - void get_version(string & out) { @@ -59,29 +40,30 @@ print_version() { string s; get_version(s); - cout << s << '\n'; + cout << s; } void get_full_version(string & out) { - ostringstream oss; - string s; - get_version(s); - oss << s << '\n'; - get_system_flavour(s); - oss << F("Running on : %s\n" + string base_version; + get_version(base_version); + string flavour; + get_system_flavour(flavour); + out = (F("%s\n" + "Running on : %s\n" "C++ compiler : %s\n" "C++ standard library: %s\n" "Boost version : %s\n" "Changes since base revision:\n" "%s") - % s - % BOOST_COMPILER - % BOOST_STDLIB - % BOOST_LIB_VERSION - % string(package_full_revision_constant); - out = oss.str(); + % base_version + % flavour + % BOOST_COMPILER + % BOOST_STDLIB + % BOOST_LIB_VERSION + % package_full_revision_constant) + .str(); } void @@ -89,7 +71,7 @@ print_full_version() { string s; get_full_version(s); - cout << s << '\n'; + cout << s; } // Local Variables: ============================================================ --- option.cc c7f033f0f2e4251e7fba65d99983d01a3cf18b53 +++ option.cc 706f424f7b995dbc6db012e814b57adcc8f642a1 @@ -5,7 +5,6 @@ #include "file_io.hh" #include "option.hh" #include "sanity.hh" -#include "ui.hh" using std::map; using std::pair; @@ -437,47 +436,24 @@ static string usagestr(concrete_option c return out; } -std::string concrete_option_set::get_usage_str() const +void +concrete_option_set::get_usage_strings(vector & names, + vector & descriptions, + unsigned int & maxnamelen) const { unsigned int namelen = 0; // the longest option name string + names.clear(); + descriptions.clear(); for (std::set::const_iterator i = options.begin(); i != options.end(); ++i) { - string names = usagestr(*i); - if (names.size() > namelen) - namelen = names.size(); + string name = usagestr(*i); + if (name.size() > namelen) + namelen = name.size(); + names.push_back(name); + descriptions.push_back(i->description); } - - // " --long [ -s ] description goes here" - // ^ ^^ ^^ ^^ ^ - // | | \ namelen / | | \ descwidth /| <- edge of screen - // ^^^^ ^^^^ - // pre_indent space - string result; - int pre_indent = 2; // empty space on the left - int space = 2; // space after the longest option, before the description - int termwidth = guess_terminal_width(); - int descindent = pre_indent + namelen + space; - int descwidth = termwidth - descindent; - for (std::set::const_iterator i = options.begin(); - i != options.end(); ++i) - { - string names = usagestr(*i); - if (names.empty()) - continue; - - result += string(pre_indent, ' ') - + names + string(namelen - names.size(), ' '); - - if (!i->description.empty()) - { - result += string(space, ' '); - result += format_text(i->description, descindent, descindent); - } - - result += '\n'; - } - return result; + maxnamelen = namelen; } } // namespace option ============================================================ --- option.hh 82dad5ae3a07d89df3078564498e9b49d4cc092a +++ option.hh 65d3756b0f12e7741a308536a0d51cee021189c1 @@ -118,7 +118,9 @@ namespace option { concrete_option_set operator | (concrete_option_set const & other) const; void reset() const; - std::string get_usage_str() const; + void get_usage_strings(std::vector & names, + std::vector & descriptions, + unsigned int & maxnamelen) const; void from_command_line(args_vector & args, bool allow_xargs = true); void from_command_line(int argc, char const * const * argv); void from_key_value_pairs(std::vector > const & keyvals); ============================================================ --- paths.cc ffe8973a49302cc9e5fc2dbf74927dd2c00bf458 +++ paths.cc 12ae002c3b8ada76d9367e7fb89db07de44eddb3 @@ -14,7 +14,6 @@ #include "paths.hh" #include "file_io.hh" #include "charset.hh" -#include "lua.hh" using std::exception; using std::ostream; @@ -343,16 +342,6 @@ normalize_path(string const & in) return leader; } -LUAEXT(normalize_path, ) -{ - const char *pathstr = luaL_checkstring(LS, -1); - E(pathstr, origin::user, - F("%s called with an invalid parameter") % "normalize_path"); - - lua_pushstring(LS, normalize_path(string(pathstr)).c_str()); - return 1; -} - static void normalize_external_path(string const & path, string & normalized, bool to_workspace_root) { ============================================================ --- project.cc 1ca851ed8ce7b4acb24a0b0d7c5200359aa55619 +++ project.cc ceed59b027d62e68f882420c3323ac8e48a5f7da @@ -12,7 +12,9 @@ #include "lua_hooks.hh" #include "keys.hh" #include "options.hh" +#include "ui.hh" #include "vocab_cast.hh" +#include "simplestring_xform.hh" using std::string; using std::set; @@ -369,7 +371,56 @@ project_t::put_cert(key_store & keys, put_simple_revision_cert(db, keys, id, name, value); } +// These should maybe be converted to member functions. +string +describe_revision(project_t & project, revision_id const & id) +{ + cert_name author_name(author_cert_name); + cert_name date_name(date_cert_name); + + string description; + + description += encode_hexenc(id.inner()(), id.inner().made_from); + + // append authors and date of this revision + vector< revision > tmp; + project.get_revision_certs_by_name(id, author_name, tmp); + for (vector< revision >::const_iterator i = tmp.begin(); + i != tmp.end(); ++i) + { + description += " "; + description += i->inner().value(); + } + project.get_revision_certs_by_name(id, date_name, tmp); + for (vector< revision >::const_iterator i = tmp.begin(); + i != tmp.end(); ++i) + { + description += " "; + description += i->inner().value(); + } + + return description; +} + +void +notify_if_multiple_heads(project_t & project, + branch_name const & branchname, + bool ignore_suspend_certs) +{ + set heads; + project.get_branch_heads(branchname, heads, ignore_suspend_certs); + if (heads.size() > 1) { + string prefixedline; + prefix_lines_with(_("note: "), + _("branch '%s' has multiple heads\n" + "perhaps consider '%s merge'"), + prefixedline); + P(i18n_format(prefixedline) % branchname % ui.prog_name); + } +} + + // Local Variables: // mode: C++ // fill-column: 76 ============================================================ --- project.hh b0977a2c8ba8a5ccd65e07087c8bcf689fbc6b21 +++ project.hh f67968192cfc5e513d2281f384c29b76eefdd5e7 @@ -101,6 +101,13 @@ public: cert_value const & value); }; +std::string +describe_revision(project_t & project, revision_id const & id); + +void +notify_if_multiple_heads(project_t & project, branch_name const & branchname, + bool ignore_suspend_certs); + #endif ============================================================ --- tester.cc d879cd83eb36960f1eb5dc12211c95bebaac7ee3 +++ tester.cc 3c883424385bb3a897f0c382910b392875eab3f4 @@ -488,6 +488,16 @@ LUAEXT(get_source_dir, ) return 1; } +LUAEXT(normalize_path, ) +{ + const char *pathstr = luaL_checkstring(LS, -1); + E(pathstr, origin::user, + F("%s called with an invalid parameter") % "normalize_path"); + + lua_pushstring(LS, system_path(pathstr).as_external().c_str()); + return 1; +} + LUAEXT(save_env, ) { orig_env_vars.clear(); ============================================================ --- transforms.cc 627ab523898554957d55dc2e9138bfaadf8bdec5 +++ transforms.cc 21d1f172b5413b4123358f3c44135b6cd44ae5d5 @@ -15,7 +15,6 @@ #include "gzip.hh" #include "transforms.hh" -#include "xdelta.hh" #include "char_classifiers.hh" using std::string; @@ -309,20 +308,6 @@ UNIT_TEST(transform, enc) UNIT_TEST_CHECK(d2 == d1); } -UNIT_TEST(transform, rdiff) -{ - data dat1(string("the first day of spring\nmakes me want to sing\n"), - origin::internal); - data dat2(string("the first day of summer\nis a major bummer\n"), - origin::internal); - delta del; - diff(dat1, dat2, del); - - data dat3; - patch(dat1, del, dat3); - UNIT_TEST_CHECK(dat3 == dat2); -} - UNIT_TEST(transform, calculate_ident) { data input(string("the only blender which can be turned into the most powerful vaccum cleaner"), ============================================================ --- ui.cc 646fea920cb0481004997c15fe10f4a829faf7d2 +++ ui.cc cbee345d5859b445b47730c1cb779cc02da6d9a1 @@ -762,13 +762,57 @@ format_text(string const & text, size_t return formatted; } -// See description for the other format_text below for more details. +// See description for the other format_text above for more details. string format_text(i18n_format const & text, size_t const col, size_t curcol) { return format_text(text.str(), col, curcol); } +// Format a block of options and their descriptions. +string +format_usage_strings(vector const & names, + vector const & descriptions, + unsigned int namelen) +{ + // " --long [ -s ] description goes here" + // ^ ^^ ^^ ^^ ^ + // | | \ namelen / | | \ descwidth /| <- edge of screen + // ^^^^ ^^^^ + // pre_indent space + string result; + int pre_indent = 2; // empty space on the left + int space = 2; // space after the longest option, before the description + int termwidth = guess_terminal_width(); + int descindent = pre_indent + namelen + space; + int descwidth = termwidth - descindent; + + I(names.size() == descriptions.size()); + + vector::const_iterator name; + vector::const_iterator desc; + for (name = names.begin(), desc = descriptions.begin(); name != names.end(); + ++name, ++desc) + { + if (name->empty()) + continue; + + result += string(pre_indent, ' ') + + *name + string(namelen - name->size(), ' '); + + if (!desc->empty()) + { + result += string(space, ' '); + result += format_text(*desc, descindent, descindent); + } + + result += '\n'; + } + result += '\n'; + return result; +} + + // Local Variables: // mode: C++ // fill-column: 76 ============================================================ --- ui.hh ed0c01eb8218bb8dabd9a999aabfe1c50d54b085 +++ ui.hh 2ca5d4fa5a7b2c36a89fa5f84c36603b9818d861 @@ -14,6 +14,8 @@ // interface. the global user_interface object 'ui' owns cerr, so // no writing to it directly! +#include "vector.hh" + struct i18n_format; class system_path; @@ -92,6 +94,11 @@ std::string format_text(i18n_format cons std::string format_text(i18n_format const & text, size_t const col = 0, size_t curcol = 0); +std::string +format_usage_strings(std::vector const & names, + std::vector const & descriptions, + unsigned int namelen); + // Local Variables: // mode: C++ // fill-column: 76 ============================================================ --- unit_tests.cc 5baf8acaed8b1a76321a7b8e95de47414bf97de7 +++ unit_tests.cc 475fa100e0921123927e14b005c6d83735057980 @@ -23,7 +23,6 @@ #include "option.hh" #include "unit_tests.hh" #include "sanity.hh" -#include "ui.hh" #include "current_exception.hh" #include "botan_pipe_cache.hh" @@ -160,8 +159,6 @@ int main(int argc, char * argv[]) bool help(false); string test_to_run; - ui.initialize(); - ui.prog_name = argv[0]; global_sanity.initialize(argc, argv, "C"); // we didn't call setlocale try @@ -285,6 +282,21 @@ localize_monotone() { } +// Global sanity object. We don't want to depend on ui. +struct unit_tester_sanity : public sanity +{ + void inform_log(std::string const &msg) + { cout << msg; } + void inform_message(std::string const &msg) + { cout << msg; } + void inform_warning(std::string const &msg) + { cerr << "warning: " << msg; } + void inform_error(std::string const &msg) + { cerr << "error: " << msg; } +}; +unit_tester_sanity real_sanity; +sanity & global_sanity = real_sanity; + // These are tests of the unit testing mechanism itself. They would all // fail, but we make use of a special mechanism to convert that failure // into a success. Since we don't want that mechanism used elsewhere, ============================================================ --- xdelta.cc 6c75c90672859828c596acd6329f5c18d0bec659 +++ xdelta.cc 3bfd03cf01ed8e07764ce268820407d13a583c22 @@ -823,6 +823,20 @@ invert_xdelta(string const & old_str, #include "unit_tests.hh" +UNIT_TEST(xdelta, basic) +{ + data dat1(string("the first day of spring\nmakes me want to sing\n"), + origin::internal); + data dat2(string("the first day of summer\nis a major bummer\n"), + origin::internal); + delta del; + diff(dat1, dat2, del); + + data dat3; + patch(dat1, del, dat3); + UNIT_TEST_CHECK(dat3 == dat2); +} + string apply_via_normal(string const & base, string const & delta) {