inital commit, still got some memory leaks but who cares

This commit is contained in:
Squibid 2025-11-05 13:26:29 -05:00
commit a68e6e67b1
Signed by: squibid
GPG key ID: BECE5684D3C4005D
19 changed files with 1471 additions and 0 deletions

7
src/config.toml Normal file
View file

@ -0,0 +1,7 @@
entries = [
{ m = "hostname", f = "fig" },
{ m = "distro" },
{ m = "kernel" },
{ m = "load" },
{ m = "uptime" },
]

5
src/formatter.zig Normal file
View file

@ -0,0 +1,5 @@
const formatter = @This();
name: []const u8,
/// takes an input value and returns it formatted
callback: *const fn(input: []const u8) anyerror![]const u8,

18
src/formatters/fig.zig Normal file
View file

@ -0,0 +1,18 @@
const std = @import("std");
const ziglet = @import("ziglet");
const formatter = @import("../formatter.zig");
const gpa = std.heap.page_allocator;
fn cb(input: []const u8) ![]const u8 {
const font_buffer: []const u8 = @embedFile("small.flf");
var font = try ziglet.DefaultFont.init(gpa, font_buffer);
defer font.deinit(gpa);
const result = try font.formatter().formatText(gpa, input, .{});
return result;
}
pub const fig = formatter {
.name = "fig",
.callback = cb
};

5
src/formatters/init.zig Normal file
View file

@ -0,0 +1,5 @@
const formatter = @import("../formatter.zig");
pub const formatters = [_]formatter {
@import("fig.zig").fig,
};

1097
src/formatters/small.flf Normal file

File diff suppressed because it is too large Load diff

80
src/main.zig Normal file
View file

@ -0,0 +1,80 @@
const std = @import("std");
const toml = @import("toml");
const formatters = @import("formatters/init.zig");
const formatter = @import("formatter.zig");
const modules = @import("modules/init.zig");
const module = @import("module.zig");
const gpa = std.heap.page_allocator;
const Entry = struct {
m: []const u8, // modifier
f: ?[]const u8, // format
};
const Config = struct {
entries: []const Entry,
};
pub fn main() !void {
var parser = toml.Parser(Config).init(gpa);
defer parser.deinit();
var args = std.process.args();
_ = args.next(); // skip the first argument
const config_path = args.next(); // use the second argument as the config path
var result: ?toml.Parsed(Config) = null;
if (config_path) |c| {
result = parser.parseFile(c) catch null;
}
if (result == null) {
result = try parser.parseString(@embedFile("config.toml"));
}
defer result.?.deinit();
// setup the stdout printer which is setup to only print on exit
var buf: [1024]u8 = undefined;
var stdout_writer = std.fs.File.stdout().writer(&buf);
const stdout = &stdout_writer.interface;
defer stdout.flush() catch {};
// print out all modules in order based on the config
const config = result.?.value;
for (config.entries) |entry| {
var mod: module = undefined;
var fmt: ?formatter = null;
// modules
for (modules.modules) |m| {
if (std.mem.eql(u8, entry.m, m.name)) {
mod = m;
break;
}
}
// formats
if (entry.f) |format| {
for (formatters.formatters) |f| {
if (std.mem.eql(u8, format, f.name)) {
fmt = f;
break;
}
}
}
// get the data and format if needed
const tmp = try mod.callback();
var txt: []const u8 = tmp;
if (fmt) |f| {
txt = try f.callback(tmp);
}
// print the module
try stdout.print("{s}{s}\n", .{
if (fmt) |_| "" else mod.prefix,
txt,
});
}
}

7
src/module.zig Normal file
View file

@ -0,0 +1,7 @@
const module = @This();
name: []const u8,
/// the prefix to use when displaying this module without any formatting
prefix: []const u8,
/// returns information
callback: *const fn() anyerror![]const u8,

36
src/modules/distro.zig Normal file
View file

@ -0,0 +1,36 @@
const std = @import("std");
const module = @import("../module.zig");
const gpa = std.heap.page_allocator;
fn cb() anyerror![]const u8 {
var file = try std.fs.openFileAbsolute("/etc/os-release", .{});
defer file.close();
const contents = try file.readToEndAlloc(gpa, 1024); // read max 1024 bytes
defer gpa.free(contents);
var lines = std.mem.splitSequence(u8, contents, "\n");
while (lines.next()) |line| {
if (std.mem.startsWith(u8, line, "NAME=")) {
// Strip 'NAME=' and optional quotes
var name_line = line[5..];
if (name_line.len >= 2 and name_line[0] == '"' and name_line[name_line.len - 1] == '"') {
name_line = name_line[1..name_line.len - 1];
}
// Allocate copy to return
const os_name = try gpa.alloc(u8, name_line.len);
std.mem.copyForwards(u8, os_name, name_line);
return os_name;
}
}
return "Unknown OS";
}
pub const distro = module {
.name = "distro",
.prefix = "Distro: ",
.callback = cb
};

19
src/modules/hostname.zig Normal file
View file

