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 llvm::Optional<std::string> 294 CommandObjectMultiword::GetRepeatCommand(Args ¤t_command_args, 295 uint32_t index) { 296 index++; 297 if (current_command_args.GetArgumentCount() <= index) 298 return llvm::None; 299 CommandObject *sub_command_object = 300 GetSubcommandObject(current_command_args[index].ref()); 301 if (sub_command_object == nullptr) 302 return llvm::None; 303 return sub_command_object->GetRepeatCommand(current_command_args, index); 304 } 305 306 CommandObjectProxy::CommandObjectProxy(CommandInterpreter &interpreter, 307 const char *name, const char *help, 308 const char *syntax, uint32_t flags) 309 : CommandObject(interpreter, name, help, syntax, flags) {} 310 311 CommandObjectProxy::~CommandObjectProxy() = default; 312 313 Options *CommandObjectProxy::GetOptions() { 314 CommandObject *proxy_command = GetProxyCommandObject(); 315 if (proxy_command) 316 return proxy_command->GetOptions(); 317 return CommandObject::GetOptions(); 318 } 319 320 llvm::StringRef CommandObjectProxy::GetHelp() { 321 CommandObject *proxy_command = GetProxyCommandObject(); 322 if (proxy_command) 323 return proxy_command->GetHelp(); 324 return CommandObject::GetHelp(); 325 } 326 327 llvm::StringRef CommandObjectProxy::GetSyntax() { 328 CommandObject *proxy_command = GetProxyCommandObject(); 329 if (proxy_command) 330 return proxy_command->GetSyntax(); 331 return CommandObject::GetSyntax(); 332 } 333 334 llvm::StringRef CommandObjectProxy::GetHelpLong() { 335 CommandObject *proxy_command = GetProxyCommandObject(); 336 if (proxy_command) 337 return proxy_command->GetHelpLong(); 338 return CommandObject::GetHelpLong(); 339 } 340 341 bool CommandObjectProxy::IsRemovable() const { 342 const CommandObject *proxy_command = 343 const_cast<CommandObjectProxy *>(this)->GetProxyCommandObject(); 344 if (proxy_command) 345 return proxy_command->IsRemovable(); 346 return false; 347 } 348 349 bool CommandObjectProxy::IsMultiwordObject() { 350 CommandObject *proxy_command = GetProxyCommandObject(); 351 if (proxy_command) 352 return proxy_command->IsMultiwordObject(); 353 return false; 354 } 355 356 CommandObjectMultiword *CommandObjectProxy::GetAsMultiwordCommand() { 357 CommandObject *proxy_command = GetProxyCommandObject(); 358 if (proxy_command) 359 return proxy_command->GetAsMultiwordCommand(); 360 return nullptr; 361 } 362 363 void CommandObjectProxy::GenerateHelpText(Stream &result) { 364 CommandObject *proxy_command = GetProxyCommandObject(); 365 if (proxy_command) 366 proxy_command->GenerateHelpText(result); 367 else 368 CommandObject::GenerateHelpText(result); 369 } 370 371 lldb::CommandObjectSP 372 CommandObjectProxy::GetSubcommandSP(llvm::StringRef sub_cmd, 373 StringList *matches) { 374 CommandObject *proxy_command = GetProxyCommandObject(); 375 if (proxy_command) 376 return proxy_command->GetSubcommandSP(sub_cmd, matches); 377 return lldb::CommandObjectSP(); 378 } 379 380 CommandObject *CommandObjectProxy::GetSubcommandObject(llvm::StringRef sub_cmd, 381 StringList *matches) { 382 CommandObject *proxy_command = GetProxyCommandObject(); 383 if (proxy_command) 384 return proxy_command->GetSubcommandObject(sub_cmd, matches); 385 return nullptr; 386 } 387 388 bool CommandObjectProxy::LoadSubCommand( 389 llvm::StringRef cmd_name, const lldb::CommandObjectSP &command_sp) { 390 CommandObject *proxy_command = GetProxyCommandObject(); 391 if (proxy_command) 392 return proxy_command->LoadSubCommand(cmd_name, command_sp); 393 return false; 394 } 395 396 bool CommandObjectProxy::WantsRawCommandString() { 397 CommandObject *proxy_command = GetProxyCommandObject(); 398 if (proxy_command) 399 return proxy_command->WantsRawCommandString(); 400 return false; 401 } 402 403 bool CommandObjectProxy::WantsCompletion() { 404 CommandObject *proxy_command = GetProxyCommandObject(); 405 if (proxy_command) 406 return proxy_command->WantsCompletion(); 407 return false; 408 } 409 410 void CommandObjectProxy::HandleCompletion(CompletionRequest &request) { 411 CommandObject *proxy_command = GetProxyCommandObject(); 412 if (proxy_command) 413 proxy_command->HandleCompletion(request); 414 } 415 416 void CommandObjectProxy::HandleArgumentCompletion( 417 CompletionRequest &request, OptionElementVector &opt_element_vector) { 418 CommandObject *proxy_command = GetProxyCommandObject(); 419 if (proxy_command) 420 proxy_command->HandleArgumentCompletion(request, opt_element_vector); 421 } 422 423 llvm::Optional<std::string> 424 CommandObjectProxy::GetRepeatCommand(Args ¤t_command_args, 425 uint32_t index) { 426 CommandObject *proxy_command = GetProxyCommandObject(); 427 if (proxy_command) 428 return proxy_command->GetRepeatCommand(current_command_args, index); 429 return llvm::None; 430 } 431 432 llvm::StringRef CommandObjectProxy::GetUnsupportedError() { 433 return "command is not implemented"; 434 } 435 436 bool CommandObjectProxy::Execute(const char *args_string, 437 CommandReturnObject &result) { 438 CommandObject *proxy_command = GetProxyCommandObject(); 439 if (proxy_command) 440 return proxy_command->Execute(args_string, result); 441 result.AppendError(GetUnsupportedError()); 442 return false; 443 } 444