summaryrefslogtreecommitdiff
path: root/chapter31/src/main/smp/sys/io.smp
blob: 14993429e8bf1159cdc0ae14749465289379a24e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95

// Simple I/O Library

// Write byte array to stdout
val p          = { u8[~] str -> write(1,str); };
val p_noInline = { u8[~] str -> write(1,str); };
// Write to stderr
val err = { u8[~] str -> write(libc.STDERR,str); };


// Read until EOF, including after short reads.  Return an empty array for an
// empty stream, or null on error with errno preserved (partial data discarded).
val read = { i32 fd ->
    val chunk = new u8[8*1024];
    i64 ptr = chunk;
    val buf = new aryu8();
    while( true ) {
        i64 rez = libc.read(fd,ptr,chunk#);
        if( rez < 0 ) {
            if( libc.errno() == libc.EINTR ) continue;
            return null;
        }
        if( rez == 0 ) break;
        if( buf.len+rez > 4294967295 ) {
            libc.set_errno(libc.EOVERFLOW);
            return null;
        }
        u32 count = rez;
        buf._grow(count);
        ary.copy(buf.buf,buf.len,chunk,0,count);
        buf.len += count;
    }
    val out = new u8[buf.len];
    return ary.copy(out,0,buf.buf,0,buf.len);
};

// Write all bytes, returning the count or -1 with errno set. An error can
// follow a successfully written prefix; it does not undo that prefix.
val write = { i32 fd, u8[~] str ->
    writeOff(fd,str,0,str#);
};

// Write a slice, capping len at the remaining bytes.  An offset past the end
// is invalid, including for an empty write.  A valid empty slice returns zero.
// Invalid arguments return -1;
val writeOff = { i32 fd, u8[~] str, u32 off, u32 len ->
    if( off > str# ) return libc.set_errno(libc.EINVAL);
    if( len > str# - off ) len = str# - off;
    i64 ptr = str;
    ptr += off;
    i64 done = 0;
    while( done < len ) {
        i64 rez = libc.write(fd,ptr+done,len-done);
        if( rez < 0 ) {
            if( libc.errno() == libc.EINTR ) continue;
            return -1;
        }
        if( rez == 0 ) {
            libc.set_errno(libc.EIO);
            return -1;
        }
        done += rez;
    }
    return done;
};

// Return stdin as a u8[].
val stdin = { -> read(0); };

// One byte, without Enter or echo. Restores the previous terminal mode before
// returning. Files/pipes also work. Returns 0..255, or -1 with libc.errno() set
// to libc.EOF or an OS error. Ctrl-C is byte 3; escape/UTF-8 sequences are multiple bytes.
val getch = { -> libc.simple_getch(); };

// Create file for writing.  Return fd, or -1 and errno has the error code
val create = { u8[~] str ->
    i64 ptr = sys.aryu8.str0(str);
    i32 perms = 6*64 + 6*8 + 6; // 0666
    return libc.creat(ptr, perms);
};

// Open file for reading.  Return fd, or -1 and errno has the error code
val open = { u8[~] str ->
    i64 ptr = sys.aryu8.str0(str);
    return libc.open(ptr, sys.libc.O_RDONLY, 0);
};

// Close an open fd.  Return 0, or -1 and errno has the error code
val close = { i32 fd -> return libc.close(fd); };

// Remove a file.  Return 0, or -1 and errno has the error code
val remove = { u8[~] str ->
    i64 ptr = sys.aryu8.str0(str);
    return libc.unlink(ptr);
};