@ -0,0 +1,19 @@
const std = @import("std");
const c = @cImport({ @cInclude("unistd.h"); });
const module = @import("../module.zig");
fn cb() ![]const u8 {
var file = try std.fs.openFileAbsolute("/etc/hostname", .{});
defer file.close();
const max_hostname_size = c.sysconf(c._SC_HOST_NAME_MAX);
const buffer = try std.heap.page_allocator.alloc(u8, @intCast(max_hostname_size));
const bytes_read = try file.read(buffer);
return buffer[0..bytes_read];
}
pub const hostname = module {
.name = "hostname",
.prefix = "Hostname: ",
.callback = cb
};

9
src/modules/init.zig Normal file
View file

@ -0,0 +1,9 @@
const module = @import("../module.zig");
pub const modules = [_]module {
@import("distro.zig").distro,
@import("hostname.zig").hostname,
@import("kernel.zig").kernel,
@import("load.zig").load,
@import("uptime.zig").uptime,
};

25
src/modules/kernel.zig Normal file
View file

@ -0,0 +1,25 @@
const std = @import("std");
const module = @import("../module.zig");
const gpa = std.heap.page_allocator;
fn cb() ![]const u8 {
var uname_buf: std.os.linux.utsname = undefined;
_ = std.os.linux.uname(&uname_buf);
// Find the null-terminated end of the release string
var len: usize = 0;
while (uname_buf.release[len] != 0) : (len += 1) {}
// Allocate and copy the string
const kernel_str = try gpa.alloc(u8, len);
std.mem.copyForwards(u8, kernel_str, uname_buf.release[0..len]);
return kernel_str;
}
pub const kernel = module {
.name = "kernel",
.prefix = "Kernel: ",
.callback = cb
};

28
src/modules/load.zig Normal file
View file

@ -0,0 +1,28 @@
const std = @import("std");
const c = @cImport({ @cInclude("stdlib.h"); });
const module = @import("../module.zig");
const gpa = std.heap.page_allocator;
fn cb() ![]const u8 {
const load_nr = 3;
const loads: []f64 = try gpa.alloc(f64, load_nr);
_ = c.getloadavg(loads.ptr, load_nr);
var list = try std.ArrayList(u8).initCapacity(gpa, 4);
defer list.deinit(gpa);
var writer = list.writer(gpa);
var i: u8 = 0;
while (i < load_nr) : (i += 1) {
try writer.print("{d:.2}{s}", .{loads[i], if (i != load_nr - 1) ", " else ""});
}
return list.toOwnedSlice(gpa);
}
pub const load = module {
.name = "load",
.prefix = "Load: ",
.callback = cb
};

25
src/modules/uptime.zig Normal file
View file

@ -0,0 +1,25 @@
const std = @import("std");
const c = @cImport({ @cInclude("unistd.h"); });
const utils = @import("../utils.zig");
const module = @import("../module.zig");
fn cb() ![]const u8 {
var file = try std.fs.cwd().openFile("/proc/uptime", .{});
defer file.close();
var buf: [64]u8 = undefined;
const bytes_read = try file.readAll(&buf);
const data = buf[0..bytes_read];
var tok = std.mem.tokenizeAny(u8, data, " ");
const first_tok = tok.next() orelse return error.InvalidFormat;
const uptime_seconds = try std.fmt.parseFloat(f64, first_tok);
return try utils.secs_to_time(@intFromFloat(uptime_seconds));
}
pub const uptime = module {
.name = "uptime",
.prefix = "Uptime: ",
.callback = cb
};

35
src/utils.zig Normal file
View file

@ -0,0 +1,35 @@
const std = @import("std");
const gpa = std.heap.page_allocator;
pub fn secs_to_time(secs: u64) ![]const u8 {
var list = try std.ArrayList(u8).initCapacity(gpa, 4);
defer list.deinit(gpa);
var writer = list.writer(gpa);
var seconds = secs;
const minute = 1 * 60;
const hour = minute * 60;
const day = hour * 24;
const week = day * 7;
const weeks = seconds / week;
seconds = seconds % week;
const days = seconds / day;
seconds = seconds % day;
const hours = seconds / hour;
seconds = seconds % hour;
const minutes = seconds / minute;
seconds = seconds % minute;
if (weeks > 0) try writer.print("{} {s}{s}, ", .{weeks, "week", if (weeks > 1) "s" else ""});
if (days > 0) try writer.print("{} {s}{s}, ", .{days, "day", if (days > 1) "s" else ""});
if (hours > 0) try writer.print("{} {s}{s}, ", .{hours, "hour", if (hours > 1) "s" else ""});
if (minutes > 0) try writer.print("{} {s}{s}", .{minutes, "minute", if (minutes > 1) "s" else ""});
if (weeks == 0 and days == 0 and hours == 0 and minutes == 0) {
try writer.print("{} {s}{s}", .{seconds, "second", if (seconds > 1) "s" else ""});
}
return try list.toOwnedSlice(gpa);
}