//! Unix-specific system calls and utilities. use std::intrinsics; /// File access modes. export record OpenFlags(i64); /// Open file for reading only. export constant O_RDONLY: OpenFlags = OpenFlags(0); /// Open file for writing only. export constant O_WRONLY: OpenFlags = OpenFlags(1); /// Open file for reading and writing. export constant O_RDWR: OpenFlags = OpenFlags(2); /// Create file if it doesn't exist. export constant O_CREAT: OpenFlags = OpenFlags(64); /// Truncate file to zero length. export constant O_TRUNC: OpenFlags = OpenFlags(512); /// Standard file descriptor for stdin. export constant STDIN: i64 = 0; /// Standard file descriptor for stdout. export constant STDOUT: i64 = 1; /// Standard file descriptor for stderr. export constant STDERR: i64 = 2; /// Special value representing current working directory for `openat()`. constant AT_FDCWD: i64 = -100; /// Open a file at the given path and return a file descriptor. /// Return a negative value on error. /// /// The caller must ensure `path.ptr` identifies at least `path.len + 1` /// readable bytes, that `path.ptr[path.len]` is NUL, and that those bytes /// remain readable until the system call returns. export unsafe fn open(path: *[u8], flags: OpenFlags) -> i64 { return intrinsics::ecall(56, AT_FDCWD, path.ptr as i64, *flags, 0); } /// Open a file at the given path with mode and return a file descriptor. /// /// The caller must ensure `path.ptr` identifies at least `path.len + 1` /// readable bytes, that `path.ptr[path.len]` is NUL, and that those bytes /// remain readable until the system call returns. export unsafe fn openOpts(path: *[u8], flags: OpenFlags, mode: i64) -> i64 { return intrinsics::ecall(56, AT_FDCWD, path.ptr as i64, *flags, mode); } /// Reads from a file descriptor into the provided buffer. /// Returns the number of bytes read, or a negative value on error. export fn read(fd: i64, buf: *mut [u8]) -> i64 { let mut result: i64 = 0; unsafe { set result = intrinsics::ecall(63, fd, buf.ptr as i64, buf.len as i64, 0); } return result; } /// Reads from a file descriptor until EOF or buffer is full. /// Returns the total number of bytes read, or a negative value on error. export fn readToEnd(fd: i64, buf: *mut [u8]) -> i64 { let mut total: u32 = 0; while total < buf.len { let chunk = &mut buf[total..]; let n = read(fd, chunk); if n < 0 { return n; } if n == 0 { break; } set total += n as u32; } return total as i64; } /// Writes to a file descriptor from the provided buffer. /// Returns the number of bytes written, or a negative value on error. export fn write(fd: i64, buf: *[u8]) -> i64 { let mut result: i64 = 0; unsafe { set result = intrinsics::ecall(64, fd, buf.ptr as i64, buf.len as i64, 0); } return result; } /// Writes the entire contents of a buffer to a file descriptor. /// Returns `false` when the descriptor cannot accept the full buffer. export fn writeAll(fd: i64, data: *[u8]) -> bool { let mut written: u32 = 0; while written < data.len { let n = write(fd, &data[written..]); if n <= 0 { return false; } set written += n as u32; } return true; } /// Closes a file descriptor. /// Returns `0` on success, or a negative value on error. export fn close(fd: i64) -> i64 { let mut result: i64 = 0; unsafe { set result = intrinsics::ecall(57, fd, 0, 0, 0); } return result; } /// Read the entire contents of a file at the given path into the buffer. /// Return a slice containing the data read, or `nil` on error. /// /// The caller must ensure `path.ptr` identifies at least `path.len + 1` /// readable bytes, that `path.ptr[path.len]` is NUL, and that those bytes /// remain readable until this function returns. export unsafe fn readFile(path: *[u8], buf: *mut [u8]) -> ?*[u8] { let fd = open(path, O_RDONLY); if fd < 0 { return nil; } let n = readToEnd(fd, buf); if close(fd) < 0 { return nil; } if n < 0 { return nil; } return &buf[..n as u32]; } /// Exit the current process with the given status code. export fn exit(status: i64) { unsafe { intrinsics::ecall(93, status, 0, 0, 0); } } /// Write the entire contents of a buffer to a file at the given path. /// Create the file if it does not exist and truncate it if it does. /// Return `true` on success. /// /// The caller must ensure `path.ptr` identifies at least `path.len + 1` /// readable bytes, that `path.ptr[path.len]` is NUL, and that those bytes /// remain readable until this function returns. export unsafe fn writeFile(path: *[u8], data: *[u8]) -> bool { return writeFileParts(path, &[data]); } /// Write each buffer to a file at the given path. /// Create the file if it does not exist and truncate it if it does. /// Return `true` only when every part and the final close succeed. /// /// The caller must ensure `path.ptr` identifies at least `path.len + 1` /// readable bytes, that `path.ptr[path.len]` is NUL, and that those bytes /// remain readable until this function returns. export unsafe fn writeFileParts(path: *[u8], parts: *[*[u8]]) -> bool { let flags = OpenFlags(*O_WRONLY | *O_CREAT | *O_TRUNC); let fd = openOpts(path, flags, 420); // 0644 in octal. if fd < 0 { return false; } for part in parts { if not writeAll(fd, part) { close(fd); return false; } } return close(fd) == 0; }