1 //===-- CommandObjectCommands.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 #include "llvm/ADT/StringRef.h" 14 15 // Project includes 16 #include "CommandObjectCommands.h" 17 #include "CommandObjectHelp.h" 18 #include "lldb/Core/Debugger.h" 19 #include "lldb/Core/IOHandler.h" 20 #include "lldb/Host/OptionParser.h" 21 #include "lldb/Interpreter/CommandHistory.h" 22 #include "lldb/Interpreter/CommandInterpreter.h" 23 #include "lldb/Interpreter/CommandObjectRegexCommand.h" 24 #include "lldb/Interpreter/CommandReturnObject.h" 25 #include "lldb/Interpreter/OptionArgParser.h" 26 #include "lldb/Interpreter/OptionValueBoolean.h" 27 #include "lldb/Interpreter/OptionValueString.h" 28 #include "lldb/Interpreter/OptionValueUInt64.h" 29 #include "lldb/Interpreter/Options.h" 30 #include "lldb/Interpreter/ScriptInterpreter.h" 31 #include "lldb/Utility/Args.h" 32 #include "lldb/Utility/StringList.h" 33 34 using namespace lldb; 35 using namespace lldb_private; 36 37 //------------------------------------------------------------------------- 38 // CommandObjectCommandsSource 39 //------------------------------------------------------------------------- 40 41 static constexpr OptionDefinition g_history_options[] = { 42 // clang-format off 43 { LLDB_OPT_SET_1, false, "count", 'c', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeUnsignedInteger, "How many history commands to print." }, 44 { LLDB_OPT_SET_1, false, "start-index", 's', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeUnsignedInteger, "Index at which to start printing history commands (or end to mean tail mode)." }, 45 { LLDB_OPT_SET_1, false, "end-index", 'e', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeUnsignedInteger, "Index at which to stop printing history commands." }, 46 { LLDB_OPT_SET_2, false, "clear", 'C', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeBoolean, "Clears the current command history." }, 47 // clang-format on 48 }; 49 50 class CommandObjectCommandsHistory : public CommandObjectParsed { 51 public: 52 CommandObjectCommandsHistory(CommandInterpreter &interpreter) 53 : CommandObjectParsed(interpreter, "command history", 54 "Dump the history of commands in this session.\n" 55 "Commands in the history list can be run again " 56 "using \"!<INDEX>\". \"!-<OFFSET>\" will re-run " 57 "the command that is <OFFSET> commands from the end" 58 " of the list (counting the current command).", 59 nullptr), 60 m_options() {} 61 62 ~CommandObjectCommandsHistory() override = default; 63 64 Options *GetOptions() override { return &m_options; } 65 66 protected: 67 class CommandOptions : public Options { 68 public: 69 CommandOptions() 70 : Options(), m_start_idx(0), m_stop_idx(0), m_count(0), m_clear(false) { 71 } 72 73 ~CommandOptions() override = default; 74 75 Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, 76 ExecutionContext *execution_context) override { 77 Status error; 78 const int short_option = m_getopt_table[option_idx].val; 79 80 switch (short_option) { 81 case 'c': 82 error = m_count.SetValueFromString(option_arg, eVarSetOperationAssign); 83 break; 84 case 's': 85 if (option_arg == "end") { 86 m_start_idx.SetCurrentValue(UINT64_MAX); 87 m_start_idx.SetOptionWasSet(); 88 } else 89 error = m_start_idx.SetValueFromString(option_arg, 90 eVarSetOperationAssign); 91 break; 92 case 'e': 93 error = 94 m_stop_idx.SetValueFromString(option_arg, eVarSetOperationAssign); 95 break; 96 case 'C': 97 m_clear.SetCurrentValue(true); 98 m_clear.SetOptionWasSet(); 99 break; 100 default: 101 error.SetErrorStringWithFormat("unrecognized option '%c'", 102 short_option); 103 break; 104 } 105 106 return error; 107 } 108 109 void OptionParsingStarting(ExecutionContext *execution_context) override { 110 m_start_idx.Clear(); 111 m_stop_idx.Clear(); 112 m_count.Clear(); 113 m_clear.Clear(); 114 } 115 116 llvm::ArrayRef<OptionDefinition> GetDefinitions() override { 117 return llvm::makeArrayRef(g_history_options); 118 } 119 120 // Instance variables to hold the values for command options. 121 122 OptionValueUInt64 m_start_idx; 123 OptionValueUInt64 m_stop_idx; 124 OptionValueUInt64 m_count; 125 OptionValueBoolean m_clear; 126 }; 127 128 bool DoExecute(Args &command, CommandReturnObject &result) override { 129 if (m_options.m_clear.GetCurrentValue() && 130 m_options.m_clear.OptionWasSet()) { 131 m_interpreter.GetCommandHistory().Clear(); 132 result.SetStatus(lldb::eReturnStatusSuccessFinishNoResult); 133 } else { 134 if (m_options.m_start_idx.OptionWasSet() && 135 m_options.m_stop_idx.OptionWasSet() && 136 m_options.m_count.OptionWasSet()) { 137 result.AppendError("--count, --start-index and --end-index cannot be " 138 "all specified in the same invocation"); 139 result.SetStatus(lldb::eReturnStatusFailed); 140 } else { 141 std::pair<bool, uint64_t> start_idx( 142 m_options.m_start_idx.OptionWasSet(), 143 m_options.m_start_idx.GetCurrentValue()); 144 std::pair<bool, uint64_t> stop_idx( 145 m_options.m_stop_idx.OptionWasSet(), 146 m_options.m_stop_idx.GetCurrentValue()); 147 std::pair<bool, uint64_t> count(m_options.m_count.OptionWasSet(), 148 m_options.m_count.GetCurrentValue()); 149 150 const CommandHistory &history(m_interpreter.GetCommandHistory()); 151 152 if (start_idx.first && start_idx.second == UINT64_MAX) { 153 if (count.first) { 154 start_idx.second = history.GetSize() - count.second; 155 stop_idx.second = history.GetSize() - 1; 156 } else if (stop_idx.first) { 157 start_idx.second = stop_idx.second; 158 stop_idx.second = history.GetSize() - 1; 159 } else { 160 start_idx.second = 0; 161 stop_idx.second = history.GetSize() - 1; 162 } 163 } else { 164 if (!start_idx.first && !stop_idx.first && !count.first) { 165 start_idx.second = 0; 166 stop_idx.second = history.GetSize() - 1; 167 } else if (start_idx.first) { 168 if (count.first) { 169 stop_idx.second = start_idx.second + count.second - 1; 170 } else if (!stop_idx.first) { 171 stop_idx.second = history.GetSize() - 1; 172 } 173 } else if (stop_idx.first) { 174 if (count.first) { 175 if (stop_idx.second >= count.second) 176 start_idx.second = stop_idx.second - count.second + 1; 177 else 178 start_idx.second = 0; 179 } 180 } else /* if (count.first) */ 181 { 182 start_idx.second = 0; 183 stop_idx.second = count.second - 1; 184 } 185 } 186 history.Dump(result.GetOutputStream(), start_idx.second, 187 stop_idx.second); 188 } 189 } 190 return result.Succeeded(); 191 } 192 193 CommandOptions m_options; 194 }; 195 196 //------------------------------------------------------------------------- 197 // CommandObjectCommandsSource 198 //------------------------------------------------------------------------- 199 200 static constexpr OptionDefinition g_source_options[] = { 201 // clang-format off 202 { LLDB_OPT_SET_ALL, false, "stop-on-error", 'e', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeBoolean, "If true, stop executing commands on error." }, 203 { LLDB_OPT_SET_ALL, false, "stop-on-continue", 'c', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeBoolean, "If true, stop executing commands on continue." }, 204 { LLDB_OPT_SET_ALL, false, "silent-run", 's', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeBoolean, "If true don't echo commands while executing." }, 205 // clang-format on 206 }; 207 208 class CommandObjectCommandsSource : public CommandObjectParsed { 209 public: 210 CommandObjectCommandsSource(CommandInterpreter &interpreter) 211 : CommandObjectParsed( 212 interpreter, "command source", 213 "Read and execute LLDB commands from the file <filename>.", 214 nullptr), 215 m_options() { 216 CommandArgumentEntry arg; 217 CommandArgumentData file_arg; 218 219 // Define the first (and only) variant of this arg. 220 file_arg.arg_type = eArgTypeFilename; 221 file_arg.arg_repetition = eArgRepeatPlain; 222 223 // There is only one variant this argument could be; put it into the 224 // argument entry. 225 arg.push_back(file_arg); 226 227 // Push the data for the first argument into the m_arguments vector. 228 m_arguments.push_back(arg); 229 } 230 231 ~CommandObjectCommandsSource() override = default; 232 233 const char *GetRepeatCommand(Args ¤t_command_args, 234 uint32_t index) override { 235 return ""; 236 } 237 238 int HandleArgumentCompletion( 239 CompletionRequest &request, 240 OptionElementVector &opt_element_vector) override { 241 CommandCompletions::InvokeCommonCompletionCallbacks( 242 GetCommandInterpreter(), CommandCompletions::eDiskFileCompletion, 243 request, nullptr); 244 return request.GetNumberOfMatches(); 245 } 246 247 Options *GetOptions() override { return &m_options; } 248 249 protected: 250 class CommandOptions : public Options { 251 public: 252 CommandOptions() 253 : Options(), m_stop_on_error(true), m_silent_run(false), 254 m_stop_on_continue(true) {} 255 256 ~CommandOptions() override = default; 257 258 Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, 259 ExecutionContext *execution_context) override { 260 Status error; 261 const int short_option = m_getopt_table[option_idx].val; 262 263 switch (short_option) { 264 case 'e': 265 error = m_stop_on_error.SetValueFromString(option_arg); 266 break; 267 268 case 'c': 269 error = m_stop_on_continue.SetValueFromString(option_arg); 270 break; 271 272 case 's': 273 error = m_silent_run.SetValueFromString(option_arg); 274 break; 275 276 default: 277 error.SetErrorStringWithFormat("unrecognized option '%c'", 278 short_option); 279 break; 280 } 281 282 return error; 283 } 284 285 void OptionParsingStarting(ExecutionContext *execution_context) override { 286 m_stop_on_error.Clear(); 287 m_silent_run.Clear(); 288 m_stop_on_continue.Clear(); 289 } 290 291 llvm::ArrayRef<OptionDefinition> GetDefinitions() override { 292 return llvm::makeArrayRef(g_source_options); 293 } 294 295 // Instance variables to hold the values for command options. 296 297 OptionValueBoolean m_stop_on_error; 298 OptionValueBoolean m_silent_run; 299 OptionValueBoolean m_stop_on_continue; 300 }; 301 302 bool DoExecute(Args &command, CommandReturnObject &result) override { 303 if (command.GetArgumentCount() != 1) { 304 result.AppendErrorWithFormat( 305 "'%s' takes exactly one executable filename argument.\n", 306 GetCommandName().str().c_str()); 307 result.SetStatus(eReturnStatusFailed); 308 return false; 309 } 310 311 FileSpec cmd_file(command[0].ref); 312 FileSystem::Instance().Resolve(cmd_file); 313 ExecutionContext *exe_ctx = nullptr; // Just use the default context. 314 315 // If any options were set, then use them 316 if (m_options.m_stop_on_error.OptionWasSet() || 317 m_options.m_silent_run.OptionWasSet() || 318 m_options.m_stop_on_continue.OptionWasSet()) { 319 // Use user set settings 320 CommandInterpreterRunOptions options; 321 options.SetStopOnContinue(m_options.m_stop_on_continue.GetCurrentValue()); 322 options.SetStopOnError(m_options.m_stop_on_error.GetCurrentValue()); 323 324 // Individual silent setting is override for global command echo settings. 325 if (m_options.m_silent_run.GetCurrentValue()) { 326 options.SetSilent(true); 327 } else { 328 options.SetPrintResults(true); 329 options.SetEchoCommands(m_interpreter.GetEchoCommands()); 330 options.SetEchoCommentCommands(m_interpreter.GetEchoCommentCommands()); 331 } 332 333 m_interpreter.HandleCommandsFromFile(cmd_file, exe_ctx, options, result); 334 } else { 335 // No options were set, inherit any settings from nested "command source" 336 // commands, or set to sane default settings... 337 CommandInterpreterRunOptions options; 338 m_interpreter.HandleCommandsFromFile(cmd_file, exe_ctx, options, result); 339 } 340 return result.Succeeded(); 341 } 342 343 CommandOptions m_options; 344 }; 345 346 #pragma mark CommandObjectCommandsAlias 347 //------------------------------------------------------------------------- 348 // CommandObjectCommandsAlias 349 //------------------------------------------------------------------------- 350 351 static constexpr OptionDefinition g_alias_options[] = { 352 // clang-format off 353 { LLDB_OPT_SET_ALL, false, "help", 'h', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeHelpText, "Help text for this command" }, 354 { LLDB_OPT_SET_ALL, false, "long-help", 'H', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeHelpText, "Long help text for this command" }, 355 // clang-format on 356 }; 357 358 static const char *g_python_command_instructions = 359 "Enter your Python command(s). Type 'DONE' to end.\n" 360 "You must define a Python function with this signature:\n" 361 "def my_command_impl(debugger, args, result, internal_dict):\n"; 362 363 class CommandObjectCommandsAlias : public CommandObjectRaw { 364 protected: 365 class CommandOptions : public OptionGroup { 366 public: 367 CommandOptions() : OptionGroup(), m_help(), m_long_help() {} 368 369 ~CommandOptions() override = default; 370 371 llvm::ArrayRef<OptionDefinition> GetDefinitions() override { 372 return llvm::makeArrayRef(g_alias_options); 373 } 374 375 Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_value, 376 ExecutionContext *execution_context) override { 377 Status error; 378 379 const int short_option = GetDefinitions()[option_idx].short_option; 380 std::string option_str(option_value); 381 382 switch (short_option) { 383 case 'h': 384 m_help.SetCurrentValue(option_str); 385 m_help.SetOptionWasSet(); 386 break; 387 388 case 'H': 389 m_long_help.SetCurrentValue(option_str); 390 m_long_help.SetOptionWasSet(); 391 break; 392 393 default: 394 error.SetErrorStringWithFormat("invalid short option character '%c'", 395 short_option); 396 break; 397 } 398 399 return error; 400 } 401 402 void OptionParsingStarting(ExecutionContext *execution_context) override { 403 m_help.Clear(); 404 m_long_help.Clear(); 405 } 406 407 OptionValueString m_help; 408 OptionValueString m_long_help; 409 }; 410 411 OptionGroupOptions m_option_group; 412 CommandOptions m_command_options; 413 414 public: 415 Options *GetOptions() override { return &m_option_group; } 416 417 CommandObjectCommandsAlias(CommandInterpreter &interpreter) 418 : CommandObjectRaw( 419 interpreter, "command alias", 420 "Define a custom command in terms of an existing command."), 421 m_option_group(), m_command_options() { 422 m_option_group.Append(&m_command_options); 423 m_option_group.Finalize(); 424 425 SetHelpLong( 426 "'alias' allows the user to create a short-cut or abbreviation for long \ 427 commands, multi-word commands, and commands that take particular options. \ 428 Below are some simple examples of how one might use the 'alias' command:" 429 R"( 430 431 (lldb) command alias sc script 432 433 Creates the abbreviation 'sc' for the 'script' command. 434 435 (lldb) command alias bp breakpoint 436 437 )" 438 " Creates the abbreviation 'bp' for the 'breakpoint' command. Since \ 439 breakpoint commands are two-word commands, the user would still need to \ 440 enter the second word after 'bp', e.g. 'bp enable' or 'bp delete'." 441 R"( 442 443 (lldb) command alias bpl breakpoint list 444 445 Creates the abbreviation 'bpl' for the two-word command 'breakpoint list'. 446 447 )" 448 "An alias can include some options for the command, with the values either \ 449 filled in at the time the alias is created, or specified as positional \ 450 arguments, to be filled in when the alias is invoked. The following example \ 451 shows how to create aliases with options:" 452 R"( 453 454 (lldb) command alias bfl breakpoint set -f %1 -l %2 455 456 )" 457 " Creates the abbreviation 'bfl' (for break-file-line), with the -f and -l \ 458 options already part of the alias. So if the user wants to set a breakpoint \ 459 by file and line without explicitly having to use the -f and -l options, the \ 460 user can now use 'bfl' instead. The '%1' and '%2' are positional placeholders \ 461 for the actual arguments that will be passed when the alias command is used. \ 462 The number in the placeholder refers to the position/order the actual value \ 463 occupies when the alias is used. All the occurrences of '%1' in the alias \ 464 will be replaced with the first argument, all the occurrences of '%2' in the \ 465 alias will be replaced with the second argument, and so on. This also allows \ 466 actual arguments to be used multiple times within an alias (see 'process \ 467 launch' example below)." 468 R"( 469 470 )" 471 "Note: the positional arguments must substitute as whole words in the resultant \ 472 command, so you can't at present do something like this to append the file extension \ 473 \".cpp\":" 474 R"( 475 476 (lldb) command alias bcppfl breakpoint set -f %1.cpp -l %2 477 478 )" 479 "For more complex aliasing, use the \"command regex\" command instead. In the \ 480 'bfl' case above, the actual file value will be filled in with the first argument \ 481 following 'bfl' and the actual line number value will be filled in with the second \ 482 argument. The user would use this alias as follows:" 483 R"( 484 485 (lldb) command alias bfl breakpoint set -f %1 -l %2 486 (lldb) bfl my-file.c 137 487 488 This would be the same as if the user had entered 'breakpoint set -f my-file.c -l 137'. 489 490 Another example: 491 492 (lldb) command alias pltty process launch -s -o %1 -e %1 493 (lldb) pltty /dev/tty0 494 495 Interpreted as 'process launch -s -o /dev/tty0 -e /dev/tty0' 496 497 )" 498 "If the user always wanted to pass the same value to a particular option, the \ 499 alias could be defined with that value directly in the alias as a constant, \ 500 rather than using a positional placeholder:" 501 R"( 502 503 (lldb) command alias bl3 breakpoint set -f %1 -l 3 504 505 Always sets a breakpoint on line 3 of whatever file is indicated.)"); 506 507 CommandArgumentEntry arg1; 508 CommandArgumentEntry arg2; 509 CommandArgumentEntry arg3; 510 CommandArgumentData alias_arg; 511 CommandArgumentData cmd_arg; 512 CommandArgumentData options_arg; 513 514 // Define the first (and only) variant of this arg. 515 alias_arg.arg_type = eArgTypeAliasName; 516 alias_arg.arg_repetition = eArgRepeatPlain; 517 518 // There is only one variant this argument could be; put it into the 519 // argument entry. 520 arg1.push_back(alias_arg); 521 522 // Define the first (and only) variant of this arg. 523 cmd_arg.arg_type = eArgTypeCommandName; 524 cmd_arg.arg_repetition = eArgRepeatPlain; 525 526 // There is only one variant this argument could be; put it into the 527 // argument entry. 528 arg2.push_back(cmd_arg); 529 530 // Define the first (and only) variant of this arg. 531 options_arg.arg_type = eArgTypeAliasOptions; 532 options_arg.arg_repetition = eArgRepeatOptional; 533 534 // There is only one variant this argument could be; put it into the 535 // argument entry. 536 arg3.push_back(options_arg); 537 538 // Push the data for the first argument into the m_arguments vector. 539 m_arguments.push_back(arg1); 540 m_arguments.push_back(arg2); 541 m_arguments.push_back(arg3); 542 } 543 544 ~CommandObjectCommandsAlias() override = default; 545 546 protected: 547 bool DoExecute(llvm::StringRef raw_command_line, 548 CommandReturnObject &result) override { 549 if (raw_command_line.empty()) { 550 result.AppendError("'command alias' requires at least two arguments"); 551 return false; 552 } 553 554 ExecutionContext exe_ctx = GetCommandInterpreter().GetExecutionContext(); 555 m_option_group.NotifyOptionParsingStarting(&exe_ctx); 556 557 OptionsWithRaw args_with_suffix(raw_command_line); 558 const char *remainder = args_with_suffix.GetRawPart().c_str(); 559 560 if (args_with_suffix.HasArgs()) 561 if (!ParseOptionsAndNotify(args_with_suffix.GetArgs(), result, 562 m_option_group, exe_ctx)) 563 return false; 564 565 llvm::StringRef raw_command_string(remainder); 566 Args args(raw_command_string); 567 568 if (args.GetArgumentCount() < 2) { 569 result.AppendError("'command alias' requires at least two arguments"); 570 result.SetStatus(eReturnStatusFailed); 571 return false; 572 } 573 574 // Get the alias command. 575 576 auto alias_command = args[0].ref; 577 if (alias_command.startswith("-")) { 578 result.AppendError("aliases starting with a dash are not supported"); 579 if (alias_command == "--help" || alias_command == "--long-help") { 580 result.AppendWarning("if trying to pass options to 'command alias' add " 581 "a -- at the end of the options"); 582 } 583 result.SetStatus(eReturnStatusFailed); 584 return false; 585 } 586 587 // Strip the new alias name off 'raw_command_string' (leave it on args, 588 // which gets passed to 'Execute', which does the stripping itself. 589 size_t pos = raw_command_string.find(alias_command); 590 if (pos == 0) { 591 raw_command_string = raw_command_string.substr(alias_command.size()); 592 pos = raw_command_string.find_first_not_of(' '); 593 if ((pos != std::string::npos) && (pos > 0)) 594 raw_command_string = raw_command_string.substr(pos); 595 } else { 596 result.AppendError("Error parsing command string. No alias created."); 597 result.SetStatus(eReturnStatusFailed); 598 return false; 599 } 600 601 // Verify that the command is alias-able. 602 if (m_interpreter.CommandExists(alias_command)) { 603 result.AppendErrorWithFormat( 604 "'%s' is a permanent debugger command and cannot be redefined.\n", 605 args[0].c_str()); 606 result.SetStatus(eReturnStatusFailed); 607 return false; 608 } 609 610 // Get CommandObject that is being aliased. The command name is read from 611 // the front of raw_command_string. raw_command_string is returned with the 612 // name of the command object stripped off the front. 613 llvm::StringRef original_raw_command_string = raw_command_string; 614 CommandObject *cmd_obj = 615 m_interpreter.GetCommandObjectForCommand(raw_command_string); 616 617 if (!cmd_obj) { 618 result.AppendErrorWithFormat("invalid command given to 'command alias'. " 619 "'%s' does not begin with a valid command." 620 " No alias created.", 621 original_raw_command_string.str().c_str()); 622 result.SetStatus(eReturnStatusFailed); 623 return false; 624 } else if (!cmd_obj->WantsRawCommandString()) { 625 // Note that args was initialized with the original command, and has not 626 // been updated to this point. Therefore can we pass it to the version of 627 // Execute that does not need/expect raw input in the alias. 628 return HandleAliasingNormalCommand(args, result); 629 } else { 630 return HandleAliasingRawCommand(alias_command, raw_command_string, 631 *cmd_obj, result); 632 } 633 return result.Succeeded(); 634 } 635 636 bool HandleAliasingRawCommand(llvm::StringRef alias_command, 637 llvm::StringRef raw_command_string, 638 CommandObject &cmd_obj, 639 CommandReturnObject &result) { 640 // Verify & handle any options/arguments passed to the alias command 641 642 OptionArgVectorSP option_arg_vector_sp = 643 OptionArgVectorSP(new OptionArgVector); 644 645 if (CommandObjectSP cmd_obj_sp = 646 m_interpreter.GetCommandSPExact(cmd_obj.GetCommandName(), false)) { 647 if (m_interpreter.AliasExists(alias_command) || 648 m_interpreter.UserCommandExists(alias_command)) { 649 result.AppendWarningWithFormat( 650 "Overwriting existing definition for '%s'.\n", 651 alias_command.str().c_str()); 652 } 653 if (CommandAlias *alias = m_interpreter.AddAlias( 654 alias_command, cmd_obj_sp, raw_command_string)) { 655 if (m_command_options.m_help.OptionWasSet()) 656 alias->SetHelp(m_command_options.m_help.GetCurrentValue()); 657 if (m_command_options.m_long_help.OptionWasSet()) 658 alias->SetHelpLong(m_command_options.m_long_help.GetCurrentValue()); 659 result.SetStatus(eReturnStatusSuccessFinishNoResult); 660 } else { 661 result.AppendError("Unable to create requested alias.\n"); 662 result.SetStatus(eReturnStatusFailed); 663 } 664 665 } else { 666 result.AppendError("Unable to create requested alias.\n"); 667 result.SetStatus(eReturnStatusFailed); 668 } 669 670 return result.Succeeded(); 671 } 672 673 bool HandleAliasingNormalCommand(Args &args, CommandReturnObject &result) { 674 size_t argc = args.GetArgumentCount(); 675 676 if (argc < 2) { 677 result.AppendError("'command alias' requires at least two arguments"); 678 result.SetStatus(eReturnStatusFailed); 679 return false; 680 } 681 682 // Save these in std::strings since we're going to shift them off. 683 const std::string alias_command(args[0].ref); 684 const std::string actual_command(args[1].ref); 685 686 args.Shift(); // Shift the alias command word off the argument vector. 687 args.Shift(); // Shift the old command word off the argument vector. 688 689 // Verify that the command is alias'able, and get the appropriate command 690 // object. 691 692 if (m_interpreter.CommandExists(alias_command)) { 693 result.AppendErrorWithFormat( 694 "'%s' is a permanent debugger command and cannot be redefined.\n", 695 alias_command.c_str()); 696 result.SetStatus(eReturnStatusFailed); 697 return false; 698 } 699 700 CommandObjectSP command_obj_sp( 701 m_interpreter.GetCommandSPExact(actual_command, true)); 702 CommandObjectSP subcommand_obj_sp; 703 bool use_subcommand = false; 704 if (!command_obj_sp) { 705 result.AppendErrorWithFormat("'%s' is not an existing command.\n", 706 actual_command.c_str()); 707 result.SetStatus(eReturnStatusFailed); 708 return false; 709 } 710 CommandObject *cmd_obj = command_obj_sp.get(); 711 CommandObject *sub_cmd_obj = nullptr; 712 OptionArgVectorSP option_arg_vector_sp = 713 OptionArgVectorSP(new OptionArgVector); 714 715 while (cmd_obj->IsMultiwordObject() && !args.empty()) { 716 auto sub_command = args[0].ref; 717 assert(!sub_command.empty()); 718 subcommand_obj_sp = cmd_obj->GetSubcommandSP(sub_command); 719 if (!subcommand_obj_sp) { 720 result.AppendErrorWithFormat( 721 "'%s' is not a valid sub-command of '%s'. " 722 "Unable to create alias.\n", 723 args[0].c_str(), actual_command.c_str()); 724 result.SetStatus(eReturnStatusFailed); 725 return false; 726 } 727 728 sub_cmd_obj = subcommand_obj_sp.get(); 729 use_subcommand = true; 730 args.Shift(); // Shift the sub_command word off the argument vector. 731 cmd_obj = sub_cmd_obj; 732 } 733 734 // Verify & handle any options/arguments passed to the alias command 735 736 std::string args_string; 737 738 if (!args.empty()) { 739 CommandObjectSP tmp_sp = 740 m_interpreter.GetCommandSPExact(cmd_obj->GetCommandName(), false); 741 if (use_subcommand) 742 tmp_sp = m_interpreter.GetCommandSPExact(sub_cmd_obj->GetCommandName(), 743 false); 744 745 args.GetCommandString(args_string); 746 } 747 748 if (m_interpreter.AliasExists(alias_command) || 749 m_interpreter.UserCommandExists(alias_command)) { 750 result.AppendWarningWithFormat( 751 "Overwriting existing definition for '%s'.\n", alias_command.c_str()); 752 } 753 754 if (CommandAlias *alias = m_interpreter.AddAlias( 755 alias_command, use_subcommand ? subcommand_obj_sp : command_obj_sp, 756 args_string)) { 757 if (m_command_options.m_help.OptionWasSet()) 758 alias->SetHelp(m_command_options.m_help.GetCurrentValue()); 759 if (m_command_options.m_long_help.OptionWasSet()) 760 alias->SetHelpLong(m_command_options.m_long_help.GetCurrentValue()); 761 result.SetStatus(eReturnStatusSuccessFinishNoResult); 762 } else { 763 result.AppendError("Unable to create requested alias.\n"); 764 result.SetStatus(eReturnStatusFailed); 765 return false; 766 } 767 768 return result.Succeeded(); 769 } 770 }; 771 772 #pragma mark CommandObjectCommandsUnalias 773 //------------------------------------------------------------------------- 774 // CommandObjectCommandsUnalias 775 //------------------------------------------------------------------------- 776 777 class CommandObjectCommandsUnalias : public CommandObjectParsed { 778 public: 779 CommandObjectCommandsUnalias(CommandInterpreter &interpreter) 780 : CommandObjectParsed( 781 interpreter, "command unalias", 782 "Delete one or more custom commands defined by 'command alias'.", 783 nullptr) { 784 CommandArgumentEntry arg; 785 CommandArgumentData alias_arg; 786 787 // Define the first (and only) variant of this arg. 788 alias_arg.arg_type = eArgTypeAliasName; 789 alias_arg.arg_repetition = eArgRepeatPlain; 790 791 // There is only one variant this argument could be; put it into the 792 // argument entry. 793 arg.push_back(alias_arg); 794 795 // Push the data for the first argument into the m_arguments vector. 796 m_arguments.push_back(arg); 797 } 798 799 ~CommandObjectCommandsUnalias() override = default; 800 801 protected: 802 bool DoExecute(Args &args, CommandReturnObject &result) override { 803 CommandObject::CommandMap::iterator pos; 804 CommandObject *cmd_obj; 805 806 if (args.empty()) { 807 result.AppendError("must call 'unalias' with a valid alias"); 808 result.SetStatus(eReturnStatusFailed); 809 return false; 810 } 811 812 auto command_name = args[0].ref; 813 cmd_obj = m_interpreter.GetCommandObject(command_name); 814 if (!cmd_obj) { 815 result.AppendErrorWithFormat( 816 "'%s' is not a known command.\nTry 'help' to see a " 817 "current list of commands.\n", 818 args[0].c_str()); 819 result.SetStatus(eReturnStatusFailed); 820 return false; 821 } 822 823 if (m_interpreter.CommandExists(command_name)) { 824 if (cmd_obj->IsRemovable()) { 825 result.AppendErrorWithFormat( 826 "'%s' is not an alias, it is a debugger command which can be " 827 "removed using the 'command delete' command.\n", 828 args[0].c_str()); 829 } else { 830 result.AppendErrorWithFormat( 831 "'%s' is a permanent debugger command and cannot be removed.\n", 832 args[0].c_str()); 833 } 834 result.SetStatus(eReturnStatusFailed); 835 return false; 836 } 837 838 if (!m_interpreter.RemoveAlias(command_name)) { 839 if (m_interpreter.AliasExists(command_name)) 840 result.AppendErrorWithFormat( 841 "Error occurred while attempting to unalias '%s'.\n", 842 args[0].c_str()); 843 else 844 result.AppendErrorWithFormat("'%s' is not an existing alias.\n", 845 args[0].c_str()); 846 result.SetStatus(eReturnStatusFailed); 847 return false; 848 } 849 850 result.SetStatus(eReturnStatusSuccessFinishNoResult); 851 return result.Succeeded(); 852 } 853 }; 854 855 #pragma mark CommandObjectCommandsDelete 856 //------------------------------------------------------------------------- 857 // CommandObjectCommandsDelete 858 //------------------------------------------------------------------------- 859 860 class CommandObjectCommandsDelete : public CommandObjectParsed { 861 public: 862 CommandObjectCommandsDelete(CommandInterpreter &interpreter) 863 : CommandObjectParsed( 864 interpreter, "command delete", 865 "Delete one or more custom commands defined by 'command regex'.", 866 nullptr) { 867 CommandArgumentEntry arg; 868 CommandArgumentData alias_arg; 869 870 // Define the first (and only) variant of this arg. 871 alias_arg.arg_type = eArgTypeCommandName; 872 alias_arg.arg_repetition = eArgRepeatPlain; 873 874 // There is only one variant this argument could be; put it into the 875 // argument entry. 876 arg.push_back(alias_arg); 877 878 // Push the data for the first argument into the m_arguments vector. 879 m_arguments.push_back(arg); 880 } 881 882 ~CommandObjectCommandsDelete() override = default; 883 884 protected: 885 bool DoExecute(Args &args, CommandReturnObject &result) override { 886 CommandObject::CommandMap::iterator pos; 887 888 if (args.empty()) { 889 result.AppendErrorWithFormat("must call '%s' with one or more valid user " 890 "defined regular expression command names", 891 GetCommandName().str().c_str()); 892 result.SetStatus(eReturnStatusFailed); 893 } 894 895 auto command_name = args[0].ref; 896 if (!m_interpreter.CommandExists(command_name)) { 897 StreamString error_msg_stream; 898 const bool generate_apropos = true; 899 const bool generate_type_lookup = false; 900 CommandObjectHelp::GenerateAdditionalHelpAvenuesMessage( 901 &error_msg_stream, command_name, llvm::StringRef(), llvm::StringRef(), 902 generate_apropos, generate_type_lookup); 903 result.AppendError(error_msg_stream.GetString()); 904 result.SetStatus(eReturnStatusFailed); 905 return false; 906 } 907 908 if (!m_interpreter.RemoveCommand(command_name)) { 909 result.AppendErrorWithFormat( 910 "'%s' is a permanent debugger command and cannot be removed.\n", 911 args[0].c_str()); 912 result.SetStatus(eReturnStatusFailed); 913 return false; 914 } 915 916 result.SetStatus(eReturnStatusSuccessFinishNoResult); 917 return true; 918 } 919 }; 920 921 //------------------------------------------------------------------------- 922 // CommandObjectCommandsAddRegex 923 //------------------------------------------------------------------------- 924 925 static constexpr OptionDefinition g_regex_options[] = { 926 // clang-format off 927 { LLDB_OPT_SET_1, false, "help" , 'h', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeNone, "The help text to display for this command." }, 928 { LLDB_OPT_SET_1, false, "syntax", 's', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeNone, "A syntax string showing the typical usage syntax." }, 929 // clang-format on 930 }; 931 932 #pragma mark CommandObjectCommandsAddRegex 933 934 class CommandObjectCommandsAddRegex : public CommandObjectParsed, 935 public IOHandlerDelegateMultiline { 936 public: 937 CommandObjectCommandsAddRegex(CommandInterpreter &interpreter) 938 : CommandObjectParsed( 939 interpreter, "command regex", "Define a custom command in terms of " 940 "existing commands by matching " 941 "regular expressions.", 942 "command regex <cmd-name> [s/<regex>/<subst>/ ...]"), 943 IOHandlerDelegateMultiline("", 944 IOHandlerDelegate::Completion::LLDBCommand), 945 m_options() { 946 SetHelpLong( 947 R"( 948 )" 949 "This command allows the user to create powerful regular expression commands \ 950 with substitutions. The regular expressions and substitutions are specified \ 951 using the regular expression substitution format of:" 952 R"( 953 954 s/<regex>/<subst>/ 955 956 )" 957 "<regex> is a regular expression that can use parenthesis to capture regular \ 958 expression input and substitute the captured matches in the output using %1 \ 959 for the first match, %2 for the second, and so on." 960 R"( 961 962 )" 963 "The regular expressions can all be specified on the command line if more than \ 964 one argument is provided. If just the command name is provided on the command \ 965 line, then the regular expressions and substitutions can be entered on separate \ 966 lines, followed by an empty line to terminate the command definition." 967 R"( 968 969 EXAMPLES 970 971 )" 972 "The following example will define a regular expression command named 'f' that \ 973 will call 'finish' if there are no arguments, or 'frame select <frame-idx>' if \ 974 a number follows 'f':" 975 R"( 976 977 (lldb) command regex f s/^$/finish/ 's/([0-9]+)/frame select %1/')"); 978 } 979 980 ~CommandObjectCommandsAddRegex() override = default; 981 982 protected: 983 void IOHandlerActivated(IOHandler &io_handler) override { 984 StreamFileSP output_sp(io_handler.GetOutputStreamFile()); 985 if (output_sp) { 986 output_sp->PutCString("Enter one of more sed substitution commands in " 987 "the form: 's/<regex>/<subst>/'.\nTerminate the " 988 "substitution list with an empty line.\n"); 989 output_sp->Flush(); 990 } 991 } 992 993 void IOHandlerInputComplete(IOHandler &io_handler, 994 std::string &data) override { 995 io_handler.SetIsDone(true); 996 if (m_regex_cmd_ap) { 997 StringList lines; 998 if (lines.SplitIntoLines(data)) { 999 const size_t num_lines = lines.GetSize(); 1000 bool check_only = false; 1001 for (size_t i = 0; i < num_lines; ++i) { 1002 llvm::StringRef bytes_strref(lines[i]); 1003 Status error = AppendRegexSubstitution(bytes_strref, check_only); 1004 if (error.Fail()) { 1005 if (!m_interpreter.GetDebugger() 1006 .GetCommandInterpreter() 1007 .GetBatchCommandMode()) { 1008 StreamSP out_stream = 1009 m_interpreter.GetDebugger().GetAsyncOutputStream(); 1010 out_stream->Printf("error: %s\n", error.AsCString()); 1011 } 1012 } 1013 } 1014 } 1015 if (m_regex_cmd_ap->HasRegexEntries()) { 1016 CommandObjectSP cmd_sp(m_regex_cmd_ap.release()); 1017 m_interpreter.AddCommand(cmd_sp->GetCommandName(), cmd_sp, true); 1018 } 1019 } 1020 } 1021 1022 bool DoExecute(Args &command, CommandReturnObject &result) override { 1023 const size_t argc = command.GetArgumentCount(); 1024 if (argc == 0) { 1025 result.AppendError("usage: 'command regex <command-name> " 1026 "[s/<regex1>/<subst1>/ s/<regex2>/<subst2>/ ...]'\n"); 1027 result.SetStatus(eReturnStatusFailed); 1028 return false; 1029 } 1030 1031 Status error; 1032 auto name = command[0].ref; 1033 m_regex_cmd_ap = llvm::make_unique<CommandObjectRegexCommand>( 1034 m_interpreter, name, m_options.GetHelp(), m_options.GetSyntax(), 10, 0, 1035 true); 1036 1037 if (argc == 1) { 1038 Debugger &debugger = m_interpreter.GetDebugger(); 1039 bool color_prompt = debugger.GetUseColor(); 1040 const bool multiple_lines = true; // Get multiple lines 1041 IOHandlerSP io_handler_sp(new IOHandlerEditline( 1042 debugger, IOHandler::Type::Other, 1043 "lldb-regex", // Name of input reader for history 1044 llvm::StringRef("> "), // Prompt 1045 llvm::StringRef(), // Continuation prompt 1046 multiple_lines, color_prompt, 1047 0, // Don't show line numbers 1048 *this)); 1049 1050 if (io_handler_sp) { 1051 debugger.PushIOHandler(io_handler_sp); 1052 result.SetStatus(eReturnStatusSuccessFinishNoResult); 1053 } 1054 } else { 1055 for (auto &entry : command.entries().drop_front()) { 1056 bool check_only = false; 1057 error = AppendRegexSubstitution(entry.ref, check_only); 1058 if (error.Fail()) 1059 break; 1060 } 1061 1062 if (error.Success()) { 1063 AddRegexCommandToInterpreter(); 1064 } 1065 } 1066 if (error.Fail()) { 1067 result.AppendError(error.AsCString()); 1068 result.SetStatus(eReturnStatusFailed); 1069 } 1070 1071 return result.Succeeded(); 1072 } 1073 1074 Status AppendRegexSubstitution(const llvm::StringRef ®ex_sed, 1075 bool check_only) { 1076 Status error; 1077 1078 if (!m_regex_cmd_ap) { 1079 error.SetErrorStringWithFormat( 1080 "invalid regular expression command object for: '%.*s'", 1081 (int)regex_sed.size(), regex_sed.data()); 1082 return error; 1083 } 1084 1085 size_t regex_sed_size = regex_sed.size(); 1086 1087 if (regex_sed_size <= 1) { 1088 error.SetErrorStringWithFormat( 1089 "regular expression substitution string is too short: '%.*s'", 1090 (int)regex_sed.size(), regex_sed.data()); 1091 return error; 1092 } 1093 1094 if (regex_sed[0] != 's') { 1095 error.SetErrorStringWithFormat("regular expression substitution string " 1096 "doesn't start with 's': '%.*s'", 1097 (int)regex_sed.size(), regex_sed.data()); 1098 return error; 1099 } 1100 const size_t first_separator_char_pos = 1; 1101 // use the char that follows 's' as the regex separator character so we can 1102 // have "s/<regex>/<subst>/" or "s|<regex>|<subst>|" 1103 const char separator_char = regex_sed[first_separator_char_pos]; 1104 const size_t second_separator_char_pos = 1105 regex_sed.find(separator_char, first_separator_char_pos + 1); 1106 1107 if (second_separator_char_pos == std::string::npos) { 1108 error.SetErrorStringWithFormat( 1109 "missing second '%c' separator char after '%.*s' in '%.*s'", 1110 separator_char, 1111 (int)(regex_sed.size() - first_separator_char_pos - 1), 1112 regex_sed.data() + (first_separator_char_pos + 1), 1113 (int)regex_sed.size(), regex_sed.data()); 1114 return error; 1115 } 1116 1117 const size_t third_separator_char_pos = 1118 regex_sed.find(separator_char, second_separator_char_pos + 1); 1119 1120 if (third_separator_char_pos == std::string::npos) { 1121 error.SetErrorStringWithFormat( 1122 "missing third '%c' separator char after '%.*s' in '%.*s'", 1123 separator_char, 1124 (int)(regex_sed.size() - second_separator_char_pos - 1), 1125 regex_sed.data() + (second_separator_char_pos + 1), 1126 (int)regex_sed.size(), regex_sed.data()); 1127 return error; 1128 } 1129 1130 if (third_separator_char_pos != regex_sed_size - 1) { 1131 // Make sure that everything that follows the last regex separator char 1132 if (regex_sed.find_first_not_of("\t\n\v\f\r ", 1133 third_separator_char_pos + 1) != 1134 std::string::npos) { 1135 error.SetErrorStringWithFormat( 1136 "extra data found after the '%.*s' regular expression substitution " 1137 "string: '%.*s'", 1138 (int)third_separator_char_pos + 1, regex_sed.data(), 1139 (int)(regex_sed.size() - third_separator_char_pos - 1), 1140 regex_sed.data() + (third_separator_char_pos + 1)); 1141 return error; 1142 } 1143 } else if (first_separator_char_pos + 1 == second_separator_char_pos) { 1144 error.SetErrorStringWithFormat( 1145 "<regex> can't be empty in 's%c<regex>%c<subst>%c' string: '%.*s'", 1146 separator_char, separator_char, separator_char, (int)regex_sed.size(), 1147 regex_sed.data()); 1148 return error; 1149 } else if (second_separator_char_pos + 1 == third_separator_char_pos) { 1150 error.SetErrorStringWithFormat( 1151 "<subst> can't be empty in 's%c<regex>%c<subst>%c' string: '%.*s'", 1152 separator_char, separator_char, separator_char, (int)regex_sed.size(), 1153 regex_sed.data()); 1154 return error; 1155 } 1156 1157 if (!check_only) { 1158 std::string regex(regex_sed.substr(first_separator_char_pos + 1, 1159 second_separator_char_pos - 1160 first_separator_char_pos - 1)); 1161 std::string subst(regex_sed.substr(second_separator_char_pos + 1, 1162 third_separator_char_pos - 1163 second_separator_char_pos - 1)); 1164 m_regex_cmd_ap->AddRegexCommand(regex.c_str(), subst.c_str()); 1165 } 1166 return error; 1167 } 1168 1169 void AddRegexCommandToInterpreter() { 1170 if (m_regex_cmd_ap) { 1171 if (m_regex_cmd_ap->HasRegexEntries()) { 1172 CommandObjectSP cmd_sp(m_regex_cmd_ap.release()); 1173 m_interpreter.AddCommand(cmd_sp->GetCommandName(), cmd_sp, true); 1174 } 1175 } 1176 } 1177 1178 private: 1179 std::unique_ptr<CommandObjectRegexCommand> m_regex_cmd_ap; 1180 1181 class CommandOptions : public Options { 1182 public: 1183 CommandOptions() : Options() {} 1184 1185 ~CommandOptions() override = default; 1186 1187 Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, 1188 ExecutionContext *execution_context) override { 1189 Status error; 1190 const int short_option = m_getopt_table[option_idx].val; 1191 1192 switch (short_option) { 1193 case 'h': 1194 m_help.assign(option_arg); 1195 break; 1196 case 's': 1197 m_syntax.assign(option_arg); 1198 break; 1199 default: 1200 error.SetErrorStringWithFormat("unrecognized option '%c'", 1201 short_option); 1202 break; 1203 } 1204 1205 return error; 1206 } 1207 1208 void OptionParsingStarting(ExecutionContext *execution_context) override { 1209 m_help.clear(); 1210 m_syntax.clear(); 1211 } 1212 1213 llvm::ArrayRef<OptionDefinition> GetDefinitions() override { 1214 return llvm::makeArrayRef(g_regex_options); 1215 } 1216 1217 // TODO: Convert these functions to return StringRefs. 1218 const char *GetHelp() { 1219 return (m_help.empty() ? nullptr : m_help.c_str()); 1220 } 1221 1222 const char *GetSyntax() { 1223 return (m_syntax.empty() ? nullptr : m_syntax.c_str()); 1224 } 1225 1226 protected: 1227 // Instance variables to hold the values for command options. 1228 1229 std::string m_help; 1230 std::string m_syntax; 1231 }; 1232 1233 Options *GetOptions() override { return &m_options; } 1234 1235 CommandOptions m_options; 1236 }; 1237 1238 class CommandObjectPythonFunction : public CommandObjectRaw { 1239 public: 1240 CommandObjectPythonFunction(CommandInterpreter &interpreter, std::string name, 1241 std::string funct, std::string help, 1242 ScriptedCommandSynchronicity synch) 1243 : CommandObjectRaw(interpreter, name), 1244 m_function_name(funct), m_synchro(synch), m_fetched_help_long(false) { 1245 if (!help.empty()) 1246 SetHelp(help); 1247 else { 1248 StreamString stream; 1249 stream.Printf("For more information run 'help %s'", name.c_str()); 1250 SetHelp(stream.GetString()); 1251 } 1252 } 1253 1254 ~CommandObjectPythonFunction() override = default; 1255 1256 bool IsRemovable() const override { return true; } 1257 1258 const std::string &GetFunctionName() { return m_function_name; } 1259 1260 ScriptedCommandSynchronicity GetSynchronicity() { return m_synchro; } 1261 1262 llvm::StringRef GetHelpLong() override { 1263 if (m_fetched_help_long) 1264 return CommandObjectRaw::GetHelpLong(); 1265 1266 ScriptInterpreter *scripter = m_interpreter.GetScriptInterpreter(); 1267 if (!scripter) 1268 return CommandObjectRaw::GetHelpLong(); 1269 1270 std::string docstring; 1271 m_fetched_help_long = 1272 scripter->GetDocumentationForItem(m_function_name.c_str(), docstring); 1273 if (!docstring.empty()) 1274 SetHelpLong(docstring); 1275 return CommandObjectRaw::GetHelpLong(); 1276 } 1277 1278 protected: 1279 bool DoExecute(llvm::StringRef raw_command_line, 1280 CommandReturnObject &result) override { 1281 ScriptInterpreter *scripter = m_interpreter.GetScriptInterpreter(); 1282 1283 Status error; 1284 1285 result.SetStatus(eReturnStatusInvalid); 1286 1287 if (!scripter || 1288 !scripter->RunScriptBasedCommand(m_function_name.c_str(), 1289 raw_command_line, m_synchro, result, 1290 error, m_exe_ctx)) { 1291 result.AppendError(error.AsCString()); 1292 result.SetStatus(eReturnStatusFailed); 1293 } else { 1294 // Don't change the status if the command already set it... 1295 if (result.GetStatus() == eReturnStatusInvalid) { 1296 if (result.GetOutputData().empty()) 1297 result.SetStatus(eReturnStatusSuccessFinishNoResult); 1298 else 1299 result.SetStatus(eReturnStatusSuccessFinishResult); 1300 } 1301 } 1302 1303 return result.Succeeded(); 1304 } 1305 1306 private: 1307 std::string m_function_name; 1308 ScriptedCommandSynchronicity m_synchro; 1309 bool m_fetched_help_long; 1310 }; 1311 1312 class CommandObjectScriptingObject : public CommandObjectRaw { 1313 public: 1314 CommandObjectScriptingObject(CommandInterpreter &interpreter, 1315 std::string name, 1316 StructuredData::GenericSP cmd_obj_sp, 1317 ScriptedCommandSynchronicity synch) 1318 : CommandObjectRaw(interpreter, name), 1319 m_cmd_obj_sp(cmd_obj_sp), m_synchro(synch), m_fetched_help_short(false), 1320 m_fetched_help_long(false) { 1321 StreamString stream; 1322 stream.Printf("For more information run 'help %s'", name.c_str()); 1323 SetHelp(stream.GetString()); 1324 if (ScriptInterpreter *scripter = m_interpreter.GetScriptInterpreter()) 1325 GetFlags().Set(scripter->GetFlagsForCommandObject(cmd_obj_sp)); 1326 } 1327 1328 ~CommandObjectScriptingObject() override = default; 1329 1330 bool IsRemovable() const override { return true; } 1331 1332 StructuredData::GenericSP GetImplementingObject() { return m_cmd_obj_sp; } 1333 1334 ScriptedCommandSynchronicity GetSynchronicity() { return m_synchro; } 1335 1336 llvm::StringRef GetHelp() override { 1337 if (m_fetched_help_short) 1338 return CommandObjectRaw::GetHelp(); 1339 ScriptInterpreter *scripter = m_interpreter.GetScriptInterpreter(); 1340 if (!scripter) 1341 return CommandObjectRaw::GetHelp(); 1342 std::string docstring; 1343 m_fetched_help_short = 1344 scripter->GetShortHelpForCommandObject(m_cmd_obj_sp, docstring); 1345 if (!docstring.empty()) 1346 SetHelp(docstring); 1347 1348 return CommandObjectRaw::GetHelp(); 1349 } 1350 1351 llvm::StringRef GetHelpLong() override { 1352 if (m_fetched_help_long) 1353 return CommandObjectRaw::GetHelpLong(); 1354 1355 ScriptInterpreter *scripter = m_interpreter.GetScriptInterpreter(); 1356 if (!scripter) 1357 return CommandObjectRaw::GetHelpLong(); 1358 1359 std::string docstring; 1360 m_fetched_help_long = 1361 scripter->GetLongHelpForCommandObject(m_cmd_obj_sp, docstring); 1362 if (!docstring.empty()) 1363 SetHelpLong(docstring); 1364 return CommandObjectRaw::GetHelpLong(); 1365 } 1366 1367 protected: 1368 bool DoExecute(llvm::StringRef raw_command_line, 1369 CommandReturnObject &result) override { 1370 ScriptInterpreter *scripter = m_interpreter.GetScriptInterpreter(); 1371 1372 Status error; 1373 1374 result.SetStatus(eReturnStatusInvalid); 1375 1376 if (!scripter || 1377 !scripter->RunScriptBasedCommand(m_cmd_obj_sp, raw_command_line, 1378 m_synchro, result, error, m_exe_ctx)) { 1379 result.AppendError(error.AsCString()); 1380 result.SetStatus(eReturnStatusFailed); 1381 } else { 1382 // Don't change the status if the command already set it... 1383 if (result.GetStatus() == eReturnStatusInvalid) { 1384 if (result.GetOutputData().empty()) 1385 result.SetStatus(eReturnStatusSuccessFinishNoResult); 1386 else 1387 result.SetStatus(eReturnStatusSuccessFinishResult); 1388 } 1389 } 1390 1391 return result.Succeeded(); 1392 } 1393 1394 private: 1395 StructuredData::GenericSP m_cmd_obj_sp; 1396 ScriptedCommandSynchronicity m_synchro; 1397 bool m_fetched_help_short : 1; 1398 bool m_fetched_help_long : 1; 1399 }; 1400 1401 //------------------------------------------------------------------------- 1402 // CommandObjectCommandsScriptImport 1403 //------------------------------------------------------------------------- 1404 1405 static constexpr OptionDefinition g_script_import_options[] = { 1406 // clang-format off 1407 { LLDB_OPT_SET_1, false, "allow-reload", 'r', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Allow the script to be loaded even if it was already loaded before. This argument exists for backwards compatibility, but reloading is always allowed, whether you specify it or not." }, 1408 // clang-format on 1409 }; 1410 1411 class CommandObjectCommandsScriptImport : public CommandObjectParsed { 1412 public: 1413 CommandObjectCommandsScriptImport(CommandInterpreter &interpreter) 1414 : CommandObjectParsed(interpreter, "command script import", 1415 "Import a scripting module in LLDB.", nullptr), 1416 m_options() { 1417 CommandArgumentEntry arg1; 1418 CommandArgumentData cmd_arg; 1419 1420 // Define the first (and only) variant of this arg. 1421 cmd_arg.arg_type = eArgTypeFilename; 1422 cmd_arg.arg_repetition = eArgRepeatPlus; 1423 1424 // There is only one variant this argument could be; put it into the 1425 // argument entry. 1426 arg1.push_back(cmd_arg); 1427 1428 // Push the data for the first argument into the m_arguments vector. 1429 m_arguments.push_back(arg1); 1430 } 1431 1432 ~CommandObjectCommandsScriptImport() override = default; 1433 1434 int HandleArgumentCompletion( 1435 CompletionRequest &request, 1436 OptionElementVector &opt_element_vector) override { 1437 CommandCompletions::InvokeCommonCompletionCallbacks( 1438 GetCommandInterpreter(), CommandCompletions::eDiskFileCompletion, 1439 request, nullptr); 1440 return request.GetNumberOfMatches(); 1441 } 1442 1443 Options *GetOptions() override { return &m_options; } 1444 1445 protected: 1446 class CommandOptions : public Options { 1447 public: 1448 CommandOptions() : Options() {} 1449 1450 ~CommandOptions() override = default; 1451 1452 Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, 1453 ExecutionContext *execution_context) override { 1454 Status error; 1455 const int short_option = m_getopt_table[option_idx].val; 1456 1457 switch (short_option) { 1458 case 'r': 1459 m_allow_reload = true; 1460 break; 1461 default: 1462 error.SetErrorStringWithFormat("unrecognized option '%c'", 1463 short_option); 1464 break; 1465 } 1466 1467 return error; 1468 } 1469 1470 void OptionParsingStarting(ExecutionContext *execution_context) override { 1471 m_allow_reload = true; 1472 } 1473 1474 llvm::ArrayRef<OptionDefinition> GetDefinitions() override { 1475 return llvm::makeArrayRef(g_script_import_options); 1476 } 1477 1478 // Instance variables to hold the values for command options. 1479 1480 bool m_allow_reload; 1481 }; 1482 1483 bool DoExecute(Args &command, CommandReturnObject &result) override { 1484 if (m_interpreter.GetDebugger().GetScriptLanguage() != 1485 lldb::eScriptLanguagePython) { 1486 result.AppendError("only scripting language supported for module " 1487 "importing is currently Python"); 1488 result.SetStatus(eReturnStatusFailed); 1489 return false; 1490 } 1491 1492 if (command.empty()) { 1493 result.AppendError("command script import needs one or more arguments"); 1494 result.SetStatus(eReturnStatusFailed); 1495 return false; 1496 } 1497 1498 for (auto &entry : command.entries()) { 1499 Status error; 1500 1501 const bool init_session = true; 1502 // FIXME: this is necessary because CommandObject::CheckRequirements() 1503 // assumes that commands won't ever be recursively invoked, but it's 1504 // actually possible to craft a Python script that does other "command 1505 // script imports" in __lldb_init_module the real fix is to have 1506 // recursive commands possible with a CommandInvocation object separate 1507 // from the CommandObject itself, so that recursive command invocations 1508 // won't stomp on each other (wrt to execution contents, options, and 1509 // more) 1510 m_exe_ctx.Clear(); 1511 if (m_interpreter.GetScriptInterpreter()->LoadScriptingModule( 1512 entry.c_str(), m_options.m_allow_reload, init_session, error)) { 1513 result.SetStatus(eReturnStatusSuccessFinishNoResult); 1514 } else { 1515 result.AppendErrorWithFormat("module importing failed: %s", 1516 error.AsCString()); 1517 result.SetStatus(eReturnStatusFailed); 1518 } 1519 } 1520 1521 return result.Succeeded(); 1522 } 1523 1524 CommandOptions m_options; 1525 }; 1526 1527 //------------------------------------------------------------------------- 1528 // CommandObjectCommandsScriptAdd 1529 //------------------------------------------------------------------------- 1530 static constexpr OptionEnumValueElement g_script_synchro_type[] = { 1531 {eScriptedCommandSynchronicitySynchronous, "synchronous", 1532 "Run synchronous"}, 1533 {eScriptedCommandSynchronicityAsynchronous, "asynchronous", 1534 "Run asynchronous"}, 1535 {eScriptedCommandSynchronicityCurrentValue, "current", 1536 "Do not alter current setting"} }; 1537 1538 static constexpr OptionEnumValues ScriptSynchroType() { 1539 return OptionEnumValues(g_script_synchro_type); 1540 } 1541 1542 static constexpr OptionDefinition g_script_add_options[] = { 1543 // clang-format off 1544 { LLDB_OPT_SET_1, false, "function", 'f', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypePythonFunction, "Name of the Python function to bind to this command name." }, 1545 { LLDB_OPT_SET_2, false, "class", 'c', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypePythonClass, "Name of the Python class to bind to this command name." }, 1546 { LLDB_OPT_SET_1, false, "help" , 'h', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeHelpText, "The help text to display for this command." }, 1547 { LLDB_OPT_SET_ALL, false, "synchronicity", 's', OptionParser::eRequiredArgument, nullptr, ScriptSynchroType(), 0, eArgTypeScriptedCommandSynchronicity, "Set the synchronicity of this command's executions with regard to LLDB event system." }, 1548 // clang-format on 1549 }; 1550 1551 class CommandObjectCommandsScriptAdd : public CommandObjectParsed, 1552 public IOHandlerDelegateMultiline { 1553 public: 1554 CommandObjectCommandsScriptAdd(CommandInterpreter &interpreter) 1555 : CommandObjectParsed(interpreter, "command script add", 1556 "Add a scripted function as an LLDB command.", 1557 nullptr), 1558 IOHandlerDelegateMultiline("DONE"), m_options() { 1559 CommandArgumentEntry arg1; 1560 CommandArgumentData cmd_arg; 1561 1562 // Define the first (and only) variant of this arg. 1563 cmd_arg.arg_type = eArgTypeCommandName; 1564 cmd_arg.arg_repetition = eArgRepeatPlain; 1565 1566 // There is only one variant this argument could be; put it into the 1567 // argument entry. 1568 arg1.push_back(cmd_arg); 1569 1570 // Push the data for the first argument into the m_arguments vector. 1571 m_arguments.push_back(arg1); 1572 } 1573 1574 ~CommandObjectCommandsScriptAdd() override = default; 1575 1576 Options *GetOptions() override { return &m_options; } 1577 1578 protected: 1579 class CommandOptions : public Options { 1580 public: 1581 CommandOptions() 1582 : Options(), m_class_name(), m_funct_name(), m_short_help(), 1583 m_synchronicity(eScriptedCommandSynchronicitySynchronous) {} 1584 1585 ~CommandOptions() override = default; 1586 1587 Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, 1588 ExecutionContext *execution_context) override { 1589 Status error; 1590 const int short_option = m_getopt_table[option_idx].val; 1591 1592 switch (short_option) { 1593 case 'f': 1594 if (!option_arg.empty()) 1595 m_funct_name = option_arg; 1596 break; 1597 case 'c': 1598 if (!option_arg.empty()) 1599 m_class_name = option_arg; 1600 break; 1601 case 'h': 1602 if (!option_arg.empty()) 1603 m_short_help = option_arg; 1604 break; 1605 case 's': 1606 m_synchronicity = 1607 (ScriptedCommandSynchronicity)OptionArgParser::ToOptionEnum( 1608 option_arg, GetDefinitions()[option_idx].enum_values, 0, error); 1609 if (!error.Success()) 1610 error.SetErrorStringWithFormat( 1611 "unrecognized value for synchronicity '%s'", 1612 option_arg.str().c_str()); 1613 break; 1614 default: 1615 error.SetErrorStringWithFormat("unrecognized option '%c'", 1616 short_option); 1617 break; 1618 } 1619 1620 return error; 1621 } 1622 1623 void OptionParsingStarting(ExecutionContext *execution_context) override { 1624 m_class_name.clear(); 1625 m_funct_name.clear(); 1626 m_short_help.clear(); 1627 m_synchronicity = eScriptedCommandSynchronicitySynchronous; 1628 } 1629 1630 llvm::ArrayRef<OptionDefinition> GetDefinitions() override { 1631 return llvm::makeArrayRef(g_script_add_options); 1632 } 1633 1634 // Instance variables to hold the values for command options. 1635 1636 std::string m_class_name; 1637 std::string m_funct_name; 1638 std::string m_short_help; 1639 ScriptedCommandSynchronicity m_synchronicity; 1640 }; 1641 1642 void IOHandlerActivated(IOHandler &io_handler) override { 1643 StreamFileSP output_sp(io_handler.GetOutputStreamFile()); 1644 if (output_sp) { 1645 output_sp->PutCString(g_python_command_instructions); 1646 output_sp->Flush(); 1647 } 1648 } 1649 1650 void IOHandlerInputComplete(IOHandler &io_handler, 1651 std::string &data) override { 1652 StreamFileSP error_sp = io_handler.GetErrorStreamFile(); 1653 1654 ScriptInterpreter *interpreter = m_interpreter.GetScriptInterpreter(); 1655 if (interpreter) { 1656 1657 StringList lines; 1658 lines.SplitIntoLines(data); 1659 if (lines.GetSize() > 0) { 1660 std::string funct_name_str; 1661 if (interpreter->GenerateScriptAliasFunction(lines, funct_name_str)) { 1662 if (funct_name_str.empty()) { 1663 error_sp->Printf("error: unable to obtain a function name, didn't " 1664 "add python command.\n"); 1665 error_sp->Flush(); 1666 } else { 1667 // everything should be fine now, let's add this alias 1668 1669 CommandObjectSP command_obj_sp(new CommandObjectPythonFunction( 1670 m_interpreter, m_cmd_name, funct_name_str, m_short_help, 1671 m_synchronicity)); 1672 1673 if (!m_interpreter.AddUserCommand(m_cmd_name, command_obj_sp, 1674 true)) { 1675 error_sp->Printf("error: unable to add selected command, didn't " 1676 "add python command.\n"); 1677 error_sp->Flush(); 1678 } 1679 } 1680 } else { 1681 error_sp->Printf( 1682 "error: unable to create function, didn't add python command.\n"); 1683 error_sp->Flush(); 1684 } 1685 } else { 1686 error_sp->Printf("error: empty function, didn't add python command.\n"); 1687 error_sp->Flush(); 1688 } 1689 } else { 1690 error_sp->Printf( 1691 "error: script interpreter missing, didn't add python command.\n"); 1692 error_sp->Flush(); 1693 } 1694 1695 io_handler.SetIsDone(true); 1696 } 1697 1698 protected: 1699 bool DoExecute(Args &command, CommandReturnObject &result) override { 1700 if (m_interpreter.GetDebugger().GetScriptLanguage() != 1701 lldb::eScriptLanguagePython) { 1702 result.AppendError("only scripting language supported for scripted " 1703 "commands is currently Python"); 1704 result.SetStatus(eReturnStatusFailed); 1705 return false; 1706 } 1707 1708 if (command.GetArgumentCount() != 1) { 1709 result.AppendError("'command script add' requires one argument"); 1710 result.SetStatus(eReturnStatusFailed); 1711 return false; 1712 } 1713 1714 // Store the options in case we get multi-line input 1715 m_cmd_name = command[0].ref; 1716 m_short_help.assign(m_options.m_short_help); 1717 m_synchronicity = m_options.m_synchronicity; 1718 1719 if (m_options.m_class_name.empty()) { 1720 if (m_options.m_funct_name.empty()) { 1721 m_interpreter.GetPythonCommandsFromIOHandler( 1722 " ", // Prompt 1723 *this, // IOHandlerDelegate 1724 true, // Run IOHandler in async mode 1725 nullptr); // Baton for the "io_handler" that will be passed back 1726 // into our IOHandlerDelegate functions 1727 } else { 1728 CommandObjectSP new_cmd(new CommandObjectPythonFunction( 1729 m_interpreter, m_cmd_name, m_options.m_funct_name, 1730 m_options.m_short_help, m_synchronicity)); 1731 if (m_interpreter.AddUserCommand(m_cmd_name, new_cmd, true)) { 1732 result.SetStatus(eReturnStatusSuccessFinishNoResult); 1733 } else { 1734 result.AppendError("cannot add command"); 1735 result.SetStatus(eReturnStatusFailed); 1736 } 1737 } 1738 } else { 1739 ScriptInterpreter *interpreter = 1740 GetCommandInterpreter().GetScriptInterpreter(); 1741 if (!interpreter) { 1742 result.AppendError("cannot find ScriptInterpreter"); 1743 result.SetStatus(eReturnStatusFailed); 1744 return false; 1745 } 1746 1747 auto cmd_obj_sp = interpreter->CreateScriptCommandObject( 1748 m_options.m_class_name.c_str()); 1749 if (!cmd_obj_sp) { 1750 result.AppendError("cannot create helper object"); 1751 result.SetStatus(eReturnStatusFailed); 1752 return false; 1753 } 1754 1755 CommandObjectSP new_cmd(new CommandObjectScriptingObject( 1756 m_interpreter, m_cmd_name, cmd_obj_sp, m_synchronicity)); 1757 if (m_interpreter.AddUserCommand(m_cmd_name, new_cmd, true)) { 1758 result.SetStatus(eReturnStatusSuccessFinishNoResult); 1759 } else { 1760 result.AppendError("cannot add command"); 1761 result.SetStatus(eReturnStatusFailed); 1762 } 1763 } 1764 1765 return result.Succeeded(); 1766 } 1767 1768 CommandOptions m_options; 1769 std::string m_cmd_name; 1770 std::string m_short_help; 1771 ScriptedCommandSynchronicity m_synchronicity; 1772 }; 1773 1774 //------------------------------------------------------------------------- 1775 // CommandObjectCommandsScriptList 1776 //------------------------------------------------------------------------- 1777 1778 class CommandObjectCommandsScriptList : public CommandObjectParsed { 1779 public: 1780 CommandObjectCommandsScriptList(CommandInterpreter &interpreter) 1781 : CommandObjectParsed(interpreter, "command script list", 1782 "List defined scripted commands.", nullptr) {} 1783 1784 ~CommandObjectCommandsScriptList() override = default; 1785 1786 bool DoExecute(Args &command, CommandReturnObject &result) override { 1787 m_interpreter.GetHelp(result, CommandInterpreter::eCommandTypesUserDef); 1788 1789 result.SetStatus(eReturnStatusSuccessFinishResult); 1790 1791 return true; 1792 } 1793 }; 1794 1795 //------------------------------------------------------------------------- 1796 // CommandObjectCommandsScriptClear 1797 //------------------------------------------------------------------------- 1798 1799 class CommandObjectCommandsScriptClear : public CommandObjectParsed { 1800 public: 1801 CommandObjectCommandsScriptClear(CommandInterpreter &interpreter) 1802 : CommandObjectParsed(interpreter, "command script clear", 1803 "Delete all scripted commands.", nullptr) {} 1804 1805 ~CommandObjectCommandsScriptClear() override = default; 1806 1807 protected: 1808 bool DoExecute(Args &command, CommandReturnObject &result) override { 1809 m_interpreter.RemoveAllUser(); 1810 1811 result.SetStatus(eReturnStatusSuccessFinishResult); 1812 1813 return true; 1814 } 1815 }; 1816 1817 //------------------------------------------------------------------------- 1818 // CommandObjectCommandsScriptDelete 1819 //------------------------------------------------------------------------- 1820 1821 class CommandObjectCommandsScriptDelete : public CommandObjectParsed { 1822 public: 1823 CommandObjectCommandsScriptDelete(CommandInterpreter &interpreter) 1824 : CommandObjectParsed(interpreter, "command script delete", 1825 "Delete a scripted command.", nullptr) { 1826 CommandArgumentEntry arg1; 1827 CommandArgumentData cmd_arg; 1828 1829 // Define the first (and only) variant of this arg. 1830 cmd_arg.arg_type = eArgTypeCommandName; 1831 cmd_arg.arg_repetition = eArgRepeatPlain; 1832 1833 // There is only one variant this argument could be; put it into the 1834 // argument entry. 1835 arg1.push_back(cmd_arg); 1836 1837 // Push the data for the first argument into the m_arguments vector. 1838 m_arguments.push_back(arg1); 1839 } 1840 1841 ~CommandObjectCommandsScriptDelete() override = default; 1842 1843 protected: 1844 bool DoExecute(Args &command, CommandReturnObject &result) override { 1845 1846 if (command.GetArgumentCount() != 1) { 1847 result.AppendError("'command script delete' requires one argument"); 1848 result.SetStatus(eReturnStatusFailed); 1849 return false; 1850 } 1851 1852 auto cmd_name = command[0].ref; 1853 1854 if (cmd_name.empty() || !m_interpreter.HasUserCommands() || 1855 !m_interpreter.UserCommandExists(cmd_name)) { 1856 result.AppendErrorWithFormat("command %s not found", command[0].c_str()); 1857 result.SetStatus(eReturnStatusFailed); 1858 return false; 1859 } 1860 1861 m_interpreter.RemoveUser(cmd_name); 1862 result.SetStatus(eReturnStatusSuccessFinishResult); 1863 return true; 1864 } 1865 }; 1866 1867 #pragma mark CommandObjectMultiwordCommandsScript 1868 1869 //------------------------------------------------------------------------- 1870 // CommandObjectMultiwordCommandsScript 1871 //------------------------------------------------------------------------- 1872 1873 class CommandObjectMultiwordCommandsScript : public CommandObjectMultiword { 1874 public: 1875 CommandObjectMultiwordCommandsScript(CommandInterpreter &interpreter) 1876 : CommandObjectMultiword( 1877 interpreter, "command script", "Commands for managing custom " 1878 "commands implemented by " 1879 "interpreter scripts.", 1880 "command script <subcommand> [<subcommand-options>]") { 1881 LoadSubCommand("add", CommandObjectSP( 1882 new CommandObjectCommandsScriptAdd(interpreter))); 1883 LoadSubCommand( 1884 "delete", 1885 CommandObjectSP(new CommandObjectCommandsScriptDelete(interpreter))); 1886 LoadSubCommand( 1887 "clear", 1888 CommandObjectSP(new CommandObjectCommandsScriptClear(interpreter))); 1889 LoadSubCommand("list", CommandObjectSP(new CommandObjectCommandsScriptList( 1890 interpreter))); 1891 LoadSubCommand( 1892 "import", 1893 CommandObjectSP(new CommandObjectCommandsScriptImport(interpreter))); 1894 } 1895 1896 ~CommandObjectMultiwordCommandsScript() override = default; 1897 }; 1898 1899 #pragma mark CommandObjectMultiwordCommands 1900 1901 //------------------------------------------------------------------------- 1902 // CommandObjectMultiwordCommands 1903 //------------------------------------------------------------------------- 1904 1905 CommandObjectMultiwordCommands::CommandObjectMultiwordCommands( 1906 CommandInterpreter &interpreter) 1907 : CommandObjectMultiword(interpreter, "command", 1908 "Commands for managing custom LLDB commands.", 1909 "command <subcommand> [<subcommand-options>]") { 1910 LoadSubCommand("source", 1911 CommandObjectSP(new CommandObjectCommandsSource(interpreter))); 1912 LoadSubCommand("alias", 1913 CommandObjectSP(new CommandObjectCommandsAlias(interpreter))); 1914 LoadSubCommand("unalias", CommandObjectSP( 1915 new CommandObjectCommandsUnalias(interpreter))); 1916 LoadSubCommand("delete", 1917 CommandObjectSP(new CommandObjectCommandsDelete(interpreter))); 1918 LoadSubCommand( 1919 "regex", CommandObjectSP(new CommandObjectCommandsAddRegex(interpreter))); 1920 LoadSubCommand("history", CommandObjectSP( 1921 new CommandObjectCommandsHistory(interpreter))); 1922 LoadSubCommand( 1923 "script", 1924 CommandObjectSP(new CommandObjectMultiwordCommandsScript(interpreter))); 1925 } 1926 1927 CommandObjectMultiwordCommands::~CommandObjectMultiwordCommands() = default; 1928