paul_os.h

Includes:
<sys/param.h>
<Block.h>
<stdio.h>
<stdlib.h>
<string.h>
<stdbool.h>
<stddef.h>
<stdarg.h>
<wchar.h>
<errno.h>
<signal.h>
<poll.h>
<fcntl.h>
<windows.h>
<io.h>
<processthreadsapi.h>
<synchapi.h>
<shlobj.h>
<objbase.h>
<combaseapi.h>
<KnownFolders.h>
<objc/objc.h>
<objc/runtime.h>
<objc/message.h>
<objc/NSObjCRuntime.h>
<Cocoa/Cocoa.h>
<sys/stat.h>
<sys/types.h>
<unistd.h>
<dirent.h>

Introduction

Cross-platform file, path, and system utilities for C/C++.

Discussion

Implementation is included when PAUL_OS_IMPLEMENTATION or PAUL_IMPLEMENTATION is defined. On MacOS optional Cocoa support can be enabled by defining PAUL_OS_USE_COCOA and linking against `-framework Foundation.framework`.

Updated:
Saturday, July 19, 2025


Functions

directory_copy

Copy a directory and contents

directory_create

Create a directory

directory_delete

Delete a directory

directory_exists

Check if a directory exists

directory_file_count

Get the number of files inside a directory

directory_glob

Glob a directory for files matching a pattern

directory_item_count

Get the number of files/directories inside a directory

directory_iter

Iterate through a directory

directory_iter_end

End a directory iteration prematurely

directory_rename

Move a directory

directory_size

Get the size of a directory

environment_variable

Get an enviroment variable

file_copy

Copy a file

file_delete

Delete a file from file system

file_exists

Check if a file exists

file_read

Read a file from disk

file_rename

Move a file

file_size

Get file size

io_advance

Advance the file stream cursor by n bytes

io_close

Close file stream

io_eof

Check if file is at the end

io_flush

Flush file stream

io_open

Open file stream

io_print

Print a message to a file stream

io_println

Print a message to a file stream with a newline

io_read

Read n bytes from file stream

io_read_line

Read until newline or end of file

io_seek

Seek file stream position

io_tell

Get current position of file stream

io_truncate

Truncate a file stream to a specific length

io_valid

Check if a file stream is valid

io_write

Write n bytes to file stream

io_write_string

Write a string to a file stream

path_exists

Check if a path exists. This will return true for both files and directories.

path_get_application_dir

Get the application directory

path_get_desktop_dir

Get the desktop directory

path_get_directory_name

Get the directory name from a path

path_get_documents_dir

Get the documents directory

path_get_downloads_dir

Get the downloads directory

path_get_file_extension

Get the file extension from a path

path_get_file_name

Get the file name from a path

path_get_file_name_no_extension

Get the file name without extension from a path

path_get_home_dir

Get the home directory

path_get_music_dir

Get the music directory

path_get_parent_directory

Get the parent directory from a path

path_get_picture_dir

Get the picture directory

path_get_root_dir

Get the root directory

path_get_video_dir

Get the video directory

path_get_working_directory

Get the current working directory

path_glob

Glob a pattern and call a callback for each match

path_glob_block

Glob a pattern and call a block for each match

path_join

Join two paths together

path_join_va

Join multiple paths together

path_resolve

Resolve a path to an absolute path

path_set_working_directory

Set the current working directory

path_split

Split a path into its components

path_walk

Walk a directory and call a callback for each file/directory

path_walk_block

Walk a directory and call a block for each file/directory

path_without_file_name

Get the directory name from a path

print

Print a message to standard out

print_err

Print a message to standard error

println

Print a message to standard out with a newline

println_err

Print a message to standard error with a newline

shell

Execute a shell command

shell_fmt

directory_copy


Copy a directory and contents

bool directory_copy(
    const char *src_path,
    const char *dst_path,
    bool write_over,
    bool delete_src);  
Parameters
src_path

Path to directory

dst_path

Path copy directory to

write_over

If this is true, write over any existing files when copying

delete_src

If this is true, delete the original directory and files after copy succeeds

Return Value

Returns true/false on success


directory_create


Create a directory

bool directory_create(
    const char *path,
    bool recursive);  
