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
15using namespace lldb;
16using namespace lldb_private;
17
18// CommandObjectMultiword
19
20CommandObjectMultiword::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
28CommandObjectMultiword::~CommandObjectMultiword() = default;
29
30CommandObjectSP
31CommandObjectMultiword::GetSubcommandSPExact(llvm::StringRef sub_cmd) {
32 if (m_subcommand_dict.empty())
33 return {};
34
35 auto pos = m_subcommand_dict.find(x: std::string(sub_cmd));
36 if (pos == m_subcommand_dict.end())
37 return {};
38
39 return pos->second;
40}
41
42CommandObjectSP 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(str: 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(in_map: m_subcommand_dict, cmd_str: sub_cmd, matches&: *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(idx: 0);
67 pos = m_subcommand_dict.find(x: 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
75CommandObject *
76CommandObjectMultiword::GetSubcommandObject(llvm::StringRef sub_cmd,
77 StringList *matches) {
78 return GetSubcommandSP(sub_cmd, matches).get();
79}
80
81bool 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(x: 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
99llvm::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(EC: llvm::inconvertibleErrorCode(),
107 Msg: "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(x: 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(EC: llvm::inconvertibleErrorCode(), Msg: error_str);
128 }
129 m_subcommand_dict[str_name] = cmd_obj_sp;
130 return llvm::Error::success();
131}
132
133llvm::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(x: str_name);
139 if (pos == m_subcommand_dict.end()) {
140 return llvm::createStringError(EC: llvm::inconvertibleErrorCode(),Fmt: "subcommand '%s' not found.",
141 Vals: str_name.c_str());
142 }
143 if (!(*pos).second->IsUserCommand()) {
144 return llvm::createStringError(EC: llvm::inconvertibleErrorCode(),Fmt: "subcommand '%s' not a user command.",
145 Vals: str_name.c_str());
146 }
147
148 if (must_be_multiword && !(*pos).second->IsMultiwordObject()) {
149 return llvm::createStringError(EC: llvm::inconvertibleErrorCode(),Fmt: "subcommand '%s' is not a container command",
150 Vals: str_name.c_str());
151 }
152 if (!must_be_multiword && (*pos).second->IsMultiwordObject()) {
153 return llvm::createStringError(EC: llvm::inconvertibleErrorCode(),Fmt: "subcommand '%s' is not a user command",
154 Vals: str_name.c_str());
155 }
156
157 m_subcommand_dict.erase(position: pos);
158
159 return llvm::Error::success();
160}
161
162void 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;
169 }
170
171 auto sub_command = args[0].ref();
172 if (sub_command.empty()) {
173 result.AppendError(in_string: "Need to specify a non-empty subcommand.");
174 return;
175 }
176
177 if (m_subcommand_dict.empty()) {
178 result.AppendErrorWithFormat(format: "'%s' does not have any subcommands.\n",
179 GetCommandName().str().c_str());
180 return;
181 }
182
183 StringList matches;
184 CommandObject *sub_cmd_obj = GetSubcommandObject(sub_cmd: sub_command, matches: &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;
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(s: "ambiguous command ");
199 else
200 error_msg.assign(s: "invalid command ");
201
202 error_msg.append(s: "'");
203 error_msg.append(str: std::string(GetCommandName()));
204 error_msg.append(s: " ");
205 error_msg.append(str: std::string(sub_command));
206 error_msg.append(s: "'.");
207
208 if (num_subcmd_matches > 0) {
209 error_msg.append(s: " Possible completions:");
210 for (const std::string &match : matches) {
211 error_msg.append(s: "\n\t");
212 error_msg.append(str: match);
213 }
214 }
215 error_msg.append(s: "\n");
216 result.AppendRawError(in_string: error_msg.c_str());
217}
218
219void CommandObjectMultiword::GenerateHelpText(Stream &output_stream) {
220 // First time through here, generate the help text for the object and push it
221 // to the return result object as well
222
223 CommandObject::GenerateHelpText(result&: output_stream);
224 output_stream.PutCString(cstr: "\nThe following subcommands are supported:\n\n");
225
226 CommandMap::iterator pos;
227 uint32_t max_len = FindLongestCommandWord(dict&: m_subcommand_dict);
228
229 if (max_len)
230 max_len += 4; // Indent the output by 4 spaces.
231
232 for (pos = m_subcommand_dict.begin(); pos != m_subcommand_dict.end(); ++pos) {
233 std::string indented_command(" ");
234 indented_command.append(str: pos->first);
235 if (pos->second->WantsRawCommandString()) {
236 std::string help_text(std::string(pos->second->GetHelp()));
237 help_text.append(s: " Expects 'raw' input (see 'help raw-input'.)");
238 m_interpreter.OutputFormattedHelpText(stream&: output_stream, command_word: indented_command,
239 separator: "--", help_text, max_word_len: max_len);
240 } else
241 m_interpreter.OutputFormattedHelpText(stream&: output_stream, command_word: indented_command,
242 separator: "--", help_text: pos->second->GetHelp(),
243 max_word_len: max_len);
244 }
245
246 output_stream.PutCString(cstr: "\nFor more help on any particular subcommand, type "
247 "'help <command> <subcommand>'.\n");
248}
249
250void CommandObjectMultiword::HandleCompletion(CompletionRequest &request) {
251 auto arg0 = request.GetParsedLine()[0].ref();
252 if (request.GetCursorIndex() == 0) {
253 StringList new_matches, descriptions;
254 AddNamesMatchingPartialString(in_map: m_subcommand_dict, cmd_str: arg0, matches&: new_matches,
255 descriptions: &descriptions);
256 request.AddCompletions(completions: new_matches, descriptions);
257
258 if (new_matches.GetSize() == 1 &&
259 new_matches.GetStringAtIndex(idx: 0) != nullptr &&
260 (arg0 == new_matches.GetStringAtIndex(idx: 0))) {
261 StringList temp_matches;
262 CommandObject *cmd_obj = GetSubcommandObject(sub_cmd: arg0, matches: &temp_matches);
263 if (cmd_obj != nullptr) {
264 if (request.GetParsedLine().GetArgumentCount() != 1) {
265 request.GetParsedLine().Shift();
266 request.AppendEmptyArgument();
267 cmd_obj->HandleCompletion(request);
268 }
269 }
270 }
271 return;
272 }
273
274 StringList new_matches;
275 CommandObject *sub_command_object = GetSubcommandObject(sub_cmd: arg0, matches: &new_matches);
276
277 // The subcommand is ambiguous. The completion isn't meaningful.
278 if (!sub_command_object)
279 return;
280
281 // Remove the one match that we got from calling GetSubcommandObject.
282 new_matches.DeleteStringAtIndex(id: 0);
283 request.AddCompletions(completions: new_matches);
284 request.ShiftArguments();
285 sub_command_object->HandleCompletion(request);
286}
287
288std::optional<std::string>
289CommandObjectMultiword::GetRepeatCommand(Args &current_command_args,
290 uint32_t index) {
291 index++;
292 if (current_command_args.GetArgumentCount() <= index)
293 return std::nullopt;
294 CommandObject *sub_command_object =
295 GetSubcommandObject(sub_cmd: current_command_args[index].ref());
296 if (sub_command_object == nullptr)
297 return std::nullopt;
298 return sub_command_object->GetRepeatCommand(current_command_args, index);
299}
300
301CommandObjectProxy::CommandObjectProxy(CommandInterpreter &interpreter,
302 const char *name, const char *help,
303 const char *syntax, uint32_t flags)
304 : CommandObject(interpreter, name, help, syntax, flags) {}
305
306CommandObjectProxy::~CommandObjectProxy() = default;
307
308Options *CommandObjectProxy::GetOptions() {
309 CommandObject *proxy_command = GetProxyCommandObject();
310 if (proxy_command)
311 return proxy_command->GetOptions();
312 return CommandObject::GetOptions();
313}
314
315llvm::StringRef CommandObjectProxy::GetHelp() {
316 CommandObject *proxy_command = GetProxyCommandObject();
317 if (proxy_command)
318 return proxy_command->GetHelp();
319 return CommandObject::GetHelp();
320}
321
322llvm::StringRef CommandObjectProxy::GetSyntax() {
323 CommandObject *proxy_command = GetProxyCommandObject();
324 if (proxy_command)
325 return proxy_command->GetSyntax();
326 return CommandObject::GetSyntax();
327}
328
329llvm::StringRef CommandObjectProxy::GetHelpLong() {
330 CommandObject *proxy_command = GetProxyCommandObject();
331 if (proxy_command)
332 return proxy_command->GetHelpLong();
333 return CommandObject::GetHelpLong();
334}
335
336bool CommandObjectProxy::IsRemovable() const {
337 const CommandObject *proxy_command =
338 const_cast<CommandObjectProxy *>(this)->GetProxyCommandObject();
339 if (proxy_command)
340 return proxy_command->IsRemovable();
341 return false;
342}
343
344bool CommandObjectProxy::IsMultiwordObject() {
345 CommandObject *proxy_command = GetProxyCommandObject();
346 if (proxy_command)
347 return proxy_command->IsMultiwordObject();
348 return false;
349}
350
351CommandObjectMultiword *CommandObjectProxy::GetAsMultiwordCommand() {
352 CommandObject *proxy_command = GetProxyCommandObject();
353 if (proxy_command)
354 return proxy_command->GetAsMultiwordCommand();
355 return nullptr;
356}
357
358void CommandObjectProxy::GenerateHelpText(Stream &result) {
359 CommandObject *proxy_command = GetProxyCommandObject();
360 if (proxy_command)
361 proxy_command->GenerateHelpText(result);
362 else
363 CommandObject::GenerateHelpText(result);
364}
365
366lldb::CommandObjectSP
367CommandObjectProxy::GetSubcommandSP(llvm::StringRef sub_cmd,
368 StringList *matches) {
369 CommandObject *proxy_command = GetProxyCommandObject();
370 if (proxy_command)
371 return proxy_command->GetSubcommandSP(sub_cmd, matches);
372 return lldb::CommandObjectSP();
373}
374
375CommandObject *CommandObjectProxy::GetSubcommandObject(llvm::StringRef sub_cmd,
376 StringList *matches) {
377 CommandObject *proxy_command = GetProxyCommandObject();
378 if (proxy_command)
379 return proxy_command->GetSubcommandObject(sub_cmd, matches);
380 return nullptr;
381}
382
383bool CommandObjectProxy::LoadSubCommand(
384 llvm::StringRef cmd_name, const lldb::CommandObjectSP &command_sp) {
385 CommandObject *proxy_command = GetProxyCommandObject();
386 if (proxy_command)
387 return proxy_command->LoadSubCommand(cmd_name, command_obj: command_sp);
388 return false;
389}
390
391bool CommandObjectProxy::WantsRawCommandString() {
392 CommandObject *proxy_command = GetProxyCommandObject();
393 if (proxy_command)
394 return proxy_command->WantsRawCommandString();
395 return false;
396}
397
398bool CommandObjectProxy::WantsCompletion() {
399 CommandObject *proxy_command = GetProxyCommandObject();
400 if (proxy_command)
401 return proxy_command->WantsCompletion();
402 return false;
403}
404
405void CommandObjectProxy::HandleCompletion(CompletionRequest &request) {
406 CommandObject *proxy_command = GetProxyCommandObject();
407 if (proxy_command)
408 proxy_command->HandleCompletion(request);
409}
410
411void CommandObjectProxy::HandleArgumentCompletion(
412 CompletionRequest &request, OptionElementVector &opt_element_vector) {
413 CommandObject *proxy_command = GetProxyCommandObject();
414 if (proxy_command)
415 proxy_command->HandleArgumentCompletion(request, opt_element_vector);
416}
417
418std::optional<std::string>
419CommandObjectProxy::GetRepeatCommand(Args &current_command_args,
420 uint32_t index) {
421 CommandObject *proxy_command = GetProxyCommandObject();
422 if (proxy_command)
423 return proxy_command->GetRepeatCommand(current_command_args, index);
424 return std::nullopt;
425}
426
427llvm::StringRef CommandObjectProxy::GetUnsupportedError() {
428 return "command is not implemented";
429}
430
431void CommandObjectProxy::Execute(const char *args_string,
432 CommandReturnObject &result) {
433 if (CommandObject *proxy_command = GetProxyCommandObject())
434 proxy_command->Execute(args_string, result);
435 else
436 result.AppendError(in_string: GetUnsupportedError());
437}
438

source code of lldb/source/Commands/CommandObjectMultiword.cpp