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