monotone

monotone Mtn Source Tree

Root/src/cmd.cc

1// Copyright (C) 2002 Graydon Hoare <graydon@pobox.com>
2// 2007 Julio M. Merino Vidal <jmmv@NetBSD.org>
3//
4// This program is made available under the GNU GPL version 2.0 or
5// greater. See the accompanying file COPYING for details.
6//
7// This program is distributed WITHOUT ANY WARRANTY; without even the
8// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
9// PURPOSE.
10
11#include "base.hh"
12#include "cmd.hh"
13
14#include "lua.hh"
15#include "app_state.hh"
16#include "globish.hh"
17#include "options_applicator.hh"
18#include "work.hh"
19#include "ui.hh"
20#include "mt_version.hh"
21#include "charset.hh"
22#include "simplestring_xform.hh"
23#include "vocab_cast.hh"
24
25#ifndef _WIN32
26#include <signal.h>
27#include <errno.h>
28#else
29#include <io.h>
30#endif
31
32#include <iostream>
33#include <cstring>
34
35using std::string;
36using std::stringstream;
37using std::vector;
38using std::set;
39using std::ostream;
40using std::make_pair;
41using std::cout;
42using boost::lexical_cast;
43
44using std::cerr;
45using std::endl;
46
47//
48// Definition of top-level commands, used to classify the real commands
49// in logical groups.
50//
51// These top level commands, while part of the final identifiers and defined
52// as regular command groups, are handled separately. The user should not
53// see them except through the help command.
54//
55// XXX This is to easily maintain compatibilty with older versions. But
56// maybe this should be revised, because exposing the top level category
57// (being optional, of course), may not be a bad idea.
58//
59
60CMD_GROUP(__root__, "__root__", "", NULL, "", "");
61
62CMD_GROUP_NO_COMPLETE(automation, "automation", "", CMD_REF(__root__),
63 N_("Commands that aid in scripted execution"),
64 "");
65CMD_GROUP(database, "database", "", CMD_REF(__root__),
66 N_("Commands that manipulate the database"),
67 "");
68CMD_GROUP(debug, "debug", "", CMD_REF(__root__),
69 N_("Commands that aid in program debugging"),
70 "");
71CMD_GROUP(informative, "informative", "", CMD_REF(__root__),
72 N_("Commands for information retrieval"),
73 "");
74CMD_GROUP(key_and_cert, "key_and_cert", "", CMD_REF(__root__),
75 N_("Commands to manage keys and certificates"),
76 "");
77CMD_GROUP(network, "network", "", CMD_REF(__root__),
78 N_("Commands that access the network"),
79 "");
80CMD_GROUP(packet_io, "packet_io", "", CMD_REF(__root__),
81 N_("Commands for packet reading and writing"),
82 "");
83CMD_GROUP(vcs, "vcs", "", CMD_REF(__root__),
84 N_("Commands for interaction with other version control systems"),
85 "");
86CMD_GROUP(review, "review", "", CMD_REF(__root__),
87 N_("Commands to review revisions"),
88 "");
89CMD_GROUP(tree, "tree", "", CMD_REF(__root__),
90 N_("Commands to manipulate the tree"),
91 "");
92CMD_GROUP(variables, "variables", "", CMD_REF(__root__),
93 N_("Commands to manage persistent variables"),
94 "");
95CMD_GROUP(workspace, "workspace", "", CMD_REF(__root__),
96 N_("Commands that deal with the workspace"),
97 "");
98CMD_GROUP(user, "user", "", CMD_REF(__root__),
99 N_("Commands defined by the user"),
100 "");
101
102namespace commands {
103
104 void remove_command_name_from_args(command_id const & ident,
105 args_vector & args,
106 size_t invisible_length)
107 {
108 MM(ident);
109 MM(args);
110 MM(invisible_length);
111 I(ident.empty() || args.size() >= ident.size() - invisible_length);
112 for (args_vector::size_type i = invisible_length; i < ident.size(); i++)
113 {
114 I(ident[i]().find(args[0]()) == 0);
115 args.erase(args.begin());
116 }
117 }
118
119 void reapply_options(app_state & app,
120 command const * cmd,
121 command_id const & cmd_ident,
122 command const * subcmd,
123 command_id const & subcmd_full_ident,
124 size_t subcmd_invisible_length,
125 args_vector const & subcmd_cmdline,
126 vector<pair<string, string> > const * const separate_params)
127 {
128 I(cmd);
129 options::opts::all_options().instantiate(&app.opts).reset();
130
131 cmd->preset_options(app.opts);
132
133 option::concrete_option_set optset
134 = (options::opts::globals() | cmd->opts())
135 .instantiate(&app.opts);
136
137 optset.from_command_line(app.reset_info.default_args);
138
139 if (subcmd)
140 {
141 args_vector subcmd_defaults;
142 app.lua.hook_get_default_command_options(subcmd_full_ident,
143 subcmd_defaults);
144 (options::opts::globals() | subcmd->opts())
145 .instantiate(&app.opts)
146 .from_command_line(subcmd_defaults);
147 }
148
149 // at this point we process the data from _MTN/options if
150 // the command needs it.
151 if ((subcmd ? subcmd : cmd)->use_workspace_options())
152 {
153 workspace::check_format();
154 workspace::get_options(app.opts);
155 }
156
157 optset.from_command_line(app.reset_info.cmdline_args);
158
159 if (subcmd)
160 {
161 app.opts.args.clear();
162 option::concrete_option_set subcmd_optset
163 = (options::opts::globals() | subcmd->opts())
164 .instantiate(&app.opts);
165 if (!separate_params)
166 {
167 /* the first argument here is only ever modified if the second is 'true' */
168 subcmd_optset.from_command_line(const_cast<args_vector &>(subcmd_cmdline));
169 }
170 else
171 {
172 subcmd_optset.from_key_value_pairs(*separate_params);
173 app.opts.args = subcmd_cmdline;
174 }
175 remove_command_name_from_args(subcmd_full_ident, app.opts.args,
176 subcmd_invisible_length);
177 }
178 else
179 {
180 remove_command_name_from_args(cmd_ident, app.opts.args);
181 }
182 }
183
184 // monotone.cc calls this function after option processing.
185 void process(app_state & app, command_id const & ident,
186 args_vector const & args)
187 {
188 static bool process_called(false);
189 I(!process_called);
190 process_called = true;
191
192 command const * cmd = CMD_REF(__root__)->find_command(ident);
193 app.reset_info.cmd = cmd;
194
195 string visibleid = join_words(vector< utf8 >(ident.begin() + 1,
196 ident.end()))();
197
198 I(cmd->is_leaf() || cmd->is_group());
199 E(!(cmd->is_group() && cmd->parent() == CMD_REF(__root__)),
200 origin::user,
201 F("command '%s' is invalid; it is a group") % join_words(ident));
202
203 if (!cmd->is_leaf())
204 {
205 // args used in the command name have not been stripped yet
206 remove_command_name_from_args(ident, app.opts.args);
207
208 E(!args.empty(), origin::user,
209 F("no subcommand specified for '%s'") % visibleid);
210
211 E(false, origin::user,
212 F("could not match '%s' to a subcommand of '%s'") %
213 join_words(args) % visibleid);
214 }
215
216 L(FL("executing command '%s'") % visibleid);
217
218 reapply_options(app, cmd, ident);
219
220 // intentional leak
221 // we don't want the options to be reset, so don't destruct this
222 new options_applicator(app.opts, options_applicator::for_primary_cmd);
223
224 cmd->exec(app, ident, args);
225 }
226
227 // Prints the abstract description of the given command or command group
228 // properly indented. The tag starts at column two. The description has
229 // to start, at the very least, two spaces after the tag's end position;
230 // this is given by the colabstract parameter.
231 static void describe(const string & tag, const string & abstract,
232 const string & subcommands, size_t colabstract,
233 ostream & out)
234 {
235 I(colabstract > 0);
236
237 size_t col = 0;
238 out << " " << tag << " ";
239 col += display_width(utf8(tag + " ", origin::internal));
240
241 out << string(colabstract - col, ' ');
242 col = colabstract;
243 string desc(abstract);
244 if (!subcommands.empty())
245 {
246 desc += " (" + subcommands + ')';
247 }
248 out << format_text(desc, colabstract, col) << '\n';
249 }
250
251 class cmd_ptr_compare
252 {
253 public:
254 bool operator()(command const * const a, command const * const b) const
255 {
256 return a->primary_name()() < b->primary_name()();
257 }
258 };
259
260 static void explain_children(command::children_set const & children,
261 bool show_hidden_commands,
262 ostream & out)
263 {
264 I(!children.empty());
265
266 vector< command const * > sorted;
267
268 size_t colabstract = 0;
269 for (command::children_set::const_iterator i = children.begin();
270 i != children.end(); i++)
271 {
272 command const * child = *i;
273
274 if (child->hidden() && !show_hidden_commands)
275 continue;
276
277 size_t len = display_width(join_words(child->names(), ", ")) +
278 display_width(utf8(" "));
279 if (colabstract < len)
280 colabstract = len;
281
282 sorted.push_back(child);
283 }
284
285 sort(sorted.begin(), sorted.end(), cmd_ptr_compare());
286
287 for (vector< command const * >::const_iterator i = sorted.begin();
288 i != sorted.end(); i++)
289 {
290 command const * child = *i;
291 describe(join_words(child->names(), ", ")(), child->abstract(),
292 join_words(child->subcommands(show_hidden_commands), ", ")(),
293 colabstract, out);
294 }
295 }
296
297 static command const *
298 find_command(command_id const & ident)
299 {
300 command const * cmd = CMD_REF(__root__)->find_command(ident);
301
302 // This function is only used internally with an identifier returned
303 // by complete_command. Therefore, it must always exist.
304 I(cmd != NULL);
305
306 return cmd;
307 }
308
309 static void explain_cmd_usage(command_id const & ident,
310 bool show_hidden_commands,
311 ostream & out)
312 {
313 I(ident.size() >= 1);
314
315 vector< string > lines;
316 command const * cmd = find_command(ident);
317
318 string visibleid = join_words(vector< utf8 >(ident.begin() + 1,
319 ident.end()))();
320
321 // Print command parameters.
322 string params = cmd->params();
323 split_into_lines(params, lines);
324
325 if (visibleid.empty())
326 out << format_text(F("Commands in group '%s':") %
327 join_words(ident)())
328 << "\n\n";
329 else
330 {
331 if (!cmd->children().empty())
332 out << format_text(F("Subcommands of '%s %s':") %
333 prog_name % visibleid)
334 << "\n\n";
335 else if (!lines.empty())
336 out << format_text(F("Syntax specific to '%s %s':") %
337 prog_name % visibleid)
338 << "\n\n";
339 }
340
341 // lines might be empty, but only when specific syntax is to be
342 // displayed, not in the other cases.
343 if (!lines.empty())
344 {
345 for (vector<string>::const_iterator j = lines.begin();
346 j != lines.end(); ++j)
347 out << " " << visibleid << ' ' << *j << '\n';
348 out << '\n';
349 }
350
351 // Explain children, if any.
352 if (!cmd->is_leaf())
353 {
354 explain_children(cmd->children(), show_hidden_commands, out);
355 out << '\n';
356 }
357
358 // Print command description.
359 if (visibleid.empty())
360 out << format_text(F("Purpose of group '%s':") %
361 join_words(ident)())
362 << "\n\n";
363 else
364 out << format_text(F("Description for '%s %s':") %
365 prog_name % visibleid)
366 << "\n\n";
367 out << format_text(cmd->desc(), 2, 0, true) << "\n\n";
368
369 // Print all available aliases.
370 if (cmd->names().size() > 1)
371 {
372 command::names_set othernames = cmd->names();
373 othernames.erase(ident[ident.size() - 1]);
374 out << format_text(F("Aliases: %s.") %
375 join_words(othernames, ", ")(), 2, 0, true)
376 << '\n';
377 }
378 }
379
380 void explain_usage(command_id const & ident,
381 bool show_hidden_commands,
382 ostream & out)
383 {
384 command const * cmd = find_command(ident);
385
386 if (ident.empty())
387 {
388 out << format_text(F("Command groups:")) << "\n\n";
389 explain_children(CMD_REF(__root__)->children(),
390 show_hidden_commands,
391 out);
392 out << '\n'
393 << format_text(F("For information on a specific command, type "
394 "'mtn help <command_name> [subcommand_name ...]'."))
395 << "\n\n"
396 << format_text(F("To see more details about the commands of a "
397 "particular group, type 'mtn help <group_name>'."))
398 << "\n\n"
399 << format_text(F("Note that you can always abbreviate a command "
400 "name as long as it does not conflict with other "
401 "names."))
402 << "\n";
403 }
404 else
405 explain_cmd_usage(ident, show_hidden_commands, out);
406 }
407
408 options::options_type command_options(command_id const & ident)
409 {
410 command const * cmd = find_command(ident);
411 return cmd->opts();
412 }
413
414 // Lua-defined user commands.
415 class cmd_lua : public command
416 {
417 lua_State *st;
418 std::string const f_name;
419 public:
420 cmd_lua(std::string const & primary_name,
421 std::string const & params,
422 std::string const & abstract,
423 std::string const & desc,
424 lua_State *L_st,
425 std::string const & func_name) :
426 command(primary_name, "", CMD_REF(user), false, false, params,
427 abstract, desc, true,
428 options::options_type() | options::opts::none, true),
429 st(L_st), f_name(func_name)
430 {
431 // because user commands are inserted after the normal
432 // initialisation process
433 CMD_REF(user)->children().insert(this);
434 }
435
436 void exec(app_state & app, command_id const & execid,
437 args_vector const & args) const
438 {
439 I(st);
440 I(app.lua.check_lua_state(st));
441
442 app_state* app_p = get_app_state(st);
443 I(app_p == & app);
444
445 Lua ll(st);
446 ll.func(f_name);
447
448 for (args_vector::const_iterator it = args.begin();
449 it != args.end(); ++it)
450 ll.push_str((*it)());
451
452 app.mtn_automate_allowed = true;
453
454 ll.call(args.size(),0);
455
456 app.mtn_automate_allowed = false;
457
458 E(ll.ok(), origin::user,
459 F("call to user command '%s' (lua command: '%s') failed.")
460 % primary_name() % f_name);
461 }
462 };
463}
464
465LUAEXT(alias_command, )
466{
467 const char *old_cmd = luaL_checkstring(LS, -2);
468 const char *new_cmd = luaL_checkstring(LS, -1);
469 E(old_cmd && new_cmd, origin::user,
470 F("'%s' called with an invalid parameter") % "alias_command");
471
472 args_vector args;
473 args.push_back(arg_type(old_cmd, origin::user));
474 commands::command_id id = commands::complete_command(args);
475 commands::command *old_cmd_p = CMD_REF(__root__)->find_command(id);
476
477 old_cmd_p->add_alias(utf8(new_cmd));
478
479 lua_pushboolean(LS, true);
480 return 1;
481}
482
483
484LUAEXT(register_command, )
485{
486 const char *cmd_name = luaL_checkstring(LS, -5);
487 const char *cmd_params = luaL_checkstring(LS, -4);
488 const char *cmd_abstract = luaL_checkstring(LS, -3);
489 const char *cmd_desc = luaL_checkstring(LS, -2);
490 const char *cmd_func = luaL_checkstring(LS, -1);
491
492 E(cmd_name && cmd_params && cmd_abstract && cmd_desc && cmd_func,
493 origin::user,
494 F("'%s' called with an invalid parameter") % "register_command");
495
496 // leak this - commands can't be removed anyway
497 new commands::cmd_lua(cmd_name, cmd_params, cmd_abstract, cmd_desc,
498 LS, cmd_func);
499
500 lua_pushboolean(LS, true);
501 return 1;
502}
503
504// Miscellaneous commands and related functions for which there is no
505// better file.
506
507CMD_NO_WORKSPACE(help, "help", "", CMD_REF(informative),
508 N_("command [ARGS...]"),
509 N_("Displays help about commands and options"),
510 "",
511 options::opts::show_hidden_commands)
512{
513 if (args.size() < 1)
514 {
515 app.opts.help = true;
516 throw usage(command_id());
517 }
518
519 command_id id = commands::complete_command(args);
520 app.opts.help = true;
521 throw usage(id);
522}
523
524CMD_NO_WORKSPACE(version, "version", "", CMD_REF(informative), "",
525 N_("Shows the program version"),
526 "",
527 options::opts::full)
528{
529 E(args.empty(), origin::user,
530 F("no arguments allowed"));
531
532 if (app.opts.full)
533 print_full_version();
534 else
535 print_version();
536}
537
538CMD_HIDDEN(check_glob, "check_glob", "", CMD_REF(debug),
539 "glob string",
540 N_("Check that a particular glob matches a particular string"),
541 "",
542 options::opts::none)
543{
544 globish g = typecast_vocab<globish>(idx(args,0));
545 string s(idx(args,1)());
546
547 E(g.matches(s), origin::user,
548 F("Glob '%s' does not match string '%s'") % g % s);
549}
550
551CMD_HIDDEN(crash, "crash", "", CMD_REF(debug),
552 "{ N | E | I | double-throw | exception | signal }",
553 N_("Triggers the specified kind of crash"),
554 "",
555 options::opts::none)
556{
557 if (args.size() != 1)
558 throw usage(execid);
559 bool spoon_exists(false);
560 if (idx(args,0)() == "N")
561 E(spoon_exists, origin::user, i18n_format("There is no spoon."));
562 else if (idx(args,0)() == "E")
563 E(spoon_exists, origin::system, i18n_format("There is no spoon."));
564 else if (idx(args,0)() == "I")
565 {
566 I(spoon_exists);
567 }
568 else if (idx(args,0)() == "double-throw")
569 {
570 // This code is rather picky, for example I(false) in the destructor
571 // won't always work like it should; see http://bugs.debian.org/516862
572 class throwing_dtor
573 {
574 public:
575 throwing_dtor() {}
576 ~throwing_dtor()
577 {
578 throw std::exception();
579 }
580 };
581 throwing_dtor td;
582 throw std::exception();
583 }
584#define maybe_throw(ex) if(idx(args,0)()==#ex) throw ex("There is no spoon.")
585#define maybe_throw_bare(ex) if(idx(args,0)()==#ex) throw ex()
586 else maybe_throw_bare(std::bad_alloc);
587 else maybe_throw_bare(std::bad_cast);
588 else maybe_throw_bare(std::bad_typeid);
589 else maybe_throw_bare(std::bad_exception);
590 else maybe_throw_bare(std::exception);
591 else maybe_throw(std::domain_error);
592 else maybe_throw(std::invalid_argument);
593 else maybe_throw(std::length_error);
594 else maybe_throw(std::out_of_range);
595 else maybe_throw(std::range_error);
596 else maybe_throw(std::overflow_error);
597 else maybe_throw(std::underflow_error);
598 else maybe_throw(std::logic_error);
599 else maybe_throw(std::runtime_error);
600 else
601 {
602#ifndef _WIN32
603 try
604 {
605 int signo = boost::lexical_cast<int>(idx(args,0)());
606 if (0 < signo && signo <= 15)
607 {
608 raise(signo);
609 // control should not get here...
610 I(!"crash: raise returned");
611 }
612 }
613 catch (boost::bad_lexical_cast&)
614 { // fall through and throw usage
615 }
616#endif
617 throw usage(execid);
618 }
619#undef maybe_throw
620#undef maybe_throw_bare
621}
622
623static string
624man_italic(string const & content)
625{
626 return "\\fI" + content + "\\fP";
627}
628
629static string
630man_bold(string const & content)
631{
632 return "\\fB" + content + "\\fP";
633}
634
635static string
636man_hyphens(string const s)
637{
638 string out;
639 size_t p1 = 0;
640 size_t p2 = s.find("-", p1);
641 while (p2 != s.npos)
642 {
643 // Sometimes, this function gets called again with the result of a
644 // previous call, so watch out for hyphens that are already preceeded
645 // with a backslash
646 if (p2 > 0 && s[p2 - 1] == '\\')
647 {
648 p2 = s.find("-", p2 + 1);
649 continue;
650 }
651
652 out += s.substr(p1, p2 - p1);
653 out += "\\-";
654 p1 = p2 + 1;
655 p2 = s.find("-", p1);
656 }
657 out += s.substr(p1, s.npos);
658 return out;
659}
660
661static string
662man_definition(vector<string> const & labels, string const & content, int width = -1)
663{
664 string out;
665 out += ".IP \"" + man_hyphens(*labels.begin()) + "\"";
666
667 if (width != -1)
668 out += " " + lexical_cast<string>(width);
669 out += "\n";
670
671 if (labels.size() > 1)
672 {
673 out += ".PD 0\n";
674 for (vector<string>::const_iterator i = labels.begin() + 1;
675 i < labels.end(); ++i)
676 {
677 out += ".IP \"" + man_hyphens(*i) + "\"\n";
678 }
679 out += ".PD\n";
680 }
681 out += man_hyphens(content);
682 if (content.rfind('\n') != (content.size() - 1))
683 out += "\n";
684
685 return out;
686}
687
688static string
689man_definition(string const & label, string const & content, int width = -1)
690{
691 vector<string> labels;
692 labels.push_back(label);
693 return man_definition(labels, content, width);
694}
695
696static string
697man_indent(string const & content)
698{
699 return ".RS\n" + content + ".RE\n";
700}
701
702static string
703man_subsection(string const & content)
704{
705 return ".SS \"" + content + "\"\n";
706}
707
708static string
709man_section(string const & content)
710{
711 return ".SH \"" + uppercase(content) + "\"\n";
712}
713
714static string
715man_title(string const & title)
716{
717 return ".TH \"" + title + "\" 1 "+
718 "\"" + BUILD_DATE + "\" " +
719 "\"" + PACKAGE_STRING + "\"\n";
720}
721
722static string
723get_options_string(options::options_type const & optset, options & opts, int width = -1)
724{
725 vector<string> names;
726 vector<string> descriptions;
727 unsigned int maxnamelen;
728
729 optset.instantiate(&opts).get_usage_strings(
730 names, descriptions, maxnamelen, opts.show_hidden_commands
731 );
732
733 string out;
734 vector<string>::const_iterator name;
735 vector<string>::const_iterator desc;
736 for (name = names.begin(), desc = descriptions.begin();
737 name != names.end(); ++name, ++desc)
738 {
739 if (name->empty())
740 continue;
741 out += man_definition(*name, *desc, width);
742 }
743 return out;
744}
745
746static string
747get_commands(options & opts, commands::command const * group)
748{
749 vector<commands::command const *> sorted_commands;
750 commands::command::children_set commands = group->children();
751 for (commands::command::children_set::const_iterator i = commands.begin();
752 i != commands.end(); ++i)
753 {
754 commands::command * command = *i;
755 if (command->hidden() && !opts.show_hidden_commands)
756 continue;
757
758 // there are no top level commands, so this must be an
759 // empty group - skip it
760 if (group->is_leaf())
761 continue;
762
763 sorted_commands.push_back(command);
764 }
765
766 sort(sorted_commands.begin(),
767 sorted_commands.end(),
768 commands::cmd_ptr_compare());
769
770 string out;
771 for (vector<commands::command const * >::const_iterator i = sorted_commands.begin();
772 i != sorted_commands.end(); ++i)
773 {
774 commands::command const * command = *i;
775
776 // don't print sub groups explicitely, just their leaves
777 if (!command->is_leaf())
778 {
779 out += get_commands(opts, command);
780 continue;
781 }
782
783 // this builds a list of already formatted command calls
784 // which are used as label for the specific command section
785 vector<string> cmd_calls;
786
787 //
788 // newline characters in the parameter section mark
789 // alternative call syntaxes which we expand here, i.e.
790 // a command "do-foo" with an alias of "foo" and an argument
791 // list of "BAR\nBAR BAZ" will be expanded to
792 //
793 // do-foo BAR
794 // do-foo BAR BAZ
795 // foo BAR
796 // foo BAR BAZ
797 //
798 vector<string> params;
799 if (!command->params().empty())
800 split_into_lines(command->params(), params);
801
802 vector<utf8> main_ident = command->ident();
803 typedef set<vector<string> > ident_set;
804 ident_set idents;
805
806 commands::command::names_set allnames = command->names();
807 for (set<utf8>::const_iterator i = allnames.begin();
808 i != allnames.end(); ++i)
809 {
810 vector<string> full_ident;
811 for (vector<utf8>::const_iterator j = main_ident.begin() + 1;
812 j < main_ident.end() - 1; ++j)
813 {
814 full_ident.push_back((*j)());
815 }
816 full_ident.push_back((*i)());
817 idents.insert(full_ident);
818 }
819
820 for (ident_set::const_iterator i = idents.begin();
821 i != idents.end(); ++i)
822 {
823 string call, name;
824 // cannot use join_words here, since this only
825 // works on containers
826 join_lines(*i, name, " ");
827
828 if (params.size() == 0)
829 {
830 call = man_bold(name);
831 cmd_calls.push_back(call);
832 continue;
833 }
834
835 for (vector<string>::const_iterator j = params.begin();
836 j < params.end(); ++j)
837 {
838 call = man_bold(name) + " " + *j;
839 cmd_calls.push_back(call);
840 }
841 }
842
843 string cmd_desc;
844 cmd_desc += command->desc() + "\n";
845
846 // this prints an indented list of available command options
847 options::options_type cmd_options =
848 commands::command_options(main_ident);
849 if (!cmd_options.empty())
850 {
851 cmd_desc += man_indent(get_options_string(cmd_options, opts, 4));
852 }
853
854 // compile everything into a man definition
855 out += man_definition(cmd_calls, cmd_desc, 4);
856 }
857
858 return out;
859}
860
861static string
862get_command_groups(options & opts)
863{
864 vector<commands::command const *> sorted_groups;
865 commands::command::children_set groups = CMD_REF(__root__)->children();
866 for (commands::command::children_set::const_iterator i = groups.begin();
867 i != groups.end(); ++i)
868 {
869 commands::command * group = *i;
870 if (group->hidden() && !opts.show_hidden_commands)
871 continue;
872
873 // there are no top level commands, so this must be an
874 // empty group - skip it
875 if (group->is_leaf())
876 continue;
877
878 sorted_groups.push_back(group);
879 }
880
881 sort(sorted_groups.begin(),
882 sorted_groups.end(),
883 commands::cmd_ptr_compare());
884
885 string out;
886 for (vector<commands::command const * >::const_iterator i = sorted_groups.begin();
887 i != sorted_groups.end(); ++i)
888 {
889 commands::command const * group = *i;
890 out += man_subsection(
891 (F("command group '%s'") % group->primary_name()).str()
892 );
893 out += group->desc() + "\n";
894
895 out += get_commands(opts, group);
896 }
897
898 return out;
899}
900
901CMD_PRESET_OPTIONS(manpage)
902{
903 opts.formatted = isatty(STDOUT_FILENO);
904}
905CMD_NO_WORKSPACE(manpage, "manpage", "", CMD_REF(informative), "",
906 N_("Generate a manual page from monotone's command help"),
907 "",
908 options::opts::show_hidden_commands | options::opts::formatted)
909{
910 stringstream ss;
911 ss << man_title("monotone");
912 ss << man_section(_("Name"));
913
914 ss << _("monotone \\- a distributed version control system") << "\n";
915 ss << man_section(_("Synopsis"));
916 ss << man_bold(prog_name) << " "
917 << man_italic(_("[options...] command [arguments...]"))
918 << "\n";
919
920 ss << man_section(_("Description"));
921 ss << _("monotone is a highly reliable, very customizable distributed "
922 "version control system that provides lightweight branches, "
923 "history-sensitive merging and a flexible trust setup. "
924 "monotone has an easy-to-learn command set and comes with a rich "
925 "interface for scripting purposes and thorough documentation.")
926 << "\n\n";
927 ss << (F("For more information on monotone, visit %s.")
928 % man_bold(PACKAGE_URL)).str()
929 << "\n\n";
930 ss << (F("The complete documentation, including a tutorial for a quick start "
931 "with the system, can be found online on %s.")
932 % man_bold(PACKAGE_URL "/docs")).str() << "\n";
933
934 ss << man_section(_("Global Options"));
935 ss << get_options_string(options::opts::globals(), app.opts, 25) << "\n";
936
937 ss << man_section(_("Commands"));
938 ss << get_command_groups(app.opts);
939
940 ss << man_section(_("See Also"));
941 ss << (F("info %s and the documentation on %s")
942 % prog_name % man_bold(PACKAGE_URL "/docs")).str() << "\n";
943
944 ss << man_section(_("Bugs"));
945 ss << (F("Please report bugs to %s.")
946 % man_bold(PACKAGE_BUGREPORT)).str()<< "\n";
947
948 ss << man_section(_("Authors"));
949 ss << _("monotone was written originally by Graydon Hoare "
950 "<graydon@pobox.com> in 2003 and has since then received "
951 "numerous contributions from many individuals. "
952 "A complete list of authors can be found in AUTHORS.")
953 << "\n\n";
954 ss << _("Nowadays, monotone is maintained by a collective of enthusiastic "
955 "programmers, known as the monotone development team.") << "\n";
956
957 ss << man_section(_("Copyright"));
958 ss << (F("monotone and this man page is Copyright (c) 2003 \\- %s by "
959 "the monotone development team.")
960 % string(BUILD_DATE).substr(0, 4)).str() << "\n";
961
962 if (!app.opts.formatted)
963 {
964 cout << ss.str();
965 return;
966 }
967
968 string cmd;
969 E(app.lua.hook_get_man_page_formatter_command(cmd) && !cmd.empty(),
970 origin::user, F("no man page formatter command configured"));
971
972 FILE * fp = popen(cmd.c_str(), "w");
973 E(fp != NULL, origin::system,
974 F("could not execute man page formatter command '%s': %s")
975 % cmd % strerror(errno));
976
977 fputs(ss.str().c_str(), fp);
978 pclose(fp);
979}
980
981// There isn't really a better place for this function.
982
983void
984process_commit_message_args(options const & opts,
985 bool & given,
986 utf8 & log_message,
987 utf8 const & message_prefix)
988{
989 // can't have both a --message and a --message-file ...
990 E(!opts.message_given || !opts.msgfile_given, origin::user,
991 F("'--message' and '--message-file' are mutually exclusive"));
992
993 if (opts.message_given)
994 {
995 string msg;
996 join_lines(opts.message, msg);
997 log_message = utf8(msg, origin::user);
998 if (!opts.no_prefix && message_prefix().length() != 0)
999 log_message = utf8(message_prefix() + "\n\n" + log_message(),
1000 origin::user);
1001 given = true;
1002 }
1003 else if (opts.msgfile_given)
1004 {
1005 data dat;
1006 read_data_for_command_line(opts.msgfile, dat);
1007 external dat2 = typecast_vocab<external>(dat);
1008 system_to_utf8(dat2, log_message);
1009 if (!opts.no_prefix && message_prefix().length() != 0)
1010 log_message = utf8(message_prefix() + "\n\n" + log_message(),
1011 origin::user);
1012 given = true;
1013 }
1014 else if (message_prefix().length() != 0)
1015 {
1016 log_message = message_prefix;
1017 given = true;
1018 }
1019 else
1020 given = false;
1021}
1022
1023// Local Variables:
1024// mode: C++
1025// fill-column: 76
1026// c-file-style: "gnu"
1027// indent-tabs-mode: nil
1028// End:
1029// vim: et:sw=2:sts=2:ts=2:cino=>2s,{s,\:s,+s,t0,g0,^-2,e-2,n-2,p2s,(0,=s:

Archive Download this file

Branches

Tags

Quick Links:     www.monotone.ca    -     Downloads    -     Documentation    -     Wiki    -     Code Forge    -     Build Status