const std = @import("std"); const cdl = @import("root.zig"); /// owned pub fn hostname() ![]const u8 { var buf: [std.posix.HOST_NAME_MAX]u8 = undefined; const hostname_buf = try std.posix.gethostname(&buf); return try cdl.gpa.dupe(u8, hostname_buf); } pub fn user() !?[]const u8 { const pwinfo = try PWInfo.get(); if (pwinfo) |pw| return pw.user; if (cdl.environ_map) |envmap| return envmap.get("USER"); if (cdl.environ_map) |envmap| return envmap.get("LOGNAME"); return null; } pub fn shell() !?[]const u8 { const shell_str = blk: { const pwinfo = try PWInfo.get(); if (pwinfo) |pw| break :blk pw.shell; break :blk if (cdl.environ_map) |envmap| envmap.get("SHELL") else null; } orelse return null; if (std.mem.cutScalarLast(u8, shell_str, '/')) |parts| return parts[1]; return shell_str; } pub fn osname() !?[]const u8 { const f = try std.Io.Dir.openFileAbsolute(cdl.io, "/etc/os-release", .{}); var buf: [0x1000]u8 = undefined; var file_reader = f.reader(cdl.io, &buf); var reader = &file_reader.interface; while (try reader.takeDelimiter('\n')) |line| if (std.mem.cutPrefix(u8, line, "NAME=")) |name| return try cdl.allocator.dupe(u8, trimquotes(name)); return null; } pub fn desktop() ?[]const u8 { if (cdl.environ_map) |envmap| return envmap.get("XDG_CURRENT_DESKTOP"); return null; } pub fn session() ?[]const u8 { if (cdl.environ_map) |envmap| return envmap.get("XDG_SESSION_TYPE"); return null; } pub fn terminal() ?[]const u8 { if (cdl.environ_map) |envmap| return envmap.get("TERMINAL"); const nterm: ?[]const u8 = blk: { const termenv = if (cdl.environ_map) |envmap| envmap.get("TERM") else null; if (termenv) |term| if (term.len > 0) if (std.mem.findScalar(u8, term, '-')) |i| break :blk term[0..i] else break :blk term; break :blk null; }; if (nterm) |term| if (!std.mem.eql(u8, term, "xterm") and !std.mem.eql(u8, term, "linux")) return term; return null; } pub fn editor() ?[]const u8 { if (cdl.environ_map) |envmap| return envmap.get("EDITOR"); return null; } const PWInfo = struct { user: ?[]const u8, shell: ?[]const u8, dir: ?[]const u8, var cached: ?@This() = null; pub fn get() !?PWInfo { if (PWInfo.cached) |c| return c; const f = try std.Io.Dir.openFileAbsolute(cdl.io, "/etc/passwd", .{}); var buf: [0x1000]u8 = undefined; var file_reader = f.reader(cdl.io, &buf); var reader = &file_reader.interface; const uid = std.os.linux.getuid(); w: while (try reader.takeDelimiter('\n')) |line| { var parts = std.mem.splitScalar(u8, line, ':'); var pw_user: ?[]const u8 = null; var pw_shell: ?[]const u8 = null; var pw_dir: ?[]const u8 = null; var i: usize = 0; while (parts.next()) |part| : (i = i + 1) switch (i) { 0 => pw_user = part, 2 => if (try std.fmt.parseInt(u32, part, 10) != uid) continue :w, 5 => pw_dir = part, 6 => pw_shell = part, else => {}, }; return .{ .user = if (pw_user) |user_| try cdl.allocator.dupe(u8, user_) else null, .shell = if (pw_shell) |shell_| try cdl.allocator.dupe(u8, shell_) else null, .dir = if (pw_dir) |dir_| try cdl.allocator.dupe(u8, dir_) else null, }; } return null; } }; // helpers ==================================================================== fn trimquotes(s: []const u8) []const u8 { if (s.len < 2) return s; const open = switch (s[0]) { '"' => s[0], '\'' => s[0], else => return s, }; // assume malformed if (s[s.len - 1] != open) return s[1..]; return s[1 .. s.len - 1]; } test "trim quotes" { try std.testing.expectEqualStrings("meow", trimquotes("meow")); try std.testing.expectEqualStrings("meow", trimquotes( \\"meow" )); try std.testing.expectEqualStrings("meow", trimquotes( \\'meow' )); try std.testing.expectEqualStrings("meow", trimquotes( \\'meow )); try std.testing.expectEqualStrings("meow", trimquotes( \\"meow )); }