forked from OSchip/llvm-project
444 lines
15 KiB
C++
444 lines
15 KiB
C++
//===-- CommandObjectMultiword.cpp ----------------------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "lldb/Interpreter/CommandObjectMultiword.h"
|
|
#include "lldb/Interpreter/CommandInterpreter.h"
|
|
#include "lldb/Interpreter/CommandReturnObject.h"
|
|
#include "lldb/Interpreter/Options.h"
|
|
|
|
using namespace lldb;
|
|
using namespace lldb_private;
|
|
|
|
// CommandObjectMultiword
|
|
|
|
CommandObjectMultiword::CommandObjectMultiword(CommandInterpreter &interpreter,
|
|
const char *name,
|
|
const char *help,
|
|
const char *syntax,
|
|
uint32_t flags)
|
|
: CommandObject(interpreter, name, help, syntax, flags),
|
|
m_can_be_removed(false) {}
|
|
|
|
CommandObjectMultiword::~CommandObjectMultiword() = default;
|
|
|
|
CommandObjectSP
|
|
CommandObjectMultiword::GetSubcommandSPExact(llvm::StringRef sub_cmd) {
|
|
if (m_subcommand_dict.empty())
|
|
return {};
|
|
|
|
auto pos = m_subcommand_dict.find(std::string(sub_cmd));
|
|
if (pos == m_subcommand_dict.end())
|
|
return {};
|
|
|
|
return pos->second;
|
|
}
|
|
|
|
CommandObjectSP CommandObjectMultiword::GetSubcommandSP(llvm::StringRef sub_cmd,
|
|
StringList *matches) {
|
|
if (m_subcommand_dict.empty())
|
|
return {};
|
|
|
|
CommandObjectSP return_cmd_sp = GetSubcommandSPExact(sub_cmd);
|
|
if (return_cmd_sp) {
|
|
if (matches)
|
|
matches->AppendString(sub_cmd);
|
|
return return_cmd_sp;
|
|
}
|
|
|
|
CommandObject::CommandMap::iterator pos;
|
|
|
|
StringList local_matches;
|
|
if (matches == nullptr)
|
|
matches = &local_matches;
|
|
int num_matches =
|
|
AddNamesMatchingPartialString(m_subcommand_dict, sub_cmd, *matches);
|
|
|
|
if (num_matches == 1) {
|
|
// Cleaner, but slightly less efficient would be to call back into this
|
|
// function, since I now know I have an exact match...
|
|
|
|
sub_cmd = matches->GetStringAtIndex(0);
|
|
pos = m_subcommand_dict.find(std::string(sub_cmd));
|
|
if (pos != m_subcommand_dict.end())
|
|
return_cmd_sp = pos->second;
|
|
}
|
|
|
|
return return_cmd_sp;
|
|
}
|
|
|
|
CommandObject *
|
|
CommandObjectMultiword::GetSubcommandObject(llvm::StringRef sub_cmd,
|
|
StringList *matches) {
|
|
return GetSubcommandSP(sub_cmd, matches).get();
|
|
}
|
|
|
|
bool CommandObjectMultiword::LoadSubCommand(llvm::StringRef name,
|
|
const CommandObjectSP &cmd_obj_sp) {
|
|
if (cmd_obj_sp)
|
|
lldbassert((&GetCommandInterpreter() == &cmd_obj_sp->GetCommandInterpreter()) &&
|
|
"tried to add a CommandObject from a different interpreter");
|
|
|
|
CommandMap::iterator pos;
|
|
bool success = true;
|
|
|
|
pos = m_subcommand_dict.find(std::string(name));
|
|
if (pos == m_subcommand_dict.end()) {
|
|
m_subcommand_dict[std::string(name)] = cmd_obj_sp;
|
|
} else
|
|
success = false;
|
|
|
|
return success;
|
|
}
|
|
|
|
llvm::Error CommandObjectMultiword::LoadUserSubcommand(
|
|
llvm::StringRef name, const CommandObjectSP &cmd_obj_sp, bool can_replace) {
|
|
Status result;
|
|
if (cmd_obj_sp)
|
|
lldbassert((&GetCommandInterpreter() == &cmd_obj_sp->GetCommandInterpreter()) &&
|
|
"tried to add a CommandObject from a different interpreter");
|
|
if (!IsUserCommand()) {
|
|
return llvm::createStringError(llvm::inconvertibleErrorCode(),
|
|
"can't add a user subcommand to a builtin container command.");
|
|
}
|
|
// Make sure this a user command if it isn't already:
|
|
cmd_obj_sp->SetIsUserCommand(true);
|
|
|
|
std::string str_name(name);
|
|
|
|
auto pos = m_subcommand_dict.find(str_name);
|
|
if (pos == m_subcommand_dict.end()) {
|
|
m_subcommand_dict[str_name] = cmd_obj_sp;
|
|
return llvm::Error::success();
|
|
}
|
|
|
|
const char *error_str = nullptr;
|
|
if (!can_replace)
|
|
error_str = "sub-command already exists";
|
|
if (!(*pos).second->IsUserCommand())
|
|
error_str = "can't replace a builtin subcommand";
|
|
|
|
if (error_str) {
|
|
return llvm::createStringError(llvm::inconvertibleErrorCode(), error_str);
|
|
}
|
|
m_subcommand_dict[str_name] = cmd_obj_sp;
|
|
return llvm::Error::success();
|
|
}
|
|
|
|
llvm::Error CommandObjectMultiword::RemoveUserSubcommand(llvm::StringRef cmd_name,
|
|
bool must_be_multiword) {
|
|
CommandMap::iterator pos;
|
|
std::string str_name(cmd_name);
|
|
|
|
pos = m_subcommand_dict.find(str_name);
|
|
if (pos == m_subcommand_dict.end()) {
|
|
return llvm::createStringError(llvm::inconvertibleErrorCode(),"subcommand '%s' not found.",
|
|
str_name.c_str());
|
|
}
|
|
if (!(*pos).second->IsUserCommand()) {
|
|
return llvm::createStringError(llvm::inconvertibleErrorCode(),"subcommand '%s' not a user command.",
|
|
str_name.c_str());
|
|
}
|
|
|
|
if (must_be_multiword && !(*pos).second->IsMultiwordObject()) {
|
|
return llvm::createStringError(llvm::inconvertibleErrorCode(),"subcommand '%s' is not a container command",
|
|
str_name.c_str());
|
|
}
|
|
if (!must_be_multiword && (*pos).second->IsMultiwordObject()) {
|
|
return llvm::createStringError(llvm::inconvertibleErrorCode(),"subcommand '%s' is not a user command",
|
|
str_name.c_str());
|
|
}
|
|
|
|
m_subcommand_dict.erase(pos);
|
|
|
|
return llvm::Error::success();
|
|
}
|
|
|
|
bool CommandObjectMultiword::Execute(const char *args_string,
|
|
CommandReturnObject &result) {
|
|
Args args(args_string);
|
|
const size_t argc = args.GetArgumentCount();
|
|
if (argc == 0) {
|
|
this->CommandObject::GenerateHelpText(result);
|
|
return result.Succeeded();
|
|
}
|
|
|
|
auto sub_command = args[0].ref();
|
|
if (sub_command.empty()) {
|
|
result.AppendError("Need to specify a non-empty subcommand.");
|
|
return result.Succeeded();
|
|
}
|
|
|
|
if (sub_command.equals_insensitive("help")) {
|
|
this->CommandObject::GenerateHelpText(result);
|
|
return result.Succeeded();
|
|
}
|
|
|
|
if (m_subcommand_dict.empty()) {
|
|
result.AppendErrorWithFormat("'%s' does not have any subcommands.\n",
|
|
GetCommandName().str().c_str());
|
|
return false;
|
|
}
|
|
|
|
StringList matches;
|
|
CommandObject *sub_cmd_obj = GetSubcommandObject(sub_command, &matches);
|
|
if (sub_cmd_obj != nullptr) {
|
|
// Now call CommandObject::Execute to process options in `rest_of_line`.
|
|
// From there the command-specific version of Execute will be called, with
|
|
// the processed arguments.
|
|
|
|
args.Shift();
|
|
sub_cmd_obj->Execute(args_string, result);
|
|
return result.Succeeded();
|
|
}
|
|
|
|
std::string error_msg;
|
|
const size_t num_subcmd_matches = matches.GetSize();
|
|
if (num_subcmd_matches > 0)
|
|
error_msg.assign("ambiguous command ");
|
|
else
|
|
error_msg.assign("invalid command ");
|
|
|
|
error_msg.append("'");
|
|
error_msg.append(std::string(GetCommandName()));
|
|
error_msg.append(" ");
|
|
error_msg.append(std::string(sub_command));
|
|
error_msg.append("'.");
|
|
|
|
if (num_subcmd_matches > 0) {
|
|
error_msg.append(" Possible completions:");
|
|
for (const std::string &match : matches) {
|
|
error_msg.append("\n\t");
|
|
error_msg.append(match);
|
|
}
|
|
}
|
|
error_msg.append("\n");
|
|
result.AppendRawError(error_msg.c_str());
|
|
return false;
|
|
}
|
|
|
|
void CommandObjectMultiword::GenerateHelpText(Stream &output_stream) {
|
|
// First time through here, generate the help text for the object and push it
|
|
// to the return result object as well
|
|
|
|
CommandObject::GenerateHelpText(output_stream);
|
|
output_stream.PutCString("\nThe following subcommands are supported:\n\n");
|
|
|
|
CommandMap::iterator pos;
|
|
uint32_t max_len = FindLongestCommandWord(m_subcommand_dict);
|
|
|
|
if (max_len)
|
|
max_len += 4; // Indent the output by 4 spaces.
|
|
|
|
for (pos = m_subcommand_dict.begin(); pos != m_subcommand_dict.end(); ++pos) {
|
|
std::string indented_command(" ");
|
|
indented_command.append(pos->first);
|
|
if (pos->second->WantsRawCommandString()) {
|
|
std::string help_text(std::string(pos->second->GetHelp()));
|
|
help_text.append(" Expects 'raw' input (see 'help raw-input'.)");
|
|
m_interpreter.OutputFormattedHelpText(output_stream, indented_command,
|
|
"--", help_text, max_len);
|
|
} else
|
|
m_interpreter.OutputFormattedHelpText(output_stream, indented_command,
|
|
"--", pos->second->GetHelp(),
|
|
max_len);
|
|
}
|
|
|
|
output_stream.PutCString("\nFor more help on any particular subcommand, type "
|
|
"'help <command> <subcommand>'.\n");
|
|
}
|
|
|
|
void CommandObjectMultiword::HandleCompletion(CompletionRequest &request) {
|
|
auto arg0 = request.GetParsedLine()[0].ref();
|
|
if (request.GetCursorIndex() == 0) {
|
|
StringList new_matches, descriptions;
|
|
AddNamesMatchingPartialString(m_subcommand_dict, arg0, new_matches,
|
|
&descriptions);
|
|
request.AddCompletions(new_matches, descriptions);
|
|
|
|
if (new_matches.GetSize() == 1 &&
|
|
new_matches.GetStringAtIndex(0) != nullptr &&
|
|
(arg0 == new_matches.GetStringAtIndex(0))) {
|
|
StringList temp_matches;
|
|
CommandObject *cmd_obj = GetSubcommandObject(arg0, &temp_matches);
|
|
if (cmd_obj != nullptr) {
|
|
if (request.GetParsedLine().GetArgumentCount() != 1) {
|
|
request.GetParsedLine().Shift();
|
|
request.AppendEmptyArgument();
|
|
cmd_obj->HandleCompletion(request);
|
|
}
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
StringList new_matches;
|
|
CommandObject *sub_command_object = GetSubcommandObject(arg0, &new_matches);
|
|
if (sub_command_object == nullptr) {
|
|
request.AddCompletions(new_matches);
|
|
return;
|
|
}
|
|
|
|
// Remove the one match that we got from calling GetSubcommandObject.
|
|
new_matches.DeleteStringAtIndex(0);
|
|
request.AddCompletions(new_matches);
|
|
request.ShiftArguments();
|
|
sub_command_object->HandleCompletion(request);
|
|
}
|
|
|
|
llvm::Optional<std::string>
|
|
CommandObjectMultiword::GetRepeatCommand(Args ¤t_command_args,
|
|
uint32_t index) {
|
|
index++;
|
|
if (current_command_args.GetArgumentCount() <= index)
|
|
return llvm::None;
|
|
CommandObject *sub_command_object =
|
|
GetSubcommandObject(current_command_args[index].ref());
|
|
if (sub_command_object == nullptr)
|
|
return llvm::None;
|
|
return sub_command_object->GetRepeatCommand(current_command_args, index);
|
|
}
|
|
|
|
CommandObjectProxy::CommandObjectProxy(CommandInterpreter &interpreter,
|
|
const char *name, const char *help,
|
|
const char *syntax, uint32_t flags)
|
|
: CommandObject(interpreter, name, help, syntax, flags) {}
|
|
|
|
CommandObjectProxy::~CommandObjectProxy() = default;
|
|
|
|
Options *CommandObjectProxy::GetOptions() {
|
|
CommandObject *proxy_command = GetProxyCommandObject();
|
|
if (proxy_command)
|
|
return proxy_command->GetOptions();
|
|
return CommandObject::GetOptions();
|
|
}
|
|
|
|
llvm::StringRef CommandObjectProxy::GetHelp() {
|
|
CommandObject *proxy_command = GetProxyCommandObject();
|
|
if (proxy_command)
|
|
return proxy_command->GetHelp();
|
|
return CommandObject::GetHelp();
|
|
}
|
|
|
|
llvm::StringRef CommandObjectProxy::GetSyntax() {
|
|
CommandObject *proxy_command = GetProxyCommandObject();
|
|
if (proxy_command)
|
|
return proxy_command->GetSyntax();
|
|
return CommandObject::GetSyntax();
|
|
}
|
|
|
|
llvm::StringRef CommandObjectProxy::GetHelpLong() {
|
|
CommandObject *proxy_command = GetProxyCommandObject();
|
|
if (proxy_command)
|
|
return proxy_command->GetHelpLong();
|
|
return CommandObject::GetHelpLong();
|
|
}
|
|
|
|
bool CommandObjectProxy::IsRemovable() const {
|
|
const CommandObject *proxy_command =
|
|
const_cast<CommandObjectProxy *>(this)->GetProxyCommandObject();
|
|
if (proxy_command)
|
|
return proxy_command->IsRemovable();
|
|
return false;
|
|
}
|
|
|
|
bool CommandObjectProxy::IsMultiwordObject() {
|
|
CommandObject *proxy_command = GetProxyCommandObject();
|
|
if (proxy_command)
|
|
return proxy_command->IsMultiwordObject();
|
|
return false;
|
|
}
|
|
|
|
CommandObjectMultiword *CommandObjectProxy::GetAsMultiwordCommand() {
|
|
CommandObject *proxy_command = GetProxyCommandObject();
|
|
if (proxy_command)
|
|
return proxy_command->GetAsMultiwordCommand();
|
|
return nullptr;
|
|
}
|
|
|
|
void CommandObjectProxy::GenerateHelpText(Stream &result) {
|
|
CommandObject *proxy_command = GetProxyCommandObject();
|
|
if (proxy_command)
|
|
proxy_command->GenerateHelpText(result);
|
|
else
|
|
CommandObject::GenerateHelpText(result);
|
|
}
|
|
|
|
lldb::CommandObjectSP
|
|
CommandObjectProxy::GetSubcommandSP(llvm::StringRef sub_cmd,
|
|
StringList *matches) {
|
|
CommandObject *proxy_command = GetProxyCommandObject();
|
|
if (proxy_command)
|
|
return proxy_command->GetSubcommandSP(sub_cmd, matches);
|
|
return lldb::CommandObjectSP();
|
|
}
|
|
|
|
CommandObject *CommandObjectProxy::GetSubcommandObject(llvm::StringRef sub_cmd,
|
|
StringList *matches) {
|
|
CommandObject *proxy_command = GetProxyCommandObject();
|
|
if (proxy_command)
|
|
return proxy_command->GetSubcommandObject(sub_cmd, matches);
|
|
return nullptr;
|
|
}
|
|
|
|
bool CommandObjectProxy::LoadSubCommand(
|
|
llvm::StringRef cmd_name, const lldb::CommandObjectSP &command_sp) {
|
|
CommandObject *proxy_command = GetProxyCommandObject();
|
|
if (proxy_command)
|
|
return proxy_command->LoadSubCommand(cmd_name, command_sp);
|
|
return false;
|
|
}
|
|
|
|
bool CommandObjectProxy::WantsRawCommandString() {
|
|
CommandObject *proxy_command = GetProxyCommandObject();
|
|
if (proxy_command)
|
|
return proxy_command->WantsRawCommandString();
|
|
return false;
|
|
}
|
|
|
|
bool CommandObjectProxy::WantsCompletion() {
|
|
CommandObject *proxy_command = GetProxyCommandObject();
|
|
if (proxy_command)
|
|
return proxy_command->WantsCompletion();
|
|
return false;
|
|
}
|
|
|
|
void CommandObjectProxy::HandleCompletion(CompletionRequest &request) {
|
|
CommandObject *proxy_command = GetProxyCommandObject();
|
|
if (proxy_command)
|
|
proxy_command->HandleCompletion(request);
|
|
}
|
|
|
|
void CommandObjectProxy::HandleArgumentCompletion(
|
|
CompletionRequest &request, OptionElementVector &opt_element_vector) {
|
|
CommandObject *proxy_command = GetProxyCommandObject();
|
|
if (proxy_command)
|
|
proxy_command->HandleArgumentCompletion(request, opt_element_vector);
|
|
}
|
|
|
|
llvm::Optional<std::string>
|
|
CommandObjectProxy::GetRepeatCommand(Args ¤t_command_args,
|
|
uint32_t index) {
|
|
CommandObject *proxy_command = GetProxyCommandObject();
|
|
if (proxy_command)
|
|
return proxy_command->GetRepeatCommand(current_command_args, index);
|
|
return llvm::None;
|
|
}
|
|
|
|
llvm::StringRef CommandObjectProxy::GetUnsupportedError() {
|
|
return "command is not implemented";
|
|
}
|
|
|
|
bool CommandObjectProxy::Execute(const char *args_string,
|
|
CommandReturnObject &result) {
|
|
CommandObject *proxy_command = GetProxyCommandObject();
|
|
if (proxy_command)
|
|
return proxy_command->Execute(args_string, result);
|
|
result.AppendError(GetUnsupportedError());
|
|
return false;
|
|
}
|