DoxigAlpha

joinSepMaybeZ

This is different from mem.join in that the separator will not be repeated if it is found at the end or beginning of a pair of consecutive paths.

Function parameters

Parameters

#
separator:u8
sepPredicate:fn (u8) bool
paths:[]const []const u8
zero:bool

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

#
fn joinSepMaybeZ(allocator: Allocator, separator: u8, comptime sepPredicate: fn (u8) bool, paths: []const []const u8, zero: bool) ![]u8 {
    if (paths.len == 0) return if (zero) try allocator.dupe(u8, &[1]u8{0}) else &[0]u8{};

    // Find first non-empty path index.
    const first_path_index = blk: {
        for (paths, 0..) |path, index| {
            if (path.len == 0) continue else break :blk index;
        }

        // All paths provided were empty, so return early.
        return if (zero) try allocator.dupe(u8, &[1]u8{0}) else &[0]u8{};
    };

    // Calculate length needed for resulting joined path buffer.
    const total_len = blk: {
        var sum: usize = paths[first_path_index].len;
        var prev_path = paths[first_path_index];
        assert(prev_path.len > 0);
        var i: usize = first_path_index + 1;
        while (i < paths.len) : (i += 1) {
            const this_path = paths[i];
            if (this_path.len == 0) continue;
            const prev_sep = sepPredicate(prev_path[prev_path.len - 1]);
            const this_sep = sepPredicate(this_path[0]);
            sum += @intFromBool(!prev_sep and !this_sep);
            sum += if (prev_sep and this_sep) this_path.len - 1 else this_path.len;
            prev_path = this_path;
        }

        if (zero) sum += 1;
        break :blk sum;
    };

    const buf = try allocator.alloc(u8, total_len);
    errdefer allocator.free(buf);

    @memcpy(buf[0..paths[first_path_index].len], paths[first_path_index]);
    var buf_index: usize = paths[first_path_index].len;
    var prev_path = paths[first_path_index];
    assert(prev_path.len > 0);
    var i: usize = first_path_index + 1;
    while (i < paths.len) : (i += 1) {
        const this_path = paths[i];
        if (this_path.len == 0) continue;
        const prev_sep = sepPredicate(prev_path[prev_path.len - 1]);
        const this_sep = sepPredicate(this_path[0]);
        if (!prev_sep and !this_sep) {
            buf[buf_index] = separator;
            buf_index += 1;
        }
        const adjusted_path = if (prev_sep and this_sep) this_path[1..] else this_path;
        @memcpy(buf[buf_index..][0..adjusted_path.len], adjusted_path);
        buf_index += adjusted_path.len;
        prev_path = this_path;
    }

    if (zero) buf[buf.len - 1] = 0;

    // No need for shrink since buf is exactly the correct size.
    return buf;
}