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