Jim Ingham | 767af88 | 2010-07-07 03:36:20 +0000 | [diff] [blame] | 1 | //===-- CommandObjectSource.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 | #include "CommandObjectCommands.h" |
| 11 | |
| 12 | // C Includes |
| 13 | // C++ Includes |
| 14 | // Other libraries and framework includes |
| 15 | // Project includes |
| 16 | #include "lldb/Interpreter/Args.h" |
| 17 | #include "lldb/Core/Debugger.h" |
| 18 | #include "lldb/Interpreter/CommandInterpreter.h" |
| 19 | #include "lldb/Interpreter/CommandReturnObject.h" |
| 20 | #include "lldb/Interpreter/Options.h" |
| 21 | |
| 22 | using namespace lldb; |
| 23 | using namespace lldb_private; |
| 24 | |
| 25 | const char *k_space_characters = "\t\n\v\f\r "; |
| 26 | |
| 27 | //------------------------------------------------------------------------- |
| 28 | // CommandObjectCommandsSource |
| 29 | //------------------------------------------------------------------------- |
| 30 | |
| 31 | class CommandObjectCommandsSource : public CommandObject |
| 32 | { |
| 33 | public: |
| 34 | CommandObjectCommandsSource() : |
| 35 | CommandObject ("commands source", |
| 36 | "Reads in debugger commands from the file <filename> and executes them.", |
| 37 | "command source <filename>") |
| 38 | { |
| 39 | } |
| 40 | |
| 41 | ~CommandObjectCommandsSource () |
| 42 | { |
| 43 | } |
| 44 | |
| 45 | bool |
| 46 | Execute |
| 47 | ( |
| 48 | CommandInterpreter &interpreter, |
| 49 | Args& args, |
| 50 | CommandReturnObject &result |
| 51 | ) |
| 52 | { |
| 53 | const int argc = args.GetArgumentCount(); |
| 54 | if (argc == 1) |
| 55 | { |
| 56 | const char *filename = args.GetArgumentAtIndex(0); |
| 57 | bool success = true; |
| 58 | |
| 59 | result.AppendMessageWithFormat ("Executing commands in '%s'.\n", filename); |
| 60 | |
| 61 | FileSpec cmd_file (filename); |
| 62 | if (cmd_file.Exists()) |
| 63 | { |
| 64 | STLStringArray commands; |
| 65 | success = cmd_file.ReadFileLines (commands); |
| 66 | |
| 67 | STLStringArray::iterator pos = commands.begin(); |
| 68 | |
| 69 | // Trim out any empty lines or lines that start with the comment |
| 70 | // char '#' |
| 71 | while (pos != commands.end()) |
| 72 | { |
| 73 | bool remove_string = false; |
| 74 | size_t non_space = pos->find_first_not_of (k_space_characters); |
| 75 | if (non_space == std::string::npos) |
| 76 | remove_string = true; // Empty line |
| 77 | else if ((*pos)[non_space] == '#') |
| 78 | remove_string = true; // Comment line that starts with '#' |
| 79 | |
| 80 | if (remove_string) |
| 81 | pos = commands.erase(pos); |
| 82 | else |
| 83 | ++pos; |
| 84 | } |
| 85 | |
| 86 | if (commands.size() > 0) |
| 87 | { |
| 88 | const size_t num_commands = commands.size(); |
| 89 | size_t i; |
| 90 | for (i = 0; i<num_commands; ++i) |
| 91 | { |
| 92 | result.GetOutputStream().Printf("%s %s\n", interpreter.GetPrompt(), commands[i].c_str()); |
| 93 | if (!interpreter.HandleCommand(commands[i].c_str(), false, result)) |
| 94 | break; |
| 95 | } |
| 96 | |
| 97 | if (i < num_commands) |
| 98 | { |
| 99 | result.AppendErrorWithFormat("Aborting source of '%s' after command '%s' failed.\n", |
| 100 | filename, commands[i].c_str()); |
| 101 | result.SetStatus (eReturnStatusSuccessFinishResult); |
| 102 | } |
| 103 | else |
| 104 | { |
| 105 | success = true; |
| 106 | result.SetStatus (eReturnStatusFailed); |
| 107 | } |
| 108 | } |
| 109 | } |
| 110 | else |
| 111 | { |
| 112 | result.AppendErrorWithFormat ("File '%s' does not exist.\n", filename); |
| 113 | result.SetStatus (eReturnStatusFailed); |
| 114 | success = false; |
| 115 | } |
| 116 | |
| 117 | if (success) |
| 118 | { |
| 119 | result.SetStatus (eReturnStatusSuccessFinishNoResult); |
| 120 | } |
| 121 | } |
| 122 | else |
| 123 | { |
| 124 | result.AppendErrorWithFormat("'%s' takes exactly one executable filename argument.\n", GetCommandName()); |
| 125 | result.SetStatus (eReturnStatusFailed); |
| 126 | } |
| 127 | return result.Succeeded(); |
| 128 | |
| 129 | } |
| 130 | }; |
| 131 | |
| 132 | #pragma mark CommandObjectCommandsAlias |
| 133 | //------------------------------------------------------------------------- |
| 134 | // CommandObjectCommandsAlias |
| 135 | //------------------------------------------------------------------------- |
| 136 | |
| 137 | class CommandObjectCommandsAlias : public CommandObject |
| 138 | { |
| 139 | public: |
| 140 | CommandObjectCommandsAlias () : |
| 141 | CommandObject ("commands alias", |
| 142 | "Allows users to define their own debugger command abbreviations.", |
Sean Callanan | a3aff73 | 2010-08-09 18:50:15 +0000 | [diff] [blame] | 143 | "commands alias <new_command> <old_command> [<options-for-aliased-command>]") |
Jim Ingham | 767af88 | 2010-07-07 03:36:20 +0000 | [diff] [blame] | 144 | { |
| 145 | SetHelpLong( |
| 146 | "'alias' allows the user to create a short-cut or abbreviation for long \n\ |
| 147 | commands, multi-word commands, and commands that take particular options. \n\ |
| 148 | Below are some simple examples of how one might use the 'alias' command: \n\ |
Sean Callanan | a3aff73 | 2010-08-09 18:50:15 +0000 | [diff] [blame] | 149 | \n 'command alias sc script' // Creates the abbreviation 'sc' for the 'script' \n\ |
| 150 | // command. \n\ |
| 151 | 'command alias bp breakpoint' // Creates the abbreviation 'bp' for the 'breakpoint' \n\ |
| 152 | // command. Since breakpoint commands are two-word \n\ |
| 153 | // commands, the user will still need to enter the \n\ |
| 154 | // second word after 'bp', e.g. 'bp enable' or \n\ |
| 155 | // 'bp delete'. \n\ |
| 156 | 'command alias bpi breakpoint list' // Creates the abbreviation 'bpi' for the \n\ |
| 157 | // two-word command 'breakpoint list'. \n\ |
Jim Ingham | 767af88 | 2010-07-07 03:36:20 +0000 | [diff] [blame] | 158 | \nAn alias can include some options for the command, with the values either \n\ |
| 159 | filled in at the time the alias is created, or specified as positional \n\ |
| 160 | arguments, to be filled in when the alias is invoked. The following example \n\ |
| 161 | shows how to create aliases with options: \n\ |
| 162 | \n\ |
Sean Callanan | a3aff73 | 2010-08-09 18:50:15 +0000 | [diff] [blame] | 163 | 'command alias bfl breakpoint set -f %1 -l %2' \n\ |
Jim Ingham | 767af88 | 2010-07-07 03:36:20 +0000 | [diff] [blame] | 164 | \nThis creates the abbreviation 'bfl' (for break-file-line), with the -f and -l \n\ |
| 165 | options already part of the alias. So if the user wants to set a breakpoint \n\ |
| 166 | by file and line without explicitly having to use the -f and -l options, the \n\ |
| 167 | user can now use 'bfl' instead. The '%1' and '%2' are positional placeholders \n\ |
| 168 | for the actual arguments that will be passed when the alias command is used. \n\ |
| 169 | The number in the placeholder refers to the position/order the actual value \n\ |
| 170 | occupies when the alias is used. So all the occurrences of '%1' in the alias \n\ |
| 171 | will be replaced with the first argument, all the occurrences of '%2' in the \n\ |
| 172 | alias will be replaced with the second argument, and so on. This also allows \n\ |
| 173 | actual arguments to be used multiple times within an alias (see 'process \n\ |
| 174 | launch' example below). So in the 'bfl' case, the actual file value will be \n\ |
| 175 | filled in with the first argument following 'bfl' and the actual line number \n\ |
| 176 | value will be filled in with the second argument. The user would use this \n\ |
| 177 | alias as follows: \n\ |
Sean Callanan | a3aff73 | 2010-08-09 18:50:15 +0000 | [diff] [blame] | 178 | \n (dbg) commands alias bfl breakpoint set -f %1 -l %2 \n\ |
| 179 | <... some time later ...> \n\ |
| 180 | (dbg) bfl my-file.c 137 \n\ |
Jim Ingham | 767af88 | 2010-07-07 03:36:20 +0000 | [diff] [blame] | 181 | \nThis would be the same as if the user had entered \n\ |
| 182 | 'breakpoint set -f my-file.c -l 137'. \n\ |
| 183 | \nAnother example: \n\ |
Sean Callanan | a3aff73 | 2010-08-09 18:50:15 +0000 | [diff] [blame] | 184 | \n (dbg) commands alias pltty process launch -s -o %1 -e %1 \n\ |
| 185 | (dbg) pltty /dev/tty0 \n\ |
| 186 | // becomes 'process launch -s -o /dev/tty0 -e /dev/tty0' \n\ |
Jim Ingham | 767af88 | 2010-07-07 03:36:20 +0000 | [diff] [blame] | 187 | \nIf the user always wanted to pass the same value to a particular option, the \n\ |
| 188 | alias could be defined with that value directly in the alias as a constant, \n\ |
| 189 | rather than using a positional placeholder: \n\ |
Sean Callanan | a3aff73 | 2010-08-09 18:50:15 +0000 | [diff] [blame] | 190 | \n commands alias bl3 breakpoint set -f %1 -l 3 // Always sets a breakpoint on line \n\ |
| 191 | // 3 of whatever file is indicated. \n"); |
Jim Ingham | 767af88 | 2010-07-07 03:36:20 +0000 | [diff] [blame] | 192 | |
| 193 | } |
| 194 | |
| 195 | ~CommandObjectCommandsAlias () |
| 196 | { |
| 197 | } |
| 198 | |
| 199 | |
| 200 | bool |
| 201 | Execute |
| 202 | ( |
| 203 | CommandInterpreter &interpreter, |
| 204 | Args& args, |
| 205 | CommandReturnObject &result |
| 206 | ) |
| 207 | { |
Greg Clayton | 54e7afa | 2010-07-09 20:39:50 +0000 | [diff] [blame] | 208 | const size_t argc = args.GetArgumentCount(); |
Jim Ingham | 767af88 | 2010-07-07 03:36:20 +0000 | [diff] [blame] | 209 | |
| 210 | if (argc < 2) |
Greg Clayton | 54e7afa | 2010-07-09 20:39:50 +0000 | [diff] [blame] | 211 | { |
Jim Ingham | 767af88 | 2010-07-07 03:36:20 +0000 | [diff] [blame] | 212 | result.AppendError ("'alias' requires at least two arguments"); |
| 213 | result.SetStatus (eReturnStatusFailed); |
| 214 | return false; |
Greg Clayton | 54e7afa | 2010-07-09 20:39:50 +0000 | [diff] [blame] | 215 | } |
Jim Ingham | 767af88 | 2010-07-07 03:36:20 +0000 | [diff] [blame] | 216 | |
| 217 | const std::string alias_command = args.GetArgumentAtIndex(0); |
| 218 | const std::string actual_command = args.GetArgumentAtIndex(1); |
| 219 | |
| 220 | args.Shift(); // Shift the alias command word off the argument vector. |
| 221 | args.Shift(); // Shift the old command word off the argument vector. |
| 222 | |
| 223 | // Verify that the command is alias'able, and get the appropriate command object. |
| 224 | |
| 225 | if (interpreter.CommandExists (alias_command.c_str())) |
| 226 | { |
| 227 | result.AppendErrorWithFormat ("'%s' is a permanent debugger command and cannot be redefined.\n", |
| 228 | alias_command.c_str()); |
| 229 | result.SetStatus (eReturnStatusFailed); |
| 230 | } |
| 231 | else |
| 232 | { |
| 233 | CommandObjectSP command_obj_sp(interpreter.GetCommandSPExact (actual_command.c_str(), true)); |
| 234 | CommandObjectSP subcommand_obj_sp; |
| 235 | bool use_subcommand = false; |
| 236 | if (command_obj_sp.get()) |
| 237 | { |
| 238 | CommandObject *cmd_obj = command_obj_sp.get(); |
Greg Clayton | 54e7afa | 2010-07-09 20:39:50 +0000 | [diff] [blame] | 239 | CommandObject *sub_cmd_obj = NULL; |
Jim Ingham | 767af88 | 2010-07-07 03:36:20 +0000 | [diff] [blame] | 240 | OptionArgVectorSP option_arg_vector_sp = OptionArgVectorSP (new OptionArgVector); |
| 241 | OptionArgVector *option_arg_vector = option_arg_vector_sp.get(); |
| 242 | |
| 243 | if (cmd_obj->IsMultiwordObject()) |
| 244 | { |
| 245 | if (argc >= 3) |
| 246 | { |
| 247 | const std::string sub_command = args.GetArgumentAtIndex(0); |
| 248 | assert (sub_command.length() != 0); |
| 249 | subcommand_obj_sp = |
| 250 | (((CommandObjectMultiword *) cmd_obj)->GetSubcommandSP (sub_command.c_str())); |
| 251 | if (subcommand_obj_sp.get()) |
| 252 | { |
| 253 | sub_cmd_obj = subcommand_obj_sp.get(); |
| 254 | use_subcommand = true; |
| 255 | args.Shift(); // Shift the sub_command word off the argument vector. |
| 256 | } |
| 257 | else |
| 258 | { |
| 259 | result.AppendErrorWithFormat ("Error occurred while attempting to look up command '%s %s'.\n", |
| 260 | alias_command.c_str(), sub_command.c_str()); |
| 261 | result.SetStatus (eReturnStatusFailed); |
| 262 | return false; |
| 263 | } |
| 264 | } |
| 265 | } |
| 266 | |
| 267 | // Verify & handle any options/arguments passed to the alias command |
| 268 | |
| 269 | if (args.GetArgumentCount () > 0) |
| 270 | { |
| 271 | if ((!use_subcommand && (cmd_obj->WantsRawCommandString())) |
| 272 | || (use_subcommand && (sub_cmd_obj->WantsRawCommandString()))) |
| 273 | { |
| 274 | result.AppendErrorWithFormat ("'%s' cannot be aliased with any options or arguments.\n", |
| 275 | (use_subcommand ? sub_cmd_obj->GetCommandName() |
| 276 | : cmd_obj->GetCommandName())); |
| 277 | result.SetStatus (eReturnStatusFailed); |
| 278 | return false; |
| 279 | } |
| 280 | |
| 281 | // options or arguments have been passed to the alias command, and must be |
| 282 | // verified & processed here. |
| 283 | if ((!use_subcommand && (cmd_obj->GetOptions() != NULL)) |
| 284 | || (use_subcommand && (sub_cmd_obj->GetOptions() != NULL))) |
| 285 | { |
| 286 | Options *options; |
| 287 | if (use_subcommand) |
| 288 | options = sub_cmd_obj->GetOptions(); |
| 289 | else |
| 290 | options = cmd_obj->GetOptions(); |
| 291 | options->ResetOptionValues (); |
| 292 | args.Unshift ("dummy_arg"); |
| 293 | args.ParseAliasOptions (*options, result, option_arg_vector); |
| 294 | args.Shift (); |
| 295 | if (result.Succeeded()) |
| 296 | options->VerifyPartialOptions (result); |
| 297 | if (!result.Succeeded()) |
| 298 | return false; |
| 299 | } |
| 300 | else |
| 301 | { |
Greg Clayton | 54e7afa | 2010-07-09 20:39:50 +0000 | [diff] [blame] | 302 | for (size_t i = 0; i < argc; ++i) |
Jim Ingham | 767af88 | 2010-07-07 03:36:20 +0000 | [diff] [blame] | 303 | option_arg_vector->push_back (OptionArgPair ("<argument>", |
| 304 | std::string (args.GetArgumentAtIndex (i)))); |
| 305 | } |
| 306 | } |
| 307 | |
| 308 | // Create the alias. |
| 309 | |
| 310 | if (interpreter.AliasExists (alias_command.c_str()) |
| 311 | || interpreter.UserCommandExists (alias_command.c_str())) |
| 312 | { |
| 313 | OptionArgVectorSP tmp_option_arg_sp (interpreter.GetAliasOptions (alias_command.c_str())); |
| 314 | if (tmp_option_arg_sp.get()) |
| 315 | { |
| 316 | if (option_arg_vector->size() == 0) |
| 317 | interpreter.RemoveAliasOptions (alias_command.c_str()); |
| 318 | } |
| 319 | result.AppendWarningWithFormat ("Overwriting existing definition for '%s'.\n", |
| 320 | alias_command.c_str()); |
| 321 | } |
| 322 | |
| 323 | if (use_subcommand) |
| 324 | interpreter.AddAlias (alias_command.c_str(), subcommand_obj_sp); |
| 325 | else |
| 326 | interpreter.AddAlias (alias_command.c_str(), command_obj_sp); |
| 327 | if (option_arg_vector->size() > 0) |
| 328 | interpreter.AddOrReplaceAliasOptions (alias_command.c_str(), option_arg_vector_sp); |
| 329 | result.SetStatus (eReturnStatusSuccessFinishNoResult); |
| 330 | } |
| 331 | else |
| 332 | { |
| 333 | result.AppendErrorWithFormat ("'%s' is not an existing command.\n", actual_command.c_str()); |
| 334 | result.SetStatus (eReturnStatusFailed); |
| 335 | } |
| 336 | } |
| 337 | |
| 338 | return result.Succeeded(); |
| 339 | } |
| 340 | }; |
| 341 | |
| 342 | #pragma mark CommandObjectCommandsUnalias |
| 343 | //------------------------------------------------------------------------- |
| 344 | // CommandObjectCommandsUnalias |
| 345 | //------------------------------------------------------------------------- |
| 346 | |
| 347 | class CommandObjectCommandsUnalias : public CommandObject |
| 348 | { |
| 349 | public: |
| 350 | CommandObjectCommandsUnalias () : |
| 351 | CommandObject ("commands unalias", |
| 352 | "Allows the user to remove/delete a user-defined command abbreviation.", |
| 353 | "unalias <alias-name-to-be-removed>") |
| 354 | { |
| 355 | } |
| 356 | |
| 357 | ~CommandObjectCommandsUnalias() |
| 358 | { |
| 359 | } |
| 360 | |
| 361 | |
| 362 | bool |
| 363 | Execute |
| 364 | ( |
| 365 | CommandInterpreter &interpreter, |
| 366 | Args& args, |
| 367 | CommandReturnObject &result |
| 368 | ) |
| 369 | { |
| 370 | CommandObject::CommandMap::iterator pos; |
| 371 | CommandObject *cmd_obj; |
| 372 | |
| 373 | if (args.GetArgumentCount() != 0) |
| 374 | { |
| 375 | const char *command_name = args.GetArgumentAtIndex(0); |
| 376 | cmd_obj = interpreter.GetCommandObject(command_name); |
| 377 | if (cmd_obj) |
| 378 | { |
| 379 | if (interpreter.CommandExists (command_name)) |
| 380 | { |
| 381 | result.AppendErrorWithFormat ("'%s' is a permanent debugger command and cannot be removed.\n", |
| 382 | command_name); |
| 383 | result.SetStatus (eReturnStatusFailed); |
| 384 | } |
| 385 | else |
| 386 | { |
| 387 | |
| 388 | if (interpreter.RemoveAlias (command_name) == false) |
| 389 | { |
| 390 | if (interpreter.AliasExists (command_name)) |
| 391 | result.AppendErrorWithFormat ("Error occurred while attempting to unalias '%s'.\n", |
| 392 | command_name); |
| 393 | else |
| 394 | result.AppendErrorWithFormat ("'%s' is not an existing alias.\n", command_name); |
| 395 | result.SetStatus (eReturnStatusFailed); |
| 396 | } |
| 397 | else |
| 398 | result.SetStatus (eReturnStatusSuccessFinishNoResult); |
| 399 | } |
| 400 | } |
| 401 | else |
| 402 | { |
| 403 | result.AppendErrorWithFormat ("'%s' is not a known command.\nTry 'help' to see a " |
| 404 | "current list of commands.\n", |
| 405 | command_name); |
| 406 | result.SetStatus (eReturnStatusFailed); |
| 407 | } |
| 408 | } |
| 409 | else |
| 410 | { |
| 411 | result.AppendError ("must call 'unalias' with a valid alias"); |
| 412 | result.SetStatus (eReturnStatusFailed); |
| 413 | } |
| 414 | |
| 415 | return result.Succeeded(); |
| 416 | } |
| 417 | }; |
| 418 | |
| 419 | #pragma mark CommandObjectMultiwordCommands |
| 420 | |
| 421 | //------------------------------------------------------------------------- |
| 422 | // CommandObjectMultiwordCommands |
| 423 | //------------------------------------------------------------------------- |
| 424 | |
| 425 | CommandObjectMultiwordCommands::CommandObjectMultiwordCommands (CommandInterpreter &interpreter) : |
| 426 | CommandObjectMultiword ("commands", |
| 427 | "Commands for managing the command interpreters commands", |
| 428 | "commands <subcommand> [<subcommand-options>]") |
| 429 | { |
| 430 | LoadSubCommand (interpreter, "source", CommandObjectSP (new CommandObjectCommandsSource ())); |
| 431 | LoadSubCommand (interpreter, "alias", CommandObjectSP (new CommandObjectCommandsAlias ())); |
| 432 | } |
| 433 | |
| 434 | CommandObjectMultiwordCommands::~CommandObjectMultiwordCommands () |
| 435 | { |
| 436 | } |
| 437 | |