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