lib/std/sys/unix.rad 5.4 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
/// Open a file at the given path and return a file descriptor.
35
/// Return a negative value on error.
36
///
37
/// The caller must ensure `path.ptr` identifies at least `path.len + 1`
38
/// readable bytes, that `path.ptr[path.len]` is NUL, and that those bytes
39
/// remain readable until the system call returns.
40
export unsafe fn open(path: *[u8], flags: OpenFlags) -> i64 {
41
    return intrinsics::ecall(56, AT_FDCWD, path.ptr as i64, *flags, 0);
42
}
43
44
/// Open a file at the given path with mode and return a file descriptor.
45
///
46
/// The caller must ensure `path.ptr` identifies at least `path.len + 1`
47
/// readable bytes, that `path.ptr[path.len]` is NUL, and that those bytes
48
/// remain readable until the system call returns.
49
export unsafe fn openOpts(path: *[u8], flags: OpenFlags, mode: i64) -> i64 {
50
    return intrinsics::ecall(56, AT_FDCWD, path.ptr as i64, *flags, mode);
51
}
52
53
/// Reads from a file descriptor into the provided buffer.
54
/// Returns the number of bytes read, or a negative value on error.
55
export fn read(fd: i64, buf: *mut [u8]) -> i64 {
56
    let mut result: i64 = 0;
57
    unsafe { set result = intrinsics::ecall(63, fd, buf.ptr as i64, buf.len as i64, 0); }
58
    return result;
59
}
60
61
/// Reads from a file descriptor until EOF or buffer is full.
62
/// Returns the total number of bytes read, or a negative value on error.
63
export fn readToEnd(fd: i64, buf: *mut [u8]) -> i64 {
64
    let mut total: u32 = 0;
65
    while total < buf.len {
66
        let chunk = &mut buf[total..];
67
        let n = read(fd, chunk);
68
69
        if n < 0 {
70
            return n;
71
        }
72
        if n == 0 {
73
            break;
74
        }
75
        set total += n as u32;
76
    }
77
    return total as i64;
78
}
79
80
/// Writes to a file descriptor from the provided buffer.
81
/// Returns the number of bytes written, or a negative value on error.
82
export fn write(fd: i64, buf: *[u8]) -> i64 {
83
    let mut result: i64 = 0;
84
    unsafe { set result = intrinsics::ecall(64, fd, buf.ptr as i64, buf.len as i64, 0); }
85
    return result;
86
}
87
88
/// Writes the entire contents of a buffer to a file descriptor.
89
/// Returns `false` when the descriptor cannot accept the full buffer.
90
export fn writeAll(fd: i64, data: *[u8]) -> bool {
91
    let mut written: u32 = 0;
92
    while written < data.len {
93
        let n = write(fd, &data[written..]);
94
        if n <= 0 {
95
            return false;
96
        }
97
        set written += n as u32;
98
    }
99
    return true;
100
}
101
102
/// Closes a file descriptor.
103
/// Returns `0` on success, or a negative value on error.
104
export fn close(fd: i64) -> i64 {
105
    let mut result: i64 = 0;
106
    unsafe { set result = intrinsics::ecall(57, fd, 0, 0, 0); }
107
    return result;
108
}
109
110
/// Read the entire contents of a file at the given path into the buffer.
111
/// Return a slice containing the data read, or `nil` on error.
112
///
113
/// The caller must ensure `path.ptr` identifies at least `path.len + 1`
114
/// readable bytes, that `path.ptr[path.len]` is NUL, and that those bytes
115
/// remain readable until this function returns.
116
export unsafe fn readFile(path: *[u8], buf: *mut [u8]) -> ?*[u8] {
117
    let fd = open(path, O_RDONLY);
118
    if fd < 0 {
119
        return nil;
120
    }
121
    let n = readToEnd(fd, buf);
122
123
    if close(fd) < 0 {
124
        return nil;
125
    }
126
    if n < 0 {
127
        return nil;
128
    }
129
    return &buf[..n as u32];
130
}
131
132
/// Exit the current process with the given status code.
133
export fn exit(status: i64) {
134
    unsafe { intrinsics::ecall(93, status, 0, 0, 0); }
135
}
136
137
/// Write the entire contents of a buffer to a file at the given path.
138
/// Create the file if it does not exist and truncate it if it does.
139
/// Return `true` on success.
140
///
141
/// The caller must ensure `path.ptr` identifies at least `path.len + 1`
142
/// readable bytes, that `path.ptr[path.len]` is NUL, and that those bytes
143
/// remain readable until this function returns.
144
export unsafe fn writeFile(path: *[u8], data: *[u8]) -> bool {
145
    return writeFileParts(path, &[data]);
146
}
147
148
/// Write each buffer to a file at the given path.
149
/// Create the file if it does not exist and truncate it if it does.
150
/// Return `true` only when every part and the final close succeed.
151
///
152
/// The caller must ensure `path.ptr` identifies at least `path.len + 1`
153
/// readable bytes, that `path.ptr[path.len]` is NUL, and that those bytes
154
/// remain readable until this function returns.
155
export unsafe fn writeFileParts(path: *[u8], parts: *[*[u8]]) -> bool {
156
    let flags = OpenFlags(*O_WRONLY | *O_CREAT | *O_TRUNC);
157
    let fd = openOpts(path, flags, 420); // 0644 in octal.
158
    if fd < 0 {
159
        return false;
160
    }
161
162
    for part in parts {
163
        if not writeAll(fd, part) {
164
            close(fd);
165
            return false;
166
        }
167
    }
168
    return close(fd) == 0;
169
}