// readfile — copy named files to fd 1, with errors carried as values. // // The first Flash port built on the v0.2 error-union surface. `dup` opens a path // and returns a `!i32`: it `try`s the descriptor, then registers an `errdefer` // so a failed validation closes it before the error propagates — on the success // path ownership passes to the caller and the errdefer does not run. `copy` // `try`s that helper, `defer`s the close so it runs on every exit, and `try`s // each read so an I/O fault unwinds cleanly. `main` recovers with `catch |e|`, // turning any error into a diagnostic instead of a crash. Every failure edge is // spelled in the source — no hidden control flow. // // Pulls in the same flibc surface as the other coreutils; only the error model // is new (`!T`, `try`, `catch`, `defer`, `errdefer`). use flibc link "flibc_start" link "flibc_mem" const BUF_LEN usize = 512 fn dup(path cstr) !i32 { fd := try flibc.sys.open(path) errdefer _ = flibc.sys.close(fd) _ = try flibc.sys.fstat(fd) return fd } fn copy(path cstr) !usize { fd := try dup(path) defer _ = flibc.sys.close(fd) var buf [BUF_LEN]u8 = undefined var total usize = 0 while true { n := try flibc.sys.read(fd, &buf, buf.len) if n == 0 { break } _ = flibc.sys.write_fd(1, &buf, #intCast(n)) total += n } return total } fn report(e flibc.Error) usize { _ = e _ = flibc.sys.write_fd(2, "readfile: I/O error\n", 20) return 0 } export fn main(argc usize, argv argv) noreturn { var i usize = 1 while i < argc { path := argv[i] orelse break _ = copy(path) catch |e| report(e) i += 1 } flibc.exit() }