Parameters
path

Path to directory

recursive

If this is true, will create any required parent directories

Return Value

Returns true/false on success

Discussion

WIP


directory_delete


Delete a directory

bool directory_delete(
    const char *path,
    bool recursive,
    bool and_files);  
Parameters
path

Path to directory

recursive

If this is true, it will delete and child directories too

and_files

If this is ture, it will delete and files inside the directory

Return Value

Returns true/false on success

Discussion

WIP


directory_exists


Check if a directory exists

bool directory_exists(
    const char *path);  
Parameters
path

Path to directory

Return Value

Returns true if file exists

Discussion

Checks if a path exists and is a directory (file will return false)


directory_file_count


Get the number of files inside a directory

int directory_file_count(
    const char *path,
    bool recursive);  
Parameters
path

Path to directory

recursive

If this is true, child directories will also be counted

Return Value

Returns number of files in directory, -1 on error

Discussion

This doesn't count directories


directory_glob


Glob a directory for files matching a pattern

const char** directory_glob(
    const char *pattern,
    int *count);  // !! 
Parameters
pattern

Glob pattern to match

count

Pointer to an integer to store the number of matches

Return Value

Returns an array of strings containing the matched file paths, or NULL on error

Discussion

The result must be freed by the caller. The count will be set to the number of matches. The array and its elements must be freed by the caller.


directory_item_count


Get the number of files/directories inside a directory

int directory_item_count(
    const char *path,
    bool recursive);  
Parameters
path

Path to directory

recursive

If this is true, child directories will also be counted

Return Value

Returns number of items in directory, -1 on error


directory_iter


Iterate through a directory

const char* directory_iter(
    dir_t *dir,
    bool *is_dir);  
Parameters
dir

Pointer to dir_t structure

is_dir

Pointer to bool to store if the current item is a directory

Return Value

Returns the name of the next item in the directory, or NULL if there are no more items

Discussion

If you need to exit an iteration early, call directory_iter_end(dir)


directory_iter_end


End a directory iteration prematurely

void directory_iter_end(
    dir_t *dir);  
Parameters
dir

Pointer to dir_t structure

Discussion

This will close the directory iterator and free any resources used. Normally this will be done at the end of the iteration.


directory_rename


Move a directory

bool directory_rename(
    const char *old_path,
    const char *new_path,
    bool write_over);  
Parameters
old_path

Path to directory

new_path

Path to directory to move to

write_over

If this is true, write over any existing files when copying

Return Value

Returns true/false on success

Discussion

WIP


directory_size


Get the size of a directory

int directory_size(
    const char *path);  
Parameters
path

Path to directory

Return Value

Returns size of directory contents, -1 on error


environment_variable


Get an enviroment variable

char* environment_variable(
    const char *name);  // ! 
Parameters
name

Variable name

Return Value

Returns enviroment variable string or NULL on failure

Discussion

Return value will need to be freed on success


file_copy


Copy a file

bool file_copy(
    const char *src_path,
    const char *dst_path,
    bool write_over);  
Parameters
src_path

Path to original file

dst_path

Path to copy

write_over

Write over existing files when moving

Return Value

Returns true/false on success

Discussion

WIP


file_delete


Delete a file from file system

bool file_delete(
    const char *path);  
Parameters
path

Path to file

Return Value

Returns true/false on success


file_exists


Check if a file exists

bool file_exists(
    const char *path);  
Parameters
path

Path to file

Return Value

Returns true if file exists

Discussion

Checks if a path exists and is a file (directory will return false)


file_read


Read a file from disk

const char* file_read(
    const char *path,
    size_t *size);  // ! 
Parameters
path

Path to file

size

Pointer to recieve file size

Return Value

Returns the contents of a file

Discussion

Return value will need to be freed on success


file_rename


Move a file

bool file_rename(
    const char *old_path,
    const char *new_path,
    bool write_over);  
Parameters
old_path

Path to file to rename

new_path

New path for file

write_over

Write over existing files when moving

Return Value

Returns true/false on success

Discussion

WIP


file_size


Get file size

int file_size(
    const char *path);  
Parameters
path

Path to file

Return Value

Returns size of a file, -1 on error


