diff --git a/ALL.vs2017.sln b/ALL.vs2017.sln index 3edda4c2b66..857536ada85 100644 --- a/ALL.vs2017.sln +++ b/ALL.vs2017.sln @@ -443,6 +443,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Common", "Plugins\src_VCPP\ EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "editlibparsers", "Externals\crystaledit\editlib\editlibparsers.vcxitems", "{4170552A-09E2-4FAC-B71D-0E2F5EB3C869}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FilterEngine", "Src\FilterEngine\FilterEngine.vcxitems", "{9C37E5D8-1DC0-4EAC-AADB-5FC8BE4FB1BC}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|ARM = Debug|ARM @@ -1388,20 +1390,24 @@ Global Externals\xdiff\xdiff.vcxitems*{733e7c0b-ac3d-47ac-a8da-e13644d6294d}*SharedItemsImports = 4 Src\CompareEngines\CompareEngines.vcxitems*{733e7c0b-ac3d-47ac-a8da-e13644d6294d}*SharedItemsImports = 4 Src\diffutils\diffutils.vcxitems*{733e7c0b-ac3d-47ac-a8da-e13644d6294d}*SharedItemsImports = 4 + Src\FilterEngine\FilterEngine.vcxitems*{733e7c0b-ac3d-47ac-a8da-e13644d6294d}*SharedItemsImports = 4 Plugins\src_VCPP\Common\Common.vcxitems*{7354ba4f-8dab-46ee-a5a2-a148d6ef2443}*SharedItemsImports = 9 Externals\crystaledit\editlib\editlib.vcxitems*{7515ac3c-389a-44cd-b940-a59dde5b8ae3}*SharedItemsImports = 9 + Src\FilterEngine\FilterEngine.vcxitems*{9c37e5d8-1dc0-4eac-aadb-5fc8be4fb1bc}*SharedItemsImports = 9 Externals\googletest\googletest\googletest.vcxitems*{9ee35458-b145-444f-92b7-27ff72112c42}*SharedItemsImports = 9 Externals\crystaledit\editlib\editlib.vcxitems*{9fda4af0-ccfd-4812-bdb9-53efedb32bde}*SharedItemsImports = 4 Externals\crystaledit\editlib\editlibparsers.vcxitems*{9fda4af0-ccfd-4812-bdb9-53efedb32bde}*SharedItemsImports = 4 Externals\xdiff\xdiff.vcxitems*{9fda4af0-ccfd-4812-bdb9-53efedb32bde}*SharedItemsImports = 4 Src\CompareEngines\CompareEngines.vcxitems*{9fda4af0-ccfd-4812-bdb9-53efedb32bde}*SharedItemsImports = 4 Src\diffutils\diffutils.vcxitems*{9fda4af0-ccfd-4812-bdb9-53efedb32bde}*SharedItemsImports = 4 + Src\FilterEngine\FilterEngine.vcxitems*{9fda4af0-ccfd-4812-bdb9-53efedb32bde}*SharedItemsImports = 4 Plugins\src_VCPP\Common\Common.vcxitems*{a644fba4-d76e-4500-b4b7-04d7a245359a}*SharedItemsImports = 4 Plugins\src_VCPP\Common\Common.vcxitems*{aa88b46e-b2e2-4b03-8cd5-1e9d60db6ab2}*SharedItemsImports = 4 Externals\crystaledit\editlib\editlibparsers.vcxitems*{ab827c6b-5116-408f-b453-e2075e9b73b4}*SharedItemsImports = 4 Externals\xdiff\xdiff.vcxitems*{ab827c6b-5116-408f-b453-e2075e9b73b4}*SharedItemsImports = 4 Src\CompareEngines\CompareEngines.vcxitems*{ab827c6b-5116-408f-b453-e2075e9b73b4}*SharedItemsImports = 4 Src\diffutils\diffutils.vcxitems*{ab827c6b-5116-408f-b453-e2075e9b73b4}*SharedItemsImports = 4 + Src\FilterEngine\FilterEngine.vcxitems*{ab827c6b-5116-408f-b453-e2075e9b73b4}*SharedItemsImports = 4 Plugins\src_VCPP\Common\Common.vcxitems*{bd0c5fe1-8457-49c2-8801-0c99a6e6cc03}*SharedItemsImports = 4 Externals\crystaledit\editlib\editlib.vcxitems*{c347d6ae-7a2b-4ed0-97ad-2595e1c5d7dd}*SharedItemsImports = 4 Externals\crystaledit\editlib\editlibparsers.vcxitems*{c347d6ae-7a2b-4ed0-97ad-2595e1c5d7dd}*SharedItemsImports = 4 diff --git a/ALL.vs2019.sln b/ALL.vs2019.sln index c87c50a4b8d..8b1a0bd2f3f 100644 --- a/ALL.vs2019.sln +++ b/ALL.vs2019.sln @@ -454,6 +454,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Common", "Plugins\src_VCPP\ EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "editlibparsers", "Externals\crystaledit\editlib\editlibparsers.vcxitems", "{4170552A-09E2-4FAC-B71D-0E2F5EB3C869}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FilterEngine", "Src\FilterEngine\FilterEngine.vcxitems", "{9C37E5D8-1DC0-4EAC-AADB-5FC8BE4FB1BC}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|ARM = Debug|ARM @@ -1457,20 +1459,24 @@ Global Externals\xdiff\xdiff.vcxitems*{733e7c0b-ac3d-47ac-a8da-e13644d6294d}*SharedItemsImports = 4 Src\CompareEngines\CompareEngines.vcxitems*{733e7c0b-ac3d-47ac-a8da-e13644d6294d}*SharedItemsImports = 4 Src\diffutils\diffutils.vcxitems*{733e7c0b-ac3d-47ac-a8da-e13644d6294d}*SharedItemsImports = 4 + Src\FilterEngine\FilterEngine.vcxitems*{733e7c0b-ac3d-47ac-a8da-e13644d6294d}*SharedItemsImports = 4 Plugins\src_VCPP\Common\Common.vcxitems*{7354ba4f-8dab-46ee-a5a2-a148d6ef2443}*SharedItemsImports = 9 Externals\crystaledit\editlib\editlib.vcxitems*{7515ac3c-389a-44cd-b940-a59dde5b8ae3}*SharedItemsImports = 9 + Src\FilterEngine\FilterEngine.vcxitems*{9c37e5d8-1dc0-4eac-aadb-5fc8be4fb1bc}*SharedItemsImports = 9 Externals\googletest\googletest\googletest.vcxitems*{9ee35458-b145-444f-92b7-27ff72112c42}*SharedItemsImports = 9 Externals\crystaledit\editlib\editlib.vcxitems*{9fda4af0-ccfd-4812-bdb9-53efedb32bde}*SharedItemsImports = 4 Externals\crystaledit\editlib\editlibparsers.vcxitems*{9fda4af0-ccfd-4812-bdb9-53efedb32bde}*SharedItemsImports = 4 Externals\xdiff\xdiff.vcxitems*{9fda4af0-ccfd-4812-bdb9-53efedb32bde}*SharedItemsImports = 4 Src\CompareEngines\CompareEngines.vcxitems*{9fda4af0-ccfd-4812-bdb9-53efedb32bde}*SharedItemsImports = 4 Src\diffutils\diffutils.vcxitems*{9fda4af0-ccfd-4812-bdb9-53efedb32bde}*SharedItemsImports = 4 + Src\FilterEngine\FilterEngine.vcxitems*{9fda4af0-ccfd-4812-bdb9-53efedb32bde}*SharedItemsImports = 4 Plugins\src_VCPP\Common\Common.vcxitems*{a644fba4-d76e-4500-b4b7-04d7a245359a}*SharedItemsImports = 4 Plugins\src_VCPP\Common\Common.vcxitems*{aa88b46e-b2e2-4b03-8cd5-1e9d60db6ab2}*SharedItemsImports = 4 Externals\crystaledit\editlib\editlibparsers.vcxitems*{ab827c6b-5116-408f-b453-e2075e9b73b4}*SharedItemsImports = 4 Externals\xdiff\xdiff.vcxitems*{ab827c6b-5116-408f-b453-e2075e9b73b4}*SharedItemsImports = 4 Src\CompareEngines\CompareEngines.vcxitems*{ab827c6b-5116-408f-b453-e2075e9b73b4}*SharedItemsImports = 4 Src\diffutils\diffutils.vcxitems*{ab827c6b-5116-408f-b453-e2075e9b73b4}*SharedItemsImports = 4 + Src\FilterEngine\FilterEngine.vcxitems*{ab827c6b-5116-408f-b453-e2075e9b73b4}*SharedItemsImports = 4 Plugins\src_VCPP\Common\Common.vcxitems*{bd0c5fe1-8457-49c2-8801-0c99a6e6cc03}*SharedItemsImports = 4 Externals\crystaledit\editlib\editlib.vcxitems*{c347d6ae-7a2b-4ed0-97ad-2595e1c5d7dd}*SharedItemsImports = 4 Externals\crystaledit\editlib\editlibparsers.vcxitems*{c347d6ae-7a2b-4ed0-97ad-2595e1c5d7dd}*SharedItemsImports = 4 diff --git a/ALL.vs2022.sln b/ALL.vs2022.sln index 71902f72ba0..7ee34f554ef 100644 --- a/ALL.vs2022.sln +++ b/ALL.vs2022.sln @@ -454,6 +454,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Common", "Plugins\src_VCPP\ EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "editlibparsers", "Externals\crystaledit\editlib\editlibparsers.vcxitems", "{4170552A-09E2-4FAC-B71D-0E2F5EB3C869}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FilterEngine", "Src\FilterEngine\FilterEngine.vcxitems", "{9C37E5D8-1DC0-4EAC-AADB-5FC8BE4FB1BC}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|ARM = Debug|ARM @@ -1457,20 +1459,24 @@ Global Externals\xdiff\xdiff.vcxitems*{733e7c0b-ac3d-47ac-a8da-e13644d6294d}*SharedItemsImports = 4 Src\CompareEngines\CompareEngines.vcxitems*{733e7c0b-ac3d-47ac-a8da-e13644d6294d}*SharedItemsImports = 4 Src\diffutils\diffutils.vcxitems*{733e7c0b-ac3d-47ac-a8da-e13644d6294d}*SharedItemsImports = 4 + Src\FilterEngine\FilterEngine.vcxitems*{733e7c0b-ac3d-47ac-a8da-e13644d6294d}*SharedItemsImports = 4 Plugins\src_VCPP\Common\Common.vcxitems*{7354ba4f-8dab-46ee-a5a2-a148d6ef2443}*SharedItemsImports = 9 Externals\crystaledit\editlib\editlib.vcxitems*{7515ac3c-389a-44cd-b940-a59dde5b8ae3}*SharedItemsImports = 9 + Src\FilterEngine\FilterEngine.vcxitems*{9c37e5d8-1dc0-4eac-aadb-5fc8be4fb1bc}*SharedItemsImports = 9 Externals\googletest\googletest\googletest.vcxitems*{9ee35458-b145-444f-92b7-27ff72112c42}*SharedItemsImports = 9 Externals\crystaledit\editlib\editlib.vcxitems*{9fda4af0-ccfd-4812-bdb9-53efedb32bde}*SharedItemsImports = 4 Externals\crystaledit\editlib\editlibparsers.vcxitems*{9fda4af0-ccfd-4812-bdb9-53efedb32bde}*SharedItemsImports = 4 Externals\xdiff\xdiff.vcxitems*{9fda4af0-ccfd-4812-bdb9-53efedb32bde}*SharedItemsImports = 4 Src\CompareEngines\CompareEngines.vcxitems*{9fda4af0-ccfd-4812-bdb9-53efedb32bde}*SharedItemsImports = 4 Src\diffutils\diffutils.vcxitems*{9fda4af0-ccfd-4812-bdb9-53efedb32bde}*SharedItemsImports = 4 + Src\FilterEngine\FilterEngine.vcxitems*{9fda4af0-ccfd-4812-bdb9-53efedb32bde}*SharedItemsImports = 4 Plugins\src_VCPP\Common\Common.vcxitems*{a644fba4-d76e-4500-b4b7-04d7a245359a}*SharedItemsImports = 4 Plugins\src_VCPP\Common\Common.vcxitems*{aa88b46e-b2e2-4b03-8cd5-1e9d60db6ab2}*SharedItemsImports = 4 Externals\crystaledit\editlib\editlibparsers.vcxitems*{ab827c6b-5116-408f-b453-e2075e9b73b4}*SharedItemsImports = 4 Externals\xdiff\xdiff.vcxitems*{ab827c6b-5116-408f-b453-e2075e9b73b4}*SharedItemsImports = 4 Src\CompareEngines\CompareEngines.vcxitems*{ab827c6b-5116-408f-b453-e2075e9b73b4}*SharedItemsImports = 4 Src\diffutils\diffutils.vcxitems*{ab827c6b-5116-408f-b453-e2075e9b73b4}*SharedItemsImports = 4 + Src\FilterEngine\FilterEngine.vcxitems*{ab827c6b-5116-408f-b453-e2075e9b73b4}*SharedItemsImports = 4 Plugins\src_VCPP\Common\Common.vcxitems*{bd0c5fe1-8457-49c2-8801-0c99a6e6cc03}*SharedItemsImports = 4 Externals\crystaledit\editlib\editlib.vcxitems*{c347d6ae-7a2b-4ed0-97ad-2595e1c5d7dd}*SharedItemsImports = 4 Externals\crystaledit\editlib\editlibparsers.vcxitems*{c347d6ae-7a2b-4ed0-97ad-2595e1c5d7dd}*SharedItemsImports = 4 diff --git a/DownloadDeps.cmd b/DownloadDeps.cmd index 4bc87996e36..7d606c55ddb 100644 --- a/DownloadDeps.cmd +++ b/DownloadDeps.cmd @@ -36,6 +36,9 @@ https://mirror.msys2.org/mingw/mingw32/mingw-w64-i686-md4c-0.5.2-1-any.pkg.tar.z https://mirror.msys2.org/msys/i686/gcc-libs-10.2.0-1-i686.pkg.tar.zst!Build\msys2_tmp ^ https://mirror.msys2.org/msys/i686/msys2-runtime-3.2.0-14-i686.pkg.tar.zst!Build\msys2_tmp ^ https://mirror.msys2.org/msys/i686/patch-2.7.6-1-i686.pkg.tar.xz!Build\msys2_tmp ^ +https://mirror.msys2.org/msys/i686/lemon-3.46.1-1-i686.pkg.tar.zst!Build\msys2_tmp ^ +https://mirror.msys2.org/msys/i686/re2c-3.1-2-i686.pkg.tar.zst!Build\msys2_tmp ^ +https://mirror.msys2.org/msys/i686/gcc-libs-13.3.0-1-i686.pkg.tar.zst!Build\msys2_tmp ^ http://www.magicnotes.com/steelbytes/SBAppLocale_ENG.zip!Docs\Manual\Tools pushd "%~dp0" @@ -71,6 +74,10 @@ mkdir Build\msys2\usr\share 2> NUL copy Build\msys2_tmp\usr\bin\patch.exe Build\msys2\usr\bin\ copy Build\msys2_tmp\usr\bin\msys-2.0.dll Build\msys2\usr\bin\ copy Build\msys2_tmp\usr\bin\msys-gcc_s-1.dll Build\msys2\usr\bin\ +copy Build\msys2_tmp\usr\bin\msys-gcc_s-seh-1.dll Build\msys2\usr\bin\ +copy "Build\msys2_tmp\usr\bin\msys-stdc++-6.dll" Build\msys2\usr\bin\ +copy Build\msys2_tmp\usr\bin\lemon.exe Build\msys2\usr\bin\ +copy Build\msys2_tmp\usr\bin\re2c.exe Build\msys2\usr\bin\ xcopy /s /y Build\msys2_tmp\usr\share\*.* Build\msys2\usr\share\ rmdir /q /s Build\msys2_tmp\ > NUL 2> NUL diff --git a/Externals/crystaledit/icudata/icudata.vcxproj b/Externals/crystaledit/icudata/icudata.vcxproj index 448a0f14114..3dc705667c0 100644 --- a/Externals/crystaledit/icudata/icudata.vcxproj +++ b/Externals/crystaledit/icudata/icudata.vcxproj @@ -39,7 +39,7 @@ Win32Proj {2710a368-ed56-4fb1-80c3-d93ba6483710} icudata - 10.0.22000.0 + 10.0.26100.0 $(LatestTargetPlatformVersion) diff --git a/Externals/poco/Foundation/Foundation.vcxproj b/Externals/poco/Foundation/Foundation.vcxproj index 43f7030aa72..f59bbef3dad 100644 --- a/Externals/poco/Foundation/Foundation.vcxproj +++ b/Externals/poco/Foundation/Foundation.vcxproj @@ -445,6 +445,15 @@ true + + true + + + true + + + true + true @@ -470,6 +479,9 @@ true + + true + true @@ -531,6 +543,9 @@ true + + true + true @@ -819,6 +834,8 @@ + + @@ -830,6 +847,7 @@ + @@ -850,6 +868,7 @@ + diff --git a/Externals/poco/Foundation/Foundation.vcxproj.filters b/Externals/poco/Foundation/Foundation.vcxproj.filters index e808acec7ab..8a657ccf35a 100644 --- a/Externals/poco/Foundation/Foundation.vcxproj.filters +++ b/Externals/poco/Foundation/Foundation.vcxproj.filters @@ -553,6 +553,21 @@ Logging\Source Files + + DateTime\Source Files + + + DateTime\Source Files + + + DateTime\Source Files + + + DateTime\Source Files + + + DateTime\Source Files + @@ -1050,5 +1065,17 @@ Logging\Header Files + + DateTime\Header Files + + + DateTime\Header Files + + + DateTime\Header Files + + + DateTime\Header Files + \ No newline at end of file diff --git a/Externals/poco/Foundation/src/Foundation.cpp b/Externals/poco/Foundation/src/Foundation.cpp index c314451e446..51af50a0c2a 100644 --- a/Externals/poco/Foundation/src/Foundation.cpp +++ b/Externals/poco/Foundation/src/Foundation.cpp @@ -16,9 +16,14 @@ // DateTime #include "Clock.cpp" #include "DateTime.cpp" +#include "DateTimeFormat.cpp" +#include "DateTimeFormatter.cpp" +#include "DateTimeParser.cpp" +#include "LocalDateTime.cpp" #include "Stopwatch.cpp" #include "Timespan.cpp" #include "Timestamp.cpp" +#include "Timezone.cpp" // Filesystem #include "DirectoryIterator.cpp" #include "File.cpp" diff --git a/Src/DiffContext.cpp b/Src/DiffContext.cpp index 90215cacf94..8f457f4a6b3 100644 --- a/Src/DiffContext.cpp +++ b/Src/DiffContext.cpp @@ -133,20 +133,14 @@ static bool CheckFileForVersion(const String& ext) void CDiffContext::UpdateVersion(DIFFITEM &di, int nIndex) const { DiffFileInfo & dfi = di.diffFileInfo[nIndex]; - // Check only binary files - dfi.version.SetFileVersionNone(); - - if (di.diffcode.isDirectory()) + if (!di.diffcode.exists(nIndex) || di.diffcode.isDirectory() || !CheckFileForVersion(paths::FindExtension(di.diffFileInfo[nIndex].filename))) + { + dfi.version.SetFileVersionNone(); return; + } - String spath; - if (!di.diffcode.exists(nIndex)) - return; - String ext = paths::FindExtension(di.diffFileInfo[nIndex].filename); - if (!CheckFileForVersion(ext)) - return; - spath = di.getFilepath(nIndex, GetNormalizedPath(nIndex)); - spath = paths::ConcatPath(spath, di.diffFileInfo[nIndex].filename); + const String spath = paths::ConcatPath( + di.getFilepath(nIndex, GetNormalizedPath(nIndex)), di.diffFileInfo[nIndex].filename); // Get version info if it exists CVersionInfo ver(spath.c_str()); diff --git a/Src/DiffItem.cpp b/Src/DiffItem.cpp index e0edaf3c5ca..ebd07f739f7 100644 --- a/Src/DiffItem.cpp +++ b/Src/DiffItem.cpp @@ -114,7 +114,7 @@ void DIFFITEM::Swap(int idx1, int idx2) void DIFFITEM::ClearAllAdditionalProperties() { - const int n = ((diffcode.diffcode & DIFFCODE::THREEWAY) != 0) ? 3 : 2; + const int n = diffcode.isThreeway() ? 3 : 2; for (int i = 0; i < n; ++i) diffFileInfo[i].m_pAdditionalProperties.reset(); if (HasChildren()) @@ -223,7 +223,7 @@ void DIFFCODE::swap(int idx1, int idx2) bool binflag2 = (diffcode & (BINSIDE1 << idx2)); Set(BINSIDE1 << idx1, binflag2 ? (BINSIDE1 << idx1) : 0); Set(BINSIDE1 << idx2, binflag1 ? (BINSIDE1 << idx2) : 0); - if ((diffcode & THREEWAY) != 0) + if (isThreeway()) { int idx = -1; switch (diffcode & COMPAREFLAGS3WAY) diff --git a/Src/DiffItem.h b/Src/DiffItem.h index c1890f59ede..6edc3b2c6f3 100644 --- a/Src/DiffItem.h +++ b/Src/DiffItem.h @@ -137,7 +137,11 @@ struct DIFFCODE } bool existAll() const { - return ((diffcode & DIFFCODE::THREEWAY) ? DIFFCODE::ALL : DIFFCODE::BOTH) == (diffcode & DIFFCODE::ALL); + return (isThreeway() ? DIFFCODE::ALL : DIFFCODE::BOTH) == (diffcode & DIFFCODE::ALL); + } + bool isThreeway() const + { + return (diffcode & DIFFCODE::THREEWAY) != 0; } // compare result diff --git a/Src/DirActions.cpp b/Src/DirActions.cpp index a39e961f6de..ed9bff292e9 100644 --- a/Src/DirActions.cpp +++ b/Src/DirActions.cpp @@ -1033,12 +1033,12 @@ int GetColImage(const DIFFITEM &di) if (di.diffcode.isSideFirstOnly()) return (di.diffcode.isDirectory() ? DIFFIMG_LDIRUNIQUE : DIFFIMG_LUNIQUE); if (di.diffcode.isSideSecondOnly()) - return ((di.diffcode.diffcode & DIFFCODE::THREEWAY) == 0 ? + return (!di.diffcode.isThreeway() ? (di.diffcode.isDirectory() ? DIFFIMG_RDIRUNIQUE : DIFFIMG_RUNIQUE) : (di.diffcode.isDirectory() ? DIFFIMG_MDIRUNIQUE : DIFFIMG_MUNIQUE)); if (di.diffcode.isSideThirdOnly()) return (di.diffcode.isDirectory() ? DIFFIMG_RDIRUNIQUE : DIFFIMG_RUNIQUE); - if ((di.diffcode.diffcode & DIFFCODE::THREEWAY) != 0) + if (di.diffcode.isThreeway()) { if (!di.diffcode.exists(0)) return (di.diffcode.isDirectory() ? DIFFIMG_LDIRMISSING : DIFFIMG_LMISSING); diff --git a/Src/DirDoc.cpp b/Src/DirDoc.cpp index 3d69f6a2931..fed21afa524 100644 --- a/Src/DirDoc.cpp +++ b/Src/DirDoc.cpp @@ -32,6 +32,7 @@ #include "LineFiltersList.h" #include "SubstitutionFiltersList.h" #include "FileFilterHelper.h" +#include "FilterErrorMessages.h" #include "DirActions.h" #include "DirScan.h" #include "MessageBoxDialog.h" @@ -246,7 +247,6 @@ void CDirDoc::InitDiffContext(CDiffContext *pCtxt) if (m_pDirView) pCtxt->m_pPropertySystem.reset(new PropertySystem(m_pDirView->GetDirViewColItems()->GetAdditionalPropertyNames())); - m_imgfileFilter.UseMask(true); m_imgfileFilter.SetMask(GetOptionsMgr()->GetString(OPT_CMP_IMG_FILEPATTERNS)); pCtxt->m_pImgfileFilter = &m_imgfileFilter; @@ -257,9 +257,21 @@ void CDirDoc::InitDiffContext(CDiffContext *pCtxt) pGlobalFileFilter->ReloadUpdatedFilters(); m_fileHelper.CloneFrom(pGlobalFileFilter); pCtxt->m_piFilterGlobal = &m_fileHelper; + pCtxt->m_piFilterGlobal->SetDiffContext(pCtxt); // All plugin management is done by our plugin manager pCtxt->m_piPluginInfos = GetOptionsMgr()->GetBool(OPT_PLUGINS_ENABLED) ? &m_pluginman : nullptr; + + CheckFilter(); +} + +void CDirDoc::CheckFilter() +{ + for (const auto* error: m_pCtxt->m_piFilterGlobal->GetErrorList()) + { + String msg = FormatFilterErrorSummary(*error); + RootLogger::Error(msg); + } } /** diff --git a/Src/DirDoc.h b/Src/DirDoc.h index 07243849253..976159c04a0 100644 --- a/Src/DirDoc.h +++ b/Src/DirDoc.h @@ -132,6 +132,7 @@ class CDirDoc : public CDocument, public IMDITab, public IDirDoc void InitDiffContext(CDiffContext *pCtxt); void LoadLineFilterList(CDiffContext *pCtxt); void LoadSubstitutionFiltersList(CDiffContext* pCtxt); + void CheckFilter(); // Generated message map functions //{{AFX_MSG(CDirDoc) diff --git a/Src/DirScan.cpp b/Src/DirScan.cpp index def84e6a5f2..a14d855ab8e 100644 --- a/Src/DirScan.cpp +++ b/Src/DirScan.cpp @@ -242,16 +242,6 @@ int DirScan_GetItems(const PathContext &paths, const String subdir[], { leftnewsub = (nDiffCode & DIFFCODE::FIRST) ? subprefix[0] + dirs[0][i].filename.get() : subprefix[0] + dirs[1][j].filename.get(); rightnewsub = (nDiffCode & DIFFCODE::SECOND) ? subprefix[1] + dirs[1][j].filename.get() : subprefix[1] + dirs[0][i].filename.get(); - - // Test against filter so we don't include contents of filtered out directories - // Also this is only place we can test for both-sides directories in recursive compare - if ((pCtxt->m_piFilterGlobal!=nullptr && !pCtxt->m_piFilterGlobal->includeDir(leftnewsub, rightnewsub)) || - (pCtxt->m_bIgnoreReparsePoints && ( - (nDiffCode & DIFFCODE::FIRST) && (dirs[0][i].flags.attributes & FILE_ATTRIBUTE_REPARSE_POINT) || - (nDiffCode & DIFFCODE::SECOND) && (dirs[1][j].flags.attributes & FILE_ATTRIBUTE_REPARSE_POINT)) - ) - ) - nDiffCode |= DIFFCODE::SKIPPED; } else { @@ -267,17 +257,6 @@ int DirScan_GetItems(const PathContext &paths, const String subdir[], if (nDiffCode & DIFFCODE::THIRD) rightnewsub += dirs[2][k].filename; else if (nDiffCode & DIFFCODE::FIRST) rightnewsub += dirs[0][i].filename; else if (nDiffCode & DIFFCODE::SECOND) rightnewsub += dirs[1][j].filename; - - // Test against filter so we don't include contents of filtered out directories - // Also this is only place we can test for both-sides directories in recursive compare - if ((pCtxt->m_piFilterGlobal!=nullptr && !pCtxt->m_piFilterGlobal->includeDir(leftnewsub, middlenewsub, rightnewsub)) || - (pCtxt->m_bIgnoreReparsePoints && ( - (nDiffCode & DIFFCODE::FIRST) && (dirs[0][i].flags.attributes & FILE_ATTRIBUTE_REPARSE_POINT) || - (nDiffCode & DIFFCODE::SECOND) && (dirs[1][j].flags.attributes & FILE_ATTRIBUTE_REPARSE_POINT) || - (nDiffCode & DIFFCODE::THIRD) && (dirs[2][k].flags.attributes & FILE_ATTRIBUTE_REPARSE_POINT)) - ) - ) - nDiffCode |= DIFFCODE::SKIPPED; } // add to list @@ -305,7 +284,7 @@ int DirScan_GetItems(const PathContext &paths, const String subdir[], (nDiffCode & DIFFCODE::FIRST ) ? &dirs[0][i] : nullptr, (nDiffCode & DIFFCODE::SECOND) ? &dirs[1][j] : nullptr, nDiffCode, myStruct, parent); - if ((nDiffCode & DIFFCODE::SKIPPED) == 0 && ((nDiffCode & DIFFCODE::SIDEFLAGS) == DIFFCODE::BOTH || bUniques)) + if ((me->diffcode.diffcode & DIFFCODE::SKIPPED) == 0 && ((nDiffCode & DIFFCODE::SIDEFLAGS) == DIFFCODE::BOTH || bUniques)) { // Scan recursively all subdirectories too, we are not adding folders String newsubdir[3] = {leftnewsub, rightnewsub}; @@ -322,7 +301,7 @@ int DirScan_GetItems(const PathContext &paths, const String subdir[], (nDiffCode & DIFFCODE::SECOND) ? &dirs[1][j] : nullptr, (nDiffCode & DIFFCODE::THIRD ) ? &dirs[2][k] : nullptr, nDiffCode, myStruct, parent); - if ((nDiffCode & DIFFCODE::SKIPPED) == 0 && ((nDiffCode & DIFFCODE::SIDEFLAGS) == DIFFCODE::ALL || bUniques)) + if ((me->diffcode.diffcode & DIFFCODE::SKIPPED) == 0 && ((nDiffCode & DIFFCODE::SIDEFLAGS) == DIFFCODE::ALL || bUniques)) { // Scan recursively all subdirectories too, we are not adding folders String newsubdir[3] = {leftnewsub, middlenewsub, rightnewsub}; @@ -873,16 +852,7 @@ static void CompareDiffItem(FolderCmp &fc, DIFFITEM &di) else { // 1. Test against filters - if (pCtxt->m_piFilterGlobal==nullptr || - (nDirs == 2 && pCtxt->m_piFilterGlobal->includeFile( - paths::ConcatPath(di.diffFileInfo[0].path, di.diffFileInfo[0].filename), - paths::ConcatPath(di.diffFileInfo[1].path, di.diffFileInfo[1].filename) - )) || - (nDirs == 3 && pCtxt->m_piFilterGlobal->includeFile( - paths::ConcatPath(di.diffFileInfo[0].path, di.diffFileInfo[0].filename), - paths::ConcatPath(di.diffFileInfo[1].path, di.diffFileInfo[1].filename), - paths::ConcatPath(di.diffFileInfo[2].path, di.diffFileInfo[2].filename) - ))) + if (pCtxt->m_piFilterGlobal==nullptr || pCtxt->m_piFilterGlobal->includeFile(di)) { di.diffcode.diffcode |= DIFFCODE::INCLUDED; di.diffcode.diffcode |= fc.prepAndCompareFiles(di); @@ -990,10 +960,32 @@ static DIFFITEM *AddToList(const String& sDir1, const String& sDir2, const Strin di->diffFileInfo[2].filename = ent2->filename; } + CDiffContext *pCtxt = myStruct->context; + + // Test against filter so we don't include contents of filtered out directories + // Also this is only place we can test for both-sides directories in recursive compare + if ((pCtxt->m_piFilterGlobal != nullptr && !pCtxt->m_piFilterGlobal->includeDir(*di))) + code |= DIFFCODE::SKIPPED; + if (nItems == 2) + { + if (pCtxt->m_bIgnoreReparsePoints && ( + (code & DIFFCODE::FIRST) && (di->diffFileInfo[0].flags.attributes & FILE_ATTRIBUTE_REPARSE_POINT) || + (code & DIFFCODE::SECOND) && (di->diffFileInfo[1].flags.attributes & FILE_ATTRIBUTE_REPARSE_POINT)) + ) + code |= DIFFCODE::SKIPPED; di->diffcode.diffcode = code; + } else + { + if (pCtxt->m_bIgnoreReparsePoints && ( + (code & DIFFCODE::FIRST) && (di->diffFileInfo[0].flags.attributes & FILE_ATTRIBUTE_REPARSE_POINT) || + (code & DIFFCODE::SECOND) && (di->diffFileInfo[1].flags.attributes & FILE_ATTRIBUTE_REPARSE_POINT) || + (code & DIFFCODE::THIRD) && (di->diffFileInfo[2].flags.attributes & FILE_ATTRIBUTE_REPARSE_POINT)) + ) + code |= DIFFCODE::SKIPPED; di->diffcode.diffcode = code | DIFFCODE::THREEWAY; + } if (!myStruct->bMarkedRescan && myStruct->m_fncCollect) { diff --git a/Src/DirView.cpp b/Src/DirView.cpp index 1a06e37b264..b0bd109c56d 100644 --- a/Src/DirView.cpp +++ b/Src/DirView.cpp @@ -4401,68 +4401,6 @@ void CDirView::GetColors (int nRow, int nCol, COLORREF& clrBk, COLORREF& clrText } } -void CDirView::OnSearch() -{ - CDirDoc *pDoc = GetDocument(); - m_pList->SetRedraw(FALSE); // Turn off updating (better performance) - int nRows = m_pList->GetItemCount(); - CDiffContext& ctxt = GetDiffContext(); - - for (int currRow = nRows - 1; currRow >= 0; currRow--) - { - DIFFITEM *pos = GetItemKey(currRow); - if (IsDiffItemSpecial(pos)) - continue; - - bool bFound = false; - DIFFITEM &di = GetDiffItem(currRow); - PathContext paths; - - for (int i = 0; i < pDoc->m_nDirs; i++) - { - if (di.diffcode.exists(i) && !di.diffcode.isDirectory()) - { - GetItemFileNames(currRow, &paths); - UniMemFile ufile; - if (!ufile.OpenReadOnly(paths[i])) - continue; - - ufile.SetUnicoding(di.diffFileInfo[i].encoding.m_unicoding); - ufile.SetBom(di.diffFileInfo[i].encoding.m_bom); - ufile.SetCodepage(di.diffFileInfo[i].encoding.m_codepage); - - ufile.ReadBom(); - - String line; - for (;;) - { - bool lossy = false; - if (!ufile.ReadString(line, &lossy)) - break; - - if (tc::tcsstr(line.c_str(), _T("DirView"))) - { - bFound = true; - break; - } - } - - ufile.Close(); - if (bFound) - break; - } - } - if (!bFound) - { - String hiddden_item_path = di.getItemRelativePath(); - SetItemViewFlag(di, ViewCustomFlags::HIDDEN, ViewCustomFlags::VISIBILITY); - DeleteItem(currRow); - ctxt.m_vCurrentlyHiddenItems.push_back(hiddden_item_path); - } - } - m_pList->SetRedraw(TRUE); // Turn updating back on -} - /** * @brief Drag files/directories from folder compare listing view. */ diff --git a/Src/DirView.h b/Src/DirView.h index 8725e5d65a3..95e9a9b38c1 100644 --- a/Src/DirView.h +++ b/Src/DirView.h @@ -401,7 +401,6 @@ class CDirView : public CListView afx_msg void OnEndLabelEdit(NMHDR* pNMHDR, LRESULT* pResult); afx_msg void OnODFindItem(NMHDR* pNMHDR, LRESULT* pResult); afx_msg void OnCustomDraw(NMHDR* pNMHDR, LRESULT* pResult); - afx_msg void OnSearch(); afx_msg void OnBeginDrag(NMHDR* pNMHDR, LRESULT* pResult); afx_msg void OnStatusBarClick(NMHDR* pNMHDR, LRESULT* pResult); diff --git a/Src/FileFilter.cpp b/Src/FileFilter.cpp index 7549c2040b4..5f61d6cf331 100644 --- a/Src/FileFilter.cpp +++ b/Src/FileFilter.cpp @@ -7,7 +7,13 @@ #include "pch.h" #include "FileFilter.h" +#include "FilterEngine/FilterExpression.h" +#include "DiffItem.h" +#include "unicoder.h" +#include "paths.h" #include +#include +#include using std::vector; @@ -20,6 +26,51 @@ FileFilter::~FileFilter() EmptyFilterList(&filefiltersExclude); EmptyFilterList(&dirfilters); EmptyFilterList(&dirfiltersExclude); + EmptyExpressionList(&fileExpressionFilters); + EmptyExpressionList(&fileExpressionFiltersExclude); + EmptyExpressionList(&dirExpressionFilters); + EmptyExpressionList(&dirExpressionFiltersExclude); +} + +/** + * @brief Add a single pattern (if nonempty & valid) to a pattern list. + * + * @param [in] filterList List where pattern is added. + * @param [in] str Temporary variable (ie, it may be altered) + * @param [in] lineNumber Line number in filter file, used for error reporting. + */ +void FileFilter::AddFilterPattern(vector* filterList, const String& str, bool fileFilter, int lineNumber) +{ + int re_opts = Poco::RegularExpression::RE_CASELESS; + std::string regexString = ucr::toUTF8(str); + re_opts |= Poco::RegularExpression::RE_UTF8; + try + { + filterList->push_back(FileFilterElementPtr(new FileFilterElement(regexString, re_opts, fileFilter))); + } + catch (const Poco::RegularExpressionException& e) + { + errors.emplace_back(FILTER_ERROR_INVALID_REGULAR_EXPRESSION, lineNumber, -1, str, e.message()); + } +} + +/** + * @brief Add a single expression (if nonempty & valid) to a expression list. + * + * @param [in] filterList List where expression is added. + * @param [in] str Temporary variable (ie, it may be altered) + * @param [in] lineNumber Line number in filter file, used for error reporting. +*/ +void FileFilter::AddFilterExpression(vector* filterList, const String& str, int lineNumber) +{ + String str2 = strutils::trim_ws(str); + std::shared_ptr pExpression(new FilterExpression(ucr::toUTF8(str))); + if (pExpression->errorCode != 0) + { + errors.emplace_back(pExpression->errorCode, lineNumber, pExpression->errorPosition, str2, pExpression->errorMessage); + return; + } + filterList->emplace_back(pExpression); } /** @@ -32,6 +83,16 @@ void FileFilter::EmptyFilterList(vector *filterList) filterList->clear(); } +/** + * @brief Deletes items from expression list. + * + * @param [in] expressionList List to empty. + */ +void FileFilter::EmptyExpressionList(vector *expressionList) +{ + expressionList->clear(); +} + /** * @brief Clone file filter from another filter. * This function clones file filter from another filter. @@ -63,6 +124,23 @@ void FileFilter::CloneFrom(const FileFilter* filter) { dirfilters.emplace_back(std::make_shared(filter->dirfilters[i].get())); } + + fileExpressionFilters.clear(); + count = filter->fileExpressionFilters.size(); + fileExpressionFilters.reserve(count); + for (size_t i = 0; i < count; i++) + { + fileExpressionFilters.emplace_back(std::make_shared(*filter->fileExpressionFilters[i].get())); + } + + dirExpressionFilters.clear(); + count = filter->dirExpressionFilters.size(); + dirExpressionFilters.reserve(count); + for (size_t i = 0; i < count; i++) + { + dirExpressionFilters.emplace_back(std::make_shared(*filter->dirExpressionFilters[i].get())); + } + filefiltersExclude.clear(); count = filter->filefiltersExclude.size(); filefiltersExclude.reserve(count); @@ -78,4 +156,223 @@ void FileFilter::CloneFrom(const FileFilter* filter) { dirfiltersExclude.emplace_back(std::make_shared(filter->dirfiltersExclude[i].get())); } + + fileExpressionFiltersExclude.clear(); + count = filter->fileExpressionFiltersExclude.size(); + fileExpressionFiltersExclude.reserve(count); + for (size_t i = 0; i < count; i++) + { + fileExpressionFiltersExclude.emplace_back(std::make_shared(*filter->fileExpressionFiltersExclude[i].get())); + } + + dirExpressionFiltersExclude.clear(); + count = filter->dirExpressionFiltersExclude.size(); + dirExpressionFiltersExclude.reserve(count); + for (size_t i = 0; i < count; i++) + { + dirExpressionFiltersExclude.emplace_back(std::make_shared(*filter->dirExpressionFiltersExclude[i].get())); + } + + errors = filter->errors; +} + +/** + * @brief Test given string against given regexp list. + * + * @param [in] filterList List of regexps to test against. + * @param [in] szTest String to test against regexps. + * @return true if string passes + * @note Matching stops when first match is found. + */ +bool TestAgainstRegList(const vector* filterList, const String& szTest) +{ + if (filterList->size() == 0) + return false; + + std::string compString, compStringFileName; + ucr::toUTF8(szTest, compString); + vector::const_iterator iter = filterList->begin(); + while (iter != filterList->end()) + { + Poco::RegularExpression::Match match; + try + { + if ((*iter)->_fileNameOnly && compStringFileName.empty()) + ucr::toUTF8(paths::FindFileName(szTest), compStringFileName); + if ((*iter)->regexp.match((*iter)->_fileNameOnly ? compStringFileName : compString, 0, match) > 0) + return true; + } + catch (...) + { + // TODO: + } + + ++iter; + } + return false; +} + +/** + * @brief Test given DIFFITEM against given regexp list. + * @param [in] filterList List of regexps to test against. + * @param [in] di DIFFITEM to test against regexps. + * @return true if DIFFITEM passes + * @note Matching stops when first match is found. + */ +bool FileFilter::TestAgainstRegList(const vector* filterList, const DIFFITEM& di) +{ + if (filterList->size() == 0) + return false; + + const int nDirs = di.diffcode.isThreeway() ? 3 : 2; + int i = 0; + for (; i < nDirs; ++i) + { + if (!di.diffFileInfo[i].filename.get().empty()) + break; + } + if (i >= nDirs) + return false; + + const String& szTest = (di.diffFileInfo[i].IsDirectory() ? _T("\\") : _T("")) + paths::ConcatPath(di.diffFileInfo[i].path, di.diffFileInfo[i].filename); + std::string compString, compStringFileName; + ucr::toUTF8(szTest, compString); + vector::const_iterator iter = filterList->begin(); + while (iter != filterList->end()) + { + Poco::RegularExpression::Match match; + try + { + if ((*iter)->_fileNameOnly && compStringFileName.empty()) + ucr::toUTF8(paths::FindFileName(szTest), compStringFileName); + if ((*iter)->regexp.match((*iter)->_fileNameOnly ? compStringFileName : compString, 0, match) > 0) + return true; + } + catch (...) + { + // TODO: + } + + ++iter; + } + return false; } + +/** + * @brief Test given DIFFITEM against given expression list. + * @param [in] filterList List of expressions to test against. + * @param [in] di DIFFITEM to test against regexps. + * @return true if DIFFITEM passes + * @note Matching stops when first match is found. + */ +bool FileFilter::TestAgainstExpressionList(const vector* filterList, const DIFFITEM& di) +{ + if (filterList->size() == 0) + return false; + + for (const auto& filter : *filterList) + { + if (filter->Evaluate(di)) + return true; + } + + return false; +} + +/** + * @brief Test given filename against filefilter. + * + * Test filename against active filefilter. If matching rule is found + * we must first determine type of rule that matched. If we return false + * from this function directory scan marks file as skipped. + * + * @param [in] szFileName Filename to test + * @return true if file passes the filter + */ +bool FileFilter::TestFileNameAgainstFilter(const String& szFileName) const +{ + if (::TestAgainstRegList(&filefilters, szFileName)) + { + if (filefiltersExclude.empty() || !::TestAgainstRegList(&filefiltersExclude, szFileName)) + return !default_include; + } + return default_include; +} + +/** + * @brief Set diff context for all filters in the given file filter. + * @param [in] pDiffContext Pointer to diff context to set for all filters. + */ +void FileFilter::SetDiffContext(const CDiffContext* pDiffContext) +{ + for (auto& filters : + { fileExpressionFilters, fileExpressionFiltersExclude, dirExpressionFilters, dirExpressionFiltersExclude }) + { + for (const auto& filter : filters) + filter->SetDiffContext(pDiffContext); + } +} + +/** + * @brief Test given DIFFITEM against filefilter. + * @param [in] di DIFFITEM to test + * @return true if DIFFITEM passes the filter + */ +bool FileFilter::TestFileDiffItemAgainstFilter(const DIFFITEM& di) const +{ + bool matched = TestAgainstRegList(&filefilters, di); + if (!matched && TestAgainstExpressionList(&fileExpressionFilters, di)) + matched = true; + if (matched) + { + matched = !TestAgainstRegList(&filefiltersExclude, di); + if (matched) + matched = !TestAgainstExpressionList(&fileExpressionFiltersExclude, di); + } + if (matched) + return !default_include; + return default_include; +} + +/** + * @brief Test given directory name against filefilter. + * + * Test directory name against active filefilter. If matching rule is found + * we must first determine type of rule that matched. If we return false + * from this function directory scan marks file as skipped. + * + * @param [in] szDirName Directory name to test + * @return true if directory name passes the filter + */ +bool FileFilter::TestDirNameAgainstFilter(const String& szDirName) const +{ + if (::TestAgainstRegList(&dirfilters, szDirName)) + { + if (dirfiltersExclude.empty() || !::TestAgainstRegList(&dirfiltersExclude, szDirName)) + return !default_include; + } + return default_include; +} + +/** + * @brief Test given DIFFITEM against filefilter. + * @param [in] di DIFFITEM to test + * @return true if DIFFITEM passes the filter + */ +bool FileFilter::TestDirDiffItemAgainstFilter(const DIFFITEM& di) const +{ + bool matched = TestAgainstRegList(&dirfilters, di); + if (!matched && TestAgainstExpressionList(&dirExpressionFilters, di)) + matched = true; + if (matched) + { + matched = !TestAgainstRegList(&dirfiltersExclude, di); + if (matched) + matched = !TestAgainstExpressionList(&dirExpressionFiltersExclude, di); + } + if (matched) + return !default_include; + return default_include; +} + + diff --git a/Src/FileFilter.h b/Src/FileFilter.h index ceb33db5199..358c760d423 100644 --- a/Src/FileFilter.h +++ b/Src/FileFilter.h @@ -6,12 +6,17 @@ */ #pragma once +#include "FilterError.h" #include #include #define POCO_NO_UNWINDOWS 1 #include #include "UnicodeString.h" +struct FilterExpression; +class CDiffContext; +class DIFFITEM; + /** * @brief FileFilter rule. * @@ -40,7 +45,21 @@ struct FileFilterElement } }; +struct FileFilterErrorInfo +{ + int line; /**< Line number in filter file where error occurred */ + FilterErrorCode errorCode; /**< Error code, see FilterErrorCode enum for values */ + int errorPosition; /**< Position in line where error occurred, if applicable */ + String srcText; /**< Source text of the line where error occurred, if applicable */ + std::string errorText; /**< Text describing the error, if applicable */ + FileFilterErrorInfo(FilterErrorCode code, int lineNumber, int position, const String& src, const std::string& msg) : + errorCode(code), line(lineNumber), errorPosition(position), srcText(src), errorText(msg) + { + } +}; + typedef std::shared_ptr FileFilterElementPtr; +typedef std::shared_ptr FilterExpressionPtr; /** * @brief One actual filter. @@ -61,11 +80,27 @@ struct FileFilter std::vector filefiltersExclude; /**< List of rules for files (exclude) */ std::vector dirfilters; /**< List of rules for directories */ std::vector dirfiltersExclude; /**< List of rules for directories (exclude) */ + std::vector fileExpressionFilters; /**< List of file filter expressions */ + std::vector fileExpressionFiltersExclude; /**< List of file filter expressions (exclude) */ + std::vector dirExpressionFilters; /**< List of dir filter expressions */ + std::vector dirExpressionFiltersExclude; /**< List of dir filter expressions (exclude) */ + std::vector errors; /**< List of errors in filter file */ FileFilter() : default_include(true) { } ~FileFilter(); + void AddFilterPattern(std::vector* filterList, const String& str, bool fileFilter, int lineNumber); + void AddFilterExpression(std::vector* filterList, const String& str, int lineNumber); static void EmptyFilterList(std::vector *filterList); + static void EmptyExpressionList(std::vector *filterList); void CloneFrom(const FileFilter* filter); + // methods to actually use filter + bool TestFileNameAgainstFilter(const String& szFileName) const; + void SetDiffContext(const CDiffContext* pDiffContext); + bool TestFileDiffItemAgainstFilter(const DIFFITEM& di) const; + bool TestDirNameAgainstFilter(const String& szDirName) const; + bool TestDirDiffItemAgainstFilter(const DIFFITEM& di) const; + static bool TestAgainstRegList(const std::vector* filterList, const DIFFITEM& di); + static bool TestAgainstExpressionList(const std::vector* filterList, const DIFFITEM& di); }; typedef std::shared_ptr FileFilterPtr; diff --git a/Src/FileFilterHelper.cpp b/Src/FileFilterHelper.cpp index e7844406060..666d72aa9ef 100644 --- a/Src/FileFilterHelper.cpp +++ b/Src/FileFilterHelper.cpp @@ -7,25 +7,24 @@ #include "pch.h" #include "FileFilterHelper.h" +#include "FilterExpression.h" #include "UnicodeString.h" #include "FilterList.h" -#include "DirItem.h" +#include "DiffItem.h" #include "FileFilterMgr.h" #include "paths.h" #include "Environment.h" #include "unicoder.h" -using std::vector; - /** * @brief Constructor, creates new filtermanager. */ FileFilterHelper::FileFilterHelper() : m_pMaskFileFilter(nullptr) +, m_pMaskFileFilterExclude(nullptr) , m_pMaskDirFilter(nullptr) -, m_bUseMask(true) +, m_pMaskDirFilterExclude(nullptr) , m_fileFilterMgr(new FileFilterMgr) -, m_currentFilter(nullptr) { } @@ -34,37 +33,12 @@ FileFilterHelper::FileFilterHelper() */ FileFilterHelper::~FileFilterHelper() = default; -/** - * @brief Store current filter path. - * - * Select filter based on filepath. If filter with that path - * is found select it. Otherwise set path to empty (default). - * @param [in] szFileFilterPath Full path to filter to select. - */ -void FileFilterHelper::SetFileFilterPath(const String& szFileFilterPath) -{ - // Use none as default path - m_sFileFilterPath.clear(); - - if (m_fileFilterMgr == nullptr) - return; - - // Don't bother to lookup empty path - if (!szFileFilterPath.empty()) - { - m_currentFilter = m_fileFilterMgr->GetFilterByPath(szFileFilterPath); - if (m_currentFilter != nullptr) - m_sFileFilterPath = szFileFilterPath; - } -} - /** * @brief Get list of filters currently available. * - * @param [out] selected Filepath of currently selected filter. * @return Filter list to receive found filters. */ -std::vector FileFilterHelper::GetFileFilters(String & selected) const +std::vector FileFilterHelper::GetFileFilters() const { std::vector filters; if (m_fileFilterMgr != nullptr) @@ -80,7 +54,6 @@ std::vector FileFilterHelper::GetFileFilters(String & selected) filters.push_back(filter); } } - selected = m_sFileFilterPath; return filters; } @@ -92,10 +65,9 @@ std::vector FileFilterHelper::GetFileFilters(String & selected) */ String FileFilterHelper::GetFileFilterName(const String& filterPath) const { - String selected; String name; - vector filters = GetFileFilters(selected); - vector::const_iterator iter = filters.begin(); + std::vector filters = GetFileFilters(); + std::vector::const_iterator iter = filters.begin(); while (iter != filters.end()) { if ((*iter).fullpath == filterPath) @@ -115,10 +87,9 @@ String FileFilterHelper::GetFileFilterName(const String& filterPath) const */ String FileFilterHelper::GetFileFilterPath(const String& filterName) const { - String selected; String path; - vector filters = GetFileFilters(selected); - vector::const_iterator iter = filters.begin(); + std::vector filters = GetFileFilters(); + std::vector::const_iterator iter = filters.begin(); while (iter != filters.end()) { if ((*iter).name == filterName) @@ -141,57 +112,68 @@ void FileFilterHelper::SetUserFilterPath(const String & filterPath) paths::normalize(m_sUserSelFilterPath); } -/** - * @brief Select between mask and filterfile. - * @param [in] bUseMask If true we use mask instead of filter files. - */ -void FileFilterHelper::UseMask(bool bUseMask) -{ - m_bUseMask = bUseMask; - if (m_bUseMask) - { - if (m_pMaskFileFilter == nullptr) - { - m_pMaskFileFilter.reset(new FilterList); - } - if (m_pMaskDirFilter == nullptr) - { - m_pMaskDirFilter.reset(new FilterList); - } - } - else - { - m_pMaskFileFilter.reset(); - m_pMaskDirFilter.reset(); - } -} - /** * @brief Set filemask for filtering. * @param [in] strMask Mask to set (e.g. *.cpp;*.h). */ void FileFilterHelper::SetMask(const String& strMask) { - if (!m_bUseMask) - { - throw "Filter mask tried to set when masks disabled!"; - } - m_sMask = strMask; - auto [regExpFile, regExpFileExclude, regExpDir, regExpDirExclude] = ParseExtensions(strMask); + String flt = strutils::trim_ws(strMask); + String path = GetFileFilterPath(flt); + if (!path.empty()) + flt = _T("fp:") + flt; + + m_sMask = flt; + auto [regExpFile, regExpFileExclude, regExpDir, regExpDirExclude, pRegexOrExpressionFilter, pRegexOrExpressionFilterExclude] + = ParseExtensions(strMask); std::string regexp_str_file = ucr::toUTF8(regExpFile); std::string regexp_str_file_excluded = ucr::toUTF8(regExpFileExclude); std::string regexp_str_dir = ucr::toUTF8(regExpDir); std::string regexp_str_dir_excluded = ucr::toUTF8(regExpDirExclude); - m_pMaskFileFilter->RemoveAllFilters(); - m_pMaskFileFilter->AddRegExp(regexp_str_file, false); + if (m_pMaskFileFilter) + m_pMaskFileFilter->RemoveAllFilters(); + if (m_pMaskDirFilter) + m_pMaskDirFilter->RemoveAllFilters(); + if (m_pMaskFileFilterExclude) + m_pMaskFileFilterExclude->RemoveAllFilters(); + if (m_pMaskDirFilterExclude) + m_pMaskDirFilterExclude->RemoveAllFilters(); + if (!regexp_str_file.empty()) + { + if (!m_pMaskFileFilter) + m_pMaskFileFilter = std::make_unique(); + m_pMaskFileFilter->AddRegExp(regexp_str_file); + } + else + m_pMaskFileFilter.reset(); + if (!regexp_str_dir.empty()) + { + if (!m_pMaskDirFilter) + m_pMaskDirFilter = std::make_unique(); + m_pMaskDirFilter->AddRegExp(regexp_str_dir); + } + else + m_pMaskDirFilter.reset(); if (!regexp_str_file_excluded.empty()) - m_pMaskFileFilter->AddRegExp(regexp_str_file_excluded, true); - m_pMaskDirFilter->RemoveAllFilters(); - m_pMaskDirFilter->AddRegExp(regexp_str_dir, false); + { + if (!m_pMaskFileFilterExclude) + m_pMaskFileFilterExclude = std::make_unique(); + m_pMaskFileFilterExclude->AddRegExp(regexp_str_file_excluded); + } + else + m_pMaskFileFilterExclude.reset(); if (!regexp_str_dir_excluded.empty()) - m_pMaskDirFilter->AddRegExp(regexp_str_dir_excluded, true); + { + if (!m_pMaskDirFilterExclude) + m_pMaskDirFilterExclude = std::make_unique(); + m_pMaskDirFilterExclude->AddRegExp(regexp_str_dir_excluded); + } + else + m_pMaskDirFilterExclude.reset(); + m_pRegexOrExpressionFilter = pRegexOrExpressionFilter; + m_pRegexOrExpressionFilterExclude = pRegexOrExpressionFilterExclude; } static String addPeriodIfNoExtension(const String& path) @@ -225,6 +207,28 @@ static String addPeriodIfNoExtension(const String& path) return ret; } +void FileFilterHelper::SetDiffContext(const CDiffContext* pCtxt) +{ + if (m_pRegexOrExpressionFilter) + m_pRegexOrExpressionFilter->SetDiffContext(pCtxt); + if (m_pRegexOrExpressionFilterExclude) + m_pRegexOrExpressionFilterExclude->SetDiffContext(pCtxt); +} + +std::vector FileFilterHelper::GetErrorList() const +{ + std::vector list; + for (const auto* pfilter : { m_pRegexOrExpressionFilter.get(), m_pRegexOrExpressionFilterExclude.get() }) + { + if (pfilter) + { + for (const auto& error : pfilter->errors) + list.push_back(&error); + } + } + return list; +} + /** * @brief Check if any of filefilter rules match to filename. * @@ -233,28 +237,74 @@ static String addPeriodIfNoExtension(const String& path) */ bool FileFilterHelper::includeFile(const String& szFileName) const { - if (m_bUseMask) - { - if (m_pMaskFileFilter == nullptr) - { - throw "Use mask set, but no filter rules for mask!"; - } + // preprend a backslash if there is none + String strFileName = strutils::makelower(szFileName); + if (strFileName.empty() || strFileName[0] != '\\') + strFileName = _T("\\") + strFileName; + // append a point if there is no extension + std::string strFileNameUtf8Period = ucr::toUTF8(addPeriodIfNoExtension(strFileName)); + bool result = m_pMaskFileFilter && m_pMaskFileFilter->Match(strFileNameUtf8Period); + if (!result) + result = m_pRegexOrExpressionFilter && TestAgainstRegList(&m_pRegexOrExpressionFilter->filefilters, szFileName); + if (!result) + return false; + if (m_pMaskFileFilterExclude && m_pMaskFileFilterExclude->Match(strFileNameUtf8Period)) + return false; + if (m_pRegexOrExpressionFilter && TestAgainstRegList(&m_pRegexOrExpressionFilter->filefiltersExclude, szFileName)) + return false; + if (m_pRegexOrExpressionFilterExclude && !m_pRegexOrExpressionFilterExclude->TestFileNameAgainstFilter(szFileName)) + return false; + return true; +} +bool FileFilterHelper::includeFile(const DIFFITEM& di) const +{ + const int nDirs = di.diffcode.isThreeway() ? 3 : 2; + int i = 0; + for (; i < nDirs; ++i) + { + if (!di.diffFileInfo[i].filename.get().empty()) + break; + } + std::string strFileNameUtf8Period; + bool result = false; + if (i < nDirs) + { + String szFileName = paths::ConcatPath(di.diffFileInfo[i].path, di.diffFileInfo[i].filename); // preprend a backslash if there is none String strFileName = strutils::makelower(szFileName); if (strFileName.empty() || strFileName[0] != '\\') strFileName = _T("\\") + strFileName; // append a point if there is no extension - strFileName = addPeriodIfNoExtension(strFileName); - - return m_pMaskFileFilter->Match(ucr::toUTF8(strFileName)); + strFileNameUtf8Period = ucr::toUTF8(addPeriodIfNoExtension(strFileName)); + result = m_pMaskFileFilter && m_pMaskFileFilter->Match(strFileNameUtf8Period); } - else + if (!result) + { + if (m_pRegexOrExpressionFilter) + { + result = FileFilter::TestAgainstRegList(&m_pRegexOrExpressionFilter->filefilters, di); + if (!result) + result = FileFilter::TestAgainstExpressionList(&m_pRegexOrExpressionFilter->fileExpressionFilters, di); + } + } + if (!result) + return false; + if (i < nDirs) + { + if (m_pMaskFileFilterExclude && m_pMaskFileFilterExclude->Match(strFileNameUtf8Period)) + return false; + } + if (m_pRegexOrExpressionFilter) { - if (m_fileFilterMgr == nullptr || m_currentFilter ==nullptr) - return true; - return m_fileFilterMgr->TestFileNameAgainstFilter(m_currentFilter, szFileName); + if (FileFilter::TestAgainstRegList(&m_pRegexOrExpressionFilter->filefiltersExclude, di)) + return false; + if (FileFilter::TestAgainstExpressionList(&m_pRegexOrExpressionFilter->fileExpressionFiltersExclude, di)) + return false; } + if (m_pRegexOrExpressionFilterExclude && !m_pRegexOrExpressionFilterExclude->TestFileDiffItemAgainstFilter(di)) + return false; + return true; } /** @@ -265,33 +315,78 @@ bool FileFilterHelper::includeFile(const String& szFileName) const */ bool FileFilterHelper::includeDir(const String& szDirName) const { - if (m_bUseMask) + // preprend a backslash if there is none + String strDirName = strutils::makelower(szDirName); + if (strDirName.empty() || strDirName[0] != '\\') + strDirName = _T("\\") + strDirName; + // append a point if there is no extension + std::string strDirNameUtf8Period = ucr::toUTF8(addPeriodIfNoExtension(strDirName)); + bool result = m_pMaskDirFilter && m_pMaskDirFilter->Match(strDirNameUtf8Period); + if (!result) { - if (m_pMaskDirFilter == nullptr) - { - throw "Use mask set, but no filter rules for mask!"; - } + if (m_pRegexOrExpressionFilter) + result = TestAgainstRegList(&m_pRegexOrExpressionFilter->dirfilters, strDirName); + } + if (!result) + return false; + if (m_pMaskDirFilterExclude && m_pMaskDirFilterExclude->Match(strDirNameUtf8Period)) + return false; + if (m_pRegexOrExpressionFilter && TestAgainstRegList(&m_pRegexOrExpressionFilter->dirfiltersExclude, strDirName)) + return false; + if (m_pRegexOrExpressionFilterExclude && !m_pRegexOrExpressionFilterExclude->TestFileNameAgainstFilter(strDirName)) + return false; + return true; +} +bool FileFilterHelper::includeDir(const DIFFITEM& di) const +{ + const int nDirs = di.diffcode.isThreeway() ? 3 : 2; + int i = 0; + for (; i < nDirs; ++i) + { + if (!di.diffFileInfo[i].filename.get().empty()) + break; + } + std::string strDirNameUtf8Period; + bool result = false; + if (i < nDirs) + { + String szDirName = paths::ConcatPath(di.diffFileInfo[i].path, di.diffFileInfo[i].filename); // preprend a backslash if there is none String strDirName = strutils::makelower(szDirName); if (strDirName.empty() || strDirName[0] != '\\') strDirName = _T("\\") + strDirName; // append a point if there is no extension - strDirName = addPeriodIfNoExtension(strDirName); - - return m_pMaskDirFilter->Match(ucr::toUTF8(strDirName)); + strDirNameUtf8Period = ucr::toUTF8(addPeriodIfNoExtension(strDirName)); + result = m_pMaskDirFilter && m_pMaskDirFilter->Match(strDirNameUtf8Period); } - else + if (!result) { - if (m_fileFilterMgr == nullptr || m_currentFilter == nullptr) - return true; - - // Add a backslash - String strDirName(_T("\\")); - strDirName += szDirName; - - return m_fileFilterMgr->TestDirNameAgainstFilter(m_currentFilter, strDirName); + if (m_pRegexOrExpressionFilter) + { + result = FileFilter::TestAgainstRegList(&m_pRegexOrExpressionFilter->dirfilters, di); + if (!result) + result = FileFilter::TestAgainstExpressionList(&m_pRegexOrExpressionFilter->dirExpressionFilters, di); + } } + if (!result) + return false; + if (i < nDirs) + { + if (m_pMaskDirFilterExclude && m_pMaskDirFilterExclude->Match(strDirNameUtf8Period)) + return false; + } + if (m_pRegexOrExpressionFilter) + { + + if (FileFilter::TestAgainstRegList(&m_pRegexOrExpressionFilter->dirfiltersExclude, di)) + return false; + if (FileFilter::TestAgainstExpressionList(&m_pRegexOrExpressionFilter->dirExpressionFiltersExclude, di)) + return false; + } + if (m_pRegexOrExpressionFilterExclude && !m_pRegexOrExpressionFilterExclude->TestDirDiffItemAgainstFilter(di)) + return false; + return true; } /** @@ -330,12 +425,59 @@ static String ConvertWildcardPatternToRegexp(const String& pattern) return _T("(^|\\\\)") + strRegex; } +static std::size_t findSeparator(const String& str, String& prefix, std::size_t startPos = 0) +{ + prefix.clear(); + bool inQuotes = false; + bool allowOnlyBasicSeparators = false; + while (startPos < str.size() && str[startPos] == ' ') + ++startPos; + const String prefixes[] = { _T("f:"), _T("d:"), _T("f!:"), _T("d!:"), _T("fe:"), _T("de:"), _T("fe!:"), _T("de!:"), _T("fp:") }; + for (const auto& pf : prefixes) + { + if (str.compare(startPos, pf.size(), pf) == 0) + { + startPos += pf.size(); + allowOnlyBasicSeparators = true; + prefix = pf; + break; + } + } + for (std::size_t i = startPos; i < str.size(); ++i) + { + const auto ch = str[i]; + if (ch == '"') + inQuotes = !inQuotes; + else if (!inQuotes && + (ch == ';' || (!allowOnlyBasicSeparators && (ch == ',' || ch == '|' || ch == ':' || ch == ' ')))) + return i; + } + return String::npos; +} + +/** + * @brief Merge filter into regex or expression filter. + */ +static void mergeFilter(FileFilter* dest, const FileFilter* src) +{ + dest->filefilters.insert(dest->filefilters.end(), src->filefilters.begin(), src->filefilters.end()); + dest->filefiltersExclude.insert(dest->filefiltersExclude.end(), src->filefiltersExclude.begin(), src->filefiltersExclude.end()); + dest->dirfilters.insert(dest->dirfilters.end(), src->dirfilters.begin(), src->dirfilters.end()); + dest->dirfiltersExclude.insert(dest->dirfiltersExclude.end(), src->dirfiltersExclude.begin(), src->dirfiltersExclude.end()); + dest->fileExpressionFilters.insert(dest->fileExpressionFilters.end(), src->fileExpressionFilters.begin(), src->fileExpressionFilters.end()); + dest->fileExpressionFiltersExclude.insert(dest->fileExpressionFiltersExclude.end(), src->fileExpressionFiltersExclude.begin(), src->fileExpressionFiltersExclude.end()); + dest->dirExpressionFilters.insert(dest->dirExpressionFilters.end(), src->dirExpressionFilters.begin(), src->dirExpressionFilters.end()); + dest->dirExpressionFiltersExclude.insert(dest->dirExpressionFiltersExclude.end(), src->dirExpressionFiltersExclude.begin(), src->dirExpressionFiltersExclude.end()); + dest->errors.insert(dest->errors.end(), src->errors.begin(), src->errors.end()); +} + /** * @brief Convert user-given extension list to valid regular expression. * @param [in] Extension list/mask to convert to regular expression. * @return Regular expression that matches extension list. */ -std::tuple FileFilterHelper::ParseExtensions(const String &extensions) const +std::tuple, std::shared_ptr> +FileFilterHelper::ParseExtensions(const String &extensions) const { String strFileParsed; String strDirParsed; @@ -344,19 +486,17 @@ std::tuple FileFilterHelper::ParseExtensions(con std::vector dirPatterns; std::vector dirPatternsExclude; String ext(extensions); - static const tchar_t pszSeps[] = _T(" ;|,:"); - - ext += _T(";"); // Add one separator char to end - size_t pos = ext.find_first_of(pszSeps); - - while (pos != String::npos) + String prefix; + std::shared_ptr pRegexOrExpressionFilter; + std::shared_ptr pRegexOrExpressionFilterExclude; + size_t pos = 0; + for (;;) { - String token = ext.substr(0, pos); // Get first extension - ext = ext.substr(pos + 1); // Remove extension + separator - - // Only "*." or "*.something" allowed, other ignored - if (token.length() >= 1) + pos = findSeparator(ext, prefix); + String token = ext.substr(0, pos == String::npos ? ext.size() : pos); + if (token.length() >= 1 && prefix.empty()) { + // Only "*." or "*.something" allowed, other ignored bool exclude = token[0] == '!'; if (exclude) token = token.substr(1); @@ -380,21 +520,83 @@ std::tuple FileFilterHelper::ParseExtensions(con filePatterns.push_back(strRegex); } } - - pos = ext.find_first_of(pszSeps); + else if (!prefix.empty()) + { + if (!pRegexOrExpressionFilter) + { + pRegexOrExpressionFilter = std::make_shared(); + pRegexOrExpressionFilter->default_include = false; + pRegexOrExpressionFilter->name = extensions; + } + token = strutils::trim_ws(token.substr(token.find(':') + 1)); + if (prefix == _T("f:")) + pRegexOrExpressionFilter->AddFilterPattern( + &pRegexOrExpressionFilter->filefilters, token, true, 0); + else if (prefix == _T("f!:")) + pRegexOrExpressionFilter->AddFilterPattern( + &pRegexOrExpressionFilter->filefiltersExclude, token, true, 0); + else if (prefix == _T("d:")) + pRegexOrExpressionFilter->AddFilterPattern( + &pRegexOrExpressionFilter->dirfilters, token, false, 0); + else if (prefix == _T("d!:")) + pRegexOrExpressionFilter->AddFilterPattern( + &pRegexOrExpressionFilter->dirfiltersExclude, token, false, 0); + else if (prefix == _T("fe:")) + pRegexOrExpressionFilter->AddFilterExpression( + &pRegexOrExpressionFilter->fileExpressionFilters, token, 0); + else if (prefix == _T("fe!:")) + pRegexOrExpressionFilter->AddFilterExpression( + &pRegexOrExpressionFilter->fileExpressionFiltersExclude, token, 0); + else if (prefix == _T("de:")) + pRegexOrExpressionFilter->AddFilterExpression( + &pRegexOrExpressionFilter->dirExpressionFilters, token, 0); + else if (prefix == _T("de!:")) + pRegexOrExpressionFilter->AddFilterExpression( + &pRegexOrExpressionFilter->dirExpressionFiltersExclude, token, 0); + else if (prefix == _T("fp:")) + { + const String path = GetFileFilterPath(token); + if (!path.empty()) + { + const FileFilter* filter = m_fileFilterMgr->GetFilterByPath(path); + if (filter) + { + if (!filter->default_include) + mergeFilter(pRegexOrExpressionFilter.get(), filter); + else + { + if (!pRegexOrExpressionFilterExclude) + { + pRegexOrExpressionFilterExclude = std::make_shared(); + pRegexOrExpressionFilterExclude->default_include = true; + pRegexOrExpressionFilterExclude->name = extensions; + } + mergeFilter(pRegexOrExpressionFilterExclude.get(), filter); + } + } + } + else + { + pRegexOrExpressionFilter->errors.emplace_back(FILTER_ERROR_FILTER_NAME_NOT_FOUND, -1, -1, token, ""); + } + } + } + if (pos == String::npos) + break; // No more separators found + ext = ext.substr(pos + 1); // Remove extension + separator } - if (filePatterns.empty()) + if (filePatterns.empty() && (!pRegexOrExpressionFilter || (pRegexOrExpressionFilter->filefilters.empty() && pRegexOrExpressionFilter->fileExpressionFilters.empty()))) strFileParsed = _T(".*"); // Match everything else strFileParsed = strutils::join(filePatterns.begin(), filePatterns.end(), _T("|")); - if (dirPatterns.empty()) + if (dirPatterns.empty() && (!pRegexOrExpressionFilter || (pRegexOrExpressionFilter->dirfilters.empty() && pRegexOrExpressionFilter->dirExpressionFilters.empty()))) strDirParsed = _T(".*"); // Match everything else strDirParsed = strutils::join(dirPatterns.begin(), dirPatterns.end(), _T("|")); String strFileParsedExclude = strutils::join(filePatternsExclude.begin(), filePatternsExclude.end(), _T("|")); String strDirParsedExclude = strutils::join(dirPatternsExclude.begin(), dirPatternsExclude.end(), _T("|")); - return { strFileParsed, strFileParsedExclude, strDirParsed, strDirParsedExclude }; + return { strFileParsed, strFileParsedExclude, strDirParsed, strDirParsedExclude, pRegexOrExpressionFilter, pRegexOrExpressionFilterExclude }; } /** @@ -403,55 +605,7 @@ std::tuple FileFilterHelper::ParseExtensions(con */ String FileFilterHelper::GetFilterNameOrMask() const { - String sFilter; - - if (!IsUsingMask()) - sFilter = GetFileFilterName(m_sFileFilterPath); - else - sFilter = m_sMask; - - return sFilter; -} - -/** - * @brief Set filter. - * - * Simple-to-use function to select filter. This function determines - * filter type so caller doesn't need to care about it. - * - * @param [in] filter File mask or filter name. - * @return true if given filter was set, false if default filter was set. - * @note If function returns false, you should ask filter set with - * GetFilterNameOrMask(). - */ -bool FileFilterHelper::SetFilter(const String &filter) -{ - // If filter is empty string set default filter - if (filter.empty()) - { - UseMask(true); - SetMask(_T("*.*")); - SetFileFilterPath(_T("")); - return false; - } - - // Remove leading and trailing whitespace characters from the string. - String flt = strutils::trim_ws(filter); - - String path = GetFileFilterPath(flt); - if (!path.empty()) - { - UseMask(false); - SetFileFilterPath(path); - } - else - { - UseMask(true); - SetMask(flt); - SetFileFilterPath(_T("")); - return false; - } - return true; + return m_sMask; } /** @@ -464,9 +618,8 @@ bool FileFilterHelper::SetFilter(const String &filter) void FileFilterHelper::ReloadUpdatedFilters() { DirItem fileInfo; - String selected; - vector filters = GetFileFilters(selected); - vector::const_iterator iter = filters.begin(); + std::vector filters = GetFileFilters(); + std::vector::const_iterator iter = filters.begin(); while (iter != filters.end()) { String path = (*iter).fullpath; @@ -476,14 +629,7 @@ void FileFilterHelper::ReloadUpdatedFilters() fileInfo.size != (*iter).fileinfo.size) { // Reload filter after changing it - int retval = m_fileFilterMgr->ReloadFilterFromDisk(path); - - if (retval == FILTER_OK) - { - // If it was active filter we have to re-set it - if (path == selected) - SetFileFilterPath(path); - } + m_fileFilterMgr->ReloadFilterFromDisk(path); } ++iter; } @@ -542,6 +688,13 @@ void FileFilterHelper::CloneFrom(const FileFilterHelper* pHelper) m_pMaskFileFilter->CloneFrom(pHelper->m_pMaskFileFilter.get()); } + if (pHelper->m_pMaskFileFilterExclude) + { + auto filterList = std::make_unique(FilterList()); + m_pMaskFileFilterExclude = std::move(filterList); + m_pMaskFileFilterExclude->CloneFrom(pHelper->m_pMaskFileFilterExclude.get()); + } + if (pHelper->m_pMaskDirFilter) { auto filterList = std::make_unique(FilterList()); @@ -549,6 +702,25 @@ void FileFilterHelper::CloneFrom(const FileFilterHelper* pHelper) m_pMaskDirFilter->CloneFrom(pHelper->m_pMaskDirFilter.get()); } + if (pHelper->m_pMaskDirFilterExclude) + { + auto filterList = std::make_unique(FilterList()); + m_pMaskDirFilterExclude = std::move(filterList); + m_pMaskDirFilterExclude->CloneFrom(pHelper->m_pMaskDirFilterExclude.get()); + } + + if (pHelper->m_pRegexOrExpressionFilter) + { + m_pRegexOrExpressionFilter.reset(new FileFilter()); + m_pRegexOrExpressionFilter->CloneFrom(pHelper->m_pRegexOrExpressionFilter.get()); + } + + if (pHelper->m_pRegexOrExpressionFilterExclude) + { + m_pRegexOrExpressionFilterExclude.reset(new FileFilter()); + m_pRegexOrExpressionFilterExclude->CloneFrom(pHelper->m_pRegexOrExpressionFilterExclude.get()); + } + if (pHelper->m_fileFilterMgr) { auto fileFilterMgr = std::make_unique(FileFilterMgr()); @@ -556,21 +728,8 @@ void FileFilterHelper::CloneFrom(const FileFilterHelper* pHelper) m_fileFilterMgr->CloneFrom(pHelper->m_fileFilterMgr.get()); } - m_currentFilter = nullptr; - if (pHelper->m_currentFilter && pHelper->m_fileFilterMgr) - { - int count = pHelper->m_fileFilterMgr->GetFilterCount(); - for (int i = 0; i < count; i++) - if (pHelper->m_fileFilterMgr->GetFilterByIndex(i) == pHelper->m_currentFilter) - { - m_currentFilter = m_fileFilterMgr->GetFilterByIndex(i); - break; - } - } - m_sFileFilterPath = pHelper->m_sFileFilterPath; m_sMask = pHelper->m_sMask; - m_bUseMask = pHelper->m_bUseMask; m_sGlobalFilterPath = pHelper->m_sGlobalFilterPath; m_sUserSelFilterPath = pHelper->m_sUserSelFilterPath; } diff --git a/Src/FileFilterHelper.h b/Src/FileFilterHelper.h index a9aaa0037b2..4090fd27c05 100644 --- a/Src/FileFilterHelper.h +++ b/Src/FileFilterHelper.h @@ -14,6 +14,9 @@ class FileFilterMgr; class FilterList; struct FileFilter; +class DIFFITEM; +class CDiffContext; +struct FileFilterErrorInfo; /** * @brief File extension of file filter files. @@ -40,8 +43,12 @@ struct FileFilterInfo class IDiffFilter { public: + virtual void SetDiffContext(const CDiffContext* pCtxt) = 0; + virtual std::vector GetErrorList() const = 0; virtual bool includeFile(const String& szFileName) const = 0; + virtual bool includeFile(const DIFFITEM& di) const = 0; virtual bool includeDir(const String& szDirName) const = 0; + virtual bool includeDir(const DIFFITEM& di) const = 0; bool includeFile(const String& szFileName1, const String& szFileName2) const { if (!szFileName1.empty()) @@ -109,41 +116,47 @@ class FileFilterHelper : public IDiffFilter String GetGlobalFilterPathWithCreate() const; String GetUserFilterPathWithCreate() const; - FileFilterMgr * GetManager() const; - void SetFileFilterPath(const String& szFileFilterPath); - std::vector GetFileFilters(String & selected) const; + FileFilterMgr* GetManager() const; + std::vector GetFileFilters() const; String GetFileFilterName(const String& filterPath) const; String GetFileFilterPath(const String& filterName) const; - void SetUserFilterPath(const String & filterPath); + void SetUserFilterPath(const String& filterPath); + FileFilter* GetRegexOrExpressionFilter() const { return m_pRegexOrExpressionFilter.get(); } + FileFilter* GetRegexOrExpressionFilterExclude() const { return m_pRegexOrExpressionFilterExclude.get(); } void ReloadUpdatedFilters(); void LoadAllFileFilters(); void LoadFileFilterDirPattern(const String& dir, const String& szPattern); - void UseMask(bool bUseMask); void SetMask(const String& strMask); - bool IsUsingMask() const; String GetFilterNameOrMask() const; - bool SetFilter(const String &filter); + void SetDiffContext(const CDiffContext* pCtxt) override; + std::vector GetErrorList() const override; bool includeFile(const String& szFileName) const override; + bool includeFile(const DIFFITEM& di) const override; bool includeDir(const String& szDirName) const override; + bool includeDir(const DIFFITEM& di) const override; void CloneFrom(const FileFilterHelper* pHelper); + protected: - std::tuple ParseExtensions(const String &extensions) const; + std::tuple, std::shared_ptr> + ParseExtensions(const String &extensions) const; private: std::unique_ptr m_pMaskFileFilter; /*< Filter for filemasks (*.cpp) */ + std::unique_ptr m_pMaskFileFilterExclude; /*< Filter for filemasks (*.cpp) */ std::unique_ptr m_pMaskDirFilter; /*< Filter for dirmasks */ - FileFilter * m_currentFilter; /*< Currently selected filefilter */ + std::unique_ptr m_pMaskDirFilterExclude; /*< Filter for dirmasks */ + std::shared_ptr m_pRegexOrExpressionFilter; + std::shared_ptr m_pRegexOrExpressionFilterExclude; std::unique_ptr m_fileFilterMgr; /*< Associated FileFilterMgr */ - String m_sFileFilterPath; /*< Path to current filter */ + std::vector m_sFileFilterPath; /*< Path to current filter */ String m_sMask; /*< File mask (if defined) "*.cpp *.h" etc */ - bool m_bUseMask; /*< If `true` file mask is used, filter otherwise */ String m_sGlobalFilterPath; /*< Path for shared filters */ String m_sUserSelFilterPath; /*< Path for user's private filters */ }; @@ -155,12 +168,3 @@ inline FileFilterMgr * FileFilterHelper::GetManager() const { return m_fileFilterMgr.get(); } - -/** - * @brief Returns true if active filter is a mask. - */ -inline bool FileFilterHelper::IsUsingMask() const -{ - return m_bUseMask; -} - diff --git a/Src/FileFilterMgr.cpp b/Src/FileFilterMgr.cpp index 959e1cc2a0d..c90835484e1 100644 --- a/Src/FileFilterMgr.cpp +++ b/Src/FileFilterMgr.cpp @@ -7,11 +7,13 @@ #include "pch.h" #include "FileFilterMgr.h" +#include "FilterEngine/FilterExpression.h" #include #include #include +#include #include "DirTravel.h" -#include "DirItem.h" +#include "DiffItem.h" #include "UnicodeString.h" #include "FileFilter.h" #include "UniFile.h" @@ -21,8 +23,6 @@ using std::vector; using Poco::Glob; using Poco::RegularExpression; -static void AddFilterPattern(vector *filterList, String & str, bool fileFilter); - /** * @brief Destructor, frees all filters. */ @@ -111,13 +111,7 @@ void FileFilterMgr::DeleteAllFilters() m_filters.clear(); } -/** - * @brief Add a single pattern (if nonempty & valid) to a pattern list. - * - * @param [in] filterList List where pattern is added. - * @param [in] str Temporary variable (ie, it may be altered) - */ -static void AddFilterPattern(vector *filterList, String & str, bool fileFilter) +static bool RemoveComment(String& str) { const String& commentLeader = _T("##"); // Starts comment str = strutils::trim_ws_begin(str); @@ -125,7 +119,7 @@ static void AddFilterPattern(vector *filterList, String & // Ignore lines beginning with '##' size_t pos = str.find(commentLeader); if (pos == 0) - return; + return true; // Find possible comment-separator '##' while (pos != std::string::npos && !(str[pos - 1] == ' ' || str[pos - 1] == '\t')) @@ -135,20 +129,37 @@ static void AddFilterPattern(vector *filterList, String & if (pos != std::string::npos) str = str.substr(0, pos); str = strutils::trim_ws_end(str); - if (str.empty()) + return (str.empty()); +} + +/** + * @brief Add a single pattern (if nonempty & valid) to a pattern list. + * + * @param [in] pfilter Pointer to file filter, used for error reporting. + * @param [in] filterList List where pattern is added. + * @param [in] str Temporary variable (ie, it may be altered) + * @param [in] lineNumber Line number in filter file, used for error reporting. + */ +static void AddFilterPattern(FileFilter* pfilter, vector *filterList, String & str, bool fileFilter, int lineNumber) +{ + if (RemoveComment(str)) return; + pfilter->AddFilterPattern(filterList, str, fileFilter, lineNumber); +} - int re_opts = RegularExpression::RE_CASELESS; - std::string regexString = ucr::toUTF8(str); - re_opts |= RegularExpression::RE_UTF8; - try - { - filterList->push_back(FileFilterElementPtr(new FileFilterElement(regexString, re_opts, fileFilter))); - } - catch (...) - { - // TODO: - } +/** + * @brief Add a single expression (if nonempty & valid) to a expression list. + * + * @param [in] pfilter Pointer to file filter, used for error reporting. + * @param [in] filterList List where expression is added. + * @param [in] str Temporary variable (ie, it may be altered) + * @param [in] lineNumber Line number in filter file, used for error reporting. +*/ +static void AddFilterExpression(FileFilter* pfilter, vector* filterList, String& str, int lineNumber) +{ + if (RemoveComment(str)) + return; + pfilter->AddFilterExpression(filterList, str, lineNumber); } /** @@ -181,6 +192,7 @@ FileFilter * FileFilterMgr::LoadFilterFile(const String& szFilepath, int & error String sLine; bool lossy = false; bool bLinesLeft = true; + int lineNumber = 0; do { // Returns false when last line is read @@ -219,26 +231,51 @@ FileFilter * FileFilterMgr::LoadFilterFile(const String& szFilepath, int & error { // file filter String str = sLine.substr(2); - AddFilterPattern(&pfilter->filefilters, str, true); + AddFilterPattern(pfilter, &pfilter->filefilters, str, true, lineNumber); } else if (0 == sLine.compare(0, 2, _T("d:"), 2)) { // directory filter String str = sLine.substr(2); - AddFilterPattern(&pfilter->dirfilters, str, false); + AddFilterPattern(pfilter, &pfilter->dirfilters, str, false, lineNumber); + } + else if (0 == sLine.compare(0, 3, _T("fe:"), 3)) + { + // file expression filter + String str = sLine.substr(3); + AddFilterExpression(pfilter, &pfilter->fileExpressionFilters, str, lineNumber); + } + else if (0 == sLine.compare(0, 3, _T("de:"), 3)) + { + // directory expression filter + String str = sLine.substr(3); + AddFilterExpression(pfilter, &pfilter->dirExpressionFilters, str, lineNumber); } else if (0 == sLine.compare(0, 3, _T("f!:"), 3)) { // file filter String str = sLine.substr(3); - AddFilterPattern(&pfilter->filefiltersExclude, str, true); + AddFilterPattern(pfilter, &pfilter->filefiltersExclude, str, true, lineNumber); } else if (0 == sLine.compare(0, 3, _T("d!:"), 3)) { // directory filter String str = sLine.substr(3); - AddFilterPattern(&pfilter->dirfiltersExclude, str, false); + AddFilterPattern(pfilter, &pfilter->dirfiltersExclude, str, false, lineNumber); + } + else if (0 == sLine.compare(0, 4, _T("fe!:"), 4)) + { + // file expression filter + String str = sLine.substr(4); + AddFilterExpression(pfilter, &pfilter->fileExpressionFiltersExclude, str, lineNumber); } + else if (0 == sLine.compare(0, 4, _T("de!:"), 4)) + { + // directory expression filter + String str = sLine.substr(4); + AddFilterExpression(pfilter, &pfilter->dirExpressionFiltersExclude, str, lineNumber); + } + lineNumber++; } while (bLinesLeft); return pfilter; @@ -277,90 +314,6 @@ FileFilter * FileFilterMgr::GetFilterByIndex(int i) return m_filters[i].get(); } -/** - * @brief Test given string against given regexp list. - * - * @param [in] filterList List of regexps to test against. - * @param [in] szTest String to test against regexps. - * @return true if string passes - * @note Matching stops when first match is found. - */ -bool TestAgainstRegList(const vector *filterList, const String& szTest) -{ - if (filterList->size() == 0) - return false; - - std::string compString, compStringFileName; - ucr::toUTF8(szTest, compString); - vector::const_iterator iter = filterList->begin(); - while (iter != filterList->end()) - { - RegularExpression::Match match; - try - { - if ((*iter)->_fileNameOnly && compStringFileName.empty()) - ucr::toUTF8(paths::FindFileName(szTest), compStringFileName); - if ((*iter)->regexp.match((*iter)->_fileNameOnly ? compStringFileName : compString, 0, match) > 0) - return true; - } - catch (...) - { - // TODO: - } - - ++iter; - } - return false; -} - -/** - * @brief Test given filename against filefilter. - * - * Test filename against active filefilter. If matching rule is found - * we must first determine type of rule that matched. If we return false - * from this function directory scan marks file as skipped. - * - * @param [in] pFilter Pointer to filefilter - * @param [in] szFileName Filename to test - * @return true if file passes the filter - */ -bool FileFilterMgr::TestFileNameAgainstFilter(const FileFilter * pFilter, - const String& szFileName) const -{ - if (pFilter == nullptr) - return true; - if (TestAgainstRegList(&pFilter->filefilters, szFileName)) - { - if (pFilter->filefiltersExclude.empty() || !TestAgainstRegList(&pFilter->filefiltersExclude, szFileName)) - return !pFilter->default_include; - } - return pFilter->default_include; -} - -/** - * @brief Test given directory name against filefilter. - * - * Test directory name against active filefilter. If matching rule is found - * we must first determine type of rule that matched. If we return false - * from this function directory scan marks file as skipped. - * - * @param [in] pFilter Pointer to filefilter - * @param [in] szDirName Directory name to test - * @return true if directory name passes the filter - */ -bool FileFilterMgr::TestDirNameAgainstFilter(const FileFilter * pFilter, - const String& szDirName) const -{ - if (pFilter == nullptr) - return true; - if (TestAgainstRegList(&pFilter->dirfilters, szDirName)) - { - if (pFilter->dirfiltersExclude.empty() || !TestAgainstRegList(&pFilter->dirfiltersExclude, szDirName)) - return !pFilter->default_include; - } - return pFilter->default_include; -} - /** * @brief Reload filter from disk * diff --git a/Src/FileFilterMgr.h b/Src/FileFilterMgr.h index 88d1b358e60..acad068ab20 100644 --- a/Src/FileFilterMgr.h +++ b/Src/FileFilterMgr.h @@ -57,10 +57,6 @@ class FileFilterMgr FileFilter * GetFilterByIndex(int i); String GetFullpath(FileFilter * pfilter) const; - // methods to actually use filter - bool TestFileNameAgainstFilter(const FileFilter * pFilter, const String& szFileName) const; - bool TestDirNameAgainstFilter(const FileFilter * pFilter, const String& szDirName) const; - void DeleteAllFilters(); void CloneFrom(const FileFilterMgr* fileFilterMgr); diff --git a/Src/FileFiltersDlg.cpp b/Src/FileFiltersDlg.cpp index d5b7b6cebf2..4ac52e1ac27 100644 --- a/Src/FileFiltersDlg.cpp +++ b/Src/FileFiltersDlg.cpp @@ -37,7 +37,10 @@ IMPLEMENT_DYNCREATE(FileFiltersDlg, CTrPropertyPage) /** * @brief Constructor. */ -FileFiltersDlg::FileFiltersDlg() : CTrPropertyPage(FileFiltersDlg::IDD) +FileFiltersDlg::FileFiltersDlg() + : CTrPropertyPage(FileFiltersDlg::IDD) + , m_pFileFilterHelper(new FileFilterHelper()) + , m_pFileFilterHelperOrg(nullptr) { m_strCaption = theApp.LoadDialogCaption(m_lpszTemplateName).c_str(); m_psp.pszTitle = m_strCaption; @@ -50,13 +53,16 @@ void FileFiltersDlg::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); //{{AFX_DATA_MAP(FileFiltersDlg) + DDX_CBStringExact(pDX, IDC_FILTERFILE_MASK, m_sMask); DDX_Control(pDX, IDC_FILTERFILE_LIST, m_listFilters); + DDX_Control(pDX, IDC_FILTERFILE_MASK, m_ctlMask); //}}AFX_DATA_MAP } BEGIN_MESSAGE_MAP(FileFiltersDlg, CTrPropertyPage) //{{AFX_MSG_MAP(FileFiltersDlg) + ON_CBN_KILLFOCUS(IDC_FILTERFILE_MASK, OnKillFocusFilterfileMask) ON_BN_CLICKED(IDC_FILTERFILE_EDITBTN, OnFiltersEditbtn) ON_NOTIFY(NM_DBLCLK, IDC_FILTERFILE_LIST, OnDblclkFiltersList) ON_WM_MOUSEMOVE() @@ -64,42 +70,20 @@ BEGIN_MESSAGE_MAP(FileFiltersDlg, CTrPropertyPage) ON_BN_CLICKED(IDC_FILTERFILE_NEWBTN, OnBnClickedFilterfileNewbutton) ON_BN_CLICKED(IDC_FILTERFILE_DELETEBTN, OnBnClickedFilterfileDelete) ON_COMMAND(ID_HELP, OnHelp) - //}}AFX_MSG_MAP ON_NOTIFY(LVN_ITEMCHANGED, IDC_FILTERFILE_LIST, OnLvnItemchangedFilterfileList) ON_NOTIFY(LVN_GETINFOTIP, IDC_FILTERFILE_LIST, OnInfoTip) ON_BN_CLICKED(IDC_FILTERFILE_INSTALL, OnBnClickedFilterfileInstall) + //}}AFX_MSG_MAP END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // CFiltersDlg message handlers -/** - * @brief Set array of filters. - * @param [in] fileFilters Array of filters to show in the dialog. - * @note Call this before actually showing the dialog. - */ -void FileFiltersDlg::SetFilterArray(const vector& fileFilters) -{ - m_Filters = fileFilters; -} - -/** - * @brief Returns path (cont. filename) of selected filter - * @return Full path to selected filter file. - */ -String FileFiltersDlg::GetSelected() +void FileFiltersDlg::SetFileFilterHelper(FileFilterHelper* pFileFilterHelper) { - return m_sFileFilterPath; -} - -/** - * @brief Set path of selected filter. - * @param [in] Path for selected filter. - * @note Call this before actually showing the dialog. - */ -void FileFiltersDlg::SetSelected(const String & selected) -{ - m_sFileFilterPath = selected; + m_pFileFilterHelper->CloneFrom(pFileFilterHelper); + m_pFileFilterHelperOrg = pFileFilterHelper; + m_Filters = m_pFileFilterHelper->GetFileFilters(); } /** @@ -109,7 +93,7 @@ void FileFiltersDlg::InitList() { // Show selection across entire row. // Also enable infotips. - m_listFilters.SetExtendedStyle(LVS_EX_FULLROWSELECT | LVS_EX_INFOTIP); + m_listFilters.SetExtendedStyle(LVS_EX_CHECKBOXES | LVS_EX_FULLROWSELECT | LVS_EX_INFOTIP); const int lpx = CClientDC(this).GetDeviceCaps(LOGPIXELSX); auto pointToPixel = [lpx](int point) { return MulDiv(point, lpx, 72); }; @@ -121,17 +105,9 @@ void FileFiltersDlg::InitList() title = _("Location"); m_listFilters.InsertColumn(2, title.c_str(), LVCFMT_LEFT, pointToPixel(262)); - title = _(""); - m_listFilters.InsertItem(1, title.c_str()); - m_listFilters.SetItemText(0, 1, title.c_str()); - m_listFilters.SetItemText(0, 2, title.c_str()); - - const int count = (int) m_Filters.size(); - + const int count = (int) m_pFileFilterHelper->GetFileFilters().size(); for (int i = 0; i < count; i++) - { AddToGrid(i); - } } /** @@ -140,7 +116,7 @@ void FileFiltersDlg::InitList() */ void FileFiltersDlg::SelectFilterByIndex(int index) { - m_listFilters.SetItemState(index, LVIS_SELECTED, LVIS_SELECTED); + m_listFilters.SetCheck(index, TRUE); bool bPartialOk = false; m_listFilters.EnsureVisible(index, bPartialOk); } @@ -161,6 +137,36 @@ void FileFiltersDlg::SelectFilterByFilePath(const String& path) } } +/** + * @brief Remove preset filters from filter expression and return the rest. + */ +static String RemovePresetFilters(const String& filterExpression, std::vector& presetFilters) +{ + auto parts = strutils::split(filterExpression, ';'); + std::vector result; + for (const auto& part : parts) + { + const String partTrimmed = strutils::trim_ws(String(part.data(), part.length())); + if (partTrimmed.substr(0, 3) == _T("fp:")) + presetFilters.push_back(partTrimmed.substr(3)); + else + result.push_back(partTrimmed); + } + return strutils::join(result.begin(), result.end(), _T(";")); +} + +static void SetCheckedState(CListCtrl& list, std::vector& presetFilters) +{ + const int count = list.GetItemCount(); + for (int i = 0; i < count; i++) + { + String desc = list.GetItemText(i, 0); + const bool isChecked = std::find(presetFilters.begin(), presetFilters.end(), desc) != presetFilters.end(); + if (isChecked) + list.SetCheck(i, true); + } +} + /** * @brief Called before dialog is shown. * @return Always TRUE. @@ -169,23 +175,16 @@ BOOL FileFiltersDlg::OnInitDialog() { CTrPropertyPage::OnInitDialog(); + m_ctlMask.SetFileControlStates(true); + m_ctlMask.LoadState(_T("Files\\Ext")); + InitList(); - if (m_sFileFilterPath.empty()) - { - SelectFilterByIndex(0); - return TRUE; - } + std::vector presetFilters; + String filterExpression = RemovePresetFilters(m_pFileFilterHelper->GetFilterNameOrMask(), presetFilters); + SetDlgItemText(IDC_FILTERFILE_MASK, filterExpression.c_str()); - int count = m_listFilters.GetItemCount(); - for (int i = 0; i < count; i++) - { - String desc = m_listFilters.GetItemText(i, 2); - if (strutils::compare_nocase(desc, m_sFileFilterPath) == 0) - { - SelectFilterByIndex(i); - } - } + SetCheckedState(m_listFilters, presetFilters); SetButtonState(); @@ -200,7 +199,7 @@ BOOL FileFiltersDlg::OnInitDialog() void FileFiltersDlg::AddToGrid(int filterIndex) { const FileFilterInfo & filterinfo = m_Filters.at(filterIndex); - const int item = filterIndex + 1; + const int item = filterIndex; m_listFilters.InsertItem(item, filterinfo.name.c_str()); m_listFilters.SetItemText(item, 1, filterinfo.description.c_str()); @@ -212,14 +211,43 @@ void FileFiltersDlg::AddToGrid(int filterIndex) */ void FileFiltersDlg::OnOK() { - int sel = m_listFilters.GetNextItem(-1, LVNI_SELECTED); - m_sFileFilterPath = m_listFilters.GetItemText(sel, 2); + String mask = m_sMask; + const int count = m_listFilters.GetItemCount(); + for (int i = 0; i < count; i++) + { + const bool checked = m_listFilters.GetCheck(i); + if (checked) + { + if (!mask.empty()) + mask += _T(";"); + mask += _T("fp:") + m_listFilters.GetItemText(i, 0); + } + } + + m_pFileFilterHelper->SetMask(mask); + m_pFileFilterHelperOrg->CloneFrom(m_pFileFilterHelper.get()); AfxGetApp()->WriteProfileInt(_T("Settings"), _T("FilterStartPage"), GetParentSheet()->GetActiveIndex()); + m_ctlMask.SetWindowTextW(mask.c_str()); + m_ctlMask.SaveState(_T("Files\\Ext")); + CDialog::OnOK(); } +void FileFiltersDlg::OnKillFocusFilterfileMask() +{ + String filterExpressionOld; + GetDlgItemText(IDC_FILTERFILE_MASK, filterExpressionOld); + std::vector presetFilters; + String filterExpression = RemovePresetFilters(filterExpressionOld, presetFilters); + if (filterExpression != filterExpressionOld) + { + SetDlgItemText(IDC_FILTERFILE_MASK, filterExpression.c_str()); + SetCheckedState(m_listFilters, presetFilters); + } +} + /** * @brief Open selected filter for editing. * @@ -234,16 +262,10 @@ void FileFiltersDlg::OnOK() */ void FileFiltersDlg::OnFiltersEditbtn() { - int sel =- 1; - + int sel = -1; sel = m_listFilters.GetNextItem(sel, LVNI_SELECTED); - - // Can't edit first "None" - if (sel > 0) - { - String path = m_listFilters.GetItemText(sel, 2); - EditFileFilter(path); - } + String path = m_listFilters.GetItemText(sel, 2); + EditFileFilter(path); } /** @@ -268,23 +290,10 @@ void FileFiltersDlg::OnDblclkFiltersList(NMHDR* pNMHDR, LRESULT* pResult) *pResult = 0; } -/** - * @brief Is item in list the item? - * @param [in] item Item to test. - * @return true if item is item. - */ -bool FileFiltersDlg::IsFilterItemNone(int item) const -{ - String txtNone = _(""); - String txt = m_listFilters.GetItemText(item, 0); - - return (strutils::compare_nocase(txt, txtNone) == 0); -} - /** * @brief Called when item state is changed. * - * Disable the "Test", "Edit" and "Remove" buttons when no item is selected or "None" filter is selected. + * Disable the "Test", "Edit" and "Remove" buttons when no item is selected. * @param [in] pNMHDR Listview item data. * @param [out] pResult Result of the action is returned in here. */ @@ -351,24 +360,10 @@ void FileFiltersDlg::OnBnClickedFilterfileTestButton() { UpdateData(TRUE); - int sel = m_listFilters.GetNextItem(-1, LVNI_SELECTED); - if (sel == -1) - return; - if (IsFilterItemNone(sel)) - return; - - m_sFileFilterPath = m_listFilters.GetItemText(sel, 2); - // Ensure filter is up-to-date (user probably just edited it) - auto* pGlobalFileFilter = theApp.GetGlobalFileFilter(); - pGlobalFileFilter->ReloadUpdatedFilters(); + m_pFileFilterHelper->ReloadUpdatedFilters(); - FileFilterMgr *pMgr = pGlobalFileFilter->GetManager(); - FileFilter * pFileFilter = pMgr->GetFilterByPath(m_sFileFilterPath); - if (pFileFilter == nullptr) - return; - - CTestFilterDlg dlg(this, pFileFilter, pMgr); + CTestFilterDlg dlg(this, m_pFileFilterHelper.get()); dlg.DoModal(); } @@ -470,9 +465,8 @@ void FileFiltersDlg::OnBnClickedFilterfileNewbutton() if (retval == FILTER_OK) { // Remove all from filterslist and re-add so we can update UI - String selected; pGlobalFileFilter->LoadAllFileFilters(); - m_Filters = pGlobalFileFilter->GetFileFilters(selected); + m_Filters = pGlobalFileFilter->GetFileFilters(); UpdateFiltersList(); SelectFilterByFilePath(s); @@ -485,39 +479,31 @@ void FileFiltersDlg::OnBnClickedFilterfileNewbutton() */ void FileFiltersDlg::OnBnClickedFilterfileDelete() { - String path; - int sel =- 1; - + int sel = -1; sel = m_listFilters.GetNextItem(sel, LVNI_SELECTED); + const String path = m_listFilters.GetItemText(sel, 2); - // Can't delete first "None" - if (sel > 0) + String sConfirm = strutils::format_string1(_("Are you sure you want to delete\n\n%1 ?"), path); + int res = AfxMessageBox(sConfirm.c_str(), MB_ICONWARNING | MB_YESNO); + if (res == IDYES) { - path = m_listFilters.GetItemText(sel, 2); + if (DeleteFile(path.c_str())) + { + auto* pGlobalFileFilter = theApp.GetGlobalFileFilter(); + FileFilterMgr *pMgr = pGlobalFileFilter->GetManager(); + pMgr->RemoveFilter(path); + + // Remove all from filterslist and re-add so we can update UI + m_Filters = pGlobalFileFilter->GetFileFilters(); - String sConfirm = strutils::format_string1(_("Are you sure you want to delete\n\n%1 ?"), path); - int res = AfxMessageBox(sConfirm.c_str(), MB_ICONWARNING | MB_YESNO); - if (res == IDYES) + UpdateFiltersList(); + } + else { - if (DeleteFile(path.c_str())) - { - auto* pGlobalFileFilter = theApp.GetGlobalFileFilter(); - FileFilterMgr *pMgr = pGlobalFileFilter->GetManager(); - pMgr->RemoveFilter(path); - - // Remove all from filterslist and re-add so we can update UI - String selected; - m_Filters = pGlobalFileFilter->GetFileFilters(selected); - - UpdateFiltersList(); - } - else - { - String msg = strutils::format_string1( - _("Failed to delete filter:\n%1\n\nFile may be read-only."), - path); - AfxMessageBox(msg.c_str(), MB_ICONSTOP); - } + String msg = strutils::format_string1( + _("Failed to delete filter:\n%1\n\nFile may be read-only."), + path); + AfxMessageBox(msg.c_str(), MB_ICONSTOP); } } SetButtonState(); @@ -528,15 +514,9 @@ void FileFiltersDlg::OnBnClickedFilterfileDelete() */ void FileFiltersDlg::UpdateFiltersList() { - int count = (int) m_Filters.size(); - m_listFilters.DeleteAllItems(); - String title = _(""); - m_listFilters.InsertItem(1, title.c_str()); - m_listFilters.SetItemText(0, 1, title.c_str()); - m_listFilters.SetItemText(0, 2, title.c_str()); - + const int count = (int) m_Filters.size(); for (int i = 0; i < count; i++) { AddToGrid(i); @@ -597,8 +577,7 @@ void FileFiltersDlg::OnBnClickedFilterfileInstall() pMgr->AddFilter(userPath); // Remove all from filterslist and re-add so we can update UI - String selected; - m_Filters = pGlobalFileFilter->GetFileFilters(selected); + m_Filters = pGlobalFileFilter->GetFileFilters(); UpdateFiltersList(); SelectFilterByFilePath(userPath); @@ -607,20 +586,13 @@ void FileFiltersDlg::OnBnClickedFilterfileInstall() } /** - * @brief Disable the "Test", "Edit" and "Remove" buttons when no item is selected or "None" filter is selected. + * @brief Disable the "Test", "Edit" and "Remove" buttons when no item is selected. */ void FileFiltersDlg::SetButtonState() { - bool isNone = true; - int sel = -1; sel = m_listFilters.GetNextItem(sel, LVNI_SELECTED); - if (sel != -1) - { - String txtNone = _(""); - String txt = m_listFilters.GetItemText(sel, 0); - isNone = strutils::compare_nocase(txt, txtNone) == 0; - } + const bool isNone = (sel == -1); EnableDlgItem(IDC_FILTERFILE_TEST_BTN, !isNone); EnableDlgItem(IDC_FILTERFILE_EDITBTN, !isNone); diff --git a/Src/FileFiltersDlg.h b/Src/FileFiltersDlg.h index 47393c0b3b9..61010f718c8 100644 --- a/Src/FileFiltersDlg.h +++ b/Src/FileFiltersDlg.h @@ -8,8 +8,8 @@ #include "TrDialogs.h" #include - -struct FileFilterInfo; +#include "FileFilterHelper.h" +#include "SuperComboBox.h" /** * @brief Class for dialog allowing user to select @@ -22,15 +22,16 @@ class FileFiltersDlg : public CTrPropertyPage // Construction public: FileFiltersDlg(); // standard constructor - void SetFilterArray(const std::vector& fileFilters); - String GetSelected(); - void SetSelected(const String & selected); + void SetFileFilterHelper(FileFilterHelper* pFileFilterHelper); // Implementation data private: - String m_sFileFilterPath; CPoint m_ptLastMousePos; + String m_sMask; + std::unique_ptr m_pFileFilterHelper; + FileFilterHelper* m_pFileFilterHelperOrg; std::vector m_Filters; + CSuperComboBox m_ctlMask; // Dialog Data //{{AFX_DATA(FileFiltersDlg) @@ -44,7 +45,6 @@ class FileFiltersDlg : public CTrPropertyPage void SelectFilterByIndex(int index); void SelectFilterByFilePath(const String& path); void AddToGrid(int filterIndex); - bool IsFilterItemNone(int item) const; void UpdateFiltersList(); void EditFileFilter(const String& path); @@ -59,6 +59,7 @@ class FileFiltersDlg : public CTrPropertyPage //{{AFX_MSG(FileFiltersDlg) virtual BOOL OnInitDialog() override; virtual void OnOK(); + afx_msg void OnKillFocusFilterfileMask(); afx_msg void OnFiltersEditbtn(); afx_msg void OnDblclkFiltersList(NMHDR* pNMHDR, LRESULT* pResult); afx_msg void OnLvnItemchangedFilterfileList(NMHDR *pNMHDR, LRESULT *pResult); diff --git a/Src/FileVersion.cpp b/Src/FileVersion.cpp index 5e49f73edbb..e49609bedce 100644 --- a/Src/FileVersion.cpp +++ b/Src/FileVersion.cpp @@ -17,9 +17,20 @@ * @brief Default constructor. */ FileVersion::FileVersion() -: m_fileVersionMS(0xffffffff) -, m_fileVersionLS(0xffffffff) { + Clear(); +} + +FileVersion::FileVersion(const FileVersion& other) +{ + m_fileVersion.store(other.m_fileVersion.load(std::memory_order_relaxed), std::memory_order_relaxed); +} + +FileVersion& FileVersion::operator=(const FileVersion& other) +{ + if (this != &other) + m_fileVersion.store(other.m_fileVersion.load(std::memory_order_relaxed), std::memory_order_relaxed); + return *this; } /** @@ -29,11 +40,14 @@ FileVersion::FileVersion() */ String FileVersion::GetFileVersionString() const { - if (m_fileVersionMS == 0xffffffff && m_fileVersionLS >= 0xfffffffe) + auto version = m_fileVersion.load(std::memory_order_relaxed); + if (version >= 0xFFFFFFFFFFFFFFFEULL) return _T(""); - return strutils::format(_T("%u.%u.%u.%u"), HIWORD(m_fileVersionMS), - LOWORD(m_fileVersionMS), HIWORD(m_fileVersionLS), - LOWORD(m_fileVersionLS)); + unsigned int fileVersionMS = static_cast(version >> 32); + unsigned int fileVersionLS = static_cast(version & 0xFFFFFFFFULL); + return strutils::format(_T("%u.%u.%u.%u"), HIWORD(fileVersionMS), + LOWORD(fileVersionMS), HIWORD(fileVersionLS), + LOWORD(fileVersionLS)); } diff --git a/Src/FileVersion.h b/Src/FileVersion.h index 8c08df0644c..ac5ef136414 100644 --- a/Src/FileVersion.h +++ b/Src/FileVersion.h @@ -5,6 +5,7 @@ */ #pragma once +#include #include "UnicodeString.h" /** @@ -15,17 +16,18 @@ class FileVersion { private: - unsigned m_fileVersionMS; //*< File version most significant dword. */ - unsigned m_fileVersionLS; //*< File version least significant dword. */ + std::atomic_uint64_t m_fileVersion; public: FileVersion(); + FileVersion(const FileVersion& other); + FileVersion& operator=(const FileVersion& other); void Clear(); - bool IsCleared() const { return m_fileVersionMS == 0xffffffff && m_fileVersionLS == 0xffffffff; }; + bool IsCleared() const; void SetFileVersion(unsigned versionMS, unsigned versionLS); - void SetFileVersionNone() { m_fileVersionMS = 0xffffffff; m_fileVersionLS = 0xfffffffe; }; + void SetFileVersionNone(); String GetFileVersionString() const; - uint64_t GetFileVersionQWORD() const { return (static_cast(m_fileVersionMS) << 32) + m_fileVersionLS; }; + uint64_t GetFileVersionQWORD() const; }; /** @@ -33,7 +35,23 @@ class FileVersion */ inline void FileVersion::Clear() { - m_fileVersionMS = m_fileVersionLS = 0xffffffff; + m_fileVersion.store(0xFFFFFFFFFFFFFFFFULL, std::memory_order_relaxed); +} + +inline bool FileVersion::IsCleared() const +{ + return m_fileVersion.load(std::memory_order_relaxed) == 0xFFFFFFFFFFFFFFFFULL; +} + +inline void FileVersion::SetFileVersion(unsigned versionMS, unsigned versionLS) +{ + uint64_t combined = (static_cast(versionMS) << 32) | versionLS; + m_fileVersion.store(combined, std::memory_order_relaxed); +} + +inline void FileVersion::SetFileVersionNone() +{ + m_fileVersion.store(0xFFFFFFFFFFFFFFFEULL, std::memory_order_relaxed); } /** @@ -41,9 +59,7 @@ inline void FileVersion::Clear() * @param [in] versionMS Most significant dword for version. * @param [in] versionLS Least significant dword for version. */ -inline void FileVersion::SetFileVersion(unsigned versionMS, unsigned versionLS) +inline uint64_t FileVersion::GetFileVersionQWORD() const { - m_fileVersionMS = versionMS; - m_fileVersionLS = versionLS; + return m_fileVersion.load(std::memory_order_relaxed); } - diff --git a/Src/FilterEngine/FileContentRef.cpp b/Src/FilterEngine/FileContentRef.cpp new file mode 100644 index 00000000000..292c9d92905 --- /dev/null +++ b/Src/FilterEngine/FileContentRef.cpp @@ -0,0 +1,190 @@ +#include "pch.h" +#include "FileContentRef.h" +#include "UnicodeString.h" +#include "OptionsMgr.h" +#include "OptionsDef.h" +#include "UniFile.h" +#include "codepage_detect.h" +#include "paths.h" +#include "MergeApp.h" +#include +#include +#include +#include +#include + +static void GuessEncoding(UniMemFile& file, const String& path) +{ + file.ReadBom(); + if (!file.HasBom()) + { + int iGuessEncodingType = GetOptionsMgr()->GetInt(OPT_CP_DETECT); + int64_t fileSize = file.GetFileSize(); + FileTextEncoding encoding = codepage_detect::Guess( + paths::FindExtension(path), file.GetBase(), static_cast( + fileSize < static_cast(codepage_detect::BufSize) ? + fileSize : static_cast(codepage_detect::BufSize)), + iGuessEncodingType); + file.SetCodepage(encoding.m_codepage); + } +} + +bool FileContentRef::operator==(const FileContentRef& other) const +{ + try { + Poco::FileInputStream fs1(ucr::toUTF8(path), std::ios::binary); + Poco::FileInputStream fs2(ucr::toUTF8(other.path), std::ios::binary); + + if (!fs1.good() || !fs2.good()) return false; + + const size_t bufferSize = 4096; + char buffer1[bufferSize]; + char buffer2[bufferSize]; + + while (true) { + fs1.read(buffer1, bufferSize); + fs2.read(buffer2, bufferSize); + + std::streamsize count1 = fs1.gcount(); + std::streamsize count2 = fs2.gcount(); + + if (count1 != count2) return false; + if (count1 == 0) return true; // end of both + + if (std::memcmp(buffer1, buffer2, static_cast(count1)) != 0) + return false; + } + } + catch (const Poco::Exception&) + { + return false; + } +} + +bool FileContentRef::Contains(const std::string& str) const +{ + UniMemFile file; + if (!file.OpenReadOnly(path)) + return false; + GuessEncoding(file, path); + String searchStr = ucr::toTString(str); + strutils::makelower(searchStr); + std::boyer_moore_horspool_searcher searcher(searchStr.begin(), searchStr.end()); + bool linesToRead = true; + bool found = false; + do + { + bool lossy; + String line, eol; + linesToRead = file.ReadString(line, eol, &lossy); + strutils::makelower(line); + using iterator = String::const_iterator; + std::pair result = searcher(line.begin(), line.end()); + if (result.first != result.second) + { + found = true; + break; + } + } while (linesToRead); + file.Close(); + return found; +} + +bool FileContentRef::REContains(const Poco::RegularExpression& regexp) const +{ + UniMemFile file; + if (!file.OpenReadOnly(path)) + return false; + GuessEncoding(file, path); + bool linesToRead = true; + bool found = false; + try + { + do + { + bool lossy; + String line, eol; + linesToRead = file.ReadString(line, eol, &lossy); + Poco::RegularExpression::Match match; + if (regexp.match(ucr::toUTF8(line), match) > 0) + { + found = true; + break; + } + } while (linesToRead); + } + catch (const Poco::RegularExpressionException&) + { + } + file.Close(); + return found; +} + +std::string FileContentRef::Sublines(int start, int len) const +{ + UniMemFile file; + if (!file.OpenReadOnly(path)) + return ""; + GuessEncoding(file, path); + bool linesToRead = true; + std::vector lines; + if (start >= 0 && len >= 0) + { + size_t count = 0; + do + { + bool lossy; + String line, eol; + linesToRead = file.ReadString(line, eol, &lossy); + if (count >= start && count < start + len && (!line.empty() || !eol.empty())) + lines.push_back(line + eol); + if (lines.size() >= static_cast(len)) + break; + ++count; + } while (linesToRead); + file.Close(); + return ucr::toUTF8(strutils::join(lines.begin(), lines.end(), _T(""))); + } + do + { + bool lossy; + String line, eol; + linesToRead = file.ReadString(line, eol, &lossy); + if (!line.empty() || !eol.empty()) + lines.push_back(line + eol); + } while (linesToRead); + if (start < 0) + { + start = static_cast(lines.size()) + start; + if (start < 0) + start = 0; + } + if (start >= static_cast(lines.size())) + return ""; + if (len < 0) + len = static_cast(lines.size()) - start; + size_t end = std::min(start + len, lines.size()); + file.Close(); + return ucr::toUTF8(strutils::join(lines.begin() + start, lines.begin() + end, _T(""))); +} + +size_t FileContentRef::LineCount() const +{ + UniMemFile file; + if (!file.OpenReadOnly(path)) + return static_cast(-1); + GuessEncoding(file, path); + bool linesToRead = true; + size_t count = 0; + do + { + bool lossy; + String line, eol; + linesToRead = file.ReadString(line, eol, &lossy); + if (!line.empty() || !eol.empty()) + ++count; + } while (linesToRead); + file.Close(); + return count; +} + diff --git a/Src/FilterEngine/FileContentRef.h b/Src/FilterEngine/FileContentRef.h new file mode 100644 index 00000000000..3afe7de3b98 --- /dev/null +++ b/Src/FilterEngine/FileContentRef.h @@ -0,0 +1,17 @@ +#pragma once + +#include "DiffFileInfo.h" +#include + +namespace Poco { class RegularExpression; } + +struct FileContentRef +{ + String path; + DiffFileInfo item; + bool operator==(const FileContentRef& other) const; + bool Contains(const std::string& str) const; + bool REContains(const Poco::RegularExpression& regexp) const; + std::string Sublines(int start, int len) const; + size_t LineCount() const; +}; diff --git a/Src/FilterEngine/FilterEngine.vcxitems b/Src/FilterEngine/FilterEngine.vcxitems new file mode 100644 index 00000000000..22459c36cf8 --- /dev/null +++ b/Src/FilterEngine/FilterEngine.vcxitems @@ -0,0 +1,66 @@ + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + true + {9c37e5d8-1dc0-4eac-aadb-5fc8be4fb1bc} + + + + %(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory) + + + + + + + + pch.h + $(IntDir)$(TargetName)2.pch + + + pch.h + $(IntDir)$(TargetName)2.pch + + + pch.h + $(IntDir)$(TargetName)2.pch + + + pch.h + $(IntDir)$(TargetName)2.pch + + + true + + + pch.h + $(IntDir)$(TargetName)2.pch + + + + + + + Document + $(SolutionDir)\Build\msys2\usr\bin\re2c.exe %(FullPath) -o %(RelativeDir)\FilterLexer.cpp + %(RelativeDir)\FilterLexer.cpp + + + + + false + Document + $(SolutionDir)\Build\msys2\usr\bin\lemon %(FullPath) -T$(SolutionDir)\Build\msys2\usr\share\lemon\lempar.c + %(RelativeDir)\FilterParser.c + + + + + + + + + + + \ No newline at end of file diff --git a/Src/FilterEngine/FilterEngine.vcxitems.filters b/Src/FilterEngine/FilterEngine.vcxitems.filters new file mode 100644 index 00000000000..56935f5e02a --- /dev/null +++ b/Src/FilterEngine/FilterEngine.vcxitems.filters @@ -0,0 +1,59 @@ + + + + + {cbb36660-3f8a-46ba-855a-fa8f32b99d72} + + + {7e65cd72-3165-42cc-b722-0dcf1593ffdd} + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + \ No newline at end of file diff --git a/Src/FilterEngine/FilterError.h b/Src/FilterEngine/FilterError.h new file mode 100644 index 00000000000..52cc6498528 --- /dev/null +++ b/Src/FilterEngine/FilterError.h @@ -0,0 +1,19 @@ +/** + * @file FilterError.h + * @brief Defines error codes for the filter engine. + */ +#pragma once + +enum FilterErrorCode +{ + FILTER_ERROR_NO_ERROR = 0, + FILTER_ERROR_UNKNOWN_CHAR = 1, + FILTER_ERROR_UNTERMINATED_STRING = 2, + FILTER_ERROR_SYNTAX_ERROR = 3, + FILTER_ERROR_PARSE_FAILURE = 4, + FILTER_ERROR_INVALID_LITERAL = 5, + FILTER_ERROR_INVALID_REGULAR_EXPRESSION = 6, + FILTER_ERROR_UNDEFINED_IDENTIFIER = 7, + FILTER_ERROR_INVALID_ARGUMENT_COUNT = 8, + FILTER_ERROR_FILTER_NAME_NOT_FOUND = 9, +}; diff --git a/Src/FilterEngine/FilterExpression.cpp b/Src/FilterEngine/FilterExpression.cpp new file mode 100644 index 00000000000..576c88ab105 --- /dev/null +++ b/Src/FilterEngine/FilterExpression.cpp @@ -0,0 +1,130 @@ +/** + * @file FilterExpression.cpp + * + * @brief Filter engine implementation. + */ +#include "pch.h" +#include "FilterExpression.h" +#include "FilterExpressionNodes.h" +#include "FilterLexer.h" +#include "DiffContext.h" +#include "DiffItem.h" +#include + +extern void Parse(void* yyp, int yymajor, YYSTYPE yyminor, FilterExpression* pCtx); +extern void* ParseAlloc(void* (*mallocProc)(size_t)); +extern void ParseFree(void* yyp, void (*freeProc)(void*)); + +void YYSTYPEDestructor(YYSTYPE& yystype) +{ + if (yystype.nodeList) + { + for (auto& node : *yystype.nodeList) + delete node; + delete yystype.nodeList; + } + delete yystype.node; + yystype.node = nullptr; + yystype.nodeList = nullptr; +} + +FilterExpression::FilterExpression() +{ +} + +FilterExpression::FilterExpression(const FilterExpression& other) + : optimize(other.optimize) + , ctxt(other.ctxt) + , now(other.now ? new Poco::Timestamp(*other.now) : nullptr) + , today(other.today ? new Poco::Timestamp(*other.today) : nullptr) + , expression(other.expression) +{ + Parse(expression); +} + +FilterExpression::FilterExpression(const std::string& expression) +{ + Parse(expression); +} + +FilterExpression::~FilterExpression() +{ + Clear(); +} + +void FilterExpression::Clear() +{ + now.reset(); + today.reset(); + rootNode.reset(); + errorCode = FILTER_ERROR_NO_ERROR; + errorPosition = -1; +} + +void FilterExpression::UpdateTimestamp() +{ + now.reset(new Poco::Timestamp()); + Poco::LocalDateTime ldt(*now); + Poco::LocalDateTime midnight(ldt.year(), ldt.month(), ldt.day(), 0, 0, 0); + today.reset(new Poco::Timestamp(midnight.timestamp())); +} + +bool FilterExpression::Parse() +{ + Clear(); + UpdateTimestamp(); + FilterLexer lexer(expression); + void* prs = ParseAlloc(malloc); + int token; + FilterErrorCode firstError = FILTER_ERROR_NO_ERROR; + while ((token = lexer.yylex()) != 0) + { + if (token < 0) + { + firstError = static_cast(-token); + errorPosition = static_cast(lexer.yycursor - expression.c_str()); + break; + } + ::Parse(prs, token, lexer.yylval, this); + if (errorCode != 0) + { + firstError = errorCode; + errorPosition = static_cast(lexer.yycursor - expression.c_str()); + break; + } + lexer.yycursor = lexer.YYCURSOR; + } + ::Parse(prs, 0, lexer.yylval, this); + if (firstError == 0 && errorCode != 0) + { + firstError = errorCode; + errorPosition = static_cast(lexer.yycursor - expression.c_str()); + } + ::ParseFree(prs, free); + if (firstError != 0) + errorCode = firstError; + return (errorCode == 0 && rootNode != nullptr); + +} + +bool FilterExpression::Parse(const std::string& expressionStr) +{ + expression = expressionStr; + return Parse(); +} + +bool FilterExpression::Evaluate(const DIFFITEM& di) +{ + const auto result = rootNode->Evaluate(di); + if (const auto boolVal = std::get_if(&result)) + return *boolVal; + if (const auto arrayVal = std::get_if>>(&result)) + { + const auto& vec = *arrayVal->get(); + return std::any_of(vec.begin(), vec.end(), [](const ValueType2& item) { + const auto boolVal = std::get_if(&item.value); + return boolVal && *boolVal; + }); + } + return false; +} diff --git a/Src/FilterEngine/FilterExpression.h b/Src/FilterEngine/FilterExpression.h new file mode 100644 index 00000000000..139e97622e5 --- /dev/null +++ b/Src/FilterEngine/FilterExpression.h @@ -0,0 +1,39 @@ +/** + * @file FilterExpression.h + * + * @brief Header file for the FilterExpression class, which provides functionality to parse and evaluate filter expressions. + */ +#pragma once + +#include +#include +#include "FilterError.h" + +class CDiffContext; +class DIFFITEM; +struct ExprNode; +struct YYSTYPE; +namespace Poco { class Timestamp; } + +struct FilterExpression +{ + FilterExpression(); + FilterExpression(const FilterExpression& other); + FilterExpression(const std::string& expression); + ~FilterExpression(); + bool Parse(const std::string& expression); + bool Parse(); + void SetDiffContext(const CDiffContext* pCtxt) { ctxt = pCtxt; } + bool Evaluate(const DIFFITEM& di); + void UpdateTimestamp(); + void Clear(); + bool optimize = true; + const CDiffContext* ctxt = nullptr; + std::unique_ptr now; + std::unique_ptr today; + std::unique_ptr rootNode; + std::string expression; + FilterErrorCode errorCode = FILTER_ERROR_NO_ERROR; + int errorPosition = -1; + std::string errorMessage; +}; diff --git a/Src/FilterEngine/FilterExpressionNodes.cpp b/Src/FilterEngine/FilterExpressionNodes.cpp new file mode 100644 index 00000000000..ff3b154ae43 --- /dev/null +++ b/Src/FilterEngine/FilterExpressionNodes.cpp @@ -0,0 +1,1103 @@ +/** + * @file FilterExpressionNodes.cpp + * + * @brief This file implements the filter expression evaluation logic for the FilterExpression. + */ +#include "pch.h" +#include "FilterExpressionNodes.h" +#include "FilterExpression.h" +#include "FileContentRef.h" +#include "DiffContext.h" +#include "DiffItem.h" +#include "paths.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static std::optional evalAsBool(const ValueType& val) +{ + auto boolVal = std::get_if(&val); + if (boolVal) return *boolVal; + + auto ary = std::get_if>>(&val); + if (ary) + { + const auto& vec = *ary->get(); + return std::any_of(vec.begin(), vec.end(), [](const ValueType2& item) { + const auto boolVal = std::get_if(&item.value); + return boolVal && *boolVal; + }); + } + return std::nullopt; +} + +ExprNode* OrNode::Optimize() +{ + if (!left || !right) + return this; + left = left->Optimize(); + right = right->Optimize(); + auto lBool = dynamic_cast(left); + auto rBool = dynamic_cast(right); + if (lBool && rBool) + { + const bool result = lBool->value || rBool->value; + delete this; + return new BoolLiteral(result); + } + return this; +} + +ValueType OrNode::Evaluate(const DIFFITEM& di) const +{ + auto lval = left->Evaluate(di); + auto lbool = evalAsBool(lval); + if (lbool && *lbool) return true; + + auto rval = right->Evaluate(di); + auto rbool = evalAsBool(rval); + if (rbool && *rbool) return true; + + if (lbool || rbool) return false; + return std::monostate{}; +} + +ExprNode* AndNode::Optimize() +{ + if (!left || !right) + return this; + left = left->Optimize(); + right = right->Optimize(); + auto lBool = dynamic_cast(left); + auto rBool = dynamic_cast(right); + if (lBool && rBool) + { + const bool result = lBool->value && rBool->value; + delete this; + return new BoolLiteral(result); + } + return this; +} + +ValueType AndNode::Evaluate(const DIFFITEM& di) const +{ + auto lval = left->Evaluate(di); + auto lbool = evalAsBool(lval); + if (!lbool) return std::monostate{}; + if (!*lbool) return false; + + auto rval = right->Evaluate(di); + auto rbool = evalAsBool(rval); + if (!rbool) return std::monostate{}; + if (!*rbool) return false; + return true; +} + +ExprNode* NotNode::Optimize() +{ + if (!expr) + return this; + expr = expr->Optimize(); + auto boolVal = dynamic_cast(expr); + if (boolVal) + { + const bool result = !boolVal->value; + delete this; + return new BoolLiteral(result); + } + return this; +} + +ValueType NotNode::Evaluate(const DIFFITEM& di) const +{ + auto val = expr->Evaluate(di); + auto boolVal = evalAsBool(val); + if (!boolVal) return std::monostate{}; + return !*boolVal; +} + +static std::optional getConstIntValue(const ExprNode* node) +{ + if (auto intNode = dynamic_cast(node)) + return intNode->value; + if (auto sizeNode = dynamic_cast(node)) + return sizeNode->value; + if (auto durationNode = dynamic_cast(node)) + return durationNode->value; + if (auto versionNode = dynamic_cast(node)) + return versionNode->value; + return std::nullopt; +} + +/** + * @brief Attempts to fold constants in an expression tree. + * + * This function evaluates constant expressions involving two nodes and an operator. + * If both nodes represent constant values, it computes the result and returns a new + * literal node representing the folded constant. Otherwise, it returns nullptr. + * + * @param left The left-hand side expression node. + * @param op The operator to apply (e.g., TK_PLUS, TK_EQ). + * @param right The right-hand side expression node. + * @return A new ExprNode representing the folded constant, or nullptr if folding is not possible. + */ +static ExprNode* TryFoldConstants(ExprNode* left, int op, ExprNode* right) +{ + // Extract constant integer values from the left and right nodes. + auto lInt = getConstIntValue(left); + auto rInt = getConstIntValue(right); + + // If both nodes are constants, attempt to fold them. + if (lInt && rInt) + { + // Handle comparison operators (e.g., ==, !=, <, <=, >, >=). + if (op >= TK_EQ && op <= TK_GE) + { + bool result; + switch (op) + { + case TK_EQ: result = *lInt == *rInt; break; + case TK_NE: result = *lInt != *rInt; break; + case TK_LT: result = *lInt < *rInt; break; + case TK_LE: result = *lInt <= *rInt; break; + case TK_GT: result = *lInt > *rInt; break; + case TK_GE: result = *lInt >= *rInt; break; + default: return nullptr; // Invalid operator. + } + // Return a boolean literal representing the comparison result. + return new BoolLiteral(result); + } + + // Handle arithmetic operators (e.g., +, -, *, /, %). + int64_t result = 0; + switch (op) + { + case TK_PLUS: result = *lInt + *rInt; break; + case TK_MINUS: result = *lInt - *rInt; break; + case TK_STAR: result = *lInt * *rInt; break; + case TK_SLASH: + // Avoid division by zero. + if (*rInt != 0) result = *lInt / *rInt; else return nullptr; + break; + case TK_MOD: + // Avoid modulo by zero. + if (*rInt != 0) result = *lInt % *rInt; else return nullptr; + break; + default: return nullptr; // Invalid operator. + } + // Return an integer literal representing the arithmetic result. + return new IntLiteral(result); + } + auto lStr = dynamic_cast(left); + auto rStr = dynamic_cast(right); + if (lStr && rStr) + { + if (op >= TK_EQ && op <= TK_GE) + { + bool result = false; + switch (op) + { + case TK_EQ: result = Poco::icompare(lStr->value, rStr->value) == 0; break; + case TK_NE: result = Poco::icompare(lStr->value, rStr->value) != 0; break; + case TK_LT: result = Poco::icompare(lStr->value, rStr->value) < 0; break; + case TK_LE: result = Poco::icompare(lStr->value, rStr->value) <= 0; break; + case TK_GT: result = Poco::icompare(lStr->value, rStr->value) > 0; break; + case TK_GE: result = Poco::icompare(lStr->value, rStr->value) >= 0; break; + } + return new BoolLiteral(result); + } + + std::string result; + switch (op) + { + case TK_PLUS: result = lStr->value + rStr->value; break; + default: return nullptr; + } + return new StringLiteral(result); + } + auto lBool = dynamic_cast(left); + auto rBool = dynamic_cast(right); + if (lBool && rBool) + { + if (op >= TK_EQ && op <= TK_GE) + { + bool result = false; + switch (op) + { + case TK_EQ: result = lBool->value == rBool->value; break; + case TK_NE: result = lBool->value != rBool->value; break; + case TK_LT: result = lBool->value < rBool->value; break; + case TK_LE: result = lBool->value <= rBool->value; break; + case TK_GT: result = lBool->value > rBool->value; break; + case TK_GE: result = lBool->value >= rBool->value; break; + default: return nullptr; + } + return new BoolLiteral(result); + } + + int64_t result; + switch (op) + { + case TK_PLUS: result = lBool->value + rBool->value; break; + case TK_MINUS: result = lBool->value - rBool->value; break; + default: return nullptr; + } + return new IntLiteral(result); + } + auto lDateTime = dynamic_cast(left); + auto rDateTime = dynamic_cast(right); + if (lDateTime && rDateTime) + { + if (op >= TK_EQ && op <= TK_GE) + { + bool result = false; + switch (op) + { + case TK_EQ: result = lDateTime->value == rDateTime->value; break; + case TK_NE: result = lDateTime->value != rDateTime->value; break; + case TK_LT: result = lDateTime->value < rDateTime->value; break; + case TK_LE: result = lDateTime->value <= rDateTime->value; break; + case TK_GT: result = lDateTime->value > rDateTime->value; break; + case TK_GE: result = lDateTime->value >= rDateTime->value; break; + default: return nullptr; + } + return new BoolLiteral(result); + } + + Poco::Timestamp result; + switch (op) + { + case TK_MINUS: result = lDateTime->value - rDateTime->value; break; + default: return nullptr; + } + return new DateTimeLiteral(result); + } + if (lDateTime && rInt) + { + if (op == TK_PLUS) return new DateTimeLiteral(lDateTime->value + *rInt); + if (op == TK_MINUS) return new DateTimeLiteral(lDateTime->value - *rInt); + } + return nullptr; +} + +ExprNode* BinaryOpNode::Optimize() +{ + if (!left || !right) + return this; + left = left->Optimize(); + right = right->Optimize(); + if (ExprNode* folded = TryFoldConstants(left, op, right)) + { + delete this; + return folded; + } + if (op == TK_RECONTAINS || op == TK_MATCHES) + { + if (auto strNode = dynamic_cast(right)) + { + right = new RegularExpressionLiteral(strNode->value); + delete strNode; + } + } + return this; +} + +ValueType BinaryOpNode::Evaluate(const DIFFITEM& di) const +{ + auto lval = left->Evaluate(di); + auto rval = right->Evaluate(di); + auto compute = [](int op, const ValueType& lval, const ValueType& rval) -> ValueType + { + if (auto lvalInt = std::get_if(&lval)) + { + if (auto rvalInt = std::get_if(&rval)) + { + if (op == TK_EQ) return *lvalInt == *rvalInt; + if (op == TK_NE) return *lvalInt != *rvalInt; + if (op == TK_LT) return *lvalInt < *rvalInt; + if (op == TK_LE) return *lvalInt <= *rvalInt; + if (op == TK_GT) return *lvalInt > *rvalInt; + if (op == TK_GE) return *lvalInt >= *rvalInt; + if (op == TK_PLUS) return *lvalInt + *rvalInt; + if (op == TK_MINUS) return *lvalInt - *rvalInt; + if (op == TK_STAR) return *lvalInt * *rvalInt; + if (op == TK_SLASH) return *lvalInt / *rvalInt; + if (op == TK_MOD) return *lvalInt % *rvalInt; + } + } + else if (auto lvalTimestamp = std::get_if(&lval)) + { + if (auto rvalTimestamp = std::get_if(&rval)) + { + if (op == TK_EQ) return *lvalTimestamp == *rvalTimestamp; + if (op == TK_NE) return *lvalTimestamp != *rvalTimestamp; + if (op == TK_LT) return *lvalTimestamp < *rvalTimestamp; + if (op == TK_LE) return *lvalTimestamp <= *rvalTimestamp; + if (op == TK_GT) return *lvalTimestamp > *rvalTimestamp; + if (op == TK_GE) return *lvalTimestamp >= *rvalTimestamp; + if (op == TK_MINUS) return *lvalTimestamp - *rvalTimestamp; + } + else if (auto rvalInt = std::get_if(&rval)) + { + if (op == TK_PLUS) return *lvalTimestamp + *rvalInt; + if (op == TK_MINUS) return *lvalTimestamp - *rvalInt; + } + } + else if (auto lvalString = std::get_if(&lval)) + { + if (auto rvalString = std::get_if(&rval)) + { + if (op == TK_EQ) return Poco::icompare(*lvalString, *rvalString) == 0; + if (op == TK_NE) return Poco::icompare(*lvalString, *rvalString) != 0; + if (op == TK_LT) return Poco::icompare(*lvalString, *rvalString) < 0; + if (op == TK_LE) return Poco::icompare(*lvalString, *rvalString) <= 0; + if (op == TK_GT) return Poco::icompare(*lvalString, *rvalString) > 0; + if (op == TK_GE) return Poco::icompare(*lvalString, *rvalString) >= 0; + if (op == TK_PLUS) return *rvalString + *lvalString; + if (op == TK_CONTAINS) + { + auto searcher = std::boyer_moore_horspool_searcher( + rvalString->cbegin(), rvalString->cend(), std::hash(), + [](char a, char b) { + return std::tolower(static_cast(a)) == + std::tolower(static_cast(b)); + } + ); + using iterator = std::string::const_iterator; + std::pair result = searcher(lvalString->begin(), lvalString->end()); + return (result.first != result.second); + } + if (op == TK_RECONTAINS) + { + try + { + Poco::RegularExpression regex(*rvalString, Poco::RegularExpression::RE_CASELESS | Poco::RegularExpression::RE_UTF8); + Poco::RegularExpression::Match match; + return (regex.match(*lvalString, match) > 0); + } + catch (const Poco::RegularExpressionException&) + { + return false; + } + } + if (op == TK_MATCHES) + { + try + { + Poco::RegularExpression regex(*rvalString, Poco::RegularExpression::RE_CASELESS | Poco::RegularExpression::RE_UTF8); + return regex.match(*lvalString); + } + catch (const Poco::RegularExpressionException&) + { + return false; + } + } + } + if (auto rvalRegexp = std::get_if>(&rval)) + { + if (op == TK_RECONTAINS) + { + try + { + Poco::RegularExpression::Match match; + return (rvalRegexp->get()->match(*lvalString, match) > 0); + } + catch (const Poco::RegularExpressionException&) + { + return false; + } + } + if (op == TK_MATCHES) + return rvalRegexp->get()->match(*lvalString); + } + } + else if (auto lvalContent = std::get_if>(&lval)) + { + if (auto rvalContent = std::get_if>(&rval)) + { + if (op == TK_EQ) return lvalContent->get()->operator==(*rvalContent->get()); + if (op == TK_NE) return !(lvalContent->get()->operator==(*rvalContent->get())); + } + if (auto rvalString = std::get_if(&rval)) + { + if (op == TK_CONTAINS) return lvalContent->get()->Contains(*rvalString); + if (op == TK_RECONTAINS) + { + try + { + Poco::RegularExpression regex(*rvalString, Poco::RegularExpression::RE_CASELESS | Poco::RegularExpression::RE_UTF8); + return lvalContent->get()->REContains(regex); + } + catch (const Poco::RegularExpressionException&) + { + return false; + } + } + } + if (auto rvalRegexp = std::get_if>(&rval)) + { + if (op == TK_RECONTAINS) return lvalContent->get()->REContains(*rvalRegexp->get()); + } + } + else if (auto lvalBool = std::get_if(&lval)) + { + if (auto rvalBool = std::get_if(&rval)) + { + if (op == TK_EQ) return *lvalBool == *rvalBool; + if (op == TK_NE) return *lvalBool != *rvalBool; + if (op == TK_LT) return *lvalBool < *rvalBool; + if (op == TK_LE) return *lvalBool <= *rvalBool; + if (op == TK_GT) return *lvalBool > *rvalBool; + if (op == TK_GE) return *lvalBool >= *rvalBool; + if (op == TK_PLUS) return static_cast(*rvalBool + *lvalBool); + if (op == TK_MINUS) return static_cast(*rvalBool - *lvalBool); + } + } + if (op == TK_EQ) + return false; + else if (op == TK_NE) + return true; + return std::monostate{}; + }; + auto lvalArray = std::get_if>>(&lval); + auto rvalArray = std::get_if>>(&rval); + if (!lvalArray && !rvalArray) + { + return compute(op, lval, rval); + } + else if (lvalArray && !rvalArray) + { + std::unique_ptr> result = std::make_unique>(); + for (const auto& item : *(lvalArray->get())) + result->emplace_back(ValueType2{ compute(op, item.value, rval) }); + return result; + } + else if (!lvalArray && rvalArray) + { + std::unique_ptr> result = std::make_unique>(); + for (const auto& item : *(rvalArray->get())) + result->emplace_back(ValueType2{ compute(op, lval, item.value) }); + return result; + } + else + { + const size_t maxSize = (std::max)(lvalArray->get()->size(), rvalArray->get()->size()); + const size_t minSize = (std::min)(lvalArray->get()->size(), rvalArray->get()->size()); + std::unique_ptr> result = std::make_unique>(); + for (size_t i = 0; i < minSize; ++i) + result->emplace_back(ValueType2{ compute(op, lvalArray->get()->at(i).value, rvalArray->get()->at(i).value) }); + for (size_t i = 0; i < maxSize - minSize; ++i) + result->emplace_back(ValueType2{ std::monostate{} }); + return result; + } +} + +ExprNode* NegateNode::Optimize() +{ + if (!right) + return this; + right = right->Optimize(); + auto rInt = getConstIntValue(right); + if (rInt) + { + delete this; + return new IntLiteral(-*rInt); + } + return this; +} + +ValueType NegateNode::Evaluate(const DIFFITEM& di) const +{ + auto rval = right->Evaluate(di); + if (auto rvalInt = std::get_if(&rval)) + return -*rvalInt; + return std::monostate{}; +} + +static auto ExistsField(int index, const FilterExpression* ctxt, const DIFFITEM& di)-> ValueType +{ + return di.diffcode.exists(index); +} + +static auto NameField(int index, const FilterExpression* ctxt, const DIFFITEM& di)-> ValueType +{ + if (!di.diffcode.exists(index)) + return std::monostate{}; + return ucr::toUTF8(di.diffFileInfo[index].filename.get()); +} + +static auto ExtensionField(int index, const FilterExpression* ctxt, const DIFFITEM& di)-> ValueType +{ + if (!di.diffcode.exists(index)) + return std::monostate{}; + const std::string ext = ucr::toUTF8(paths::FindExtension(di.diffFileInfo[index].filename.get())); + return std::string(ext.c_str() + strspn(ext.c_str(), ".")); +} + +static auto FolderField(int index, const FilterExpression* ctxt, const DIFFITEM& di)-> ValueType +{ + return ucr::toUTF8(di.diffFileInfo[index].path.get()); +} + +static auto SizeField(int index, const FilterExpression* ctxt, const DIFFITEM& di) -> ValueType +{ + if (!di.diffcode.exists(index)) + return std::monostate{}; + return static_cast(di.diffFileInfo[index].size); +} + +static auto DateField(int index, const FilterExpression* ctxt, const DIFFITEM& di) -> ValueType +{ + if (!di.diffcode.exists(index)) + return std::monostate{}; + return di.diffFileInfo[index].mtime; +} + +static auto DateStrField(int index, const FilterExpression* ctxt, const DIFFITEM& di) -> ValueType +{ + if (!di.diffcode.exists(index)) + return std::monostate{}; + Poco::LocalDateTime ldt(Poco::Timezone::tzd(), di.diffFileInfo[index].mtime); + return Poco::DateTimeFormatter::format(ldt, "%Y-%m-%d"); +} + +static auto CreationTimeField(int index, const FilterExpression* ctxt, const DIFFITEM& di) -> ValueType +{ + if (!di.diffcode.exists(index)) + return std::monostate{}; + return di.diffFileInfo[index].ctime; +} + +static auto FileVersionField(int index, const FilterExpression* ctxt, const DIFFITEM& di) -> ValueType +{ + if (!di.diffcode.exists(index)) + return std::monostate{}; + if (di.diffFileInfo[index].version.IsCleared()) + ctxt->ctxt->UpdateVersion(const_cast(di), index); + return static_cast(di.diffFileInfo[index].version.GetFileVersionQWORD()); +} + +static auto AttributesField(int index, const FilterExpression* ctxt, const DIFFITEM& di) -> ValueType +{ + if (!di.diffcode.exists(index)) + return std::monostate{}; + return static_cast(di.diffFileInfo[index].flags.attributes); +} + +static auto AttrStrField(int index, const FilterExpression* ctxt, const DIFFITEM& di) -> ValueType +{ + if (!di.diffcode.exists(index)) + return std::monostate{}; + return ucr::toUTF8(di.diffFileInfo[index].flags.ToString()); +} + +static auto CodepageField(int index, const FilterExpression* ctxt, const DIFFITEM& di) -> ValueType +{ + if (!di.diffcode.exists(index)) + return std::monostate{}; + return static_cast(di.diffFileInfo[index].encoding.m_codepage); +} + +static auto DiffCodeField(int index, const FilterExpression* ctxt, const DIFFITEM& di) -> ValueType +{ + return static_cast(di.diffcode.diffcode); +} + +static auto DifferencesField(int index, const FilterExpression* ctxt, const DIFFITEM& di) -> ValueType +{ + return static_cast(di.nsdiffs); +} + +static auto IgnoredDiffsField(int index, const FilterExpression* ctxt, const DIFFITEM& di) -> ValueType +{ + return static_cast(di.nidiffs); +} + +static auto EncodingField(int index, const FilterExpression* ctxt, const DIFFITEM& di) -> ValueType +{ + if (!di.diffcode.exists(index)) + return std::monostate{}; + return ucr::toUTF8(di.diffFileInfo[index].encoding.GetName()); +} + +static auto FullPathField(int index, const FilterExpression* ctxt, const DIFFITEM& di) -> ValueType +{ + if (!di.diffcode.exists(index)) + return std::monostate{}; + const String relpath = paths::ConcatPath(di.diffFileInfo[index].path, di.diffFileInfo[index].filename); + return ucr::toUTF8(paths::ConcatPath(ctxt->ctxt->GetPath(index), relpath)); +} + +static auto ContentField(int index, const FilterExpression* ctxt, const DIFFITEM& di) -> ValueType +{ + if (!di.diffcode.exists(index)) + return std::monostate{}; + const String relpath = paths::ConcatPath(di.diffFileInfo[index].path, di.diffFileInfo[index].filename); + std::shared_ptr content{ new FileContentRef }; + content->path = paths::ConcatPath(ctxt->ctxt->GetPath(index), relpath); + content->item.size = di.diffFileInfo[index].size; + content->item.flags = di.diffFileInfo[index].flags; + content->item.mtime = di.diffFileInfo[index].mtime; + content->item.ctime = di.diffFileInfo[index].ctime; + content->item.version = di.diffFileInfo[index].version; + content->item.encoding = di.diffFileInfo[index].encoding; + return content; +} + +FieldNode::FieldNode(const FilterExpression* ctxt, const std::string& v) : ctxt(ctxt), field(v) +{ + int prefixlen = 0; + int side = 0; + std::string vl = Poco::toLower(v); + if (vl.compare(0, 4, "left") == 0) + { + side = 0; + prefixlen = 4; + } + else if (vl.compare(0, 6, "middle") == 0) + { + side = 1; + prefixlen = 6; + } + else if (vl.compare(0, 5, "right") == 0) + { + side = -1; + prefixlen = 5; + } + ValueType (*functmp)(int, const FilterExpression*, const DIFFITEM&) = nullptr; + const char* p = vl.c_str() + prefixlen; + if (strcmp(p, "exists") == 0) + functmp = ExistsField; + else if (strcmp(p, "name") == 0) + functmp = NameField; + else if (strcmp(p, "extension") == 0) + functmp = ExtensionField; + else if (strcmp(p, "fullpath") == 0) + functmp = FullPathField; + else if (strcmp(p, "folder") == 0) + functmp = FolderField; + else if (strcmp(p, "size") == 0) + functmp = SizeField; + else if (strcmp(p, "datestr") == 0) + functmp = DateStrField; + else if (strcmp(p, "date") == 0) + functmp = DateField; + else if (strcmp(p, "attributes") == 0) + functmp = AttributesField; + else if (strcmp(p, "attrstr") == 0) + functmp = AttrStrField; + else if (strcmp(p, "creationtime") == 0) + functmp = CreationTimeField; + else if (strcmp(p, "version") == 0) + functmp = FileVersionField; + else if (strcmp(p, "codepage") == 0) + functmp = CodepageField; + else if (strcmp(p, "encoding") == 0) + functmp = EncodingField; + else if (strcmp(p, "diffcode") == 0) + { + functmp = DiffCodeField; + side = -2; + } + else if (strcmp(p, "differences") == 0) + { + functmp = DifferencesField; + side = -2; + } + else if (strcmp(p, "ignoreddiffs") == 0) + { + functmp = IgnoredDiffsField; + side = -2; + } + else if (strcmp(p, "content") == 0) + functmp = ContentField; + else + throw std::runtime_error("Invalid field name: " + std::string(v.begin(), v.end())); + if (prefixlen > 0) + func = [side, functmp](const FilterExpression* ctxt, const DIFFITEM& di)-> ValueType { return functmp(side < 0 ? ctxt->ctxt->GetCompareDirs() + side: side, ctxt, di); }; + else + func = [functmp](const FilterExpression* ctxt, const DIFFITEM& di)-> ValueType { + const int dirs = ctxt->ctxt->GetCompareDirs(); + std::unique_ptr> values = std::make_unique>(); + for (int i = 0; i < dirs; ++i) + values->emplace_back(ValueType2{ functmp(i, ctxt, di) }); + return values; + }; +} + +ValueType FieldNode::Evaluate(const DIFFITEM& di) const +{ + return func(ctxt, di); +} + +static auto AbsFunc(const FilterExpression* ctxt, const DIFFITEM& di, std::vector* args) -> ValueType +{ + auto arg1 = (*args)[0]->Evaluate(di); + if (auto arg1Int = std::get_if(&arg1)) + return abs(*arg1Int); + return std::monostate{}; +} + +static auto AnyOfFunc(const FilterExpression* ctxt, const DIFFITEM& di, std::vector* args) -> ValueType +{ + auto arg1 = (*args)[0]->Evaluate(di); + if (const auto arrayVal = std::get_if>>(&arg1)) + { + const auto& vec = *arrayVal->get(); + return std::any_of(vec.begin(), vec.end(), [](const ValueType2& item) { + const auto boolVal = std::get_if(&item.value); + return boolVal && *boolVal; + }); + } + if (auto arg1Bool = std::get_if(&arg1)) + return *arg1Bool; + return std::monostate{}; +} + +static auto AllOfFunc(const FilterExpression* ctxt, const DIFFITEM& di, std::vector* args) -> ValueType +{ + auto arg1 = (*args)[0]->Evaluate(di); + if (const auto arrayVal = std::get_if>>(&arg1)) + { + const auto& vec = *arrayVal->get(); + return std::all_of(vec.begin(), vec.end(), [](const ValueType2& item) { + const auto boolVal = std::get_if(&item.value); + return boolVal && *boolVal; + }); + } + if (auto arg1Bool = std::get_if(&arg1)) + return *arg1Bool; + return std::monostate{}; +} + +static auto AllEqualFunc(const FilterExpression* ctxt, const DIFFITEM& di, std::vector* args) -> ValueType +{ + ValueType first = args->at(0)->Evaluate(di); + if (auto pArray = std::get_if>>(&first); pArray && *pArray) + { + const auto& vec = **pArray; + if (vec.size() <= 1) + return true; + const ValueType& base = vec[0].value; + for (size_t i = 1; i < vec.size(); ++i) + { + if (!(vec[i].value == base)) + return false; + } + return true; + } + else + { + for (size_t i = 1; i < args->size(); ++i) + { + ValueType val = args->at(i)->Evaluate(di); + if (!(val == first)) + return false; + } + return true; + } +} + +static auto LengthFunc(const FilterExpression* ctxt, const DIFFITEM& di, std::vector* args) -> ValueType +{ + auto arg1 = (*args)[0]->Evaluate(di); + if (auto arg1String = std::get_if(&arg1)) + return static_cast(arg1String->length()); + return std::monostate{}; +} + +static auto SubstrFunc(const FilterExpression* ctxt, const DIFFITEM& di, std::vector* args) -> ValueType +{ + if (args->size() < 2 || args->size() > 3) + return std::monostate{}; + + auto argStr = (*args)[0]->Evaluate(di); + auto argStart = (*args)[1]->Evaluate(di); + std::optional argLen; + if (args->size() == 3) + argLen = (*args)[2]->Evaluate(di); + + const std::string* str = std::get_if(&argStr); + const int64_t* start = std::get_if(&argStart); + const int64_t* len = argLen ? std::get_if(&*argLen) : nullptr; + + if (!str || !start) + return std::monostate{}; + + int64_t s = *start; + if (s < 0) + s += static_cast(str->length()); + if (s < 0 || s > str->length()) + return std::string{}; + + if (!len) + return str->substr(s); + return str->substr(s, static_cast(*len)); +} + +static auto LineCountFunc(const FilterExpression* ctxt, const DIFFITEM& di, std::vector* args) -> ValueType +{ + auto arg1 = (*args)[0]->Evaluate(di); + if (auto arg1ContentRef = std::get_if>(&arg1)) + return static_cast(arg1ContentRef->get()->LineCount()); + return std::monostate{}; +} + +static auto SublinesFunc(const FilterExpression* ctxt, const DIFFITEM& di, std::vector* args) -> ValueType +{ + if (args->size() < 2 || args->size() > 3) + return std::monostate{}; + + auto argContentRef = (*args)[0]->Evaluate(di); + auto argStart = (*args)[1]->Evaluate(di); + std::optional argLen; + if (args->size() == 3) + argLen = (*args)[2]->Evaluate(di); + + const auto contentref = std::get_if>(&argContentRef); + const int64_t* start = std::get_if(&argStart); + const int64_t* len = argLen ? std::get_if(&*argLen) : nullptr; + + if (!contentref || !start) + return std::monostate{}; + + return contentref->get()->Sublines(*start, len ? *len : -1); +} + +static auto ReplaceFunc(const FilterExpression* ctxt, const DIFFITEM& di, std::vector* args) -> ValueType +{ + if (!args || args->size() != 3) + return std::monostate{}; + + auto argStr = (*args)[0]->Evaluate(di); + auto argFrom = (*args)[1]->Evaluate(di); + auto argTo = (*args)[2]->Evaluate(di); + + const std::string* str = std::get_if(&argStr); + const std::string* from = std::get_if(&argFrom); + const std::string* to = std::get_if(&argTo); + + if (!str || !from || !to || from->empty()) + return std::monostate{}; + + return Poco::replace(*str, *from, *to); +} + +static auto TodayFunc(const FilterExpression* ctxt, const DIFFITEM& di, std::vector* args) -> ValueType +{ + return *ctxt->today; +} + +static auto NowFunc(const FilterExpression* ctxt, const DIFFITEM& di, std::vector* args) -> ValueType +{ + return *ctxt->now; +} + +FunctionNode::FunctionNode(const FilterExpression* ctxt, const std::string& name, std::vector* args) + : ctxt(ctxt), functionName(Poco::toLower(name)), args(args) +{ + if (functionName == "abs") + { + if (!args || args->size() != 1) + throw std::invalid_argument("abs function requires 1 arguments"); + func = AbsFunc; + } + else if (functionName == "anyof") + { + if (!args || args->size() != 1) + throw std::invalid_argument("anyof function requires 1 arguments"); + func = AnyOfFunc; + } + else if (functionName == "allof") + { + if (!args || args->size() != 1) + throw std::invalid_argument("allof function requires 1 arguments"); + func = AllOfFunc; + } + else if (functionName == "allequal") + { + if (!args || args->size() < 1) + throw std::invalid_argument("allequal function requires at least 1 arguments"); + func = AllEqualFunc; + } + else if (functionName == "length") + { + if (!args || args->size() < 1) + throw std::invalid_argument("length function requires 1 arguments"); + func = LengthFunc; + } + else if (functionName == "substr") + { + if (!args || (args->size() < 2 || args->size() > 3)) + throw std::invalid_argument("substr function requires 2 or 3 arguments: substr(string, start [, length])"); + func = SubstrFunc; + } + else if (functionName == "linecount") + { + if (!args || args->size() < 1) + throw std::invalid_argument("linecount function requires 1 arguments"); + func = LineCountFunc; + } + else if (functionName == "sublines") + { + if (!args || (args->size() < 2 || args->size() > 3)) + throw std::invalid_argument("sublines nesfunction requires 2 or 3 arguments: sublines(content, start [, length])"); + func = SublinesFunc; + } + else if (functionName == "replace") + { + if (!args || (args->size() < 2 || args->size() > 3)) + throw std::invalid_argument("replace function requires exactly 3 arguments: replace(string, from, to)"); + func = ReplaceFunc; + } + else if (functionName == "today") + { + if (args && args->size() != 0) + throw std::invalid_argument("today function requires 0 arguments"); + func = TodayFunc; + } + else if (functionName == "now") + { + if (args && args->size() != 0) + throw std::invalid_argument("now function requires 0 arguments"); + func = NowFunc; + } + else + { + throw std::runtime_error("Unknown function: " + std::string(functionName.begin(), functionName.end())); + } +} + +FunctionNode::~FunctionNode() +{ + if (args) + { + for (auto arg : *args) + { + delete arg; + } + } + delete args; +} + +ValueType FunctionNode::Evaluate(const DIFFITEM& di) const +{ + return func(ctxt, di, args); +} + +SizeLiteral::SizeLiteral(const std::string& v) +{ + size_t pos = 0; + while (pos < v.size() && (isdigit(v[pos]) || v[pos] == '.')) + ++pos; + std::string numberPart = v.substr(0, pos); + std::string unitPart = v.substr(pos); + std::transform(unitPart.begin(), unitPart.end(), unitPart.begin(), ::tolower); + double number = 0.0; + try + { + number = std::stod(numberPart); + } + catch (...) { } + if (unitPart == "kb") + value = static_cast(number * 1024LL); + else if (unitPart == "mb") + value = static_cast(number * 1024LL * 1024LL); + else if (unitPart == "gb") + value = static_cast(number * 1024LL * 1024LL * 1024LL); + else if (unitPart == "tb") + value = static_cast(number * 1024LL * 1024LL * 1024LL * 1024LL); + else + value = static_cast(number); +} + +DateTimeLiteral::DateTimeLiteral(const std::string& v) +{ + static const std::vector formats = { + Poco::DateTimeFormat::ISO8601_FORMAT, + Poco::DateTimeFormat::ISO8601_FRAC_FORMAT, + "%Y-%m-%d %H:%M:%S", + "%Y-%m-%d", + "%Y/%m/%d %H:%M:%S", + "%Y/%m/%d", + "%Y.%m.%d %H:%M:%S", + "%Y.%m.%d", + "%d-%b-%Y %H:%M:%S", + "%d-%b-%Y", + }; + + Poco::DateTime dt; + int tz = 0; + bool parsed = false; + for (const auto& fmt : formats) + { + try + { + Poco::DateTimeParser::parse(fmt, v, dt, tz); + dt.makeUTC( + (fmt == Poco::DateTimeFormat::ISO8601_FORMAT || fmt == Poco::DateTimeFormat::ISO8601_FRAC_FORMAT) + ? tz : Poco::Timezone::tzd()); + value = dt.timestamp(); + parsed = true; + break; + } + catch (Poco::SyntaxException&) + { + // Try next format + } + } + if (!parsed) + { + throw std::invalid_argument("Unrecognized date/time format: " + v); + } +} + +DurationLiteral::DurationLiteral(const std::string& v) +{ + size_t pos = 0; + while (pos < v.size() && (isdigit(v[pos]) || v[pos] == '.')) + ++pos; + std::string numberPart = v.substr(0, pos); + std::string unitPart = v.substr(pos); + std::transform(unitPart.begin(), unitPart.end(), unitPart.begin(), ::tolower); + double number = 0.0; + try + { + number = std::stod(numberPart); + } + catch (...) { } + if (unitPart == "w" || unitPart == "week" || unitPart == "weeks") + value = static_cast(number * 604800LL * 1000000LL); + else if (unitPart == "d" || unitPart == "day" || unitPart == "days") + value = static_cast(number * 86400LL * 1000000LL); + else if (unitPart == "h" || unitPart == "hr" || unitPart == "hour" || unitPart == "hours") + value = static_cast(number * 3600LL * 1000000LL); + else if (unitPart == "m" || unitPart == "min" || unitPart == "minute" || unitPart == "minutes") + value = static_cast(number * 60LL * 1000000LL); + else if (unitPart == "s" || unitPart == "sec" || unitPart == "second" || unitPart == "seconds") + value = static_cast(number * 1LL * 1000000LL); + else if (unitPart == "ms" || unitPart == "msec" || unitPart == "millisecond" || unitPart == "milliseconds") + value = static_cast(number * 1000LL); + else + value = static_cast(number); +} + +VersionLiteral::VersionLiteral(const std::string& v) +{ + int major = 0, minor = 0, build = 0, revision = 0; + sscanf_s(v.c_str(), "%d.%d.%d.%d", &major, &minor, &build, &revision); + value = (static_cast(major) << 48) + (static_cast(minor) << 32) + (static_cast(build) << 16) + revision; +} + +RegularExpressionLiteral::RegularExpressionLiteral(const std::string& v) +{ + value.reset(new Poco::RegularExpression(v, Poco::RegularExpression::RE_CASELESS | Poco::RegularExpression::RE_UTF8)); +} + diff --git a/Src/FilterEngine/FilterExpressionNodes.h b/Src/FilterEngine/FilterExpressionNodes.h new file mode 100644 index 00000000000..63fca51e615 --- /dev/null +++ b/Src/FilterEngine/FilterExpressionNodes.h @@ -0,0 +1,174 @@ +/** + * @file FilterExpressionNodes.h + * + * @brief Filter expression evaluation classes. + */ +#pragma once + +#include "FilterParser.h" +#include +#include +#include +#include +#include +#include + +namespace Poco { class RegularExpression; } +struct FilterExpression; +struct FileContentRef; +class DIFFITEM; +struct ValueType2; +using ValueType = std::variant, std::string, std::shared_ptr, std::unique_ptr>>; +struct ValueType2 { ValueType value; }; + +struct ExprNode +{ + virtual ~ExprNode() { } + virtual ExprNode* Optimize() { return this; } + virtual ValueType Evaluate(const DIFFITEM& di) const = 0; +}; + +struct OrNode : public ExprNode +{ + OrNode(ExprNode* l, ExprNode* r) : left(l), right(r) { } + virtual ~OrNode() + { + delete left; + delete right; + } + ExprNode* Optimize() override; + ValueType Evaluate(const DIFFITEM& di) const override; + ExprNode* left; + ExprNode* right; +}; + +struct AndNode : public ExprNode +{ + AndNode(ExprNode* l, ExprNode* r) : left(l), right(r) { } + virtual ~AndNode() + { + delete left; + delete right; + } + ExprNode* Optimize() override; + ValueType Evaluate(const DIFFITEM& di) const override; + ExprNode* left; + ExprNode* right; +}; + +struct NotNode : public ExprNode +{ + NotNode(ExprNode* e) : expr(e) { } + virtual ~NotNode() + { + delete expr; + } + ExprNode* Optimize() override; + ValueType Evaluate(const DIFFITEM& di) const override; + ExprNode* expr; +}; + +struct BinaryOpNode : public ExprNode +{ + BinaryOpNode(ExprNode* l, int o, ExprNode* r) : left(l), right(r), op(o) { } + virtual ~BinaryOpNode() + { + delete left; + delete right; + } + ExprNode* Optimize() override; + ValueType Evaluate(const DIFFITEM& di) const override; + int op; + ExprNode* left; + ExprNode* right; +}; + +struct NegateNode : public ExprNode +{ + NegateNode(ExprNode* r) : right(r) { } + virtual ~NegateNode() + { + delete right; + } + ExprNode* Optimize() override; + ValueType Evaluate(const DIFFITEM& di) const override; + ExprNode* right; +}; + +struct FieldNode : public ExprNode +{ + FieldNode(const FilterExpression* ctxt, const std::string& v); + ValueType Evaluate(const DIFFITEM& di) const override; + const FilterExpression* ctxt; + std::string field; + std::function func; +}; + +struct FunctionNode : public ExprNode +{ + FunctionNode(const FilterExpression* ctxt, const std::string& name, std::vector* args); + virtual ~FunctionNode(); + ValueType Evaluate(const DIFFITEM& di) const override; + const FilterExpression* ctxt; + std::string functionName; + std::vector* args; + std::function* args)> func; +}; + +struct BoolLiteral : public ExprNode +{ + BoolLiteral(bool v) : value(v) { } + inline ValueType Evaluate(const DIFFITEM& di) const override { return value; } + bool value; +}; + +struct IntLiteral : public ExprNode +{ + IntLiteral(int64_t v) : value(v) { } + inline ValueType Evaluate(const DIFFITEM& di) const override { return value; } + int64_t value; +}; + +struct StringLiteral : public ExprNode +{ + StringLiteral(const std::string& v) : value(v) { } + inline ValueType Evaluate(const DIFFITEM& di) const override { return value; } + std::string value; +}; + +struct SizeLiteral : public ExprNode +{ + SizeLiteral(const std::string& v); + inline ValueType Evaluate(const DIFFITEM& di) const override { return value; } + int64_t value; +}; + +struct DateTimeLiteral : public ExprNode +{ + DateTimeLiteral(const std::string& v); + DateTimeLiteral(const Poco::Timestamp& v) : value(v) { } + inline ValueType Evaluate(const DIFFITEM& di) const override { return value; } + Poco::Timestamp value; +}; + +struct DurationLiteral : public ExprNode +{ + DurationLiteral(const std::string& v); + inline ValueType Evaluate(const DIFFITEM& di) const override { return value; } + int64_t value; +}; + +struct VersionLiteral : public ExprNode +{ + VersionLiteral(const std::string& v); + inline ValueType Evaluate(const DIFFITEM& di) const override { return value; } + int64_t value; +}; + +struct RegularExpressionLiteral : public ExprNode +{ + RegularExpressionLiteral(const std::string& v); + inline ValueType Evaluate(const DIFFITEM& di) const override { return value; } + std::shared_ptr value; +}; + diff --git a/Src/FilterEngine/FilterLexer.cpp b/Src/FilterEngine/FilterLexer.cpp new file mode 100644 index 00000000000..02c55d69012 --- /dev/null +++ b/Src/FilterEngine/FilterLexer.cpp @@ -0,0 +1,1579 @@ +/* Generated by re2c 3.1 on Fri Jun 27 06:33:04 2025 */ +#line 1 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterLexer.re" +/** + * @file FilterLexer.re + * + * @brief Lexer for the filter parser. + */ +#line 11 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterLexer.re" + + +#include "pch.h" +#include "FilterParser.h" +#include "FilterLexer.h" +#include + +int FilterLexer::yylex() +{ +begin: + +#line 21 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterLexer.cpp" +{ + char yych; + unsigned int yyaccept = 0; + yych = *YYCURSOR; + switch (yych) { + case '\t': + case '\n': + case '\r': + case ' ': goto yy3; + case '!': goto yy5; + case '"': goto yy6; + case '%': goto yy7; + case '(': goto yy8; + case ')': goto yy9; + case '*': goto yy10; + case '+': goto yy11; + case ',': goto yy12; + case '-': goto yy13; + case '/': goto yy14; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': goto yy15; + case '<': goto yy17; + case '=': goto yy19; + case '>': goto yy21; + case 'A': + case 'a': goto yy23; + case 'B': + case 'E': + case 'G': + case 'H': + case 'I': + case 'J': + case 'K': + case 'L': + case 'P': + case 'Q': + case 'S': + case 'U': + case 'W': + case 'X': + case 'Y': + case 'Z': + case '_': + case 'b': + case 'e': + case 'g': + case 'h': + case 'i': + case 'j': + case 'k': + case 'l': + case 'p': + case 'q': + case 's': + case 'u': + case 'w': + case 'x': + case 'y': + case 'z': goto yy25; + case 'C': + case 'c': goto yy27; + case 'D': + case 'd': goto yy28; + case 'F': + case 'f': goto yy29; + case 'M': + case 'm': goto yy30; + case 'N': + case 'n': goto yy31; + case 'O': + case 'o': goto yy32; + case 'R': + case 'r': goto yy33; + case 'T': + case 't': goto yy34; + case 'V': + case 'v': goto yy35; + default: + if (YYLIMIT <= YYCURSOR) goto yy118; + goto yy1; + } +yy1: + ++YYCURSOR; +yy2: +#line 96 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterLexer.re" + { return LEXER_ERR_UNKNOWN_CHAR; } +#line 116 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterLexer.cpp" +yy3: + yych = *++YYCURSOR; + switch (yych) { + case '\t': + case '\n': + case '\r': + case ' ': goto yy3; + default: goto yy4; + } +yy4: +#line 22 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterLexer.re" + { goto begin; } +#line 129 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterLexer.cpp" +yy5: + yych = *++YYCURSOR; + switch (yych) { + case '=': goto yy36; + default: goto yy2; + } +yy6: + ++YYCURSOR; +#line 73 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterLexer.re" + { + std::string str = UnescapeQuotes(YYCURSOR); + if (*(YYCURSOR - 1) != '"') + return LEXER_ERR_UNTERMINATED_STRING; + yylval.string = DupString(str.c_str()); + return TK_STRING_LITERAL; + } +#line 146 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterLexer.cpp" +yy7: + ++YYCURSOR; +#line 93 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterLexer.re" + { return TK_MOD; } +#line 151 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterLexer.cpp" +yy8: + ++YYCURSOR; +#line 87 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterLexer.re" + { return TK_LPAREN; } +#line 156 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterLexer.cpp" +yy9: + ++YYCURSOR; +#line 88 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterLexer.re" + { return TK_RPAREN; } +#line 161 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterLexer.cpp" +yy10: + ++YYCURSOR; +#line 91 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterLexer.re" + { return TK_STAR; } +#line 166 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterLexer.cpp" +yy11: + ++YYCURSOR; +#line 89 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterLexer.re" + { return TK_PLUS; } +#line 171 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterLexer.cpp" +yy12: + ++YYCURSOR; +#line 95 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterLexer.re" + { return TK_COMMA; } +#line 176 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterLexer.cpp" +yy13: + ++YYCURSOR; +#line 90 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterLexer.re" + { return TK_MINUS; } +#line 181 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterLexer.cpp" +yy14: + ++YYCURSOR; +#line 92 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterLexer.re" + { return TK_SLASH; } +#line 186 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterLexer.cpp" +yy15: + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + switch (yych) { + case '.': goto yy37; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': goto yy15; + case 'D': + case 'd': goto yy39; + case 'G': + case 'K': + case 'T': + case 'g': + case 'k': + case 't': goto yy41; + case 'H': + case 'h': goto yy42; + case 'M': + case 'm': goto yy43; + case 'S': + case 's': goto yy44; + case 'W': + case 'w': goto yy45; + default: goto yy16; + } +yy16: +#line 61 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterLexer.re" + { + yylval.integer = std::stoi(std::string((const char*)yycursor, YYCURSOR - yycursor)); + return TK_INTEGER_LITERAL; + } +#line 226 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterLexer.cpp" +yy17: + yych = *++YYCURSOR; + switch (yych) { + case '=': goto yy46; + default: goto yy18; + } +yy18: +#line 83 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterLexer.re" + { return TK_LT; } +#line 236 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterLexer.cpp" +yy19: + yych = *++YYCURSOR; + switch (yych) { + case '=': goto yy47; + default: goto yy20; + } +yy20: +#line 81 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterLexer.re" + { return TK_EQ; } +#line 246 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterLexer.cpp" +yy21: + yych = *++YYCURSOR; + switch (yych) { + case '=': goto yy48; + default: goto yy22; + } +yy22: +#line 85 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterLexer.re" + { return TK_GT; } +#line 256 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterLexer.cpp" +yy23: + yych = *++YYCURSOR; + switch (yych) { + case 0x00: goto yy24; + case 'N': + case 'n': goto yy49; + default: goto yy26; + } +yy24: +#line 65 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterLexer.re" + { + const char* p = yycursor; + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') + p++; + std::string tmp = std::string(p, YYCURSOR - p); + yylval.string = DupString(tmp.c_str()); + return TK_IDENTIFIER; + } +#line 275 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterLexer.cpp" +yy25: + yych = *++YYCURSOR; +yy26: + switch (yych) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + case 'G': + case 'H': + case 'I': + case 'J': + case 'K': + case 'L': + case 'M': + case 'N': + case 'O': + case 'P': + case 'Q': + case 'R': + case 'S': + case 'T': + case 'U': + case 'V': + case 'W': + case 'X': + case 'Y': + case 'Z': + case '_': + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + case 'g': + case 'h': + case 'i': + case 'j': + case 'k': + case 'l': + case 'm': + case 'n': + case 'o': + case 'p': + case 'q': + case 'r': + case 's': + case 't': + case 'u': + case 'v': + case 'w': + case 'x': + case 'y': + case 'z': goto yy25; + default: goto yy24; + } +yy27: + yych = *++YYCURSOR; + switch (yych) { + case 0x00: goto yy24; + case 'O': + case 'o': goto yy50; + default: goto yy26; + } +yy28: + yych = *++YYCURSOR; + switch (yych) { + case 0x00: goto yy24; + case '"': goto yy51; + default: goto yy26; + } +yy29: + yych = *++YYCURSOR; + switch (yych) { + case 0x00: goto yy24; + case 'A': + case 'a': goto yy52; + default: goto yy26; + } +yy30: + yych = *++YYCURSOR; + switch (yych) { + case 0x00: goto yy24; + case 'A': + case 'a': goto yy53; + default: goto yy26; + } +yy31: + yych = *++YYCURSOR; + switch (yych) { + case 0x00: goto yy24; + case 'O': + case 'o': goto yy54; + default: goto yy26; + } +yy32: + yych = *++YYCURSOR; + switch (yych) { + case 0x00: goto yy24; + case 'R': + case 'r': goto yy55; + default: goto yy26; + } +yy33: + yych = *++YYCURSOR; + switch (yych) { + case 0x00: goto yy24; + case 'E': + case 'e': goto yy57; + default: goto yy26; + } +yy34: + yych = *++YYCURSOR; + switch (yych) { + case 0x00: goto yy24; + case 'R': + case 'r': goto yy58; + default: goto yy26; + } +yy35: + yych = *++YYCURSOR; + switch (yych) { + case 0x00: goto yy24; + case '"': goto yy59; + default: goto yy26; + } +yy36: + ++YYCURSOR; +#line 82 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterLexer.re" + { return TK_NE; } +#line 419 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterLexer.cpp" +yy37: + yych = *++YYCURSOR; + switch (yych) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': goto yy60; + default: goto yy38; + } +yy38: + YYCURSOR = YYMARKER; + if (yyaccept == 0) { + goto yy16; + } else { + goto yy40; + } +yy39: + yyaccept = 1; + yych = *(YYMARKER = ++YYCURSOR); + switch (yych) { + case 'A': + case 'a': goto yy61; + default: goto yy40; + } +yy40: +#line 39 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterLexer.re" + { + const char* p = yycursor; + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') + p++; + std::string lit(p, YYCURSOR - p); + yylval.string = DupString(lit.c_str()); + return TK_DURATION_LITERAL; + } +#line 460 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterLexer.cpp" +yy41: + yych = *++YYCURSOR; + switch (yych) { + case 'B': + case 'b': goto yy62; + default: goto yy38; + } +yy42: + yyaccept = 1; + yych = *(YYMARKER = ++YYCURSOR); + switch (yych) { + case 'O': + case 'o': goto yy63; + case 'R': + case 'r': goto yy64; + default: goto yy40; + } +yy43: + yyaccept = 1; + yych = *(YYMARKER = ++YYCURSOR); + switch (yych) { + case 'B': + case 'b': goto yy62; + case 'I': + case 'i': goto yy65; + case 'S': + case 's': goto yy66; + default: goto yy40; + } +yy44: + yyaccept = 1; + yych = *(YYMARKER = ++YYCURSOR); + switch (yych) { + case 'E': + case 'e': goto yy67; + default: goto yy40; + } +yy45: + yyaccept = 1; + yych = *(YYMARKER = ++YYCURSOR); + switch (yych) { + case 'E': + case 'e': goto yy68; + default: goto yy40; + } +yy46: + ++YYCURSOR; +#line 84 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterLexer.re" + { return TK_LE; } +#line 510 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterLexer.cpp" +yy47: + ++YYCURSOR; +#line 80 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterLexer.re" + { return TK_EQ; } +#line 515 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterLexer.cpp" +yy48: + ++YYCURSOR; +#line 86 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterLexer.re" + { return TK_GE; } +#line 520 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterLexer.cpp" +yy49: + yych = *++YYCURSOR; + switch (yych) { + case 0x00: goto yy24; + case 'D': + case 'd': goto yy69; + default: goto yy26; + } +yy50: + yych = *++YYCURSOR; + switch (yych) { + case 0x00: goto yy24; + case 'N': + case 'n': goto yy71; + default: goto yy26; + } +yy51: + ++YYCURSOR; +#line 47 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterLexer.re" + { + std::string str = UnescapeQuotes(YYCURSOR); + if (*(YYCURSOR - 1) != '"') + return LEXER_ERR_UNTERMINATED_STRING; + yylval.string = DupString(str.c_str()); + return TK_DATETIME_LITERAL; + } +#line 547 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterLexer.cpp" +yy52: + yych = *++YYCURSOR; + switch (yych) { + case 0x00: goto yy24; + case 'L': + case 'l': goto yy72; + default: goto yy26; + } +yy53: + yych = *++YYCURSOR; + switch (yych) { + case 0x00: goto yy24; + case 'T': + case 't': goto yy73; + default: goto yy26; + } +yy54: + yych = *++YYCURSOR; + switch (yych) { + case 0x00: goto yy24; + case 'T': + case 't': goto yy74; + default: goto yy26; + } +yy55: + yych = *++YYCURSOR; + switch (yych) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + case 'G': + case 'H': + case 'I': + case 'J': + case 'K': + case 'L': + case 'M': + case 'N': + case 'O': + case 'P': + case 'Q': + case 'R': + case 'S': + case 'T': + case 'U': + case 'V': + case 'W': + case 'X': + case 'Y': + case 'Z': + case '_': + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + case 'g': + case 'h': + case 'i': + case 'j': + case 'k': + case 'l': + case 'm': + case 'n': + case 'o': + case 'p': + case 'q': + case 'r': + case 's': + case 't': + case 'u': + case 'v': + case 'w': + case 'x': + case 'y': + case 'z': goto yy25; + default: goto yy56; + } +yy56: +#line 24 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterLexer.re" + { return TK_OR; } +#line 643 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterLexer.cpp" +yy57: + yych = *++YYCURSOR; + switch (yych) { + case 0x00: goto yy24; + case 'C': + case 'c': goto yy76; + default: goto yy26; + } +yy58: + yych = *++YYCURSOR; + switch (yych) { + case 0x00: goto yy24; + case 'U': + case 'u': goto yy77; + default: goto yy26; + } +yy59: + ++YYCURSOR; +#line 54 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterLexer.re" + { + std::string str = UnescapeQuotes(YYCURSOR); + if (*(YYCURSOR - 1) != '"') + return LEXER_ERR_UNTERMINATED_STRING; + yylval.string = DupString(str.c_str()); + return TK_VERSION_LITERAL; + } +#line 670 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterLexer.cpp" +yy60: + yych = *++YYCURSOR; + switch (yych) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': goto yy60; + case 'D': + case 'd': goto yy39; + case 'G': + case 'K': + case 'T': + case 'g': + case 'k': + case 't': goto yy41; + case 'H': + case 'h': goto yy42; + case 'M': + case 'm': goto yy43; + case 'S': + case 's': goto yy44; + case 'W': + case 'w': goto yy45; + default: goto yy38; + } +yy61: + yych = *++YYCURSOR; + switch (yych) { + case 'Y': + case 'y': goto yy78; + default: goto yy38; + } +yy62: + ++YYCURSOR; +#line 31 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterLexer.re" + { + const char* p = yycursor; + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') + p++; + std::string lit(p, YYCURSOR - p); + yylval.string = DupString(lit.c_str()); + return TK_SIZE_LITERAL; + } +#line 720 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterLexer.cpp" +yy63: + yych = *++YYCURSOR; + switch (yych) { + case 'U': + case 'u': goto yy79; + default: goto yy38; + } +yy64: + ++YYCURSOR; + goto yy40; +yy65: + yych = *++YYCURSOR; + switch (yych) { + case 'L': + case 'l': goto yy80; + case 'N': + case 'n': goto yy81; + default: goto yy38; + } +yy66: + yyaccept = 1; + yych = *(YYMARKER = ++YYCURSOR); + switch (yych) { + case 'E': + case 'e': goto yy82; + default: goto yy40; + } +yy67: + yych = *++YYCURSOR; + switch (yych) { + case 'C': + case 'c': goto yy83; + default: goto yy38; + } +yy68: + yych = *++YYCURSOR; + switch (yych) { + case 'E': + case 'e': goto yy84; + default: goto yy38; + } +yy69: + yych = *++YYCURSOR; + switch (yych) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + case 'G': + case 'H': + case 'I': + case 'J': + case 'K': + case 'L': + case 'M': + case 'N': + case 'O': + case 'P': + case 'Q': + case 'R': + case 'S': + case 'T': + case 'U': + case 'V': + case 'W': + case 'X': + case 'Y': + case 'Z': + case '_': + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + case 'g': + case 'h': + case 'i': + case 'j': + case 'k': + case 'l': + case 'm': + case 'n': + case 'o': + case 'p': + case 'q': + case 'r': + case 's': + case 't': + case 'u': + case 'v': + case 'w': + case 'x': + case 'y': + case 'z': goto yy25; + default: goto yy70; + } +yy70: +#line 23 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterLexer.re" + { return TK_AND; } +#line 833 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterLexer.cpp" +yy71: + yych = *++YYCURSOR; + switch (yych) { + case 0x00: goto yy24; + case 'T': + case 't': goto yy85; + default: goto yy26; + } +yy72: + yych = *++YYCURSOR; + switch (yych) { + case 0x00: goto yy24; + case 'S': + case 's': goto yy86; + default: goto yy26; + } +yy73: + yych = *++YYCURSOR; + switch (yych) { + case 0x00: goto yy24; + case 'C': + case 'c': goto yy87; + default: goto yy26; + } +yy74: + yych = *++YYCURSOR; + switch (yych) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + case 'G': + case 'H': + case 'I': + case 'J': + case 'K': + case 'L': + case 'M': + case 'N': + case 'O': + case 'P': + case 'Q': + case 'R': + case 'S': + case 'T': + case 'U': + case 'V': + case 'W': + case 'X': + case 'Y': + case 'Z': + case '_': + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + case 'g': + case 'h': + case 'i': + case 'j': + case 'k': + case 'l': + case 'm': + case 'n': + case 'o': + case 'p': + case 'q': + case 'r': + case 's': + case 't': + case 'u': + case 'v': + case 'w': + case 'x': + case 'y': + case 'z': goto yy25; + default: goto yy75; + } +yy75: +#line 25 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterLexer.re" + { return TK_NOT; } +#line 929 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterLexer.cpp" +yy76: + yych = *++YYCURSOR; + switch (yych) { + case 0x00: goto yy24; + case 'O': + case 'o': goto yy88; + default: goto yy26; + } +yy77: + yych = *++YYCURSOR; + switch (yych) { + case 0x00: goto yy24; + case 'E': + case 'e': goto yy89; + default: goto yy26; + } +yy78: + yych = *++YYCURSOR; + switch (yych) { + case 'S': + case 's': goto yy64; + default: goto yy40; + } +yy79: + yych = *++YYCURSOR; + switch (yych) { + case 'R': + case 'r': goto yy78; + default: goto yy38; + } +yy80: + yych = *++YYCURSOR; + switch (yych) { + case 'L': + case 'l': goto yy91; + default: goto yy38; + } +yy81: + yyaccept = 1; + yych = *(YYMARKER = ++YYCURSOR); + switch (yych) { + case 'U': + case 'u': goto yy92; + default: goto yy40; + } +yy82: + yych = *++YYCURSOR; + switch (yych) { + case 'C': + case 'c': goto yy64; + default: goto yy38; + } +yy83: + yyaccept = 1; + yych = *(YYMARKER = ++YYCURSOR); + switch (yych) { + case 'O': + case 'o': goto yy93; + default: goto yy40; + } +yy84: + yych = *++YYCURSOR; + switch (yych) { + case 'K': + case 'k': goto yy78; + default: goto yy38; + } +yy85: + yych = *++YYCURSOR; + switch (yych) { + case 0x00: goto yy24; + case 'A': + case 'a': goto yy94; + default: goto yy26; + } +yy86: + yych = *++YYCURSOR; + switch (yych) { + case 0x00: goto yy24; + case 'E': + case 'e': goto yy95; + default: goto yy26; + } +yy87: + yych = *++YYCURSOR; + switch (yych) { + case 0x00: goto yy24; + case 'H': + case 'h': goto yy97; + default: goto yy26; + } +yy88: + yych = *++YYCURSOR; + switch (yych) { + case 0x00: goto yy24; + case 'N': + case 'n': goto yy98; + default: goto yy26; + } +yy89: + yych = *++YYCURSOR; + switch (yych) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + case 'G': + case 'H': + case 'I': + case 'J': + case 'K': + case 'L': + case 'M': + case 'N': + case 'O': + case 'P': + case 'Q': + case 'R': + case 'S': + case 'T': + case 'U': + case 'V': + case 'W': + case 'X': + case 'Y': + case 'Z': + case '_': + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + case 'g': + case 'h': + case 'i': + case 'j': + case 'k': + case 'l': + case 'm': + case 'n': + case 'o': + case 'p': + case 'q': + case 'r': + case 's': + case 't': + case 'u': + case 'v': + case 'w': + case 'x': + case 'y': + case 'z': goto yy25; + default: goto yy90; + } +yy90: +#line 26 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterLexer.re" + { yylval.boolean = true; return TK_TRUE_LITERAL; } +#line 1100 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterLexer.cpp" +yy91: + yych = *++YYCURSOR; + switch (yych) { + case 'I': + case 'i': goto yy99; + default: goto yy38; + } +yy92: + yych = *++YYCURSOR; + switch (yych) { + case 'T': + case 't': goto yy100; + default: goto yy38; + } +yy93: + yych = *++YYCURSOR; + switch (yych) { + case 'N': + case 'n': goto yy101; + default: goto yy38; + } +yy94: + yych = *++YYCURSOR; + switch (yych) { + case 0x00: goto yy24; + case 'I': + case 'i': goto yy102; + default: goto yy26; + } +yy95: + yych = *++YYCURSOR; + switch (yych) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + case 'G': + case 'H': + case 'I': + case 'J': + case 'K': + case 'L': + case 'M': + case 'N': + case 'O': + case 'P': + case 'Q': + case 'R': + case 'S': + case 'T': + case 'U': + case 'V': + case 'W': + case 'X': + case 'Y': + case 'Z': + case '_': + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + case 'g': + case 'h': + case 'i': + case 'j': + case 'k': + case 'l': + case 'm': + case 'n': + case 'o': + case 'p': + case 'q': + case 'r': + case 's': + case 't': + case 'u': + case 'v': + case 'w': + case 'x': + case 'y': + case 'z': goto yy25; + default: goto yy96; + } +yy96: +#line 27 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterLexer.re" + { yylval.boolean = false; return TK_FALSE_LITERAL; } +#line 1201 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterLexer.cpp" +yy97: + yych = *++YYCURSOR; + switch (yych) { + case 0x00: goto yy24; + case 'E': + case 'e': goto yy103; + default: goto yy26; + } +yy98: + yych = *++YYCURSOR; + switch (yych) { + case 0x00: goto yy24; + case 'T': + case 't': goto yy104; + default: goto yy26; + } +yy99: + yych = *++YYCURSOR; + switch (yych) { + case 'S': + case 's': goto yy105; + default: goto yy38; + } +yy100: + yych = *++YYCURSOR; + switch (yych) { + case 'E': + case 'e': goto yy78; + default: goto yy38; + } +yy101: + yych = *++YYCURSOR; + switch (yych) { + case 'D': + case 'd': goto yy78; + default: goto yy38; + } +yy102: + yych = *++YYCURSOR; + switch (yych) { + case 0x00: goto yy24; + case 'N': + case 'n': goto yy106; + default: goto yy26; + } +yy103: + yych = *++YYCURSOR; + switch (yych) { + case 0x00: goto yy24; + case 'S': + case 's': goto yy107; + default: goto yy26; + } +yy104: + yych = *++YYCURSOR; + switch (yych) { + case 0x00: goto yy24; + case 'A': + case 'a': goto yy109; + default: goto yy26; + } +yy105: + yych = *++YYCURSOR; + switch (yych) { + case 'E': + case 'e': goto yy110; + default: goto yy38; + } +yy106: + yych = *++YYCURSOR; + switch (yych) { + case 0x00: goto yy24; + case 'S': + case 's': goto yy111; + default: goto yy26; + } +yy107: + yych = *++YYCURSOR; + switch (yych) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + case 'G': + case 'H': + case 'I': + case 'J': + case 'K': + case 'L': + case 'M': + case 'N': + case 'O': + case 'P': + case 'Q': + case 'R': + case 'S': + case 'T': + case 'U': + case 'V': + case 'W': + case 'X': + case 'Y': + case 'Z': + case '_': + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + case 'g': + case 'h': + case 'i': + case 'j': + case 'k': + case 'l': + case 'm': + case 'n': + case 'o': + case 'p': + case 'q': + case 'r': + case 's': + case 't': + case 'u': + case 'v': + case 'w': + case 'x': + case 'y': + case 'z': goto yy25; + default: goto yy108; + } +yy108: +#line 30 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterLexer.re" + { return TK_MATCHES; } +#line 1349 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterLexer.cpp" +yy109: + yych = *++YYCURSOR; + switch (yych) { + case 0x00: goto yy24; + case 'I': + case 'i': goto yy113; + default: goto yy26; + } +yy110: + yych = *++YYCURSOR; + switch (yych) { + case 'C': + case 'c': goto yy114; + default: goto yy38; + } +yy111: + yych = *++YYCURSOR; + switch (yych) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + case 'G': + case 'H': + case 'I': + case 'J': + case 'K': + case 'L': + case 'M': + case 'N': + case 'O': + case 'P': + case 'Q': + case 'R': + case 'S': + case 'T': + case 'U': + case 'V': + case 'W': + case 'X': + case 'Y': + case 'Z': + case '_': + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + case 'g': + case 'h': + case 'i': + case 'j': + case 'k': + case 'l': + case 'm': + case 'n': + case 'o': + case 'p': + case 'q': + case 'r': + case 's': + case 't': + case 'u': + case 'v': + case 'w': + case 'x': + case 'y': + case 'z': goto yy25; + default: goto yy112; + } +yy112: +#line 28 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterLexer.re" + { return TK_CONTAINS; } +#line 1436 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterLexer.cpp" +yy113: + yych = *++YYCURSOR; + switch (yych) { + case 0x00: goto yy24; + case 'N': + case 'n': goto yy115; + default: goto yy26; + } +yy114: + yych = *++YYCURSOR; + switch (yych) { + case 'O': + case 'o': goto yy93; + default: goto yy38; + } +yy115: + yych = *++YYCURSOR; + switch (yych) { + case 0x00: goto yy24; + case 'S': + case 's': goto yy116; + default: goto yy26; + } +yy116: + yych = *++YYCURSOR; + switch (yych) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + case 'G': + case 'H': + case 'I': + case 'J': + case 'K': + case 'L': + case 'M': + case 'N': + case 'O': + case 'P': + case 'Q': + case 'R': + case 'S': + case 'T': + case 'U': + case 'V': + case 'W': + case 'X': + case 'Y': + case 'Z': + case '_': + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + case 'g': + case 'h': + case 'i': + case 'j': + case 'k': + case 'l': + case 'm': + case 'n': + case 'o': + case 'p': + case 'q': + case 'r': + case 's': + case 't': + case 'u': + case 'v': + case 'w': + case 'x': + case 'y': + case 'z': goto yy25; + default: goto yy117; + } +yy117: +#line 29 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterLexer.re" + { return TK_RECONTAINS; } +#line 1531 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterLexer.cpp" +yy118: +#line 94 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterLexer.re" + { return 0; } +#line 1535 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterLexer.cpp" +} +#line 97 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterLexer.re" + +} + +std::string FilterLexer::UnescapeQuotes(char*& str) +{ + std::string result; + while (*str != '\0') + { + if (*str == '"') + { + if (*(str + 1) == '"') + { + result += '"'; + str += 2; + } + else + { + str++; + break; + } + } + else + { + result += *str++; + } + } + return result; +} + +const char* FilterLexer::DupString(const char* str) +{ + char* newStr = _strdup(str); + strings.push_back(newStr); + return newStr; +} + +void FilterLexer::FreeStrings() +{ + for (auto str : strings) + free(str); + strings.clear(); +} + diff --git a/Src/FilterEngine/FilterLexer.h b/Src/FilterEngine/FilterLexer.h new file mode 100644 index 00000000000..509dba02836 --- /dev/null +++ b/Src/FilterEngine/FilterLexer.h @@ -0,0 +1,56 @@ +/** + * @file FilterLexer.h + * + * @brief Header file for the FilterLexer class, which is responsible for tokenizing filter expressions. + */ +#pragma once + +#include +#include +#include + +struct ExprNode; + +struct YYSTYPE +{ + ExprNode* node; + std::vector *nodeList; + const char* string; + int64_t integer; + bool boolean; +}; + +extern void YYSTYPEDestructor(YYSTYPE& yystype); + +struct FilterLexer +{ + enum ErrorCode { + LEXER_ERR_UNKNOWN_CHAR = -1, + LEXER_ERR_UNTERMINATED_STRING = -2, + }; + + FilterLexer(const std::string& input) + : yycursor((char*)input.c_str()) + , YYCURSOR((char*)input.c_str()) + , YYLIMIT((char*)input.c_str() + input.length()) + { + } + + ~FilterLexer() + { + FreeStrings(); + } + + int yylex(); + + std::string UnescapeQuotes(char*& str); + const char* DupString(const char* str); + void FreeStrings(); + + YYSTYPE yylval{}; + char* yycursor = nullptr; + char* YYMARKER = nullptr; + char* YYCURSOR = nullptr; + char* YYLIMIT = nullptr; + std::vector strings; +}; diff --git a/Src/FilterEngine/FilterLexer.re b/Src/FilterEngine/FilterLexer.re new file mode 100644 index 00000000000..69b7504db97 --- /dev/null +++ b/Src/FilterEngine/FilterLexer.re @@ -0,0 +1,139 @@ +/** + * @file FilterLexer.re + * + * @brief Lexer for the filter parser. + */ +/*!re2c +re2c:define:YYCTYPE = char; +re2c:yyfill:enable = 0; +re2c:flags:case-insensitive = 1; +re2c:eof = 0; +*/ + +#include "pch.h" +#include "FilterParser.h" +#include "FilterLexer.h" +#include + +int FilterLexer::yylex() +{ +begin: + /*!re2c + [ \t\r\n]+ { goto begin; } + "AND" { return TK_AND; } + "OR" { return TK_OR; } + "NOT" { return TK_NOT; } + "TRUE" { yylval.boolean = true; return TK_TRUE_LITERAL; } + "FALSE" { yylval.boolean = false; return TK_FALSE_LITERAL; } + "CONTAINS" { return TK_CONTAINS; } + "RECONTAINS" { return TK_RECONTAINS; } + "MATCHES" { return TK_MATCHES; } + ([0-9]+([.][0-9]+)?)("KB"|"MB"|"GB"|"TB") { + const char* p = yycursor; + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') + p++; + std::string lit(p, YYCURSOR - p); + yylval.string = DupString(lit.c_str()); + return TK_SIZE_LITERAL; + } + ([0-9]+([.][0-9]+)?)("weeks"|"week"|"w"|"days"|"day"|"d"|"hours"|"hour"|"hr"|"h"|"minutes"|"minute"|"min"|"m"|"seconds"|"second"|"sec"|"s"|"milliseconds"|"millisecond"|"msec"|"ms") { + const char* p = yycursor; + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') + p++; + std::string lit(p, YYCURSOR - p); + yylval.string = DupString(lit.c_str()); + return TK_DURATION_LITERAL; + } + "d\"" { + std::string str = UnescapeQuotes(YYCURSOR); + if (*(YYCURSOR - 1) != '"') + return LEXER_ERR_UNTERMINATED_STRING; + yylval.string = DupString(str.c_str()); + return TK_DATETIME_LITERAL; + } + "v\"" { + std::string str = UnescapeQuotes(YYCURSOR); + if (*(YYCURSOR - 1) != '"') + return LEXER_ERR_UNTERMINATED_STRING; + yylval.string = DupString(str.c_str()); + return TK_VERSION_LITERAL; + } + [0-9]+ { + yylval.integer = std::stoi(std::string((const char*)yycursor, YYCURSOR - yycursor)); + return TK_INTEGER_LITERAL; + } + [a-zA-Z_][a-zA-Z0-9_]* { + const char* p = yycursor; + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') + p++; + std::string tmp = std::string(p, YYCURSOR - p); + yylval.string = DupString(tmp.c_str()); + return TK_IDENTIFIER; + } + "\"" { + std::string str = UnescapeQuotes(YYCURSOR); + if (*(YYCURSOR - 1) != '"') + return LEXER_ERR_UNTERMINATED_STRING; + yylval.string = DupString(str.c_str()); + return TK_STRING_LITERAL; + } + "==" { return TK_EQ; } + "=" { return TK_EQ; } + "!=" { return TK_NE; } + "<" { return TK_LT; } + "<=" { return TK_LE; } + ">" { return TK_GT; } + ">=" { return TK_GE; } + "(" { return TK_LPAREN; } + ")" { return TK_RPAREN; } + "+" { return TK_PLUS; } + "-" { return TK_MINUS; } + "*" { return TK_STAR; } + "/" { return TK_SLASH; } + "%" { return TK_MOD; } + $ { return 0; } + "," { return TK_COMMA; } + . { return LEXER_ERR_UNKNOWN_CHAR; } + */ +} + +std::string FilterLexer::UnescapeQuotes(char*& str) +{ + std::string result; + while (*str != '\0') + { + if (*str == '"') + { + if (*(str + 1) == '"') + { + result += '"'; + str += 2; + } + else + { + str++; + break; + } + } + else + { + result += *str++; + } + } + return result; +} + +const char* FilterLexer::DupString(const char* str) +{ + char* newStr = _strdup(str); + strings.push_back(newStr); + return newStr; +} + +void FilterLexer::FreeStrings() +{ + for (auto str : strings) + free(str); + strings.clear(); +} + diff --git a/Src/FilterEngine/FilterParser.c b/Src/FilterEngine/FilterParser.c new file mode 100644 index 00000000000..d2dad8a73ec --- /dev/null +++ b/Src/FilterEngine/FilterParser.c @@ -0,0 +1,1691 @@ +/* This file is automatically generated by Lemon from input grammar +** source file "E:\dev\winmerge\Src\FilterEngine\FilterParser.y". +*/ +/* +** 2000-05-29 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** Driver template for the LEMON parser generator. +** +** The "lemon" program processes an LALR(1) input grammar file, then uses +** this template to construct a parser. The "lemon" program inserts text +** at each "%%" line. Also, any "P-a-r-s-e" identifer prefix (without the +** interstitial "-" characters) contained in this template is changed into +** the value of the %name directive from the grammar. Otherwise, the content +** of this template is copied straight through into the generate parser +** source file. +** +** The following is the concatenation of all %include directives from the +** input grammar file: +*/ +/************ Begin %include sections from the grammar ************************/ +#line 19 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterParser.y" + +#include "FilterLexer.h" +#include "FilterExpressionNodes.h" +#include "FilterExpression.h" +#include +#line 36 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterParser.c" +/**************** End of %include directives **********************************/ +/* These constants specify the various numeric values for terminal symbols. +***************** Begin token definitions *************************************/ +#ifndef TK_AND +#define TK_AND 1 +#define TK_OR 2 +#define TK_NOT 3 +#define TK_TRUE_LITERAL 4 +#define TK_FALSE_LITERAL 5 +#define TK_INTEGER_LITERAL 6 +#define TK_STRING_LITERAL 7 +#define TK_SIZE_LITERAL 8 +#define TK_DATETIME_LITERAL 9 +#define TK_DURATION_LITERAL 10 +#define TK_VERSION_LITERAL 11 +#define TK_IDENTIFIER 12 +#define TK_EQ 13 +#define TK_NE 14 +#define TK_LT 15 +#define TK_LE 16 +#define TK_GT 17 +#define TK_GE 18 +#define TK_CONTAINS 19 +#define TK_RECONTAINS 20 +#define TK_MATCHES 21 +#define TK_LPAREN 22 +#define TK_RPAREN 23 +#define TK_PLUS 24 +#define TK_MINUS 25 +#define TK_STAR 26 +#define TK_SLASH 27 +#define TK_MOD 28 +#define TK_COMMA 29 +#endif +/**************** End token definitions ***************************************/ + +/* The next sections is a series of control #defines. +** various aspects of the generated parser. +** YYCODETYPE is the data type used to store the integer codes +** that represent terminal and non-terminal symbols. +** "unsigned char" is used if there are fewer than +** 256 symbols. Larger types otherwise. +** YYNOCODE is a number of type YYCODETYPE that is not used for +** any terminal or nonterminal symbol. +** YYFALLBACK If defined, this indicates that one or more tokens +** (also known as: "terminal symbols") have fall-back +** values which should be used if the original symbol +** would not parse. This permits keywords to sometimes +** be used as identifiers, for example. +** YYACTIONTYPE is the data type used for "action codes" - numbers +** that indicate what to do in response to the next +** token. +** ParseTOKENTYPE is the data type used for minor type for terminal +** symbols. Background: A "minor type" is a semantic +** value associated with a terminal or non-terminal +** symbols. For example, for an "ID" terminal symbol, +** the minor type might be the name of the identifier. +** Each non-terminal can have a different minor type. +** Terminal symbols all have the same minor type, though. +** This macros defines the minor type for terminal +** symbols. +** YYMINORTYPE is the data type used for all minor types. +** This is typically a union of many types, one of +** which is ParseTOKENTYPE. The entry in the union +** for terminal symbols is called "yy0". +** YYSTACKDEPTH is the maximum depth of the parser's stack. If +** zero the stack is dynamically sized using realloc() +** ParseARG_SDECL A static variable declaration for the %extra_argument +** ParseARG_PDECL A parameter declaration for the %extra_argument +** ParseARG_PARAM Code to pass %extra_argument as a subroutine parameter +** ParseARG_STORE Code to store %extra_argument into yypParser +** ParseARG_FETCH Code to extract %extra_argument from yypParser +** ParseCTX_* As ParseARG_ except for %extra_context +** YYREALLOC Name of the realloc() function to use +** YYFREE Name of the free() function to use +** YYDYNSTACK True if stack space should be extended on heap +** YYERRORSYMBOL is the code number of the error symbol. If not +** defined, then do no error processing. +** YYNSTATE the combined number of states. +** YYNRULE the number of rules in the grammar +** YYNTOKEN Number of terminal symbols +** YY_MAX_SHIFT Maximum value for shift actions +** YY_MIN_SHIFTREDUCE Minimum value for shift-reduce actions +** YY_MAX_SHIFTREDUCE Maximum value for shift-reduce actions +** YY_ERROR_ACTION The yy_action[] code for syntax error +** YY_ACCEPT_ACTION The yy_action[] code for accept +** YY_NO_ACTION The yy_action[] code for no-op +** YY_MIN_REDUCE Minimum value for reduce actions +** YY_MAX_REDUCE Maximum value for reduce actions +** YY_MIN_DSTRCTR Minimum symbol value that has a destructor +** YY_MAX_DSTRCTR Maximum symbol value that has a destructor +*/ +#ifndef INTERFACE +# define INTERFACE 1 +#endif +/************* Begin control #defines *****************************************/ +#define YYCODETYPE unsigned char +#define YYNOCODE 40 +#define YYACTIONTYPE unsigned char +#define ParseTOKENTYPE YYSTYPE +typedef union { + int yyinit; + ParseTOKENTYPE yy0; +} YYMINORTYPE; +#ifndef YYSTACKDEPTH +#define YYSTACKDEPTH 100 +#endif +#define ParseARG_SDECL FilterExpression* pCtx ; +#define ParseARG_PDECL , FilterExpression* pCtx +#define ParseARG_PARAM ,pCtx +#define ParseARG_FETCH FilterExpression* pCtx =yypParser->pCtx ; +#define ParseARG_STORE yypParser->pCtx =pCtx ; +#define YYREALLOC realloc +#define YYFREE free +#define YYDYNSTACK 0 +#define ParseCTX_SDECL +#define ParseCTX_PDECL +#define ParseCTX_PARAM +#define ParseCTX_FETCH +#define ParseCTX_STORE +#define YYNSTATE 41 +#define YYNRULE 40 +#define YYNRULE_WITH_ACTION 33 +#define YYNTOKEN 30 +#define YY_MAX_SHIFT 40 +#define YY_MIN_SHIFTREDUCE 64 +#define YY_MAX_SHIFTREDUCE 103 +#define YY_ERROR_ACTION 104 +#define YY_ACCEPT_ACTION 105 +#define YY_NO_ACTION 106 +#define YY_MIN_REDUCE 107 +#define YY_MAX_REDUCE 146 +#define YY_MIN_DSTRCTR 0 +#define YY_MAX_DSTRCTR 39 +/************* End control #defines *******************************************/ +#define YY_NLOOKAHEAD ((int)(sizeof(yy_lookahead)/sizeof(yy_lookahead[0]))) + +/* Define the yytestcase() macro to be a no-op if is not already defined +** otherwise. +** +** Applications can choose to define yytestcase() in the %include section +** to a macro that can assist in verifying code coverage. For production +** code the yytestcase() macro should be turned off. But it is useful +** for testing. +*/ +#ifndef yytestcase +# define yytestcase(X) +#endif + +/* Macro to determine if stack space has the ability to grow using +** heap memory. +*/ +#if YYSTACKDEPTH<=0 || YYDYNSTACK +# define YYGROWABLESTACK 1 +#else +# define YYGROWABLESTACK 0 +#endif + +/* Guarantee a minimum number of initial stack slots. +*/ +#if YYSTACKDEPTH<=0 +# undef YYSTACKDEPTH +# define YYSTACKDEPTH 2 /* Need a minimum stack size */ +#endif + + +/* Next are the tables used to determine what action to take based on the +** current state and lookahead token. These tables are used to implement +** functions that take a state number and lookahead value and return an +** action integer. +** +** Suppose the action integer is N. Then the action is determined as +** follows +** +** 0 <= N <= YY_MAX_SHIFT Shift N. That is, push the lookahead +** token onto the stack and goto state N. +** +** N between YY_MIN_SHIFTREDUCE Shift to an arbitrary state then +** and YY_MAX_SHIFTREDUCE reduce by rule N-YY_MIN_SHIFTREDUCE. +** +** N == YY_ERROR_ACTION A syntax error has occurred. +** +** N == YY_ACCEPT_ACTION The parser accepts its input. +** +** N == YY_NO_ACTION No such action. Denotes unused +** slots in the yy_action[] table. +** +** N between YY_MIN_REDUCE Reduce by rule N-YY_MIN_REDUCE +** and YY_MAX_REDUCE +** +** The action table is constructed as a single large table named yy_action[]. +** Given state S and lookahead X, the action is computed as either: +** +** (A) N = yy_action[ yy_shift_ofst[S] + X ] +** (B) N = yy_default[S] +** +** The (A) formula is preferred. The B formula is used instead if +** yy_lookahead[yy_shift_ofst[S]+X] is not equal to X. +** +** The formulas above are for computing the action when the lookahead is +** a terminal symbol. If the lookahead is a non-terminal (as occurs after +** a reduce action) then the yy_reduce_ofst[] array is used in place of +** the yy_shift_ofst[] array. +** +** The following are the tables generated in this section: +** +** yy_action[] A single table containing all actions. +** yy_lookahead[] A table containing the lookahead for each entry in +** yy_action. Used to detect hash collisions. +** yy_shift_ofst[] For each state, the offset into yy_action for +** shifting terminals. +** yy_reduce_ofst[] For each state, the offset into yy_action for +** shifting non-terminals after a reduce. +** yy_default[] Default action for each state. +** +*********** Begin parsing tables **********************************************/ +#define YY_ACTTAB_COUNT (177) +static const YYACTIONTYPE yy_action[] = { + /* 0 */ 38, 37, 37, 37, 22, 22, 138, 22, 34, 23, + /* 10 */ 23, 6, 23, 20, 14, 13, 12, 11, 10, 9, + /* 20 */ 8, 7, 4, 1, 19, 18, 17, 16, 15, 5, + /* 30 */ 83, 84, 85, 86, 87, 88, 89, 90, 39, 105, + /* 40 */ 35, 37, 37, 37, 22, 22, 106, 22, 2, 91, + /* 50 */ 94, 21, 5, 83, 84, 85, 86, 87, 88, 89, + /* 60 */ 90, 39, 83, 84, 85, 86, 87, 88, 89, 90, + /* 70 */ 39, 2, 106, 106, 21, 40, 40, 40, 22, 22, + /* 80 */ 2, 22, 106, 21, 38, 37, 37, 37, 22, 22, + /* 90 */ 36, 22, 38, 37, 37, 37, 22, 22, 139, 22, + /* 100 */ 110, 110, 22, 22, 125, 22, 125, 106, 109, 109, + /* 110 */ 22, 22, 106, 22, 19, 18, 17, 16, 15, 24, + /* 120 */ 24, 106, 24, 25, 25, 106, 25, 26, 26, 106, + /* 130 */ 26, 27, 27, 106, 27, 28, 28, 106, 28, 29, + /* 140 */ 29, 107, 29, 4, 30, 30, 106, 30, 106, 124, + /* 150 */ 124, 92, 124, 123, 123, 106, 123, 3, 122, 122, + /* 160 */ 106, 122, 32, 32, 106, 32, 33, 33, 106, 33, + /* 170 */ 31, 31, 106, 31, 17, 16, 15, +}; +static const YYCODETYPE yy_lookahead[] = { + /* 0 */ 31, 32, 33, 34, 35, 36, 37, 38, 39, 35, + /* 10 */ 36, 1, 38, 13, 14, 15, 16, 17, 18, 19, + /* 20 */ 20, 21, 2, 22, 24, 25, 26, 27, 28, 3, + /* 30 */ 4, 5, 6, 7, 8, 9, 10, 11, 12, 30, + /* 40 */ 31, 32, 33, 34, 35, 36, 40, 38, 22, 23, + /* 50 */ 23, 25, 3, 4, 5, 6, 7, 8, 9, 10, + /* 60 */ 11, 12, 4, 5, 6, 7, 8, 9, 10, 11, + /* 70 */ 12, 22, 40, 40, 25, 32, 33, 34, 35, 36, + /* 80 */ 22, 38, 40, 25, 31, 32, 33, 34, 35, 36, + /* 90 */ 37, 38, 31, 32, 33, 34, 35, 36, 37, 38, + /* 100 */ 33, 34, 35, 36, 36, 38, 38, 40, 33, 34, + /* 110 */ 35, 36, 40, 38, 24, 25, 26, 27, 28, 35, + /* 120 */ 36, 40, 38, 35, 36, 40, 38, 35, 36, 40, + /* 130 */ 38, 35, 36, 40, 38, 35, 36, 40, 38, 35, + /* 140 */ 36, 0, 38, 2, 35, 36, 40, 38, 40, 35, + /* 150 */ 36, 23, 38, 35, 36, 40, 38, 29, 35, 36, + /* 160 */ 40, 38, 35, 36, 40, 38, 35, 36, 40, 38, + /* 170 */ 35, 36, 40, 38, 26, 27, 28, 40, 40, 40, + /* 180 */ 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, + /* 190 */ 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, + /* 200 */ 40, 40, 40, 40, 40, 30, 30, +}; +#define YY_SHIFT_COUNT (40) +#define YY_SHIFT_MIN (0) +#define YY_SHIFT_MAX (148) +static const unsigned char yy_shift_ofst[] = { + /* 0 */ 49, 26, 49, 49, 49, 49, 49, 58, 58, 58, + /* 10 */ 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + /* 20 */ 58, 58, 0, 90, 90, 90, 90, 90, 90, 90, + /* 30 */ 90, 90, 148, 148, 128, 141, 27, 10, 20, 1, + /* 40 */ 10, +}; +#define YY_REDUCE_COUNT (21) +#define YY_REDUCE_MIN (-31) +#define YY_REDUCE_MAX (135) +static const short yy_reduce_ofst[] = { + /* 0 */ 9, -31, 53, 61, 43, 67, 75, -26, 84, 88, + /* 10 */ 92, 96, 100, 104, 109, 114, 118, 123, 127, 131, + /* 20 */ 135, 68, +}; +static const YYACTIONTYPE yy_default[] = { + /* 0 */ 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, + /* 10 */ 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, + /* 20 */ 104, 104, 143, 119, 118, 117, 116, 115, 114, 113, + /* 30 */ 112, 111, 121, 120, 104, 104, 104, 140, 145, 136, + /* 40 */ 108, +}; +/********** End of lemon-generated parsing tables *****************************/ + +/* The next table maps tokens (terminal symbols) into fallback tokens. +** If a construct like the following: +** +** %fallback ID X Y Z. +** +** appears in the grammar, then ID becomes a fallback token for X, Y, +** and Z. Whenever one of the tokens X, Y, or Z is input to the parser +** but it does not parse, the type of the token is changed to ID and +** the parse is retried before an error is thrown. +** +** This feature can be used, for example, to cause some keywords in a language +** to revert to identifiers if they keyword does not apply in the context where +** it appears. +*/ +#ifdef YYFALLBACK +static const YYCODETYPE yyFallback[] = { +}; +#endif /* YYFALLBACK */ + +/* The following structure represents a single element of the +** parser's stack. Information stored includes: +** +** + The state number for the parser at this level of the stack. +** +** + The value of the token stored at this level of the stack. +** (In other words, the "major" token.) +** +** + The semantic value stored at this level of the stack. This is +** the information used by the action routines in the grammar. +** It is sometimes called the "minor" token. +** +** After the "shift" half of a SHIFTREDUCE action, the stateno field +** actually contains the reduce action for the second half of the +** SHIFTREDUCE. +*/ +struct yyStackEntry { + YYACTIONTYPE stateno; /* The state-number, or reduce action in SHIFTREDUCE */ + YYCODETYPE major; /* The major token value. This is the code + ** number for the token at this stack level */ + YYMINORTYPE minor; /* The user-supplied minor token value. This + ** is the value of the token */ +}; +typedef struct yyStackEntry yyStackEntry; + +/* The state of the parser is completely contained in an instance of +** the following structure */ +struct yyParser { + yyStackEntry *yytos; /* Pointer to top element of the stack */ +#ifdef YYTRACKMAXSTACKDEPTH + int yyhwm; /* High-water mark of the stack */ +#endif +#ifndef YYNOERRORRECOVERY + int yyerrcnt; /* Shifts left before out of the error */ +#endif + ParseARG_SDECL /* A place to hold %extra_argument */ + ParseCTX_SDECL /* A place to hold %extra_context */ + yyStackEntry *yystackEnd; /* Last entry in the stack */ + yyStackEntry *yystack; /* The parser stack */ + yyStackEntry yystk0[YYSTACKDEPTH]; /* Initial stack space */ +}; +typedef struct yyParser yyParser; + +#include +#ifndef NDEBUG +#include +static FILE *yyTraceFILE = 0; +static char *yyTracePrompt = 0; +#endif /* NDEBUG */ + +#ifndef NDEBUG +/* +** Turn parser tracing on by giving a stream to which to write the trace +** and a prompt to preface each trace message. Tracing is turned off +** by making either argument NULL +** +** Inputs: +**
    +**
  • A FILE* to which trace output should be written. +** If NULL, then tracing is turned off. +**
  • A prefix string written at the beginning of every +** line of trace output. If NULL, then tracing is +** turned off. +**
+** +** Outputs: +** None. +*/ +void ParseTrace(FILE *TraceFILE, char *zTracePrompt){ + yyTraceFILE = TraceFILE; + yyTracePrompt = zTracePrompt; + if( yyTraceFILE==0 ) yyTracePrompt = 0; + else if( yyTracePrompt==0 ) yyTraceFILE = 0; +} +#endif /* NDEBUG */ + +#if defined(YYCOVERAGE) || !defined(NDEBUG) +/* For tracing shifts, the names of all terminals and nonterminals +** are required. The following table supplies these names */ +static const char *const yyTokenName[] = { + /* 0 */ "$", + /* 1 */ "AND", + /* 2 */ "OR", + /* 3 */ "NOT", + /* 4 */ "TRUE_LITERAL", + /* 5 */ "FALSE_LITERAL", + /* 6 */ "INTEGER_LITERAL", + /* 7 */ "STRING_LITERAL", + /* 8 */ "SIZE_LITERAL", + /* 9 */ "DATETIME_LITERAL", + /* 10 */ "DURATION_LITERAL", + /* 11 */ "VERSION_LITERAL", + /* 12 */ "IDENTIFIER", + /* 13 */ "EQ", + /* 14 */ "NE", + /* 15 */ "LT", + /* 16 */ "LE", + /* 17 */ "GT", + /* 18 */ "GE", + /* 19 */ "CONTAINS", + /* 20 */ "RECONTAINS", + /* 21 */ "MATCHES", + /* 22 */ "LPAREN", + /* 23 */ "RPAREN", + /* 24 */ "PLUS", + /* 25 */ "MINUS", + /* 26 */ "STAR", + /* 27 */ "SLASH", + /* 28 */ "MOD", + /* 29 */ "COMMA", + /* 30 */ "filter_expr", + /* 31 */ "or_expr", + /* 32 */ "and_expr", + /* 33 */ "not_expr", + /* 34 */ "cmp_expr", + /* 35 */ "arithmetic", + /* 36 */ "unary", + /* 37 */ "expr", + /* 38 */ "term", + /* 39 */ "expr_list", +}; +#endif /* defined(YYCOVERAGE) || !defined(NDEBUG) */ + +#ifndef NDEBUG +/* For tracing reduce actions, the names of all rules are required. +*/ +static const char *const yyRuleName[] = { + /* 0 */ "filter_expr ::= or_expr", + /* 1 */ "or_expr ::= or_expr OR and_expr", + /* 2 */ "and_expr ::= and_expr AND not_expr", + /* 3 */ "not_expr ::= NOT not_expr", + /* 4 */ "cmp_expr ::= arithmetic EQ arithmetic", + /* 5 */ "cmp_expr ::= arithmetic NE arithmetic", + /* 6 */ "cmp_expr ::= arithmetic LT arithmetic", + /* 7 */ "cmp_expr ::= arithmetic LE arithmetic", + /* 8 */ "cmp_expr ::= arithmetic GT arithmetic", + /* 9 */ "cmp_expr ::= arithmetic GE arithmetic", + /* 10 */ "cmp_expr ::= arithmetic CONTAINS arithmetic", + /* 11 */ "cmp_expr ::= arithmetic RECONTAINS arithmetic", + /* 12 */ "cmp_expr ::= arithmetic MATCHES arithmetic", + /* 13 */ "arithmetic ::= arithmetic PLUS arithmetic", + /* 14 */ "arithmetic ::= arithmetic MINUS arithmetic", + /* 15 */ "arithmetic ::= arithmetic STAR arithmetic", + /* 16 */ "arithmetic ::= arithmetic SLASH arithmetic", + /* 17 */ "arithmetic ::= arithmetic MOD arithmetic", + /* 18 */ "unary ::= MINUS unary", + /* 19 */ "term ::= TRUE_LITERAL", + /* 20 */ "term ::= FALSE_LITERAL", + /* 21 */ "term ::= INTEGER_LITERAL", + /* 22 */ "term ::= STRING_LITERAL", + /* 23 */ "term ::= SIZE_LITERAL", + /* 24 */ "term ::= DATETIME_LITERAL", + /* 25 */ "term ::= DURATION_LITERAL", + /* 26 */ "term ::= VERSION_LITERAL", + /* 27 */ "term ::= IDENTIFIER LPAREN RPAREN", + /* 28 */ "term ::= IDENTIFIER LPAREN expr_list RPAREN", + /* 29 */ "term ::= IDENTIFIER", + /* 30 */ "term ::= LPAREN expr RPAREN", + /* 31 */ "expr_list ::= expr", + /* 32 */ "expr_list ::= expr_list COMMA expr", + /* 33 */ "or_expr ::= and_expr", + /* 34 */ "and_expr ::= not_expr", + /* 35 */ "not_expr ::= cmp_expr", + /* 36 */ "cmp_expr ::= arithmetic", + /* 37 */ "arithmetic ::= unary", + /* 38 */ "expr ::= or_expr", + /* 39 */ "unary ::= term", +}; +#endif /* NDEBUG */ + + +#if YYGROWABLESTACK +/* +** Try to increase the size of the parser stack. Return the number +** of errors. Return 0 on success. +*/ +static int yyGrowStack(yyParser *p){ + int oldSize = 1 + (int)(p->yystackEnd - p->yystack); + int newSize; + int idx; + yyStackEntry *pNew; + + newSize = oldSize*2 + 100; + idx = (int)(p->yytos - p->yystack); + if( p->yystack==p->yystk0 ){ + pNew = YYREALLOC(0, newSize*sizeof(pNew[0])); + if( pNew==0 ) return 1; + memcpy(pNew, p->yystack, oldSize*sizeof(pNew[0])); + }else{ + pNew = YYREALLOC(p->yystack, newSize*sizeof(pNew[0])); + if( pNew==0 ) return 1; + } + p->yystack = pNew; + p->yytos = &p->yystack[idx]; +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sStack grows from %d to %d entries.\n", + yyTracePrompt, oldSize, newSize); + } +#endif + p->yystackEnd = &p->yystack[newSize-1]; + return 0; +} +#endif /* YYGROWABLESTACK */ + +#if !YYGROWABLESTACK +/* For builds that do no have a growable stack, yyGrowStack always +** returns an error. +*/ +# define yyGrowStack(X) 1 +#endif + +/* Datatype of the argument to the memory allocated passed as the +** second argument to ParseAlloc() below. This can be changed by +** putting an appropriate #define in the %include section of the input +** grammar. +*/ +#ifndef YYMALLOCARGTYPE +# define YYMALLOCARGTYPE size_t +#endif + +/* Initialize a new parser that has already been allocated. +*/ +void ParseInit(void *yypRawParser ParseCTX_PDECL){ + yyParser *yypParser = (yyParser*)yypRawParser; + ParseCTX_STORE +#ifdef YYTRACKMAXSTACKDEPTH + yypParser->yyhwm = 0; +#endif + yypParser->yystack = yypParser->yystk0; + yypParser->yystackEnd = &yypParser->yystack[YYSTACKDEPTH-1]; +#ifndef YYNOERRORRECOVERY + yypParser->yyerrcnt = -1; +#endif + yypParser->yytos = yypParser->yystack; + yypParser->yystack[0].stateno = 0; + yypParser->yystack[0].major = 0; +} + +#ifndef Parse_ENGINEALWAYSONSTACK +/* +** This function allocates a new parser. +** The only argument is a pointer to a function which works like +** malloc. +** +** Inputs: +** A pointer to the function used to allocate memory. +** +** Outputs: +** A pointer to a parser. This pointer is used in subsequent calls +** to Parse and ParseFree. +*/ +void *ParseAlloc(void *(*mallocProc)(YYMALLOCARGTYPE) ParseCTX_PDECL){ + yyParser *yypParser; + yypParser = (yyParser*)(*mallocProc)( (YYMALLOCARGTYPE)sizeof(yyParser) ); + if( yypParser ){ + ParseCTX_STORE + ParseInit(yypParser ParseCTX_PARAM); + } + return (void*)yypParser; +} +#endif /* Parse_ENGINEALWAYSONSTACK */ + + +/* The following function deletes the "minor type" or semantic value +** associated with a symbol. The symbol can be either a terminal +** or nonterminal. "yymajor" is the symbol code, and "yypminor" is +** a pointer to the value to be deleted. The code used to do the +** deletions is derived from the %destructor and/or %token_destructor +** directives of the input grammar. +*/ +static void yy_destructor( + yyParser *yypParser, /* The parser */ + YYCODETYPE yymajor, /* Type code for object to destroy */ + YYMINORTYPE *yypminor /* The object to be destroyed */ +){ + ParseARG_FETCH + ParseCTX_FETCH + switch( yymajor ){ + /* Here is inserted the actions which take place when a + ** terminal or non-terminal is destroyed. This can happen + ** when the symbol is popped from the stack during a + ** reduce or during error processing or when a parser is + ** being destroyed before it is finished parsing. + ** + ** Note: during a reduce, the only symbols destroyed are those + ** which appear on the RHS of the rule, but which are *not* used + ** inside the C code. + */ +/********* Begin destructor definitions ***************************************/ + /* Default NON-TERMINAL Destructor */ + case 30: /* filter_expr */ + case 31: /* or_expr */ + case 32: /* and_expr */ + case 33: /* not_expr */ + case 34: /* cmp_expr */ + case 35: /* arithmetic */ + case 36: /* unary */ + case 37: /* expr */ + case 38: /* term */ + case 39: /* expr_list */ +{ +#line 32 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterParser.y" + + YYSTYPEDestructor((yypminor->yy0)); + +#line 648 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterParser.c" +} + break; +/********* End destructor definitions *****************************************/ + default: break; /* If no destructor action specified: do nothing */ + } +} + +/* +** Pop the parser's stack once. +** +** If there is a destructor routine associated with the token which +** is popped from the stack, then call it. +*/ +static void yy_pop_parser_stack(yyParser *pParser){ + yyStackEntry *yytos; + assert( pParser->yytos!=0 ); + assert( pParser->yytos > pParser->yystack ); + yytos = pParser->yytos--; +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sPopping %s\n", + yyTracePrompt, + yyTokenName[yytos->major]); + } +#endif + yy_destructor(pParser, yytos->major, &yytos->minor); +} + +/* +** Clear all secondary memory allocations from the parser +*/ +void ParseFinalize(void *p){ + yyParser *pParser = (yyParser*)p; + + /* In-lined version of calling yy_pop_parser_stack() for each + ** element left in the stack */ + yyStackEntry *yytos = pParser->yytos; + while( yytos>pParser->yystack ){ +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sPopping %s\n", + yyTracePrompt, + yyTokenName[yytos->major]); + } +#endif + if( yytos->major>=YY_MIN_DSTRCTR ){ + yy_destructor(pParser, yytos->major, &yytos->minor); + } + yytos--; + } + +#if YYGROWABLESTACK + if( pParser->yystack!=pParser->yystk0 ) YYFREE(pParser->yystack); +#endif +} + +#ifndef Parse_ENGINEALWAYSONSTACK +/* +** Deallocate and destroy a parser. Destructors are called for +** all stack elements before shutting the parser down. +** +** If the YYPARSEFREENEVERNULL macro exists (for example because it +** is defined in a %include section of the input grammar) then it is +** assumed that the input pointer is never NULL. +*/ +void ParseFree( + void *p, /* The parser to be deleted */ + void (*freeProc)(void*) /* Function used to reclaim memory */ +){ +#ifndef YYPARSEFREENEVERNULL + if( p==0 ) return; +#endif + ParseFinalize(p); + (*freeProc)(p); +} +#endif /* Parse_ENGINEALWAYSONSTACK */ + +/* +** Return the peak depth of the stack for a parser. +*/ +#ifdef YYTRACKMAXSTACKDEPTH +int ParseStackPeak(void *p){ + yyParser *pParser = (yyParser*)p; + return pParser->yyhwm; +} +#endif + +/* This array of booleans keeps track of the parser statement +** coverage. The element yycoverage[X][Y] is set when the parser +** is in state X and has a lookahead token Y. In a well-tested +** systems, every element of this matrix should end up being set. +*/ +#if defined(YYCOVERAGE) +static unsigned char yycoverage[YYNSTATE][YYNTOKEN]; +#endif + +/* +** Write into out a description of every state/lookahead combination that +** +** (1) has not been used by the parser, and +** (2) is not a syntax error. +** +** Return the number of missed state/lookahead combinations. +*/ +#if defined(YYCOVERAGE) +int ParseCoverage(FILE *out){ + int stateno, iLookAhead, i; + int nMissed = 0; + for(stateno=0; statenoYY_MAX_SHIFT ) return stateno; + assert( stateno <= YY_SHIFT_COUNT ); +#if defined(YYCOVERAGE) + yycoverage[stateno][iLookAhead] = 1; +#endif + do{ + i = yy_shift_ofst[stateno]; + assert( i>=0 ); + assert( i<=YY_ACTTAB_COUNT ); + assert( i+YYNTOKEN<=(int)YY_NLOOKAHEAD ); + assert( iLookAhead!=YYNOCODE ); + assert( iLookAhead < YYNTOKEN ); + i += iLookAhead; + assert( i<(int)YY_NLOOKAHEAD ); + if( yy_lookahead[i]!=iLookAhead ){ +#ifdef YYFALLBACK + YYCODETYPE iFallback; /* Fallback token */ + assert( iLookAhead %s\n", + yyTracePrompt, yyTokenName[iLookAhead], yyTokenName[iFallback]); + } +#endif + assert( yyFallback[iFallback]==0 ); /* Fallback loop must terminate */ + iLookAhead = iFallback; + continue; + } +#endif +#ifdef YYWILDCARD + { + int j = i - iLookAhead + YYWILDCARD; + assert( j<(int)(sizeof(yy_lookahead)/sizeof(yy_lookahead[0])) ); + if( yy_lookahead[j]==YYWILDCARD && iLookAhead>0 ){ +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE, "%sWILDCARD %s => %s\n", + yyTracePrompt, yyTokenName[iLookAhead], + yyTokenName[YYWILDCARD]); + } +#endif /* NDEBUG */ + return yy_action[j]; + } + } +#endif /* YYWILDCARD */ + return yy_default[stateno]; + }else{ + assert( i>=0 && i<(int)(sizeof(yy_action)/sizeof(yy_action[0])) ); + return yy_action[i]; + } + }while(1); +} + +/* +** Find the appropriate action for a parser given the non-terminal +** look-ahead token iLookAhead. +*/ +static YYACTIONTYPE yy_find_reduce_action( + YYACTIONTYPE stateno, /* Current state number */ + YYCODETYPE iLookAhead /* The look-ahead token */ +){ + int i; +#ifdef YYERRORSYMBOL + if( stateno>YY_REDUCE_COUNT ){ + return yy_default[stateno]; + } +#else + assert( stateno<=YY_REDUCE_COUNT ); +#endif + i = yy_reduce_ofst[stateno]; + assert( iLookAhead!=YYNOCODE ); + i += iLookAhead; +#ifdef YYERRORSYMBOL + if( i<0 || i>=YY_ACTTAB_COUNT || yy_lookahead[i]!=iLookAhead ){ + return yy_default[stateno]; + } +#else + assert( i>=0 && iyytos>yypParser->yystack ) yy_pop_parser_stack(yypParser); + /* Here code is inserted which will execute if the parser + ** stack every overflows */ +/******** Begin %stack_overflow code ******************************************/ +/******** End %stack_overflow code ********************************************/ + ParseARG_STORE /* Suppress warning about unused %extra_argument var */ + ParseCTX_STORE +} + +/* +** Print tracing information for a SHIFT action +*/ +#ifndef NDEBUG +static void yyTraceShift(yyParser *yypParser, int yyNewState, const char *zTag){ + if( yyTraceFILE ){ + if( yyNewStateyytos->major], + yyNewState); + }else{ + fprintf(yyTraceFILE,"%s%s '%s', pending reduce %d\n", + yyTracePrompt, zTag, yyTokenName[yypParser->yytos->major], + yyNewState - YY_MIN_REDUCE); + } + } +} +#else +# define yyTraceShift(X,Y,Z) +#endif + +/* +** Perform a shift action. +*/ +static void yy_shift( + yyParser *yypParser, /* The parser to be shifted */ + YYACTIONTYPE yyNewState, /* The new state to shift in */ + YYCODETYPE yyMajor, /* The major token to shift in */ + ParseTOKENTYPE yyMinor /* The minor token to shift in */ +){ + yyStackEntry *yytos; + yypParser->yytos++; +#ifdef YYTRACKMAXSTACKDEPTH + if( (int)(yypParser->yytos - yypParser->yystack)>yypParser->yyhwm ){ + yypParser->yyhwm++; + assert( yypParser->yyhwm == (int)(yypParser->yytos - yypParser->yystack) ); + } +#endif + yytos = yypParser->yytos; + if( yytos>yypParser->yystackEnd ){ + if( yyGrowStack(yypParser) ){ + yypParser->yytos--; + yyStackOverflow(yypParser); + return; + } + yytos = yypParser->yytos; + assert( yytos <= yypParser->yystackEnd ); + } + if( yyNewState > YY_MAX_SHIFT ){ + yyNewState += YY_MIN_REDUCE - YY_MIN_SHIFTREDUCE; + } + yytos->stateno = yyNewState; + yytos->major = yyMajor; + yytos->minor.yy0 = yyMinor; + yyTraceShift(yypParser, yyNewState, "Shift"); +} + +/* For rule J, yyRuleInfoLhs[J] contains the symbol on the left-hand side +** of that rule */ +static const YYCODETYPE yyRuleInfoLhs[] = { + 30, /* (0) filter_expr ::= or_expr */ + 31, /* (1) or_expr ::= or_expr OR and_expr */ + 32, /* (2) and_expr ::= and_expr AND not_expr */ + 33, /* (3) not_expr ::= NOT not_expr */ + 34, /* (4) cmp_expr ::= arithmetic EQ arithmetic */ + 34, /* (5) cmp_expr ::= arithmetic NE arithmetic */ + 34, /* (6) cmp_expr ::= arithmetic LT arithmetic */ + 34, /* (7) cmp_expr ::= arithmetic LE arithmetic */ + 34, /* (8) cmp_expr ::= arithmetic GT arithmetic */ + 34, /* (9) cmp_expr ::= arithmetic GE arithmetic */ + 34, /* (10) cmp_expr ::= arithmetic CONTAINS arithmetic */ + 34, /* (11) cmp_expr ::= arithmetic RECONTAINS arithmetic */ + 34, /* (12) cmp_expr ::= arithmetic MATCHES arithmetic */ + 35, /* (13) arithmetic ::= arithmetic PLUS arithmetic */ + 35, /* (14) arithmetic ::= arithmetic MINUS arithmetic */ + 35, /* (15) arithmetic ::= arithmetic STAR arithmetic */ + 35, /* (16) arithmetic ::= arithmetic SLASH arithmetic */ + 35, /* (17) arithmetic ::= arithmetic MOD arithmetic */ + 36, /* (18) unary ::= MINUS unary */ + 38, /* (19) term ::= TRUE_LITERAL */ + 38, /* (20) term ::= FALSE_LITERAL */ + 38, /* (21) term ::= INTEGER_LITERAL */ + 38, /* (22) term ::= STRING_LITERAL */ + 38, /* (23) term ::= SIZE_LITERAL */ + 38, /* (24) term ::= DATETIME_LITERAL */ + 38, /* (25) term ::= DURATION_LITERAL */ + 38, /* (26) term ::= VERSION_LITERAL */ + 38, /* (27) term ::= IDENTIFIER LPAREN RPAREN */ + 38, /* (28) term ::= IDENTIFIER LPAREN expr_list RPAREN */ + 38, /* (29) term ::= IDENTIFIER */ + 38, /* (30) term ::= LPAREN expr RPAREN */ + 39, /* (31) expr_list ::= expr */ + 39, /* (32) expr_list ::= expr_list COMMA expr */ + 31, /* (33) or_expr ::= and_expr */ + 32, /* (34) and_expr ::= not_expr */ + 33, /* (35) not_expr ::= cmp_expr */ + 34, /* (36) cmp_expr ::= arithmetic */ + 35, /* (37) arithmetic ::= unary */ + 37, /* (38) expr ::= or_expr */ + 36, /* (39) unary ::= term */ +}; + +/* For rule J, yyRuleInfoNRhs[J] contains the negative of the number +** of symbols on the right-hand side of that rule. */ +static const signed char yyRuleInfoNRhs[] = { + -1, /* (0) filter_expr ::= or_expr */ + -3, /* (1) or_expr ::= or_expr OR and_expr */ + -3, /* (2) and_expr ::= and_expr AND not_expr */ + -2, /* (3) not_expr ::= NOT not_expr */ + -3, /* (4) cmp_expr ::= arithmetic EQ arithmetic */ + -3, /* (5) cmp_expr ::= arithmetic NE arithmetic */ + -3, /* (6) cmp_expr ::= arithmetic LT arithmetic */ + -3, /* (7) cmp_expr ::= arithmetic LE arithmetic */ + -3, /* (8) cmp_expr ::= arithmetic GT arithmetic */ + -3, /* (9) cmp_expr ::= arithmetic GE arithmetic */ + -3, /* (10) cmp_expr ::= arithmetic CONTAINS arithmetic */ + -3, /* (11) cmp_expr ::= arithmetic RECONTAINS arithmetic */ + -3, /* (12) cmp_expr ::= arithmetic MATCHES arithmetic */ + -3, /* (13) arithmetic ::= arithmetic PLUS arithmetic */ + -3, /* (14) arithmetic ::= arithmetic MINUS arithmetic */ + -3, /* (15) arithmetic ::= arithmetic STAR arithmetic */ + -3, /* (16) arithmetic ::= arithmetic SLASH arithmetic */ + -3, /* (17) arithmetic ::= arithmetic MOD arithmetic */ + -2, /* (18) unary ::= MINUS unary */ + -1, /* (19) term ::= TRUE_LITERAL */ + -1, /* (20) term ::= FALSE_LITERAL */ + -1, /* (21) term ::= INTEGER_LITERAL */ + -1, /* (22) term ::= STRING_LITERAL */ + -1, /* (23) term ::= SIZE_LITERAL */ + -1, /* (24) term ::= DATETIME_LITERAL */ + -1, /* (25) term ::= DURATION_LITERAL */ + -1, /* (26) term ::= VERSION_LITERAL */ + -3, /* (27) term ::= IDENTIFIER LPAREN RPAREN */ + -4, /* (28) term ::= IDENTIFIER LPAREN expr_list RPAREN */ + -1, /* (29) term ::= IDENTIFIER */ + -3, /* (30) term ::= LPAREN expr RPAREN */ + -1, /* (31) expr_list ::= expr */ + -3, /* (32) expr_list ::= expr_list COMMA expr */ + -1, /* (33) or_expr ::= and_expr */ + -1, /* (34) and_expr ::= not_expr */ + -1, /* (35) not_expr ::= cmp_expr */ + -1, /* (36) cmp_expr ::= arithmetic */ + -1, /* (37) arithmetic ::= unary */ + -1, /* (38) expr ::= or_expr */ + -1, /* (39) unary ::= term */ +}; + +static void yy_accept(yyParser*); /* Forward Declaration */ + +/* +** Perform a reduce action and the shift that must immediately +** follow the reduce. +** +** The yyLookahead and yyLookaheadToken parameters provide reduce actions +** access to the lookahead token (if any). The yyLookahead will be YYNOCODE +** if the lookahead token has already been consumed. As this procedure is +** only called from one place, optimizing compilers will in-line it, which +** means that the extra parameters have no performance impact. +*/ +static YYACTIONTYPE yy_reduce( + yyParser *yypParser, /* The parser */ + unsigned int yyruleno, /* Number of the rule by which to reduce */ + int yyLookahead, /* Lookahead token, or YYNOCODE if none */ + ParseTOKENTYPE yyLookaheadToken /* Value of the lookahead token */ + ParseCTX_PDECL /* %extra_context */ +){ + int yygoto; /* The next state */ + YYACTIONTYPE yyact; /* The next action */ + yyStackEntry *yymsp; /* The top of the parser's stack */ + int yysize; /* Amount to pop the stack */ + ParseARG_FETCH + (void)yyLookahead; + (void)yyLookaheadToken; + yymsp = yypParser->yytos; + + switch( yyruleno ){ + /* Beginning here are the reduction cases. A typical example + ** follows: + ** case 0: + ** #line + ** { ... } // User supplied code + ** #line + ** break; + */ +/********** Begin reduce actions **********************************************/ + YYMINORTYPE yylhsminor; + case 0: /* filter_expr ::= or_expr */ +#line 36 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterParser.y" +{ + if (pCtx->errorCode == 0 && pCtx->optimize) + { + try + { + pCtx->rootNode.reset(yymsp[0].minor.yy0.node->Optimize()); + } + catch (Poco::RegularExpressionException& e) + { + pCtx->errorCode = FILTER_ERROR_INVALID_REGULAR_EXPRESSION; + pCtx->rootNode.reset(yymsp[0].minor.yy0.node); + pCtx->errorMessage = e.message(); + } + catch (const std::exception&) + { + pCtx->rootNode.reset(yymsp[0].minor.yy0.node); + } + } + else + { + pCtx->rootNode.reset(yymsp[0].minor.yy0.node); + } +} +#line 1099 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterParser.c" + break; + case 1: /* or_expr ::= or_expr OR and_expr */ +#line 60 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterParser.y" +{ yylhsminor.yy0 = { new OrNode(yymsp[-2].minor.yy0.node, yymsp[0].minor.yy0.node) }; } +#line 1104 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterParser.c" + yymsp[-2].minor.yy0 = yylhsminor.yy0; + break; + case 2: /* and_expr ::= and_expr AND not_expr */ +#line 63 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterParser.y" +{ yylhsminor.yy0 = { new AndNode(yymsp[-2].minor.yy0.node, yymsp[0].minor.yy0.node) }; } +#line 1110 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterParser.c" + yymsp[-2].minor.yy0 = yylhsminor.yy0; + break; + case 3: /* not_expr ::= NOT not_expr */ +#line 66 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterParser.y" +{ yymsp[-1].minor.yy0 = { new NotNode(yymsp[0].minor.yy0.node) }; } +#line 1116 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterParser.c" + break; + case 4: /* cmp_expr ::= arithmetic EQ arithmetic */ +#line 69 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterParser.y" +{ yylhsminor.yy0 = { new BinaryOpNode(yymsp[-2].minor.yy0.node, TK_EQ, yymsp[0].minor.yy0.node) }; } +#line 1121 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterParser.c" + yymsp[-2].minor.yy0 = yylhsminor.yy0; + break; + case 5: /* cmp_expr ::= arithmetic NE arithmetic */ +#line 70 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterParser.y" +{ yylhsminor.yy0 = { new BinaryOpNode(yymsp[-2].minor.yy0.node, TK_NE, yymsp[0].minor.yy0.node) }; } +#line 1127 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterParser.c" + yymsp[-2].minor.yy0 = yylhsminor.yy0; + break; + case 6: /* cmp_expr ::= arithmetic LT arithmetic */ +#line 71 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterParser.y" +{ yylhsminor.yy0 = { new BinaryOpNode(yymsp[-2].minor.yy0.node, TK_LT, yymsp[0].minor.yy0.node) }; } +#line 1133 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterParser.c" + yymsp[-2].minor.yy0 = yylhsminor.yy0; + break; + case 7: /* cmp_expr ::= arithmetic LE arithmetic */ +#line 72 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterParser.y" +{ yylhsminor.yy0 = { new BinaryOpNode(yymsp[-2].minor.yy0.node, TK_LE, yymsp[0].minor.yy0.node) }; } +#line 1139 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterParser.c" + yymsp[-2].minor.yy0 = yylhsminor.yy0; + break; + case 8: /* cmp_expr ::= arithmetic GT arithmetic */ +#line 73 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterParser.y" +{ yylhsminor.yy0 = { new BinaryOpNode(yymsp[-2].minor.yy0.node, TK_GT, yymsp[0].minor.yy0.node) }; } +#line 1145 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterParser.c" + yymsp[-2].minor.yy0 = yylhsminor.yy0; + break; + case 9: /* cmp_expr ::= arithmetic GE arithmetic */ +#line 74 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterParser.y" +{ yylhsminor.yy0 = { new BinaryOpNode(yymsp[-2].minor.yy0.node, TK_GE, yymsp[0].minor.yy0.node) }; } +#line 1151 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterParser.c" + yymsp[-2].minor.yy0 = yylhsminor.yy0; + break; + case 10: /* cmp_expr ::= arithmetic CONTAINS arithmetic */ +#line 75 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterParser.y" +{ yylhsminor.yy0 = { new BinaryOpNode(yymsp[-2].minor.yy0.node, TK_CONTAINS, yymsp[0].minor.yy0.node) }; } +#line 1157 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterParser.c" + yymsp[-2].minor.yy0 = yylhsminor.yy0; + break; + case 11: /* cmp_expr ::= arithmetic RECONTAINS arithmetic */ +#line 76 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterParser.y" +{ yylhsminor.yy0 = { new BinaryOpNode(yymsp[-2].minor.yy0.node, TK_RECONTAINS, yymsp[0].minor.yy0.node) }; } +#line 1163 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterParser.c" + yymsp[-2].minor.yy0 = yylhsminor.yy0; + break; + case 12: /* cmp_expr ::= arithmetic MATCHES arithmetic */ +#line 77 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterParser.y" +{ yylhsminor.yy0 = { new BinaryOpNode(yymsp[-2].minor.yy0.node, TK_MATCHES, yymsp[0].minor.yy0.node) }; } +#line 1169 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterParser.c" + yymsp[-2].minor.yy0 = yylhsminor.yy0; + break; + case 13: /* arithmetic ::= arithmetic PLUS arithmetic */ +#line 80 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterParser.y" +{ yylhsminor.yy0 = { new BinaryOpNode(yymsp[-2].minor.yy0.node, TK_PLUS, yymsp[0].minor.yy0.node) }; } +#line 1175 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterParser.c" + yymsp[-2].minor.yy0 = yylhsminor.yy0; + break; + case 14: /* arithmetic ::= arithmetic MINUS arithmetic */ +#line 81 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterParser.y" +{ yylhsminor.yy0 = { new BinaryOpNode(yymsp[-2].minor.yy0.node, TK_MINUS, yymsp[0].minor.yy0.node) }; } +#line 1181 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterParser.c" + yymsp[-2].minor.yy0 = yylhsminor.yy0; + break; + case 15: /* arithmetic ::= arithmetic STAR arithmetic */ +#line 82 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterParser.y" +{ yylhsminor.yy0 = { new BinaryOpNode(yymsp[-2].minor.yy0.node, TK_STAR, yymsp[0].minor.yy0.node) }; } +#line 1187 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterParser.c" + yymsp[-2].minor.yy0 = yylhsminor.yy0; + break; + case 16: /* arithmetic ::= arithmetic SLASH arithmetic */ +#line 83 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterParser.y" +{ yylhsminor.yy0 = { new BinaryOpNode(yymsp[-2].minor.yy0.node, TK_SLASH, yymsp[0].minor.yy0.node) }; } +#line 1193 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterParser.c" + yymsp[-2].minor.yy0 = yylhsminor.yy0; + break; + case 17: /* arithmetic ::= arithmetic MOD arithmetic */ +#line 84 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterParser.y" +{ yylhsminor.yy0 = { new BinaryOpNode(yymsp[-2].minor.yy0.node, TK_MOD, yymsp[0].minor.yy0.node) }; } +#line 1199 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterParser.c" + yymsp[-2].minor.yy0 = yylhsminor.yy0; + break; + case 18: /* unary ::= MINUS unary */ +#line 89 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterParser.y" +{ yymsp[-1].minor.yy0 = { new NegateNode(yymsp[0].minor.yy0.node) }; } +#line 1205 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterParser.c" + break; + case 19: /* term ::= TRUE_LITERAL */ +#line 92 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterParser.y" +{ yymsp[0].minor.yy0 = { new BoolLiteral(true) }; } +#line 1210 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterParser.c" + break; + case 20: /* term ::= FALSE_LITERAL */ +#line 93 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterParser.y" +{ yymsp[0].minor.yy0 = { new BoolLiteral(false) }; } +#line 1215 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterParser.c" + break; + case 21: /* term ::= INTEGER_LITERAL */ +#line 94 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterParser.y" +{ yylhsminor.yy0 = { new IntLiteral(yymsp[0].minor.yy0.integer) }; } +#line 1220 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterParser.c" + yymsp[0].minor.yy0 = yylhsminor.yy0; + break; + case 22: /* term ::= STRING_LITERAL */ +#line 95 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterParser.y" +{ yylhsminor.yy0 = { new StringLiteral(yymsp[0].minor.yy0.string) }; } +#line 1226 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterParser.c" + yymsp[0].minor.yy0 = yylhsminor.yy0; + break; + case 23: /* term ::= SIZE_LITERAL */ +#line 96 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterParser.y" +{ yylhsminor.yy0 = { new SizeLiteral(yymsp[0].minor.yy0.string) }; } +#line 1232 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterParser.c" + yymsp[0].minor.yy0 = yylhsminor.yy0; + break; + case 24: /* term ::= DATETIME_LITERAL */ +#line 97 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterParser.y" +{ + try + { + yylhsminor.yy0 = {}; + yylhsminor.yy0.node = new DateTimeLiteral(yymsp[0].minor.yy0.string); + } + catch (const std::exception&) + { + pCtx->errorCode = FILTER_ERROR_INVALID_LITERAL; + } +} +#line 1248 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterParser.c" + yymsp[0].minor.yy0 = yylhsminor.yy0; + break; + case 25: /* term ::= DURATION_LITERAL */ +#line 108 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterParser.y" +{ yylhsminor.yy0 = { new DurationLiteral(yymsp[0].minor.yy0.string) }; } +#line 1254 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterParser.c" + yymsp[0].minor.yy0 = yylhsminor.yy0; + break; + case 26: /* term ::= VERSION_LITERAL */ +#line 109 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterParser.y" +{ yylhsminor.yy0 = { new VersionLiteral(yymsp[0].minor.yy0.string) }; } +#line 1260 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterParser.c" + yymsp[0].minor.yy0 = yylhsminor.yy0; + break; + case 27: /* term ::= IDENTIFIER LPAREN RPAREN */ +#line 110 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterParser.y" +{ + try + { + yylhsminor.yy0 = {}; + yylhsminor.yy0.node = new FunctionNode(pCtx, yymsp[-2].minor.yy0.string, {}); + } + catch (const std::invalid_argument& e) + { + pCtx->errorCode = FILTER_ERROR_INVALID_ARGUMENT_COUNT; + pCtx->errorMessage = e.what(); + } + catch (const std::runtime_error& e) + { + pCtx->errorCode = FILTER_ERROR_UNDEFINED_IDENTIFIER; + pCtx->errorMessage = e.what(); + } +} +#line 1282 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterParser.c" + yymsp[-2].minor.yy0 = yylhsminor.yy0; + break; + case 28: /* term ::= IDENTIFIER LPAREN expr_list RPAREN */ +#line 127 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterParser.y" +{ + try + { + yylhsminor.yy0 = {}; + yylhsminor.yy0.node = new FunctionNode(pCtx, yymsp[-3].minor.yy0.string, yymsp[-1].minor.yy0.nodeList); + } + catch (const std::invalid_argument& e) + { + pCtx->errorCode = FILTER_ERROR_INVALID_ARGUMENT_COUNT; + pCtx->errorMessage = e.what(); + YYSTYPEDestructor(yymsp[-1].minor.yy0); + } + catch (const std::runtime_error& e) + { + pCtx->errorCode = FILTER_ERROR_UNDEFINED_IDENTIFIER; + pCtx->errorMessage = e.what(); + YYSTYPEDestructor(yymsp[-1].minor.yy0); + } +} +#line 1306 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterParser.c" + yymsp[-3].minor.yy0 = yylhsminor.yy0; + break; + case 29: /* term ::= IDENTIFIER */ +#line 146 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterParser.y" +{ + try + { + yylhsminor.yy0 = {}; + yylhsminor.yy0.node = new FieldNode(pCtx, yymsp[0].minor.yy0.string); + } + catch (const std::exception&) + { + pCtx->errorCode = FILTER_ERROR_UNDEFINED_IDENTIFIER; + } +} +#line 1322 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterParser.c" + yymsp[0].minor.yy0 = yylhsminor.yy0; + break; + case 30: /* term ::= LPAREN expr RPAREN */ +#line 157 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterParser.y" +{ yymsp[-2].minor.yy0 = yymsp[-1].minor.yy0; } +#line 1328 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterParser.c" + break; + case 31: /* expr_list ::= expr */ +#line 159 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterParser.y" +{ + yylhsminor.yy0 = {}; + yylhsminor.yy0.nodeList = new std::vector{ yymsp[0].minor.yy0.node }; +} +#line 1336 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterParser.c" + yymsp[0].minor.yy0 = yylhsminor.yy0; + break; + case 32: /* expr_list ::= expr_list COMMA expr */ +#line 163 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterParser.y" +{ + yylhsminor.yy0 = {}; + yymsp[-2].minor.yy0.nodeList->push_back(yymsp[0].minor.yy0.node); + yylhsminor.yy0.nodeList = yymsp[-2].minor.yy0.nodeList; +} +#line 1346 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterParser.c" + yymsp[-2].minor.yy0 = yylhsminor.yy0; + break; + default: + /* (33) or_expr ::= and_expr */ yytestcase(yyruleno==33); + /* (34) and_expr ::= not_expr (OPTIMIZED OUT) */ assert(yyruleno!=34); + /* (35) not_expr ::= cmp_expr (OPTIMIZED OUT) */ assert(yyruleno!=35); + /* (36) cmp_expr ::= arithmetic */ yytestcase(yyruleno==36); + /* (37) arithmetic ::= unary (OPTIMIZED OUT) */ assert(yyruleno!=37); + /* (38) expr ::= or_expr */ yytestcase(yyruleno==38); + /* (39) unary ::= term (OPTIMIZED OUT) */ assert(yyruleno!=39); + break; +/********** End reduce actions ************************************************/ + }; + assert( yyrulenoYY_MAX_SHIFT && yyact<=YY_MAX_SHIFTREDUCE) ); + + /* It is not possible for a REDUCE to be followed by an error */ + assert( yyact!=YY_ERROR_ACTION ); + + yymsp += yysize+1; + yypParser->yytos = yymsp; + yymsp->stateno = (YYACTIONTYPE)yyact; + yymsp->major = (YYCODETYPE)yygoto; + yyTraceShift(yypParser, yyact, "... then shift"); + return yyact; +} + +/* +** The following code executes when the parse fails +*/ +#ifndef YYNOERRORRECOVERY +static void yy_parse_failed( + yyParser *yypParser /* The parser */ +){ + ParseARG_FETCH + ParseCTX_FETCH +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sFail!\n",yyTracePrompt); + } +#endif + while( yypParser->yytos>yypParser->yystack ) yy_pop_parser_stack(yypParser); + /* Here code is inserted which will be executed whenever the + ** parser fails */ +/************ Begin %parse_failure code ***************************************/ +#line 29 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterParser.y" + + pCtx->errorCode = FILTER_ERROR_PARSE_FAILURE; +#line 1401 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterParser.c" +/************ End %parse_failure code *****************************************/ + ParseARG_STORE /* Suppress warning about unused %extra_argument variable */ + ParseCTX_STORE +} +#endif /* YYNOERRORRECOVERY */ + +/* +** The following code executes when a syntax error first occurs. +*/ +static void yy_syntax_error( + yyParser *yypParser, /* The parser */ + int yymajor, /* The major type of the error token */ + ParseTOKENTYPE yyminor /* The minor type of the error token */ +){ + ParseARG_FETCH + ParseCTX_FETCH +#define TOKEN yyminor +/************ Begin %syntax_error code ****************************************/ +#line 26 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterParser.y" + + pCtx->errorCode = FILTER_ERROR_SYNTAX_ERROR; +#line 1423 "E:\\dev\\winmerge\\Src\\FilterEngine\\FilterParser.c" +/************ End %syntax_error code ******************************************/ + ParseARG_STORE /* Suppress warning about unused %extra_argument variable */ + ParseCTX_STORE +} + +/* +** The following is executed when the parser accepts +*/ +static void yy_accept( + yyParser *yypParser /* The parser */ +){ + ParseARG_FETCH + ParseCTX_FETCH +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sAccept!\n",yyTracePrompt); + } +#endif +#ifndef YYNOERRORRECOVERY + yypParser->yyerrcnt = -1; +#endif + assert( yypParser->yytos==yypParser->yystack ); + /* Here code is inserted which will be executed whenever the + ** parser accepts */ +/*********** Begin %parse_accept code *****************************************/ +/*********** End %parse_accept code *******************************************/ + ParseARG_STORE /* Suppress warning about unused %extra_argument variable */ + ParseCTX_STORE +} + +/* The main parser program. +** The first argument is a pointer to a structure obtained from +** "ParseAlloc" which describes the current state of the parser. +** The second argument is the major token number. The third is +** the minor token. The fourth optional argument is whatever the +** user wants (and specified in the grammar) and is available for +** use by the action routines. +** +** Inputs: +**
    +**
  • A pointer to the parser (an opaque structure.) +**
  • The major token number. +**
  • The minor token number. +**
  • An option argument of a grammar-specified type. +**
+** +** Outputs: +** None. +*/ +void Parse( + void *yyp, /* The parser */ + int yymajor, /* The major token code number */ + ParseTOKENTYPE yyminor /* The value for the token */ + ParseARG_PDECL /* Optional %extra_argument parameter */ +){ + YYMINORTYPE yyminorunion; + YYACTIONTYPE yyact; /* The parser action. */ +#if !defined(YYERRORSYMBOL) && !defined(YYNOERRORRECOVERY) + int yyendofinput; /* True if we are at the end of input */ +#endif +#ifdef YYERRORSYMBOL + int yyerrorhit = 0; /* True if yymajor has invoked an error */ +#endif + yyParser *yypParser = (yyParser*)yyp; /* The parser */ + ParseCTX_FETCH + ParseARG_STORE + + assert( yypParser->yytos!=0 ); +#if !defined(YYERRORSYMBOL) && !defined(YYNOERRORRECOVERY) + yyendofinput = (yymajor==0); +#endif + + yyact = yypParser->yytos->stateno; +#ifndef NDEBUG + if( yyTraceFILE ){ + if( yyact < YY_MIN_REDUCE ){ + fprintf(yyTraceFILE,"%sInput '%s' in state %d\n", + yyTracePrompt,yyTokenName[yymajor],yyact); + }else{ + fprintf(yyTraceFILE,"%sInput '%s' with pending reduce %d\n", + yyTracePrompt,yyTokenName[yymajor],yyact-YY_MIN_REDUCE); + } + } +#endif + + while(1){ /* Exit by "break" */ + assert( yypParser->yytos>=yypParser->yystack ); + assert( yyact==yypParser->yytos->stateno ); + yyact = yy_find_shift_action((YYCODETYPE)yymajor,yyact); + if( yyact >= YY_MIN_REDUCE ){ + unsigned int yyruleno = yyact - YY_MIN_REDUCE; /* Reduce by this rule */ +#ifndef NDEBUG + assert( yyruleno<(int)(sizeof(yyRuleName)/sizeof(yyRuleName[0])) ); + if( yyTraceFILE ){ + int yysize = yyRuleInfoNRhs[yyruleno]; + if( yysize ){ + fprintf(yyTraceFILE, "%sReduce %d [%s]%s, pop back to state %d.\n", + yyTracePrompt, + yyruleno, yyRuleName[yyruleno], + yyrulenoyytos[yysize].stateno); + }else{ + fprintf(yyTraceFILE, "%sReduce %d [%s]%s.\n", + yyTracePrompt, yyruleno, yyRuleName[yyruleno], + yyrulenoyytos - yypParser->yystack)>yypParser->yyhwm ){ + yypParser->yyhwm++; + assert( yypParser->yyhwm == + (int)(yypParser->yytos - yypParser->yystack)); + } +#endif + if( yypParser->yytos>=yypParser->yystackEnd ){ + if( yyGrowStack(yypParser) ){ + yyStackOverflow(yypParser); + break; + } + } + } + yyact = yy_reduce(yypParser,yyruleno,yymajor,yyminor ParseCTX_PARAM); + }else if( yyact <= YY_MAX_SHIFTREDUCE ){ + yy_shift(yypParser,yyact,(YYCODETYPE)yymajor,yyminor); +#ifndef YYNOERRORRECOVERY + yypParser->yyerrcnt--; +#endif + break; + }else if( yyact==YY_ACCEPT_ACTION ){ + yypParser->yytos--; + yy_accept(yypParser); + return; + }else{ + assert( yyact == YY_ERROR_ACTION ); + yyminorunion.yy0 = yyminor; +#ifdef YYERRORSYMBOL + int yymx; +#endif +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sSyntax Error!\n",yyTracePrompt); + } +#endif +#ifdef YYERRORSYMBOL + /* A syntax error has occurred. + ** The response to an error depends upon whether or not the + ** grammar defines an error token "ERROR". + ** + ** This is what we do if the grammar does define ERROR: + ** + ** * Call the %syntax_error function. + ** + ** * Begin popping the stack until we enter a state where + ** it is legal to shift the error symbol, then shift + ** the error symbol. + ** + ** * Set the error count to three. + ** + ** * Begin accepting and shifting new tokens. No new error + ** processing will occur until three tokens have been + ** shifted successfully. + ** + */ + if( yypParser->yyerrcnt<0 ){ + yy_syntax_error(yypParser,yymajor,yyminor); + } + yymx = yypParser->yytos->major; + if( yymx==YYERRORSYMBOL || yyerrorhit ){ +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sDiscard input token %s\n", + yyTracePrompt,yyTokenName[yymajor]); + } +#endif + yy_destructor(yypParser, (YYCODETYPE)yymajor, &yyminorunion); + yymajor = YYNOCODE; + }else{ + while( yypParser->yytos > yypParser->yystack ){ + yyact = yy_find_reduce_action(yypParser->yytos->stateno, + YYERRORSYMBOL); + if( yyact<=YY_MAX_SHIFTREDUCE ) break; + yy_pop_parser_stack(yypParser); + } + if( yypParser->yytos <= yypParser->yystack || yymajor==0 ){ + yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion); + yy_parse_failed(yypParser); +#ifndef YYNOERRORRECOVERY + yypParser->yyerrcnt = -1; +#endif + yymajor = YYNOCODE; + }else if( yymx!=YYERRORSYMBOL ){ + yy_shift(yypParser,yyact,YYERRORSYMBOL,yyminor); + } + } + yypParser->yyerrcnt = 3; + yyerrorhit = 1; + if( yymajor==YYNOCODE ) break; + yyact = yypParser->yytos->stateno; +#elif defined(YYNOERRORRECOVERY) + /* If the YYNOERRORRECOVERY macro is defined, then do not attempt to + ** do any kind of error recovery. Instead, simply invoke the syntax + ** error routine and continue going as if nothing had happened. + ** + ** Applications can set this macro (for example inside %include) if + ** they intend to abandon the parse upon the first syntax error seen. + */ + yy_syntax_error(yypParser,yymajor, yyminor); + yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion); + break; +#else /* YYERRORSYMBOL is not defined */ + /* This is what we do if the grammar does not define ERROR: + ** + ** * Report an error message, and throw away the input token. + ** + ** * If the input token is $, then fail the parse. + ** + ** As before, subsequent error messages are suppressed until + ** three input tokens have been successfully shifted. + */ + if( yypParser->yyerrcnt<=0 ){ + yy_syntax_error(yypParser,yymajor, yyminor); + } + yypParser->yyerrcnt = 3; + yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion); + if( yyendofinput ){ + yy_parse_failed(yypParser); +#ifndef YYNOERRORRECOVERY + yypParser->yyerrcnt = -1; +#endif + } + break; +#endif + } + } +#ifndef NDEBUG + if( yyTraceFILE ){ + yyStackEntry *i; + char cDiv = '['; + fprintf(yyTraceFILE,"%sReturn. Stack=",yyTracePrompt); + for(i=&yypParser->yystack[1]; i<=yypParser->yytos; i++){ + fprintf(yyTraceFILE,"%c%s", cDiv, yyTokenName[i->major]); + cDiv = ' '; + } + fprintf(yyTraceFILE,"]\n"); + } +#endif + return; +} + +/* +** Return the fallback token corresponding to canonical token iToken, or +** 0 if iToken has no fallback. +*/ +int ParseFallback(int iToken){ +#ifdef YYFALLBACK + assert( iToken<(int)(sizeof(yyFallback)/sizeof(yyFallback[0])) ); + return yyFallback[iToken]; +#else + (void)iToken; + return 0; +#endif +} diff --git a/Src/FilterEngine/FilterParser.cpp b/Src/FilterEngine/FilterParser.cpp new file mode 100644 index 00000000000..546a3998850 --- /dev/null +++ b/Src/FilterEngine/FilterParser.cpp @@ -0,0 +1,2 @@ +#include "pch.h" +#include "FilterParser.c" diff --git a/Src/FilterEngine/FilterParser.h b/Src/FilterEngine/FilterParser.h new file mode 100644 index 00000000000..da3ddfde9d6 --- /dev/null +++ b/Src/FilterEngine/FilterParser.h @@ -0,0 +1,29 @@ +#define TK_AND 1 +#define TK_OR 2 +#define TK_NOT 3 +#define TK_TRUE_LITERAL 4 +#define TK_FALSE_LITERAL 5 +#define TK_INTEGER_LITERAL 6 +#define TK_STRING_LITERAL 7 +#define TK_SIZE_LITERAL 8 +#define TK_DATETIME_LITERAL 9 +#define TK_DURATION_LITERAL 10 +#define TK_VERSION_LITERAL 11 +#define TK_IDENTIFIER 12 +#define TK_EQ 13 +#define TK_NE 14 +#define TK_LT 15 +#define TK_LE 16 +#define TK_GT 17 +#define TK_GE 18 +#define TK_CONTAINS 19 +#define TK_RECONTAINS 20 +#define TK_MATCHES 21 +#define TK_LPAREN 22 +#define TK_RPAREN 23 +#define TK_PLUS 24 +#define TK_MINUS 25 +#define TK_STAR 26 +#define TK_SLASH 27 +#define TK_MOD 28 +#define TK_COMMA 29 diff --git a/Src/FilterEngine/FilterParser.out b/Src/FilterEngine/FilterParser.out new file mode 100644 index 00000000000..54d9d6994c3 --- /dev/null +++ b/Src/FilterEngine/FilterParser.out @@ -0,0 +1,1307 @@ +State 0: + filter_expr ::= * or_expr + or_expr ::= * or_expr OR and_expr + or_expr ::= * and_expr + and_expr ::= * and_expr AND not_expr + and_expr ::= * not_expr + not_expr ::= * NOT not_expr + not_expr ::= * cmp_expr + cmp_expr ::= * arithmetic EQ arithmetic + cmp_expr ::= * arithmetic NE arithmetic + cmp_expr ::= * arithmetic LT arithmetic + cmp_expr ::= * arithmetic LE arithmetic + cmp_expr ::= * arithmetic GT arithmetic + cmp_expr ::= * arithmetic GE arithmetic + cmp_expr ::= * arithmetic CONTAINS arithmetic + cmp_expr ::= * arithmetic RECONTAINS arithmetic + cmp_expr ::= * arithmetic MATCHES arithmetic + cmp_expr ::= * arithmetic + arithmetic ::= * arithmetic PLUS arithmetic + arithmetic ::= * arithmetic MINUS arithmetic + arithmetic ::= * arithmetic STAR arithmetic + arithmetic ::= * arithmetic SLASH arithmetic + arithmetic ::= * arithmetic MOD arithmetic + arithmetic ::= * unary + unary ::= * MINUS unary + unary ::= * term + term ::= * TRUE_LITERAL + term ::= * FALSE_LITERAL + term ::= * INTEGER_LITERAL + term ::= * STRING_LITERAL + term ::= * SIZE_LITERAL + term ::= * DATETIME_LITERAL + term ::= * DURATION_LITERAL + term ::= * VERSION_LITERAL + term ::= * IDENTIFIER LPAREN RPAREN + term ::= * IDENTIFIER LPAREN expr_list RPAREN + term ::= * IDENTIFIER + term ::= * LPAREN expr RPAREN + + NOT shift 5 + TRUE_LITERAL shift-reduce 19 term ::= TRUE_LITERAL + FALSE_LITERAL shift-reduce 20 term ::= FALSE_LITERAL + INTEGER_LITERAL shift-reduce 21 term ::= INTEGER_LITERAL + STRING_LITERAL shift-reduce 22 term ::= STRING_LITERAL + SIZE_LITERAL shift-reduce 23 term ::= SIZE_LITERAL + DATETIME_LITERAL shift-reduce 24 term ::= DATETIME_LITERAL + DURATION_LITERAL shift-reduce 25 term ::= DURATION_LITERAL + VERSION_LITERAL shift-reduce 26 term ::= VERSION_LITERAL + IDENTIFIER shift 39 + LPAREN shift 2 + MINUS shift 21 + filter_expr accept + or_expr shift 35 + and_expr shift 37 + not_expr shift 37 /* because not_expr==and_expr */ + cmp_expr shift 37 /* because cmp_expr==not_expr */ + arithmetic shift 22 + unary shift 22 /* because unary==arithmetic */ + term shift 22 /* because term==unary */ + +State 1: + or_expr ::= * or_expr OR and_expr + or_expr ::= * and_expr + and_expr ::= * and_expr AND not_expr + and_expr ::= * not_expr + not_expr ::= * NOT not_expr + not_expr ::= * cmp_expr + cmp_expr ::= * arithmetic EQ arithmetic + cmp_expr ::= * arithmetic NE arithmetic + cmp_expr ::= * arithmetic LT arithmetic + cmp_expr ::= * arithmetic LE arithmetic + cmp_expr ::= * arithmetic GT arithmetic + cmp_expr ::= * arithmetic GE arithmetic + cmp_expr ::= * arithmetic CONTAINS arithmetic + cmp_expr ::= * arithmetic RECONTAINS arithmetic + cmp_expr ::= * arithmetic MATCHES arithmetic + cmp_expr ::= * arithmetic + arithmetic ::= * arithmetic PLUS arithmetic + arithmetic ::= * arithmetic MINUS arithmetic + arithmetic ::= * arithmetic STAR arithmetic + arithmetic ::= * arithmetic SLASH arithmetic + arithmetic ::= * arithmetic MOD arithmetic + arithmetic ::= * unary + expr ::= * or_expr + unary ::= * MINUS unary + unary ::= * term + term ::= * TRUE_LITERAL + term ::= * FALSE_LITERAL + term ::= * INTEGER_LITERAL + term ::= * STRING_LITERAL + term ::= * SIZE_LITERAL + term ::= * DATETIME_LITERAL + term ::= * DURATION_LITERAL + term ::= * VERSION_LITERAL + term ::= * IDENTIFIER LPAREN RPAREN + term ::= IDENTIFIER LPAREN * RPAREN + term ::= * IDENTIFIER LPAREN expr_list RPAREN + term ::= IDENTIFIER LPAREN * expr_list RPAREN + term ::= * IDENTIFIER + term ::= * LPAREN expr RPAREN + expr_list ::= * expr + expr_list ::= * expr_list COMMA expr + + NOT shift 5 + TRUE_LITERAL shift-reduce 19 term ::= TRUE_LITERAL + FALSE_LITERAL shift-reduce 20 term ::= FALSE_LITERAL + INTEGER_LITERAL shift-reduce 21 term ::= INTEGER_LITERAL + STRING_LITERAL shift-reduce 22 term ::= STRING_LITERAL + SIZE_LITERAL shift-reduce 23 term ::= SIZE_LITERAL + DATETIME_LITERAL shift-reduce 24 term ::= DATETIME_LITERAL + DURATION_LITERAL shift-reduce 25 term ::= DURATION_LITERAL + VERSION_LITERAL shift-reduce 26 term ::= VERSION_LITERAL + IDENTIFIER shift 39 + LPAREN shift 2 + RPAREN shift-reduce 27 term ::= IDENTIFIER LPAREN RPAREN + MINUS shift 21 + or_expr shift 38 + and_expr shift 37 + not_expr shift 37 /* because not_expr==and_expr */ + cmp_expr shift 37 /* because cmp_expr==not_expr */ + arithmetic shift 22 + unary shift 22 /* because unary==arithmetic */ + expr shift-reduce 31 expr_list ::= expr + term shift 22 /* because term==unary */ + expr_list shift 34 + +State 2: + or_expr ::= * or_expr OR and_expr + or_expr ::= * and_expr + and_expr ::= * and_expr AND not_expr + and_expr ::= * not_expr + not_expr ::= * NOT not_expr + not_expr ::= * cmp_expr + cmp_expr ::= * arithmetic EQ arithmetic + cmp_expr ::= * arithmetic NE arithmetic + cmp_expr ::= * arithmetic LT arithmetic + cmp_expr ::= * arithmetic LE arithmetic + cmp_expr ::= * arithmetic GT arithmetic + cmp_expr ::= * arithmetic GE arithmetic + cmp_expr ::= * arithmetic CONTAINS arithmetic + cmp_expr ::= * arithmetic RECONTAINS arithmetic + cmp_expr ::= * arithmetic MATCHES arithmetic + cmp_expr ::= * arithmetic + arithmetic ::= * arithmetic PLUS arithmetic + arithmetic ::= * arithmetic MINUS arithmetic + arithmetic ::= * arithmetic STAR arithmetic + arithmetic ::= * arithmetic SLASH arithmetic + arithmetic ::= * arithmetic MOD arithmetic + arithmetic ::= * unary + expr ::= * or_expr + unary ::= * MINUS unary + unary ::= * term + term ::= * TRUE_LITERAL + term ::= * FALSE_LITERAL + term ::= * INTEGER_LITERAL + term ::= * STRING_LITERAL + term ::= * SIZE_LITERAL + term ::= * DATETIME_LITERAL + term ::= * DURATION_LITERAL + term ::= * VERSION_LITERAL + term ::= * IDENTIFIER LPAREN RPAREN + term ::= * IDENTIFIER LPAREN expr_list RPAREN + term ::= * IDENTIFIER + term ::= * LPAREN expr RPAREN + term ::= LPAREN * expr RPAREN + + NOT shift 5 + TRUE_LITERAL shift-reduce 19 term ::= TRUE_LITERAL + FALSE_LITERAL shift-reduce 20 term ::= FALSE_LITERAL + INTEGER_LITERAL shift-reduce 21 term ::= INTEGER_LITERAL + STRING_LITERAL shift-reduce 22 term ::= STRING_LITERAL + SIZE_LITERAL shift-reduce 23 term ::= SIZE_LITERAL + DATETIME_LITERAL shift-reduce 24 term ::= DATETIME_LITERAL + DURATION_LITERAL shift-reduce 25 term ::= DURATION_LITERAL + VERSION_LITERAL shift-reduce 26 term ::= VERSION_LITERAL + IDENTIFIER shift 39 + LPAREN shift 2 + MINUS shift 21 + or_expr shift 38 + and_expr shift 37 + not_expr shift 37 /* because not_expr==and_expr */ + cmp_expr shift 37 /* because cmp_expr==not_expr */ + arithmetic shift 22 + unary shift 22 /* because unary==arithmetic */ + expr shift 36 + term shift 22 /* because term==unary */ + +State 3: + or_expr ::= * or_expr OR and_expr + or_expr ::= * and_expr + and_expr ::= * and_expr AND not_expr + and_expr ::= * not_expr + not_expr ::= * NOT not_expr + not_expr ::= * cmp_expr + cmp_expr ::= * arithmetic EQ arithmetic + cmp_expr ::= * arithmetic NE arithmetic + cmp_expr ::= * arithmetic LT arithmetic + cmp_expr ::= * arithmetic LE arithmetic + cmp_expr ::= * arithmetic GT arithmetic + cmp_expr ::= * arithmetic GE arithmetic + cmp_expr ::= * arithmetic CONTAINS arithmetic + cmp_expr ::= * arithmetic RECONTAINS arithmetic + cmp_expr ::= * arithmetic MATCHES arithmetic + cmp_expr ::= * arithmetic + arithmetic ::= * arithmetic PLUS arithmetic + arithmetic ::= * arithmetic MINUS arithmetic + arithmetic ::= * arithmetic STAR arithmetic + arithmetic ::= * arithmetic SLASH arithmetic + arithmetic ::= * arithmetic MOD arithmetic + arithmetic ::= * unary + expr ::= * or_expr + unary ::= * MINUS unary + unary ::= * term + term ::= * TRUE_LITERAL + term ::= * FALSE_LITERAL + term ::= * INTEGER_LITERAL + term ::= * STRING_LITERAL + term ::= * SIZE_LITERAL + term ::= * DATETIME_LITERAL + term ::= * DURATION_LITERAL + term ::= * VERSION_LITERAL + term ::= * IDENTIFIER LPAREN RPAREN + term ::= * IDENTIFIER LPAREN expr_list RPAREN + term ::= * IDENTIFIER + term ::= * LPAREN expr RPAREN + expr_list ::= expr_list COMMA * expr + + NOT shift 5 + TRUE_LITERAL shift-reduce 19 term ::= TRUE_LITERAL + FALSE_LITERAL shift-reduce 20 term ::= FALSE_LITERAL + INTEGER_LITERAL shift-reduce 21 term ::= INTEGER_LITERAL + STRING_LITERAL shift-reduce 22 term ::= STRING_LITERAL + SIZE_LITERAL shift-reduce 23 term ::= SIZE_LITERAL + DATETIME_LITERAL shift-reduce 24 term ::= DATETIME_LITERAL + DURATION_LITERAL shift-reduce 25 term ::= DURATION_LITERAL + VERSION_LITERAL shift-reduce 26 term ::= VERSION_LITERAL + IDENTIFIER shift 39 + LPAREN shift 2 + MINUS shift 21 + or_expr shift 38 + and_expr shift 37 + not_expr shift 37 /* because not_expr==and_expr */ + cmp_expr shift 37 /* because cmp_expr==not_expr */ + arithmetic shift 22 + unary shift 22 /* because unary==arithmetic */ + expr shift-reduce 32 expr_list ::= expr_list COMMA expr + term shift 22 /* because term==unary */ + +State 4: + or_expr ::= or_expr OR * and_expr + and_expr ::= * and_expr AND not_expr + and_expr ::= * not_expr + not_expr ::= * NOT not_expr + not_expr ::= * cmp_expr + cmp_expr ::= * arithmetic EQ arithmetic + cmp_expr ::= * arithmetic NE arithmetic + cmp_expr ::= * arithmetic LT arithmetic + cmp_expr ::= * arithmetic LE arithmetic + cmp_expr ::= * arithmetic GT arithmetic + cmp_expr ::= * arithmetic GE arithmetic + cmp_expr ::= * arithmetic CONTAINS arithmetic + cmp_expr ::= * arithmetic RECONTAINS arithmetic + cmp_expr ::= * arithmetic MATCHES arithmetic + cmp_expr ::= * arithmetic + arithmetic ::= * arithmetic PLUS arithmetic + arithmetic ::= * arithmetic MINUS arithmetic + arithmetic ::= * arithmetic STAR arithmetic + arithmetic ::= * arithmetic SLASH arithmetic + arithmetic ::= * arithmetic MOD arithmetic + arithmetic ::= * unary + unary ::= * MINUS unary + unary ::= * term + term ::= * TRUE_LITERAL + term ::= * FALSE_LITERAL + term ::= * INTEGER_LITERAL + term ::= * STRING_LITERAL + term ::= * SIZE_LITERAL + term ::= * DATETIME_LITERAL + term ::= * DURATION_LITERAL + term ::= * VERSION_LITERAL + term ::= * IDENTIFIER LPAREN RPAREN + term ::= * IDENTIFIER LPAREN expr_list RPAREN + term ::= * IDENTIFIER + term ::= * LPAREN expr RPAREN + + NOT shift 5 + TRUE_LITERAL shift-reduce 19 term ::= TRUE_LITERAL + FALSE_LITERAL shift-reduce 20 term ::= FALSE_LITERAL + INTEGER_LITERAL shift-reduce 21 term ::= INTEGER_LITERAL + STRING_LITERAL shift-reduce 22 term ::= STRING_LITERAL + SIZE_LITERAL shift-reduce 23 term ::= SIZE_LITERAL + DATETIME_LITERAL shift-reduce 24 term ::= DATETIME_LITERAL + DURATION_LITERAL shift-reduce 25 term ::= DURATION_LITERAL + VERSION_LITERAL shift-reduce 26 term ::= VERSION_LITERAL + IDENTIFIER shift 39 + LPAREN shift 2 + MINUS shift 21 + and_expr shift 40 + not_expr shift 40 /* because not_expr==and_expr */ + cmp_expr shift 40 /* because cmp_expr==not_expr */ + arithmetic shift 22 + unary shift 22 /* because unary==arithmetic */ + term shift 22 /* because term==unary */ + +State 5: + not_expr ::= * NOT not_expr + not_expr ::= NOT * not_expr + not_expr ::= * cmp_expr + cmp_expr ::= * arithmetic EQ arithmetic + cmp_expr ::= * arithmetic NE arithmetic + cmp_expr ::= * arithmetic LT arithmetic + cmp_expr ::= * arithmetic LE arithmetic + cmp_expr ::= * arithmetic GT arithmetic + cmp_expr ::= * arithmetic GE arithmetic + cmp_expr ::= * arithmetic CONTAINS arithmetic + cmp_expr ::= * arithmetic RECONTAINS arithmetic + cmp_expr ::= * arithmetic MATCHES arithmetic + cmp_expr ::= * arithmetic + arithmetic ::= * arithmetic PLUS arithmetic + arithmetic ::= * arithmetic MINUS arithmetic + arithmetic ::= * arithmetic STAR arithmetic + arithmetic ::= * arithmetic SLASH arithmetic + arithmetic ::= * arithmetic MOD arithmetic + arithmetic ::= * unary + unary ::= * MINUS unary + unary ::= * term + term ::= * TRUE_LITERAL + term ::= * FALSE_LITERAL + term ::= * INTEGER_LITERAL + term ::= * STRING_LITERAL + term ::= * SIZE_LITERAL + term ::= * DATETIME_LITERAL + term ::= * DURATION_LITERAL + term ::= * VERSION_LITERAL + term ::= * IDENTIFIER LPAREN RPAREN + term ::= * IDENTIFIER LPAREN expr_list RPAREN + term ::= * IDENTIFIER + term ::= * LPAREN expr RPAREN + + NOT shift 5 + TRUE_LITERAL shift-reduce 19 term ::= TRUE_LITERAL + FALSE_LITERAL shift-reduce 20 term ::= FALSE_LITERAL + INTEGER_LITERAL shift-reduce 21 term ::= INTEGER_LITERAL + STRING_LITERAL shift-reduce 22 term ::= STRING_LITERAL + SIZE_LITERAL shift-reduce 23 term ::= SIZE_LITERAL + DATETIME_LITERAL shift-reduce 24 term ::= DATETIME_LITERAL + DURATION_LITERAL shift-reduce 25 term ::= DURATION_LITERAL + VERSION_LITERAL shift-reduce 26 term ::= VERSION_LITERAL + IDENTIFIER shift 39 + LPAREN shift 2 + MINUS shift 21 + not_expr shift-reduce 3 not_expr ::= NOT not_expr + cmp_expr shift-reduce 3 not_expr ::= NOT not_expr /* because cmp_expr==not_expr */ + arithmetic shift 22 + unary shift 22 /* because unary==arithmetic */ + term shift 22 /* because term==unary */ + +State 6: + and_expr ::= and_expr AND * not_expr + not_expr ::= * NOT not_expr + not_expr ::= * cmp_expr + cmp_expr ::= * arithmetic EQ arithmetic + cmp_expr ::= * arithmetic NE arithmetic + cmp_expr ::= * arithmetic LT arithmetic + cmp_expr ::= * arithmetic LE arithmetic + cmp_expr ::= * arithmetic GT arithmetic + cmp_expr ::= * arithmetic GE arithmetic + cmp_expr ::= * arithmetic CONTAINS arithmetic + cmp_expr ::= * arithmetic RECONTAINS arithmetic + cmp_expr ::= * arithmetic MATCHES arithmetic + cmp_expr ::= * arithmetic + arithmetic ::= * arithmetic PLUS arithmetic + arithmetic ::= * arithmetic MINUS arithmetic + arithmetic ::= * arithmetic STAR arithmetic + arithmetic ::= * arithmetic SLASH arithmetic + arithmetic ::= * arithmetic MOD arithmetic + arithmetic ::= * unary + unary ::= * MINUS unary + unary ::= * term + term ::= * TRUE_LITERAL + term ::= * FALSE_LITERAL + term ::= * INTEGER_LITERAL + term ::= * STRING_LITERAL + term ::= * SIZE_LITERAL + term ::= * DATETIME_LITERAL + term ::= * DURATION_LITERAL + term ::= * VERSION_LITERAL + term ::= * IDENTIFIER LPAREN RPAREN + term ::= * IDENTIFIER LPAREN expr_list RPAREN + term ::= * IDENTIFIER + term ::= * LPAREN expr RPAREN + + NOT shift 5 + TRUE_LITERAL shift-reduce 19 term ::= TRUE_LITERAL + FALSE_LITERAL shift-reduce 20 term ::= FALSE_LITERAL + INTEGER_LITERAL shift-reduce 21 term ::= INTEGER_LITERAL + STRING_LITERAL shift-reduce 22 term ::= STRING_LITERAL + SIZE_LITERAL shift-reduce 23 term ::= SIZE_LITERAL + DATETIME_LITERAL shift-reduce 24 term ::= DATETIME_LITERAL + DURATION_LITERAL shift-reduce 25 term ::= DURATION_LITERAL + VERSION_LITERAL shift-reduce 26 term ::= VERSION_LITERAL + IDENTIFIER shift 39 + LPAREN shift 2 + MINUS shift 21 + not_expr shift-reduce 2 and_expr ::= and_expr AND not_expr + cmp_expr shift-reduce 2 and_expr ::= and_expr AND not_expr /* because cmp_expr==not_expr */ + arithmetic shift 22 + unary shift 22 /* because unary==arithmetic */ + term shift 22 /* because term==unary */ + +State 7: + cmp_expr ::= arithmetic MATCHES * arithmetic + arithmetic ::= * arithmetic PLUS arithmetic + arithmetic ::= * arithmetic MINUS arithmetic + arithmetic ::= * arithmetic STAR arithmetic + arithmetic ::= * arithmetic SLASH arithmetic + arithmetic ::= * arithmetic MOD arithmetic + arithmetic ::= * unary + unary ::= * MINUS unary + unary ::= * term + term ::= * TRUE_LITERAL + term ::= * FALSE_LITERAL + term ::= * INTEGER_LITERAL + term ::= * STRING_LITERAL + term ::= * SIZE_LITERAL + term ::= * DATETIME_LITERAL + term ::= * DURATION_LITERAL + term ::= * VERSION_LITERAL + term ::= * IDENTIFIER LPAREN RPAREN + term ::= * IDENTIFIER LPAREN expr_list RPAREN + term ::= * IDENTIFIER + term ::= * LPAREN expr RPAREN + + TRUE_LITERAL shift-reduce 19 term ::= TRUE_LITERAL + FALSE_LITERAL shift-reduce 20 term ::= FALSE_LITERAL + INTEGER_LITERAL shift-reduce 21 term ::= INTEGER_LITERAL + STRING_LITERAL shift-reduce 22 term ::= STRING_LITERAL + SIZE_LITERAL shift-reduce 23 term ::= SIZE_LITERAL + DATETIME_LITERAL shift-reduce 24 term ::= DATETIME_LITERAL + DURATION_LITERAL shift-reduce 25 term ::= DURATION_LITERAL + VERSION_LITERAL shift-reduce 26 term ::= VERSION_LITERAL + IDENTIFIER shift 39 + LPAREN shift 2 + MINUS shift 21 + arithmetic shift 23 + unary shift 23 /* because unary==arithmetic */ + term shift 23 /* because term==unary */ + +State 8: + cmp_expr ::= arithmetic RECONTAINS * arithmetic + arithmetic ::= * arithmetic PLUS arithmetic + arithmetic ::= * arithmetic MINUS arithmetic + arithmetic ::= * arithmetic STAR arithmetic + arithmetic ::= * arithmetic SLASH arithmetic + arithmetic ::= * arithmetic MOD arithmetic + arithmetic ::= * unary + unary ::= * MINUS unary + unary ::= * term + term ::= * TRUE_LITERAL + term ::= * FALSE_LITERAL + term ::= * INTEGER_LITERAL + term ::= * STRING_LITERAL + term ::= * SIZE_LITERAL + term ::= * DATETIME_LITERAL + term ::= * DURATION_LITERAL + term ::= * VERSION_LITERAL + term ::= * IDENTIFIER LPAREN RPAREN + term ::= * IDENTIFIER LPAREN expr_list RPAREN + term ::= * IDENTIFIER + term ::= * LPAREN expr RPAREN + + TRUE_LITERAL shift-reduce 19 term ::= TRUE_LITERAL + FALSE_LITERAL shift-reduce 20 term ::= FALSE_LITERAL + INTEGER_LITERAL shift-reduce 21 term ::= INTEGER_LITERAL + STRING_LITERAL shift-reduce 22 term ::= STRING_LITERAL + SIZE_LITERAL shift-reduce 23 term ::= SIZE_LITERAL + DATETIME_LITERAL shift-reduce 24 term ::= DATETIME_LITERAL + DURATION_LITERAL shift-reduce 25 term ::= DURATION_LITERAL + VERSION_LITERAL shift-reduce 26 term ::= VERSION_LITERAL + IDENTIFIER shift 39 + LPAREN shift 2 + MINUS shift 21 + arithmetic shift 24 + unary shift 24 /* because unary==arithmetic */ + term shift 24 /* because term==unary */ + +State 9: + cmp_expr ::= arithmetic CONTAINS * arithmetic + arithmetic ::= * arithmetic PLUS arithmetic + arithmetic ::= * arithmetic MINUS arithmetic + arithmetic ::= * arithmetic STAR arithmetic + arithmetic ::= * arithmetic SLASH arithmetic + arithmetic ::= * arithmetic MOD arithmetic + arithmetic ::= * unary + unary ::= * MINUS unary + unary ::= * term + term ::= * TRUE_LITERAL + term ::= * FALSE_LITERAL + term ::= * INTEGER_LITERAL + term ::= * STRING_LITERAL + term ::= * SIZE_LITERAL + term ::= * DATETIME_LITERAL + term ::= * DURATION_LITERAL + term ::= * VERSION_LITERAL + term ::= * IDENTIFIER LPAREN RPAREN + term ::= * IDENTIFIER LPAREN expr_list RPAREN + term ::= * IDENTIFIER + term ::= * LPAREN expr RPAREN + + TRUE_LITERAL shift-reduce 19 term ::= TRUE_LITERAL + FALSE_LITERAL shift-reduce 20 term ::= FALSE_LITERAL + INTEGER_LITERAL shift-reduce 21 term ::= INTEGER_LITERAL + STRING_LITERAL shift-reduce 22 term ::= STRING_LITERAL + SIZE_LITERAL shift-reduce 23 term ::= SIZE_LITERAL + DATETIME_LITERAL shift-reduce 24 term ::= DATETIME_LITERAL + DURATION_LITERAL shift-reduce 25 term ::= DURATION_LITERAL + VERSION_LITERAL shift-reduce 26 term ::= VERSION_LITERAL + IDENTIFIER shift 39 + LPAREN shift 2 + MINUS shift 21 + arithmetic shift 25 + unary shift 25 /* because unary==arithmetic */ + term shift 25 /* because term==unary */ + +State 10: + cmp_expr ::= arithmetic GE * arithmetic + arithmetic ::= * arithmetic PLUS arithmetic + arithmetic ::= * arithmetic MINUS arithmetic + arithmetic ::= * arithmetic STAR arithmetic + arithmetic ::= * arithmetic SLASH arithmetic + arithmetic ::= * arithmetic MOD arithmetic + arithmetic ::= * unary + unary ::= * MINUS unary + unary ::= * term + term ::= * TRUE_LITERAL + term ::= * FALSE_LITERAL + term ::= * INTEGER_LITERAL + term ::= * STRING_LITERAL + term ::= * SIZE_LITERAL + term ::= * DATETIME_LITERAL + term ::= * DURATION_LITERAL + term ::= * VERSION_LITERAL + term ::= * IDENTIFIER LPAREN RPAREN + term ::= * IDENTIFIER LPAREN expr_list RPAREN + term ::= * IDENTIFIER + term ::= * LPAREN expr RPAREN + + TRUE_LITERAL shift-reduce 19 term ::= TRUE_LITERAL + FALSE_LITERAL shift-reduce 20 term ::= FALSE_LITERAL + INTEGER_LITERAL shift-reduce 21 term ::= INTEGER_LITERAL + STRING_LITERAL shift-reduce 22 term ::= STRING_LITERAL + SIZE_LITERAL shift-reduce 23 term ::= SIZE_LITERAL + DATETIME_LITERAL shift-reduce 24 term ::= DATETIME_LITERAL + DURATION_LITERAL shift-reduce 25 term ::= DURATION_LITERAL + VERSION_LITERAL shift-reduce 26 term ::= VERSION_LITERAL + IDENTIFIER shift 39 + LPAREN shift 2 + MINUS shift 21 + arithmetic shift 26 + unary shift 26 /* because unary==arithmetic */ + term shift 26 /* because term==unary */ + +State 11: + cmp_expr ::= arithmetic GT * arithmetic + arithmetic ::= * arithmetic PLUS arithmetic + arithmetic ::= * arithmetic MINUS arithmetic + arithmetic ::= * arithmetic STAR arithmetic + arithmetic ::= * arithmetic SLASH arithmetic + arithmetic ::= * arithmetic MOD arithmetic + arithmetic ::= * unary + unary ::= * MINUS unary + unary ::= * term + term ::= * TRUE_LITERAL + term ::= * FALSE_LITERAL + term ::= * INTEGER_LITERAL + term ::= * STRING_LITERAL + term ::= * SIZE_LITERAL + term ::= * DATETIME_LITERAL + term ::= * DURATION_LITERAL + term ::= * VERSION_LITERAL + term ::= * IDENTIFIER LPAREN RPAREN + term ::= * IDENTIFIER LPAREN expr_list RPAREN + term ::= * IDENTIFIER + term ::= * LPAREN expr RPAREN + + TRUE_LITERAL shift-reduce 19 term ::= TRUE_LITERAL + FALSE_LITERAL shift-reduce 20 term ::= FALSE_LITERAL + INTEGER_LITERAL shift-reduce 21 term ::= INTEGER_LITERAL + STRING_LITERAL shift-reduce 22 term ::= STRING_LITERAL + SIZE_LITERAL shift-reduce 23 term ::= SIZE_LITERAL + DATETIME_LITERAL shift-reduce 24 term ::= DATETIME_LITERAL + DURATION_LITERAL shift-reduce 25 term ::= DURATION_LITERAL + VERSION_LITERAL shift-reduce 26 term ::= VERSION_LITERAL + IDENTIFIER shift 39 + LPAREN shift 2 + MINUS shift 21 + arithmetic shift 27 + unary shift 27 /* because unary==arithmetic */ + term shift 27 /* because term==unary */ + +State 12: + cmp_expr ::= arithmetic LE * arithmetic + arithmetic ::= * arithmetic PLUS arithmetic + arithmetic ::= * arithmetic MINUS arithmetic + arithmetic ::= * arithmetic STAR arithmetic + arithmetic ::= * arithmetic SLASH arithmetic + arithmetic ::= * arithmetic MOD arithmetic + arithmetic ::= * unary + unary ::= * MINUS unary + unary ::= * term + term ::= * TRUE_LITERAL + term ::= * FALSE_LITERAL + term ::= * INTEGER_LITERAL + term ::= * STRING_LITERAL + term ::= * SIZE_LITERAL + term ::= * DATETIME_LITERAL + term ::= * DURATION_LITERAL + term ::= * VERSION_LITERAL + term ::= * IDENTIFIER LPAREN RPAREN + term ::= * IDENTIFIER LPAREN expr_list RPAREN + term ::= * IDENTIFIER + term ::= * LPAREN expr RPAREN + + TRUE_LITERAL shift-reduce 19 term ::= TRUE_LITERAL + FALSE_LITERAL shift-reduce 20 term ::= FALSE_LITERAL + INTEGER_LITERAL shift-reduce 21 term ::= INTEGER_LITERAL + STRING_LITERAL shift-reduce 22 term ::= STRING_LITERAL + SIZE_LITERAL shift-reduce 23 term ::= SIZE_LITERAL + DATETIME_LITERAL shift-reduce 24 term ::= DATETIME_LITERAL + DURATION_LITERAL shift-reduce 25 term ::= DURATION_LITERAL + VERSION_LITERAL shift-reduce 26 term ::= VERSION_LITERAL + IDENTIFIER shift 39 + LPAREN shift 2 + MINUS shift 21 + arithmetic shift 28 + unary shift 28 /* because unary==arithmetic */ + term shift 28 /* because term==unary */ + +State 13: + cmp_expr ::= arithmetic LT * arithmetic + arithmetic ::= * arithmetic PLUS arithmetic + arithmetic ::= * arithmetic MINUS arithmetic + arithmetic ::= * arithmetic STAR arithmetic + arithmetic ::= * arithmetic SLASH arithmetic + arithmetic ::= * arithmetic MOD arithmetic + arithmetic ::= * unary + unary ::= * MINUS unary + unary ::= * term + term ::= * TRUE_LITERAL + term ::= * FALSE_LITERAL + term ::= * INTEGER_LITERAL + term ::= * STRING_LITERAL + term ::= * SIZE_LITERAL + term ::= * DATETIME_LITERAL + term ::= * DURATION_LITERAL + term ::= * VERSION_LITERAL + term ::= * IDENTIFIER LPAREN RPAREN + term ::= * IDENTIFIER LPAREN expr_list RPAREN + term ::= * IDENTIFIER + term ::= * LPAREN expr RPAREN + + TRUE_LITERAL shift-reduce 19 term ::= TRUE_LITERAL + FALSE_LITERAL shift-reduce 20 term ::= FALSE_LITERAL + INTEGER_LITERAL shift-reduce 21 term ::= INTEGER_LITERAL + STRING_LITERAL shift-reduce 22 term ::= STRING_LITERAL + SIZE_LITERAL shift-reduce 23 term ::= SIZE_LITERAL + DATETIME_LITERAL shift-reduce 24 term ::= DATETIME_LITERAL + DURATION_LITERAL shift-reduce 25 term ::= DURATION_LITERAL + VERSION_LITERAL shift-reduce 26 term ::= VERSION_LITERAL + IDENTIFIER shift 39 + LPAREN shift 2 + MINUS shift 21 + arithmetic shift 29 + unary shift 29 /* because unary==arithmetic */ + term shift 29 /* because term==unary */ + +State 14: + cmp_expr ::= arithmetic NE * arithmetic + arithmetic ::= * arithmetic PLUS arithmetic + arithmetic ::= * arithmetic MINUS arithmetic + arithmetic ::= * arithmetic STAR arithmetic + arithmetic ::= * arithmetic SLASH arithmetic + arithmetic ::= * arithmetic MOD arithmetic + arithmetic ::= * unary + unary ::= * MINUS unary + unary ::= * term + term ::= * TRUE_LITERAL + term ::= * FALSE_LITERAL + term ::= * INTEGER_LITERAL + term ::= * STRING_LITERAL + term ::= * SIZE_LITERAL + term ::= * DATETIME_LITERAL + term ::= * DURATION_LITERAL + term ::= * VERSION_LITERAL + term ::= * IDENTIFIER LPAREN RPAREN + term ::= * IDENTIFIER LPAREN expr_list RPAREN + term ::= * IDENTIFIER + term ::= * LPAREN expr RPAREN + + TRUE_LITERAL shift-reduce 19 term ::= TRUE_LITERAL + FALSE_LITERAL shift-reduce 20 term ::= FALSE_LITERAL + INTEGER_LITERAL shift-reduce 21 term ::= INTEGER_LITERAL + STRING_LITERAL shift-reduce 22 term ::= STRING_LITERAL + SIZE_LITERAL shift-reduce 23 term ::= SIZE_LITERAL + DATETIME_LITERAL shift-reduce 24 term ::= DATETIME_LITERAL + DURATION_LITERAL shift-reduce 25 term ::= DURATION_LITERAL + VERSION_LITERAL shift-reduce 26 term ::= VERSION_LITERAL + IDENTIFIER shift 39 + LPAREN shift 2 + MINUS shift 21 + arithmetic shift 30 + unary shift 30 /* because unary==arithmetic */ + term shift 30 /* because term==unary */ + +State 15: + arithmetic ::= * arithmetic PLUS arithmetic + arithmetic ::= * arithmetic MINUS arithmetic + arithmetic ::= * arithmetic STAR arithmetic + arithmetic ::= * arithmetic SLASH arithmetic + arithmetic ::= * arithmetic MOD arithmetic + arithmetic ::= arithmetic MOD * arithmetic + arithmetic ::= * unary + unary ::= * MINUS unary + unary ::= * term + term ::= * TRUE_LITERAL + term ::= * FALSE_LITERAL + term ::= * INTEGER_LITERAL + term ::= * STRING_LITERAL + term ::= * SIZE_LITERAL + term ::= * DATETIME_LITERAL + term ::= * DURATION_LITERAL + term ::= * VERSION_LITERAL + term ::= * IDENTIFIER LPAREN RPAREN + term ::= * IDENTIFIER LPAREN expr_list RPAREN + term ::= * IDENTIFIER + term ::= * LPAREN expr RPAREN + + TRUE_LITERAL shift-reduce 19 term ::= TRUE_LITERAL + FALSE_LITERAL shift-reduce 20 term ::= FALSE_LITERAL + INTEGER_LITERAL shift-reduce 21 term ::= INTEGER_LITERAL + STRING_LITERAL shift-reduce 22 term ::= STRING_LITERAL + SIZE_LITERAL shift-reduce 23 term ::= SIZE_LITERAL + DATETIME_LITERAL shift-reduce 24 term ::= DATETIME_LITERAL + DURATION_LITERAL shift-reduce 25 term ::= DURATION_LITERAL + VERSION_LITERAL shift-reduce 26 term ::= VERSION_LITERAL + IDENTIFIER shift 39 + LPAREN shift 2 + MINUS shift 21 + arithmetic shift-reduce 17 arithmetic ::= arithmetic MOD arithmetic + unary shift-reduce 17 arithmetic ::= arithmetic MOD arithmetic /* because unary==arithmetic */ + term shift-reduce 17 arithmetic ::= arithmetic MOD arithmetic /* because term==unary */ + +State 16: + arithmetic ::= * arithmetic PLUS arithmetic + arithmetic ::= * arithmetic MINUS arithmetic + arithmetic ::= * arithmetic STAR arithmetic + arithmetic ::= * arithmetic SLASH arithmetic + arithmetic ::= arithmetic SLASH * arithmetic + arithmetic ::= * arithmetic MOD arithmetic + arithmetic ::= * unary + unary ::= * MINUS unary + unary ::= * term + term ::= * TRUE_LITERAL + term ::= * FALSE_LITERAL + term ::= * INTEGER_LITERAL + term ::= * STRING_LITERAL + term ::= * SIZE_LITERAL + term ::= * DATETIME_LITERAL + term ::= * DURATION_LITERAL + term ::= * VERSION_LITERAL + term ::= * IDENTIFIER LPAREN RPAREN + term ::= * IDENTIFIER LPAREN expr_list RPAREN + term ::= * IDENTIFIER + term ::= * LPAREN expr RPAREN + + TRUE_LITERAL shift-reduce 19 term ::= TRUE_LITERAL + FALSE_LITERAL shift-reduce 20 term ::= FALSE_LITERAL + INTEGER_LITERAL shift-reduce 21 term ::= INTEGER_LITERAL + STRING_LITERAL shift-reduce 22 term ::= STRING_LITERAL + SIZE_LITERAL shift-reduce 23 term ::= SIZE_LITERAL + DATETIME_LITERAL shift-reduce 24 term ::= DATETIME_LITERAL + DURATION_LITERAL shift-reduce 25 term ::= DURATION_LITERAL + VERSION_LITERAL shift-reduce 26 term ::= VERSION_LITERAL + IDENTIFIER shift 39 + LPAREN shift 2 + MINUS shift 21 + arithmetic shift-reduce 16 arithmetic ::= arithmetic SLASH arithmetic + unary shift-reduce 16 arithmetic ::= arithmetic SLASH arithmetic /* because unary==arithmetic */ + term shift-reduce 16 arithmetic ::= arithmetic SLASH arithmetic /* because term==unary */ + +State 17: + arithmetic ::= * arithmetic PLUS arithmetic + arithmetic ::= * arithmetic MINUS arithmetic + arithmetic ::= * arithmetic STAR arithmetic + arithmetic ::= arithmetic STAR * arithmetic + arithmetic ::= * arithmetic SLASH arithmetic + arithmetic ::= * arithmetic MOD arithmetic + arithmetic ::= * unary + unary ::= * MINUS unary + unary ::= * term + term ::= * TRUE_LITERAL + term ::= * FALSE_LITERAL + term ::= * INTEGER_LITERAL + term ::= * STRING_LITERAL + term ::= * SIZE_LITERAL + term ::= * DATETIME_LITERAL + term ::= * DURATION_LITERAL + term ::= * VERSION_LITERAL + term ::= * IDENTIFIER LPAREN RPAREN + term ::= * IDENTIFIER LPAREN expr_list RPAREN + term ::= * IDENTIFIER + term ::= * LPAREN expr RPAREN + + TRUE_LITERAL shift-reduce 19 term ::= TRUE_LITERAL + FALSE_LITERAL shift-reduce 20 term ::= FALSE_LITERAL + INTEGER_LITERAL shift-reduce 21 term ::= INTEGER_LITERAL + STRING_LITERAL shift-reduce 22 term ::= STRING_LITERAL + SIZE_LITERAL shift-reduce 23 term ::= SIZE_LITERAL + DATETIME_LITERAL shift-reduce 24 term ::= DATETIME_LITERAL + DURATION_LITERAL shift-reduce 25 term ::= DURATION_LITERAL + VERSION_LITERAL shift-reduce 26 term ::= VERSION_LITERAL + IDENTIFIER shift 39 + LPAREN shift 2 + MINUS shift 21 + arithmetic shift-reduce 15 arithmetic ::= arithmetic STAR arithmetic + unary shift-reduce 15 arithmetic ::= arithmetic STAR arithmetic /* because unary==arithmetic */ + term shift-reduce 15 arithmetic ::= arithmetic STAR arithmetic /* because term==unary */ + +State 18: + arithmetic ::= * arithmetic PLUS arithmetic + arithmetic ::= * arithmetic MINUS arithmetic + arithmetic ::= arithmetic MINUS * arithmetic + arithmetic ::= * arithmetic STAR arithmetic + arithmetic ::= * arithmetic SLASH arithmetic + arithmetic ::= * arithmetic MOD arithmetic + arithmetic ::= * unary + unary ::= * MINUS unary + unary ::= * term + term ::= * TRUE_LITERAL + term ::= * FALSE_LITERAL + term ::= * INTEGER_LITERAL + term ::= * STRING_LITERAL + term ::= * SIZE_LITERAL + term ::= * DATETIME_LITERAL + term ::= * DURATION_LITERAL + term ::= * VERSION_LITERAL + term ::= * IDENTIFIER LPAREN RPAREN + term ::= * IDENTIFIER LPAREN expr_list RPAREN + term ::= * IDENTIFIER + term ::= * LPAREN expr RPAREN + + TRUE_LITERAL shift-reduce 19 term ::= TRUE_LITERAL + FALSE_LITERAL shift-reduce 20 term ::= FALSE_LITERAL + INTEGER_LITERAL shift-reduce 21 term ::= INTEGER_LITERAL + STRING_LITERAL shift-reduce 22 term ::= STRING_LITERAL + SIZE_LITERAL shift-reduce 23 term ::= SIZE_LITERAL + DATETIME_LITERAL shift-reduce 24 term ::= DATETIME_LITERAL + DURATION_LITERAL shift-reduce 25 term ::= DURATION_LITERAL + VERSION_LITERAL shift-reduce 26 term ::= VERSION_LITERAL + IDENTIFIER shift 39 + LPAREN shift 2 + MINUS shift 21 + arithmetic shift 32 + unary shift 32 /* because unary==arithmetic */ + term shift 32 /* because term==unary */ + +State 19: + arithmetic ::= * arithmetic PLUS arithmetic + arithmetic ::= arithmetic PLUS * arithmetic + arithmetic ::= * arithmetic MINUS arithmetic + arithmetic ::= * arithmetic STAR arithmetic + arithmetic ::= * arithmetic SLASH arithmetic + arithmetic ::= * arithmetic MOD arithmetic + arithmetic ::= * unary + unary ::= * MINUS unary + unary ::= * term + term ::= * TRUE_LITERAL + term ::= * FALSE_LITERAL + term ::= * INTEGER_LITERAL + term ::= * STRING_LITERAL + term ::= * SIZE_LITERAL + term ::= * DATETIME_LITERAL + term ::= * DURATION_LITERAL + term ::= * VERSION_LITERAL + term ::= * IDENTIFIER LPAREN RPAREN + term ::= * IDENTIFIER LPAREN expr_list RPAREN + term ::= * IDENTIFIER + term ::= * LPAREN expr RPAREN + + TRUE_LITERAL shift-reduce 19 term ::= TRUE_LITERAL + FALSE_LITERAL shift-reduce 20 term ::= FALSE_LITERAL + INTEGER_LITERAL shift-reduce 21 term ::= INTEGER_LITERAL + STRING_LITERAL shift-reduce 22 term ::= STRING_LITERAL + SIZE_LITERAL shift-reduce 23 term ::= SIZE_LITERAL + DATETIME_LITERAL shift-reduce 24 term ::= DATETIME_LITERAL + DURATION_LITERAL shift-reduce 25 term ::= DURATION_LITERAL + VERSION_LITERAL shift-reduce 26 term ::= VERSION_LITERAL + IDENTIFIER shift 39 + LPAREN shift 2 + MINUS shift 21 + arithmetic shift 33 + unary shift 33 /* because unary==arithmetic */ + term shift 33 /* because term==unary */ + +State 20: + cmp_expr ::= arithmetic EQ * arithmetic + arithmetic ::= * arithmetic PLUS arithmetic + arithmetic ::= * arithmetic MINUS arithmetic + arithmetic ::= * arithmetic STAR arithmetic + arithmetic ::= * arithmetic SLASH arithmetic + arithmetic ::= * arithmetic MOD arithmetic + arithmetic ::= * unary + unary ::= * MINUS unary + unary ::= * term + term ::= * TRUE_LITERAL + term ::= * FALSE_LITERAL + term ::= * INTEGER_LITERAL + term ::= * STRING_LITERAL + term ::= * SIZE_LITERAL + term ::= * DATETIME_LITERAL + term ::= * DURATION_LITERAL + term ::= * VERSION_LITERAL + term ::= * IDENTIFIER LPAREN RPAREN + term ::= * IDENTIFIER LPAREN expr_list RPAREN + term ::= * IDENTIFIER + term ::= * LPAREN expr RPAREN + + TRUE_LITERAL shift-reduce 19 term ::= TRUE_LITERAL + FALSE_LITERAL shift-reduce 20 term ::= FALSE_LITERAL + INTEGER_LITERAL shift-reduce 21 term ::= INTEGER_LITERAL + STRING_LITERAL shift-reduce 22 term ::= STRING_LITERAL + SIZE_LITERAL shift-reduce 23 term ::= SIZE_LITERAL + DATETIME_LITERAL shift-reduce 24 term ::= DATETIME_LITERAL + DURATION_LITERAL shift-reduce 25 term ::= DURATION_LITERAL + VERSION_LITERAL shift-reduce 26 term ::= VERSION_LITERAL + IDENTIFIER shift 39 + LPAREN shift 2 + MINUS shift 21 + arithmetic shift 31 + unary shift 31 /* because unary==arithmetic */ + term shift 31 /* because term==unary */ + +State 21: + unary ::= * MINUS unary + unary ::= MINUS * unary + unary ::= * term + term ::= * TRUE_LITERAL + term ::= * FALSE_LITERAL + term ::= * INTEGER_LITERAL + term ::= * STRING_LITERAL + term ::= * SIZE_LITERAL + term ::= * DATETIME_LITERAL + term ::= * DURATION_LITERAL + term ::= * VERSION_LITERAL + term ::= * IDENTIFIER LPAREN RPAREN + term ::= * IDENTIFIER LPAREN expr_list RPAREN + term ::= * IDENTIFIER + term ::= * LPAREN expr RPAREN + + TRUE_LITERAL shift-reduce 19 term ::= TRUE_LITERAL + FALSE_LITERAL shift-reduce 20 term ::= FALSE_LITERAL + INTEGER_LITERAL shift-reduce 21 term ::= INTEGER_LITERAL + STRING_LITERAL shift-reduce 22 term ::= STRING_LITERAL + SIZE_LITERAL shift-reduce 23 term ::= SIZE_LITERAL + DATETIME_LITERAL shift-reduce 24 term ::= DATETIME_LITERAL + DURATION_LITERAL shift-reduce 25 term ::= DURATION_LITERAL + VERSION_LITERAL shift-reduce 26 term ::= VERSION_LITERAL + IDENTIFIER shift 39 + LPAREN shift 2 + MINUS shift 21 + unary shift-reduce 18 unary ::= MINUS unary + term shift-reduce 18 unary ::= MINUS unary /* because term==unary */ + +State 22: + cmp_expr ::= arithmetic * EQ arithmetic + cmp_expr ::= arithmetic * NE arithmetic + cmp_expr ::= arithmetic * LT arithmetic + cmp_expr ::= arithmetic * LE arithmetic + cmp_expr ::= arithmetic * GT arithmetic + cmp_expr ::= arithmetic * GE arithmetic + cmp_expr ::= arithmetic * CONTAINS arithmetic + cmp_expr ::= arithmetic * RECONTAINS arithmetic + cmp_expr ::= arithmetic * MATCHES arithmetic + (36) cmp_expr ::= arithmetic * + arithmetic ::= arithmetic * PLUS arithmetic + arithmetic ::= arithmetic * MINUS arithmetic + arithmetic ::= arithmetic * STAR arithmetic + arithmetic ::= arithmetic * SLASH arithmetic + arithmetic ::= arithmetic * MOD arithmetic + + EQ shift 20 + NE shift 14 + LT shift 13 + LE shift 12 + GT shift 11 + GE shift 10 + CONTAINS shift 9 + RECONTAINS shift 8 + MATCHES shift 7 + PLUS shift 19 + MINUS shift 18 + STAR shift 17 + SLASH shift 16 + MOD shift 15 + {default} reduce 36 cmp_expr ::= arithmetic + +State 23: + (12) cmp_expr ::= arithmetic MATCHES arithmetic * + arithmetic ::= arithmetic * PLUS arithmetic + arithmetic ::= arithmetic * MINUS arithmetic + arithmetic ::= arithmetic * STAR arithmetic + arithmetic ::= arithmetic * SLASH arithmetic + arithmetic ::= arithmetic * MOD arithmetic + + PLUS shift 19 + MINUS shift 18 + STAR shift 17 + SLASH shift 16 + MOD shift 15 + {default} reduce 12 cmp_expr ::= arithmetic MATCHES arithmetic + +State 24: + (11) cmp_expr ::= arithmetic RECONTAINS arithmetic * + arithmetic ::= arithmetic * PLUS arithmetic + arithmetic ::= arithmetic * MINUS arithmetic + arithmetic ::= arithmetic * STAR arithmetic + arithmetic ::= arithmetic * SLASH arithmetic + arithmetic ::= arithmetic * MOD arithmetic + + PLUS shift 19 + MINUS shift 18 + STAR shift 17 + SLASH shift 16 + MOD shift 15 + {default} reduce 11 cmp_expr ::= arithmetic RECONTAINS arithmetic + +State 25: + (10) cmp_expr ::= arithmetic CONTAINS arithmetic * + arithmetic ::= arithmetic * PLUS arithmetic + arithmetic ::= arithmetic * MINUS arithmetic + arithmetic ::= arithmetic * STAR arithmetic + arithmetic ::= arithmetic * SLASH arithmetic + arithmetic ::= arithmetic * MOD arithmetic + + PLUS shift 19 + MINUS shift 18 + STAR shift 17 + SLASH shift 16 + MOD shift 15 + {default} reduce 10 cmp_expr ::= arithmetic CONTAINS arithmetic + +State 26: + (9) cmp_expr ::= arithmetic GE arithmetic * + arithmetic ::= arithmetic * PLUS arithmetic + arithmetic ::= arithmetic * MINUS arithmetic + arithmetic ::= arithmetic * STAR arithmetic + arithmetic ::= arithmetic * SLASH arithmetic + arithmetic ::= arithmetic * MOD arithmetic + + PLUS shift 19 + MINUS shift 18 + STAR shift 17 + SLASH shift 16 + MOD shift 15 + {default} reduce 9 cmp_expr ::= arithmetic GE arithmetic + +State 27: + (8) cmp_expr ::= arithmetic GT arithmetic * + arithmetic ::= arithmetic * PLUS arithmetic + arithmetic ::= arithmetic * MINUS arithmetic + arithmetic ::= arithmetic * STAR arithmetic + arithmetic ::= arithmetic * SLASH arithmetic + arithmetic ::= arithmetic * MOD arithmetic + + PLUS shift 19 + MINUS shift 18 + STAR shift 17 + SLASH shift 16 + MOD shift 15 + {default} reduce 8 cmp_expr ::= arithmetic GT arithmetic + +State 28: + (7) cmp_expr ::= arithmetic LE arithmetic * + arithmetic ::= arithmetic * PLUS arithmetic + arithmetic ::= arithmetic * MINUS arithmetic + arithmetic ::= arithmetic * STAR arithmetic + arithmetic ::= arithmetic * SLASH arithmetic + arithmetic ::= arithmetic * MOD arithmetic + + PLUS shift 19 + MINUS shift 18 + STAR shift 17 + SLASH shift 16 + MOD shift 15 + {default} reduce 7 cmp_expr ::= arithmetic LE arithmetic + +State 29: + (6) cmp_expr ::= arithmetic LT arithmetic * + arithmetic ::= arithmetic * PLUS arithmetic + arithmetic ::= arithmetic * MINUS arithmetic + arithmetic ::= arithmetic * STAR arithmetic + arithmetic ::= arithmetic * SLASH arithmetic + arithmetic ::= arithmetic * MOD arithmetic + + PLUS shift 19 + MINUS shift 18 + STAR shift 17 + SLASH shift 16 + MOD shift 15 + {default} reduce 6 cmp_expr ::= arithmetic LT arithmetic + +State 30: + (5) cmp_expr ::= arithmetic NE arithmetic * + arithmetic ::= arithmetic * PLUS arithmetic + arithmetic ::= arithmetic * MINUS arithmetic + arithmetic ::= arithmetic * STAR arithmetic + arithmetic ::= arithmetic * SLASH arithmetic + arithmetic ::= arithmetic * MOD arithmetic + + PLUS shift 19 + MINUS shift 18 + STAR shift 17 + SLASH shift 16 + MOD shift 15 + {default} reduce 5 cmp_expr ::= arithmetic NE arithmetic + +State 31: + (4) cmp_expr ::= arithmetic EQ arithmetic * + arithmetic ::= arithmetic * PLUS arithmetic + arithmetic ::= arithmetic * MINUS arithmetic + arithmetic ::= arithmetic * STAR arithmetic + arithmetic ::= arithmetic * SLASH arithmetic + arithmetic ::= arithmetic * MOD arithmetic + + PLUS shift 19 + MINUS shift 18 + STAR shift 17 + SLASH shift 16 + MOD shift 15 + {default} reduce 4 cmp_expr ::= arithmetic EQ arithmetic + +State 32: + arithmetic ::= arithmetic * PLUS arithmetic + arithmetic ::= arithmetic * MINUS arithmetic + (14) arithmetic ::= arithmetic MINUS arithmetic * + arithmetic ::= arithmetic * STAR arithmetic + arithmetic ::= arithmetic * SLASH arithmetic + arithmetic ::= arithmetic * MOD arithmetic + + STAR shift 17 + SLASH shift 16 + MOD shift 15 + {default} reduce 14 arithmetic ::= arithmetic MINUS arithmetic + +State 33: + arithmetic ::= arithmetic * PLUS arithmetic + (13) arithmetic ::= arithmetic PLUS arithmetic * + arithmetic ::= arithmetic * MINUS arithmetic + arithmetic ::= arithmetic * STAR arithmetic + arithmetic ::= arithmetic * SLASH arithmetic + arithmetic ::= arithmetic * MOD arithmetic + + STAR shift 17 + SLASH shift 16 + MOD shift 15 + {default} reduce 13 arithmetic ::= arithmetic PLUS arithmetic + +State 34: + term ::= IDENTIFIER LPAREN expr_list * RPAREN + expr_list ::= expr_list * COMMA expr + + RPAREN shift-reduce 28 term ::= IDENTIFIER LPAREN expr_list RPAREN + COMMA shift 3 + +State 35: + (0) filter_expr ::= or_expr * + or_expr ::= or_expr * OR and_expr + + $ reduce 0 filter_expr ::= or_expr + OR shift 4 + +State 36: + term ::= LPAREN expr * RPAREN + + RPAREN shift-reduce 30 term ::= LPAREN expr RPAREN + +State 37: + (33) or_expr ::= and_expr * + and_expr ::= and_expr * AND not_expr + + AND shift 6 + {default} reduce 33 or_expr ::= and_expr + +State 38: + or_expr ::= or_expr * OR and_expr + (38) expr ::= or_expr * + + OR shift 4 + {default} reduce 38 expr ::= or_expr + +State 39: + term ::= IDENTIFIER * LPAREN RPAREN + term ::= IDENTIFIER * LPAREN expr_list RPAREN + (29) term ::= IDENTIFIER * + + LPAREN shift 1 + {default} reduce 29 term ::= IDENTIFIER + +State 40: + (1) or_expr ::= or_expr OR and_expr * + and_expr ::= and_expr * AND not_expr + + AND shift 6 + {default} reduce 1 or_expr ::= or_expr OR and_expr + +---------------------------------------------------- +Symbols: +The first-set of non-terminals is shown after the name. + + 0: $: + 1: AND (precedence=2) + 2: OR (precedence=1) + 3: NOT (precedence=4) + 4: TRUE_LITERAL + 5: FALSE_LITERAL + 6: INTEGER_LITERAL + 7: STRING_LITERAL + 8: SIZE_LITERAL + 9: DATETIME_LITERAL + 10: DURATION_LITERAL + 11: VERSION_LITERAL + 12: IDENTIFIER + 13: EQ (precedence=3) + 14: NE (precedence=3) + 15: LT (precedence=3) + 16: LE (precedence=3) + 17: GT (precedence=3) + 18: GE (precedence=3) + 19: CONTAINS (precedence=3) + 20: RECONTAINS + 21: MATCHES (precedence=3) + 22: LPAREN + 23: RPAREN + 24: PLUS (precedence=5) + 25: MINUS (precedence=5) + 26: STAR (precedence=6) + 27: SLASH (precedence=6) + 28: MOD (precedence=6) + 29: COMMA + 30: filter_expr: NOT TRUE_LITERAL FALSE_LITERAL INTEGER_LITERAL STRING_LITERAL SIZE_LITERAL DATETIME_LITERAL DURATION_LITERAL VERSION_LITERAL IDENTIFIER LPAREN MINUS + 31: or_expr: NOT TRUE_LITERAL FALSE_LITERAL INTEGER_LITERAL STRING_LITERAL SIZE_LITERAL DATETIME_LITERAL DURATION_LITERAL VERSION_LITERAL IDENTIFIER LPAREN MINUS + 32: and_expr: NOT TRUE_LITERAL FALSE_LITERAL INTEGER_LITERAL STRING_LITERAL SIZE_LITERAL DATETIME_LITERAL DURATION_LITERAL VERSION_LITERAL IDENTIFIER LPAREN MINUS + 33: not_expr: NOT TRUE_LITERAL FALSE_LITERAL INTEGER_LITERAL STRING_LITERAL SIZE_LITERAL DATETIME_LITERAL DURATION_LITERAL VERSION_LITERAL IDENTIFIER LPAREN MINUS + 34: cmp_expr: TRUE_LITERAL FALSE_LITERAL INTEGER_LITERAL STRING_LITERAL SIZE_LITERAL DATETIME_LITERAL DURATION_LITERAL VERSION_LITERAL IDENTIFIER LPAREN MINUS + 35: arithmetic: TRUE_LITERAL FALSE_LITERAL INTEGER_LITERAL STRING_LITERAL SIZE_LITERAL DATETIME_LITERAL DURATION_LITERAL VERSION_LITERAL IDENTIFIER LPAREN MINUS + 36: unary: TRUE_LITERAL FALSE_LITERAL INTEGER_LITERAL STRING_LITERAL SIZE_LITERAL DATETIME_LITERAL DURATION_LITERAL VERSION_LITERAL IDENTIFIER LPAREN MINUS + 37: expr: NOT TRUE_LITERAL FALSE_LITERAL INTEGER_LITERAL STRING_LITERAL SIZE_LITERAL DATETIME_LITERAL DURATION_LITERAL VERSION_LITERAL IDENTIFIER LPAREN MINUS + 38: term: TRUE_LITERAL FALSE_LITERAL INTEGER_LITERAL STRING_LITERAL SIZE_LITERAL DATETIME_LITERAL DURATION_LITERAL VERSION_LITERAL IDENTIFIER LPAREN + 39: expr_list: NOT TRUE_LITERAL FALSE_LITERAL INTEGER_LITERAL STRING_LITERAL SIZE_LITERAL DATETIME_LITERAL DURATION_LITERAL VERSION_LITERAL IDENTIFIER LPAREN MINUS +---------------------------------------------------- +Syntax-only Symbols: +The following symbols never carry semantic content. + +$ AND OR NOT TRUE_LITERAL FALSE_LITERAL EQ NE LT LE GT GE CONTAINS +RECONTAINS MATCHES LPAREN RPAREN PLUS MINUS STAR SLASH MOD COMMA filter_expr +---------------------------------------------------- +Rules: + 0: filter_expr ::= or_expr. + 1: or_expr ::= or_expr OR and_expr. [OR precedence=1] + 2: and_expr ::= and_expr AND not_expr. [AND precedence=2] + 3: not_expr ::= NOT not_expr. [NOT precedence=4] + 4: cmp_expr ::= arithmetic EQ arithmetic. [EQ precedence=3] + 5: cmp_expr ::= arithmetic NE arithmetic. [NE precedence=3] + 6: cmp_expr ::= arithmetic LT arithmetic. [LT precedence=3] + 7: cmp_expr ::= arithmetic LE arithmetic. [LE precedence=3] + 8: cmp_expr ::= arithmetic GT arithmetic. [GT precedence=3] + 9: cmp_expr ::= arithmetic GE arithmetic. [GE precedence=3] + 10: cmp_expr ::= arithmetic CONTAINS arithmetic. [CONTAINS precedence=3] + 11: cmp_expr ::= arithmetic RECONTAINS arithmetic. + 12: cmp_expr ::= arithmetic MATCHES arithmetic. [MATCHES precedence=3] + 13: arithmetic ::= arithmetic PLUS arithmetic. [PLUS precedence=5] + 14: arithmetic ::= arithmetic MINUS arithmetic. [MINUS precedence=5] + 15: arithmetic ::= arithmetic STAR arithmetic. [STAR precedence=6] + 16: arithmetic ::= arithmetic SLASH arithmetic. [SLASH precedence=6] + 17: arithmetic ::= arithmetic MOD arithmetic. [MOD precedence=6] + 18: unary ::= MINUS unary. [MINUS precedence=5] + 19: term ::= TRUE_LITERAL. + 20: term ::= FALSE_LITERAL. + 21: term ::= INTEGER_LITERAL. + 22: term ::= STRING_LITERAL. + 23: term ::= SIZE_LITERAL. + 24: term ::= DATETIME_LITERAL. + 25: term ::= DURATION_LITERAL. + 26: term ::= VERSION_LITERAL. + 27: term ::= IDENTIFIER LPAREN RPAREN. + 28: term ::= IDENTIFIER LPAREN expr_list RPAREN. + 29: term ::= IDENTIFIER. + 30: term ::= LPAREN expr RPAREN. + 31: expr_list ::= expr. + 32: expr_list ::= expr_list COMMA expr. + 33: or_expr ::= and_expr. + 34: and_expr ::= not_expr. + 35: not_expr ::= cmp_expr. + 36: cmp_expr ::= arithmetic. + 37: arithmetic ::= unary. + 38: expr ::= or_expr. + 39: unary ::= term. diff --git a/Src/FilterEngine/FilterParser.y b/Src/FilterEngine/FilterParser.y new file mode 100644 index 00000000000..417d49d1d79 --- /dev/null +++ b/Src/FilterEngine/FilterParser.y @@ -0,0 +1,168 @@ +/** + * @file FilterParser.y + * + * @brief Parser for filter expressions. + */ +%token AND OR NOT TRUE_LITERAL FALSE_LITERAL INTEGER_LITERAL STRING_LITERAL SIZE_LITERAL DATETIME_LITERAL DURATION_LITERAL VERSION_LITERAL IDENTIFIER EQ NE LT LE GT GE CONTAINS RECONTAINS MATCHES LPAREN RPAREN PLUS MINUS STAR SLASH MOD COMMA. + +%left OR. +%left AND. +%left EQ NE LT LE GT GE CONTAINS MATCHES. +%right NOT. +%left PLUS MINUS. +%left STAR SLASH MOD. + +%token_type {YYSTYPE} +%token_prefix TK_ +%extra_argument { FilterExpression* pCtx } + +%include { +#include "FilterLexer.h" +#include "FilterExpressionNodes.h" +#include "FilterExpression.h" +#include +} + +%syntax_error { + pCtx->errorCode = FILTER_ERROR_SYNTAX_ERROR; +} +%parse_failure { + pCtx->errorCode = FILTER_ERROR_PARSE_FAILURE; +} +%default_destructor { + YYSTYPEDestructor($$); +} + +filter_expr ::= or_expr(A). { + if (pCtx->errorCode == 0 && pCtx->optimize) + { + try + { + pCtx->rootNode.reset(A.node->Optimize()); + } + catch (Poco::RegularExpressionException& e) + { + pCtx->errorCode = FILTER_ERROR_INVALID_REGULAR_EXPRESSION; + pCtx->rootNode.reset(A.node); + pCtx->errorMessage = e.message(); + } + catch (const std::exception&) + { + pCtx->rootNode.reset(A.node); + } + } + else + { + pCtx->rootNode.reset(A.node); + } +} + +or_expr(A) ::= or_expr(B) OR and_expr(C). { A = { new OrNode(B.node, C.node) }; } +or_expr(A) ::= and_expr(A). + +and_expr(A) ::= and_expr(B) AND not_expr(C). { A = { new AndNode(B.node, C.node) }; } +and_expr(A) ::= not_expr(A). + +not_expr(A) ::= NOT not_expr(B). { A = { new NotNode(B.node) }; } +not_expr(A) ::= cmp_expr(A). + +cmp_expr(A) ::= arithmetic(B) EQ arithmetic(C). { A = { new BinaryOpNode(B.node, TK_EQ, C.node) }; } +cmp_expr(A) ::= arithmetic(B) NE arithmetic(C). { A = { new BinaryOpNode(B.node, TK_NE, C.node) }; } +cmp_expr(A) ::= arithmetic(B) LT arithmetic(C). { A = { new BinaryOpNode(B.node, TK_LT, C.node) }; } +cmp_expr(A) ::= arithmetic(B) LE arithmetic(C). { A = { new BinaryOpNode(B.node, TK_LE, C.node) }; } +cmp_expr(A) ::= arithmetic(B) GT arithmetic(C). { A = { new BinaryOpNode(B.node, TK_GT, C.node) }; } +cmp_expr(A) ::= arithmetic(B) GE arithmetic(C). { A = { new BinaryOpNode(B.node, TK_GE, C.node) }; } +cmp_expr(A) ::= arithmetic(B) CONTAINS arithmetic(C). { A = { new BinaryOpNode(B.node, TK_CONTAINS, C.node) }; } +cmp_expr(A) ::= arithmetic(B) RECONTAINS arithmetic(C). { A = { new BinaryOpNode(B.node, TK_RECONTAINS, C.node) }; } +cmp_expr(A) ::= arithmetic(B) MATCHES arithmetic(C). { A = { new BinaryOpNode(B.node, TK_MATCHES, C.node) }; } +cmp_expr(A) ::= arithmetic(A). + +arithmetic(A) ::= arithmetic(B) PLUS arithmetic(C). { A = { new BinaryOpNode(B.node, TK_PLUS, C.node) }; } +arithmetic(A) ::= arithmetic(B) MINUS arithmetic(C). { A = { new BinaryOpNode(B.node, TK_MINUS, C.node) }; } +arithmetic(A) ::= arithmetic(B) STAR arithmetic(C). { A = { new BinaryOpNode(B.node, TK_STAR, C.node) }; } +arithmetic(A) ::= arithmetic(B) SLASH arithmetic(C). { A = { new BinaryOpNode(B.node, TK_SLASH, C.node) }; } +arithmetic(A) ::= arithmetic(B) MOD arithmetic(C). { A = { new BinaryOpNode(B.node, TK_MOD, C.node) }; } +arithmetic(A) ::= unary(A). + +expr(A) ::= or_expr(A). + +unary(A) ::= MINUS unary(B). { A = { new NegateNode(B.node) }; } +unary(A) ::= term(A). + +term(A) ::= TRUE_LITERAL. { A = { new BoolLiteral(true) }; } +term(A) ::= FALSE_LITERAL. { A = { new BoolLiteral(false) }; } +term(A) ::= INTEGER_LITERAL(B). { A = { new IntLiteral(B.integer) }; } +term(A) ::= STRING_LITERAL(B). { A = { new StringLiteral(B.string) }; } +term(A) ::= SIZE_LITERAL(B). { A = { new SizeLiteral(B.string) }; } +term(A) ::= DATETIME_LITERAL(B).{ + try + { + A = {}; + A.node = new DateTimeLiteral(B.string); + } + catch (const std::exception&) + { + pCtx->errorCode = FILTER_ERROR_INVALID_LITERAL; + } +} +term(A) ::= DURATION_LITERAL(B). { A = { new DurationLiteral(B.string) }; } +term(A) ::= VERSION_LITERAL(B). { A = { new VersionLiteral(B.string) }; } +term(A) ::= IDENTIFIER(B) LPAREN RPAREN. { + try + { + A = {}; + A.node = new FunctionNode(pCtx, B.string, {}); + } + catch (const std::invalid_argument& e) + { + pCtx->errorCode = FILTER_ERROR_INVALID_ARGUMENT_COUNT; + pCtx->errorMessage = e.what(); + } + catch (const std::runtime_error& e) + { + pCtx->errorCode = FILTER_ERROR_UNDEFINED_IDENTIFIER; + pCtx->errorMessage = e.what(); + } +} +term(A) ::= IDENTIFIER(B) LPAREN expr_list(C) RPAREN. { + try + { + A = {}; + A.node = new FunctionNode(pCtx, B.string, C.nodeList); + } + catch (const std::invalid_argument& e) + { + pCtx->errorCode = FILTER_ERROR_INVALID_ARGUMENT_COUNT; + pCtx->errorMessage = e.what(); + YYSTYPEDestructor(C); + } + catch (const std::runtime_error& e) + { + pCtx->errorCode = FILTER_ERROR_UNDEFINED_IDENTIFIER; + pCtx->errorMessage = e.what(); + YYSTYPEDestructor(C); + } +} +term(A) ::= IDENTIFIER(B). { + try + { + A = {}; + A.node = new FieldNode(pCtx, B.string); + } + catch (const std::exception&) + { + pCtx->errorCode = FILTER_ERROR_UNDEFINED_IDENTIFIER; + } +} +term(A) ::= LPAREN expr(B) RPAREN. { A = B; } + +expr_list(A) ::= expr(B). { + A = {}; + A.nodeList = new std::vector{ B.node }; +} +expr_list(A) ::= expr_list(B) COMMA expr(C). { + A = {}; + B.nodeList->push_back(C.node); + A.nodeList = B.nodeList; +} + diff --git a/Src/FilterErrorMessages.cpp b/Src/FilterErrorMessages.cpp new file mode 100644 index 00000000000..59c7a836d79 --- /dev/null +++ b/Src/FilterErrorMessages.cpp @@ -0,0 +1,47 @@ +#include "pch.h" +#include "FilterEngine/FilterError.h" +#include "FileFilter.h" +#include "UnicodeString.h" +#include "MergeApp.h" +#include "unicoder.h" + +String GetFilterErrorMessage(FilterErrorCode code) +{ + switch (code) + { + case FILTER_ERROR_NO_ERROR: + return _("No error"); + case FILTER_ERROR_UNKNOWN_CHAR: + return _("Unknown character in filter expression"); + case FILTER_ERROR_UNTERMINATED_STRING: + return _("Unterminated string literal"); + case FILTER_ERROR_SYNTAX_ERROR: + return _("Syntax error in filter expression"); + case FILTER_ERROR_PARSE_FAILURE: + return _("Failed to parse filter expression"); + case FILTER_ERROR_INVALID_LITERAL: + return _("Invalid literal value"); + case FILTER_ERROR_INVALID_REGULAR_EXPRESSION: + return _("Invalid regular expression"); + case FILTER_ERROR_UNDEFINED_IDENTIFIER: + return _("Undefined identifier"); + case FILTER_ERROR_INVALID_ARGUMENT_COUNT: + return _("Invalid number of arguments"); + case FILTER_ERROR_FILTER_NAME_NOT_FOUND: + return _("Filter name not found"); + default: + return _("Unknown error"); + } +} + +String FormatFilterErrorSummary(const FileFilterErrorInfo& fei) +{ + if (fei.errorCode == FILTER_ERROR_NO_ERROR) + return _T(""); + String msg; + msg = GetFilterErrorMessage(fei.errorCode); + if (fei.errorPosition >= 0) + msg += _T(" ") + _("at position") + _T(" ") + strutils::to_str(fei.errorPosition + 1); + msg += _T(": ") + ucr::toTString(fei.srcText); + return msg; +} \ No newline at end of file diff --git a/Src/FilterErrorMessages.h b/Src/FilterErrorMessages.h new file mode 100644 index 00000000000..0412b03bccd --- /dev/null +++ b/Src/FilterErrorMessages.h @@ -0,0 +1,9 @@ +#pragma once + +#include "FilterEngine/FilterError.h" +#include "UnicodeString.h" + +struct FileFilterErrorInfo; + +String GetFilterErrorMessage(FilterErrorCode code); +String FormatFilterErrorSummary(const FileFilterErrorInfo& fei); \ No newline at end of file diff --git a/Src/FilterList.cpp b/Src/FilterList.cpp index 0a54bdf649d..ef2c4f9ad2e 100644 --- a/Src/FilterList.cpp +++ b/Src/FilterList.cpp @@ -6,6 +6,7 @@ #include "pch.h" #include "FilterList.h" +#include "FileFilter.h" #include #include #include @@ -32,14 +33,12 @@ FilterList::~FilterList() * The regular expression is compiled and studied for better performance. * @param [in] regularExpression Regular expression string. * @param [in] encoding Expression encoding. - * @param [in] excluded */ -void FilterList::AddRegExp(const std::string& regularExpression, bool exclude, bool throwIfInvalid) +void FilterList::AddRegExp(const std::string& regularExpression, bool throwIfInvalid) { try { - auto& list = exclude ? m_listExclude : m_list; - list.push_back(filter_item_ptr(new filter_item(regularExpression, RegularExpression::RE_UTF8))); + m_list.push_back(filter_item_ptr(new filter_item(regularExpression, RegularExpression::RE_UTF8))); } catch (Poco::RegularExpressionException& e) { @@ -48,82 +47,50 @@ void FilterList::AddRegExp(const std::string& regularExpression, bool exclude, b } } -/** - * @brief Match string against list of expressions. - * This function matches given @p string against the list of regular - * expressions. The matching ends when first match is found, so all - * expressions may not be matched against. - * @param [in] string string to match. - * @param [in] codepage codepage of string. - * @return true if any of the expressions did match the string. - */ -bool FilterList::Match(const std::string& string, int codepage/*=CP_UTF8*/) +static bool match(const std::vector & list, const std::string& string) { - const size_t count = m_list.size(); - bool retval = m_list.size() == 0; - - // convert string into UTF-8 - ucr::buffer buf(string.length() * 2); - - if (codepage != ucr::CP_UTF_8) - ucr::convert(ucr::NONE, codepage, reinterpret_cast(string.c_str()), - string.length(), ucr::UTF8, ucr::CP_UTF_8, &buf); - - unsigned i = 0; - while (i < count && !retval) + if (list.empty()) + return false; + bool retval = false; + for (const auto& item : list) { - const filter_item_ptr& item = m_list[i]; int result = 0; RegularExpression::Match match; try { - if (buf.size > 0) - result = item->regexp.match(std::string(reinterpret_cast(buf.ptr), buf.size), 0, match); - else - result = item->regexp.match(string, 0, match); + result = item->regexp.match(string, 0, match); } catch (...) { - // TODO: } if (result > 0) { retval = true; + break; } - else - ++i; } + return retval; +} - if (!retval) - return retval; +/** + * @brief Match string against list of expressions. + * This function matches given @p string against the list of regular + * expressions. The matching ends when first match is found, so all + * expressions may not be matched against. + * @param [in] string string to match. + * @param [in] codepage codepage of string. + * @return true if any of the expressions did match the string. + */ +bool FilterList::Match(const std::string& string, int codepage/*=CP_UTF8*/) +{ + // convert string into UTF-8 + ucr::buffer buf(string.length() * 2); - i = 0; - const size_t countExclude = m_listExclude.size(); - while (i < countExclude && retval) - { - const filter_item_ptr& item = m_listExclude[i]; - int result = 0; - RegularExpression::Match match; - try - { - if (buf.size > 0) - result = item->regexp.match(std::string(reinterpret_cast(buf.ptr), buf.size), 0, match); - else - result = item->regexp.match(string, 0, match); - } - catch (...) - { - // TODO: - } - if (result > 0) - { - retval = false; - } - else - ++i; - } + if (codepage != ucr::CP_UTF_8) + ucr::convert(ucr::NONE, codepage, reinterpret_cast(string.c_str()), + string.length(), ucr::UTF8, ucr::CP_UTF_8, &buf); - return retval; + return match(m_list, (buf.size > 0) ? std::string(reinterpret_cast(buf.ptr), buf.size) : string); } /** @@ -145,13 +112,4 @@ void FilterList::CloneFrom(const FilterList* filterList) { m_list.emplace_back(std::make_shared(filterList->m_list[i].get())); } - - m_listExclude.clear(); - - count = filterList->m_listExclude.size(); - m_listExclude.reserve(count); - for (size_t i = 0; i < count; i++) - { - m_listExclude.emplace_back(std::make_shared(filterList->m_listExclude[i].get())); - } } diff --git a/Src/FilterList.h b/Src/FilterList.h index 0a3f47a3fe2..c5579951d28 100644 --- a/Src/FilterList.h +++ b/Src/FilterList.h @@ -39,7 +39,7 @@ class FilterList FilterList(); ~FilterList(); - void AddRegExp(const std::string& regularExpression, bool exclude = false, bool throwIfInvalid = false); + void AddRegExp(const std::string& regularExpression, bool throwIfInvalid = false); void RemoveAllFilters(); bool HasRegExps() const; bool Match(const std::string& string, int codepage = ucr::CP_UTF_8); @@ -47,8 +47,6 @@ class FilterList private: std::vector m_list; - std::vector m_listExclude; - }; /** @@ -57,7 +55,6 @@ class FilterList inline void FilterList::RemoveAllFilters() { m_list.clear(); - m_listExclude.clear(); } /** @@ -66,5 +63,5 @@ inline void FilterList::RemoveAllFilters() */ inline bool FilterList::HasRegExps() const { - return !m_list.empty() || !m_listExclude.empty(); + return !m_list.empty(); } diff --git a/Src/LineFiltersList.cpp b/Src/LineFiltersList.cpp index 30bc62a271a..2e82654d375 100644 --- a/Src/LineFiltersList.cpp +++ b/Src/LineFiltersList.cpp @@ -184,7 +184,7 @@ std::shared_ptr LineFiltersList::MakeFilterList(bool throwIfInvalid) { try { - plist->AddRegExp(ucr::toUTF8(item->filterStr), false, throwIfInvalid); + plist->AddRegExp(ucr::toUTF8(item->filterStr), throwIfInvalid); } catch (const std::runtime_error& e) { diff --git a/Src/MainFrm.cpp b/Src/MainFrm.cpp index 133f9cf1754..a2a943d145e 100644 --- a/Src/MainFrm.cpp +++ b/Src/MainFrm.cpp @@ -812,10 +812,8 @@ bool CMainFrame::ShowAutoMergeDoc(UINT nID, IDirDoc * pDirDoc, } FileFilterHelper filterImg, filterBin; const String& imgPatterns = GetOptionsMgr()->GetString(OPT_CMP_IMG_FILEPATTERNS); - filterImg.UseMask(true); filterImg.SetMask(imgPatterns); const String& binPatterns = GetOptionsMgr()->GetString(OPT_CMP_BIN_FILEPATTERNS); - filterBin.UseMask(true); filterBin.SetMask(binPatterns); for (int pane = 0; pane < nFiles; ++pane) { @@ -1269,7 +1267,11 @@ static bool AddToRecentDocs(const PathContext& paths, if (recurse.has_value()) params += *recurse ? _T("/r ") : _T("/r- "); if (!filter.empty()) - params += _T("/f \"") + filter + _T("\" "); + { + String filter2 = filter; + strutils::replace(filter2, _T("\""), _T("\"\"")); + params += _T("/f \"") + filter2 + _T("\" "); + } switch (nID) { case ID_MERGE_COMPARE_TEXT: params += _T("/t text "); break; @@ -2098,7 +2100,6 @@ void CMainFrame::OnToolsFilters() FileFiltersDlg fileFiltersDlg; auto lineFilters = std::make_unique(LineFiltersList()); auto SubstitutionFilters = std::make_unique(SubstitutionFiltersList()); - String selectedFilter; auto* pGlobalFileFilter = theApp.GetGlobalFileFilter(); const String origFilter = pGlobalFileFilter->GetFilterNameOrMask(); sht.AddPage(&fileFiltersDlg); @@ -2109,8 +2110,7 @@ void CMainFrame::OnToolsFilters() // Make sure all filters are up-to-date pGlobalFileFilter->ReloadUpdatedFilters(); - fileFiltersDlg.SetFilterArray(pGlobalFileFilter->GetFileFilters(selectedFilter)); - fileFiltersDlg.SetSelected(selectedFilter); + fileFiltersDlg.SetFileFilterHelper(pGlobalFileFilter); const bool lineFiltersEnabledOrig = GetOptionsMgr()->GetBool(OPT_LINEFILTER_ENABLED); lineFiltersDlg.m_bIgnoreRegExp = lineFiltersEnabledOrig; @@ -2124,28 +2124,8 @@ void CMainFrame::OnToolsFilters() if (sht.DoModal() == IDOK) { - String strNone = _(""); - String path = fileFiltersDlg.GetSelected(); - if (!path.empty()) - { - if (path.find(strNone) != String::npos) - { - // Don't overwrite mask we already have - if (!pGlobalFileFilter->IsUsingMask()) - { - String sFilter(_T("*.*")); - pGlobalFileFilter->SetFilter(sFilter); - GetOptionsMgr()->SaveOption(OPT_FILEFILTER_CURRENT, sFilter); - } - } - else - { - pGlobalFileFilter->SetFileFilterPath(path); - pGlobalFileFilter->UseMask(false); - String sFilter = pGlobalFileFilter->GetFilterNameOrMask(); - GetOptionsMgr()->SaveOption(OPT_FILEFILTER_CURRENT, sFilter); - } - } + String sFilter = pGlobalFileFilter->GetFilterNameOrMask(); + GetOptionsMgr()->SaveOption(OPT_FILEFILTER_CURRENT, sFilter); bool linefiltersEnabled = lineFiltersDlg.m_bIgnoreRegExp; GetOptionsMgr()->SaveOption(OPT_LINEFILTER_ENABLED, linefiltersEnabled); diff --git a/Src/Merge.cpp b/Src/Merge.cpp index 247b0bff831..d662669b014 100644 --- a/Src/Merge.cpp +++ b/Src/Merge.cpp @@ -909,7 +909,7 @@ bool CMergeApp::ParseArgsAndDoOpen(MergeCmdLineInfo& cmdInfo, CMainFrame* pMainF // Set the global file filter. if (!cmdInfo.m_sFileFilter.empty()) { - GetGlobalFileFilter()->SetFilter(cmdInfo.m_sFileFilter); + GetGlobalFileFilter()->SetMask(cmdInfo.m_sFileFilter); } // Set codepage. @@ -1154,14 +1154,7 @@ FileFilterHelper* CMergeApp::GetGlobalFileFilter() InitializeFileFilters(); // Read last used filter from registry - // If filter fails to set, reset to default - const String filterString = m_pOptions->GetString(OPT_FILEFILTER_CURRENT); - bool bFilterSet = m_pGlobalFileFilter->SetFilter(filterString); - if (!bFilterSet) - { - String filter = m_pGlobalFileFilter->GetFilterNameOrMask(); - m_pOptions->SaveOption(OPT_FILEFILTER_CURRENT, filter); - } + m_pGlobalFileFilter->SetMask(m_pOptions->GetString(OPT_FILEFILTER_CURRENT)); } return m_pGlobalFileFilter.get(); @@ -1511,7 +1504,7 @@ bool CMergeApp::LoadAndOpenProjectFile(const String& sProject, const String& sRe { String filter = projItem.GetFilter(); filter = strutils::trim_ws(filter); - GetGlobalFileFilter()->SetFilter(filter); + GetGlobalFileFilter()->SetMask(filter); } bool bRecursive = GetOptionsMgr()->GetBool(OPT_CMP_INCLUDE_SUBDIRS); if (Options::Project::Get(GetOptionsMgr(), Options::Project::Operation::Open, Options::Project::Item::IncludeSubfolders) && projItem.HasSubfolders()) diff --git a/Src/Merge.rc b/Src/Merge.rc index 0f60f4c3ee5..c1fe062f46d 100644 --- a/Src/Merge.rc +++ b/Src/Merge.rc @@ -1541,20 +1541,6 @@ BEGIN PUSHBUTTON "Cancel",IDCANCEL,247,41,70,14 END -IDD_DIR_FILTER DIALOGEX 30, 73, 293, 74 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Find" -FONT 8, FONTNAME, 0, 0, 0x1 -BEGIN - LTEXT "Fi&nd what:",IDC_STATIC,7,9,50,10 - COMBOBOX IDC_EDIT_FINDTEXT,59,7,160,65,CBS_DROPDOWN | CBS_AUTOHSCROLL | WS_VSCROLL | WS_TABSTOP - CONTROL "Match &whole word only",IDC_EDIT_WHOLE_WORD,"Button",BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,7,27,140,10 - CONTROL "Match &case",IDC_EDIT_MATCH_CASE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,37,140,10 - CONTROL "Regular &expression",IDC_EDIT_REGEXP,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,47,140,10 - DEFPUSHBUTTON "&Ok",IDOK,226,7,60,14,WS_GROUP - PUSHBUTTON "Cancel",IDCANCEL,226,24,60,14 -END - IDD_EDIT_REPLACE DIALOGEX 36, 44, 324, 96 STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "Replace" @@ -2043,11 +2029,14 @@ STYLE DS_SETFONT | DS_FIXEDSYS | WS_POPUP | WS_CAPTION CAPTION "File Filters" FONT 8, FONTNAME, 0, 0, 0x1 BEGIN - CONTROL "",IDC_FILTERFILE_LIST,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_NOSORTHEADER | WS_BORDER | WS_TABSTOP,6,6,516,144 + LTEXT "&Mask / Filter Expression",IDC_FILTERFILE_MASK_BTN,6,6,260,10 + CONTROL "",IDC_FILTERFILE_MASK,"ComboBoxEx32",CBS_DROPDOWN | CBS_AUTOHSCROLL | WS_VSCROLL | WS_TABSTOP,6,16,516,95 + LTEXT "Preset &Filters",IDC_FILTERFILE_PRESET_BTN,6,34,260,10 + CONTROL "",IDC_FILTERFILE_LIST,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_NOSORTHEADER | WS_BORDER | WS_TABSTOP,6,44,516,110 PUSHBUTTON "Test...",IDC_FILTERFILE_TEST_BTN,6,156,50,14 - PUSHBUTTON "Install...",IDC_FILTERFILE_INSTALL,305,156,50,14 - PUSHBUTTON "New...",IDC_FILTERFILE_NEWBTN,360,156,50,14 - PUSHBUTTON "Edit...",IDC_FILTERFILE_EDITBTN,414,156,50,14 + PUSHBUTTON "Install...",IDC_FILTERFILE_INSTALL,308,156,50,14 + PUSHBUTTON "New...",IDC_FILTERFILE_NEWBTN,363,156,50,14 + PUSHBUTTON "Edit...",IDC_FILTERFILE_EDITBTN,418,156,50,14 PUSHBUTTON "Delete...",IDC_FILTERFILE_DELETEBTN,473,156,50,14 END @@ -3638,7 +3627,6 @@ BEGIN IDS_FILTERFILE_NAMETITLE "Name" IDS_FILTERFILE_PATHTITLE "Location" IDS_FILTER_TITLE "Filters" - IDS_FILTER_PREFIX "[F] " IDS_FILTERFILE_DESCTITLE "Description" IDS_FILEFILTER_SAVENEW "Select Filename for New Filter" IDS_FILEFILTER_FILEMASK "File Filters (*.flt)|*.flt|All Files (*.*)|*.*||" @@ -4654,6 +4642,21 @@ BEGIN IDS_LOG_FILEOPERATION_CANCELED "File operation canceled" END +STRINGTABLE +BEGIN + IDS_FILTER_ERROR_NO_ERROR "No error" + IDS_FILTER_ERROR_UNKNOWN_CHAR "Unknown character in filter expression" + IDS_FILTER_ERROR_UNTERMINATED_STRING "Unterminated string literal" + IDS_FILTER_ERROR_SYNTAX_ERROR "Syntax error in filter expression" + IDS_FILTER_ERROR_PARSE_FAILURE "Failed to parse filter expression" + IDS_FILTER_ERROR_INVALID_LITERAL "Invalid literal value" + IDS_FILTER_ERROR_INVALID_REGULAR_EXPRESSION "Invalid regular expression" + IDS_FILTER_ERROR_UNDEFINED_IDENTIFIER "Undefined identifier" + IDS_FILTER_ERROR_INVALID_ARGUMENT_COUNT "Invalid number of arguments" + IDS_FILTER_ERROR_FILTER_NAME_NOT_FOUND "Filter name not found" + IDS_FILTER_ERROR_UNKNOWN_ERROR "Unknown error" +END + #endif // English (United States) resources ///////////////////////////////////////////////////////////////////////////// diff --git a/Src/Merge.vcxproj b/Src/Merge.vcxproj index d5c21d148df..655c4aea97a 100644 --- a/Src/Merge.vcxproj +++ b/Src/Merge.vcxproj @@ -181,6 +181,7 @@ + @@ -943,6 +944,10 @@ $(IntDir)$(TargetName)2.pch + + pch.h + $(IntDir)$(TargetName)2.pch + pch.h @@ -956,6 +961,7 @@ pch.h $(IntDir)$(TargetName)2.pch + CppCode diff --git a/Src/Merge.vcxproj.filters b/Src/Merge.vcxproj.filters index 3c27b795908..6efeec1ad77 100644 --- a/Src/Merge.vcxproj.filters +++ b/Src/Merge.vcxproj.filters @@ -768,6 +768,9 @@ MFCGui\Common\Source Files + + Source Files + @@ -1463,6 +1466,9 @@ MFCGui\Common\Header Files + + Header Files + diff --git a/Src/MergeAppLib.h b/Src/MergeAppLib.h index 44dcfec1dc6..df432c4eaa1 100644 --- a/Src/MergeAppLib.h +++ b/Src/MergeAppLib.h @@ -3,12 +3,12 @@ /* this ALWAYS GENERATED file contains the definitions for the interfaces */ - /* File created by MIDL compiler version 8.01.0628 */ -/* at Tue Jan 19 12:14:07 2038 + /* File created by MIDL compiler version 7.00.0555 */ +/* at Wed Jun 18 21:09:30 2025 */ /* Compiler settings for MergeAppLib.idl: - Oicf, W1, Zp8, env=Win64 (32b run), target_arch=AMD64 8.01.0628 - protocol : all , ms_ext, c_ext, robust + Oicf, W1, Zp8, env=Win32 (32b run), target_arch=X86 7.00.0555 + protocol : dce , ms_ext, c_ext, robust error checks: allocation ref bounds_check enum stub_data VC __declspec() decoration level: __declspec(uuid()), __declspec(selectany), __declspec(novtable) @@ -16,11 +16,12 @@ */ /* @@MIDL_FILE_HEADING( ) */ +#pragma warning( disable: 4049 ) /* more than 64k source lines */ /* verify that the version is high enough to compile this file*/ #ifndef __REQUIRED_RPCNDR_H_VERSION__ -#define __REQUIRED_RPCNDR_H_VERSION__ 500 +#define __REQUIRED_RPCNDR_H_VERSION__ 475 #endif #include "rpc.h" @@ -28,7 +29,7 @@ #ifndef __RPCNDR_H_VERSION__ #error this stub requires an updated version of -#endif /* __RPCNDR_H_VERSION__ */ +#endif // __RPCNDR_H_VERSION__ #ifndef COM_NO_WINDOWS_H #include "windows.h" @@ -42,27 +43,17 @@ #pragma once #endif -#ifndef DECLSPEC_XFGVIRT -#if defined(_CONTROL_FLOW_GUARD_XFG) -#define DECLSPEC_XFGVIRT(base, func) __declspec(xfg_virtual(base, func)) -#else -#define DECLSPEC_XFGVIRT(base, func) -#endif -#endif - /* Forward Declarations */ #ifndef __IMergeApp_FWD_DEFINED__ #define __IMergeApp_FWD_DEFINED__ typedef interface IMergeApp IMergeApp; - #endif /* __IMergeApp_FWD_DEFINED__ */ #ifndef __IMergeApp_FWD_DEFINED__ #define __IMergeApp_FWD_DEFINED__ typedef interface IMergeApp IMergeApp; - #endif /* __IMergeApp_FWD_DEFINED__ */ @@ -120,41 +111,34 @@ EXTERN_C const IID IID_IMergeApp; }; - #else /* C style interface */ typedef struct IMergeAppVtbl { BEGIN_INTERFACE - DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IMergeApp * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ - _COM_Outptr_ void **ppvObject); + __RPC__deref_out void **ppvObject); - DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( IMergeApp * This); - DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( IMergeApp * This); - DECLSPEC_XFGVIRT(IDispatch, GetTypeInfoCount) HRESULT ( STDMETHODCALLTYPE *GetTypeInfoCount )( IMergeApp * This, /* [out] */ UINT *pctinfo); - DECLSPEC_XFGVIRT(IDispatch, GetTypeInfo) HRESULT ( STDMETHODCALLTYPE *GetTypeInfo )( IMergeApp * This, /* [in] */ UINT iTInfo, /* [in] */ LCID lcid, /* [out] */ ITypeInfo **ppTInfo); - DECLSPEC_XFGVIRT(IDispatch, GetIDsOfNames) HRESULT ( STDMETHODCALLTYPE *GetIDsOfNames )( IMergeApp * This, /* [in] */ REFIID riid, @@ -163,46 +147,33 @@ EXTERN_C const IID IID_IMergeApp; /* [in] */ LCID lcid, /* [size_is][out] */ DISPID *rgDispId); - DECLSPEC_XFGVIRT(IDispatch, Invoke) /* [local] */ HRESULT ( STDMETHODCALLTYPE *Invoke )( IMergeApp * This, - /* [annotation][in] */ - _In_ DISPID dispIdMember, - /* [annotation][in] */ - _In_ REFIID riid, - /* [annotation][in] */ - _In_ LCID lcid, - /* [annotation][in] */ - _In_ WORD wFlags, - /* [annotation][out][in] */ - _In_ DISPPARAMS *pDispParams, - /* [annotation][out] */ - _Out_opt_ VARIANT *pVarResult, - /* [annotation][out] */ - _Out_opt_ EXCEPINFO *pExcepInfo, - /* [annotation][out] */ - _Out_opt_ UINT *puArgErr); + /* [in] */ DISPID dispIdMember, + /* [in] */ REFIID riid, + /* [in] */ LCID lcid, + /* [in] */ WORD wFlags, + /* [out][in] */ DISPPARAMS *pDispParams, + /* [out] */ VARIANT *pVarResult, + /* [out] */ EXCEPINFO *pExcepInfo, + /* [out] */ UINT *puArgErr); - DECLSPEC_XFGVIRT(IMergeApp, Translate) /* [helpstring][id] */ HRESULT ( STDMETHODCALLTYPE *Translate )( IMergeApp * This, /* [in] */ BSTR text, /* [retval][out] */ BSTR *pRet); - DECLSPEC_XFGVIRT(IMergeApp, GetOption) /* [helpstring][id] */ HRESULT ( STDMETHODCALLTYPE *GetOption )( IMergeApp * This, /* [in] */ BSTR name, /* [in] */ VARIANT varDefault, /* [retval][out] */ VARIANT *pRet); - DECLSPEC_XFGVIRT(IMergeApp, SaveOption) /* [helpstring][id] */ HRESULT ( STDMETHODCALLTYPE *SaveOption )( IMergeApp * This, /* [in] */ BSTR name, /* [in] */ VARIANT varValue); - DECLSPEC_XFGVIRT(IMergeApp, MsgBox) /* [helpstring][id] */ HRESULT ( STDMETHODCALLTYPE *MsgBox )( IMergeApp * This, /* [in] */ BSTR prompt, @@ -210,7 +181,6 @@ EXTERN_C const IID IID_IMergeApp; /* [optional][in] */ VARIANT varTitle, /* [retval][out] */ int *pRet); - DECLSPEC_XFGVIRT(IMergeApp, InputBox) /* [helpstring][id] */ HRESULT ( STDMETHODCALLTYPE *InputBox )( IMergeApp * This, /* [in] */ BSTR prompt, @@ -218,7 +188,6 @@ EXTERN_C const IID IID_IMergeApp; /* [optional][in] */ VARIANT varDefault, /* [retval][out] */ BSTR *pRet); - DECLSPEC_XFGVIRT(IMergeApp, LogError) /* [helpstring][id] */ HRESULT ( STDMETHODCALLTYPE *LogError )( IMergeApp * This, /* [in] */ BSTR text); @@ -312,16 +281,6 @@ unsigned char * __RPC_USER VARIANT_UserMarshal( unsigned long *, unsigned char unsigned char * __RPC_USER VARIANT_UserUnmarshal(unsigned long *, unsigned char *, VARIANT * ); void __RPC_USER VARIANT_UserFree( unsigned long *, VARIANT * ); -unsigned long __RPC_USER BSTR_UserSize64( unsigned long *, unsigned long , BSTR * ); -unsigned char * __RPC_USER BSTR_UserMarshal64( unsigned long *, unsigned char *, BSTR * ); -unsigned char * __RPC_USER BSTR_UserUnmarshal64(unsigned long *, unsigned char *, BSTR * ); -void __RPC_USER BSTR_UserFree64( unsigned long *, BSTR * ); - -unsigned long __RPC_USER VARIANT_UserSize64( unsigned long *, unsigned long , VARIANT * ); -unsigned char * __RPC_USER VARIANT_UserMarshal64( unsigned long *, unsigned char *, VARIANT * ); -unsigned char * __RPC_USER VARIANT_UserUnmarshal64(unsigned long *, unsigned char *, VARIANT * ); -void __RPC_USER VARIANT_UserFree64( unsigned long *, VARIANT * ); - /* end of Additional Prototypes */ #ifdef __cplusplus diff --git a/Src/MergeDoc.cpp b/Src/MergeDoc.cpp index 42e5fb8edec..308f6f6e277 100644 --- a/Src/MergeDoc.cpp +++ b/Src/MergeDoc.cpp @@ -2164,7 +2164,6 @@ CMergeDoc::TableProps CMergeDoc::MakeTablePropertiesByFileName(const String& pat const String& csvFilePattern = GetOptionsMgr()->GetString(OPT_CMP_CSV_FILEPATTERNS); if (!csvFilePattern.empty()) { - filterCSV.UseMask(true); filterCSV.SetMask(csvFilePattern); if (filterCSV.includeFile(path)) return { true, strutils::from_charstr(GetOptionsMgr()->GetString(OPT_CMP_CSV_DELIM_CHAR)), quote, allowNewlineIQuotes }; @@ -2172,7 +2171,6 @@ CMergeDoc::TableProps CMergeDoc::MakeTablePropertiesByFileName(const String& pat const String& tsvFilePattern = GetOptionsMgr()->GetString(OPT_CMP_TSV_FILEPATTERNS); if (!tsvFilePattern.empty()) { - filterTSV.UseMask(true); filterTSV.SetMask(tsvFilePattern); if (filterTSV.includeFile(path)) return { true, '\t', quote, allowNewlineIQuotes }; @@ -2180,7 +2178,6 @@ CMergeDoc::TableProps CMergeDoc::MakeTablePropertiesByFileName(const String& pat const String& dsvFilePattern = GetOptionsMgr()->GetString(OPT_CMP_DSV_FILEPATTERNS); if (!dsvFilePattern.empty()) { - filterDSV.UseMask(true); filterDSV.SetMask(dsvFilePattern); if (filterDSV.includeFile(path)) return { true, strutils::from_charstr(GetOptionsMgr()->GetString(OPT_CMP_DSV_DELIM_CHAR)), quote }; diff --git a/Src/OpenView.cpp b/Src/OpenView.cpp index d3615b986f8..2abdf8299ae 100644 --- a/Src/OpenView.cpp +++ b/Src/OpenView.cpp @@ -278,14 +278,6 @@ void COpenView::OnInitialUpdate() auto* pGlobalFileFilter = theApp.GetGlobalFileFilter(); String filterNameOrMask = pGlobalFileFilter->GetFilterNameOrMask(); - bool bMask = pGlobalFileFilter->IsUsingMask(); - - if (!bMask) - { - String filterPrefix = _("[F] "); - filterNameOrMask = filterPrefix + filterNameOrMask; - } - int ind = m_ctlExt.FindStringExact(0, filterNameOrMask.c_str()); if (ind != CB_ERR) m_ctlExt.SetCurSel(ind); @@ -629,7 +621,7 @@ void COpenView::OnSwapButton() void COpenView::OnCompare(UINT nID) { int pathsType; // enum from paths::PATH_EXISTENCE in paths.h - const String filterPrefix = _("[F] "); + const String filterPrefix = _T("[F] "); auto* pGlobalFileFilter = theApp.GetGlobalFileFilter(); UpdateData(TRUE); @@ -714,21 +706,9 @@ void COpenView::OnCompare(UINT nID) { // Remove prefix + space filter.erase(0, filterPrefix.length()); - if (!pGlobalFileFilter->SetFilter(filter)) - { - // If filtername is not found use default *.* mask - pGlobalFileFilter->SetFilter(_T("*.*")); - filter = _T("*.*"); - } - GetOptionsMgr()->SaveOption(OPT_FILEFILTER_CURRENT, filter); - } - else - { - bool bFilterSet = pGlobalFileFilter->SetFilter(filter); - if (!bFilterSet) - m_strExt = pGlobalFileFilter->GetFilterNameOrMask(); - GetOptionsMgr()->SaveOption(OPT_FILEFILTER_CURRENT, filter); } + pGlobalFileFilter->SetMask(filter); + GetOptionsMgr()->SaveOption(OPT_FILEFILTER_CURRENT, filter); SaveComboboxStates(); GetOptionsMgr()->SaveOption(OPT_CMP_INCLUDE_SUBDIRS, m_bRecurse); @@ -989,7 +969,7 @@ void COpenView::OnSaveProject() if (bSaveFileFilter && !m_strExt.empty()) { // Remove possbile prefix from the filter name - String prefix = _("[F] "); + String prefix = _T("[F] "); String strExt = m_strExt; size_t ind = strExt.find(prefix, 0); if (ind == 0) @@ -1504,25 +1484,15 @@ void COpenView::OnSelectFilter() String curFilter; auto* pGlobalFileFilter = theApp.GetGlobalFileFilter(); - const bool bUseMask = pGlobalFileFilter->IsUsingMask(); GetDlgItemText(IDC_EXT_COMBO, curFilter); curFilter = strutils::trim_ws(curFilter); GetMainFrame()->SelectFilter(); String filterNameOrMask = pGlobalFileFilter->GetFilterNameOrMask(); - if (pGlobalFileFilter->IsUsingMask()) - { - // If we had filter chosen and now has mask we can overwrite filter - if (!bUseMask || curFilter[0] != '*') - { - SetDlgItemText(IDC_EXT_COMBO, filterNameOrMask); - } - } - else + // If we had filter chosen and now has mask we can overwrite filter + if (curFilter != filterNameOrMask) { - String filterPrefix = _("[F] "); - filterNameOrMask = filterPrefix + filterNameOrMask; SetDlgItemText(IDC_EXT_COMBO, filterNameOrMask); } } diff --git a/Src/TestFilterDlg.cpp b/Src/TestFilterDlg.cpp index 6f9c1131046..fffaa381c01 100644 --- a/Src/TestFilterDlg.cpp +++ b/Src/TestFilterDlg.cpp @@ -8,7 +8,7 @@ #include "TestFilterDlg.h" #include "resource.h" #include "UnicodeString.h" -#include "FileFilterMgr.h" +#include "FileFilterHelper.h" #ifdef _DEBUG #define new DEBUG_NEW @@ -17,13 +17,11 @@ /** * @brief Constructor. * @param [in] pParent Parent window. - * @param [in] pFileFilter File filter to test. - * @param [in] pFilterMgr File filter manager. + * @param [in] pFilterHelper File filter helper. */ -CTestFilterDlg::CTestFilterDlg(CWnd* pParent, FileFilter * pFileFilter, FileFilterMgr *pFilterMgr) +CTestFilterDlg::CTestFilterDlg(CWnd* pParent, FileFilterHelper* pFileFilterHelper) : CTrDialog(CTestFilterDlg::IDD, pParent) -, m_pFileFilter(pFileFilter) -, m_pFileFilterMgr(pFilterMgr) +, m_pFileFilterHelper(pFileFilterHelper) { } @@ -55,7 +53,7 @@ BOOL CTestFilterDlg::OnInitDialog() SetDlgItemFocus(IDC_TEST_TEXT); - String name = m_pFileFilterMgr->GetFilterName(m_pFileFilter); + String name = m_pFileFilterHelper->GetFilterNameOrMask(); SetDlgItemText(IDC_HEADER_FILTER_NAME, name); return FALSE; // return TRUE unless you set the focus to a control @@ -88,11 +86,11 @@ bool CTestFilterDlg::CheckText(String text) const { // Convert any forward slashes to canonical Windows-style backslashes strutils::replace(text, _T("/"), _T("\\")); - return m_pFileFilterMgr->TestDirNameAgainstFilter(m_pFileFilter, text); + return m_pFileFilterHelper->includeDir(text); } else { - return m_pFileFilterMgr->TestFileNameAgainstFilter(m_pFileFilter, text); + return m_pFileFilterHelper->includeFile(text); } } diff --git a/Src/TestFilterDlg.h b/Src/TestFilterDlg.h index 5b8a9624f49..4820232c7ee 100644 --- a/Src/TestFilterDlg.h +++ b/Src/TestFilterDlg.h @@ -8,8 +8,7 @@ #include "TrDialogs.h" -struct FileFilter; -class FileFilterMgr; +class FileFilterHelper; /** * @brief Dialog allowing user to test out file filter strings. @@ -19,12 +18,11 @@ class CTestFilterDlg : public CTrDialog { // Construction public: - CTestFilterDlg(CWnd* pParent, FileFilter * pFileFilter, FileFilterMgr *pFilterMgr); + CTestFilterDlg(CWnd* pParent, FileFilterHelper* pFileFilterHelper); // Implementation data private: - FileFilter * m_pFileFilter; /**< Selected file filter. */ - FileFilterMgr * m_pFileFilterMgr; /**< File filter manager. */ + FileFilterHelper * m_pFileFilterHelper; /**< File filter helper. */ // Implementation methods private: diff --git a/Src/resource.h b/Src/resource.h index 3cec37b41f7..14f2f36e518 100644 --- a/Src/resource.h +++ b/Src/resource.h @@ -49,7 +49,6 @@ #define IDD_CONFIRM_COPY 236 #define IDD_PLUGINS_LIST 237 #define IDD_PLUGINS_SELECTPLUGIN 238 -#define IDD_DIR_FILTER 240 #define IDD_ENCODINGERROR 241 #define IDD_SELECT_FILES_OR_FOLDERS 247 #define IDD_DIALOG_WINDOWSMANAGER 251 @@ -594,6 +593,9 @@ #define IDC_JUMP_LIST 1429 #define IDC_CLEAR_ALL_RECENT_ITEMS 1430 #define IDC_ITEMS_PER_SEC 1431 +#define IDC_FILTERFILE_MASK_BTN 1432 +#define IDC_FILTERFILE_PRESET_BTN 1433 +#define IDC_FILTERFILE_MASK 1434 #define IDC_EXPAND_SUBDIRS 1600 #define IDC_FILEENCODING 1601 #define IDC_PLUGIN 1602 @@ -1280,7 +1282,6 @@ #define IDS_FILTERFILE_NAMETITLE 40448 #define IDS_FILTERFILE_PATHTITLE 40449 #define IDS_FILTER_TITLE 40450 -#define IDS_FILTER_PREFIX 40451 #define IDS_FILTERFILE_DESCTITLE 40452 #define IDS_FILEFILTER_SAVENEW 40453 #define IDS_FILEFILTER_FILEMASK 40454 @@ -1819,6 +1820,17 @@ #define IDS_LOG_PERMANENTLY_DELETED 44660 #define IDS_LOG_FILEOPERATION_COMPLETED 44661 #define IDS_LOG_FILEOPERATION_CANCELED 44662 +#define IDS_FILTER_ERROR_NO_ERROR 44663 +#define IDS_FILTER_ERROR_UNKNOWN_CHAR 44664 +#define IDS_FILTER_ERROR_UNTERMINATED_STRING 44665 +#define IDS_FILTER_ERROR_SYNTAX_ERROR 44666 +#define IDS_FILTER_ERROR_PARSE_FAILURE 44667 +#define IDS_FILTER_ERROR_INVALID_LITERAL 44668 +#define IDS_FILTER_ERROR_INVALID_REGULAR_EXPRESSION 44669 +#define IDS_FILTER_ERROR_UNDEFINED_IDENTIFIER 44670 +#define IDS_FILTER_ERROR_INVALID_ARGUMENT_COUNT 44671 +#define IDS_FILTER_ERROR_FILTER_NAME_NOT_FOUND 44672 +#define IDS_FILTER_ERROR_UNKNOWN_ERROR 44673 // Next default values for new objects // diff --git a/Testing/FolderCompare/FolderCompare.cpp b/Testing/FolderCompare/FolderCompare.cpp index 2c5641c3cf6..92da345cc10 100644 --- a/Testing/FolderCompare/FolderCompare.cpp +++ b/Testing/FolderCompare/FolderCompare.cpp @@ -4,6 +4,8 @@ #include "DiffThread.h" #include "DiffWrapper.h" #include "FileFilterHelper.h" +#include "FileFilter.h" +#include "FilterErrorMessages.h" #include "FolderCmp.h" #include "DirScan.h" #include "paths.h" @@ -60,7 +62,6 @@ int main() int dm = CMP_CONTENT; // Default compare method PathContext paths(_T(""), _T("")); // Default empty paths FileFilterHelper filter; - filter.UseMask(true); filter.SetMask(_T("*.*")); std::wcout << L"WinMerge folder comparison test tool\n"; @@ -104,6 +105,16 @@ int main() { std::wstring mask = cmd.substr(2); filter.SetMask(mask.c_str()); + if (filter.GetRegexOrExpressionFilter() && filter.GetRegexOrExpressionFilter()->errors.size() > 0) + { + for (auto error : filter.GetRegexOrExpressionFilter()->errors) + std::wcout << FormatFilterErrorSummary(error) << "\n"; + } + if (filter.GetRegexOrExpressionFilterExclude() && filter.GetRegexOrExpressionFilterExclude()->errors.size() > 0) + { + for (auto error : filter.GetRegexOrExpressionFilterExclude()->errors) + std::wcout << FormatFilterErrorSummary(error) << "\n"; + } } else if (cmd[0] == L'm') // Set method { @@ -143,6 +154,7 @@ int main() ctx.m_pCompareStats = &cmpstats; ctx.m_bRecursive = true; ctx.m_piFilterGlobal = &filter; + filter.SetDiffContext(&ctx); CDiffThread diffThread; diffThread.SetContext(&ctx); @@ -170,14 +182,7 @@ int main() while (pos) { DIFFITEM& di = ctx.GetNextDiffRefPosition(pos); - if ((paths.GetSize() == 2 && ctx.m_piFilterGlobal->includeFile( - paths::ConcatPath(di.diffFileInfo[0].path, di.diffFileInfo[0].filename), - paths::ConcatPath(di.diffFileInfo[1].path, di.diffFileInfo[1].filename)) - || - (paths.GetSize() == 3 && ctx.m_piFilterGlobal->includeFile( - paths::ConcatPath(di.diffFileInfo[0].path, di.diffFileInfo[0].filename), - paths::ConcatPath(di.diffFileInfo[1].path, di.diffFileInfo[1].filename), - paths::ConcatPath(di.diffFileInfo[1].path, di.diffFileInfo[2].filename))))) + if (ctx.m_piFilterGlobal->includeFile(di)) { FolderCmp folderCmp(&ctx); folderCmp.prepAndCompareFiles(di); diff --git a/Testing/FolderCompare/FolderCompare.vcxproj b/Testing/FolderCompare/FolderCompare.vcxproj index ba0806fd011..90553bab8f3 100644 --- a/Testing/FolderCompare/FolderCompare.vcxproj +++ b/Testing/FolderCompare/FolderCompare.vcxproj @@ -126,6 +126,7 @@ + @@ -627,6 +628,10 @@ pch.h $(IntDir)$(TargetName)2.pch + + pch.h + $(IntDir)$(TargetName)2.pch + Use pch.h @@ -657,6 +662,10 @@ pch.h $(IntDir)$(TargetName)2.pch + + pch.h + $(IntDir)$(TargetName)2.pch + Use pch.h @@ -780,30 +789,9 @@ $(IntDir)$(TargetName)2.pch - Create - pch.h - Create - pch.h - Create - pch.h - Create - pch.h - $(IntDir)$(TargetName)2.pch - $(IntDir)$(TargetName)2.pch - $(IntDir)$(TargetName)2.pch - $(IntDir)$(TargetName)2.pch - Create - Create - pch.h - pch.h - $(IntDir)$(TargetName)2.pch - $(IntDir)$(TargetName)2.pch - Create - Create - pch.h - pch.h - $(IntDir)$(TargetName)2.pch - $(IntDir)$(TargetName)2.pch + Create + pch.h + $(IntDir)$(TargetName)2.pch @@ -829,6 +817,7 @@ + @@ -836,6 +825,7 @@ + diff --git a/Testing/FolderCompare/FolderCompare.vcxproj.filters b/Testing/FolderCompare/FolderCompare.vcxproj.filters index 47594a2aa18..8af16918c09 100644 --- a/Testing/FolderCompare/FolderCompare.vcxproj.filters +++ b/Testing/FolderCompare/FolderCompare.vcxproj.filters @@ -177,6 +177,9 @@ Source Files + + Source Files + @@ -350,5 +353,8 @@ Header Files + + Header Files + \ No newline at end of file diff --git a/Testing/GoogleTest/DirWatcher/DirWatcher_test.cpp b/Testing/GoogleTest/DirWatcher/DirWatcher_test.cpp index d0172695d64..72b44348316 100644 --- a/Testing/GoogleTest/DirWatcher/DirWatcher_test.cpp +++ b/Testing/GoogleTest/DirWatcher/DirWatcher_test.cpp @@ -3,7 +3,7 @@ #include #include "UnicodeString.h" #include "DirWatcher.h" -#include +#include "TFile.h" using std::vector; @@ -63,10 +63,12 @@ namespace EXPECT_TRUE(watcher.Add(3, false, L"..\\TestData\\DirWatcher\\test.txt", func)); EXPECT_TRUE(watcher.Add(4, false, L"..\\TestData\\DirWatcher\\test2.txt", func)); - SetFileAttributes(L"..\\TestData\\DirWatcher\\test2.txt", FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_READONLY); - SetFileAttributes(L"..\\TestData\\DirWatcher\\test2.txt", FILE_ATTRIBUTE_NORMAL); - SetFileAttributes(L"..\\TestData\\DirWatcher\\Subdir1\\test.txt", FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_READONLY); - SetFileAttributes(L"..\\TestData\\DirWatcher\\Subdir1\\test.txt", FILE_ATTRIBUTE_NORMAL); + TFile file1(L"..\\TestData\\DirWatcher\\test2.txt"); + file1.setWriteable(false); + file1.setWriteable(true); + TFile file2(L"..\\TestData\\DirWatcher\\Subdir1\\test.txt"); + file2.setWriteable(false); + file2.setWriteable(true); // while (!completed) // Sleep(1); diff --git a/Testing/GoogleTest/FileFilter/FileFilterHelper_test.cpp b/Testing/GoogleTest/FileFilter/FileFilterHelper_test.cpp index 786ea58e0b4..0aa6f21016e 100644 --- a/Testing/GoogleTest/FileFilter/FileFilterHelper_test.cpp +++ b/Testing/GoogleTest/FileFilter/FileFilterHelper_test.cpp @@ -2,15 +2,34 @@ #include #include #include "FileFilterHelper.h" +#include "FileFilter.h" #include "Environment.h" #include "paths.h" +#include "DiffItem.h" namespace { + void SetDiffItem(const String& path, const String& left, const String& right, bool isfile, DIFFITEM& di) + { + di.diffcode.setSideFlag(0); + di.diffcode.setSideFlag(1); + di.diffcode.diffcode |= isfile ? DIFFCODE::FILE : DIFFCODE::DIR; + di.diffFileInfo[0].path = path; + di.diffFileInfo[0].filename = left; + di.diffFileInfo[0].mtime = Poco::Timestamp(); + di.diffFileInfo[0].flags.attributes = FILE_ATTRIBUTE_DIRECTORY; + di.diffFileInfo[1].path = path; + di.diffFileInfo[1].filename = right; + di.diffFileInfo[1].mtime = Poco::Timestamp(); + di.diffFileInfo[1].flags.attributes = FILE_ATTRIBUTE_DIRECTORY; + } + // The fixture for testing string differencing functions. class FileFilterHelperTest : public testing::Test { protected: + String m_oldProgPath; + // You can remove any or all of the following functions if its body // is empty. @@ -31,7 +50,8 @@ namespace { // Code here will be called immediately after the constructor (right // before each test). - env::SetProgPath(env::GetProgPath() + _T("/../FileFilter")); + m_oldProgPath = env::GetProgPath(); + env::SetProgPath(m_oldProgPath + _T("/../FileFilter")); m_fileFilterHelper.LoadAllFileFilters(); } @@ -39,6 +59,7 @@ namespace { // Code here will be called immediately after each test (right // before the destructor). + env::SetProgPath(m_oldProgPath); } // Objects declared here can be used by all tests in the test case for Foo. @@ -63,6 +84,11 @@ namespace filtername = m_fileFilterHelper.GetFileFilterName(filterpath.c_str()); EXPECT_TRUE(filtername.compare(_T("simple include dir")) == 0); + filterpath = m_fileFilterHelper.GetFileFilterPath(_T("error include")); + EXPECT_TRUE(filterpath.find_first_of(_T("Filters\\error_include.flt")) != String::npos); + filtername = m_fileFilterHelper.GetFileFilterName(filterpath.c_str()); + EXPECT_TRUE(filtername.compare(_T("error include")) == 0); + filterpath = m_fileFilterHelper.GetFileFilterPath(_T("non-existent file filter name")); EXPECT_TRUE(filterpath.empty()); @@ -70,26 +96,9 @@ namespace EXPECT_TRUE(filtername.empty()); } - TEST_F(FileFilterHelperTest, SetFileFilterPath) - { - String selected; - m_fileFilterHelper.SetFileFilterPath(_T("")); - std::vector filters = m_fileFilterHelper.GetFileFilters(selected); - EXPECT_TRUE(selected.compare(_T("")) == 0); - - m_fileFilterHelper.SetFileFilterPath(_T("non-existent file filter path")); - filters = m_fileFilterHelper.GetFileFilters(selected); - EXPECT_TRUE(selected.compare(_T("")) == 0); - - m_fileFilterHelper.SetFileFilterPath(m_fileFilterHelper.GetFileFilterPath(_T("simple include file")).c_str()); - filters = m_fileFilterHelper.GetFileFilters(selected); - EXPECT_TRUE(selected.find_first_of(_T("Filters\\simple_include_file.flt")) != String::npos); - } - TEST_F(FileFilterHelperTest, GetFileFilters) { - String selected; - std::vector filters = m_fileFilterHelper.GetFileFilters(selected); + std::vector filters = m_fileFilterHelper.GetFileFilters(); for (std::vector::iterator it = filters.begin(); it != filters.end(); it++) { @@ -103,6 +112,11 @@ namespace EXPECT_TRUE((*it).fullpath.find_first_of(_T("Filters\\simple_include_dir.flt")) != String::npos); EXPECT_TRUE((*it).description.compare(_T("simple directory filter long description")) == 0); } + else if ((*it).name.compare(_T("error include")) == 0) + { + EXPECT_TRUE((*it).fullpath.find_first_of(_T("Filters\\error_include.flt")) != String::npos); + EXPECT_TRUE((*it).description.compare(_T("error file filter long description")) == 0); + } else { EXPECT_TRUE(false); @@ -110,10 +124,9 @@ namespace } } - TEST_F(FileFilterHelperTest, SetFilter) + TEST_F(FileFilterHelperTest, SetMask1) { - m_fileFilterHelper.SetFilter(_T("simple include file")); - EXPECT_EQ(false, m_fileFilterHelper.IsUsingMask()); + m_fileFilterHelper.SetMask(_T("simple include file")); EXPECT_EQ(true, m_fileFilterHelper.includeFile(_T("a.c"))); EXPECT_EQ(true, m_fileFilterHelper.includeFile(_T("a.cpp"))); @@ -126,7 +139,7 @@ namespace EXPECT_EQ(true, m_fileFilterHelper.includeDir(_T("a.ext"))); EXPECT_EQ(true, m_fileFilterHelper.includeDir(_T("a.b.c"))); - m_fileFilterHelper.SetFilter(_T("simple include dir")); + m_fileFilterHelper.SetMask(_T("fp:simple include dir")); EXPECT_EQ(true, m_fileFilterHelper.includeFile(_T("a.c"))); EXPECT_EQ(true, m_fileFilterHelper.includeFile(_T("a.cpp"))); @@ -140,11 +153,8 @@ namespace } - TEST_F(FileFilterHelperTest, SetMask) + TEST_F(FileFilterHelperTest, SetMask2) { - m_fileFilterHelper.UseMask(true); - EXPECT_EQ(true, m_fileFilterHelper.IsUsingMask()); - m_fileFilterHelper.SetMask(_T("")); EXPECT_EQ(true, m_fileFilterHelper.includeFile(_T("a.c"))); EXPECT_EQ(true, m_fileFilterHelper.includeFile(_T("a.cpp"))); @@ -165,7 +175,6 @@ namespace EXPECT_EQ(true, m_fileFilterHelper.includeDir(_T("a.b.c"))); m_fileFilterHelper.SetMask(_T("*.c;*.cpp;*.cxx")); - EXPECT_EQ(true, m_fileFilterHelper.IsUsingMask()); EXPECT_EQ(true, m_fileFilterHelper.includeFile(_T("a.c"))); EXPECT_EQ(true, m_fileFilterHelper.includeFile(_T("a.cpp"))); EXPECT_EQ(true, m_fileFilterHelper.includeFile(_T("a.cxx"))); @@ -179,7 +188,6 @@ namespace EXPECT_EQ(true, m_fileFilterHelper.includeDir(_T("a.b.c"))); m_fileFilterHelper.SetMask(_T("!*.h")); - EXPECT_EQ(true, m_fileFilterHelper.IsUsingMask()); EXPECT_EQ(true, m_fileFilterHelper.includeFile(_T("a.c"))); EXPECT_EQ(true, m_fileFilterHelper.includeFile(_T("a.cpp"))); EXPECT_EQ(true, m_fileFilterHelper.includeFile(_T("a.cxx"))); @@ -193,7 +201,6 @@ namespace EXPECT_EQ(true, m_fileFilterHelper.includeDir(_T("a.b.c"))); m_fileFilterHelper.SetMask(_T("*.c*;!*.cxx;!Makefile;!.git\\;!abc\\;!de*hi\\;!Debug\\;!Release\\")); - EXPECT_EQ(true, m_fileFilterHelper.IsUsingMask()); EXPECT_EQ(true, m_fileFilterHelper.includeFile(_T("a.c"))); EXPECT_EQ(true, m_fileFilterHelper.includeFile(_T("a.cpp"))); EXPECT_EQ(false, m_fileFilterHelper.includeFile(_T("a.cxx"))); @@ -217,24 +224,53 @@ namespace EXPECT_EQ(false, m_fileFilterHelper.includeDir(_T("dir1\\Debug"))); m_fileFilterHelper.SetMask(_T("abc.\\def.\\*.*")); - EXPECT_EQ(true, m_fileFilterHelper.IsUsingMask()); EXPECT_EQ(true, m_fileFilterHelper.includeFile(_T("abc\\def\\efg"))); EXPECT_EQ(false, m_fileFilterHelper.includeFile(_T("abc.1\\def\\efg"))); EXPECT_EQ(false, m_fileFilterHelper.includeFile(_T("abc\\def.1\\efg"))); m_fileFilterHelper.SetMask(_T("abc.\\def.\\")); - EXPECT_EQ(true, m_fileFilterHelper.IsUsingMask()); EXPECT_EQ(true, m_fileFilterHelper.includeDir(_T("abc\\def"))); EXPECT_EQ(false, m_fileFilterHelper.includeDir(_T("abc.1\\def"))); EXPECT_EQ(false, m_fileFilterHelper.includeDir(_T("abc\\def.1"))); + DIFFITEM di{}; + // Test for bugs introduced in version 2.16.26 m_fileFilterHelper.SetMask(_T("*.*")); - EXPECT_EQ(true, m_fileFilterHelper.IsUsingMask()); EXPECT_EQ(true, m_fileFilterHelper.includeFile(_T(".git\\config"))); + SetDiffItem(_T(".git"), _T("config"), _T("config"), true, di); + EXPECT_EQ(true, m_fileFilterHelper.includeFile(di)); + + m_fileFilterHelper.SetMask(_T("f: \\.o$ ; f: \\.lib$ ; f: \\.bak$; d: \\\\\\.svn$; d: \\\\_svn$;d:\\\\cvs$;d:\\\\\\.git$;d:\\\\\\.bzr$;d:\\\\\\.hg$;")); + SetDiffItem(_T("abc"), _T("a.o"), _T("A.o"), true, di); + EXPECT_EQ(true, m_fileFilterHelper.includeFile(di)); + SetDiffItem(_T(""), _T("abc.lib"), _T("abc.lib"), true, di); + EXPECT_EQ(true, m_fileFilterHelper.includeFile(di)); + SetDiffItem(_T(""), _T("a d.bak"), _T("a d.bak"), true, di); + EXPECT_EQ(true, m_fileFilterHelper.includeFile(di)); + SetDiffItem(_T(""), _T(".svn"), _T(".svn"), false, di); + EXPECT_EQ(true, m_fileFilterHelper.includeDir(di)); + SetDiffItem(_T("abc"), _T("_svn"), _T("_svn"), false, di); + EXPECT_EQ(true, m_fileFilterHelper.includeDir(di)); + SetDiffItem(_T("abc"), _T("cvs"), _T("cvs"), false, di); + EXPECT_EQ(true, m_fileFilterHelper.includeDir(di)); + SetDiffItem(_T("abc\\def"), _T(".git"), _T(".git"), false, di); + EXPECT_EQ(true, m_fileFilterHelper.includeDir(di)); + SetDiffItem(_T("abc\\def"), _T(".bzr"), _T(".bzr"), false, di); + EXPECT_EQ(true, m_fileFilterHelper.includeDir(di)); + SetDiffItem(_T("abc\\def"), _T(".hg"), _T(".hg"), false, di); + EXPECT_EQ(true, m_fileFilterHelper.includeDir(di)); + SetDiffItem(_T("abc\\def"), _T("a.obj"), _T("a.obj"), true, di); + EXPECT_EQ(false, m_fileFilterHelper.includeFile(di)); + SetDiffItem(_T("abc\\def"), _T("svv"), _T("svv"), false, di); + EXPECT_EQ(false, m_fileFilterHelper.includeDir(di)); } - + TEST_F(FileFilterHelperTest, Error) + { + m_fileFilterHelper.SetMask(_T("fp:error include")); + EXPECT_EQ((size_t)8, m_fileFilterHelper.GetErrorList().size()); + } } // namespace diff --git a/Testing/GoogleTest/FileFilter/Filters/error_include.flt b/Testing/GoogleTest/FileFilter/Filters/error_include.flt new file mode 100644 index 00000000000..c91ed1954b9 --- /dev/null +++ b/Testing/GoogleTest/FileFilter/Filters/error_include.flt @@ -0,0 +1,11 @@ +name: error include +desc: error file filter long description +def: include +f: [A-Z]*[$ +f!: a{2,1}$ +d: (abc$ +d!: \c +fe: siz > 0 +fe!: abs(1, 2) < 1000 +de: Name matches "(abc" +de!: Name $ "aaa" diff --git a/Testing/GoogleTest/FileVersion/FileVersion_test.cpp b/Testing/GoogleTest/FileVersion/FileVersion_test.cpp index c0bc6c00710..234ed833e82 100644 --- a/Testing/GoogleTest/FileVersion/FileVersion_test.cpp +++ b/Testing/GoogleTest/FileVersion/FileVersion_test.cpp @@ -1,6 +1,5 @@ #include "pch.h" #include -#include #include "FileVersion.h" namespace @@ -68,8 +67,8 @@ namespace TEST_F(FileVersionTest, getfilever_real) { FileVersion version; - DWORD hi = (1 << 16) | 2; - DWORD lo = (3 << 16) | 4; + unsigned hi = (1 << 16) | 2; + unsigned lo = (3 << 16) | 4; version.SetFileVersion(hi, lo); EXPECT_EQ(_T("1.2.3.4"), version.GetFileVersionString()); } diff --git a/Testing/GoogleTest/FilterEngine/FilterExpression_test.cpp b/Testing/GoogleTest/FilterEngine/FilterExpression_test.cpp new file mode 100644 index 00000000000..1ef9fa0e74c --- /dev/null +++ b/Testing/GoogleTest/FilterEngine/FilterExpression_test.cpp @@ -0,0 +1,678 @@ +#include "pch.h" +#include +#include "FilterEngine/FilterExpression.h" +#include "DiffContext.h" +#include "DiffItem.h" +#include "PathContext.h" +#include "Poco/DateTimeParser.h" +#include "Poco/Timezone.h" +#include "Environment.h" +#include "paths.h" +#include "OptionsMgr.h" +#include "OptionsDef.h" +#include "MergeApp.h" + +struct FilterTestParam { bool optimize; }; + +// The fixture for testing paths functions. +class FilterExpressionTest : public ::testing::TestWithParam {}; + +TEST_P(FilterExpressionTest, Literals) +{ + // Test case for evaluating filter expressions with literal values. + // This function sets up a test context, initializes test data, and verifies + // the behavior of the FilterExpression class. + + // Initialize path context and diff context for the test. + PathContext paths(L"D:\\dev\\winmerge\\src", L"D:\\dev\\winmerge\\src"); + CDiffContext ctxt(paths, 0); + + // Set up DIFFITEM object with file information and timestamps. + DIFFITEM di; + int tzd; + di.diffFileInfo[0].filename = L"Alice.txt"; + di.diffFileInfo[0].size = 1000; + Poco::DateTime dt0 = Poco::DateTimeParser::parse("%Y-%m-%d %H:%M", "2025-05-16 15:34:56", tzd); + dt0 -= Poco::Timezone::tzd() * 1000; + di.diffFileInfo[0].mtime = dt0.timestamp(); + di.diffFileInfo[1].filename = L"Alice.txt"; + di.diffFileInfo[1].size = 1100; + Poco::DateTime dt1 = Poco::DateTimeParser::parse("%Y-%m-%d %H:%M:%S", "2025-05-16 15:34:57", tzd); + dt1 -= Poco::Timezone::tzd() * 1000; + di.diffFileInfo[1].mtime = dt1.timestamp(); + + // Set side flags for the diff code. + di.diffcode.setSideFlag(0); + di.diffcode.setSideFlag(1); + + // Initialize FilterExpression and set the diff context. + FilterExpression fe; + fe.SetDiffContext(&ctxt); + fe.optimize = GetParam().optimize; + + // Verify that the filter expression correctly parses and evaluates literals. + EXPECT_TRUE(fe.Parse("123 == 123")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("123 == 124")); + EXPECT_FALSE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("2 < 3")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("3 < 3")); + EXPECT_FALSE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("2 <= 3")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("3 <= 3")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("3 <= 2")); + EXPECT_FALSE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("3 > 2")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("3 > 3")); + EXPECT_FALSE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("3 >= 2")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("3 >= 3")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("2 >= 3")); + EXPECT_FALSE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("3 != 3")); + EXPECT_FALSE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("3 != 2")); + EXPECT_TRUE(fe.Evaluate(di)); + + EXPECT_TRUE(fe.Parse("d\"2025-05-27\" == d\"2025-05-27\"")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("d\"2025-05-27\" == d\"2025-05-28\"")); + EXPECT_FALSE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("d\"2025-05-27\" < d\"2025-05-28\"")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("d\"2025-05-28\" < d\"2025-05-28\"")); + EXPECT_FALSE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("d\"2025-05-27\" <= d\"2025-05-28\"")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("d\"2025-05-28\" <= d\"2025-05-28\"")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("d\"2025-05-29\" <= d\"2025-05-28\"")); + EXPECT_FALSE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("d\"2025-05-28\" > d\"2025-05-27\"")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("d\"2025-05-28\" > d\"2025-05-28\"")); + EXPECT_FALSE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("d\"2025-05-28\" >= d\"2025-05-27\"")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("d\"2025-05-28\" >= d\"2025-05-28\"")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("d\"2025-05-28\" >= d\"2025-05-29\"")); + EXPECT_FALSE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("d\"2025-05-27\" != d\"2025-05-28\"")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("d\"2025-05-27\" != d\"2025-05-27\"")); + EXPECT_FALSE(fe.Evaluate(di)); + + EXPECT_TRUE(fe.Parse("\"abc\" == \"abc\"")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("\"abc\" == \"abd\"")); + EXPECT_FALSE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("\"abb\" < \"abc\"")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("\"abb\" < \"abb\"")); + EXPECT_FALSE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("\"abb\" <= \"abc\"")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("\"abc\" <= \"abc\"")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("\"abd\" <= \"abc\"")); + EXPECT_FALSE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("\"abc\" > \"abb\"")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("\"abc\" > \"abc\"")); + EXPECT_FALSE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("\"abc\" >= \"abb\"")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("\"abc\" >= \"abc\"")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("\"abc\" >= \"abd\"")); + EXPECT_FALSE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("substr(\"abcd\", 0, 4) = \"abcd\"")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("substr(\"abcd\", 0, 5) = \"abcd\"")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("substr(\"abcd\", 0, 0) = \"\"")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("substr(\"abcd\", 0, 3) = \"abc\"")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("substr(\"abcd\", 2, 2) = \"cd\"")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("substr(\"abcd\", -1, 2) = \"d\"")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("substr(\"abcd\", -2, 2) = \"cd\"")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("substr(\"abcd\", -5, 2) = \"\"")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("substr(\"abcd\", 0) = \"abcd\"")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("substr(\"abcd\", 2) = \"cd\"")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("substr(\"abcd\", 4) = \"\"")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("substr(\"abcd\", -1) = \"d\"")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("length(\"abcd\") = 4")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("length(\"\") = 0")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("replace(\"abcd\", \"ab\", \"cd\") = \"cdcd\"")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("replace(\"\", \"ab\", \"cd\") = \"\"")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("replace(\"abcd\", \"cd\", \"\") = \"ab\"")); + EXPECT_TRUE(fe.Evaluate(di)); + + EXPECT_TRUE(fe.Parse("v\"2.16.48.2\" == v\"002.016.048.002\"")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("v\"2.16.48.2\" < v\"2.16.49\"")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("v\"2.16.48.2\" <= v\"2.16.49\"")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("v\"2.16.49\" <= v\"2.16.49\"")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("v\"2.16.49\" > v\"2.16.48.2\"")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("v\"2.16.49\" >= v\"2.16.48.2\"")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("v\"2.16.49\" >= v\"2.16.49\"")); + EXPECT_TRUE(fe.Evaluate(di)); + + EXPECT_TRUE(fe.Parse("1weeks == 7days")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("1week == 7day")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("1w == 7d")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("1days == 24hours")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("0.25days == 6hours")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("1day == 24hour")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("1d == 24hr")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("1d == 24h")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("1hours == 60minutes")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("0.5hours == 30minutes")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("1hour == 60minute")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("1h == 60min")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("0.5h == 30min")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("1h == 60m")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("1minutes == 60seconds")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("0.1minutes == 6seconds")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("1minute == 60second")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("1min == 60sec")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("1m == 60s")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("1seconds == 1000milliseconds")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("1second == 1000millisecond")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("1sec == 1000msec")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("1s == 1000ms")); + EXPECT_TRUE(fe.Evaluate(di)); + + EXPECT_TRUE(fe.Parse("2hours == 60minutes")); + EXPECT_FALSE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("2hour == 60minute")); + EXPECT_FALSE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("2h == 60min")); + EXPECT_FALSE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("2h == 60m")); + EXPECT_FALSE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("2minutes == 60seconds")); + EXPECT_FALSE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("2minute == 60second")); + EXPECT_FALSE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("2min == 60sec")); + EXPECT_FALSE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("2m == 60s")); + EXPECT_FALSE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("2seconds == 1000milliseconds")); + EXPECT_FALSE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("2second == 1000millisecond")); + EXPECT_FALSE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("2sec == 1000msec")); + EXPECT_FALSE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("2s == 1000ms")); + EXPECT_FALSE(fe.Evaluate(di)); + + EXPECT_TRUE(fe.Parse("1KB == 1024")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("0.5KB == 512")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("1MB == 1024KB")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("0.5MB == 512KB")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("1GB == 1024MB")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("0.5GB == 512MB")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("1TB == 1024GB")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("0.5TB == 512GB")); + EXPECT_TRUE(fe.Evaluate(di)); + + EXPECT_TRUE(fe.Parse("2KB == 1024")); + EXPECT_FALSE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("2MB == 1024KB")); + EXPECT_FALSE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("2GB == 1024MB")); + EXPECT_FALSE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("2TB == 1024GB")); + EXPECT_FALSE(fe.Evaluate(di)); + + EXPECT_TRUE(fe.Parse("d\"2025-05-27\" == d\"2025-05-27 00:00:00\"")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("d\"2025-05-27\" == d\"2025-05-27 00:00:01\"")); + EXPECT_FALSE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("d\"2025-05-27\" < d\"2025-05-27 00:00:01\"")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("d\"2025-05-27\" < d\"2025-05-27 00:00:00\"")); + EXPECT_FALSE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("d\"2025-05-27\" <= d\"2025-05-27 00:00:00\"")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("d\"2025-05-27\" <= d\"2025-05-27 00:00:01\"")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("d\"2025-05-27 00:00:02\" <= d\"2025-05-27 00:00:01\"")); + EXPECT_FALSE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("d\"2025-05-27 00:00:01\" > d\"2025-05-27 00:00:00\"")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("d\"2025-05-27 00:00:01\" > d\"2025-05-27 00:00:01\"")); + EXPECT_FALSE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("d\"2025-05-27 00:00:01\" >= d\"2025-05-27 00:00:00\"")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("d\"2025-05-27 00:00:01\" >= d\"2025-05-27 00:00:01\"")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("d\"2025-05-27 00:00:01\" >= d\"2025-05-27 00:00:02\"")); + EXPECT_FALSE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("d\"2025-05-27\" != d\"2025-05-27 00:00:01\"")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("d\"2025-05-27\" != d\"2025-05-27 00:00:00\"")); + EXPECT_FALSE(fe.Evaluate(di)); + + EXPECT_TRUE(fe.Parse("d\"2025-05-28\" == d\"2025-05-21\" + 1week")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("d\"2025-05-28\" == d\"2025-05-27\" + 1day")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("d\"2025-05-28 12:34:56\" == d\"2025-05-28 11:34:56\" + 1hour")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("d\"2025-05-28 12:34:56\" == d\"2025-05-28 12:35:56\" - 1minute")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("d\"2025-05-28 12:34:56\" == d\"2025-05-28 12:34:55\" + 1second")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("d\"2025-05-28 12:34:56\" == d\"2025-05-28 12:34:57\" - 1000millisecond")); + EXPECT_TRUE(fe.Evaluate(di)); + + EXPECT_TRUE(fe.Parse("d\"2025-05-28\" == d\"2025-05-27 00:00:00\"")); + EXPECT_FALSE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("d\"2025-05-29\" == d\"2025-05-21\" + 1week")); + EXPECT_FALSE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("d\"2025-05-29\" == d\"2025-05-27\" + 1day")); + EXPECT_FALSE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("d\"2025-05-29 12:34:56\" == d\"2025-05-28 11:34:56\" + 1hour")); + EXPECT_FALSE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("d\"2025-05-29 12:34:56\" == d\"2025-05-28 12:33:56\" + 1minute")); + EXPECT_FALSE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("d\"2025-05-29 12:34:56\" == d\"2025-05-28 12:34:55\" + 1second")); + EXPECT_FALSE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("d\"2025-05-29 12:34:56\" == d\"2025-05-28 12:34:55\" + 1000millisecond")); + EXPECT_FALSE(fe.Evaluate(di)); + + EXPECT_TRUE(fe.Parse("TRUE")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("FALSE")); + EXPECT_FALSE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("true")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("false")); + EXPECT_FALSE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("true = TRUE")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("true = false")); + EXPECT_FALSE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("false = FALSE")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("false = true")); + EXPECT_FALSE(fe.Evaluate(di)); + + EXPECT_TRUE(fe.Parse("not TRUE")); + EXPECT_FALSE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("not FALSE")); + EXPECT_TRUE(fe.Evaluate(di)); + + EXPECT_TRUE(fe.Parse("TRUE and TRUE")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("TRUE and FALSE")); + EXPECT_FALSE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("FALSE and TRUE")); + EXPECT_FALSE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("FALSE and FALSE")); + EXPECT_FALSE(fe.Evaluate(di)); + + EXPECT_TRUE(fe.Parse("TRUE OR TRUE")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("TRUE OR FALSE")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("FALSE OR TRUE")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("FALSE OR FALSE")); + EXPECT_FALSE(fe.Evaluate(di)); + +} + +TEST_P(FilterExpressionTest, FileAttributes) +{ + PathContext paths(L"C:\\dev\\winmerge\\src", L"D:\\dev\\winmerge\\src", L"E:\\dev\\winmerge\\src"); + CDiffContext ctxt(paths, 0); + DIFFITEM di; + int tzd; + di.diffFileInfo[0].path = L"abc"; + di.diffFileInfo[0].filename = L"Alice.txt"; + di.diffFileInfo[0].size = 1000; + di.diffFileInfo[0].flags.attributes = FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE; + Poco::DateTime dt0 = Poco::DateTimeParser::parse("%Y-%m-%d %H:%M:%S", "2025-05-16 15:34:56", tzd); + dt0.makeUTC(Poco::Timezone::tzd()); + di.diffFileInfo[0].mtime = dt0.timestamp(); + di.diffFileInfo[0].ctime = dt0.timestamp(); + di.diffFileInfo[0].encoding.SetCodepage(65001); + di.diffFileInfo[0].version.SetFileVersion(0x00020010, 0x00300002); + di.diffFileInfo[1].path = L"abc"; + di.diffFileInfo[2].path = L"abc"; + di.diffFileInfo[2].filename = L"Alice.txt"; + di.diffFileInfo[2].size = 1100; + di.diffFileInfo[2].flags.attributes = FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE; + Poco::DateTime dt1 = Poco::DateTimeParser::parse("%Y-%m-%d %H:%M:%S", "2025-05-16 15:34:57", tzd); + dt1.makeUTC(Poco::Timezone::tzd()); + di.diffFileInfo[2].mtime = dt1.timestamp(); + di.diffFileInfo[2].ctime = dt1.timestamp(); + di.diffFileInfo[2].encoding.SetCodepage(65001); + di.diffFileInfo[2].version.SetFileVersion(0x00020010, 0x00300002); + di.diffcode.setSideFlag(0); + di.diffcode.setSideFlag(2); + di.nsdiffs = 3; + di.nidiffs = 2; + + FilterExpression fe; + fe.SetDiffContext(&ctxt); + fe.optimize = GetParam().optimize; + + EXPECT_TRUE(fe.Parse("Size <= 1000")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("allequal(Size)")); + EXPECT_FALSE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("allof(Size <= 1000)")); + EXPECT_FALSE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("anyof(Size <= 1000)")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("LeftDate < now()")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("LeftDate != RightDate")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("LeftDate != MiddleDate")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("LeftCreationTime != RightCreationTime")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("LeftCreationTime != MiddleCreationTime")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("abs(-100) == 100")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("abs(LeftSize - RightSize) == (1100 - 1000)")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("LeftSize <= 100 * (1 + 9)")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("LeftSize < 200 + 20 * 40")); + EXPECT_FALSE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("MiddleSize != 0")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("LeftName = \"Alice.txt\"")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("RightName = \"Bob.txt\"")); + EXPECT_FALSE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("LeftName CONTAINS \"alice\"")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("RightName contains \".txt\"")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("RightName matches \"a.*t\"")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("MiddleName != \"Alice.txt\"")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("LeftExtension = \"txt\"")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("MiddleExtension != \"txt\"")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("LeftFolder = \"abc\"")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("MiddleFolder = \"abc\"")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("LeftDateStr = \"2025-05-16\"")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("MiddleDateStr != \"2025-05-16\"")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("DiffCode != 0")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("Differences = 3")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("IgnoredDiffs = 2")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("LeftFullPath = \"C:\\dev\\winmerge\\src\\abc\\Alice.txt\"")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("MiddleFullPath != \"D:\\dev\\winmerge\\src\\abc\\Alice.txt\"")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("RightAttributes > 0")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("MiddleAttributes != 0")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("LeftAttrStr = \"RHSA\"")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("MiddleAttrStr != \"RHSA\"")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("LeftCodepage = 65001")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("MiddleCodepage != 65001")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("LeftEncoding = \"UTF-8\"")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("MiddleEncoding != \"UTF-8\"")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("LeftVersion = v\"2.16.48.2\"")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("MiddleVersion != v\"2.16.48.2\"")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("LeftExists")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("RightExists")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("MiddleExists")); + EXPECT_FALSE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("Not MiddleExists")); + EXPECT_TRUE(fe.Evaluate(di)); +} + +TEST_P(FilterExpressionTest, Content1) +{ + const String dir = paths::ConcatPath(env::GetProgPath(), L"..\\TestData"); + PathContext paths(dir, dir); + CDiffContext ctxt(paths, 0); + DIFFITEM di; + FilterExpression fe; + fe.SetDiffContext(&ctxt); + fe.optimize = GetParam().optimize; + + di.diffFileInfo[1].filename = L"LeftAndRight.WinMerge"; + di.diffFileInfo[1].path = L""; + di.diffcode.setSideFlag(1); + + GetOptionsMgr()->InitOption(OPT_CP_DETECT, 0); + + EXPECT_TRUE(fe.Parse("RightContent = RightContent")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("RightContent != LeftContent")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("RightContent contains \"UTF-8\"")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("LeftContent contains \"UTF-8\"")); + EXPECT_FALSE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("RightContent recontains \"xml.*UTF-8\"")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("LeftContent recontains \"xml.*UTF-8\"")); + EXPECT_FALSE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("linecount(RightContent) = 7")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("sublines(RightContent, 0, 1) contains \"\"")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("sublines(RightContent, 1, 2) contains \"paths\"")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("sublines(RightContent, -1, 1) contains \"\"")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("sublines(RightContent, -1, -1) contains \"\"")); + EXPECT_TRUE(fe.Evaluate(di)); +} + +TEST_P(FilterExpressionTest, ContentEmpty) +{ + const String dir = paths::ConcatPath(env::GetProgPath(), L"..\\..\\Data\\Compare"); + PathContext paths(paths::ConcatPath(dir, _T("dir1")), paths::ConcatPath(dir, _T("dir2"))); + CDiffContext ctxt(paths, 0); + DIFFITEM di; + FilterExpression fe; + fe.SetDiffContext(&ctxt); + fe.optimize = GetParam().optimize; + + di.diffFileInfo[1].filename = L"file123_0.txt"; + di.diffFileInfo[1].path = L""; + di.diffcode.setSideFlag(1); + + GetOptionsMgr()->InitOption(OPT_CP_DETECT, 0); + + EXPECT_TRUE(fe.Parse("RightContent = RightContent")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("RightContent != LeftContent")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("RightContent contains \"UTF-8\"")); + EXPECT_FALSE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("RightContent recontains \"xml.*UTF-8\"")); + EXPECT_FALSE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("linecount(RightContent) = 0")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("sublines(RightContent, 0, 1) contains \"\"")); + EXPECT_FALSE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("sublines(RightContent, 1, 2) contains \"paths\"")); + EXPECT_FALSE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("sublines(RightContent, -1, 1) contains \"\"")); + EXPECT_FALSE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("sublines(RightContent, -1, -1) contains \"\"")); + EXPECT_FALSE(fe.Evaluate(di)); +} + +TEST_P(FilterExpressionTest, ParseError) +{ + PathContext paths(L"D:\\dev\\winmerge\\src", L"D:\\dev\\winmerge\\src"); + CDiffContext ctxt(paths, 0); + FilterExpression fe; + fe.SetDiffContext(&ctxt); + fe.optimize = GetParam().optimize; + + EXPECT_FALSE(fe.Parse("LeftDate $ a")); + EXPECT_EQ(FILTER_ERROR_UNKNOWN_CHAR, fe.errorCode); + EXPECT_FALSE(fe.Parse("LeftName = \"aaa")); + EXPECT_EQ(FILTER_ERROR_UNTERMINATED_STRING, fe.errorCode); + EXPECT_FALSE(fe.Parse("LeftSize = 100 RightSize < 100")); + EXPECT_EQ(FILTER_ERROR_SYNTAX_ERROR, fe.errorCode); + EXPECT_FALSE(fe.Parse("LeftDate = d\"2025-13-32 25:60:61\"")); + EXPECT_EQ(FILTER_ERROR_INVALID_LITERAL, fe.errorCode); + EXPECT_FALSE(fe.Parse("aaa(1234)")); + EXPECT_EQ(FILTER_ERROR_UNDEFINED_IDENTIFIER, fe.errorCode); + EXPECT_FALSE(fe.Parse("aaa = 1234")); + EXPECT_EQ(FILTER_ERROR_UNDEFINED_IDENTIFIER, fe.errorCode); + EXPECT_FALSE(fe.Parse("abs()")); + EXPECT_EQ(FILTER_ERROR_INVALID_ARGUMENT_COUNT, fe.errorCode); + EXPECT_FALSE(fe.Parse("abs(1, 2)")); + EXPECT_EQ(FILTER_ERROR_INVALID_ARGUMENT_COUNT, fe.errorCode); + if (fe.optimize) { + EXPECT_FALSE(fe.Parse("LeftName matches \"[[\"")); + EXPECT_EQ(FILTER_ERROR_INVALID_REGULAR_EXPRESSION, fe.errorCode); + } + EXPECT_FALSE(fe.Parse("abs(1)) + abc(2)")); + EXPECT_EQ(FILTER_ERROR_SYNTAX_ERROR, fe.errorCode); + EXPECT_FALSE(fe.Parse(")LeftSize == 1")); + EXPECT_EQ(FILTER_ERROR_SYNTAX_ERROR, fe.errorCode); +} + +TEST_P(FilterExpressionTest, Test1) +{ + PathContext paths(L"D:\\dev\\winmerge\\src", L"D:\\dev\\winmerge\\src"); + CDiffContext ctxt(paths, 0); + DIFFITEM di; + int tzd = 0; + di.diffFileInfo[0].filename = L"Alice.txt"; + di.diffFileInfo[0].size = 1000; + Poco::DateTime dt0 = Poco::DateTimeParser::parse("%Y-%m-%d %H:%M", "2025-05-16 15:34:56", tzd); + dt0.makeUTC(Poco::Timezone::tzd()); + di.diffFileInfo[0].mtime = dt0.timestamp(); + di.diffFileInfo[1].filename = L"Alice.txt"; + di.diffFileInfo[1].size = 1100; + Poco::DateTime dt1 = Poco::DateTimeParser::parse("%Y-%m-%d %H:%M:%S", "2025-05-16 15:34:57", tzd); + dt1.makeUTC(Poco::Timezone::tzd()); + di.diffFileInfo[1].mtime = dt1.timestamp(); + di.diffcode.setSideFlag(0); + di.diffcode.setSideFlag(1); + + FilterExpression fe; + fe.SetDiffContext(&ctxt); + fe.optimize = GetParam().optimize; + + EXPECT_TRUE(fe.Parse("1 or true")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("1 or false")); + EXPECT_FALSE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("true or 1")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("true or 1")); + EXPECT_TRUE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("1 or 1")); + EXPECT_FALSE(fe.Evaluate(di)); + + EXPECT_TRUE(fe.Parse("1 and true")); + EXPECT_FALSE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("1 and false")); + EXPECT_FALSE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("true and 1")); + EXPECT_FALSE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("true and 1")); + EXPECT_FALSE(fe.Evaluate(di)); + EXPECT_TRUE(fe.Parse("1 and 1")); + EXPECT_FALSE(fe.Evaluate(di)); + + EXPECT_TRUE(fe.Parse("not 1")); + EXPECT_FALSE(fe.Evaluate(di)); +} + +INSTANTIATE_TEST_SUITE_P( + OptimizationCases, + FilterExpressionTest, + ::testing::Values( + FilterTestParam{ true }, + FilterTestParam{ false } + ) +); + diff --git a/Testing/GoogleTest/Plugins/Plugins_test.cpp b/Testing/GoogleTest/Plugins/Plugins_test.cpp index 974a06aed29..cbb45872701 100644 --- a/Testing/GoogleTest/Plugins/Plugins_test.cpp +++ b/Testing/GoogleTest/Plugins/Plugins_test.cpp @@ -59,6 +59,7 @@ namespace ppi->FetchPluginInfos(_T("../../Data/Office/excel.xls|../../Data/Office/excel.xls"), &iu, &ip); String file = paths::ConcatPath(oldModulePath, _T("..\\..\\Data\\Office\\excel.xls")); iu->Unpacking(0, &subcodes, file, _T(".*\\.xls"), { file }); + env::SetProgPath(oldModulePath); } TEST_F(PluginsTest, ParsePluginPipeline) diff --git a/Testing/GoogleTest/UnitTests/UnitTests.vcxproj b/Testing/GoogleTest/UnitTests/UnitTests.vcxproj index 1cd2e261f78..f9cff9923fe 100644 --- a/Testing/GoogleTest/UnitTests/UnitTests.vcxproj +++ b/Testing/GoogleTest/UnitTests/UnitTests.vcxproj @@ -115,29 +115,9 @@ + - - - - - - - - - - - - - - - - - - - - - - + @@ -212,7 +192,7 @@ $(IntDir)$(TargetName)2.pch - shlwapi.lib;Iphlpapi.lib;comsuppw.lib;uafxcwd.lib;LIBCMTD.lib;%(AdditionalDependencies) + shlwapi.lib;Iphlpapi.lib;comsuppw.lib;version.lib;uafxcwd.lib;LIBCMTD.lib;%(AdditionalDependencies) $(OutDir)UnitTests.exe ..\..\..\Externals\poco\lib;%(AdditionalLibraryDirectories) true @@ -242,7 +222,7 @@ $(IntDir)$(TargetName)2.pch - shlwapi.lib;Iphlpapi.lib;comsuppw.lib;uafxcwd.lib;LIBCMTD.lib;uafxcwd.lib;LIBCMTD.lib;%(AdditionalDependencies) + shlwapi.lib;Iphlpapi.lib;comsuppw.lib;version.lib;uafxcwd.lib;LIBCMTD.lib;uafxcwd.lib;LIBCMTD.lib;%(AdditionalDependencies) $(OutDir)UnitTests.exe ..\..\..\Externals\poco\lib64;%(AdditionalLibraryDirectories) true @@ -270,7 +250,7 @@ $(IntDir)$(TargetName)2.pch - shlwapi.lib;Iphlpapi.lib;comsuppw.lib;uafxcwd.lib;LIBCMTD.lib;%(AdditionalDependencies) + shlwapi.lib;Iphlpapi.lib;comsuppw.lib;version.lib;uafxcwd.lib;LIBCMTD.lib;%(AdditionalDependencies) $(OutDir)UnitTests.exe ..\..\..\Externals\poco\lib$(Platform);%(AdditionalLibraryDirectories) true @@ -298,7 +278,7 @@ $(IntDir)$(TargetName)2.pch - shlwapi.lib;Iphlpapi.lib;comsuppw.lib;uafxcwd.lib;LIBCMTD.lib;%(AdditionalDependencies) + shlwapi.lib;Iphlpapi.lib;comsuppw.lib;version.lib;uafxcwd.lib;LIBCMTD.lib;%(AdditionalDependencies) $(OutDir)UnitTests.exe ..\..\..\Externals\poco\lib$(Platform);%(AdditionalLibraryDirectories) true @@ -324,7 +304,7 @@ $(IntDir)$(TargetName)2.pch - shlwapi.lib;Iphlpapi.lib;comsuppw.lib;%(AdditionalDependencies) + shlwapi.lib;Iphlpapi.lib;comsuppw.lib;version.lib;%(AdditionalDependencies) $(OutDir)UnitTests.exe ..\..\..\Externals\poco\lib;%(AdditionalLibraryDirectories) true @@ -353,7 +333,7 @@ $(IntDir)$(TargetName)2.pch - shlwapi.lib;Iphlpapi.lib;comsuppw.lib;%(AdditionalDependencies) + shlwapi.lib;Iphlpapi.lib;comsuppw.lib;version.lib;%(AdditionalDependencies) $(OutDir)UnitTests.exe ..\..\..\Externals\poco\lib64;%(AdditionalLibraryDirectories) true @@ -380,7 +360,7 @@ $(IntDir)$(TargetName)2.pch - shlwapi.lib;Iphlpapi.lib;comsuppw.lib;%(AdditionalDependencies) + shlwapi.lib;Iphlpapi.lib;comsuppw.lib;version.lib;%(AdditionalDependencies) $(OutDir)UnitTests.exe ..\..\..\Externals\poco\lib$(Platform);%(AdditionalLibraryDirectories) true @@ -407,7 +387,7 @@ $(IntDir)$(TargetName)2.pch - shlwapi.lib;Iphlpapi.lib;comsuppw.lib;%(AdditionalDependencies) + shlwapi.lib;Iphlpapi.lib;comsuppw.lib;version.lib;%(AdditionalDependencies) $(OutDir)UnitTests.exe ..\..\..\Externals\poco\lib$(Platform);%(AdditionalLibraryDirectories) true @@ -436,6 +416,7 @@ pch.h $(IntDir)$(TargetName)2.pch + Use pch.h @@ -446,6 +427,7 @@ pch.h $(IntDir)$(TargetName)2.pch + Use pch.h @@ -495,6 +477,7 @@ pch.h $(IntDir)$(TargetName)2.pch + Use pch.h @@ -604,6 +587,7 @@ Use + Use @@ -846,8 +830,10 @@ + + @@ -857,6 +843,7 @@ + @@ -906,6 +893,11 @@ + + + CppCode + + diff --git a/Testing/GoogleTest/UnitTests/UnitTests.vcxproj.filters b/Testing/GoogleTest/UnitTests/UnitTests.vcxproj.filters index 6500ea3d084..d026a94c466 100644 --- a/Testing/GoogleTest/UnitTests/UnitTests.vcxproj.filters +++ b/Testing/GoogleTest/UnitTests/UnitTests.vcxproj.filters @@ -312,6 +312,18 @@ Source Files + + Tests + + + Source Files + + + Source Files + + + Source Files + @@ -467,6 +479,15 @@ Header Files + + Header Files + + + Header Files + + + Header Files + diff --git a/Translations/WinMerge/Arabic.po b/Translations/WinMerge/Arabic.po index 38aad6b7351..3e5415a924b 100644 --- a/Translations/WinMerge/Arabic.po +++ b/Translations/WinMerge/Arabic.po @@ -1365,9 +1365,6 @@ msgstr "البحث في ال&تالي" msgid "Find &Prev" msgstr "البحث في ال&سابق" -msgid "&Ok" -msgstr "&موافق" - msgid "Replace" msgstr "استبدال" @@ -1407,6 +1404,9 @@ msgstr "جديد" msgid "&Background color:" msgstr "&لون الخلفية:" +msgid "&Ok" +msgstr "&موافق" + msgid "&Apply" msgstr "ت&طبيق" @@ -1814,6 +1814,12 @@ msgstr "إعادة تعيين" msgid "File Filters" msgstr "" +msgid "&Mask / Filter Expression" +msgstr "" + +msgid "Preset &Filters" +msgstr "" + msgid "Test..." msgstr "اختبار..." @@ -2553,9 +2559,6 @@ msgstr "الموقع" msgid "Filters" msgstr "المرشحات" -msgid "[F] " -msgstr "[F] " - msgid "Description" msgstr "الوصف" @@ -4355,6 +4358,39 @@ msgstr "" msgid "File operation canceled" msgstr "" +msgid "No error" +msgstr "" + +msgid "Unknown character in filter expression" +msgstr "" + +msgid "Unterminated string literal" +msgstr "" + +msgid "Syntax error in filter expression" +msgstr "" + +msgid "Failed to parse filter expression" +msgstr "" + +msgid "Invalid literal value" +msgstr "" + +msgid "Invalid regular expression" +msgstr "" + +msgid "Undefined identifier" +msgstr "" + +msgid "Invalid number of arguments" +msgstr "" + +msgid "Filter name not found" +msgstr "" + +msgid "Unknown error" +msgstr "" + msgid "Prettification" msgstr "" diff --git a/Translations/WinMerge/Basque.po b/Translations/WinMerge/Basque.po index 4d920ecef83..82935c69125 100644 --- a/Translations/WinMerge/Basque.po +++ b/Translations/WinMerge/Basque.po @@ -1633,10 +1633,6 @@ msgstr "Bilatu &Hurrengoa" msgid "Find &Prev" msgstr "" -#, c-format -msgid "&Ok" -msgstr "&Ongi" - #, c-format msgid "Replace" msgstr "Ordeztu" @@ -1688,6 +1684,10 @@ msgstr "Berria" msgid "&Background color:" msgstr "" +#, c-format +msgid "&Ok" +msgstr "&Ongi" + #, c-format msgid "&Apply" msgstr "&Ezarri" @@ -2184,6 +2184,12 @@ msgstr "Birrezarri" msgid "File Filters" msgstr "" +msgid "&Mask / Filter Expression" +msgstr "" + +msgid "Preset &Filters" +msgstr "" + msgid "Test..." msgstr "Azterketa..." @@ -3041,10 +3047,6 @@ msgstr "Kokalekua" msgid "Filters" msgstr "Iragazkiak" -#, c-format -msgid "[F] " -msgstr "[F] " - #, c-format msgid "Description" msgstr "Azalpena" @@ -4970,6 +4972,39 @@ msgstr "" msgid "File operation canceled" msgstr "" +msgid "No error" +msgstr "" + +msgid "Unknown character in filter expression" +msgstr "" + +msgid "Unterminated string literal" +msgstr "" + +msgid "Syntax error in filter expression" +msgstr "" + +msgid "Failed to parse filter expression" +msgstr "" + +msgid "Invalid literal value" +msgstr "" + +msgid "Invalid regular expression" +msgstr "" + +msgid "Undefined identifier" +msgstr "" + +msgid "Invalid number of arguments" +msgstr "" + +msgid "Filter name not found" +msgstr "" + +msgid "Unknown error" +msgstr "" + msgid "Prettification" msgstr "" diff --git a/Translations/WinMerge/Brazilian.po b/Translations/WinMerge/Brazilian.po index 7c18f09fe56..34e5f2b2138 100644 --- a/Translations/WinMerge/Brazilian.po +++ b/Translations/WinMerge/Brazilian.po @@ -1362,9 +1362,6 @@ msgstr "&Achar o Próximo" msgid "Find &Prev" msgstr "Achar &Anterior" -msgid "&Ok" -msgstr "&Ok" - msgid "Replace" msgstr "Substituir" @@ -1404,6 +1401,9 @@ msgstr "Novo" msgid "&Background color:" msgstr "&Cor do 2º plano:" +msgid "&Ok" +msgstr "&Ok" + msgid "&Apply" msgstr "&Aplicar" @@ -1809,6 +1809,12 @@ msgstr "Resetar" msgid "File Filters" msgstr "Filtros de Arquivo" +msgid "&Mask / Filter Expression" +msgstr "" + +msgid "Preset &Filters" +msgstr "" + msgid "Test..." msgstr "Teste..." @@ -2486,9 +2492,6 @@ msgstr "Local" msgid "Filters" msgstr "Filtros" -msgid "[F] " -msgstr "[F] " - msgid "Description" msgstr "Descrição" @@ -4073,6 +4076,39 @@ msgstr "Operação de arquivos completada com sucesso" msgid "File operation canceled" msgstr "Operação de arquivos cancelada" +msgid "No error" +msgstr "" + +msgid "Unknown character in filter expression" +msgstr "" + +msgid "Unterminated string literal" +msgstr "" + +msgid "Syntax error in filter expression" +msgstr "" + +msgid "Failed to parse filter expression" +msgstr "" + +msgid "Invalid literal value" +msgstr "" + +msgid "Invalid regular expression" +msgstr "" + +msgid "Undefined identifier" +msgstr "" + +msgid "Invalid number of arguments" +msgstr "" + +msgid "Filter name not found" +msgstr "" + +msgid "Unknown error" +msgstr "" + msgid "Prettification" msgstr "Embelezamento" diff --git a/Translations/WinMerge/Bulgarian.po b/Translations/WinMerge/Bulgarian.po index 61cdfc3a9ec..9d9f8d40343 100644 --- a/Translations/WinMerge/Bulgarian.po +++ b/Translations/WinMerge/Bulgarian.po @@ -1359,9 +1359,6 @@ msgstr "&Търсене" msgid "Find &Prev" msgstr "Търсене &назад" -msgid "&Ok" -msgstr "&Добре" - msgid "Replace" msgstr "Заменяне" @@ -1401,6 +1398,9 @@ msgstr "&Нов" msgid "&Background color:" msgstr "Цвят на &фона:" +msgid "&Ok" +msgstr "&Добре" + msgid "&Apply" msgstr "&Прилагане" @@ -1806,6 +1806,12 @@ msgstr "Нулиране" msgid "File Filters" msgstr "Филтри за файлове" +msgid "&Mask / Filter Expression" +msgstr "" + +msgid "Preset &Filters" +msgstr "" + msgid "Test..." msgstr "Изпробване..." @@ -2539,9 +2545,6 @@ msgstr "Място" msgid "Filters" msgstr "Филтри" -msgid "[F] " -msgstr "[Ф] " - msgid "Description" msgstr "Описание" @@ -4447,6 +4450,39 @@ msgstr "" msgid "File operation canceled" msgstr "" +msgid "No error" +msgstr "" + +msgid "Unknown character in filter expression" +msgstr "" + +msgid "Unterminated string literal" +msgstr "" + +msgid "Syntax error in filter expression" +msgstr "" + +msgid "Failed to parse filter expression" +msgstr "" + +msgid "Invalid literal value" +msgstr "" + +msgid "Invalid regular expression" +msgstr "" + +msgid "Undefined identifier" +msgstr "" + +msgid "Invalid number of arguments" +msgstr "" + +msgid "Filter name not found" +msgstr "" + +msgid "Unknown error" +msgstr "" + msgid "Prettification" msgstr "" diff --git a/Translations/WinMerge/Catalan.po b/Translations/WinMerge/Catalan.po index 20063048580..3b5bdf10e92 100644 --- a/Translations/WinMerge/Catalan.po +++ b/Translations/WinMerge/Catalan.po @@ -1631,10 +1631,6 @@ msgstr "&Següent" msgid "Find &Prev" msgstr "&Anterior" -#, c-format -msgid "&Ok" -msgstr "&D'acord" - #, c-format msgid "Replace" msgstr "Substitueix" @@ -1686,6 +1682,10 @@ msgstr "Nova" msgid "&Background color:" msgstr "Color de fons:" +#, c-format +msgid "&Ok" +msgstr "&D'acord" + #, c-format msgid "&Apply" msgstr "&Aplica" @@ -2183,6 +2183,12 @@ msgstr "Reinicialitza" msgid "File Filters" msgstr "Filtres de fitxer" +msgid "&Mask / Filter Expression" +msgstr "" + +msgid "Preset &Filters" +msgstr "" + msgid "Test..." msgstr "&Prova..." @@ -3066,10 +3072,6 @@ msgstr "Ubicació" msgid "Filters" msgstr "Filtres" -#, c-format -msgid "[F] " -msgstr "[F] " - #, c-format msgid "Description" msgstr "Descripció" @@ -5125,6 +5127,39 @@ msgstr "" msgid "File operation canceled" msgstr "" +msgid "No error" +msgstr "" + +msgid "Unknown character in filter expression" +msgstr "" + +msgid "Unterminated string literal" +msgstr "" + +msgid "Syntax error in filter expression" +msgstr "" + +msgid "Failed to parse filter expression" +msgstr "" + +msgid "Invalid literal value" +msgstr "" + +msgid "Invalid regular expression" +msgstr "" + +msgid "Undefined identifier" +msgstr "" + +msgid "Invalid number of arguments" +msgstr "" + +msgid "Filter name not found" +msgstr "" + +msgid "Unknown error" +msgstr "" + msgid "Prettification" msgstr "Embellir" diff --git a/Translations/WinMerge/ChineseSimplified.po b/Translations/WinMerge/ChineseSimplified.po index f3427b1f109..f528d80674e 100644 --- a/Translations/WinMerge/ChineseSimplified.po +++ b/Translations/WinMerge/ChineseSimplified.po @@ -1366,9 +1366,6 @@ msgstr "查找下一个(&F)" msgid "Find &Prev" msgstr "查找上一个(&P)" -msgid "&Ok" -msgstr "确定(&O)" - msgid "Replace" msgstr "替换" @@ -1408,6 +1405,9 @@ msgstr "新建" msgid "&Background color:" msgstr "背景颜色(&B)" +msgid "&Ok" +msgstr "确定(&O)" + msgid "&Apply" msgstr "应用(&A)" @@ -1818,6 +1818,12 @@ msgstr "重置" msgid "File Filters" msgstr "文件过滤器" +msgid "&Mask / Filter Expression" +msgstr "" + +msgid "Preset &Filters" +msgstr "" + msgid "Test..." msgstr "测试..." @@ -2565,9 +2571,6 @@ msgstr "位置" msgid "Filters" msgstr "过滤器" -msgid "[F] " -msgstr "[F] " - msgid "Description" msgstr "描述" @@ -4519,6 +4522,39 @@ msgstr "文件操作已成功完成" msgid "File operation canceled" msgstr "文件操作已取消" +msgid "No error" +msgstr "" + +msgid "Unknown character in filter expression" +msgstr "" + +msgid "Unterminated string literal" +msgstr "" + +msgid "Syntax error in filter expression" +msgstr "" + +msgid "Failed to parse filter expression" +msgstr "" + +msgid "Invalid literal value" +msgstr "" + +msgid "Invalid regular expression" +msgstr "" + +msgid "Undefined identifier" +msgstr "" + +msgid "Invalid number of arguments" +msgstr "" + +msgid "Filter name not found" +msgstr "" + +msgid "Unknown error" +msgstr "" + msgid "Prettification" msgstr "格式美化" diff --git a/Translations/WinMerge/ChineseTraditional.po b/Translations/WinMerge/ChineseTraditional.po index 22bf10dcb0a..1e586045ec0 100644 --- a/Translations/WinMerge/ChineseTraditional.po +++ b/Translations/WinMerge/ChineseTraditional.po @@ -1640,10 +1640,6 @@ msgstr "找下一個 (&F)" msgid "Find &Prev" msgstr "找上一個 (&P)" -#, c-format -msgid "&Ok" -msgstr "確定 (&O)" - #, c-format msgid "Replace" msgstr "取代" @@ -1695,6 +1691,10 @@ msgstr "新增" msgid "&Background color:" msgstr "背景顏色 (&B)" +#, c-format +msgid "&Ok" +msgstr "確定 (&O)" + #, c-format msgid "&Apply" msgstr "套用 (&A)" @@ -2193,6 +2193,12 @@ msgstr "重設" msgid "File Filters" msgstr "檔案篩選器" +msgid "&Mask / Filter Expression" +msgstr "" + +msgid "Preset &Filters" +msgstr "" + msgid "Test..." msgstr "測試..." @@ -3076,10 +3082,6 @@ msgstr "位置" msgid "Filters" msgstr "篩選器" -#, c-format -msgid "[F] " -msgstr "[F] " - #, c-format msgid "Description" msgstr "描述" @@ -5106,6 +5108,39 @@ msgstr "" msgid "File operation canceled" msgstr "" +msgid "No error" +msgstr "" + +msgid "Unknown character in filter expression" +msgstr "" + +msgid "Unterminated string literal" +msgstr "" + +msgid "Syntax error in filter expression" +msgstr "" + +msgid "Failed to parse filter expression" +msgstr "" + +msgid "Invalid literal value" +msgstr "" + +msgid "Invalid regular expression" +msgstr "" + +msgid "Undefined identifier" +msgstr "" + +msgid "Invalid number of arguments" +msgstr "" + +msgid "Filter name not found" +msgstr "" + +msgid "Unknown error" +msgstr "" + msgid "Prettification" msgstr "美化" diff --git a/Translations/WinMerge/Corsican.po b/Translations/WinMerge/Corsican.po index 16d54714d30..69ef39ee3a7 100644 --- a/Translations/WinMerge/Corsican.po +++ b/Translations/WinMerge/Corsican.po @@ -1366,9 +1366,6 @@ msgstr "Circà &seguente" msgid "Find &Prev" msgstr "Circà &precedente" -msgid "&Ok" -msgstr "&Vai" - msgid "Replace" msgstr "Rimpiazzà" @@ -1408,6 +1405,9 @@ msgstr "Novu" msgid "&Background color:" msgstr "Culore di u &fondu :" +msgid "&Ok" +msgstr "&Vai" + msgid "&Apply" msgstr "&Appiecà" @@ -1823,6 +1823,12 @@ msgstr "Reinizià" msgid "File Filters" msgstr "Filtri di schedariu" +msgid "&Mask / Filter Expression" +msgstr "" + +msgid "Preset &Filters" +msgstr "" + msgid "Test..." msgstr "Prova…" @@ -2577,9 +2583,6 @@ msgstr "Piazza" msgid "Filters" msgstr "Filtri" -msgid "[F] " -msgstr "[F] " - msgid "Description" msgstr "Discrizzione" @@ -4555,6 +4558,39 @@ msgstr "Operazione di schedariu compia currettamente" msgid "File operation canceled" msgstr "Operazione di schedariu abbandunata" +msgid "No error" +msgstr "" + +msgid "Unknown character in filter expression" +msgstr "" + +msgid "Unterminated string literal" +msgstr "" + +msgid "Syntax error in filter expression" +msgstr "" + +msgid "Failed to parse filter expression" +msgstr "" + +msgid "Invalid literal value" +msgstr "" + +msgid "Invalid regular expression" +msgstr "" + +msgid "Undefined identifier" +msgstr "" + +msgid "Invalid number of arguments" +msgstr "" + +msgid "Filter name not found" +msgstr "" + +msgid "Unknown error" +msgstr "" + msgid "Prettification" msgstr "Imbellimentu" diff --git a/Translations/WinMerge/Croatian.po b/Translations/WinMerge/Croatian.po index 0ab3715508b..042bd4d91dd 100644 --- a/Translations/WinMerge/Croatian.po +++ b/Translations/WinMerge/Croatian.po @@ -1630,10 +1630,6 @@ msgstr "Traži &slijedeći" msgid "Find &Prev" msgstr "" -#, c-format -msgid "&Ok" -msgstr "&U redu" - #, c-format msgid "Replace" msgstr "Zamijeni" @@ -1685,6 +1681,10 @@ msgstr "Novi" msgid "&Background color:" msgstr "" +#, c-format +msgid "&Ok" +msgstr "&U redu" + #, c-format msgid "&Apply" msgstr "&Primjeni" @@ -2182,6 +2182,12 @@ msgstr "Poništi" msgid "File Filters" msgstr "" +msgid "&Mask / Filter Expression" +msgstr "" + +msgid "Preset &Filters" +msgstr "" + msgid "Test..." msgstr "Test..." @@ -3039,10 +3045,6 @@ msgstr "Položaj" msgid "Filters" msgstr "Filtri..." -#, c-format -msgid "[F] " -msgstr "[F] " - #, c-format msgid "Description" msgstr "Opis" @@ -4969,6 +4971,39 @@ msgstr "" msgid "File operation canceled" msgstr "" +msgid "No error" +msgstr "" + +msgid "Unknown character in filter expression" +msgstr "" + +msgid "Unterminated string literal" +msgstr "" + +msgid "Syntax error in filter expression" +msgstr "" + +msgid "Failed to parse filter expression" +msgstr "" + +msgid "Invalid literal value" +msgstr "" + +msgid "Invalid regular expression" +msgstr "" + +msgid "Undefined identifier" +msgstr "" + +msgid "Invalid number of arguments" +msgstr "" + +msgid "Filter name not found" +msgstr "" + +msgid "Unknown error" +msgstr "" + msgid "Prettification" msgstr "" diff --git a/Translations/WinMerge/Czech.po b/Translations/WinMerge/Czech.po index eba7a96b459..6726027fde4 100644 --- a/Translations/WinMerge/Czech.po +++ b/Translations/WinMerge/Czech.po @@ -1630,10 +1630,6 @@ msgstr "&Najít další" msgid "Find &Prev" msgstr "" -#, c-format -msgid "&Ok" -msgstr "&OK" - #, c-format msgid "Replace" msgstr "Nahradit" @@ -1685,6 +1681,10 @@ msgstr "&Nový" msgid "&Background color:" msgstr "" +#, c-format +msgid "&Ok" +msgstr "&OK" + #, c-format msgid "&Apply" msgstr "P&oužít" @@ -2184,6 +2184,12 @@ msgstr "V&rátit" msgid "File Filters" msgstr "" +msgid "&Mask / Filter Expression" +msgstr "" + +msgid "Preset &Filters" +msgstr "" + msgid "Test..." msgstr "Z&kouška..." @@ -3011,10 +3017,6 @@ msgstr "Umístění" msgid "Filters" msgstr "Filtry" -#, c-format -msgid "[F] " -msgstr "" - #, c-format msgid "Description" msgstr "Popis" @@ -4915,6 +4917,39 @@ msgstr "" msgid "File operation canceled" msgstr "" +msgid "No error" +msgstr "" + +msgid "Unknown character in filter expression" +msgstr "" + +msgid "Unterminated string literal" +msgstr "" + +msgid "Syntax error in filter expression" +msgstr "" + +msgid "Failed to parse filter expression" +msgstr "" + +msgid "Invalid literal value" +msgstr "" + +msgid "Invalid regular expression" +msgstr "" + +msgid "Undefined identifier" +msgstr "" + +msgid "Invalid number of arguments" +msgstr "" + +msgid "Filter name not found" +msgstr "" + +msgid "Unknown error" +msgstr "" + msgid "Prettification" msgstr "" diff --git a/Translations/WinMerge/Danish.po b/Translations/WinMerge/Danish.po index 12caa0c9134..f1b68e83072 100644 --- a/Translations/WinMerge/Danish.po +++ b/Translations/WinMerge/Danish.po @@ -1631,10 +1631,6 @@ msgstr "&Find næste" msgid "Find &Prev" msgstr "" -#, c-format -msgid "&Ok" -msgstr "&OK" - #, c-format msgid "Replace" msgstr "Erstat" @@ -1686,6 +1682,10 @@ msgstr "Ny" msgid "&Background color:" msgstr "" +#, c-format +msgid "&Ok" +msgstr "&OK" + #, c-format msgid "&Apply" msgstr "&Anvend" @@ -2185,6 +2185,12 @@ msgstr "Nulstil" msgid "File Filters" msgstr "" +msgid "&Mask / Filter Expression" +msgstr "" + +msgid "Preset &Filters" +msgstr "" + msgid "Test..." msgstr "Test..." @@ -3051,10 +3057,6 @@ msgstr "Sti" msgid "Filters" msgstr "Filtre" -#, c-format -msgid "[F] " -msgstr "[F]" - #, c-format msgid "Description" msgstr "Beskrivelse" @@ -4999,6 +5001,39 @@ msgstr "" msgid "File operation canceled" msgstr "" +msgid "No error" +msgstr "" + +msgid "Unknown character in filter expression" +msgstr "" + +msgid "Unterminated string literal" +msgstr "" + +msgid "Syntax error in filter expression" +msgstr "" + +msgid "Failed to parse filter expression" +msgstr "" + +msgid "Invalid literal value" +msgstr "" + +msgid "Invalid regular expression" +msgstr "" + +msgid "Undefined identifier" +msgstr "" + +msgid "Invalid number of arguments" +msgstr "" + +msgid "Filter name not found" +msgstr "" + +msgid "Unknown error" +msgstr "" + msgid "Prettification" msgstr "" diff --git a/Translations/WinMerge/Dutch.po b/Translations/WinMerge/Dutch.po index 93481118641..754ede6e196 100644 --- a/Translations/WinMerge/Dutch.po +++ b/Translations/WinMerge/Dutch.po @@ -1360,9 +1360,6 @@ msgstr "Volgende zoeken" msgid "Find &Prev" msgstr "Vorige zoeken" -msgid "&Ok" -msgstr "Ok" - msgid "Replace" msgstr "Vervangen" @@ -1402,6 +1399,9 @@ msgstr "Nieuw" msgid "&Background color:" msgstr "Achtergrondkleur:" +msgid "&Ok" +msgstr "Ok" + msgid "&Apply" msgstr "Toepassen" @@ -1817,6 +1817,12 @@ msgstr "Herstellen" msgid "File Filters" msgstr "Bestandsfilters" +msgid "&Mask / Filter Expression" +msgstr "" + +msgid "Preset &Filters" +msgstr "" + msgid "Test..." msgstr "Testen..." @@ -2564,9 +2570,6 @@ msgstr "Locatie" msgid "Filters" msgstr "Filters" -msgid "[F] " -msgstr "[F] " - msgid "Description" msgstr "Beschrijving" @@ -4501,6 +4504,39 @@ msgstr "" msgid "File operation canceled" msgstr "" +msgid "No error" +msgstr "" + +msgid "Unknown character in filter expression" +msgstr "" + +msgid "Unterminated string literal" +msgstr "" + +msgid "Syntax error in filter expression" +msgstr "" + +msgid "Failed to parse filter expression" +msgstr "" + +msgid "Invalid literal value" +msgstr "" + +msgid "Invalid regular expression" +msgstr "" + +msgid "Undefined identifier" +msgstr "" + +msgid "Invalid number of arguments" +msgstr "" + +msgid "Filter name not found" +msgstr "" + +msgid "Unknown error" +msgstr "" + msgid "Prettification" msgstr "Mooi maken" diff --git a/Translations/WinMerge/English.pot b/Translations/WinMerge/English.pot index 0a954fc20c9..222243ed1a8 100644 --- a/Translations/WinMerge/English.pot +++ b/Translations/WinMerge/English.pot @@ -5,7 +5,7 @@ msgid "" msgstr "" "Project-Id-Version: WinMerge\n" "Report-Msgid-Bugs-To: https://github.com/WinMerge/winmerge/issues/\n" -"POT-Creation-Date: 2025-04-17 09:05+0000\n" +"POT-Creation-Date: 2025-06-24 07:52+0000\n" "PO-Revision-Date: \n" "Last-Translator: \n" "Language-Team: English \n" @@ -1356,9 +1356,6 @@ msgstr "" msgid "Find &Prev" msgstr "" -msgid "&Ok" -msgstr "" - msgid "Replace" msgstr "" @@ -1398,6 +1395,9 @@ msgstr "" msgid "&Background color:" msgstr "" +msgid "&Ok" +msgstr "" + msgid "&Apply" msgstr "" @@ -1803,6 +1803,12 @@ msgstr "" msgid "File Filters" msgstr "" +msgid "&Mask / Filter Expression" +msgstr "" + +msgid "Preset &Filters" +msgstr "" + msgid "Test..." msgstr "" @@ -2480,9 +2486,6 @@ msgstr "" msgid "Filters" msgstr "" -msgid "[F] " -msgstr "" - msgid "Description" msgstr "" @@ -4068,6 +4071,39 @@ msgstr "" msgid "File operation canceled" msgstr "" +msgid "No error" +msgstr "" + +msgid "Unknown character in filter expression" +msgstr "" + +msgid "Unterminated string literal" +msgstr "" + +msgid "Syntax error in filter expression" +msgstr "" + +msgid "Failed to parse filter expression" +msgstr "" + +msgid "Invalid literal value" +msgstr "" + +msgid "Invalid regular expression" +msgstr "" + +msgid "Undefined identifier" +msgstr "" + +msgid "Invalid number of arguments" +msgstr "" + +msgid "Filter name not found" +msgstr "" + +msgid "Unknown error" +msgstr "" + msgid "Prettification" msgstr "" diff --git a/Translations/WinMerge/Finnish.po b/Translations/WinMerge/Finnish.po index 488b2ca61b9..2d96324a4b4 100644 --- a/Translations/WinMerge/Finnish.po +++ b/Translations/WinMerge/Finnish.po @@ -1362,9 +1362,6 @@ msgstr "Etsi seuraava" msgid "Find &Prev" msgstr "Etsi &edellinen" -msgid "&Ok" -msgstr "OK" - msgid "Replace" msgstr "Korvaa" @@ -1404,6 +1401,9 @@ msgstr "Uusi" msgid "&Background color:" msgstr "&Taustaväri:" +msgid "&Ok" +msgstr "OK" + msgid "&Apply" msgstr "Käytä" @@ -1818,6 +1818,12 @@ msgstr "Palauta" msgid "File Filters" msgstr "Tiedostosuodattimet" +msgid "&Mask / Filter Expression" +msgstr "" + +msgid "Preset &Filters" +msgstr "" + msgid "Test..." msgstr "Testi..." @@ -2560,9 +2566,6 @@ msgstr "Sijainti" msgid "Filters" msgstr "Suotimet" -msgid "[F] " -msgstr "[F] " - msgid "Description" msgstr "Kuvaus" @@ -4408,6 +4411,39 @@ msgstr "" msgid "File operation canceled" msgstr "" +msgid "No error" +msgstr "" + +msgid "Unknown character in filter expression" +msgstr "" + +msgid "Unterminated string literal" +msgstr "" + +msgid "Syntax error in filter expression" +msgstr "" + +msgid "Failed to parse filter expression" +msgstr "" + +msgid "Invalid literal value" +msgstr "" + +msgid "Invalid regular expression" +msgstr "" + +msgid "Undefined identifier" +msgstr "" + +msgid "Invalid number of arguments" +msgstr "" + +msgid "Filter name not found" +msgstr "" + +msgid "Unknown error" +msgstr "" + msgid "Prettification" msgstr "Kaunistelu" diff --git a/Translations/WinMerge/French.po b/Translations/WinMerge/French.po index 37144144f89..a9735b3acb5 100644 --- a/Translations/WinMerge/French.po +++ b/Translations/WinMerge/French.po @@ -1369,9 +1369,6 @@ msgstr "&Suivant" msgid "Find &Prev" msgstr "&Précédent" -msgid "&Ok" -msgstr "&Ok" - msgid "Replace" msgstr "Remplacer" @@ -1411,6 +1408,9 @@ msgstr "Nouveau" msgid "&Background color:" msgstr "&Couleur du fond :" +msgid "&Ok" +msgstr "&Ok" + msgid "&Apply" msgstr "&Appliquer" @@ -1818,6 +1818,12 @@ msgstr "Réinitialiser" msgid "File Filters" msgstr "Filtres de fichier" +msgid "&Mask / Filter Expression" +msgstr "" + +msgid "Preset &Filters" +msgstr "" + msgid "Test..." msgstr "Tester..." @@ -2551,9 +2557,6 @@ msgstr "Emplacement" msgid "Filters" msgstr "Filtres" -msgid "[F] " -msgstr "[F] " - msgid "Description" msgstr "Description" @@ -4494,6 +4497,39 @@ msgstr "L'opération sur le fichier s'est déroulé avec succès" msgid "File operation canceled" msgstr "L'opération sur le fichier a été annulée" +msgid "No error" +msgstr "" + +msgid "Unknown character in filter expression" +msgstr "" + +msgid "Unterminated string literal" +msgstr "" + +msgid "Syntax error in filter expression" +msgstr "" + +msgid "Failed to parse filter expression" +msgstr "" + +msgid "Invalid literal value" +msgstr "" + +msgid "Invalid regular expression" +msgstr "" + +msgid "Undefined identifier" +msgstr "" + +msgid "Invalid number of arguments" +msgstr "" + +msgid "Filter name not found" +msgstr "" + +msgid "Unknown error" +msgstr "" + msgid "Prettification" msgstr "Embellissement" diff --git a/Translations/WinMerge/Galician.po b/Translations/WinMerge/Galician.po index 9de93103d22..99abeced23a 100644 --- a/Translations/WinMerge/Galician.po +++ b/Translations/WinMerge/Galician.po @@ -1363,9 +1363,6 @@ msgstr "&Seguinte" msgid "Find &Prev" msgstr "&Anterior" -msgid "&Ok" -msgstr "Ace&ptar" - msgid "Replace" msgstr "Substituír" @@ -1405,6 +1402,9 @@ msgstr "Novo" msgid "&Background color:" msgstr "Cor de &fondo:" +msgid "&Ok" +msgstr "Ace&ptar" + msgid "&Apply" msgstr "&Aplicar" @@ -1810,6 +1810,12 @@ msgstr "Restablecer" msgid "File Filters" msgstr "Filtros de ficheiro" +msgid "&Mask / Filter Expression" +msgstr "" + +msgid "Preset &Filters" +msgstr "" + msgid "Test..." msgstr "Probar..." @@ -2546,9 +2552,6 @@ msgstr "Ubicación" msgid "Filters" msgstr "Filtros" -msgid "[F] " -msgstr "[F] " - msgid "Description" msgstr "Descrición" @@ -4411,6 +4414,39 @@ msgstr "" msgid "File operation canceled" msgstr "" +msgid "No error" +msgstr "" + +msgid "Unknown character in filter expression" +msgstr "" + +msgid "Unterminated string literal" +msgstr "" + +msgid "Syntax error in filter expression" +msgstr "" + +msgid "Failed to parse filter expression" +msgstr "" + +msgid "Invalid literal value" +msgstr "" + +msgid "Invalid regular expression" +msgstr "" + +msgid "Undefined identifier" +msgstr "" + +msgid "Invalid number of arguments" +msgstr "" + +msgid "Filter name not found" +msgstr "" + +msgid "Unknown error" +msgstr "" + msgid "Prettification" msgstr "Melloras" diff --git a/Translations/WinMerge/German.po b/Translations/WinMerge/German.po index a14ab644c1a..fd2a716bf26 100644 --- a/Translations/WinMerge/German.po +++ b/Translations/WinMerge/German.po @@ -1634,10 +1634,6 @@ msgstr "&Weitersuchen" msgid "Find &Prev" msgstr "R&ückwärtssuchen" -#, c-format -msgid "&Ok" -msgstr "&OK" - #, c-format msgid "Replace" msgstr "Ersetzen" @@ -1689,6 +1685,10 @@ msgstr "Neu" msgid "&Background color:" msgstr "&Hintergrundfarbe:" +#, c-format +msgid "&Ok" +msgstr "&OK" + #, c-format msgid "&Apply" msgstr "An&wenden" @@ -2186,6 +2186,12 @@ msgstr "Zurücksetzen" msgid "File Filters" msgstr "Dateifilter" +msgid "&Mask / Filter Expression" +msgstr "" + +msgid "Preset &Filters" +msgstr "" + msgid "Test..." msgstr "Testen..." @@ -3010,10 +3016,6 @@ msgstr "Pfad" msgid "Filters" msgstr "Filter" -#, c-format -msgid "[F] " -msgstr "[F] " - #, c-format msgid "Description" msgstr "Beschreibung" @@ -4791,6 +4793,39 @@ msgstr "" msgid "File operation canceled" msgstr "" +msgid "No error" +msgstr "" + +msgid "Unknown character in filter expression" +msgstr "" + +msgid "Unterminated string literal" +msgstr "" + +msgid "Syntax error in filter expression" +msgstr "" + +msgid "Failed to parse filter expression" +msgstr "" + +msgid "Invalid literal value" +msgstr "" + +msgid "Invalid regular expression" +msgstr "" + +msgid "Undefined identifier" +msgstr "" + +msgid "Invalid number of arguments" +msgstr "" + +msgid "Filter name not found" +msgstr "" + +msgid "Unknown error" +msgstr "" + msgid "Prettification" msgstr "Verschönerung" diff --git a/Translations/WinMerge/Greek.po b/Translations/WinMerge/Greek.po index 84e5b447810..a5ad0c4f64e 100644 --- a/Translations/WinMerge/Greek.po +++ b/Translations/WinMerge/Greek.po @@ -1629,10 +1629,6 @@ msgstr "Εύρεση &Επομένου" msgid "Find &Prev" msgstr "" -#, c-format -msgid "&Ok" -msgstr "&ΟΚ" - #, c-format msgid "Replace" msgstr "Αντικατάσταση" @@ -1684,6 +1680,10 @@ msgstr "Δημιουργία" msgid "&Background color:" msgstr "" +#, c-format +msgid "&Ok" +msgstr "&ΟΚ" + #, c-format msgid "&Apply" msgstr "Ε&φαρμογή" @@ -2181,6 +2181,12 @@ msgstr "Επαναφορά" msgid "File Filters" msgstr "" +msgid "&Mask / Filter Expression" +msgstr "" + +msgid "Preset &Filters" +msgstr "" + msgid "Test..." msgstr "Δοκιμή..." @@ -3036,10 +3042,6 @@ msgstr "Τοποθεσία" msgid "Filters" msgstr "Φίλτρα" -#, c-format -msgid "[F] " -msgstr "" - #, c-format msgid "Description" msgstr "Περιγραφή" @@ -4952,6 +4954,39 @@ msgstr "" msgid "File operation canceled" msgstr "" +msgid "No error" +msgstr "" + +msgid "Unknown character in filter expression" +msgstr "" + +msgid "Unterminated string literal" +msgstr "" + +msgid "Syntax error in filter expression" +msgstr "" + +msgid "Failed to parse filter expression" +msgstr "" + +msgid "Invalid literal value" +msgstr "" + +msgid "Invalid regular expression" +msgstr "" + +msgid "Undefined identifier" +msgstr "" + +msgid "Invalid number of arguments" +msgstr "" + +msgid "Filter name not found" +msgstr "" + +msgid "Unknown error" +msgstr "" + msgid "Prettification" msgstr "" diff --git a/Translations/WinMerge/Hebrew.po b/Translations/WinMerge/Hebrew.po index 1830554f206..e5569116ed8 100644 --- a/Translations/WinMerge/Hebrew.po +++ b/Translations/WinMerge/Hebrew.po @@ -1359,9 +1359,6 @@ msgstr "חפש הבא" msgid "Find &Prev" msgstr "חפש קודם" -msgid "&Ok" -msgstr "אישור" - msgid "Replace" msgstr "החלף" @@ -1401,6 +1398,9 @@ msgstr "חדש" msgid "&Background color:" msgstr "צבע רקע:" +msgid "&Ok" +msgstr "אישור" + msgid "&Apply" msgstr "החל" @@ -1806,6 +1806,12 @@ msgstr "אפס" msgid "File Filters" msgstr "מסנני קבצים" +msgid "&Mask / Filter Expression" +msgstr "" + +msgid "Preset &Filters" +msgstr "" + msgid "Test..." msgstr "בדיקה..." @@ -2530,9 +2536,6 @@ msgstr "מיקום" msgid "Filters" msgstr "מסננים" -msgid "[F] " -msgstr "[F] " - msgid "Description" msgstr "תיאור" @@ -4327,6 +4330,39 @@ msgstr "" msgid "File operation canceled" msgstr "" +msgid "No error" +msgstr "" + +msgid "Unknown character in filter expression" +msgstr "" + +msgid "Unterminated string literal" +msgstr "" + +msgid "Syntax error in filter expression" +msgstr "" + +msgid "Failed to parse filter expression" +msgstr "" + +msgid "Invalid literal value" +msgstr "" + +msgid "Invalid regular expression" +msgstr "" + +msgid "Undefined identifier" +msgstr "" + +msgid "Invalid number of arguments" +msgstr "" + +msgid "Filter name not found" +msgstr "" + +msgid "Unknown error" +msgstr "" + msgid "Prettification" msgstr "עיצוב" diff --git a/Translations/WinMerge/Hungarian.po b/Translations/WinMerge/Hungarian.po index efb3314ee82..ee062157812 100644 --- a/Translations/WinMerge/Hungarian.po +++ b/Translations/WinMerge/Hungarian.po @@ -1632,10 +1632,6 @@ msgstr "Kere&sés" msgid "Find &Prev" msgstr "Következő" -#, c-format -msgid "&Ok" -msgstr "OK" - #, c-format msgid "Replace" msgstr "Csere" @@ -1687,6 +1683,10 @@ msgstr "Új" msgid "&Background color:" msgstr "Háttérszín:" +#, c-format +msgid "&Ok" +msgstr "OK" + #, c-format msgid "&Apply" msgstr "Alkalmaz" @@ -2184,6 +2184,12 @@ msgstr "Visszaállítás" msgid "File Filters" msgstr "Fájlszűrők" +msgid "&Mask / Filter Expression" +msgstr "" + +msgid "Preset &Filters" +msgstr "" + msgid "Test..." msgstr "Teszt..." @@ -3063,10 +3069,6 @@ msgstr "Hely" msgid "Filters" msgstr "Szűrők" -#, c-format -msgid "[F] " -msgstr "[F] " - #, c-format msgid "Description" msgstr "Leírás" @@ -5081,6 +5083,39 @@ msgstr "A fájlművelet sikeresen befejeződött" msgid "File operation canceled" msgstr "A fájlművelet megszakítva" +msgid "No error" +msgstr "" + +msgid "Unknown character in filter expression" +msgstr "" + +msgid "Unterminated string literal" +msgstr "" + +msgid "Syntax error in filter expression" +msgstr "" + +msgid "Failed to parse filter expression" +msgstr "" + +msgid "Invalid literal value" +msgstr "" + +msgid "Invalid regular expression" +msgstr "" + +msgid "Undefined identifier" +msgstr "" + +msgid "Invalid number of arguments" +msgstr "" + +msgid "Filter name not found" +msgstr "" + +msgid "Unknown error" +msgstr "" + msgid "Prettification" msgstr "Megjelenés optimalizálás" diff --git a/Translations/WinMerge/Italian.po b/Translations/WinMerge/Italian.po index 8b87efb0d83..319e5e96aa3 100644 --- a/Translations/WinMerge/Italian.po +++ b/Translations/WinMerge/Italian.po @@ -1365,9 +1365,6 @@ msgstr "Trova &successivo" msgid "Find &Prev" msgstr "Trova &precedente" -msgid "&Ok" -msgstr "&OK" - msgid "Replace" msgstr "Sostituisci" @@ -1407,6 +1404,9 @@ msgstr "Nuovo" msgid "&Background color:" msgstr "Colore di s&fondo:" +msgid "&Ok" +msgstr "&OK" + msgid "&Apply" msgstr "&Applica" @@ -1823,6 +1823,12 @@ msgstr "Ripristina" msgid "File Filters" msgstr "Filtri file" +msgid "&Mask / Filter Expression" +msgstr "" + +msgid "Preset &Filters" +msgstr "" + msgid "Test..." msgstr "Prova..." @@ -2575,9 +2581,6 @@ msgstr "Percorso" msgid "Filters" msgstr "Filtri" -msgid "[F] " -msgstr "[F] " - msgid "Description" msgstr "Descrizione" @@ -4580,6 +4583,39 @@ msgstr "Operazione file completata correttamente." msgid "File operation canceled" msgstr "Operazione file annullata" +msgid "No error" +msgstr "" + +msgid "Unknown character in filter expression" +msgstr "" + +msgid "Unterminated string literal" +msgstr "" + +msgid "Syntax error in filter expression" +msgstr "" + +msgid "Failed to parse filter expression" +msgstr "" + +msgid "Invalid literal value" +msgstr "" + +msgid "Invalid regular expression" +msgstr "" + +msgid "Undefined identifier" +msgstr "" + +msgid "Invalid number of arguments" +msgstr "" + +msgid "Filter name not found" +msgstr "" + +msgid "Unknown error" +msgstr "" + msgid "Prettification" msgstr "Abbellimento" diff --git a/Translations/WinMerge/Japanese.po b/Translations/WinMerge/Japanese.po index 926408d1d46..a2ca22f14dd 100644 --- a/Translations/WinMerge/Japanese.po +++ b/Translations/WinMerge/Japanese.po @@ -1361,9 +1361,6 @@ msgstr "次を検索(&F)" msgid "Find &Prev" msgstr "前を検索(&P)" -msgid "&Ok" -msgstr "&OK" - msgid "Replace" msgstr "置換" @@ -1403,6 +1400,9 @@ msgstr "新規" msgid "&Background color:" msgstr "背景色(&B)" +msgid "&Ok" +msgstr "&OK" + msgid "&Apply" msgstr "適用(&A)" @@ -1812,6 +1812,12 @@ msgstr "リセット" msgid "File Filters" msgstr "ファイル フィルター" +msgid "&Mask / Filter Expression" +msgstr "" + +msgid "Preset &Filters" +msgstr "" + msgid "Test..." msgstr "テスト..." @@ -2548,9 +2554,6 @@ msgstr "場所" msgid "Filters" msgstr "フィルター" -msgid "[F] " -msgstr "[F] " - msgid "Description" msgstr "説明" @@ -4468,6 +4471,39 @@ msgstr "ファイル操作が正常に完了しました" msgid "File operation canceled" msgstr "ファイル操作がキャンセルされました" +msgid "No error" +msgstr "エラーはありません" + +msgid "Unknown character in filter expression" +msgstr "フィルター式に不明な文字があります" + +msgid "Unterminated string literal" +msgstr "文字列リテラルが閉じられていません" + +msgid "Syntax error in filter expression" +msgstr "フィルター式の構文エラーです" + +msgid "Failed to parse filter expression" +msgstr "フィルター式の解析に失敗しました" + +msgid "Invalid literal value" +msgstr "無効なリテラル値です" + +msgid "Invalid regular expression" +msgstr "無効な正規表現です" + +msgid "Undefined identifier" +msgstr "未定義の識別子です" + +msgid "Invalid number of arguments" +msgstr "引数の数が正しくありません" + +msgid "Filter name not found" +msgstr "" + +msgid "Unknown error" +msgstr "不明なエラーです" + msgid "Prettification" msgstr "整形" diff --git a/Translations/WinMerge/Korean.po b/Translations/WinMerge/Korean.po index 9726ff6cfa7..078e7d22db3 100644 --- a/Translations/WinMerge/Korean.po +++ b/Translations/WinMerge/Korean.po @@ -1639,10 +1639,6 @@ msgstr "다음 찾기(&F)" msgid "Find &Prev" msgstr "이전 찾기(&P)" -#, c-format -msgid "&Ok" -msgstr "확인(&O)" - #, c-format msgid "Replace" msgstr "바꾸기" @@ -1694,6 +1690,10 @@ msgstr "새로 만들기" msgid "&Background color:" msgstr "배경 색상(&B):" +#, c-format +msgid "&Ok" +msgstr "확인(&O)" + #, c-format msgid "&Apply" msgstr "적용(&A)" @@ -2191,6 +2191,12 @@ msgstr "재설정" msgid "File Filters" msgstr "파일 필터" +msgid "&Mask / Filter Expression" +msgstr "" + +msgid "Preset &Filters" +msgstr "" + msgid "Test..." msgstr "테스트..." @@ -3074,10 +3080,6 @@ msgstr "위치" msgid "Filters" msgstr "필터" -#, c-format -msgid "[F] " -msgstr "[F] " - #, c-format msgid "Description" msgstr "설명" @@ -5220,6 +5222,39 @@ msgstr "파일 작업이 성공적으로 완료되었습니다" msgid "File operation canceled" msgstr "파일 작업이 취소되었습니다" +msgid "No error" +msgstr "" + +msgid "Unknown character in filter expression" +msgstr "" + +msgid "Unterminated string literal" +msgstr "" + +msgid "Syntax error in filter expression" +msgstr "" + +msgid "Failed to parse filter expression" +msgstr "" + +msgid "Invalid literal value" +msgstr "" + +msgid "Invalid regular expression" +msgstr "" + +msgid "Undefined identifier" +msgstr "" + +msgid "Invalid number of arguments" +msgstr "" + +msgid "Filter name not found" +msgstr "" + +msgid "Unknown error" +msgstr "" + msgid "Prettification" msgstr "꾸미기" diff --git a/Translations/WinMerge/Lithuanian.po b/Translations/WinMerge/Lithuanian.po index ffcfb62132c..79bff18a5d1 100644 --- a/Translations/WinMerge/Lithuanian.po +++ b/Translations/WinMerge/Lithuanian.po @@ -1362,9 +1362,6 @@ msgstr "&Rasti kitą" msgid "Find &Prev" msgstr "Rasti &praeitą" -msgid "&Ok" -msgstr "&Gerai" - msgid "Replace" msgstr "Pakeisti" @@ -1404,6 +1401,9 @@ msgstr "Naujas" msgid "&Background color:" msgstr "&Fono spalva:" +msgid "&Ok" +msgstr "&Gerai" + msgid "&Apply" msgstr "T&aikyti" @@ -1809,6 +1809,12 @@ msgstr "Atkurti" msgid "File Filters" msgstr "Failo filtrai" +msgid "&Mask / Filter Expression" +msgstr "" + +msgid "Preset &Filters" +msgstr "" + msgid "Test..." msgstr "Testas..." @@ -2486,9 +2492,6 @@ msgstr "Vieta" msgid "Filters" msgstr "Filtrai" -msgid "[F] " -msgstr "[F] " - msgid "Description" msgstr "Aprašas" @@ -4073,6 +4076,39 @@ msgstr "Failo operacija baigta sėkmingai" msgid "File operation canceled" msgstr "Failo operacija atšaukta" +msgid "No error" +msgstr "" + +msgid "Unknown character in filter expression" +msgstr "" + +msgid "Unterminated string literal" +msgstr "" + +msgid "Syntax error in filter expression" +msgstr "" + +msgid "Failed to parse filter expression" +msgstr "" + +msgid "Invalid literal value" +msgstr "" + +msgid "Invalid regular expression" +msgstr "" + +msgid "Undefined identifier" +msgstr "" + +msgid "Invalid number of arguments" +msgstr "" + +msgid "Filter name not found" +msgstr "" + +msgid "Unknown error" +msgstr "" + msgid "Prettification" msgstr "Sutvarkymas" diff --git a/Translations/WinMerge/Norwegian.po b/Translations/WinMerge/Norwegian.po index fdbd7043ac4..9c04d1dc1a0 100644 --- a/Translations/WinMerge/Norwegian.po +++ b/Translations/WinMerge/Norwegian.po @@ -1361,9 +1361,6 @@ msgstr "&Finn neste" msgid "Find &Prev" msgstr "" -msgid "&Ok" -msgstr "&Ok" - msgid "Replace" msgstr "Erstatt" @@ -1403,6 +1400,9 @@ msgstr "Ny" msgid "&Background color:" msgstr "" +msgid "&Ok" +msgstr "&Ok" + msgid "&Apply" msgstr "&Bruk" @@ -1814,6 +1814,12 @@ msgstr "Tilbakestill" msgid "File Filters" msgstr "" +msgid "&Mask / Filter Expression" +msgstr "" + +msgid "Preset &Filters" +msgstr "" + msgid "Test..." msgstr "Test..." @@ -2557,9 +2563,6 @@ msgstr "Plassering" msgid "Filters" msgstr "Filter" -msgid "[F] " -msgstr "[F] " - msgid "Description" msgstr "Beskrivelse" @@ -4380,6 +4383,39 @@ msgstr "" msgid "File operation canceled" msgstr "" +msgid "No error" +msgstr "" + +msgid "Unknown character in filter expression" +msgstr "" + +msgid "Unterminated string literal" +msgstr "" + +msgid "Syntax error in filter expression" +msgstr "" + +msgid "Failed to parse filter expression" +msgstr "" + +msgid "Invalid literal value" +msgstr "" + +msgid "Invalid regular expression" +msgstr "" + +msgid "Undefined identifier" +msgstr "" + +msgid "Invalid number of arguments" +msgstr "" + +msgid "Filter name not found" +msgstr "" + +msgid "Unknown error" +msgstr "" + msgid "Prettification" msgstr "" diff --git a/Translations/WinMerge/Persian.po b/Translations/WinMerge/Persian.po index 9da1f66cc3d..16198569aa0 100644 --- a/Translations/WinMerge/Persian.po +++ b/Translations/WinMerge/Persian.po @@ -1633,10 +1633,6 @@ msgstr "&F يافتن بعدي " msgid "Find &Prev" msgstr "" -#, c-format -msgid "&Ok" -msgstr "&O تاييد " - #, c-format msgid "Replace" msgstr " جايگزيني " @@ -1688,6 +1684,10 @@ msgstr " جديد " msgid "&Background color:" msgstr "" +#, c-format +msgid "&Ok" +msgstr "&O تاييد " + #, c-format msgid "&Apply" msgstr "&A به کار بستن " @@ -2187,6 +2187,12 @@ msgstr " بازنشاني " msgid "File Filters" msgstr "" +msgid "&Mask / Filter Expression" +msgstr "" + +msgid "Preset &Filters" +msgstr "" + msgid "Test..." msgstr " آزمايش ..." @@ -3054,10 +3060,6 @@ msgstr " محل " msgid "Filters" msgstr " صافيها " -#, c-format -msgid "[F] " -msgstr "[F] " - #, c-format msgid "Description" msgstr " شرح " @@ -5009,6 +5011,39 @@ msgstr "" msgid "File operation canceled" msgstr "" +msgid "No error" +msgstr "" + +msgid "Unknown character in filter expression" +msgstr "" + +msgid "Unterminated string literal" +msgstr "" + +msgid "Syntax error in filter expression" +msgstr "" + +msgid "Failed to parse filter expression" +msgstr "" + +msgid "Invalid literal value" +msgstr "" + +msgid "Invalid regular expression" +msgstr "" + +msgid "Undefined identifier" +msgstr "" + +msgid "Invalid number of arguments" +msgstr "" + +msgid "Filter name not found" +msgstr "" + +msgid "Unknown error" +msgstr "" + msgid "Prettification" msgstr "" diff --git a/Translations/WinMerge/Polish.po b/Translations/WinMerge/Polish.po index 36bf16b082c..5ec9c1dbc17 100644 --- a/Translations/WinMerge/Polish.po +++ b/Translations/WinMerge/Polish.po @@ -1363,9 +1363,6 @@ msgstr "Znajdź nast." msgid "Find &Prev" msgstr "Znajdź &poprz." -msgid "&Ok" -msgstr "&Ok" - msgid "Replace" msgstr "Zastąp" @@ -1405,6 +1402,9 @@ msgstr "Nowy" msgid "&Background color:" msgstr "Kolor tła:" +msgid "&Ok" +msgstr "&Ok" + msgid "&Apply" msgstr "Z&astosuj" @@ -1810,6 +1810,12 @@ msgstr "Resetuj" msgid "File Filters" msgstr "Filtry plików" +msgid "&Mask / Filter Expression" +msgstr "" + +msgid "Preset &Filters" +msgstr "" + msgid "Test..." msgstr "Testuj..." @@ -2487,9 +2493,6 @@ msgstr "Lokalizacja" msgid "Filters" msgstr "Filtry" -msgid "[F] " -msgstr "[F] " - msgid "Description" msgstr "Opis" @@ -4074,6 +4077,39 @@ msgstr "" msgid "File operation canceled" msgstr "" +msgid "No error" +msgstr "" + +msgid "Unknown character in filter expression" +msgstr "" + +msgid "Unterminated string literal" +msgstr "" + +msgid "Syntax error in filter expression" +msgstr "" + +msgid "Failed to parse filter expression" +msgstr "" + +msgid "Invalid literal value" +msgstr "" + +msgid "Invalid regular expression" +msgstr "" + +msgid "Undefined identifier" +msgstr "" + +msgid "Invalid number of arguments" +msgstr "" + +msgid "Filter name not found" +msgstr "" + +msgid "Unknown error" +msgstr "" + msgid "Prettification" msgstr "Upiększanie" diff --git a/Translations/WinMerge/Portuguese.po b/Translations/WinMerge/Portuguese.po index a5f28fb2683..0041c08b6d9 100644 --- a/Translations/WinMerge/Portuguese.po +++ b/Translations/WinMerge/Portuguese.po @@ -1363,9 +1363,6 @@ msgstr "Localizar &seguinte" msgid "Find &Prev" msgstr "Localizar &anterior" -msgid "&Ok" -msgstr "&Ok" - msgid "Replace" msgstr "Substituir" @@ -1405,6 +1402,9 @@ msgstr "Novo" msgid "&Background color:" msgstr "&Cor de fundo:" +msgid "&Ok" +msgstr "&Ok" + msgid "&Apply" msgstr "Aplicar" @@ -1810,6 +1810,12 @@ msgstr "Repor" msgid "File Filters" msgstr "Filtros de ficheiro" +msgid "&Mask / Filter Expression" +msgstr "" + +msgid "Preset &Filters" +msgstr "" + msgid "Test..." msgstr "Testar..." @@ -2548,9 +2554,6 @@ msgstr "Localização" msgid "Filters" msgstr "Filtros" -msgid "[F] " -msgstr "[F] " - msgid "Description" msgstr "Descrição" @@ -4503,6 +4506,39 @@ msgstr "Operação de ficheiros concluída com sucesso" msgid "File operation canceled" msgstr "Operação de ficheiros cancelada" +msgid "No error" +msgstr "" + +msgid "Unknown character in filter expression" +msgstr "" + +msgid "Unterminated string literal" +msgstr "" + +msgid "Syntax error in filter expression" +msgstr "" + +msgid "Failed to parse filter expression" +msgstr "" + +msgid "Invalid literal value" +msgstr "" + +msgid "Invalid regular expression" +msgstr "" + +msgid "Undefined identifier" +msgstr "" + +msgid "Invalid number of arguments" +msgstr "" + +msgid "Filter name not found" +msgstr "" + +msgid "Unknown error" +msgstr "" + msgid "Prettification" msgstr "Embelezamento" diff --git a/Translations/WinMerge/Romanian.po b/Translations/WinMerge/Romanian.po index 820a4b7babc..6e8012e4a66 100644 --- a/Translations/WinMerge/Romanian.po +++ b/Translations/WinMerge/Romanian.po @@ -1363,9 +1363,6 @@ msgstr "&Găsește următorul" msgid "Find &Prev" msgstr "Găsește precedentul" -msgid "&Ok" -msgstr "&Ok" - msgid "Replace" msgstr "Înlocuiește" @@ -1405,6 +1402,9 @@ msgstr "Nou" msgid "&Background color:" msgstr "Culoare fundal:" +msgid "&Ok" +msgstr "&Ok" + msgid "&Apply" msgstr "&Aplică" @@ -1818,6 +1818,12 @@ msgstr "Reinițializare" msgid "File Filters" msgstr "Filtre de fișiere" +msgid "&Mask / Filter Expression" +msgstr "" + +msgid "Preset &Filters" +msgstr "" + msgid "Test..." msgstr "Testează..." @@ -2567,9 +2573,6 @@ msgstr "Locație" msgid "Filters" msgstr "Filtre" -msgid "[F] " -msgstr "[F] " - msgid "Description" msgstr "Descriere" @@ -4459,6 +4462,39 @@ msgstr "" msgid "File operation canceled" msgstr "" +msgid "No error" +msgstr "" + +msgid "Unknown character in filter expression" +msgstr "" + +msgid "Unterminated string literal" +msgstr "" + +msgid "Syntax error in filter expression" +msgstr "" + +msgid "Failed to parse filter expression" +msgstr "" + +msgid "Invalid literal value" +msgstr "" + +msgid "Invalid regular expression" +msgstr "" + +msgid "Undefined identifier" +msgstr "" + +msgid "Invalid number of arguments" +msgstr "" + +msgid "Filter name not found" +msgstr "" + +msgid "Unknown error" +msgstr "" + msgid "Prettification" msgstr "Înfrumusețare" diff --git a/Translations/WinMerge/Russian.po b/Translations/WinMerge/Russian.po index 08ee1389191..208bc0ff81a 100644 --- a/Translations/WinMerge/Russian.po +++ b/Translations/WinMerge/Russian.po @@ -1364,9 +1364,6 @@ msgstr "Следующее" msgid "Find &Prev" msgstr "Предыдущее" -msgid "&Ok" -msgstr "&Ok" - msgid "Replace" msgstr "Заменить" @@ -1406,6 +1403,9 @@ msgstr "Создать" msgid "&Background color:" msgstr "Цвет фона:" +msgid "&Ok" +msgstr "&Ok" + msgid "&Apply" msgstr "&Применить" @@ -1811,6 +1811,12 @@ msgstr "Сброс" msgid "File Filters" msgstr "Фильтры файлов" +msgid "&Mask / Filter Expression" +msgstr "" + +msgid "Preset &Filters" +msgstr "" + msgid "Test..." msgstr "Тест..." @@ -2488,9 +2494,6 @@ msgstr "Расположение" msgid "Filters" msgstr "Фильтры" -msgid "[F] " -msgstr "[F] " - msgid "Description" msgstr "Описание" @@ -4075,6 +4078,39 @@ msgstr "Файловая операция успешно завершена" msgid "File operation canceled" msgstr "Файловая операция отменена" +msgid "No error" +msgstr "" + +msgid "Unknown character in filter expression" +msgstr "" + +msgid "Unterminated string literal" +msgstr "" + +msgid "Syntax error in filter expression" +msgstr "" + +msgid "Failed to parse filter expression" +msgstr "" + +msgid "Invalid literal value" +msgstr "" + +msgid "Invalid regular expression" +msgstr "" + +msgid "Undefined identifier" +msgstr "" + +msgid "Invalid number of arguments" +msgstr "" + +msgid "Filter name not found" +msgstr "" + +msgid "Unknown error" +msgstr "" + msgid "Prettification" msgstr "Приукрашивание" diff --git a/Translations/WinMerge/Serbian.po b/Translations/WinMerge/Serbian.po index 11acd767d47..5d7211cd48c 100644 --- a/Translations/WinMerge/Serbian.po +++ b/Translations/WinMerge/Serbian.po @@ -1617,10 +1617,6 @@ msgstr "&Нађи следеће" msgid "Find &Prev" msgstr "" -#, c-format -msgid "&Ok" -msgstr "&У реду" - #, c-format msgid "Replace" msgstr "Замени" @@ -1672,6 +1668,10 @@ msgstr "Нови" msgid "&Background color:" msgstr "" +#, c-format +msgid "&Ok" +msgstr "&У реду" + #, c-format msgid "&Apply" msgstr "П&римени" @@ -2165,6 +2165,12 @@ msgstr "Почетни" msgid "File Filters" msgstr "" +msgid "&Mask / Filter Expression" +msgstr "" + +msgid "Preset &Filters" +msgstr "" + msgid "Test..." msgstr "Провера..." @@ -3022,10 +3028,6 @@ msgstr "Положај" msgid "Filters" msgstr "Филтери" -#, c-format -msgid "[F] " -msgstr "[F]" - #, c-format msgid "Description" msgstr "Опис" @@ -4941,6 +4943,39 @@ msgstr "" msgid "File operation canceled" msgstr "" +msgid "No error" +msgstr "" + +msgid "Unknown character in filter expression" +msgstr "" + +msgid "Unterminated string literal" +msgstr "" + +msgid "Syntax error in filter expression" +msgstr "" + +msgid "Failed to parse filter expression" +msgstr "" + +msgid "Invalid literal value" +msgstr "" + +msgid "Invalid regular expression" +msgstr "" + +msgid "Undefined identifier" +msgstr "" + +msgid "Invalid number of arguments" +msgstr "" + +msgid "Filter name not found" +msgstr "" + +msgid "Unknown error" +msgstr "" + msgid "Prettification" msgstr "" diff --git a/Translations/WinMerge/Sinhala.po b/Translations/WinMerge/Sinhala.po index d93d1de644e..058abc0062c 100644 --- a/Translations/WinMerge/Sinhala.po +++ b/Translations/WinMerge/Sinhala.po @@ -1630,10 +1630,6 @@ msgstr "සෙවීම එ ළඟ" msgid "Find &Prev" msgstr "" -#, c-format -msgid "&Ok" -msgstr "&හරි" - #, c-format msgid "Replace" msgstr "ප්‍රතිස්ථාපනය කරන්න" @@ -1685,6 +1681,10 @@ msgstr "නව" msgid "&Background color:" msgstr "" +#, c-format +msgid "&Ok" +msgstr "&හරි" + #, c-format msgid "&Apply" msgstr "&යොමු කරන්න" @@ -2182,6 +2182,12 @@ msgstr "යලි පිහිටුවීමට" msgid "File Filters" msgstr "" +msgid "&Mask / Filter Expression" +msgstr "" + +msgid "Preset &Filters" +msgstr "" + msgid "Test..." msgstr "පරීක්ෂාව..." @@ -3037,10 +3043,6 @@ msgstr "පිහිටීම" msgid "Filters" msgstr "පෙරහන" -#, c-format -msgid "[F] " -msgstr "[F] " - #, c-format msgid "Description" msgstr "විස්තරය" @@ -4970,6 +4972,39 @@ msgstr "" msgid "File operation canceled" msgstr "" +msgid "No error" +msgstr "" + +msgid "Unknown character in filter expression" +msgstr "" + +msgid "Unterminated string literal" +msgstr "" + +msgid "Syntax error in filter expression" +msgstr "" + +msgid "Failed to parse filter expression" +msgstr "" + +msgid "Invalid literal value" +msgstr "" + +msgid "Invalid regular expression" +msgstr "" + +msgid "Undefined identifier" +msgstr "" + +msgid "Invalid number of arguments" +msgstr "" + +msgid "Filter name not found" +msgstr "" + +msgid "Unknown error" +msgstr "" + msgid "Prettification" msgstr "" diff --git a/Translations/WinMerge/Slovak.po b/Translations/WinMerge/Slovak.po index b1a8bfb1753..5859d5fbd88 100644 --- a/Translations/WinMerge/Slovak.po +++ b/Translations/WinMerge/Slovak.po @@ -1362,9 +1362,6 @@ msgstr "Nájsť ďalší" msgid "Find &Prev" msgstr "Nájsť &predošlý" -msgid "&Ok" -msgstr "&OK" - msgid "Replace" msgstr "Nahradiť" @@ -1404,6 +1401,9 @@ msgstr "Nový" msgid "&Background color:" msgstr "Farba &pozadia:" +msgid "&Ok" +msgstr "&OK" + msgid "&Apply" msgstr "&Použiť" @@ -1809,6 +1809,12 @@ msgstr "Vynulovať" msgid "File Filters" msgstr "Filtre súborov" +msgid "&Mask / Filter Expression" +msgstr "" + +msgid "Preset &Filters" +msgstr "" + msgid "Test..." msgstr "Testovať..." @@ -2545,9 +2551,6 @@ msgstr "Umiestnenie" msgid "Filters" msgstr "Filtre" -msgid "[F] " -msgstr "[F] " - msgid "Description" msgstr "Popis" @@ -4386,6 +4389,39 @@ msgstr "" msgid "File operation canceled" msgstr "" +msgid "No error" +msgstr "" + +msgid "Unknown character in filter expression" +msgstr "" + +msgid "Unterminated string literal" +msgstr "" + +msgid "Syntax error in filter expression" +msgstr "" + +msgid "Failed to parse filter expression" +msgstr "" + +msgid "Invalid literal value" +msgstr "" + +msgid "Invalid regular expression" +msgstr "" + +msgid "Undefined identifier" +msgstr "" + +msgid "Invalid number of arguments" +msgstr "" + +msgid "Filter name not found" +msgstr "" + +msgid "Unknown error" +msgstr "" + msgid "Prettification" msgstr "Prikrášlenie" diff --git a/Translations/WinMerge/Slovenian.po b/Translations/WinMerge/Slovenian.po index a177042b091..d9b4f79f59e 100644 --- a/Translations/WinMerge/Slovenian.po +++ b/Translations/WinMerge/Slovenian.po @@ -1362,9 +1362,6 @@ msgstr "&Najdi naslednje" msgid "Find &Prev" msgstr "Najdi pre&jšnje" -msgid "&Ok" -msgstr "&Vredu" - msgid "Replace" msgstr "Zamenjaj" @@ -1404,6 +1401,9 @@ msgstr "Nov" msgid "&Background color:" msgstr "Bar&va ozadja:" +msgid "&Ok" +msgstr "&Vredu" + msgid "&Apply" msgstr "&Uporabi" @@ -1809,6 +1809,12 @@ msgstr "Ponastavi" msgid "File Filters" msgstr "Filtri datotek" +msgid "&Mask / Filter Expression" +msgstr "" + +msgid "Preset &Filters" +msgstr "" + msgid "Test..." msgstr "Preizkusi..." @@ -2545,9 +2551,6 @@ msgstr "Lokacija" msgid "Filters" msgstr "Filtri" -msgid "[F] " -msgstr "[F] " - msgid "Description" msgstr "Opis" @@ -4409,6 +4412,39 @@ msgstr "" msgid "File operation canceled" msgstr "" +msgid "No error" +msgstr "" + +msgid "Unknown character in filter expression" +msgstr "" + +msgid "Unterminated string literal" +msgstr "" + +msgid "Syntax error in filter expression" +msgstr "" + +msgid "Failed to parse filter expression" +msgstr "" + +msgid "Invalid literal value" +msgstr "" + +msgid "Invalid regular expression" +msgstr "" + +msgid "Undefined identifier" +msgstr "" + +msgid "Invalid number of arguments" +msgstr "" + +msgid "Filter name not found" +msgstr "" + +msgid "Unknown error" +msgstr "" + msgid "Prettification" msgstr "Polepšanje" diff --git a/Translations/WinMerge/Spanish.po b/Translations/WinMerge/Spanish.po index c0c1fd53a14..2b0ea224a43 100644 --- a/Translations/WinMerge/Spanish.po +++ b/Translations/WinMerge/Spanish.po @@ -1365,9 +1365,6 @@ msgstr "&Siguiente" msgid "Find &Prev" msgstr "&Anterior" -msgid "&Ok" -msgstr "Ace&ptar" - msgid "Replace" msgstr "Reemplazar" @@ -1407,6 +1404,9 @@ msgstr "Nuevo" msgid "&Background color:" msgstr "Color de fondo:" +msgid "&Ok" +msgstr "Ace&ptar" + msgid "&Apply" msgstr "&Aplicar" @@ -1812,6 +1812,12 @@ msgstr "Restablecer" msgid "File Filters" msgstr "Filtros de archivo" +msgid "&Mask / Filter Expression" +msgstr "" + +msgid "Preset &Filters" +msgstr "" + msgid "Test..." msgstr "Probar..." @@ -2548,9 +2554,6 @@ msgstr "Ubicación" msgid "Filters" msgstr "Filtros" -msgid "[F] " -msgstr "[F] " - msgid "Description" msgstr "Descripción" @@ -4413,6 +4416,39 @@ msgstr "" msgid "File operation canceled" msgstr "" +msgid "No error" +msgstr "" + +msgid "Unknown character in filter expression" +msgstr "" + +msgid "Unterminated string literal" +msgstr "" + +msgid "Syntax error in filter expression" +msgstr "" + +msgid "Failed to parse filter expression" +msgstr "" + +msgid "Invalid literal value" +msgstr "" + +msgid "Invalid regular expression" +msgstr "" + +msgid "Undefined identifier" +msgstr "" + +msgid "Invalid number of arguments" +msgstr "" + +msgid "Filter name not found" +msgstr "" + +msgid "Unknown error" +msgstr "" + msgid "Prettification" msgstr "Mejoras" diff --git a/Translations/WinMerge/Swedish.po b/Translations/WinMerge/Swedish.po index 6a346d24fa4..824d273dc27 100644 --- a/Translations/WinMerge/Swedish.po +++ b/Translations/WinMerge/Swedish.po @@ -1365,9 +1365,6 @@ msgstr "Sök nästa" msgid "Find &Prev" msgstr "Sök föregående" -msgid "&Ok" -msgstr "OK" - msgid "Replace" msgstr "Ersätt" @@ -1407,6 +1404,9 @@ msgstr "Ny" msgid "&Background color:" msgstr "Bakgrundsfärg:" +msgid "&Ok" +msgstr "OK" + msgid "&Apply" msgstr "Tillämpa" @@ -1812,6 +1812,12 @@ msgstr "Återställ" msgid "File Filters" msgstr "Filfilter" +msgid "&Mask / Filter Expression" +msgstr "" + +msgid "Preset &Filters" +msgstr "" + msgid "Test..." msgstr "Testa..." @@ -2545,9 +2551,6 @@ msgstr "Sökväg" msgid "Filters" msgstr "Filter" -msgid "[F] " -msgstr "[F] " - msgid "Description" msgstr "Beskrivning" @@ -4501,6 +4504,39 @@ msgstr "Filoperation framgångsrikt gjord" msgid "File operation canceled" msgstr "Filoperation avbruten" +msgid "No error" +msgstr "" + +msgid "Unknown character in filter expression" +msgstr "" + +msgid "Unterminated string literal" +msgstr "" + +msgid "Syntax error in filter expression" +msgstr "" + +msgid "Failed to parse filter expression" +msgstr "" + +msgid "Invalid literal value" +msgstr "" + +msgid "Invalid regular expression" +msgstr "" + +msgid "Undefined identifier" +msgstr "" + +msgid "Invalid number of arguments" +msgstr "" + +msgid "Filter name not found" +msgstr "" + +msgid "Unknown error" +msgstr "" + msgid "Prettification" msgstr "Finputsning" diff --git a/Translations/WinMerge/Tamil.po b/Translations/WinMerge/Tamil.po index 87c12b5a5a6..f43263f888a 100644 --- a/Translations/WinMerge/Tamil.po +++ b/Translations/WinMerge/Tamil.po @@ -1373,9 +1373,6 @@ msgstr "அடுத்து கண்டுபிடி" msgid "Find &Prev" msgstr "முந்தையதைக் கண்டுபிடி" -msgid "&Ok" -msgstr "சரி" - msgid "Replace" msgstr "மாற்று" @@ -1415,6 +1412,9 @@ msgstr "புதிய" msgid "&Background color:" msgstr "பின்னணி நிறம்:" +msgid "&Ok" +msgstr "சரி" + msgid "&Apply" msgstr "இடு" @@ -1829,6 +1829,12 @@ msgstr "மீட்டமை" msgid "File Filters" msgstr "கோப்பு வடிப்பான்கள்" +msgid "&Mask / Filter Expression" +msgstr "" + +msgid "Preset &Filters" +msgstr "" + msgid "Test..." msgstr "சோதனை..." @@ -2583,9 +2589,6 @@ msgstr "இடம்" msgid "Filters" msgstr "வடிப்பான்கள்" -msgid "[F] " -msgstr "[F] " - msgid "Description" msgstr "விளக்கம்" @@ -4563,6 +4566,39 @@ msgstr "கோப்பு செயல்பாடு வெற்றிகர msgid "File operation canceled" msgstr "கோப்பு செயல்பாடு கைவிடப்பட்டது" +msgid "No error" +msgstr "" + +msgid "Unknown character in filter expression" +msgstr "" + +msgid "Unterminated string literal" +msgstr "" + +msgid "Syntax error in filter expression" +msgstr "" + +msgid "Failed to parse filter expression" +msgstr "" + +msgid "Invalid literal value" +msgstr "" + +msgid "Invalid regular expression" +msgstr "" + +msgid "Undefined identifier" +msgstr "" + +msgid "Invalid number of arguments" +msgstr "" + +msgid "Filter name not found" +msgstr "" + +msgid "Unknown error" +msgstr "" + msgid "Prettification" msgstr "அழகானமாக்கல்" diff --git a/Translations/WinMerge/Turkish.po b/Translations/WinMerge/Turkish.po index 0468edd6747..9fa1d09fd5a 100644 --- a/Translations/WinMerge/Turkish.po +++ b/Translations/WinMerge/Turkish.po @@ -1364,9 +1364,6 @@ msgstr "S&onrakini bul" msgid "Find &Prev" msgstr "Ö&ncekini bul" -msgid "&Ok" -msgstr "&Tamam" - msgid "Replace" msgstr "Değiştir" @@ -1406,6 +1403,9 @@ msgstr "Yeni" msgid "&Background color:" msgstr "A&rka plan rengi:" +msgid "&Ok" +msgstr "&Tamam" + msgid "&Apply" msgstr "&Uygula" @@ -1821,6 +1821,12 @@ msgstr "Sıfırla" msgid "File Filters" msgstr "Dosya süzgeçleri" +msgid "&Mask / Filter Expression" +msgstr "" + +msgid "Preset &Filters" +msgstr "" + msgid "Test..." msgstr "Sına..." @@ -2572,9 +2578,6 @@ msgstr "Konum" msgid "Filters" msgstr "Süzgeçler" -msgid "[F] " -msgstr "[F] " - msgid "Description" msgstr "Açıklama" @@ -4559,6 +4562,39 @@ msgstr "Dosya işlemi başarıyla tamamlandı" msgid "File operation canceled" msgstr "Dosya işlemi iptal edildi" +msgid "No error" +msgstr "" + +msgid "Unknown character in filter expression" +msgstr "" + +msgid "Unterminated string literal" +msgstr "" + +msgid "Syntax error in filter expression" +msgstr "" + +msgid "Failed to parse filter expression" +msgstr "" + +msgid "Invalid literal value" +msgstr "" + +msgid "Invalid regular expression" +msgstr "" + +msgid "Undefined identifier" +msgstr "" + +msgid "Invalid number of arguments" +msgstr "" + +msgid "Filter name not found" +msgstr "" + +msgid "Unknown error" +msgstr "" + msgid "Prettification" msgstr "Sadeleştirme" diff --git a/Translations/WinMerge/Ukrainian.po b/Translations/WinMerge/Ukrainian.po index 9384c944466..37fcae54371 100644 --- a/Translations/WinMerge/Ukrainian.po +++ b/Translations/WinMerge/Ukrainian.po @@ -1594,10 +1594,6 @@ msgstr "&Далі" msgid "Find &Prev" msgstr "" -#, c-format -msgid "&Ok" -msgstr "&OК" - #, c-format msgid "Replace" msgstr "Заміна" @@ -1649,6 +1645,10 @@ msgstr "Створити" msgid "&Background color:" msgstr "" +#, c-format +msgid "&Ok" +msgstr "&OК" + #, c-format msgid "&Apply" msgstr "&Застосувати" @@ -2145,6 +2145,12 @@ msgstr "Скинути" msgid "File Filters" msgstr "" +msgid "&Mask / Filter Expression" +msgstr "" + +msgid "Preset &Filters" +msgstr "" + msgid "Test..." msgstr "Випробувати..." @@ -3002,10 +3008,6 @@ msgstr "Місце розташування" msgid "Filters" msgstr "Фільтри" -#, c-format -msgid "[F] " -msgstr "[F] " - #, c-format msgid "Description" msgstr "Опис" @@ -4935,6 +4937,39 @@ msgstr "" msgid "File operation canceled" msgstr "" +msgid "No error" +msgstr "" + +msgid "Unknown character in filter expression" +msgstr "" + +msgid "Unterminated string literal" +msgstr "" + +msgid "Syntax error in filter expression" +msgstr "" + +msgid "Failed to parse filter expression" +msgstr "" + +msgid "Invalid literal value" +msgstr "" + +msgid "Invalid regular expression" +msgstr "" + +msgid "Undefined identifier" +msgstr "" + +msgid "Invalid number of arguments" +msgstr "" + +msgid "Filter name not found" +msgstr "" + +msgid "Unknown error" +msgstr "" + msgid "Prettification" msgstr "" diff --git a/WinMerge.vs2017.sln b/WinMerge.vs2017.sln index 56ceb72ddc4..883a9eb516f 100644 --- a/WinMerge.vs2017.sln +++ b/WinMerge.vs2017.sln @@ -58,7 +58,31 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "googletest", "Externals\goo EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "editlibparsers", "Externals\crystaledit\editlib\editlibparsers.vcxitems", "{4170552A-09E2-4FAC-B71D-0E2F5EB3C869}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FilterEngine", "Src\FilterEngine\FilterEngine.vcxitems", "{9C37E5D8-1DC0-4EAC-AADB-5FC8BE4FB1BC}" +EndProject Global + GlobalSection(SharedMSBuildProjectFiles) = preSolution + Externals\googletest\googletest\googletest.vcxitems*{0a3727b1-51e7-4702-ad0c-8aee317ea510}*SharedItemsImports = 4 + Src\CompareEngines\CompareEngines.vcxitems*{0f686afa-d587-43c0-bada-2beddc3fa758}*SharedItemsImports = 9 + Externals\crystaledit\editlib\editlibparsers.vcxitems*{4170552a-09e2-4fac-b71d-0e2f5eb3c869}*SharedItemsImports = 9 + Externals\xdiff\xdiff.vcxitems*{68f1d3a1-9dca-4b3d-b245-f4aca5f16563}*SharedItemsImports = 9 + Externals\crystaledit\editlib\editlibparsers.vcxitems*{733e7c0b-ac3d-47ac-a8da-e13644d6294d}*SharedItemsImports = 4 + Externals\googletest\googletest\googletest.vcxitems*{733e7c0b-ac3d-47ac-a8da-e13644d6294d}*SharedItemsImports = 4 + Externals\xdiff\xdiff.vcxitems*{733e7c0b-ac3d-47ac-a8da-e13644d6294d}*SharedItemsImports = 4 + Src\CompareEngines\CompareEngines.vcxitems*{733e7c0b-ac3d-47ac-a8da-e13644d6294d}*SharedItemsImports = 4 + Src\diffutils\diffutils.vcxitems*{733e7c0b-ac3d-47ac-a8da-e13644d6294d}*SharedItemsImports = 4 + Src\FilterEngine\FilterEngine.vcxitems*{733e7c0b-ac3d-47ac-a8da-e13644d6294d}*SharedItemsImports = 4 + Externals\crystaledit\editlib\editlib.vcxitems*{7515ac3c-389a-44cd-b940-a59dde5b8ae3}*SharedItemsImports = 9 + Src\FilterEngine\FilterEngine.vcxitems*{9c37e5d8-1dc0-4eac-aadb-5fc8be4fb1bc}*SharedItemsImports = 9 + Externals\googletest\googletest\googletest.vcxitems*{9ee35458-b145-444f-92b7-27ff72112c42}*SharedItemsImports = 9 + Externals\crystaledit\editlib\editlib.vcxitems*{9fda4af0-ccfd-4812-bdb9-53efedb32bde}*SharedItemsImports = 4 + Externals\crystaledit\editlib\editlibparsers.vcxitems*{9fda4af0-ccfd-4812-bdb9-53efedb32bde}*SharedItemsImports = 4 + Externals\xdiff\xdiff.vcxitems*{9fda4af0-ccfd-4812-bdb9-53efedb32bde}*SharedItemsImports = 4 + Src\CompareEngines\CompareEngines.vcxitems*{9fda4af0-ccfd-4812-bdb9-53efedb32bde}*SharedItemsImports = 4 + Src\diffutils\diffutils.vcxitems*{9fda4af0-ccfd-4812-bdb9-53efedb32bde}*SharedItemsImports = 4 + Src\FilterEngine\FilterEngine.vcxitems*{9fda4af0-ccfd-4812-bdb9-53efedb32bde}*SharedItemsImports = 4 + Src\diffutils\diffutils.vcxitems*{fc3b9df3-2854-4264-ab4b-ee8c43982513}*SharedItemsImports = 9 + EndGlobalSection GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|ARM = Debug|ARM Debug|ARM64 = Debug|ARM64 @@ -229,23 +253,4 @@ Global GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {BA780561-B5A9-4ED1-B35C-B342695F3F39} EndGlobalSection - GlobalSection(SharedMSBuildProjectFiles) = preSolution - Externals\googletest\googletest\googletest.vcxitems*{0a3727b1-51e7-4702-ad0c-8aee317ea510}*SharedItemsImports = 4 - Src\CompareEngines\CompareEngines.vcxitems*{0f686afa-d587-43c0-bada-2beddc3fa758}*SharedItemsImports = 9 - Externals\crystaledit\editlib\editlibparsers.vcxitems*{4170552a-09e2-4fac-b71d-0e2f5eb3c869}*SharedItemsImports = 9 - Externals\xdiff\xdiff.vcxitems*{68f1d3a1-9dca-4b3d-b245-f4aca5f16563}*SharedItemsImports = 9 - Externals\crystaledit\editlib\editlibparsers.vcxitems*{733e7c0b-ac3d-47ac-a8da-e13644d6294d}*SharedItemsImports = 4 - Externals\googletest\googletest\googletest.vcxitems*{733e7c0b-ac3d-47ac-a8da-e13644d6294d}*SharedItemsImports = 4 - Externals\xdiff\xdiff.vcxitems*{733e7c0b-ac3d-47ac-a8da-e13644d6294d}*SharedItemsImports = 4 - Src\CompareEngines\CompareEngines.vcxitems*{733e7c0b-ac3d-47ac-a8da-e13644d6294d}*SharedItemsImports = 4 - Src\diffutils\diffutils.vcxitems*{733e7c0b-ac3d-47ac-a8da-e13644d6294d}*SharedItemsImports = 4 - Externals\crystaledit\editlib\editlib.vcxitems*{7515ac3c-389a-44cd-b940-a59dde5b8ae3}*SharedItemsImports = 9 - Externals\googletest\googletest\googletest.vcxitems*{9ee35458-b145-444f-92b7-27ff72112c42}*SharedItemsImports = 9 - Externals\crystaledit\editlib\editlib.vcxitems*{9fda4af0-ccfd-4812-bdb9-53efedb32bde}*SharedItemsImports = 4 - Externals\crystaledit\editlib\editlibparsers.vcxitems*{9fda4af0-ccfd-4812-bdb9-53efedb32bde}*SharedItemsImports = 4 - Externals\xdiff\xdiff.vcxitems*{9fda4af0-ccfd-4812-bdb9-53efedb32bde}*SharedItemsImports = 4 - Src\CompareEngines\CompareEngines.vcxitems*{9fda4af0-ccfd-4812-bdb9-53efedb32bde}*SharedItemsImports = 4 - Src\diffutils\diffutils.vcxitems*{9fda4af0-ccfd-4812-bdb9-53efedb32bde}*SharedItemsImports = 4 - Src\diffutils\diffutils.vcxitems*{fc3b9df3-2854-4264-ab4b-ee8c43982513}*SharedItemsImports = 9 - EndGlobalSection EndGlobal diff --git a/WinMerge.vs2019.sln b/WinMerge.vs2019.sln index 33e49f5e67a..ef5e976a930 100644 --- a/WinMerge.vs2019.sln +++ b/WinMerge.vs2019.sln @@ -58,7 +58,31 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "googletest", "Externals\goo EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "editlibparsers", "Externals\crystaledit\editlib\editlibparsers.vcxitems", "{4170552A-09E2-4FAC-B71D-0E2F5EB3C869}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FilterEngine", "Src\FilterEngine\FilterEngine.vcxitems", "{9C37E5D8-1DC0-4EAC-AADB-5FC8BE4FB1BC}" +EndProject Global + GlobalSection(SharedMSBuildProjectFiles) = preSolution + Externals\googletest\googletest\googletest.vcxitems*{0a3727b1-51e7-4702-ad0c-8aee317ea510}*SharedItemsImports = 4 + Src\CompareEngines\CompareEngines.vcxitems*{0f686afa-d587-43c0-bada-2beddc3fa758}*SharedItemsImports = 9 + Externals\crystaledit\editlib\editlibparsers.vcxitems*{4170552a-09e2-4fac-b71d-0e2f5eb3c869}*SharedItemsImports = 9 + Externals\xdiff\xdiff.vcxitems*{68f1d3a1-9dca-4b3d-b245-f4aca5f16563}*SharedItemsImports = 9 + Externals\crystaledit\editlib\editlibparsers.vcxitems*{733e7c0b-ac3d-47ac-a8da-e13644d6294d}*SharedItemsImports = 4 + Externals\googletest\googletest\googletest.vcxitems*{733e7c0b-ac3d-47ac-a8da-e13644d6294d}*SharedItemsImports = 4 + Externals\xdiff\xdiff.vcxitems*{733e7c0b-ac3d-47ac-a8da-e13644d6294d}*SharedItemsImports = 4 + Src\CompareEngines\CompareEngines.vcxitems*{733e7c0b-ac3d-47ac-a8da-e13644d6294d}*SharedItemsImports = 4 + Src\diffutils\diffutils.vcxitems*{733e7c0b-ac3d-47ac-a8da-e13644d6294d}*SharedItemsImports = 4 + Src\FilterEngine\FilterEngine.vcxitems*{733e7c0b-ac3d-47ac-a8da-e13644d6294d}*SharedItemsImports = 4 + Externals\crystaledit\editlib\editlib.vcxitems*{7515ac3c-389a-44cd-b940-a59dde5b8ae3}*SharedItemsImports = 9 + Src\FilterEngine\FilterEngine.vcxitems*{9c37e5d8-1dc0-4eac-aadb-5fc8be4fb1bc}*SharedItemsImports = 9 + Externals\googletest\googletest\googletest.vcxitems*{9ee35458-b145-444f-92b7-27ff72112c42}*SharedItemsImports = 9 + Externals\crystaledit\editlib\editlib.vcxitems*{9fda4af0-ccfd-4812-bdb9-53efedb32bde}*SharedItemsImports = 4 + Externals\crystaledit\editlib\editlibparsers.vcxitems*{9fda4af0-ccfd-4812-bdb9-53efedb32bde}*SharedItemsImports = 4 + Externals\xdiff\xdiff.vcxitems*{9fda4af0-ccfd-4812-bdb9-53efedb32bde}*SharedItemsImports = 4 + Src\CompareEngines\CompareEngines.vcxitems*{9fda4af0-ccfd-4812-bdb9-53efedb32bde}*SharedItemsImports = 4 + Src\diffutils\diffutils.vcxitems*{9fda4af0-ccfd-4812-bdb9-53efedb32bde}*SharedItemsImports = 4 + Src\FilterEngine\FilterEngine.vcxitems*{9fda4af0-ccfd-4812-bdb9-53efedb32bde}*SharedItemsImports = 4 + Src\diffutils\diffutils.vcxitems*{fc3b9df3-2854-4264-ab4b-ee8c43982513}*SharedItemsImports = 9 + EndGlobalSection GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|ARM = Debug|ARM Debug|ARM64 = Debug|ARM64 @@ -229,23 +253,4 @@ Global GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {BA780561-B5A9-4ED1-B35C-B342695F3F39} EndGlobalSection - GlobalSection(SharedMSBuildProjectFiles) = preSolution - Externals\googletest\googletest\googletest.vcxitems*{0a3727b1-51e7-4702-ad0c-8aee317ea510}*SharedItemsImports = 4 - Src\CompareEngines\CompareEngines.vcxitems*{0f686afa-d587-43c0-bada-2beddc3fa758}*SharedItemsImports = 9 - Externals\crystaledit\editlib\editlibparsers.vcxitems*{4170552a-09e2-4fac-b71d-0e2f5eb3c869}*SharedItemsImports = 9 - Externals\xdiff\xdiff.vcxitems*{68f1d3a1-9dca-4b3d-b245-f4aca5f16563}*SharedItemsImports = 9 - Externals\crystaledit\editlib\editlibparsers.vcxitems*{733e7c0b-ac3d-47ac-a8da-e13644d6294d}*SharedItemsImports = 4 - Externals\googletest\googletest\googletest.vcxitems*{733e7c0b-ac3d-47ac-a8da-e13644d6294d}*SharedItemsImports = 4 - Externals\xdiff\xdiff.vcxitems*{733e7c0b-ac3d-47ac-a8da-e13644d6294d}*SharedItemsImports = 4 - Src\CompareEngines\CompareEngines.vcxitems*{733e7c0b-ac3d-47ac-a8da-e13644d6294d}*SharedItemsImports = 4 - Src\diffutils\diffutils.vcxitems*{733e7c0b-ac3d-47ac-a8da-e13644d6294d}*SharedItemsImports = 4 - Externals\crystaledit\editlib\editlib.vcxitems*{7515ac3c-389a-44cd-b940-a59dde5b8ae3}*SharedItemsImports = 9 - Externals\googletest\googletest\googletest.vcxitems*{9ee35458-b145-444f-92b7-27ff72112c42}*SharedItemsImports = 9 - Externals\crystaledit\editlib\editlib.vcxitems*{9fda4af0-ccfd-4812-bdb9-53efedb32bde}*SharedItemsImports = 4 - Externals\crystaledit\editlib\editlibparsers.vcxitems*{9fda4af0-ccfd-4812-bdb9-53efedb32bde}*SharedItemsImports = 4 - Externals\xdiff\xdiff.vcxitems*{9fda4af0-ccfd-4812-bdb9-53efedb32bde}*SharedItemsImports = 4 - Src\CompareEngines\CompareEngines.vcxitems*{9fda4af0-ccfd-4812-bdb9-53efedb32bde}*SharedItemsImports = 4 - Src\diffutils\diffutils.vcxitems*{9fda4af0-ccfd-4812-bdb9-53efedb32bde}*SharedItemsImports = 4 - Src\diffutils\diffutils.vcxitems*{fc3b9df3-2854-4264-ab4b-ee8c43982513}*SharedItemsImports = 9 - EndGlobalSection EndGlobal diff --git a/WinMerge.vs2022.sln b/WinMerge.vs2022.sln index 408153712e7..15e6cdfcd97 100644 --- a/WinMerge.vs2022.sln +++ b/WinMerge.vs2022.sln @@ -58,7 +58,31 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "googletest", "Externals\goo EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "editlibparsers", "Externals\crystaledit\editlib\editlibparsers.vcxitems", "{4170552A-09E2-4FAC-B71D-0E2F5EB3C869}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FilterEngine", "Src\FilterEngine\FilterEngine.vcxitems", "{9C37E5D8-1DC0-4EAC-AADB-5FC8BE4FB1BC}" +EndProject Global + GlobalSection(SharedMSBuildProjectFiles) = preSolution + Externals\googletest\googletest\googletest.vcxitems*{0a3727b1-51e7-4702-ad0c-8aee317ea510}*SharedItemsImports = 4 + Src\CompareEngines\CompareEngines.vcxitems*{0f686afa-d587-43c0-bada-2beddc3fa758}*SharedItemsImports = 9 + Externals\crystaledit\editlib\editlibparsers.vcxitems*{4170552a-09e2-4fac-b71d-0e2f5eb3c869}*SharedItemsImports = 9 + Externals\xdiff\xdiff.vcxitems*{68f1d3a1-9dca-4b3d-b245-f4aca5f16563}*SharedItemsImports = 9 + Externals\crystaledit\editlib\editlibparsers.vcxitems*{733e7c0b-ac3d-47ac-a8da-e13644d6294d}*SharedItemsImports = 4 + Externals\googletest\googletest\googletest.vcxitems*{733e7c0b-ac3d-47ac-a8da-e13644d6294d}*SharedItemsImports = 4 + Externals\xdiff\xdiff.vcxitems*{733e7c0b-ac3d-47ac-a8da-e13644d6294d}*SharedItemsImports = 4 + Src\CompareEngines\CompareEngines.vcxitems*{733e7c0b-ac3d-47ac-a8da-e13644d6294d}*SharedItemsImports = 4 + Src\diffutils\diffutils.vcxitems*{733e7c0b-ac3d-47ac-a8da-e13644d6294d}*SharedItemsImports = 4 + Src\FilterEngine\FilterEngine.vcxitems*{733e7c0b-ac3d-47ac-a8da-e13644d6294d}*SharedItemsImports = 4 + Externals\crystaledit\editlib\editlib.vcxitems*{7515ac3c-389a-44cd-b940-a59dde5b8ae3}*SharedItemsImports = 9 + Src\FilterEngine\FilterEngine.vcxitems*{9c37e5d8-1dc0-4eac-aadb-5fc8be4fb1bc}*SharedItemsImports = 9 + Externals\googletest\googletest\googletest.vcxitems*{9ee35458-b145-444f-92b7-27ff72112c42}*SharedItemsImports = 9 + Externals\crystaledit\editlib\editlib.vcxitems*{9fda4af0-ccfd-4812-bdb9-53efedb32bde}*SharedItemsImports = 4 + Externals\crystaledit\editlib\editlibparsers.vcxitems*{9fda4af0-ccfd-4812-bdb9-53efedb32bde}*SharedItemsImports = 4 + Externals\xdiff\xdiff.vcxitems*{9fda4af0-ccfd-4812-bdb9-53efedb32bde}*SharedItemsImports = 4 + Src\CompareEngines\CompareEngines.vcxitems*{9fda4af0-ccfd-4812-bdb9-53efedb32bde}*SharedItemsImports = 4 + Src\diffutils\diffutils.vcxitems*{9fda4af0-ccfd-4812-bdb9-53efedb32bde}*SharedItemsImports = 4 + Src\FilterEngine\FilterEngine.vcxitems*{9fda4af0-ccfd-4812-bdb9-53efedb32bde}*SharedItemsImports = 4 + Src\diffutils\diffutils.vcxitems*{fc3b9df3-2854-4264-ab4b-ee8c43982513}*SharedItemsImports = 9 + EndGlobalSection GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|ARM = Debug|ARM Debug|ARM64 = Debug|ARM64 @@ -229,23 +253,4 @@ Global GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {BA780561-B5A9-4ED1-B35C-B342695F3F39} EndGlobalSection - GlobalSection(SharedMSBuildProjectFiles) = preSolution - Externals\googletest\googletest\googletest.vcxitems*{0a3727b1-51e7-4702-ad0c-8aee317ea510}*SharedItemsImports = 4 - Src\CompareEngines\CompareEngines.vcxitems*{0f686afa-d587-43c0-bada-2beddc3fa758}*SharedItemsImports = 9 - Externals\crystaledit\editlib\editlibparsers.vcxitems*{4170552a-09e2-4fac-b71d-0e2f5eb3c869}*SharedItemsImports = 9 - Externals\xdiff\xdiff.vcxitems*{68f1d3a1-9dca-4b3d-b245-f4aca5f16563}*SharedItemsImports = 9 - Externals\crystaledit\editlib\editlibparsers.vcxitems*{733e7c0b-ac3d-47ac-a8da-e13644d6294d}*SharedItemsImports = 4 - Externals\googletest\googletest\googletest.vcxitems*{733e7c0b-ac3d-47ac-a8da-e13644d6294d}*SharedItemsImports = 4 - Externals\xdiff\xdiff.vcxitems*{733e7c0b-ac3d-47ac-a8da-e13644d6294d}*SharedItemsImports = 4 - Src\CompareEngines\CompareEngines.vcxitems*{733e7c0b-ac3d-47ac-a8da-e13644d6294d}*SharedItemsImports = 4 - Src\diffutils\diffutils.vcxitems*{733e7c0b-ac3d-47ac-a8da-e13644d6294d}*SharedItemsImports = 4 - Externals\crystaledit\editlib\editlib.vcxitems*{7515ac3c-389a-44cd-b940-a59dde5b8ae3}*SharedItemsImports = 9 - Externals\googletest\googletest\googletest.vcxitems*{9ee35458-b145-444f-92b7-27ff72112c42}*SharedItemsImports = 9 - Externals\crystaledit\editlib\editlib.vcxitems*{9fda4af0-ccfd-4812-bdb9-53efedb32bde}*SharedItemsImports = 4 - Externals\crystaledit\editlib\editlibparsers.vcxitems*{9fda4af0-ccfd-4812-bdb9-53efedb32bde}*SharedItemsImports = 4 - Externals\xdiff\xdiff.vcxitems*{9fda4af0-ccfd-4812-bdb9-53efedb32bde}*SharedItemsImports = 4 - Src\CompareEngines\CompareEngines.vcxitems*{9fda4af0-ccfd-4812-bdb9-53efedb32bde}*SharedItemsImports = 4 - Src\diffutils\diffutils.vcxitems*{9fda4af0-ccfd-4812-bdb9-53efedb32bde}*SharedItemsImports = 4 - Src\diffutils\diffutils.vcxitems*{fc3b9df3-2854-4264-ab4b-ee8c43982513}*SharedItemsImports = 9 - EndGlobalSection EndGlobal