diff --git a/.gitignore b/.gitignore index 4c82b07..86f9df4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ -zig-cache +.zig-cache zig-out +result +result-cache diff --git a/applets.zig b/applets.zig index 0b2246f..50718f3 100644 --- a/applets.zig +++ b/applets.zig @@ -1,10 +1,50 @@ -pub const arch = @import("applets/arch.zig"); -pub const cal = @import("applets/cal.zig"); -pub const cat = @import("applets/cat.zig"); -pub const chroot = @import("applets/chroot.zig"); -pub const @"false" = @import("applets/false.zig"); -pub const pwd = @import("applets/pwd.zig"); -pub const @"true" = @import("applets/true.zig"); -pub const umount = @import("applets/umount.zig"); -pub const uptime = @import("applets/uptime.zig"); -pub const yes = @import("applets/yes.zig"); +pub const all: []const []const u8 = &.{ + "arch", + "basename", + "cat", + "chmod", + "chroot", + "date", + "dsh", + "expr", + "false", + "find", + "grep", + "head", + "install", + "mkdir", + "mktemp", + "nproc", + "pwd", + "rm", + "sed", + "sort", + "tail", + "tar", + "true", + "uptime", +}; + +pub const min: []const []const u8 = &.{ + "arch", + "basename", + "cat", + "date", + "expr", + "false", + "find", + "grep", + "head", + "install", + "mkdir", + "mktemp", + "nproc", + "pwd", + "rm", + "sed", + "sort", + "tail", + "tar", + "true", + "uptime", +}; diff --git a/applets/arch.zig b/applets/arch.zig deleted file mode 100644 index a6ccc68..0000000 --- a/applets/arch.zig +++ /dev/null @@ -1,7 +0,0 @@ -const builtin = @import("builtin"); -const std = @import("std"); -const ziggybox = @import("ziggybox"); - -pub fn run(_: *std.process.ArgIterator) !void { - return ziggybox.io.getStdOut().print("{s}\n", .{@tagName(builtin.cpu.arch)}); -} diff --git a/applets/arch/main.zig b/applets/arch/main.zig new file mode 100644 index 0000000..c5bfe92 --- /dev/null +++ b/applets/arch/main.zig @@ -0,0 +1,7 @@ +const builtin = @import("builtin"); +const std = @import("std"); + +pub fn main() !void { + const stdout = std.io.getStdOut().writer().any(); + try stdout.writeAll(@tagName(builtin.cpu.arch) ++ "\n"); +} diff --git a/applets/basename/main.zig b/applets/basename/main.zig new file mode 100644 index 0000000..3bf29f5 --- /dev/null +++ b/applets/basename/main.zig @@ -0,0 +1,36 @@ +const builtin = @import("builtin"); +const std = @import("std"); +const native_os = builtin.os.tag; + +var arena_allocator: std.heap.ArenaAllocator = .init(std.heap.page_allocator); +var debug_allocator: std.heap.DebugAllocator(.{}) = .init; + +pub fn main() !void { + const gpa, const gpa_deinit = gpa: { + if (native_os == .wasi) break :gpa .{ std.heap.wasm_allocator, false }; + if (builtin.single_threaded) break :gpa .{ arena_allocator.allocator(), true }; + break :gpa switch (builtin.mode) { + .Debug, .ReleaseSafe => .{ debug_allocator.allocator(), true }, + .ReleaseFast, .ReleaseSmall => .{ std.heap.smp_allocator, false }, + }; + }; + defer if (gpa_deinit) { + _ = debug_allocator.deinit(); + }; + + var args = try std.process.argsWithAllocator(gpa); + defer args.deinit(); + + _ = args.skip(); + + if (args.next()) |comp| { + const basename = std.fs.path.basename(comp); + const stdout = std.io.getStdOut().writer().any(); + try stdout.writeAll(if (basename.len == 0) "/" else basename); + try stdout.writeByte('\n'); + } else { + const stderr = std.io.getStdErr().writer().any(); + try stderr.writeAll("basename: path is required\n"); + std.process.exit(1); + } +} diff --git a/applets/cal.zig b/applets/cal.zig deleted file mode 100644 index 2e9fd1a..0000000 --- a/applets/cal.zig +++ /dev/null @@ -1,158 +0,0 @@ -const builtin = @import("builtin"); -const clap = @import("clap"); -const std = @import("std"); -const ziggybox = @import("ziggybox"); - -const math = struct { - pub const weekLen = 20; - pub const headSep = 2; - pub const jWeekLen = weekLen + 7; - - pub inline fn headerText(isJulian: bool) usize { - const base = (weekLen * 3) + (headSep * 2); - if (isJulian) { - const julian = (jWeekLen * 2) - ((weekLen * 3) + (headSep * 2)); - return base + julian; - } - return base; - } - - pub inline fn calcWeekLen(isJulian: bool) usize { - if (isJulian) return weekLen + (jWeekLen - weekLen); - return weekLen; - } -}; - -const MonthAndYear = struct { - month: u4, - year: u16, - - pub fn setMonth(self: *MonthAndYear, value: u16) !void { - if (value < 1 or value > 12) { - try ziggybox.io.getStdErr().print("calc: number {} is not in 1..12 range\n", .{value}); - return error.OutOfRange; - } - - self.month = @intCast(value); - } - - pub fn setYear(self: *MonthAndYear, value: u16) !void { - if (value < 1 or value > 9999) { - try ziggybox.io.getStdErr().print("calc: number {} is not in 1..9999 range\n", .{value}); - return error.OutOfRange; - } - - self.year = @intCast(value); - } -}; - -inline fn monthName(month: std.time.epoch.Month) []const u8 { - return switch (month) { - .jan => "January", - .feb => "February", - .mar => "March", - .apr => "April", - .may => "May", - .jun => "June", - .jul => "July", - .aug => "August", - .sep => "September", - .oct => "October", - .nov => "November", - .dec => "December", - }; -} - -fn pad(len: usize, writer: anytype) !void { - var i: usize = 0; - while (i < len) : (i += 1) try writer.writeByte(' '); -} - -fn center(str: []const u8, len: usize, sep: usize, writer: anytype) !void { - const n = str.len; - const nlen = len - n; - - try pad((nlen / 2) + n, writer); - try writer.writeAll(str); - try pad((nlen / 2) + (nlen % 2) + sep, writer); -} - -fn fmtCenter(comptime fmt: []const u8, args: anytype, len: usize, sep: usize, writer: anytype) !void { - const str = try std.fmt.allocPrint(ziggybox.common.allocator, fmt, args); - defer ziggybox.common.allocator.free(str); - return try center(str, len, sep, writer); -} - -pub fn run(args: *std.process.ArgIterator) !void { - const stderr = ziggybox.io.getStdErr(); - - const params = comptime clap.parseParamsComptime( - \\-h, --help Display this help and exit. - \\-j, --julian Use the Julian calendar. - \\-m, --monday Weeks start on Monday. - \\-y, --year Displays the entire year. - \\ Month - \\ Year - \\ - ); - - var diag = clap.Diagnostic{}; - var res = ziggybox.clap.parseEx(clap.Help, ¶ms, comptime .{ - .month = clap.parsers.int(u16, 0), - .year = clap.parsers.int(u16, 0), - }, args, .{ - .allocator = ziggybox.common.allocator, - .diagnostic = &diag, - }) catch |err| { - diag.report(stderr, err) catch {}; - return err; - }; - defer res.deinit(); - - if (res.args.help != 0 or res.positionals.len > 2) - return clap.help(stderr, clap.Help, ¶ms, .{}); - - var value = MonthAndYear{ .month = 1, .year = 0 }; - var isMonthSet = false; - var isYearSet = false; - - if (res.positionals.len == 1) { - try value.setYear(res.positionals[0]); - isYearSet = true; - } - - if (res.positionals.len == 2) { - try value.setMonth(res.positionals[0]); - try value.setYear(res.positionals[1]); - - isYearSet = true; - isMonthSet = true; - } - - if (!isYearSet and !isMonthSet) { - const time = try ziggybox.os.time(); - value.year = time.getEpochDay().calculateYearDay().year; - value.month = @as(u4, @intFromEnum(time.getEpochDay().calculateYearDay().calculateMonthDay().month)); - } - - const stdout = ziggybox.io.getStdOut(); - const julian = @min(res.args.julian, 1); - - if (res.args.year > 0) { - try fmtCenter("{}", .{value.year}, math.headerText(julian > 0), 0, stdout); - try stdout.writeAll("\n\n"); - - const weekLen = math.calcWeekLen(julian > 0); - var month: u4 = 0; - while (month < 12) : (month += (3 - @as(u4, @intCast(julian)))) { - try fmtCenter("{s}", .{monthName(@enumFromInt(month + 1))}, weekLen, math.headSep, stdout); - - if (julian == 0) { - try fmtCenter("{s}", .{monthName(@enumFromInt(month + 2))}, weekLen, math.headSep, stdout); - } - - try fmtCenter("{s}", .{monthName(@enumFromInt(month + 3 - @as(u4, @intCast(julian))))}, weekLen, 0, stdout); - try stdout.writeByte('\n'); - } - } -} diff --git a/applets/cat.zig b/applets/cat.zig deleted file mode 100644 index 221651f..0000000 --- a/applets/cat.zig +++ /dev/null @@ -1,58 +0,0 @@ -const builtin = @import("builtin"); -const clap = @import("clap"); -const std = @import("std"); -const ziggybox = @import("ziggybox"); - -pub fn run(args: *std.process.ArgIterator) !void { - const stderr = ziggybox.io.getStdErr(); - const stdout = ziggybox.io.getStdOut(); - - const params = comptime clap.parseParamsComptime( - \\-h, --help Display this help and exit. - \\... The file or files to list - \\ - ); - - var diag = clap.Diagnostic{}; - var res = ziggybox.clap.parseEx(clap.Help, ¶ms, comptime .{ - .file = clap.parsers.string, - }, args, .{ - .allocator = ziggybox.common.allocator, - .diagnostic = &diag, - }) catch |err| { - diag.report(stderr, err) catch {}; - return err; - }; - defer res.deinit(); - - if (res.args.help != 0) - return clap.help(stderr, clap.Help, ¶ms, .{}); - - if (res.positionals.len == 0) { - const stdin = ziggybox.io.getStdIn(); - - while (stdin.readUntilDelimiterAlloc(ziggybox.common.allocator, '\n', 1000) catch null) |line| { - defer ziggybox.common.allocator.free(line); - stdout.writeAll(line) catch break; - stdout.writeByte('\n') catch break; - } - } else { - for (res.positionals) |positional| { - const path = if (std.fs.path.isAbsolute(positional)) try ziggybox.common.allocator.dupe(u8, positional) else blk: { - const cwd = try std.process.getCwdAlloc(ziggybox.common.allocator); - defer ziggybox.common.allocator.free(cwd); - break :blk try std.fs.path.join(ziggybox.common.allocator, &.{ - cwd, - positional, - }); - }; - defer ziggybox.common.allocator.free(path); - - var file = try std.fs.openFileAbsolute(path, .{}); - defer file.close(); - while (true) { - try stdout.writeByte(file.reader().readByte() catch break); - } - } - } -} diff --git a/applets/cat/main.zig b/applets/cat/main.zig new file mode 100644 index 0000000..1431908 --- /dev/null +++ b/applets/cat/main.zig @@ -0,0 +1,47 @@ +const builtin = @import("builtin"); +const std = @import("std"); +const native_os = builtin.os.tag; + +var arena_allocator: std.heap.ArenaAllocator = .init(std.heap.page_allocator); +var debug_allocator: std.heap.DebugAllocator(.{}) = .init; + +pub fn main() !void { + const gpa, const gpa_deinit = gpa: { + if (native_os == .wasi) break :gpa .{ std.heap.wasm_allocator, false }; + if (builtin.single_threaded) break :gpa .{ arena_allocator.allocator(), true }; + break :gpa switch (builtin.mode) { + .Debug, .ReleaseSafe => .{ debug_allocator.allocator(), true }, + .ReleaseFast, .ReleaseSmall => .{ std.heap.smp_allocator, false }, + }; + }; + defer if (gpa_deinit) { + _ = debug_allocator.deinit(); + }; + + var args = try std.process.argsWithAllocator(gpa); + defer args.deinit(); + + _ = args.skip(); + + var i: usize = 0; + + const raw_stdout = std.io.getStdOut().writer().any(); + var buffered_stdout = std.io.bufferedWriter(raw_stdout); + defer buffered_stdout.flush() catch |err| std.debug.panic("Failed to flush stdout: {}", .{err}); + const stdout = buffered_stdout.writer().any(); + + while (args.next()) |p| : (i += 1) { + var file = if (std.fs.path.isAbsolute(p)) try std.fs.openFileAbsolute(p, .{}) else try std.fs.cwd().openFile(p, .{}); + defer file.close(); + + const reader = file.reader(); + + while (reader.readByte() catch null) |b| try stdout.writeByte(b); + } + + if (i == 0) { + const stderr = std.io.getStdErr().writer().any(); + try stderr.writeAll("cat: path is required\n"); + std.process.exit(1); + } +} diff --git a/applets/chmod/main.zig b/applets/chmod/main.zig new file mode 100644 index 0000000..7f02b5a --- /dev/null +++ b/applets/chmod/main.zig @@ -0,0 +1,25 @@ +const builtin = @import("builtin"); +const std = @import("std"); +const native_os = builtin.os.tag; + +var arena_allocator: std.heap.ArenaAllocator = .init(std.heap.page_allocator); +var debug_allocator: std.heap.DebugAllocator(.{}) = .init; + +pub fn main() !void { + const gpa, const gpa_deinit = gpa: { + if (native_os == .wasi) break :gpa .{ std.heap.wasm_allocator, false }; + if (builtin.single_threaded) break :gpa .{ arena_allocator.allocator(), true }; + break :gpa switch (builtin.mode) { + .Debug, .ReleaseSafe => .{ debug_allocator.allocator(), true }, + .ReleaseFast, .ReleaseSmall => .{ std.heap.smp_allocator, false }, + }; + }; + defer if (gpa_deinit) { + _ = debug_allocator.deinit(); + }; + + var args = try std.process.argsWithAllocator(gpa); + defer args.deinit(); + + _ = args.skip(); +} diff --git a/applets/chroot.zig b/applets/chroot.zig deleted file mode 100644 index bde362a..0000000 --- a/applets/chroot.zig +++ /dev/null @@ -1,50 +0,0 @@ -const builtin = @import("builtin"); -const clap = @import("clap"); -const std = @import("std"); -const ziggybox = @import("ziggybox"); - -fn chroot(path: [*:0]const u8) !void { - const r = std.os.linux.chroot(path); - return switch (std.os.errno(r)) { - .SUCCESS => {}, - .PERM => error.AccessDenied, - else => |err| return std.os.unexpectedErrno(err), - }; -} - -pub fn run(args: *std.process.ArgIterator) !void { - const stderr = ziggybox.io.getStdErr(); - - const params = comptime clap.parseParamsComptime( - \\-h, --help Display this help and exit. - \\ The directory to chroot into. - \\... The command to execute. - \\ - ); - - var diag = clap.Diagnostic{}; - var res = ziggybox.clap.parseEx(clap.Help, ¶ms, comptime .{ - .path = clap.parsers.string, - .str = clap.parsers.string, - }, args, .{ - .allocator = ziggybox.common.allocator, - .diagnostic = &diag, - }) catch |err| { - diag.report(stderr, err) catch {}; - return err; - }; - defer res.deinit(); - - if (res.args.help != 0 or res.positionals.len < 2) - return clap.help(stderr, clap.Help, ¶ms, .{}); - - const path = try ziggybox.common.allocator.dupeZ(u8, res.positionals[0]); - defer ziggybox.common.allocator.free(path); - try chroot(path); - - var proc = std.ChildProcess.init(res.positionals[1..], ziggybox.common.allocator); - proc.stdin_behavior = .Inherit; - proc.stdout_behavior = .Inherit; - proc.stderr_behavior = .Inherit; - _ = try proc.spawnAndWait(); -} diff --git a/applets/chroot/main.zig b/applets/chroot/main.zig new file mode 100644 index 0000000..a381eec --- /dev/null +++ b/applets/chroot/main.zig @@ -0,0 +1,51 @@ +const builtin = @import("builtin"); +const std = @import("std"); +const native_os = builtin.os.tag; + +fn chroot(path: [*:0]const u8) !void { + const r = std.os.linux.chroot(path); + return switch (std.posix.errno(r)) { + .SUCCESS => {}, + .NOENT => error.FileNotFound, + .NOTDIR => error.NotDirectory, + .PERM => error.AccessDenied, + else => |err| return std.posix.unexpectedErrno(err), + }; +} + +var arena_allocator: std.heap.ArenaAllocator = .init(std.heap.page_allocator); +var debug_allocator: std.heap.DebugAllocator(.{}) = .init; + +pub fn main() !void { + const gpa, const gpa_deinit = gpa: { + if (native_os == .wasi) break :gpa .{ std.heap.wasm_allocator, false }; + if (builtin.single_threaded) break :gpa .{ arena_allocator.allocator(), true }; + break :gpa switch (builtin.mode) { + .Debug, .ReleaseSafe => .{ debug_allocator.allocator(), true }, + .ReleaseFast, .ReleaseSmall => .{ std.heap.smp_allocator, false }, + }; + }; + defer if (gpa_deinit) { + _ = debug_allocator.deinit(); + }; + + var args = try std.process.argsWithAllocator(gpa); + defer args.deinit(); + + _ = args.skip(); + + const path = args.next() orelse return error.MissingArgument; + + var cargs = std.ArrayList([]const u8).init(gpa); + defer cargs.deinit(); + + while (args.next()) |a| try cargs.append(a); + + try chroot(path); + + var proc = std.process.Child.init(cargs.items, gpa); + proc.stdin_behavior = .Inherit; + proc.stdout_behavior = .Inherit; + proc.stderr_behavior = .Inherit; + _ = try proc.spawnAndWait(); +} diff --git a/applets/date/format.zig b/applets/date/format.zig new file mode 100644 index 0000000..c7fb2ca --- /dev/null +++ b/applets/date/format.zig @@ -0,0 +1,7 @@ +const std = @import("std"); + +fn formatSecondsSinceEpoch(writer: anytype, timestamp: i64) !void { + try writer.print("{}", .{timestamp}); +} + +pub const s = formatSecondsSinceEpoch; diff --git a/applets/date/main.zig b/applets/date/main.zig new file mode 100644 index 0000000..7a5dd90 --- /dev/null +++ b/applets/date/main.zig @@ -0,0 +1,69 @@ +const builtin = @import("builtin"); +const std = @import("std"); +const native_os = builtin.os.tag; + +const fields = @import("format.zig"); + +fn format(writer: anytype, fmt: []const u8, timestamp: i64) !void { + var i: usize = 0; + while (i < fmt.len) : (i += 1) { + if (fmt[i] == '%') { + var handled = false; + inline for (comptime std.meta.declarations(fields)) |f| { + if (std.mem.startsWith(u8, fmt[i..], "%" ++ f.name)) { + try @field(fields, f.name)(writer, timestamp); + handled = true; + } + } + + if (std.mem.startsWith(u8, fmt[i..], "%%")) { + try writer.writeByte('%'); + handled = true; + } + + if (handled) { + i += 1; + continue; + } + return error.InvalidField; + } + + try writer.writeByte(fmt[i]); + } +} + +var arena_allocator: std.heap.ArenaAllocator = .init(std.heap.page_allocator); +var debug_allocator: std.heap.DebugAllocator(.{}) = .init; + +pub fn main() !void { + const gpa, const gpa_deinit = gpa: { + if (native_os == .wasi) break :gpa .{ std.heap.wasm_allocator, false }; + if (builtin.single_threaded) break :gpa .{ arena_allocator.allocator(), true }; + break :gpa switch (builtin.mode) { + .Debug, .ReleaseSafe => .{ debug_allocator.allocator(), true }, + .ReleaseFast, .ReleaseSmall => .{ std.heap.smp_allocator, false }, + }; + }; + defer if (gpa_deinit) { + _ = debug_allocator.deinit(); + }; + + const stdout = std.io.getStdOut().writer().any(); + + var args = try std.process.argsWithAllocator(gpa); + defer args.deinit(); + + _ = args.skip(); + + const timestamp = std.time.timestamp(); + + if (args.next()) |comp| { + if (std.mem.startsWith(u8, comp, "+")) { + try format(stdout, comp[1..], timestamp); + try stdout.writeByte('\n'); + } + } else { + try format(stdout, "", timestamp); + try stdout.writeByte('\n'); + } +} diff --git a/applets/dsh/main.zig b/applets/dsh/main.zig new file mode 100644 index 0000000..81c2a5a --- /dev/null +++ b/applets/dsh/main.zig @@ -0,0 +1,99 @@ +const builtin = @import("builtin"); +const std = @import("std"); +const native_os = builtin.os.tag; + +fn splitArgs(gpa: std.mem.Allocator, line: []const u8) ![]const []const u8 { + var args = std.ArrayList([]const u8).init(gpa); + defer args.deinit(); + + var i: usize = 0; + var x: usize = 0; + while (i < line.len) : (i += 1) { + if (line[i] == ' ' and i > 0) { + try args.append(line[x..i]); + x = i + 1; + } else if (line[i] == '"') { + const end = std.mem.indexOf(u8, line[(i + 1)..], "\"") orelse line.len; + try args.append(line[(i + 1)..end]); + x += end; + } + } + + if (args.items.len == 0) { + try args.append(line); + } else { + try args.append(line[x..]); + } + + return try args.toOwnedSlice(); +} + +fn isSuperUser() bool { + return switch (native_os) { + .linux => std.os.linux.geteuid() == 0, + else => false, + }; +} + +var arena_allocator: std.heap.ArenaAllocator = .init(std.heap.page_allocator); +var debug_allocator: std.heap.DebugAllocator(.{}) = .init; + +pub fn main() !void { + const gpa, const gpa_deinit = gpa: { + if (native_os == .wasi) break :gpa .{ std.heap.wasm_allocator, false }; + if (builtin.single_threaded) break :gpa .{ arena_allocator.allocator(), true }; + break :gpa switch (builtin.mode) { + .Debug, .ReleaseSafe => .{ debug_allocator.allocator(), true }, + .ReleaseFast, .ReleaseSmall => .{ std.heap.smp_allocator, false }, + }; + }; + defer if (gpa_deinit) { + _ = debug_allocator.deinit(); + }; + + var args = try std.process.argsWithAllocator(gpa); + defer args.deinit(); + + const shexec = args.next() orelse unreachable; + + const stdout = std.io.getStdOut().writer().any(); + const stderr = std.io.getStdErr().writer().any(); + const stdin = std.io.getStdIn().reader().any(); + + var env_map = try std.process.getEnvMap(gpa); + defer env_map.deinit(); + + try env_map.put("0", shexec); + try env_map.put("SHELL", shexec); + + var line = std.ArrayList(u8).init(gpa); + + while (true) { + try stdout.writeByte(if (isSuperUser()) '#' else '$'); + try stdout.writeByte(' '); + + line.clearAndFree(); + stdin.streamUntilDelimiter(line.writer(), '\n', null) catch |err| switch (err) { + error.EndOfStream => { + try stdout.writeByte('\n'); + break; + }, + else => return err, + }; + + const argv = try splitArgs(gpa, line.items); + defer gpa.free(argv); + + var proc = std.process.Child.init(argv, gpa); + + proc.env_map = &env_map; + + proc.stdout_behavior = .Inherit; + proc.stderr_behavior = .Inherit; + proc.stdin_behavior = .Inherit; + + _ = proc.spawnAndWait() catch |err| try stderr.print("dsh: failed to execute {s}: {}\n", .{ argv[0], err }); + + try stdout.writeByte('\n'); + } +} diff --git a/applets/expr/main.zig b/applets/expr/main.zig new file mode 100644 index 0000000..7f02b5a --- /dev/null +++ b/applets/expr/main.zig @@ -0,0 +1,25 @@ +const builtin = @import("builtin"); +const std = @import("std"); +const native_os = builtin.os.tag; + +var arena_allocator: std.heap.ArenaAllocator = .init(std.heap.page_allocator); +var debug_allocator: std.heap.DebugAllocator(.{}) = .init; + +pub fn main() !void { + const gpa, const gpa_deinit = gpa: { + if (native_os == .wasi) break :gpa .{ std.heap.wasm_allocator, false }; + if (builtin.single_threaded) break :gpa .{ arena_allocator.allocator(), true }; + break :gpa switch (builtin.mode) { + .Debug, .ReleaseSafe => .{ debug_allocator.allocator(), true }, + .ReleaseFast, .ReleaseSmall => .{ std.heap.smp_allocator, false }, + }; + }; + defer if (gpa_deinit) { + _ = debug_allocator.deinit(); + }; + + var args = try std.process.argsWithAllocator(gpa); + defer args.deinit(); + + _ = args.skip(); +} diff --git a/applets/false.zig b/applets/false.zig deleted file mode 100644 index 5d97706..0000000 --- a/applets/false.zig +++ /dev/null @@ -1,7 +0,0 @@ -const builtin = @import("builtin"); -const std = @import("std"); -const ziggybox = @import("ziggybox"); - -pub fn run(_: *std.process.ArgIterator) !void { - std.os.exit(1); -} diff --git a/applets/false/main.zig b/applets/false/main.zig new file mode 100644 index 0000000..e5e584e --- /dev/null +++ b/applets/false/main.zig @@ -0,0 +1,5 @@ +const std = @import("std"); + +pub fn main() void { + std.process.exit(1); +} diff --git a/applets/find/main.zig b/applets/find/main.zig new file mode 100644 index 0000000..7f02b5a --- /dev/null +++ b/applets/find/main.zig @@ -0,0 +1,25 @@ +const builtin = @import("builtin"); +const std = @import("std"); +const native_os = builtin.os.tag; + +var arena_allocator: std.heap.ArenaAllocator = .init(std.heap.page_allocator); +var debug_allocator: std.heap.DebugAllocator(.{}) = .init; + +pub fn main() !void { + const gpa, const gpa_deinit = gpa: { + if (native_os == .wasi) break :gpa .{ std.heap.wasm_allocator, false }; + if (builtin.single_threaded) break :gpa .{ arena_allocator.allocator(), true }; + break :gpa switch (builtin.mode) { + .Debug, .ReleaseSafe => .{ debug_allocator.allocator(), true }, + .ReleaseFast, .ReleaseSmall => .{ std.heap.smp_allocator, false }, + }; + }; + defer if (gpa_deinit) { + _ = debug_allocator.deinit(); + }; + + var args = try std.process.argsWithAllocator(gpa); + defer args.deinit(); + + _ = args.skip(); +} diff --git a/applets/grep/main.zig b/applets/grep/main.zig new file mode 100644 index 0000000..7f02b5a --- /dev/null +++ b/applets/grep/main.zig @@ -0,0 +1,25 @@ +const builtin = @import("builtin"); +const std = @import("std"); +const native_os = builtin.os.tag; + +var arena_allocator: std.heap.ArenaAllocator = .init(std.heap.page_allocator); +var debug_allocator: std.heap.DebugAllocator(.{}) = .init; + +pub fn main() !void { + const gpa, const gpa_deinit = gpa: { + if (native_os == .wasi) break :gpa .{ std.heap.wasm_allocator, false }; + if (builtin.single_threaded) break :gpa .{ arena_allocator.allocator(), true }; + break :gpa switch (builtin.mode) { + .Debug, .ReleaseSafe => .{ debug_allocator.allocator(), true }, + .ReleaseFast, .ReleaseSmall => .{ std.heap.smp_allocator, false }, + }; + }; + defer if (gpa_deinit) { + _ = debug_allocator.deinit(); + }; + + var args = try std.process.argsWithAllocator(gpa); + defer args.deinit(); + + _ = args.skip(); +} diff --git a/applets/head/main.zig b/applets/head/main.zig new file mode 100644 index 0000000..6a71dee --- /dev/null +++ b/applets/head/main.zig @@ -0,0 +1,103 @@ +const builtin = @import("builtin"); +const std = @import("std"); +const native_os = builtin.os.tag; + +fn indexOfCount(comptime T: type, haystack: []const T, max: usize, needle: []const T) ?usize { + var i: usize = 0; + var x: usize = 0; + + while (i < haystack.len) : (i += 1) { + if (x == max) return i; + + i += std.mem.indexOfPos(T, haystack, i, needle) orelse return null; + x += 1; + } + return null; +} + +fn head(bytes: ?isize, lines: isize, delim: []const u8, source_file: std.fs.File, gpa: std.mem.Allocator) !void { + const source = try source_file.readToEndAlloc(gpa, std.math.maxInt(usize)); + defer gpa.free(source); + + var stdout = std.io.getStdOut(); + if (bytes) |b| { + const byte_pos = if (b < 0) source.len - @abs(b) else @abs(b); + + try stdout.writeAll(source[0..byte_pos]); + } else { + const line_count = std.mem.count(u8, source, delim); + const line_pos = if (lines < 0) line_count - @abs(lines) else @abs(lines); + + try stdout.writeAll(source[0..(indexOfCount(u8, source, line_pos, delim) orelse source.len)]); + } +} + +var arena_allocator: std.heap.ArenaAllocator = .init(std.heap.page_allocator); +var debug_allocator: std.heap.DebugAllocator(.{}) = .init; + +pub fn main() !void { + const gpa, const gpa_deinit = gpa: { + if (native_os == .wasi) break :gpa .{ std.heap.wasm_allocator, false }; + if (builtin.single_threaded) break :gpa .{ arena_allocator.allocator(), true }; + break :gpa switch (builtin.mode) { + .Debug, .ReleaseSafe => .{ debug_allocator.allocator(), true }, + .ReleaseFast, .ReleaseSmall => .{ std.heap.smp_allocator, false }, + }; + }; + defer if (gpa_deinit) { + _ = debug_allocator.deinit(); + }; + + var args = try std.process.argsWithAllocator(gpa); + defer args.deinit(); + + _ = args.skip(); + + var bytes: ?isize = null; + var lines: isize = 10; + var delim: []const u8 = "\n"; + + var files = std.ArrayList(std.fs.File).init(gpa); + defer { + for (files.items) |file| file.close(); + files.deinit(); + } + + while (args.next()) |arg| { + if (std.mem.startsWith(u8, arg, "-c") or std.mem.startsWith(u8, arg, "--bytes=")) { + bytes = try std.fmt.parseInt( + isize, + blk: { + if (std.mem.startsWith(u8, arg, "--bytes=")) break :blk arg[8..]; + if (std.mem.startsWith(u8, arg, "-c") and arg.len > 2) break :blk arg[2..]; + break :blk args.next() orelse return error.MissingArgument; + }, + 10, + ); + } else if (std.mem.startsWith(u8, arg, "-n") or std.mem.eql(u8, arg, "--lines")) { + lines = try std.fmt.parseInt( + isize, + blk: { + if (std.mem.startsWith(u8, arg, "--lines=")) break :blk arg[8..]; + if (std.mem.startsWith(u8, arg, "-n") and arg.len > 2) break :blk arg[2..]; + break :blk args.next() orelse return error.MissingArgument; + }, + 10, + ); + } else if (std.mem.startsWith(u8, arg, "--zero-terminated")) { + delim = &.{0}; + } else if (std.mem.startsWith(u8, arg, "-")) { + const stderr = std.io.getStdErr().writer().any(); + try stderr.print("head: unknown argument {s}\n", .{arg}); + std.process.exit(1); + } else { + try files.append(if (std.fs.path.isAbsolute(arg)) try std.fs.openFileAbsolute(arg, .{}) else try std.fs.cwd().openFile(arg, .{})); + } + } + + if (files.items.len == 0) { + try head(bytes, lines, delim, std.io.getStdIn(), gpa); + } else { + for (files.items) |file| try head(bytes, lines, delim, file, gpa); + } +} diff --git a/applets/install/main.zig b/applets/install/main.zig new file mode 100644 index 0000000..7f02b5a --- /dev/null +++ b/applets/install/main.zig @@ -0,0 +1,25 @@ +const builtin = @import("builtin"); +const std = @import("std"); +const native_os = builtin.os.tag; + +var arena_allocator: std.heap.ArenaAllocator = .init(std.heap.page_allocator); +var debug_allocator: std.heap.DebugAllocator(.{}) = .init; + +pub fn main() !void { + const gpa, const gpa_deinit = gpa: { + if (native_os == .wasi) break :gpa .{ std.heap.wasm_allocator, false }; + if (builtin.single_threaded) break :gpa .{ arena_allocator.allocator(), true }; + break :gpa switch (builtin.mode) { + .Debug, .ReleaseSafe => .{ debug_allocator.allocator(), true }, + .ReleaseFast, .ReleaseSmall => .{ std.heap.smp_allocator, false }, + }; + }; + defer if (gpa_deinit) { + _ = debug_allocator.deinit(); + }; + + var args = try std.process.argsWithAllocator(gpa); + defer args.deinit(); + + _ = args.skip(); +} diff --git a/applets/mkdir/main.zig b/applets/mkdir/main.zig new file mode 100644 index 0000000..fde9723 --- /dev/null +++ b/applets/mkdir/main.zig @@ -0,0 +1,46 @@ +const builtin = @import("builtin"); +const std = @import("std"); +const native_os = builtin.os.tag; + +var arena_allocator: std.heap.ArenaAllocator = .init(std.heap.page_allocator); +var debug_allocator: std.heap.DebugAllocator(.{}) = .init; + +pub fn main() !void { + const gpa, const gpa_deinit = gpa: { + if (native_os == .wasi) break :gpa .{ std.heap.wasm_allocator, false }; + if (builtin.single_threaded) break :gpa .{ arena_allocator.allocator(), true }; + break :gpa switch (builtin.mode) { + .Debug, .ReleaseSafe => .{ debug_allocator.allocator(), true }, + .ReleaseFast, .ReleaseSmall => .{ std.heap.smp_allocator, false }, + }; + }; + defer if (gpa_deinit) { + _ = debug_allocator.deinit(); + }; + + var args = try std.process.argsWithAllocator(gpa); + defer args.deinit(); + + _ = args.skip(); + + var i: usize = 0; + + while (args.next()) |p| : (i += 1) { + var parent, const is_cwd = blk: { + if (std.fs.path.dirname(p)) |dirname| { + if (std.fs.path.isAbsolute(dirname)) break :blk .{ try std.fs.openDirAbsolute(dirname, .{}), false }; + break :blk .{ try std.fs.cwd().openDir(dirname, .{}), false }; + } + break :blk .{ std.fs.cwd(), true }; + }; + defer if (!is_cwd) parent.close(); + + try parent.makeDir(std.fs.path.basename(p)); + } + + if (i == 0) { + const stderr = std.io.getStdErr().writer().any(); + try stderr.writeAll("mkdir: path is required\n"); + std.process.exit(1); + } +} diff --git a/applets/mktemp/main.zig b/applets/mktemp/main.zig new file mode 100644 index 0000000..7f02b5a --- /dev/null +++ b/applets/mktemp/main.zig @@ -0,0 +1,25 @@ +const builtin = @import("builtin"); +const std = @import("std"); +const native_os = builtin.os.tag; + +var arena_allocator: std.heap.ArenaAllocator = .init(std.heap.page_allocator); +var debug_allocator: std.heap.DebugAllocator(.{}) = .init; + +pub fn main() !void { + const gpa, const gpa_deinit = gpa: { + if (native_os == .wasi) break :gpa .{ std.heap.wasm_allocator, false }; + if (builtin.single_threaded) break :gpa .{ arena_allocator.allocator(), true }; + break :gpa switch (builtin.mode) { + .Debug, .ReleaseSafe => .{ debug_allocator.allocator(), true }, + .ReleaseFast, .ReleaseSmall => .{ std.heap.smp_allocator, false }, + }; + }; + defer if (gpa_deinit) { + _ = debug_allocator.deinit(); + }; + + var args = try std.process.argsWithAllocator(gpa); + defer args.deinit(); + + _ = args.skip(); +} diff --git a/applets/nproc/main.zig b/applets/nproc/main.zig new file mode 100644 index 0000000..cedcae4 --- /dev/null +++ b/applets/nproc/main.zig @@ -0,0 +1,34 @@ +const builtin = @import("builtin"); +const std = @import("std"); + +fn countUnknown() !usize { + return 1; +} + +const count = blk: { + const impls = struct { + pub fn linux() !usize { + var dir = try std.fs.openDirAbsolute("/sys/devices/system/cpu", .{ .iterate = true }); + defer dir.close(); + + var i: usize = 0; + + var iter = dir.iterate(); + while (try iter.next()) |entry| { + if (std.mem.startsWith(u8, entry.name, "cpu")) { + if (std.fmt.parseInt(usize, entry.name[3..], 10) catch null) |_| { + i += 1; + } + } + } + return i; + } + }; + + break :blk if (@hasDecl(impls, @tagName(builtin.os.tag))) @field(impls, @tagName(builtin.os.tag)) else countUnknown; +}; + +pub fn main() !void { + const stdout = std.io.getStdOut().writer().any(); + try stdout.print("{}\n", .{try count()}); +} diff --git a/applets/pwd.zig b/applets/pwd.zig deleted file mode 100644 index 6c78428..0000000 --- a/applets/pwd.zig +++ /dev/null @@ -1,9 +0,0 @@ -const builtin = @import("builtin"); -const std = @import("std"); -const ziggybox = @import("ziggybox"); - -pub fn run(_: *std.process.ArgIterator) !void { - const str = try std.process.getCwdAlloc(ziggybox.common.allocator); - defer ziggybox.common.allocator.free(str); - return ziggybox.io.getStdOut().print("{s}\n", .{str}); -} diff --git a/applets/pwd/main.zig b/applets/pwd/main.zig new file mode 100644 index 0000000..7b34dd4 --- /dev/null +++ b/applets/pwd/main.zig @@ -0,0 +1,27 @@ +const builtin = @import("builtin"); +const std = @import("std"); +const native_os = builtin.os.tag; + +var arena_allocator: std.heap.ArenaAllocator = .init(std.heap.page_allocator); +var debug_allocator: std.heap.DebugAllocator(.{}) = .init; + +pub fn main() !void { + const gpa, const gpa_deinit = gpa: { + if (native_os == .wasi) break :gpa .{ std.heap.wasm_allocator, false }; + if (builtin.single_threaded) break :gpa .{ arena_allocator.allocator(), true }; + break :gpa switch (builtin.mode) { + .Debug, .ReleaseSafe => .{ debug_allocator.allocator(), true }, + .ReleaseFast, .ReleaseSmall => .{ std.heap.smp_allocator, false }, + }; + }; + defer if (gpa_deinit) { + _ = debug_allocator.deinit(); + }; + + const cwd = try std.process.getCwdAlloc(gpa); + defer gpa.free(cwd); + + const stdout = std.io.getStdOut().writer().any(); + try stdout.writeAll(cwd); + try stdout.writeByte('\n'); +} diff --git a/applets/rm/main.zig b/applets/rm/main.zig new file mode 100644 index 0000000..7f02b5a --- /dev/null +++ b/applets/rm/main.zig @@ -0,0 +1,25 @@ +const builtin = @import("builtin"); +const std = @import("std"); +const native_os = builtin.os.tag; + +var arena_allocator: std.heap.ArenaAllocator = .init(std.heap.page_allocator); +var debug_allocator: std.heap.DebugAllocator(.{}) = .init; + +pub fn main() !void { + const gpa, const gpa_deinit = gpa: { + if (native_os == .wasi) break :gpa .{ std.heap.wasm_allocator, false }; + if (builtin.single_threaded) break :gpa .{ arena_allocator.allocator(), true }; + break :gpa switch (builtin.mode) { + .Debug, .ReleaseSafe => .{ debug_allocator.allocator(), true }, + .ReleaseFast, .ReleaseSmall => .{ std.heap.smp_allocator, false }, + }; + }; + defer if (gpa_deinit) { + _ = debug_allocator.deinit(); + }; + + var args = try std.process.argsWithAllocator(gpa); + defer args.deinit(); + + _ = args.skip(); +} diff --git a/applets/sed/main.zig b/applets/sed/main.zig new file mode 100644 index 0000000..7f02b5a --- /dev/null +++ b/applets/sed/main.zig @@ -0,0 +1,25 @@ +const builtin = @import("builtin"); +const std = @import("std"); +const native_os = builtin.os.tag; + +var arena_allocator: std.heap.ArenaAllocator = .init(std.heap.page_allocator); +var debug_allocator: std.heap.DebugAllocator(.{}) = .init; + +pub fn main() !void { + const gpa, const gpa_deinit = gpa: { + if (native_os == .wasi) break :gpa .{ std.heap.wasm_allocator, false }; + if (builtin.single_threaded) break :gpa .{ arena_allocator.allocator(), true }; + break :gpa switch (builtin.mode) { + .Debug, .ReleaseSafe => .{ debug_allocator.allocator(), true }, + .ReleaseFast, .ReleaseSmall => .{ std.heap.smp_allocator, false }, + }; + }; + defer if (gpa_deinit) { + _ = debug_allocator.deinit(); + }; + + var args = try std.process.argsWithAllocator(gpa); + defer args.deinit(); + + _ = args.skip(); +} diff --git a/applets/sort/main.zig b/applets/sort/main.zig new file mode 100644 index 0000000..7f02b5a --- /dev/null +++ b/applets/sort/main.zig @@ -0,0 +1,25 @@ +const builtin = @import("builtin"); +const std = @import("std"); +const native_os = builtin.os.tag; + +var arena_allocator: std.heap.ArenaAllocator = .init(std.heap.page_allocator); +var debug_allocator: std.heap.DebugAllocator(.{}) = .init; + +pub fn main() !void { + const gpa, const gpa_deinit = gpa: { + if (native_os == .wasi) break :gpa .{ std.heap.wasm_allocator, false }; + if (builtin.single_threaded) break :gpa .{ arena_allocator.allocator(), true }; + break :gpa switch (builtin.mode) { + .Debug, .ReleaseSafe => .{ debug_allocator.allocator(), true }, + .ReleaseFast, .ReleaseSmall => .{ std.heap.smp_allocator, false }, + }; + }; + defer if (gpa_deinit) { + _ = debug_allocator.deinit(); + }; + + var args = try std.process.argsWithAllocator(gpa); + defer args.deinit(); + + _ = args.skip(); +} diff --git a/applets/tail/main.zig b/applets/tail/main.zig new file mode 100644 index 0000000..f3f835e --- /dev/null +++ b/applets/tail/main.zig @@ -0,0 +1,103 @@ +const builtin = @import("builtin"); +const std = @import("std"); +const native_os = builtin.os.tag; + +fn indexOfCount(comptime T: type, haystack: []const T, max: usize, needle: []const T) ?usize { + var i: usize = 0; + var x: usize = 0; + + while (i < haystack.len) : (i += 1) { + if (x == max) return i; + i += std.mem.indexOfPos(T, haystack, i, needle) orelse return null; + x += 1; + } + return null; +} + +fn tail(bytes: ?isize, lines: isize, delim: []const u8, source_file: std.fs.File, gpa: std.mem.Allocator) !void { + const source = try source_file.readToEndAlloc(gpa, std.math.maxInt(usize)); + defer gpa.free(source); + + var stdout = std.io.getStdOut(); + if (bytes) |b| { + const byte_pos = if (b < 0) source.len - @abs(b) else @abs(b); + + try stdout.writeAll(source[byte_pos..]); + } else { + if (lines < 0) { + try stdout.writeAll(source[0..(indexOfCount(u8, source, @abs(lines), delim) orelse source.len)]); + } else { + try stdout.writeAll(source[(indexOfCount(u8, source, @abs(lines), delim) orelse 0)..]); + } + } +} + +var arena_allocator: std.heap.ArenaAllocator = .init(std.heap.page_allocator); +var debug_allocator: std.heap.DebugAllocator(.{}) = .init; + +pub fn main() !void { + const gpa, const gpa_deinit = gpa: { + if (native_os == .wasi) break :gpa .{ std.heap.wasm_allocator, false }; + if (builtin.single_threaded) break :gpa .{ arena_allocator.allocator(), true }; + break :gpa switch (builtin.mode) { + .Debug, .ReleaseSafe => .{ debug_allocator.allocator(), true }, + .ReleaseFast, .ReleaseSmall => .{ std.heap.smp_allocator, false }, + }; + }; + defer if (gpa_deinit) { + _ = debug_allocator.deinit(); + }; + + var args = try std.process.argsWithAllocator(gpa); + defer args.deinit(); + + _ = args.skip(); + + var bytes: ?isize = null; + var lines: isize = 10; + var delim: []const u8 = "\n"; + + var files = std.ArrayList(std.fs.File).init(gpa); + defer { + for (files.items) |file| file.close(); + files.deinit(); + } + + while (args.next()) |arg| { + if (std.mem.startsWith(u8, arg, "-c") or std.mem.startsWith(u8, arg, "--bytes=")) { + bytes = try std.fmt.parseInt( + isize, + blk: { + if (std.mem.startsWith(u8, arg, "--bytes=")) break :blk arg[8..]; + if (std.mem.startsWith(u8, arg, "-c") and arg.len > 2) break :blk arg[2..]; + break :blk args.next() orelse return error.MissingArgument; + }, + 10, + ); + } else if (std.mem.startsWith(u8, arg, "-n") or std.mem.eql(u8, arg, "--lines")) { + lines = try std.fmt.parseInt( + isize, + blk: { + if (std.mem.startsWith(u8, arg, "--lines=")) break :blk arg[8..]; + if (std.mem.startsWith(u8, arg, "-n") and arg.len > 2) break :blk arg[2..]; + break :blk args.next() orelse return error.MissingArgument; + }, + 10, + ); + } else if (std.mem.startsWith(u8, arg, "--zero-terminated")) { + delim = &.{0}; + } else if (std.mem.startsWith(u8, arg, "-")) { + const stderr = std.io.getStdErr().writer().any(); + try stderr.print("tail: unknown argument {s}\n", .{arg}); + std.process.exit(1); + } else { + try files.append(if (std.fs.path.isAbsolute(arg)) try std.fs.openFileAbsolute(arg, .{}) else try std.fs.cwd().openFile(arg, .{})); + } + } + + if (files.items.len == 0) { + try tail(bytes, lines, delim, std.io.getStdIn(), gpa); + } else { + for (files.items) |file| try tail(bytes, lines, delim, file, gpa); + } +} diff --git a/applets/tar/extract.zig b/applets/tar/extract.zig new file mode 100644 index 0000000..e7a01b8 --- /dev/null +++ b/applets/tar/extract.zig @@ -0,0 +1,70 @@ +const std = @import("std"); +const Operation = @import("main.zig").Operation; + +const Source = union(enum) { + stdin: void, + file: std.fs.File, + + pub fn readAllAlloc(self: *Source, gpa: std.mem.Allocator) ![]const u8 { + return switch (self.*) { + .stdin => std.io.getStdIn().readToEndAlloc(gpa, std.math.maxInt(usize)), + .file => |file| file.readToEndAlloc(gpa, std.math.maxInt(usize)), + }; + } + + pub fn reader(self: *Source) std.io.AnyReader { + return switch (self.*) { + .stdin => std.io.getStdIn().reader().any(), + .file => |file| file.reader().any(), + }; + } + + pub fn close(self: *Source) void { + return switch (self.*) { + .stdin => {}, + .file => |file| file.close(), + }; + } +}; + +pub fn extract(args: *std.process.ArgIterator, gpa: std.mem.Allocator, flags: []const Operation) !void { + var source, const is_verbose = blk: { + var set_source: ?Source = null; + var set_verbose = false; + for (flags) |flag| { + switch (flag) { + .f => { + const arg = args.next() orelse return error.MissingArgument; + set_source = .{ + .file = if (std.fs.path.isAbsolute(arg)) try std.fs.openFileAbsolute(arg, .{ + .mode = .read_only, + }) else try std.fs.cwd().openFile(arg, .{ + .mode = .read_only, + }), + }; + }, + .v => set_verbose = true, + else => return error.UnknownFlag, + } + } + break :blk .{ set_source orelse .stdin, set_verbose }; + }; + defer source.close(); + + const source_buffer = try source.readAllAlloc(gpa); + defer gpa.free(source_buffer); + + var fixed_buffer_source = std.io.fixedBufferStream(source_buffer); + + var buffer = std.ArrayList(u8).init(gpa); + defer buffer.deinit(); + + try std.compress.gzip.decompress(fixed_buffer_source.reader(), buffer.writer()); + + var fixed_buffer_tmp = std.io.fixedBufferStream(buffer.items); + + // TODO: support destination path + try std.tar.pipeToFileSystem(std.fs.cwd(), fixed_buffer_tmp.reader(), .{}); + + _ = is_verbose; +} diff --git a/applets/tar/main.zig b/applets/tar/main.zig new file mode 100644 index 0000000..5ca5fe4 --- /dev/null +++ b/applets/tar/main.zig @@ -0,0 +1,54 @@ +const builtin = @import("builtin"); +const std = @import("std"); +const assert = std.debug.assert; +const native_os = builtin.os.tag; + +pub const Operation = enum { + x, + v, + f, +}; + +var arena_allocator: std.heap.ArenaAllocator = .init(std.heap.page_allocator); +var debug_allocator: std.heap.DebugAllocator(.{}) = .init; + +pub fn main() !void { + const gpa, const gpa_deinit = gpa: { + if (native_os == .wasi) break :gpa .{ std.heap.wasm_allocator, false }; + if (builtin.single_threaded) break :gpa .{ arena_allocator.allocator(), true }; + break :gpa switch (builtin.mode) { + .Debug, .ReleaseSafe => .{ debug_allocator.allocator(), true }, + .ReleaseFast, .ReleaseSmall => .{ std.heap.smp_allocator, false }, + }; + }; + defer if (gpa_deinit) { + _ = debug_allocator.deinit(); + }; + + var args = try std.process.argsWithAllocator(gpa); + defer args.deinit(); + + _ = args.skip(); + + if (args.next()) |ops| { + var opflags = std.ArrayList(Operation).init(gpa); + defer opflags.deinit(); + + for (ops) |op| { + if (std.meta.stringToEnum(Operation, &.{op})) |flag| { + try opflags.append(flag); + continue; + } + return error.UnknownFlag; + } + + assert(opflags.items.len > 0); + + const cmd: *const fn (args: *std.process.ArgIterator, gpa: std.mem.Allocator, flags: []const Operation) anyerror!void = switch (opflags.items[0]) { + .x => @import("extract.zig").extract, + else => return error.UnknownOp, + }; + + return try cmd(&args, gpa, opflags.items[1..]); + } +} diff --git a/applets/true.zig b/applets/true.zig deleted file mode 100644 index 1ab2e7b..0000000 --- a/applets/true.zig +++ /dev/null @@ -1,7 +0,0 @@ -const builtin = @import("builtin"); -const std = @import("std"); -const ziggybox = @import("ziggybox"); - -pub fn run(_: *std.process.ArgIterator) !void { - std.os.exit(0); -} diff --git a/applets/true/main.zig b/applets/true/main.zig new file mode 100644 index 0000000..f6e2a29 --- /dev/null +++ b/applets/true/main.zig @@ -0,0 +1,5 @@ +const std = @import("std"); + +pub fn main() void { + std.process.exit(0); +} diff --git a/applets/umount.zig b/applets/umount.zig deleted file mode 100644 index 3f590a5..0000000 --- a/applets/umount.zig +++ /dev/null @@ -1,42 +0,0 @@ -const builtin = @import("builtin"); -const clap = @import("clap"); -const std = @import("std"); -const ziggybox = @import("ziggybox"); - -fn umount(path: [*:0]const u8) !void { - const r = std.os.linux.umount(path); - return switch (std.os.errno(r)) { - .SUCCESS => {}, - .PERM => error.AccessDenied, - else => |err| return std.os.unexpectedErrno(err), - }; -} - -pub fn run(args: *std.process.ArgIterator) !void { - const stderr = ziggybox.io.getStdErr(); - - const params = comptime clap.parseParamsComptime( - \\-h, --help Display this help and exit. - \\ The directory to unmount - \\ - ); - - var diag = clap.Diagnostic{}; - var res = ziggybox.clap.parseEx(clap.Help, ¶ms, comptime .{ - .path = clap.parsers.string, - }, args, .{ - .allocator = ziggybox.common.allocator, - .diagnostic = &diag, - }) catch |err| { - diag.report(stderr, err) catch {}; - return err; - }; - defer res.deinit(); - - if (res.args.help != 0 or res.positionals.len < 1) - return clap.help(stderr, clap.Help, ¶ms, .{}); - - const path = try ziggybox.common.allocator.dupeZ(u8, res.positionals[0]); - defer ziggybox.common.allocator.free(path); - try umount(path); -} diff --git a/applets/uptime.zig b/applets/uptime.zig deleted file mode 100644 index 272de09..0000000 --- a/applets/uptime.zig +++ /dev/null @@ -1,100 +0,0 @@ -const builtin = @import("builtin"); -const clap = @import("clap"); -const std = @import("std"); -const ziggybox = @import("ziggybox"); - -inline fn load(value: c_ulong) f32 { - const fshift: u8 = 16; - const fixed1: u17 = 65536; - const loadInt = value >> fshift; - const loadFrac = ((value & (fixed1 - 1)) * 100) >> fshift; - return std.math.lossyCast(f32, loadInt) + (std.math.lossyCast(f32, loadFrac) / @as(f32, 100.0)); -} - -const Uptime = struct { - time: std.time.epoch.EpochSeconds, - uptime: std.time.epoch.EpochSeconds, - users: usize = 0, - loads: [3]f32, - - pub fn init() !Uptime { - return switch (builtin.os.tag) { - .linux => blk: { - var sysinfo: ziggybox.os.linux.Sysinfo = undefined; - switch (if (builtin.link_libc) std.c.getErrno(ziggybox.os.linux.sysinfo(&sysinfo)) else std.os.errno(ziggybox.os.linux.sysinfo(&sysinfo))) { - .SUCCESS => {}, - .FAULT => unreachable, - else => |err| return std.os.unexpectedErrno(err), - } - - const time = std.time.epoch.EpochSeconds{ - .secs = std.math.lossyCast(u64, std.time.timestamp()), - }; - - const uptime = std.time.epoch.EpochSeconds{ - .secs = std.math.lossyCast(u64, sysinfo.uptime), - }; - - break :blk .{ - .time = time, - .uptime = uptime, - .loads = [3]f32{ - load(sysinfo.loads[0]), - load(sysinfo.loads[1]), - load(sysinfo.loads[2]), - }, - }; - }, - .windows => .{ - .time = .{ - .secs = std.math.lossyCast(u64, std.time.timestamp()), - }, - .uptime = .{ - .secs = std.math.lossyCast(u64, ziggybox.os.windows.GetTickCount64() / std.time.ms_per_s), - }, - .loads = [3]f32{ 0, 0, 0 }, - }, - else => .{ - .time = try ziggybox.os.time(), - .uptime = .{ .secs = 0 }, - .loads = [3]f32{ 0, 0, 0 }, - }, - }; - } -}; - -pub fn run(_: *std.process.ArgIterator) !void { - const value = try Uptime.init(); - const stdout = ziggybox.io.getStdOut(); - - try stdout.print(" {d:0>2}:{d:0>2}:{d:0>2} up ", .{ - value.time.getDaySeconds().getHoursIntoDay(), - value.time.getDaySeconds().getMinutesIntoHour(), - value.time.secs % 60, - }); - - const updays = value.uptime.getEpochDay().day; - if (updays > 0) { - try stdout.print("{} day", .{updays}); - if (updays > 1) try stdout.writeByte('s'); - try stdout.writeAll(", "); - } - - const uphours = value.uptime.getDaySeconds().getHoursIntoDay(); - const upminutes = value.uptime.getDaySeconds().getMinutesIntoHour(); - if (uphours > 0) { - try stdout.print("{}:{}", .{ uphours, upminutes }); - } else { - try stdout.print("{} min", .{upminutes}); - } - - if (value.users > 0) { - try stdout.print(", {} users", .{value.users}); - } - - try stdout.print(", load average: {d:.2}, {d:.2}, {d:.2}\n", .{ - value.loads[0], - value.loads[1], - value.loads[2], - }); -} diff --git a/applets/uptime/Uptime.zig b/applets/uptime/Uptime.zig new file mode 100644 index 0000000..1bbd3f6 --- /dev/null +++ b/applets/uptime/Uptime.zig @@ -0,0 +1,65 @@ +const builtin = @import("builtin"); +const std = @import("std"); +const linux = @import("linux.zig"); +const windows = @import("windows.zig"); +const Uptime = @This(); + +time: std.time.epoch.EpochSeconds, +uptime: std.time.epoch.EpochSeconds, +users: usize = 0, +loads: [3]f32, + +inline fn load(value: c_ulong) f32 { + const fshift: u8 = 16; + const fixed1: u17 = 65536; + const loadInt = value >> fshift; + const loadFrac = ((value & (fixed1 - 1)) * 100) >> fshift; + return std.math.lossyCast(f32, loadInt) + (std.math.lossyCast(f32, loadFrac) / @as(f32, 100.0)); +} + +pub fn init() !Uptime { + return switch (builtin.os.tag) { + .linux => blk: { + var sysinfo: linux.Sysinfo = undefined; + switch (std.posix.errno(linux.sysinfo(&sysinfo))) { + .SUCCESS => {}, + .FAULT => unreachable, + else => |err| return std.posix.unexpectedErrno(err), + } + + const time = std.time.epoch.EpochSeconds{ + .secs = std.math.lossyCast(u64, std.time.timestamp()), + }; + + const uptime = std.time.epoch.EpochSeconds{ + .secs = std.math.lossyCast(u64, sysinfo.uptime), + }; + + break :blk .{ + .time = time, + .uptime = uptime, + .loads = [3]f32{ + load(sysinfo.loads[0]), + load(sysinfo.loads[1]), + load(sysinfo.loads[2]), + }, + }; + }, + .windows => .{ + .time = .{ + .secs = std.math.lossyCast(u64, std.time.timestamp()), + }, + .uptime = .{ + .secs = std.math.lossyCast(u64, windows.GetTickCount64() / std.time.ms_per_s), + }, + .loads = [3]f32{ 0, 0, 0 }, + }, + else => .{ + .time = .{ + .secs = std.math.lossyCast(u64, std.time.timestamp()), + }, + .uptime = .{ .secs = 0 }, + .loads = [3]f32{ 0, 0, 0 }, + }, + }; +} diff --git a/lib/ziggybox/os/linux.zig b/applets/uptime/linux.zig similarity index 100% rename from lib/ziggybox/os/linux.zig rename to applets/uptime/linux.zig diff --git a/applets/uptime/main.zig b/applets/uptime/main.zig new file mode 100644 index 0000000..204e773 --- /dev/null +++ b/applets/uptime/main.zig @@ -0,0 +1,38 @@ +const std = @import("std"); +const Uptime = @import("Uptime.zig"); + +pub fn main() !void { + const value = try Uptime.init(); + const stdout = std.io.getStdOut().writer(); + + try stdout.print(" {d:0>2}:{d:0>2}:{d:0>2} up ", .{ + value.time.getDaySeconds().getHoursIntoDay(), + value.time.getDaySeconds().getMinutesIntoHour(), + value.time.secs % 60, + }); + + const updays = value.uptime.getEpochDay().day; + if (updays > 0) { + try stdout.print("{} day", .{updays}); + if (updays > 1) try stdout.writeByte('s'); + try stdout.writeAll(", "); + } + + const uphours = value.uptime.getDaySeconds().getHoursIntoDay(); + const upminutes = value.uptime.getDaySeconds().getMinutesIntoHour(); + if (uphours > 0) { + try stdout.print("{}:{}", .{ uphours, upminutes }); + } else { + try stdout.print("{} min", .{upminutes}); + } + + if (value.users > 0) { + try stdout.print(", {} users", .{value.users}); + } + + try stdout.print(", load average: {d:.2}, {d:.2}, {d:.2}\n", .{ + value.loads[0], + value.loads[1], + value.loads[2], + }); +} diff --git a/lib/ziggybox/os/windows.zig b/applets/uptime/windows.zig similarity index 100% rename from lib/ziggybox/os/windows.zig rename to applets/uptime/windows.zig diff --git a/applets/yes.zig b/applets/yes.zig deleted file mode 100644 index 462de57..0000000 --- a/applets/yes.zig +++ /dev/null @@ -1,30 +0,0 @@ -const builtin = @import("builtin"); -const clap = @import("clap"); -const std = @import("std"); -const ziggybox = @import("ziggybox"); - -pub fn run(args: *std.process.ArgIterator) !void { - const params = comptime clap.parseParamsComptime( - \\-h, --help Display this help and exit. - \\... - \\ - ); - - var diag = clap.Diagnostic{}; - var res = ziggybox.clap.parseEx(clap.Help, ¶ms, clap.parsers.default, args, .{ - .allocator = ziggybox.common.allocator, - .diagnostic = &diag, - }) catch |err| { - diag.report(ziggybox.io.getStdErr(), err) catch {}; - return err; - }; - defer res.deinit(); - - if (res.args.help != 0) - return clap.help(ziggybox.io.getStdErr(), clap.Help, ¶ms, .{}); - - const str = try (if (res.positionals.len > 0) std.mem.join(ziggybox.common.allocator, " ", res.positionals) else ziggybox.common.allocator.dupe(u8, "y")); - defer ziggybox.common.allocator.free(str); - - while (true) try ziggybox.io.getStdOut().print("{s}\n", .{str}); -} diff --git a/build.zig b/build.zig index f229086..fa6b91f 100644 --- a/build.zig +++ b/build.zig @@ -1,4 +1,5 @@ const std = @import("std"); +const applets = @import("applets.zig"); fn runAllowFail(b: *std.Build, argv: []const []const u8) ?[]const u8 { var c: u8 = 0; @@ -13,157 +14,50 @@ pub fn build(b: *std.Build) !void { const target = b.standardTargetOptions(.{}); const optimize = b.standardOptimizeOption(.{}); - const linkage = b.option(std.builtin.LinkMode, "linkage", "Sets the link mode") orelse @as(std.builtin.LinkMode, if (target.result.isGnuLibC()) .dynamic else .static); - const appletsList = b.option([]const []const u8, "applets", "List of applets") orelse &[_][]const u8{ - "arch", - "cal", - "cat", - "chroot", - "false", - "pwd", - "true", - "umount", - "uptime", - "yes", + const linkage = b.option(std.builtin.LinkMode, "linkage", "Sets the link mode"); + const applets_list = b.option([]const []const u8, "applets", "List of applets") orelse switch (target.result.os.tag) { + .linux, .macos => applets.all, + else => applets.min, }; - const versionTag = b.option([]const u8, "version-tag", "Sets the version tag") orelse runAllowFail(b, &.{ "git", "rev-parse", "--abbrev-ref", "HEAD" }); - const buildHash = b.option([]const u8, "build-hash", "Sets the build hash") orelse runAllowFail(b, &.{ "git", "rev-parse", "HEAD" }); + const version_tag = b.option([]const u8, "version-tag", "Sets the version tag") orelse runAllowFail(b, &.{ "git", "rev-parse", "--abbrev-ref", "HEAD" }); + const build_hash = b.option([]const u8, "build-hash", "Sets the build hash") orelse runAllowFail(b, &.{ "git", "rev-parse", "HEAD" }); + const link_libc = b.option(bool, "link-libc", "Use Zig's stdlib or the C library"); + const strip = b.option(bool, "strip", "Whether to strip the binaries"); + const single_threaded = b.option(bool, "single-threaded", "Whether to only build for single-threaded operations") orelse (optimize == .ReleaseSmall); const version = std.SemanticVersion{ .major = 0, .minor = 1, .patch = 0, - .pre = versionTag, - .build = if (buildHash) |h| h[0..@min(h.len, 7)] else null, + .pre = version_tag, + .build = if (build_hash) |h| h[0..@min(h.len, 7)] else null, }; - const clap = b.dependency("clap", .{ - .target = target, - .optimize = optimize, - }); - - const ziggybox = b.createModule(.{ - .root_source_file = .{ .path = b.pathFromRoot("lib/ziggybox.zig") }, - .imports = &.{ - .{ - .name = "clap", - .module = clap.module("clap"), - }, - }, - }); - - var appletsImports = std.ArrayList(u8).init(b.allocator); - errdefer appletsImports.deinit(); - - for (appletsList) |applet| { - try appletsImports.writer().print( - \\pub const @"{s}" = @import("ziggybox-applets").@"{s}"; - \\ - , .{ applet, applet }); - } - - const writeFiles = b.addWriteFiles(); - - const applets = b.createModule(.{ - .root_source_file = writeFiles.add("applet-imports.zig", appletsImports.items), - .imports = &.{ - .{ - .name = "ziggybox-applets", - .module = b.createModule(.{ - .root_source_file = .{ .path = b.pathFromRoot("applets.zig") }, - .imports = &.{ - .{ - .name = "clap", - .module = clap.module("clap"), - }, - .{ - .name = "ziggybox", - .module = ziggybox, - }, - }, - }), - }, - }, - }); - const options = b.addOptions(); - try options.contents.writer().print( - \\const std = @import("std"); - \\pub const version = std.SemanticVersion.parse("{}") catch |err| @compileError(@errorName(err)); - \\ - , .{version}); - - const optionsModule = b.createModule(.{ - .root_source_file = options.getOutput(), - .imports = &.{ - .{ - .name = "applets", - .module = applets, - }, - }, - }); - - const exec = b.addExecutable(.{ - .name = "ziggybox", - .root_source_file = .{ .path = b.pathFromRoot("ziggybox.zig") }, - .target = target, - .optimize = optimize, - .linkage = linkage, - .link_libc = linkage == .dynamic, - .version = version, - }); - - if (buildHash) |h| { - exec.build_id = std.zig.BuildId.initHexString(h[0..@min(h.len, 32)]); - } - - exec.root_module.addImport("applets", applets); - exec.root_module.addImport("clap", clap.module("clap")); - exec.root_module.addImport("options", optionsModule); - exec.root_module.addImport("ziggybox", ziggybox); - b.installArtifact(exec); + options.addOption(std.SemanticVersion, "version", version); + options.addOption([]const []const u8, "applets", applets_list); - for (appletsList) |applet| { - const appletOptions = b.addOptions(); - try appletOptions.contents.appendSlice(options.contents.items); - - try appletOptions.contents.writer().print( - \\pub const applet: ?std.meta.DeclEnum(@import("applets")) = .{s}; - , .{applet}); - - const appletOptionsModule = b.createModule(.{ - .root_source_file = appletOptions.getOutput(), - .imports = &.{ - .{ - .name = "applets", - .module = applets, - }, - }, - }); - - const appletExec = b.addExecutable(.{ + for (applets_list) |applet| { + const exe = b.addExecutable(.{ .name = applet, - .root_source_file = exec.root_module.root_source_file, - .target = target, - .optimize = optimize, - .linkage = linkage, - .link_libc = linkage == .dynamic, .version = version, + .linkage = linkage, + .root_module = b.createModule(.{ + .root_source_file = b.path(b.pathJoin(&.{ "applets", applet, "main.zig" })), + .target = target, + .optimize = optimize, + .imports = &.{ + .{ + .name = "options", + .module = options.createModule(), + }, + }, + .link_libc = link_libc, + .strip = strip, + .single_threaded = single_threaded, + }), }); - - if (buildHash) |h| { - appletExec.build_id = std.zig.BuildId.initHexString(h[0..@min(h.len, 32)]); - } - - appletExec.root_module.addImport("applets", applets); - appletExec.root_module.addImport("clap", clap.module("clap")); - appletExec.root_module.addImport("options", appletOptionsModule); - appletExec.root_module.addImport("ziggybox", ziggybox); - b.installArtifact(appletExec); + b.installArtifact(exe); } - - try options.contents.writer().print( - \\pub const applet: ?std.meta.DeclEnum(@import("applets")) = null; - , .{}); } diff --git a/build.zig.zon b/build.zig.zon index 64de6e1..5a4a889 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -1,11 +1,7 @@ .{ - .name = "ziggybox", + .name = .ziggybox, .version = "0.1.0", .paths = .{"."}, - .dependencies = .{ - .clap = .{ - .url = "https://github.com/Hejsil/zig-clap/archive/209ba4da76e46412acfe18f711cb0b041ff37f10.tar.gz", - .hash = "12200103e7b4a0cb162f2912df4fe97914024a25b5c9fcce6ea4119744f3f2a7f24e", - }, - }, + .dependencies = .{}, + .fingerprint = 0x99a0b186d096a2ce, } diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..77c56df --- /dev/null +++ b/flake.lock @@ -0,0 +1,135 @@ +{ + "nodes": { + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1696426674, + "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-parts": { + "inputs": { + "nixpkgs-lib": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1741352980, + "narHash": "sha256-+u2UunDA4Cl5Fci3m7S643HzKmIDAe+fiXrLqYsR2fs=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "f4330d22f1c5d2ba72d3d22df5597d123fdb60a9", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "flake-utils": { + "inputs": { + "systems": "systems_2" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1741995594, + "narHash": "sha256-2kbqs6PBc2SdPvLn2YZ+WnX3tsmUR4TROxWUtqaWUiI=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "708bb03fb173634aebc79ece93b818f00fa005d9", + "type": "github" + }, + "original": { + "owner": "NixOS", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-parts": "flake-parts", + "nixpkgs": "nixpkgs", + "systems": "systems", + "zig-overlay": "zig-overlay" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "systems_2": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "zig-overlay": { + "inputs": { + "flake-compat": "flake-compat", + "flake-utils": "flake-utils", + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1741825901, + "narHash": "sha256-aeopo+aXg5I2IksOPFN79usw7AeimH1+tjfuMzJHFdk=", + "owner": "mitchellh", + "repo": "zig-overlay", + "rev": "0b14285e283f5a747f372fb2931835dd937c4383", + "type": "github" + }, + "original": { + "owner": "mitchellh", + "repo": "zig-overlay", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..cb042d3 --- /dev/null +++ b/flake.nix @@ -0,0 +1,102 @@ +{ + description = "Busybox in Zig"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs"; + flake-parts = { + url = "github:hercules-ci/flake-parts"; + inputs.nixpkgs-lib.follows = "nixpkgs"; + }; + systems.url = "github:nix-systems/default"; + zig-overlay = { + url = "github:mitchellh/zig-overlay"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + }; + + outputs = + { + self, + nixpkgs, + flake-parts, + systems, + zig-overlay, + ... + }@inputs: + let + inherit (nixpkgs) lib; + in + flake-parts.lib.mkFlake { inherit inputs; } ( + { inputs, ... }: + { + systems = import inputs.systems; + + perSystem = + { + self', + inputs', + system, + pkgs, + ... + }: + { + _module.args.pkgs = import inputs.nixpkgs { + inherit system; + overlays = [ + zig-overlay.overlays.default + ]; + }; + + legacyPackages = pkgs; + + packages = + let + variants = { + default = pkgs.zig; + default-zig_0_15 = pkgs.zigpkgs.master.overrideAttrs ( + final: prev: { + passthru.hook = pkgs.callPackage "${inputs.nixpkgs}/pkgs/development/compilers/zig/hook.nix" { + zig = final.finalPackage; + }; + + inherit (pkgs.zig) meta; + } + ); + }; + + normal = lib.mapAttrs ( + _: zig: + pkgs.callPackage ./nix/package.nix { + inherit zig; + src = inputs.self; + } + ) variants; + + bootstraps = lib.listToAttrs ( + lib.attrValues ( + lib.mapAttrs ( + name: pkg: lib.nameValuePair "bootstrap${lib.removePrefix "default" name}" pkg.passthru.bootstrap + ) normal + ) + ); + in + normal // bootstraps; + + checks = lib.foldAttrs (item: acc: item // acc) { } ( + lib.attrValues ( + lib.mapAttrs ( + topName: topPkg: + lib.listToAttrs ( + lib.attrValues ( + lib.mapAttrs (name: lib.nameValuePair "${topName}-${name}") ( + builtins.removeAttrs topPkg.passthru.tests [ "override" "overrideDerivation" "extend" "__unfix__" ] + ) + ) + ) + ) self'.packages + ) + ); + }; + } + ); +} diff --git a/lib/ziggybox.zig b/lib/ziggybox.zig deleted file mode 100644 index f976415..0000000 --- a/lib/ziggybox.zig +++ /dev/null @@ -1,4 +0,0 @@ -pub const clap = @import("ziggybox/clap.zig"); -pub const common = @import("ziggybox/common.zig"); -pub const io = @import("ziggybox/io.zig"); -pub const os = @import("ziggybox/os.zig"); diff --git a/lib/ziggybox/clap.zig b/lib/ziggybox/clap.zig deleted file mode 100644 index ef7ac50..0000000 --- a/lib/ziggybox/clap.zig +++ /dev/null @@ -1,213 +0,0 @@ -// Code from https://github.com/Hejsil/zig-clap/blob/master/clap.zig licensed under MIT -const clap = @import("clap"); -const std = @import("std"); -const common = @import("common.zig"); - -fn findPositional(comptime Id: type, params: []const clap.Param(Id)) ?clap.Param(Id) { - for (params) |param| { - const longest = param.names.longest(); - if (longest.kind == .positional) - return param; - } - - return null; -} - -fn ParamType( - comptime Id: type, - comptime param: clap.Param(Id), - comptime value_parsers: anytype, -) type { - const parser = switch (param.takes_value) { - .none => clap.parsers.string, - .one, .many => @field(value_parsers, param.id.value()), - }; - return clap.parsers.Result(@TypeOf(parser)); -} - -fn FindPositionalType( - comptime Id: type, - comptime params: []const clap.Param(Id), - comptime value_parsers: anytype, -) type { - const pos = findPositional(Id, params) orelse return []const u8; - return ParamType(Id, pos, value_parsers); -} - -fn parseArg( - comptime Id: type, - comptime param: clap.Param(Id), - comptime value_parsers: anytype, - allocator: std.mem.Allocator, - arguments: anytype, - positionals: anytype, - arg: clap.streaming.Arg(Id), -) !void { - const parser = comptime switch (param.takes_value) { - .none => undefined, - .one, .many => @field(value_parsers, param.id.value()), - }; - - const longest = comptime param.names.longest(); - const name = longest.name[0..longest.name.len].*; - switch (longest.kind) { - .short, .long => switch (param.takes_value) { - .none => @field(arguments, &name) +|= 1, - .one => @field(arguments, &name) = try parser(arg.value.?), - .many => { - const value = try parser(arg.value.?); - try @field(arguments, &name).append(allocator, value); - }, - }, - .positional => try positionals.append(try parser(arg.value.?)), - } -} - -fn deinitArgs( - comptime Id: type, - comptime params: []const clap.Param(Id), - allocator: std.mem.Allocator, - arguments: anytype, -) void { - inline for (params) |param| { - const longest = comptime param.names.longest(); - if (longest.kind == .positional) - continue; - if (param.takes_value != .many) - continue; - - const field = @field(arguments, longest.name); - - // If the multi value field is a struct, we know it is a list and should be deinited. - // Otherwise, it is a slice that should be freed. - switch (@typeInfo(@TypeOf(field))) { - .Struct => @field(arguments, longest.name).deinit(allocator), - else => allocator.free(@field(arguments, longest.name)), - } - } -} - -const MultiArgKind = enum { slice, list }; - -fn Arguments( - comptime Id: type, - comptime params: []const clap.Param(Id), - comptime value_parsers: anytype, - comptime multi_arg_kind: MultiArgKind, -) type { - var fields: [params.len]std.builtin.Type.StructField = undefined; - - var i: usize = 0; - for (params) |param| { - const longest = param.names.longest(); - if (longest.kind == .positional) - continue; - - const T = ParamType(Id, param, value_parsers); - const default_value = switch (param.takes_value) { - .none => @as(u8, 0), - .one => @as(?T, null), - .many => switch (multi_arg_kind) { - .slice => @as([]const T, &[_]T{}), - .list => std.ArrayListUnmanaged(T){}, - }, - }; - - const name = longest.name[0..longest.name.len] ++ ""; - fields[i] = .{ - .name = name, - .type = @TypeOf(default_value), - .default_value = @ptrCast(&default_value), - .is_comptime = false, - .alignment = @alignOf(@TypeOf(default_value)), - }; - i += 1; - } - - return @Type(.{ .Struct = .{ - .layout = .auto, - .fields = fields[0..i], - .decls = &.{}, - .is_tuple = false, - } }); -} - -pub const ParseOptions = struct { - allocator: std.mem.Allocator = common.allocator, - diagnostic: ?*clap.Diagnostic = null, - limit: bool = false, -}; - -pub fn parse(comptime Id: type, comptime params: []const clap.Param(Id), comptime value_parsers: anytype, opt: ParseOptions) !clap.ResultEx(Id, params, value_parsers) { - var iter = try std.process.ArgIterator.initWithAllocator(opt.allocator); - defer iter.deinit(); - - _ = iter.next(); - return parseEx(Id, params, value_parsers, &iter, opt); -} - -pub fn parseEx( - comptime Id: type, - comptime params: []const clap.Param(Id), - comptime value_parsers: anytype, - iter: anytype, - opt: ParseOptions, -) !clap.ResultEx(Id, params, value_parsers) { - const Positional = FindPositionalType(Id, params, value_parsers); - - var positionals = std.ArrayList(Positional).init(opt.allocator); - var arguments = Arguments(Id, params, value_parsers, .list){}; - errdefer deinitArgs(Id, params, opt.allocator, &arguments); - - var stream = clap.streaming.Clap(Id, std.meta.Child(@TypeOf(iter))){ - .params = params, - .iter = iter, - .diagnostic = opt.diagnostic, - }; - - var i: usize = 1; - while (try stream.next()) |arg| { - var res: anyerror!void = {}; - var willBreak = false; - inline for (params, 0..) |*param, x| { - if (param == arg.param) { - res = parseArg( - Id, - param.*, - value_parsers, - opt.allocator, - &arguments, - &positionals, - arg, - ); - - if (opt.limit) { - if (i == x and x == (params.len - 1)) willBreak = true; - } - } - } - - try res; - i += 1; - - if (willBreak) break; - } - - var result_args = @typeInfo(clap.ResultEx(Id, params, value_parsers)).Struct.fields[0].type{}; - inline for (std.meta.fields(@TypeOf(arguments))) |field| { - if (@typeInfo(field.type) == .Struct and - @hasDecl(field.type, "toOwnedSlice")) - { - const slice = try @field(arguments, field.name).toOwnedSlice(opt.allocator); - @field(result_args, field.name) = slice; - } else { - @field(result_args, field.name) = @field(arguments, field.name); - } - } - - return clap.ResultEx(Id, params, value_parsers){ - .args = result_args, - .positionals = try positionals.toOwnedSlice(), - .allocator = opt.allocator, - }; -} diff --git a/lib/ziggybox/common.zig b/lib/ziggybox/common.zig deleted file mode 100644 index 29d1196..0000000 --- a/lib/ziggybox/common.zig +++ /dev/null @@ -1,5 +0,0 @@ -const builtin = @import("builtin"); -const std = @import("std"); - -pub const allocator = if (builtin.os.tag == .uefi) std.os.uefi.pool_allocator else if (builtin.link_libc) std.heap.c_allocator else std.heap.page_allocator; -pub const ArgIterator = std.process.ArgIterator; diff --git a/lib/ziggybox/io.zig b/lib/ziggybox/io.zig deleted file mode 100644 index b82cb63..0000000 --- a/lib/ziggybox/io.zig +++ /dev/null @@ -1,52 +0,0 @@ -const builtin = @import("builtin"); -const std = @import("std"); -const common = @import("common.zig"); -const alloc = common.allocator; - -pub usingnamespace switch (builtin.os.tag) { - .uefi => struct { - const StoWriter = std.io.Writer(*std.os.uefi.protocol.SimpleTextOutput, std.os.uefi.Status.EfiError || std.mem.Allocator.Error || error{InvalidUtf8}, stoWrite); - const StiReader = std.io.Reader(*std.os.uefi.protocol.SimpleTextInput, std.os.uefi.Status.EfiError || std.mem.Allocator.Error, stiRead); - - fn stoWrite(sto: *std.os.uefi.protocol.SimpleTextOutput, buf: []const u8) !usize { - const buf16 = try std.unicode.utf8ToUtf16LeWithNull(alloc, buf); - defer alloc.free(buf16); - try sto.outputString(buf16).err(); - if (buf.len > 0) { - if (buf[buf.len - 1] == '\n') try sto.outputString(&[1:0]u16{0x000D}).err(); - } - return buf.len; - } - - fn stiRead(sti: *std.os.uefi.protocol.SimpleTextInput, buf: []const u8) !usize { - _ = sti; - _ = buf; - return 0; - } - - pub inline fn getStdErr() StoWriter { - return StoWriter{ .context = std.os.uefi.system_table.std_err.? }; - } - - pub inline fn getStdOut() StoWriter { - return StoWriter{ .context = std.os.uefi.system_table.con_out.? }; - } - - pub inline fn getStdIn() StiReader { - return StiReader{ .context = std.os.uefi.system_table.con_in.? }; - } - }, - else => struct { - pub inline fn getStdErr() std.fs.File.Writer { - return std.io.getStdErr().writer(); - } - - pub inline fn getStdOut() std.fs.File.Writer { - return std.io.getStdOut().writer(); - } - - pub inline fn getStdIn() std.fs.File.Reader { - return std.io.getStdOut().reader(); - } - }, -}; diff --git a/lib/ziggybox/os.zig b/lib/ziggybox/os.zig deleted file mode 100644 index c737a3e..0000000 --- a/lib/ziggybox/os.zig +++ /dev/null @@ -1,18 +0,0 @@ -const builtin = @import("builtin"); -const std = @import("std"); - -pub const linux = @import("os/linux.zig"); -pub const windows = @import("os/windows.zig"); -pub const uefi = @import("os/uefi.zig"); - -pub inline fn time() !std.time.epoch.EpochSeconds { - if (builtin.os.tag == .uefi) { - var value: std.os.uefi.Time = undefined; - try std.os.uefi.system_table.runtime_services.getTime(&value, null).err(); - return .{ .secs = uefi.epochFromTime(value) }; - } - - return .{ - .secs = std.math.lossyCast(u64, std.time.timestamp()), - }; -} diff --git a/lib/ziggybox/os/uefi.zig b/lib/ziggybox/os/uefi.zig deleted file mode 100644 index d3091b9..0000000 --- a/lib/ziggybox/os/uefi.zig +++ /dev/null @@ -1,25 +0,0 @@ -const std = @import("std"); - -fn daysInYear(year: u16, maxMonth: u4) u32 { - const leapYear: std.time.epoch.YearLeapKind = if (std.time.epoch.isLeapYear(year)) .leap else .not_leap; - var days: u32 = 0; - var month: u4 = 0; - while (month < maxMonth) : (month += 1) { - days += std.time.epoch.getDaysInMonth(leapYear, @enumFromInt(month + 1)); - } - return days; -} - -pub fn epochFromTime(time: std.os.uefi.Time) u64 { - var year: u16 = 0; - var days: u32 = 0; - - while (year < (time.year - 1971)) : (year += 1) { - days += daysInYear(year + 1970, 12); - } - - days += daysInYear(time.year, @as(u4, @intCast(time.month)) - 1) + time.day; - const hours = time.hour + (days * 24); - const minutes = time.minute + (hours * 60); - return time.second + (minutes * 60); -} diff --git a/nix/bootstrap.nix b/nix/bootstrap.nix new file mode 100644 index 0000000..7d6fdb7 --- /dev/null +++ b/nix/bootstrap.nix @@ -0,0 +1,54 @@ +{ + lib, + zig, + src, + system, + callPackage, +}: +lib.makeExtensible ( + finalAttrs: + let + drv = derivation { + pname = "ziggybox"; + version = "0.1.0-git"; + name = "${finalAttrs.pname}-${finalAttrs.version}"; + + inherit src system; + + builder = lib.getExe zig; + realBuilder = lib.getExe zig; + + outputs = [ + "out" + "cache" + ]; + + args = [ + "build" + "--build-file" + "${finalAttrs.src}/build.zig" + "--global-cache-dir" + "${placeholder "cache"}" + "--cache-dir" + "${placeholder "cache"}" + "--prefix" + "${placeholder "out"}" + "--release=small" + "-Dcpu=baseline" + "-Dtarget=${system}-musl" + "-Dstrip" + ]; + }; + + passthru = { + tests = callPackage ./tests { + ziggybox = drv; + }; + }; + in + drv + // passthru + // { + inherit passthru; + } +) diff --git a/nix/package.nix b/nix/package.nix new file mode 100644 index 0000000..8daec61 --- /dev/null +++ b/nix/package.nix @@ -0,0 +1,28 @@ +{ + lib, + stdenv, + zig, + src, + callPackage, +}: +stdenv.mkDerivation (finalAttrs: { + pname = "ziggybox"; + version = "0.1.0-git"; + + inherit src; + + nativeBuildInputs = [ + zig.hook + ]; + + passthru = { + bootstrap = callPackage ./bootstrap.nix { + inherit lib zig src; + inherit (stdenv.hostPlatform) system; + }; + + tests = callPackage ./tests { + ziggybox = finalAttrs.finalPackage; + }; + }; +}) diff --git a/nix/tests/default.nix b/nix/tests/default.nix new file mode 100644 index 0000000..c25b074 --- /dev/null +++ b/nix/tests/default.nix @@ -0,0 +1,11 @@ +{ + lib, + stdenv, + callPackage, + ziggybox, + hello, +}: +lib.makeExtensible (final: { + stdenv = callPackage ./stdenv.nix { inherit ziggybox; }; + hello = hello.override { inherit (final) stdenv; }; +}) diff --git a/nix/tests/stdenv.nix b/nix/tests/stdenv.nix new file mode 100644 index 0000000..dd257ce --- /dev/null +++ b/nix/tests/stdenv.nix @@ -0,0 +1,26 @@ +{ + lib, + config, + path, + system, + buildPlatform, + hostPlatform, + targetPlatform, + fetchurl, + bash, + ziggybox, + gnumake, +}: +import "${path}/pkgs/stdenv/generic/default.nix" { + name = "stdenv-test"; + cc = null; + shell = lib.getExe bash; + initialPath = [ ziggybox gnumake ]; + fetchurlBoot = fetchurl; + inherit + config + buildPlatform + hostPlatform + targetPlatform + ; +} diff --git a/ziggybox.zig b/ziggybox.zig deleted file mode 100644 index 5d8840f..0000000 --- a/ziggybox.zig +++ /dev/null @@ -1,127 +0,0 @@ -const applets = @import("applets"); -const builtin = @import("builtin"); -const clap = @import("clap"); -const options = @import("options"); -const std = @import("std"); -const ziggybox = @import("ziggybox"); - -const MainResult = if (builtin.os.tag == .uefi) std.os.uefi.Status else anyerror!void; -const Applet = std.meta.DeclEnum(applets); - -pub const std_options = std.Options{ - .logFn = log, -}; - -pub fn log( - comptime message_level: std.log.Level, - comptime scope: @Type(.EnumLiteral), - comptime format: []const u8, - args: anytype, -) void { - const level_txt = comptime message_level.asText(); - const prefix2 = if (scope == .default) ": " else "(" ++ @tagName(scope) ++ "): "; - const stderr = ziggybox.io.getStdErr(); - nosuspend stderr.print(level_txt ++ prefix2 ++ format ++ "\n", args) catch return; -} - -fn runApplet(applet: Applet, args: *std.process.ArgIterator) !void { - inline for (comptime std.meta.declarations(applets), 0..) |decl, i| { - if (@as(usize, @intFromEnum(applet)) == i) { - return @field(applets, decl.name).run(args); - } - } - unreachable; -} - -fn dumpStackTrace(stack_trace: std.builtin.StackTrace) void { - nosuspend { - if (comptime builtin.target.isWasm()) { - if (builtin.os.tag == .wasi) { - const stderr = ziggybox.io.getStdErr(); - stderr.print("Unable to dump stack trace: not implemented for Wasm\n", .{}) catch return; - } - return; - } - const stderr = ziggybox.io.getStdErr(); - if (builtin.strip_debug_info) { - stderr.print("Unable to dump stack trace: debug info stripped\n", .{}) catch return; - return; - } - const debug_info = std.debug.getSelfDebugInfo() catch |err| { - stderr.print("Unable to dump stack trace: Unable to open debug info: {s}\n", .{@errorName(err)}) catch return; - return; - }; - std.debug.writeStackTrace(stack_trace, stderr, ziggybox.common.allocator, debug_info, .escape_codes) catch |err| { - stderr.print("Unable to dump stack trace: {s}\n", .{@errorName(err)}) catch return; - return; - }; - } -} - -fn realMain() !void { - var iter = try std.process.ArgIterator.initWithAllocator(ziggybox.common.allocator); - defer iter.deinit(); - - if (options.applet) |applet| { - _ = iter.next(); - return try runApplet(applet, &iter); - } - - const stderr = ziggybox.io.getStdErr(); - const stdout = ziggybox.io.getStdOut(); - - const params = comptime clap.parseParamsComptime( - \\-h, --help Display this help and exit. - \\-v, --version Prints the version of ziggybox. - \\ The applet to run. - \\ - ); - - if (iter.next()) |execpath| { - const binname = std.fs.path.stem(execpath); - - if (!std.mem.eql(u8, binname, "ziggybox")) { - if (std.meta.stringToEnum(Applet, binname)) |applet| { - return try runApplet(applet, &iter); - } else { - _ = stderr.print("Applet {s} is not enabled or does not exist.\n", .{binname}) catch {}; - return error.InvalidApplet; - } - } - } - - var diag = clap.Diagnostic{}; - var res = ziggybox.clap.parse(clap.Help, ¶ms, comptime .{ - .applet = clap.parsers.enumeration(Applet), - }, .{ - .allocator = ziggybox.common.allocator, - .limit = true, - .diagnostic = &diag, - }) catch |err| { - diag.report(stderr, err) catch {}; - return err; - }; - defer res.deinit(); - - if (res.args.version != 0) { - return try stdout.print("{}\n", .{options.version}); - } - - if (res.args.help != 0 or res.positionals.len != 1) { - return clap.help(stdout, clap.Help, ¶ms, .{}); - } - - try runApplet(res.positionals[0], &iter); -} - -pub fn main() MainResult { - realMain() catch |err| { - if (builtin.os.tag == .uefi) { - std.log.err("{s}", .{@errorName(err)}); - return .Aborted; - } - return err; - }; - - if (builtin.os.tag == .uefi) return .Success; -}