diff options
Diffstat (limited to 'lib/std/process.zig')
| -rw-r--r-- | lib/std/process.zig | 135 |
1 files changed, 111 insertions, 24 deletions
diff --git a/lib/std/process.zig b/lib/std/process.zig index 5007c86736..5bdee4971d 100644 --- a/lib/std/process.zig +++ b/lib/std/process.zig @@ -6,12 +6,12 @@ const math = std.math; const Allocator = mem.Allocator; const assert = std.debug.assert; const testing = std.testing; -const child_process = @import("child_process.zig"); const native_os = builtin.os.tag; const posix = std.posix; const windows = std.os.windows; +const unicode = std.unicode; -pub const Child = child_process.ChildProcess; +pub const Child = @import("process/Child.zig"); pub const abort = posix.abort; pub const exit = posix.exit; pub const changeCurDir = posix.chdir; @@ -86,7 +86,7 @@ pub const EnvMap = struct { _ = self; if (native_os == .windows) { var h = std.hash.Wyhash.init(0); - var it = std.unicode.Wtf8View.initUnchecked(s).iterator(); + var it = unicode.Wtf8View.initUnchecked(s).iterator(); while (it.nextCodepoint()) |cp| { const cp_upper = upcase(cp); h.update(&[_]u8{ @@ -103,8 +103,8 @@ pub const EnvMap = struct { pub fn eql(self: @This(), a: []const u8, b: []const u8) bool { _ = self; if (native_os == .windows) { - var it_a = std.unicode.Wtf8View.initUnchecked(a).iterator(); - var it_b = std.unicode.Wtf8View.initUnchecked(b).iterator(); + var it_a = unicode.Wtf8View.initUnchecked(a).iterator(); + var it_b = unicode.Wtf8View.initUnchecked(b).iterator(); while (true) { const c_a = it_a.nextCodepoint() orelse break; const c_b = it_b.nextCodepoint() orelse return false; @@ -141,7 +141,7 @@ pub const EnvMap = struct { /// If `putMove` fails, the ownership of key and value does not transfer. /// On Windows `key` must be a valid [WTF-8](https://simonsapin.github.io/wtf-8/) string. pub fn putMove(self: *EnvMap, key: []u8, value: []u8) !void { - assert(std.unicode.wtf8ValidateSlice(key)); + assert(unicode.wtf8ValidateSlice(key)); const get_or_put = try self.hash_map.getOrPut(key); if (get_or_put.found_existing) { self.free(get_or_put.key_ptr.*); @@ -154,7 +154,7 @@ pub const EnvMap = struct { /// `key` and `value` are copied into the EnvMap. /// On Windows `key` must be a valid [WTF-8](https://simonsapin.github.io/wtf-8/) string. pub fn put(self: *EnvMap, key: []const u8, value: []const u8) !void { - assert(std.unicode.wtf8ValidateSlice(key)); + assert(unicode.wtf8ValidateSlice(key)); const value_copy = try self.copy(value); errdefer self.free(value_copy); const get_or_put = try self.hash_map.getOrPut(key); @@ -173,7 +173,7 @@ pub const EnvMap = struct { /// The returned pointer is invalidated if the map resizes. /// On Windows `key` must be a valid [WTF-8](https://simonsapin.github.io/wtf-8/) string. pub fn getPtr(self: EnvMap, key: []const u8) ?*[]const u8 { - assert(std.unicode.wtf8ValidateSlice(key)); + assert(unicode.wtf8ValidateSlice(key)); return self.hash_map.getPtr(key); } @@ -182,7 +182,7 @@ pub const EnvMap = struct { /// key is removed from the map. /// On Windows `key` must be a valid [WTF-8](https://simonsapin.github.io/wtf-8/) string. pub fn get(self: EnvMap, key: []const u8) ?[]const u8 { - assert(std.unicode.wtf8ValidateSlice(key)); + assert(unicode.wtf8ValidateSlice(key)); return self.hash_map.get(key); } @@ -190,7 +190,7 @@ pub const EnvMap = struct { /// This invalidates the value returned by get() for this key. /// On Windows `key` must be a valid [WTF-8](https://simonsapin.github.io/wtf-8/) string. pub fn remove(self: *EnvMap, key: []const u8) void { - assert(std.unicode.wtf8ValidateSlice(key)); + assert(unicode.wtf8ValidateSlice(key)); const kv = self.hash_map.fetchRemove(key) orelse return; self.free(kv.key); self.free(kv.value); @@ -260,7 +260,7 @@ test EnvMap { try testing.expectEqualStrings("something else", env.get("кириллица").?); // and WTF-8 that's not valid UTF-8 - const wtf8_with_surrogate_pair = try std.unicode.wtf16LeToWtf8Alloc(testing.allocator, &[_]u16{ + const wtf8_with_surrogate_pair = try unicode.wtf16LeToWtf8Alloc(testing.allocator, &[_]u16{ std.mem.nativeToLittle(u16, 0xD83D), // unpaired high surrogate }); defer testing.allocator.free(wtf8_with_surrogate_pair); @@ -300,7 +300,7 @@ pub fn getEnvMap(allocator: Allocator) GetEnvMapError!EnvMap { while (ptr[i] != 0 and ptr[i] != '=') : (i += 1) {} const key_w = ptr[key_start..i]; - const key = try std.unicode.wtf16LeToWtf8Alloc(allocator, key_w); + const key = try unicode.wtf16LeToWtf8Alloc(allocator, key_w); errdefer allocator.free(key); if (ptr[i] == '=') i += 1; @@ -308,7 +308,7 @@ pub fn getEnvMap(allocator: Allocator) GetEnvMapError!EnvMap { const value_start = i; while (ptr[i] != 0) : (i += 1) {} const value_w = ptr[value_start..i]; - const value = try std.unicode.wtf16LeToWtf8Alloc(allocator, value_w); + const value = try unicode.wtf16LeToWtf8Alloc(allocator, value_w); errdefer allocator.free(value); i += 1; // skip over null byte @@ -401,13 +401,13 @@ pub fn getEnvVarOwned(allocator: Allocator, key: []const u8) GetEnvVarOwnedError const result_w = blk: { var stack_alloc = std.heap.stackFallback(256 * @sizeOf(u16), allocator); const stack_allocator = stack_alloc.get(); - const key_w = try std.unicode.wtf8ToWtf16LeAllocZ(stack_allocator, key); + const key_w = try unicode.wtf8ToWtf16LeAllocZ(stack_allocator, key); defer stack_allocator.free(key_w); break :blk getenvW(key_w) orelse return error.EnvironmentVariableNotFound; }; // wtf16LeToWtf8Alloc can only fail with OutOfMemory - return std.unicode.wtf16LeToWtf8Alloc(allocator, result_w); + return unicode.wtf16LeToWtf8Alloc(allocator, result_w); } else if (native_os == .wasi and !builtin.link_libc) { var envmap = getEnvMap(allocator) catch return error.OutOfMemory; defer envmap.deinit(); @@ -422,7 +422,7 @@ pub fn getEnvVarOwned(allocator: Allocator, key: []const u8) GetEnvVarOwnedError /// On Windows, `key` must be valid UTF-8. pub fn hasEnvVarConstant(comptime key: []const u8) bool { if (native_os == .windows) { - const key_w = comptime std.unicode.utf8ToUtf16LeStringLiteral(key); + const key_w = comptime unicode.utf8ToUtf16LeStringLiteral(key); return getenvW(key_w) != null; } else if (native_os == .wasi and !builtin.link_libc) { @compileError("hasEnvVarConstant is not supported for WASI without libc"); @@ -445,7 +445,7 @@ pub fn hasEnvVar(allocator: Allocator, key: []const u8) HasEnvVarError!bool { if (native_os == .windows) { var stack_alloc = std.heap.stackFallback(256 * @sizeOf(u16), allocator); const stack_allocator = stack_alloc.get(); - const key_w = try std.unicode.wtf8ToWtf16LeAllocZ(stack_allocator, key); + const key_w = try unicode.wtf8ToWtf16LeAllocZ(stack_allocator, key); defer stack_allocator.free(key_w); return getenvW(key_w) != null; } else if (native_os == .wasi and !builtin.link_libc) { @@ -659,7 +659,7 @@ pub const ArgIteratorWindows = struct { /// The iterator makes a copy of `cmd_line_w` converted WTF-8 and keeps it; it does *not* take /// ownership of `cmd_line_w`. pub fn init(allocator: Allocator, cmd_line_w: [*:0]const u16) InitError!ArgIteratorWindows { - const cmd_line = try std.unicode.wtf16LeToWtf8Alloc(allocator, mem.sliceTo(cmd_line_w, 0)); + const cmd_line = try unicode.wtf16LeToWtf8Alloc(allocator, mem.sliceTo(cmd_line_w, 0)); errdefer allocator.free(cmd_line); const buffer = try allocator.alloc(u8, cmd_line.len + 1); @@ -728,17 +728,17 @@ pub const ArgIteratorWindows = struct { // must be 6 bytes for a surrogate pair to exist. if (self.end - self.start >= 6) { const window = self.buffer[self.end - 6 .. self.end]; - const view = std.unicode.Wtf8View.init(window) catch return; + const view = unicode.Wtf8View.init(window) catch return; var it = view.iterator(); var pair: [2]u16 = undefined; pair[0] = std.mem.nativeToLittle(u16, std.math.cast(u16, it.nextCodepoint().?) orelse return); - if (!std.unicode.utf16IsHighSurrogate(std.mem.littleToNative(u16, pair[0]))) return; + if (!unicode.utf16IsHighSurrogate(std.mem.littleToNative(u16, pair[0]))) return; pair[1] = std.mem.nativeToLittle(u16, std.math.cast(u16, it.nextCodepoint().?) orelse return); - if (!std.unicode.utf16IsLowSurrogate(std.mem.littleToNative(u16, pair[1]))) return; + if (!unicode.utf16IsLowSurrogate(std.mem.littleToNative(u16, pair[1]))) return; // We know we have a valid surrogate pair, so convert // it to UTF-8, overwriting the surrogate pair's bytes // and then chop off the extra bytes. - const len = std.unicode.utf16LeToUtf8(window, &pair) catch unreachable; + const len = unicode.utf16LeToUtf8(window, &pair) catch unreachable; const delta = 6 - len; self.end -= delta; } @@ -1341,7 +1341,7 @@ test ArgIteratorWindows { } fn testArgIteratorWindows(cmd_line: []const u8, expected_args: []const []const u8) !void { - const cmd_line_w = try std.unicode.wtf8ToWtf16LeAllocZ(testing.allocator, cmd_line); + const cmd_line_w = try unicode.wtf8ToWtf16LeAllocZ(testing.allocator, cmd_line); defer testing.allocator.free(cmd_line_w); // next @@ -1642,7 +1642,7 @@ pub fn execve( const envp = m: { if (env_map) |m| { - const envp_buf = try child_process.createNullDelimitedEnvMap(arena, m); + const envp_buf = try createNullDelimitedEnvMap(arena, m); break :m envp_buf.ptr; } else if (builtin.link_libc) { break :m std.c.environ; @@ -1789,3 +1789,90 @@ pub fn raiseFileDescriptorLimit() void { test raiseFileDescriptorLimit { raiseFileDescriptorLimit(); } + +pub fn createNullDelimitedEnvMap(arena: mem.Allocator, env_map: *const EnvMap) ![:null]?[*:0]u8 { + const envp_count = env_map.count(); + const envp_buf = try arena.allocSentinel(?[*:0]u8, envp_count, null); + { + var it = env_map.iterator(); + var i: usize = 0; + while (it.next()) |pair| : (i += 1) { + const env_buf = try arena.allocSentinel(u8, pair.key_ptr.len + pair.value_ptr.len + 1, 0); + @memcpy(env_buf[0..pair.key_ptr.len], pair.key_ptr.*); + env_buf[pair.key_ptr.len] = '='; + @memcpy(env_buf[pair.key_ptr.len + 1 ..][0..pair.value_ptr.len], pair.value_ptr.*); + envp_buf[i] = env_buf.ptr; + } + assert(i == envp_count); + } + return envp_buf; +} + +test createNullDelimitedEnvMap { + const allocator = testing.allocator; + var envmap = EnvMap.init(allocator); + defer envmap.deinit(); + + try envmap.put("HOME", "/home/ifreund"); + try envmap.put("WAYLAND_DISPLAY", "wayland-1"); + try envmap.put("DISPLAY", ":1"); + try envmap.put("DEBUGINFOD_URLS", " "); + try envmap.put("XCURSOR_SIZE", "24"); + + var arena = std.heap.ArenaAllocator.init(allocator); + defer arena.deinit(); + const environ = try createNullDelimitedEnvMap(arena.allocator(), &envmap); + + try testing.expectEqual(@as(usize, 5), environ.len); + + inline for (.{ + "HOME=/home/ifreund", + "WAYLAND_DISPLAY=wayland-1", + "DISPLAY=:1", + "DEBUGINFOD_URLS= ", + "XCURSOR_SIZE=24", + }) |target| { + for (environ) |variable| { + if (mem.eql(u8, mem.span(variable orelse continue), target)) break; + } else { + try testing.expect(false); // Environment variable not found + } + } +} + +/// Caller must free result. +pub fn createWindowsEnvBlock(allocator: mem.Allocator, env_map: *const EnvMap) ![]u16 { + // count bytes needed + const max_chars_needed = x: { + var max_chars_needed: usize = 4; // 4 for the final 4 null bytes + var it = env_map.iterator(); + while (it.next()) |pair| { + // +1 for '=' + // +1 for null byte + max_chars_needed += pair.key_ptr.len + pair.value_ptr.len + 2; + } + break :x max_chars_needed; + }; + const result = try allocator.alloc(u16, max_chars_needed); + errdefer allocator.free(result); + + var it = env_map.iterator(); + var i: usize = 0; + while (it.next()) |pair| { + i += try unicode.wtf8ToWtf16Le(result[i..], pair.key_ptr.*); + result[i] = '='; + i += 1; + i += try unicode.wtf8ToWtf16Le(result[i..], pair.value_ptr.*); + result[i] = 0; + i += 1; + } + result[i] = 0; + i += 1; + result[i] = 0; + i += 1; + result[i] = 0; + i += 1; + result[i] = 0; + i += 1; + return try allocator.realloc(result, i); +} |
