| //===-- CommandObjectSource.cpp ---------------------------------*- C++ -*-===// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| // C Includes |
| // C++ Includes |
| // Other libraries and framework includes |
| #include "llvm/ADT/StringRef.h" |
| |
| // Project includes |
| #include "CommandObjectCommands.h" |
| #include "CommandObjectHelp.h" |
| #include "lldb/Core/Debugger.h" |
| #include "lldb/Core/IOHandler.h" |
| #include "lldb/Core/StringList.h" |
| #include "lldb/Interpreter/Args.h" |
| #include "lldb/Interpreter/CommandHistory.h" |
| #include "lldb/Interpreter/CommandInterpreter.h" |
| #include "lldb/Interpreter/CommandObjectRegexCommand.h" |
| #include "lldb/Interpreter/CommandReturnObject.h" |
| #include "lldb/Interpreter/OptionValueBoolean.h" |
| #include "lldb/Interpreter/OptionValueString.h" |
| #include "lldb/Interpreter/OptionValueUInt64.h" |
| #include "lldb/Interpreter/Options.h" |
| #include "lldb/Interpreter/ScriptInterpreter.h" |
| |
| using namespace lldb; |
| using namespace lldb_private; |
| |
| //------------------------------------------------------------------------- |
| // CommandObjectCommandsSource |
| //------------------------------------------------------------------------- |
| |
| class CommandObjectCommandsHistory : public CommandObjectParsed |
| { |
| public: |
| CommandObjectCommandsHistory(CommandInterpreter &interpreter) : |
| CommandObjectParsed(interpreter, |
| "command history", |
| "Dump the history of commands in this session.", |
| nullptr), |
| m_options (interpreter) |
| { |
| } |
| |
| ~CommandObjectCommandsHistory() override = default; |
| |
| Options * |
| GetOptions () override |
| { |
| return &m_options; |
| } |
| |
| protected: |
| class CommandOptions : public Options |
| { |
| public: |
| CommandOptions (CommandInterpreter &interpreter) : |
| Options (interpreter), |
| m_start_idx(0), |
| m_stop_idx(0), |
| m_count(0), |
| m_clear(false) |
| { |
| } |
| |
| ~CommandOptions() override = default; |
| |
| Error |
| SetOptionValue (uint32_t option_idx, const char *option_arg) override |
| { |
| Error error; |
| const int short_option = m_getopt_table[option_idx].val; |
| |
| switch (short_option) |
| { |
| case 'c': |
| error = m_count.SetValueFromString(option_arg,eVarSetOperationAssign); |
| break; |
| case 's': |
| if (option_arg && strcmp("end", option_arg) == 0) |
| { |
| m_start_idx.SetCurrentValue(UINT64_MAX); |
| m_start_idx.SetOptionWasSet(); |
| } |
| else |
| error = m_start_idx.SetValueFromString(option_arg,eVarSetOperationAssign); |
| break; |
| case 'e': |
| error = m_stop_idx.SetValueFromString(option_arg,eVarSetOperationAssign); |
| break; |
| case 'C': |
| m_clear.SetCurrentValue(true); |
| m_clear.SetOptionWasSet(); |
| break; |
| default: |
| error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); |
| break; |
| } |
| |
| return error; |
| } |
| |
| void |
| OptionParsingStarting () override |
| { |
| m_start_idx.Clear(); |
| m_stop_idx.Clear(); |
| m_count.Clear(); |
| m_clear.Clear(); |
| } |
| |
| const OptionDefinition* |
| GetDefinitions () override |
| { |
| return g_option_table; |
| } |
| |
| // Options table: Required for subclasses of Options. |
| |
| static OptionDefinition g_option_table[]; |
| |
| // Instance variables to hold the values for command options. |
| |
| OptionValueUInt64 m_start_idx; |
| OptionValueUInt64 m_stop_idx; |
| OptionValueUInt64 m_count; |
| OptionValueBoolean m_clear; |
| }; |
| |
| bool |
| DoExecute (Args& command, CommandReturnObject &result) override |
| { |
| if (m_options.m_clear.GetCurrentValue() && m_options.m_clear.OptionWasSet()) |
| { |
| m_interpreter.GetCommandHistory().Clear(); |
| result.SetStatus(lldb::eReturnStatusSuccessFinishNoResult); |
| } |
| else |
| { |
| if (m_options.m_start_idx.OptionWasSet() && m_options.m_stop_idx.OptionWasSet() && m_options.m_count.OptionWasSet()) |
| { |
| result.AppendError("--count, --start-index and --end-index cannot be all specified in the same invocation"); |
| result.SetStatus(lldb::eReturnStatusFailed); |
| } |
| else |
| { |
| std::pair<bool,uint64_t> start_idx(m_options.m_start_idx.OptionWasSet(),m_options.m_start_idx.GetCurrentValue()); |
| std::pair<bool,uint64_t> stop_idx(m_options.m_stop_idx.OptionWasSet(),m_options.m_stop_idx.GetCurrentValue()); |
| std::pair<bool,uint64_t> count(m_options.m_count.OptionWasSet(),m_options.m_count.GetCurrentValue()); |
| |
| const CommandHistory& history(m_interpreter.GetCommandHistory()); |
| |
| if (start_idx.first && start_idx.second == UINT64_MAX) |
| { |
| if (count.first) |
| { |
| start_idx.second = history.GetSize() - count.second; |
| stop_idx.second = history.GetSize() - 1; |
| } |
| else if (stop_idx.first) |
| { |
| start_idx.second = stop_idx.second; |
| stop_idx.second = history.GetSize() - 1; |
| } |
| else |
| { |
| start_idx.second = 0; |
| stop_idx.second = history.GetSize() - 1; |
| } |
| } |
| else |
| { |
| if (!start_idx.first && !stop_idx.first && !count.first) |
| { |
| start_idx.second = 0; |
| stop_idx.second = history.GetSize() - 1; |
| } |
| else if (start_idx.first) |
| { |
| if (count.first) |
| { |
| stop_idx.second = start_idx.second + count.second - 1; |
| } |
| else if (!stop_idx.first) |
| { |
| stop_idx.second = history.GetSize() - 1; |
| } |
| } |
| else if (stop_idx.first) |
| { |
| if (count.first) |
| { |
| if (stop_idx.second >= count.second) |
| start_idx.second = stop_idx.second - count.second + 1; |
| else |
| start_idx.second = 0; |
| } |
| } |
| else /* if (count.first) */ |
| { |
| start_idx.second = 0; |
| stop_idx.second = count.second - 1; |
| } |
| } |
| history.Dump(result.GetOutputStream(), start_idx.second, stop_idx.second); |
| } |
| } |
| return result.Succeeded(); |
| |
| } |
| |
| CommandOptions m_options; |
| }; |
| |
| OptionDefinition |
| CommandObjectCommandsHistory::CommandOptions::g_option_table[] = |
| { |
| { LLDB_OPT_SET_1, false, "count", 'c', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeUnsignedInteger, "How many history commands to print."}, |
| { LLDB_OPT_SET_1, false, "start-index", 's', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeUnsignedInteger, "Index at which to start printing history commands (or end to mean tail mode)."}, |
| { LLDB_OPT_SET_1, false, "end-index", 'e', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeUnsignedInteger, "Index at which to stop printing history commands."}, |
| { LLDB_OPT_SET_2, false, "clear", 'C', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeBoolean, "Clears the current command history."}, |
| { 0, false, nullptr, 0, 0, nullptr, nullptr, 0, eArgTypeNone, nullptr } |
| }; |
| |
| //------------------------------------------------------------------------- |
| // CommandObjectCommandsSource |
| //------------------------------------------------------------------------- |
| |
| class CommandObjectCommandsSource : public CommandObjectParsed |
| { |
| public: |
| CommandObjectCommandsSource(CommandInterpreter &interpreter) |
| : CommandObjectParsed(interpreter, "command source", "Read and execute LLDB commands from the file <filename>.", |
| nullptr), |
| m_options(interpreter) |
| { |
| CommandArgumentEntry arg; |
| CommandArgumentData file_arg; |
| |
| // Define the first (and only) variant of this arg. |
| file_arg.arg_type = eArgTypeFilename; |
| file_arg.arg_repetition = eArgRepeatPlain; |
| |
| // There is only one variant this argument could be; put it into the argument entry. |
| arg.push_back (file_arg); |
| |
| // Push the data for the first argument into the m_arguments vector. |
| m_arguments.push_back (arg); |
| } |
| |
| ~CommandObjectCommandsSource() override = default; |
| |
| const char* |
| GetRepeatCommand (Args ¤t_command_args, uint32_t index) override |
| { |
| return ""; |
| } |
| |
| int |
| HandleArgumentCompletion (Args &input, |
| int &cursor_index, |
| int &cursor_char_position, |
| OptionElementVector &opt_element_vector, |
| int match_start_point, |
| int max_return_elements, |
| bool &word_complete, |
| StringList &matches) override |
| { |
| std::string completion_str (input.GetArgumentAtIndex(cursor_index)); |
| completion_str.erase (cursor_char_position); |
| |
| CommandCompletions::InvokeCommonCompletionCallbacks(m_interpreter, |
| CommandCompletions::eDiskFileCompletion, |
| completion_str.c_str(), |
| match_start_point, |
| max_return_elements, |
| nullptr, |
| word_complete, |
| matches); |
| return matches.GetSize(); |
| } |
| |
| Options * |
| GetOptions () override |
| { |
| return &m_options; |
| } |
| |
| protected: |
| class CommandOptions : public Options |
| { |
| public: |
| CommandOptions (CommandInterpreter &interpreter) : |
| Options (interpreter), |
| m_stop_on_error (true), |
| m_silent_run (false), |
| m_stop_on_continue (true) |
| { |
| } |
| |
| ~CommandOptions() override = default; |
| |
| Error |
| SetOptionValue (uint32_t option_idx, const char *option_arg) override |
| { |
| Error error; |
| const int short_option = m_getopt_table[option_idx].val; |
| |
| switch (short_option) |
| { |
| case 'e': |
| error = m_stop_on_error.SetValueFromString(option_arg); |
| break; |
| |
| case 'c': |
| error = m_stop_on_continue.SetValueFromString(option_arg); |
| break; |
| |
| case 's': |
| error = m_silent_run.SetValueFromString(option_arg); |
| break; |
| |
| default: |
| error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); |
| break; |
| } |
| |
| return error; |
| } |
| |
| void |
| OptionParsingStarting () override |
| { |
| m_stop_on_error.Clear(); |
| m_silent_run.Clear(); |
| m_stop_on_continue.Clear(); |
| } |
| |
| const OptionDefinition* |
| GetDefinitions () override |
| { |
| return g_option_table; |
| } |
| |
| // Options table: Required for subclasses of Options. |
| |
| static OptionDefinition g_option_table[]; |
| |
| // Instance variables to hold the values for command options. |
| |
| OptionValueBoolean m_stop_on_error; |
| OptionValueBoolean m_silent_run; |
| OptionValueBoolean m_stop_on_continue; |
| }; |
| |
| bool |
| DoExecute(Args& command, CommandReturnObject &result) override |
| { |
| const size_t argc = command.GetArgumentCount(); |
| if (argc == 1) |
| { |
| const char *filename = command.GetArgumentAtIndex(0); |
| |
| FileSpec cmd_file (filename, true); |
| ExecutionContext *exe_ctx = nullptr; // Just use the default context. |
| |
| // If any options were set, then use them |
| if (m_options.m_stop_on_error.OptionWasSet() || |
| m_options.m_silent_run.OptionWasSet() || |
| m_options.m_stop_on_continue.OptionWasSet()) |
| { |
| // Use user set settings |
| CommandInterpreterRunOptions options; |
| options.SetStopOnContinue(m_options.m_stop_on_continue.GetCurrentValue()); |
| options.SetStopOnError (m_options.m_stop_on_error.GetCurrentValue()); |
| options.SetEchoCommands (!m_options.m_silent_run.GetCurrentValue()); |
| options.SetPrintResults (!m_options.m_silent_run.GetCurrentValue()); |
| |
| m_interpreter.HandleCommandsFromFile (cmd_file, |
| exe_ctx, |
| options, |
| result); |
| } |
| else |
| { |
| // No options were set, inherit any settings from nested "command source" commands, |
| // or set to sane default settings... |
| CommandInterpreterRunOptions options; |
| m_interpreter.HandleCommandsFromFile (cmd_file, |
| exe_ctx, |
| options, |
| result); |
| } |
| } |
| else |
| { |
| result.AppendErrorWithFormat("'%s' takes exactly one executable filename argument.\n", GetCommandName()); |
| result.SetStatus (eReturnStatusFailed); |
| } |
| return result.Succeeded(); |
| } |
| |
| CommandOptions m_options; |
| }; |
| |
| OptionDefinition |
| CommandObjectCommandsSource::CommandOptions::g_option_table[] = |
| { |
| { LLDB_OPT_SET_ALL, false, "stop-on-error", 'e', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeBoolean, "If true, stop executing commands on error."}, |
| { LLDB_OPT_SET_ALL, false, "stop-on-continue", 'c', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeBoolean, "If true, stop executing commands on continue."}, |
| { LLDB_OPT_SET_ALL, false, "silent-run", 's', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeBoolean, "If true don't echo commands while executing."}, |
| { 0, false, nullptr, 0, 0, nullptr, nullptr, 0, eArgTypeNone, nullptr } |
| }; |
| |
| #pragma mark CommandObjectCommandsAlias |
| //------------------------------------------------------------------------- |
| // CommandObjectCommandsAlias |
| //------------------------------------------------------------------------- |
| |
| static const char *g_python_command_instructions = "Enter your Python command(s). Type 'DONE' to end.\n" |
| "You must define a Python function with this signature:\n" |
| "def my_command_impl(debugger, args, result, internal_dict):\n"; |
| |
| class CommandObjectCommandsAlias : public CommandObjectRaw |
| { |
| protected: |
| class CommandOptions : public OptionGroup |
| { |
| public: |
| CommandOptions () : |
| OptionGroup(), |
| m_help(), |
| m_long_help() |
| {} |
| |
| ~CommandOptions() override = default; |
| |
| uint32_t |
| GetNumDefinitions () override |
| { |
| return 3; |
| } |
| |
| const OptionDefinition* |
| GetDefinitions () override |
| { |
| return g_option_table; |
| } |
| |
| Error |
| SetOptionValue (CommandInterpreter &interpreter, |
| uint32_t option_idx, |
| const char *option_value) override |
| { |
| Error error; |
| |
| const int short_option = g_option_table[option_idx].short_option; |
| |
| switch (short_option) |
| { |
| case 'h': |
| m_help.SetCurrentValue(option_value); |
| m_help.SetOptionWasSet(); |
| break; |
| |
| case 'H': |
| m_long_help.SetCurrentValue(option_value); |
| m_long_help.SetOptionWasSet(); |
| break; |
| |
| default: |
| error.SetErrorStringWithFormat("invalid short option character '%c'", short_option); |
| break; |
| } |
| |
| return error; |
| } |
| |
| void |
| OptionParsingStarting (CommandInterpreter &interpreter) override |
| { |
| m_help.Clear(); |
| m_long_help.Clear(); |
| } |
| |
| // Options table: Required for subclasses of Options. |
| |
| static OptionDefinition g_option_table[]; |
| OptionValueString m_help; |
| OptionValueString m_long_help; |
| }; |
| |
| OptionGroupOptions m_option_group; |
| CommandOptions m_command_options; |
| |
| public: |
| Options * |
| GetOptions () override |
| { |
| return &m_option_group; |
| } |
| |
| CommandObjectCommandsAlias(CommandInterpreter &interpreter) |
| : CommandObjectRaw(interpreter, "command alias", "Define a custom command in terms of an existing command.", |
| nullptr), |
| m_option_group(interpreter), |
| m_command_options() |
| { |
| m_option_group.Append(&m_command_options); |
| m_option_group.Finalize(); |
| |
| SetHelpLong( |
| "'alias' allows the user to create a short-cut or abbreviation for long \ |
| commands, multi-word commands, and commands that take particular options. \ |
| Below are some simple examples of how one might use the 'alias' command:" R"( |
| |
| (lldb) command alias sc script |
| |
| Creates the abbreviation 'sc' for the 'script' command. |
| |
| (lldb) command alias bp breakpoint |
| |
| )" " Creates the abbreviation 'bp' for the 'breakpoint' command. Since \ |
| breakpoint commands are two-word commands, the user would still need to \ |
| enter the second word after 'bp', e.g. 'bp enable' or 'bp delete'." R"( |
| |
| (lldb) command alias bpl breakpoint list |
| |
| Creates the abbreviation 'bpl' for the two-word command 'breakpoint list'. |
| |
| )" "An alias can include some options for the command, with the values either \ |
| filled in at the time the alias is created, or specified as positional \ |
| arguments, to be filled in when the alias is invoked. The following example \ |
| shows how to create aliases with options:" R"( |
| |
| (lldb) command alias bfl breakpoint set -f %1 -l %2 |
| |
| )" " Creates the abbreviation 'bfl' (for break-file-line), with the -f and -l \ |
| options already part of the alias. So if the user wants to set a breakpoint \ |
| by file and line without explicitly having to use the -f and -l options, the \ |
| user can now use 'bfl' instead. The '%1' and '%2' are positional placeholders \ |
| for the actual arguments that will be passed when the alias command is used. \ |
| The number in the placeholder refers to the position/order the actual value \ |
| occupies when the alias is used. All the occurrences of '%1' in the alias \ |
| will be replaced with the first argument, all the occurrences of '%2' in the \ |
| alias will be replaced with the second argument, and so on. This also allows \ |
| actual arguments to be used multiple times within an alias (see 'process \ |
| launch' example below)." R"( |
| |
| )" "Note: the positional arguments must substitute as whole words in the resultant \ |
| command, so you can't at present do something like this to append the file extension \ |
| \".cpp\":" R"( |
| |
| (lldb) command alias bcppfl breakpoint set -f %1.cpp -l %2 |
| |
| )" "For more complex aliasing, use the \"command regex\" command instead. In the \ |
| 'bfl' case above, the actual file value will be filled in with the first argument \ |
| following 'bfl' and the actual line number value will be filled in with the second \ |
| argument. The user would use this alias as follows:" R"( |
| |
| (lldb) command alias bfl breakpoint set -f %1 -l %2 |
| (lldb) bfl my-file.c 137 |
| |
| This would be the same as if the user had entered 'breakpoint set -f my-file.c -l 137'. |
| |
| Another example: |
| |
| (lldb) command alias pltty process launch -s -o %1 -e %1 |
| (lldb) pltty /dev/tty0 |
| |
| Interpreted as 'process launch -s -o /dev/tty0 -e /dev/tty0' |
| |
| )" "If the user always wanted to pass the same value to a particular option, the \ |
| alias could be defined with that value directly in the alias as a constant, \ |
| rather than using a positional placeholder:" R"( |
| |
| (lldb) command alias bl3 breakpoint set -f %1 -l 3 |
| |
| Always sets a breakpoint on line 3 of whatever file is indicated.)" |
| ); |
| |
| CommandArgumentEntry arg1; |
| CommandArgumentEntry arg2; |
| CommandArgumentEntry arg3; |
| CommandArgumentData alias_arg; |
| CommandArgumentData cmd_arg; |
| CommandArgumentData options_arg; |
| |
| // Define the first (and only) variant of this arg. |
| alias_arg.arg_type = eArgTypeAliasName; |
| alias_arg.arg_repetition = eArgRepeatPlain; |
| |
| // There is only one variant this argument could be; put it into the argument entry. |
| arg1.push_back (alias_arg); |
| |
| // Define the first (and only) variant of this arg. |
| cmd_arg.arg_type = eArgTypeCommandName; |
| cmd_arg.arg_repetition = eArgRepeatPlain; |
| |
| // There is only one variant this argument could be; put it into the argument entry. |
| arg2.push_back (cmd_arg); |
| |
| // Define the first (and only) variant of this arg. |
| options_arg.arg_type = eArgTypeAliasOptions; |
| options_arg.arg_repetition = eArgRepeatOptional; |
| |
| // There is only one variant this argument could be; put it into the argument entry. |
| arg3.push_back (options_arg); |
| |
| // Push the data for the first argument into the m_arguments vector. |
| m_arguments.push_back (arg1); |
| m_arguments.push_back (arg2); |
| m_arguments.push_back (arg3); |
| } |
| |
| ~CommandObjectCommandsAlias() override = default; |
| |
| protected: |
| bool |
| DoExecute (const char *raw_command_line, CommandReturnObject &result) override |
| { |
| if (!raw_command_line || !raw_command_line[0]) |
| { |
| result.AppendError ("'command alias' requires at least two arguments"); |
| return false; |
| } |
| |
| m_option_group.NotifyOptionParsingStarting(); |
| |
| const char * remainder = nullptr; |
| |
| if (raw_command_line[0] == '-') |
| { |
| // We have some options and these options MUST end with --. |
| const char *end_options = nullptr; |
| const char *s = raw_command_line; |
| while (s && s[0]) |
| { |
| end_options = ::strstr (s, "--"); |
| if (end_options) |
| { |
| end_options += 2; // Get past the "--" |
| if (::isspace (end_options[0])) |
| { |
| remainder = end_options; |
| while (::isspace (*remainder)) |
| ++remainder; |
| break; |
| } |
| } |
| s = end_options; |
| } |
| |
| if (end_options) |
| { |
| Args args (llvm::StringRef(raw_command_line, end_options - raw_command_line)); |
| if (!ParseOptions (args, result)) |
| return false; |
| |
| Error error (m_option_group.NotifyOptionParsingFinished()); |
| if (error.Fail()) |
| { |
| result.AppendError (error.AsCString()); |
| result.SetStatus (eReturnStatusFailed); |
| return false; |
| } |
| } |
| } |
| if (nullptr == remainder) |
| remainder = raw_command_line; |
| |
| std::string raw_command_string (remainder); |
| Args args (raw_command_string.c_str()); |
| |
| size_t argc = args.GetArgumentCount(); |
| |
| if (argc < 2) |
| { |
| result.AppendError ("'command alias' requires at least two arguments"); |
| result.SetStatus (eReturnStatusFailed); |
| return false; |
| } |
| |
| // Get the alias command. |
| |
| const std::string alias_command = args.GetArgumentAtIndex (0); |
| if (alias_command.size() > 1 && |
| alias_command[0] == '-') |
| { |
| result.AppendError("aliases starting with a dash are not supported"); |
| if (alias_command == "--help" || alias_command == "--long-help") |
| { |
| result.AppendWarning("if trying to pass options to 'command alias' add a -- at the end of the options"); |
| } |
| result.SetStatus (eReturnStatusFailed); |
| return false; |
| } |
| |
| // Strip the new alias name off 'raw_command_string' (leave it on args, which gets passed to 'Execute', which |
| // does the stripping itself. |
| size_t pos = raw_command_string.find (alias_command); |
| if (pos == 0) |
| { |
| raw_command_string = raw_command_string.substr (alias_command.size()); |
| pos = raw_command_string.find_first_not_of (' '); |
| if ((pos != std::string::npos) && (pos > 0)) |
| raw_command_string = raw_command_string.substr (pos); |
| } |
| else |
| { |
| result.AppendError ("Error parsing command string. No alias created."); |
| result.SetStatus (eReturnStatusFailed); |
| return false; |
| } |
| |
| |
| // Verify that the command is alias-able. |
| if (m_interpreter.CommandExists (alias_command.c_str())) |
| { |
| result.AppendErrorWithFormat ("'%s' is a permanent debugger command and cannot be redefined.\n", |
| alias_command.c_str()); |
| result.SetStatus (eReturnStatusFailed); |
| return false; |
| } |
| |
| // Get CommandObject that is being aliased. The command name is read from the front of raw_command_string. |
| // raw_command_string is returned with the name of the command object stripped off the front. |
| std::string original_raw_command_string(raw_command_string); |
| CommandObject *cmd_obj = m_interpreter.GetCommandObjectForCommand (raw_command_string); |
| |
| if (!cmd_obj) |
| { |
| result.AppendErrorWithFormat ("invalid command given to 'command alias'. '%s' does not begin with a valid command." |
| " No alias created.", original_raw_command_string.c_str()); |
| result.SetStatus (eReturnStatusFailed); |
| return false; |
| } |
| else if (!cmd_obj->WantsRawCommandString ()) |
| { |
| // Note that args was initialized with the original command, and has not been updated to this point. |
| // Therefore can we pass it to the version of Execute that does not need/expect raw input in the alias. |
| return HandleAliasingNormalCommand (args, result); |
| } |
| else |
| { |
| return HandleAliasingRawCommand (alias_command, raw_command_string, *cmd_obj, result); |
| } |
| return result.Succeeded(); |
| } |
| |
| bool |
| HandleAliasingRawCommand (const std::string &alias_command, std::string &raw_command_string, CommandObject &cmd_obj, CommandReturnObject &result) |
| { |
| // Verify & handle any options/arguments passed to the alias command |
| |
| OptionArgVectorSP option_arg_vector_sp = OptionArgVectorSP (new OptionArgVector); |
| |
| if (CommandObjectSP cmd_obj_sp = m_interpreter.GetCommandSPExact (cmd_obj.GetCommandName(), false)) |
| { |
| if (m_interpreter.AliasExists (alias_command.c_str()) |
| || m_interpreter.UserCommandExists (alias_command.c_str())) |
| { |
| result.AppendWarningWithFormat ("Overwriting existing definition for '%s'.\n", |
| alias_command.c_str()); |
| } |
| if (CommandAlias *alias = m_interpreter.AddAlias (alias_command.c_str(), cmd_obj_sp, raw_command_string.c_str())) |
| { |
| if (m_command_options.m_help.OptionWasSet()) |
| alias->SetHelp(m_command_options.m_help.GetCurrentValue()); |
| if (m_command_options.m_long_help.OptionWasSet()) |
| alias->SetHelpLong(m_command_options.m_long_help.GetCurrentValue()); |
| result.SetStatus (eReturnStatusSuccessFinishNoResult); |
| } |
| else |
| { |
| result.AppendError ("Unable to create requested alias.\n"); |
| result.SetStatus (eReturnStatusFailed); |
| } |
| |
| } |
| else |
| { |
| result.AppendError ("Unable to create requested alias.\n"); |
| result.SetStatus (eReturnStatusFailed); |
| } |
| |
| return result.Succeeded (); |
| } |
| |
| bool |
| HandleAliasingNormalCommand (Args& args, CommandReturnObject &result) |
| { |
| size_t argc = args.GetArgumentCount(); |
| |
| if (argc < 2) |
| { |
| result.AppendError ("'command alias' requires at least two arguments"); |
| result.SetStatus (eReturnStatusFailed); |
| return false; |
| } |
| |
| const std::string alias_command = args.GetArgumentAtIndex(0); |
| const std::string actual_command = args.GetArgumentAtIndex(1); |
| |
| args.Shift(); // Shift the alias command word off the argument vector. |
| args.Shift(); // Shift the old command word off the argument vector. |
| |
| // Verify that the command is alias'able, and get the appropriate command object. |
| |
| if (m_interpreter.CommandExists (alias_command.c_str())) |
| { |
| result.AppendErrorWithFormat ("'%s' is a permanent debugger command and cannot be redefined.\n", |
| alias_command.c_str()); |
| result.SetStatus (eReturnStatusFailed); |
| } |
| else |
| { |
| CommandObjectSP command_obj_sp(m_interpreter.GetCommandSPExact (actual_command.c_str(), true)); |
| CommandObjectSP subcommand_obj_sp; |
| bool use_subcommand = false; |
| if (command_obj_sp) |
| { |
| CommandObject *cmd_obj = command_obj_sp.get(); |
| CommandObject *sub_cmd_obj = nullptr; |
| OptionArgVectorSP option_arg_vector_sp = OptionArgVectorSP (new OptionArgVector); |
| |
| while (cmd_obj->IsMultiwordObject() && args.GetArgumentCount() > 0) |
| { |
| if (argc >= 3) |
| { |
| const std::string sub_command = args.GetArgumentAtIndex(0); |
| assert (sub_command.length() != 0); |
| subcommand_obj_sp = cmd_obj->GetSubcommandSP (sub_command.c_str()); |
| if (subcommand_obj_sp) |
| { |
| sub_cmd_obj = subcommand_obj_sp.get(); |
| use_subcommand = true; |
| args.Shift(); // Shift the sub_command word off the argument vector. |
| cmd_obj = sub_cmd_obj; |
| } |
| else |
| { |
| result.AppendErrorWithFormat("'%s' is not a valid sub-command of '%s'. " |
| "Unable to create alias.\n", |
| sub_command.c_str(), actual_command.c_str()); |
| result.SetStatus (eReturnStatusFailed); |
| return false; |
| } |
| } |
| } |
| |
| // Verify & handle any options/arguments passed to the alias command |
| |
| std::string args_string; |
| |
| if (args.GetArgumentCount () > 0) |
| { |
| CommandObjectSP tmp_sp = m_interpreter.GetCommandSPExact (cmd_obj->GetCommandName(), false); |
| if (use_subcommand) |
| tmp_sp = m_interpreter.GetCommandSPExact (sub_cmd_obj->GetCommandName(), false); |
| |
| args.GetCommandString (args_string); |
| } |
| |
| if (m_interpreter.AliasExists (alias_command.c_str()) |
| || m_interpreter.UserCommandExists (alias_command.c_str())) |
| { |
| result.AppendWarningWithFormat ("Overwriting existing definition for '%s'.\n", |
| alias_command.c_str()); |
| } |
| |
| if (CommandAlias *alias = m_interpreter.AddAlias(alias_command.c_str(), |
| use_subcommand ? subcommand_obj_sp : command_obj_sp, |
| args_string.c_str())) |
| { |
| if (m_command_options.m_help.OptionWasSet()) |
| alias->SetHelp(m_command_options.m_help.GetCurrentValue()); |
| if (m_command_options.m_long_help.OptionWasSet()) |
| alias->SetHelpLong(m_command_options.m_long_help.GetCurrentValue()); |
| result.SetStatus (eReturnStatusSuccessFinishNoResult); |
| } |
| else |
| { |
| result.AppendError ("Unable to create requested alias.\n"); |
| result.SetStatus (eReturnStatusFailed); |
| return false; |
| } |
| } |
| else |
| { |
| result.AppendErrorWithFormat ("'%s' is not an existing command.\n", actual_command.c_str()); |
| result.SetStatus (eReturnStatusFailed); |
| return false; |
| } |
| } |
| |
| return result.Succeeded(); |
| } |
| }; |
| |
| OptionDefinition |
| CommandObjectCommandsAlias::CommandOptions::g_option_table[] = |
| { |
| { LLDB_OPT_SET_ALL, false, "help", 'h', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeHelpText, "Help text for this command"}, |
| { LLDB_OPT_SET_ALL, false, "long-help", 'H', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeHelpText, "Long help text for this command"}, |
| { 0, false, nullptr, 0, 0, nullptr, nullptr, 0, eArgTypeNone, nullptr } |
| }; |
| |
| #pragma mark CommandObjectCommandsUnalias |
| //------------------------------------------------------------------------- |
| // CommandObjectCommandsUnalias |
| //------------------------------------------------------------------------- |
| |
| class CommandObjectCommandsUnalias : public CommandObjectParsed |
| { |
| public: |
| CommandObjectCommandsUnalias(CommandInterpreter &interpreter) |
| : CommandObjectParsed(interpreter, "command unalias", |
| "Delete one or more custom commands defined by 'command alias'.", nullptr) |
| { |
| CommandArgumentEntry arg; |
| CommandArgumentData alias_arg; |
| |
| // Define the first (and only) variant of this arg. |
| alias_arg.arg_type = eArgTypeAliasName; |
| alias_arg.arg_repetition = eArgRepeatPlain; |
| |
| // There is only one variant this argument could be; put it into the argument entry. |
| arg.push_back (alias_arg); |
| |
| // Push the data for the first argument into the m_arguments vector. |
| m_arguments.push_back (arg); |
| } |
| |
| ~CommandObjectCommandsUnalias() override = default; |
| |
| protected: |
| bool |
| DoExecute (Args& args, CommandReturnObject &result) override |
| { |
| CommandObject::CommandMap::iterator pos; |
| CommandObject *cmd_obj; |
| |
| if (args.GetArgumentCount() != 0) |
| { |
| const char *command_name = args.GetArgumentAtIndex(0); |
| cmd_obj = m_interpreter.GetCommandObject(command_name); |
| if (cmd_obj) |
| { |
| if (m_interpreter.CommandExists (command_name)) |
| { |
| if (cmd_obj->IsRemovable()) |
| { |
| result.AppendErrorWithFormat ("'%s' is not an alias, it is a debugger command which can be removed using the 'command delete' command.\n", |
| command_name); |
| } |
| else |
| { |
| result.AppendErrorWithFormat ("'%s' is a permanent debugger command and cannot be removed.\n", |
| command_name); |
| } |
| result.SetStatus (eReturnStatusFailed); |
| } |
| else |
| { |
| if (!m_interpreter.RemoveAlias(command_name)) |
| { |
| if (m_interpreter.AliasExists (command_name)) |
| result.AppendErrorWithFormat ("Error occurred while attempting to unalias '%s'.\n", |
| command_name); |
| else |
| result.AppendErrorWithFormat ("'%s' is not an existing alias.\n", command_name); |
| result.SetStatus (eReturnStatusFailed); |
| } |
| else |
| result.SetStatus (eReturnStatusSuccessFinishNoResult); |
| } |
| } |
| else |
| { |
| result.AppendErrorWithFormat ("'%s' is not a known command.\nTry 'help' to see a " |
| "current list of commands.\n", |
| command_name); |
| result.SetStatus (eReturnStatusFailed); |
| } |
| } |
| else |
| { |
| result.AppendError ("must call 'unalias' with a valid alias"); |
| result.SetStatus (eReturnStatusFailed); |
| } |
| |
| return result.Succeeded(); |
| } |
| }; |
| |
| #pragma mark CommandObjectCommandsDelete |
| //------------------------------------------------------------------------- |
| // CommandObjectCommandsDelete |
| //------------------------------------------------------------------------- |
| |
| class CommandObjectCommandsDelete : public CommandObjectParsed |
| { |
| public: |
| CommandObjectCommandsDelete(CommandInterpreter &interpreter) |
| : CommandObjectParsed(interpreter, "command delete", |
| "Delete one or more custom commands defined by 'command regex'.", nullptr) |
| { |
| CommandArgumentEntry arg; |
| CommandArgumentData alias_arg; |
| |
| // Define the first (and only) variant of this arg. |
| alias_arg.arg_type = eArgTypeCommandName; |
| alias_arg.arg_repetition = eArgRepeatPlain; |
| |
| // There is only one variant this argument could be; put it into the argument entry. |
| arg.push_back (alias_arg); |
| |
| // Push the data for the first argument into the m_arguments vector. |
| m_arguments.push_back (arg); |
| } |
| |
| ~CommandObjectCommandsDelete() override = default; |
| |
| protected: |
| bool |
| DoExecute (Args& args, CommandReturnObject &result) override |
| { |
| CommandObject::CommandMap::iterator pos; |
| |
| if (args.GetArgumentCount() != 0) |
| { |
| const char *command_name = args.GetArgumentAtIndex(0); |
| if (m_interpreter.CommandExists (command_name)) |
| { |
| if (m_interpreter.RemoveCommand (command_name)) |
| { |
| result.SetStatus (eReturnStatusSuccessFinishNoResult); |
| } |
| else |
| { |
| result.AppendErrorWithFormat ("'%s' is a permanent debugger command and cannot be removed.\n", |
| command_name); |
| result.SetStatus (eReturnStatusFailed); |
| } |
| } |
| else |
| { |
| StreamString error_msg_stream; |
| const bool generate_apropos = true; |
| const bool generate_type_lookup = false; |
| CommandObjectHelp::GenerateAdditionalHelpAvenuesMessage(&error_msg_stream, |
| command_name, |
| nullptr, |
| nullptr, |
| generate_apropos, |
| generate_type_lookup); |
| result.AppendErrorWithFormat ("%s", error_msg_stream.GetData()); |
| result.SetStatus (eReturnStatusFailed); |
| } |
| } |
| else |
| { |
| result.AppendErrorWithFormat( |
| "must call '%s' with one or more valid user defined regular expression command names", |
| GetCommandName()); |
| result.SetStatus (eReturnStatusFailed); |
| } |
| |
| return result.Succeeded(); |
| } |
| }; |
| |
| //------------------------------------------------------------------------- |
| // CommandObjectCommandsAddRegex |
| //------------------------------------------------------------------------- |
| #pragma mark CommandObjectCommandsAddRegex |
| |
| class CommandObjectCommandsAddRegex : |
| public CommandObjectParsed, |
| public IOHandlerDelegateMultiline |
| { |
| public: |
| CommandObjectCommandsAddRegex(CommandInterpreter &interpreter) |
| : CommandObjectParsed(interpreter, "command regex", |
| "Define a custom command in terms of existing commands by matching regular expressions.", |
| "command regex <cmd-name> [s/<regex>/<subst>/ ...]"), |
| IOHandlerDelegateMultiline("", IOHandlerDelegate::Completion::LLDBCommand), |
| m_options(interpreter) |
| { |
| SetHelpLong(R"( |
| )" "This command allows the user to create powerful regular expression commands \ |
| with substitutions. The regular expressions and substitutions are specified \ |
| using the regular expression substitution format of:" R"( |
| |
| s/<regex>/<subst>/ |
| |
| )" "<regex> is a regular expression that can use parenthesis to capture regular \ |
| expression input and substitute the captured matches in the output using %1 \ |
| for the first match, %2 for the second, and so on." R"( |
| |
| )" "The regular expressions can all be specified on the command line if more than \ |
| one argument is provided. If just the command name is provided on the command \ |
| line, then the regular expressions and substitutions can be entered on separate \ |
| lines, followed by an empty line to terminate the command definition." R"( |
| |
| EXAMPLES |
| |
| )" "The following example will define a regular expression command named 'f' that \ |
| will call 'finish' if there are no arguments, or 'frame select <frame-idx>' if \ |
| a number follows 'f':" R"( |
| |
| (lldb) command regex f s/^$/finish/ 's/([0-9]+)/frame select %1/')" |
| ); |
| } |
| |
| ~CommandObjectCommandsAddRegex() override = default; |
| |
| protected: |
| void |
| IOHandlerActivated (IOHandler &io_handler) override |
| { |
| StreamFileSP output_sp(io_handler.GetOutputStreamFile()); |
| if (output_sp) |
| { |
| output_sp->PutCString("Enter one of more sed substitution commands in the form: 's/<regex>/<subst>/'.\nTerminate the substitution list with an empty line.\n"); |
| output_sp->Flush(); |
| } |
| } |
| |
| void |
| IOHandlerInputComplete (IOHandler &io_handler, std::string &data) override |
| { |
| io_handler.SetIsDone(true); |
| if (m_regex_cmd_ap) |
| { |
| StringList lines; |
| if (lines.SplitIntoLines (data)) |
| { |
| const size_t num_lines = lines.GetSize(); |
| bool check_only = false; |
| for (size_t i=0; i<num_lines; ++i) |
| { |
| llvm::StringRef bytes_strref (lines[i]); |
| Error error = AppendRegexSubstitution (bytes_strref, check_only); |
| if (error.Fail()) |
| { |
| if (!m_interpreter.GetDebugger().GetCommandInterpreter().GetBatchCommandMode()) |
| { |
| StreamSP out_stream = m_interpreter.GetDebugger().GetAsyncOutputStream(); |
| out_stream->Printf("error: %s\n", error.AsCString()); |
| } |
| } |
| } |
| } |
| if (m_regex_cmd_ap->HasRegexEntries()) |
| { |
| CommandObjectSP cmd_sp (m_regex_cmd_ap.release()); |
| m_interpreter.AddCommand(cmd_sp->GetCommandName(), cmd_sp, true); |
| } |
| } |
| } |
| |
| bool |
| DoExecute (Args& command, CommandReturnObject &result) override |
| { |
| const size_t argc = command.GetArgumentCount(); |
| if (argc == 0) |
| { |
| result.AppendError ("usage: 'command regex <command-name> [s/<regex1>/<subst1>/ s/<regex2>/<subst2>/ ...]'\n"); |
| result.SetStatus (eReturnStatusFailed); |
| } |
| else |
| { |
| Error error; |
| const char *name = command.GetArgumentAtIndex(0); |
| m_regex_cmd_ap.reset (new CommandObjectRegexCommand (m_interpreter, |
| name, |
| m_options.GetHelp (), |
| m_options.GetSyntax (), |
| 10, |
| 0, |
| true)); |
| |
| if (argc == 1) |
| { |
| Debugger &debugger = m_interpreter.GetDebugger(); |
| bool color_prompt = debugger.GetUseColor(); |
| const bool multiple_lines = true; // Get multiple lines |
| IOHandlerSP io_handler_sp(new IOHandlerEditline(debugger, |
| IOHandler::Type::Other, |
| "lldb-regex", // Name of input reader for history |
| "> ", // Prompt |
| nullptr, // Continuation prompt |
| multiple_lines, |
| color_prompt, |
| 0, // Don't show line numbers |
| *this)); |
| |
| if (io_handler_sp) |
| { |
| debugger.PushIOHandler(io_handler_sp); |
| result.SetStatus (eReturnStatusSuccessFinishNoResult); |
| } |
| } |
| else |
| { |
| for (size_t arg_idx = 1; arg_idx < argc; ++arg_idx) |
| { |
| llvm::StringRef arg_strref (command.GetArgumentAtIndex(arg_idx)); |
| bool check_only = false; |
| error = AppendRegexSubstitution (arg_strref, check_only); |
| if (error.Fail()) |
| break; |
| } |
| |
| if (error.Success()) |
| { |
| AddRegexCommandToInterpreter(); |
| } |
| } |
| if (error.Fail()) |
| { |
| result.AppendError (error.AsCString()); |
| result.SetStatus (eReturnStatusFailed); |
| } |
| } |
| |
| return result.Succeeded(); |
| } |
| |
| Error |
| AppendRegexSubstitution (const llvm::StringRef ®ex_sed, bool check_only) |
| { |
| Error error; |
| |
| if (!m_regex_cmd_ap) |
| { |
| error.SetErrorStringWithFormat("invalid regular expression command object for: '%.*s'", |
| (int)regex_sed.size(), |
| regex_sed.data()); |
| return error; |
| } |
| |
| size_t regex_sed_size = regex_sed.size(); |
| |
| if (regex_sed_size <= 1) |
| { |
| error.SetErrorStringWithFormat("regular expression substitution string is too short: '%.*s'", |
| (int)regex_sed.size(), |
| regex_sed.data()); |
| return error; |
| } |
| |
| if (regex_sed[0] != 's') |
| { |
| error.SetErrorStringWithFormat("regular expression substitution string doesn't start with 's': '%.*s'", |
| (int)regex_sed.size(), |
| regex_sed.data()); |
| return error; |
| } |
| const size_t first_separator_char_pos = 1; |
| // use the char that follows 's' as the regex separator character |
| // so we can have "s/<regex>/<subst>/" or "s|<regex>|<subst>|" |
| const char separator_char = regex_sed[first_separator_char_pos]; |
| const size_t second_separator_char_pos = regex_sed.find (separator_char, first_separator_char_pos + 1); |
| |
| if (second_separator_char_pos == std::string::npos) |
| { |
| error.SetErrorStringWithFormat("missing second '%c' separator char after '%.*s' in '%.*s'", |
| separator_char, |
| (int)(regex_sed.size() - first_separator_char_pos - 1), |
| regex_sed.data() + (first_separator_char_pos + 1), |
| (int)regex_sed.size(), |
| regex_sed.data()); |
| return error; |
| } |
| |
| const size_t third_separator_char_pos = regex_sed.find (separator_char, second_separator_char_pos + 1); |
| |
| if (third_separator_char_pos == std::string::npos) |
| { |
| error.SetErrorStringWithFormat("missing third '%c' separator char after '%.*s' in '%.*s'", |
| separator_char, |
| (int)(regex_sed.size() - second_separator_char_pos - 1), |
| regex_sed.data() + (second_separator_char_pos + 1), |
| (int)regex_sed.size(), |
| regex_sed.data()); |
| return error; |
| } |
| |
| if (third_separator_char_pos != regex_sed_size - 1) |
| { |
| // Make sure that everything that follows the last regex |
| // separator char |
| if (regex_sed.find_first_not_of("\t\n\v\f\r ", third_separator_char_pos + 1) != std::string::npos) |
| { |
| error.SetErrorStringWithFormat("extra data found after the '%.*s' regular expression substitution string: '%.*s'", |
| (int)third_separator_char_pos + 1, |
| regex_sed.data(), |
| (int)(regex_sed.size() - third_separator_char_pos - 1), |
| regex_sed.data() + (third_separator_char_pos + 1)); |
| return error; |
| } |
| } |
| else if (first_separator_char_pos + 1 == second_separator_char_pos) |
| { |
| error.SetErrorStringWithFormat("<regex> can't be empty in 's%c<regex>%c<subst>%c' string: '%.*s'", |
| separator_char, |
| separator_char, |
| separator_char, |
| (int)regex_sed.size(), |
| regex_sed.data()); |
| return error; |
| } |
| else if (second_separator_char_pos + 1 == third_separator_char_pos) |
| { |
| error.SetErrorStringWithFormat("<subst> can't be empty in 's%c<regex>%c<subst>%c' string: '%.*s'", |
| separator_char, |
| separator_char, |
| separator_char, |
| (int)regex_sed.size(), |
| regex_sed.data()); |
| return error; |
| } |
| |
| if (!check_only) |
| { |
| std::string regex(regex_sed.substr(first_separator_char_pos + 1, second_separator_char_pos - first_separator_char_pos - 1)); |
| std::string subst(regex_sed.substr(second_separator_char_pos + 1, third_separator_char_pos - second_separator_char_pos - 1)); |
| m_regex_cmd_ap->AddRegexCommand (regex.c_str(), |
| subst.c_str()); |
| } |
| return error; |
| } |
| |
| void |
| AddRegexCommandToInterpreter() |
| { |
| if (m_regex_cmd_ap) |
| { |
| if (m_regex_cmd_ap->HasRegexEntries()) |
| { |
| CommandObjectSP cmd_sp (m_regex_cmd_ap.release()); |
| m_interpreter.AddCommand(cmd_sp->GetCommandName(), cmd_sp, true); |
| } |
| } |
| } |
| |
| private: |
| std::unique_ptr<CommandObjectRegexCommand> m_regex_cmd_ap; |
| |
| class CommandOptions : public Options |
| { |
| public: |
| CommandOptions (CommandInterpreter &interpreter) : |
| Options (interpreter) |
| { |
| } |
| |
| ~CommandOptions() override = default; |
| |
| Error |
| SetOptionValue (uint32_t option_idx, const char *option_arg) override |
| { |
| Error error; |
| const int short_option = m_getopt_table[option_idx].val; |
| |
| switch (short_option) |
| { |
| case 'h': |
| m_help.assign (option_arg); |
| break; |
| case 's': |
| m_syntax.assign (option_arg); |
| break; |
| default: |
| error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); |
| break; |
| } |
| |
| return error; |
| } |
| |
| void |
| OptionParsingStarting () override |
| { |
| m_help.clear(); |
| m_syntax.clear(); |
| } |
| |
| const OptionDefinition* |
| GetDefinitions () override |
| { |
| return g_option_table; |
| } |
| |
| // Options table: Required for subclasses of Options. |
| |
| static OptionDefinition g_option_table[]; |
| |
| const char * |
| GetHelp() |
| { |
| return (m_help.empty() ? nullptr : m_help.c_str()); |
| } |
| |
| const char * |
| GetSyntax () |
| { |
| return (m_syntax.empty() ? nullptr : m_syntax.c_str()); |
| } |
| |
| protected: |
| // Instance variables to hold the values for command options. |
| |
| std::string m_help; |
| std::string m_syntax; |
| }; |
| |
| Options * |
| GetOptions () override |
| { |
| return &m_options; |
| } |
| |
| CommandOptions m_options; |
| }; |
| |
| OptionDefinition |
| CommandObjectCommandsAddRegex::CommandOptions::g_option_table[] = |
| { |
| { LLDB_OPT_SET_1, false, "help" , 'h', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeNone, "The help text to display for this command."}, |
| { LLDB_OPT_SET_1, false, "syntax", 's', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeNone, "A syntax string showing the typical usage syntax."}, |
| { 0 , false, nullptr , 0 , 0 , nullptr, nullptr, 0, eArgTypeNone, nullptr } |
| }; |
| |
| class CommandObjectPythonFunction : public CommandObjectRaw |
| { |
| public: |
| CommandObjectPythonFunction (CommandInterpreter &interpreter, |
| std::string name, |
| std::string funct, |
| std::string help, |
| ScriptedCommandSynchronicity synch) : |
| CommandObjectRaw(interpreter, |
| name.c_str(), |
| nullptr, |
| nullptr), |
| m_function_name(funct), |
| m_synchro(synch), |
| m_fetched_help_long(false) |
| { |
| if (!help.empty()) |
| SetHelp(help.c_str()); |
| else |
| { |
| StreamString stream; |
| stream.Printf("For more information run 'help %s'",name.c_str()); |
| SetHelp(stream.GetData()); |
| } |
| } |
| |
| ~CommandObjectPythonFunction() override = default; |
| |
| bool |
| IsRemovable () const override |
| { |
| return true; |
| } |
| |
| const std::string& |
| GetFunctionName () |
| { |
| return m_function_name; |
| } |
| |
| ScriptedCommandSynchronicity |
| GetSynchronicity () |
| { |
| return m_synchro; |
| } |
| |
| const char * |
| GetHelpLong () override |
| { |
| if (!m_fetched_help_long) |
| { |
| ScriptInterpreter* scripter = m_interpreter.GetScriptInterpreter(); |
| if (scripter) |
| { |
| std::string docstring; |
| m_fetched_help_long = scripter->GetDocumentationForItem(m_function_name.c_str(),docstring); |
| if (!docstring.empty()) |
| SetHelpLong(docstring.c_str()); |
| } |
| } |
| return CommandObjectRaw::GetHelpLong(); |
| } |
| |
| protected: |
| bool |
| DoExecute (const char *raw_command_line, CommandReturnObject &result) override |
| { |
| ScriptInterpreter* scripter = m_interpreter.GetScriptInterpreter(); |
| |
| Error error; |
| |
| result.SetStatus(eReturnStatusInvalid); |
| |
| if (!scripter || !scripter->RunScriptBasedCommand(m_function_name.c_str(), |
| raw_command_line, |
| m_synchro, |
| result, |
| error, |
| m_exe_ctx)) |
| { |
| result.AppendError(error.AsCString()); |
| result.SetStatus(eReturnStatusFailed); |
| } |
| else |
| { |
| // Don't change the status if the command already set it... |
| if (result.GetStatus() == eReturnStatusInvalid) |
| { |
| if (result.GetOutputData() == nullptr || result.GetOutputData()[0] == '\0') |
| result.SetStatus(eReturnStatusSuccessFinishNoResult); |
| else |
| result.SetStatus(eReturnStatusSuccessFinishResult); |
| } |
| } |
| |
| return result.Succeeded(); |
| } |
| |
| private: |
| std::string m_function_name; |
| ScriptedCommandSynchronicity m_synchro; |
| bool m_fetched_help_long; |
| }; |
| |
| class CommandObjectScriptingObject : public CommandObjectRaw |
| { |
| public: |
| CommandObjectScriptingObject (CommandInterpreter &interpreter, |
| std::string name, |
| StructuredData::GenericSP cmd_obj_sp, |
| ScriptedCommandSynchronicity synch) : |
| CommandObjectRaw(interpreter, |
| name.c_str(), |
| nullptr, |
| nullptr), |
| m_cmd_obj_sp(cmd_obj_sp), |
| m_synchro(synch), |
| m_fetched_help_short(false), |
| m_fetched_help_long(false) |
| { |
| StreamString stream; |
| stream.Printf("For more information run 'help %s'",name.c_str()); |
| SetHelp(stream.GetData()); |
| if (ScriptInterpreter* scripter = m_interpreter.GetScriptInterpreter()) |
| GetFlags().Set(scripter->GetFlagsForCommandObject(cmd_obj_sp)); |
| } |
| |
| ~CommandObjectScriptingObject() override = default; |
| |
| bool |
| IsRemovable () const override |
| { |
| return true; |
| } |
| |
| StructuredData::GenericSP |
| GetImplementingObject () |
| { |
| return m_cmd_obj_sp; |
| } |
| |
| ScriptedCommandSynchronicity |
| GetSynchronicity () |
| { |
| return m_synchro; |
| } |
| |
| const char * |
| GetHelp () override |
| { |
| if (!m_fetched_help_short) |
| { |
| ScriptInterpreter* scripter = m_interpreter.GetScriptInterpreter(); |
| if (scripter) |
| { |
| std::string docstring; |
| m_fetched_help_short = scripter->GetShortHelpForCommandObject(m_cmd_obj_sp,docstring); |
| if (!docstring.empty()) |
| SetHelp(docstring.c_str()); |
| } |
| } |
| return CommandObjectRaw::GetHelp(); |
| } |
| |
| const char * |
| GetHelpLong () override |
| { |
| if (!m_fetched_help_long) |
| { |
| ScriptInterpreter* scripter = m_interpreter.GetScriptInterpreter(); |
| if (scripter) |
| { |
| std::string docstring; |
| m_fetched_help_long = scripter->GetLongHelpForCommandObject(m_cmd_obj_sp,docstring); |
| if (!docstring.empty()) |
| SetHelpLong(docstring.c_str()); |
| } |
| } |
| return CommandObjectRaw::GetHelpLong(); |
| } |
| |
| protected: |
| bool |
| DoExecute (const char *raw_command_line, CommandReturnObject &result) override |
| { |
| ScriptInterpreter* scripter = m_interpreter.GetScriptInterpreter(); |
| |
| Error error; |
| |
| result.SetStatus(eReturnStatusInvalid); |
| |
| if (!scripter || !scripter->RunScriptBasedCommand(m_cmd_obj_sp, |
| raw_command_line, |
| m_synchro, |
| result, |
| error, |
| m_exe_ctx)) |
| { |
| result.AppendError(error.AsCString()); |
| result.SetStatus(eReturnStatusFailed); |
| } |
| else |
| { |
| // Don't change the status if the command already set it... |
| if (result.GetStatus() == eReturnStatusInvalid) |
| { |
| if (result.GetOutputData() == nullptr || result.GetOutputData()[0] == '\0') |
| result.SetStatus(eReturnStatusSuccessFinishNoResult); |
| else |
| result.SetStatus(eReturnStatusSuccessFinishResult); |
| } |
| } |
| |
| return result.Succeeded(); |
| } |
| |
| private: |
| StructuredData::GenericSP m_cmd_obj_sp; |
| ScriptedCommandSynchronicity m_synchro; |
| bool m_fetched_help_short: 1; |
| bool m_fetched_help_long: 1; |
| }; |
| |
| //------------------------------------------------------------------------- |
| // CommandObjectCommandsScriptImport |
| //------------------------------------------------------------------------- |
| |
| class CommandObjectCommandsScriptImport : public CommandObjectParsed |
| { |
| public: |
| CommandObjectCommandsScriptImport (CommandInterpreter &interpreter) : |
| CommandObjectParsed(interpreter, |
| "command script import", |
| "Import a scripting module in LLDB.", |
| nullptr), |
| m_options(interpreter) |
| { |
| CommandArgumentEntry arg1; |
| CommandArgumentData cmd_arg; |
| |
| // Define the first (and only) variant of this arg. |
| cmd_arg.arg_type = eArgTypeFilename; |
| cmd_arg.arg_repetition = eArgRepeatPlus; |
| |
| // There is only one variant this argument could be; put it into the argument entry. |
| arg1.push_back (cmd_arg); |
| |
| // Push the data for the first argument into the m_arguments vector. |
| m_arguments.push_back (arg1); |
| } |
| |
| ~CommandObjectCommandsScriptImport() override = default; |
| |
| int |
| HandleArgumentCompletion (Args &input, |
| int &cursor_index, |
| int &cursor_char_position, |
| OptionElementVector &opt_element_vector, |
| int match_start_point, |
| int max_return_elements, |
| bool &word_complete, |
| StringList &matches) override |
| { |
| std::string completion_str (input.GetArgumentAtIndex(cursor_index)); |
| completion_str.erase (cursor_char_position); |
| |
| CommandCompletions::InvokeCommonCompletionCallbacks(m_interpreter, |
| CommandCompletions::eDiskFileCompletion, |
| completion_str.c_str(), |
| match_start_point, |
| max_return_elements, |
| nullptr, |
| word_complete, |
| matches); |
| return matches.GetSize(); |
| } |
| |
| Options * |
| GetOptions () override |
| { |
| return &m_options; |
| } |
| |
| protected: |
| class CommandOptions : public Options |
| { |
| public: |
| CommandOptions (CommandInterpreter &interpreter) : |
| Options (interpreter) |
| { |
| } |
| |
| ~CommandOptions() override = default; |
| |
| Error |
| SetOptionValue (uint32_t option_idx, const char *option_arg) override |
| { |
| Error error; |
| const int short_option = m_getopt_table[option_idx].val; |
| |
| switch (short_option) |
| { |
| case 'r': |
| m_allow_reload = true; |
| break; |
| default: |
| error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); |
| break; |
| } |
| |
| return error; |
| } |
| |
| void |
| OptionParsingStarting () override |
| { |
| m_allow_reload = true; |
| } |
| |
| const OptionDefinition* |
| GetDefinitions () override |
| { |
| return g_option_table; |
| } |
| |
| // Options table: Required for subclasses of Options. |
| |
| static OptionDefinition g_option_table[]; |
| |
| // Instance variables to hold the values for command options. |
| |
| bool m_allow_reload; |
| }; |
| |
| bool |
| DoExecute (Args& command, CommandReturnObject &result) override |
| { |
| if (m_interpreter.GetDebugger().GetScriptLanguage() != lldb::eScriptLanguagePython) |
| { |
| result.AppendError ("only scripting language supported for module importing is currently Python"); |
| result.SetStatus (eReturnStatusFailed); |
| return false; |
| } |
| |
| size_t argc = command.GetArgumentCount(); |
| if (0 == argc) |
| { |
| result.AppendError("command script import needs one or more arguments"); |
| result.SetStatus (eReturnStatusFailed); |
| return false; |
| } |
| |
| for (size_t i = 0; |
| i < argc; |
| i++) |
| { |
| std::string path = command.GetArgumentAtIndex(i); |
| Error error; |
| |
| const bool init_session = true; |
| // FIXME: this is necessary because CommandObject::CheckRequirements() assumes that |
| // commands won't ever be recursively invoked, but it's actually possible to craft |
| // a Python script that does other "command script imports" in __lldb_init_module |
| // the real fix is to have recursive commands possible with a CommandInvocation object |
| // separate from the CommandObject itself, so that recursive command invocations |
| // won't stomp on each other (wrt to execution contents, options, and more) |
| m_exe_ctx.Clear(); |
| if (m_interpreter.GetScriptInterpreter()->LoadScriptingModule(path.c_str(), |
| m_options.m_allow_reload, |
| init_session, |
| error)) |
| { |
| result.SetStatus (eReturnStatusSuccessFinishNoResult); |
| } |
| else |
| { |
| result.AppendErrorWithFormat("module importing failed: %s", error.AsCString()); |
| result.SetStatus (eReturnStatusFailed); |
| } |
| } |
| |
| return result.Succeeded(); |
| } |
| |
| CommandOptions m_options; |
| }; |
| |
| OptionDefinition |
| CommandObjectCommandsScriptImport::CommandOptions::g_option_table[] = |
| { |
| { LLDB_OPT_SET_1, false, "allow-reload", 'r', OptionParser::eNoArgument, nullptr, 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."}, |
| { 0, false, nullptr, 0, 0, nullptr, nullptr, 0, eArgTypeNone, nullptr } |
| }; |
| |
| //------------------------------------------------------------------------- |
| // CommandObjectCommandsScriptAdd |
| //------------------------------------------------------------------------- |
| |
| class CommandObjectCommandsScriptAdd : |
| public CommandObjectParsed, |
| public IOHandlerDelegateMultiline |
| { |
| public: |
| CommandObjectCommandsScriptAdd(CommandInterpreter &interpreter) : |
| CommandObjectParsed(interpreter, |
| "command script add", |
| "Add a scripted function as an LLDB command.", |
| nullptr), |
| IOHandlerDelegateMultiline ("DONE"), |
| m_options (interpreter) |
| { |
| CommandArgumentEntry arg1; |
| CommandArgumentData cmd_arg; |
| |
| // Define the first (and only) variant of this arg. |
| cmd_arg.arg_type = eArgTypeCommandName; |
| cmd_arg.arg_repetition = eArgRepeatPlain; |
| |
| // There is only one variant this argument could be; put it into the argument entry. |
| arg1.push_back (cmd_arg); |
| |
| // Push the data for the first argument into the m_arguments vector. |
| m_arguments.push_back (arg1); |
| } |
| |
| ~CommandObjectCommandsScriptAdd() override = default; |
| |
| Options * |
| GetOptions () override |
| { |
| return &m_options; |
| } |
| |
| protected: |
| class CommandOptions : public Options |
| { |
| public: |
| CommandOptions (CommandInterpreter &interpreter) : |
| Options (interpreter), |
| m_class_name(), |
| m_funct_name(), |
| m_short_help(), |
| m_synchronicity(eScriptedCommandSynchronicitySynchronous) |
| { |
| } |
| |
| ~CommandOptions() override = default; |
| |
| Error |
| SetOptionValue (uint32_t option_idx, const char *option_arg) override |
| { |
| Error error; |
| const int short_option = m_getopt_table[option_idx].val; |
| |
| switch (short_option) |
| { |
| case 'f': |
| if (option_arg) |
| m_funct_name.assign(option_arg); |
| break; |
| case 'c': |
| if (option_arg) |
| m_class_name.assign(option_arg); |
| break; |
| case 'h': |
| if (option_arg) |
| m_short_help.assign(option_arg); |
| break; |
| case 's': |
| m_synchronicity = (ScriptedCommandSynchronicity) Args::StringToOptionEnum(option_arg, g_option_table[option_idx].enum_values, 0, error); |
| if (!error.Success()) |
| error.SetErrorStringWithFormat ("unrecognized value for synchronicity '%s'", option_arg); |
| break; |
| default: |
| error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); |
| break; |
| } |
| |
| return error; |
| } |
| |
| void |
| OptionParsingStarting () override |
| { |
| m_class_name.clear(); |
| m_funct_name.clear(); |
| m_short_help.clear(); |
| m_synchronicity = eScriptedCommandSynchronicitySynchronous; |
| } |
| |
| const OptionDefinition* |
| GetDefinitions () override |
| { |
| return g_option_table; |
| } |
| |
| // Options table: Required for subclasses of Options. |
| |
| static OptionDefinition g_option_table[]; |
| |
| // Instance variables to hold the values for command options. |
| |
| std::string m_class_name; |
| std::string m_funct_name; |
| std::string m_short_help; |
| ScriptedCommandSynchronicity m_synchronicity; |
| }; |
| |
| void |
| IOHandlerActivated (IOHandler &io_handler) override |
| { |
| StreamFileSP output_sp(io_handler.GetOutputStreamFile()); |
| if (output_sp) |
| { |
| output_sp->PutCString(g_python_command_instructions); |
| output_sp->Flush(); |
| } |
| } |
| |
| |
| void |
| IOHandlerInputComplete (IOHandler &io_handler, std::string &data) override |
| { |
| StreamFileSP error_sp = io_handler.GetErrorStreamFile(); |
| |
| ScriptInterpreter *interpreter = m_interpreter.GetScriptInterpreter(); |
| if (interpreter) |
| { |
| |
| StringList lines; |
| lines.SplitIntoLines(data); |
| if (lines.GetSize() > 0) |
| { |
| std::string funct_name_str; |
| if (interpreter->GenerateScriptAliasFunction (lines, funct_name_str)) |
| { |
| if (funct_name_str.empty()) |
| { |
| error_sp->Printf ("error: unable to obtain a function name, didn't add python command.\n"); |
| error_sp->Flush(); |
| } |
| else |
| { |
| // everything should be fine now, let's add this alias |
| |
| CommandObjectSP command_obj_sp(new CommandObjectPythonFunction (m_interpreter, |
| m_cmd_name, |
| funct_name_str.c_str(), |
| m_short_help, |
| m_synchronicity)); |
| |
| if (!m_interpreter.AddUserCommand(m_cmd_name, command_obj_sp, true)) |
| { |
| error_sp->Printf ("error: unable to add selected command, didn't add python command.\n"); |
| error_sp->Flush(); |
| } |
| } |
| } |
| else |
| { |
| error_sp->Printf ("error: unable to create function, didn't add python command.\n"); |
| error_sp->Flush(); |
| } |
| } |
| else |
| { |
| error_sp->Printf ("error: empty function, didn't add python command.\n"); |
| error_sp->Flush(); |
| } |
| } |
| else |
| { |
| error_sp->Printf ("error: script interpreter missing, didn't add python command.\n"); |
| error_sp->Flush(); |
| } |
| |
| io_handler.SetIsDone(true); |
| } |
| |
| protected: |
| bool |
| DoExecute (Args& command, CommandReturnObject &result) override |
| { |
| if (m_interpreter.GetDebugger().GetScriptLanguage() != lldb::eScriptLanguagePython) |
| { |
| result.AppendError ("only scripting language supported for scripted commands is currently Python"); |
| result.SetStatus (eReturnStatusFailed); |
| return false; |
| } |
| |
| size_t argc = command.GetArgumentCount(); |
| |
| if (argc != 1) |
| { |
| result.AppendError ("'command script add' requires one argument"); |
| result.SetStatus (eReturnStatusFailed); |
| return false; |
| } |
| |
| // Store the options in case we get multi-line input |
| m_cmd_name = command.GetArgumentAtIndex(0); |
| m_short_help.assign(m_options.m_short_help); |
| m_synchronicity = m_options.m_synchronicity; |
| |
| if (m_options.m_class_name.empty()) |
| { |
| if (m_options.m_funct_name.empty()) |
| { |
| m_interpreter.GetPythonCommandsFromIOHandler(" ", // Prompt |
| *this, // IOHandlerDelegate |
| true, // Run IOHandler in async mode |
| nullptr); // Baton for the "io_handler" that will be passed back into our IOHandlerDelegate functions |
| } |
| else |
| { |
| CommandObjectSP new_cmd(new CommandObjectPythonFunction(m_interpreter, |
| m_cmd_name, |
| m_options.m_funct_name, |
| m_options.m_short_help, |
| m_synchronicity)); |
| if (m_interpreter.AddUserCommand(m_cmd_name, new_cmd, true)) |
| { |
| result.SetStatus (eReturnStatusSuccessFinishNoResult); |
| } |
| else |
| { |
| result.AppendError("cannot add command"); |
| result.SetStatus (eReturnStatusFailed); |
| } |
| } |
| } |
| else |
| { |
| ScriptInterpreter *interpreter = GetCommandInterpreter().GetScriptInterpreter(); |
| if (!interpreter) |
| { |
| result.AppendError("cannot find ScriptInterpreter"); |
| result.SetStatus(eReturnStatusFailed); |
| return false; |
| } |
| |
| auto cmd_obj_sp = interpreter->CreateScriptCommandObject(m_options.m_class_name.c_str()); |
| if (!cmd_obj_sp) |
| { |
| result.AppendError("cannot create helper object"); |
| result.SetStatus(eReturnStatusFailed); |
| return false; |
| } |
| |
| CommandObjectSP new_cmd(new CommandObjectScriptingObject(m_interpreter, |
| m_cmd_name, |
| cmd_obj_sp, |
| m_synchronicity)); |
| if (m_interpreter.AddUserCommand(m_cmd_name, new_cmd, true)) |
| { |
| result.SetStatus (eReturnStatusSuccessFinishNoResult); |
| } |
| else |
| { |
| result.AppendError("cannot add command"); |
| result.SetStatus (eReturnStatusFailed); |
| } |
| } |
| |
| return result.Succeeded(); |
| } |
| |
| CommandOptions m_options; |
| std::string m_cmd_name; |
| std::string m_short_help; |
| ScriptedCommandSynchronicity m_synchronicity; |
| }; |
| |
| static OptionEnumValueElement g_script_synchro_type[] = |
| { |
| { eScriptedCommandSynchronicitySynchronous, "synchronous", "Run synchronous"}, |
| { eScriptedCommandSynchronicityAsynchronous, "asynchronous", "Run asynchronous"}, |
| { eScriptedCommandSynchronicityCurrentValue, "current", "Do not alter current setting"}, |
| { 0, nullptr, nullptr } |
| }; |
| |
| OptionDefinition |
| CommandObjectCommandsScriptAdd::CommandOptions::g_option_table[] = |
| { |
| { LLDB_OPT_SET_1, false, "function", 'f', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypePythonFunction, "Name of the Python function to bind to this command name."}, |
| { LLDB_OPT_SET_2, false, "class", 'c', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypePythonClass, "Name of the Python class to bind to this command name."}, |
| { LLDB_OPT_SET_1, false, "help" , 'h', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeHelpText, "The help text to display for this command."}, |
| { LLDB_OPT_SET_ALL, false, "synchronicity", 's', OptionParser::eRequiredArgument, nullptr, g_script_synchro_type, 0, eArgTypeScriptedCommandSynchronicity, "Set the synchronicity of this command's executions with regard to LLDB event system."}, |
| { 0, false, nullptr, 0, 0, nullptr, nullptr, 0, eArgTypeNone, nullptr } |
| }; |
| |
| //------------------------------------------------------------------------- |
| // CommandObjectCommandsScriptList |
| //------------------------------------------------------------------------- |
| |
| class CommandObjectCommandsScriptList : public CommandObjectParsed |
| { |
| public: |
| CommandObjectCommandsScriptList(CommandInterpreter &interpreter) : |
| CommandObjectParsed(interpreter, |
| "command script list", |
| "List defined scripted commands.", |
| nullptr) |
| { |
| } |
| |
| ~CommandObjectCommandsScriptList() override = default; |
| |
| bool |
| DoExecute (Args& command, CommandReturnObject &result) override |
| { |
| m_interpreter.GetHelp(result, |
| CommandInterpreter::eCommandTypesUserDef); |
| |
| result.SetStatus (eReturnStatusSuccessFinishResult); |
| |
| return true; |
| } |
| }; |
| |
| //------------------------------------------------------------------------- |
| // CommandObjectCommandsScriptClear |
| //------------------------------------------------------------------------- |
| |
| class CommandObjectCommandsScriptClear : public CommandObjectParsed |
| { |
| public: |
| CommandObjectCommandsScriptClear(CommandInterpreter &interpreter) : |
| CommandObjectParsed(interpreter, |
| "command script clear", |
| "Delete all scripted commands.", |
| nullptr) |
| { |
| } |
| |
| ~CommandObjectCommandsScriptClear() override = default; |
| |
| protected: |
| bool |
| DoExecute (Args& command, CommandReturnObject &result) override |
| { |
| m_interpreter.RemoveAllUser(); |
| |
| result.SetStatus (eReturnStatusSuccessFinishResult); |
| |
| return true; |
| } |
| }; |
| |
| //------------------------------------------------------------------------- |
| // CommandObjectCommandsScriptDelete |
| //------------------------------------------------------------------------- |
| |
| class CommandObjectCommandsScriptDelete : public CommandObjectParsed |
| { |
| public: |
| CommandObjectCommandsScriptDelete(CommandInterpreter &interpreter) : |
| CommandObjectParsed(interpreter, |
| "command script delete", |
| "Delete a scripted command.", |
| nullptr) |
| { |
| CommandArgumentEntry arg1; |
| CommandArgumentData cmd_arg; |
| |
| // Define the first (and only) variant of this arg. |
| cmd_arg.arg_type = eArgTypeCommandName; |
| cmd_arg.arg_repetition = eArgRepeatPlain; |
| |
| // There is only one variant this argument could be; put it into the argument entry. |
| arg1.push_back (cmd_arg); |
| |
| // Push the data for the first argument into the m_arguments vector. |
| m_arguments.push_back (arg1); |
| } |
| |
| ~CommandObjectCommandsScriptDelete() override = default; |
| |
| protected: |
| bool |
| DoExecute (Args& command, CommandReturnObject &result) override |
| { |
| |
| size_t argc = command.GetArgumentCount(); |
| |
| if (argc != 1) |
| { |
| result.AppendError ("'command script delete' requires one argument"); |
| result.SetStatus (eReturnStatusFailed); |
| return false; |
| } |
| |
| const char* cmd_name = command.GetArgumentAtIndex(0); |
| |
| if (cmd_name && *cmd_name && m_interpreter.HasUserCommands() && m_interpreter.UserCommandExists(cmd_name)) |
| { |
| m_interpreter.RemoveUser(cmd_name); |
| result.SetStatus (eReturnStatusSuccessFinishResult); |
| } |
| else |
| { |
| result.AppendErrorWithFormat ("command %s not found", cmd_name); |
| result.SetStatus (eReturnStatusFailed); |
| } |
| |
| return result.Succeeded(); |
| } |
| }; |
| |
| #pragma mark CommandObjectMultiwordCommandsScript |
| |
| //------------------------------------------------------------------------- |
| // CommandObjectMultiwordCommandsScript |
| //------------------------------------------------------------------------- |
| |
| class CommandObjectMultiwordCommandsScript : public CommandObjectMultiword |
| { |
| public: |
| CommandObjectMultiwordCommandsScript(CommandInterpreter &interpreter) |
| : CommandObjectMultiword(interpreter, "command script", |
| "Commands for managing custom commands implemented by interpreter scripts.", |
| "command script <subcommand> [<subcommand-options>]") |
| { |
| LoadSubCommand ("add", CommandObjectSP (new CommandObjectCommandsScriptAdd (interpreter))); |
| LoadSubCommand ("delete", CommandObjectSP (new CommandObjectCommandsScriptDelete (interpreter))); |
| LoadSubCommand ("clear", CommandObjectSP (new CommandObjectCommandsScriptClear (interpreter))); |
| LoadSubCommand ("list", CommandObjectSP (new CommandObjectCommandsScriptList (interpreter))); |
| LoadSubCommand ("import", CommandObjectSP (new CommandObjectCommandsScriptImport (interpreter))); |
| } |
| |
| ~CommandObjectMultiwordCommandsScript() override = default; |
| }; |
| |
| #pragma mark CommandObjectMultiwordCommands |
| |
| //------------------------------------------------------------------------- |
| // CommandObjectMultiwordCommands |
| //------------------------------------------------------------------------- |
| |
| CommandObjectMultiwordCommands::CommandObjectMultiwordCommands(CommandInterpreter &interpreter) |
| : CommandObjectMultiword(interpreter, "command", "Commands for managing custom LLDB commands.", |
| "command <subcommand> [<subcommand-options>]") |
| { |
| LoadSubCommand ("source", CommandObjectSP (new CommandObjectCommandsSource (interpreter))); |
| LoadSubCommand ("alias", CommandObjectSP (new CommandObjectCommandsAlias (interpreter))); |
| LoadSubCommand ("unalias", CommandObjectSP (new CommandObjectCommandsUnalias (interpreter))); |
| LoadSubCommand ("delete", CommandObjectSP (new CommandObjectCommandsDelete (interpreter))); |
| LoadSubCommand ("regex", CommandObjectSP (new CommandObjectCommandsAddRegex (interpreter))); |
| LoadSubCommand ("history", CommandObjectSP (new CommandObjectCommandsHistory (interpreter))); |
| LoadSubCommand ("script", CommandObjectSP (new CommandObjectMultiwordCommandsScript (interpreter))); |
| } |
| |
| CommandObjectMultiwordCommands::~CommandObjectMultiwordCommands() = default; |