From 936d0b18b116ea126893d2f165f9be72f8bef845 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 22 Feb 2020 15:59:13 -0500 Subject: [PATCH 1/4] update std lib to integrate with libc for environ closes #3511 --- lib/std/c.zig | 2 ++ lib/std/os.zig | 49 ++++++++++++++++++++++++++++++++++++++++----- lib/std/process.zig | 31 +++++++++++++++++++--------- lib/std/start.zig | 10 +++++++-- src/os.cpp | 6 +----- 5 files changed, 77 insertions(+), 21 deletions(-) diff --git a/lib/std/c.zig b/lib/std/c.zig index 93c6c246641f6b5183f14fb450ed6613e31a40cd..f6c0e07dbd94c8421343dac74c6ce89c45b15034 100644 --- a/lib/std/c.zig +++ b/lib/std/c.zig @@ -62,6 +62,8 @@ pub fn versionCheck(glibc_version: builtin.Version) type { }; } +pub extern "c" var environ: [*:null]?[*:0]u8; + pub extern "c" fn fopen(filename: [*:0]const u8, modes: [*:0]const u8) ?*FILE; pub extern "c" fn fclose(stream: *FILE) c_int; pub extern "c" fn fwrite(ptr: [*]const u8, size_of_type: usize, item_count: usize, stream: *FILE) usize; diff --git a/lib/std/os.zig b/lib/std/os.zig index 766b678c46b704929806919053f1155f62a944fb..a4f148ae00c2be5506589dcaa962371820799069 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -70,6 +70,8 @@ else switch (builtin.os) { pub usingnamespace @import("os/bits.zig"); /// See also `getenv`. Populated by startup code before main(). +/// TODO this is a footgun because the value will be undefined when using `zig build-lib`. +/// https://github.com/ziglang/zig/issues/4524 pub var environ: [][*:0]u8 = undefined; /// Populated by startup code before main(). @@ -922,7 +924,11 @@ pub const execveC = execveZ; /// Like `execve` except the parameters are null-terminated, /// matching the syscall API on all targets. This removes the need for an allocator. /// This function ignores PATH environment variable. See `execvpeZ` for that. -pub fn execveZ(path: [*:0]const u8, child_argv: [*:null]const ?[*:0]const u8, envp: [*:null]const ?[*:0]const u8) ExecveError { +pub fn execveZ( + path: [*:0]const u8, + child_argv: [*:null]const ?[*:0]const u8, + envp: [*:null]const ?[*:0]const u8, +) ExecveError { switch (errno(system.execve(path, child_argv, envp))) { 0 => unreachable, EFAULT => unreachable, @@ -966,7 +972,7 @@ pub fn execvpeZ_expandArg0( envp: [*:null]const ?[*:0]const u8, ) ExecveError { const file_slice = mem.toSliceConst(u8, file); - if (mem.indexOfScalar(u8, file_slice, '/') != null) return execveC(file, child_argv, envp); + if (mem.indexOfScalar(u8, file_slice, '/') != null) return execveZ(file, child_argv, envp); const PATH = getenvZ("PATH") orelse "/usr/local/bin:/bin/:/usr/bin"; var path_buf: [MAX_PATH_BYTES]u8 = undefined; @@ -993,7 +999,7 @@ pub fn execvpeZ_expandArg0( .expand => child_argv[0] = full_path, .no_expand => {}, } - err = execveC(full_path, child_argv, envp); + err = execveZ(full_path, child_argv, envp); switch (err) { error.AccessDenied => seen_eacces = true, error.FileNotFound, error.NotDir => {}, @@ -1007,7 +1013,7 @@ pub fn execvpeZ_expandArg0( /// Like `execvpe` except the parameters are null-terminated, /// matching the syscall API on all targets. This removes the need for an allocator. /// This function also uses the PATH environment variable to get the full path to the executable. -/// If `file` is an absolute path, this is the same as `execveC`. +/// If `file` is an absolute path, this is the same as `execveZ`. pub fn execvpeZ( file: [*:0]const u8, argv: [*:null]const ?[*:0]const u8, @@ -1097,8 +1103,37 @@ pub fn freeNullDelimitedEnvMap(allocator: *mem.Allocator, envp_buf: []?[*:0]u8) /// Get an environment variable. /// See also `getenvZ`. -/// TODO make this go through libc when we have it pub fn getenv(key: []const u8) ?[]const u8 { + if (builtin.os == .windows) { + // TODO update this to use the ProcessEnvironmentBlock + @compileError("TODO implement std.os.getenv for Windows"); + } + if (builtin.link_libc) { + var small_key_buf: [64]u8 = undefined; + if (key.len < small_key_buf.len) { + mem.copy(u8, &small_key_buf, key); + small_key_buf[key.len] = 0; + const key0 = small_key_buf[0..key.len :0]; + return getenvZ(key0); + } + // Search the entire `environ` because we don't have a null terminated pointer. + var ptr = std.c.environ; + while (ptr.*) |line| : (ptr += 1) { + var line_i: usize = 0; + while (line[line_i] != 0 and line[line_i] != '=') : (line_i += 1) {} + const this_key = line[0..line_i]; + + if (!mem.eql(u8, this_key, key)) continue; + + var end_i: usize = line_i; + while (line[end_i] != 0) : (end_i += 1) {} + const value = line[line_i + 1 .. end_i]; + + return value; + } + return null; + } + // TODO see https://github.com/ziglang/zig/issues/4524 for (environ) |ptr| { var line_i: usize = 0; while (ptr[line_i] != 0 and ptr[line_i] != '=') : (line_i += 1) {} @@ -1120,6 +1155,10 @@ pub const getenvC = getenvZ; /// Get an environment variable with a null-terminated name. /// See also `getenv`. pub fn getenvZ(key: [*:0]const u8) ?[]const u8 { + if (builtin.os == .windows) { + // TODO update this to use the ProcessEnvironmentBlock + @compileError("TODO implement std.os.getenv for Windows"); + } if (builtin.link_libc) { const value = system.getenv(key) orelse return null; return mem.toSliceConst(u8, value); diff --git a/lib/std/process.zig b/lib/std/process.zig index 89307b44da66d49f57bb347ec7c83f64d1ba44cd..ba124ff262a2bfafab3a5ed8ddbdf54d4026ab26 100644 --- a/lib/std/process.zig +++ b/lib/std/process.zig @@ -1,5 +1,5 @@ -const builtin = @import("builtin"); const std = @import("std.zig"); +const builtin = std.builtin; const os = std.os; const fs = std.fs; const BufMap = std.BufMap; @@ -31,13 +31,13 @@ test "getCwdAlloc" { testing.allocator.free(cwd); } -/// Caller must free result when done. -/// TODO make this go through libc when we have it +/// Caller owns resulting `BufMap`. pub fn getEnvMap(allocator: *Allocator) !BufMap { var result = BufMap.init(allocator); errdefer result.deinit(); if (builtin.os == .windows) { + // TODO update this to use the ProcessEnvironmentBlock const ptr = try os.windows.GetEnvironmentStringsW(); defer os.windows.FreeEnvironmentStringsW(ptr); @@ -95,15 +95,29 @@ pub fn getEnvMap(allocator: *Allocator) !BufMap { } } return result; + } else if (builtin.link_libc) { + var ptr = std.c.environ; + while (ptr.*) |line| : (ptr += 1) { + var line_i: usize = 0; + while (line[line_i] != 0 and line[line_i] != '=') : (line_i += 1) {} + const key = line[0..line_i]; + + var end_i: usize = line_i; + while (line[end_i] != 0) : (end_i += 1) {} + const value = line[line_i + 1 .. end_i]; + + try result.set(key, value); + } + return result; } else { - for (os.environ) |ptr| { + for (os.environ) |line| { var line_i: usize = 0; - while (ptr[line_i] != 0 and ptr[line_i] != '=') : (line_i += 1) {} - const key = ptr[0..line_i]; + while (line[line_i] != 0 and line[line_i] != '=') : (line_i += 1) {} + const key = line[0..line_i]; var end_i: usize = line_i; - while (ptr[end_i] != 0) : (end_i += 1) {} - const value = ptr[line_i + 1 .. end_i]; + while (line[end_i] != 0) : (end_i += 1) {} + const value = line[line_i + 1 .. end_i]; try result.set(key, value); } @@ -125,7 +139,6 @@ pub const GetEnvVarOwnedError = error{ }; /// Caller must free returned memory. -/// TODO make this go through libc when we have it pub fn getEnvVarOwned(allocator: *mem.Allocator, key: []const u8) GetEnvVarOwnedError![]u8 { if (builtin.os == .windows) { const key_with_null = try std.unicode.utf8ToUtf16LeWithNull(allocator, key); diff --git a/lib/std/start.zig b/lib/std/start.zig index e6c21ba9b8af4c7a09f27957983d200f33731fd5..b58b6e8144354e475e8e83bcdc12c94b414a8f18 100644 --- a/lib/std/start.zig +++ b/lib/std/start.zig @@ -21,7 +21,9 @@ comptime { @export(main, .{ .name = "main", .linkage = .Weak }); } } else if (builtin.os == .windows) { - if (!@hasDecl(root, "WinMain") and !@hasDecl(root, "WinMainCRTStartup") and !@hasDecl(root, "wWinMain") and !@hasDecl(root, "wWinMainCRTStartup")) { + if (!@hasDecl(root, "WinMain") and !@hasDecl(root, "WinMainCRTStartup") and + !@hasDecl(root, "wWinMain") and !@hasDecl(root, "wWinMainCRTStartup")) + { @export(WinMainCRTStartup, .{ .name = "WinMainCRTStartup" }); } } else if (builtin.os == .uefi) { @@ -34,7 +36,11 @@ comptime { } } -fn _DllMainCRTStartup(hinstDLL: std.os.windows.HINSTANCE, fdwReason: std.os.windows.DWORD, lpReserved: std.os.windows.LPVOID) callconv(.Stdcall) std.os.windows.BOOL { +fn _DllMainCRTStartup( + hinstDLL: std.os.windows.HINSTANCE, + fdwReason: std.os.windows.DWORD, + lpReserved: std.os.windows.LPVOID, +) callconv(.Stdcall) std.os.windows.BOOL { if (@hasDecl(root, "DllMain")) { return root.DllMain(hinstDLL, fdwReason, lpReserved); } diff --git a/src/os.cpp b/src/os.cpp index 268e8120502d10a67d119ec91e17f26a4944acdf..8e0bcc433b89622772dc0d3ce5494c1d31623344 100644 --- a/src/os.cpp +++ b/src/os.cpp @@ -81,11 +81,7 @@ static clock_serv_t macos_monotonic_clock; #include #include -// Apple doesn't provide the environ global variable -#if defined(__APPLE__) && !defined(environ) -#include -#define environ (*_NSGetEnviron()) -#elif defined(ZIG_OS_FREEBSD) || defined(ZIG_OS_NETBSD) || defined(ZIG_OS_DRAGONFLY) +#if !defined(environ) extern char **environ; #endif -- 2.54.0 From cfffb9c5e96eeeae43cd724e2d02ec8c2b7714e0 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 22 Feb 2020 17:35:36 -0500 Subject: [PATCH 2/4] improve handling of environment variables on Windows std.os.getenv and std.os.getenvZ have nice compile errors when not linking libc and using Windows. std.os.getenvW is provided as a Windows-only API that does not require an allocator. It uses the Process Environment Block. std.process.getEnvVarOwned is improved to be a simple wrapper on top of std.os.getenvW. std.process.getEnvMap is improved to use the Process Environment Block rather than calling GetEnvironmentVariableW. std.zig.system.NativePaths uses process.getEnvVarOwned instead of std.os.getenvZ, which works on Windows as well as POSIX. --- lib/std/os.zig | 42 ++++++++++++++++++++----- lib/std/os/test.zig | 8 +++++ lib/std/os/windows/bits.zig | 2 +- lib/std/process.zig | 46 +++++++++------------------- lib/std/zig/system.zig | 61 ++++++++++++++++++++++--------------- 5 files changed, 93 insertions(+), 66 deletions(-) diff --git a/lib/std/os.zig b/lib/std/os.zig index a4f148ae00c2be5506589dcaa962371820799069..cdb48a05a5cee1602cf8591b8817dbf2374a9931 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -1104,10 +1104,6 @@ pub fn freeNullDelimitedEnvMap(allocator: *mem.Allocator, envp_buf: []?[*:0]u8) /// Get an environment variable. /// See also `getenvZ`. pub fn getenv(key: []const u8) ?[]const u8 { - if (builtin.os == .windows) { - // TODO update this to use the ProcessEnvironmentBlock - @compileError("TODO implement std.os.getenv for Windows"); - } if (builtin.link_libc) { var small_key_buf: [64]u8 = undefined; if (key.len < small_key_buf.len) { @@ -1133,6 +1129,9 @@ pub fn getenv(key: []const u8) ?[]const u8 { } return null; } + if (builtin.os == .windows) { + @compileError("std.os.getenv is unavailable for Windows because environment string is in WTF-16 format. See std.process.getEnvVarOwned for cross-platform API or std.os.getenvW for Windows-specific API."); + } // TODO see https://github.com/ziglang/zig/issues/4524 for (environ) |ptr| { var line_i: usize = 0; @@ -1155,17 +1154,44 @@ pub const getenvC = getenvZ; /// Get an environment variable with a null-terminated name. /// See also `getenv`. pub fn getenvZ(key: [*:0]const u8) ?[]const u8 { - if (builtin.os == .windows) { - // TODO update this to use the ProcessEnvironmentBlock - @compileError("TODO implement std.os.getenv for Windows"); - } if (builtin.link_libc) { const value = system.getenv(key) orelse return null; return mem.toSliceConst(u8, value); } + if (builtin.os == .windows) { + @compileError("std.os.getenvZ is unavailable for Windows because environment string is in WTF-16 format. See std.process.getEnvVarOwned for cross-platform API or std.os.getenvW for Windows-specific API."); + } return getenv(mem.toSliceConst(u8, key)); } +/// Windows-only. Get an environment variable with a null-terminated, WTF-16 encoded name. +/// See also `getenv`. +pub fn getenvW(key: [*:0]const u16) ?[:0]const u16 { + if (builtin.os != .windows) { + @compileError("std.os.getenvW is a Windows-only API"); + } + const key_slice = mem.toSliceConst(u16, key); + const ptr = windows.peb().ProcessParameters.Environment; + var i: usize = 0; + while (ptr[i] != 0) { + const key_start = i; + + while (ptr[i] != 0 and ptr[i] != '=') : (i += 1) {} + const this_key = ptr[key_start..i]; + + if (ptr[i] == '=') i += 1; + + const value_start = i; + while (ptr[i] != 0) : (i += 1) {} + const this_value = ptr[value_start..i :0]; + + if (mem.eql(u16, key_slice, this_key)) return this_value; + + i += 1; // skip over null byte + } + return null; +} + pub const GetCwdError = error{ NameTooLong, CurrentWorkingDirectoryUnlinked, diff --git a/lib/std/os/test.zig b/lib/std/os/test.zig index 055d049f69a19ca9088c0e249cdd7af9ecf3b2d6..488a557ed6e4ac92b0a45ad1ef9a084a2f7758db 100644 --- a/lib/std/os/test.zig +++ b/lib/std/os/test.zig @@ -351,3 +351,11 @@ test "mmap" { try fs.cwd().deleteFile(test_out_file); } + +test "getenv" { + if (builtin.os == .windows) { + expect(os.getenvW(&[_:0]u16{ 'B', 'O', 'G', 'U', 'S', 0x11, 0x22, 0x33, 0x44, 0x55 }) == null); + } else { + expect(os.getenvZ("BOGUSDOESNOTEXISTENVVAR") == null); + } +} diff --git a/lib/std/os/windows/bits.zig b/lib/std/os/windows/bits.zig index 265d3439e2d0d284fc49f23a77e18f5f7a81650d..4e153d40edb5e73c419d2a742a3106f4012dda10 100644 --- a/lib/std/os/windows/bits.zig +++ b/lib/std/os/windows/bits.zig @@ -1187,7 +1187,7 @@ pub const RTL_USER_PROCESS_PARAMETERS = extern struct { DllPath: UNICODE_STRING, ImagePathName: UNICODE_STRING, CommandLine: UNICODE_STRING, - Environment: [*]WCHAR, + Environment: [*:0]WCHAR, dwX: ULONG, dwY: ULONG, dwXSize: ULONG, diff --git a/lib/std/process.zig b/lib/std/process.zig index ba124ff262a2bfafab3a5ed8ddbdf54d4026ab26..ac75eff7250ef3817d0b37485e1ebe1e31995ada 100644 --- a/lib/std/process.zig +++ b/lib/std/process.zig @@ -37,14 +37,11 @@ pub fn getEnvMap(allocator: *Allocator) !BufMap { errdefer result.deinit(); if (builtin.os == .windows) { - // TODO update this to use the ProcessEnvironmentBlock - const ptr = try os.windows.GetEnvironmentStringsW(); + const ptr = windows.peb().ProcessParameters.Environment; defer os.windows.FreeEnvironmentStringsW(ptr); var i: usize = 0; - while (true) { - if (ptr[i] == 0) return result; - + while (ptr[i] != 0) { const key_start = i; while (ptr[i] != 0 and ptr[i] != '=') : (i += 1) {} @@ -64,6 +61,7 @@ pub fn getEnvMap(allocator: *Allocator) !BufMap { try result.setMove(key, value); } + return result; } else if (builtin.os == .wasi) { var environ_count: usize = undefined; var environ_buf_size: usize = undefined; @@ -141,34 +139,18 @@ pub const GetEnvVarOwnedError = error{ /// Caller must free returned memory. pub fn getEnvVarOwned(allocator: *mem.Allocator, key: []const u8) GetEnvVarOwnedError![]u8 { if (builtin.os == .windows) { - const key_with_null = try std.unicode.utf8ToUtf16LeWithNull(allocator, key); - defer allocator.free(key_with_null); + const result_w = blk: { + const key_w = try std.unicode.utf8ToUtf16LeWithNull(allocator, key); + defer allocator.free(key_w); - var buf = try allocator.alloc(u16, 256); - defer allocator.free(buf); - - while (true) { - const windows_buf_len = math.cast(os.windows.DWORD, buf.len) catch return error.OutOfMemory; - const result = os.windows.GetEnvironmentVariableW( - key_with_null.ptr, - buf.ptr, - windows_buf_len, - ) catch |err| switch (err) { - error.Unexpected => return error.EnvironmentVariableNotFound, - else => |e| return e, - }; - if (result > buf.len) { - buf = try allocator.realloc(buf, result); - continue; - } - - return std.unicode.utf16leToUtf8Alloc(allocator, buf[0..result]) catch |err| switch (err) { - error.DanglingSurrogateHalf => return error.InvalidUtf8, - error.ExpectedSecondSurrogateHalf => return error.InvalidUtf8, - error.UnexpectedSecondSurrogateHalf => return error.InvalidUtf8, - else => |e| return e, - }; - } + break :blk std.os.getenvW(key_w) orelse return error.EnvironmentVariableNotFound; + }; + return std.unicode.utf16leToUtf8Alloc(allocator, result_w) catch |err| switch (err) { + error.DanglingSurrogateHalf => return error.InvalidUtf8, + error.ExpectedSecondSurrogateHalf => return error.InvalidUtf8, + error.UnexpectedSecondSurrogateHalf => return error.InvalidUtf8, + else => |e| return e, + }; } else { const result = os.getenv(key) orelse return error.EnvironmentVariableNotFound; return mem.dupe(allocator, u8, result); diff --git a/lib/std/zig/system.zig b/lib/std/zig/system.zig index 74a9ffdc70d43a1718833f5a5ddec1b66f421589..3931e362c6c3270528f2a15c1cac71af44d8f4a8 100644 --- a/lib/std/zig/system.zig +++ b/lib/std/zig/system.zig @@ -5,6 +5,8 @@ const ArrayList = std.ArrayList; const assert = std.debug.assert; const process = std.process; +const is_windows = std.Target.current.isWindows(); + pub const NativePaths = struct { include_dirs: ArrayList([:0]u8), lib_dirs: ArrayList([:0]u8), @@ -21,7 +23,9 @@ pub const NativePaths = struct { errdefer self.deinit(); var is_nix = false; - if (std.os.getenvZ("NIX_CFLAGS_COMPILE")) |nix_cflags_compile| { + if (process.getEnvVarOwned(allocator, "NIX_CFLAGS_COMPILE")) |nix_cflags_compile| { + defer allocator.free(nix_cflags_compile); + is_nix = true; var it = mem.tokenize(nix_cflags_compile, " "); while (true) { @@ -37,8 +41,14 @@ pub const NativePaths = struct { break; } } + } else |err| switch (err) { + error.InvalidUtf8 => {}, + error.EnvironmentVariableNotFound => {}, + error.OutOfMemory => |e| return e, } - if (std.os.getenvZ("NIX_LDFLAGS")) |nix_ldflags| { + if (process.getEnvVarOwned(allocator, "NIX_LDFLAGS")) |nix_ldflags| { + defer allocator.free(nix_ldflags); + is_nix = true; var it = mem.tokenize(nix_ldflags, " "); while (true) { @@ -57,39 +67,40 @@ pub const NativePaths = struct { break; } } + } else |err| switch (err) { + error.InvalidUtf8 => {}, + error.EnvironmentVariableNotFound => {}, + error.OutOfMemory => |e| return e, } if (is_nix) { return self; } - switch (std.builtin.os) { - .windows => {}, - else => { - const triple = try std.Target.current.linuxTriple(allocator); + if (!is_windows) { + const triple = try std.Target.current.linuxTriple(allocator); - // TODO: $ ld --verbose | grep SEARCH_DIR - // the output contains some paths that end with lib64, maybe include them too? - // TODO: what is the best possible order of things? - // TODO: some of these are suspect and should only be added on some systems. audit needed. + // TODO: $ ld --verbose | grep SEARCH_DIR + // the output contains some paths that end with lib64, maybe include them too? + // TODO: what is the best possible order of things? + // TODO: some of these are suspect and should only be added on some systems. audit needed. - try self.addIncludeDir("/usr/local/include"); - try self.addLibDir("/usr/local/lib"); - try self.addLibDir("/usr/local/lib64"); + try self.addIncludeDir("/usr/local/include"); + try self.addLibDir("/usr/local/lib"); + try self.addLibDir("/usr/local/lib64"); - try self.addIncludeDirFmt("/usr/include/{}", .{triple}); - try self.addLibDirFmt("/usr/lib/{}", .{triple}); + try self.addIncludeDirFmt("/usr/include/{}", .{triple}); + try self.addLibDirFmt("/usr/lib/{}", .{triple}); - try self.addIncludeDir("/usr/include"); - try self.addLibDir("/lib"); - try self.addLibDir("/lib64"); - try self.addLibDir("/usr/lib"); - try self.addLibDir("/usr/lib64"); + try self.addIncludeDir("/usr/include"); + try self.addLibDir("/lib"); + try self.addLibDir("/lib64"); + try self.addLibDir("/usr/lib"); + try self.addLibDir("/usr/lib64"); - // example: on a 64-bit debian-based linux distro, with zlib installed from apt: - // zlib.h is in /usr/include (added above) - // libz.so.1 is in /lib/x86_64-linux-gnu (added here) - try self.addLibDirFmt("/lib/{}", .{triple}); - }, + // example: on a 64-bit debian-based linux distro, with zlib installed from apt: + // zlib.h is in /usr/include (added above) + // libz.so.1 is in /lib/x86_64-linux-gnu (added here) + try self.addLibDirFmt("/lib/{}", .{triple}); } return self; -- 2.54.0 From 767f041068369af021aaca421e8b31ce3aaae631 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 22 Feb 2020 17:49:52 -0500 Subject: [PATCH 3/4] std.process: fix typo --- lib/std/process.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/process.zig b/lib/std/process.zig index ac75eff7250ef3817d0b37485e1ebe1e31995ada..81b75faeb1cf9f1f590988e7028134d908e80844 100644 --- a/lib/std/process.zig +++ b/lib/std/process.zig @@ -37,7 +37,7 @@ pub fn getEnvMap(allocator: *Allocator) !BufMap { errdefer result.deinit(); if (builtin.os == .windows) { - const ptr = windows.peb().ProcessParameters.Environment; + const ptr = os.windows.peb().ProcessParameters.Environment; defer os.windows.FreeEnvironmentStringsW(ptr); var i: usize = 0; -- 2.54.0 From 94c3dbf9e3f9fcc63b2495adec36e6b4acb1813e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 23 Feb 2020 01:05:39 -0500 Subject: [PATCH 4/4] remove no-longer-valid defer --- lib/std/process.zig | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/std/process.zig b/lib/std/process.zig index 81b75faeb1cf9f1f590988e7028134d908e80844..b796150b3ba38c8828ceef01b6fe8250456090e4 100644 --- a/lib/std/process.zig +++ b/lib/std/process.zig @@ -38,7 +38,6 @@ pub fn getEnvMap(allocator: *Allocator) !BufMap { if (builtin.os == .windows) { const ptr = os.windows.peb().ProcessParameters.Environment; - defer os.windows.FreeEnvironmentStringsW(ptr); var i: usize = 0; while (ptr[i] != 0) { -- 2.54.0