Function resolveWindows [src]
This function is like a series of cd statements executed one after another.
It resolves "." and "..", but will not convert relative path to absolute path, use std.fs.Dir.realpath instead.
The result does not have a trailing path separator.
Each drive has its own current working directory.
Path separators are canonicalized to '\' and drives are canonicalized to capital letters.
Note: all usage of this function should be audited due to the existence of symlinks.
Without performing actual syscalls, resolving .. could be incorrect.
This API may break in the future: https://github.com/ziglang/zig/issues/13613
Prototype
pub fn resolveWindows(allocator: Allocator, paths: []const []const u8) ![]u8
Parameters
allocator: Allocator
paths: []const []const u8
Example
test resolveWindows {
try testResolveWindows(
&[_][]const u8{ "Z:\\", "/usr/local", "lib\\zig\\std\\array_list.zig" },
"Z:\\usr\\local\\lib\\zig\\std\\array_list.zig",
);
try testResolveWindows(
&[_][]const u8{ "z:\\", "usr/local", "lib\\zig" },
"Z:\\usr\\local\\lib\\zig",
);
try testResolveWindows(&[_][]const u8{ "c:\\a\\b\\c", "/hi", "ok" }, "C:\\hi\\ok");
try testResolveWindows(&[_][]const u8{ "c:/blah\\blah", "d:/games", "c:../a" }, "C:\\blah\\a");
try testResolveWindows(&[_][]const u8{ "c:/blah\\blah", "d:/games", "C:../a" }, "C:\\blah\\a");
try testResolveWindows(&[_][]const u8{ "c:/ignore", "d:\\a/b\\c/d", "\\e.exe" }, "D:\\e.exe");
try testResolveWindows(&[_][]const u8{ "c:/ignore", "c:/some/file" }, "C:\\some\\file");
try testResolveWindows(&[_][]const u8{ "d:/ignore", "d:some/dir//" }, "D:\\ignore\\some\\dir");
try testResolveWindows(&[_][]const u8{ "//server/share", "..", "relative\\" }, "\\\\server\\share\\relative");
try testResolveWindows(&[_][]const u8{ "c:/", "//" }, "C:\\");
try testResolveWindows(&[_][]const u8{ "c:/", "//dir" }, "C:\\dir");
try testResolveWindows(&[_][]const u8{ "c:/", "//server/share" }, "\\\\server\\share\\");
try testResolveWindows(&[_][]const u8{ "c:/", "//server//share" }, "\\\\server\\share\\");
try testResolveWindows(&[_][]const u8{ "c:/", "///some//dir" }, "C:\\some\\dir");
try testResolveWindows(&[_][]const u8{ "C:\\foo\\tmp.3\\", "..\\tmp.3\\cycles\\root.js" }, "C:\\foo\\tmp.3\\cycles\\root.js");
// Keep relative paths relative.
try testResolveWindows(&[_][]const u8{"a/b"}, "a\\b");
}
Source
pub fn resolveWindows(allocator: Allocator, paths: []const []const u8) ![]u8 {
assert(paths.len > 0);
// determine which disk designator we will result with, if any
var result_drive_buf = "_:".*;
var disk_designator: []const u8 = "";
var drive_kind = WindowsPath.Kind.None;
var have_abs_path = false;
var first_index: usize = 0;
for (paths, 0..) |p, i| {
const parsed = windowsParsePath(p);
if (parsed.is_abs) {
have_abs_path = true;
first_index = i;
}
switch (parsed.kind) {
.Drive => {
result_drive_buf[0] = ascii.toUpper(parsed.disk_designator[0]);
disk_designator = result_drive_buf[0..];
drive_kind = WindowsPath.Kind.Drive;
},
.NetworkShare => {
disk_designator = parsed.disk_designator;
drive_kind = WindowsPath.Kind.NetworkShare;
},
.None => {},
}
}
// if we will result with a disk designator, loop again to determine
// which is the last time the disk designator is absolutely specified, if any
// and count up the max bytes for paths related to this disk designator
if (drive_kind != WindowsPath.Kind.None) {
have_abs_path = false;
first_index = 0;
var correct_disk_designator = false;
for (paths, 0..) |p, i| {
const parsed = windowsParsePath(p);
if (parsed.kind != WindowsPath.Kind.None) {
if (parsed.kind == drive_kind) {
correct_disk_designator = compareDiskDesignators(drive_kind, disk_designator, parsed.disk_designator);
} else {
continue;
}
}
if (!correct_disk_designator) {
continue;
}
if (parsed.is_abs) {
first_index = i;
have_abs_path = true;
}
}
}
// Allocate result and fill in the disk designator.
var result = std.ArrayList(u8).init(allocator);
defer result.deinit();
const disk_designator_len: usize = l: {
if (!have_abs_path) break :l 0;
switch (drive_kind) {
.Drive => {
try result.appendSlice(disk_designator);
break :l disk_designator.len;
},
.NetworkShare => {
var it = mem.tokenizeAny(u8, paths[first_index], "/\\");
const server_name = it.next().?;
const other_name = it.next().?;
try result.ensureUnusedCapacity(2 + 1 + server_name.len + other_name.len);
result.appendSliceAssumeCapacity("\\\\");
result.appendSliceAssumeCapacity(server_name);
result.appendAssumeCapacity('\\');
result.appendSliceAssumeCapacity(other_name);
break :l result.items.len;
},
.None => {
break :l 1;
},
}
};
var correct_disk_designator = true;
var negative_count: usize = 0;
for (paths[first_index..]) |p| {
const parsed = windowsParsePath(p);
if (parsed.kind != .None) {
if (parsed.kind == drive_kind) {
const dd = result.items[0..disk_designator_len];
correct_disk_designator = compareDiskDesignators(drive_kind, dd, parsed.disk_designator);
} else {
continue;
}
}
if (!correct_disk_designator) {
continue;
}
var it = mem.tokenizeAny(u8, p[parsed.disk_designator.len..], "/\\");
while (it.next()) |component| {
if (mem.eql(u8, component, ".")) {
continue;
} else if (mem.eql(u8, component, "..")) {
if (result.items.len == 0) {
negative_count += 1;
continue;
}
while (true) {
if (result.items.len == disk_designator_len) {
break;
}
const end_with_sep = switch (result.items[result.items.len - 1]) {
'\\', '/' => true,
else => false,
};
result.items.len -= 1;
if (end_with_sep or result.items.len == 0) break;
}
} else if (!have_abs_path and result.items.len == 0) {
try result.appendSlice(component);
} else {
try result.ensureUnusedCapacity(1 + component.len);
result.appendAssumeCapacity('\\');
result.appendSliceAssumeCapacity(component);
}
}
}
if (disk_designator_len != 0 and result.items.len == disk_designator_len) {
try result.append('\\');
return result.toOwnedSlice();
}
if (result.items.len == 0) {
if (negative_count == 0) {
return allocator.dupe(u8, ".");
} else {
const real_result = try allocator.alloc(u8, 3 * negative_count - 1);
var count = negative_count - 1;
var i: usize = 0;
while (count > 0) : (count -= 1) {
real_result[i..][0..3].* = "..\\".*;
i += 3;
}
real_result[i..][0..2].* = "..".*;
return real_result;
}
}
if (negative_count == 0) {
return result.toOwnedSlice();
} else {
const real_result = try allocator.alloc(u8, 3 * negative_count + result.items.len);
var count = negative_count;
var i: usize = 0;
while (count > 0) : (count -= 1) {
real_result[i..][0..3].* = "..\\".*;
i += 3;
}
@memcpy(real_result[i..][0..result.items.len], result.items);
return real_result;
}
}