monotone

monotone Mtn Source Tree

Root/commands.cc

1// Copyright (C) 2002 Graydon Hoare <graydon@pobox.com>
2// Copyright (C) 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 <map>
13#include <algorithm>
14#include <iostream>
15
16#include "transforms.hh"
17#include "simplestring_xform.hh"
18#include "file_io.hh"
19#include "charset.hh"
20#include "diff_patch.hh"
21#include "inodeprint.hh"
22#include "cert.hh"
23#include "ui.hh"
24#include "cmd.hh"
25#include "constants.hh"
26#include "app_state.hh"
27#include "project.hh"
28#include "work.hh"
29
30#ifndef _WIN32
31#include "lexical_cast.hh"
32#include <signal.h>
33#endif
34
35using std::cin;
36using std::make_pair;
37using std::map;
38using std::ostream;
39using std::pair;
40using std::set;
41using std::string;
42using std::strlen;
43using std::vector;
44
45CMD_GROUP(__root__, "__root__", "", NULL, "", "");
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//
59CMD_GROUP_NO_COMPLETE(automation, "automation", "", CMD_REF(__root__),
60 N_("Commands that aid in scripted execution"),
61 "");
62CMD_GROUP(database, "database", "", CMD_REF(__root__),
63 N_("Commands that manipulate the database"),
64 "");
65CMD_GROUP(debug, "debug", "", CMD_REF(__root__),
66 N_("Commands that aid in program debugging"),
67 "");
68CMD_GROUP(informative, "informative", "", CMD_REF(__root__),
69 N_("Commands for information retrieval"),
70 "");
71CMD_GROUP(key_and_cert, "key_and_cert", "", CMD_REF(__root__),
72 N_("Commands to manage keys and certificates"),
73 "");
74CMD_GROUP(network, "network", "", CMD_REF(__root__),
75 N_("Commands that access the network"),
76 "");
77CMD_GROUP(packet_io, "packet_io", "", CMD_REF(__root__),
78 N_("Commands for packet reading and writing"),
79 "");
80CMD_GROUP(rcs, "rcs", "", CMD_REF(__root__),
81 N_("Commands for interaction with RCS and CVS"),
82 "");
83CMD_GROUP(review, "review", "", CMD_REF(__root__),
84 N_("Commands to review revisions"),
85 "");
86CMD_GROUP(tree, "tree", "", CMD_REF(__root__),
87 N_("Commands to manipulate the tree"),
88 "");
89CMD_GROUP(variables, "variables", "", CMD_REF(__root__),
90 N_("Commands to manage persistent variables"),
91 "");
92CMD_GROUP(workspace, "workspace", "", CMD_REF(__root__),
93 N_("Commands that deal with the workspace"),
94 "");
95CMD_GROUP(user, "user", "", CMD_REF(__root__),
96 N_("Commands defined by the user"),
97 "");
98
99// this file defines the task-oriented "top level" commands which can be
100// issued as part of a monotone command line. the command line can only
101// have one such command on it, followed by a vector of strings which are its
102// arguments. all --options will be processed by the main program *before*
103// calling a command
104//
105// we might expose this blunt command interface to scripting someday. but
106// not today.
107
108namespace commands
109{
110 const char * safe_gettext(const char * msgid)
111 {
112 if (strlen(msgid) == 0)
113 return msgid;
114
115 return _(msgid);
116 }
117
118 // This must be a pointer.
119 // It's used by the constructor of other static objects in different
120 // files (cmd_*.cc), and since they're in different files, there's no
121 // guarantee about what order they'll be initialized in. So have this
122 // be something that doesn't get automatic initialization, and initialize
123 // it ourselves the first time we use it.
124 typedef map< command *, command * > relation_map;
125 static relation_map * cmds_relation_map = NULL;
126
127 static void init_children(void)
128 {
129 static bool children_inited = false;
130
131 if (!children_inited)
132 {
133 children_inited = true;
134
135 for (relation_map::iterator iter = cmds_relation_map->begin();
136 iter != cmds_relation_map->end(); iter++)
137 {
138 if ((*iter).second != NULL)
139 (*iter).second->children().insert((*iter).first);
140 }
141 }
142 }
143}
144
145//
146// Implementation of the commands::command class.
147//
148namespace commands {
149 command::command(std::string const & primary_name,
150 std::string const & other_names,
151 command * parent,
152 bool is_group,
153 bool hidden,
154 std::string const & params,
155 std::string const & abstract,
156 std::string const & desc,
157 bool use_workspace_options,
158 options::options_type const & opts,
159 bool _allow_completion)
160 : m_primary_name(utf8(primary_name)),
161 m_parent(parent),
162 m_is_group(is_group),
163 m_hidden(hidden),
164 m_params(utf8(params)),
165 m_abstract(utf8(abstract)),
166 m_desc(utf8(desc)),
167 m_use_workspace_options(use_workspace_options),
168 m_opts(opts),
169 m_allow_completion(_allow_completion)
170 {
171 // A warning about the parent pointer: commands are defined as global
172 // variables, so they are initialized during program startup. As they
173 // are spread over different compilation units, we have no idea of the
174 // order in which they will be initialized. Therefore, accessing
175 // *parent from here is dangerous.
176 //
177 // This is the reason for the cmds_relation_map. We cannot set up
178 // the m_children set until a late stage during program execution.
179
180 if (cmds_relation_map == NULL)
181 cmds_relation_map = new relation_map();
182 (*cmds_relation_map)[this] = m_parent;
183
184 m_names.insert(m_primary_name);
185
186 vector< utf8 > onv = split_into_words(utf8(other_names));
187 m_names.insert(onv.begin(), onv.end());
188 }
189
190 command::~command(void)
191 {
192 }
193
194 bool
195 command::allow_completion() const
196 {
197 return m_allow_completion &&
198 (m_parent?m_parent->allow_completion():true);
199 }
200
201 command_id
202 command::ident(void) const
203 {
204 I(this != CMD_REF(__root__));
205
206 command_id i;
207
208 if (parent() != CMD_REF(__root__))
209 i = parent()->ident();
210 i.push_back(primary_name());
211
212 I(!i.empty());
213 return i;
214 }
215
216 const utf8 &
217 command::primary_name(void) const
218 {
219 return m_primary_name;
220 }
221
222 const command::names_set &
223 command::names(void) const
224 {
225 return m_names;
226 }
227
228 void
229 command::add_alias(const utf8 &new_name)
230 {
231 m_names.insert(new_name);
232 }
233
234
235 command *
236 command::parent(void) const
237 {
238 return m_parent;
239 }
240
241 bool
242 command::is_group(void) const
243 {
244 return m_is_group;
245 }
246
247 bool
248 command::hidden(void) const
249 {
250 return m_hidden;
251 }
252
253 std::string
254 command::params() const
255 {
256 return safe_gettext(m_params().c_str());
257 }
258
259 std::string
260 command::abstract() const
261 {
262 return safe_gettext(m_abstract().c_str());
263 }
264
265 std::string
266 command::desc() const
267 {
268 return abstract() + ".\n" + safe_gettext(m_desc().c_str());
269 }
270
271 command::names_set
272 command::subcommands(void) const
273 {
274 names_set set;
275 init_children();
276 for (children_set::const_iterator i = m_children.begin();
277 i != m_children.end(); i++)
278 {
279 if ((*i)->hidden())
280 continue;
281 names_set const & other = (*i)->names();
282 set.insert(other.begin(), other.end());
283 }
284 return set;
285 }
286
287 options::options_type const &
288 command::opts(void) const
289 {
290 return m_opts;
291 }
292
293 bool
294 command::use_workspace_options(void) const
295 {
296 return m_use_workspace_options;
297 }
298
299 command::children_set &
300 command::children(void)
301 {
302 init_children();
303 return m_children;
304 }
305
306 command::children_set const &
307 command::children(void) const
308 {
309 init_children();
310 return m_children;
311 }
312
313 bool
314 command::is_leaf(void) const
315 {
316 return children().empty();
317 }
318
319 bool
320 command::operator<(command const & cmd) const
321 {
322 // *twitch*
323 return (parent()->primary_name() < cmd.parent()->primary_name() ||
324 ((parent() == cmd.parent()) &&
325 primary_name() < cmd.primary_name()));
326 }
327
328 bool
329 command::has_name(utf8 const & name) const
330 {
331 return names().find(name) != names().end();
332 }
333
334 command const *
335 command::find_command(command_id const & id) const
336 {
337 command const * cmd;
338
339 if (id.empty())
340 cmd = this;
341 else
342 {
343 utf8 component = *(id.begin());
344 command const * match = find_child_by_name(component);
345
346 if (match != NULL)
347 {
348 command_id remaining(id.begin() + 1, id.end());
349 I(remaining.size() == id.size() - 1);
350 cmd = match->find_command(remaining);
351 }
352 else
353 cmd = NULL;
354 }
355
356 return cmd;
357 }
358
359 command *
360 command::find_command(command_id const & id)
361 {
362 command * cmd;
363
364 if (id.empty())
365 cmd = this;
366 else
367 {
368 utf8 component = *(id.begin());
369 command * match = find_child_by_name(component);
370
371 if (match != NULL)
372 {
373 command_id remaining(id.begin() + 1, id.end());
374 I(remaining.size() == id.size() - 1);
375 cmd = match->find_command(remaining);
376 }
377 else
378 cmd = NULL;
379 }
380
381 return cmd;
382 }
383
384 map< command_id, command * >
385 command::find_completions(utf8 const & prefix, command_id const & completed,
386 bool completion_ok)
387 const
388 {
389 map< command_id, command * > matches;
390
391 I(!prefix().empty());
392
393 for (children_set::const_iterator iter = children().begin();
394 iter != children().end(); iter++)
395 {
396 command * child = *iter;
397
398 for (names_set::const_iterator iter2 = child->names().begin();
399 iter2 != child->names().end(); iter2++)
400 {
401 command_id caux = completed;
402 caux.push_back(*iter2);
403
404 // If one of the command names was an exact match,
405 // do not try to find other possible completions.
406 // This would eventually hinder us to ever call a command
407 // whose name is also the prefix for another command in the
408 // same group (f.e. mtn automate cert and mtn automate certs)
409 if (prefix == *iter2)
410 {
411 // since the command children are not sorted, we
412 // need to ensure that no other partial completed
413 // commands matched
414 matches.clear();
415 matches[caux] = child;
416 return matches;
417 }
418
419 if (!child->hidden() &&
420 prefix().length() < (*iter2)().length() &&
421 allow_completion() && completion_ok)
422 {
423 string temp((*iter2)(), 0, prefix().length());
424 utf8 p(temp);
425 if (prefix == p)
426 matches[caux] = child;
427 }
428 }
429 }
430
431 return matches;
432 }
433
434 set< command_id >
435 command::complete_command(command_id const & id,
436 command_id completed,
437 bool completion_ok) const
438 {
439 I(this != CMD_REF(__root__) || !id.empty());
440 I(!id.empty());
441
442 set< command_id > matches;
443
444 utf8 component = *(id.begin());
445 command_id remaining(id.begin() + 1, id.end());
446
447 map< command_id, command * >
448 m2 = find_completions(component,
449 completed,
450 allow_completion() && completion_ok);
451 for (map< command_id, command * >::const_iterator iter = m2.begin();
452 iter != m2.end(); iter++)
453 {
454 command_id const & i2 = (*iter).first;
455 command * child = (*iter).second;
456
457 if (child->is_leaf() || remaining.empty())
458 matches.insert(i2);
459 else
460 {
461 I(remaining.size() == id.size() - 1);
462 command_id caux = completed;
463 caux.push_back(i2[i2.size() - 1]);
464 set< command_id > maux = child->complete_command(remaining, caux);
465 if (maux.empty())
466 matches.insert(i2);
467 else
468 matches.insert(maux.begin(), maux.end());
469 }
470 }
471
472 return matches;
473 }
474
475 command *
476 command::find_child_by_name(utf8 const & name) const
477 {
478 I(!name().empty());
479
480 command * cmd = NULL;
481
482 for (children_set::const_iterator iter = children().begin();
483 iter != children().end() && cmd == NULL; iter++)
484 {
485 command * child = *iter;
486
487 if (child->has_name(name))
488 cmd = child;
489 }
490
491 return cmd;
492 }
493};
494
495namespace std
496{
497 template <>
498 struct greater<commands::command *>
499 {
500 bool operator()(commands::command const * a, commands::command const * b)
501 {
502 return *a < *b;
503 }
504 };
505};
506
507namespace commands
508{
509 command_id
510 complete_command(args_vector const & args)
511 {
512 // Handle categories early; no completion allowed.
513 if (CMD_REF(__root__)->find_command(make_command_id(args[0]())) != NULL)
514 return make_command_id(args[0]());
515
516 command_id id;
517 for (args_vector::const_iterator iter = args.begin();
518 iter != args.end(); iter++)
519 id.push_back(utf8((*iter)()));
520
521 set< command_id > matches;
522
523 command::children_set const & cs = CMD_REF(__root__)->children();
524 for (command::children_set::const_iterator iter = cs.begin();
525 iter != cs.end(); iter++)
526 {
527 command const * child = *iter;
528
529 set< command_id > m2 = child->complete_command(id, child->ident());
530 matches.insert(m2.begin(), m2.end());
531 }
532
533 if (matches.size() >= 2)
534 {
535 // If there is an exact match at the lowest level, pick it. Needed
536 // to automatically resolve ambiguities between, e.g., 'drop' and
537 // 'dropkey'.
538 command_id tmp;
539
540 for (set< command_id >::const_iterator iter = matches.begin();
541 iter != matches.end() && tmp.empty(); iter++)
542 {
543 command_id const & id = *iter;
544 I(id.size() >= 2);
545 if (id[id.size() - 1]() == args[id.size() - 2]())
546 tmp = id;
547 }
548
549 if (!tmp.empty())
550 {
551 matches.clear();
552 matches.insert(tmp);
553 }
554 }
555
556 if (matches.empty())
557 {
558 N(false,
559 F("unknown command '%s'") % join_words(id)());
560 }
561 else if (matches.size() == 1)
562 {
563 id = *matches.begin();
564 }
565 else
566 {
567 I(matches.size() > 1);
568 string err =
569 (F("'%s' is ambiguous; possible completions are:") %
570 join_words(id)()).str();
571 for (set< command_id >::const_iterator iter = matches.begin();
572 iter != matches.end(); iter++)
573 err += '\n' + join_words(*iter)();
574 N(false, i18n_format(err));
575 }
576
577 I(!id.empty());
578 return id;
579 }
580
581 static command const *
582 find_command(command_id const & ident)
583 {
584 command const * cmd = CMD_REF(__root__)->find_command(ident);
585
586 // This function is only used internally with an identifier returned
587 // by complete_command. Therefore, it must always exist.
588 I(cmd != NULL);
589
590 return cmd;
591 }
592
593 // Prints the abstract description of the given command or command group
594 // properly indented. The tag starts at column two. The description has
595 // to start, at the very least, two spaces after the tag's end position;
596 // this is given by the colabstract parameter.
597 static void describe(const string & tag, const string & abstract,
598 const string & subcommands, size_t colabstract,
599 ostream & out)
600 {
601 I(colabstract > 0);
602
603 size_t col = 0;
604 out << " " << tag << " ";
605 col += display_width(utf8(tag + " "));
606
607 out << string(colabstract - col, ' ');
608 col = colabstract;
609 string desc(abstract);
610 if (!subcommands.empty())
611 {
612 desc += " (" + subcommands + ')';
613 }
614 out << format_text(desc, colabstract, col) << '\n';
615 }
616
617 static void explain_children(command::children_set const & children,
618 ostream & out)
619 {
620 I(!children.empty());
621
622 vector< command const * > sorted;
623
624 size_t colabstract = 0;
625 for (command::children_set::const_iterator i = children.begin();
626 i != children.end(); i++)
627 {
628 command const * child = *i;
629
630 if (child->hidden())
631 continue;
632
633 size_t len = display_width(join_words(child->names(), ", ")) +
634 display_width(utf8(" "));
635 if (colabstract < len)
636 colabstract = len;
637
638 sorted.push_back(child);
639 }
640
641 sort(sorted.begin(), sorted.end(), std::greater< command * >());
642
643 for (vector< command const * >::const_iterator i = sorted.begin();
644 i != sorted.end(); i++)
645 {
646 command const * child = *i;
647 describe(join_words(child->names(), ", ")(), child->abstract(),
648 join_words(child->subcommands(), ", ")(),
649 colabstract, out);
650 }
651 }
652
653 static void explain_cmd_usage(command_id const & ident, ostream & out)
654 {
655 I(ident.size() >= 1);
656
657 vector< string > lines;
658 command const * cmd = find_command(ident);
659
660 string visibleid = join_words(vector< utf8 >(ident.begin() + 1,
661 ident.end()))();
662
663 if (visibleid.empty())
664 out << format_text(F("Commands in group '%s':") %
665 join_words(ident)())
666 << "\n\n";
667 else
668 {
669 if (!cmd->children().empty())
670 out << format_text(F("Subcommands of '%s %s':") %
671 ui.prog_name % visibleid)
672 << "\n\n";
673 else
674 out << format_text(F("Syntax specific to '%s %s':") %
675 ui.prog_name % visibleid)
676 << "\n\n";
677 }
678
679 // Print command parameters.
680 string params = cmd->params();
681 split_into_lines(params, lines);
682 if (!lines.empty())
683 {
684 for (vector<string>::const_iterator j = lines.begin();
685 j != lines.end(); ++j)
686 out << " " << visibleid << ' ' << *j << '\n';
687 out << '\n';
688 }
689
690 // Explain children, if any.
691 if (!cmd->is_leaf())
692 {
693 explain_children(cmd->children(), out);
694 out << '\n';
695 }
696
697 // Print command description.
698 if (visibleid.empty())
699 out << format_text(F("Purpose of group '%s':") %
700 join_words(ident)())
701 << "\n\n";
702 else
703 out << format_text(F("Description for '%s %s':") %
704 ui.prog_name % visibleid)
705 << "\n\n";
706 out << format_text(cmd->desc(), 2) << "\n\n";
707
708 // Print all available aliases.
709 if (cmd->names().size() > 1)
710 {
711 command::names_set othernames = cmd->names();
712 othernames.erase(ident[ident.size() - 1]);
713 out << format_text(F("Aliases: %s.") %
714 join_words(othernames, ", ")(), 2)
715 << '\n';
716 }
717 }
718
719 command_id make_command_id(std::string const & path)
720 {
721 return split_into_words(utf8(path));
722 }
723
724 void explain_usage(command_id const & ident, ostream & out)
725 {
726 command const * cmd = find_command(ident);
727
728 if (ident.empty())
729 {
730 out << format_text(F("Command groups:")) << "\n\n";
731 explain_children(CMD_REF(__root__)->children(), out);
732 out << '\n'
733 << format_text(F("For information on a specific command, type "
734 "'mtn help <command_name> [subcommand_name ...]'."))
735 << "\n\n"
736 << format_text(F("To see more details about the commands of a "
737 "particular group, type 'mtn help <group_name>'."))
738 << "\n\n"
739 << format_text(F("Note that you can always abbreviate a command "
740 "name as long as it does not conflict with other "
741 "names."))
742 << "\n";
743 }
744 else
745 explain_cmd_usage(ident, out);
746 }
747
748 void process(app_state & app, command_id const & ident,
749 args_vector const & args)
750 {
751 command const * cmd = CMD_REF(__root__)->find_command(ident);
752
753 string visibleid = join_words(vector< utf8 >(ident.begin() + 1,
754 ident.end()))();
755
756 I(cmd->is_leaf() || cmd->is_group());
757 N(!(cmd->is_group() && cmd->parent() == CMD_REF(__root__)),
758 F("command '%s' is invalid; it is a group") % join_words(ident));
759
760 N(!(!cmd->is_leaf() && args.empty()),
761 F("no subcommand specified for '%s'") % visibleid);
762
763 N(!(!cmd->is_leaf() && !args.empty()),
764 F("could not match '%s' to a subcommand of '%s'") %
765 join_words(args) % visibleid);
766
767 L(FL("executing command '%s'") % visibleid);
768
769 // at this point we process the data from _MTN/options if
770 // the command needs it.
771 if (cmd->use_workspace_options())
772 {
773 workspace::check_ws_format();
774 workspace::get_ws_options(app.opts);
775 }
776
777 cmd->exec(app, ident, args);
778 }
779
780 options::options_type command_options(command_id const & ident)
781 {
782 command const * cmd = find_command(ident);
783 return cmd->opts();
784 }
785}
786////////////////////////////////////////////////////////////////////////
787
788CMD(help, "help", "", CMD_REF(informative), N_("command [ARGS...]"),
789 N_("Displays help about commands and options"),
790 "",
791 options::opts::none)
792{
793 if (args.size() < 1)
794 {
795 app.opts.help = true;
796 throw usage(command_id());
797 }
798
799 command_id id = commands::complete_command(args);
800 app.opts.help = true;
801 throw usage(id);
802}
803
804CMD_HIDDEN(crash, "crash", "", CMD_REF(debug),
805 "{ N | E | I | exception | signal }",
806 N_("Triggers the specified kind of crash"),
807 "",
808 options::opts::none)
809{
810 if (args.size() != 1)
811 throw usage(execid);
812 bool spoon_exists(false);
813 if (idx(args,0)() == "N")
814 N(spoon_exists, i18n_format("There is no spoon."));
815 else if (idx(args,0)() == "E")
816 E(spoon_exists, i18n_format("There is no spoon."));
817 else if (idx(args,0)() == "I")
818 {
819 I(spoon_exists);
820 }
821#define maybe_throw(ex) if(idx(args,0)()==#ex) throw ex("There is no spoon.")
822#define maybe_throw_bare(ex) if(idx(args,0)()==#ex) throw ex()
823 else maybe_throw_bare(std::bad_alloc);
824 else maybe_throw_bare(std::bad_cast);
825 else maybe_throw_bare(std::bad_typeid);
826 else maybe_throw_bare(std::bad_exception);
827 else maybe_throw_bare(std::exception);
828 else maybe_throw(std::domain_error);
829 else maybe_throw(std::invalid_argument);
830 else maybe_throw(std::length_error);
831 else maybe_throw(std::out_of_range);
832 else maybe_throw(std::range_error);
833 else maybe_throw(std::overflow_error);
834 else maybe_throw(std::underflow_error);
835 else maybe_throw(std::logic_error);
836 else maybe_throw(std::runtime_error);
837 else
838 {
839#ifndef _WIN32
840 try
841 {
842 int signo = boost::lexical_cast<int>(idx(args,0)());
843 if (0 < signo && signo <= 15)
844 {
845 raise(signo);
846 // control should not get here...
847 I(!"crash: raise returned");
848 }
849 }
850 catch (boost::bad_lexical_cast&)
851 { // fall through and throw usage
852 }
853#endif
854 throw usage(execid);
855 }
856#undef maybe_throw
857#undef maybe_throw_bare
858}
859
860string
861describe_revision(project_t & project, revision_id const & id)
862{
863 cert_name author_name(author_cert_name);
864 cert_name date_name(date_cert_name);
865
866 string description;
867
868 description += encode_hexenc(id.inner()());
869
870 // append authors and date of this revision
871 vector< revision<cert> > tmp;
872 project.get_revision_certs_by_name(id, author_name, tmp);
873 for (vector< revision<cert> >::const_iterator i = tmp.begin();
874 i != tmp.end(); ++i)
875 {
876 description += " ";
877 description += i->inner().value();
878 }
879 project.get_revision_certs_by_name(id, date_name, tmp);
880 for (vector< revision<cert> >::const_iterator i = tmp.begin();
881 i != tmp.end(); ++i)
882 {
883 description += " ";
884 description += i->inner().value();
885 }
886
887 return description;
888}
889
890void
891notify_if_multiple_heads(project_t & project,
892 branch_name const & branchname,
893 bool ignore_suspend_certs)
894{
895 set<revision_id> heads;
896 project.get_branch_heads(branchname, heads, ignore_suspend_certs);
897 if (heads.size() > 1) {
898 string prefixedline;
899 prefix_lines_with(_("note: "),
900 _("branch '%s' has multiple heads\n"
901 "perhaps consider '%s merge'"),
902 prefixedline);
903 P(i18n_format(prefixedline) % branchname % ui.prog_name);
904 }
905}
906
907void
908process_commit_message_args(options const & opts,
909 bool & given,
910 utf8 & log_message,
911 utf8 const & message_prefix)
912{
913 // can't have both a --message and a --message-file ...
914 N(!opts.message_given || !opts.msgfile_given,
915 F("--message and --message-file are mutually exclusive"));
916
917 if (opts.message_given)
918 {
919 string msg;
920 join_lines(opts.message, msg);
921 log_message = utf8(msg);
922 if (message_prefix().length() != 0)
923 log_message = utf8(message_prefix() + "\n\n" + log_message());
924 given = true;
925 }
926 else if (opts.msgfile_given)
927 {
928 data dat;
929 read_data_for_command_line(opts.msgfile, dat);
930 external dat2 = external(dat());
931 system_to_utf8(dat2, log_message);
932 if (message_prefix().length() != 0)
933 log_message = utf8(message_prefix() + "\n\n" + log_message());
934 given = true;
935 }
936 else if (message_prefix().length() != 0)
937 {
938 log_message = message_prefix;
939 given = true;
940 }
941 else
942 given = false;
943}
944
945#ifdef BUILD_UNIT_TESTS
946#include "unit_tests.hh"
947
948CMD_GROUP(top, "top", "", CMD_REF(__root__),
949 "", "");
950CMD(test, "test", "", CMD_REF(top),
951 "", "", "", options::opts::none) {}
952CMD(test1, "test1", "alias1", CMD_REF(top),
953 "", "", "", options::opts::none) {}
954CMD(test2, "test2", "alias2", CMD_REF(top),
955 "", "", "", options::opts::none) {}
956CMD_HIDDEN(test3, "test3", "", CMD_REF(top),
957 "", "", "", options::opts::none) {}
958
959CMD_GROUP(testg, "testg", "aliasg", CMD_REF(top),
960 "", "");
961CMD(testg1, "testg1", "", CMD_REF(testg),
962 "", "", "", options::opts::none) {}
963CMD(testg2, "testg2", "", CMD_REF(testg),
964 "", "", "", options::opts::none) {}
965CMD_HIDDEN(testg3, "testg3", "", CMD_REF(testg),
966 "", "", "", options::opts::none) {}
967
968static args_vector
969mkargs(const char *words)
970{
971 return split_into_words(arg_type(words));
972}
973
974UNIT_TEST(commands, make_command_id)
975{
976 using commands::command_id;
977 using commands::make_command_id;
978
979 {
980 command_id id = make_command_id("foo");
981 UNIT_TEST_CHECK(id.size() == 1);
982 UNIT_TEST_CHECK(id[0]() == "foo");
983 }
984
985 {
986 command_id id = make_command_id("foo bar");
987 UNIT_TEST_CHECK(id.size() == 2);
988 UNIT_TEST_CHECK(id[0]() == "foo");
989 UNIT_TEST_CHECK(id[1]() == "bar");
990 }
991}
992
993UNIT_TEST(commands, complete_command)
994{
995 using commands::command_id;
996 using commands::complete_command;
997 using commands::make_command_id;
998
999 // Single-word identifier, top-level category.
1000 {
1001 command_id id = complete_command(mkargs("top"));
1002 UNIT_TEST_CHECK(id == make_command_id("top"));
1003 }
1004
1005 // Single-word identifier.
1006 {
1007 command_id id = complete_command(mkargs("testg"));
1008 UNIT_TEST_CHECK(id == make_command_id("top testg"));
1009 }
1010
1011 // Single-word identifier, non-primary name.
1012 {
1013 command_id id = complete_command(mkargs("alias1"));
1014 UNIT_TEST_CHECK(id == make_command_id("top alias1"));
1015 }
1016
1017 // Multi-word identifier.
1018 {
1019 command_id id = complete_command(mkargs("testg testg1"));
1020 UNIT_TEST_CHECK(id == make_command_id("top testg testg1"));
1021 }
1022
1023 // Multi-word identifier, non-primary names.
1024 {
1025 command_id id = complete_command(mkargs("al testg1"));
1026 UNIT_TEST_CHECK(id == make_command_id("top aliasg testg1"));
1027 }
1028}
1029
1030UNIT_TEST(commands, command_complete_command)
1031{
1032 using commands::command_id;
1033 using commands::make_command_id;
1034
1035 // Non-existent single-word identifier.
1036 {
1037 command_id id = make_command_id("foo");
1038 set< command_id > matches = CMD_REF(top)->complete_command(id);
1039 UNIT_TEST_REQUIRE(matches.empty());
1040 }
1041
1042 // Non-existent multi-word identifier.
1043 {
1044 command_id id = make_command_id("foo bar");
1045 set< command_id > matches = CMD_REF(top)->complete_command(id);
1046 UNIT_TEST_REQUIRE(matches.empty());
1047 }
1048
1049 // Single-word identifier with one match. Exact matches are found
1050 // before any possible completions.
1051 {
1052 command_id id = make_command_id("test");
1053 set< command_id > matches = CMD_REF(top)->complete_command(id);
1054 UNIT_TEST_REQUIRE(matches.size() == 1);
1055 UNIT_TEST_CHECK(*matches.begin() == make_command_id("test"));
1056 }
1057
1058 // Single-word identifier with one match, non-primary name.
1059 {
1060 command_id id = make_command_id("alias1");
1061 set< command_id > matches = CMD_REF(top)->complete_command(id);
1062 UNIT_TEST_REQUIRE(matches.size() == 1);
1063 UNIT_TEST_CHECK(*matches.begin() == make_command_id("alias1"));
1064 }
1065
1066 // Single-word identifier with multiple matches.
1067 {
1068 command_id id = make_command_id("tes");
1069 set< command_id > matches = CMD_REF(top)->complete_command(id);
1070 UNIT_TEST_REQUIRE(matches.size() == 4);
1071
1072 set< command_id > expected;
1073 expected.insert(make_command_id("test"));
1074 expected.insert(make_command_id("test1"));
1075 expected.insert(make_command_id("test2"));
1076 expected.insert(make_command_id("testg"));
1077 UNIT_TEST_CHECK(matches == expected);
1078 }
1079
1080 // Single-word identifier with multiple matches, non-primary name.
1081 {
1082 command_id id = make_command_id("alias");
1083 set< command_id > matches = CMD_REF(top)->complete_command(id);
1084 UNIT_TEST_REQUIRE(matches.size() == 3);
1085
1086 set< command_id > expected;
1087 expected.insert(make_command_id("alias1"));
1088 expected.insert(make_command_id("alias2"));
1089 expected.insert(make_command_id("aliasg"));
1090 UNIT_TEST_CHECK(matches == expected);
1091 }
1092
1093 // Multi-word identifier with one match.
1094 {
1095 command_id id = make_command_id("testg testg1");
1096 set< command_id > matches = CMD_REF(top)->complete_command(id);
1097 UNIT_TEST_REQUIRE(matches.size() == 1);
1098
1099 set< command_id > expected;
1100 expected.insert(make_command_id("testg testg1"));
1101 UNIT_TEST_CHECK(matches == expected);
1102 }
1103
1104 // Multi-word identifier with multiple matches.
1105 {
1106 command_id id = make_command_id("testg testg");
1107 set< command_id > matches = CMD_REF(top)->complete_command(id);
1108 UNIT_TEST_REQUIRE(matches.size() == 2);
1109
1110 set< command_id > expected;
1111 expected.insert(make_command_id("testg testg1"));
1112 expected.insert(make_command_id("testg testg2"));
1113 UNIT_TEST_CHECK(matches == expected);
1114 }
1115
1116 // Multi-word identifier with multiple matches at different levels.
1117 {
1118 command_id id = make_command_id("tes testg1");
1119 set< command_id > matches = CMD_REF(top)->complete_command(id);
1120 UNIT_TEST_REQUIRE(matches.size() == 4);
1121
1122 set< command_id > expected;
1123 expected.insert(make_command_id("test"));
1124 expected.insert(make_command_id("test1"));
1125 expected.insert(make_command_id("test2"));
1126 expected.insert(make_command_id("testg testg1"));
1127 UNIT_TEST_CHECK(matches == expected);
1128 }
1129
1130 // Multi-word identifier with one match and extra words.
1131 {
1132 command_id id = make_command_id("testg testg1 foo");
1133 set< command_id > matches = CMD_REF(top)->complete_command(id);
1134 UNIT_TEST_REQUIRE(matches.size() == 1);
1135
1136 set< command_id > expected;
1137 expected.insert(make_command_id("testg testg1"));
1138 UNIT_TEST_CHECK(matches == expected);
1139 }
1140}
1141
1142UNIT_TEST(commands, command_find_command)
1143{
1144 using commands::command;
1145 using commands::command_id;
1146 using commands::make_command_id;
1147
1148 // Non-existent single-word identifier.
1149 {
1150 command_id id = make_command_id("foo");
1151 command const * cmd = CMD_REF(top)->find_command(id);
1152 UNIT_TEST_CHECK(cmd == NULL);
1153 }
1154
1155 // Non-existent multi-word identifier.
1156 {
1157 command_id id = make_command_id("foo bar");
1158 command const * cmd = CMD_REF(top)->find_command(id);
1159 UNIT_TEST_CHECK(cmd == NULL);
1160 }
1161
1162 // Single-word identifier that could be completed.
1163 {
1164 command_id id = make_command_id("tes");
1165 command const * cmd = CMD_REF(top)->find_command(id);
1166 UNIT_TEST_CHECK(cmd == NULL);
1167 }
1168
1169 // Single-word identifier.
1170 {
1171 command_id id = make_command_id("test1");
1172 command const * cmd = CMD_REF(top)->find_command(id);
1173 UNIT_TEST_CHECK(cmd == CMD_REF(test1));
1174 }
1175
1176 // Hidden single-word identifier.
1177 {
1178 command_id id = make_command_id("test3");
1179 command const * cmd = CMD_REF(top)->find_command(id);
1180 UNIT_TEST_CHECK(cmd == CMD_REF(test3));
1181 }
1182
1183 // Multi-word identifier that could be completed.
1184 {
1185 command_id id = make_command_id("testg testg");
1186 command const * cmd = CMD_REF(top)->find_command(id);
1187 UNIT_TEST_CHECK(cmd == NULL);
1188 }
1189
1190 // Multi-word identifier.
1191 {
1192 command_id id = make_command_id("testg testg1");
1193 command const * cmd = CMD_REF(top)->find_command(id);
1194 UNIT_TEST_CHECK(cmd == CMD_REF(testg1));
1195 }
1196
1197 // Hidden multi-word identifier.
1198 {
1199 command_id id = make_command_id("testg testg3");
1200 command const * cmd = CMD_REF(top)->find_command(id);
1201 UNIT_TEST_CHECK(cmd == CMD_REF(testg3));
1202 }
1203
1204 // Multi-word identifier with extra words.
1205 {
1206 command_id id = make_command_id("testg testg1 foo");
1207 command const * cmd = CMD_REF(top)->find_command(id);
1208 UNIT_TEST_CHECK(cmd == NULL);
1209 }
1210}
1211#endif // BUILD_UNIT_TESTS
1212
1213// Local Variables:
1214// mode: C++
1215// fill-column: 76
1216// c-file-style: "gnu"
1217// indent-tabs-mode: nil
1218// End:
1219// 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