1 //===-- CommandObjectMultiword.cpp ----------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "lldb/Interpreter/CommandObjectMultiword.h"
10 #include "lldb/Interpreter/CommandInterpreter.h"
11 #include "lldb/Interpreter/CommandReturnObject.h"
12 #include "lldb/Interpreter/Options.h"
13 
14 using namespace lldb;
15 using namespace lldb_private;
16 
17 // CommandObjectMultiword
18 
CommandObjectMultiword(CommandInterpreter & interpreter,const char * name,const char * help,const char * syntax,uint32_t flags)19 CommandObjectMultiword::CommandObjectMultiword(CommandInterpreter &interpreter,
20                                                const char *name,
21                                                const char *help,
22                                                const char *syntax,
23                                                uint32_t flags)
24     : CommandObject(interpreter, name, help, syntax, flags),
25       m_can_be_removed(false) {}
26 
27 CommandObjectMultiword::~CommandObjectMultiword() = default;
28 
GetSubcommandSP(llvm::StringRef sub_cmd,StringList * matches)29 CommandObjectSP CommandObjectMultiword::GetSubcommandSP(llvm::StringRef sub_cmd,
30                                                         StringList *matches) {
31   CommandObjectSP return_cmd_sp;
32   CommandObject::CommandMap::iterator pos;
33 
34   if (!m_subcommand_dict.empty()) {
35     pos = m_subcommand_dict.find(std::string(sub_cmd));
36     if (pos != m_subcommand_dict.end()) {
37       // An exact match; append the sub_cmd to the 'matches' string list.
38       if (matches)
39         matches->AppendString(sub_cmd);
40       return_cmd_sp = pos->second;
41     } else {
42       StringList local_matches;
43       if (matches == nullptr)
44         matches = &local_matches;
45       int num_matches =
46           AddNamesMatchingPartialString(m_subcommand_dict, sub_cmd, *matches);
47 
48       if (num_matches == 1) {
49         // Cleaner, but slightly less efficient would be to call back into this
50         // function, since I now know I have an exact match...
51 
52         sub_cmd = matches->GetStringAtIndex(0);
53         pos = m_subcommand_dict.find(std::string(sub_cmd));
54         if (pos != m_subcommand_dict.end())
55           return_cmd_sp = pos->second;
56       }
57     }
58   }
59   return return_cmd_sp;
60 }
61 
62 CommandObject *
GetSubcommandObject(llvm::StringRef sub_cmd,StringList * matches)63 CommandObjectMultiword::GetSubcommandObject(llvm::StringRef sub_cmd,
64                                             StringList *matches) {
65   return GetSubcommandSP(sub_cmd, matches).get();
66 }
67 
LoadSubCommand(llvm::StringRef name,const CommandObjectSP & cmd_obj)68 bool CommandObjectMultiword::LoadSubCommand(llvm::StringRef name,
69                                             const CommandObjectSP &cmd_obj) {
70   if (cmd_obj)
71     assert((&GetCommandInterpreter() == &cmd_obj->GetCommandInterpreter()) &&
72            "tried to add a CommandObject from a different interpreter");
73 
74   CommandMap::iterator pos;
75   bool success = true;
76 
77   pos = m_subcommand_dict.find(std::string(name));
78   if (pos == m_subcommand_dict.end()) {
79     m_subcommand_dict[std::string(name)] = cmd_obj;
80   } else
81     success = false;
82 
83   return success;
84 }
85 
Execute(const char * args_string,CommandReturnObject & result)86 bool CommandObjectMultiword::Execute(const char *args_string,
87                                      CommandReturnObject &result) {
88   Args args(args_string);
89   const size_t argc = args.GetArgumentCount();
90   if (argc == 0) {
91     this->CommandObject::GenerateHelpText(result);
92     return result.Succeeded();
93   }
94 
95   auto sub_command = args[0].ref();
96   if (sub_command.empty()) {
97     result.AppendError("Need to specify a non-empty subcommand.");
98     return result.Succeeded();
99   }
100 
101   if (sub_command.equals_insensitive("help")) {
102     this->CommandObject::GenerateHelpText(result);
103     return result.Succeeded();
104   }
105 
106   if (m_subcommand_dict.empty()) {
107     result.AppendErrorWithFormat("'%s' does not have any subcommands.\n",
108                                  GetCommandName().str().c_str());
109     return false;
110   }
111 
112   StringList matches;
113   CommandObject *sub_cmd_obj = GetSubcommandObject(sub_command, &matches);
114   if (sub_cmd_obj != nullptr) {
115     // Now call CommandObject::Execute to process options in `rest_of_line`.
116     // From there the command-specific version of Execute will be called, with
117     // the processed arguments.
118 
119     args.Shift();
120     sub_cmd_obj->Execute(args_string, result);
121     return result.Succeeded();
122   }
123 
124   std::string error_msg;
125   const size_t num_subcmd_matches = matches.GetSize();
126   if (num_subcmd_matches > 0)
127     error_msg.assign("ambiguous command ");
128   else
129     error_msg.assign("invalid command ");
130 
131   error_msg.append("'");
132   error_msg.append(std::string(GetCommandName()));
133   error_msg.append(" ");
134   error_msg.append(std::string(sub_command));
135   error_msg.append("'.");
136 
137   if (num_subcmd_matches > 0) {
138     error_msg.append(" Possible completions:");
139     for (const std::string &match : matches) {
140       error_msg.append("\n\t");
141       error_msg.append(match);
142     }
143   }
144   error_msg.append("\n");
145   result.AppendRawError(error_msg.c_str());
146   return false;
147 }
148 
GenerateHelpText(Stream & output_stream)149 void CommandObjectMultiword::GenerateHelpText(Stream &output_stream) {
150   // First time through here, generate the help text for the object and push it
151   // to the return result object as well
152 
153   CommandObject::GenerateHelpText(output_stream);
154   output_stream.PutCString("\nThe following subcommands are supported:\n\n");
155 
156   CommandMap::iterator pos;
157   uint32_t max_len = FindLongestCommandWord(m_subcommand_dict);
158 
159   if (max_len)
160     max_len += 4; // Indent the output by 4 spaces.
161 
162   for (pos = m_subcommand_dict.begin(); pos != m_subcommand_dict.end(); ++pos) {
163     std::string indented_command("    ");
164     indented_command.append(pos->first);
165     if (pos->second->WantsRawCommandString()) {
166       std::string help_text(std::string(pos->second->GetHelp()));
167       help_text.append("  Expects 'raw' input (see 'help raw-input'.)");
168       m_interpreter.OutputFormattedHelpText(output_stream, indented_command,
169                                             "--", help_text, max_len);
170     } else
171       m_interpreter.OutputFormattedHelpText(output_stream, indented_command,
172                                             "--", pos->second->GetHelp(),
173                                             max_len);
174   }
175 
176   output_stream.PutCString("\nFor more help on any particular subcommand, type "
177                            "'help <command> <subcommand>'.\n");
178 }
179 
HandleCompletion(CompletionRequest & request)180 void CommandObjectMultiword::HandleCompletion(CompletionRequest &request) {
181   auto arg0 = request.GetParsedLine()[0].ref();
182   if (request.GetCursorIndex() == 0) {
183     StringList new_matches, descriptions;
184     AddNamesMatchingPartialString(m_subcommand_dict, arg0, new_matches,
185                                   &descriptions);
186     request.AddCompletions(new_matches, descriptions);
187 
188     if (new_matches.GetSize() == 1 &&
189         new_matches.GetStringAtIndex(0) != nullptr &&
190         (arg0 == new_matches.GetStringAtIndex(0))) {
191       StringList temp_matches;
192       CommandObject *cmd_obj = GetSubcommandObject(arg0, &temp_matches);
193       if (cmd_obj != nullptr) {
194         if (request.GetParsedLine().GetArgumentCount() != 1) {
195           request.GetParsedLine().Shift();
196           request.AppendEmptyArgument();
197           cmd_obj->HandleCompletion(request);
198         }
199       }
200     }
201     return;
202   }
203 
204   StringList new_matches;
205   CommandObject *sub_command_object = GetSubcommandObject(arg0, &new_matches);
206   if (sub_command_object == nullptr) {
207     request.AddCompletions(new_matches);
208     return;
209   }
210 
211   // Remove the one match that we got from calling GetSubcommandObject.
212   new_matches.DeleteStringAtIndex(0);
213   request.AddCompletions(new_matches);
214   request.ShiftArguments();
215   sub_command_object->HandleCompletion(request);
216 }
217 
GetRepeatCommand(Args & current_command_args,uint32_t index)218 const char *CommandObjectMultiword::GetRepeatCommand(Args &current_command_args,
219                                                      uint32_t index) {
220   index++;
221   if (current_command_args.GetArgumentCount() <= index)
222     return nullptr;
223   CommandObject *sub_command_object =
224       GetSubcommandObject(current_command_args[index].ref());
225   if (sub_command_object == nullptr)
226     return nullptr;
227   return sub_command_object->GetRepeatCommand(current_command_args, index);
228 }
229 
AproposAllSubCommands(llvm::StringRef prefix,llvm::StringRef search_word,StringList & commands_found,StringList & commands_help)230 void CommandObjectMultiword::AproposAllSubCommands(llvm::StringRef prefix,
231                                                    llvm::StringRef search_word,
232                                                    StringList &commands_found,
233                                                    StringList &commands_help) {
234   CommandObject::CommandMap::const_iterator pos;
235 
236   for (pos = m_subcommand_dict.begin(); pos != m_subcommand_dict.end(); ++pos) {
237     const char *command_name = pos->first.c_str();
238     CommandObject *sub_cmd_obj = pos->second.get();
239     StreamString complete_command_name;
240 
241     complete_command_name << prefix << " " << command_name;
242 
243     if (sub_cmd_obj->HelpTextContainsWord(search_word)) {
244       commands_found.AppendString(complete_command_name.GetString());
245       commands_help.AppendString(sub_cmd_obj->GetHelp());
246     }
247 
248     if (sub_cmd_obj->IsMultiwordObject())
249       sub_cmd_obj->AproposAllSubCommands(complete_command_name.GetString(),
250                                          search_word, commands_found,
251                                          commands_help);
252   }
253 }
254 
CommandObjectProxy(CommandInterpreter & interpreter,const char * name,const char * help,const char * syntax,uint32_t flags)255 CommandObjectProxy::CommandObjectProxy(CommandInterpreter &interpreter,
256                                        const char *name, const char *help,
257                                        const char *syntax, uint32_t flags)
258     : CommandObject(interpreter, name, help, syntax, flags) {}
259 
260 CommandObjectProxy::~CommandObjectProxy() = default;
261 
GetOptions()262 Options *CommandObjectProxy::GetOptions() {
263   CommandObject *proxy_command = GetProxyCommandObject();
264   if (proxy_command)
265     return proxy_command->GetOptions();
266   return CommandObject::GetOptions();
267 }
268 
GetHelp()269 llvm::StringRef CommandObjectProxy::GetHelp() {
270   CommandObject *proxy_command = GetProxyCommandObject();
271   if (proxy_command)
272     return proxy_command->GetHelp();
273   return CommandObject::GetHelp();
274 }
275 
GetSyntax()276 llvm::StringRef CommandObjectProxy::GetSyntax() {
277   CommandObject *proxy_command = GetProxyCommandObject();
278   if (proxy_command)
279     return proxy_command->GetSyntax();
280   return CommandObject::GetSyntax();
281 }
282 
GetHelpLong()283 llvm::StringRef CommandObjectProxy::GetHelpLong() {
284   CommandObject *proxy_command = GetProxyCommandObject();
285   if (proxy_command)
286     return proxy_command->GetHelpLong();
287   return CommandObject::GetHelpLong();
288 }
289 
IsRemovable() const290 bool CommandObjectProxy::IsRemovable() const {
291   const CommandObject *proxy_command =
292       const_cast<CommandObjectProxy *>(this)->GetProxyCommandObject();
293   if (proxy_command)
294     return proxy_command->IsRemovable();
295   return false;
296 }
297 
IsMultiwordObject()298 bool CommandObjectProxy::IsMultiwordObject() {
299   CommandObject *proxy_command = GetProxyCommandObject();
300   if (proxy_command)
301     return proxy_command->IsMultiwordObject();
302   return false;
303 }
304 
GetAsMultiwordCommand()305 CommandObjectMultiword *CommandObjectProxy::GetAsMultiwordCommand() {
306   CommandObject *proxy_command = GetProxyCommandObject();
307   if (proxy_command)
308     return proxy_command->GetAsMultiwordCommand();
309   return nullptr;
310 }
311 
GenerateHelpText(Stream & result)312 void CommandObjectProxy::GenerateHelpText(Stream &result) {
313   CommandObject *proxy_command = GetProxyCommandObject();
314   if (proxy_command)
315     proxy_command->GenerateHelpText(result);
316   else
317     CommandObject::GenerateHelpText(result);
318 }
319 
320 lldb::CommandObjectSP
GetSubcommandSP(llvm::StringRef sub_cmd,StringList * matches)321 CommandObjectProxy::GetSubcommandSP(llvm::StringRef sub_cmd,
322                                     StringList *matches) {
323   CommandObject *proxy_command = GetProxyCommandObject();
324   if (proxy_command)
325     return proxy_command->GetSubcommandSP(sub_cmd, matches);
326   return lldb::CommandObjectSP();
327 }
328 
GetSubcommandObject(llvm::StringRef sub_cmd,StringList * matches)329 CommandObject *CommandObjectProxy::GetSubcommandObject(llvm::StringRef sub_cmd,
330                                                        StringList *matches) {
331   CommandObject *proxy_command = GetProxyCommandObject();
332   if (proxy_command)
333     return proxy_command->GetSubcommandObject(sub_cmd, matches);
334   return nullptr;
335 }
336 
AproposAllSubCommands(llvm::StringRef prefix,llvm::StringRef search_word,StringList & commands_found,StringList & commands_help)337 void CommandObjectProxy::AproposAllSubCommands(llvm::StringRef prefix,
338                                                llvm::StringRef search_word,
339                                                StringList &commands_found,
340                                                StringList &commands_help) {
341   CommandObject *proxy_command = GetProxyCommandObject();
342   if (proxy_command)
343     return proxy_command->AproposAllSubCommands(prefix, search_word,
344                                                 commands_found, commands_help);
345 }
346 
LoadSubCommand(llvm::StringRef cmd_name,const lldb::CommandObjectSP & command_sp)347 bool CommandObjectProxy::LoadSubCommand(
348     llvm::StringRef cmd_name, const lldb::CommandObjectSP &command_sp) {
349   CommandObject *proxy_command = GetProxyCommandObject();
350   if (proxy_command)
351     return proxy_command->LoadSubCommand(cmd_name, command_sp);
352   return false;
353 }
354 
WantsRawCommandString()355 bool CommandObjectProxy::WantsRawCommandString() {
356   CommandObject *proxy_command = GetProxyCommandObject();
357   if (proxy_command)
358     return proxy_command->WantsRawCommandString();
359   return false;
360 }
361 
WantsCompletion()362 bool CommandObjectProxy::WantsCompletion() {
363   CommandObject *proxy_command = GetProxyCommandObject();
364   if (proxy_command)
365     return proxy_command->WantsCompletion();
366   return false;
367 }
368 
HandleCompletion(CompletionRequest & request)369 void CommandObjectProxy::HandleCompletion(CompletionRequest &request) {
370   CommandObject *proxy_command = GetProxyCommandObject();
371   if (proxy_command)
372     proxy_command->HandleCompletion(request);
373 }
374 
HandleArgumentCompletion(CompletionRequest & request,OptionElementVector & opt_element_vector)375 void CommandObjectProxy::HandleArgumentCompletion(
376     CompletionRequest &request, OptionElementVector &opt_element_vector) {
377   CommandObject *proxy_command = GetProxyCommandObject();
378   if (proxy_command)
379     proxy_command->HandleArgumentCompletion(request, opt_element_vector);
380 }
381 
GetRepeatCommand(Args & current_command_args,uint32_t index)382 const char *CommandObjectProxy::GetRepeatCommand(Args &current_command_args,
383                                                  uint32_t index) {
384   CommandObject *proxy_command = GetProxyCommandObject();
385   if (proxy_command)
386     return proxy_command->GetRepeatCommand(current_command_args, index);
387   return nullptr;
388 }
389 
GetUnsupportedError()390 llvm::StringRef CommandObjectProxy::GetUnsupportedError() {
391   return "command is not implemented";
392 }
393 
Execute(const char * args_string,CommandReturnObject & result)394 bool CommandObjectProxy::Execute(const char *args_string,
395                                  CommandReturnObject &result) {
396   CommandObject *proxy_command = GetProxyCommandObject();
397   if (proxy_command)
398     return proxy_command->Execute(args_string, result);
399   result.AppendError(GetUnsupportedError());
400   return false;
401 }
402