xref: /freebsd-src/contrib/llvm-project/lldb/source/Interpreter/CommandAlias.cpp (revision 5f757f3ff9144b609b3c433dfd370cc6bdc191ad)
15ffd83dbSDimitry Andric //===-- CommandAlias.cpp --------------------------------------------------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric 
90b57cec5SDimitry Andric #include "lldb/Interpreter/CommandAlias.h"
100b57cec5SDimitry Andric 
1106c3fb27SDimitry Andric #include "llvm/ADT/STLExtras.h"
120b57cec5SDimitry Andric #include "llvm/Support/ErrorHandling.h"
130b57cec5SDimitry Andric 
140b57cec5SDimitry Andric #include "lldb/Interpreter/CommandInterpreter.h"
150b57cec5SDimitry Andric #include "lldb/Interpreter/CommandObject.h"
160b57cec5SDimitry Andric #include "lldb/Interpreter/CommandReturnObject.h"
170b57cec5SDimitry Andric #include "lldb/Interpreter/Options.h"
180b57cec5SDimitry Andric #include "lldb/Utility/StreamString.h"
190b57cec5SDimitry Andric 
200b57cec5SDimitry Andric using namespace lldb;
210b57cec5SDimitry Andric using namespace lldb_private;
220b57cec5SDimitry Andric 
ProcessAliasOptionsArgs(lldb::CommandObjectSP & cmd_obj_sp,llvm::StringRef options_args,OptionArgVectorSP & option_arg_vector_sp)230b57cec5SDimitry Andric static bool ProcessAliasOptionsArgs(lldb::CommandObjectSP &cmd_obj_sp,
240b57cec5SDimitry Andric                                     llvm::StringRef options_args,
250b57cec5SDimitry Andric                                     OptionArgVectorSP &option_arg_vector_sp) {
260b57cec5SDimitry Andric   bool success = true;
270b57cec5SDimitry Andric   OptionArgVector *option_arg_vector = option_arg_vector_sp.get();
280b57cec5SDimitry Andric 
290b57cec5SDimitry Andric   if (options_args.size() < 1)
300b57cec5SDimitry Andric     return true;
310b57cec5SDimitry Andric 
320b57cec5SDimitry Andric   Args args(options_args);
330b57cec5SDimitry Andric   std::string options_string(options_args);
340b57cec5SDimitry Andric   // TODO: Find a way to propagate errors in this CommandReturnObject up the
350b57cec5SDimitry Andric   // stack.
365ffd83dbSDimitry Andric   CommandReturnObject result(false);
370b57cec5SDimitry Andric   // Check to see if the command being aliased can take any command options.
380b57cec5SDimitry Andric   Options *options = cmd_obj_sp->GetOptions();
390b57cec5SDimitry Andric   if (options) {
400b57cec5SDimitry Andric     // See if any options were specified as part of the alias;  if so, handle
410b57cec5SDimitry Andric     // them appropriately.
420b57cec5SDimitry Andric     ExecutionContext exe_ctx =
430b57cec5SDimitry Andric         cmd_obj_sp->GetCommandInterpreter().GetExecutionContext();
440b57cec5SDimitry Andric     options->NotifyOptionParsingStarting(&exe_ctx);
450b57cec5SDimitry Andric 
460b57cec5SDimitry Andric     llvm::Expected<Args> args_or =
470b57cec5SDimitry Andric         options->ParseAlias(args, option_arg_vector, options_string);
480b57cec5SDimitry Andric     if (!args_or) {
490b57cec5SDimitry Andric       result.AppendError(toString(args_or.takeError()));
500b57cec5SDimitry Andric       result.AppendError("Unable to create requested alias.\n");
510b57cec5SDimitry Andric       return false;
520b57cec5SDimitry Andric     }
530b57cec5SDimitry Andric     args = std::move(*args_or);
540b57cec5SDimitry Andric     options->VerifyPartialOptions(result);
550b57cec5SDimitry Andric     if (!result.Succeeded() &&
560b57cec5SDimitry Andric         result.GetStatus() != lldb::eReturnStatusStarted) {
570b57cec5SDimitry Andric       result.AppendError("Unable to create requested alias.\n");
580b57cec5SDimitry Andric       return false;
590b57cec5SDimitry Andric     }
600b57cec5SDimitry Andric   }
610b57cec5SDimitry Andric 
620b57cec5SDimitry Andric   if (!options_string.empty()) {
630b57cec5SDimitry Andric     if (cmd_obj_sp->WantsRawCommandString())
64bdd1243dSDimitry Andric       option_arg_vector->emplace_back(CommandInterpreter::g_argument,
65bdd1243dSDimitry Andric                                       -1, options_string);
660b57cec5SDimitry Andric     else {
670b57cec5SDimitry Andric       for (auto &entry : args.entries()) {
689dba64beSDimitry Andric         if (!entry.ref().empty())
69bdd1243dSDimitry Andric           option_arg_vector->emplace_back(std::string(CommandInterpreter::g_argument), -1,
7013138422SDimitry Andric                                           std::string(entry.ref()));
710b57cec5SDimitry Andric       }
720b57cec5SDimitry Andric     }
730b57cec5SDimitry Andric   }
740b57cec5SDimitry Andric 
750b57cec5SDimitry Andric   return success;
760b57cec5SDimitry Andric }
770b57cec5SDimitry Andric 
CommandAlias(CommandInterpreter & interpreter,lldb::CommandObjectSP cmd_sp,llvm::StringRef options_args,llvm::StringRef name,llvm::StringRef help,llvm::StringRef syntax,uint32_t flags)780b57cec5SDimitry Andric CommandAlias::CommandAlias(CommandInterpreter &interpreter,
790b57cec5SDimitry Andric                            lldb::CommandObjectSP cmd_sp,
800b57cec5SDimitry Andric                            llvm::StringRef options_args, llvm::StringRef name,
810b57cec5SDimitry Andric                            llvm::StringRef help, llvm::StringRef syntax,
820b57cec5SDimitry Andric                            uint32_t flags)
830b57cec5SDimitry Andric     : CommandObject(interpreter, name, help, syntax, flags),
84fe6060f1SDimitry Andric       m_option_string(std::string(options_args)),
850b57cec5SDimitry Andric       m_option_args_sp(new OptionArgVector),
860b57cec5SDimitry Andric       m_is_dashdash_alias(eLazyBoolCalculate), m_did_set_help(false),
870b57cec5SDimitry Andric       m_did_set_help_long(false) {
880b57cec5SDimitry Andric   if (ProcessAliasOptionsArgs(cmd_sp, options_args, m_option_args_sp)) {
890b57cec5SDimitry Andric     m_underlying_command_sp = cmd_sp;
900b57cec5SDimitry Andric     for (int i = 0;
910b57cec5SDimitry Andric          auto cmd_entry = m_underlying_command_sp->GetArgumentEntryAtIndex(i);
920b57cec5SDimitry Andric          i++) {
930b57cec5SDimitry Andric       m_arguments.push_back(*cmd_entry);
940b57cec5SDimitry Andric     }
950b57cec5SDimitry Andric     if (!help.empty()) {
960b57cec5SDimitry Andric       StreamString sstr;
970b57cec5SDimitry Andric       StreamString translation_and_help;
980b57cec5SDimitry Andric       GetAliasExpansion(sstr);
990b57cec5SDimitry Andric 
1000b57cec5SDimitry Andric       translation_and_help.Printf(
1010b57cec5SDimitry Andric           "(%s)  %s", sstr.GetData(),
1020b57cec5SDimitry Andric           GetUnderlyingCommand()->GetHelp().str().c_str());
1030b57cec5SDimitry Andric       SetHelp(translation_and_help.GetString());
1040b57cec5SDimitry Andric     }
1050b57cec5SDimitry Andric   }
1060b57cec5SDimitry Andric }
1070b57cec5SDimitry Andric 
WantsRawCommandString()1080b57cec5SDimitry Andric bool CommandAlias::WantsRawCommandString() {
1090b57cec5SDimitry Andric   if (IsValid())
1100b57cec5SDimitry Andric     return m_underlying_command_sp->WantsRawCommandString();
1110b57cec5SDimitry Andric   return false;
1120b57cec5SDimitry Andric }
1130b57cec5SDimitry Andric 
WantsCompletion()1140b57cec5SDimitry Andric bool CommandAlias::WantsCompletion() {
1150b57cec5SDimitry Andric   if (IsValid())
1160b57cec5SDimitry Andric     return m_underlying_command_sp->WantsCompletion();
1170b57cec5SDimitry Andric   return false;
1180b57cec5SDimitry Andric }
1190b57cec5SDimitry Andric 
HandleCompletion(CompletionRequest & request)1209dba64beSDimitry Andric void CommandAlias::HandleCompletion(CompletionRequest &request) {
1210b57cec5SDimitry Andric   if (IsValid())
1229dba64beSDimitry Andric     m_underlying_command_sp->HandleCompletion(request);
1230b57cec5SDimitry Andric }
1240b57cec5SDimitry Andric 
HandleArgumentCompletion(CompletionRequest & request,OptionElementVector & opt_element_vector)1259dba64beSDimitry Andric void CommandAlias::HandleArgumentCompletion(
1260b57cec5SDimitry Andric     CompletionRequest &request, OptionElementVector &opt_element_vector) {
1270b57cec5SDimitry Andric   if (IsValid())
1289dba64beSDimitry Andric     m_underlying_command_sp->HandleArgumentCompletion(request,
1299dba64beSDimitry Andric                                                       opt_element_vector);
1300b57cec5SDimitry Andric }
1310b57cec5SDimitry Andric 
GetOptions()1320b57cec5SDimitry Andric Options *CommandAlias::GetOptions() {
1330b57cec5SDimitry Andric   if (IsValid())
1340b57cec5SDimitry Andric     return m_underlying_command_sp->GetOptions();
1350b57cec5SDimitry Andric   return nullptr;
1360b57cec5SDimitry Andric }
1370b57cec5SDimitry Andric 
Execute(const char * args_string,CommandReturnObject & result)138*5f757f3fSDimitry Andric void CommandAlias::Execute(const char *args_string,
1390b57cec5SDimitry Andric                            CommandReturnObject &result) {
1400b57cec5SDimitry Andric   llvm_unreachable("CommandAlias::Execute is not to be called");
1410b57cec5SDimitry Andric }
1420b57cec5SDimitry Andric 
GetAliasExpansion(StreamString & help_string) const1430b57cec5SDimitry Andric void CommandAlias::GetAliasExpansion(StreamString &help_string) const {
1440b57cec5SDimitry Andric   llvm::StringRef command_name = m_underlying_command_sp->GetCommandName();
1450b57cec5SDimitry Andric   help_string.Printf("'%*s", (int)command_name.size(), command_name.data());
1460b57cec5SDimitry Andric 
1470b57cec5SDimitry Andric   if (!m_option_args_sp) {
1480b57cec5SDimitry Andric     help_string.Printf("'");
1490b57cec5SDimitry Andric     return;
1500b57cec5SDimitry Andric   }
1510b57cec5SDimitry Andric 
1520b57cec5SDimitry Andric   OptionArgVector *options = m_option_args_sp.get();
1530b57cec5SDimitry Andric   std::string opt;
1540b57cec5SDimitry Andric   std::string value;
1550b57cec5SDimitry Andric 
1560b57cec5SDimitry Andric   for (const auto &opt_entry : *options) {
1570b57cec5SDimitry Andric     std::tie(opt, std::ignore, value) = opt_entry;
158bdd1243dSDimitry Andric     if (opt == CommandInterpreter::g_argument) {
1590b57cec5SDimitry Andric       help_string.Printf(" %s", value.c_str());
1600b57cec5SDimitry Andric     } else {
1610b57cec5SDimitry Andric       help_string.Printf(" %s", opt.c_str());
162bdd1243dSDimitry Andric       if ((value != CommandInterpreter::g_no_argument)
163bdd1243dSDimitry Andric            && (value != CommandInterpreter::g_need_argument)) {
1640b57cec5SDimitry Andric         help_string.Printf(" %s", value.c_str());
1650b57cec5SDimitry Andric       }
1660b57cec5SDimitry Andric     }
1670b57cec5SDimitry Andric   }
1680b57cec5SDimitry Andric 
1690b57cec5SDimitry Andric   help_string.Printf("'");
1700b57cec5SDimitry Andric }
1710b57cec5SDimitry Andric 
IsDashDashCommand()1720b57cec5SDimitry Andric bool CommandAlias::IsDashDashCommand() {
1730b57cec5SDimitry Andric   if (m_is_dashdash_alias != eLazyBoolCalculate)
1740b57cec5SDimitry Andric     return (m_is_dashdash_alias == eLazyBoolYes);
1750b57cec5SDimitry Andric   m_is_dashdash_alias = eLazyBoolNo;
1760b57cec5SDimitry Andric   if (!IsValid())
1770b57cec5SDimitry Andric     return false;
1780b57cec5SDimitry Andric 
1790b57cec5SDimitry Andric   std::string opt;
1800b57cec5SDimitry Andric   std::string value;
1810b57cec5SDimitry Andric 
1820b57cec5SDimitry Andric   for (const auto &opt_entry : *GetOptionArguments()) {
1830b57cec5SDimitry Andric     std::tie(opt, std::ignore, value) = opt_entry;
184bdd1243dSDimitry Andric     if (opt == CommandInterpreter::g_argument && !value.empty() &&
185*5f757f3fSDimitry Andric         llvm::StringRef(value).ends_with("--")) {
1860b57cec5SDimitry Andric       m_is_dashdash_alias = eLazyBoolYes;
1870b57cec5SDimitry Andric       break;
1880b57cec5SDimitry Andric     }
1890b57cec5SDimitry Andric   }
1900b57cec5SDimitry Andric 
1910b57cec5SDimitry Andric   // if this is a nested alias, it may be adding arguments on top of an already
1920b57cec5SDimitry Andric   // dash-dash alias
1930b57cec5SDimitry Andric   if ((m_is_dashdash_alias == eLazyBoolNo) && IsNestedAlias())
1940b57cec5SDimitry Andric     m_is_dashdash_alias =
1950b57cec5SDimitry Andric         (GetUnderlyingCommand()->IsDashDashCommand() ? eLazyBoolYes
1960b57cec5SDimitry Andric                                                      : eLazyBoolNo);
1970b57cec5SDimitry Andric   return (m_is_dashdash_alias == eLazyBoolYes);
1980b57cec5SDimitry Andric }
1990b57cec5SDimitry Andric 
IsNestedAlias()2000b57cec5SDimitry Andric bool CommandAlias::IsNestedAlias() {
2010b57cec5SDimitry Andric   if (GetUnderlyingCommand())
2020b57cec5SDimitry Andric     return GetUnderlyingCommand()->IsAlias();
2030b57cec5SDimitry Andric   return false;
2040b57cec5SDimitry Andric }
2050b57cec5SDimitry Andric 
Desugar()2060b57cec5SDimitry Andric std::pair<lldb::CommandObjectSP, OptionArgVectorSP> CommandAlias::Desugar() {
2070b57cec5SDimitry Andric   auto underlying = GetUnderlyingCommand();
2080b57cec5SDimitry Andric   if (!underlying)
2090b57cec5SDimitry Andric     return {nullptr, nullptr};
2100b57cec5SDimitry Andric 
2110b57cec5SDimitry Andric   if (underlying->IsAlias()) {
212bdd1243dSDimitry Andric     // FIXME: This doesn't work if the original alias fills a slot in the
213bdd1243dSDimitry Andric     // underlying alias, since this just appends the two lists.
2140b57cec5SDimitry Andric     auto desugared = ((CommandAlias *)underlying.get())->Desugar();
21506c3fb27SDimitry Andric     OptionArgVectorSP options = std::make_shared<OptionArgVector>();
21606c3fb27SDimitry Andric     llvm::append_range(*options, *desugared.second);
21706c3fb27SDimitry Andric     llvm::append_range(*options, *GetOptionArguments());
2180b57cec5SDimitry Andric     return {desugared.first, options};
2190b57cec5SDimitry Andric   }
2200b57cec5SDimitry Andric 
2210b57cec5SDimitry Andric   return {underlying, GetOptionArguments()};
2220b57cec5SDimitry Andric }
2230b57cec5SDimitry Andric 
2240b57cec5SDimitry Andric // allow CommandAlias objects to provide their own help, but fallback to the
2250b57cec5SDimitry Andric // info for the underlying command if no customization has been provided
SetHelp(llvm::StringRef str)2260b57cec5SDimitry Andric void CommandAlias::SetHelp(llvm::StringRef str) {
2270b57cec5SDimitry Andric   this->CommandObject::SetHelp(str);
2280b57cec5SDimitry Andric   m_did_set_help = true;
2290b57cec5SDimitry Andric }
2300b57cec5SDimitry Andric 
SetHelpLong(llvm::StringRef str)2310b57cec5SDimitry Andric void CommandAlias::SetHelpLong(llvm::StringRef str) {
2320b57cec5SDimitry Andric   this->CommandObject::SetHelpLong(str);
2330b57cec5SDimitry Andric   m_did_set_help_long = true;
2340b57cec5SDimitry Andric }
2350b57cec5SDimitry Andric 
GetHelp()2360b57cec5SDimitry Andric llvm::StringRef CommandAlias::GetHelp() {
2370b57cec5SDimitry Andric   if (!m_cmd_help_short.empty() || m_did_set_help)
2380b57cec5SDimitry Andric     return m_cmd_help_short;
2390b57cec5SDimitry Andric   if (IsValid())
2400b57cec5SDimitry Andric     return m_underlying_command_sp->GetHelp();
2410b57cec5SDimitry Andric   return llvm::StringRef();
2420b57cec5SDimitry Andric }
2430b57cec5SDimitry Andric 
GetHelpLong()2440b57cec5SDimitry Andric llvm::StringRef CommandAlias::GetHelpLong() {
2450b57cec5SDimitry Andric   if (!m_cmd_help_long.empty() || m_did_set_help_long)
2460b57cec5SDimitry Andric     return m_cmd_help_long;
2470b57cec5SDimitry Andric   if (IsValid())
2480b57cec5SDimitry Andric     return m_underlying_command_sp->GetHelpLong();
2490b57cec5SDimitry Andric   return llvm::StringRef();
2500b57cec5SDimitry Andric }
251