io_advance


Advance the file stream cursor by n bytes

bool io_advance(
    file_t file,
    long offset);  
Parameters
file

File stream to modify

offset

Number of bytes to seek

Return Value

Returns true/false on success


io_close


Close file stream

bool io_close(
    file_t *file);  
Parameters
file

Pointer to file steam to close

Return Value

Returns true/false on success


io_eof


Check if file is at the end

bool io_eof(
    file_t file);  
Parameters
file

File stream to check

Return Value

Returns true is file is at the end


io_flush


Flush file stream

bool io_flush(
    file_t file);  
Parameters
file

File stream to flush

Return Value

Returns true/false on success


io_open


Open file stream

bool io_open(
    file_t *dst,
    const char *path,
    enum file_mode_t mode);  
Parameters
dst

File steam destination

path

Path to file

mode

File steam access mode(s)


io_print


Print a message to a file stream

#define io_print(IO, MSG) \ 
    do \ 
    { \ 
    if (io_valid(IO)) \ 
    io_write_string(IO, MSG); \ 
    } while (0) 
Parameters
IO

File stream to write to

MSG

Message to print


io_println


Print a message to a file stream with a newline

#define io_println(IO, MSG) \ 
    do \ 
    { \ 
    io_print(IO, MSG); \ 
    io_print(IO, "\n") \ 
    } while(0) 
Parameters
IO

File stream to write to

MSG

Message to print


io_read


Read n bytes from file stream

size_t io_read(
    file_t file,
    void *buffer,
    size_t size);  
Parameters
file

File stream to read from

buffer

Buffer to read to

size

Size of the destination buffer

Return Value

Returns true/false on success

Discussion

Please ensure that size >= destination buffer size


io_read_line


Read until newline or end of file

bool io_read_line(
    file_t file,
    char *buffer,
    size_t size);  
Parameters
file

File stream to read

buffer

Destination buffer

size

Size of destination buffer

Return Value

Returns true/false on success


io_seek


Seek file stream position

bool io_seek(
    file_t file,
    long offset,
    enum file_seek_t whence);  
Parameters
file

File stream to modify

offset

The number of bytes to seek

whence

The offset of the seek

Return Value

Returns true/false on success


io_tell


Get current position of file stream

size_t io_tell(
    file_t file);  
Parameters
file

File stream to check

Return Value

Returns the current file stream position, -1 on error


io_truncate


Truncate a file stream to a specific length

bool io_truncate(
    file_t file,
    long size);  
Parameters
file

File stream to modify

size

Offset bytes

Return Value

Returns true/false on success


io_valid


Check if a file stream is valid

bool io_valid(
    file_t file);  
Parameters
file

File stream to check

Return Value

Check if a file stream is valid


io_write


Write n bytes to file stream

size_t io_write(
    file_t file,
    const void *buffer,
    size_t size);  
Parameters
file

File stream to write to

buffer

Source buffer to write

size

Number of bytes to write

Return Value

Returns number of bytes read, -1 on error

Discussion

Please ensure that size is >= the source buffer size


io_write_string


Write a string to a file stream

bool io_write_string(
    file_t file,
    const char *str);  
Parameters
file

File stream to write to

str

String to write

Return Value

Returns true/false on success


path_exists


Check if a path exists. This will return true for both files and directories.

bool path_exists(
    const char *path);  
Parameters
path

The path to check

Return Value

Returns true/false on success


path_get_application_dir


Get the application directory

const char* path_get_application_dir(
    void);  
Return Value

Returns the application directory


path_get_desktop_dir


Get the desktop directory

const char* path_get_desktop_dir(
    void);  
Return Value

Returns the desktop directory


path_get_directory_name


Get the directory name from a path

const char* path_get_directory_name(
    const char *path);  // ! 
Parameters
path

The path to get the directory name from

Return Value

Returns the directory name or NULL if there is no directory name

Discussion

The result must be freed by the caller.


path_get_documents_dir


Get the documents directory

const char* path_get_documents_dir(
    void);  
Return Value

Returns the documents directory


path_get_downloads_dir


Get the downloads directory

const char* path_get_downloads_dir(
    void);  
Return Value

