authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-12-22 15:21:43-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-12-23 22:15:12-08:00
logc98157a3c9b0a46bd7af6099e4fbef969bb88223
tree6b256659f40a04f7d1cb8a1fff91f3c8d2cec547
parent0992f1204e6e1ebf4437d98647f4336aa1c5c95e

std.Io.Threaded: implement environment variable scanning


1 files changed, 126 insertions(+), 14 deletions(-)

lib/std/Io/Threaded.zig+126-14
...@@ -77,17 +77,31 @@ pub const Environ = struct {...@@ -77,17 +77,31 @@ pub const Environ = struct {
77 /// memoized based on `block`.77 /// memoized based on `block`.
78 initialized: bool = false,78 initialized: bool = false,
79 /// Protected by `mutex`. Memoized based on `block`. Tracks whether the79 /// Protected by `mutex`. Memoized based on `block`. Tracks whether the
80 /// environment variables are present and non-empty.80 /// environment variables are present, ignoring their value.
81 exist: struct {81 exist: Exist = .{},
82 NO_COLOR: bool = false,
83 CLICOLOR_FORCE: bool = false,
84 } = .{},
85 /// Protected by `mutex`. Memoized based on `block`.82 /// Protected by `mutex`. Memoized based on `block`.
86 string: struct {83 string: String = .{},
87 PATH: ?[:0]const u8 = null,84 /// Protected by `mutex`. Tracks the problem, if any, that occurred when
88 } = .{},85 /// trying to scan environment variables.
86 ///
87 /// Errors are only possible on WASI.
88 err: ?Error = null,
89
90 pub const Error = Allocator.Error || Io.UnexpectedError;
8991
90 pub const Block = []const [*:0]const u8;92 pub const Block = []const [*:0]const u8;
93
94 pub const Exist = struct {
95 NO_COLOR: bool = false,
96 CLICOLOR_FORCE: bool = false,
97 };
98
99 pub const String = switch (native_os) {
100 .openbsd, .haiku => struct {
101 PATH: ?[:0]const u8 = null,
102 },
103 else => struct {},
104 };
91};105};
92106
93pub const RobustCancel = if (std.Thread.use_pthreads or native_os == .linux) enum {107pub const RobustCancel = if (std.Thread.use_pthreads or native_os == .linux) enum {
...@@ -11917,13 +11931,111 @@ fn scanEnviron(t: *Threaded) void {...@@ -11917,13 +11931,111 @@ fn scanEnviron(t: *Threaded) void {
11917 if (t.environ.initialized) return;11931 if (t.environ.initialized) return;
11918 t.environ.initialized = true;11932 t.environ.initialized = true;
1191911933
11920 if (native_os == .wasi) {11934 if (is_windows) {
11921 @panic("TODO");11935 const ptr = windows.peb().ProcessParameters.Environment;
11922 }
1192311936
11924 for (t.environ.block) |kv| {11937 var i: usize = 0;
11925 _ = kv;11938 while (ptr[i] != 0) {
11926 @panic("TODO");11939 const key_start = i;
11940
11941 // There are some special environment variables that start with =,
11942 // so we need a special case to not treat = as a key/value separator
11943 // if it's the first character.
11944 // https://devblogs.microsoft.com/oldnewthing/20100506-00/?p=14133
11945 if (ptr[key_start] == '=') i += 1;
11946
11947 while (ptr[i] != 0 and ptr[i] != '=') : (i += 1) {}
11948 const key_w = ptr[key_start..i];
11949 if (std.mem.eql(u16, key_w, &.{ 'N', 'O', '_', 'C', 'O', 'L', 'O', 'R' })) {
11950 t.environ.exist.NO_COLOR = true;
11951 } else if (std.mem.eql(u16, key_w, &.{ 'C', 'L', 'I', 'C', 'O', 'L', 'O', 'R', '_', 'F', 'O', 'R', 'C', 'E' })) {
11952 t.environ.exist.CLICOLOR_FORCE = true;
11953 }
11954 comptime assert(@sizeOf(Environ.String) == 0);
11955
11956 while (ptr[i] != 0) : (i += 1) {} // skip over '=' and value
11957 i += 1; // skip over null byte
11958 }
11959 } else if (native_os == .wasi and !builtin.link_libc) {
11960 var environ_count: usize = undefined;
11961 var environ_buf_size: usize = undefined;
11962
11963 switch (std.os.wasi.environ_sizes_get(&environ_count, &environ_buf_size)) {
11964 .SUCCESS => {},
11965 else => |err| {
11966 t.environ.err = posix.unexpectedErrno(err);
11967 return;
11968 },
11969 }
11970 if (environ_count == 0) return;
11971
11972 const environ = t.allocator.alloc([*:0]u8, environ_count) catch |err| {
11973 t.environ.err = err;
11974 return;
11975 };
11976 defer t.allocator.free(environ);
11977 const environ_buf = t.allocator.alloc(u8, environ_buf_size) catch |err| {
11978 t.environ.err = err;
11979 return;
11980 };
11981 defer t.allocator.free(environ_buf);
11982
11983 switch (std.os.wasi.environ_get(environ.ptr, environ_buf.ptr)) {
11984 .SUCCESS => {},
11985 else => |err| {
11986 t.environ.err = posix.unexpectedErrno(err);
11987 return;
11988 },
11989 }
11990
11991 for (environ) |env| {
11992 const pair = std.mem.sliceTo(env, 0);
11993 var parts = std.mem.splitScalar(u8, pair, '=');
11994 const key = parts.first();
11995 if (std.mem.eql(u8, key, "NO_COLOR")) {
11996 t.environ.exist.NO_COLOR = true;
11997 } else if (std.mem.eql(u8, key, "CLICOLOR_FORCE")) {
11998 t.environ.exist.CLICOLOR_FORCE = true;
11999 }
12000 comptime assert(@sizeOf(Environ.String) == 0);
12001 }
12002 } else if (builtin.link_libc) {
12003 var ptr = std.c.environ;
12004 while (ptr[0]) |line| : (ptr += 1) {
12005 var line_i: usize = 0;
12006 while (line[line_i] != 0 and line[line_i] != '=') : (line_i += 1) {}
12007 const key = line[0..line_i];
12008
12009 var end_i: usize = line_i;
12010 while (line[end_i] != 0) : (end_i += 1) {}
12011 const value = line[line_i + 1 .. end_i];
12012
12013 if (std.mem.eql(u8, key, "NO_COLOR")) {
12014 t.environ.exist.NO_COLOR = true;
12015 } else if (std.mem.eql(u8, key, "CLICOLOR_FORCE")) {
12016 t.environ.exist.CLICOLOR_FORCE = true;
12017 } else if (@hasField(Environ.String, "PATH") and std.mem.eql(u8, key, "PATH")) {
12018 t.environ.string.PATH = value;
12019 }
12020 }
12021 } else {
12022 for (t.environ.block) |line| {
12023 var line_i: usize = 0;
12024 while (line[line_i] != 0 and line[line_i] != '=') : (line_i += 1) {}
12025 const key = line[0..line_i];
12026
12027 var end_i: usize = line_i;
12028 while (line[end_i] != 0) : (end_i += 1) {}
12029 const value = line[line_i + 1 .. end_i];
12030
12031 if (std.mem.eql(u8, key, "NO_COLOR")) {
12032 t.environ.exist.NO_COLOR = true;
12033 } else if (std.mem.eql(u8, key, "CLICOLOR_FORCE")) {
12034 t.environ.exist.CLICOLOR_FORCE = true;
12035 } else if (@hasField(Environ.String, "PATH") and std.mem.eql(u8, key, "PATH")) {
12036 t.environ.string.PATH = value;
12037 }
12038 }
11927 }12039 }
11928}12040}
1192912041