Skip to content

Commit 568e980

Browse files
committed
pcre2
1 parent 9aad78c commit 568e980

10 files changed

Lines changed: 130 additions & 7 deletions

File tree

cli/cmdlineparser.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1356,6 +1356,9 @@ CmdLineParser::Result CmdLineParser::parseFromArgs(int argc, const char* const a
13561356
if (std::strcmp(engine, "pcre") == 0) {
13571357
rule.engine = Regex::Engine::Pcre;
13581358
}
1359+
else if (std::strcmp(engine, "pcre2") == 0) {
1360+
rule.engine = Regex::Engine::Pcre2;
1361+
}
13591362
else {
13601363
mLogger.printError(std::string("unknown regex engine '") + engine + "'.");
13611364
return Result::Fail;

cmake/findDependencies.cmake

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,13 @@ if(HAVE_RULES)
3232
find_path(PCRE_INCLUDE pcre.h)
3333
find_library(PCRE_LIBRARY NAMES pcre pcred)
3434
if(NOT PCRE_LIBRARY OR NOT PCRE_INCLUDE)
35-
message(FATAL_ERROR "pcre dependency for RULES has not been found")
35+
message(FATAL_ERROR "PCRE dependency for RULES has not been found")
36+
endif()
37+
38+
find_path(PCRE2_INCLUDE pcre2.h)
39+
find_library(PCRE2_LIBRARY NAMES pcre2-8) # TODO: is this the proper library?
40+
if(NOT PCRE2_LIBRARY OR NOT PCRE2_INCLUDE)
41+
message(FATAL_ERROR "PCRE2 dependency for RULES has not been found")
3642
endif()
3743
else()
3844
set(PCRE_LIBRARY "")

cmake/printInfo.cmake

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ message(STATUS)
8080
message(STATUS "HAVE_RULES = ${HAVE_RULES}")
8181
if(HAVE_RULES)
8282
message(STATUS "PCRE_LIBRARY = ${PCRE_LIBRARY}")
83+
message(STATUS "PCRE2_INCLUDE = ${PCRE2_INCLUDE}")
84+
message(STATUS "PCRE2_LIBRARY = ${PCRE2_LIBRARY}")
8385
endif()
8486
message(STATUS)
8587
message(STATUS "DISALLOW_THREAD_EXECUTOR = ${DISALLOW_THREAD_EXECUTOR}")

gui/test/resultstree/CMakeLists.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ add_executable(test-resultstree
2424
target_include_directories(test-resultstree PRIVATE ${CMAKE_SOURCE_DIR}/gui)
2525
target_link_libraries(test-resultstree cppcheck-core simplecpp tinyxml2)
2626
if (HAVE_RULES)
27-
target_link_libraries(test-resultstree ${PCRE_LIBRARY})
28-
target_include_directories(test-resultstree SYSTEM PRIVATE ${PCRE_INCLUDE})
27+
target_link_libraries(test-resultstree ${PCRE_LIBRARY} ${PCRE2_LIBRARY})
28+
target_include_directories(test-resultstree SYSTEM PRIVATE ${PCRE_INCLUDE} ${PCRE2_INCLUDE})
2929
endif()
3030
target_compile_definitions(test-resultstree PRIVATE SRCDIR="${CMAKE_CURRENT_SOURCE_DIR}")
3131
target_link_libraries(test-resultstree ${QT_CORE_LIB} ${QT_GUI_LIB} ${QT_WIDGETS_LIB} ${QT_TEST_LIB})

lib/CMakeLists.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,10 @@ endif()
5353
target_dll_compile_definitions(cppcheck-core EXPORT CPPCHECKLIB_EXPORT IMPORT CPPCHECKLIB_IMPORT)
5454

5555
target_include_directories(cppcheck-core PUBLIC .)
56-
target_link_libraries(cppcheck-core PRIVATE tinyxml2 simplecpp picojson ${PCRE_LIBRARY})
56+
target_link_libraries(cppcheck-core PRIVATE tinyxml2 simplecpp picojson ${PCRE_LIBRARY} ${PCRE2_LIBRARY})
5757

5858
if (HAVE_RULES)
59-
target_include_directories(cppcheck-core SYSTEM PRIVATE ${PCRE_INCLUDE})
59+
target_include_directories(cppcheck-core SYSTEM PRIVATE ${PCRE_INCLUDE} ${PCRE2_INCLUDE})
6060
endif()
6161
if (Boost_FOUND)
6262
target_include_directories(cppcheck-core SYSTEM PUBLIC ${Boost_INCLUDE_DIRS})

lib/regex.cpp

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@
2727
#endif
2828
#include <pcre.h>
2929

30+
#define PCRE2_CODE_UNIT_WIDTH 8
31+
#include <pcre2.h>
32+
3033
namespace {
3134
class PcreRegex : public Regex
3235
{
@@ -248,6 +251,85 @@ namespace {
248251
}
249252
}
250253

254+
namespace {
255+
class Pcre2Regex : public Regex
256+
{
257+
public:
258+
explicit Pcre2Regex(std::string pattern)
259+
: mPattern(std::move(pattern))
260+
{}
261+
262+
~Pcre2Regex() override
263+
{
264+
if (mRe) {
265+
pcre_free(mRe);
266+
mRe = nullptr;
267+
}
268+
}
269+
270+
std::string compile();
271+
std::string match(const std::string& str, const MatchFn& match) const override;
272+
273+
private:
274+
std::string mPattern;
275+
pcre2_code* mRe{};
276+
};
277+
278+
std::string Pcre2Regex::compile()
279+
{
280+
if (mRe)
281+
return "regular expression has already been compiled";
282+
283+
int errnumber = 0;
284+
size_t erroffset = 0;
285+
pcre2_code * const re = pcre2_compile(reinterpret_cast<PCRE2_SPTR8>(mPattern.c_str()), PCRE2_ZERO_TERMINATED, 0, &errnumber, &erroffset, nullptr);
286+
if (!re) {
287+
PCRE2_UCHAR buffer[256];
288+
pcre2_get_error_message(errnumber, buffer, sizeof(buffer));
289+
return reinterpret_cast<char*>(buffer);
290+
}
291+
292+
mRe = re;
293+
294+
return "";
295+
}
296+
297+
std::string Pcre2Regex::match(const std::string& str, const MatchFn& match) const
298+
{
299+
if (!mRe)
300+
return "regular expression has not been compiled yet";
301+
302+
pcre2_match_data* match_data = pcre2_match_data_create_from_pattern(mRe, NULL);
303+
304+
int pos = 0;
305+
while (pos < static_cast<int>(str.size())) {
306+
const int pcreExecRet = pcre2_match(mRe, reinterpret_cast<PCRE2_SPTR8>(str.c_str()), static_cast<int>(str.size()), pos, 0, match_data, nullptr);
307+
if (pcreExecRet == PCRE2_ERROR_NOMATCH)
308+
return "";
309+
if (pcreExecRet < 0) {
310+
PCRE2_UCHAR errorMessageBuf[120];
311+
const int res = pcre2_get_error_message(pcreExecRet, errorMessageBuf, sizeof(errorMessageBuf));
312+
if (res < 0)
313+
return std::string("failed to get error message ") + std::to_string(res) + " (pos: " + std::to_string(pos) + ")";
314+
return std::string(reinterpret_cast<char*>(errorMessageBuf)) + " (pos: " + std::to_string(pos) + ")";
315+
}
316+
PCRE2_SIZE* ovector = pcre2_get_ovector_pointer(match_data);
317+
const uint32_t ovcount = pcre2_get_ovector_count(match_data);
318+
if (ovcount != 1)
319+
return "invalid ovector count";
320+
const auto pos1 = static_cast<unsigned int>(ovector[0]);
321+
const auto pos2 = static_cast<unsigned int>(ovector[1]);
322+
323+
match(pos1, pos2);
324+
325+
// jump to the end of the match for the next pcre_exec
326+
pos = static_cast<int>(pos2);
327+
}
328+
329+
return "";
330+
}
331+
}
332+
251333
template<typename T>
252334
static T* createAndCompileRegex(std::string pattern, std::string& err)
253335
{
@@ -261,6 +343,8 @@ std::shared_ptr<Regex> Regex::create(std::string pattern, Engine engine, std::st
261343
Regex* regex = nullptr;
262344
if (engine == Engine::Pcre)
263345
regex = createAndCompileRegex<PcreRegex>(std::move(pattern), err);
346+
else if (engine == Engine::Pcre2)
347+
regex = createAndCompileRegex<Pcre2Regex>(std::move(pattern), err);
264348
else {
265349
err = "unknown regular expression engine";
266350
}

lib/regex.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@ class CPPCHECKLIB Regex
4141
enum class Engine : std::uint8_t
4242
{
4343
Unknown = 0,
44-
Pcre = 1
44+
Pcre = 1,
45+
Pcre2 = 2
4546
};
4647

4748
static std::shared_ptr<Regex> create(std::string pattern, Engine engine, std::string& err);

releasenotes.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,4 @@ Infrastructure & dependencies:
2121

2222
Other:
2323
- Make it possible to specify the regular expression engine using the `engine` element in a rule XML.
24+
- Added support for PCRE2 (`pcre`) as the regular expression engine for rules.

test/testcmdlineparser.cpp

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2682,10 +2682,20 @@ class TestCmdlineParser : public TestFixture {
26822682
"<summary>ruleSummary2</summary>\n"
26832683
"</message>\n"
26842684
"</rule>\n"
2685+
"<rule>\n"
2686+
"<engine>pcre2</engine>\n"
2687+
"<tokenlist>raw</tokenlist>\n"
2688+
"<pattern>[A-Z]</pattern>\n"
2689+
"<message>\n"
2690+
"<severity>style</severity>\n"
2691+
"<id>ruleId3</id>\n"
2692+
"<summary>ruleSummary3</summary>\n"
2693+
"</message>\n"
2694+
"</rule>\n"
26852695
"</rules>");
26862696
const char * const argv[] = {"cppcheck", "--rule-file=rule.xml", "file.cpp"};
26872697
ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parseFromArgs(argv));
2688-
ASSERT_EQUALS(2, settings->rules.size());
2698+
ASSERT_EQUALS(3, settings->rules.size());
26892699
auto it = settings->rules.cbegin();
26902700
ASSERT_EQUALS_ENUM(Regex::Engine::Pcre, it->engine);
26912701
ASSERT_EQUALS("raw", it->tokenlist);
@@ -2700,6 +2710,13 @@ class TestCmdlineParser : public TestFixture {
27002710
ASSERT_EQUALS_ENUM(Severity::warning, it->severity);
27012711
ASSERT_EQUALS("ruleId2", it->id);
27022712
ASSERT_EQUALS("ruleSummary2", it->summary);
2713+
++it;
2714+
ASSERT_EQUALS_ENUM(Regex::Engine::Pcre2, it->engine);
2715+
ASSERT_EQUALS("raw", it->tokenlist);
2716+
ASSERT_EQUALS("[A-Z]", it->pattern);
2717+
ASSERT_EQUALS_ENUM(Severity::style, it->severity);
2718+
ASSERT_EQUALS("ruleId3", it->id);
2719+
ASSERT_EQUALS("ruleSummary3", it->summary);
27032720
}
27042721

27052722
void ruleFileSingle() {

test/testregex.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@ class TestRegExBase : public TestFixture {
8585
std::string exp;
8686
if (mEngine == Regex::Engine::Pcre)
8787
exp = "missing terminating ] for character class";
88+
else if (mEngine == Regex::Engine::Pcre2)
89+
exp = "missing terminating ] for character class";
8890

8991
(void)assertRegex("[", exp);
9092
}
@@ -201,6 +203,13 @@ class TestRegExPcre : public TestRegExBase {
201203
TestRegExPcre() : TestRegExBase("TestRegExPcre", Regex::Engine::Pcre) {}
202204
};
203205

206+
class TestRegExPcre2 : public TestRegExBase {
207+
public:
208+
TestRegExPcre2() : TestRegExBase("TestRegExPcre2", Regex::Engine::Pcre2) {}
209+
};
210+
211+
204212
REGISTER_TEST(TestRegExPcre)
213+
REGISTER_TEST(TestRegExPcre2)
205214

206215
#endif // HAVE_RULES

0 commit comments

Comments
 (0)