Landin library reference source

hosted/io

Hosted targets only.

Hosted file, stream and process operations through libc.

system supplies the shared I/O concepts. host exposes process arguments and standard streams. Open-write creates or truncates regular files. last_errno captures the terminal failing call as provider-specific detail; it is not a portable error type.

Items

Executable example

This complete program is maintained in the repository runtime tests. View source.

import core/io
import hosted/io as hosted

path_rules_code: () -> (code: i32) =
    code = 10
    mut scratch: [4]u8 = [91, 92, 93, 94]
    empty: [1]u8 = [65]
    mut empty_path: ptr u8 = addr scratch[0]
    empty_path = io.terminate_path(empty[0..<0], scratch[0..<4])
      else (problem)
        if problem <> io.empty_path or scratch[0] <> 91
          or scratch[1] <> 92 or scratch[2] <> 93 or scratch[3] <> 94
        then
            return
        end if
        empty_path
    end
    _ = empty_path
    code = 11

    with_nul: [3]u8 = [65, 0, 66]
    mut nul_path: ptr u8 = addr scratch[0]
    nul_path = io.terminate_path(with_nul[0..<3], scratch[0..<4])
      else (problem)
        if problem <> io.path_contains_nul or scratch[0] <> 91
          or scratch[1] <> 92 or scratch[2] <> 93 or scratch[3] <> 94
        then
            return
        end if
        nul_path
    end
    _ = nul_path
    code = 12

    too_wide: [2]u8 = [65, 66]
    mut short_scratch: [2]u8 = [81, 82]
    mut long_path: ptr u8 = addr short_scratch[0]
    long_path = io.terminate_path(too_wide[0..<2], short_scratch[0..<2])
      else (problem)
        if problem <> io.path_too_long or short_scratch[0] <> 81
          or short_scratch[1] <> 82
        then
            return
        end if
        long_path
    end
    _ = long_path
    code = 13

    mut exact_scratch: [3]u8 = [71, 72, 73]
    exact: ptr u8 = io.terminate_path(too_wide[0..<2],
                                      exact_scratch[0..<3])
      else (problem)
        _ = problem
        return
    end
    if exact <> addr exact_scratch[0] or exact_scratch[0] <> 65
      or exact_scratch[1] <> 66 or exact_scratch[2] <> 0
    then
        return
    end if
    code = 14
    mut right: [5]u8 = [65, 66, 67, 88, 88]
    right_path: ptr u8 = io.terminate_path(right[0..<3], right[1..<5])
      else (problem)
        _ = problem
        return
    end
    if right_path <> addr right[1] or right[1] <> 65 or right[2] <> 66
      or right[3] <> 67 or right[4] <> 0
    then
        return
    end if
    code = 15
    mut left: [5]u8 = [88, 65, 66, 67, 88]
    left_path: ptr u8 = io.terminate_path(left[1..<4], left[0..<4])
      else (problem)
        _ = problem
        return
    end
    if left_path <> addr left[0] or left[0] <> 65 or left[1] <> 66
      or left[2] <> 67 or left[3] <> 0
    then
        return
    end if
    code = 16
    mut same: [4]u8 = [65, 66, 67, 88]
    same_path: ptr u8 = io.terminate_path(same[0..<3], same[0..<4])
      else (problem)
        _ = problem
        return
    end
    if same_path <> addr same[0] or same[0] <> 65 or same[1] <> 66
      or same[2] <> 67 or same[3] <> 0
    then
        return
    end if
    code = 17
    mut invalid: [5]u8 = [65, 0, 67, 88, 88]
    mut invalid_path: ptr u8 = addr invalid[1]
    invalid_path = io.terminate_path(invalid[0..<3], invalid[1..<5])
      else (problem)
        if problem <> io.path_contains_nul or invalid[0] <> 65
          or invalid[1] <> 0 or invalid[2] <> 67 or invalid[3] <> 88
          or invalid[4] <> 88
        then
            return
        end if
        invalid_path
    end
    _ = invalid_path
    code = 42
end path_rules_code

