From 17cb52e49dc5244ee1b97049545e8d72726b64b3 Mon Sep 17 00:00:00 2001 From: Kartik Naik Date: Thu, 20 Aug 2026 18:33:21 +0530 Subject: [PATCH 1/3] guard end-before-start match length in pcre2grep -o and --output --- RunGrepTest | 7 +++++++ src/pcre2grep.c | 11 +++++++++-- testdata/grepoutput | 4 ++++ 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/RunGrepTest b/RunGrepTest index 09271ba60..93d3abeb3 100755 --- a/RunGrepTest +++ b/RunGrepTest @@ -927,6 +927,13 @@ echo "RC=$?" >>testtrygrep (cd $srcdir; $valgrind $vjs $pcre2grep -n -B4 -A2 '^(ert|dfg)' ./testdata/grepinput) >>testtrygrep echo "RC=$?" >>testtrygrep +echo "---------------------------- Test 161 -----------------------------" >>testtrygrep +printf 'XfooY\n' >testNinputgrep +$valgrind $vjs $pcre2grep --allow-lookaround-bsk -o '(?=foo\K)' testNinputgrep >>testtrygrep +echo "RC=$?" >>testtrygrep +$valgrind $vjs $pcre2grep --allow-lookaround-bsk --output '$0' '(?=foo\K)' testNinputgrep >>testtrygrep +echo "RC=$?" >>testtrygrep + # Now compare the results. diff --git a/src/pcre2grep.c b/src/pcre2grep.c index 667d2edd3..b741e9b7c 100644 --- a/src/pcre2grep.c +++ b/src/pcre2grep.c @@ -2210,7 +2210,10 @@ for (; *string != 0; string++) { PCRE2_SIZE capturesize; value *= 2; - capturesize = ovector[value + 1] - ovector[value]; + /* The use of \K may make the end offset earlier than the start; treat + such a capture as empty rather than letting the length underflow. */ + capturesize = (ovector[value + 1] > ovector[value])? + ovector[value + 1] - ovector[value] : 0; if (capturesize > 0) { print_match(subject + ovector[value], capturesize); @@ -2881,7 +2884,11 @@ while (ptr < endptr) int n = om->groupnum; if (n == 0 || n < mrc) { - size_t plen = offsets[2*n + 1] - offsets[2*n]; + /* The use of \K may make the end offset earlier than the start; + treat such a match as empty rather than letting the length + underflow. */ + size_t plen = (offsets[2*n + 1] > offsets[2*n])? + offsets[2*n + 1] - offsets[2*n] : 0; if (plen > 0) { if (printed && om_separator != NULL) diff --git a/testdata/grepoutput b/testdata/grepoutput index 9d494dd49..21030ced7 100644 --- a/testdata/grepoutput +++ b/testdata/grepoutput @@ -1339,3 +1339,7 @@ RC=0 631-ghj 632-jkl RC=0 +---------------------------- Test 161 ----------------------------- +RC=0 + +RC=0 From f36296d0d00959b4ca9561293e9e705f75734aeb Mon Sep 17 00:00:00 2001 From: Kartik Naik Date: Fri, 21 Aug 2026 13:36:52 +0530 Subject: [PATCH 2/3] pcre2grep: swap reversed match offsets in output paths instead of zeroing Follow the existing convention used by the colour and multiline code: when \K leaves the end offset before the start, print the range [min, max] rather than treating it as empty. Apply the same swap to --line-offsets, --file-offsets, and the callout argument length and copy, which had the same unsigned subtraction. --- RunGrepTest | 4 ++ src/pcre2grep.c | 98 +++++++++++++++++++++++++++++++++------------ testdata/grepoutput | 7 +++- 3 files changed, 83 insertions(+), 26 deletions(-) diff --git a/RunGrepTest b/RunGrepTest index 93d3abeb3..a01652a3e 100755 --- a/RunGrepTest +++ b/RunGrepTest @@ -933,6 +933,10 @@ $valgrind $vjs $pcre2grep --allow-lookaround-bsk -o '(?=foo\K)' testNinputgrep > echo "RC=$?" >>testtrygrep $valgrind $vjs $pcre2grep --allow-lookaround-bsk --output '$0' '(?=foo\K)' testNinputgrep >>testtrygrep echo "RC=$?" >>testtrygrep +$valgrind $vjs $pcre2grep --allow-lookaround-bsk --line-offsets '(?=foo\K)' testNinputgrep >>testtrygrep +echo "RC=$?" >>testtrygrep +$valgrind $vjs $pcre2grep --allow-lookaround-bsk --file-offsets '(?=foo\K)' testNinputgrep >>testtrygrep +echo "RC=$?" >>testtrygrep # Now compare the results. diff --git a/src/pcre2grep.c b/src/pcre2grep.c index b741e9b7c..1eebb7402 100644 --- a/src/pcre2grep.c +++ b/src/pcre2grep.c @@ -2208,15 +2208,25 @@ for (; *string != 0; string++) case DDE_CAPTURE: if (value < capture_top) { - PCRE2_SIZE capturesize; + PCRE2_SIZE capturesize, start, end; value *= 2; - /* The use of \K may make the end offset earlier than the start; treat - such a capture as empty rather than letting the length underflow. */ - capturesize = (ovector[value + 1] > ovector[value])? - ovector[value + 1] - ovector[value] : 0; + start = ovector[value]; + end = ovector[value + 1]; + + /* The use of \K may make the end offset earlier than the start. In + this situation, swap them round. */ + + if (start > end) + { + PCRE2_SIZE temp = start; + start = end; + end = temp; + } + + capturesize = end - start; if (capturesize > 0) { - print_match(subject + ovector[value], capturesize); + print_match(subject + start, capturesize); printed = TRUE; } } @@ -2356,7 +2366,12 @@ while (length > 0) if (value < capture_top) { value *= 2; - argslen += ovector[value + 1] - ovector[value]; + + /* The use of \K may make the end offset earlier than the start. */ + + argslen += (ovector[value + 1] > ovector[value])? + ovector[value + 1] - ovector[value] : + ovector[value] - ovector[value + 1]; } argslen--; /* Negate the effect of argslen++ below. */ break; @@ -2422,10 +2437,23 @@ while (length > 0) case DDE_CAPTURE: if (value < capture_top) { - PCRE2_SIZE capturesize; + PCRE2_SIZE capturesize, start, end; value *= 2; - capturesize = ovector[value + 1] - ovector[value]; - memcpy(argsptr, subject + ovector[value], capturesize); + start = ovector[value]; + end = ovector[value + 1]; + + /* The use of \K may make the end offset earlier than the start. In + this situation, swap them round. */ + + if (start > end) + { + PCRE2_SIZE temp = start; + start = end; + end = temp; + } + + capturesize = end - start; + memcpy(argsptr, subject + start, capturesize); argsptr += capturesize; } break; @@ -2850,18 +2878,28 @@ while (ptr < endptr) printname_colon); if (number) fprintf(stdout, "%lu:", linenumber); - /* Handle --line-offsets */ + /* Handle --line-offsets and --file-offsets. The use of \K may make + the end offset earlier than the start. In this situation, swap them + round. */ - if (line_offsets) - fprintf(stdout, "%d,%d" STDOUT_NL, (int)(ptr + offsets[0] - ptr), - (int)(offsets[1] - offsets[0])); + if (line_offsets || file_offsets) + { + PCRE2_SIZE start = offsets[0]; + PCRE2_SIZE end = offsets[1]; - /* Handle --file-offsets */ + if (start > end) + { + PCRE2_SIZE temp = start; + start = end; + end = temp; + } - else if (file_offsets) - fprintf(stdout, "%d,%d" STDOUT_NL, - (int)(filepos + ptr + offsets[0] - ptr), - (int)(offsets[1] - offsets[0])); + if (line_offsets) + fprintf(stdout, "%d,%d" STDOUT_NL, (int)start, (int)(end - start)); + else + fprintf(stdout, "%d,%d" STDOUT_NL, (int)(filepos + start), + (int)(end - start)); + } /* Handle --output (which has already been syntax checked) */ @@ -2884,16 +2922,26 @@ while (ptr < endptr) int n = om->groupnum; if (n == 0 || n < mrc) { - /* The use of \K may make the end offset earlier than the start; - treat such a match as empty rather than letting the length - underflow. */ - size_t plen = (offsets[2*n + 1] > offsets[2*n])? - offsets[2*n + 1] - offsets[2*n] : 0; + PCRE2_SIZE start = offsets[2*n]; + PCRE2_SIZE end = offsets[2*n + 1]; + size_t plen; + + /* The use of \K may make the end offset earlier than the start. + In this situation, swap them round. */ + + if (start > end) + { + PCRE2_SIZE temp = start; + start = end; + end = temp; + } + + plen = end - start; if (plen > 0) { if (printed && om_separator != NULL) fprintf(stdout, "%s", om_separator); - print_match(ptr + offsets[n*2], plen); + print_match(ptr + start, plen); printed = TRUE; } } diff --git a/testdata/grepoutput b/testdata/grepoutput index 21030ced7..3c8b2f48c 100644 --- a/testdata/grepoutput +++ b/testdata/grepoutput @@ -1340,6 +1340,11 @@ RC=0 632-jkl RC=0 ---------------------------- Test 161 ----------------------------- +foo RC=0 - +foo +RC=0 +1:1,3 +RC=0 +1,3 RC=0 From f1d4b5f13ae934f533561d4437d031d3bfd6a7f8 Mon Sep 17 00:00:00 2001 From: Nicholas Wilson Date: Sat, 22 Aug 2026 11:52:20 +0100 Subject: [PATCH 3/3] Add matching test to RunGrepTest.bat --- RunGrepTest.bat | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/RunGrepTest.bat b/RunGrepTest.bat index 8280534c7..38eeabee5 100644 --- a/RunGrepTest.bat +++ b/RunGrepTest.bat @@ -897,6 +897,17 @@ echo RC=^%ERRORLEVEL%>>testtrygrep (pushd %srcdir% & %pcre2grep% -n -B4 -A2 "^(ert|dfg)" ./testdata/grepinput & popd) >>testtrygrep echo RC=^%ERRORLEVEL%>>testtrygrep +echo ---------------------------- Test 161 ----------------------------->>testtrygrep +%printf% "XfooY\n" >testNinputgrep +%pcre2grep% --allow-lookaround-bsk -o "(?=foo\K)" testNinputgrep >>testtrygrep +echo RC=^%ERRORLEVEL%>>testtrygrep +%pcre2grep% --allow-lookaround-bsk --output "$0" "(?=foo\K)" testNinputgrep >>testtrygrep +echo RC=^%ERRORLEVEL%>>testtrygrep +%pcre2grep% --allow-lookaround-bsk --line-offsets "(?=foo\K)" testNinputgrep >>testtrygrep +echo RC=^%ERRORLEVEL%>>testtrygrep +%pcre2grep% --allow-lookaround-bsk --file-offsets "(?=foo\K)" testNinputgrep >>testtrygrep +echo RC=^%ERRORLEVEL%>>testtrygrep + :: Now compare the results. %cf% %srcdir%\testdata\grepoutput testtrygrep %cfout%