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