text_path_rules_code: (inout state: hosted.system) -> (code: i32) =
    mut scratch: [4]u8 = [91, 92, 93, 94]
    mut opened: io.file = io.out(state)
    code = 32
    opened = io.open_read_text(state, "", scratch[0..<4]) else (problem)
        if problem <> io.empty_path or scratch[0] <> 91
          or scratch[1] <> 92 or scratch[2] <> 93 or scratch[3] <> 94
        then
            return
        end if
        code = 33
        io.out(state)
    end
    if code <> 33 then
        return
    end if

    opened = io.open_read_text(state, "A\u{0}B", scratch[0..<4])
      else (problem)
        if problem <> io.path_contains_nul or scratch[0] <> 91
          or scratch[1] <> 92 or scratch[2] <> 93 or scratch[3] <> 94
        then
            return
        end if
        code = 34
        io.out(state)
    end
    if code <> 34 then
        return
    end if

    opened = io.open_read_text(state, "ABCD", scratch[0..<4])
      else (problem)
        if problem <> io.path_too_long or scratch[0] <> 91
          or scratch[1] <> 92 or scratch[2] <> 93 or scratch[3] <> 94
        then
            return
        end if
        code = 42
        io.out(state)
    end
end text_path_rules_code

no_second_argument: (state: any io.world) -> (yes: bool) =
    second: usize = 1
    unexpected := state.argument(second) else (problem)
        yes = problem == io.no_argument
        return
    end
    _ = unexpected.length
    yes = false
end no_second_argument

missing_read_is_reported: (state: any io.world, path: ptr u8)
                          -> (yes: bool) =
    opened := state.open_read(path) else (problem)
        yes = problem == io.not_found
        return
    end
    state.close(opened) else (problem)
        _ = problem
    end
    yes = false
end missing_read_is_reported

missing_write_is_reported: (state: any io.world, path: ptr u8)
                           -> (yes: bool) =
    opened := state.open_write(path) else (problem)
        yes = problem == io.not_found
        return
    end
    state.close(opened) else (problem)
        _ = problem
    end
    yes = false
end missing_write_is_reported

denied_write_is_reported: (state: any io.world, path: ptr u8)
                          -> (yes: bool) =
    opened := state.open_write(path) else (problem)
        yes = problem == io.no_access
        return
    end
    state.close(opened) else (problem)
        _ = problem
    end
    yes = false
end denied_write_is_reported

