# from [221b45f4b0c008da11516b07ca2048fc2a21ce03] # to [a8f6acf503875a2c432c971ea16a86b7f31e88da] # # patch "options_list.hh" # from [fa4f0a044e96e403f838eba547e1a1d83219071c] # to [309fa72238b0810abd6be18d04264fcfe2ac7ee6] --- +++ @@ -0,0 +1,985 @@ +// 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 +#else +#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_PRESET_OPTIONS(manpage) +{ + opts.formatted = isatty(STDOUT_FILENO); +} +CMD_NO_WORKSPACE(manpage, "manpage", "", CMD_REF(informative), "", + N_("Generate a manual page from monotone's command help"), + "", + options::opts::show_hidden_commands | options::opts::formatted) +{ + 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 (!app.opts.formatted) + { + 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: --- options_list.hh +++ options_list.hh @@ -0,0 +1,658 @@ +// Copyright (C) 2006 Timothy Brownawell +// 2008-2010 Stephen Leake +// +// 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. + +/* + * This is a list of all options that monotone can take, what variables + * they get put into, and how they get there. There are 6 important macros + * available here (and only here): + * + * OPTSET(name) + * Defines a set of related options, which can easily be allowed for + * a particular command or reset together. It is named + * 'options::opts::name'. + * + * This is used primarily for convenience in specifying options for the + * CMD macro. Especially if several options always go together, they + * can be grouped into an optset and then only that optset has to be + * specified as an argument to the CMD macro when declaring commands + * that use all of those options. + * + * This is also used for resettable options; any option that has a + * reset flag needs to be directly in an optset that *only* contains + * (directly or through OPTSET_REL) the optvar's that should be + * re-initialized when the reset flag is given. Because the + * SIMPLE_OPTION family all declare an optset containing only the + * one option and its one optvar, you don't typically need to care + * about this. + * + * OPTSET_REL(parent, child) + * Declare a relationship between two optsets, so that if the parent + * is reset or allowed for a command the child will also be. + * + * For example "diff" takes all of the options that "automate diff" + * takes, plus some additional ones. So there is a line below, + * "OPTSET_REL(diff_options, au_diff_options)", and then "diff" takes + * options::opts::diff_options and "automate diff" takes + * options::opts::au_diff_options. Options that only apply to "diff" + * go in the diff_options optset, while options that apply to both + * go in the au_diff_options optset. + * + * OPTVAR(optset, type, name, default) + * Defines a variable 'type options::name' which is initialized to + * 'type (default)' and belongs to the named optset. When the optset + * is reset, this variable will be reset to 'type (default)'. + * + * OPTION(optset, name, hasarg, optstring, description) + * Declare an option named 'options::opts::name', which belongs to the + * given optset. 'optstring' can look like "foo", in which case it is + * specified as "--foo", or it can look like "foo,f", in which case it + * is specified as either "--foo" or "-f". The description is a + * translatable help text. 'hasarg' is a bool indicating whether this + * option takes an argument. + * + * Some expansions of this macro expect a function body, which looks + * like 'void (std::string const & arg)'. This is the 'setter' function + * for this option, and is called when the option is parsed. If the + * option was declared to not take an argument, 'arg' is empty. + * Otherwise, it is the given argument. In any case this function + * should set the option variables for this option and throw a + * 'bad_arg_internal' if this fails. The variables that are set must be + * part of the same optset as the option, or they won't get reset + * properly. When the function body is needed, 'option_bodies' will + * be defined. + * + * + * HIDE(option) + * Do not show the named option in normal help output. Hidden options + * are shown by the --hidden option. + * + * In general, options should be hidden if they are introduced for + * testing purposes. + * + * DEPRECATE(option, reason, deprecated_in, will_remove_in) + * Do not show the named option in help output (even with --hidden), and + * give a warning if it is used. The reason should be + * gettext_noopt("some text here") as it is translatable. + * + * + * Option Strings + * + * Options can have a long name, a short name, and a 'reset' name. The + * long and short names run the function body in the '#ifdef + * option_bodies'. The 'reset' name is slightly different, it makes the + * optset that the option belongs to get reset. This means that if you + * have several related options in the same optset, you probably *don't* + * want to specify a reset name for any of them. + * + * If you want an option to belong to an optset and also be resettable, + * you can use OPTSET_REL to make the desired optset include the option's + * personal optset. + * + * The option string format is "long,s/reset". "--long" and "-s" are + * the long and short names, and set the option. "--reset" is the + * reset name, and resets the option. An option *must* have a long + * and/or short name, but isn't required to have a reset name. So + * "/foo" is invalid, but "foo,f", "foo/no-foo", "f/no-f", and + * "foo,f/no-foo" are all allowed. + */ + +/* + * If you want different *default* values for an option based on what command + * is being run (for example --with-header/--no-with-header for 'diff' and + * 'automate diff'), use CMD_PRESET_OPTIONS(cmdname) { ... } defined in cmd.hh. + * It doesn't go in this file, but rather in the file where the command is + * defined (ideally immediately before the CMD() declaration for that same + * command, just to be consistent). + */ + +#ifdef option_bodies +template +void set_simple_option(T & t, std::string const & arg) +{ t = T(arg, origin::user); } +template +void set_simple_option(std::vector & t, std::string const & arg) +{ t.push_back(T(arg, origin::user)); } +template +void set_simple_option(std::set & t, std::string const & arg) +{ t.insert(T(arg, origin::user)); } +template<> +void set_simple_option(bool & t, std::string const & arg) +{ t = true; } +void set_simple_option(u8 & t, std::string const & arg) +{ + long l = boost::lexical_cast(arg); + if (l < 0 || l > 255) + throw bad_arg_internal(F("must be between 0 and 255").str()); + else + t = (u8)l; +} +template<> +void set_simple_option(std::string & t, std::string const & arg) +{ t = arg; } +void set_simple_option(date_t & t, std::string const & arg) +{ + try { t = date_t(arg); } + catch (std::exception & e) + { throw bad_arg_internal(e.what()); } +} +template<> +void set_simple_option(std::vector & t, std::string const & arg) +{ t.push_back(arg); } +template<> +void set_simple_option(std::set & t, std::string const & arg) +{ t.insert(arg); } +template<> +void set_simple_option(enum_string & t, std::string const & arg) +{ t.set(arg); } +template<> +void set_simple_option(enum_string_set & t, std::string const & arg) +{ t.add(arg); } + +# define SIMPLE_OPTION_BODY(name) { set_simple_option(name, arg); } +#else +# define SIMPLE_OPTION_BODY(name) +#endif +/* + * This is a 'magic' option, and Does The Right Thing based on its data type: + * * If it's a bool, it will be true if the option is given and false if + * not given (or reset), and will not take an argument. + * * If it's a string or vocab type, it will be set to the argument if + * given, and set to the empty value (default constructor) if reset + * or not given. + * * If it's a container (vector/set) of strings or vocab types, each + * time the option is given the argument will be added to the collection + * (with push_back or insert), and the collection will be empty if the + * option is not given or is reset. + */ +#define SIMPLE_INITIALIZED_OPTION(name, optstring, type, init, description) \ + OPTSET(name) \ + OPTVAR(name, type, name, init) \ + OPTION(name, name, has_arg(), optstring, description) \ + SIMPLE_OPTION_BODY(name) + +#define SIMPLE_OPTION(name, optstring, type, description) \ + SIMPLE_INITIALIZED_OPTION(name, optstring, type, , description) + +#define GROUPED_SIMPLE_OPTION(group, name, optstring, type, description) \ + OPTSET_REL(group, name) \ + SIMPLE_OPTION(name, optstring, type, description) + + +// because 'default_' is constructor arguments, and may need to be a list +// This doesn't work if fed through SIMPLE_INITIALIZED_OPTION (it wouldn't +// work with the other 2 SIMPLE_OPTION macros either, but it wouldn't make +// sense in the first place with those). +#define COMMA , + +OPTSET(globals) + +// this is a magic option +OPTVAR(globals, args_vector, args, ) +OPTION(globals, positionals, true, "--", "") +#ifdef option_bodies +{ + args.push_back(arg_type(arg, origin::user)); +} +#endif +// this is a more magic option +OPTION(globals, xargs, true, "xargs,@", + gettext_noop("insert command line arguments taken from the given file")) +#ifdef option_bodies +{ +} +#endif + + +SIMPLE_OPTION(author, "author", utf8, gettext_noop("override author for commit")) + +SIMPLE_OPTION(automate_stdio_size, "automate-stdio-size", + restricted_long<1>, + gettext_noop("block size in bytes for \"automate stdio\" output")) + +SIMPLE_OPTION(auto_update, "update/no-update", bool, + gettext_noop("automatically update the workspace, if it is clean and the base " + "revision is a head of an affected branch")) + +OPTSET(bind_opts) +GROUPED_SIMPLE_OPTION(bind_opts, bind_uris, "bind", std::vector, + gettext_noop("address:port to listen on (default :4691)")) +HIDE(no_transport_auth) +GROUPED_SIMPLE_OPTION(bind_opts, no_transport_auth, "no-transport-auth", bool, + gettext_noop("disable transport authentication")) +HIDE(bind_stdio) +GROUPED_SIMPLE_OPTION(bind_opts, bind_stdio, "stdio", bool, + gettext_noop("serve netsync on stdio")) + +HIDE(max_netsync_version) +SIMPLE_OPTION(max_netsync_version, "max-netsync-version", u8, + gettext_noop("cause monotone to lie about the maximum netsync " + "protocol version that it supports, mostly for debugging")) +HIDE(min_netsync_version) +SIMPLE_OPTION(min_netsync_version, "min-netsync-version", u8, + gettext_noop("cause monotone to lie about the minimum netsync " + "protocol version it supports, useful for debugging or " + "if you want to prevent use of older protocol versions")) + +SIMPLE_OPTION(remote_stdio_host, "remote-stdio-host", arg_type, + gettext_noop("sets the host (and optionally the port) for a " + "remote netsync action")) + +SIMPLE_OPTION(branch, "branch,b", branch_name, + gettext_noop("select branch cert for operation")) + +SIMPLE_OPTION(brief, "brief/no-brief", bool, + gettext_noop("print a brief version of the normal output")) + +SIMPLE_OPTION(revs_only, "revs-only", bool, + gettext_noop("annotate using full revision ids only")) + +OPTVAR(globals, system_path, conf_dir, get_default_confdir() COMMA origin::user) +OPTION(globals, conf_dir, true, "confdir", + gettext_noop("set location of configuration directory")) +#ifdef option_bodies +{ + conf_dir = system_path(arg, origin::user); + if (!key_dir_given) + key_dir = (conf_dir / "keys"); +} +#endif + +GROUPED_SIMPLE_OPTION(globals, no_default_confdir, "no-default-confdir/allow-default-confdir", bool, + gettext_noop("forbid use of the default confdir")) + +SIMPLE_OPTION(date, "date", date_t, + gettext_noop("override date/time for commit")) + +OPTSET(date_formats) +OPTSET_REL(globals, date_formats) +OPTVAR(date_formats, std::string, date_fmt, ) +OPTION(date_formats, date_fmt, true, "date-format", + gettext_noop("strftime(3) format specification for printing dates")) +#ifdef option_bodies +{ + date_fmt = arg; + no_format_dates = false; +} +#endif +GROUPED_SIMPLE_OPTION(date_formats, no_format_dates, + "no-format-dates", bool, + gettext_noop("print date certs exactly as stored in the database")) + + +OPTVAR(globals, db_type, dbname_type, ) +OPTVAR(globals, std::string, dbname_alias, ) +OPTVAR(globals, system_path, dbname, ) +OPTION(globals, dbname, true, "db,d", gettext_noop("set name of database")) +#ifdef option_bodies +{ + if (arg == memory_db_identifier) + { + dbname_type = memory_db; + } + else if (arg.size() > 0 && arg.substr(0, 1) == ":") + { + dbname_alias = arg; + dbname_type = managed_db; + } + else + { + dbname = system_path(arg, origin::user); + dbname_type = unmanaged_db; + } +} +#endif + +HIDE(roster_cache_performance_log) +GROUPED_SIMPLE_OPTION(globals, roster_cache_performance_log, "roster-cache-performance-log", + system_path, + gettext_noop("log roster cache statistic to the given file")) + +SIMPLE_OPTION(depth, "depth", restricted_long<0>, + gettext_noop("limit the number of levels of directories to descend")) + + +OPTSET(diff_options) +OPTSET(au_diff_options) +OPTSET_REL(diff_options, au_diff_options) + +GROUPED_SIMPLE_OPTION(diff_options, external_diff_args, "diff-args", std::string, + gettext_noop("argument to pass external diff hook")) +GROUPED_SIMPLE_OPTION(au_diff_options, reverse, "reverse", bool, + gettext_noop("reverse order of diff")) +GROUPED_SIMPLE_OPTION(diff_options, no_show_encloser, "no-show-encloser/show-encloser", bool, + gettext_noop("do not show the function containing each block of changes")) +OPTSET_REL(au_diff_options, with_header) +SIMPLE_OPTION(with_header, "with-header/without-header", bool, + gettext_noop("show the matching cset in the diff header")) + +OPTVAR(diff_options, diff_type, diff_format, unified_diff) +OPTION(diff_options, diff_context, false, "context", + gettext_noop("use context diff format")) +#ifdef option_bodies +{ + diff_format = context_diff; +} +#endif +OPTION(diff_options, diff_external, false, "external", + gettext_noop("use external diff hook for generating diffs")) +#ifdef option_bodies +{ + diff_format = external_diff; +} +#endif +OPTION(diff_options, diff_unified, false, "unified", + gettext_noop("use unified diff format")) +#ifdef option_bodies +{ + diff_format = unified_diff; +} +#endif + + +SIMPLE_OPTION(diffs, "diffs/no-diffs", bool, gettext_noop("print diffs along with logs")) + +SIMPLE_OPTION(attrs_to_drop, "drop-attr", std::set, + gettext_noop("when rosterifying, drop attrs entries with the given key")) + +SIMPLE_OPTION(dryrun, "dry-run/no-dry-run", bool, + gettext_noop("don't perform the operation, just show what would have happened")) + +SIMPLE_OPTION(drop_bad_certs, "drop-bad-certs", bool, + gettext_noop("drop certs signed by keys we don't know about")) + +GROUPED_SIMPLE_OPTION(globals, dump, "dump", system_path, + gettext_noop("file to dump debugging log to, on failure")) + +SIMPLE_OPTION(exclude, "exclude", args_vector, + gettext_noop("leave out anything described by its argument")) +SIMPLE_OPTION(include, "include", args_vector, + gettext_noop("include anything described by its argument")) + +SIMPLE_OPTION(bookkeep_only, "bookkeep-only", bool, + gettext_noop("only update monotone's internal bookkeeping, not the filesystem")) + +SIMPLE_OPTION(move_conflicting_paths, + "move-conflicting-paths/no-move-conflicting-paths", + bool, + gettext_noop("move conflicting, unversioned paths into _MTN/resolutions " + "before proceeding with any workspace change")) + +OPTSET_REL(globals, ssh_sign) +SIMPLE_INITIALIZED_OPTION(ssh_sign, "ssh-sign", enum_string, "yes,no,only,check", + gettext_noop("controls use of ssh-agent. valid arguments are: " + "'yes' to use ssh-agent to make signatures if possible, " + "'no' to force use of monotone's internal code, " + "'only' to force use of ssh-agent, " + "'check' to sign with both and compare")) + +SIMPLE_OPTION(force_duplicate_key, "force-duplicate-key", bool, + gettext_noop("force genkey to not error out when the named key " + "already exists")) + + +GROUPED_SIMPLE_OPTION(globals, help, "help,h", bool, gettext_noop("display help message")) + +SIMPLE_OPTION(show_hidden_commands, "hidden/no-hidden", bool, + gettext_noop("show hidden commands and options")) + +GROUPED_SIMPLE_OPTION(globals, ignore_suspend_certs, "ignore-suspend-certs/no-ignore-suspend-certs", bool, + gettext_noop("do not ignore revisions marked as suspended")) + +GROUPED_SIMPLE_OPTION(globals, non_interactive, "non-interactive/interactive", bool, + gettext_noop("do not prompt the user for input")) + +GROUPED_SIMPLE_OPTION(globals, key, "key,k/use-default-key", external_key_name, + gettext_noop("sets the key for signatures, using either the key " + "name or the key hash")) + +GROUPED_SIMPLE_OPTION(globals, key_dir, "keydir", system_path, + gettext_noop("set location of key store")) + +SIMPLE_OPTION(keys_to_push, "key-to-push", std::vector, + gettext_noop("push the specified key even if it hasn't signed anything")) + +SIMPLE_OPTION(last, "last", restricted_long<1>, + gettext_noop("limit log output to the last number of entries")) + +GROUPED_SIMPLE_OPTION(globals, log, "log", system_path, + gettext_noop("file to write the log to")) + +OPTSET(messages) +GROUPED_SIMPLE_OPTION(messages, message, "message,m", std::vector, + gettext_noop("set commit changelog message")) +GROUPED_SIMPLE_OPTION(messages, msgfile, "message-file", utf8, + gettext_noop("set filename containing commit changelog message")) +HIDE(no_prefix) +GROUPED_SIMPLE_OPTION(messages, no_prefix, "no-prefix", bool, + gettext_noop("no prefix to message")) + +SIMPLE_OPTION(missing, "missing", bool, + gettext_noop("perform the operations for files missing from workspace")) + +SIMPLE_OPTION(next, "next", restricted_long<1>, + gettext_noop("limit log output to the next number of entries")) + +SIMPLE_OPTION(no_files, "no-files/files", bool, + gettext_noop("exclude files when printing logs")) + +SIMPLE_OPTION(no_graph, "no-graph/graph", bool, + gettext_noop("do not use ASCII graph to display ancestry")) + +SIMPLE_OPTION(no_ignore, "no-respect-ignore/respect-ignore", bool, + gettext_noop("do not ignore any files")) + +SIMPLE_OPTION(no_merges, "no-merges/merges", bool, + gettext_noop("exclude merges when printing logs")) + +#ifdef WIN32 +# define NORC_TEXT gettext_noop("do not load %APPDATA%\\monotone\\monotonerc or " \ + "_MTN\\monotonerc lua files") +#else +# define NORC_TEXT gettext_noop("do not load ~/.monotone/monotonerc or " \ + "_MTN/monotonerc lua files") +#endif +GROUPED_SIMPLE_OPTION(globals, norc, "no-standard-rcfiles/standard-rcfiles", bool, NORC_TEXT) +#undef NORC_TEXT + +GROUPED_SIMPLE_OPTION(globals, nostd, "no-builtin-rcfile/builtin-rcfile", bool, + gettext_noop("do not load the built-in lua file with the default hooks")) + +DEPRECATE(old_norc, gettext_noop("please use --no-standard-rcfiles instead"), 1.0, 2.0) +OPTION(globals, old_norc, false, "norc", + gettext_noop("old version of --no-standard-rcfiles")) +#ifdef option_bodies +{ norc = true; } +#endif +DEPRECATE(old_nostd, gettext_noop("please use --no-builtin-rcfile instead"), 1.0, 2.0) +OPTION(globals, old_nostd, false, "nostd", + gettext_noop("old version of --no-builtin-rcfile")) +#ifdef option_bodies +{ nostd = true; } +#endif + +GROUPED_SIMPLE_OPTION(globals, extra_rcfiles, "rcfile/clear-rcfiles", args_vector, + gettext_noop("load extra lua file")) + +SIMPLE_OPTION(pidfile, "pid-file/no-pid-file", system_path, + gettext_noop("record process id of server")) + +OPTSET(verbosity) +OPTSET_REL(globals, verbosity) +OPTVAR(verbosity, int, verbosity, 0) +OPTION(verbosity, set_verbosity, true, "verbosity", + gettext_noop("set verbosity level: 0 is default; 1 is print debug messages; " + "-1 is hide tickers and progress messages; -2 is also hide warnings")) +#ifdef option_bodies +{ + verbosity = boost::lexical_cast(arg); +} +#endif + +OPTION(globals, debug, false, "debug", + gettext_noop("print debug log to stderr while running (--verbosity=1)")) +#ifdef option_bodies +{ + verbosity = 1; +} +#endif + +SIMPLE_OPTION(full, "full/concise", bool, + gettext_noop("print detailed information")) + +OPTION(verbosity, quiet, false, "quiet,q", + gettext_noop("suppress verbose, informational and progress messages (set verbosity to -1)")) +#ifdef option_bodies +{ + if (verbosity > -1) + verbosity = -1; +} +#endif + +DEPRECATE(reallyquiet, gettext_noop("please use --verbosity=-2"), 1.0, 2.0) +OPTION(verbosity, reallyquiet, false, "reallyquiet", + gettext_noop("suppress warning, verbose, informational and progress messages (set verbosity to -2)")) +#ifdef option_bodies +{ + verbosity = -2; +} +#endif + +SIMPLE_OPTION(formatted, "formatted/plain", bool, + gettext_noop("automatically run the output through nroff (default if the output is a terminal)")) + + +GROUPED_SIMPLE_OPTION(globals, timestamps, "timestamps", bool, + gettext_noop("show timestamps in front of errors, warnings and progress messages")) + +SIMPLE_OPTION(recursive, "recursive,R/no-recursive", bool, + gettext_noop("also operate on the contents of any listed directories")) + +SIMPLE_OPTION(revision, "revision,r",args_vector, + gettext_noop("select revision id for operation")) + +GROUPED_SIMPLE_OPTION(globals, root, "root", std::string, + gettext_noop("limit search for workspace to specified root")) + +GROUPED_SIMPLE_OPTION(globals, no_workspace, "no-workspace/allow-workspace", bool, + gettext_noop("don't look for a workspace")) + +SIMPLE_OPTION(set_default, "set-default/no-set-default", bool, + gettext_noop("use the current netsync arguments and options " + "as the future default")) + +OPTSET_REL(globals, ticker) +SIMPLE_INITIALIZED_OPTION(ticker, "ticker", enum_string, "count,dot,none", + gettext_noop("set ticker style (count|dot|none)")) + +SIMPLE_OPTION(from, "from/clear-from", args_vector, + gettext_noop("revision(s) to start logging at")) + +SIMPLE_OPTION(to, "to/clear-to", args_vector, + gettext_noop("revision(s) to stop logging at")) + +SIMPLE_OPTION(unknown, "unknown/no-unknown", bool, + gettext_noop("perform the operations for unknown files from workspace")) + +GROUPED_SIMPLE_OPTION(globals, version, "version", bool, + gettext_noop("print version number, then exit")) + + +OPTSET(automate_inventory_opts) + +OPTSET_REL(automate_inventory_opts, no_ignored) +SIMPLE_OPTION(no_ignored, "no-ignored/ignored", bool, + gettext_noop("don't output ignored files")) +OPTSET_REL(automate_inventory_opts, no_unknown) +SIMPLE_OPTION(no_unknown, "no-unknown/unknown",bool, + gettext_noop("don't output unknown files")) +OPTSET_REL(automate_inventory_opts, no_unchanged) +SIMPLE_OPTION(no_unchanged, "no-unchanged/unchanged", bool, + gettext_noop("don't output unchanged files")) +OPTSET_REL(automate_inventory_opts, no_corresponding_renames) +SIMPLE_OPTION(no_corresponding_renames, "no-corresponding-renames/corresponding-renames", bool, + gettext_noop("don't output corresponding renames if restricted on such nodes")) + + +OPTSET(resolve_conflicts_opts) +OPTVAR(resolve_conflicts_opts, bookkeeping_path, + resolve_conflicts_file, "_MTN/conflicts") + +OPTION(resolve_conflicts_opts, resolve_conflicts_file, true, "resolve-conflicts-file", + gettext_noop("use file to resolve conflicts")) +#ifdef option_bodies +{ + // we can't call bookkeeping_path::external_string_is_bookkeeping_path + // here, because we haven't found the workspace yet. + E(bookkeeping_path::internal_string_is_bookkeeping_path(utf8(arg, origin::user)), + origin::user, + F("conflicts file must be under _MTN")); + resolve_conflicts_file = bookkeeping_path(arg, origin::user); + resolve_conflicts = true; +} +#endif + +OPTSET_REL(resolve_conflicts_opts, resolve_conflicts) +SIMPLE_OPTION(resolve_conflicts, "resolve-conflicts/no-resolve-conflicts", bool, + gettext_noop("specify conflict resolutions in a file, instead of interactively")) + +OPTSET(conflicts_opts) +OPTVAR(conflicts_opts, bookkeeping_path, conflicts_file, bookkeeping_path("_MTN/conflicts")) + +OPTION(conflicts_opts, conflicts_file, true, "conflicts-file", + gettext_noop("file in which to store conflicts")) +#ifdef option_bodies +{ + // we can't call bookkeeping_path::external_string_is_bookkeeping_path + // here, because we haven't found the workspace yet. + E(bookkeeping_path::internal_string_is_bookkeeping_path(utf8(arg, origin::user)), + origin::user, + F("conflicts file must be under _MTN")); + conflicts_file = bookkeeping_path(arg, origin::user); +} +#endif + +SIMPLE_OPTION(use_one_changelog, "use-one-changelog", bool, + gettext_noop("use only one changelog cert for the git commit message")) + +SIMPLE_OPTION(authors_file, "authors-file", system_path, + gettext_noop("file mapping author names from original to new values")) + +SIMPLE_OPTION(branches_file, "branches-file", system_path, + gettext_noop("file mapping branch names from original to new values ")) + +SIMPLE_INITIALIZED_OPTION(refs, "refs", enum_string_set, "revs,roots,leaves", + gettext_noop("include git refs for 'revs', 'roots' or 'leaves'")) + +SIMPLE_OPTION(log_revids, "log-revids/no-log-revids", bool, + gettext_noop("include revision ids in commit logs")) + +SIMPLE_OPTION(log_certs, "log-certs/no-log-certs", bool, + gettext_noop("include standard cert values in commit logs")) + +SIMPLE_OPTION(import_marks, "import-marks", system_path, + gettext_noop("load the internal marks table before exporting revisions")) + +SIMPLE_OPTION(export_marks, "export-marks", system_path, + gettext_noop("save the internal marks table after exporting revisions")) + +// clean up after ourselves +#undef SIMPLE_OPTION +#undef SIMPLE_OPTION_BODY +#undef GROUPED_SIMPLE_OPTION +#undef SIMPLE_INITIALIZED_OPTION +#undef COMMA + +// 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: