diff --git a/std/build.zig b/std/build.zig index 526e2c3cf..22aac8419 100644 --- a/std/build.zig +++ b/std/build.zig @@ -308,7 +308,7 @@ pub const Builder = struct { } fn processNixOSEnvVars(self: &Builder) { - if (os.getEnv("NIX_CFLAGS_COMPILE")) |nix_cflags_compile| { + if (os.getEnvVarOwned(self.allocator, "NIX_CFLAGS_COMPILE")) |nix_cflags_compile| { var it = mem.split(nix_cflags_compile, " "); while (true) { const word = it.next() ?? break; @@ -323,8 +323,10 @@ pub const Builder = struct { break; } } + } else |err| { + assert(err == error.EnvironmentVariableNotFound); } - if (os.getEnv("NIX_LDFLAGS")) |nix_ldflags| { + if (os.getEnvVarOwned(self.allocator, "NIX_LDFLAGS")) |nix_ldflags| { var it = mem.split(nix_ldflags, " "); while (true) { const word = it.next() ?? break; @@ -342,6 +344,8 @@ pub const Builder = struct { break; } } + } else |err| { + assert(err == error.EnvironmentVariableNotFound); } } @@ -1248,7 +1252,13 @@ pub const LibExeObjStep = struct { } fn makeC(self: &LibExeObjStep) -> %void { - const cc = os.getEnv("CC") ?? "cc"; + const cc = os.getEnvVarOwned(self.builder.allocator, "CC") %% |err| { + if (err == error.EnvironmentVariableNotFound) { + ([]const u8)("cc") + } else { + return err + } + }; const builder = self.builder; assert(!self.is_zig); diff --git a/std/os/child_process.zig b/std/os/child_process.zig index 4efb50da9..4fdfb1c37 100644 --- a/std/os/child_process.zig +++ b/std/os/child_process.zig @@ -564,7 +564,7 @@ pub const ChildProcess = struct { const cwd_ptr = if (cwd_slice) |cwd| cwd.ptr else null; const maybe_envp_buf = if (self.env_map) |env_map| { - %return os.createNullDelimitedEnvMap(self.allocator, env_map) + %return os.createWindowsEnvBlock(self.allocator, env_map) } else { null }; @@ -578,6 +578,7 @@ pub const ChildProcess = struct { const err = windows.GetLastError(); return switch (err) { windows.ERROR.FILE_NOT_FOUND => error.FileNotFound, + windows.ERROR.INVALID_PARAMETER => unreachable, else => error.Unexpected, }; } diff --git a/std/os/index.zig b/std/os/index.zig index 2f6b70d90..0feb829da 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -32,6 +32,7 @@ pub const windowsWrite = windows_util.windowsWrite; pub const windowsIsTty = windows_util.windowsIsTty; pub const windowsIsCygwinPty = windows_util.windowsIsCygwinPty; pub const windowsOpen = windows_util.windowsOpen; +pub const createWindowsEnvBlock = windows_util.createWindowsEnvBlock; const debug = @import("../debug.zig"); const assert = debug.assert; @@ -48,6 +49,7 @@ const io = @import("../io.zig"); const base64 = @import("../base64.zig"); const ArrayList = @import("../array_list.zig").ArrayList; const Buffer = @import("../buffer.zig").Buffer; +const math = @import("../index.zig").math; error Unexpected; error SystemResources; @@ -362,7 +364,7 @@ pub fn posixExecve(argv: []const []const u8, env_map: &const BufMap, return posixExecveErrnoToErr(posix.getErrno(posix.execve(??argv_buf[0], argv_buf.ptr, envp_buf.ptr))); } - const PATH = getEnv("PATH") ?? "/usr/local/bin:/bin/:/usr/bin"; + const PATH = getEnvPosix("PATH") ?? "/usr/local/bin:/bin/:/usr/bin"; // PATH.len because it is >= the largest search_path // +1 for the / to join the search path and exe_path // +1 for the null terminating byte @@ -406,29 +408,55 @@ fn posixExecveErrnoToErr(err: usize) -> error { }; } -pub var environ_raw: []&u8 = undefined; +pub var posix_environ_raw: []&u8 = undefined; /// Caller must free result when done. pub fn getEnvMap(allocator: &Allocator) -> %BufMap { var result = BufMap.init(allocator); %defer result.deinit(); - for (environ_raw) |ptr| { - var line_i: usize = 0; - while (ptr[line_i] != 0 and ptr[line_i] != '=') : (line_i += 1) {} - const key = ptr[0..line_i]; + if (is_windows) { + const ptr = windows.GetEnvironmentStringsA() ?? return error.OutOfMemory; + defer assert(windows.FreeEnvironmentStringsA(ptr)); - var end_i: usize = line_i; - while (ptr[end_i] != 0) : (end_i += 1) {} - const value = ptr[line_i + 1..end_i]; + var i: usize = 0; + while (true) { + if (ptr[i] == 0) + return result; - %return result.set(key, value); + const key_start = i; + + while (ptr[i] != 0 and ptr[i] != '=') : (i += 1) {} + const key = ptr[key_start..i]; + + if (ptr[i] == '=') i += 1; + + const value_start = i; + while (ptr[i] != 0) : (i += 1) {} + const value = ptr[value_start..i]; + + i += 1; // skip over null byte + + %return result.set(key, value); + } + } else { + for (posix_environ_raw) |ptr| { + var line_i: usize = 0; + while (ptr[line_i] != 0 and ptr[line_i] != '=') : (line_i += 1) {} + const key = ptr[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]; + + %return result.set(key, value); + } + return result; } - return result; } -pub fn getEnv(key: []const u8) -> ?[]const u8 { - for (environ_raw) |ptr| { +pub fn getEnvPosix(key: []const u8) -> ?[]const u8 { + for (posix_environ_raw) |ptr| { var line_i: usize = 0; while (ptr[line_i] != 0 and ptr[line_i] != '=') : (line_i += 1) {} const this_key = ptr[0..line_i]; @@ -444,6 +472,42 @@ pub fn getEnv(key: []const u8) -> ?[]const u8 { return null; } +error EnvironmentVariableNotFound; + +/// Caller must free returned memory. +pub fn getEnvVarOwned(allocator: &mem.Allocator, key: []const u8) -> %[]u8 { + if (is_windows) { + const key_with_null = %return cstr.addNullByte(allocator, key); + defer allocator.free(key_with_null); + + var buf = %return allocator.alloc(u8, 256); + %defer allocator.free(buf); + + while (true) { + const windows_buf_len = %return math.cast(windows.DWORD, buf.len); + const result = windows.GetEnvironmentVariableA(key_with_null.ptr, buf.ptr, windows_buf_len); + + if (result == 0) { + const err = windows.GetLastError(); + return switch (err) { + windows.ERROR.ENVVAR_NOT_FOUND => error.EnvironmentVariableNotFound, + else => error.Unexpected, + }; + } + + if (result > buf.len) { + buf = %return allocator.realloc(u8, buf, result); + continue; + } + + return allocator.shrink(u8, buf, result); + } + } else { + const result = getEnvPosix(key) ?? return error.EnvironmentVariableNotFound; + return mem.dupe(u8, allocator, result); + } +} + /// Caller must free the returned memory. pub fn getCwd(allocator: &Allocator) -> %[]u8 { switch (builtin.os) { diff --git a/std/os/windows/index.zig b/std/os/windows/index.zig index 95dc83644..bad7eaf19 100644 --- a/std/os/windows/index.zig +++ b/std/os/windows/index.zig @@ -32,12 +32,18 @@ pub extern "kernel32" stdcallcc fn DeleteFileA(lpFileName: LPCSTR) -> bool; pub extern "kernel32" stdcallcc fn ExitProcess(exit_code: UINT) -> noreturn; +pub extern "kernel32" stdcallcc fn FreeEnvironmentStringsA(penv: LPCH) -> BOOL; + pub extern "kernel32" stdcallcc fn GetCommandLineA() -> LPSTR; pub extern "kernel32" stdcallcc fn GetConsoleMode(in_hConsoleHandle: HANDLE, out_lpMode: &DWORD) -> bool; pub extern "kernel32" stdcallcc fn GetCurrentDirectoryA(nBufferLength: WORD, lpBuffer: ?LPSTR) -> DWORD; +pub extern "kernel32" stdcallcc fn GetEnvironmentStringsA() -> ?LPCH; + +pub extern "kernel32" stdcallcc fn GetEnvironmentVariableA(lpName: LPCSTR, lpBuffer: LPSTR, nSize: DWORD) -> DWORD; + pub extern "kernel32" stdcallcc fn GetExitCodeProcess(hProcess: HANDLE, lpExitCode: &DWORD) -> BOOL; pub extern "kernel32" stdcallcc fn GetLastError() -> DWORD; @@ -49,11 +55,11 @@ pub extern "kernel32" stdcallcc fn GetFileInformationByHandleEx(in_hFile: HANDLE pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleA(hFile: HANDLE, lpszFilePath: LPSTR, cchFilePath: DWORD, dwFlags: DWORD) -> DWORD; -pub extern "kernel32" stdcallcc fn GetProcessHeap() -> HANDLE; +pub extern "kernel32" stdcallcc fn GetProcessHeap() -> ?HANDLE; pub extern "kernel32" stdcallcc fn GetStdHandle(in_nStdHandle: DWORD) -> ?HANDLE; -pub extern "kernel32" stdcallcc fn HeapAlloc(hHeap: HANDLE, dwFlags: DWORD, dwBytes: SIZE_T) -> LPVOID; +pub extern "kernel32" stdcallcc fn HeapAlloc(hHeap: HANDLE, dwFlags: DWORD, dwBytes: SIZE_T) -> ?LPVOID; pub extern "kernel32" stdcallcc fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem: LPVOID) -> BOOL; @@ -91,6 +97,7 @@ pub const HCRYPTPROV = ULONG_PTR; pub const HINSTANCE = &@OpaqueType(); pub const INT = c_int; pub const LPBYTE = &BYTE; +pub const LPCH = &CHAR; pub const LPCSTR = &const CHAR; pub const LPCTSTR = &const TCHAR; pub const LPCVOID = &const c_void; diff --git a/std/os/windows/util.zig b/std/os/windows/util.zig index da17496e8..99a6dd2a4 100644 --- a/std/os/windows/util.zig +++ b/std/os/windows/util.zig @@ -3,6 +3,7 @@ const os = std.os; const windows = std.os.windows; const assert = std.debug.assert; const mem = std.mem; +const BufMap = std.BufMap; error WaitAbandoned; error WaitTimeOut; @@ -111,3 +112,35 @@ pub fn windowsOpen(file_path: []const u8, desired_access: windows.DWORD, share_m return result; } + +/// Caller must free result. +pub fn createWindowsEnvBlock(allocator: &mem.Allocator, env_map: &const BufMap) -> %[]u8 { + // count bytes needed + const bytes_needed = { + var bytes_needed: usize = 1; // 1 for the final null byte + var it = env_map.iterator(); + while (it.next()) |pair| { + // +1 for '=' + // +1 for null byte + bytes_needed += pair.key.len + pair.value.len + 2; + } + bytes_needed + }; + const result = %return allocator.alloc(u8, bytes_needed); + %defer allocator.free(result); + + var it = env_map.iterator(); + var i: usize = 0; + while (it.next()) |pair| { + mem.copy(u8, result[i..], pair.key); + i += pair.key.len; + result[i] = '='; + i += 1; + mem.copy(u8, result[i..], pair.value); + i += pair.value.len; + result[i] = 0; + i += 1; + } + result[i] = 0; + return result; +} diff --git a/std/special/bootstrap.zig b/std/special/bootstrap.zig index c202d07f0..924e537dd 100644 --- a/std/special/bootstrap.zig +++ b/std/special/bootstrap.zig @@ -56,7 +56,7 @@ fn callMain(argc: usize, argv: &&u8, envp: &?&u8) -> %void { var env_count: usize = 0; while (envp[env_count] != null) : (env_count += 1) {} - std.os.environ_raw = @ptrCast(&&u8, envp)[0..env_count]; + std.os.posix_environ_raw = @ptrCast(&&u8, envp)[0..env_count]; std.debug.user_main_fn = root.main;