Factor out the Unix-specific parts of canonPathInner

This prepares the code to also support Windows paths in the next commit.
This commit is contained in:
John Ericson 2024-01-29 17:16:18 -05:00
parent 60936f28e5
commit 4531585275
3 changed files with 45 additions and 11 deletions

View File

@ -8,7 +8,7 @@ CanonPath CanonPath::root = CanonPath("/");
static std::string absPathPure(std::string_view path) 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) CanonPath::CanonPath(std::string_view raw)

View File

@ -10,6 +10,39 @@
namespace nix { 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. * 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 * This is a chance to modify those two paths in arbitrary way, e.g. if
* "result" points to a symlink. * "result" points to a symlink.
*/ */
typename std::string canonPathInner( template<class PathDict>
std::string_view remaining, typename PathDict::String canonPathInner(
typename PathDict::StringView remaining,
auto && hookComponent) auto && hookComponent)
{ {
assert(remaining != ""); assert(remaining != "");
std::string result; typename PathDict::String result;
result.reserve(256); result.reserve(256);
while (true) { while (true) {
/* Skip slashes. */ /* Skip slashes. */
while (!remaining.empty() && remaining[0] == '/') while (!remaining.empty() && PathDict::isPathSep(remaining[0]))
remaining.remove_prefix(1); remaining.remove_prefix(1);
if (remaining.empty()) break; if (remaining.empty()) break;
auto nextComp = ({ auto nextComp = ({
auto nextPathSep = remaining.find('/'); auto nextPathSep = PathDict::findPathSep(remaining);
nextPathSep == remaining.npos ? remaining : remaining.substr(0, nextPathSep); nextPathSep == remaining.npos ? remaining : remaining.substr(0, nextPathSep);
}); });
@ -53,14 +87,14 @@ typename std::string canonPathInner(
/* If `..', delete the last component. */ /* If `..', delete the last component. */
else if (nextComp == "..") else if (nextComp == "..")
{ {
if (!result.empty()) result.erase(result.rfind('/')); if (!result.empty()) result.erase(PathDict::rfindPathSep(result));
remaining.remove_prefix(2); remaining.remove_prefix(2);
} }
/* Normal component; copy it. */ /* Normal component; copy it. */
else { else {
result += '/'; result += PathDict::preferredSep;
if (const auto slash = remaining.find('/'); slash == result.npos) { if (const auto slash = PathDict::findPathSep(remaining); slash == result.npos) {
result += remaining; result += remaining;
remaining = {}; remaining = {};
} else { } else {
@ -73,7 +107,7 @@ typename std::string canonPathInner(
} }
if (result.empty()) if (result.empty())
result = "/"; result = typename PathDict::String { PathDict::preferredSep };
return result; return result;
} }

View File

@ -78,7 +78,7 @@ Path canonPath(PathView path, bool resolveSymlinks)
arbitrary (but high) limit to prevent infinite loops. */ arbitrary (but high) limit to prevent infinite loops. */
unsigned int followCount = 0, maxFollow = 1024; unsigned int followCount = 0, maxFollow = 1024;
return canonPathInner( return canonPathInner<UnixPathTrait>(
path, path,
[&followCount, &temp, maxFollow, resolveSymlinks] [&followCount, &temp, maxFollow, resolveSymlinks]
(std::string & result, std::string_view & remaining) { (std::string & result, std::string_view & remaining) {