authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-02-22 15:59:13-05:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-02-22 15:59:13-05:00
log936d0b18b116ea126893d2f165f9be72f8bef845
tree8481d2c4265d34217e6b045cbf1c7e34c30aab51
parent0cd89e9176ab36fc5e267120dc4d75cb79d32684
signature Commit is signed but in an unrecognized format.

update std lib to integrate with libc for environ

closes #3511

5 files changed, 77 insertions(+), 21 deletions(-)

lib/std/c.zig+2
...@@ -62,6 +62,8 @@ pub fn versionCheck(glibc_version: builtin.Version) type {...@@ -62,6 +62,8 @@ pub fn versionCheck(glibc_version: builtin.Version) type {
62 };62 };
63}63}
6464
65pub extern "c" var environ: [*:null]?[*:0]u8;
66
65pub extern "c" fn fopen(filename: [*:0]const u8, modes: [*:0]const u8) ?*FILE;67pub extern "c" fn fopen(filename: [*:0]const u8, modes: [*:0]const u8) ?*FILE;
66pub extern "c" fn fclose(stream: *FILE) c_int;68pub extern "c" fn fclose(stream: *FILE) c_int;
67pub extern "c" fn fwrite(ptr: [*]const u8, size_of_type: usize, item_count: usize, stream: *FILE) usize;69pub extern "c" fn fwrite(ptr: [*]const u8, size_of_type: usize, item_count: usize, stream: *FILE) usize;
lib/std/os.zig+44-5
...@@ -70,6 +70,8 @@ else switch (builtin.os) {...@@ -70,6 +70,8 @@ else switch (builtin.os) {
70pub usingnamespace @import("os/bits.zig");70pub usingnamespace @import("os/bits.zig");
7171
72/// See also `getenv`. Populated by startup code before main().72/// See also `getenv`. Populated by startup code before main().
73/// TODO this is a footgun because the value will be undefined when using `zig build-lib`.
74/// https://github.com/ziglang/zig/issues/4524
73pub var environ: [][*:0]u8 = undefined;75pub var environ: [][*:0]u8 = undefined;
7476
75/// Populated by startup code before main().77/// Populated by startup code before main().
...@@ -922,7 +924,11 @@ pub const execveC = execveZ;...@@ -922,7 +924,11 @@ pub const execveC = execveZ;
922/// Like `execve` except the parameters are null-terminated,924/// Like `execve` except the parameters are null-terminated,
923/// matching the syscall API on all targets. This removes the need for an allocator.925/// matching the syscall API on all targets. This removes the need for an allocator.
924/// This function ignores PATH environment variable. See `execvpeZ` for that.926/// This function ignores PATH environment variable. See `execvpeZ` for that.
925pub fn execveZ(path: [*:0]const u8, child_argv: [*:null]const ?[*:0]const u8, envp: [*:null]const ?[*:0]const u8) ExecveError {927pub fn execveZ(
928 path: [*:0]const u8,
929 child_argv: [*:null]const ?[*:0]const u8,
930 envp: [*:null]const ?[*:0]const u8,
931) ExecveError {
926 switch (errno(system.execve(path, child_argv, envp))) {932 switch (errno(system.execve(path, child_argv, envp))) {
927 0 => unreachable,933 0 => unreachable,
928 EFAULT => unreachable,934 EFAULT => unreachable,
...@@ -966,7 +972,7 @@ pub fn execvpeZ_expandArg0(...@@ -966,7 +972,7 @@ pub fn execvpeZ_expandArg0(
966 envp: [*:null]const ?[*:0]const u8,972 envp: [*:null]const ?[*:0]const u8,
967) ExecveError {973) ExecveError {
968 const file_slice = mem.toSliceConst(u8, file);974 const file_slice = mem.toSliceConst(u8, file);
969 if (mem.indexOfScalar(u8, file_slice, '/') != null) return execveC(file, child_argv, envp);975 if (mem.indexOfScalar(u8, file_slice, '/') != null) return execveZ(file, child_argv, envp);
970976
971 const PATH = getenvZ("PATH") orelse "/usr/local/bin:/bin/:/usr/bin";977 const PATH = getenvZ("PATH") orelse "/usr/local/bin:/bin/:/usr/bin";
972 var path_buf: [MAX_PATH_BYTES]u8 = undefined;978 var path_buf: [MAX_PATH_BYTES]u8 = undefined;
...@@ -993,7 +999,7 @@ pub fn execvpeZ_expandArg0(...@@ -993,7 +999,7 @@ pub fn execvpeZ_expandArg0(
993 .expand => child_argv[0] = full_path,999 .expand => child_argv[0] = full_path,
994 .no_expand => {},1000 .no_expand => {},
995 }1001 }
996 err = execveC(full_path, child_argv, envp);1002 err = execveZ(full_path, child_argv, envp);
997 switch (err) {1003 switch (err) {
998 error.AccessDenied => seen_eacces = true,1004 error.AccessDenied => seen_eacces = true,
999 error.FileNotFound, error.NotDir => {},1005 error.FileNotFound, error.NotDir => {},
...@@ -1007,7 +1013,7 @@ pub fn execvpeZ_expandArg0(...@@ -1007,7 +1013,7 @@ pub fn execvpeZ_expandArg0(
1007/// Like `execvpe` except the parameters are null-terminated,1013/// Like `execvpe` except the parameters are null-terminated,
1008/// matching the syscall API on all targets. This removes the need for an allocator.1014/// matching the syscall API on all targets. This removes the need for an allocator.
1009/// This function also uses the PATH environment variable to get the full path to the executable.1015/// This function also uses the PATH environment variable to get the full path to the executable.
1010/// If `file` is an absolute path, this is the same as `execveC`.1016/// If `file` is an absolute path, this is the same as `execveZ`.
1011pub fn execvpeZ(1017pub fn execvpeZ(
1012 file: [*:0]const u8,1018 file: [*:0]const u8,
1013 argv: [*:null]const ?[*:0]const u8,1019 argv: [*:null]const ?[*:0]const u8,
...@@ -1097,8 +1103,37 @@ pub fn freeNullDelimitedEnvMap(allocator: *mem.Allocator, envp_buf: []?[*:0]u8)...@@ -1097,8 +1103,37 @@ pub fn freeNullDelimitedEnvMap(allocator: *mem.Allocator, envp_buf: []?[*:0]u8)
10971103
1098/// Get an environment variable.1104/// Get an environment variable.
1099/// See also `getenvZ`.1105/// See also `getenvZ`.
1100/// TODO make this go through libc when we have it
1101pub fn getenv(key: []const u8) ?[]const u8 {1106pub fn getenv(key: []const u8) ?[]const u8 {
1107 if (builtin.os == .windows) {
1108 // TODO update this to use the ProcessEnvironmentBlock
1109 @compileError("TODO implement std.os.getenv for Windows");
1110 }
1111 if (builtin.link_libc) {
1112 var small_key_buf: [64]u8 = undefined;
1113 if (key.len < small_key_buf.len) {
1114 mem.copy(u8, &small_key_buf, key);
1115 small_key_buf[key.len] = 0;
1116 const key0 = small_key_buf[0..key.len :0];
1117 return getenvZ(key0);
1118 }
1119 // Search the entire `environ` because we don't have a null terminated pointer.
1120 var ptr = std.c.environ;
1121 while (ptr.*) |line| : (ptr += 1) {
1122 var line_i: usize = 0;
1123 while (line[line_i] != 0 and line[line_i] != '=') : (line_i += 1) {}
1124 const this_key = line[0..line_i];
1125
1126 if (!mem.eql(u8, this_key, key)) continue;
1127
1128 var end_i: usize = line_i;
1129 while (line[end_i] != 0) : (end_i += 1) {}
1130 const value = line[line_i + 1 .. end_i];
1131
1132 return value;
1133 }
1134 return null;
1135 }
1136 // TODO see https://github.com/ziglang/zig/issues/4524
1102 for (environ) |ptr| {1137 for (environ) |ptr| {
1103 var line_i: usize = 0;1138 var line_i: usize = 0;
1104 while (ptr[line_i] != 0 and ptr[line_i] != '=') : (line_i += 1) {}1139 while (ptr[line_i] != 0 and ptr[line_i] != '=') : (line_i += 1) {}
...@@ -1120,6 +1155,10 @@ pub const getenvC = getenvZ;...@@ -1120,6 +1155,10 @@ pub const getenvC = getenvZ;
1120/// Get an environment variable with a null-terminated name.1155/// Get an environment variable with a null-terminated name.
1121/// See also `getenv`.1156/// See also `getenv`.
1122pub fn getenvZ(key: [*:0]const u8) ?[]const u8 {1157pub fn getenvZ(key: [*:0]const u8) ?[]const u8 {
1158 if (builtin.os == .windows) {
1159 // TODO update this to use the ProcessEnvironmentBlock
1160 @compileError("TODO implement std.os.getenv for Windows");
1161 }
1123 if (builtin.link_libc) {1162 if (builtin.link_libc) {
1124 const value = system.getenv(key) orelse return null;1163 const value = system.getenv(key) orelse return null;
1125 return mem.toSliceConst(u8, value);1164 return mem.toSliceConst(u8, value);
lib/std/process.zig+22-9
...@@ -1,5 +1,5 @@...@@ -1,5 +1,5 @@
1const builtin = @import("builtin");
2const std = @import("std.zig");1const std = @import("std.zig");
2const builtin = std.builtin;
3const os = std.os;3const os = std.os;
4const fs = std.fs;4const fs = std.fs;
5const BufMap = std.BufMap;5const BufMap = std.BufMap;
...@@ -31,13 +31,13 @@ test "getCwdAlloc" {...@@ -31,13 +31,13 @@ test "getCwdAlloc" {
31 testing.allocator.free(cwd);31 testing.allocator.free(cwd);
32}32}
3333
34/// Caller must free result when done.34/// Caller owns resulting `BufMap`.
35/// TODO make this go through libc when we have it
36pub fn getEnvMap(allocator: *Allocator) !BufMap {35pub fn getEnvMap(allocator: *Allocator) !BufMap {
37 var result = BufMap.init(allocator);36 var result = BufMap.init(allocator);
38 errdefer result.deinit();37 errdefer result.deinit();
3938
40 if (builtin.os == .windows) {39 if (builtin.os == .windows) {
40 // TODO update this to use the ProcessEnvironmentBlock
41 const ptr = try os.windows.GetEnvironmentStringsW();41 const ptr = try os.windows.GetEnvironmentStringsW();
42 defer os.windows.FreeEnvironmentStringsW(ptr);42 defer os.windows.FreeEnvironmentStringsW(ptr);
4343
...@@ -95,15 +95,29 @@ pub fn getEnvMap(allocator: *Allocator) !BufMap {...@@ -95,15 +95,29 @@ pub fn getEnvMap(allocator: *Allocator) !BufMap {
95 }95 }
96 }96 }
97 return result;97 return result;
98 } else if (builtin.link_libc) {
99 var ptr = std.c.environ;
100 while (ptr.*) |line| : (ptr += 1) {
101 var line_i: usize = 0;
102 while (line[line_i] != 0 and line[line_i] != '=') : (line_i += 1) {}
103 const key = line[0..line_i];
104
105 var end_i: usize = line_i;
106 while (line[end_i] != 0) : (end_i += 1) {}
107 const value = line[line_i + 1 .. end_i];
108
109 try result.set(key, value);
110 }
111 return result;
98 } else {112 } else {
99 for (os.environ) |ptr| {113 for (os.environ) |line| {
100 var line_i: usize = 0;114 var line_i: usize = 0;
101 while (ptr[line_i] != 0 and ptr[line_i] != '=') : (line_i += 1) {}115 while (line[line_i] != 0 and line[line_i] != '=') : (line_i += 1) {}
102 const key = ptr[0..line_i];116 const key = line[0..line_i];
103117
104 var end_i: usize = line_i;118 var end_i: usize = line_i;
105 while (ptr[end_i] != 0) : (end_i += 1) {}119 while (line[end_i] != 0) : (end_i += 1) {}
106 const value = ptr[line_i + 1 .. end_i];120 const value = line[line_i + 1 .. end_i];
107121
108 try result.set(key, value);122 try result.set(key, value);
109 }123 }
...@@ -125,7 +139,6 @@ pub const GetEnvVarOwnedError = error{...@@ -125,7 +139,6 @@ pub const GetEnvVarOwnedError = error{
125};139};
126140
127/// Caller must free returned memory.141/// Caller must free returned memory.
128/// TODO make this go through libc when we have it
129pub fn getEnvVarOwned(allocator: *mem.Allocator, key: []const u8) GetEnvVarOwnedError![]u8 {142pub fn getEnvVarOwned(allocator: *mem.Allocator, key: []const u8) GetEnvVarOwnedError![]u8 {
130 if (builtin.os == .windows) {143 if (builtin.os == .windows) {
131 const key_with_null = try std.unicode.utf8ToUtf16LeWithNull(allocator, key);144 const key_with_null = try std.unicode.utf8ToUtf16LeWithNull(allocator, key);
lib/std/start.zig+8-2
...@@ -21,7 +21,9 @@ comptime {...@@ -21,7 +21,9 @@ comptime {
21 @export(main, .{ .name = "main", .linkage = .Weak });21 @export(main, .{ .name = "main", .linkage = .Weak });
22 }22 }
23 } else if (builtin.os == .windows) {23 } else if (builtin.os == .windows) {
24 if (!@hasDecl(root, "WinMain") and !@hasDecl(root, "WinMainCRTStartup") and !@hasDecl(root, "wWinMain") and !@hasDecl(root, "wWinMainCRTStartup")) {24 if (!@hasDecl(root, "WinMain") and !@hasDecl(root, "WinMainCRTStartup") and
25 !@hasDecl(root, "wWinMain") and !@hasDecl(root, "wWinMainCRTStartup"))
26 {
25 @export(WinMainCRTStartup, .{ .name = "WinMainCRTStartup" });27 @export(WinMainCRTStartup, .{ .name = "WinMainCRTStartup" });
26 }28 }
27 } else if (builtin.os == .uefi) {29 } else if (builtin.os == .uefi) {
...@@ -34,7 +36,11 @@ comptime {...@@ -34,7 +36,11 @@ comptime {
34 }36 }
35}37}
3638
37fn _DllMainCRTStartup(hinstDLL: std.os.windows.HINSTANCE, fdwReason: std.os.windows.DWORD, lpReserved: std.os.windows.LPVOID) callconv(.Stdcall) std.os.windows.BOOL {39fn _DllMainCRTStartup(
40 hinstDLL: std.os.windows.HINSTANCE,
41 fdwReason: std.os.windows.DWORD,
42 lpReserved: std.os.windows.LPVOID,
43) callconv(.Stdcall) std.os.windows.BOOL {
38 if (@hasDecl(root, "DllMain")) {44 if (@hasDecl(root, "DllMain")) {
39 return root.DllMain(hinstDLL, fdwReason, lpReserved);45 return root.DllMain(hinstDLL, fdwReason, lpReserved);
40 }46 }
src/os.cpp+1-5
...@@ -81,11 +81,7 @@ static clock_serv_t macos_monotonic_clock;...@@ -81,11 +81,7 @@ static clock_serv_t macos_monotonic_clock;
81#include <errno.h>81#include <errno.h>
82#include <time.h>82#include <time.h>
8383
84// Apple doesn't provide the environ global variable84#if !defined(environ)
85#if defined(__APPLE__) && !defined(environ)
86#include <crt_externs.h>
87#define environ (*_NSGetEnviron())
88#elif defined(ZIG_OS_FREEBSD) || defined(ZIG_OS_NETBSD) || defined(ZIG_OS_DRAGONFLY)
89extern char **environ;85extern char **environ;
90#endif86#endif
9187