Index: lldb/source/Commands/CommandObjectRegexCommand.h =================================================================== --- lldb/source/Commands/CommandObjectRegexCommand.h +++ lldb/source/Commands/CommandObjectRegexCommand.h @@ -39,6 +39,15 @@ protected: bool DoExecute(llvm::StringRef command, CommandReturnObject &result) override; + /// Substitute variables of the format %1 in the input string. + /// + /// The replacements vector is expected to be the result of a regex match + /// with the first element being the full match. That means that %1 will be + /// matched with replacements[1] and not replacements[0]. + static std::string SubstituteVariables( + llvm::StringRef input, + const llvm::SmallVectorImpl &replacements); + struct Entry { RegularExpression regex; std::string command; Index: lldb/source/Commands/CommandObjectRegexCommand.cpp =================================================================== --- lldb/source/Commands/CommandObjectRegexCommand.cpp +++ lldb/source/Commands/CommandObjectRegexCommand.cpp @@ -25,28 +25,38 @@ // Destructor CommandObjectRegexCommand::~CommandObjectRegexCommand() = default; +std::string CommandObjectRegexCommand::SubstituteVariables( + llvm::StringRef input, + const llvm::SmallVectorImpl &replacements) { + std::string buffer; + llvm::raw_string_ostream output(buffer); + + llvm::SmallVector parts; + input.split(parts, '%'); + + output << parts[0]; + for (llvm::StringRef part : drop_begin(parts)) { + size_t idx = 0; + if (part.consumeInteger(10, idx)) + output << '%'; + else if (idx > 0 && idx < replacements.size()) + output << replacements[idx]; + else + output << '%' << idx; + output << part; + } + + return output.str(); +} + bool CommandObjectRegexCommand::DoExecute(llvm::StringRef command, CommandReturnObject &result) { EntryCollection::const_iterator pos, end = m_entries.end(); for (pos = m_entries.begin(); pos != end; ++pos) { llvm::SmallVector matches; if (pos->regex.Execute(command, &matches)) { - std::string new_command(pos->command); - char percent_var[8]; - size_t idx, percent_var_idx; - for (uint32_t match_idx = 1; match_idx <= m_max_matches; ++match_idx) { - if (match_idx < matches.size()) { - const std::string match_str = matches[match_idx].str(); - const int percent_var_len = - ::snprintf(percent_var, sizeof(percent_var), "%%%u", match_idx); - for (idx = 0; (percent_var_idx = new_command.find( - percent_var, idx)) != std::string::npos;) { - new_command.erase(percent_var_idx, percent_var_len); - new_command.insert(percent_var_idx, match_str); - idx = percent_var_idx + match_str.size(); - } - } - } + std::string new_command = SubstituteVariables(pos->command, matches); + // Interpret the new command and return this as the result! if (m_interpreter.GetExpandRegexAliases()) result.GetOutputStream().Printf("%s\n", new_command.c_str()); Index: lldb/unittests/Interpreter/CMakeLists.txt =================================================================== --- lldb/unittests/Interpreter/CMakeLists.txt +++ lldb/unittests/Interpreter/CMakeLists.txt @@ -4,6 +4,7 @@ TestOptionArgParser.cpp TestOptionValue.cpp TestOptionValueFileColonLine.cpp + TestRegexCommand.cpp LINK_LIBS lldbCore Index: lldb/unittests/Interpreter/TestRegexCommand.cpp =================================================================== --- /dev/null +++ lldb/unittests/Interpreter/TestRegexCommand.cpp @@ -0,0 +1,50 @@ +//===-- TestRegexCommand.cpp ----------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "Commands/CommandObjectRegexCommand.h" + +#include "gtest/gtest.h" + +using namespace lldb_private; +using namespace lldb; + +namespace { +class TestRegexCommand : public CommandObjectRegexCommand { +public: + using CommandObjectRegexCommand::SubstituteVariables; +}; +} // namespace + +TEST(RegexCommandTest, SubstituteVariables) { + const llvm::SmallVector substitutions = {"UNUSED", "foo", + "bar", "baz"}; + + EXPECT_EQ(TestRegexCommand::SubstituteVariables("%0", substitutions), "%0"); + EXPECT_EQ(TestRegexCommand::SubstituteVariables("%1", substitutions), "foo"); + EXPECT_EQ(TestRegexCommand::SubstituteVariables("%2", substitutions), "bar"); + EXPECT_EQ(TestRegexCommand::SubstituteVariables("%3", substitutions), "baz"); + EXPECT_EQ(TestRegexCommand::SubstituteVariables("%1%2%3", substitutions), + "foobarbaz"); + EXPECT_EQ(TestRegexCommand::SubstituteVariables("%1%2%3%4", substitutions), + "foobarbaz%4"); + EXPECT_EQ(TestRegexCommand::SubstituteVariables("#%1#%2#%3#", substitutions), + "#foo#bar#baz#"); + EXPECT_EQ(TestRegexCommand::SubstituteVariables("%11", substitutions), "%11"); +} + +TEST(RegexCommandTest, SubstituteVariablesNoRecursion) { + const llvm::SmallVector substitutions = {"UNUSED", "%2", + "%3", "%4"}; + + EXPECT_EQ(TestRegexCommand::SubstituteVariables("%0", substitutions), "%0"); + EXPECT_EQ(TestRegexCommand::SubstituteVariables("%1", substitutions), "%2"); + EXPECT_EQ(TestRegexCommand::SubstituteVariables("%2", substitutions), "%3"); + EXPECT_EQ(TestRegexCommand::SubstituteVariables("%3", substitutions), "%4"); + EXPECT_EQ(TestRegexCommand::SubstituteVariables("%1%2%3", substitutions), + "%2%3%4"); +}