diff options
| author | Bobby <[email protected]> | 2026-02-20 11:42:21 +0530 |
|---|---|---|
| committer | Bobby <[email protected]> | 2026-02-20 11:42:21 +0530 |
| commit | cbadff7321682914e9473134d838b2c268fb6afd (patch) | |
| tree | b5d29d4c11ac0c9147b23bebbf26ce5e06c02d07 | |
| parent | 98d2a091f675f2bf6987098ffe11f765835459c0 (diff) | |
| download | akiba-cbadff7321682914e9473134d838b2c268fb6afd.tar.xz akiba-cbadff7321682914e9473134d838b2c268fb6afd.zip | |
feat: Implement parameter parsing library and integrate into existing binaries
| -rw-r--r-- | Makefile | 19 | ||||
| -rw-r--r-- | binaries/mi/mi.zig | 37 | ||||
| -rw-r--r-- | binaries/mi/mi.zon | 1 | ||||
| -rw-r--r-- | binaries/nav/nav.zig | 32 | ||||
| -rw-r--r-- | binaries/nav/nav.zon | 1 | ||||
| -rw-r--r-- | binaries/rd/rd.zig | 30 | ||||
| -rw-r--r-- | binaries/rd/rd.zon | 1 | ||||
| -rw-r--r-- | binaries/wipe/wipe.zig | 17 | ||||
| -rw-r--r-- | binaries/wipe/wipe.zon | 3 | ||||
| -rw-r--r-- | system/libraries/build.zig | 3 | ||||
| -rw-r--r-- | system/libraries/params/params.zig | 233 | ||||
| -rw-r--r-- | system/libraries/params/params.zon | 6 |
12 files changed, 353 insertions, 30 deletions
@@ -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", +} |
