#File Helpers
#define FIO_FILES
#include "fio-stl.h"POSIX-style file helpers: open, read, write, remove, folder creation, size, type, path safety, filename parsing, and temporary files. Nothing fancy — just the usual paperwork. Implemented in ./004 files.h.
#Configuration Macros
#FIO_FD_FIND_BLOCK
#ifndef FIO_FD_FIND_BLOCK
#define FIO_FD_FIND_BLOCK 4096
#endifStack buffer size used by fio_fd_find_next for each read cycle. Override before inclusion if you want larger or smaller scans.
#FIO_FILENAME_PATH_CAPA
#ifndef FIO_FILENAME_PATH_CAPA
#define FIO_FILENAME_PATH_CAPA (PATH_MAX | 4094)
#endifStack buffer capacity used for path manipulation ("~/" expansion in fio_filename_open, recursive fio_filename_remove, nested fio_filename_make_path). These functions allocate nothing — paths longer than this fail with errno == ENAMETOOLONG. The default guarantees at least ~4KB.
#Types
#fio_filename_s
typedef struct {
fio_buf_info_s folder;
fio_buf_info_s basename;
fio_buf_info_s ext;
} fio_filename_s;Zero-copy result from fio_filename_parse / fio_filename_parse2. All members point into the original path string.
Members:
folder- directory path including trailing separator, or{NULL,0}if nonebasename- file name without extension, or{NULL,0}if emptyext- extension without leading., or{NULL,0}if none
#Macros
#FIO_FOLDER_SEPARATOR
#if FIO_OS_WIN
#define FIO_FOLDER_SEPARATOR '\\'
#else
#define FIO_FOLDER_SEPARATOR '/'
#endifNative path separator. On Windows the parser also accepts /.
#fio_file_dup
#if FIO_OS_WIN
#define fio_file_dup(fd) _dup(fd)
#else
#define fio_file_dup(fd) dup(fd)
#endifDuplicates a file descriptor.
#fio_filename_is_folder
#define fio_filename_is_folder(filename) \
(fio_filename_type((filename)) == S_IFDIR)Returns non-zero if filename is a directory, 0 otherwise (including errors).
#FIO_FD_FIND_EOF
#define FIO_FD_FIND_EOF ((size_t)-1)Sentinel returned by fio_fd_find_next when the token is not found before EOF.
#Opening and Creating Files
#fio_filename_open
SFUNC int fio_filename_open(const char *filename, int flags);Opens filename with POSIX open semantics. If filename starts with "~/" (or "~\\" on Windows), it is resolved relative to $HOME.
Parameters:
filename- path to openflags-O_RDONLY,O_RDWR | O_CREAT | O_TRUNC, etc.
Returns: file descriptor on success, -1 on error.
Note: uses a fixed size stack buffer (zero allocations); over-long "~/..." paths fail with errno == ENAMETOOLONG (see FIO_FILENAME_PATH_CAPA).
#fio_filename_tmp
SFUNC int fio_filename_tmp(void);Creates a temporary file and returns its descriptor. Tries TMPDIR, TMP, TEMP, P_tmpdir, then the current directory. On Linux it attempts O_TMPFILE first; otherwise it builds a name prefixed with facil_io_tmp_ and opens with O_CREAT | O_EXCL.
Returns: file descriptor on success, -1 on error.
#fio_filename_overwrite
FIO_IFUNC int fio_filename_overwrite(const char *filename,
const void *buf,
size_t len);Opens filename with O_RDWR | O_CREAT | O_TRUNC, writes len bytes from buf, and closes the file.
Returns: 0 on success, -1 on error. On error the file state is undefined.
#Removing Files and Folders
#fio_filename_remove
typedef struct {
const char *path;
uint8_t folder;
uint8_t recursive;
} fio_filename_remove_args_s;
SFUNC int fio_filename_remove(fio_filename_remove_args_s args);
#define fio_filename_remove(...) \
fio_filename_remove((fio_filename_remove_args_s){__VA_ARGS__})Removes the file / link or folder at path, abstracting OS differences (unlink / rmdir vs. DeleteFileA / RemoveDirectoryA).
Parameters:
path- NUL-terminated path of the file / folder to removefolder- set to allow removal of an empty folder (likermdir)recursive- set to remove a folder and all of its content (likerm -r); impliesfolder. Ifpathisn't a folder, the flag is ignored andpathis removed as a file / link
Returns: 0 on success, -1 on error. Recursive removal stops on the first error (some content may remain). Uses a fixed size stack buffer (zero allocations); over-long paths fail with errno == ENAMETOOLONG (see FIO_FILENAME_PATH_CAPA).
Note: links (symlinks / reparse points) are removed, never followed into.
Example:
fio_filename_remove(.path = "old.log");
fio_filename_remove(.path = "empty_dir", .folder = 1);
fio_filename_remove(.path = "build_tree", .recursive = 1);#Creating Folders
#fio_filename_make_path
typedef struct {
const char *path;
uint32_t mode;
} fio_filename_make_path_args_s;
SFUNC int fio_filename_make_path(fio_filename_make_path_args_s args);
#define fio_filename_make_path(...) \
fio_filename_make_path((fio_filename_make_path_args_s){__VA_ARGS__})Creates the folder at path, including any missing parent folders (similar to mkdir -p). Abstracts OS differences (mkdir vs. CreateDirectoryA).
Parameters:
path- NUL-terminated folder path to create; nested folders are created as needed, trailing separators are allowedmode- creation mode (POSIX only, ignored on Windows); zero defaults to0755
Returns: 0 on success, -1 on error. An existing folder is NOT an error. A non-folder component along the way IS an error (errno == ENOTDIR). Uses a fixed size stack buffer (zero allocations); over-long paths fail with errno == ENAMETOOLONG (see FIO_FILENAME_PATH_CAPA).
Note: root / drive / UNC \\server\share prefixes are never created, only the components after them.
Example:
if (fio_filename_make_path(.path = "./build/out/artifacts"))
perror("mkdir -p");#Reading and Writing
#fio_fd_write
FIO_IFUNC ssize_t fio_fd_write(int fd, const void *buf, size_t len);Writes len bytes to fd, fragmenting the syscall into write blocks of up to 1 << 17 bytes. Retries on EINTR.
Returns: number of bytes written, or -1 on error. If fd == -1, buf == NULL, or len == 0, returns -1.
Note: for non-blocking descriptors, check errno for EAGAIN / EWOULDBLOCK.
#fio_fd_read
FIO_IFUNC size_t fio_fd_read(int fd, void *buf, size_t len, off_t start_at);Reads up to len bytes from fd starting at start_at. On POSIX systems with pread, the file offset is preserved; otherwise it seeks. Reads are fragmented to 1 << 27 byte chunks. Retries on EINTR.
Parameters:
fd- file descriptorbuf- destination bufferlen- maximum bytes to readstart_at- offset; negative values seek from end
Returns: bytes read, or 0 on error / EOF. Sets errno = ENOENT if fd == -1, buf == NULL, or len == 0.
#File Information
#fio_filename_size
FIO_IFUNC size_t fio_filename_size(const char *filename);Returns file size in bytes, or 0 on error or empty file.
#fio_fd_size
FIO_IFUNC size_t fio_fd_size(int fd);Returns file size for an open descriptor, or 0 on error or empty file.
#fio_filename_type
FIO_IFUNC size_t fio_filename_type(const char *filename);Returns the file type bits (st_mode & S_IFMT), e.g. S_IFREG, S_IFDIR, or 0 on error.
#fio_fd_type
FIO_IFUNC size_t fio_fd_type(int fd);Same as fio_filename_type, but for an open descriptor.
#fio_filename_stat
FIO_IFUNC int fio_filename_stat(const char *filename, struct stat *stat_buf);Populates stat_buf via stat. Returns 0 on success, -1 on error (including filename == NULL or stat_buf == NULL).
#Path Safety
#fio_filename_is_unsafe
SFUNC int fio_filename_is_unsafe(const char *path);Returns 1 if path possibly folds backwards or contains double separators:
- leading
../ /../or trailing/..//
Uses the OS separator (\ on Windows, / elsewhere). Returns 0 for NULL paths.
#fio_filename_is_unsafe_url
SFUNC int fio_filename_is_unsafe_url(const char *path);Same check as fio_filename_is_unsafe, but always uses / as the separator. Use this for URL paths.
#Filename Parsing
#fio_filename_parse
SFUNC fio_filename_s fio_filename_parse(const char *filename);Splits a NUL-terminated path into folder, basename, and extension without copying.
Returns: fio_filename_s with pointers into filename.
#fio_filename_parse2
SFUNC fio_filename_s fio_filename_parse2(const char *filename, size_t len);Same as fio_filename_parse, but reads at most len bytes. Use when the input is not NUL-terminated.
#File Search
#fio_fd_find_next
SFUNC size_t fio_fd_find_next(int fd, char token, size_t start_at);Scans fd for the next occurrence of token starting at start_at. Reads in FIO_FD_FIND_BLOCK byte chunks.
Returns: byte offset of the match, or FIO_FD_FIND_EOF if not found.
#Example
#define FIO_FILES
#define FIO_LOG
#include "fio-stl.h"
int main(void) {
fio_filename_s parts = fio_filename_parse("/var/log/app.log");
printf("folder=%.*s base=%.*s ext=%.*s\n",
(int)parts.folder.len, parts.folder.buf,
(int)parts.basename.len, parts.basename.buf,
(int)parts.ext.len, parts.ext.buf);
const char *unsafe = "/var/www/../etc/passwd";
printf("unsafe? %s\n", fio_filename_is_unsafe(unsafe) ? "yes" : "no");
const char *msg = "hello, files";
if (fio_filename_overwrite("/tmp/fio_test.txt", msg, strlen(msg)) == 0) {
printf("wrote %zu bytes\n", strlen(msg));
}
size_t sz = fio_filename_size("/tmp/fio_test.txt");
printf("size = %zu\n", sz);
int tmp = fio_filename_tmp();
if (tmp != -1) {
fio_fd_write(tmp, "temp", 4);
close(tmp);
}
return 0;
}