zig/lib/std/process/Preopens.zig
2026-01-30 22:06:33 -08:00

78 lines
2.4 KiB
Zig

const Preopens = @This();
const builtin = @import("builtin");
const native_os = builtin.os.tag;
const std = @import("../std.zig");
const Io = std.Io;
const Allocator = std.mem.Allocator;
map: Map,
pub const empty: Preopens = switch (native_os) {
.wasi => .{ .map = .empty },
else => .{ .map = {} },
};
pub const Map = switch (native_os) {
// Indexed by file descriptor number.
.wasi => std.StringArrayHashMapUnmanaged(void),
else => void,
};
pub const Resource = union(enum) {
file: Io.File,
dir: Io.Dir,
};
pub fn get(p: *const Preopens, name: []const u8) ?Resource {
switch (native_os) {
.wasi => {
const index = p.map.getIndex(name) orelse return null;
if (index <= 2) return .{ .file = .{
.handle = @intCast(index),
.flags = .{ .nonblocking = false },
} };
return .{ .dir = .{ .handle = @intCast(index) } };
},
else => {
if (std.mem.eql(u8, name, "stdin")) return .{ .file = .stdin() };
if (std.mem.eql(u8, name, "stdout")) return .{ .file = .stdout() };
if (std.mem.eql(u8, name, "stderr")) return .{ .file = .stderr() };
return null;
},
}
}
pub const InitError = Allocator.Error || error{Unexpected};
pub fn init(arena: Allocator) InitError!Preopens {
if (native_os != .wasi) return .{ .map = {} };
const wasi = std.os.wasi;
var map: Map = .empty;
try map.ensureUnusedCapacity(arena, 3);
map.putAssumeCapacityNoClobber("stdin", {}); // 0
map.putAssumeCapacityNoClobber("stdout", {}); // 1
map.putAssumeCapacityNoClobber("stderr", {}); // 2
while (true) {
const fd: wasi.fd_t = @intCast(map.entries.len);
var prestat: wasi.prestat_t = undefined;
switch (wasi.fd_prestat_get(fd, &prestat)) {
.SUCCESS => {},
.OPNOTSUPP, .BADF => return .{ .map = map },
else => return error.Unexpected,
}
try map.ensureUnusedCapacity(arena, 1);
// This length does not include a null byte. Let's keep it this way to
// gently encourage WASI implementations to behave properly.
const name_len = prestat.u.dir.pr_name_len;
const name = try arena.alloc(u8, name_len);
switch (wasi.fd_prestat_dir_name(fd, name.ptr, name.len)) {
.SUCCESS => {},
else => return error.Unexpected,
}
map.putAssumeCapacityNoClobber(name, {});
}
}