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 ¤t_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 ¤t_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