Skip to content

Commit 1d61493

Browse files
committed
fixed #14261 - Regex: added std::regex implementation / added engine to rule XML to specify regex engine
1 parent 3747685 commit 1d61493

File tree

9 files changed

+316
-195
lines changed

9 files changed

+316
-195
lines changed

Makefile

Lines changed: 129 additions & 129 deletions
Large diffs are not rendered by default.

cli/cmdlineparser.cpp

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1293,7 +1293,7 @@ CmdLineParser::Result CmdLineParser::parseFromArgs(int argc, const char* const a
12931293
}
12941294

12951295
std::string regex_err;
1296-
auto regex = Regex::create(rule.pattern, regex_err);
1296+
auto regex = Regex::create(rule.pattern, Regex::Engine::Pcre, regex_err);
12971297
if (!regex) {
12981298
mLogger.printError("failed to compile rule pattern '" + rule.pattern + "' (" + regex_err + ").");
12991299
return Result::Fail;
@@ -1350,6 +1350,19 @@ CmdLineParser::Result CmdLineParser::parseFromArgs(int argc, const char* const a
13501350
}
13511351
}
13521352
}
1353+
else if (std::strcmp(subname, "engine") == 0) {
1354+
const char * const engine = empty_if_null(subtext);
1355+
if (std::strcmp(engine, "pcre") == 0) {
1356+
rule.engine = Regex::Engine::Pcre;
1357+
}
1358+
else if (std::strcmp(engine, "std") == 0) {
1359+
rule.engine = Regex::Engine::Std;
1360+
}
1361+
else {
1362+
mLogger.printError(std::string("unknown regex engine '") + engine + "'.");
1363+
return Result::Fail;
1364+
}
1365+
}
13531366
else {
13541367
mLogger.printError("unable to load rule-file '" + ruleFile + "' - unknown element '" + subname + "' encountered in 'rule'.");
13551368
return Result::Fail;
@@ -1377,7 +1390,7 @@ CmdLineParser::Result CmdLineParser::parseFromArgs(int argc, const char* const a
13771390
}
13781391

13791392
std::string regex_err;
1380-
auto regex = Regex::create(rule.pattern, regex_err);
1393+
auto regex = Regex::create(rule.pattern, rule.engine, regex_err);
13811394
if (!regex) {
13821395
mLogger.printError("unable to load rule-file '" + ruleFile + "' - pattern '" + rule.pattern + "' failed to compile (" + regex_err + ").");
13831396
return Result::Fail;

lib/regex.cpp

Lines changed: 68 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
#include "regex.h"
2222

23+
#include <regex>
2324
#include <utility>
2425

2526
#ifdef _WIN32
@@ -188,15 +189,15 @@ namespace {
188189
std::string PcreRegex::compile()
189190
{
190191
if (mRe)
191-
return "pcre_compile failed: regular expression has already been compiled";
192+
return "regular expression has already been compiled";
192193

193194
const char *pcreCompileErrorStr = nullptr;
194195
int erroffset = 0;
195196
pcre * const re = pcre_compile(mPattern.c_str(),0,&pcreCompileErrorStr,&erroffset,nullptr);
196197
if (!re) {
197198
if (pcreCompileErrorStr)
198-
return "pcre_compile failed: " + std::string(pcreCompileErrorStr);
199-
return "pcre_compile failed: unknown error";
199+
return pcreCompileErrorStr;
200+
return "unknown error";
200201
}
201202

202203
// Optimize the regex, but only if PCRE_CONFIG_JIT is available
@@ -209,7 +210,7 @@ namespace {
209210
if (pcreStudyErrorStr) {
210211
// pcre_compile() worked, but pcre_study() returned an error. Free the resources allocated by pcre_compile().
211212
pcre_free(re);
212-
return "pcre_study failed: " + std::string(pcreStudyErrorStr);
213+
return std::string(pcreStudyErrorStr) + " (pcre_study)";
213214
}
214215
mExtra = pcreExtra;
215216
#endif
@@ -222,7 +223,7 @@ namespace {
222223
std::string PcreRegex::match(const std::string& str, const MatchFn& match) const
223224
{
224225
if (!mRe)
225-
return "pcre_exec failed: regular expression has not been compiled yet";
226+
return "regular expression has not been compiled yet";
226227

227228
int pos = 0;
228229
int ovector[30]= {0};
@@ -231,7 +232,7 @@ namespace {
231232
if (pcreExecRet == PCRE_ERROR_NOMATCH)
232233
return "";
233234
if (pcreExecRet < 0) {
234-
return "pcre_exec failed (pos: " + std::to_string(pos) + "): " + pcreErrorCodeToString(pcreExecRet);
235+
return pcreErrorCodeToString(pcreExecRet) + " (pos: " + std::to_string(pos) + ")";
235236
}
236237
const auto pos1 = static_cast<unsigned int>(ovector[0]);
237238
const auto pos2 = static_cast<unsigned int>(ovector[1]);
@@ -246,10 +247,69 @@ namespace {
246247
}
247248
}
248249

249-
std::shared_ptr<Regex> Regex::create(std::string pattern, std::string& err)
250+
namespace {
251+
class StdRegex : public Regex
252+
{
253+
public:
254+
explicit StdRegex(std::string pattern)
255+
: mPattern(std::move(pattern))
256+
{}
257+
258+
std::string compile()
259+
{
260+
if (mCompiled)
261+
return "regular expression has already been compiled";
262+
263+
try {
264+
mRegex = std::regex(mPattern);
265+
} catch(const std::exception& e) {
266+
return e.what();
267+
}
268+
mCompiled = true;
269+
return "";
270+
}
271+
272+
std::string match(const std::string& str, const MatchFn& matchFn) const override
273+
{
274+
if (!mCompiled)
275+
return "regular expression has not been compiled yet";
276+
277+
auto I = std::sregex_iterator(str.cbegin(), str.cend(), mRegex);
278+
const auto E = std::sregex_iterator();
279+
while (I != E)
280+
{
281+
const std::smatch& match = *I;
282+
matchFn(match.position(), match.position() + match.length());
283+
++I;
284+
}
285+
return "";
286+
}
287+
288+
private:
289+
std::string mPattern;
290+
std::regex mRegex;
291+
bool mCompiled{};
292+
};
293+
}
294+
295+
template<typename T>
296+
static T* createAndCompileRegex(std::string pattern, std::string& err)
250297
{
251-
auto* regex = new PcreRegex(std::move(pattern));
298+
T* regex = new T(std::move(pattern));
252299
err = regex->compile();
300+
return regex;
301+
}
302+
303+
std::shared_ptr<Regex> Regex::create(std::string pattern, Engine engine, std::string& err)
304+
{
305+
Regex* regex = nullptr;
306+
if (engine == Engine::Pcre)
307+
regex = createAndCompileRegex<PcreRegex>(std::move(pattern), err);
308+
else if (engine == Engine::Std)
309+
regex = createAndCompileRegex<StdRegex>(std::move(pattern), err);
310+
else {
311+
err = "unknown regular expression engine";
312+
}
253313
if (!err.empty()) {
254314
delete regex;
255315
return nullptr;

lib/regex.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,14 @@ class CPPCHECKLIB Regex
3737
using MatchFn = std::function<void (int start, int end)>;
3838
virtual std::string match(const std::string& str, const MatchFn& matchFn) const = 0;
3939

40-
static std::shared_ptr<Regex> create(std::string pattern, std::string& err);
40+
enum class Engine
41+
{
42+
Unknown = 0,
43+
Pcre = 1,
44+
Std = 2
45+
};
46+
47+
static std::shared_ptr<Regex> create(std::string pattern, Engine engine, std::string& err);
4148
};
4249

4350
#endif // HAVE_RULES

lib/settings.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@
4040
#include <unordered_set>
4141
#include <utility>
4242

43+
#include "regex.h"
44+
4345
#if defined(USE_WINDOWS_SEH) || defined(USE_UNIX_SIGNAL_HANDLING)
4446
#include <cstdio>
4547
#endif
@@ -347,6 +349,7 @@ class CPPCHECKLIB WARN_UNUSED Settings {
347349
std::string id = "rule"; // default id
348350
std::string summary;
349351
Severity severity = Severity::style; // default severity
352+
Regex::Engine engine = Regex::Engine::Pcre;
350353
std::shared_ptr<Regex> regex;
351354
};
352355

0 commit comments

Comments
 (0)