|  | //===-- CommandAlias.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/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); | 
|  | // TODO: Find a way to propagate errors in this CommandReturnObject up the | 
|  | // stack. | 
|  | CommandReturnObject result(false); | 
|  | // 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(std::string("<argument>"), -1, | 
|  | std::string(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_option_string(std::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; | 
|  | } | 
|  |  | 
|  | void CommandAlias::HandleCompletion(CompletionRequest &request) { | 
|  | if (IsValid()) | 
|  | m_underlying_command_sp->HandleCompletion(request); | 
|  | } | 
|  |  | 
|  | void CommandAlias::HandleArgumentCompletion( | 
|  | CompletionRequest &request, OptionElementVector &opt_element_vector) { | 
|  | if (IsValid()) | 
|  | m_underlying_command_sp->HandleArgumentCompletion(request, | 
|  | opt_element_vector); | 
|  | } | 
|  |  | 
|  | 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 != "<no-argument>") && (value != "<need-argument")) { | 
|  | 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(); | 
|  | } |