Something went wrong. Try again.
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179const std = @import("std");const builtin = @import("builtin");const Allocator = std.mem.Allocator;const posix = std.posix;const isFlatpak = @import("flatpak.zig").isFlatpak;
pub const Error = Allocator.Error;
/// Get the environment map.pub fn getEnvMap(alloc: Allocator) !std.process.EnvMap { return if (isFlatpak()) std.process.EnvMap.init(alloc) else try std.process.getEnvMap(alloc);}
/// Append a value to an environment variable such as PATH./// The returned value is always allocated so it must be freed.pub fn appendEnv( alloc: Allocator, current: []const u8, value: []const u8,) Error![]u8 { // If there is no prior value, we return it as-is if (current.len == 0) return try alloc.dupe(u8, value);
// Otherwise we must prefix. return try appendEnvAlways(alloc, current, value);}
/// Always append value to environment, even when it is empty./// This is useful because some env vars (like MANPATH) want there/// to be an empty prefix to preserve existing values.////// The returned value is always allocated so it must be freed.pub fn appendEnvAlways( alloc: Allocator, current: []const u8, value: []const u8,) Error![]u8 { return try std.fmt.allocPrint(alloc, "{s}{c}{s}", .{ current, std.fs.path.delimiter, value, });}
/// Prepend a value to an environment variable such as PATH./// The returned value is always allocated so it must be freed.pub fn prependEnv( alloc: Allocator, current: []const u8, value: []const u8,) Error![]u8 { // If there is no prior value, we return it as-is if (current.len == 0) return try alloc.dupe(u8, value);
return try std.fmt.allocPrint(alloc, "{s}{c}{s}", .{ value, std.fs.path.delimiter, current, });}
/// 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) Error!?GetEnvResult { return switch (builtin.os.tag) { // Non-Windows doesn't need to allocate else => if (posix.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, error.InvalidWtf8 => null, else => |e| e, }, };}
/// Gets the value of an environment variable. Returns null if not found or the/// value is empty. 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 getenvNotEmpty(alloc: Allocator, key: []const u8) !?GetEnvResult { const result_ = try getenv(alloc, key); if (result_) |result| { if (result.value.len == 0) { result.deinit(alloc); return null; } } return result_;}
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" { const testing = std.testing; const alloc = testing.allocator;
const result = try appendEnv(alloc, "", "foo"); defer alloc.free(result); try testing.expectEqualStrings(result, "foo");}
test "appendEnv existing" { const testing = std.testing; const alloc = testing.allocator;
const result = try appendEnv(alloc, "a:b", "foo"); defer alloc.free(result); if (builtin.os.tag == .windows) { try testing.expectEqualStrings(result, "a:b;foo"); } else { try testing.expectEqualStrings(result, "a:b:foo"); }}
test "prependEnv empty" { const testing = std.testing; const alloc = testing.allocator;
const result = try prependEnv(alloc, "", "foo"); defer alloc.free(result); try testing.expectEqualStrings(result, "foo");}
test "prependEnv existing" { const testing = std.testing; const alloc = testing.allocator;
const result = try prependEnv(alloc, "a:b", "foo"); defer alloc.free(result); if (builtin.os.tag == .windows) { try testing.expectEqualStrings(result, "foo;a:b"); } else { try testing.expectEqualStrings(result, "foo:a:b"); }}