Skip to content

Commit 1b79a29

Browse files
committed
Add 'is_absolute_path()' function
Also, the 'get_relative_path(pathA, pathB)' function has been made case-insensitive. This is done to correctly calculate common folders in case one of the entered paths has a case difference.
1 parent bd36a11 commit 1b79a29

File tree

5 files changed

+87
-6
lines changed

5 files changed

+87
-6
lines changed

base/fs.cpp

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// LAF Base Library
2-
// Copyright (c) 2021-2024 Igara Studio S.A.
2+
// Copyright (c) 2021-2025 Igara Studio S.A.
33
// Copyright (c) 2001-2018 David Capello
44
//
55
// This file is released under the terms of the MIT license.
@@ -20,6 +20,11 @@
2020
#include "base/fs_unix.h"
2121
#endif
2222

23+
#if LAF_MACOS
24+
#include <fstream>
25+
#include <cstdio>
26+
#endif
27+
2328
#include <algorithm>
2429
#include <cctype>
2530
#include <cstdlib>
@@ -35,6 +40,46 @@ const std::string::value_type* path_separators = "\\/";
3540
const std::string::value_type* path_separators = "/";
3641
#endif
3742

43+
#if LAF_MACOS
44+
static bool* g_is_filesystem_case_sensitive = nullptr;
45+
46+
bool isFilesystemCaseSensitive() {
47+
if (g_is_filesystem_case_sensitive)
48+
return *g_is_filesystem_case_sensitive;
49+
std::string path1 = get_current_path() + "/filesystemTest.txt";
50+
std::string path2 = get_current_path() + "/FilesystemTest.TXT";
51+
52+
std::ofstream file1(path1);
53+
if (!file1) {
54+
// Just in case, the case-sensitive approach is adopted.
55+
*g_is_filesystem_case_sensitive = true;
56+
return true;
57+
}
58+
file1.close();
59+
60+
std::ofstream file2(path2);
61+
bool caseSensitive = file2.is_open();
62+
if (caseSensitive) file2.close();
63+
64+
std::remove(path1.c_str());
65+
std::remove(path2.c_str());
66+
67+
*g_is_filesystem_case_sensitive = caseSensitive;
68+
return caseSensitive;
69+
}
70+
#endif
71+
72+
bool is_absolute_path(const std::string& path)
73+
{
74+
#if LAF_WINDOWS
75+
return (path.size() > 2 && std::isalpha(path[0]) && path[1] == ':' && path[2] == path_separator)
76+
// Network path (UNC)
77+
|| (path.size() > 1 && path[0] == path_separator && path[1] == path_separator);
78+
#else
79+
return !path.empty() && path[0] == path_separator;
80+
#endif
81+
}
82+
3883
void make_all_directories(const std::string& path)
3984
{
4085
std::vector<std::string> parts;
@@ -194,7 +239,17 @@ std::string get_relative_path(const std::string& filename, const std::string& ba
194239
auto itFrom = baseDirs.begin();
195240
auto itTo = toParts.begin();
196241

197-
while (itFrom != baseDirs.end() && itTo != toParts.end() && *itFrom == *itTo) {
242+
while (itFrom != baseDirs.end() && itTo != toParts.end()
243+
// Linux OS is case sensitive
244+
#if LAF_LINUX
245+
&& *itFrom == *itTo
246+
#elif LAF_WINDOWS
247+
&& base::string_to_lower(*itFrom) == base::string_to_lower((*itTo))
248+
#elif LAF_MACOS
249+
&& ((!isFilesystemCaseSensitive() && (base::string_to_lower(*itFrom) == base::string_to_lower(*itTo)))
250+
|| (isFilesystemCaseSensitive() && (*itFrom == *itTo)))
251+
#endif
252+
) {
198253
++itFrom;
199254
++itTo;
200255
}

base/fs.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// LAF Base Library
2-
// Copyright (c) 2020-2024 Igara Studio S.A.
2+
// Copyright (c) 2020-2025 Igara Studio S.A.
33
// Copyright (c) 2001-2018 David Capello
44
//
55
// This file is released under the terms of the MIT license.
@@ -28,6 +28,7 @@ extern const std::string::value_type* path_separators;
2828

2929
bool is_file(const std::string& path);
3030
bool is_directory(const std::string& path);
31+
bool is_absolute_path(const std::string& path);
3132

3233
size_t file_size(const std::string& path);
3334

base/fs_tests.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,31 @@ TEST(FS, GetAbsolutePath)
258258
#endif
259259
}
260260

261+
TEST(FS, IsAbsolutePath)
262+
{
263+
EXPECT_FALSE(is_absolute_path(""));
264+
EXPECT_FALSE(is_absolute_path("a"));
265+
EXPECT_FALSE(is_absolute_path("./a"));
266+
EXPECT_FALSE(is_absolute_path("../a"));
267+
EXPECT_FALSE(is_absolute_path("."));
268+
EXPECT_FALSE(is_absolute_path("./."));
269+
EXPECT_FALSE(is_absolute_path("./a/.."));
270+
EXPECT_FALSE(is_absolute_path(".\\//."));
271+
272+
#if LAF_WINDOWS
273+
EXPECT_TRUE(is_absolute_path("C:\\path\\..\\file"));
274+
EXPECT_TRUE(is_absolute_path("\\\\network\\path"));
275+
EXPECT_FALSE(is_absolute_path("C:user\\name\\"));
276+
EXPECT_FALSE(is_absolute_path("C\\user\\name\\"));
277+
EXPECT_FALSE(is_absolute_path("\\user\\path\\"));
278+
EXPECT_FALSE(is_absolute_path(".:\\user\\name"));
279+
EXPECT_FALSE(is_absolute_path("\\:\\user\\name"));
280+
#else
281+
EXPECT_TRUE(is_absolute_path("/path/../file"));
282+
EXPECT_FALSE(is_absolute_path("path/../file"));
283+
#endif
284+
}
285+
261286
TEST(FS, GetCanonicalPath)
262287
{
263288
const auto cp = get_current_path();

base/fs_unix.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// LAF Base Library
2-
// Copyright (c) 2021-2024 Igara Studio S.A.
2+
// Copyright (c) 2021-2025 Igara Studio S.A.
33
// Copyright (c) 2001-2018 David Capello
44
//
55
// This file is released under the terms of the MIT license.
@@ -213,7 +213,7 @@ std::string get_canonical_path(const std::string& path)
213213
std::string get_absolute_path(const std::string& path)
214214
{
215215
std::string full = path;
216-
if (!full.empty() && full[0] != '/')
216+
if (!full.empty() && !is_absolute_path(full))
217217
full = join_path(get_current_path(), full);
218218
full = normalize_path(full);
219219
if (!full.empty() && full.back() == path_separator)

base/fs_win32.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ std::string get_canonical_path(const std::string& path)
161161
std::string get_absolute_path(const std::string& path)
162162
{
163163
std::string full;
164-
if (path.size() > 2 && path[1] != ':')
164+
if (!full.empty() && !base::is_absolute_path(path))
165165
full = base::join_path(base::get_current_path(), path);
166166
else
167167
full = path;

0 commit comments

Comments
 (0)