std/fs

Function Result
path_join(a, b) a/b
path_dir(path), path_base(path) src/lib and text.cz for src/lib/text.cz
path_ext(path), path_stem(path) .cz and text for src/text.cz
read_lines(path) the lines of a file as a Result[str[]]
split_lines(text) the lines of text with LF or CRLF line ends
write_lines(path, lines) replaces a file with lines
walk(dir) every file below a directory, sorted

The built-ins cover the rest: reading and writing whole files as text or bytes, appending to them, and writing them atomically (read_bytes, append_text, write_atomic, ...), and exists, is_dir, make_dir, list_dir, delete_file, rename_file, current_dir, change_dir, temp_dir, and temp_file. See program input and output.

Open files

open_file(path, mode) opens a file to read and write it in parts, and gives a File, which with closes:

import "std/fs"

with log = open_file("events.log", "a")? do
    file_write_text(log, "started\n")?
end

File input = open_file("data.bin", "r")?
file_seek(input, 16, SeekFrom.START)?
bytes header = file_read(input, 8)?
close_file(input)?
Mode Opens the file to
"r" read
"w" write, replacing it or creating it
"a" add to its end, creating it if needed
"r+" read and write, keeping it
"w+" read and write, replacing it
"x" write a new file, failing with kind "exists" when one is there
Function Result
file_read(f, n) up to n bytes, empty at the end of the file
file_read_line(f) SOME of the next line without its LF or CRLF, or NONE at the end
file_write(f, data), file_write_text(f, text) writes bytes or text and gives how many bytes
file_seek(f, offset, from) moves to offset from SeekFrom.START, CURRENT, or END, and gives the new position
file_position(f) the position
file_truncate(f, size) makes the file size bytes long
file_flush(f), file_sync(f) hands what was written to the system, or makes sure it reached the disk
close_file(f) closes the file; again does nothing
lock_file(f, exclusive) locks the whole file against other processes, waiting while another holds it
try_lock_file(f, exclusive) the same, giving OK(false) instead of waiting
unlock_file(f) releases the lock

Each returns a Result; the ones with nothing else to give return OK(true). Files are binary: nothing is changed in what is read or written. Reading and writing can follow each other on a file opened for both. An exclusive lock is for one writer; shared locks let several readers hold the file at once. A File is a Reader and a Writer, so it works with functions written for any source or destination of bytes. Using a closed file stops the program.

file_info(path) tells what a file is, as a FileInfo:

Field Value
kind FileKind.FILE, DIRECTORY, LINK, or OTHER
size the size in bytes; 0 for a directory
modified_ms, accessed_ms when the file was last changed and read, in milliseconds since 1970-01-01 UTC
created_ms SOME of when it was made, where the system keeps it: Windows, macOS, and FreeBSD
permissions the POSIX permission bits, such as 0o644; on Windows, 0o444 for a read-only file and 0o666 otherwise
owner, group the POSIX user and group numbers; -1 on Windows
hidden whether the name starts with ., or on Windows whether the file is marked hidden
link_target for a link, SOME of what it points to

A link is described itself rather than followed. These change files and links:

Function Result
set_permissions(path, bits) sets the permissions, as in set_permissions("run.sh", 0o755); on Windows only whether the owner may write counts
set_modified_time(path, ms) sets when the file was last changed
make_link(target, path) a symbolic link at path to target, which need not exist; Windows lets a program make one in developer mode or as an administrator
make_hard_link(existing, path) another name for the same file
read_link(path) what a link points to, as it was written; an ERROR of kind "invalid" for a file that is not a link
real_path(path) the absolute path, with every link followed and no . or ..

Each returns a Result whose error says what the system reported.

Directories

Function Result
remove_dir(path) removes an empty directory
remove_tree(path) removes a file, or a directory with everything in it; links are removed, not followed
copy_file(from, target) copies a file with its permissions and modified time, and gives how many bytes
copy_tree(from, target) copies a file or a whole directory, links as links, and gives how many files
move_path(from, target) moves a file or a directory, also to another file system, where it copies and then removes
list_dir_info(path) the entries of a directory, sorted, as DirEntry values with name, path, and info
home_dir(), config_dir(), data_dir(), cache_dir(), documents_dir(), desktop_dir() the user's directories

Each returns a Result. The user's directories follow each system: ~/.config, ~/.local/share, and ~/.cache on Linux and the BSDs (or the XDG_ variables that move them, and user-dirs.dirs for the documents and the desktop), ~/Library/Application Support and ~/Library/Caches on macOS, and the roaming and local AppData folders on Windows.

walk_dir(root) goes through everything below a directory one entry at a time, reading each directory only when it gets there, so it can stop early on a large tree. Each directory's entries come in order of name, and those of a subdirectory right after it:

Walker w = walk_dir("src")
w.max_depth = 3                 // 1 is the entries of src alone; -1, the default, has no limit
while true do
    match next_entry(w)? do
        when SOME(entry) then
            if entry.name == "build" then
                skip_dir(w)     // leaves out what is inside
            else if path_ext(entry.name) == ".cz" then
                print(entry.path, entry.info.size)
            end
        when NONE then
            break
    end
end

A link to a directory is given as a link and not gone into, unless follow_links is set; then each directory is gone into once, so a link that leads back up does not loop.

Large files and the trash

map_file(path) gives the bytes of a file through the system's memory map of it instead of reading it all: the system brings in the parts a program looks at, so a program can take slices of a file of gigabytes cheaply. The map stays until the program ends, and the file must not change while it is mapped.

trash(path) moves a file or a directory to the system's trash, from where the user can bring it back: the Recycle Bin on Windows, the Trash on macOS, and on Linux and the BSDs the trash of the freedesktop.org specification that file managers share. There, a file on another file system than the home directory cannot go to the trash, which is an ERROR of kind "cross_device". Both return a Result.