| //===-- CommandAlias.cpp -----------------------------------------*- C++-*-===// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "lldb/Interpreter/CommandAlias.h" |
| |
| #include "llvm/Support/ErrorHandling.h" |
| |
| #include "lldb/Interpreter/CommandInterpreter.h" |
| #include "lldb/Interpreter/CommandObject.h" |
| #include "lldb/Interpreter/CommandReturnObject.h" |
| #include "lldb/Interpreter/Options.h" |
| #include "lldb/Utility/StreamString.h" |
| |
| using namespace lldb; |
| using namespace lldb_private; |
| |
| static bool ProcessAliasOptionsArgs(lldb::CommandObjectSP &cmd_obj_sp, |
| llvm::StringRef options_args, |
| OptionArgVectorSP &option_arg_vector_sp) { |
| bool success = true; |
| OptionArgVector *option_arg_vector = option_arg_vector_sp.get(); |
| |
| if (options_args.size() < 1) |
| return true; |
| |
| Args args(options_args); |
| std::string options_string(options_args); |
| CommandReturnObject result; |
| // Check to see if the command being aliased can take any command options. |
| Options *options = cmd_obj_sp->GetOptions(); |
| if (options) { |
| // See if any options were specified as part of the alias; if so, handle |
| // them appropriately. |
| ExecutionContext exe_ctx = |
| cmd_obj_sp->GetCommandInterpreter().GetExecutionContext(); |
| options->NotifyOptionParsingStarting(&exe_ctx); |
| |
| llvm::Expected<Args> args_or = |
| options->ParseAlias(args, option_arg_vector, options_string); |
| if (!args_or) { |
| result.AppendError(toString(args_or.takeError())); |
| result.AppendError("Unable to create requested alias.\n"); |
| result.SetStatus(eReturnStatusFailed); |
| return false; |
| } |
| args = std::move(*args_or); |
| options->VerifyPartialOptions(result); |
| if (!result.Succeeded() && |
| result.GetStatus() != lldb::eReturnStatusStarted) { |
| result.AppendError("Unable to create requested alias.\n"); |
| return false; |
| } |
| } |
| |
| if (!options_string.empty()) { |
| if (cmd_obj_sp->WantsRawCommandString()) |
| option_arg_vector->emplace_back("<argument>", -1, options_string); |
| else { |
| for (auto &entry : args.entries()) { |
| if (!entry.ref.empty()) |
| option_arg_vector->emplace_back("<argument>", -1, entry.ref); |
| } |
| } |
| } |
| |
| return success; |
| } |
| |
| CommandAlias::CommandAlias(CommandInterpreter &interpreter, |
| lldb::CommandObjectSP cmd_sp, |
| llvm::StringRef options_args, llvm::StringRef name, |
| llvm::StringRef help, llvm::StringRef syntax, |
| uint32_t flags) |
| : CommandObject(interpreter, name, help, syntax, flags), |
| m_underlying_command_sp(), m_option_string(options_args), |
| m_option_args_sp(new OptionArgVector), |
| m_is_dashdash_alias(eLazyBoolCalculate), m_did_set_help(false), |
| m_did_set_help_long(false) { |
| if (ProcessAliasOptionsArgs(cmd_sp, options_args, m_option_args_sp)) { |
| m_underlying_command_sp = cmd_sp; |
| for (int i = 0; |
| auto cmd_entry = m_underlying_command_sp->GetArgumentEntryAtIndex(i); |
| i++) { |
| m_arguments.push_back(*cmd_entry); |
| } |
| if (!help.empty()) { |
| StreamString sstr; |
| StreamString translation_and_help; |
| GetAliasExpansion(sstr); |
| |
| translation_and_help.Printf( |
| "(%s) %s", sstr.GetData(), |
| GetUnderlyingCommand()->GetHelp().str().c_str()); |
| SetHelp(translation_and_help.GetString()); |
| } |
| } |
| } |
| |
| bool CommandAlias::WantsRawCommandString() { |
| if (IsValid()) |
| return m_underlying_command_sp->WantsRawCommandString(); |
| return false; |
| } |
| |
| bool CommandAlias::WantsCompletion() { |
| if (IsValid()) |
| return m_underlying_command_sp->WantsCompletion(); |
| return false; |
| } |
| |
| int CommandAlias::HandleCompletion(CompletionRequest &request) { |
| if (IsValid()) |
| return m_underlying_command_sp->HandleCompletion(request); |
| return -1; |
| } |
| |
| int CommandAlias::HandleArgumentCompletion( |
| CompletionRequest &request, OptionElementVector &opt_element_vector) { |
| if (IsValid()) |
| return m_underlying_command_sp->HandleArgumentCompletion( |
| request, opt_element_vector); |
| return -1; |
| } |
| |
| Options *CommandAlias::GetOptions() { |
| if (IsValid()) |
| return m_underlying_command_sp->GetOptions(); |
| return nullptr; |
| } |
| |
| bool CommandAlias::Execute(const char *args_string, |
| CommandReturnObject &result) { |
| llvm_unreachable("CommandAlias::Execute is not to be called"); |
| } |
| |
| void CommandAlias::GetAliasExpansion(StreamString &help_string) const { |
| llvm::StringRef command_name = m_underlying_command_sp->GetCommandName(); |
| help_string.Printf("'%*s", (int)command_name.size(), command_name.data()); |
| |
| if (!m_option_args_sp) { |
| help_string.Printf("'"); |
| return; |
| } |
| |
| OptionArgVector *options = m_option_args_sp.get(); |
| std::string opt; |
| std::string value; |
| |
| for (const auto &opt_entry : *options) { |
| std::tie(opt, std::ignore, value) = opt_entry; |
| if (opt == "<argument>") { |
| help_string.Printf(" %s", value.c_str()); |
| } else { |
| help_string.Printf(" %s", opt.c_str()); |
| if ((value.compare("<no-argument>") != 0) && |
| (value.compare("<need-argument") != 0)) { |
| help_string.Printf(" %s", value.c_str()); |
| } |
| } |
| } |
| |
| help_string.Printf("'"); |
| } |
| |
| bool CommandAlias::IsDashDashCommand() { |
| if (m_is_dashdash_alias != eLazyBoolCalculate) |
| return (m_is_dashdash_alias == eLazyBoolYes); |
| m_is_dashdash_alias = eLazyBoolNo; |
| if (!IsValid()) |
| return false; |
| |
| std::string opt; |
| std::string value; |
| |
| for (const auto &opt_entry : *GetOptionArguments()) { |
| std::tie(opt, std::ignore, value) = opt_entry; |
| if (opt == "<argument>" && !value.empty() && |
| llvm::StringRef(value).endswith("--")) { |
| m_is_dashdash_alias = eLazyBoolYes; |
| break; |
| } |
| } |
| |
| // if this is a nested alias, it may be adding arguments on top of an already |
| // dash-dash alias |
| if ((m_is_dashdash_alias == eLazyBoolNo) && IsNestedAlias()) |
| m_is_dashdash_alias = |
| (GetUnderlyingCommand()->IsDashDashCommand() ? eLazyBoolYes |
| : eLazyBoolNo); |
| return (m_is_dashdash_alias == eLazyBoolYes); |
| } |
| |
| bool CommandAlias::IsNestedAlias() { |
| if (GetUnderlyingCommand()) |
| return GetUnderlyingCommand()->IsAlias(); |
| return false; |
| } |
| |
| std::pair<lldb::CommandObjectSP, OptionArgVectorSP> CommandAlias::Desugar() { |
| auto underlying = GetUnderlyingCommand(); |
| if (!underlying) |
| return {nullptr, nullptr}; |
| |
| if (underlying->IsAlias()) { |
| auto desugared = ((CommandAlias *)underlying.get())->Desugar(); |
| auto options = GetOptionArguments(); |
| options->insert(options->begin(), desugared.second->begin(), |
| desugared.second->end()); |
| return {desugared.first, options}; |
| } |
| |
| return {underlying, GetOptionArguments()}; |
| } |
| |
| // allow CommandAlias objects to provide their own help, but fallback to the |
| // info for the underlying command if no customization has been provided |
| void CommandAlias::SetHelp(llvm::StringRef str) { |
| this->CommandObject::SetHelp(str); |
| m_did_set_help = true; |
| } |
| |
| void CommandAlias::SetHelpLong(llvm::StringRef str) { |
| this->CommandObject::SetHelpLong(str); |
| m_did_set_help_long = true; |
| } |
| |
| llvm::StringRef CommandAlias::GetHelp() { |
| if (!m_cmd_help_short.empty() || m_did_set_help) |
| return m_cmd_help_short; |
| if (IsValid()) |
| return m_underlying_command_sp->GetHelp(); |
| return llvm::StringRef(); |
| } |
| |
| llvm::StringRef CommandAlias::GetHelpLong() { |
| if (!m_cmd_help_long.empty() || m_did_set_help_long) |
| return m_cmd_help_long; |
| if (IsValid()) |
| return m_underlying_command_sp->GetHelpLong(); |
| return llvm::StringRef(); |
| } |