Repository :
https://github.com/colorer/Colorer-library
On branch : cregexp_refactoring
Link :
https://github.com/colorer/Colorer-library/commit/d947a99e9754e6509d205df8606168420c92be3e
>---------------------------------------------------------------
commit d947a99e9754e6509d205df8606168420c92be3e
Author: Aleksey Dobrunov <
cta...@ctapmex.com>
Date: Sun Aug 16 21:17:11 2026 +0500
Reject out-of-range CRegExp escapes and copy \\Y{name} from named groups.
>---------------------------------------------------------------
d947a99e9754e6509d205df8606168420c92be3e
src/colorer/cregexp/cregexp.cpp | 22 ++++++++++++++++------
tests/unit/test_cregexp.cpp | 8 +++++---
2 files changed, 21 insertions(+), 9 deletions(-)
diff --git a/src/colorer/cregexp/cregexp.cpp b/src/colorer/cregexp/cregexp.cpp
index 8569929..621a58d 100644
--- a/src/colorer/cregexp/cregexp.cpp
+++ b/src/colorer/cregexp/cregexp.cpp
@@ -128,11 +128,10 @@ EError CRegExp::setRELow(const UnicodeString& expr)
#endif
endChange = startChange = false;
int start = 0;
- while (Character::isWhitespace(expr[start])) start++;
- if (expr[start] == '/')
- start++;
- else
+ while (start < len && Character::isWhitespace(expr[start])) start++;
+ if (start >= len || expr[start] != '/')
return EError::ESYNTAX;
+ start++;
bool ok = false;
ignoreCase = extend = singleLine = multiLine = false;
@@ -369,6 +368,8 @@ EError CRegExp::setStructs(SRegInfo*& re, const UnicodeString& expr, int& retPos
}
// Escape symbol
if (expr[i] == '\\') {
+ if (i + 1 >= expr.length())
+ return EError::ESYNTAX;
int blen;
switch (expr[i + 1]) {
case 'd':
@@ -440,6 +441,8 @@ EError CRegExp::setStructs(SRegInfo*& re, const UnicodeString& expr, int& retPos
case 'y':
case 'Y':
next->op = (expr[i + 1] == 'y' ? EOps::ReBkTrace : EOps::ReBkTraceN);
+ if (i + 2 >= expr.length())
+ return EError::ESYNTAX;
next->param0 = UnicodeTools::getHex(expr[i + 2]);
if (next->param0 != -1) {
i++;
@@ -464,6 +467,8 @@ EError CRegExp::setStructs(SRegInfo*& re, const UnicodeString& expr, int& retPos
case 'p': // \p{name}
{
+ if (i + 2 >= expr.length())
+ return EError::ESYNTAX;
next->op = EOps::ReBkBrackName;
auto br_name = UnicodeTools::getCurlyContent(expr, i + 2);
if (br_name == nullptr)
@@ -1174,12 +1179,17 @@ bool CRegExp::lowParse(SRegInfo* re, SRegInfo* prev, int toParse)
case EOps::ReBkTraceNName:
#ifndef NAMED_MATCHES_IN_HASH
sv = re->param0;
- if (!backStr || !backTrace || sv == -1) {
+ if (!backStr || !backTrace || sv == -1 || backTrace->cnMatch <= sv) {
+ check_stack(false, &re, &prev, &toParse, &leftenter, &action);
+ continue;
+ }
+ backTrace->topnseSanitize(sv);
+ if (backTrace->ns[sv] == -1 || backTrace->ne[sv] == -1) {
check_stack(false, &re, &prev, &toParse, &leftenter, &action);
continue;
}
br = false;
- for (i = backTrace->s[sv]; i < backTrace->e[sv]; i++) {
+ for (i = backTrace->ns[sv]; i < backTrace->ne[sv]; i++) {
if (toParse >= end || Character::toLowerCase(pattern[toParse]) != Character::toLowerCase((*backStr)[i])) {
check_stack(false, &re, &prev, &toParse, &leftenter, &action);
br = true;
diff --git a/tests/unit/test_cregexp.cpp b/tests/unit/test_cregexp.cpp
index 955aca5..f98c010 100644
--- a/tests/unit/test_cregexp.cpp
+++ b/tests/unit/test_cregexp.cpp
@@ -113,6 +113,10 @@ TEST_CASE("CRegExp compilation errors", "[cregexp]")
{u"/+/", EError::EOP},
{u"/{2}/", EError::EOP},
{u"/*/", EError::EOP},
+ {u"/\\/", EError::ESYNTAX},
+ {u"/\\y/", EError::ESYNTAX},
+ {u"/\\p/", EError::ESYNTAX},
+ {u" ", EError::ESYNTAX},
}));
const auto pre = ustr(pattern);
CRegExp re(&pre);
@@ -531,10 +535,8 @@ TEST_CASE("CRegExp stack reuse after clearRegExpStack", "[cregexp]")
REQUIRE(match.e[0] == 6);
}
-TEST_CASE("CRegExp \\Y{name} copies named group case-insensitively", "[cregexp][bugs]")
+TEST_CASE("CRegExp \\Y{name} copies named group case-insensitively", "[cregexp]")
{
- SKIP("\\Y{name} currently reads numeric s/e instead of ns/ne");
-
const auto start_re = ustr(u"/(x)(?{n}Foo)/");
const auto start_text = ustr(u"xFoo");
CRegExp start(&start_re);