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