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