diff --git a/lib/std/c.zig b/lib/std/c.zig index 93c6c24664..f6c0e07dbd 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 766b678c46..a4f148ae00 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 89307b44da..ba124ff262 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 { - for (os.environ) |ptr| { + } else if (builtin.link_libc) { + var ptr = std.c.environ; + while (ptr.*) |line| : (ptr += 1) { 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); + } + return result; + } else { + for (os.environ) |line| { + 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); } @@ -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 e6c21ba9b8..b58b6e8144 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 268e812050..8e0bcc433b 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