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
29 CommandObjectSP
GetSubcommandSPExact(llvm::StringRef sub_cmd)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
GetSubcommandSP(llvm::StringRef sub_cmd,StringList * matches)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 *
GetSubcommandObject(llvm::StringRef sub_cmd,StringList * matches)75 CommandObjectMultiword::GetSubcommandObject(llvm::StringRef sub_cmd,
76 StringList *matches) {
77 return GetSubcommandSP(sub_cmd, matches).get();
78 }
79
LoadSubCommand(llvm::StringRef name,const CommandObjectSP & cmd_obj_sp)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
LoadUserSubcommand(llvm::StringRef name,const CommandObjectSP & cmd_obj_sp,bool can_replace)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
RemoveUserSubcommand(llvm::StringRef cmd_name,bool must_be_multiword)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
Execute(const char * args_string,CommandReturnObject & result)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
GenerateHelpText(Stream & output_stream)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
HandleCompletion(CompletionRequest & request)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>
GetRepeatCommand(Args & current_command_args,uint32_t index)294 CommandObjectMultiword::GetRepeatCommand(Args ¤t_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
CommandObjectProxy(CommandInterpreter & interpreter,const char * name,const char * help,const char * syntax,uint32_t flags)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
GetOptions()313 Options *CommandObjectProxy::GetOptions() {
314 CommandObject *proxy_command = GetProxyCommandObject();
315 if (proxy_command)
316 return proxy_command->GetOptions();
317 return CommandObject::GetOptions();
318 }
319
GetHelp()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
GetSyntax()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
GetHelpLong()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
IsRemovable() const341 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
IsMultiwordObject()349 bool CommandObjectProxy::IsMultiwordObject() {
350 CommandObject *proxy_command = GetProxyCommandObject();
351 if (proxy_command)
352 return proxy_command->IsMultiwordObject();
353 return false;
354 }
355
GetAsMultiwordCommand()356 CommandObjectMultiword *CommandObjectProxy::GetAsMultiwordCommand() {
357 CommandObject *proxy_command = GetProxyCommandObject();
358 if (proxy_command)
359 return proxy_command->GetAsMultiwordCommand();
360 return nullptr;
361 }
362
GenerateHelpText(Stream & result)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
GetSubcommandSP(llvm::StringRef sub_cmd,StringList * matches)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
GetSubcommandObject(llvm::StringRef sub_cmd,StringList * matches)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
LoadSubCommand(llvm::StringRef cmd_name,const lldb::CommandObjectSP & command_sp)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
WantsRawCommandString()396 bool CommandObjectProxy::WantsRawCommandString() {
397 CommandObject *proxy_command = GetProxyCommandObject();
398 if (proxy_command)
399 return proxy_command->WantsRawCommandString();
400 return false;
401 }
402
WantsCompletion()403 bool CommandObjectProxy::WantsCompletion() {
404 CommandObject *proxy_command = GetProxyCommandObject();
405 if (proxy_command)
406 return proxy_command->WantsCompletion();
407 return false;
408 }
409
HandleCompletion(CompletionRequest & request)410 void CommandObjectProxy::HandleCompletion(CompletionRequest &request) {
411 CommandObject *proxy_command = GetProxyCommandObject();
412 if (proxy_command)
413 proxy_command->HandleCompletion(request);
414 }
415
HandleArgumentCompletion(CompletionRequest & request,OptionElementVector & opt_element_vector)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>
GetRepeatCommand(Args & current_command_args,uint32_t index)424 CommandObjectProxy::GetRepeatCommand(Args ¤t_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
GetUnsupportedError()432 llvm::StringRef CommandObjectProxy::GetUnsupportedError() {
433 return "command is not implemented";
434 }
435
Execute(const char * args_string,CommandReturnObject & result)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