xref: /freebsd-src/contrib/llvm-project/lldb/source/Commands/CommandObjectMultiword.cpp (revision 5e801ac66d24704442eba426ed13c3effb8a34e7)
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 
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 
29 CommandObjectSP
30 CommandObjectMultiword::GetSubcommandSPExact(llvm::StringRef sub_cmd) {
31   if (m_subcommand_dict.empty())
32     return {};
33 
34   auto pos = m_subcommand_dict.find(std::string(sub_cmd));
35   if (pos == m_subcommand_dict.end())
36     return {};
37 
38   return pos->second;
39 }
40 
41 CommandObjectSP CommandObjectMultiword::GetSubcommandSP(llvm::StringRef sub_cmd,
42                                                         StringList *matches) {
43   if (m_subcommand_dict.empty())
44     return {};
45 
46   CommandObjectSP return_cmd_sp = GetSubcommandSPExact(sub_cmd);
47   if (return_cmd_sp) {
48     if (matches)
49       matches->AppendString(sub_cmd);
50     return return_cmd_sp;
51   }
52 
53   CommandObject::CommandMap::iterator pos;
54 
55   StringList local_matches;
56   if (matches == nullptr)
57     matches = &local_matches;
58   int num_matches =
59       AddNamesMatchingPartialString(m_subcommand_dict, sub_cmd, *matches);
60 
61   if (num_matches == 1) {
62     // Cleaner, but slightly less efficient would be to call back into this
63     // function, since I now know I have an exact match...
64 
65     sub_cmd = matches->GetStringAtIndex(0);
66     pos = m_subcommand_dict.find(std::string(sub_cmd));
67     if (pos != m_subcommand_dict.end())
68       return_cmd_sp = pos->second;
69   }
70 
71   return return_cmd_sp;
72 }
73 
74 CommandObject *
75 CommandObjectMultiword::GetSubcommandObject(llvm::StringRef sub_cmd,
76                                             StringList *matches) {
77   return GetSubcommandSP(sub_cmd, matches).get();
78 }
79 
80 bool CommandObjectMultiword::LoadSubCommand(llvm::StringRef name,
81                                             const CommandObjectSP &cmd_obj_sp) {
82   if (cmd_obj_sp)
83     lldbassert((&GetCommandInterpreter() == &cmd_obj_sp->GetCommandInterpreter()) &&
84            "tried to add a CommandObject from a different interpreter");
85 
86   CommandMap::iterator pos;
87   bool success = true;
88 
89   pos = m_subcommand_dict.find(std::string(name));
90   if (pos == m_subcommand_dict.end()) {
91     m_subcommand_dict[std::string(name)] = cmd_obj_sp;
92   } else
93     success = false;
94 
95   return success;
96 }
97 
98 llvm::Error CommandObjectMultiword::LoadUserSubcommand(
99     llvm::StringRef name, const CommandObjectSP &cmd_obj_sp, bool can_replace) {
100   Status result;
101   if (cmd_obj_sp)
102     lldbassert((&GetCommandInterpreter() == &cmd_obj_sp->GetCommandInterpreter()) &&
103            "tried to add a CommandObject from a different interpreter");
104   if (!IsUserCommand()) {
105     return llvm::createStringError(llvm::inconvertibleErrorCode(),
106                               "can't add a user subcommand to a builtin container command.");
107   }
108   // Make sure this a user command if it isn't already:
109   cmd_obj_sp->SetIsUserCommand(true);
110 
111   std::string str_name(name);
112 
113   auto pos = m_subcommand_dict.find(str_name);
114   if (pos == m_subcommand_dict.end()) {
115     m_subcommand_dict[str_name] = cmd_obj_sp;
116     return llvm::Error::success();
117   }
118 
119   const char *error_str = nullptr;
120   if (!can_replace)
121     error_str = "sub-command already exists";
122   if (!(*pos).second->IsUserCommand())
123     error_str = "can't replace a builtin subcommand";
124 
125   if (error_str) {
126     return llvm::createStringError(llvm::inconvertibleErrorCode(), error_str);
127   }
128   m_subcommand_dict[str_name] = cmd_obj_sp;
129   return llvm::Error::success();
130 }
131 
132 llvm::Error CommandObjectMultiword::RemoveUserSubcommand(llvm::StringRef cmd_name,
133                                                     bool must_be_multiword) {
134   CommandMap::iterator pos;
135   std::string str_name(cmd_name);
136 
137   pos = m_subcommand_dict.find(str_name);
138   if (pos == m_subcommand_dict.end()) {
139     return llvm::createStringError(llvm::inconvertibleErrorCode(),"subcommand '%s' not found.",
140                                    str_name.c_str());
141   }
142   if (!(*pos).second->IsUserCommand()) {
143     return llvm::createStringError(llvm::inconvertibleErrorCode(),"subcommand '%s' not a user command.",
144                                    str_name.c_str());
145   }
146 
147   if (must_be_multiword && !(*pos).second->IsMultiwordObject()) {
148     return llvm::createStringError(llvm::inconvertibleErrorCode(),"subcommand '%s' is not a container command",
149                                    str_name.c_str());
150   }
151   if (!must_be_multiword && (*pos).second->IsMultiwordObject()) {
152     return llvm::createStringError(llvm::inconvertibleErrorCode(),"subcommand '%s' is not a user command",
153                                    str_name.c_str());
154   }
155 
156   m_subcommand_dict.erase(pos);
157 
158   return llvm::Error::success();
159 }
160 
161 bool CommandObjectMultiword::Execute(const char *args_string,
162                                      CommandReturnObject &result) {
163   Args args(args_string);
164   const size_t argc = args.GetArgumentCount();
165   if (argc == 0) {
166     this->CommandObject::GenerateHelpText(result);
167     return result.Succeeded();
168   }
169 
170   auto sub_command = args[0].ref();
171   if (sub_command.empty()) {
172     result.AppendError("Need to specify a non-empty subcommand.");
173     return result.Succeeded();
174   }
175 
176   if (sub_command.equals_insensitive("help")) {
177     this->CommandObject::GenerateHelpText(result);
178     return result.Succeeded();
179   }
180 
181   if (m_subcommand_dict.empty()) {
182     result.AppendErrorWithFormat("'%s' does not have any subcommands.\n",
183                                  GetCommandName().str().c_str());
184     return false;
185   }
186 
187   StringList matches;
188   CommandObject *sub_cmd_obj = GetSubcommandObject(sub_command, &matches);
189   if (sub_cmd_obj != nullptr) {
190     // Now call CommandObject::Execute to process options in `rest_of_line`.
191     // From there the command-specific version of Execute will be called, with
192     // the processed arguments.
193 
194     args.Shift();
195     sub_cmd_obj->Execute(args_string, result);
196     return result.Succeeded();
197   }
198 
199   std::string error_msg;
200   const size_t num_subcmd_matches = matches.GetSize();
201   if (num_subcmd_matches > 0)
202     error_msg.assign("ambiguous command ");
203   else
204     error_msg.assign("invalid command ");
205 
206   error_msg.append("'");
207   error_msg.append(std::string(GetCommandName()));
208   error_msg.append(" ");
209   error_msg.append(std::string(sub_command));
210   error_msg.append("'.");
211 
212   if (num_subcmd_matches > 0) {
213     error_msg.append(" Possible completions:");
214     for (const std::string &match : matches) {
215       error_msg.append("\n\t");
216       error_msg.append(match);
217     }
218   }
219   error_msg.append("\n");
220   result.AppendRawError(error_msg.c_str());
221   return false;
222 }
223 
224 void CommandObjectMultiword::GenerateHelpText(Stream &output_stream) {
225   // First time through here, generate the help text for the object and push it
226   // to the return result object as well
227 
228   CommandObject::GenerateHelpText(output_stream);
229   output_stream.PutCString("\nThe following subcommands are supported:\n\n");
230 
231   CommandMap::iterator pos;
232   uint32_t max_len = FindLongestCommandWord(m_subcommand_dict);
233 
234   if (max_len)
235     max_len += 4; // Indent the output by 4 spaces.
236 
237   for (pos = m_subcommand_dict.begin(); pos != m_subcommand_dict.end(); ++pos) {
238     std::string indented_command("    ");
239     indented_command.append(pos->first);
240     if (pos->second->WantsRawCommandString()) {
241       std::string help_text(std::string(pos->second->GetHelp()));
242       help_text.append("  Expects 'raw' input (see 'help raw-input'.)");
243       m_interpreter.OutputFormattedHelpText(output_stream, indented_command,
244                                             "--", help_text, max_len);
245     } else
246       m_interpreter.OutputFormattedHelpText(output_stream, indented_command,
247                                             "--", pos->second->GetHelp(),
248                                             max_len);
249   }
250 
251   output_stream.PutCString("\nFor more help on any particular subcommand, type "
252                            "'help <command> <subcommand>'.\n");
253 }
254 
255 void CommandObjectMultiword::HandleCompletion(CompletionRequest &request) {
256   auto arg0 = request.GetParsedLine()[0].ref();
257   if (request.GetCursorIndex() == 0) {
258     StringList new_matches, descriptions;
259     AddNamesMatchingPartialString(m_subcommand_dict, arg0, new_matches,
260                                   &descriptions);
261     request.AddCompletions(new_matches, descriptions);
262 
263     if (new_matches.GetSize() == 1 &&
264         new_matches.GetStringAtIndex(0) != nullptr &&
265         (arg0 == new_matches.GetStringAtIndex(0))) {
266       StringList temp_matches;
267       CommandObject *cmd_obj = GetSubcommandObject(arg0, &temp_matches);
268       if (cmd_obj != nullptr) {
269         if (request.GetParsedLine().GetArgumentCount() != 1) {
270           request.GetParsedLine().Shift();
271           request.AppendEmptyArgument();
272           cmd_obj->HandleCompletion(request);
273         }
274       }
275     }
276     return;
277   }
278 
279   StringList new_matches;
280   CommandObject *sub_command_object = GetSubcommandObject(arg0, &new_matches);
281   if (sub_command_object == nullptr) {
282     request.AddCompletions(new_matches);
283     return;
284   }
285 
286   // Remove the one match that we got from calling GetSubcommandObject.
287   new_matches.DeleteStringAtIndex(0);
288   request.AddCompletions(new_matches);
289   request.ShiftArguments();
290   sub_command_object->HandleCompletion(request);
291 }
292 
293 const char *CommandObjectMultiword::GetRepeatCommand(Args &current_command_args,
294                                                      uint32_t index) {
295   index++;
296   if (current_command_args.GetArgumentCount() <= index)
297     return nullptr;
298   CommandObject *sub_command_object =
299       GetSubcommandObject(current_command_args[index].ref());
300   if (sub_command_object == nullptr)
301     return nullptr;
302   return sub_command_object->GetRepeatCommand(current_command_args, index);
303 }
304 
305 void CommandObjectMultiword::AproposAllSubCommands(llvm::StringRef prefix,
306                                                    llvm::StringRef search_word,
307                                                    StringList &commands_found,
308                                                    StringList &commands_help) {
309   CommandObject::CommandMap::const_iterator pos;
310 
311   for (pos = m_subcommand_dict.begin(); pos != m_subcommand_dict.end(); ++pos) {
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 << prefix << " " << command_name;
317 
318     if (sub_cmd_obj->HelpTextContainsWord(search_word)) {
319       commands_found.AppendString(complete_command_name.GetString());
320       commands_help.AppendString(sub_cmd_obj->GetHelp());
321     }
322 
323     if (sub_cmd_obj->IsMultiwordObject())
324       sub_cmd_obj->AproposAllSubCommands(complete_command_name.GetString(),
325                                          search_word, commands_found,
326                                          commands_help);
327   }
328 }
329 
330 CommandObjectProxy::CommandObjectProxy(CommandInterpreter &interpreter,
331                                        const char *name, const char *help,
332                                        const char *syntax, uint32_t flags)
333     : CommandObject(interpreter, name, help, syntax, flags) {}
334 
335 CommandObjectProxy::~CommandObjectProxy() = default;
336 
337 Options *CommandObjectProxy::GetOptions() {
338   CommandObject *proxy_command = GetProxyCommandObject();
339   if (proxy_command)
340     return proxy_command->GetOptions();
341   return CommandObject::GetOptions();
342 }
343 
344 llvm::StringRef CommandObjectProxy::GetHelp() {
345   CommandObject *proxy_command = GetProxyCommandObject();
346   if (proxy_command)
347     return proxy_command->GetHelp();
348   return CommandObject::GetHelp();
349 }
350 
351 llvm::StringRef CommandObjectProxy::GetSyntax() {
352   CommandObject *proxy_command = GetProxyCommandObject();
353   if (proxy_command)
354     return proxy_command->GetSyntax();
355   return CommandObject::GetSyntax();
356 }
357 
358 llvm::StringRef CommandObjectProxy::GetHelpLong() {
359   CommandObject *proxy_command = GetProxyCommandObject();
360   if (proxy_command)
361     return proxy_command->GetHelpLong();
362   return CommandObject::GetHelpLong();
363 }
364 
365 bool CommandObjectProxy::IsRemovable() const {
366   const CommandObject *proxy_command =
367       const_cast<CommandObjectProxy *>(this)->GetProxyCommandObject();
368   if (proxy_command)
369     return proxy_command->IsRemovable();
370   return false;
371 }
372 
373 bool CommandObjectProxy::IsMultiwordObject() {
374   CommandObject *proxy_command = GetProxyCommandObject();
375   if (proxy_command)
376     return proxy_command->IsMultiwordObject();
377   return false;
378 }
379 
380 CommandObjectMultiword *CommandObjectProxy::GetAsMultiwordCommand() {
381   CommandObject *proxy_command = GetProxyCommandObject();
382   if (proxy_command)
383     return proxy_command->GetAsMultiwordCommand();
384   return nullptr;
385 }
386 
387 void CommandObjectProxy::GenerateHelpText(Stream &result) {
388   CommandObject *proxy_command = GetProxyCommandObject();
389   if (proxy_command)
390     proxy_command->GenerateHelpText(result);
391   else
392     CommandObject::GenerateHelpText(result);
393 }
394 
395 lldb::CommandObjectSP
396 CommandObjectProxy::GetSubcommandSP(llvm::StringRef sub_cmd,
397                                     StringList *matches) {
398   CommandObject *proxy_command = GetProxyCommandObject();
399   if (proxy_command)
400     return proxy_command->GetSubcommandSP(sub_cmd, matches);
401   return lldb::CommandObjectSP();
402 }
403 
404 CommandObject *CommandObjectProxy::GetSubcommandObject(llvm::StringRef sub_cmd,
405                                                        StringList *matches) {
406   CommandObject *proxy_command = GetProxyCommandObject();
407   if (proxy_command)
408     return proxy_command->GetSubcommandObject(sub_cmd, matches);
409   return nullptr;
410 }
411 
412 void CommandObjectProxy::AproposAllSubCommands(llvm::StringRef prefix,
413                                                llvm::StringRef search_word,
414                                                StringList &commands_found,
415                                                StringList &commands_help) {
416   CommandObject *proxy_command = GetProxyCommandObject();
417   if (proxy_command)
418     return proxy_command->AproposAllSubCommands(prefix, search_word,
419                                                 commands_found, commands_help);
420 }
421 
422 bool CommandObjectProxy::LoadSubCommand(
423     llvm::StringRef cmd_name, const lldb::CommandObjectSP &command_sp) {
424   CommandObject *proxy_command = GetProxyCommandObject();
425   if (proxy_command)
426     return proxy_command->LoadSubCommand(cmd_name, command_sp);
427   return false;
428 }
429 
430 bool CommandObjectProxy::WantsRawCommandString() {
431   CommandObject *proxy_command = GetProxyCommandObject();
432   if (proxy_command)
433     return proxy_command->WantsRawCommandString();
434   return false;
435 }
436 
437 bool CommandObjectProxy::WantsCompletion() {
438   CommandObject *proxy_command = GetProxyCommandObject();
439   if (proxy_command)
440     return proxy_command->WantsCompletion();
441   return false;
442 }
443 
444 void CommandObjectProxy::HandleCompletion(CompletionRequest &request) {
445   CommandObject *proxy_command = GetProxyCommandObject();
446   if (proxy_command)
447     proxy_command->HandleCompletion(request);
448 }
449 
450 void CommandObjectProxy::HandleArgumentCompletion(
451     CompletionRequest &request, OptionElementVector &opt_element_vector) {
452   CommandObject *proxy_command = GetProxyCommandObject();
453   if (proxy_command)
454     proxy_command->HandleArgumentCompletion(request, opt_element_vector);
455 }
456 
457 const char *CommandObjectProxy::GetRepeatCommand(Args &current_command_args,
458                                                  uint32_t index) {
459   CommandObject *proxy_command = GetProxyCommandObject();
460   if (proxy_command)
461     return proxy_command->GetRepeatCommand(current_command_args, index);
462   return nullptr;
463 }
464 
465 llvm::StringRef CommandObjectProxy::GetUnsupportedError() {
466   return "command is not implemented";
467 }
468 
469 bool CommandObjectProxy::Execute(const char *args_string,
470                                  CommandReturnObject &result) {
471   CommandObject *proxy_command = GetProxyCommandObject();
472   if (proxy_command)
473     return proxy_command->Execute(args_string, result);
474   result.AppendError(GetUnsupportedError());
475   return false;
476 }
477