1const Preopens = @This();
2
3const builtin = @import("builtin");
4const native_os = builtin.os.tag;
5
6const std = @import("../std.zig");
7const Io = std.Io;
8const Allocator = std.mem.Allocator;
9
10map: Map,
11
12pub const empty: Preopens = switch (native_os) {
13 .wasi => .{ .map = .empty },
14 else => .{ .map = {} },
15};
16
17pub const Map = switch (native_os) {
18 // Indexed by file descriptor number.
19 .wasi => std.array_hash_map.String(void),
20 else => void,
21};
22
23pub const Resource = union(enum) {
24 file: Io.File,
25 dir: Io.Dir,
26};
27
28pub fn get(p: *const Preopens, name: []const u8) ?Resource {
29 switch (native_os) {
30 .wasi => {
31 const index = p.map.getIndex(name) orelse return null;
32 if (index <= 2) return .{ .file = .{
33 .handle = @intCast(index),
34 .flags = .{ .nonblocking = false },
35 } };
36 return .{ .dir = .{ .handle = @intCast(index) } };
37 },
38 else => {
39 if (std.mem.eql(u8, name, "stdin")) return .{ .file = .stdin() };
40 if (std.mem.eql(u8, name, "stdout")) return .{ .file = .stdout() };
41 if (std.mem.eql(u8, name, "stderr")) return .{ .file = .stderr() };
42 return null;
43 },
44 }
45}
46
47pub const InitError = Allocator.Error || error{Unexpected};
48
49pub fn init(arena: Allocator) InitError!Preopens {
50 if (native_os != .wasi) return .{ .map = {} };
51 const wasi = std.os.wasi;
52 var map: Map = .empty;
53
54 try map.ensureUnusedCapacity(arena, 3);
55
56 map.putAssumeCapacityNoClobber("stdin", {}); // 0
57 map.putAssumeCapacityNoClobber("stdout", {}); // 1
58 map.putAssumeCapacityNoClobber("stderr", {}); // 2
59 while (true) {
60 const fd: wasi.fd_t = @intCast(map.entries.len);
61 var prestat: wasi.prestat_t = undefined;
62 switch (wasi.fd_prestat_get(fd, &prestat)) {
63 .SUCCESS => {},
64 .OPNOTSUPP, .BADF => return .{ .map = map },
65 else => return error.Unexpected,
66 }
67 try map.ensureUnusedCapacity(arena, 1);
68 // This length does not include a null byte. Let's keep it this way to
69 // gently encourage WASI implementations to behave properly.
70 const name_len = prestat.u.dir.pr_name_len;
71 const name = try arena.alloc(u8, name_len);
72 switch (wasi.fd_prestat_dir_name(fd, name.ptr, name.len)) {
73 .SUCCESS => {},
74 else => return error.Unexpected,
75 }
76 map.putAssumeCapacityNoClobber(name, {});
77 }
78}