pub fn relativeWindows(
gpa: Allocator,
cwd: []const u8,
environ_map: ?*const std.process.Environ.Map,
from: []const u8,
to: []const u8,
) Allocator.Error![]u8 {
const parsed_from = parsePathWindows(u8, from);
const parsed_to = parsePathWindows(u8, to);
const result_is_always_to = x: {
if (parsed_from.kind != parsed_to.kind) {
break :x false;
}
switch (parsed_from.kind) {
.drive_relative, .drive_absolute => {
break :x !compareDiskDesignators(u8, .drive, parsed_from.root, parsed_to.root);
},
.unc_absolute => {
break :x !compareDiskDesignators(u8, .unc, parsed_from.root, parsed_to.root);
},
.relative, .rooted, .local_device => break :x false,
.root_local_device => break :x true,
}
};
if (result_is_always_to) {
return windowsResolveAgainstCwd(gpa, cwd, environ_map, to, parsed_to);
}
const resolved_from = try windowsResolveAgainstCwd(gpa, cwd, environ_map, from, parsed_from);
defer gpa.free(resolved_from);
var clean_up_resolved_to = true;
const resolved_to = try windowsResolveAgainstCwd(gpa, cwd, environ_map, to, parsed_to);
defer if (clean_up_resolved_to) gpa.free(resolved_to);
const parsed_resolved_from = parsePathWindows(u8, resolved_from);
const parsed_resolved_to = parsePathWindows(u8, resolved_to);
const result_is_to = x: {
if (parsed_resolved_from.kind != parsed_resolved_to.kind) {
break :x true;
}
switch (parsed_resolved_from.kind) {
.drive_absolute, .drive_relative => {
break :x !compareDiskDesignators(u8, .drive, parsed_resolved_from.root, parsed_resolved_to.root);
},
.unc_absolute => {
break :x !compareDiskDesignators(u8, .unc, parsed_resolved_from.root, parsed_resolved_to.root);
},
.relative, .rooted, .local_device => break :x false,
.root_local_device => break :x true,
}
};
if (result_is_to) {
clean_up_resolved_to = false;
return resolved_to;
}
var from_it = mem.tokenizeAny(u8, resolved_from[parsed_resolved_from.root.len..], "/\\");
var to_it = mem.tokenizeAny(u8, resolved_to[parsed_resolved_to.root.len..], "/\\");
while (true) {
const from_component = from_it.next() orelse return gpa.dupe(u8, to_it.rest());
const to_rest = to_it.rest();
if (to_it.next()) |to_component| {
if (eqlIgnoreCaseWtf8(from_component, to_component))
continue;
}
var up_index_end = "..".len;
while (from_it.next()) |_| {
up_index_end += "\\..".len;
}
const result = try gpa.alloc(u8, up_index_end + @intFromBool(to_rest.len > 0) + to_rest.len);
errdefer gpa.free(result);
result[0..2].* = "..".*;
var result_index: usize = 2;
while (result_index < up_index_end) {
result[result_index..][0..3].* = "\\..".*;
result_index += 3;
}
var rest_it = mem.tokenizeAny(u8, to_rest, "/\\");
while (rest_it.next()) |to_component| {
result[result_index] = '\\';
result_index += 1;
@memcpy(result[result_index..][0..to_component.len], to_component);
result_index += to_component.len;
}
return gpa.realloc(result, result_index);
}
return [_]u8{};
}
Returns the non-absolute path from
fromtotoaccording to Windows rules.Other than memory allocation, this is a pure function; the result solely depends on the input parameters.
If
fromandtoeach resolve to the same path (after callingresolveon each), a zero-length string is returned.The result is not guaranteed to be relative, as the paths may be on different volumes. In that case, the result will be the canonicalized absolute path of
to.Per-drive CWDs are stored in special semi-hidden environment variables of the format
=<drive-letter>:, e.g.=C:. This type of CWD is purely a shell concept, so there's no guarantee that it'll be set or that it'll even be accurate. This is the only reason for theenviron_mapparameter.nullis treated equivalent to the environment variable missing.