lib/std/sys/unix.rad 4.2 KiB raw
1
//! Unix-specific system calls and utilities.
2
use std::intrinsics;
3
4
/// File access modes.
5
export record OpenFlags(i64);
6
7
/// Open file for reading only.
8
export constant O_RDONLY: OpenFlags = OpenFlags(0);
9
10
/// Open file for writing only.
11
export constant O_WRONLY: OpenFlags = OpenFlags(1);
12
13
/// Open file for reading and writing.
14
export constant O_RDWR: OpenFlags = OpenFlags(2);
15
16
/// Create file if it doesn't exist.
17
export constant O_CREAT: OpenFlags = OpenFlags(64);
18
19
/// Truncate file to zero length.
20
export constant O_TRUNC: OpenFlags = OpenFlags(512);
21
22
/// Standard file descriptor for stdin.
23
export constant STDIN: i64 = 0;
24
25
/// Standard file descriptor for stdout.
26
export constant STDOUT: i64 = 1;
27
28
/// Standard file descriptor for stderr.
29
export constant STDERR: i64 = 2;
30
31
/// Special value representing current working directory for `openat()`.
32
constant AT_FDCWD: i64 = -100;
33
34
/// Opens a file at the given path and returns a file descriptor.
35
/// Returns a negative value on error.
36
export fn open(path: *[u8], flags: OpenFlags) -> i64 {
37
    return intrinsics::ecall(56, AT_FDCWD, path.ptr as i64, *flags, 0);
38
}
39
40
/// Opens a file at the given path with mode, returns a file descriptor.
41
export fn openOpts(path: *[u8], flags: OpenFlags, mode: i64) -> i64 {
42
    return intrinsics::ecall(56, AT_FDCWD, path.ptr as i64, *flags, mode);
43
}
44
45
/// Reads from a file descriptor into the provided buffer.
46
/// Returns the number of bytes read, or a negative value on error.
47
export fn read(fd: i64, buf: *mut [u8]) -> i64 {
48
    return intrinsics::ecall(63, fd, buf.ptr as i64, buf.len as i64, 0);
49
}
50
51
/// Reads from a file descriptor until EOF or buffer is full.
52
/// Returns the total number of bytes read, or a negative value on error.
53
export fn readToEnd(fd: i64, buf: *mut [u8]) -> i64 {
54
    let mut total: u32 = 0;
55
    while total < buf.len {
56
        let chunk = &mut buf[total..];
57
        let n = read(fd, chunk);
58
59
        if n < 0 {
60
            return n;
61
        }
62
        if n == 0 {
63
            break;
64
        }
65
        set total += n as u32;
66
    }
67
    return total as i64;
68
}
69
70
/// Writes to a file descriptor from the provided buffer.
71
/// Returns the number of bytes written, or a negative value on error.
72
export fn write(fd: i64, buf: *[u8]) -> i64 {
73
    return intrinsics::ecall(64, fd, buf.ptr as i64, buf.len as i64, 0);
74
}
75
76
/// Writes the entire contents of a buffer to a file descriptor.
77
/// Returns `false` when the descriptor cannot accept the full buffer.
78
export fn writeAll(fd: i64, data: *[u8]) -> bool {
79
    let mut written: u32 = 0;
80
    while written < data.len {
81
        let n = write(fd, &data[written..]);
82
        if n <= 0 {
83
            return false;
84
        }
85
        set written += n as u32;
86
    }
87
    return true;
88
}
89
90
/// Closes a file descriptor.
91
/// Returns `0` on success, or a negative value on error.
92
export fn close(fd: i64) -> i64 {
93
    return intrinsics::ecall(57, fd, 0, 0, 0);
94
}
95
96
/// Reads the entire contents of a file at the given path into the provided buffer.
97
/// Returns a slice containing the data read, or `nil` on error.
98
export fn readFile(path: *[u8], buf: *mut [u8]) -> ?*[u8] {
99
    let fd = open(path, O_RDONLY);
100
    if fd < 0 {
101
        return nil;
102
    }
103
    let n = readToEnd(fd, buf);
104
105
    if close(fd) < 0 {
106
        return nil;
107
    }
108
    if n < 0 {
109
        return nil;
110
    }
111
    return &buf[..n as u32];
112
}
113
114
/// Exit the current process with the given status code.
115
export fn exit(status: i64) {
116
    intrinsics::ecall(93, status, 0, 0, 0);
117
}
118
119
/// Writes the entire contents of a buffer to a file at the given path.
120
/// Creates the file if it doesn't exist, truncates if it does.
121
/// Returns `true` on success.
122
export fn writeFile(path: *[u8], data: *[u8]) -> bool {
123
    return writeFileParts(path, &[data]);
124
}
125
126
/// Writes each buffer to a file at the given path.
127
/// Creates the file if it doesn't exist, truncates if it does.
128
/// Returns `true` only when every part and the final close succeed.
129
export fn writeFileParts(path: *[u8], parts: *[*[u8]]) -> bool {
130
    let flags = OpenFlags(*O_WRONLY | *O_CREAT | *O_TRUNC);
131
    let fd = openOpts(path, flags, 420); // 0644 in octal.
132
    if fd < 0 {
133
        return false;
134
    }
135
136
    for part in parts {
137
        if not writeAll(fd, part) {
138
            close(fd);
139
            return false;
140
        }
141
    }
142
    return close(fd) == 0;
143
}