os: rename env to be posix-like, do not allocate on posix

This commit is contained in:
Mitchell Hashimoto
2023-11-05 15:39:25 -08:00
parent 7594bbd621
commit 8f35d5251e
3 changed files with 68 additions and 46 deletions

View File

@ -243,9 +243,9 @@ pub const GlobalState = struct {
// maybe once for logging) so for now this is an easy way to do // maybe once for logging) so for now this is an easy way to do
// this. Env vars are useful for logging too because they are // this. Env vars are useful for logging too because they are
// easy to set. // easy to set.
if ((try internal_os.getEnvVarOwned(self.alloc, "GHOSTTY_LOG"))) |v| { if ((try internal_os.getenv(self.alloc, "GHOSTTY_LOG"))) |v| {
defer self.alloc.free(v); defer v.deinit(self.alloc);
if (v.len > 0) { if (v.value.len > 0) {
self.logging = .{ .stderr = {} }; self.logging = .{ .stderr = {} };
} }
} }

View File

@ -25,6 +25,61 @@ pub fn appendEnv(
}); });
} }
/// The result of getenv, with a shared deinit to properly handle allocation
/// on Windows.
pub const GetEnvResult = struct {
value: []const u8,
pub fn deinit(self: GetEnvResult, alloc: Allocator) void {
switch (builtin.os.tag) {
.windows => alloc.free(self.value),
else => {},
}
}
};
/// Gets the value of an environment variable, or null if not found.
/// This will allocate on Windows but not on other platforms. The returned
/// value should have deinit called to do the proper cleanup no matter what
/// platform you are on.
pub fn getenv(alloc: Allocator, key: []const u8) !?GetEnvResult {
return switch (builtin.os.tag) {
// Non-Windows doesn't need to allocate
else => if (std.os.getenv(key)) |v| .{ .value = v } else null,
// Windows needs to allocate
.windows => if (std.process.getEnvVarOwned(alloc, key)) |v| .{
.value = v,
} else |err| switch (err) {
error.EnvironmentVariableNotFound => null,
else => err,
},
};
}
pub fn setenv(key: [:0]const u8, value: [:0]const u8) c_int {
return switch (builtin.os.tag) {
.windows => c._putenv_s(key.ptr, value.ptr),
else => c.setenv(key.ptr, value.ptr, 1),
};
}
pub fn unsetenv(key: [:0]const u8) c_int {
return switch (builtin.os.tag) {
.windows => c._putenv_s(key.ptr, ""),
else => c.unsetenv(key.ptr),
};
}
const c = struct {
// POSIX
extern "c" fn setenv(name: ?[*]const u8, value: ?[*]const u8, overwrite: c_int) c_int;
extern "c" fn unsetenv(name: ?[*]const u8) c_int;
// Windows
extern "c" fn _putenv_s(varname: ?[*]const u8, value_string: ?[*]const u8) c_int;
};
test "appendEnv empty" { test "appendEnv empty" {
const testing = std.testing; const testing = std.testing;
const alloc = testing.allocator; const alloc = testing.allocator;
@ -46,36 +101,3 @@ test "appendEnv existing" {
try testing.expectEqualStrings(result, "a:b:foo"); try testing.expectEqualStrings(result, "a:b:foo");
} }
} }
extern "c" fn setenv(name: ?[*]const u8, value: ?[*]const u8, overwrite: c_int) c_int;
extern "c" fn unsetenv(name: ?[*]const u8) c_int;
extern "c" fn _putenv_s(varname: ?[*]const u8, value_string: ?[*]const u8) c_int;
pub fn setEnv(key: [:0]const u8, value: [:0]const u8) c_int {
if (builtin.os.tag == .windows) {
return _putenv_s(key.ptr, value.ptr);
} else {
return setenv(key.ptr, value.ptr, 1);
}
}
pub fn unsetEnv(key: [:0]const u8) c_int {
if (builtin.os.tag == .windows) {
return _putenv_s(key.ptr, "");
} else {
return unsetenv(key.ptr);
}
}
/// Returns the value of an environment variable, or null if not found.
/// The returned value is always allocated so it must be freed.
pub fn getEnvVarOwned(alloc: std.mem.Allocator, key: []const u8) !?[]u8 {
if (std.process.getEnvVarOwned(alloc, key)) |v| {
return v;
} else |err| switch (err) {
error.EnvironmentVariableNotFound => {},
else => return err,
}
return null;
}

