aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBobby <[email protected]>2026-02-15 21:44:15 +0530
committerBobby <[email protected]>2026-02-15 21:44:15 +0530
commit85ea61c58c96b5ae052e40fce777f8e3d2b7ca62 (patch)
treeab7e84dd64e8600d6c102ed12a8f598962b88aff
parent71e5250b05d06505612dc5a0fcf3d25fb5277cf5 (diff)
downloadakiba-85ea61c58c96b5ae052e40fce777f8e3d2b7ca62.tar.xz
akiba-85ea61c58c96b5ae052e40fce777f8e3d2b7ca62.zip
feat: Add core libraries for color, formatting, I/O, string manipulation, and memory utilities
-rw-r--r--system/libraries/colors/colors.zig12
-rw-r--r--system/libraries/colors/colors.zon6
-rw-r--r--system/libraries/format/date.zig108
-rw-r--r--system/libraries/format/format.zig17
-rw-r--r--system/libraries/format/format.zon10
-rw-r--r--system/libraries/format/int.zig25
-rw-r--r--system/libraries/format/print.zig94
-rw-r--r--system/libraries/format/size.zig33
-rw-r--r--system/libraries/io/attachment.zig33
-rw-r--r--system/libraries/io/io.zig36
-rw-r--r--system/libraries/io/io.zon10
-rw-r--r--system/libraries/io/letter.zig36
-rw-r--r--system/libraries/io/location.zig30
-rw-r--r--system/libraries/io/stream.zig41
-rw-r--r--system/libraries/io/types.zig39
-rw-r--r--system/libraries/kata/kata.zig45
-rw-r--r--system/libraries/kata/kata.zon9
-rw-r--r--system/libraries/mem/mem.zig22
-rw-r--r--system/libraries/mem/mem.zon6
-rw-r--r--system/libraries/string/builder.zig48
-rw-r--r--system/libraries/string/cstring.zig17
-rw-r--r--system/libraries/string/location.zig59
-rw-r--r--system/libraries/string/string.zig16
-rw-r--r--system/libraries/string/string.zon6
-rw-r--r--system/libraries/sys/start.zig16
-rw-r--r--system/libraries/sys/sys.zig58
-rw-r--r--system/libraries/sys/sys.zon6
27 files changed, 838 insertions, 0 deletions
diff --git a/system/libraries/colors/colors.zig b/system/libraries/colors/colors.zig
new file mode 100644
index 0000000..1e151da
--- /dev/null
+++ b/system/libraries/colors/colors.zig
@@ -0,0 +1,12 @@
+//! Display colors
+
+pub const white: u32 = 0x00FFFFFF;
+pub const black: u32 = 0x00000000;
+pub const red: u32 = 0x00FF4444;
+pub const green: u32 = 0x0088FF88;
+pub const blue: u32 = 0x004488DD;
+pub const yellow: u32 = 0x00DDDD00;
+pub const cyan: u32 = 0x0000FFFF;
+pub const magenta: u32 = 0x00FF00FF;
+pub const gray: u32 = 0x00888888;
+pub const purple: u32 = 0x00BB88FF;
diff --git a/system/libraries/colors/colors.zon b/system/libraries/colors/colors.zon
new file mode 100644
index 0000000..8a0a157
--- /dev/null
+++ b/system/libraries/colors/colors.zon
@@ -0,0 +1,6 @@
+.{
+ .name = "colors",
+ .version = "1.0.0",
+ .type = .library,
+ .entry = "colors.zig",
+}
diff --git a/system/libraries/format/date.zig b/system/libraries/format/date.zig
new file mode 100644
index 0000000..d90adec
--- /dev/null
+++ b/system/libraries/format/date.zig
@@ -0,0 +1,108 @@
+//! Date formatting
+
+const int = @import("int.zig");
+
+const SECONDS_PER_DAY: u64 = 86400;
+const SECONDS_PER_HOUR: u64 = 3600;
+const SECONDS_PER_MINUTE: u64 = 60;
+const DAYS_PER_4_YEARS: u64 = 1461;
+
+const MONTH_NAMES = [_][]const u8{ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
+const DAYS_NORMAL = [_]u64{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+const DAYS_LEAP = [_]u64{ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+
+pub fn format(timestamp: u64, buf: []u8) []u8 {
+ if (timestamp == 0) {
+ const default = "01 Jan 1970 00:00";
+ for (default, 0..) |c, i| {
+ buf[i] = c;
+ }
+ return buf[0..default.len];
+ }
+
+ const days_since_epoch = timestamp / SECONDS_PER_DAY;
+ const seconds_today = timestamp % SECONDS_PER_DAY;
+ const hours = seconds_today / SECONDS_PER_HOUR;
+ const minutes = (seconds_today % SECONDS_PER_HOUR) / SECONDS_PER_MINUTE;
+
+ var year: u64 = 1970;
+ var remaining_days = days_since_epoch;
+
+ const four_year_cycles = remaining_days / DAYS_PER_4_YEARS;
+ year += four_year_cycles * 4;
+ remaining_days = remaining_days % DAYS_PER_4_YEARS;
+
+ while (remaining_days >= 365) {
+ const is_leap = isLeapYear(year);
+ const days_this_year: u64 = if (is_leap) 366 else 365;
+ if (remaining_days >= days_this_year) {
+ remaining_days -= days_this_year;
+ year += 1;
+ } else {
+ break;
+ }
+ }
+
+ const is_leap = isLeapYear(year);
+ const days_in_months = if (is_leap) DAYS_LEAP else DAYS_NORMAL;
+
+ var month: usize = 0;
+ var day: u64 = remaining_days + 1;
+
+ for (days_in_months, 0..) |days, m| {
+ if (day <= days) {
+ month = m;
+ break;
+ }
+ day -= days;
+ }
+
+ var pos: usize = 0;
+
+ if (day < 10) {
+ buf[pos] = '0';
+ pos += 1;
+ }
+ const day_str = int.toStr(day, buf[pos..]);
+ pos += day_str.len;
+
+ buf[pos] = ' ';
+ pos += 1;
+
+ for (MONTH_NAMES[month]) |c| {
+ buf[pos] = c;
+ pos += 1;
+ }
+
+ buf[pos] = ' ';
+ pos += 1;
+
+ const year_str = int.toStr(year, buf[pos..]);
+ pos += year_str.len;
+
+ buf[pos] = ' ';
+ pos += 1;
+
+ if (hours < 10) {
+ buf[pos] = '0';
+ pos += 1;
+ }
+ const hour_str = int.toStr(hours, buf[pos..]);
+ pos += hour_str.len;
+
+ buf[pos] = ':';
+ pos += 1;
+
+ if (minutes < 10) {
+ buf[pos] = '0';
+ pos += 1;
+ }
+ const min_str = int.toStr(minutes, buf[pos..]);
+ pos += min_str.len;
+
+ return buf[0..pos];
+}
+
+fn isLeapYear(year: u64) bool {
+ return (year % 4 == 0 and year % 100 != 0) or (year % 400 == 0);
+}
diff --git a/system/libraries/format/format.zig b/system/libraries/format/format.zig
new file mode 100644
index 0000000..f764ebd
--- /dev/null
+++ b/system/libraries/format/format.zig
@@ -0,0 +1,17 @@
+//! Formatting utilities
+
+pub const int = @import("int.zig");
+pub const size = @import("size.zig");
+pub const date = @import("date.zig");
+pub const printmod = @import("print.zig");
+
+pub const intToStr = int.toStr;
+pub const formatSize = size.format;
+pub const formatDate = date.format;
+
+pub const print = printmod.print;
+pub const println = printmod.println;
+pub const printf = printmod.printf;
+pub const color = printmod.color;
+pub const colorln = printmod.colorln;
+pub const colorf = printmod.colorf;
diff --git a/system/libraries/format/format.zon b/system/libraries/format/format.zon
new file mode 100644
index 0000000..2fff0a7
--- /dev/null
+++ b/system/libraries/format/format.zon
@@ -0,0 +1,10 @@
+.{
+ .name = "format",
+ .version = "1.0.0",
+ .type = .library,
+ .entry = "format.zig",
+ .dependencies = .{
+ .io = .{ .path = "../io" },
+ .colors = .{ .path = "../colors" },
+ },
+}
diff --git a/system/libraries/format/int.zig b/system/libraries/format/int.zig
new file mode 100644
index 0000000..17cf4c3
--- /dev/null
+++ b/system/libraries/format/int.zig
@@ -0,0 +1,25 @@
+//! Integer formatting
+
+pub fn toStr(num: u64, buf: []u8) []u8 {
+ if (num == 0) {
+ buf[0] = '0';
+ return buf[0..1];
+ }
+
+ var n = num;
+ var i: usize = 0;
+
+ while (n > 0) : (i += 1) {
+ buf[i] = @as(u8, @intCast(n % 10)) + '0';
+ n /= 10;
+ }
+
+ var j: usize = 0;
+ while (j < i / 2) : (j += 1) {
+ const tmp = buf[j];
+ buf[j] = buf[i - 1 - j];
+ buf[i - 1 - j] = tmp;
+ }
+
+ return buf[0..i];
+}
diff --git a/system/libraries/format/print.zig b/system/libraries/format/print.zig
new file mode 100644
index 0000000..473d651
--- /dev/null
+++ b/system/libraries/format/print.zig
@@ -0,0 +1,94 @@
+//! Print utilities
+
+const colors = @import("colors");
+const io = @import("io");
+
+pub fn print(text: []const u8) void {
+ _ = io.mark(io.stream, text, colors.white) catch {};
+}
+
+pub fn println(text: []const u8) void {
+ _ = io.mark(io.stream, text, colors.white) catch {};
+ _ = io.mark(io.stream, "\n", colors.white) catch {};
+}
+
+pub fn color(text: []const u8, col: u32) void {
+ _ = io.mark(io.stream, text, col) catch {};
+}
+
+pub fn colorln(text: []const u8, col: u32) void {
+ _ = io.mark(io.stream, text, col) catch {};
+ _ = io.mark(io.stream, "\n", colors.white) catch {};
+}
+
+pub fn printf(comptime fmt: []const u8, args: anytype, buf: []u8) void {
+ const len = formatBuf(fmt, args, buf);
+ _ = io.mark(io.stream, buf[0..len], colors.white) catch {};
+}
+
+pub fn colorf(comptime fmt: []const u8, args: anytype, buf: []u8, col: u32) void {
+ const len = formatBuf(fmt, args, buf);
+ _ = io.mark(io.stream, buf[0..len], col) catch {};
+}
+
+fn formatBuf(comptime fmt: []const u8, args: anytype, buf: []u8) usize {
+ var pos: usize = 0;
+ comptime var arg_idx: usize = 0;
+
+ comptime var i: usize = 0;
+ inline while (i < fmt.len) {
+ if (fmt[i] == '{' and i + 1 < fmt.len and fmt[i + 1] == '}') {
+ const arg = args[arg_idx];
+ const T = @TypeOf(arg);
+
+ if (T == []const u8 or T == []u8) {
+ for (arg) |c| {
+ if (pos >= buf.len) break;
+ buf[pos] = c;
+ pos += 1;
+ }
+ } else if (@typeInfo(T) == .int or @typeInfo(T) == .comptime_int) {
+ pos += writeInt(@intCast(arg), buf[pos..]);
+ }
+
+ arg_idx += 1;
+ i += 2;
+ } else {
+ if (pos < buf.len) {
+ buf[pos] = fmt[i];
+ pos += 1;
+ }
+ i += 1;
+ }
+ }
+
+ return pos;
+}
+
+fn writeInt(num: u64, buf: []u8) usize {
+ if (num == 0) {
+ if (buf.len > 0) {
+ buf[0] = '0';
+ return 1;
+ }
+ return 0;
+ }
+
+ var temp: [20]u8 = undefined;
+ var n = num;
+ var i: usize = 0;
+
+ while (n > 0) : (i += 1) {
+ temp[i] = @as(u8, @intCast(n % 10)) + '0';
+ n /= 10;
+ }
+
+ var written: usize = 0;
+ while (i > 0 and written < buf.len) {
+ i -= 1;
+ buf[written] = temp[i];
+ written += 1;
+ }
+
+ return written;
+}
diff --git a/system/libraries/format/size.zig b/system/libraries/format/size.zig
new file mode 100644
index 0000000..a38c0c8
--- /dev/null
+++ b/system/libraries/format/size.zig
@@ -0,0 +1,33 @@
+//! Size formatting
+
+const int = @import("int.zig");
+
+const KB: u64 = 1024;
+const MB: u64 = 1024 * 1024;
+const GB: u64 = 1024 * 1024 * 1024;
+
+pub fn format(bytes: u64, buf: []u8) []u8 {
+ if (bytes < KB) {
+ const s = int.toStr(bytes, buf);
+ buf[s.len] = 'B';
+ return buf[0 .. s.len + 1];
+ } else if (bytes < MB) {
+ const kb = bytes / KB;
+ const s = int.toStr(kb, buf);
+ buf[s.len] = 'K';
+ buf[s.len + 1] = 'B';
+ return buf[0 .. s.len + 2];
+ } else if (bytes < GB) {
+ const mb = bytes / MB;
+ const s = int.toStr(mb, buf);
+ buf[s.len] = 'M';
+ buf[s.len + 1] = 'B';
+ return buf[0 .. s.len + 2];
+ } else {
+ const gb = bytes / GB;
+ const s = int.toStr(gb, buf);
+ buf[s.len] = 'G';
+ buf[s.len + 1] = 'B';
+ return buf[0 .. s.len + 2];
+ }
+}
diff --git a/system/libraries/io/attachment.zig b/system/libraries/io/attachment.zig
new file mode 100644
index 0000000..c68adf8
--- /dev/null
+++ b/system/libraries/io/attachment.zig
@@ -0,0 +1,33 @@
+//! Attachment operations
+
+const sys = @import("sys");
+const types = @import("types.zig");
+
+const ERROR_RESULT: u64 = @bitCast(@as(i64, -1));
+
+pub fn attach(path: []const u8, flags: u32) types.Error!types.FileDescriptor {
+ const result = sys.syscall(.attach, .{ @intFromPtr(path.ptr), flags });
+ if (result == ERROR_RESULT) {
+ return types.Error.NotFound;
+ }
+ return @truncate(result);
+}
+
+pub fn seal(fd: types.FileDescriptor) void {
+ _ = sys.syscall(.seal, .{fd});
+}
+
+pub fn viewstack(path: []const u8, entries: []types.StackEntry) types.Error!usize {
+ const result = sys.syscall(.viewstack, .{
+ @intFromPtr(path.ptr),
+ path.len,
+ @intFromPtr(entries.ptr),
+ entries.len,
+ });
+
+ if (result == ERROR_RESULT) {
+ return types.Error.InvalidPath;
+ }
+
+ return @intCast(result);
+}
diff --git a/system/libraries/io/io.zig b/system/libraries/io/io.zig
new file mode 100644
index 0000000..588ff0c
--- /dev/null
+++ b/system/libraries/io/io.zig
@@ -0,0 +1,36 @@
+//! I/O operations
+
+pub const attachment = @import("attachment.zig");
+pub const stream_ops = @import("stream.zig");
+pub const location = @import("location.zig");
+pub const letter = @import("letter.zig");
+pub const types = @import("types.zig");
+
+pub const attach = attachment.attach;
+pub const seal = attachment.seal;
+pub const viewstack = attachment.viewstack;
+
+pub const view = stream_ops.view;
+pub const mark = stream_ops.mark;
+pub const getchar = stream_ops.getchar;
+pub const wipe = stream_ops.wipe;
+
+pub const getlocation = location.get;
+pub const setlocation = location.set;
+
+pub const sendLetter = letter.send;
+pub const readLetter = letter.read;
+
+pub const StackEntry = types.StackEntry;
+pub const FileDescriptor = types.FileDescriptor;
+pub const Letter = types.Letter;
+pub const Error = types.Error;
+
+pub const source = types.source;
+pub const stream = types.stream;
+pub const trace = types.trace;
+
+pub const VIEW_ONLY = types.VIEW_ONLY;
+pub const MARK_ONLY = types.MARK_ONLY;
+pub const BOTH = types.BOTH;
+pub const CREATE = types.CREATE;
diff --git a/system/libraries/io/io.zon b/system/libraries/io/io.zon
new file mode 100644
index 0000000..124ef55
--- /dev/null
+++ b/system/libraries/io/io.zon
@@ -0,0 +1,10 @@
+.{
+ .name = "io",
+ .version = "1.0.0",
+ .type = .library,
+ .entry = "io.zig",
+ .dependencies = .{
+ .sys = .{ .path = "../sys" },
+ .kata = .{ .path = "../kata" },
+ },
+}
diff --git a/system/libraries/io/letter.zig b/system/libraries/io/letter.zig
new file mode 100644
index 0000000..3cde330
--- /dev/null
+++ b/system/libraries/io/letter.zig
@@ -0,0 +1,36 @@
+//! Letter (postman) operations
+
+const sys = @import("sys");
+const types = @import("types.zig");
+
+const ERROR_RESULT: u64 = @bitCast(@as(i64, -1));
+
+const MODE_SEND: u64 = 0;
+const MODE_READ: u64 = 1;
+
+pub fn send(letter_type: u8, data: []const u8) types.Error!void {
+ const result = sys.syscall(.postman, .{
+ MODE_SEND,
+ @as(u64, letter_type),
+ @intFromPtr(data.ptr),
+ data.len,
+ });
+
+ if (result == ERROR_RESULT) {
+ return types.Error.SendFailed;
+ }
+}
+
+pub fn read(buffer: []u8) types.Error!u8 {
+ const result = sys.syscall(.postman, .{
+ MODE_READ,
+ @intFromPtr(buffer.ptr),
+ buffer.len,
+ });
+
+ if (result == ERROR_RESULT) {
+ return types.Error.ReadFailed;
+ }
+
+ return @intCast(result);
+}
diff --git a/system/libraries/io/location.zig b/system/libraries/io/location.zig
new file mode 100644
index 0000000..37d3115
--- /dev/null
+++ b/system/libraries/io/location.zig
@@ -0,0 +1,30 @@
+//! Location operations
+
+const sys = @import("sys");
+const types = @import("types.zig");
+
+const ERROR_RESULT: u64 = @bitCast(@as(i64, -1));
+
+pub fn get(buffer: []u8) types.Error![]u8 {
+ const result = sys.syscall(.getlocation, .{
+ @intFromPtr(buffer.ptr),
+ buffer.len,
+ });
+
+ if (result == ERROR_RESULT) {
+ return types.Error.GetLocationFailed;
+ }
+
+ return buffer[0..@intCast(result)];
+}
+
+pub fn set(path: []const u8) types.Error!void {
+ const result = sys.syscall(.setlocation, .{
+ @intFromPtr(path.ptr),
+ path.len,
+ });
+
+ if (result == ERROR_RESULT) {
+ return types.Error.InvalidPath;
+ }
+}
diff --git a/system/libraries/io/stream.zig b/system/libraries/io/stream.zig
new file mode 100644
index 0000000..7f93c9d
--- /dev/null
+++ b/system/libraries/io/stream.zig
@@ -0,0 +1,41 @@
+//! Stream operations
+
+const kata = @import("kata");
+const sys = @import("sys");
+const types = @import("types.zig");
+
+const ERROR_RESULT: u64 = @bitCast(@as(i64, -1));
+const EAGAIN_RESULT: u64 = @bitCast(@as(i64, -2));
+
+pub fn view(fd: types.FileDescriptor, buffer: []u8) types.Error!usize {
+ const result = sys.syscall(.view, .{ fd, @intFromPtr(buffer.ptr), buffer.len });
+ if (result == ERROR_RESULT) {
+ return types.Error.ReadFailed;
+ }
+ return result;
+}
+
+pub fn mark(fd: types.FileDescriptor, data: []const u8, color: u32) types.Error!usize {
+ const result = sys.syscall(.mark, .{ fd, @intFromPtr(data.ptr), data.len, color });
+ if (result == ERROR_RESULT) {
+ return types.Error.WriteFailed;
+ }
+ return result;
+}
+
+pub fn getchar() types.Error!u8 {
+ while (true) {
+ const result = sys.syscall(.getkeychar, .{});
+ if (result != EAGAIN_RESULT) {
+ if (result != ERROR_RESULT) {
+ return @truncate(result);
+ }
+ return types.Error.ReadFailed;
+ }
+ kata.yield();
+ }
+}
+
+pub fn wipe() void {
+ _ = sys.syscall(.wipe, .{});
+}
diff --git a/system/libraries/io/types.zig b/system/libraries/io/types.zig
new file mode 100644
index 0000000..d2ddb91
--- /dev/null
+++ b/system/libraries/io/types.zig
@@ -0,0 +1,39 @@
+//! I/O types and constants
+
+pub const Error = error{
+ NotFound,
+ PermissionDenied,
+ InvalidDescriptor,
+ ReadFailed,
+ WriteFailed,
+ InvalidPath,
+ SendFailed,
+ GetLocationFailed,
+};
+
+pub const FileDescriptor = u32;
+
+pub const source: FileDescriptor = 0;
+pub const stream: FileDescriptor = 1;
+pub const trace: FileDescriptor = 2;
+
+pub const VIEW_ONLY: u32 = 0x01;
+pub const MARK_ONLY: u32 = 0x02;
+pub const BOTH: u32 = 0x03;
+pub const CREATE: u32 = 0x0100;
+
+pub const StackEntry = extern struct {
+ identity: [64]u8,
+ identity_len: u8,
+ is_stack: bool,
+ owner_name_len: u8,
+ permission_type: u8,
+ size: u32,
+ modified_time: u64,
+ owner_name: [64]u8,
+};
+
+pub const Letter = struct {
+ pub const NONE: u8 = 0;
+ pub const NAVIGATE: u8 = 1;
+};
diff --git a/system/libraries/kata/kata.zig b/system/libraries/kata/kata.zig
new file mode 100644
index 0000000..951a742
--- /dev/null
+++ b/system/libraries/kata/kata.zig
@@ -0,0 +1,45 @@
+//! Kata (process) control
+
+const sys = @import("sys");
+
+const ERROR_RESULT: u64 = @bitCast(@as(i64, -1));
+
+pub const Error = error{
+ SpawnFailed,
+ WaitFailed,
+};
+
+pub fn yield() void {
+ _ = sys.syscall(.yield, .{});
+}
+
+pub fn exit(code: u64) noreturn {
+ _ = sys.syscall(.exit, .{code});
+ unreachable;
+}
+
+pub fn spawn(path: []const u8) Error!u32 {
+ const result = sys.syscall(.spawn, .{ @intFromPtr(path.ptr), path.len, @as(u64, 0), @as(u64, 0) });
+ if (result == ERROR_RESULT) {
+ return Error.SpawnFailed;
+ }
+ return @truncate(result);
+}
+
+pub fn spawnWithArgs(path: []const u8, argv: [][*:0]const u8) Error!u32 {
+ const result = sys.syscall(.spawn, .{ @intFromPtr(path.ptr), path.len, @intFromPtr(argv.ptr), argv.len });
+ if (result == ERROR_RESULT) {
+ return Error.SpawnFailed;
+ }
+ return @truncate(result);
+}
+
+pub fn wait(pid: u32) Error!u64 {
+ while (true) {
+ const result = sys.syscall(.wait, .{pid});
+ if (result != ERROR_RESULT) {
+ return result;
+ }
+ yield();
+ }
+}
diff --git a/system/libraries/kata/kata.zon b/system/libraries/kata/kata.zon
new file mode 100644
index 0000000..f792175
--- /dev/null
+++ b/system/libraries/kata/kata.zon
@@ -0,0 +1,9 @@
+.{
+ .name = "kata",
+ .version = "1.0.0",
+ .type = .library,
+ .entry = "kata.zig",
+ .dependencies = .{
+ .sys = .{ .path = "../sys" },
+ },
+}
diff --git a/system/libraries/mem/mem.zig b/system/libraries/mem/mem.zig
new file mode 100644
index 0000000..b513567
--- /dev/null
+++ b/system/libraries/mem/mem.zig
@@ -0,0 +1,22 @@
+//! Memory utilities
+
+pub fn copy(dest: []u8, src: []const u8) void {
+ const len = @min(dest.len, src.len);
+ for (0..len) |i| {
+ dest[i] = src[i];
+ }
+}
+
+pub fn zero(buf: []u8) void {
+ for (buf) |*b| {
+ b.* = 0;
+ }
+}
+
+pub fn equals(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;
+}
diff --git a/system/libraries/mem/mem.zon b/system/libraries/mem/mem.zon
new file mode 100644
index 0000000..18e964f
--- /dev/null
+++ b/system/libraries/mem/mem.zon
@@ -0,0 +1,6 @@
+.{
+ .name = "mem",
+ .version = "1.0.0",
+ .type = .library,
+ .entry = "mem.zig",
+}
diff --git a/system/libraries/string/builder.zig b/system/libraries/string/builder.zig
new file mode 100644
index 0000000..16bd35c
--- /dev/null
+++ b/system/libraries/string/builder.zig
@@ -0,0 +1,48 @@
+//! String building
+
+pub fn build(buf: []u8, parts: anytype) []const u8 {
+ var pos: usize = 0;
+ inline for (parts) |part| {
+ for (part) |c| {
+ if (pos >= buf.len) break;
+ buf[pos] = c;
+ pos += 1;
+ }
+ }
+ return buf[0..pos];
+}
+
+pub fn concat(buf: []u8, a: []const u8, b: []const u8) []const u8 {
+ var pos: usize = 0;
+ for (a) |c| {
+ if (pos >= buf.len) break;
+ buf[pos] = c;
+ pos += 1;
+ }
+ for (b) |c| {
+ if (pos >= buf.len) break;
+ buf[pos] = c;
+ pos += 1;
+ }
+ return buf[0..pos];
+}
+
+pub fn concat3(buf: []u8, a: []const u8, b: []const u8, c: []const u8) []const u8 {
+ var pos: usize = 0;
+ for (a) |ch| {
+ if (pos >= buf.len) break;
+ buf[pos] = ch;
+ pos += 1;
+ }
+ for (b) |ch| {
+ if (pos >= buf.len) break;
+ buf[pos] = ch;
+ pos += 1;
+ }
+ for (c) |ch| {
+ if (pos >= buf.len) break;
+ buf[pos] = ch;
+ pos += 1;
+ }
+ return buf[0..pos];
+}
diff --git a/system/libraries/string/cstring.zig b/system/libraries/string/cstring.zig
new file mode 100644
index 0000000..32ad60f
--- /dev/null
+++ b/system/libraries/string/cstring.zig
@@ -0,0 +1,17 @@
+//! C-string utilities
+
+pub fn len(str: [*:0]const u8) usize {
+ var i: usize = 0;
+ while (str[i] != 0) : (i += 1) {}
+ return i;
+}
+
+pub fn toSlice(str: [*:0]const u8) []const u8 {
+ return str[0..len(str)];
+}
+
+pub fn findNull(buf: []const u8) usize {
+ var i: usize = 0;
+ while (i < buf.len and buf[i] != 0) : (i += 1) {}
+ return i;
+}
diff --git a/system/libraries/string/location.zig b/system/libraries/string/location.zig
new file mode 100644
index 0000000..ff1c915
--- /dev/null
+++ b/system/libraries/string/location.zig
@@ -0,0 +1,59 @@
+//! Location utilities
+
+pub fn getStackName(location: []const u8) []const u8 {
+ if (location.len == 0 or (location.len == 1 and location[0] == '/')) {
+ return "/";
+ }
+
+ var last_slash: usize = 0;
+ for (location, 0..) |c, i| {
+ if (c == '/') {
+ last_slash = i;
+ }
+ }
+
+ if (last_slash == location.len - 1 and location.len > 1) {
+ var i: usize = location.len - 2;
+ while (i > 0) : (i -= 1) {
+ if (location[i] == '/') {
+ return location[i + 1 .. location.len - 1];
+ }
+ }
+ return location[1 .. location.len - 1];
+ }
+
+ if (last_slash + 1 < location.len) {
+ return location[last_slash + 1 ..];
+ }
+
+ return location;
+}
+
+pub fn parent(location: []const u8, buf: []u8) []const u8 {
+ if (location.len <= 1) {
+ buf[0] = '/';
+ return buf[0..1];
+ }
+
+ var end = location.len;
+ if (location[end - 1] == '/') {
+ end -= 1;
+ }
+
+ var i: usize = end;
+ while (i > 0) : (i -= 1) {
+ if (location[i - 1] == '/') {
+ if (i == 1) {
+ buf[0] = '/';
+ return buf[0..1];
+ }
+ for (location[0 .. i - 1], 0..) |c, j| {
+ buf[j] = c;
+ }
+ return buf[0 .. i - 1];
+ }
+ }
+
+ buf[0] = '/';
+ return buf[0..1];
+}
diff --git a/system/libraries/string/string.zig b/system/libraries/string/string.zig
new file mode 100644
index 0000000..1d7c798
--- /dev/null
+++ b/system/libraries/string/string.zig
@@ -0,0 +1,16 @@
+//! String utilities
+
+pub const cstring = @import("cstring.zig");
+pub const location = @import("location.zig");
+pub const builder = @import("builder.zig");
+
+pub const len = cstring.len;
+pub const toSlice = cstring.toSlice;
+pub const findNull = cstring.findNull;
+
+pub const getStackName = location.getStackName;
+pub const parent = location.parent;
+
+pub const build = builder.build;
+pub const concat = builder.concat;
+pub const concat3 = builder.concat3;
diff --git a/system/libraries/string/string.zon b/system/libraries/string/string.zon
new file mode 100644
index 0000000..e722bf8
--- /dev/null
+++ b/system/libraries/string/string.zon
@@ -0,0 +1,6 @@
+.{
+ .name = "string",
+ .version = "1.0.0",
+ .type = .library,
+ .entry = "string.zig",
+}
diff --git a/system/libraries/sys/start.zig b/system/libraries/sys/start.zig
new file mode 100644
index 0000000..a65eec8
--- /dev/null
+++ b/system/libraries/sys/start.zig
@@ -0,0 +1,16 @@
+//! Entry point
+
+extern fn main(pc: u32, pv: [*]const [*:0]const u8) u8;
+
+export fn _start() callconv(.naked) noreturn {
+ asm volatile (
+ \\mov (%%rsp), %%edi
+ \\mov 8(%%rsp), %%rsi
+ \\and $-16, %%rsp
+ \\call main
+ \\movzbl %%al, %%edi
+ \\mov $0x01, %%eax
+ \\syscall
+ \\ud2
+ );
+}
diff --git a/system/libraries/sys/sys.zig b/system/libraries/sys/sys.zig
new file mode 100644
index 0000000..d37e4c8
--- /dev/null
+++ b/system/libraries/sys/sys.zig
@@ -0,0 +1,58 @@
+//! System runtime
+
+pub const start = @import("start.zig");
+
+comptime {
+ _ = start;
+}
+
+pub const Invocation = enum(u64) {
+ exit = 0x01,
+ attach = 0x02,
+ seal = 0x03,
+ view = 0x04,
+ mark = 0x05,
+ spawn = 0x06,
+ wait = 0x07,
+ yield = 0x08,
+ getkeychar = 0x09,
+ viewstack = 0x0A,
+ getlocation = 0x0B,
+ setlocation = 0x0C,
+ postman = 0x0D,
+ wipe = 0x0E,
+};
+
+pub inline fn syscall(invocation: Invocation, args: anytype) u64 {
+ const Args = @TypeOf(args);
+ const fields = @typeInfo(Args).@"struct".fields;
+
+ const arg0: u64 = if (fields.len > 0) toU64(args[0]) else 0;
+ const arg1: u64 = if (fields.len > 1) toU64(args[1]) else 0;
+ const arg2: u64 = if (fields.len > 2) toU64(args[2]) else 0;
+ const arg3: u64 = if (fields.len > 3) toU64(args[3]) else 0;
+ const arg4: u64 = if (fields.len > 4) toU64(args[4]) else 0;
+ const arg5: u64 = if (fields.len > 5) toU64(args[5]) else 0;
+
+ return asm volatile ("syscall"
+ : [ret] "={rax}" (-> u64),
+ : [number] "{rax}" (@intFromEnum(invocation)),
+ [arg0] "{rdi}" (arg0),
+ [arg1] "{rsi}" (arg1),
+ [arg2] "{rdx}" (arg2),
+ [arg3] "{r10}" (arg3),
+ [arg4] "{r8}" (arg4),
+ [arg5] "{r9}" (arg5),
+ : .{ .rcx = true, .r11 = true, .memory = true }
+ );
+}
+
+inline fn toU64(value: anytype) u64 {
+ const T = @TypeOf(value);
+ return switch (@typeInfo(T)) {
+ .int, .comptime_int => @intCast(value),
+ .pointer => @intFromPtr(value),
+ .@"enum" => @intFromEnum(value),
+ else => @bitCast(value),
+ };
+}
diff --git a/system/libraries/sys/sys.zon b/system/libraries/sys/sys.zon
new file mode 100644
index 0000000..35d065e
--- /dev/null
+++ b/system/libraries/sys/sys.zon
@@ -0,0 +1,6 @@
+.{
+ .name = "sys",
+ .version = "1.0.0",
+ .type = .library,
+ .entry = "sys.zig",
+}