Returns the downloads directory


path_get_file_extension


Get the file extension from a path

const char* path_get_file_extension(
    const char *path);  
Parameters
path

The path to get the file extension from

Return Value

Returns the file extension or NULL if there is no extension


path_get_file_name


Get the file name from a path

const char* path_get_file_name(
    const char *path);  
Parameters
path

The path to get the file name from

Return Value

Returns the file name or NULL if there is no file name


path_get_file_name_no_extension


Get the file name without extension from a path

const char* path_get_file_name_no_extension(
    const char *path);  // ! 
Parameters
path

The path to get the file name without extension from

Return Value

Returns the file name without extension or NULL if there is no file name

Discussion

The result must be freed by the caller.


path_get_home_dir


Get the home directory

const char* path_get_home_dir(
    void);  
Return Value

Returns the home directory


path_get_music_dir


Get the music directory

const char* path_get_music_dir(
    void);  
Return Value

Returns the music directory


path_get_parent_directory


Get the parent directory from a path

const char* path_get_parent_directory(
    const char *path);  // ! 
Parameters
path

The path to get the parent directory from

Return Value

Returns the parent directory or NULL if there is no parent directory

Discussion

The result must be freed by the caller.


path_get_picture_dir


Get the picture directory

const char* path_get_picture_dir(
    void);  
Return Value

Returns the picture directory


path_get_root_dir


Get the root directory

const char* path_get_root_dir(
    void);  
Return Value

Returns the root directory


path_get_video_dir


Get the video directory

const char* path_get_video_dir(
    void);  
Return Value

Returns the video directory


path_get_working_directory


Get the current working directory

const char* path_get_working_directory(
    void);  
Return Value

Returns the current working directory


path_glob


Glob a pattern and call a callback for each match

bool path_glob(
    const char *pattern,
    glob_callback callback,
    void *userdata);  
Parameters
pattern

The glob pattern to match

callback

Callback function to call for each match

userdata

User data pointer to pass to the callback

Return Value

Returns true/false on success


path_glob_block


Glob a pattern and call a block for each match

bool path_glob_block(
    const char *pattern,
    glob_block callback,
    void *userdata);  
Parameters
pattern

The glob pattern to match

callback

Block to call for each match

userdata

User data pointer to pass to the block

Return Value

Returns true/false on success

Discussion

Blocks are a clang/gcc extension. -fblocks must be enabled to use this.


path_join


Join two paths together

const char* path_join(
    const char *a,
    const char *b);  // ! 
Parameters
a

The first path

b

The second path

Return Value

Returns the joined path or NULL on error

Discussion

The result must be freed by the caller.


path_join_va


Join multiple paths together

const char* path_join_va(
    int n,
    ...);  // ! 
Parameters
n

The number of paths to join

...

The paths to join

Return Value

Returns the joined path or NULL on error

Discussion

The result must be freed by the caller.


path_resolve


Resolve a path to an absolute path

const char* path_resolve(
    const char *path);  // ! 
Parameters
path

The path to resolve

Return Value

Returns the resolved path or NULL on error

Discussion

This will attempt to resolve ~ to the user's home directory and ".." to the parent directory. The result must be freed by the caller. WIP.


path_set_working_directory


Set the current working directory

bool path_set_working_directory(
    const char *path);  
Parameters
path

The path to set as the working directory

Return Value

Returns true/false on success


path_split


Split a path into its components

const char** path_split(
    const char *path,
    size_t *count);  // !! 
Parameters
path

The path to split

count

Pointer to an integer to store the number of parts

Return Value

Returns an array of strings containing the parts of the path, or NULL on error

Discussion

The result must be freed by the caller. The count will be set to the number of parts. The array and its elements must be freed by the caller.


path_walk


Walk a directory and call a callback for each file/directory

bool path_walk(
    const char *path,
    bool recursive,
    walk_callback callback,
    void *userdata);  
Parameters
path

The path to walk

recursive

If this is true, it will walk recursively

callback

Callback function to call for each file/directory

userdata

User data pointer to pass to the callback

Return Value

Returns true/false on success


path_walk_block


Walk a directory and call a block for each file/directory