View File

@ -12,8 +12,8 @@ pub fn ensureLocale(alloc: std.mem.Allocator) !void {
// Get our LANG env var. We use this many times but we also need // Get our LANG env var. We use this many times but we also need
// the original value later. // the original value later.
const lang = try internal_os.getEnvVarOwned(alloc, "LANG"); const lang = try internal_os.getenv(alloc, "LANG");
defer if (lang) |v| alloc.free(v); defer if (lang) |v| v.deinit(alloc);
// On macOS, pre-populate the LANG env var with system preferences. // On macOS, pre-populate the LANG env var with system preferences.
// When launching the .app, LANG is not set so we must query it from the // When launching the .app, LANG is not set so we must query it from the
@ -22,7 +22,7 @@ pub fn ensureLocale(alloc: std.mem.Allocator) !void {
if (comptime builtin.target.isDarwin()) { if (comptime builtin.target.isDarwin()) {
// Set the lang if it is not set or if its empty. // Set the lang if it is not set or if its empty.
if (lang) |l| { if (lang) |l| {
if (l.len == 0) { if (l.value.len == 0) {
setLangFromCocoa(); setLangFromCocoa();
} }
} }
@ -37,13 +37,13 @@ pub fn ensureLocale(alloc: std.mem.Allocator) !void {
// setlocale failed. This is probably because the LANG env var is // setlocale failed. This is probably because the LANG env var is
// invalid. Try to set it without the LANG var set to use the system // invalid. Try to set it without the LANG var set to use the system
// default. // default.
if ((try internal_os.getEnvVarOwned(alloc, "LANG"))) |old_lang| { if ((try internal_os.getenv(alloc, "LANG"))) |old_lang| {
defer alloc.free(old_lang); defer old_lang.deinit(alloc);
if (old_lang.len > 0) { if (old_lang.value.len > 0) {
// We don't need to do both of these things but we do them // We don't need to do both of these things but we do them
// both to be sure that lang is either empty or unset completely. // both to be sure that lang is either empty or unset completely.
_ = internal_os.setEnv("LANG", ""); _ = internal_os.setenv("LANG", "");
_ = internal_os.unsetEnv("LANG"); _ = internal_os.unsetenv("LANG");
if (setlocale(LC_ALL, "")) |v| { if (setlocale(LC_ALL, "")) |v| {
log.info("setlocale after unset lang result={s}", .{v}); log.info("setlocale after unset lang result={s}", .{v});
@ -59,7 +59,7 @@ pub fn ensureLocale(alloc: std.mem.Allocator) !void {
// Failure again... fallback to en_US.UTF-8 // Failure again... fallback to en_US.UTF-8
log.warn("setlocale failed with LANG and system default. Falling back to en_US.UTF-8", .{}); log.warn("setlocale failed with LANG and system default. Falling back to en_US.UTF-8", .{});
if (setlocale(LC_ALL, "en_US.UTF-8")) |v| { if (setlocale(LC_ALL, "en_US.UTF-8")) |v| {
_ = internal_os.setEnv("LANG", "en_US.UTF-8"); _ = internal_os.setenv("LANG", "en_US.UTF-8");
log.info("setlocale default result={s}", .{v}); log.info("setlocale default result={s}", .{v});
return; return;
} else log.err("setlocale failed even with the fallback, uncertain results", .{}); } else log.err("setlocale failed even with the fallback, uncertain results", .{});
@ -100,7 +100,7 @@ fn setLangFromCocoa() void {
log.info("detected system locale={s}", .{env_value}); log.info("detected system locale={s}", .{env_value});
// Set it onto our environment // Set it onto our environment
if (internal_os.setEnv("LANG", env_value) < 0) { if (internal_os.setenv("LANG", env_value) < 0) {
log.err("error setting locale env var", .{}); log.err("error setting locale env var", .{});
return; return;
} }