public main: () -> (code: i32) =
    code = 1
    path_code: i32 = path_rules_code()
    if path_code <> 42 then
        code = path_code
        return
    end if

    mut host := hosted.host()
    code = 2
    if io.argument_count(host) <> 1 then
        return
    end if
    source_state: ptr hosted.system = addr host
    direct_index: usize = 0
    direct_source := io.argument_at(source_state, direct_index) else (problem)
        _ = problem
        return
    end
    code = 3
    if direct_source.length == 0 then
        return
    end if
    text_path_code: i32 = text_path_rules_code(host)
    if text_path_code <> 42 then
        code = text_path_code
        return
    end if
    mut world: any io.world = any(addr host)
    code = 4
    if world.argument_count() <> 1 then
        return
    end if

    code = 5
    first: usize = 0
    source := world.argument(first) else (problem)
        _ = problem
        return
    end
    code = 6
    if source.length == 0 then
        return
    end if
    return when not no_second_argument(world)
    code = 7

    opened := world.open_read(source.data) else (problem)
        _ = problem
        return
    end
    code = 8
    mut copied_opened: io.file = opened
    mut input: [13]u8 = zeroed
    count: usize = world.read(opened, input[0..<13]) else (problem)
        _ = problem
        return
    end
    code = 9
    if count <> 13 or input[0] <> 112 or input[6] <> 32
      or input[12] <> 10
    then
        return
    end if
    code = 14
    eof: usize = world.read(opened, input[0..<13]) else (problem)
        _ = problem
        return
    end
    if eof <> 0 then
        return
    end if
    code = 15
    world.close(opened) else (problem)
        _ = problem
        return
    end
    code = 16
    world.close(copied_opened) else (problem)
        if problem <> io.io_failed then
            return
        end if
        copied_opened = world.out()
    end
    code = 17

    mut generic_host := hosted.host()
    mut path_scratch: [30]u8 = zeroed
    code = 18
    output := io.open_write_text(generic_host,
                                 "/tmp/landin-r420-io-world.out",
                                 path_scratch[0..<30]) else (problem)
        _ = problem
        return
    end
    code = 19
    payload: [7]u8 = [76, 97, 110, 100, 105, 110, 10]
    world.write(output, payload[0..<0]) else (problem)
        _ = problem
        return
    end
    world.write(output, payload[0..<7]) else (problem)
        _ = problem
        return
    end
    code = 20
    world.close(output) else (problem)
        _ = problem
        return
    end
    code = 21

    mut second_scratch: [30]u8 = zeroed
    verify := io.open_read_text(generic_host,
                                "/tmp/landin-r420-io-world.out",
                                second_scratch[0..<30]) else (problem)
        _ = problem
        return
    end
    code = 22
    mut result: [8]u8 = zeroed
    result_length: usize = io.read(generic_host, verify, result[0..<8])
      else (problem)
        _ = problem
        return
    end
    code = 23
    io.close(generic_host, verify) else (problem)
        _ = problem
        return
    end
    code = 24
    if result_length <> 7 or result[0] <> 76 or result[1] <> 97
      or result[2] <> 110 or result[3] <> 100 or result[4] <> 105
      or result[5] <> 110 or result[6] <> 10
    then
        return
    end if
    code = 25

    missing: [45]u8 =
        [47, 100, 101, 102, 105, 110, 105, 116, 101, 108, 121, 45,
         110, 111, 116, 45, 97, 45, 108, 97, 110, 100, 105, 110, 45,
         100, 105, 114, 101, 99, 116, 111, 114, 121, 45, 114, 52, 50,
         48, 47, 102, 105, 108, 101, 0]
    missing_path: ptr u8 = addr missing[0]
    return when not missing_read_is_reported(world, missing_path)
    code = 26
    return when not missing_write_is_reported(world, missing_path)
    code = 27

    denied: [24]u8 =
        [47, 115, 121, 115, 47, 108, 97, 110, 100, 105, 110, 45,
         114, 52, 50, 48, 45, 100, 101, 110, 105, 101, 100, 0]
    denied_path: ptr u8 = addr denied[0]
    return when not denied_write_is_reported(world, denied_path)
    code = 28

    full_name: [10]u8 = [47, 100, 101, 118, 47, 102, 117, 108, 108, 0]
    full_path: ptr u8 = addr full_name[0]
    full := world.open_write(full_path) else (problem)
        _ = problem
        return
    end
    code = 29
    world.write(full, payload[0..<0]) else (problem)
        _ = problem
        return
    end
    mut saw_write_failure: bool = false
    world.write(full, payload[0..<7]) else (problem)
        if problem <> io.io_failed then
            return
        end if
        saw_write_failure = true
    end
    return when not saw_write_failure
    code = 30
    world.close(full) else (problem)
        _ = problem
        return
    end
    code = 31

    output_stream := world.out()
    error_stream := world.err()
    world.write(error_stream, payload[0..<0]) else (problem)
        _ = problem
        return
    end
    okay: [3]u8 = [79, 75, 10]
    world.write(output_stream, okay[0..<3]) else (problem)
        _ = problem
        return
    end
    code = 42
end main

system type

public system: type = system_value

hosted/io/io.ldn:38

Hosted implementation of all four I/O capabilities and io.world, backed by libc. File identifiers belong to this provider; borrowed arguments refer to process storage.

host function

public host: () -> (state: system)

hosted/io/io.ldn:276

Create the hosted I/O provider and expose process arguments and standard streams. Regular open-write operations create or truncate files; close regular files explicitly.

last_errno function

public last_errno: (state: system) -> (number: i32)

hosted/io/io.ldn:49

Return errno captured for the terminal failing provider call. Read it immediately after failure; it is provider state, not a portable error category.

Zero means that the most recent open/read/write/close did not report a failing libc call. A nonzero value is the exact errno captured for the terminal call before the provider made any other host call. Retriable EINTR attempts are internal and do not replace a previously reported operation with a failure.