Initial import

This commit is contained in:
slhx agent
2026-07-04 20:44:28 +02:00
commit 624011da51
6 changed files with 374 additions and 0 deletions
+178
View File
@@ -0,0 +1,178 @@
const std = @import("std");
fn usage(file: std.Io.File, io: std.Io) !void {
var buf: [1024]u8 = undefined;
var w = file.writer(io, &buf);
try w.interface.writeAll(
\\usage: facts [PATH...]
\\
\\Print file metadata as TSV. With no PATH args, reads one path per stdin line.
\\Columns: path kind size mode mtime_ns
\\
\\stdout is records only; diagnostics go to stderr.
\\No filtering, sorting, deletion, exec, expression language, JSON, color, or hidden writes.
\\
\\examples:
\\ files src | facts | pick --header path size
\\ facts README.md build.zig
\\
);
try w.interface.flush();
}
fn die(io: std.Io, comptime fmt: []const u8, args: anytype) noreturn {
var buf: [512]u8 = undefined;
var w = std.Io.File.stderr().writer(io, &buf);
w.interface.print(fmt, args) catch {};
w.interface.flush() catch {};
std.process.exit(1);
}
fn isHelp(arg: []const u8) bool {
return std.mem.eql(u8, arg, "--help") or std.mem.eql(u8, arg, "-h");
}
fn kindName(kind: std.Io.File.Kind) []const u8 {
return switch (kind) {
.file => "file",
.directory => "dir",
.sym_link => "symlink",
.block_device => "block",
.character_device => "char",
.named_pipe => "fifo",
.unix_domain_socket => "socket",
.whiteout => "whiteout",
.door => "door",
.event_port => "eventport",
.unknown => "unknown",
};
}
fn modeValue(stat: std.Io.File.Stat) u64 {
if (@hasDecl(std.Io.File.Permissions, "toMode")) return @intCast(stat.permissions.toMode());
return 0;
}
fn printError(io: std.Io, path: []const u8, err: anyerror) !void {
var err_buf: [512]u8 = undefined;
var errw = std.Io.File.stderr().writer(io, &err_buf);
try errw.interface.print("facts: {s}: {s}\n", .{ path, @errorName(err) });
try errw.interface.flush();
}
fn unsupportedPathByte(path: []const u8) ?[]const u8 {
for (path) |byte| switch (byte) {
'\t' => return "tab",
'\n' => return "newline",
'\r' => return "carriage return",
else => {},
};
return null;
}
fn printUnsupportedPath(io: std.Io, byte_name: []const u8) !void {
var err_buf: [512]u8 = undefined;
var errw = std.Io.File.stderr().writer(io, &err_buf);
try errw.interface.print("facts: unsupported path: contains {s}\n", .{byte_name});
try errw.interface.flush();
}
fn printFact(io: std.Io, writer: *std.Io.Writer, path: []const u8) !bool {
if (unsupportedPathByte(path)) |byte_name| {
try printUnsupportedPath(io, byte_name);
return false;
}
const st = std.Io.Dir.cwd().statFile(io, path, .{}) catch |err| {
try printError(io, path, err);
return false;
};
try writer.print("{s}\t{s}\t{}\t{o}\t{}\n", .{ path, kindName(st.kind), st.size, modeValue(st), st.mtime.nanoseconds });
return true;
}
fn printStdinError(io: std.Io, comptime message: []const u8) !void {
var err_buf: [512]u8 = undefined;
var errw = std.Io.File.stderr().writer(io, &err_buf);
try errw.interface.writeAll("facts: stdin: " ++ message ++ "\n");
try errw.interface.flush();
}
fn printStdinLine(io: std.Io, writer: *std.Io.Writer, raw: []const u8) !bool {
if (raw.len == 0) return true;
const ok = try printFact(io, writer, raw);
try writer.flush();
return ok;
}
fn readStdinLines(io: std.Io, writer: *std.Io.Writer) !bool {
var had_error = false;
var read_buf: [8192]u8 = undefined;
var line_buf: [std.Io.Dir.max_path_bytes]u8 = undefined;
var line_len: usize = 0;
var dropping_long_line = false;
while (true) {
const n = try std.posix.read(std.posix.STDIN_FILENO, &read_buf);
if (n == 0) break;
for (read_buf[0..n]) |byte| {
if (byte == '\n') {
if (!dropping_long_line) {
if (!try printStdinLine(io, writer, line_buf[0..line_len])) had_error = true;
}
line_len = 0;
dropping_long_line = false;
continue;
}
if (dropping_long_line) continue;
if (line_len == line_buf.len) {
try printStdinError(io, "input path exceeds max path length");
had_error = true;
line_len = 0;
dropping_long_line = true;
continue;
}
line_buf[line_len] = byte;
line_len += 1;
}
}
if (!dropping_long_line and line_len != 0) {
if (!try printStdinLine(io, writer, line_buf[0..line_len])) had_error = true;
}
return had_error;
}
pub fn main(init: std.process.Init) !void {
const allocator = init.gpa;
var help_args = try std.process.Args.Iterator.initAllocator(init.minimal.args, allocator);
defer help_args.deinit();
_ = help_args.skip();
while (help_args.next()) |arg| {
if (isHelp(arg)) {
try usage(std.Io.File.stdout(), init.io);
return;
}
}
var args = try std.process.Args.Iterator.initAllocator(init.minimal.args, allocator);
defer args.deinit();
_ = args.skip();
var out_buf: [8192]u8 = undefined;
var out = std.Io.File.stdout().writer(init.io, &out_buf);
try out.interface.writeAll("path\tkind\tsize\tmode\tmtime_ns\n");
var saw_arg = false;
var had_error = false;
while (args.next()) |arg| {
saw_arg = true;
if (!try printFact(init.io, &out.interface, arg)) had_error = true;
}
if (!saw_arg) {
try out.interface.flush();
had_error = readStdinLines(init.io, &out.interface) catch |err| die(init.io, "facts: stdin: {s}\n", .{@errorName(err)});
}
try out.interface.flush();
if (had_error) std.process.exit(1);
}