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