From 903cf7fa76ca375c3cae65a5df2ae4a130142a83 Mon Sep 17 00:00:00 2001 From: Tal500 Date: Wed, 21 May 2025 13:04:45 +0300 Subject: [PATCH 1/7] fix: parent relative paths, and rework on the whole path extraction mechanics Include also another integration test function to check header in parent dir usage (was failing before) --- integration_test.py | 49 ++++++++++++++++++++++--- simplecpp.cpp | 89 ++++++++++++++++++++++++++++++--------------- 2 files changed, 102 insertions(+), 36 deletions(-) diff --git a/integration_test.py b/integration_test.py index 4fe0129b..d8f43643 100644 --- a/integration_test.py +++ b/integration_test.py @@ -35,14 +35,16 @@ def __test_relative_header_create_source(dir, include1, include2, is_include1_sy @pytest.mark.parametrize("with_pragma_once", (False, True)) @pytest.mark.parametrize("is_sys", (False, True)) -def test_relative_header_1(tmpdir, with_pragma_once, is_sys): +def test_relative_header_1(record_property, tmpdir, with_pragma_once, is_sys): _, double_include_error = __test_relative_header_create_header(tmpdir, with_pragma_once=with_pragma_once) test_file = __test_relative_header_create_source(tmpdir, "test.h", "test.h", is_include1_sys=is_sys, is_include2_sys=is_sys) args = ([format_include_path_arg(tmpdir)] if is_sys else []) + [test_file] - _, _, stderr = simplecpp(args, cwd=tmpdir) + _, stdout, stderr = simplecpp(args, cwd=tmpdir) + record_property("stdout", stdout) + record_property("stderr", stderr) if with_pragma_once: assert stderr == '' @@ -51,7 +53,7 @@ def test_relative_header_1(tmpdir, with_pragma_once, is_sys): @pytest.mark.parametrize("inv", (False, True)) @pytest.mark.parametrize("source_relative", (False, True)) -def test_relative_header_2(tmpdir, inv, source_relative): +def test_relative_header_2(record_property, tmpdir, inv, source_relative): header_file, _ = __test_relative_header_create_header(tmpdir) test_file = __test_relative_header_create_source(tmpdir, "test.h", header_file, inv=inv) @@ -59,6 +61,8 @@ def test_relative_header_2(tmpdir, inv, source_relative): args = ["test.c" if source_relative else test_file] _, stdout, stderr = simplecpp(args, cwd=tmpdir) + record_property("stdout", stdout) + record_property("stderr", stderr) assert stderr == '' if source_relative and not inv: assert '#line 8 "test.h"' in stdout @@ -68,7 +72,7 @@ def test_relative_header_2(tmpdir, inv, source_relative): @pytest.mark.parametrize("is_sys", (False, True)) @pytest.mark.parametrize("inv", (False, True)) @pytest.mark.parametrize("source_relative", (False, True)) -def test_relative_header_3(tmpdir, is_sys, inv, source_relative): +def test_relative_header_3(record_property, tmpdir, is_sys, inv, source_relative): test_subdir = os.path.join(tmpdir, "test_subdir") os.mkdir(test_subdir) header_file, _ = __test_relative_header_create_header(test_subdir) @@ -78,6 +82,8 @@ def test_relative_header_3(tmpdir, is_sys, inv, source_relative): args = ["test.c" if source_relative else test_file] _, stdout, stderr = simplecpp(args, cwd=tmpdir) + record_property("stdout", stdout) + record_property("stderr", stderr) if is_sys: assert "missing header: Header not found" in stderr @@ -91,7 +97,7 @@ def test_relative_header_3(tmpdir, is_sys, inv, source_relative): @pytest.mark.parametrize("use_short_path", (False, True)) @pytest.mark.parametrize("is_sys", (False, True)) @pytest.mark.parametrize("inv", (False, True)) -def test_relative_header_4(tmpdir, use_short_path, is_sys, inv): +def test_relative_header_4(record_property, tmpdir, use_short_path, is_sys, inv): test_subdir = os.path.join(tmpdir, "test_subdir") os.mkdir(test_subdir) header_file, _ = __test_relative_header_create_header(test_subdir) @@ -102,5 +108,36 @@ def test_relative_header_4(tmpdir, use_short_path, is_sys, inv): args = [format_include_path_arg(test_subdir), test_file] - _, _, stderr = simplecpp(args, cwd=tmpdir) + _, stdout, stderr = simplecpp(args, cwd=tmpdir) + record_property("stdout", stdout) + record_property("stderr", stderr) assert stderr == '' + +@pytest.mark.parametrize("with_pragma_once", (False, True)) +@pytest.mark.parametrize("is_sys", (False, True)) +@pytest.mark.parametrize("inv", (False, True)) +def test_relative_header_5(record_property, tmpdir, with_pragma_once, is_sys, inv): # test relative paths with .. + ## in this test, the subdir role is the opposite then the previous - it contains the test.c file, while the parent tmpdir contains the header file + header_file, double_include_error = __test_relative_header_create_header(tmpdir, with_pragma_once=with_pragma_once) + if is_sys: + header_file_second_path = "test.h" + else: + header_file_second_path = "../test.h" + + test_subdir = os.path.join(tmpdir, "test_subdir") + os.mkdir(test_subdir) + test_file = __test_relative_header_create_source(test_subdir, header_file, header_file_second_path, is_include2_sys=is_sys, inv=inv) + + args = ([format_include_path_arg(tmpdir)] if is_sys else []) + ["test.c"] + + _, stdout, stderr = simplecpp(args, cwd=test_subdir) + record_property("stdout", stdout) + record_property("stderr", stderr) + if with_pragma_once: + assert stderr == '' + if inv: + assert '#line 8 "../test.h"' in stdout + else: + assert f'#line 8 "{pathlib.PurePath(tmpdir).as_posix()}/test.h"' in stdout + else: + assert double_include_error in stderr diff --git a/simplecpp.cpp b/simplecpp.cpp index d1fa91bf..4ef751f6 100755 --- a/simplecpp.cpp +++ b/simplecpp.cpp @@ -2719,14 +2719,40 @@ static std::string toAbsolutePath(const std::string& path) { return simplecpp::simplifyPath(path); } -static std::pair extractRelativePathFromAbsolute(const std::string& absolutepath) { - static const std::string prefix = currentDirectory() + "/"; - if (startsWith_(absolutepath, prefix)) { - const std::size_t size = prefix.size(); - return std::make_pair(absolutepath.substr(size, absolutepath.size() - size), true); +// templated function for compiler optimization opportunities +template +static std::string dirPath(const std::string& path) { + const std::size_t firstSlash = path.find_last_of("\\/"); + if (firstSlash == std::string::npos) { + return ""; + } else { + return path.substr(0, firstSlash + (withTrailingSlash ? 1U : 0U)); } - // otherwise - return std::make_pair("", false); +} + +static std::string omitPathTrailingSlash(const std::string& path) { + if (endsWith(path, "/")) { + return path.substr(0, path.size() - 1U); + } else { + return path; + } +} + +static std::string extractRelativePathFromAbsolute(const std::string& absoluteSimplifiedPath, const std::string& prefixSimplifiedAbsoluteDir = currentDirectory()) { + const std::string normalizedAbsolutePath = omitPathTrailingSlash(absoluteSimplifiedPath); + std::string currentPrefix = omitPathTrailingSlash(prefixSimplifiedAbsoluteDir); + std::string leadingParenting = ""; + while (!startsWith_(normalizedAbsolutePath, currentPrefix)) { + leadingParenting = "../" + leadingParenting; + currentPrefix = dirPath(currentPrefix); + } + const std::size_t size = currentPrefix.size(); + std::string relativeFromMeetingPath = normalizedAbsolutePath.substr(size, normalizedAbsolutePath.size() - size); + if (startsWith_(relativeFromMeetingPath.c_str(), "/")) { + // omit the leading slash + relativeFromMeetingPath = relativeFromMeetingPath.substr(1, relativeFromMeetingPath.size()); + } + return leadingParenting + relativeFromMeetingPath; } static std::string openHeader(std::ifstream &f, const simplecpp::DUI &dui, const std::string &sourcefile, const std::string &header, bool systemheader); @@ -3145,19 +3171,25 @@ static std::string openHeader(std::ifstream &f, const std::string &path) return ""; } +// templated function for compiler optimization opportunities +template static std::string getRelativeFileName(const std::string &baseFile, const std::string &header) { - std::string path; - if (baseFile.find_first_of("\\/") != std::string::npos) - path = baseFile.substr(0, baseFile.find_last_of("\\/") + 1U) + header; - else - path = header; - return simplecpp::simplifyPath(path); + const std::string baseFileNormalized = (expandBaseWithAbsolutePath && !isAbsolutePath(baseFile)) ? (currentDirectory() + "/" + baseFile) : baseFile; + const std::string path = isAbsolutePath(header) ? header : (dirPath(baseFileNormalized) + header); + const std::string simplifiedPath = simplecpp::simplifyPath(path); + if (exactRelative) { + const std::string absolutePath = expandBaseWithAbsolutePath ? simplifiedPath : toAbsolutePath(simplifiedPath); + const std::string absoluteBaseFile = expandBaseWithAbsolutePath ? baseFileNormalized : toAbsolutePath(baseFileNormalized); + return extractRelativePathFromAbsolute(absolutePath, dirPath(absoluteBaseFile)); + } else { + return simplifiedPath; + } } static std::string openHeaderRelative(std::ifstream &f, const std::string &sourcefile, const std::string &header) { - return openHeader(f, getRelativeFileName(sourcefile, header)); + return openHeader(f, getRelativeFileName(sourcefile, header)); } // returns the simplified header path: @@ -3174,8 +3206,8 @@ static std::string getIncludePathFileName(const std::string &includePath, const std::string basePath = toAbsolutePath(includePath); if (!basePath.empty() && basePath[basePath.size()-1U]!='/' && basePath[basePath.size()-1U]!='\\') basePath += '/'; - const std::string absolutesimplifiedHeaderPath = basePath + simplifiedHeader; - return extractRelativePathFromAbsolute(absolutesimplifiedHeaderPath).first; + const std::string absoluteSimplifiedHeaderPath = basePath + simplifiedHeader; + return extractRelativePathFromAbsolute(absoluteSimplifiedHeaderPath); } static std::string openHeaderIncludePath(std::ifstream &f, const simplecpp::DUI &dui, const std::string &header) @@ -3210,23 +3242,20 @@ static std::string findPathInMapBothRelativeAndAbsolute(const std::map relativeExtractedResult = extractRelativePathFromAbsolute(path); - if (relativeExtractedResult.second) { - const std::string relativePath = relativeExtractedResult.first; - if (filedata.find(relativePath) != filedata.end()) { - return relativePath; - } - } + alternativePath = extractRelativePathFromAbsolute(simplecpp::simplifyPath(path)); } else { - const std::string absolutePath = toAbsolutePath(path); - if (filedata.find(absolutePath) != filedata.end()) { - return absolutePath; - } + alternativePath = toAbsolutePath(path); + } + + if (filedata.find(alternativePath) != filedata.end()) { + return alternativePath; + } else { + return ""; } - // otherwise - return ""; } static std::string getFileIdPath(const std::map &filedata, const std::string &sourcefile, const std::string &header, const simplecpp::DUI &dui, bool systemheader) @@ -3243,7 +3272,7 @@ static std::string getFileIdPath(const std::map(sourcefile, header);// unknown if absolute or relative, but always simplified const std::string match = findPathInMapBothRelativeAndAbsolute(filedata, relativeOrAbsoluteFilename); if (!match.empty()) { return match; From 89377e6e84dd85ef37f6a2c799fb04453c98ff82 Mon Sep 17 00:00:00 2001 From: Tal500 Date: Wed, 21 May 2025 13:14:16 +0300 Subject: [PATCH 2/7] fixes --- .github/workflows/CI-unixish.yml | 2 +- .github/workflows/CI-windows.yml | 2 +- simplecpp.cpp | 16 ++++++---------- 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/.github/workflows/CI-unixish.yml b/.github/workflows/CI-unixish.yml index c84fc052..f5a78ea5 100644 --- a/.github/workflows/CI-unixish.yml +++ b/.github/workflows/CI-unixish.yml @@ -53,7 +53,7 @@ jobs: - name: integration test run: | - python3 -m pytest integration_test.py + python3 -m pytest integration_test.py -vv - name: Run CMake run: | diff --git a/.github/workflows/CI-windows.yml b/.github/workflows/CI-windows.yml index 3e017182..971f3827 100644 --- a/.github/workflows/CI-windows.yml +++ b/.github/workflows/CI-windows.yml @@ -57,5 +57,5 @@ jobs: - name: integration test run: | set SIMPLECPP_EXE_PATH=.\${{ matrix.config }}\simplecpp.exe - python -m pytest integration_test.py || exit /b !errorlevel! + python -m pytest integration_test.py -vv || exit /b !errorlevel! diff --git a/simplecpp.cpp b/simplecpp.cpp index 4ef751f6..577b98b8 100755 --- a/simplecpp.cpp +++ b/simplecpp.cpp @@ -2725,30 +2725,28 @@ static std::string dirPath(const std::string& path) { const std::size_t firstSlash = path.find_last_of("\\/"); if (firstSlash == std::string::npos) { return ""; - } else { - return path.substr(0, firstSlash + (withTrailingSlash ? 1U : 0U)); } + return path.substr(0, firstSlash + (withTrailingSlash ? 1U : 0U)); } static std::string omitPathTrailingSlash(const std::string& path) { if (endsWith(path, "/")) { return path.substr(0, path.size() - 1U); - } else { - return path; } + return path; } static std::string extractRelativePathFromAbsolute(const std::string& absoluteSimplifiedPath, const std::string& prefixSimplifiedAbsoluteDir = currentDirectory()) { const std::string normalizedAbsolutePath = omitPathTrailingSlash(absoluteSimplifiedPath); std::string currentPrefix = omitPathTrailingSlash(prefixSimplifiedAbsoluteDir); - std::string leadingParenting = ""; + std::string leadingParenting; while (!startsWith_(normalizedAbsolutePath, currentPrefix)) { leadingParenting = "../" + leadingParenting; currentPrefix = dirPath(currentPrefix); } const std::size_t size = currentPrefix.size(); std::string relativeFromMeetingPath = normalizedAbsolutePath.substr(size, normalizedAbsolutePath.size() - size); - if (startsWith_(relativeFromMeetingPath.c_str(), "/")) { + if (startsWith_(relativeFromMeetingPath, "/")) { // omit the leading slash relativeFromMeetingPath = relativeFromMeetingPath.substr(1, relativeFromMeetingPath.size()); } @@ -3182,9 +3180,8 @@ static std::string getRelativeFileName(const std::string &baseFile, const std::s const std::string absolutePath = expandBaseWithAbsolutePath ? simplifiedPath : toAbsolutePath(simplifiedPath); const std::string absoluteBaseFile = expandBaseWithAbsolutePath ? baseFileNormalized : toAbsolutePath(baseFileNormalized); return extractRelativePathFromAbsolute(absolutePath, dirPath(absoluteBaseFile)); - } else { - return simplifiedPath; } + return simplifiedPath; } static std::string openHeaderRelative(std::ifstream &f, const std::string &sourcefile, const std::string &header) @@ -3253,9 +3250,8 @@ static std::string findPathInMapBothRelativeAndAbsolute(const std::map &filedata, const std::string &sourcefile, const std::string &header, const simplecpp::DUI &dui, bool systemheader) From 4d4bab8ba16e333fd5baed1134d7a7d620d17b6e Mon Sep 17 00:00:00 2001 From: Tal500 Date: Wed, 21 May 2025 15:00:35 +0300 Subject: [PATCH 3/7] more fixes --- simplecpp.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/simplecpp.cpp b/simplecpp.cpp index 577b98b8..58ccb140 100755 --- a/simplecpp.cpp +++ b/simplecpp.cpp @@ -3173,15 +3173,17 @@ static std::string openHeader(std::ifstream &f, const std::string &path) template static std::string getRelativeFileName(const std::string &baseFile, const std::string &header) { - const std::string baseFileNormalized = (expandBaseWithAbsolutePath && !isAbsolutePath(baseFile)) ? (currentDirectory() + "/" + baseFile) : baseFile; - const std::string path = isAbsolutePath(header) ? header : (dirPath(baseFileNormalized) + header); - const std::string simplifiedPath = simplecpp::simplifyPath(path); + const std::string baseFileSimplified = simplecpp::simplifyPath(baseFile); + const std::string baseFileNormalized = (expandBaseWithAbsolutePath && !isAbsolutePath(baseFileSimplified)) ? (currentDirectory() + "/" + baseFileSimplified) : baseFileSimplified; + + const std::string headerSimplified = simplecpp::simplifyPath(header); + const std::string path = isAbsolutePath(headerSimplified) ? headerSimplified : (dirPath(baseFileNormalized) + headerSimplified); if (exactRelative) { - const std::string absolutePath = expandBaseWithAbsolutePath ? simplifiedPath : toAbsolutePath(simplifiedPath); + const std::string absolutePath = expandBaseWithAbsolutePath ? path : toAbsolutePath(path); const std::string absoluteBaseFile = expandBaseWithAbsolutePath ? baseFileNormalized : toAbsolutePath(baseFileNormalized); return extractRelativePathFromAbsolute(absolutePath, dirPath(absoluteBaseFile)); } - return simplifiedPath; + return path; } static std::string openHeaderRelative(std::ifstream &f, const std::string &sourcefile, const std::string &header) From e688cf24c5f860b94fd710482f333e2291c9a903 Mon Sep 17 00:00:00 2001 From: Tal500 Date: Thu, 22 May 2025 13:16:58 +0300 Subject: [PATCH 4/7] fixes, safe asserts, and more tests --- integration_test.py | 64 +++++++++++++++++++++++++++++++++++---------- simplecpp.cpp | 47 +++++++++++++++++++-------------- 2 files changed, 77 insertions(+), 34 deletions(-) diff --git a/integration_test.py b/integration_test.py index d8f43643..122ce9aa 100644 --- a/integration_test.py +++ b/integration_test.py @@ -51,10 +51,11 @@ def test_relative_header_1(record_property, tmpdir, with_pragma_once, is_sys): else: assert double_include_error in stderr +@pytest.mark.parametrize("with_pragma_once", (False, True)) @pytest.mark.parametrize("inv", (False, True)) @pytest.mark.parametrize("source_relative", (False, True)) -def test_relative_header_2(record_property, tmpdir, inv, source_relative): - header_file, _ = __test_relative_header_create_header(tmpdir) +def test_relative_header_2(record_property, tmpdir, with_pragma_once, inv, source_relative): + header_file, double_include_error = __test_relative_header_create_header(tmpdir, with_pragma_once=with_pragma_once) test_file = __test_relative_header_create_source(tmpdir, "test.h", header_file, inv=inv) @@ -63,11 +64,14 @@ def test_relative_header_2(record_property, tmpdir, inv, source_relative): _, stdout, stderr = simplecpp(args, cwd=tmpdir) record_property("stdout", stdout) record_property("stderr", stderr) - assert stderr == '' - if source_relative and not inv: - assert '#line 8 "test.h"' in stdout + if with_pragma_once: + assert stderr == '' + if inv: + assert f'#line 8 "{pathlib.PurePath(tmpdir).as_posix()}/test.h"' in stdout + else: + assert '#line 8 "test.h"' in stdout else: - assert f'#line 8 "{pathlib.PurePath(tmpdir).as_posix()}/test.h"' in stdout + assert double_include_error in stderr @pytest.mark.parametrize("is_sys", (False, True)) @pytest.mark.parametrize("inv", (False, True)) @@ -89,15 +93,16 @@ def test_relative_header_3(record_property, tmpdir, is_sys, inv, source_relative assert "missing header: Header not found" in stderr else: assert stderr == '' - if source_relative and not inv: - assert '#line 8 "test_subdir/test.h"' in stdout - else: + if inv: assert f'#line 8 "{pathlib.PurePath(test_subdir).as_posix()}/test.h"' in stdout + else: + assert '#line 8 "test_subdir/test.h"' in stdout @pytest.mark.parametrize("use_short_path", (False, True)) +@pytest.mark.parametrize("relative_include_dir", (False, True)) @pytest.mark.parametrize("is_sys", (False, True)) @pytest.mark.parametrize("inv", (False, True)) -def test_relative_header_4(record_property, tmpdir, use_short_path, is_sys, inv): +def test_relative_header_4(record_property, tmpdir, use_short_path, relative_include_dir, is_sys, inv): test_subdir = os.path.join(tmpdir, "test_subdir") os.mkdir(test_subdir) header_file, _ = __test_relative_header_create_header(test_subdir) @@ -106,17 +111,22 @@ def test_relative_header_4(record_property, tmpdir, use_short_path, is_sys, inv) test_file = __test_relative_header_create_source(tmpdir, header_file, "test.h", is_include2_sys=is_sys, inv=inv) - args = [format_include_path_arg(test_subdir), test_file] + args = [format_include_path_arg("test_subdir" if relative_include_dir else test_subdir), test_file] _, stdout, stderr = simplecpp(args, cwd=tmpdir) record_property("stdout", stdout) record_property("stderr", stderr) assert stderr == '' + if (use_short_path and not inv) or (relative_include_dir and inv): + assert '#line 8 "test_subdir/test.h"' in stdout + else: + assert f'#line 8 "{pathlib.PurePath(test_subdir).as_posix()}/test.h"' in stdout @pytest.mark.parametrize("with_pragma_once", (False, True)) +@pytest.mark.parametrize("relative_include_dir", (False, True)) @pytest.mark.parametrize("is_sys", (False, True)) @pytest.mark.parametrize("inv", (False, True)) -def test_relative_header_5(record_property, tmpdir, with_pragma_once, is_sys, inv): # test relative paths with .. +def test_relative_header_5(record_property, tmpdir, with_pragma_once, relative_include_dir, is_sys, inv): # test relative paths with .. ## in this test, the subdir role is the opposite then the previous - it contains the test.c file, while the parent tmpdir contains the header file header_file, double_include_error = __test_relative_header_create_header(tmpdir, with_pragma_once=with_pragma_once) if is_sys: @@ -128,16 +138,42 @@ def test_relative_header_5(record_property, tmpdir, with_pragma_once, is_sys, in os.mkdir(test_subdir) test_file = __test_relative_header_create_source(test_subdir, header_file, header_file_second_path, is_include2_sys=is_sys, inv=inv) - args = ([format_include_path_arg(tmpdir)] if is_sys else []) + ["test.c"] + args = ([format_include_path_arg(".." if relative_include_dir else tmpdir)] if is_sys else []) + ["test.c"] _, stdout, stderr = simplecpp(args, cwd=test_subdir) record_property("stdout", stdout) record_property("stderr", stderr) if with_pragma_once: assert stderr == '' - if inv: + if (relative_include_dir or not is_sys) and inv: assert '#line 8 "../test.h"' in stdout else: assert f'#line 8 "{pathlib.PurePath(tmpdir).as_posix()}/test.h"' in stdout else: assert double_include_error in stderr + +@pytest.mark.parametrize("with_pragma_once", (False, True)) +@pytest.mark.parametrize("relative_include_dir", (False, True)) +@pytest.mark.parametrize("is_sys", (False, True)) +@pytest.mark.parametrize("inv", (False, True)) +def test_relative_header_6(record_property, tmpdir, with_pragma_once, relative_include_dir, is_sys, inv): # test relative paths with .. that is resolved only by an include dir + ## in this test, both the header and the source file are at the same dir, but there is a dummy inclusion dir as a subdir + header_file, double_include_error = __test_relative_header_create_header(tmpdir, with_pragma_once=with_pragma_once) + + test_subdir = os.path.join(tmpdir, "test_subdir") + os.mkdir(test_subdir) + test_file = __test_relative_header_create_source(tmpdir, header_file, "../test.h", is_include2_sys=is_sys, inv=inv) + + args = [format_include_path_arg("test_subdir" if relative_include_dir else test_subdir), "test.c"] + + _, stdout, stderr = simplecpp(args, cwd=tmpdir) + record_property("stdout", stdout) + record_property("stderr", stderr) + if with_pragma_once: + assert stderr == '' + if relative_include_dir and inv: + assert '#line 8 "test.h"' in stdout + else: + assert f'#line 8 "{pathlib.PurePath(tmpdir).as_posix()}/test.h"' in stdout + else: + assert double_include_error in stderr diff --git a/simplecpp.cpp b/simplecpp.cpp index 58ccb140..4f701e81 100755 --- a/simplecpp.cpp +++ b/simplecpp.cpp @@ -2719,9 +2719,7 @@ static std::string toAbsolutePath(const std::string& path) { return simplecpp::simplifyPath(path); } -// templated function for compiler optimization opportunities -template -static std::string dirPath(const std::string& path) { +static std::string dirPath(const std::string& path, bool withTrailingSlash=true) { const std::size_t firstSlash = path.find_last_of("\\/"); if (firstSlash == std::string::npos) { return ""; @@ -2742,7 +2740,7 @@ static std::string extractRelativePathFromAbsolute(const std::string& absoluteSi std::string leadingParenting; while (!startsWith_(normalizedAbsolutePath, currentPrefix)) { leadingParenting = "../" + leadingParenting; - currentPrefix = dirPath(currentPrefix); + currentPrefix = dirPath(currentPrefix, false); } const std::size_t size = currentPrefix.size(); std::string relativeFromMeetingPath = normalizedAbsolutePath.substr(size, normalizedAbsolutePath.size() - size); @@ -3169,26 +3167,24 @@ static std::string openHeader(std::ifstream &f, const std::string &path) return ""; } -// templated function for compiler optimization opportunities -template static std::string getRelativeFileName(const std::string &baseFile, const std::string &header) { const std::string baseFileSimplified = simplecpp::simplifyPath(baseFile); - const std::string baseFileNormalized = (expandBaseWithAbsolutePath && !isAbsolutePath(baseFileSimplified)) ? (currentDirectory() + "/" + baseFileSimplified) : baseFileSimplified; + const std::string baseFileAbsolute = isAbsolutePath(baseFileSimplified) ? + baseFileSimplified : + simplecpp::simplifyPath(currentDirectory() + "/" + baseFileSimplified); const std::string headerSimplified = simplecpp::simplifyPath(header); - const std::string path = isAbsolutePath(headerSimplified) ? headerSimplified : (dirPath(baseFileNormalized) + headerSimplified); - if (exactRelative) { - const std::string absolutePath = expandBaseWithAbsolutePath ? path : toAbsolutePath(path); - const std::string absoluteBaseFile = expandBaseWithAbsolutePath ? baseFileNormalized : toAbsolutePath(baseFileNormalized); - return extractRelativePathFromAbsolute(absolutePath, dirPath(absoluteBaseFile)); - } - return path; + const std::string path = isAbsolutePath(headerSimplified) ? + headerSimplified : + simplecpp::simplifyPath(dirPath(baseFileAbsolute) + headerSimplified); + + return extractRelativePathFromAbsolute(path); } static std::string openHeaderRelative(std::ifstream &f, const std::string &sourcefile, const std::string &header) { - return openHeader(f, getRelativeFileName(sourcefile, header)); + return openHeader(f, getRelativeFileName(sourcefile, header)); } // returns the simplified header path: @@ -3205,8 +3201,9 @@ static std::string getIncludePathFileName(const std::string &includePath, const std::string basePath = toAbsolutePath(includePath); if (!basePath.empty() && basePath[basePath.size()-1U]!='/' && basePath[basePath.size()-1U]!='\\') basePath += '/'; - const std::string absoluteSimplifiedHeaderPath = basePath + simplifiedHeader; - return extractRelativePathFromAbsolute(absoluteSimplifiedHeaderPath); + const std::string absoluteSimplifiedHeaderPath = simplecpp::simplifyPath(basePath + simplifiedHeader); + // preserve absoluteness/relativieness of the including dir + return isAbsolutePath(includePath) ? absoluteSimplifiedHeaderPath : extractRelativePathFromAbsolute(absoluteSimplifiedHeaderPath); } static std::string openHeaderIncludePath(std::ifstream &f, const simplecpp::DUI &dui, const std::string &header) @@ -3270,7 +3267,7 @@ static std::string getFileIdPath(const std::map(sourcefile, header);// unknown if absolute or relative, but always simplified + const std::string relativeOrAbsoluteFilename = getRelativeFileName(sourcefile, header);// unknown if absolute or relative, but always simplified const std::string match = findPathInMapBothRelativeAndAbsolute(filedata, relativeOrAbsoluteFilename); if (!match.empty()) { return match; @@ -3294,6 +3291,16 @@ static bool hasFile(const std::map &filedat return !getFileIdPath(filedata, sourcefile, header, dui, systemheader).empty(); } +static void safeInsertTokenListToMap(std::map &filedata, const std::string &header2, simplecpp::TokenList *tokens, const std::string &header, const std::string &sourcefile, bool systemheader, const char* contextDesc) +{ + const bool inserted = filedata.insert(std::make_pair(header2, tokens)).second; + if (!inserted) { + std::cerr << "error in " << contextDesc << " - attempt to add a tokenized file to the file map, but this file is already in the map! Details:" << + "header: " << header << " header2: " << header2 << " source: " << sourcefile << " systemheader: " << systemheader << std::endl; + abort(); + } +} + std::map simplecpp::load(const simplecpp::TokenList &rawtokens, std::vector &filenames, const simplecpp::DUI &dui, simplecpp::OutputList *outputList) { #ifdef SIMPLECPP_WINDOWS @@ -3370,7 +3377,7 @@ std::map simplecpp::load(const simplecpp::To TokenList *tokens = new TokenList(header2, filenames, outputList); if (dui.removeComments) tokens->removeComments(); - ret[header2] = tokens; + safeInsertTokenListToMap(ret, header2, tokens, header, rawtok->location.file(), systemheader, "simplecpp::load"); if (tokens->front()) filelist.push_back(tokens->front()); } @@ -3657,7 +3664,7 @@ void simplecpp::preprocess(simplecpp::TokenList &output, const simplecpp::TokenL TokenList * const tokens = new TokenList(header2, files, outputList); if (dui.removeComments) tokens->removeComments(); - filedata[header2] = tokens; + safeInsertTokenListToMap(filedata, header2, tokens, header, rawtok->location.file(), systemheader, "simplecpp::preprocess"); } } if (header2.empty()) { From 18620e20b18a08639ee54be89d3faa1634ab950f Mon Sep 17 00:00:00 2001 From: Tal500 Date: Thu, 22 May 2025 13:18:17 +0300 Subject: [PATCH 5/7] typo fix --- simplecpp.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/simplecpp.cpp b/simplecpp.cpp index 4f701e81..1d8e770b 100755 --- a/simplecpp.cpp +++ b/simplecpp.cpp @@ -2720,11 +2720,11 @@ static std::string toAbsolutePath(const std::string& path) { } static std::string dirPath(const std::string& path, bool withTrailingSlash=true) { - const std::size_t firstSlash = path.find_last_of("\\/"); - if (firstSlash == std::string::npos) { + const std::size_t lastSlash = path.find_last_of("\\/"); + if (lastSlash == std::string::npos) { return ""; } - return path.substr(0, firstSlash + (withTrailingSlash ? 1U : 0U)); + return path.substr(0, lastSlash + (withTrailingSlash ? 1U : 0U)); } static std::string omitPathTrailingSlash(const std::string& path) { From 3b5a01cb0c43614418fa872d8dbfe0f461b15c84 Mon Sep 17 00:00:00 2001 From: Tal500 Date: Thu, 22 May 2025 13:23:44 +0300 Subject: [PATCH 6/7] tidy fix --- simplecpp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/simplecpp.cpp b/simplecpp.cpp index 1d8e770b..fc1bf5cb 100755 --- a/simplecpp.cpp +++ b/simplecpp.cpp @@ -3297,7 +3297,7 @@ static void safeInsertTokenListToMap(std::map Date: Mon, 26 May 2025 14:16:36 +0300 Subject: [PATCH 7/7] robustness under theoretical call to `extractRelativePathFromAbsolute` --- simplecpp.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/simplecpp.cpp b/simplecpp.cpp index fc1bf5cb..9ff66d8b 100755 --- a/simplecpp.cpp +++ b/simplecpp.cpp @@ -2744,6 +2744,12 @@ static std::string extractRelativePathFromAbsolute(const std::string& absoluteSi } const std::size_t size = currentPrefix.size(); std::string relativeFromMeetingPath = normalizedAbsolutePath.substr(size, normalizedAbsolutePath.size() - size); + if (currentPrefix.empty() && !(startsWith_(absoluteSimplifiedPath, "/") && startsWith_(prefixSimplifiedAbsoluteDir, "/"))) { + // In the case that there is no common prefix path, + // and at not both of the paths start with `/` (can happen only in Windows paths on distinct partitions), + // return the absolute simplified path as is because no relative path can match. + return absoluteSimplifiedPath; + } if (startsWith_(relativeFromMeetingPath, "/")) { // omit the leading slash relativeFromMeetingPath = relativeFromMeetingPath.substr(1, relativeFromMeetingPath.size());