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