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