burkey.co /docs/libflint/dir
index dir
~/docs/libflint/dir.md
libflint

Dir

libflint·Utilities

Walk one directory with POSIX dirent. Names come back one at a time. lf_path_join joins two path components with a single / and does not allocate.

Concurrent use is unsupported. The walker skips . and .. only. It does not skip other dotfiles, and it does not recurse. Callers that also want to reject other dotfiles do that on the name.

Usage#

Zero the handle before lf_dir_open if a failed open should be safe to close. The caller owns LfDir. lf_dir_close releases the directory stream and the current name. It does not free the handle.

LfDir dir;
memset(&dir, 0, sizeof dir);
if (lf_dir_open(&dir, "assets") == 0) {
    const char *name;
    while (lf_dir_next(&dir, &name) == 0) {
        char path[256];
        if (lf_path_join(path, sizeof path, "assets", name) == 0) {
            /* path is "assets/<name>" */
        }
    }
    lf_dir_close(&dir);
}

name points at storage owned by dir. It stays valid until a later successful lf_dir_next or lf_dir_close. A return of 1 or -1 leaves the previous name in place. Do not free it. The string is the directory entry, not a joined path.

Structs#

LfDir#

The handle is complete so the caller can declare LfDir dir with no create function. dir and name are private. Do not read or write them.

typedef struct LfDir {
    void *dir;  /* DIR *, private */
    char *name; /* private, owned by the LfDir */
} LfDir;

dir holds the open DIR *. name holds the current entry copy. The header does not include dirent.h.

Functions#

lf_dir_open#

Opens path as a directory. Returns 0 on success and -1 on error: d is NULL, path is NULL, path is not a directory, or the OS reports an error.

On failure, *d is unchanged when d is non-NULL. Nothing is written before opendir succeeds, so a zeroed handle that fails to open stays zeroed and lf_dir_close on it is safe. Close of an uninitialized handle is not safe.

On success, dir is the open stream and name is NULL. lf_dir_open does not close a stream already stored in the handle. Close a live handle before opening another path; otherwise the previous stream and name leak.

int lf_dir_open(LfDir *d, const char *path);

lf_dir_close#

Closes the DIR and frees the name copy. Does not free the handle. NULL is safe. A zeroed handle is safe (dir and name are NULL). After close, both fields are NULL, so a second close is safe.

void lf_dir_close(LfDir *d);

lf_dir_next#

Returns 0 after writing an entry name, 1 when the directory is exhausted, and -1 on error. Skips . and ... Does not skip other dotfiles. Does not recurse. The name is the directory entry only.

On 0, *name receives the new entry. That pointer stays valid until a later successful lf_dir_next or lf_dir_close on this handle. On 1 or -1, *name is unchanged and the previous name stays valid. A NULL d, a NULL out pointer, or a handle that is not open returns -1 and does not write through name.

The owned copy is replaced only when the return value is 0. lf_dir_open does not free a name already stored in the handle. lf_dir_next sets errno to 0 before readdir and may change it again. The status is the return value; callers do not need to read errno.

int lf_dir_next(LfDir *d, const char **name);

lf_path_join#

Writes a and b joined by a single / into dest. Returns 0 on success. Returns -1 if dest is NULL, a or b is NULL, b is empty, or the result does not fit in dest_cap including the NUL. On failure, the bytes in dest are unchanged. The function does not allocate. It checks the size before writing.

If a already ends with /, no second slash is written: "a/" and "b" become "a/b". "a" and "b" become "a/b".

An empty a is not an error. The result is b with no extra slash. An empty b is an error.

a and b are NUL-terminated. dest must not overlap either string. The function does not normalize ., .., or slashes that are not at the join point.

int lf_path_join(char *dest, size_t dest_cap, const char *a, const char *b);