aboutsummaryrefslogtreecommitdiff
path: root/vendor/libghostty-vt/src/os/env.zig
diff options
context:
space:
mode:
authorCan Celik <ogulcancelik@gmail.com>2026-04-04 19:22:04 +0300
committerCan Celik <ogulcancelik@gmail.com>2026-04-05 01:23:06 +0300
commit41dd733ca7123c9ac4bfc6c781697a660e95b052 (patch)
tree3fc91815ca139ef2e0b281bba0abda4ef15175f4 /vendor/libghostty-vt/src/os/env.zig
parent55c138263e190a1e79d4b997bfb9397306c0ec1f (diff)
feat: finish ghostty pane backend integration
Diffstat (limited to 'vendor/libghostty-vt/src/os/env.zig')
-rw-r--r--vendor/libghostty-vt/src/os/env.zig178
1 files changed, 178 insertions, 0 deletions
diff --git a/vendor/libghostty-vt/src/os/env.zig b/vendor/libghostty-vt/src/os/env.zig
new file mode 100644
index 00000000..8f01a14d
--- /dev/null
+++ b/vendor/libghostty-vt/src/os/env.zig
@@ -0,0 +1,178 @@
+const 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");
+ }
+}