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