Repository :
https://github.com/FarGroup/FarManager
On branch : master
Link :
https://github.com/FarGroup/FarManager/commit/5d99c4f22bc4cc03b71880b35879f37de2b3b538
>---------------------------------------------------------------
commit 5d99c4f22bc4cc03b71880b35879f37de2b3b538
Author: Alex Alabuzhev <
alab...@gmail.com>
Date: Tue Dec 2 23:55:26 2025 +0000
Fix composite line endings handling in multiline regexes
>---------------------------------------------------------------
5d99c4f22bc4cc03b71880b35879f37de2b3b538
far/RegExp.cpp | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++--
far/changelog | 5 +++++
far/vbuild.m4 | 2 +-
3 files changed, 59 insertions(+), 3 deletions(-)
diff --git a/far/RegExp.cpp b/far/RegExp.cpp
index 576e6d188..dbf39c666 100644
--- a/far/RegExp.cpp
+++ b/far/RegExp.cpp
@@ -55,6 +55,23 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//----------------------------------------------------------------------------
+bool is_far_eol_before(const wchar_t* const Begin, const wchar_t* const Iterator, const wchar_t* const End)
+{
+ if (Iterator == Begin)
+ return true;
+
+ // After \n or \r\n or \r\r\n
+ if (Iterator[-1] == L'\n')
+ return true;
+
+ // After \r, but not within \r\n or \r\r\n
+ string_view const Str(Iterator, End);
+ if (Iterator[-1] == L'\r' && !Str.starts_with(L"\n"sv) && !Str.starts_with(L"\r\n"))
+ return true;
+
+ return false;
+}
+
string_view regex_exception::to_string(REError const Code)
{
// TODO: localization
@@ -1866,14 +1883,14 @@ bool RegExp::InnerMatch(const wchar_t* start, const wchar_t* str, const wchar_t*
{
case opLineStart:
{
- if (str == start || IsEol(str[-1]))
+ if (str == start || is_far_eol_before(start, str, strend))
continue;
break;
}
case opLineEnd:
{
- if (str == strend || IsEol(str[0]))
+ if (str == strend || is_far_eol_before(start, str + 1, strend))
continue;
break;
@@ -3897,4 +3914,38 @@ TEST_CASE("regex.ex")
}
}
+TEST_CASE("regex.multiline")
+{
+ RegExp reBegin, reEnd;
+ REQUIRE_NOTHROW(reBegin.Compile(L"^"sv, OP_MULTILINE));
+ REQUIRE_NOTHROW(reEnd.Compile(L"$"sv, OP_MULTILINE));
+
+ // We expect it to work correctly with all the EOLs we support - \r, \n, \r\n, \r\r\n
+
+ const auto Str = L"1\r2\n3\r\n4\r\r\n5\r\r6"sv;
+ // ^ ^ ^ ^ ^ ^^
+ // 0 00 00 0 00 0 0 11 1 11
+ // 0 12 34 5 67 8 9 01 2 34
+
+ int const ExpectedBegins[]
+ {
+ 0, 2, 4, 7, 11, 13, 14
+ };
+
+ regex_match Match;
+
+ for (size_t i = 0; i != Str.size(); ++i)
+ {
+ REQUIRE(reBegin.SearchEx(Str, i, Match));
+ const auto NextStartIterator = std::ranges::find_if(ExpectedBegins, [&](int const n){ return n >= static_cast<int>(i); });
+ const auto NextStart = *NextStartIterator;
+ REQUIRE(Match.Matches[0].start == NextStart);
+ REQUIRE(Match.Matches[0].end == NextStart);
+
+ REQUIRE(reEnd.SearchEx(Str, NextStart, Match));
+ const auto NextEnd = NextStartIterator + 1 == std::end(ExpectedBegins)? static_cast<int>(Str.size()) : NextStartIterator[1] - 1;
+ REQUIRE(Match.Matches[0].start == NextEnd);
+ REQUIRE(Match.Matches[0].end == NextEnd);
+ }
+}
#endif
diff --git a/far/changelog b/far/changelog
index b23a531bf..ae98568d6 100644
--- a/far/changelog
+++ b/far/changelog
@@ -1,3 +1,8 @@
+--------------------------------------------------------------------------------
+drkns 2025-12-02 23:51:44+00:00 - build 6609
+
+1. Fix composite line endings handling in multiline regexes.
+
--------------------------------------------------------------------------------
drkns 2025-12-02 21:38:32+00:00 - build 6608
diff --git a/far/vbuild.m4 b/far/vbuild.m4
index ce5e96cce..6eceed0e9 100644
--- a/far/vbuild.m4
+++ b/far/vbuild.m4
@@ -1 +1 @@
-6608
+6609