Skip to content

Commit

Permalink
Add base::get_absolute_path_from_relative() function
Browse files Browse the repository at this point in the history
This addition is part of the solution for aseprite/aseprite#4851
which was a bug introduced in aseprite/asseprite#4389.
  • Loading branch information
Gasparoken committed Dec 16, 2024
1 parent c84b89b commit 2ef8e31
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 30 deletions.
39 changes: 39 additions & 0 deletions base/fs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,45 @@ std::string get_relative_path(const std::string& filename, const std::string& ba
return relativePath;
}

std::string get_absolute_path_from_relative(const std::string& base_path,
const std::string& relative_filename)
{
std::vector<std::string> absoluteParts;
split_string(base_path, absoluteParts, path_separators);

std::vector<std::string> relativeParts;
split_string(relative_filename, relativeParts, path_separators);

// If the first element is empty, it means that the relative
// file name starts with a separator and it also means that
// it's an absolute address.
if (relativeParts.size() >= 1 && relativeParts[0] == "")
return relative_filename;

bool relative_fn_browsable = false;
for (auto it = relativeParts.begin(); it != relativeParts.end(); ++it) {
if (*it == "..") {
relative_fn_browsable = true;
break;
}
}
if (!relative_fn_browsable)
return base::join_path(base_path, relative_filename);

for (auto it = relativeParts.begin(); it != relativeParts.end(); ++it) {
if (*it == "..")
absoluteParts.pop_back();
else
absoluteParts.push_back(*it);
}

std::string absolutePath = path_separators;
for (auto it = absoluteParts.begin(); it != absoluteParts.end(); ++it)
absolutePath = base::join_path(absolutePath, *it);

return absolutePath;
}

std::string join_path(const std::string& path, const std::string& file)
{
std::string result(path);
Expand Down
64 changes: 34 additions & 30 deletions base/fs.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,49 +87,53 @@ inline constexpr bool is_path_separator(std::string::value_type chr)
chr == '/');
}

// Returns only the path (without the last trailing slash).
std::string get_file_path(const std::string& filename);
// Returns only the path (without the last trailing slash).
std::string get_file_path(const std::string& filename);

// Returns the file name with its extension, removing the path.
std::string get_file_name(const std::string& filename);
// Returns the file name with its extension, removing the path.
std::string get_file_name(const std::string& filename);

// Returns the extension of the file name (without the dot).
std::string get_file_extension(const std::string& filename);
// Returns the extension of the file name (without the dot).
std::string get_file_extension(const std::string& filename);

// Returns the whole path with another extension.
std::string replace_extension(const std::string& filename, const std::string& extension);
// Returns the whole path with another extension.
std::string replace_extension(const std::string& filename, const std::string& extension);

// Returns the file name without path and without extension.
std::string get_file_title(const std::string& filename);
std::string get_file_title_with_path(const std::string& filename);
// Returns the file name without path and without extension.
std::string get_file_title(const std::string& filename);
std::string get_file_title_with_path(const std::string& filename);

// Returns the relative path of the given filename from the base_path.
std::string get_relative_path(const std::string& filename, const std::string& base_path);
// Returns the relative path of the given filename from the base_path.
std::string get_relative_path(const std::string& filename, const std::string& base_path);

// Joins two paths or a path and a file name with a path-separator.
std::string join_path(const std::string& path, const std::string& file);
// Returns the absolute path of a given relative filename referred to the base_path.
std::string get_absolute_path_from_relative(const std::string& base_path,
const std::string& relative_filename);

// Removes the trailing separator from the given path.
std::string remove_path_separator(const std::string& path);
// Joins two paths or a path and a file name with a path-separator.
std::string join_path(const std::string& path, const std::string& file);

// Replaces all separators with the system separator.
std::string fix_path_separators(const std::string& filename);
// Removes the trailing separator from the given path.
std::string remove_path_separator(const std::string& path);

// Remove superfluous path elements ("/../" and "/./") and call
// fix_path_separators() for the given path.
std::string normalize_path(const std::string& path);
// Replaces all separators with the system separator.
std::string fix_path_separators(const std::string& filename);

// Returns true if the filename contains one of the specified
// extensions. The "extensions" parameter must be a set of possible
// extensions.
bool has_file_extension(const std::string& filename, const base::paths& extensions);
// Remove superfluous path elements ("/../" and "/./") and call
// fix_path_separators() for the given path.
std::string normalize_path(const std::string& path);

int compare_filenames(const std::string& a, const std::string& b);
// Returns true if the filename contains one of the specified
// extensions. The "extensions" parameter must be a set of possible
// extensions.
bool has_file_extension(const std::string& filename, const base::paths& extensions);

int compare_filenames(const std::string& a, const std::string& b);

#if LAF_WINDOWS
class Version;
Version get_file_version(const std::string& filename);
Version get_file_version(const wchar_t* filename);
class Version;
Version get_file_version(const std::string& filename);
Version get_file_version(const wchar_t* filename);
#endif

} // namespace base
Expand Down
19 changes: 19 additions & 0 deletions base/fs_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,25 @@ TEST(FS, GetRelativePath)
#endif
}

TEST(FS, GetAbsolutePathFromRelativePath)
{
#if LAF_WINDOWS
EXPECT_EQ("C:\\foo\\bar\\test.file", get_absolute_path_from_relative("C:\\foo", "bar\\test.file"));
EXPECT_EQ("D:\\another\\disk\\foo\\bar\\test.file",
get_absolute_path_from_relative("D:\\another\\disk", "foo\\bar\\test.file"));
EXPECT_EQ("\\foo\\bar\\test.file", get_absolute_path_from_relative("D:\\another\\disk", "\\foo\\bar\\test.file"));
EXPECT_EQ("C:\\foo\\bar\\test.file", get_absolute_path_from_relative("C:\\foo\\another", "..\\bar\\test.file"));
EXPECT_EQ("C:\\foo\\bar\\test.file", get_absolute_path_from_relative("C:\\foo\\a\\b", "..\\..\\bar\\test.file"));
#else
EXPECT_EQ("C:/foo/bar/test.file", get_absolute_path_from_relative("C:/foo", "bar/test.file"));
EXPECT_EQ("D:/another/disk/foo/bar/test.file",
get_absolute_path_from_relative("D:/another/disk", "foo/bar/test.file"));
EXPECT_EQ("/foo/bar/test.file", get_absolute_path_from_relative("D:/another/disk", "/foo/bar/test.file"));
EXPECT_EQ("/foo/bar/test.file", get_absolute_path_from_relative("/foo/another", "../bar/test.file"));
EXPECT_EQ("/foo/bar/test.file", get_absolute_path_from_relative("/foo/a/b", "../../bar/test.file"));
#endif
}

TEST(FS, GetAbsolutePath)
{
const auto cp = get_current_path();
Expand Down

0 comments on commit 2ef8e31

Please sign in to comment.