Skip to content

Commit

Permalink
Factor out the Unix-specific parts of canonPathInner
Browse files Browse the repository at this point in the history
This prepares the code to also support Windows paths in the next commit.
  • Loading branch information
Ericson2314 committed Feb 16, 2024
1 parent 60936f2 commit 4531585
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 11 deletions.
2 changes: 1 addition & 1 deletion src/libutil/canon-path.cc
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ CanonPath CanonPath::root = CanonPath("/");

static std::string absPathPure(std::string_view path)
{
return canonPathInner(path, [](auto &, auto &){});
return canonPathInner<UnixPathTrait>(path, [](auto &, auto &){});
}

CanonPath::CanonPath(std::string_view raw)
Expand Down
52 changes: 43 additions & 9 deletions src/libutil/file-path-impl.hh
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,39 @@

namespace nix {

/**
* Unix-style path primives.
*
* Nix'result own "logical" paths are always Unix-style. So this is always
* used for that, and additionally used for native paths on Unix.
*/
struct UnixPathTrait
{
using CharT = char;

using String = std::string;

using StringView = std::string_view;

constexpr static char preferredSep = '/';

static inline bool isPathSep(char c)
{
return c == '/';
}

static inline size_t findPathSep(StringView path, size_t from = 0)
{
return path.find('/', from);
}

static inline size_t rfindPathSep(StringView path, size_t from = StringView::npos)
{
return path.rfind('/', from);
}
};


/**
* Core pure path canonicalization algorithm.
*
Expand All @@ -24,25 +57,26 @@ namespace nix {
* This is a chance to modify those two paths in arbitrary way, e.g. if
* "result" points to a symlink.
*/
typename std::string canonPathInner(
std::string_view remaining,
template<class PathDict>
typename PathDict::String canonPathInner(
typename PathDict::StringView remaining,
auto && hookComponent)
{
assert(remaining != "");

std::string result;
typename PathDict::String result;
result.reserve(256);

while (true) {

/* Skip slashes. */
while (!remaining.empty() && remaining[0] == '/')
while (!remaining.empty() && PathDict::isPathSep(remaining[0]))
remaining.remove_prefix(1);

if (remaining.empty()) break;

auto nextComp = ({
auto nextPathSep = remaining.find('/');
auto nextPathSep = PathDict::findPathSep(remaining);
nextPathSep == remaining.npos ? remaining : remaining.substr(0, nextPathSep);
});

Expand All @@ -53,14 +87,14 @@ typename std::string canonPathInner(
/* If `..', delete the last component. */
else if (nextComp == "..")
{
if (!result.empty()) result.erase(result.rfind('/'));
if (!result.empty()) result.erase(PathDict::rfindPathSep(result));
remaining.remove_prefix(2);
}

/* Normal component; copy it. */
else {
result += '/';
if (const auto slash = remaining.find('/'); slash == result.npos) {
result += PathDict::preferredSep;
if (const auto slash = PathDict::findPathSep(remaining); slash == result.npos) {
result += remaining;
remaining = {};
} else {
Expand All @@ -73,7 +107,7 @@ typename std::string canonPathInner(
}

if (result.empty())
result = "/";
result = typename PathDict::String { PathDict::preferredSep };

return result;
}
Expand Down
2 changes: 1 addition & 1 deletion src/libutil/file-system.cc
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ Path canonPath(PathView path, bool resolveSymlinks)
arbitrary (but high) limit to prevent infinite loops. */
unsigned int followCount = 0, maxFollow = 1024;

return canonPathInner(
return canonPathInner<UnixPathTrait>(
path,
[&followCount, &temp, maxFollow, resolveSymlinks]
(std::string & result, std::string_view & remaining) {
Expand Down

0 comments on commit 4531585

Please sign in to comment.