1 //===-- CommandObjectMultiword.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/CommandObjectMultiword.h" 10 #include "lldb/Interpreter/CommandInterpreter.h" 11 #include "lldb/Interpreter/CommandReturnObject.h" 12 #include "lldb/Interpreter/Options.h" 13 14 using namespace lldb; 15 using namespace lldb_private; 16 17 // CommandObjectMultiword 18 19 CommandObjectMultiword::CommandObjectMultiword(CommandInterpreter &interpreter, 20 const char *name, 21 const char *help, 22 const char *syntax, 23 uint32_t flags) 24 : CommandObject(interpreter, name, help, syntax, flags), 25 m_can_be_removed(false) {} 26 27 CommandObjectMultiword::~CommandObjectMultiword() = default; 28 29 CommandObjectSP CommandObjectMultiword::GetSubcommandSP(llvm::StringRef sub_cmd, 30 StringList *matches) { 31 CommandObjectSP return_cmd_sp; 32 CommandObject::CommandMap::iterator pos; 33 34 if (!m_subcommand_dict.empty()) { 35 pos = m_subcommand_dict.find(std::string(sub_cmd)); 36 if (pos != m_subcommand_dict.end()) { 37 // An exact match; append the sub_cmd to the 'matches' string list. 38 if (matches) 39 matches->AppendString(sub_cmd); 40 return_cmd_sp = pos->second; 41 } else { 42 StringList local_matches; 43 if (matches == nullptr) 44 matches = &local_matches; 45 int num_matches = 46 AddNamesMatchingPartialString(m_subcommand_dict, sub_cmd, *matches); 47 48 if (num_matches == 1) { 49 // Cleaner, but slightly less efficient would be to call back into this 50 // function, since I now know I have an exact match... 51 52 sub_cmd = matches->GetStringAtIndex(0); 53 pos = m_subcommand_dict.find(std::string(sub_cmd)); 54 if (pos != m_subcommand_dict.end()) 55 return_cmd_sp = pos->second; 56 } 57 } 58 } 59 return return_cmd_sp; 60 } 61 62 CommandObject * 63 CommandObjectMultiword::GetSubcommandObject(llvm::StringRef sub_cmd, 64 StringList *matches) { 65 return GetSubcommandSP(sub_cmd, matches).get(); 66 } 67 68 bool CommandObjectMultiword::LoadSubCommand(llvm::StringRef name, 69 const CommandObjectSP &cmd_obj) { 70 if (cmd_obj) 71 assert((&GetCommandInterpreter() == &cmd_obj->GetCommandInterpreter()) && 72 "tried to add a CommandObject from a different interpreter"); 73 74 CommandMap::iterator pos; 75 bool success = true; 76 77 pos = m_subcommand_dict.find(std::string(name)); 78 if (pos == m_subcommand_dict.end()) { 79 m_subcommand_dict[std::string(name)] = cmd_obj; 80 } else 81 success = false; 82 83 return success; 84 } 85 86 bool CommandObjectMultiword::Execute(const char *args_string, 87 CommandReturnObject &result) { 88 Args args(args_string); 89 const size_t argc = args.GetArgumentCount(); 90 if (argc == 0) { 91 this->CommandObject::GenerateHelpText(result); 92 return result.Succeeded(); 93 } 94 95 auto sub_command = args[0].ref(); 96 if (sub_command.empty()) { 97 result.AppendError("Need to specify a non-empty subcommand."); 98 return result.Succeeded(); 99 } 100 101 if (sub_command.equals_insensitive("help")) { 102 this->CommandObject::GenerateHelpText(result); 103 return result.Succeeded(); 104 } 105 106 if (m_subcommand_dict.empty()) { 107 result.AppendErrorWithFormat("'%s' does not have any subcommands.\n", 108 GetCommandName().str().c_str()); 109 return false; 110 } 111 112 StringList matches; 113 CommandObject *sub_cmd_obj = GetSubcommandObject(sub_command, &matches); 114 if (sub_cmd_obj != nullptr) { 115 // Now call CommandObject::Execute to process options in `rest_of_line`. 116 // From there the command-specific version of Execute will be called, with 117 // the processed arguments. 118 119 args.Shift(); 120 sub_cmd_obj->Execute(args_string, result); 121 return result.Succeeded(); 122 } 123 124 std::string error_msg; 125 const size_t num_subcmd_matches = matches.GetSize(); 126 if (num_subcmd_matches > 0) 127 error_msg.assign("ambiguous command "); 128 else 129 error_msg.assign("invalid command "); 130 131 error_msg.append("'"); 132 error_msg.append(std::string(GetCommandName())); 133 error_msg.append(" "); 134 error_msg.append(std::string(sub_command)); 135 error_msg.append("'."); 136 137 if (num_subcmd_matches > 0) { 138 error_msg.append(" Possible completions:"); 139 for (const std::string &match : matches) { 140 error_msg.append("\n\t"); 141 error_msg.append(match); 142 } 143 } 144 error_msg.append("\n"); 145 result.AppendRawError(error_msg.c_str()); 146 return false; 147 } 148 149 void CommandObjectMultiword::GenerateHelpText(Stream &output_stream) { 150 // First time through here, generate the help text for the object and push it 151 // to the return result object as well 152 153 CommandObject::GenerateHelpText(output_stream); 154 output_stream.PutCString("\nThe following subcommands are supported:\n\n"); 155 156 CommandMap::iterator pos; 157 uint32_t max_len = FindLongestCommandWord(m_subcommand_dict); 158 159 if (max_len) 160 max_len += 4; // Indent the output by 4 spaces. 161 162 for (pos = m_subcommand_dict.begin(); pos != m_subcommand_dict.end(); ++pos) { 163 std::string indented_command(" "); 164 indented_command.append(pos->first); 165 if (pos->second->WantsRawCommandString()) { 166 std::string help_text(std::string(pos->second->GetHelp())); 167 help_text.append(" Expects 'raw' input (see 'help raw-input'.)"); 168 m_interpreter.OutputFormattedHelpText(output_stream, indented_command, 169 "--", help_text, max_len); 170 } else 171 m_interpreter.OutputFormattedHelpText(output_stream, indented_command, 172 "--", pos->second->GetHelp(), 173 max_len); 174 } 175 176 output_stream.PutCString("\nFor more help on any particular subcommand, type " 177 "'help <command> <subcommand>'.\n"); 178 } 179 180 void CommandObjectMultiword::HandleCompletion(CompletionRequest &request) { 181 auto arg0 = request.GetParsedLine()[0].ref(); 182 if (request.GetCursorIndex() == 0) { 183 StringList new_matches, descriptions; 184 AddNamesMatchingPartialString(m_subcommand_dict, arg0, new_matches, 185 &descriptions); 186 request.AddCompletions(new_matches, descriptions); 187 188 if (new_matches.GetSize() == 1 && 189 new_matches.GetStringAtIndex(0) != nullptr && 190 (arg0 == new_matches.GetStringAtIndex(0))) { 191 StringList temp_matches; 192 CommandObject *cmd_obj = GetSubcommandObject(arg0, &temp_matches); 193 if (cmd_obj != nullptr) { 194 if (request.GetParsedLine().GetArgumentCount() != 1) { 195 request.GetParsedLine().Shift(); 196 request.AppendEmptyArgument(); 197 cmd_obj->HandleCompletion(request); 198 } 199 } 200 } 201 return; 202 } 203 204 StringList new_matches; 205 CommandObject *sub_command_object = GetSubcommandObject(arg0, &new_matches); 206 if (sub_command_object == nullptr) { 207 request.AddCompletions(new_matches); 208 return; 209 } 210 211 // Remove the one match that we got from calling GetSubcommandObject. 212 new_matches.DeleteStringAtIndex(0); 213 request.AddCompletions(new_matches); 214 request.ShiftArguments(); 215 sub_command_object->HandleCompletion(request); 216 } 217 218 const char *CommandObjectMultiword::GetRepeatCommand(Args ¤t_command_args, 219 uint32_t index) { 220 index++; 221 if (current_command_args.GetArgumentCount() <= index) 222 return nullptr; 223 CommandObject *sub_command_object = 224 GetSubcommandObject(current_command_args[index].ref()); 225 if (sub_command_object == nullptr) 226 return nullptr; 227 return sub_command_object->GetRepeatCommand(current_command_args, index); 228 } 229 230 void CommandObjectMultiword::AproposAllSubCommands(llvm::StringRef prefix, 231 llvm::StringRef search_word, 232 StringList &commands_found, 233 StringList &commands_help) { 234 CommandObject::CommandMap::const_iterator pos; 235 236 for (pos = m_subcommand_dict.begin(); pos != m_subcommand_dict.end(); ++pos) { 237 const char *command_name = pos->first.c_str(); 238 CommandObject *sub_cmd_obj = pos->second.get(); 239 StreamString complete_command_name; 240 241 complete_command_name << prefix << " " << command_name; 242 243 if (sub_cmd_obj->HelpTextContainsWord(search_word)) { 244 commands_found.AppendString(complete_command_name.GetString()); 245 commands_help.AppendString(sub_cmd_obj->GetHelp()); 246 } 247 248 if (sub_cmd_obj->IsMultiwordObject()) 249 sub_cmd_obj->AproposAllSubCommands(complete_command_name.GetString(), 250 search_word, commands_found, 251 commands_help); 252 } 253 } 254 255 CommandObjectProxy::CommandObjectProxy(CommandInterpreter &interpreter, 256 const char *name, const char *help, 257 const char *syntax, uint32_t flags) 258 : CommandObject(interpreter, name, help, syntax, flags) {} 259 260 CommandObjectProxy::~CommandObjectProxy() = default; 261 262 Options *CommandObjectProxy::GetOptions() { 263 CommandObject *proxy_command = GetProxyCommandObject(); 264 if (proxy_command) 265 return proxy_command->GetOptions(); 266 return CommandObject::GetOptions(); 267 } 268 269 llvm::StringRef CommandObjectProxy::GetHelp() { 270 CommandObject *proxy_command = GetProxyCommandObject(); 271 if (proxy_command) 272 return proxy_command->GetHelp(); 273 return CommandObject::GetHelp(); 274 } 275 276 llvm::StringRef CommandObjectProxy::GetSyntax() { 277 CommandObject *proxy_command = GetProxyCommandObject(); 278 if (proxy_command) 279 return proxy_command->GetSyntax(); 280 return CommandObject::GetSyntax(); 281 } 282 283 llvm::StringRef CommandObjectProxy::GetHelpLong() { 284 CommandObject *proxy_command = GetProxyCommandObject(); 285 if (proxy_command) 286 return proxy_command->GetHelpLong(); 287 return CommandObject::GetHelpLong(); 288 } 289 290 bool CommandObjectProxy::IsRemovable() const { 291 const CommandObject *proxy_command = 292 const_cast<CommandObjectProxy *>(this)->GetProxyCommandObject(); 293 if (proxy_command) 294 return proxy_command->IsRemovable(); 295 return false; 296 } 297 298 bool CommandObjectProxy::IsMultiwordObject() { 299 CommandObject *proxy_command = GetProxyCommandObject(); 300 if (proxy_command) 301 return proxy_command->IsMultiwordObject(); 302 return false; 303 } 304 305 CommandObjectMultiword *CommandObjectProxy::GetAsMultiwordCommand() { 306 CommandObject *proxy_command = GetProxyCommandObject(); 307 if (proxy_command) 308 return proxy_command->GetAsMultiwordCommand(); 309 return nullptr; 310 } 311 312 void CommandObjectProxy::GenerateHelpText(Stream &result) { 313 CommandObject *proxy_command = GetProxyCommandObject(); 314 if (proxy_command) 315 proxy_command->GenerateHelpText(result); 316 else 317 CommandObject::GenerateHelpText(result); 318 } 319 320 lldb::CommandObjectSP 321 CommandObjectProxy::GetSubcommandSP(llvm::StringRef sub_cmd, 322 StringList *matches) { 323 CommandObject *proxy_command = GetProxyCommandObject(); 324 if (proxy_command) 325 return proxy_command->GetSubcommandSP(sub_cmd, matches); 326 return lldb::CommandObjectSP(); 327 } 328 329 CommandObject *CommandObjectProxy::GetSubcommandObject(llvm::StringRef sub_cmd, 330 StringList *matches) { 331 CommandObject *proxy_command = GetProxyCommandObject(); 332 if (proxy_command) 333 return proxy_command->GetSubcommandObject(sub_cmd, matches); 334 return nullptr; 335 } 336 337 void CommandObjectProxy::AproposAllSubCommands(llvm::StringRef prefix, 338 llvm::StringRef search_word, 339 StringList &commands_found, 340 StringList &commands_help) { 341 CommandObject *proxy_command = GetProxyCommandObject(); 342 if (proxy_command) 343 return proxy_command->AproposAllSubCommands(prefix, search_word, 344 commands_found, commands_help); 345 } 346 347 bool CommandObjectProxy::LoadSubCommand( 348 llvm::StringRef cmd_name, const lldb::CommandObjectSP &command_sp) { 349 CommandObject *proxy_command = GetProxyCommandObject(); 350 if (proxy_command) 351 return proxy_command->LoadSubCommand(cmd_name, command_sp); 352 return false; 353 } 354 355 bool CommandObjectProxy::WantsRawCommandString() { 356 CommandObject *proxy_command = GetProxyCommandObject(); 357 if (proxy_command) 358 return proxy_command->WantsRawCommandString(); 359 return false; 360 } 361 362 bool CommandObjectProxy::WantsCompletion() { 363 CommandObject *proxy_command = GetProxyCommandObject(); 364 if (proxy_command) 365 return proxy_command->WantsCompletion(); 366 return false; 367 } 368 369 void CommandObjectProxy::HandleCompletion(CompletionRequest &request) { 370 CommandObject *proxy_command = GetProxyCommandObject(); 371 if (proxy_command) 372 proxy_command->HandleCompletion(request); 373 } 374 375 void CommandObjectProxy::HandleArgumentCompletion( 376 CompletionRequest &request, OptionElementVector &opt_element_vector) { 377 CommandObject *proxy_command = GetProxyCommandObject(); 378 if (proxy_command) 379 proxy_command->HandleArgumentCompletion(request, opt_element_vector); 380 } 381 382 const char *CommandObjectProxy::GetRepeatCommand(Args ¤t_command_args, 383 uint32_t index) { 384 CommandObject *proxy_command = GetProxyCommandObject(); 385 if (proxy_command) 386 return proxy_command->GetRepeatCommand(current_command_args, index); 387 return nullptr; 388 } 389 390 llvm::StringRef CommandObjectProxy::GetUnsupportedError() { 391 return "command is not implemented"; 392 } 393 394 bool CommandObjectProxy::Execute(const char *args_string, 395 CommandReturnObject &result) { 396 CommandObject *proxy_command = GetProxyCommandObject(); 397 if (proxy_command) 398 return proxy_command->Execute(args_string, result); 399 result.AppendError(GetUnsupportedError()); 400 return false; 401 } 402