1 //===-- CommandObjectMultiword.cpp ------------------------------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 // C Includes
11 // C++ Includes
12 // Other libraries and framework includes
13 // Project includes
14 #include "lldb/Interpreter/CommandObjectMultiword.h"
15 #include "lldb/Core/Debugger.h"
16 #include "lldb/Interpreter/CommandInterpreter.h"
17 #include "lldb/Interpreter/Options.h"
18 #include "lldb/Interpreter/CommandReturnObject.h"
19 
20 using namespace lldb;
21 using namespace lldb_private;
22 
23 //-------------------------------------------------------------------------
24 // CommandObjectMultiword
25 //-------------------------------------------------------------------------
26 
27 CommandObjectMultiword::CommandObjectMultiword(CommandInterpreter &interpreter,
28                                                const char *name,
29                                                const char *help,
30                                                const char *syntax,
31                                                uint32_t flags) :
32     CommandObject (interpreter, name, help, syntax, flags),
33     m_can_be_removed(false)
34 {
35 }
36 
37 CommandObjectMultiword::~CommandObjectMultiword() = default;
38 
39 CommandObjectSP
40 CommandObjectMultiword::GetSubcommandSP (const char *sub_cmd, StringList *matches)
41 {
42     CommandObjectSP return_cmd_sp;
43     CommandObject::CommandMap::iterator pos;
44 
45     if (!m_subcommand_dict.empty())
46     {
47         pos = m_subcommand_dict.find (sub_cmd);
48         if (pos != m_subcommand_dict.end()) {
49             // An exact match; append the sub_cmd to the 'matches' string list.
50             if (matches)
51                 matches->AppendString(sub_cmd);
52             return_cmd_sp = pos->second;
53         }
54         else
55         {
56             StringList local_matches;
57             if (matches == nullptr)
58                 matches = &local_matches;
59             int num_matches = AddNamesMatchingPartialString (m_subcommand_dict, sub_cmd, *matches);
60 
61             if (num_matches == 1)
62             {
63                 // Cleaner, but slightly less efficient would be to call back into this function, since I now
64                 // know I have an exact match...
65 
66                 sub_cmd = matches->GetStringAtIndex(0);
67                 pos = m_subcommand_dict.find(sub_cmd);
68                 if (pos != m_subcommand_dict.end())
69                     return_cmd_sp = pos->second;
70             }
71         }
72     }
73     return return_cmd_sp;
74 }
75 
76 CommandObject *
77 CommandObjectMultiword::GetSubcommandObject (const char *sub_cmd, StringList *matches)
78 {
79     return GetSubcommandSP(sub_cmd, matches).get();
80 }
81 
82 bool
83 CommandObjectMultiword::LoadSubCommand(const char *name,
84                                        const CommandObjectSP& cmd_obj)
85 {
86     if (cmd_obj)
87         assert((&GetCommandInterpreter() == &cmd_obj->GetCommandInterpreter()) && "tried to add a CommandObject from a different interpreter");
88 
89     CommandMap::iterator pos;
90     bool success = true;
91 
92     pos = m_subcommand_dict.find(name);
93     if (pos == m_subcommand_dict.end())
94     {
95         m_subcommand_dict[name] = cmd_obj;
96     }
97     else
98         success = false;
99 
100     return success;
101 }
102 
103 bool
104 CommandObjectMultiword::Execute(const char *args_string, CommandReturnObject &result)
105 {
106     Args args (args_string);
107     const size_t argc = args.GetArgumentCount();
108     if (argc == 0)
109     {
110         this->CommandObject::GenerateHelpText (result);
111     }
112     else
113     {
114         const char *sub_command = args.GetArgumentAtIndex (0);
115 
116         if (sub_command)
117         {
118             if (::strcasecmp (sub_command, "help") == 0)
119             {
120                 this->CommandObject::GenerateHelpText (result);
121             }
122             else if (!m_subcommand_dict.empty())
123             {
124                 StringList matches;
125                 CommandObject *sub_cmd_obj = GetSubcommandObject(sub_command, &matches);
126                 if (sub_cmd_obj != nullptr)
127                 {
128                     // Now call CommandObject::Execute to process and options in 'rest_of_line'.  From there
129                     // the command-specific version of Execute will be called, with the processed arguments.
130 
131                     args.Shift();
132 
133                     sub_cmd_obj->Execute (args_string, result);
134                 }
135                 else
136                 {
137                     std::string error_msg;
138                     const size_t num_subcmd_matches = matches.GetSize();
139                     if (num_subcmd_matches > 0)
140                         error_msg.assign ("ambiguous command ");
141                     else
142                         error_msg.assign ("invalid command ");
143 
144                     error_msg.append ("'");
145                     error_msg.append (GetCommandName());
146                     error_msg.append (" ");
147                     error_msg.append (sub_command);
148                     error_msg.append ("'.");
149 
150                     if (num_subcmd_matches > 0)
151                     {
152                         error_msg.append (" Possible completions:");
153                         for (size_t i = 0; i < num_subcmd_matches; i++)
154                         {
155                             error_msg.append ("\n\t");
156                             error_msg.append (matches.GetStringAtIndex (i));
157                         }
158                     }
159                     error_msg.append ("\n");
160                     result.AppendRawError (error_msg.c_str());
161                     result.SetStatus (eReturnStatusFailed);
162                 }
163             }
164             else
165             {
166                 result.AppendErrorWithFormat ("'%s' does not have any subcommands.\n", GetCommandName());
167                 result.SetStatus (eReturnStatusFailed);
168             }
169         }
170     }
171 
172     return result.Succeeded();
173 }
174 
175 void
176 CommandObjectMultiword::GenerateHelpText (Stream &output_stream)
177 {
178     // First time through here, generate the help text for the object and
179     // push it to the return result object as well
180 
181     output_stream.PutCString ("The following subcommands are supported:\n\n");
182 
183     CommandMap::iterator pos;
184     uint32_t max_len = FindLongestCommandWord (m_subcommand_dict);
185 
186     if (max_len)
187         max_len += 4; // Indent the output by 4 spaces.
188 
189     for (pos = m_subcommand_dict.begin(); pos != m_subcommand_dict.end(); ++pos)
190     {
191         std::string indented_command ("    ");
192         indented_command.append (pos->first);
193         if (pos->second->WantsRawCommandString ())
194         {
195             std::string help_text (pos->second->GetHelp());
196             help_text.append ("  This command takes 'raw' input (no need to quote stuff).");
197             m_interpreter.OutputFormattedHelpText (output_stream,
198                                                    indented_command.c_str(),
199                                                    "--",
200                                                    help_text.c_str(),
201                                                    max_len);
202         }
203         else
204             m_interpreter.OutputFormattedHelpText (output_stream,
205                                                    indented_command.c_str(),
206                                                    "--",
207                                                    pos->second->GetHelp(),
208                                                    max_len);
209     }
210 
211     output_stream.PutCString ("\nFor more help on any particular subcommand, type 'help <command> <subcommand>'.\n");
212 }
213 
214 int
215 CommandObjectMultiword::HandleCompletion(Args &input,
216                                          int &cursor_index,
217                                          int &cursor_char_position,
218                                          int match_start_point,
219                                          int max_return_elements,
220                                          bool &word_complete,
221                                          StringList &matches)
222 {
223     // Any of the command matches will provide a complete word, otherwise the individual
224     // completers will override this.
225     word_complete = true;
226 
227     const char *arg0 = input.GetArgumentAtIndex(0);
228     if (cursor_index == 0)
229     {
230         AddNamesMatchingPartialString (m_subcommand_dict,
231                                        arg0,
232                                        matches);
233 
234         if (matches.GetSize() == 1
235             && matches.GetStringAtIndex(0) != nullptr
236             && strcmp (arg0, matches.GetStringAtIndex(0)) == 0)
237         {
238             StringList temp_matches;
239             CommandObject *cmd_obj = GetSubcommandObject (arg0,
240                                                           &temp_matches);
241             if (cmd_obj != nullptr)
242             {
243                 if (input.GetArgumentCount() == 1)
244                 {
245                     word_complete = true;
246                 }
247                 else
248                 {
249                     matches.DeleteStringAtIndex (0);
250                     input.Shift();
251                     cursor_char_position = 0;
252                     input.AppendArgument ("");
253                     return cmd_obj->HandleCompletion (input,
254                                                       cursor_index,
255                                                       cursor_char_position,
256                                                       match_start_point,
257                                                       max_return_elements,
258                                                       word_complete,
259                                                       matches);
260                 }
261             }
262         }
263         return matches.GetSize();
264     }
265     else
266     {
267         CommandObject *sub_command_object = GetSubcommandObject (arg0,
268                                                                  &matches);
269         if (sub_command_object == nullptr)
270         {
271             return matches.GetSize();
272         }
273         else
274         {
275             // Remove the one match that we got from calling GetSubcommandObject.
276             matches.DeleteStringAtIndex(0);
277             input.Shift();
278             cursor_index--;
279             return sub_command_object->HandleCompletion (input,
280                                                          cursor_index,
281                                                          cursor_char_position,
282                                                          match_start_point,
283                                                          max_return_elements,
284                                                          word_complete,
285                                                          matches);
286         }
287     }
288 }
289 
290 const char *
291 CommandObjectMultiword::GetRepeatCommand (Args &current_command_args, uint32_t index)
292 {
293     index++;
294     if (current_command_args.GetArgumentCount() <= index)
295         return nullptr;
296     CommandObject *sub_command_object = GetSubcommandObject (current_command_args.GetArgumentAtIndex(index));
297     if (sub_command_object == nullptr)
298         return nullptr;
299     return sub_command_object->GetRepeatCommand(current_command_args, index);
300 }
301 
302 void
303 CommandObjectMultiword::AproposAllSubCommands (const char *prefix,
304                                                const char *search_word,
305                                                StringList &commands_found,
306                                                StringList &commands_help)
307 {
308     CommandObject::CommandMap::const_iterator pos;
309 
310     for (pos = m_subcommand_dict.begin(); pos != m_subcommand_dict.end(); ++pos)
311     {
312         const char * command_name = pos->first.c_str();
313         CommandObject *sub_cmd_obj = pos->second.get();
314         StreamString complete_command_name;
315 
316         complete_command_name.Printf ("%s %s", prefix, command_name);
317 
318         if (sub_cmd_obj->HelpTextContainsWord (search_word))
319         {
320             commands_found.AppendString (complete_command_name.GetData());
321             commands_help.AppendString (sub_cmd_obj->GetHelp());
322         }
323 
324         if (sub_cmd_obj->IsMultiwordObject())
325             sub_cmd_obj->AproposAllSubCommands (complete_command_name.GetData(),
326                                                 search_word,
327                                                 commands_found,
328                                                 commands_help);
329     }
330 }
331 
332 CommandObjectProxy::CommandObjectProxy (CommandInterpreter &interpreter,
333                                         const char *name,
334                                         const char *help,
335                                         const char *syntax,
336                                         uint32_t flags) :
337     CommandObject (interpreter, name, help, syntax, flags)
338 {
339 }
340 
341 CommandObjectProxy::~CommandObjectProxy() = default;
342 
343 const char *
344 CommandObjectProxy::GetHelpLong ()
345 {
346     CommandObject *proxy_command = GetProxyCommandObject();
347     if (proxy_command)
348         return proxy_command->GetHelpLong();
349     return nullptr;
350 }
351 
352 bool
353 CommandObjectProxy::IsRemovable() const
354 {
355     const CommandObject *proxy_command = const_cast<CommandObjectProxy *>(this)->GetProxyCommandObject();
356     if (proxy_command)
357         return proxy_command->IsRemovable();
358     return false;
359 }
360 
361 bool
362 CommandObjectProxy::IsMultiwordObject ()
363 {
364     CommandObject *proxy_command = GetProxyCommandObject();
365     if (proxy_command)
366         return proxy_command->IsMultiwordObject();
367     return false;
368 }
369 
370 void
371 CommandObjectProxy::GenerateHelpText (Stream &result)
372 {
373     CommandObject *proxy_command = GetProxyCommandObject();
374     if (proxy_command)
375         return proxy_command->GenerateHelpText(result);
376 }
377 
378 lldb::CommandObjectSP
379 CommandObjectProxy::GetSubcommandSP (const char *sub_cmd, StringList *matches)
380 {
381     CommandObject *proxy_command = GetProxyCommandObject();
382     if (proxy_command)
383         return proxy_command->GetSubcommandSP(sub_cmd, matches);
384     return lldb::CommandObjectSP();
385 }
386 
387 CommandObject *
388 CommandObjectProxy::GetSubcommandObject (const char *sub_cmd, StringList *matches)
389 {
390     CommandObject *proxy_command = GetProxyCommandObject();
391     if (proxy_command)
392         return proxy_command->GetSubcommandObject(sub_cmd, matches);
393     return nullptr;
394 }
395 
396 void
397 CommandObjectProxy::AproposAllSubCommands (const char *prefix,
398                                            const char *search_word,
399                                            StringList &commands_found,
400                                            StringList &commands_help)
401 {
402     CommandObject *proxy_command = GetProxyCommandObject();
403     if (proxy_command)
404         return proxy_command->AproposAllSubCommands (prefix,
405                                                      search_word,
406                                                      commands_found,
407                                                      commands_help);
408 }
409 
410 bool
411 CommandObjectProxy::LoadSubCommand (const char *cmd_name,
412                                     const lldb::CommandObjectSP& command_sp)
413 {
414     CommandObject *proxy_command = GetProxyCommandObject();
415     if (proxy_command)
416         return proxy_command->LoadSubCommand (cmd_name, command_sp);
417     return false;
418 }
419 
420 bool
421 CommandObjectProxy::WantsRawCommandString()
422 {
423     CommandObject *proxy_command = GetProxyCommandObject();
424     if (proxy_command)
425         return proxy_command->WantsRawCommandString();
426     return false;
427 }
428 
429 bool
430 CommandObjectProxy::WantsCompletion()
431 {
432     CommandObject *proxy_command = GetProxyCommandObject();
433     if (proxy_command)
434         return proxy_command->WantsCompletion();
435     return false;
436 }
437 
438 Options *
439 CommandObjectProxy::GetOptions ()
440 {
441     CommandObject *proxy_command = GetProxyCommandObject();
442     if (proxy_command)
443         return proxy_command->GetOptions ();
444     return nullptr;
445 }
446 
447 int
448 CommandObjectProxy::HandleCompletion (Args &input,
449                                       int &cursor_index,
450                                       int &cursor_char_position,
451                                       int match_start_point,
452                                       int max_return_elements,
453                                       bool &word_complete,
454                                       StringList &matches)
455 {
456     CommandObject *proxy_command = GetProxyCommandObject();
457     if (proxy_command)
458         return proxy_command->HandleCompletion (input,
459                                                 cursor_index,
460                                                 cursor_char_position,
461                                                 match_start_point,
462                                                 max_return_elements,
463                                                 word_complete,
464                                                 matches);
465     matches.Clear();
466     return 0;
467 }
468 
469 int
470 CommandObjectProxy::HandleArgumentCompletion (Args &input,
471                                               int &cursor_index,
472                                               int &cursor_char_position,
473                                               OptionElementVector &opt_element_vector,
474                                               int match_start_point,
475                                               int max_return_elements,
476                                               bool &word_complete,
477                                               StringList &matches)
478 {
479     CommandObject *proxy_command = GetProxyCommandObject();
480     if (proxy_command)
481         return proxy_command->HandleArgumentCompletion (input,
482                                                         cursor_index,
483                                                         cursor_char_position,
484                                                         opt_element_vector,
485                                                         match_start_point,
486                                                         max_return_elements,
487                                                         word_complete,
488                                                         matches);
489     matches.Clear();
490     return 0;
491 }
492 
493 const char *
494 CommandObjectProxy::GetRepeatCommand (Args &current_command_args,
495                                       uint32_t index)
496 {
497     CommandObject *proxy_command = GetProxyCommandObject();
498     if (proxy_command)
499         return proxy_command->GetRepeatCommand (current_command_args, index);
500     return nullptr;
501 }
502 
503 bool
504 CommandObjectProxy::Execute (const char *args_string,
505                              CommandReturnObject &result)
506 {
507     CommandObject *proxy_command = GetProxyCommandObject();
508     if (proxy_command)
509         return proxy_command->Execute (args_string, result);
510     result.AppendError ("command is not implemented");
511     result.SetStatus (eReturnStatusFailed);
512     return false;
513 }
514