bool path_walk_block(
    const char *path,
    bool recursive,
    walk_block callback,
    void *userdata);  
Parameters
path

The path to walk

recursive

If this is true, it will walk recursively

callback

Block to call for each file/directory

userdata

User data pointer to pass to the block

Return Value

Returns true/false on success

Discussion

Blocks are a clang/gcc extension. -fblocks must be enabled to use this.


path_without_file_name


Get the directory name from a path

const char* path_without_file_name(
    const char *path);  // ! 
Parameters
path

The path to get the directory name from

Return Value

Returns the directory name or NULL if there is no directory name

Discussion

The result must be freed by the caller.


print


Print a message to standard out

#define print(MSG) io_print(io_out, MSG) 
Parameters
MSG

Message to print


print_err


Print a message to standard error

Parameters
MSG

Message to print


println


Print a message to standard out with a newline

Parameters
MSG

Message to print


println_err


Print a message to standard error with a newline

Parameters
MSG

Message to print


shell


Execute a shell command

int shell(
    const char *command,
    shell_io *io);  
Parameters
command

Command to execute

io

Capture structure, NULL to ignore (no capture output)

Return Value

Returns the exit code of the command, negative on internal error

Discussion

Execute a shell command (bourne shell syntax, |, >, <, &&, ;)


shell_fmt


int shell_fmt(
    shell_io *io,
    const char *command,
    ...);  
Parameters
io

Capture structure, NULL to ignore (no capture output)

command

Command to execute (formatted)

...

printf style format arguments for command

Return Value

Returns the exit code of the command, negative on internal error

Discussion

shell with printf formatting


Constants

io_err
io_in
io_invalid
io_out

io_err


extern const file_t io_err;  
Discussion

Standard error


io_in


extern const file_t io_in;  
Discussion

Standard in


io_invalid


extern const file_t io_invalid;  
Discussion

Invalid file handler (-1)


io_out


extern const file_t io_out;  
Discussion

Standard out


Typedefs

dir_t
file_t
glob_block
glob_callback
shell_error_t
shell_io

I/O capture and streaming control structure

shell_stream_cb_t

Callback type for streaming output

walk_block
walk_callback

dir_t


typedef struct dir { 
    const char *path; 
    #ifdef PLATFORM_WINDOWS 
    WIN32_FIND_DATA findData; 
    HANDLE hFind; 
    #elif defined(PLATFORM_POSIX) 
    DIR *dir; 
    #endif  
} dir_t;  
Fields
path

Path to directory

Discussion

dir_t is a wrapper around OS specific directory iterators


file_t


typedef struct file { 
    file_handle_t fd; 
} file_t;  
Fields
fd

Underlying file handler

Discussion

file_t is a wrapper around OS specific file handlers


glob_block


typedef int(^glob_block)(
    const char *pattern,
    const char *filename,
    void *userdata);  
Parameters
pattern

The glob pattern

filename

The filename that matches the pattern

userdata

User data pointer

Return Value

Returns 0 to continue iteration, non-zero to stop

Discussion

This is a block version of glob_callback. Blocks are a clang/gcc extension. -fblocks must be enabled to use this.


glob_callback


typedef int( *glob_callback)(
    const char *pattern,
    const char *filename,
    void *userdata);  
Parameters
pattern

The glob pattern

filename

The filename that matches the pattern

userdata

User data pointer

Return Value

Returns 0 to continue iteration, non-zero to stop


shell_error_t


typedef enum shell_error { 
    SHELL_OK = 0, 
    SHELL_ERR_GENERIC = -1, 
    SHELL_ERR_TOKENIZE = -2, 
    SHELL_ERR_EVAL = -3, 
    SHELL_ERR_PIPE = -4, 
    SHELL_ERR_FORK = -5, 
    SHELL_ERR_READ = -6 
} shell_error_t;  
Constants
SHELL_OK

No error

SHELL_ERR_GENERIC

Generic error

SHELL_ERR_TOKENIZE

Error tokenizing command

SHELL_ERR_EVAL

Error evaluating command

SHELL_ERR_PIPE

Error creating pipe

SHELL_ERR_FORK

Error forking process

SHELL_ERR_READ

Error reading from pipe

Discussion

