1 //===-- CommandObjectHelp.cpp -----------------------------------*- C++ -*-===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 10 // C Includes 11 // C++ Includes 12 // Other libraries and framework includes 13 // Project includes 14 #include "CommandObjectHelp.h" 15 #include "lldb/Interpreter/CommandObjectMultiword.h" 16 #include "lldb/Interpreter/CommandInterpreter.h" 17 #include "lldb/Interpreter/Options.h" 18 #include "lldb/Interpreter/CommandReturnObject.h" 19 20 using namespace lldb; 21 using namespace lldb_private; 22 23 //------------------------------------------------------------------------- 24 // CommandObjectHelp 25 //------------------------------------------------------------------------- 26 27 void 28 CommandObjectHelp::GenerateAdditionalHelpAvenuesMessage (Stream *s, 29 const char* command, 30 const char* prefix, 31 const char* subcommand, 32 bool include_apropos, 33 bool include_type_lookup) 34 { 35 if (s && command && *command) 36 { 37 s->Printf("'%s' is not a known command.\n", command); 38 s->Printf("Try '%shelp' to see a current list of commands.\n", prefix ? prefix : ""); 39 if (include_apropos) 40 { 41 s->Printf("Try '%sapropos %s' for a list of related commands.\n", 42 prefix ? prefix : "", subcommand ? subcommand : command); 43 } 44 if (include_type_lookup) 45 { 46 s->Printf("Try '%stype lookup %s' for information on types, methods, functions, modules, etc.", 47 prefix ? prefix : "", subcommand ? subcommand : command); 48 } 49 } 50 } 51 52 CommandObjectHelp::CommandObjectHelp(CommandInterpreter &interpreter) 53 : CommandObjectParsed(interpreter, "help", 54 "Show a list of all debugger commands, or give details about a specific command.", 55 "help [<cmd-name>]"), 56 m_options() 57 { 58 CommandArgumentEntry arg; 59 CommandArgumentData command_arg; 60 61 // Define the first (and only) variant of this arg. 62 command_arg.arg_type = eArgTypeCommandName; 63 command_arg.arg_repetition = eArgRepeatStar; 64 65 // There is only one variant this argument could be; put it into the argument entry. 66 arg.push_back (command_arg); 67 68 // Push the data for the first argument into the m_arguments vector. 69 m_arguments.push_back (arg); 70 } 71 72 CommandObjectHelp::~CommandObjectHelp() = default; 73 74 OptionDefinition 75 CommandObjectHelp::CommandOptions::g_option_table[] = 76 { 77 { LLDB_OPT_SET_ALL, false, "hide-aliases", 'a', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Hide aliases in the command list."}, 78 { LLDB_OPT_SET_ALL, false, "hide-user-commands", 'u', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Hide user-defined commands from the list."}, 79 { LLDB_OPT_SET_ALL, false, "show-hidden-commands", 'h', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Include commands prefixed with an underscore."}, 80 { 0, false, nullptr, 0, 0, nullptr, nullptr, 0, eArgTypeNone, nullptr } 81 }; 82 83 bool 84 CommandObjectHelp::DoExecute (Args& command, CommandReturnObject &result) 85 { 86 CommandObject::CommandMap::iterator pos; 87 CommandObject *cmd_obj; 88 const size_t argc = command.GetArgumentCount (); 89 90 // 'help' doesn't take any arguments, other than command names. If argc is 0, we show the user 91 // all commands (aliases and user commands if asked for). Otherwise every argument must be the name of a command or a sub-command. 92 if (argc == 0) 93 { 94 uint32_t cmd_types = CommandInterpreter::eCommandTypesBuiltin; 95 if (m_options.m_show_aliases) 96 cmd_types |= CommandInterpreter::eCommandTypesAliases; 97 if (m_options.m_show_user_defined) 98 cmd_types |= CommandInterpreter::eCommandTypesUserDef; 99 if (m_options.m_show_hidden) 100 cmd_types |= CommandInterpreter::eCommandTypesHidden; 101 102 result.SetStatus (eReturnStatusSuccessFinishNoResult); 103 m_interpreter.GetHelp (result, cmd_types); // General help 104 } 105 else 106 { 107 // Get command object for the first command argument. Only search built-in command dictionary. 108 StringList matches; 109 cmd_obj = m_interpreter.GetCommandObject (command.GetArgumentAtIndex (0), &matches); 110 bool is_alias_command = m_interpreter.AliasExists (command.GetArgumentAtIndex (0)); 111 std::string alias_name = command.GetArgumentAtIndex(0); 112 113 if (cmd_obj != nullptr) 114 { 115 StringList matches; 116 bool all_okay = true; 117 CommandObject *sub_cmd_obj = cmd_obj; 118 // Loop down through sub_command dictionaries until we find the command object that corresponds 119 // to the help command entered. 120 std::string sub_command; 121 for (size_t i = 1; i < argc && all_okay; ++i) 122 { 123 sub_command = command.GetArgumentAtIndex(i); 124 matches.Clear(); 125 if (sub_cmd_obj->IsAlias()) 126 sub_cmd_obj = ((CommandAlias*)sub_cmd_obj)->GetUnderlyingCommand().get(); 127 if (! sub_cmd_obj->IsMultiwordObject ()) 128 { 129 all_okay = false; 130 } 131 else 132 { 133 CommandObject *found_cmd; 134 found_cmd = sub_cmd_obj->GetSubcommandObject(sub_command.c_str(), &matches); 135 if (found_cmd == nullptr) 136 all_okay = false; 137 else if (matches.GetSize() > 1) 138 all_okay = false; 139 else 140 sub_cmd_obj = found_cmd; 141 } 142 } 143 144 if (!all_okay || (sub_cmd_obj == nullptr)) 145 { 146 std::string cmd_string; 147 command.GetCommandString (cmd_string); 148 if (matches.GetSize() >= 2) 149 { 150 StreamString s; 151 s.Printf ("ambiguous command %s", cmd_string.c_str()); 152 size_t num_matches = matches.GetSize(); 153 for (size_t match_idx = 0; match_idx < num_matches; match_idx++) 154 { 155 s.Printf ("\n\t%s", matches.GetStringAtIndex(match_idx)); 156 } 157 s.Printf ("\n"); 158 result.AppendError(s.GetData()); 159 result.SetStatus (eReturnStatusFailed); 160 return false; 161 } 162 else if (!sub_cmd_obj) 163 { 164 StreamString error_msg_stream; 165 GenerateAdditionalHelpAvenuesMessage(&error_msg_stream, 166 cmd_string.c_str(), 167 m_interpreter.GetCommandPrefix(), 168 sub_command.c_str()); 169 result.AppendErrorWithFormat("%s",error_msg_stream.GetData()); 170 result.SetStatus (eReturnStatusFailed); 171 return false; 172 } 173 else 174 { 175 GenerateAdditionalHelpAvenuesMessage(&result.GetOutputStream(), 176 cmd_string.c_str(), 177 m_interpreter.GetCommandPrefix(), 178 sub_command.c_str()); 179 result.GetOutputStream().Printf("\nThe closest match is '%s'. Help on it follows.\n\n", sub_cmd_obj->GetCommandName()); 180 } 181 } 182 183 sub_cmd_obj->GenerateHelpText(result); 184 185 if (is_alias_command) 186 { 187 StreamString sstr; 188 m_interpreter.GetAlias(alias_name.c_str())->GetAliasExpansion(sstr); 189 result.GetOutputStream().Printf ("\n'%s' is an abbreviation for %s\n", alias_name.c_str(), sstr.GetData()); 190 } 191 } 192 else if (matches.GetSize() > 0) 193 { 194 Stream &output_strm = result.GetOutputStream(); 195 output_strm.Printf("Help requested with ambiguous command name, possible completions:\n"); 196 const size_t match_count = matches.GetSize(); 197 for (size_t i = 0; i < match_count; i++) 198 { 199 output_strm.Printf("\t%s\n", matches.GetStringAtIndex(i)); 200 } 201 } 202 else 203 { 204 // Maybe the user is asking for help about a command argument rather than a command. 205 const CommandArgumentType arg_type = CommandObject::LookupArgumentName (command.GetArgumentAtIndex (0)); 206 if (arg_type != eArgTypeLastArg) 207 { 208 Stream &output_strm = result.GetOutputStream (); 209 CommandObject::GetArgumentHelp (output_strm, arg_type, m_interpreter); 210 result.SetStatus (eReturnStatusSuccessFinishNoResult); 211 } 212 else 213 { 214 StreamString error_msg_stream; 215 GenerateAdditionalHelpAvenuesMessage(&error_msg_stream, command.GetArgumentAtIndex(0), m_interpreter.GetCommandPrefix()); 216 result.AppendErrorWithFormat("%s",error_msg_stream.GetData()); 217 result.SetStatus (eReturnStatusFailed); 218 } 219 } 220 } 221 222 return result.Succeeded(); 223 } 224 225 int 226 CommandObjectHelp::HandleCompletion(Args &input, 227 int &cursor_index, 228 int &cursor_char_position, 229 int match_start_point, 230 int max_return_elements, 231 bool &word_complete, 232 StringList &matches) 233 { 234 // Return the completions of the commands in the help system: 235 if (cursor_index == 0) 236 { 237 return m_interpreter.HandleCompletionMatches (input, 238 cursor_index, 239 cursor_char_position, 240 match_start_point, 241 max_return_elements, 242 word_complete, 243 matches); 244 } 245 else 246 { 247 CommandObject *cmd_obj = m_interpreter.GetCommandObject (input.GetArgumentAtIndex(0)); 248 249 // The command that they are getting help on might be ambiguous, in which case we should complete that, 250 // otherwise complete with the command the user is getting help on... 251 252 if (cmd_obj) 253 { 254 input.Shift(); 255 cursor_index--; 256 return cmd_obj->HandleCompletion (input, 257 cursor_index, 258 cursor_char_position, 259 match_start_point, 260 max_return_elements, 261 word_complete, 262 matches); 263 } 264 else 265 { 266 return m_interpreter.HandleCompletionMatches (input, 267 cursor_index, 268 cursor_char_position, 269 match_start_point, 270 max_return_elements, 271 word_complete, 272 matches); 273 } 274 } 275 } 276