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