// std.fs — file I/O. A `File` wraps a libc `FILE*`. // // const { File } = import("std/fs"); // // match (File.open("path.txt")) { // Some(f) { var data: Vec(u8) = f.readAll(); f.close(); } // None { /* open failed */ } // } // // Methods are `pub` so they dispatch from importing modules. Fallibility // is carried in the return type via `Option`, mirroring Zig's `!File` / // Rust's `io::Result`. The libc declarations are `pub extern` so // importers can also use them directly (single source of the file FFI). const { Option } = import("option"); const { Vec } = import("collections"); pub extern fn fopen(path: *const[] u8, mode: *const[] u8) *mut[] u8; pub extern fn fread(buf: *mut[] u8, size: u64, n: u64, f: *mut[] u8) u64; pub extern fn fwrite(buf: *const[] u8, size: u64, n: u64, f: *mut[] u8) u64; pub extern fn fclose(f: *mut[] u8) i32; pub extern fn fseek(f: *mut[] u8, off: i64, whence: i32) i32; pub extern fn ftell(f: *mut[] u8) i64; pub extern fn access(path: *const[] u8, mode: i32) i32; pub extern fn fgetc(f: *mut[] u8) i32; pub extern fn feof(f: *mut[] u8) i32; pub extern fn realpath(path: *const[] u8, resolved: *mut[] u8) *mut[] u8; pub const SEEK_SET: i32 = 0; pub const SEEK_CUR: i32 = 1; pub const SEEK_END: i32 = 2; // Copy a `[]u8` path into a caller-provided buffer with a trailing NUL. // A `[]u8` carries no NUL (Zig `toPosixPath` / Rust `CString` style). // `pub` because File.open/create/exists (pub fns) call it, and an // imported pub fn's body is codegen'd in the caller's module — its // callees must be visible there too. pub fn cpath(path: []u8, buf: *mut[] u8) { var i: u64 = 0; while (i < path.len && i < 4095) { buf[i] = path.ptr[i]; i = i + 1; } buf[i] = 0; } pub fn openMode(path: []u8, mode: *const[] u8) Option(File) { var buf: [4096]u8 = [0; 4096]; cpath(path, buf.asMutPtr()); var h: *mut[] u8 = fopen(buf.asPtr(), mode); // libc returns NULL on failure; Jam can't compare a pointer to null // directly, so reinterpret the handle as an integer address. if ((h as u64) == 0) { return Option(File).None(); } return Option(File).Some(File { handle: h }); } pub const File = struct { handle: *mut[] u8, // Open `path` for binary reading; None if it can't be opened. pub fn open(path: []u8) Option(File) { var mode: []u8 = "rb"; return openMode(path, mode.ptr); } // Create/truncate `path` for binary writing; None on failure. pub fn create(path: []u8) Option(File) { var mode: []u8 = "wb"; return openMode(path, mode.ptr); } // Read up to `dst.len` bytes into `dst`; returns the number read. pub fn read(self: File, dst: []u8) u64 { return fread(dst.ptr as *mut[] u8, 1, dst.len, self.handle); } // Write all of `data`; returns the number of bytes written. pub fn write(self: File, data: []u8) u64 { return fwrite(data.ptr, 1, data.len, self.handle); } // Reposition the read/write cursor (whence = SEEK_SET/CUR/END). pub fn seek(self: File, off: i64, whence: i32) i32 { return fseek(self.handle, off, whence); } // Total size of the file in bytes (restores the cursor to start). pub fn size(self: File) u64 { fseek(self.handle, 0, SEEK_END); var n: i64 = ftell(self.handle); fseek(self.handle, 0, SEEK_SET); return n as u64; } // Read the whole file into a freshly-allocated Vec(u8). pub fn readAll(self: File) Vec(u8) { var n: u64 = self.size(); var v: Vec(u8) = Vec(u8).withCapacity(n as u32); var got: u64 = fread(v.ptr, 1, n, self.handle); v.setLen(got as u32); return v; } // Read one byte; returns 0..255, or -1 at EOF (libc fgetc semantics), // so streaming parsers can loop on the result. pub fn readByte(self: File) i32 { return fgetc(self.handle); } // True once the end-of-file indicator is set (after a read past end). pub fn eof(self: File) bool { return feof(self.handle) != 0; } pub fn close(self: File) { fclose(self.handle); } }; // True if `path` exists and is accessible (libc access(path, F_OK)). pub fn exists(path: []u8) bool { var buf: [4096]u8 = [0; 4096]; cpath(path, buf.asMutPtr()); return access(buf.asPtr(), 0) == 0; } // Resolve `path` (symlinks, relative segments) into `out` — a caller // buffer of at least PATH_MAX bytes. Wraps libc realpath; mirrors // Rust's fs::canonicalize / Zig's realpath. pub fn canonicalize(path: []u8, out: *mut[] u8) { var buf: [4096]u8 = [0; 4096]; cpath(path, buf.asMutPtr()); realpath(buf.asPtr(), out); }