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 //------------------------------------------------------------------------- 19 // CommandObjectMultiword 20 //------------------------------------------------------------------------- 21 22 CommandObjectMultiword::CommandObjectMultiword(CommandInterpreter &interpreter, 23 const char *name, 24 const char *help, 25 const char *syntax, 26 uint32_t flags) 27 : CommandObject(interpreter, name, help, syntax, flags), 28 m_can_be_removed(false) {} 29 30 CommandObjectMultiword::~CommandObjectMultiword() = default; 31 32 CommandObjectSP CommandObjectMultiword::GetSubcommandSP(llvm::StringRef sub_cmd, 33 StringList *matches) { 34 CommandObjectSP return_cmd_sp; 35 CommandObject::CommandMap::iterator pos; 36 37 if (!m_subcommand_dict.empty()) { 38 pos = m_subcommand_dict.find(sub_cmd); 39 if (pos != m_subcommand_dict.end()) { 40 // An exact match; append the sub_cmd to the 'matches' string list. 41 if (matches) 42 matches->AppendString(sub_cmd); 43 return_cmd_sp = pos->second; 44 } else { 45 StringList local_matches; 46 if (matches == nullptr) 47 matches = &local_matches; 48 int num_matches = 49 AddNamesMatchingPartialString(m_subcommand_dict, sub_cmd, *matches); 50 51 if (num_matches == 1) { 52 // Cleaner, but slightly less efficient would be to call back into this 53 // function, since I now know I have an exact match... 54 55 sub_cmd = matches->GetStringAtIndex(0); 56 pos = m_subcommand_dict.find(sub_cmd); 57 if (pos != m_subcommand_dict.end()) 58 return_cmd_sp = pos->second; 59 } 60 } 61 } 62 return return_cmd_sp; 63 } 64 65 CommandObject * 66 CommandObjectMultiword::GetSubcommandObject(llvm::StringRef sub_cmd, 67 StringList *matches) { 68 return GetSubcommandSP(sub_cmd, matches).get(); 69 } 70 71 bool CommandObjectMultiword::LoadSubCommand(llvm::StringRef name, 72 const CommandObjectSP &cmd_obj) { 73 if (cmd_obj) 74 assert((&GetCommandInterpreter() == &cmd_obj->GetCommandInterpreter()) && 75 "tried to add a CommandObject from a different interpreter"); 76 77 CommandMap::iterator pos; 78 bool success = true; 79 80 pos = m_subcommand_dict.find(name); 81 if (pos == m_subcommand_dict.end()) { 82 m_subcommand_dict[name] = cmd_obj; 83 } else 84 success = false; 85 86 return success; 87 } 88 89 bool CommandObjectMultiword::Execute(const char *args_string, 90 CommandReturnObject &result) { 91 Args args(args_string); 92 const size_t argc = args.GetArgumentCount(); 93 if (argc == 0) { 94 this->CommandObject::GenerateHelpText(result); 95 return result.Succeeded(); 96 } 97 98 auto sub_command = args[0].ref; 99 if (sub_command.empty()) 100 return result.Succeeded(); 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 (size_t i = 0; i < matches.GetSize(); i++) { 142 error_msg.append("\n\t"); 143 error_msg.append(matches.GetStringAtIndex(i)); 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 int CommandObjectMultiword::HandleCompletion(CompletionRequest &request) { 185 // Any of the command matches will provide a complete word, otherwise the 186 // individual completers will override this. 187 request.SetWordComplete(true); 188 189 auto arg0 = request.GetParsedLine()[0].ref; 190 if (request.GetCursorIndex() == 0) { 191 StringList new_matches, descriptions; 192 AddNamesMatchingPartialString(m_subcommand_dict, arg0, new_matches, 193 &descriptions); 194 request.AddCompletions(new_matches, descriptions); 195 196 if (new_matches.GetSize() == 1 && 197 new_matches.GetStringAtIndex(0) != nullptr && 198 (arg0 == new_matches.GetStringAtIndex(0))) { 199 StringList temp_matches; 200 CommandObject *cmd_obj = GetSubcommandObject(arg0, &temp_matches); 201 if (cmd_obj != nullptr) { 202 if (request.GetParsedLine().GetArgumentCount() == 1) { 203 request.SetWordComplete(true); 204 } else { 205 request.GetParsedLine().Shift(); 206 request.SetCursorCharPosition(0); 207 request.GetParsedLine().AppendArgument(llvm::StringRef()); 208 return cmd_obj->HandleCompletion(request); 209 } 210 } 211 } 212 return new_matches.GetSize(); 213 } else { 214 StringList new_matches; 215 CommandObject *sub_command_object = GetSubcommandObject(arg0, &new_matches); 216 if (sub_command_object == nullptr) { 217 request.AddCompletions(new_matches); 218 return request.GetNumberOfMatches(); 219 } else { 220 // Remove the one match that we got from calling GetSubcommandObject. 221 new_matches.DeleteStringAtIndex(0); 222 request.AddCompletions(new_matches); 223 request.GetParsedLine().Shift(); 224 request.SetCursorIndex(request.GetCursorIndex() - 1); 225 return sub_command_object->HandleCompletion(request); 226 } 227 } 228 } 229 230 const char *CommandObjectMultiword::GetRepeatCommand(Args ¤t_command_args, 231 uint32_t index) { 232 index++; 233 if (current_command_args.GetArgumentCount() <= index) 234 return nullptr; 235 CommandObject *sub_command_object = 236 GetSubcommandObject(current_command_args[index].ref); 237 if (sub_command_object == nullptr) 238 return nullptr; 239 return sub_command_object->GetRepeatCommand(current_command_args, index); 240 } 241 242 void CommandObjectMultiword::AproposAllSubCommands(llvm::StringRef prefix, 243 llvm::StringRef search_word, 244 StringList &commands_found, 245 StringList &commands_help) { 246 CommandObject::CommandMap::const_iterator pos; 247 248 for (pos = m_subcommand_dict.begin(); pos != m_subcommand_dict.end(); ++pos) { 249 const char *command_name = pos->first.c_str(); 250 CommandObject *sub_cmd_obj = pos->second.get(); 251 StreamString complete_command_name; 252 253 complete_command_name << prefix << " " << command_name; 254 255 if (sub_cmd_obj->HelpTextContainsWord(search_word)) { 256 commands_found.AppendString(complete_command_name.GetString()); 257 commands_help.AppendString(sub_cmd_obj->GetHelp()); 258 } 259 260 if (sub_cmd_obj->IsMultiwordObject()) 261 sub_cmd_obj->AproposAllSubCommands(complete_command_name.GetString(), 262 search_word, commands_found, 263 commands_help); 264 } 265 } 266 267 CommandObjectProxy::CommandObjectProxy(CommandInterpreter &interpreter, 268 const char *name, const char *help, 269 const char *syntax, uint32_t flags) 270 : CommandObject(interpreter, name, help, syntax, flags) {} 271 272 CommandObjectProxy::~CommandObjectProxy() = default; 273 274 llvm::StringRef CommandObjectProxy::GetHelpLong() { 275 CommandObject *proxy_command = GetProxyCommandObject(); 276 if (proxy_command) 277 return proxy_command->GetHelpLong(); 278 return llvm::StringRef(); 279 } 280 281 bool CommandObjectProxy::IsRemovable() const { 282 const CommandObject *proxy_command = 283 const_cast<CommandObjectProxy *>(this)->GetProxyCommandObject(); 284 if (proxy_command) 285 return proxy_command->IsRemovable(); 286 return false; 287 } 288 289 bool CommandObjectProxy::IsMultiwordObject() { 290 CommandObject *proxy_command = GetProxyCommandObject(); 291 if (proxy_command) 292 return proxy_command->IsMultiwordObject(); 293 return false; 294 } 295 296 CommandObjectMultiword *CommandObjectProxy::GetAsMultiwordCommand() { 297 CommandObject *proxy_command = GetProxyCommandObject(); 298 if (proxy_command) 299 return proxy_command->GetAsMultiwordCommand(); 300 return nullptr; 301 } 302 303 void CommandObjectProxy::GenerateHelpText(Stream &result) { 304 CommandObject *proxy_command = GetProxyCommandObject(); 305 if (proxy_command) 306 return proxy_command->GenerateHelpText(result); 307 } 308 309 lldb::CommandObjectSP 310 CommandObjectProxy::GetSubcommandSP(llvm::StringRef sub_cmd, 311 StringList *matches) { 312 CommandObject *proxy_command = GetProxyCommandObject(); 313 if (proxy_command) 314 return proxy_command->GetSubcommandSP(sub_cmd, matches); 315 return lldb::CommandObjectSP(); 316 } 317 318 CommandObject *CommandObjectProxy::GetSubcommandObject(llvm::StringRef sub_cmd, 319 StringList *matches) { 320 CommandObject *proxy_command = GetProxyCommandObject(); 321 if (proxy_command) 322 return proxy_command->GetSubcommandObject(sub_cmd, matches); 323 return nullptr; 324 } 325 326 void CommandObjectProxy::AproposAllSubCommands(llvm::StringRef prefix, 327 llvm::StringRef search_word, 328 StringList &commands_found, 329 StringList &commands_help) { 330 CommandObject *proxy_command = GetProxyCommandObject(); 331 if (proxy_command) 332 return proxy_command->AproposAllSubCommands(prefix, search_word, 333 commands_found, commands_help); 334 } 335 336 bool CommandObjectProxy::LoadSubCommand( 337 llvm::StringRef cmd_name, const lldb::CommandObjectSP &command_sp) { 338 CommandObject *proxy_command = GetProxyCommandObject(); 339 if (proxy_command) 340 return proxy_command->LoadSubCommand(cmd_name, command_sp); 341 return false; 342 } 343 344 bool CommandObjectProxy::WantsRawCommandString() { 345 CommandObject *proxy_command = GetProxyCommandObject(); 346 if (proxy_command) 347 return proxy_command->WantsRawCommandString(); 348 return false; 349 } 350 351 bool CommandObjectProxy::WantsCompletion() { 352 CommandObject *proxy_command = GetProxyCommandObject(); 353 if (proxy_command) 354 return proxy_command->WantsCompletion(); 355 return false; 356 } 357 358 Options *CommandObjectProxy::GetOptions() { 359 CommandObject *proxy_command = GetProxyCommandObject(); 360 if (proxy_command) 361 return proxy_command->GetOptions(); 362 return nullptr; 363 } 364 365 int CommandObjectProxy::HandleCompletion(CompletionRequest &request) { 366 CommandObject *proxy_command = GetProxyCommandObject(); 367 if (proxy_command) 368 return proxy_command->HandleCompletion(request); 369 return 0; 370 } 371 372 int CommandObjectProxy::HandleArgumentCompletion( 373 CompletionRequest &request, OptionElementVector &opt_element_vector) { 374 CommandObject *proxy_command = GetProxyCommandObject(); 375 if (proxy_command) 376 return proxy_command->HandleArgumentCompletion(request, opt_element_vector); 377 return 0; 378 } 379 380 const char *CommandObjectProxy::GetRepeatCommand(Args ¤t_command_args, 381 uint32_t index) { 382 CommandObject *proxy_command = GetProxyCommandObject(); 383 if (proxy_command) 384 return proxy_command->GetRepeatCommand(current_command_args, index); 385 return nullptr; 386 } 387 388 bool CommandObjectProxy::Execute(const char *args_string, 389 CommandReturnObject &result) { 390 CommandObject *proxy_command = GetProxyCommandObject(); 391 if (proxy_command) 392 return proxy_command->Execute(args_string, result); 393 result.AppendError("command is not implemented"); 394 result.SetStatus(eReturnStatusFailed); 395 return false; 396 } 397