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 void 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; 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; 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; 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; 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 } 218 219 void CommandObjectMultiword::GenerateHelpText(Stream &output_stream) { 220 // First time through here, generate the help text for the object and push it 221 // to the return result object as well 222 223 CommandObject::GenerateHelpText(output_stream); 224 output_stream.PutCString("\nThe following subcommands are supported:\n\n"); 225 226 CommandMap::iterator pos; 227 uint32_t max_len = FindLongestCommandWord(m_subcommand_dict); 228 229 if (max_len) 230 max_len += 4; // Indent the output by 4 spaces. 231 232 for (pos = m_subcommand_dict.begin(); pos != m_subcommand_dict.end(); ++pos) { 233 std::string indented_command(" "); 234 indented_command.append(pos->first); 235 if (pos->second->WantsRawCommandString()) { 236 std::string help_text(std::string(pos->second->GetHelp())); 237 help_text.append(" Expects 'raw' input (see 'help raw-input'.)"); 238 m_interpreter.OutputFormattedHelpText(output_stream, indented_command, 239 "--", help_text, max_len); 240 } else 241 m_interpreter.OutputFormattedHelpText(output_stream, indented_command, 242 "--", pos->second->GetHelp(), 243 max_len); 244 } 245 246 output_stream.PutCString("\nFor more help on any particular subcommand, type " 247 "'help <command> <subcommand>'.\n"); 248 } 249 250 void CommandObjectMultiword::HandleCompletion(CompletionRequest &request) { 251 auto arg0 = request.GetParsedLine()[0].ref(); 252 if (request.GetCursorIndex() == 0) { 253 StringList new_matches, descriptions; 254 AddNamesMatchingPartialString(m_subcommand_dict, arg0, new_matches, 255 &descriptions); 256 request.AddCompletions(new_matches, descriptions); 257 258 if (new_matches.GetSize() == 1 && 259 new_matches.GetStringAtIndex(0) != nullptr && 260 (arg0 == new_matches.GetStringAtIndex(0))) { 261 StringList temp_matches; 262 CommandObject *cmd_obj = GetSubcommandObject(arg0, &temp_matches); 263 if (cmd_obj != nullptr) { 264 if (request.GetParsedLine().GetArgumentCount() != 1) { 265 request.GetParsedLine().Shift(); 266 request.AppendEmptyArgument(); 267 cmd_obj->HandleCompletion(request); 268 } 269 } 270 } 271 return; 272 } 273 274 StringList new_matches; 275 CommandObject *sub_command_object = GetSubcommandObject(arg0, &new_matches); 276 277 // The subcommand is ambiguous. The completion isn't meaningful. 278 if (!sub_command_object) 279 return; 280 281 // Remove the one match that we got from calling GetSubcommandObject. 282 new_matches.DeleteStringAtIndex(0); 283 request.AddCompletions(new_matches); 284 request.ShiftArguments(); 285 sub_command_object->HandleCompletion(request); 286 } 287 288 std::optional<std::string> 289 CommandObjectMultiword::GetRepeatCommand(Args ¤t_command_args, 290 uint32_t index) { 291 index++; 292 if (current_command_args.GetArgumentCount() <= index) 293 return std::nullopt; 294 CommandObject *sub_command_object = 295 GetSubcommandObject(current_command_args[index].ref()); 296 if (sub_command_object == nullptr) 297 return std::nullopt; 298 return sub_command_object->GetRepeatCommand(current_command_args, index); 299 } 300 301 CommandObjectProxy::CommandObjectProxy(CommandInterpreter &interpreter, 302 const char *name, const char *help, 303 const char *syntax, uint32_t flags) 304 : CommandObject(interpreter, name, help, syntax, flags) {} 305 306 CommandObjectProxy::~CommandObjectProxy() = default; 307 308 Options *CommandObjectProxy::GetOptions() { 309 CommandObject *proxy_command = GetProxyCommandObject(); 310 if (proxy_command) 311 return proxy_command->GetOptions(); 312 return CommandObject::GetOptions(); 313 } 314 315 llvm::StringRef CommandObjectProxy::GetHelp() { 316 CommandObject *proxy_command = GetProxyCommandObject(); 317 if (proxy_command) 318 return proxy_command->GetHelp(); 319 return CommandObject::GetHelp(); 320 } 321 322 llvm::StringRef CommandObjectProxy::GetSyntax() { 323 CommandObject *proxy_command = GetProxyCommandObject(); 324 if (proxy_command) 325 return proxy_command->GetSyntax(); 326 return CommandObject::GetSyntax(); 327 } 328 329 llvm::StringRef CommandObjectProxy::GetHelpLong() { 330 CommandObject *proxy_command = GetProxyCommandObject(); 331 if (proxy_command) 332 return proxy_command->GetHelpLong(); 333 return CommandObject::GetHelpLong(); 334 } 335 336 bool CommandObjectProxy::IsRemovable() const { 337 const CommandObject *proxy_command = 338 const_cast<CommandObjectProxy *>(this)->GetProxyCommandObject(); 339 if (proxy_command) 340 return proxy_command->IsRemovable(); 341 return false; 342 } 343 344 bool CommandObjectProxy::IsMultiwordObject() { 345 CommandObject *proxy_command = GetProxyCommandObject(); 346 if (proxy_command) 347 return proxy_command->IsMultiwordObject(); 348 return false; 349 } 350 351 CommandObjectMultiword *CommandObjectProxy::GetAsMultiwordCommand() { 352 CommandObject *proxy_command = GetProxyCommandObject(); 353 if (proxy_command) 354 return proxy_command->GetAsMultiwordCommand(); 355 return nullptr; 356 } 357 358 void CommandObjectProxy::GenerateHelpText(Stream &result) { 359 CommandObject *proxy_command = GetProxyCommandObject(); 360 if (proxy_command) 361 proxy_command->GenerateHelpText(result); 362 else 363 CommandObject::GenerateHelpText(result); 364 } 365 366 lldb::CommandObjectSP 367 CommandObjectProxy::GetSubcommandSP(llvm::StringRef sub_cmd, 368 StringList *matches) { 369 CommandObject *proxy_command = GetProxyCommandObject(); 370 if (proxy_command) 371 return proxy_command->GetSubcommandSP(sub_cmd, matches); 372 return lldb::CommandObjectSP(); 373 } 374 375 CommandObject *CommandObjectProxy::GetSubcommandObject(llvm::StringRef sub_cmd, 376 StringList *matches) { 377 CommandObject *proxy_command = GetProxyCommandObject(); 378 if (proxy_command) 379 return proxy_command->GetSubcommandObject(sub_cmd, matches); 380 return nullptr; 381 } 382 383 bool CommandObjectProxy::LoadSubCommand( 384 llvm::StringRef cmd_name, const lldb::CommandObjectSP &command_sp) { 385 CommandObject *proxy_command = GetProxyCommandObject(); 386 if (proxy_command) 387 return proxy_command->LoadSubCommand(cmd_name, command_sp); 388 return false; 389 } 390 391 bool CommandObjectProxy::WantsRawCommandString() { 392 CommandObject *proxy_command = GetProxyCommandObject(); 393 if (proxy_command) 394 return proxy_command->WantsRawCommandString(); 395 return false; 396 } 397 398 bool CommandObjectProxy::WantsCompletion() { 399 CommandObject *proxy_command = GetProxyCommandObject(); 400 if (proxy_command) 401 return proxy_command->WantsCompletion(); 402 return false; 403 } 404 405 void CommandObjectProxy::HandleCompletion(CompletionRequest &request) { 406 CommandObject *proxy_command = GetProxyCommandObject(); 407 if (proxy_command) 408 proxy_command->HandleCompletion(request); 409 } 410 411 void CommandObjectProxy::HandleArgumentCompletion( 412 CompletionRequest &request, OptionElementVector &opt_element_vector) { 413 CommandObject *proxy_command = GetProxyCommandObject(); 414 if (proxy_command) 415 proxy_command->HandleArgumentCompletion(request, opt_element_vector); 416 } 417 418 std::optional<std::string> 419 CommandObjectProxy::GetRepeatCommand(Args ¤t_command_args, 420 uint32_t index) { 421 CommandObject *proxy_command = GetProxyCommandObject(); 422 if (proxy_command) 423 return proxy_command->GetRepeatCommand(current_command_args, index); 424 return std::nullopt; 425 } 426 427 llvm::StringRef CommandObjectProxy::GetUnsupportedError() { 428 return "command is not implemented"; 429 } 430 431 void CommandObjectProxy::Execute(const char *args_string, 432 CommandReturnObject &result) { 433 if (CommandObject *proxy_command = GetProxyCommandObject()) 434 proxy_command->Execute(args_string, result); 435 else 436 result.AppendError(GetUnsupportedError()); 437 } 438