aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBobby <[email protected]>2026-02-20 11:42:21 +0530
committerBobby <[email protected]>2026-02-20 11:42:21 +0530
commitcbadff7321682914e9473134d838b2c268fb6afd (patch)
treeb5d29d4c11ac0c9147b23bebbf26ce5e06c02d07
parent98d2a091f675f2bf6987098ffe11f765835459c0 (diff)
downloadakiba-cbadff7321682914e9473134d838b2c268fb6afd.tar.xz
akiba-cbadff7321682914e9473134d838b2c268fb6afd.zip
feat: Implement parameter parsing library and integrate into existing binaries
-rw-r--r--Makefile19
-rw-r--r--binaries/mi/mi.zig37
-rw-r--r--binaries/mi/mi.zon1
-rw-r--r--binaries/nav/nav.zig32
-rw-r--r--binaries/nav/nav.zon1
-rw-r--r--binaries/rd/rd.zig30
-rw-r--r--binaries/rd/rd.zon1
-rw-r--r--binaries/wipe/wipe.zig17
-rw-r--r--binaries/wipe/wipe.zon3
-rw-r--r--system/libraries/build.zig3
-rw-r--r--system/libraries/params/params.zig233
-rw-r--r--system/libraries/params/params.zon6
12 files changed, 353 insertions, 30 deletions
diff --git a/Makefile b/Makefile
index b8ccdbb..65c81df 100644
--- a/Makefile
+++ b/Makefile
@@ -150,12 +150,19 @@ prepare-filesystem: build-grub
@echo "→ Building libraries..."
@mkdir -p $(BUILD_DIR)/lib $(FS_ROOT)/system/libraries
- @cd system/libraries && zig build --cache-dir ../../$(BUILD_DIR)/lib-cache --prefix ../../$(BUILD_DIR)
- @for lib in $(BUILD_DIR)/lib/*.a; do \
- if [ -f "$$lib" ]; then \
- libname=$$(basename $$lib .a | sed 's/^lib//'); \
- cp "$$lib" "$(FS_ROOT)/system/libraries/$$libname.arx"; \
- echo " ✓ $$libname.arx"; \
+ @for libdir in system/libraries/*/; do \
+ if [ -d "$$libdir" ]; then \
+ libname=$$(basename $$libdir); \
+ if [ -f "$$libdir$$libname.zig" ]; then \
+ zig build-obj \
+ -target x86_64-freestanding-none \
+ -O ReleaseSmall \
+ "$$libdir$$libname.zig" \
+ -femit-bin="$(BUILD_DIR)/lib/$$libname.o" 2>/dev/null && \
+ ar rcs "$(BUILD_DIR)/lib/lib$$libname.a" "$(BUILD_DIR)/lib/$$libname.o" && \
+ cp "$(BUILD_DIR)/lib/lib$$libname.a" "$(FS_ROOT)/system/libraries/$$libname.arx" && \
+ echo " ✓ $$libname.arx"; \
+ fi; \
fi; \
done
diff --git a/binaries/mi/mi.zig b/binaries/mi/mi.zig
index ca9e46f..837e7c4 100644
--- a/binaries/mi/mi.zig
+++ b/binaries/mi/mi.zig
@@ -3,6 +3,7 @@
const colors = @import("colors");
const format = @import("format");
const io = @import("io");
+const params = @import("params");
const sys = @import("sys");
const PERM_OWNER: u8 = 1;
@@ -10,19 +11,35 @@ const PERM_WORLD: u8 = 2;
const PERM_READ_ONLY: u8 = 3;
export fn main(pc: u32, pv: [*]const [*:0]const u8) u8 {
+ const p = params.parse(pc, pv) catch |err| {
+ format.color("mi: ", colors.red);
+ format.println(@errorName(err));
+ return 1;
+ };
+
var target_path: []const u8 = "";
- if (pc > 1) {
- const arg = pv[1];
- var len: usize = 0;
- while (arg[len] != 0) : (len += 1) {}
- target_path = arg[0..len];
+ if (p.positionals.len > 1) {
+ format.colorln("mi: invalid number of positional parameters.", colors.red);
+ return 1;
}
- display_stack(target_path) catch |err| {
- format.color("mi: ", colors.white);
- format.color(@errorName(err), colors.red);
- format.print("\n");
+ if (p.named.len > 0) {
+ format.colorln("mi: named parameters are not supported.", colors.red);
+ return 1;
+ }
+
+ if (p.positional(0)) |val| {
+ switch (val) {
+ .scalar => |s| target_path = s,
+ .list => {
+ format.colorln("mi: only one location allowed", colors.red);
+ return 1;
+ },
+ }
+ }
+
+ display_stack(target_path) catch {
return 1;
};
@@ -35,7 +52,7 @@ fn display_stack(path: []const u8) !void {
format.color("mi: cannot access '", colors.red);
format.print(path);
format.colorln("': No such stack.", colors.red);
- return;
+ return error.Failed;
};
if (count == 0) {
diff --git a/binaries/mi/mi.zon b/binaries/mi/mi.zon
index 352d31e..7439d47 100644
--- a/binaries/mi/mi.zon
+++ b/binaries/mi/mi.zon
@@ -6,6 +6,7 @@
.io,
.colors,
.format,
+ .params,
},
.entry = "mi.zig",
}
diff --git a/binaries/nav/nav.zig b/binaries/nav/nav.zig
index acdeeb6..dbdcb88 100644
--- a/binaries/nav/nav.zig
+++ b/binaries/nav/nav.zig
@@ -3,12 +3,20 @@
const colors = @import("colors");
const format = @import("format");
const io = @import("io");
+const params = @import("params");
const sys = @import("sys");
var location_buf: [256]u8 = undefined;
export fn main(pc: u32, pv: [*]const [*:0]const u8) u8 {
- if (pc <= 1) {
+ const p = params.parse(pc, pv) catch |err| {
+ format.color("nav: ", colors.red);
+ format.println(@errorName(err));
+ return 1;
+ };
+
+ // No params - print current location
+ if (p.positionals.len == 0) {
const location = io.getlocation(&location_buf) catch {
format.colorln("nav: cannot get current location.", colors.red);
return 1;
@@ -17,10 +25,24 @@ export fn main(pc: u32, pv: [*]const [*:0]const u8) u8 {
return 0;
}
- const arg = pv[1];
- var target_len: usize = 0;
- while (arg[target_len] != 0) : (target_len += 1) {}
- const target = arg[0..target_len];
+ if (p.positionals.len > 1) {
+ format.colorln("nav: invalid number of positional parameters.", colors.red);
+ return 1;
+ }
+
+ if (p.named.len > 0) {
+ format.colorln("nav: named parameters are not supported.", colors.red);
+ return 1;
+ }
+
+ // Get target from first positional
+ const target = switch (p.positional(0).?) {
+ .scalar => |s| s,
+ .list => {
+ format.colorln("nav: only one location allowed", colors.red);
+ return 1;
+ },
+ };
io.setlocation(target) catch {
format.color("nav: cannot navigate to '", colors.red);
diff --git a/binaries/nav/nav.zon b/binaries/nav/nav.zon
index 21d50a4..9fc5bb5 100644
--- a/binaries/nav/nav.zon
+++ b/binaries/nav/nav.zon
@@ -6,6 +6,7 @@
.io,
.colors,
.format,
+ .params,
},
.entry = "nav.zig",
}
diff --git a/binaries/rd/rd.zig b/binaries/rd/rd.zig
index 008cb54..882de3c 100644
--- a/binaries/rd/rd.zig
+++ b/binaries/rd/rd.zig
@@ -3,20 +3,40 @@
const colors = @import("colors");
const format = @import("format");
const io = @import("io");
+const params = @import("params");
const sys = @import("sys");
var file_buffer: [64 * 1024]u8 = undefined;
export fn main(pc: u32, pv: [*]const [*:0]const u8) u8 {
- if (pc <= 1) {
+ const p = params.parse(pc, pv) catch |err| {
+ format.color("rd: ", colors.red);
+ format.println(@errorName(err));
+ return 1;
+ };
+
+ if (p.positionals.len == 0) {
format.colorln("rd: missing unit location.", colors.red);
return 1;
}
- const arg = pv[1];
- var location_len: usize = 0;
- while (arg[location_len] != 0) : (location_len += 1) {}
- const location = arg[0..location_len];
+ if (p.positionals.len > 1) {
+ format.colorln("rd: invalid number of positional parameters.", colors.red);
+ return 1;
+ }
+
+ if (p.named.len > 0) {
+ format.colorln("rd: named parameters are not supported.", colors.red);
+ return 1;
+ }
+
+ const location = switch (p.positional(0).?) {
+ .scalar => |s| s,
+ .list => {
+ format.colorln("rd: only one location allowed", colors.red);
+ return 1;
+ },
+ };
const fd = io.attach(location, io.VIEW_ONLY) catch {
format.color("rd: cannot access '", colors.red);
diff --git a/binaries/rd/rd.zon b/binaries/rd/rd.zon
index 47cddc8..a180566 100644
--- a/binaries/rd/rd.zon
+++ b/binaries/rd/rd.zon
@@ -6,6 +6,7 @@
.io,
.colors,
.format,
+ .params,
},
.entry = "rd.zig",
}
diff --git a/binaries/wipe/wipe.zig b/binaries/wipe/wipe.zig
index 8bdc336..6cc82a2 100644
--- a/binaries/wipe/wipe.zig
+++ b/binaries/wipe/wipe.zig
@@ -1,11 +1,24 @@
//! wipe - Clear the terminal screen
+const colors = @import("colors");
+const format = @import("format");
const io = @import("io");
+const params = @import("params");
const sys = @import("sys");
export fn main(pc: u32, pv: [*]const [*:0]const u8) u8 {
- _ = pc;
- _ = pv;
+ const p = params.parse(pc, pv) catch return 1;
+
+ if (p.positionals.len > 0) {
+ format.colorln("wipe: positional parameters are not supported.", colors.red);
+ return 1;
+ }
+
+ if (p.named.len > 0) {
+ format.colorln("wipe: named parameters are not supported.", colors.red);
+ return 1;
+ }
+
io.wipe();
return 0;
}
diff --git a/binaries/wipe/wipe.zon b/binaries/wipe/wipe.zon
index 4c90d6c..b4f09bc 100644
--- a/binaries/wipe/wipe.zon
+++ b/binaries/wipe/wipe.zon
@@ -2,8 +2,11 @@
.name = "wipe",
.version = "0.1.0",
.dependencies = .{
+ .colors,
+ .format,
.sys,
.io,
+ .params,
},
.entry = "wipe.zig",
}
diff --git a/system/libraries/build.zig b/system/libraries/build.zig
index a38cbc3..50c3071 100644
--- a/system/libraries/build.zig
+++ b/system/libraries/build.zig
@@ -14,9 +14,8 @@ pub fn build(b: *std.Build) void {
while (lib_iter.next() catch null) |entry| {
if (entry.kind != .directory) continue;
- const lib_name = entry.name;
+ const lib_name = b.allocator.dupe(u8, entry.name) catch continue;
const lib_file = std.fmt.allocPrint(b.allocator, "{s}/{s}.zig", .{ lib_name, lib_name }) catch continue;
- defer b.allocator.free(lib_file);
std.fs.cwd().access(lib_file, .{}) catch continue;
diff --git a/system/libraries/params/params.zig b/system/libraries/params/params.zig
new file mode 100644
index 0000000..d7e5ba1
--- /dev/null
+++ b/system/libraries/params/params.zig
@@ -0,0 +1,233 @@
+//! Akiba parameter parsing
+
+pub const Value = union(enum) {
+ scalar: []const u8,
+ list: []const []const u8,
+};
+
+pub const Named = struct {
+ key: []const u8,
+ value: Value,
+};
+
+pub const Params = struct {
+ positionals: []const Value,
+ named: []const Named,
+
+ pub fn positional(self: Params, index: usize) ?Value {
+ if (index >= self.positionals.len) return null;
+ return self.positionals[index];
+ }
+
+ pub fn get(self: Params, key: []const u8) ?Value {
+ for (self.named) |n| {
+ if (eql(n.key, key)) return n.value;
+ }
+ return null;
+ }
+
+ pub fn getString(self: Params, key: []const u8) ?[]const u8 {
+ const val = self.get(key) orelse return null;
+ return switch (val) {
+ .scalar => |s| s,
+ .list => null,
+ };
+ }
+
+ pub fn getList(self: Params, key: []const u8) ?[]const []const u8 {
+ const val = self.get(key) orelse return null;
+ return switch (val) {
+ .scalar => null,
+ .list => |l| l,
+ };
+ }
+
+ pub fn getBool(self: Params, key: []const u8) ?bool {
+ const val = self.getString(key) orelse return null;
+ if (eql(val, "true")) return true;
+ if (eql(val, "false")) return false;
+ return null;
+ }
+
+ pub fn getInt(self: Params, key: []const u8) ?i64 {
+ const val = self.getString(key) orelse return null;
+ return parseInt(val);
+ }
+};
+
+pub const Error = error{
+ EmptyToken,
+ EmptyKey,
+ EmptyValue,
+ InvalidKey,
+ DuplicateKey,
+ PositionalAfterNamed,
+ LeadingComma,
+ TrailingComma,
+};
+
+const MAX_POSITIONALS = 16;
+const MAX_NAMED = 32;
+const MAX_LIST_ITEMS = 16;
+
+var positional_storage: [MAX_POSITIONALS]Value = undefined;
+var named_storage: [MAX_NAMED]Named = undefined;
+var list_storage: [MAX_POSITIONALS + MAX_NAMED][MAX_LIST_ITEMS][]const u8 = undefined;
+var list_index: usize = 0;
+
+pub fn parse(pc: u32, pv: [*]const [*:0]const u8) Error!Params {
+ var positional_count: usize = 0;
+ var named_count: usize = 0;
+ var in_named = false;
+ list_index = 0;
+
+ // Skip program name (pv[0])
+ var i: u32 = 1;
+ while (i < pc) : (i += 1) {
+ const arg = sliceFromCstr(pv[i]);
+
+ if (arg.len == 0) return Error.EmptyToken;
+
+ const eq_pos = indexOf(arg, '=');
+
+ if (eq_pos) |pos| {
+ // Named parameter
+ in_named = true;
+
+ if (pos == 0) return Error.EmptyKey;
+ if (pos == arg.len - 1) return Error.EmptyValue;
+
+ const key = arg[0..pos];
+ const value_str = arg[pos + 1 ..];
+
+ if (!isValidKey(key)) return Error.InvalidKey;
+ if (hasDuplicateKey(named_storage[0..named_count], key)) return Error.DuplicateKey;
+
+ const value = try parseValue(value_str);
+ named_storage[named_count] = .{ .key = key, .value = value };
+ named_count += 1;
+ } else {
+ // Positional
+ if (in_named) return Error.PositionalAfterNamed;
+
+ const value = try parseValue(arg);
+ positional_storage[positional_count] = value;
+ positional_count += 1;
+ }
+ }
+
+ return Params{
+ .positionals = positional_storage[0..positional_count],
+ .named = named_storage[0..named_count],
+ };
+}
+
+fn parseValue(str: []const u8) Error!Value {
+ if (str.len == 0) return Error.EmptyValue;
+ if (str[0] == ',') return Error.LeadingComma;
+ if (str[str.len - 1] == ',') return Error.TrailingComma;
+
+ // Check for commas
+ var comma_count: usize = 0;
+ for (str) |c| {
+ if (c == ',') comma_count += 1;
+ }
+
+ if (comma_count == 0) {
+ return Value{ .scalar = str };
+ }
+
+ // Parse list
+ var items: [][]const u8 = list_storage[list_index][0..];
+ var item_count: usize = 0;
+ var start: usize = 0;
+
+ for (str, 0..) |c, j| {
+ if (c == ',') {
+ if (j == start) return Error.LeadingComma; // Empty segment
+ items[item_count] = str[start..j];
+ item_count += 1;
+ start = j + 1;
+ }
+ }
+
+ // Last item
+ if (start == str.len) return Error.TrailingComma;
+ items[item_count] = str[start..];
+ item_count += 1;
+
+ list_index += 1;
+
+ return Value{ .list = items[0..item_count] };
+}
+
+fn isValidKey(key: []const u8) bool {
+ if (key.len == 0) return false;
+
+ const first = key[0];
+ if (!isLetter(first)) return false;
+
+ for (key[1..]) |c| {
+ if (!isLetter(c) and !isDigit(c) and c != '_') return false;
+ }
+
+ return true;
+}
+
+fn isLetter(c: u8) bool {
+ return (c >= 'a' and c <= 'z') or (c >= 'A' and c <= 'Z');
+}
+
+fn isDigit(c: u8) bool {
+ return c >= '0' and c <= '9';
+}
+
+fn hasDuplicateKey(named: []const Named, key: []const u8) bool {
+ for (named) |n| {
+ if (eql(n.key, key)) return true;
+ }
+ return false;
+}
+
+fn indexOf(str: []const u8, char: u8) ?usize {
+ for (str, 0..) |c, i| {
+ if (c == char) return i;
+ }
+ return null;
+}
+
+fn eql(a: []const u8, b: []const u8) bool {
+ if (a.len != b.len) return false;
+ for (a, b) |ac, bc| {
+ if (ac != bc) return false;
+ }
+ return true;
+}
+
+fn sliceFromCstr(cstr: [*:0]const u8) []const u8 {
+ var len: usize = 0;
+ while (cstr[len] != 0) : (len += 1) {}
+ return cstr[0..len];
+}
+
+fn parseInt(str: []const u8) ?i64 {
+ if (str.len == 0) return null;
+
+ var negative = false;
+ var start: usize = 0;
+
+ if (str[0] == '-') {
+ negative = true;
+ start = 1;
+ }
+
+ if (start >= str.len) return null;
+
+ var result: i64 = 0;
+ for (str[start..]) |c| {
+ if (!isDigit(c)) return null;
+ result = result * 10 + @as(i64, c - '0');
+ }
+
+ return if (negative) -result else result;
+}
diff --git a/system/libraries/params/params.zon b/system/libraries/params/params.zon
new file mode 100644
index 0000000..b629ab3
--- /dev/null
+++ b/system/libraries/params/params.zon
@@ -0,0 +1,6 @@
+.{
+ .name = "params",
+ .version = "1.0.0",
+ .type = .library,
+ .entry = "params.zig",
+}