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