Similar to Dir.path.resolve, but converts to a cwd-relative path, or, if that would
start with a relative up-dir (".."), an absolute path based on the cwd. Also, the cwd
returns the empty string ("") instead of ".".
pub fn resolvePath(
gpa: Allocator,
/// The return value of `getResolvedCwd`.
/// Passed as an argument to avoid pointlessly repeating the call.
cwd_resolved: []const u8,
paths: []const []const u8,
) Allocator.Error![]u8
pub fn resolvePath(
gpa: Allocator,
/// The return value of `getResolvedCwd`.
/// Passed as an argument to avoid pointlessly repeating the call.
cwd_resolved: []const u8,
paths: []const []const u8,
) Allocator.Error![]u8 {
if (builtin.target.os.tag == .wasi) {
assert(mem.eql(u8, cwd_resolved, ""));
const res = try Dir.path.resolve(gpa, paths);
if (mem.eql(u8, res, ".")) {
gpa.free(res);
return "";
}
return res;
}
// Heuristic for a fast path: if no component is absolute and ".." never appears, we just need to resolve `paths`.
for (paths) |p| {
if (Dir.path.isAbsolute(p)) break; // absolute path
if (mem.indexOf(u8, p, "..") != null) break; // may contain up-dir
} else {
// no absolute path, no "..".
const res = try Dir.path.resolve(gpa, paths);
if (mem.eql(u8, res, ".")) {
gpa.free(res);
return "";
}
assert(!Dir.path.isAbsolute(res));
assert(!isUpDir(res));
return res;
}
// The fast path failed; resolve the whole thing.
// Optimization: `paths` often has just one element.
const path_resolved = switch (paths.len) {
0 => unreachable,
1 => try Dir.path.resolve(gpa, &.{ cwd_resolved, paths[0] }),
else => r: {
const all_paths = try gpa.alloc([]const u8, paths.len + 1);
defer gpa.free(all_paths);
all_paths[0] = cwd_resolved;
@memcpy(all_paths[1..], paths);
break :r try Dir.path.resolve(gpa, all_paths);
},
};
errdefer gpa.free(path_resolved);
assert(Dir.path.isAbsolute(path_resolved));
assert(Dir.path.isAbsolute(cwd_resolved));
if (!mem.startsWith(u8, path_resolved, cwd_resolved)) return path_resolved; // not in cwd
if (path_resolved.len == cwd_resolved.len) {
// equal to cwd
gpa.free(path_resolved);
return "";
}
if (path_resolved[cwd_resolved.len] != Dir.path.sep) return path_resolved; // not in cwd (last component differs)
// in cwd; extract sub path
const sub_path = try gpa.dupe(u8, path_resolved[cwd_resolved.len + 1 ..]);
gpa.free(path_resolved);
return sub_path;
}