From ff0c805e3e3a8aec0c96142818f61f855f4b225a Mon Sep 17 00:00:00 2001 From: "Jeffrey C. Ollie" Date: Sat, 4 Jan 2025 12:52:32 -0600 Subject: [PATCH] core: add iterator to handle XDG_*_DIRS --- src/os/xdg.zig | 74 ++++++++++++++++++++++++++++++++ src/termio/Exec.zig | 8 ++-- src/termio/shell_integration.zig | 6 +-- 3 files changed, 81 insertions(+), 7 deletions(-) diff --git a/src/os/xdg.zig b/src/os/xdg.zig index 1383679fe..6f32df0bf 100644 --- a/src/os/xdg.zig +++ b/src/os/xdg.zig @@ -193,3 +193,77 @@ test parseTerminalExec { try testing.expectEqualSlices([*:0]const u8, actual, &.{ "a", "-e", "b", "c" }); } } + +/// https://specifications.freedesktop.org/basedir-spec/latest/ +pub const Dir = enum { + config, + data, + + pub fn key(self: Dir) [:0]const u8 { + return switch (self) { + .config => "XDG_CONFIG_DIRS", + .data => "XDG_DATA_DIRS", + }; + } + + pub fn default(self: Dir) [:0]const u8 { + return switch (self) { + .config => "/etc/xdg", + .data => "/usr/local/share:/usr/share", + }; + } +}; + +pub const DirIterator = struct { + data: []const u8, + iterator: std.mem.SplitIterator(u8, .scalar), + + /// https://specifications.freedesktop.org/basedir-spec/latest/ + pub fn init(key: Dir) DirIterator { + const data = data: { + if (posix.getenv(key.key())) |data| { + if (std.mem.trim(u8, data, &std.ascii.whitespace).len > 0) break :data data; + } + + break :data key.default(); + }; + + return .{ + .data = data, + .iterator = std.mem.splitScalar(u8, data, ':'), + }; + } + + pub fn next(self: *DirIterator) ?[]const u8 { + return self.iterator.next(); + } +}; + +test "xdg dirs" { + const c = @cImport({ + @cInclude("stdlib.h"); + }); + + const testing = std.testing; + { + _ = c.unsetenv(Dir.config.key()); + var it = DirIterator.init(.config); + try testing.expectEqualStrings("/etc/xdg", it.next().?); + try testing.expect(it.next() == null); + } + { + _ = c.unsetenv(Dir.data.key()); + var it = DirIterator.init(.data); + try testing.expectEqualStrings("/usr/local/share", it.next().?); + try testing.expectEqualStrings("/usr/share", it.next().?); + try testing.expect(it.next() == null); + } + { + _ = c.setenv(Dir.config.key(), "a:b:c", 1); + var it = DirIterator.init(.config); + try testing.expectEqualStrings("a", it.next().?); + try testing.expectEqualStrings("b", it.next().?); + try testing.expectEqualStrings("c", it.next().?); + try testing.expect(it.next() == null); + } +} diff --git a/src/termio/Exec.zig b/src/termio/Exec.zig index 1a3b8cad0..3555845fb 100644 --- a/src/termio/Exec.zig +++ b/src/termio/Exec.zig @@ -15,6 +15,7 @@ const configpkg = @import("../config.zig"); const crash = @import("../crash/main.zig"); const fastmem = @import("../fastmem.zig"); const internal_os = @import("../os/main.zig"); +const xdg = internal_os.xdg; const renderer = @import("../renderer.zig"); const shell_integration = @import("shell_integration.zig"); const terminal = @import("../terminal/main.zig"); @@ -801,18 +802,17 @@ const Subprocess = struct { var buf: [std.fs.max_path_bytes]u8 = undefined; - const xdg_data_dir_key = "XDG_DATA_DIRS"; if (std.fmt.bufPrint(&buf, "{s}/..", .{resources_dir})) |data_dir| { try env.put( - xdg_data_dir_key, + xdg.Dir.data.key(), try internal_os.appendEnv( alloc, - env.get(xdg_data_dir_key) orelse "/usr/local/share:/usr/share", + env.get(xdg.Dir.data.key()) orelse xdg.Dir.data.default(), data_dir, ), ); } else |err| { - log.warn("error building {s}; err={}", .{ xdg_data_dir_key, err }); + log.warn("error building {s}; err={}", .{ xdg.Dir.data.key(), err }); } const manpath_key = "MANPATH"; diff --git a/src/termio/shell_integration.zig b/src/termio/shell_integration.zig index 8cd2a92ae..db5a65d71 100644 --- a/src/termio/shell_integration.zig +++ b/src/termio/shell_integration.zig @@ -6,6 +6,7 @@ const EnvMap = std.process.EnvMap; const config = @import("../config.zig"); const homedir = @import("../os/homedir.zig"); const internal_os = @import("../os/main.zig"); +const xdg = internal_os.xdg; const log = std.log.scoped(.shell_integration); @@ -410,12 +411,11 @@ fn setupXdgDataDirs( // This ensures that the default directories aren't lost by setting // our desired integration dir directly. See #2711. // - const xdg_data_dirs_key = "XDG_DATA_DIRS"; try env.put( - xdg_data_dirs_key, + xdg.Dir.data.key(), try internal_os.prependEnv( stack_alloc, - env.get(xdg_data_dirs_key) orelse "/usr/local/share:/usr/share", + env.get(xdg.Dir.data.key()) orelse xdg.Dir.data.default(), integ_dir, ), );