From 4531585275254f13dae1ff61434e15865a1e796a Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 29 Jan 2024 17:16:18 -0500 Subject: [PATCH] Factor out the Unix-specific parts of `canonPathInner` This prepares the code to also support Windows paths in the next commit. --- src/libutil/canon-path.cc | 2 +- src/libutil/file-path-impl.hh | 52 +++++++++++++++++++++++++++++------ src/libutil/file-system.cc | 2 +- 3 files changed, 45 insertions(+), 11 deletions(-) diff --git a/src/libutil/canon-path.cc b/src/libutil/canon-path.cc index fcd53862b..27f048697 100644 --- a/src/libutil/canon-path.cc +++ b/src/libutil/canon-path.cc @@ -8,7 +8,7 @@ CanonPath CanonPath::root = CanonPath("/"); static std::string absPathPure(std::string_view path) { - return canonPathInner(path, [](auto &, auto &){}); + return canonPathInner(path, [](auto &, auto &){}); } CanonPath::CanonPath(std::string_view raw) diff --git a/src/libutil/file-path-impl.hh b/src/libutil/file-path-impl.hh index 39159c7c2..941d433e0 100644 --- a/src/libutil/file-path-impl.hh +++ b/src/libutil/file-path-impl.hh @@ -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. * @@ -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 +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); }); @@ -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 { @@ -73,7 +107,7 @@ typename std::string canonPathInner( } if (result.empty()) - result = "/"; + result = typename PathDict::String { PathDict::preferredSep }; return result; } diff --git a/src/libutil/file-system.cc b/src/libutil/file-system.cc index 3c019a9ed..ff83bc4ea 100644 --- a/src/libutil/file-system.cc +++ b/src/libutil/file-system.cc @@ -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( path, [&followCount, &temp, maxFollow, resolveSymlinks] (std::string & result, std::string_view & remaining) {