Error codes for shell() function


shell_io


I/O capture and streaming control structure

typedef struct shell_io { 
    /* I/O capture and streaming control structure
     *
     * - Pass a non-NULL `shell_io *` to `shell()` to enable capture/streaming.
     * - If `out_cb` and/or `err_cb` are set, `shell()` invokes the callback
     *   as chunks of data arrive on the child's STDOUT/STDERR. When callbacks
     *   are provided, `shell()` does NOT fill the `out`/`err` buffers (they
     *   will remain NULL). Callbacks are useful for streaming large outputs
     *   without buffering everything in memory.
     * - If callbacks are NULL, `shell()` accumulates the full contents of
     *   STDOUT and STDERR into freshly allocated, NUL-terminated buffers
     *   stored in `out` and `err` respectively. The sizes are in `out_len`
     *   and `err_len`. The caller is responsible for calling `free()` on
     *   `out` and `err` when done.
     * - To provide input to the child, set `in` to a buffer of length
     *   `in_len`. `shell()` will write the contents of `in` to the child's
     *   STDIN and then close it. Ownership of `in` stays with the caller.
     * - Return values: non-negative return values are the child's exit code.
     *   Negative return values are library-level errors (see SHELL_ERR_*).
        */
    /* Captured output buffers (allocated by shell() when callbacks are NULL). */
    char *out; /* captured stdout (NUL-terminated) */
    size_t out_len; 
    char *err; /* captured stderr (NUL-terminated) */
    size_t err_len;  
    /* Input to be written to child's STDIN before closing it. Not modified by shell(). */
    const char *in; 
    size_t in_len;  
    /* Streaming callbacks. If non-NULL, shell() will call these with each
     * incoming chunk. Callbacks receive `userdata` as the last argument.
     * When callbacks are used, `out`/`err` buffers are not populated.
        */
    shell_stream_cb_t out_cb; 
    shell_stream_cb_t err_cb; 
    void *userdata; 
} shell_io;  
Fields
out

Captured stdout buffer (NUL-terminated)

out_len

Length of captured stdout buffer

err

Captured stderr buffer (NUL-terminated)

err_len

Length of captured stderr buffer

in

Input buffer to write to child's stdin

in_len

Length of input buffer

out_cb

Callback for streaming stdout chunks

err_cb

Callback for streaming stderr chunks

userdata

User data pointer passed to callbacks

Discussion

shell_io is used to control input/output capture and streaming for the shell function. It allows capturing stdout and stderr into buffers or streaming them via callbacks. Input can be provided to the child process via a buffer. Callbacks receive chunks of data as they arrive, useful for large outputs. When callbacks are used, output buffers are not populated. The caller is responsible for freeing captured output buffers when done.


shell_stream_cb_t


Callback type for streaming output

typedef void ( *shell_stream_cb_t)(
    const char *data,
    size_t len,
    void *userdata);  

walk_block


typedef int(^walk_block)(
    const char *path,
    const char *filename,
    void *userdata);  
Parameters
path

The current path being walked

filename

The filename in the current path

userdata

User data pointer

Return Value

Returns 0 to continue walking, non-zero to stop

Discussion

This is a block version of walk_callback. Blocks are a clang/gcc extension. -fblocks must be enabled to use this.


walk_callback


typedef int( *walk_callback)(
    const char *path,
    const char *filename,
    void *userdata);  
Parameters
path

The current path being walked

filename

The filename in the current path

userdata

User data pointer

Return Value

Returns 0 to continue walking, non-zero to stop


Enumerated Types

file_mode_t
file_seek_t

file_mode_t


enum file_mode_t { 
    FILE_READ = 0x00000001, 
    FILE_WRITE = 0x00000002, 
    FILE_APPEND = 0x00000004 
};  
Constants
FILE_READ

File read flag (r)

FILE_WRITE

File write flag (w)

FILE_APPEND

File append flag (a)

Discussion

File permissions for io_open


file_seek_t


Constants
FILE_START

Beginning of file marker (SEEK_SET)

FILE_CURSOR

Current file position marker (SEEK_CUR)

FILE_FINISH

End of file marker (SEEK_END)

Discussion

File offset positions for io_seek