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 {
7777 /// memoized based on `block`.
7878 initialized: bool = false,
7979 /// Protected by `mutex`. Memoized based on `block`. Tracks whether the
80 /// environment variables are present and non-empty.
81 exist: struct {
82 NO_COLOR: bool = false,
83 CLICOLOR_FORCE: bool = false,
84 } = .{},
80 /// environment variables are present, ignoring their value.
81 exist: Exist = .{},
8582 /// Protected by `mutex`. Memoized based on `block`.
86 string: struct {
87 PATH: ?[:0]const u8 = null,
88 } = .{},
83 string: String = .{},
84 /// Protected by `mutex`. Tracks the problem, if any, that occurred when
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
9092 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 };
91105};
92106
93107pub const RobustCancel = if (std.Thread.use_pthreads or native_os == .linux) enum {
......@@ -11917,13 +11931,111 @@ fn scanEnviron(t: *Threaded) void {
1191711931 if (t.environ.initialized) return;
1191811932 t.environ.initialized = true;
1191911933
11920 if (native_os == .wasi) {
11921 @panic("TODO");
11922 }
11934 if (is_windows) {
11935 const ptr = windows.peb().ProcessParameters.Environment;
1192311936
11924 for (t.environ.block) |kv| {
11925 _ = kv;
11926 @panic("TODO");
11937 var i: usize = 0;
11938 while (ptr[i] != 0) {
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 }
1192712039 }
1192812040}
1192912041