mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-14 15:56:13 +03:00
os: add prependEnv(), like appendEnv() (#2983)
We can use this function in setupXdgDataDirs() to simplify the XDG_DATA_DIRS environment variable code in a more standardized way.
This commit is contained in:
@ -34,6 +34,23 @@ pub fn appendEnvAlways(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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,
|
||||||
|
) ![]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
|
/// The result of getenv, with a shared deinit to properly handle allocation
|
||||||
/// on Windows.
|
/// on Windows.
|
||||||
pub const GetEnvResult = struct {
|
pub const GetEnvResult = struct {
|
||||||
@ -110,3 +127,25 @@ test "appendEnv existing" {
|
|||||||
try testing.expectEqualStrings(result, "a:b:foo");
|
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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -27,6 +27,7 @@ pub const CFReleaseThread = @import("cf_release_thread.zig");
|
|||||||
pub const TempDir = @import("TempDir.zig");
|
pub const TempDir = @import("TempDir.zig");
|
||||||
pub const appendEnv = env.appendEnv;
|
pub const appendEnv = env.appendEnv;
|
||||||
pub const appendEnvAlways = env.appendEnvAlways;
|
pub const appendEnvAlways = env.appendEnvAlways;
|
||||||
|
pub const prependEnv = env.prependEnv;
|
||||||
pub const getenv = env.getenv;
|
pub const getenv = env.getenv;
|
||||||
pub const setenv = env.setenv;
|
pub const setenv = env.setenv;
|
||||||
pub const unsetenv = env.unsetenv;
|
pub const unsetenv = env.unsetenv;
|
||||||
|
@ -5,6 +5,7 @@ const ArenaAllocator = std.heap.ArenaAllocator;
|
|||||||
const EnvMap = std.process.EnvMap;
|
const EnvMap = std.process.EnvMap;
|
||||||
const config = @import("../config.zig");
|
const config = @import("../config.zig");
|
||||||
const homedir = @import("../os/homedir.zig");
|
const homedir = @import("../os/homedir.zig");
|
||||||
|
const internal_os = @import("../os/main.zig");
|
||||||
|
|
||||||
const log = std.log.scoped(.shell_integration);
|
const log = std.log.scoped(.shell_integration);
|
||||||
|
|
||||||
@ -435,8 +436,8 @@ test "bash: preserve ENV" {
|
|||||||
/// Setup automatic shell integration for shells that include
|
/// Setup automatic shell integration for shells that include
|
||||||
/// their modules from paths in `XDG_DATA_DIRS` env variable.
|
/// their modules from paths in `XDG_DATA_DIRS` env variable.
|
||||||
///
|
///
|
||||||
/// Path of shell-integration dir is prepended to `XDG_DATA_DIRS`.
|
/// The shell-integration path is prepended to `XDG_DATA_DIRS`.
|
||||||
/// It is also saved in `GHOSTTY_SHELL_INTEGRATION_XDG_DIR` variable
|
/// It is also saved in the `GHOSTTY_SHELL_INTEGRATION_XDG_DIR` variable
|
||||||
/// so that the shell can refer to it and safely remove this directory
|
/// so that the shell can refer to it and safely remove this directory
|
||||||
/// from `XDG_DATA_DIRS` when integration is complete.
|
/// from `XDG_DATA_DIRS` when integration is complete.
|
||||||
fn setupXdgDataDirs(
|
fn setupXdgDataDirs(
|
||||||
@ -458,32 +459,60 @@ fn setupXdgDataDirs(
|
|||||||
// so that our modifications don't interfere with other commands.
|
// so that our modifications don't interfere with other commands.
|
||||||
try env.put("GHOSTTY_SHELL_INTEGRATION_XDG_DIR", integ_dir);
|
try env.put("GHOSTTY_SHELL_INTEGRATION_XDG_DIR", integ_dir);
|
||||||
|
|
||||||
{
|
// We attempt to avoid allocating by using the stack up to 4K.
|
||||||
const xdg_data_dir_key = "XDG_DATA_DIRS";
|
// Max stack size is considerably larger on mac
|
||||||
|
// 4K is a reasonable size for this for most cases. However, env
|
||||||
|
// vars can be significantly larger so if we have to we fall
|
||||||
|
// back to a heap allocated value.
|
||||||
|
var stack_alloc_state = std.heap.stackFallback(4096, alloc_arena);
|
||||||
|
const stack_alloc = stack_alloc_state.get();
|
||||||
|
|
||||||
// We attempt to avoid allocating by using the stack up to 4K.
|
// If no XDG_DATA_DIRS set use the default value as specified.
|
||||||
// Max stack size is considerably larger on macOS and Linux but
|
// This ensures that the default directories aren't lost by setting
|
||||||
// 4K is a reasonable size for this for most cases. However, env
|
// our desired integration dir directly. See #2711.
|
||||||
// vars can be significantly larger so if we have to we fall
|
// <https://specifications.freedesktop.org/basedir-spec/0.6/#variables>
|
||||||
// back to a heap allocated value.
|
const xdg_data_dirs_key = "XDG_DATA_DIRS";
|
||||||
var stack_alloc_state = std.heap.stackFallback(4096, alloc_arena);
|
try env.put(
|
||||||
const stack_alloc = stack_alloc_state.get();
|
xdg_data_dirs_key,
|
||||||
|
try internal_os.prependEnv(
|
||||||
// If no XDG_DATA_DIRS set use the default value as specified.
|
stack_alloc,
|
||||||
// This ensures that the default directories aren't lost by setting
|
env.get(xdg_data_dirs_key) orelse "/usr/local/share:/usr/share",
|
||||||
// our desired integration dir directly. See #2711.
|
|
||||||
// <https://specifications.freedesktop.org/basedir-spec/0.6/#variables>
|
|
||||||
const old = env.get(xdg_data_dir_key) orelse "/usr/local/share:/usr/share";
|
|
||||||
|
|
||||||
const prepended = try std.fmt.allocPrint(stack_alloc, "{s}{c}{s}", .{
|
|
||||||
integ_dir,
|
integ_dir,
|
||||||
std.fs.path.delimiter,
|
),
|
||||||
old,
|
);
|
||||||
});
|
}
|
||||||
defer stack_alloc.free(prepended);
|
|
||||||
|
|
||||||
try env.put(xdg_data_dir_key, prepended);
|
test "xdg: empty XDG_DATA_DIRS" {
|
||||||
}
|
const testing = std.testing;
|
||||||
|
|
||||||
|
var arena = ArenaAllocator.init(testing.allocator);
|
||||||
|
defer arena.deinit();
|
||||||
|
const alloc = arena.allocator();
|
||||||
|
|
||||||
|
var env = EnvMap.init(alloc);
|
||||||
|
defer env.deinit();
|
||||||
|
|
||||||
|
try setupXdgDataDirs(alloc, ".", &env);
|
||||||
|
|
||||||
|
try testing.expectEqualStrings("./shell-integration", env.get("GHOSTTY_SHELL_INTEGRATION_XDG_DIR").?);
|
||||||
|
try testing.expectEqualStrings("./shell-integration:/usr/local/share:/usr/share", env.get("XDG_DATA_DIRS").?);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "xdg: existing XDG_DATA_DIRS" {
|
||||||
|
const testing = std.testing;
|
||||||
|
|
||||||
|
var arena = ArenaAllocator.init(testing.allocator);
|
||||||
|
defer arena.deinit();
|
||||||
|
const alloc = arena.allocator();
|
||||||
|
|
||||||
|
var env = EnvMap.init(alloc);
|
||||||
|
defer env.deinit();
|
||||||
|
|
||||||
|
try env.put("XDG_DATA_DIRS", "/opt/share");
|
||||||
|
try setupXdgDataDirs(alloc, ".", &env);
|
||||||
|
|
||||||
|
try testing.expectEqualStrings("./shell-integration", env.get("GHOSTTY_SHELL_INTEGRATION_XDG_DIR").?);
|
||||||
|
try testing.expectEqualStrings("./shell-integration:/opt/share", env.get("XDG_DATA_DIRS").?);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Setup the zsh automatic shell integration. This works by setting
|
/// Setup the zsh automatic shell integration. This works by setting
|
||||||
|
Reference in New Issue
Block a user