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/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 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(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(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 *
63 CommandObjectMultiword::GetSubcommandObject(llvm::StringRef sub_cmd,
64                                             StringList *matches) {
65   return GetSubcommandSP(sub_cmd, matches).get();
66 }
67 
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(name);
78   if (pos == m_subcommand_dict.end()) {
79     m_subcommand_dict[name] = cmd_obj;
80   } else
81     success = false;
82 
83   return success;
84 }
85 
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_lower("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     result.SetStatus(eReturnStatusFailed);
110     return false;
111   }
112 
113   StringList matches;
114   CommandObject *sub_cmd_obj = GetSubcommandObject(sub_command, &matches);
115   if (sub_cmd_obj != nullptr) {
116     // Now call CommandObject::Execute to process options in `rest_of_line`.
117     // From there the command-specific version of Execute will be called, with
118     // the processed arguments.
119 
120     args.Shift();
121     sub_cmd_obj->Execute(args_string, result);
122     return result.Succeeded();
123   }
124 
125   std::string error_msg;
126   const size_t num_subcmd_matches = matches.GetSize();
127   if (num_subcmd_matches > 0)
128     error_msg.assign("ambiguous command ");
129   else
130     error_msg.assign("invalid command ");
131 
132   error_msg.append("'");
133   error_msg.append(GetCommandName());
134   error_msg.append(" ");
135   error_msg.append(sub_command);
136   error_msg.append("'.");
137 
138   if (num_subcmd_matches > 0) {
139     error_msg.append(" Possible completions:");
140     for (const std::string &match : matches) {
141       error_msg.append("\n\t");
142       error_msg.append(match);
143     }
144   }
145   error_msg.append("\n");
146   result.AppendRawError(error_msg.c_str());
147   result.SetStatus(eReturnStatusFailed);
148   return false;
149 }
150 
151 void CommandObjectMultiword::GenerateHelpText(Stream &output_stream) {
152   // First time through here, generate the help text for the object and push it
153   // to the return result object as well
154 
155   CommandObject::GenerateHelpText(output_stream);
156   output_stream.PutCString("\nThe following subcommands are supported:\n\n");
157 
158   CommandMap::iterator pos;
159   uint32_t max_len = FindLongestCommandWord(m_subcommand_dict);
160 
161   if (max_len)
162     max_len += 4; // Indent the output by 4 spaces.
163 
164   for (pos = m_subcommand_dict.begin(); pos != m_subcommand_dict.end(); ++pos) {
165     std::string indented_command("    ");
166     indented_command.append(pos->first);
167     if (pos->second->WantsRawCommandString()) {
168       std::string help_text(pos->second->GetHelp());
169       help_text.append("  Expects 'raw' input (see 'help raw-input'.)");
170       m_interpreter.OutputFormattedHelpText(output_stream,
171                                             indented_command.c_str(), "--",
172                                             help_text.c_str(), max_len);
173     } else
174       m_interpreter.OutputFormattedHelpText(output_stream,
175                                             indented_command.c_str(), "--",
176                                             pos->second->GetHelp(), max_len);
177   }
178 
179   output_stream.PutCString("\nFor more help on any particular subcommand, type "
180                            "'help <command> <subcommand>'.\n");
181 }
182 
183 void CommandObjectMultiword::HandleCompletion(CompletionRequest &request) {
184   auto arg0 = request.GetParsedLine()[0].ref();
185   if (request.GetCursorIndex() == 0) {
186     StringList new_matches, descriptions;
187     AddNamesMatchingPartialString(m_subcommand_dict, arg0, new_matches,
188                                   &descriptions);
189     request.AddCompletions(new_matches, descriptions);
190 
191     if (new_matches.GetSize() == 1 &&
192         new_matches.GetStringAtIndex(0) != nullptr &&
193         (arg0 == new_matches.GetStringAtIndex(0))) {
194       StringList temp_matches;
195       CommandObject *cmd_obj = GetSubcommandObject(arg0, &temp_matches);
196       if (cmd_obj != nullptr) {
197         if (request.GetParsedLine().GetArgumentCount() != 1) {
198           request.GetParsedLine().Shift();
199           request.AppendEmptyArgument();
200           cmd_obj->HandleCompletion(request);
201         }
202       }
203     }
204     return;
205   }
206 
207   StringList new_matches;
208   CommandObject *sub_command_object = GetSubcommandObject(arg0, &new_matches);
209   if (sub_command_object == nullptr) {
210     request.AddCompletions(new_matches);
211     return;
212   }
213 
214   // Remove the one match that we got from calling GetSubcommandObject.
215   new_matches.DeleteStringAtIndex(0);
216   request.AddCompletions(new_matches);
217   request.ShiftArguments();
218   sub_command_object->HandleCompletion(request);
219 }
220 
221 const char *CommandObjectMultiword::GetRepeatCommand(Args &current_command_args,
222                                                      uint32_t index) {
223   index++;
224   if (current_command_args.GetArgumentCount() <= index)
225     return nullptr;
226   CommandObject *sub_command_object =
227       GetSubcommandObject(current_command_args[index].ref());
228   if (sub_command_object == nullptr)
229     return nullptr;
230   return sub_command_object->GetRepeatCommand(current_command_args, index);
231 }
232 
233 void CommandObjectMultiword::AproposAllSubCommands(llvm::StringRef prefix,
234                                                    llvm::StringRef search_word,
235                                                    StringList &commands_found,
236                                                    StringList &commands_help) {
237   CommandObject::CommandMap::const_iterator pos;
238 
239   for (pos = m_subcommand_dict.begin(); pos != m_subcommand_dict.end(); ++pos) {
240     const char *command_name = pos->first.c_str();
241     CommandObject *sub_cmd_obj = pos->second.get();
242     StreamString complete_command_name;
243 
244     complete_command_name << prefix << " " << command_name;
245 
246     if (sub_cmd_obj->HelpTextContainsWord(search_word)) {
247       commands_found.AppendString(complete_command_name.GetString());
248       commands_help.AppendString(sub_cmd_obj->GetHelp());
249     }
250 
251     if (sub_cmd_obj->IsMultiwordObject())
252       sub_cmd_obj->AproposAllSubCommands(complete_command_name.GetString(),
253                                          search_word, commands_found,
254                                          commands_help);
255   }
256 }
257 
258 CommandObjectProxy::CommandObjectProxy(CommandInterpreter &interpreter,
259                                        const char *name, const char *help,
260                                        const char *syntax, uint32_t flags)
261     : CommandObject(interpreter, name, help, syntax, flags) {}
262 
263 CommandObjectProxy::~CommandObjectProxy() = default;
264 
265 llvm::StringRef CommandObjectProxy::GetHelpLong() {
266   CommandObject *proxy_command = GetProxyCommandObject();
267   if (proxy_command)
268     return proxy_command->GetHelpLong();
269   return llvm::StringRef();
270 }
271 
272 bool CommandObjectProxy::IsRemovable() const {
273   const CommandObject *proxy_command =
274       const_cast<CommandObjectProxy *>(this)->GetProxyCommandObject();
275   if (proxy_command)
276     return proxy_command->IsRemovable();
277   return false;
278 }
279 
280 bool CommandObjectProxy::IsMultiwordObject() {
281   CommandObject *proxy_command = GetProxyCommandObject();
282   if (proxy_command)
283     return proxy_command->IsMultiwordObject();
284   return false;
285 }
286 
287 CommandObjectMultiword *CommandObjectProxy::GetAsMultiwordCommand() {
288   CommandObject *proxy_command = GetProxyCommandObject();
289   if (proxy_command)
290     return proxy_command->GetAsMultiwordCommand();
291   return nullptr;
292 }
293 
294 void CommandObjectProxy::GenerateHelpText(Stream &result) {
295   CommandObject *proxy_command = GetProxyCommandObject();
296   if (proxy_command)
297     return proxy_command->GenerateHelpText(result);
298 }
299 
300 lldb::CommandObjectSP
301 CommandObjectProxy::GetSubcommandSP(llvm::StringRef sub_cmd,
302                                     StringList *matches) {
303   CommandObject *proxy_command = GetProxyCommandObject();
304   if (proxy_command)
305     return proxy_command->GetSubcommandSP(sub_cmd, matches);
306   return lldb::CommandObjectSP();
307 }
308 
309 CommandObject *CommandObjectProxy::GetSubcommandObject(llvm::StringRef sub_cmd,
310                                                        StringList *matches) {
311   CommandObject *proxy_command = GetProxyCommandObject();
312   if (proxy_command)
313     return proxy_command->GetSubcommandObject(sub_cmd, matches);
314   return nullptr;
315 }
316 
317 void CommandObjectProxy::AproposAllSubCommands(llvm::StringRef prefix,
318                                                llvm::StringRef search_word,
319                                                StringList &commands_found,
320                                                StringList &commands_help) {
321   CommandObject *proxy_command = GetProxyCommandObject();
322   if (proxy_command)
323     return proxy_command->AproposAllSubCommands(prefix, search_word,
324                                                 commands_found, commands_help);
325 }
326 
327 bool CommandObjectProxy::LoadSubCommand(
328     llvm::StringRef cmd_name, const lldb::CommandObjectSP &command_sp) {
329   CommandObject *proxy_command = GetProxyCommandObject();
330   if (proxy_command)
331     return proxy_command->LoadSubCommand(cmd_name, command_sp);
332   return false;
333 }
334 
335 bool CommandObjectProxy::WantsRawCommandString() {
336   CommandObject *proxy_command = GetProxyCommandObject();
337   if (proxy_command)
338     return proxy_command->WantsRawCommandString();
339   return false;
340 }
341 
342 bool CommandObjectProxy::WantsCompletion() {
343   CommandObject *proxy_command = GetProxyCommandObject();
344   if (proxy_command)
345     return proxy_command->WantsCompletion();
346   return false;
347 }
348 
349 Options *CommandObjectProxy::GetOptions() {
350   CommandObject *proxy_command = GetProxyCommandObject();
351   if (proxy_command)
352     return proxy_command->GetOptions();
353   return nullptr;
354 }
355 
356 void CommandObjectProxy::HandleCompletion(CompletionRequest &request) {
357   CommandObject *proxy_command = GetProxyCommandObject();
358   if (proxy_command)
359     proxy_command->HandleCompletion(request);
360 }
361 
362 void CommandObjectProxy::HandleArgumentCompletion(
363     CompletionRequest &request, OptionElementVector &opt_element_vector) {
364   CommandObject *proxy_command = GetProxyCommandObject();
365   if (proxy_command)
366     proxy_command->HandleArgumentCompletion(request, opt_element_vector);
367 }
368 
369 const char *CommandObjectProxy::GetRepeatCommand(Args &current_command_args,
370                                                  uint32_t index) {
371   CommandObject *proxy_command = GetProxyCommandObject();
372   if (proxy_command)
373     return proxy_command->GetRepeatCommand(current_command_args, index);
374   return nullptr;
375 }
376 
377 bool CommandObjectProxy::Execute(const char *args_string,
378                                  CommandReturnObject &result) {
379   CommandObject *proxy_command = GetProxyCommandObject();
380   if (proxy_command)
381     return proxy_command->Execute(args_string, result);
382   result.AppendError("command is not implemented");
383   result.SetStatus(eReturnStatusFailed);
384   return false;
385 }
386