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 return result.Succeeded(); 99 100 if (sub_command.equals_lower("help")) { 101 this->CommandObject::GenerateHelpText(result); 102 return result.Succeeded(); 103 } 104 105 if (m_subcommand_dict.empty()) { 106 result.AppendErrorWithFormat("'%s' does not have any subcommands.\n", 107 GetCommandName().str().c_str()); 108 result.SetStatus(eReturnStatusFailed); 109 return false; 110 } 111 112 StringList matches; 113 CommandObject *sub_cmd_obj = GetSubcommandObject(sub_command, &matches); 114 if (sub_cmd_obj != nullptr) { 115 // Now call CommandObject::Execute to process options in `rest_of_line`. 116 // From there the command-specific version of Execute will be called, with 117 // the processed arguments. 118 119 args.Shift(); 120 sub_cmd_obj->Execute(args_string, result); 121 return result.Succeeded(); 122 } 123 124 std::string error_msg; 125 const size_t num_subcmd_matches = matches.GetSize(); 126 if (num_subcmd_matches > 0) 127 error_msg.assign("ambiguous command "); 128 else 129 error_msg.assign("invalid command "); 130 131 error_msg.append("'"); 132 error_msg.append(GetCommandName()); 133 error_msg.append(" "); 134 error_msg.append(sub_command); 135 error_msg.append("'."); 136 137 if (num_subcmd_matches > 0) { 138 error_msg.append(" Possible completions:"); 139 for (size_t i = 0; i < matches.GetSize(); i++) { 140 error_msg.append("\n\t"); 141 error_msg.append(matches.GetStringAtIndex(i)); 142 } 143 } 144 error_msg.append("\n"); 145 result.AppendRawError(error_msg.c_str()); 146 result.SetStatus(eReturnStatusFailed); 147 return false; 148 } 149 150 void CommandObjectMultiword::GenerateHelpText(Stream &output_stream) { 151 // First time through here, generate the help text for the object and push it 152 // to the return result object as well 153 154 CommandObject::GenerateHelpText(output_stream); 155 output_stream.PutCString("\nThe following subcommands are supported:\n\n"); 156 157 CommandMap::iterator pos; 158 uint32_t max_len = FindLongestCommandWord(m_subcommand_dict); 159 160 if (max_len) 161 max_len += 4; // Indent the output by 4 spaces. 162 163 for (pos = m_subcommand_dict.begin(); pos != m_subcommand_dict.end(); ++pos) { 164 std::string indented_command(" "); 165 indented_command.append(pos->first); 166 if (pos->second->WantsRawCommandString()) { 167 std::string help_text(pos->second->GetHelp()); 168 help_text.append(" Expects 'raw' input (see 'help raw-input'.)"); 169 m_interpreter.OutputFormattedHelpText(output_stream, 170 indented_command.c_str(), "--", 171 help_text.c_str(), max_len); 172 } else 173 m_interpreter.OutputFormattedHelpText(output_stream, 174 indented_command.c_str(), "--", 175 pos->second->GetHelp(), max_len); 176 } 177 178 output_stream.PutCString("\nFor more help on any particular subcommand, type " 179 "'help <command> <subcommand>'.\n"); 180 } 181 182 int CommandObjectMultiword::HandleCompletion(CompletionRequest &request) { 183 // Any of the command matches will provide a complete word, otherwise the 184 // individual completers will override this. 185 request.SetWordComplete(true); 186 187 auto arg0 = request.GetParsedLine()[0].ref; 188 if (request.GetCursorIndex() == 0) { 189 StringList new_matches, descriptions; 190 AddNamesMatchingPartialString(m_subcommand_dict, arg0, new_matches, 191 &descriptions); 192 request.AddCompletions(new_matches, descriptions); 193 194 if (new_matches.GetSize() == 1 && 195 new_matches.GetStringAtIndex(0) != nullptr && 196 (arg0 == new_matches.GetStringAtIndex(0))) { 197 StringList temp_matches; 198 CommandObject *cmd_obj = GetSubcommandObject(arg0, &temp_matches); 199 if (cmd_obj != nullptr) { 200 if (request.GetParsedLine().GetArgumentCount() == 1) { 201 request.SetWordComplete(true); 202 } else { 203 request.GetParsedLine().Shift(); 204 request.SetCursorCharPosition(0); 205 request.GetParsedLine().AppendArgument(llvm::StringRef()); 206 return cmd_obj->HandleCompletion(request); 207 } 208 } 209 } 210 return new_matches.GetSize(); 211 } else { 212 StringList new_matches; 213 CommandObject *sub_command_object = GetSubcommandObject(arg0, &new_matches); 214 if (sub_command_object == nullptr) { 215 request.AddCompletions(new_matches); 216 return request.GetNumberOfMatches(); 217 } else { 218 // Remove the one match that we got from calling GetSubcommandObject. 219 new_matches.DeleteStringAtIndex(0); 220 request.AddCompletions(new_matches); 221 request.GetParsedLine().Shift(); 222 request.SetCursorIndex(request.GetCursorIndex() - 1); 223 return sub_command_object->HandleCompletion(request); 224 } 225 } 226 } 227 228 const char *CommandObjectMultiword::GetRepeatCommand(Args ¤t_command_args, 229 uint32_t index) { 230 index++; 231 if (current_command_args.GetArgumentCount() <= index) 232 return nullptr; 233 CommandObject *sub_command_object = 234 GetSubcommandObject(current_command_args[index].ref); 235 if (sub_command_object == nullptr) 236 return nullptr; 237 return sub_command_object->GetRepeatCommand(current_command_args, index); 238 } 239 240 void CommandObjectMultiword::AproposAllSubCommands(llvm::StringRef prefix, 241 llvm::StringRef search_word, 242 StringList &commands_found, 243 StringList &commands_help) { 244 CommandObject::CommandMap::const_iterator pos; 245 246 for (pos = m_subcommand_dict.begin(); pos != m_subcommand_dict.end(); ++pos) { 247 const char *command_name = pos->first.c_str(); 248 CommandObject *sub_cmd_obj = pos->second.get(); 249 StreamString complete_command_name; 250 251 complete_command_name << prefix << " " << command_name; 252 253 if (sub_cmd_obj->HelpTextContainsWord(search_word)) { 254 commands_found.AppendString(complete_command_name.GetString()); 255 commands_help.AppendString(sub_cmd_obj->GetHelp()); 256 } 257 258 if (sub_cmd_obj->IsMultiwordObject()) 259 sub_cmd_obj->AproposAllSubCommands(complete_command_name.GetString(), 260 search_word, commands_found, 261 commands_help); 262 } 263 } 264 265 CommandObjectProxy::CommandObjectProxy(CommandInterpreter &interpreter, 266 const char *name, const char *help, 267 const char *syntax, uint32_t flags) 268 : CommandObject(interpreter, name, help, syntax, flags) {} 269 270 CommandObjectProxy::~CommandObjectProxy() = default; 271 272 llvm::StringRef CommandObjectProxy::GetHelpLong() { 273 CommandObject *proxy_command = GetProxyCommandObject(); 274 if (proxy_command) 275 return proxy_command->GetHelpLong(); 276 return llvm::StringRef(); 277 } 278 279 bool CommandObjectProxy::IsRemovable() const { 280 const CommandObject *proxy_command = 281 const_cast<CommandObjectProxy *>(this)->GetProxyCommandObject(); 282 if (proxy_command) 283 return proxy_command->IsRemovable(); 284 return false; 285 } 286 287 bool CommandObjectProxy::IsMultiwordObject() { 288 CommandObject *proxy_command = GetProxyCommandObject(); 289 if (proxy_command) 290 return proxy_command->IsMultiwordObject(); 291 return false; 292 } 293 294 CommandObjectMultiword *CommandObjectProxy::GetAsMultiwordCommand() { 295 CommandObject *proxy_command = GetProxyCommandObject(); 296 if (proxy_command) 297 return proxy_command->GetAsMultiwordCommand(); 298 return nullptr; 299 } 300 301 void CommandObjectProxy::GenerateHelpText(Stream &result) { 302 CommandObject *proxy_command = GetProxyCommandObject(); 303 if (proxy_command) 304 return proxy_command->GenerateHelpText(result); 305 } 306 307 lldb::CommandObjectSP 308 CommandObjectProxy::GetSubcommandSP(llvm::StringRef sub_cmd, 309 StringList *matches) { 310 CommandObject *proxy_command = GetProxyCommandObject(); 311 if (proxy_command) 312 return proxy_command->GetSubcommandSP(sub_cmd, matches); 313 return lldb::CommandObjectSP(); 314 } 315 316 CommandObject *CommandObjectProxy::GetSubcommandObject(llvm::StringRef sub_cmd, 317 StringList *matches) { 318 CommandObject *proxy_command = GetProxyCommandObject(); 319 if (proxy_command) 320 return proxy_command->GetSubcommandObject(sub_cmd, matches); 321 return nullptr; 322 } 323 324 void CommandObjectProxy::AproposAllSubCommands(llvm::StringRef prefix, 325 llvm::StringRef search_word, 326 StringList &commands_found, 327 StringList &commands_help) { 328 CommandObject *proxy_command = GetProxyCommandObject(); 329 if (proxy_command) 330 return proxy_command->AproposAllSubCommands(prefix, search_word, 331 commands_found, commands_help); 332 } 333 334 bool CommandObjectProxy::LoadSubCommand( 335 llvm::StringRef cmd_name, const lldb::CommandObjectSP &command_sp) { 336 CommandObject *proxy_command = GetProxyCommandObject(); 337 if (proxy_command) 338 return proxy_command->LoadSubCommand(cmd_name, command_sp); 339 return false; 340 } 341 342 bool CommandObjectProxy::WantsRawCommandString() { 343 CommandObject *proxy_command = GetProxyCommandObject(); 344 if (proxy_command) 345 return proxy_command->WantsRawCommandString(); 346 return false; 347 } 348 349 bool CommandObjectProxy::WantsCompletion() { 350 CommandObject *proxy_command = GetProxyCommandObject(); 351 if (proxy_command) 352 return proxy_command->WantsCompletion(); 353 return false; 354 } 355 356 Options *CommandObjectProxy::GetOptions() { 357 CommandObject *proxy_command = GetProxyCommandObject(); 358 if (proxy_command) 359 return proxy_command->GetOptions(); 360 return nullptr; 361 } 362 363 int CommandObjectProxy::HandleCompletion(CompletionRequest &request) { 364 CommandObject *proxy_command = GetProxyCommandObject(); 365 if (proxy_command) 366 return proxy_command->HandleCompletion(request); 367 return 0; 368 } 369 370 int CommandObjectProxy::HandleArgumentCompletion( 371 CompletionRequest &request, OptionElementVector &opt_element_vector) { 372 CommandObject *proxy_command = GetProxyCommandObject(); 373 if (proxy_command) 374 return proxy_command->HandleArgumentCompletion(request, opt_element_vector); 375 return 0; 376 } 377 378 const char *CommandObjectProxy::GetRepeatCommand(Args ¤t_command_args, 379 uint32_t index) { 380 CommandObject *proxy_command = GetProxyCommandObject(); 381 if (proxy_command) 382 return proxy_command->GetRepeatCommand(current_command_args, index); 383 return nullptr; 384 } 385 386 bool CommandObjectProxy::Execute(const char *args_string, 387 CommandReturnObject &result) { 388 CommandObject *proxy_command = GetProxyCommandObject(); 389 if (proxy_command) 390 return proxy_command->Execute(args_string, result); 391 result.AppendError("command is not implemented"); 392 result.SetStatus(eReturnStatusFailed); 393 return false; 394 } 395