# from [b0c21243f7f344006c05aebb2b4f5c9f9ee6ccfd]
# to [221b45f4b0c008da11516b07ca2048fc2a21ce03]
---
+++
@@ -0,0 +1,983 @@
+// Copyright (C) 2002 Graydon Hoare
+// 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 "options_applicator.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
+#include
+#endif
+
+#ifdef WIN32
+#include
+#endif
+
+#include
+#include
+
+using std::string;
+using std::stringstream;
+using std::vector;
+using std::set;
+using std::ostream;
+using std::make_pair;
+using std::cout;
+using boost::lexical_cast;
+
+//
+// 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(vcs, "vcs", "", CMD_REF(__root__),
+ N_("Commands for interaction with other version control systems"),
+ "");
+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 {
+
+ void remove_command_name_from_args(command_id const & ident,
+ args_vector & args,
+ size_t invisible_length)
+ {
+ MM(ident);
+ MM(args);
+ MM(invisible_length);
+ I(ident.empty() || args.size() >= ident.size() - invisible_length);
+ for (args_vector::size_type i = invisible_length; i < ident.size(); i++)
+ {
+ I(ident[i]().find(args[0]()) == 0);
+ args.erase(args.begin());
+ }
+ }
+
+ void reapply_options(app_state & app,
+ command const * cmd,
+ command_id const & cmd_ident,
+ command const * subcmd,
+ command_id const & subcmd_full_ident,
+ size_t subcmd_invisible_length,
+ args_vector const & subcmd_cmdline,
+ vector > const * const separate_params)
+ {
+ I(cmd);
+ options::opts::all_options().instantiate(&app.opts).reset();
+
+ cmd->preset_options(app.opts);
+
+ option::concrete_option_set optset
+ = (options::opts::globals() | cmd->opts())
+ .instantiate(&app.opts);
+
+ optset.from_command_line(app.reset_info.default_args);
+
+ if (subcmd)
+ {
+ args_vector subcmd_defaults;
+ app.lua.hook_get_default_command_options(subcmd_full_ident,
+ subcmd_defaults);
+ (options::opts::globals() | subcmd->opts())
+ .instantiate(&app.opts)
+ .from_command_line(subcmd_defaults);
+ }
+
+ // at this point we process the data from _MTN/options if
+ // the command needs it.
+ if ((subcmd ? subcmd : cmd)->use_workspace_options())
+ {
+ workspace::check_format();
+ workspace::get_options(app.opts);
+ }
+
+ optset.from_command_line(app.reset_info.cmdline_args);
+
+ if (subcmd)
+ {
+ app.opts.args.clear();
+ option::concrete_option_set subcmd_optset
+ = (options::opts::globals() | subcmd->opts())
+ .instantiate(&app.opts);
+ if (!separate_params)
+ {
+ /* the first argument here is only ever modified if the second is 'true' */
+ subcmd_optset.from_command_line(const_cast(subcmd_cmdline));
+ }
+ else
+ {
+ subcmd_optset.from_key_value_pairs(*separate_params);
+ app.opts.args = subcmd_cmdline;
+ }
+ remove_command_name_from_args(subcmd_full_ident, app.opts.args,
+ subcmd_invisible_length);
+ }
+ else
+ {
+ remove_command_name_from_args(cmd_ident, app.opts.args);
+ }
+ }
+
+ // monotone.cc calls this function after option processing.
+ void process(app_state & app, command_id const & ident,
+ args_vector const & args)
+ {
+ static bool process_called(false);
+ I(!process_called);
+ process_called = true;
+
+ command const * cmd = CMD_REF(__root__)->find_command(ident);
+ app.reset_info.cmd = cmd;
+
+ 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));
+
+ if (!cmd->is_leaf())
+ {
+ // args used in the command name have not been stripped yet
+ remove_command_name_from_args(ident, app.opts.args);
+
+ E(!args.empty(), origin::user,
+ F("no subcommand specified for '%s'") % visibleid);
+
+ E(false, origin::user,
+ F("could not match '%s' to a subcommand of '%s'") %
+ join_words(args) % visibleid);
+ }
+
+ L(FL("executing command '%s'") % visibleid);
+
+ reapply_options(app, cmd, ident);
+
+ // intentional leak
+ // we don't want the options to be reset, so don't destruct this
+ new options_applicator(app.opts, options_applicator::for_primary_cmd);
+
+ 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';
+ }
+
+ class cmd_ptr_compare
+ {
+ public:
+ bool operator()(command const * const a, command const * const b) const
+ {
+ return a->primary_name()() < b->primary_name()();
+ }
+ };
+
+ static void explain_children(command::children_set const & children,
+ bool show_hidden_commands,
+ 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() && !show_hidden_commands)
+ 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(), cmd_ptr_compare());
+
+ 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(show_hidden_commands), ", ")(),
+ 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,
+ bool show_hidden_commands,
+ 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':") %
+ prog_name % visibleid)
+ << "\n\n";
+ else if (!lines.empty())
+ out << format_text(F("Syntax specific to '%s %s':") %
+ 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(), show_hidden_commands, 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':") %
+ 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,
+ bool show_hidden_commands,
+ 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(),
+ show_hidden_commands,
+ 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, show_hidden_commands, 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::show_hidden_commands)
+{
+ 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 | double-throw | 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);
+ }
+ else if (idx(args,0)() == "double-throw")
+ {
+ // This code is rather picky, for example I(false) in the destructor
+ // won't always work like it should; see http://bugs.debian.org/516862
+ class throwing_dtor
+ {
+ public:
+ throwing_dtor() {}
+ ~throwing_dtor()
+ {
+ throw std::exception();
+ }
+ };
+ throwing_dtor td;
+ throw std::exception();
+ }
+#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
+}
+
+static string
+man_italic(string const & content)
+{
+ return "\\fI" + content + "\\fP";
+}
+
+static string
+man_bold(string const & content)
+{
+ return "\\fB" + content + "\\fP";
+}
+
+static string
+man_definition(vector const & labels, string const & content, int width = -1)
+{
+ string out;
+ out += ".IP \"" + (*labels.begin()) + "\"";
+
+ if (width != -1)
+ out += " " + lexical_cast(width);
+ out += "\n";
+
+ if (labels.size() > 1)
+ {
+ out += ".PD 0\n";
+ for (vector::const_iterator i = labels.begin() + 1;
+ i < labels.end(); ++i)
+ {
+ out += ".IP \"" + (*i) + "\"\n";
+ }
+ out += ".PD\n";
+ }
+ out += content;
+ if (content.rfind('\n') != (content.size() - 1))
+ out += "\n";
+ return out;
+}
+
+static string
+man_definition(string const & label, string const & content, int width = -1)
+{
+ vector labels;
+ labels.push_back(label);
+ return man_definition(labels, content, width);
+}
+
+static string
+man_indent(string const & content)
+{
+ return ".RS\n" + content + ".RE\n";
+}
+
+static string
+man_subsection(string const & content)
+{
+ return ".SS \"" + content + "\"\n";
+}
+
+static string
+man_section(string const & content)
+{
+ return ".SH \"" + uppercase(content) + "\"\n";
+}
+
+static string
+man_title(string const & title)
+{
+ return ".TH \"" + title + "\" 1 "+
+ "\"" + date_t::now().as_formatted_localtime("%Y-%m-%d") + "\" " +
+ "\"" + PACKAGE_STRING + "\"\n";
+}
+
+static string
+get_options_string(options::options_type const & optset, options & opts, int width = -1)
+{
+ vector names;
+ vector descriptions;
+ unsigned int maxnamelen;
+
+ optset.instantiate(&opts).get_usage_strings(
+ names, descriptions, maxnamelen, opts.show_hidden_commands
+ );
+
+ string out;
+ vector::const_iterator name;
+ vector::const_iterator desc;
+ for (name = names.begin(), desc = descriptions.begin();
+ name != names.end(); ++name, ++desc)
+ {
+ if (name->empty())
+ continue;
+ out += man_definition(*name, *desc, width);
+ }
+ return out;
+}
+
+static string
+get_commands(options & opts, commands::command const * group)
+{
+ vector sorted_commands;
+ commands::command::children_set commands = group->children();
+ for (commands::command::children_set::const_iterator i = commands.begin();
+ i != commands.end(); ++i)
+ {
+ commands::command * command = *i;
+ if (command->hidden() && !opts.show_hidden_commands)
+ continue;
+
+ // there are no top level commands, so this must be an
+ // empty group - skip it
+ if (group->is_leaf())
+ continue;
+
+ sorted_commands.push_back(command);
+ }
+
+ sort(sorted_commands.begin(),
+ sorted_commands.end(),
+ commands::cmd_ptr_compare());
+
+ string out;
+ for (vector::const_iterator i = sorted_commands.begin();
+ i != sorted_commands.end(); ++i)
+ {
+ commands::command const * command = *i;
+
+ // don't print sub groups explicitely, just their leaves
+ if (!command->is_leaf())
+ {
+ out += get_commands(opts, command);
+ continue;
+ }
+
+ // this builds a list of already formatted command calls
+ // which are used as label for the specific command section
+ vector cmd_calls;
+
+ //
+ // newline characters in the parameter section mark
+ // alternative call syntaxes which we expand here, i.e.
+ // a command "do-foo" with an alias of "foo" and an argument
+ // list of "BAR\nBAR BAZ" will be expanded to
+ //
+ // do-foo BAR
+ // do-foo BAR BAZ
+ // foo BAR
+ // foo BAR BAZ
+ //
+ vector params;
+ if (!command->params().empty())
+ split_into_lines(command->params(), params);
+
+ vector main_ident = command->ident();
+ typedef set > ident_set;
+ ident_set idents;
+
+ commands::command::names_set allnames = command->names();
+ for (set::const_iterator i = allnames.begin();
+ i != allnames.end(); ++i)
+ {
+ vector full_ident;
+ for (vector::const_iterator j = main_ident.begin() + 1;
+ j < main_ident.end() - 1; ++j)
+ {
+ full_ident.push_back((*j)());
+ }
+ full_ident.push_back((*i)());
+ idents.insert(full_ident);
+ }
+
+ for (ident_set::const_iterator i = idents.begin();
+ i != idents.end(); ++i)
+ {
+ string call, name;
+ // cannot use join_words here, since this only
+ // works on containers
+ join_lines(*i, name, " ");
+
+ if (params.size() == 0)
+ {
+ call = man_bold(name);
+ cmd_calls.push_back(call);
+ continue;
+ }
+
+ for (vector::const_iterator j = params.begin();
+ j < params.end(); ++j)
+ {
+ call = man_bold(name) + " " + *j;
+ cmd_calls.push_back(call);
+ }
+ }
+
+ string cmd_desc;
+ cmd_desc += command->desc() + "\n";
+
+ // this prints an indented list of available command options
+ options::options_type cmd_options =
+ commands::command_options(main_ident);
+ if (!cmd_options.empty())
+ {
+ cmd_desc += man_indent(get_options_string(cmd_options, opts, 4));
+ }
+
+ // compile everything into a man definition
+ out += man_definition(cmd_calls, cmd_desc, 4);
+ }
+
+ return out;
+}
+
+static string
+get_command_groups(options & opts)
+{
+ vector sorted_groups;
+ commands::command::children_set groups = CMD_REF(__root__)->children();
+ for (commands::command::children_set::const_iterator i = groups.begin();
+ i != groups.end(); ++i)
+ {
+ commands::command * group = *i;
+ if (group->hidden() && !opts.show_hidden_commands)
+ continue;
+
+ // there are no top level commands, so this must be an
+ // empty group - skip it
+ if (group->is_leaf())
+ continue;
+
+ sorted_groups.push_back(group);
+ }
+
+ sort(sorted_groups.begin(),
+ sorted_groups.end(),
+ commands::cmd_ptr_compare());
+
+ string out;
+ for (vector::const_iterator i = sorted_groups.begin();
+ i != sorted_groups.end(); ++i)
+ {
+ commands::command const * group = *i;
+ out += man_subsection(
+ (F("command group '%s'") % group->primary_name()).str()
+ );
+ out += group->desc() + "\n";
+
+ out += get_commands(opts, group);
+ }
+
+ return out;
+}
+
+CMD_NO_WORKSPACE(manpage, "manpage", "", CMD_REF(informative), "",
+ N_("Displays monotone's command help as manual page"),
+ "",
+ options::opts::show_hidden_commands)
+{
+ stringstream ss;
+ ss << man_title("monotone");
+ ss << man_section(_("Name"));
+
+ ss << _("monotone - a distributed version control system") << "\n";
+ ss << man_section(_("Synopsis"));
+ ss << man_bold(prog_name) << " "
+ << man_italic(_("[options...] command [arguments...]"))
+ << "\n";
+
+ ss << man_section(_("Description"));
+ ss << _("monotone is a highly reliable, very customizable distributed "
+ "version control system that provides lightweight branches, "
+ "history-sensitive merging and a flexible trust setup. "
+ "monotone has an easy-to-learn command set and comes with a rich "
+ "interface for scripting purposes and thorough documentation.")
+ << "\n\n";
+ ss << (F("For more information on monotone, visit %s.")
+ % man_bold("http://www.monotone.ca")).str()
+ << "\n\n";
+ ss << (F("The complete documentation, including a tutorial for a quick start "
+ "with the system, can be found online on %s.")
+ % man_bold("http://www.monotone.ca/docs")).str() << "\n";
+
+ ss << man_section(_("Global Options"));
+ ss << get_options_string(options::opts::globals(), app.opts, 25) << "\n";
+
+ ss << man_section(_("Commands"));
+ ss << get_command_groups(app.opts);
+
+ ss << man_section(_("See Also"));
+ ss << (F("info %s and the documentation on %s")
+ % prog_name % man_bold("http://monotone.ca/docs")).str() << "\n";
+
+ ss << man_section("Bugs");
+ ss << (F("Please report bugs to %s.")
+ % man_bold("http://savannah.nongnu.org/bugs/?group=monotone")).str()<< "\n";
+
+ ss << man_section("Authors");
+ ss << _("monotone was written originally by Graydon Hoare "
+ " in 2004 and has since then received "
+ "numerous contributions from many individuals. "
+ "A complete list of authors can be found in AUTHORS.")
+ << "\n\n";
+ ss << _("Nowadays, monotone is maintained by a collective of enthusiastic "
+ "programmers, known as the monotone developement team.") << "\n";
+
+ ss << man_section("Copyright");
+ ss << (F("monotone and this man page is Copyright (c) 2004 - %s by "
+ "the monotone development team.")
+ % date_t::now().as_formatted_localtime("%Y")).str() << "\n";
+
+ if (!isatty(STDOUT_FILENO))
+ {
+ cout << ss.str();
+ return;
+ }
+
+ string cmd;
+ E(app.lua.hook_get_man_page_formatter_command(cmd) && !cmd.empty(),
+ origin::user, F("no man page formatter command configured"));
+
+ FILE * fp = popen(cmd.c_str(), "w");
+ E(fp != NULL, origin::system,
+ F("could not execute man page formatter command '%s': %s")
+ % cmd % strerror(errno));
+
+ I(fprintf(fp, ss.str().c_str()) != -1);
+ pclose(fp);
+}
+
+// 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: