diff options
Diffstat (limited to 'lldb/source/Commands/CommandObjectCommands.cpp')
-rw-r--r-- | lldb/source/Commands/CommandObjectCommands.cpp | 255 |
1 files changed, 185 insertions, 70 deletions
diff --git a/lldb/source/Commands/CommandObjectCommands.cpp b/lldb/source/Commands/CommandObjectCommands.cpp index bc04b6b..142f29d 100644 --- a/lldb/source/Commands/CommandObjectCommands.cpp +++ b/lldb/source/Commands/CommandObjectCommands.cpp @@ -12,6 +12,8 @@ // C Includes // C++ Includes // Other libraries and framework includes +#include "llvm/ADT/StringRef.h" + // Project includes #include "lldb/Core/Debugger.h" #include "lldb/Core/InputReader.h" @@ -105,7 +107,7 @@ private: public: CommandObjectCommandsSource(CommandInterpreter &interpreter) : CommandObject (interpreter, - "commands source", + "command source", "Read in debugger commands from the file <filename> and execute them.", NULL), m_options (interpreter) @@ -183,7 +185,7 @@ class CommandObjectCommandsAlias : public CommandObject public: CommandObjectCommandsAlias (CommandInterpreter &interpreter) : CommandObject (interpreter, - "commands alias", + "command alias", "Allow users to define their own debugger command abbreviations.", NULL) { @@ -559,7 +561,7 @@ class CommandObjectCommandsUnalias : public CommandObject public: CommandObjectCommandsUnalias (CommandInterpreter &interpreter) : CommandObject (interpreter, - "commands unalias", + "command unalias", "Allow the user to remove/delete a user-defined command abbreviation.", NULL) { @@ -648,11 +650,34 @@ class CommandObjectCommandsAddRegex : public CommandObject public: CommandObjectCommandsAddRegex (CommandInterpreter &interpreter) : CommandObject (interpreter, - "commands regex", + "command regex", "Allow the user to create a regular expression command.", - NULL), + "command regex <cmd-name> [s/<regex>/<subst>/ ...]"), m_options (interpreter) { + SetHelpLong( +"This command allows the user to create powerful regular expression commands\n" +"with substitutions. The regular expressions and substitutions are specified\n" +"using the regular exression substitution format of:\n" +"\n" +" s/<regex>/<subst>/\n" +"\n" +"<regex> is a regular expression that can use parenthesis to capture regular\n" +"expression input and substitute the captured matches in the output using %1\n" +"for the first match, %2 for the second, and so on.\n" +"\n" +"The regular expressions can all be specified on the command line if more than\n" +"one argument is provided. If just the command name is provided on the command\n" +"line, then the regular expressions and substitutions can be entered on separate\n" +" lines, followed by an empty line to terminate the command definition.\n" +"\n" +"EXAMPLES\n" +"\n" +"The following example with define a regular expression command named 'f' that\n" +"will call 'finish' if there are no arguments, or 'frame select <frame-idx>' if\n" +"a number follows 'f':\n" +"(lldb) command regex f s/^$/finish/ 's/([0-9]+)/frame select %1/'\n" + ); } ~CommandObjectCommandsAddRegex() @@ -663,56 +688,166 @@ public: bool Execute (Args& args, CommandReturnObject &result) { - if (args.GetArgumentCount() == 1) + const size_t argc = args.GetArgumentCount(); + if (argc == 0) { + result.AppendError ("usage: 'commands regex <command-name> [s/<regex1>/<subst1>/ s/<regex2>/<subst2>/ ...]'\n"); + result.SetStatus (eReturnStatusFailed); + } + else + { + Error error; const char *name = args.GetArgumentAtIndex(0); - InputReaderSP reader_sp (new InputReader(m_interpreter.GetDebugger())); m_regex_cmd_ap.reset (new CommandObjectRegexCommand (m_interpreter, name, m_options.GetHelp (), m_options.GetSyntax (), 10)); - if (reader_sp) + + if (argc == 1) { - Error err (reader_sp->Initialize (CommandObjectCommandsAddRegex::InputReaderCallback, + InputReaderSP reader_sp (new InputReader(m_interpreter.GetDebugger())); + if (reader_sp) + { + error =reader_sp->Initialize (CommandObjectCommandsAddRegex::InputReaderCallback, this, // baton eInputReaderGranularityLine, // token size, to pass to callback function - "DONE", // end token + NULL, // end token "> ", // prompt - true)); // echo input - if (err.Success()) + true); // echo input + if (error.Success()) + { + m_interpreter.GetDebugger().PushInputReader (reader_sp); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + return true; + } + } + } + else + { + for (size_t arg_idx = 1; arg_idx < argc; ++arg_idx) { - m_interpreter.GetDebugger().PushInputReader (reader_sp); - result.SetStatus (eReturnStatusSuccessFinishNoResult); + llvm::StringRef arg_strref (args.GetArgumentAtIndex(arg_idx)); + error = AppendRegexSubstitution (arg_strref); + if (error.Fail()) + break; } - else + + if (error.Success()) { - result.AppendError (err.AsCString()); - result.SetStatus (eReturnStatusFailed); + AddRegexCommandToInterpreter(); } } + if (error.Fail()) + { + result.AppendError (error.AsCString()); + result.SetStatus (eReturnStatusFailed); + } } - else - { - result.AppendError ("invalid arguments.\n"); - result.SetStatus (eReturnStatusFailed); - } + return result.Succeeded(); } - bool - AppendRegexAndSubstitution (const char *regex, const char *subst) + Error + AppendRegexSubstitution (const llvm::StringRef ®ex_sed) { - if (m_regex_cmd_ap.get()) + Error error; + + if (m_regex_cmd_ap.get() == NULL) + { + 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) { - m_regex_cmd_ap->AddRegexCommand (regex, subst); - return true; + error.SetErrorStringWithFormat("regular expression substitution string is too short: '%.*s'", + (int)regex_sed.size(), + regex_sed.data()); + return error; } - return false; + + 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'", + separator_char, + (int)(regex_sed.size() - first_separator_char_pos - 1), + regex_sed.data() + (first_separator_char_pos + 1)); + 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'", + separator_char, + (int)(regex_sed.size() - second_separator_char_pos - 1), + regex_sed.data() + (second_separator_char_pos + 1)); + 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; + } + 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 - InputReaderIsDone() + AddRegexCommandToInterpreter() { if (m_regex_cmd_ap.get()) { @@ -724,6 +859,12 @@ public: } } + void + InputReaderDidCancel() + { + m_regex_cmd_ap.reset(); + } + static size_t InputReaderCallback (void *baton, InputReader &reader, @@ -827,7 +968,7 @@ CommandObjectCommandsAddRegex::InputReaderCallback (void *baton, switch (notification) { case eInputReaderActivate: - reader.GetDebugger().GetOutputStream().Printf("%s\n", "Enter multiple regular expressions in the form s/find/replace/ then terminate with an empty line:"); + reader.GetDebugger().GetOutputStream().Printf("%s\n", "Enter regular expressions in the form 's/<regex>/<subst>/' and terminate with an empty line:"); break; case eInputReaderReactivate: break; @@ -836,46 +977,19 @@ CommandObjectCommandsAddRegex::InputReaderCallback (void *baton, break; case eInputReaderGotToken: + while (bytes_len > 0 && (bytes[bytes_len-1] == '\r' || bytes[bytes_len-1] == '\n')) + --bytes_len; if (bytes_len == 0) reader.SetIsDone(true); else if (bytes) { - std::string regex_sed (bytes, bytes_len); - bool success = regex_sed.size() > 3 && regex_sed[0] == 's'; - if (success) + llvm::StringRef bytes_strref (bytes, bytes_len); + Error error (add_regex_cmd->AppendRegexSubstitution (bytes_strref)); + if (error.Fail()) { - const size_t first_separator_char_pos = 1; - const char separator_char = regex_sed[first_separator_char_pos]; - const size_t third_separator_char_pos = regex_sed.rfind (separator_char); - - if (third_separator_char_pos != regex_sed.size() - 1) - success = false; // Didn't end with regex separator char - else - { - 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) - success = false; // Didn't find second regex separator char - else - { - if (second_separator_char_pos <= 3) - success = false; // Empty regex is invalid ("s///") - else - { - 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)); - if (regex.empty() || subst.empty()) - success= false; - else - { - add_regex_cmd->AppendRegexAndSubstitution(regex.c_str(), subst.c_str()); - } - } - } - } - } - if (!success) - { - reader.GetDebugger().GetOutputStream().PutCString("Regular expressions should be in the form s/<regex>/<subst>/.\n"); + reader.GetDebugger().GetOutputStream().Printf("error: %s\n", error.AsCString()); + add_regex_cmd->InputReaderDidCancel (); + reader.SetIsDone (true); } } break; @@ -883,6 +997,7 @@ CommandObjectCommandsAddRegex::InputReaderCallback (void *baton, case eInputReaderInterrupt: reader.SetIsDone (true); reader.GetDebugger().GetOutputStream().PutCString("Regular expression command creations was cancelled.\n"); + add_regex_cmd->InputReaderDidCancel (); break; case eInputReaderEndOfFile: @@ -890,7 +1005,7 @@ CommandObjectCommandsAddRegex::InputReaderCallback (void *baton, break; case eInputReaderDone: - add_regex_cmd->InputReaderIsDone(); + add_regex_cmd->AddRegexCommandToInterpreter(); break; } @@ -901,9 +1016,9 @@ CommandObjectCommandsAddRegex::InputReaderCallback (void *baton, OptionDefinition CommandObjectCommandsAddRegex::CommandOptions::g_option_table[] = { -{ LLDB_OPT_SET_1, false, "help", 'h', required_argument, NULL, 0, eArgTypeNone, "The help text to display for this command."}, +{ LLDB_OPT_SET_1, false, "help" , 'h', required_argument, NULL, 0, eArgTypeNone, "The help text to display for this command."}, { LLDB_OPT_SET_1, false, "syntax", 's', required_argument, NULL, 0, eArgTypeNone, "A syntax string showing the typical usage syntax."}, -{ 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +{ 0 , false, NULL , 0 , 0 , NULL, 0, eArgTypeNone, NULL } }; @@ -915,9 +1030,9 @@ CommandObjectCommandsAddRegex::CommandOptions::g_option_table[] = CommandObjectMultiwordCommands::CommandObjectMultiwordCommands (CommandInterpreter &interpreter) : CommandObjectMultiword (interpreter, - "commands", + "command", "A set of commands for managing or customizing the debugger commands.", - "commands <subcommand> [<subcommand-options>]") + "command <subcommand> [<subcommand-options>]") { LoadSubCommand ("source", CommandObjectSP (new CommandObjectCommandsSource (interpreter))); LoadSubCommand ("alias", CommandObjectSP (new CommandObjectCommandsAlias (interpreter))); |