DoxigAlpha

resolveWindows

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

Function parameters

Parameters

#
paths:[]const []const u8

Type definitions in this namespace

Types

#

Returns if the given byte is a valid path separator

Functions

#
isSep
Returns if the given byte is a valid path separator
join
Naively combines a series of paths with the native path separator.
joinZ
Naively combines a series of paths with the native path separator and null terminator.
resolve
On Windows, this calls `resolveWindows` and on POSIX it calls `resolvePosix`.
resolveWindows
This function is like a series of `cd` statements executed one after another.
resolvePosix
This function is like a series of `cd` statements executed one after another.
dirname
Strip the last component from a file path.
relative
Returns the relative path from `from` to `to`.
extension
Searches for a file extension separated by a `.` and returns the string after that `.`.
stem
Returns the last component of this path without its extension (if any):
ComponentIterator
A path component iterator that can move forwards and backwards.
fmtAsUtf8Lossy
Format a path encoded as bytes for display as UTF-8.
fmtWtf16LeAsUtf8Lossy
Format a path encoded as WTF-16 LE for display as UTF-8.

= '\\'

Values

#
sep
= switch (native_os) { .windows, .uefi => sep_windows, else => sep_posix, }
sep_str
= switch (native_os) { .windows, .uefi => sep_str_windows, else => sep_str_posix, }
delimiter
= if (native_os == .windows) delimiter_windows else delimiter_posix

Source

Implementation

#
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.array_list.Managed(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;
    }
}