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