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 llvm::Optional<std::string>
294 CommandObjectMultiword::GetRepeatCommand(Args &current_command_args,
295                                          uint32_t index) {
296   index++;
297   if (current_command_args.GetArgumentCount() <= index)
298     return llvm::None;
299   CommandObject *sub_command_object =
300       GetSubcommandObject(current_command_args[index].ref());
301   if (sub_command_object == nullptr)
302     return llvm::None;
303   return sub_command_object->GetRepeatCommand(current_command_args, index);
304 }
305 
306 CommandObjectProxy::CommandObjectProxy(CommandInterpreter &interpreter,
307                                        const char *name, const char *help,
308                                        const char *syntax, uint32_t flags)
309     : CommandObject(interpreter, name, help, syntax, flags) {}
310 
311 CommandObjectProxy::~CommandObjectProxy() = default;
312 
313 Options *CommandObjectProxy::GetOptions() {
314   CommandObject *proxy_command = GetProxyCommandObject();
315   if (proxy_command)
316     return proxy_command->GetOptions();
317   return CommandObject::GetOptions();
318 }
319 
320 llvm::StringRef CommandObjectProxy::GetHelp() {
321   CommandObject *proxy_command = GetProxyCommandObject();
322   if (proxy_command)
323     return proxy_command->GetHelp();
324   return CommandObject::GetHelp();
325 }
326 
327 llvm::StringRef CommandObjectProxy::GetSyntax() {
328   CommandObject *proxy_command = GetProxyCommandObject();
329   if (proxy_command)
330     return proxy_command->GetSyntax();
331   return CommandObject::GetSyntax();
332 }
333 
334 llvm::StringRef CommandObjectProxy::GetHelpLong() {
335   CommandObject *proxy_command = GetProxyCommandObject();
336   if (proxy_command)
337     return proxy_command->GetHelpLong();
338   return CommandObject::GetHelpLong();
339 }
340 
341 bool CommandObjectProxy::IsRemovable() const {
342   const CommandObject *proxy_command =
343       const_cast<CommandObjectProxy *>(this)->GetProxyCommandObject();
344   if (proxy_command)
345     return proxy_command->IsRemovable();
346   return false;
347 }
348 
349 bool CommandObjectProxy::IsMultiwordObject() {
350   CommandObject *proxy_command = GetProxyCommandObject();
351   if (proxy_command)
352     return proxy_command->IsMultiwordObject();
353   return false;
354 }
355 
356 CommandObjectMultiword *CommandObjectProxy::GetAsMultiwordCommand() {
357   CommandObject *proxy_command = GetProxyCommandObject();
358   if (proxy_command)
359     return proxy_command->GetAsMultiwordCommand();
360   return nullptr;
361 }
362 
363 void CommandObjectProxy::GenerateHelpText(Stream &result) {
364   CommandObject *proxy_command = GetProxyCommandObject();
365   if (proxy_command)
366     proxy_command->GenerateHelpText(result);
367   else
368     CommandObject::GenerateHelpText(result);
369 }
370 
371 lldb::CommandObjectSP
372 CommandObjectProxy::GetSubcommandSP(llvm::StringRef sub_cmd,
373                                     StringList *matches) {
374   CommandObject *proxy_command = GetProxyCommandObject();
375   if (proxy_command)
376     return proxy_command->GetSubcommandSP(sub_cmd, matches);
377   return lldb::CommandObjectSP();
378 }
379 
380 CommandObject *CommandObjectProxy::GetSubcommandObject(llvm::StringRef sub_cmd,
381                                                        StringList *matches) {
382   CommandObject *proxy_command = GetProxyCommandObject();
383   if (proxy_command)
384     return proxy_command->GetSubcommandObject(sub_cmd, matches);
385   return nullptr;
386 }
387 
388 bool CommandObjectProxy::LoadSubCommand(
389     llvm::StringRef cmd_name, const lldb::CommandObjectSP &command_sp) {
390   CommandObject *proxy_command = GetProxyCommandObject();
391   if (proxy_command)
392     return proxy_command->LoadSubCommand(cmd_name, command_sp);
393   return false;
394 }
395 
396 bool CommandObjectProxy::WantsRawCommandString() {
397   CommandObject *proxy_command = GetProxyCommandObject();
398   if (proxy_command)
399     return proxy_command->WantsRawCommandString();
400   return false;
401 }
402 
403 bool CommandObjectProxy::WantsCompletion() {
404   CommandObject *proxy_command = GetProxyCommandObject();
405   if (proxy_command)
406     return proxy_command->WantsCompletion();
407   return false;
408 }
409 
410 void CommandObjectProxy::HandleCompletion(CompletionRequest &request) {
411   CommandObject *proxy_command = GetProxyCommandObject();
412   if (proxy_command)
413     proxy_command->HandleCompletion(request);
414 }
415 
416 void CommandObjectProxy::HandleArgumentCompletion(
417     CompletionRequest &request, OptionElementVector &opt_element_vector) {
418   CommandObject *proxy_command = GetProxyCommandObject();
419   if (proxy_command)
420     proxy_command->HandleArgumentCompletion(request, opt_element_vector);
421 }
422 
423 llvm::Optional<std::string>
424 CommandObjectProxy::GetRepeatCommand(Args &current_command_args,
425                                      uint32_t index) {
426   CommandObject *proxy_command = GetProxyCommandObject();
427   if (proxy_command)
428     return proxy_command->GetRepeatCommand(current_command_args, index);
429   return llvm::None;
430 }
431 
432 llvm::StringRef CommandObjectProxy::GetUnsupportedError() {
433   return "command is not implemented";
434 }
435 
436 bool CommandObjectProxy::Execute(const char *args_string,
437                                  CommandReturnObject &result) {
438   CommandObject *proxy_command = GetProxyCommandObject();
439   if (proxy_command)
440     return proxy_command->Execute(args_string, result);
441   result.AppendError(GetUnsupportedError());
442   return false;
443 }
444