ajhahn.de
← Flash
Flash 60 lines
// 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()
}