1 //===-- CommandAlias.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/CommandAlias.h" 10 11 #include "llvm/ADT/STLExtras.h" 12 #include "llvm/Support/ErrorHandling.h" 13 14 #include "lldb/Interpreter/CommandInterpreter.h" 15 #include "lldb/Interpreter/CommandObject.h" 16 #include "lldb/Interpreter/CommandReturnObject.h" 17 #include "lldb/Interpreter/Options.h" 18 #include "lldb/Utility/StreamString.h" 19 20 using namespace lldb; 21 using namespace lldb_private; 22 23 static bool ProcessAliasOptionsArgs(lldb::CommandObjectSP &cmd_obj_sp, 24 llvm::StringRef options_args, 25 OptionArgVectorSP &option_arg_vector_sp) { 26 bool success = true; 27 OptionArgVector *option_arg_vector = option_arg_vector_sp.get(); 28 29 if (options_args.size() < 1) 30 return true; 31 32 Args args(options_args); 33 std::string options_string(options_args); 34 // TODO: Find a way to propagate errors in this CommandReturnObject up the 35 // stack. 36 CommandReturnObject result(false); 37 // Check to see if the command being aliased can take any command options. 38 Options *options = cmd_obj_sp->GetOptions(); 39 if (options) { 40 // See if any options were specified as part of the alias; if so, handle 41 // them appropriately. 42 ExecutionContext exe_ctx = 43 cmd_obj_sp->GetCommandInterpreter().GetExecutionContext(); 44 options->NotifyOptionParsingStarting(&exe_ctx); 45 46 llvm::Expected<Args> args_or = 47 options->ParseAlias(args, option_arg_vector, options_string); 48 if (!args_or) { 49 result.AppendError(toString(args_or.takeError())); 50 result.AppendError("Unable to create requested alias.\n"); 51 return false; 52 } 53 args = std::move(*args_or); 54 options->VerifyPartialOptions(result); 55 if (!result.Succeeded() && 56 result.GetStatus() != lldb::eReturnStatusStarted) { 57 result.AppendError("Unable to create requested alias.\n"); 58 return false; 59 } 60 } 61 62 if (!options_string.empty()) { 63 if (cmd_obj_sp->WantsRawCommandString()) 64 option_arg_vector->emplace_back(CommandInterpreter::g_argument, 65 -1, options_string); 66 else { 67 for (auto &entry : args.entries()) { 68 if (!entry.ref().empty()) 69 option_arg_vector->emplace_back(std::string(CommandInterpreter::g_argument), -1, 70 std::string(entry.ref())); 71 } 72 } 73 } 74 75 return success; 76 } 77 78 CommandAlias::CommandAlias(CommandInterpreter &interpreter, 79 lldb::CommandObjectSP cmd_sp, 80 llvm::StringRef options_args, llvm::StringRef name, 81 llvm::StringRef help, llvm::StringRef syntax, 82 uint32_t flags) 83 : CommandObject(interpreter, name, help, syntax, flags), 84 m_option_string(std::string(options_args)), 85 m_option_args_sp(new OptionArgVector), 86 m_is_dashdash_alias(eLazyBoolCalculate), m_did_set_help(false), 87 m_did_set_help_long(false) { 88 if (ProcessAliasOptionsArgs(cmd_sp, options_args, m_option_args_sp)) { 89 m_underlying_command_sp = cmd_sp; 90 for (int i = 0; 91 auto cmd_entry = m_underlying_command_sp->GetArgumentEntryAtIndex(i); 92 i++) { 93 m_arguments.push_back(*cmd_entry); 94 } 95 if (!help.empty()) { 96 StreamString sstr; 97 StreamString translation_and_help; 98 GetAliasExpansion(sstr); 99 100 translation_and_help.Printf( 101 "(%s) %s", sstr.GetData(), 102 GetUnderlyingCommand()->GetHelp().str().c_str()); 103 SetHelp(translation_and_help.GetString()); 104 } 105 } 106 } 107 108 bool CommandAlias::WantsRawCommandString() { 109 if (IsValid()) 110 return m_underlying_command_sp->WantsRawCommandString(); 111 return false; 112 } 113 114 bool CommandAlias::WantsCompletion() { 115 if (IsValid()) 116 return m_underlying_command_sp->WantsCompletion(); 117 return false; 118 } 119 120 void CommandAlias::HandleCompletion(CompletionRequest &request) { 121 if (IsValid()) 122 m_underlying_command_sp->HandleCompletion(request); 123 } 124 125 void CommandAlias::HandleArgumentCompletion( 126 CompletionRequest &request, OptionElementVector &opt_element_vector) { 127 if (IsValid()) 128 m_underlying_command_sp->HandleArgumentCompletion(request, 129 opt_element_vector); 130 } 131 132 Options *CommandAlias::GetOptions() { 133 if (IsValid()) 134 return m_underlying_command_sp->GetOptions(); 135 return nullptr; 136 } 137 138 void CommandAlias::Execute(const char *args_string, 139 CommandReturnObject &result) { 140 llvm_unreachable("CommandAlias::Execute is not to be called"); 141 } 142 143 void CommandAlias::GetAliasExpansion(StreamString &help_string) const { 144 llvm::StringRef command_name = m_underlying_command_sp->GetCommandName(); 145 help_string.Printf("'%*s", (int)command_name.size(), command_name.data()); 146 147 if (!m_option_args_sp) { 148 help_string.Printf("'"); 149 return; 150 } 151 152 OptionArgVector *options = m_option_args_sp.get(); 153 std::string opt; 154 std::string value; 155 156 for (const auto &opt_entry : *options) { 157 std::tie(opt, std::ignore, value) = opt_entry; 158 if (opt == CommandInterpreter::g_argument) { 159 help_string.Printf(" %s", value.c_str()); 160 } else { 161 help_string.Printf(" %s", opt.c_str()); 162 if ((value != CommandInterpreter::g_no_argument) 163 && (value != CommandInterpreter::g_need_argument)) { 164 help_string.Printf(" %s", value.c_str()); 165 } 166 } 167 } 168 169 help_string.Printf("'"); 170 } 171 172 bool CommandAlias::IsDashDashCommand() { 173 if (m_is_dashdash_alias != eLazyBoolCalculate) 174 return (m_is_dashdash_alias == eLazyBoolYes); 175 m_is_dashdash_alias = eLazyBoolNo; 176 if (!IsValid()) 177 return false; 178 179 std::string opt; 180 std::string value; 181 182 for (const auto &opt_entry : *GetOptionArguments()) { 183 std::tie(opt, std::ignore, value) = opt_entry; 184 if (opt == CommandInterpreter::g_argument && !value.empty() && 185 llvm::StringRef(value).ends_with("--")) { 186 m_is_dashdash_alias = eLazyBoolYes; 187 break; 188 } 189 } 190 191 // if this is a nested alias, it may be adding arguments on top of an already 192 // dash-dash alias 193 if ((m_is_dashdash_alias == eLazyBoolNo) && IsNestedAlias()) 194 m_is_dashdash_alias = 195 (GetUnderlyingCommand()->IsDashDashCommand() ? eLazyBoolYes 196 : eLazyBoolNo); 197 return (m_is_dashdash_alias == eLazyBoolYes); 198 } 199 200 bool CommandAlias::IsNestedAlias() { 201 if (GetUnderlyingCommand()) 202 return GetUnderlyingCommand()->IsAlias(); 203 return false; 204 } 205 206 std::pair<lldb::CommandObjectSP, OptionArgVectorSP> CommandAlias::Desugar() { 207 auto underlying = GetUnderlyingCommand(); 208 if (!underlying) 209 return {nullptr, nullptr}; 210 211 if (underlying->IsAlias()) { 212 // FIXME: This doesn't work if the original alias fills a slot in the 213 // underlying alias, since this just appends the two lists. 214 auto desugared = ((CommandAlias *)underlying.get())->Desugar(); 215 OptionArgVectorSP options = std::make_shared<OptionArgVector>(); 216 llvm::append_range(*options, *desugared.second); 217 llvm::append_range(*options, *GetOptionArguments()); 218 return {desugared.first, options}; 219 } 220 221 return {underlying, GetOptionArguments()}; 222 } 223 224 // allow CommandAlias objects to provide their own help, but fallback to the 225 // info for the underlying command if no customization has been provided 226 void CommandAlias::SetHelp(llvm::StringRef str) { 227 this->CommandObject::SetHelp(str); 228 m_did_set_help = true; 229 } 230 231 void CommandAlias::SetHelpLong(llvm::StringRef str) { 232 this->CommandObject::SetHelpLong(str); 233 m_did_set_help_long = true; 234 } 235 236 llvm::StringRef CommandAlias::GetHelp() { 237 if (!m_cmd_help_short.empty() || m_did_set_help) 238 return m_cmd_help_short; 239 if (IsValid()) 240 return m_underlying_command_sp->GetHelp(); 241 return llvm::StringRef(); 242 } 243 244 llvm::StringRef CommandAlias::GetHelpLong() { 245 if (!m_cmd_help_long.empty() || m_did_set_help_long) 246 return m_cmd_help_long; 247 if (IsValid()) 248 return m_underlying_command_sp->GetHelpLong(); 249 return llvm::StringRef(); 250 } 251