Filesys Stream

Table Of Contents

This API provides a comprehensive, cross-platform filesystem abstraction layer in C, designed to unify file, directory, and link operations under a consistent interface while exposing rich metadata, thread-safety primitives, and advanced utilities like hashing, compression, deduplication, and transactional operations. At its core is fossil_io_filesys_obj_t, a base structure that models any filesystem entity with normalized fields such as path, permissions, ownership, timestamps, and size, allowing higher-level types like files, directories, and links to extend it cleanly. The API goes beyond simple I/O by supporting atomic operations (move, swap), recursive directory processing, symbolic link management, and even transaction-style workflows (tx_begin, commit, rollback) for safer multi-step changes. Additionally, it includes practical developer-focused utilities like path normalization, format detection, and file transformation callbacks, making it suitable for building robust tooling, package managers, or database-like storage layers directly on top of the filesystem.

HEADER REFERENCE #

#ifndef FOSSIL_IO_FILESYS_H
#define FOSSIL_IO_FILESYS_H

#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include <time.h>

#ifdef _WIN32
    #define FOSSIL_FILESYS_PATHSEP '\\'
#else
    #define FOSSIL_FILESYS_PATHSEP '/'
#endif

#ifdef __cplusplus
extern "C"
{
#endif

/* ------------------------------------------------------------
    * Core Definitions
    * ------------------------------------------------------------ */

#define FOSSIL_FILESYS_MAX_PATH 512
#define FOSSIL_FILESYS_MAX_ID 128
#define FOSSIL_FILESYS_MAX_TAGS 16

/* ------------------------------------------------------------
    * Filesystem Object Types
    * ------------------------------------------------------------ */

typedef enum
{
    FOSSIL_FILESYS_TYPE_UNKNOWN = 0,
    FOSSIL_FILESYS_TYPE_FILE,
    FOSSIL_FILESYS_TYPE_DIR,
    FOSSIL_FILESYS_TYPE_LINK,
    FOSSIL_FILESYS_TYPE_ARCHIVE
} fossil_io_filesys_type_t;

/* ------------------------------------------------------------
    * Permissions (cross-platform abstraction)
    * ------------------------------------------------------------ */

typedef struct
{
    bool read;
    bool write;
    bool execute;
} fossil_io_filesys_perms_t;

/* ------------------------------------------------------------
    * Thread / Locking Support
    * ------------------------------------------------------------ */

typedef struct
{
    void *handle; /* platform-specific mutex / lock */
    bool locked;
} fossil_io_filesys_lock_t;

/* ------------------------------------------------------------
    * Base Filesystem Object (shared by file/dir/link)
    * ------------------------------------------------------------ */

/**
 * @brief Base structure representing any filesystem object.
 *
 * This structure provides a unified abstraction over files, directories,
 * and symbolic/hard links. It is designed for cross-platform compatibility,
 * high-performance operations, and optional AI-driven metadata processing.
 *
 * Members:
 *  - char id[]: Unique string ID for referencing object (stable across operations).
 *  - char path[]: Full path to the object.
 *  - fossil_io_filesys_type_t type: Object type (file, dir, link).
 *  - fossil_io_filesys_perms_t perms: Access permissions.
 *  - uint32_t mode: Raw platform permission bits.
 *  - char owner[], group[]: Ownership metadata.
 *  - size_t size: Size (files) or entry count hint (dirs).
 *  - timestamps: created, modified, accessed.
 *  - lock: Thread-safe locking primitive.
 *  - flags: internal state flags.
 *
 * AI Fields:
 *  - indexed / analyzed
 *  - tags / embeddings
 */
typedef struct
{
    char id[FOSSIL_FILESYS_MAX_ID];
    char path[FOSSIL_FILESYS_MAX_PATH];

    fossil_io_filesys_type_t type;

    fossil_io_filesys_perms_t perms;
    uint32_t mode;

    char owner[64];
    char group[64];

    size_t size;

    time_t created_at;
    time_t modified_at;
    time_t accessed_at;

    fossil_io_filesys_lock_t lock;

    uint32_t flags;

} fossil_io_filesys_obj_t;

/* ------------------------------------------------------------
    * File Object
    * ------------------------------------------------------------ */

typedef struct
{
    fossil_io_filesys_obj_t base;

    void *handle; /* FILE* or platform handle */
    int fd;

    bool is_open;
    bool append_mode;
    bool temporary;

    size_t position;

    void *buffer;
    size_t buffer_size;

} fossil_io_filesys_file_t;

/* ------------------------------------------------------------
    * Directory Object
    * ------------------------------------------------------------ */

typedef struct
{
    fossil_io_filesys_obj_t base;

    size_t entry_count;

    bool recursive;
    bool mounted;

} fossil_io_filesys_dir_t;

/* ------------------------------------------------------------
    * Link Object
    * ------------------------------------------------------------ */

typedef struct
{
    fossil_io_filesys_obj_t base;

    char target[FOSSIL_FILESYS_MAX_PATH];

    bool symbolic;

} fossil_io_filesys_link_t;



extern fossil_io_filesys_file_t *_FOSSIL_STDIN;
extern fossil_io_filesys_file_t *_FOSSIL_STDOUT;
extern fossil_io_filesys_file_t *_FOSSIL_STDERR;

#define FOSSIL_STDIN (_FOSSIL_STDIN)
#define FOSSIL_STDOUT (_FOSSIL_STDOUT)
#define FOSSIL_STDERR (_FOSSIL_STDERR)

/* ------------------------------------------------------------
    * General Filesystem Operations
    * ------------------------------------------------------------ */

/**
 * Initialize or refresh a filesystem object from a given path.
 *
 * Populates metadata (type, size, permissions, timestamps).
 *
 * @param obj Pointer to filesystem object
 * @param path Path to object
 * @return 0 on success, negative on failure
 */
int32_t fossil_io_filesys_init(fossil_io_filesys_obj_t *obj, const char *path);

/**
 * Refresh the metadata of an existing filesystem object.
 *
 * Updates all metadata (size, permissions, timestamps) for an already
 * initialized filesystem object.
 *
 * @param obj Pointer to filesystem object to refresh
 * @return 0 on success, negative on failure
 */
int32_t fossil_io_filesys_refresh(fossil_io_filesys_obj_t *obj);

/**
 * Check if a filesystem object exists.
 *
 * @param path Path to check
 * @return 1 if exists, 0 if not, negative on error
 */
int32_t fossil_io_filesys_exists(const char *path);

/**
 * Remove a filesystem object (file, dir, link).
 *
 * @param path Path to remove
 * @param recursive Recursively remove contents if directory
 * @return 0 on success, negative on failure
 */
int32_t fossil_io_filesys_remove(const char *path, bool recursive);

/**
 * Rename or move a filesystem object.
 *
 * Handles cross-directory moves, atomic if possible.
 *
 * @param src Source path
 * @param dest Destination path
 * @return 0 on success, negative on failure
 */
int32_t fossil_io_filesys_move(const char *src, const char *dest);

/**
 * Copy a filesystem object.
 *
 * Copies files or directories. Can preserve metadata.
 *
 * @param src Source path
 * @param dest Destination path
 * @param preserve_meta Preserve permissions, timestamps
 * @return 0 on success, negative on failure
 */
int32_t fossil_io_filesys_copy(const char *src, const char *dest, bool preserve_meta);

/**
 * Atomic swap of two filesystem objects.
 *
 * @param path1 First path
 * @param path2 Second path
 * @return 0 on success, negative on failure
 */
int32_t fossil_io_filesys_swap(const char *path1, const char *path2);

/**
 * Deduplicate files in a directory (optional recursive).
 *
 * @param path Directory path
 * @param recursive Scan subdirectories
 * @return Number of duplicates removed, negative on failure
 */
int32_t fossil_io_filesys_deduplicate(const char *path, bool recursive);

/**
 * Get filesystem object info (type, size, permissions, timestamps).
 *
 * @param path Path to object
 * @param obj Pointer to store metadata
 * @return 0 on success, negative on failure
 */
int32_t fossil_io_filesys_stat(const char *path, fossil_io_filesys_obj_t *obj);

/* ------------------------------------------------------------
    * File-Specific Operations
    * ------------------------------------------------------------ */

/**
 * @brief Open a file and initialize a file object.
 *
 * Opens a file at the specified path with the given access mode and initializes
 * the provided file object structure. The file object will contain handle, position,
 * and other metadata needed for subsequent file operations.
 *
 * @param f Pointer to the file object to initialize
 * @param path Path to the file to open
 * @param mode File access mode (e.g., "r", "w", "a", "rb", "wb")
 * @return 0 on success, negative error code on failure
 */
int32_t fossil_io_filesys_file_open(fossil_io_filesys_file_t *f, const char *path, const char *mode);

/**
 * @brief Close an open file and release resources.
 *
 * Closes the file associated with the given file object, flushes any buffered data,
 * and releases platform-specific file handles and resources. The file object should
 * not be used after this call without reopening.
 *
 * @param f Pointer to the file object to close
 * @return 0 on success, negative error code on failure
 */
int32_t fossil_io_filesys_file_close(fossil_io_filesys_file_t *f);

/**
 * @brief Read data from an open file.
 *
 * Reads up to count elements of size bytes from the current file position into the
 * provided buffer. Advances the file position by the number of bytes read.
 *
 * @param f Pointer to the open file object
 * @param buf Pointer to buffer where data will be read
 * @param size Size of each element in bytes
 * @param count Number of elements to read
 * @return Number of elements successfully read, 0 on EOF or error
 */
size_t fossil_io_filesys_file_read(fossil_io_filesys_file_t *f, void *buf, size_t size, size_t count);

/**
 * @brief Write data to an open file.
 *
 * Writes count elements of size bytes from the provided buffer to the file at the
 * current file position. May be buffered depending on the file object's buffer settings.
 *
 * @param f Pointer to the open file object
 * @param buf Pointer to buffer containing data to write
 * @param size Size of each element in bytes
 * @param count Number of elements to write
 * @return Number of elements successfully written, 0 on error
 */
size_t fossil_io_filesys_file_write(fossil_io_filesys_file_t *f, const void *buf, size_t size, size_t count);

/**
 * @brief Seek to a specific position in an open file.
 *
 * Changes the file position indicator to the specified offset relative to the given
 * origin point (beginning, current position, or end of file).
 *
 * @param f Pointer to the open file object
 * @param offset Byte offset from the origin
 * @param origin Reference point for offset (SEEK_SET, SEEK_CUR, SEEK_END)
 * @return 0 on success, negative error code on failure
 */
int32_t fossil_io_filesys_file_seek(fossil_io_filesys_file_t *f, int64_t offset, int32_t origin);

/**
 * @brief Get the current position in an open file.
 *
 * Returns the current byte offset of the file position indicator from the start
 * of the file.
 *
 * @param f Pointer to the open file object
 * @return Current file position in bytes, or negative error code on failure
 */
int64_t fossil_io_filesys_file_tell(fossil_io_filesys_file_t *f);

/**
 * @brief Flush buffered data to the file.
 *
 * Forces any pending buffered write operations on the file to be written to the
 * underlying storage medium, ensuring data durability.
 *
 * @param f Pointer to the open file object
 * @return 0 on success, negative error code on failure
 */
int32_t fossil_io_filesys_file_flush(fossil_io_filesys_file_t *f);

/**
 * @brief Get the size of a file.
 *
 * Returns the total size in bytes of the file at the specified path without
 * requiring the file to be opened.
 *
 * @param path Path to the file
 * @return File size in bytes, or negative error code on failure
 */
int32_t fossil_io_filesys_file_size(const char *path);

/**
 * @brief Truncate or extend a file to a specific size.
 *
 * Resizes the file at the specified path to the given size. If the file is larger,
 * it will be truncated. If smaller, it will be extended (typically with null bytes).
 *
 * @param path Path to the file
 * @param size New size in bytes
 * @return 0 on success, negative error code on failure
 */
int32_t fossil_io_filesys_file_truncate(const char *path, size_t size);

/**
 * @brief Compute the cryptographic hash of a file.
 *
 * Reads the entire contents of a file at the given path and computes its hash
 * using the specified algorithm. The resulting hash is stored in the provided
 * output buffer. Supported algorithms include "sha256", "md5", "sha1", etc.
 *
 * @param path Path to the file to hash
 * @param hash_out Pointer to buffer where computed hash will be stored
 * @param hash_size Size of the hash_out buffer in bytes
 * @param algorithm Name of the hashing algorithm to use (e.g., "sha256")
 * @return 0 on success, negative error code on failure (invalid path, algorithm, etc.)
 */
int32_t fossil_io_filesys_file_hash(const char *path, unsigned char *hash_out, size_t hash_size, const char *algorithm);

/**
 * @brief Merge two files into a single destination file.
 *
 * Combines the contents of two source files into a single destination file according
 * to the specified merge strategy (concatenation, interleave, etc.).
 *
 * @param src1 Path to the first source file
 * @param src2 Path to the second source file
 * @param dest Path to the destination file
 * @param strategy Merge strategy to apply
 * @return 0 on success, negative error code on failure
 */
int32_t fossil_io_filesys_file_merge(const char *src1, const char *src2, const char *dest, uint32_t strategy);

/**
 * @brief Split a file into multiple parts of specified size.
 *
 * Divides a file into smaller chunks of the specified size, creating multiple output
 * files with names prefixed by the given prefix and numbered sequentially.
 *
 * @param path Path to the file to split
 * @param part_size Size of each part in bytes
 * @param prefix Prefix for generated part file names
 * @return 0 on success, negative error code on failure
 */
int32_t fossil_io_filesys_file_split(const char *path, size_t part_size, const char *prefix);

/**
 * @brief Join multiple file parts into a single file.
 *
 * Combines multiple file parts (typically created by file_split) into a single
 * destination file in the order specified by the parts array.
 *
 * @param parts Array of pointers to part file paths
 * @param count Number of parts in the array
 * @param dest Path to the destination file
 * @return 0 on success, negative error code on failure
 */
int32_t fossil_io_filesys_file_join(const char **parts, size_t count, const char *dest);

/**
 * @brief Compress a file using the specified algorithm.
 *
 * Compresses the source file using the provided compression algorithm (e.g., "gzip", "bzip2")
 * and writes the compressed data to the destination file.
 *
 * @param src Path to the source file to compress
 * @param dest Path to the destination compressed file
 * @param algorithm Compression algorithm to use (e.g., "gzip", "bzip2")
 * @return 0 on success, negative error code on failure
 */
int32_t fossil_io_filesys_file_compress(const char *src, const char *dest, const char *algorithm);

/**
 * @brief Decompress a file to its original form.
 *
 * Decompresses a previously compressed file and writes the decompressed data to the
 * destination file. The decompression algorithm is typically detected automatically.
 *
 * @param src Path to the compressed source file
 * @param dest Path to the destination decompressed file
 * @return 0 on success, negative error code on failure
 */
int32_t fossil_io_filesys_file_decompress(const char *src, const char *dest);

/**
 * @brief Rewrite file contents by applying a transformation function.
 *
 * Opens a file, reads its contents, applies a transformation function to the data,
 * and writes the transformed contents back to the file. Operates atomically using
 * a temporary file to preserve the original if an error occurs.
 *
 * @param path Path to the file to rewrite
 * @param transform Callback function that processes data (returns 0 on success, negative on failure)
 * @param user_data Pointer to user-defined data passed to the transform function
 * @return 0 on success, negative error code on failure
 */
int32_t fossil_io_filesys_file_rewrite(const char *path, int (*transform)(void *buf, size_t *size, void *user_data), void *user_data);

/**
 * @brief Detect the format of a file based on its magic bytes or extension.
 *
 * Analyzes a file to determine its format by examining magic bytes (file signature)
 * or file extension. Returns a string identifier for the detected format.
 *
 * @param path Path to the file to analyze
 * @param format_out Buffer to store the detected format string
 * @param max_len Maximum length of the format_out buffer in bytes
 * @return 0 on success, negative error code on failure
 */
int32_t fossil_io_filesys_file_format(const char *path, char *format_out, size_t max_len);

/**
 * @brief Check if a file is readable.
 *
 * Determines whether the file at the specified path can be opened for reading.
 *
 * @param path Path to the file
 * @return 1 if readable, 0 if not, negative on error
 */
int32_t fossil_io_filesys_file_is_readable(const char *path);

/**
 * @brief Check if a file is writable.
 *
 * Determines whether the file at the specified path can be opened for writing.
 *
 * @param path Path to the file
 * @return 1 if writable, 0 if not, negative on error
 */
int32_t fossil_io_filesys_file_is_writable(const char *path);

/**
 * @brief Set the permissions of a file.
 *
 * Changes the access permissions of the file at the specified path.
 *
 * @param path Path to the file
 * @param perms Permissions to set
 * @return 0 on success, negative error code on failure
 */
int32_t fossil_io_filesys_file_set_perms(const char *path, fossil_io_filesys_perms_t perms);

/**
 * @brief Set the owner and group of a file.
 *
 * Changes the owner and group of the file at the specified path.
 *
 * @param path Path to the file
 * @param owner New owner name or ID (platform-dependent)
 * @param group New group name or ID (platform-dependent)
 * @return 0 on success, negative error code on failure
 */
int32_t fossil_io_filesys_file_set_owner(const char *path, const char *owner, const char *group);

/* ------------------------------------------------------------
    * Directory-Specific Operations
    * ------------------------------------------------------------ */

/**
 * @brief Create a directory at the specified path.
 *
 * Creates a new directory at the given path. If recursive is true, it will create any
 * necessary parent directories as well.
 * @param path Path to the directory to create
 * @param recursive Whether to create parent directories if they do not exist
 * @return 0 on success, negative error code on failure
 */
int32_t fossil_io_filesys_dir_create(const char *path, bool recursive);

/**
 * @brief List the contents of a directory.
 *
 * Retrieves a list of entries in the specified directory, including their metadata.
 *
 * @param path Path to the directory to list
 * @param entries Array to store directory entries (caller-allocated)
 * @param max_entries Maximum number of entries to store in the array
 * @param out_count Pointer to store the actual number of entries found
 * @return 0 on success, negative error code on failure
 */
int32_t fossil_io_filesys_dir_list(const char *path, fossil_io_filesys_obj_t *entries, size_t max_entries, size_t *out_count);

/**
 * @brief Walk a directory tree and apply a callback to each entry.
 *
 * Recursively traverses a directory and its subdirectories, invoking the provided
 * callback function for each filesystem object encountered.
 *
 * @param path Path to the directory to walk
 * @param callback Function to call for each entry (returns 0 to continue, non-zero to stop)
 * @param user_data Pointer to user data to pass to the callback
 * @return 0 on success, negative error code on failure
 */
int32_t fossil_io_filesys_dir_walk(const char *path, int (*callback)(const fossil_io_filesys_obj_t *, void *), void *user_data);

/**
 * @brief Merge the contents of one directory into another.
 *
 * Merges the contents of the source directory into the destination directory. If
 * overwrite is true, existing files in the destination will be overwritten. If
 * recursive is true, subdirectories will also be merged.
 *
 * @param src Path to the source directory
 * @param dest Path to the destination directory
 * @param overwrite Whether to overwrite existing files in the destination
 * @param recursive Whether to merge subdirectories recursively
 * @return 0 on success, negative error code on failure
 */
int32_t fossil_io_filesys_dir_merge(const char *src, const char *dest, bool overwrite, bool recursive);

/**
 * @brief Mirror a directory to another location.
 *
 * Creates an exact mirror of the source directory at the destination path. If
 * delete_extras is true, any files in the destination that are not in the source
 * will be deleted.
 *
 * @param src Path to the source directory
 * @param dest Path to the destination directory
 * @param delete_extras Whether to delete files in the destination that are not in the source
 * @return 0 on success, negative error code on failure
 */
int32_t fossil_io_filesys_dir_mirror(const char *src, const char *dest, bool delete_extras);

/**
 * @brief Check if a directory exists at the given path.
 *
 * Determines whether a directory exists at the specified path.
 *
 * @param path Path to the directory
 * @return 1 if the directory exists, 0 if not, negative on error
 */
int32_t fossil_io_filesys_dir_exists(const char *path);

/**
 * @brief Check if a directory is readable.
 *
 * Determines whether the directory at the specified path can be opened for reading.
 *
 * @param path Path to the directory
 * @return 1 if readable, 0 if not, negative on error
 */
int32_t fossil_io_filesys_dir_is_readable(const char *path);

/**
 * @brief Check if a directory is writable.
 *
 * Determines whether the directory at the specified path can be written to.
 *
 * @param path Path to the directory
 * @return 1 if writable, 0 if not, negative on error
 */
int32_t fossil_io_filesys_dir_is_writable(const char *path);

/**
 * @brief Set the permissions of a directory.
 *
 * Changes the access permissions of the directory at the specified path.
 *
 * @param path Path to the directory
 * @param perms Permissions to set
 * @return 0 on success, negative error code on failure
 */
int32_t fossil_io_filesys_dir_set_perms(const char *path, fossil_io_filesys_perms_t perms);

/**
 * @brief Set the owner and group of a directory.
 *
 * Changes the owner and group of the directory at the specified path.
 *
 * @param path Path to the directory
 * @param owner New owner name or ID (platform-dependent)
 * @param group New group name or ID (platform-dependent)
 * @return 0 on success, negative error code on failure
 */
int32_t fossil_io_filesys_dir_set_owner(const char *path, const char *owner, const char *group);

/* ------------------------------------------------------------
    * Link-Specific Operations
    * ------------------------------------------------------------ */

/**
 * @brief Create a hard or symbolic link to a file, with optional metadata propagation.
 *
 * This function creates a new link (hard or symbolic) from `src->filename` to `dest_path`.
 * It optionally populates a fossil_io_filesys_file_t structure for the destination link and can
 * propagate file metadata such as timestamps and mode.
 *
 * @param src        Pointer to source fossil_io_filesys_file_t representing the existing file.
 * @param dest       Pointer to destination fossil_io_filesys_file_t to populate (may be NULL).
 * @param dest_path  Path to the file to create.
 * @param symbolic   If true, create a symbolic link; else create a hard link.
 * @param copy_meta  If true, copy metadata (mode, timestamps).
 *
 * @return int       0 on success, negative errno-style value on error.
 */
int32_t fossil_io_filesys_link_create(const char *target, const char *link_path, bool symbolic);

/**
 * @brief Read the target of a symbolic link.
 *
 * This function reads the target path of a symbolic link at the specified path.
 *
 * @param link_path Path to the symbolic link
 * @param target_out Buffer to store the target path
 * @param max_len Maximum length of the target_out buffer
 * @return 0 on success, negative error code on failure
 */
int32_t fossil_io_filesys_link_read(const char *link_path, char *target_out, size_t max_len);

/**
 * @brief Resolve a symbolic link to its ultimate target.
 *
 * This function resolves a symbolic link at the specified path to its final target,
 * following any intermediate links if necessary.
 *
 * @param link_path Path to the symbolic link
 * @param target_out Buffer to store the resolved target path
 * @param max_len Maximum length of the target_out buffer
 * @return 0 on success, negative error code on failure
 */
int32_t fossil_io_filesys_link_resolve(const char *link_path, char *target_out, size_t max_len);

/**
 * @brief Check if a link is symbolic or hard.
 *
 * This function checks whether the link at the specified path is a symbolic link
 * or a hard link.
 *
 * @param link_path Path to the link to check
 * @param is_symbolic Pointer to store the result (true if symbolic, false if hard)
 * @return 0 on success, negative error code on failure
 */
int32_t fossil_io_filesys_link_is_symbolic(const char *link_path, bool *is_symbolic);

/**
 * @brief Remove a link (symbolic or hard) at the specified path.
 *
 * Deletes the link at the given path. Does not affect the target file.
 *
 * @param link_path Path to the link to remove
 * @return 0 on success, negative error code on failure
 */
int32_t fossil_io_filesys_link_remove(const char *link_path);

/**
 * @brief Check if a link exists at the specified path.
 *
 * Determines whether a link (symbolic or hard) exists at the given path.
 *
 * @param link_path Path to the link to check
 * @return 1 if the link exists, 0 if not, negative on error
 */
int32_t fossil_io_filesys_link_exists(const char *link_path);

/**
 * @brief Set the permissions of a link (if supported).
 *
 * Changes the access permissions of the link itself (not the target).
 * On most systems, this is only meaningful for symbolic links and may not be supported.
 *
 * @param link_path Path to the link
 * @param perms Permissions to set
 * @return 0 on success, negative error code on failure
 */
int32_t fossil_io_filesys_link_set_perms(const char *link_path, fossil_io_filesys_perms_t perms);

/**
 * @brief Set the owner and group of a link (if supported).
 *
 * Changes the owner and group of the link itself (not the target).
 * On most systems, this is only meaningful for symbolic links and may not be supported.
 *
 * @param link_path Path to the link
 * @param owner New owner name or ID (platform-dependent)
 * @param group New group name or ID (platform-dependent)
 * @return 0 on success, negative error code on failure
 */
int32_t fossil_io_filesys_link_set_owner(const char *link_path, const char *owner, const char *group);

/* ------------------------------------------------------------
    * Transaction Operations
    * ------------------------------------------------------------ */

/**
 * @brief Begin a filesystem transaction.
 *
 * This function starts a new transaction, allowing multiple filesystem operations
 * to be performed atomically.
 *
 * @return 0 on success, negative error code on failure
 */
int32_t fossil_io_filesys_tx_begin(void);

/**
 * @brief Commit a filesystem transaction.
 *
 * This function commits the current transaction, making all changes permanent.
 *
 * @return 0 on success, negative error code on failure
 */
int32_t fossil_io_filesys_tx_commit(void);

/**
 * @brief Rollback a filesystem transaction.
 *
 * This function rolls back the current transaction, undoing all changes made
 * since the transaction began.
 *
 * @return 0 on success, negative error code on failure
 */
int32_t fossil_io_filesys_tx_rollback(void);

/* ------------------------------------------------------------
    * Path / Utility Operations
    * ------------------------------------------------------------ */

/**
 * @brief Get the current working directory.
 *
 * Retrieves the absolute path of the current working directory and stores it in the
 * provided buffer. The buffer must be large enough to hold the path plus null terminator.
 *
 * @param buf Pointer to buffer where the current working directory path will be stored
 * @param size Size of the buffer in bytes
 * @return 0 on success, negative error code on failure (buffer too small, permission denied, etc.)
 */
int32_t fossil_io_filesys_getcwd(char *buf, size_t size);

/**
 * @brief Change the current working directory.
 *
 * Changes the process's current working directory to the specified path. All relative
 * paths in subsequent operations will be resolved relative to this new directory.
 *
 * @param path Path to the directory to change to (absolute or relative)
 * @return 0 on success, negative error code on failure (path not found, permission denied, etc.)
 */
int32_t fossil_io_filesys_chdir(const char *path);

/**
 * @brief Convert a path to its absolute form.
 *
 * Resolves relative paths to absolute paths, expanding any relative path components
 * (., .., ~) and removing redundant separators. Symbolic links are not followed.
 *
 * @param path Input path (absolute or relative)
 * @param abs_path Buffer to store the resulting absolute path
 * @param max_len Maximum length of the abs_path buffer in bytes
 * @return 0 on success, negative error code on failure (buffer too small, invalid path, etc.)
 */
int32_t fossil_io_filesys_abspath(const char *path, char *abs_path, size_t max_len);

/**
 * @brief Extract the directory component from a path.
 *
 * Extracts the directory portion of a path string. For example, "/home/user/file.txt"
 * returns "/home/user". The trailing separator is not included in the output.
 *
 * @param path Input path string
 * @param dir_out Buffer to store the extracted directory name
 * @param max_len Maximum length of the dir_out buffer in bytes
 * @return 0 on success, negative error code on failure (buffer too small, invalid path, etc.)
 */
int32_t fossil_io_filesys_dirname(const char *path, char *dir_out, size_t max_len);

/**
 * @brief Extract the filename component from a path.
 *
 * Extracts the filename (basename) portion of a path string, removing all directory
 * components. For example, "/home/user/file.txt" returns "file.txt".
 *
 * @param path Input path string
 * @param name_out Buffer to store the extracted filename
 * @param max_len Maximum length of the name_out buffer in bytes
 * @return 0 on success, negative error code on failure (buffer too small, invalid path, etc.)
 */
int32_t fossil_io_filesys_basename(const char *path, char *name_out, size_t max_len);

/**
 * @brief Extract the file extension from a path.
 *
 * Extracts the file extension (suffix) from a filename, including the leading dot.
 * For example, "/home/user/file.tar.gz" may return ".gz" depending on implementation.
 * If no extension is present, an empty string is returned.
 *
 * @param path Input path string
 * @param ext_out Buffer to store the extracted file extension
 * @param max_len Maximum length of the ext_out buffer in bytes
 * @return 0 on success, negative error code on failure (buffer too small, invalid path, etc.)
 */
int32_t fossil_io_filesys_extension(const char *path, char *ext_out, size_t max_len);

/**
 * @brief Normalize a file path by converting separators and removing redundant components.
 *
 * This function takes an input path string and normalizes it by converting all path
 * separators to the platform-specific separator, removing redundant separators, and
 * resolving any relative path components (., ..) where possible. The resulting normalized
 * path is returned as a newly allocated string that must be freed by the caller.
 *
 * @param path Input path string to normalize
 * @return Normalized path string on success (caller must free), or NULL on failure (invalid input, memory allocation failure, etc.)
 */
char *fossil_io_filesys_path_normalize(const char *path);

/**
 * @brief Set the access and modification times of a filesystem object.
 *
 * This function updates the access and modification timestamps of the filesystem
 * object at the specified path to the provided time values. The created_at timestamp
 * is typically not modified by this operation.
 *
 * @param path Path to the filesystem object to update
 * @param accessed_at New access time (time_t value)
 * @param modified_at New modification time (time_t value)
 * @return 0 on success, negative error code on failure (invalid path, permission denied, etc.)
 */
int fossil_io_filesys_set_times(const char *path, time_t accessed_at, time_t modified_at);

/* ------------------------------------------------------------
    * Utility Functions
    * ------------------------------------------------------------ */

/**
 * @brief Convert a filesystem object type enum to a human-readable string.
 *
 * This function takes a fossil_io_filesys_type_t value and returns a corresponding
 * string representation (e.g., "file", "directory", "link", "unknown").
 *
 * @param type The filesystem object type to convert
 * @return A string representing the filesystem object type
 */
const char *fossil_io_filesys_type_string(fossil_io_filesys_type_t type);

#ifdef __cplusplus
}

#include <string>

namespace fossil::io
{

    class Filesys
    {
    public:
        // Constructor/Destructor
        Filesys() = default;
        ~Filesys() = default;

        /**
         * @brief Initialize a filesystem object from a given path.
         *
         * Populates metadata (type, size, permissions, timestamps) for the object
         * at the specified path.
         *
         * @param obj Pointer to filesystem object to initialize
         * @param path Path to the filesystem object
         * @return 0 on success, negative on failure
         */
        int32_t init(fossil_io_filesys_obj_t *obj, const std::string &path)
        {
            return fossil_io_filesys_init(obj, path.c_str());
        }

        /**
         * @brief Refresh the metadata of an existing filesystem object.
         *
         * Updates all metadata (size, permissions, timestamps) for an already
         * initialized filesystem object.
         *
         * @param obj Pointer to filesystem object to refresh
         * @return 0 on success, negative on failure
         */
        int32_t refresh(fossil_io_filesys_obj_t *obj)
        {
            return fossil_io_filesys_refresh(obj);
        }

        /**
         * @brief Check if a filesystem object exists at the given path.
         *
         * Determines whether a file, directory, or link exists at the specified path
         * without modifying any metadata or file access times.
         *
         * @param path Path to check for existence
         * @return 1 if exists, 0 if not, negative on error
         */
        int32_t exists(const std::string &path)
        {
            return fossil_io_filesys_exists(path.c_str());
        }

        /**
         * @brief Remove a filesystem object (file, directory, or link).
         *
         * Deletes the filesystem object at the specified path. If recursive is true
         * and the object is a directory, removes all contents recursively.
         *
         * @param path Path to the object to remove
         * @param recursive If true, recursively remove directory contents
         * @return 0 on success, negative on failure
         */
        int32_t remove(const std::string &path, bool recursive = false)
        {
            return fossil_io_filesys_remove(path.c_str(), recursive);
        }

        /**
         * @brief Rename or move a filesystem object to a new location.
         *
         * Atomically moves (or renames) a filesystem object from src to dest.
         * Supports cross-directory moves when possible.
         *
         * @param src Source path of the object to move
         * @param dest Destination path for the object
         * @return 0 on success, negative on failure
         */
        int32_t move(const std::string &src, const std::string &dest)
        {
            return fossil_io_filesys_move(src.c_str(), dest.c_str());
        }

        /**
         * @brief Copy a filesystem object to a new location.
         *
         * Creates a copy of the file or directory at src to dest. If preserve_meta
         * is true, retains permissions and timestamps from the source.
         *
         * @param src Source path of the object to copy
         * @param dest Destination path for the copy
         * @param preserve_meta If true, preserve permissions and timestamps
         * @return 0 on success, negative on failure
         */
        int32_t copy(const std::string &src, const std::string &dest, bool preserve_meta = true)
        {
            return fossil_io_filesys_copy(src.c_str(), dest.c_str(), preserve_meta);
        }

        /**
         * @brief Atomically swap two filesystem objects.
         *
         * Exchanges the contents and metadata of two filesystem objects in an
         * atomic operation when supported by the platform.
         *
         * @param path1 Path to the first object
         * @param path2 Path to the second object
         * @return 0 on success, negative on failure
         */
        int32_t swap(const std::string &path1, const std::string &path2)
        {
            return fossil_io_filesys_swap(path1.c_str(), path2.c_str());
        }

        /**
         * @brief Deduplicate files in a directory by removing duplicates.
         *
         * Scans a directory and removes duplicate files, optionally scanning
         * subdirectories recursively. Uses hash-based comparison.
         *
         * @param path Directory path to scan for duplicates
         * @param recursive If true, scan subdirectories recursively
         * @return Number of duplicates removed, or negative on failure
         */
        int32_t deduplicate(const std::string &path, bool recursive = false)
        {
            return fossil_io_filesys_deduplicate(path.c_str(), recursive);
        }

        /**
         * @brief Get filesystem object metadata (type, size, permissions, timestamps).
         *
         * Retrieves complete metadata information about a filesystem object
         * without opening it or modifying access times.
         *
         * @param path Path to the object
         * @param obj Pointer to storage for metadata
         * @return 0 on success, negative on failure
         */
        int32_t stat(const std::string &path, fossil_io_filesys_obj_t *obj)
        {
            return fossil_io_filesys_stat(path.c_str(), obj);
        }

        /**
         * @brief Detect the format of a file based on its magic bytes or extension.
         *
         * Analyzes a file to determine its format by examining magic bytes (file signature)
         * or file extension. Returns a string identifier for the detected format.
         *
         * @param path Path to the file to analyze
         * @param format_out Buffer to store the detected format string
         * @param max_len Maximum length of the format_out buffer in bytes
         * @return 0 on success, negative error code on failure
         */
        int32_t file_format(const std::string &path, char *format_out, size_t max_len)
        {
            return fossil_io_filesys_file_format(path.c_str(), format_out, max_len);
        }

        /**
         * @brief Rewrite file contents by applying a transformation function.
         *
         * Opens a file, reads its contents, applies a transformation function to the data,
         * and writes the transformed contents back to the file. Operates atomically using
         * a temporary file to preserve the original if an error occurs.
         *
         * @param path Path to the file to rewrite
         * @param transform Callback function that processes data (returns 0 on success, negative on failure)
         * @param user_data Pointer to user-defined data passed to the transform function
         * @return 0 on success, negative error code on failure
         */
        int32_t file_rewrite(const std::string &path, int (*transform)(void *buf, size_t *size, void *user_data), void *user_data)
        {
            return fossil_io_filesys_file_rewrite(path.c_str(), transform, user_data);
        }

        /**
         * @brief Open a file and initialize a file object.
         *
         * Opens a file at the specified path with the given mode and initializes
         * the file object structure for subsequent read/write operations.
         *
         * @param f Pointer to file object to initialize
         * @param path Path to the file to open
         * @param mode File access mode (e.g., "r", "w", "a", "rb", "wb")
         * @return 0 on success, negative on failure
         */
        int32_t file_open(fossil_io_filesys_file_t *f, const std::string &path, const std::string &mode)
        {
            return fossil_io_filesys_file_open(f, path.c_str(), mode.c_str());
        }

        /**
         * @brief Close an open file and release resources.
         *
         * Closes the file, flushes any buffered data, and releases platform-specific
         * file handles. The file object should not be used after this call without reopening.
         *
         * @param f Pointer to the file object to close
         * @return 0 on success, negative on failure
         */
        int32_t file_close(fossil_io_filesys_file_t *f)
        {
            return fossil_io_filesys_file_close(f);
        }

        /**
         * @brief Read data from an open file.
         *
         * Reads up to count elements of size bytes from the current file position
         * into the provided buffer. Advances the position by bytes read.
         *
         * @param f Pointer to the open file object
         * @param buf Pointer to buffer for read data
         * @param size Size of each element in bytes
         * @param count Number of elements to read
         * @return Number of elements successfully read, 0 on EOF or error
         */
        size_t file_read(fossil_io_filesys_file_t *f, void *buf, size_t size, size_t count)
        {
            return fossil_io_filesys_file_read(f, buf, size, count);
        }

        /**
         * @brief Write data to an open file.
         *
         * Writes count elements of size bytes from the buffer to the file at the
         * current position. May be buffered depending on file object settings.
         *
         * @param f Pointer to the open file object
         * @param buf Pointer to buffer containing data to write
         * @param size Size of each element in bytes
         * @param count Number of elements to write
         * @return Number of elements successfully written, 0 on error
         */
        size_t file_write(fossil_io_filesys_file_t *f, const void *buf, size_t size, size_t count)
        {
            return fossil_io_filesys_file_write(f, buf, size, count);
        }

        /**
         * @brief Seek to a specific position in an open file.
         *
         * Changes the file position indicator to the specified offset relative to
         * the given origin (SEEK_SET, SEEK_CUR, SEEK_END).
         *
         * @param f Pointer to the open file object
         * @param offset Byte offset from the origin
         * @param origin Reference point: SEEK_SET, SEEK_CUR, or SEEK_END
         * @return 0 on success, negative on failure
         */
        int32_t file_seek(fossil_io_filesys_file_t *f, int64_t offset, int32_t origin)
        {
            return fossil_io_filesys_file_seek(f, offset, origin);
        }

        /**
         * @brief Get the current position in an open file.
         *
         * Returns the current byte offset of the file position indicator from the
         * start of the file.
         *
         * @param f Pointer to the open file object
         * @return Current file position in bytes, or negative on failure
         */
        int64_t file_tell(fossil_io_filesys_file_t *f)
        {
            return fossil_io_filesys_file_tell(f);
        }

        /**
         * @brief Flush buffered data to the file.
         *
         * Forces any pending buffered write operations on the file to be written
         * to the underlying storage medium, ensuring data durability.
         *
         * @param f Pointer to the open file object
         * @return 0 on success, negative on failure
         */
        int32_t file_flush(fossil_io_filesys_file_t *f)
        {
            return fossil_io_filesys_file_flush(f);
        }

        /**
         * @brief Get the size of a file.
         *
         * Returns the total size in bytes of the file at the specified path
         * without requiring the file to be opened.
         *
         * @param path Path to the file
         * @return File size in bytes, or negative on failure
         */
        int32_t file_size(const std::string &path)
        {
            return fossil_io_filesys_file_size(path.c_str());
        }

        /**
         * @brief Truncate or extend a file to a specific size.
         *
         * Resizes the file to the given size. If larger, it will be truncated.
         * If smaller, it will be extended (typically with null bytes).
         *
         * @param path Path to the file
         * @param size New size in bytes
         * @return 0 on success, negative on failure
         */
        int32_t file_truncate(const std::string &path, size_t size)
        {
            return fossil_io_filesys_file_truncate(path.c_str(), size);
        }

        /**
         * @brief Compute the cryptographic hash of a file.
         *
         * Reads the entire contents of a file and computes its hash using the
         * specified algorithm (e.g., "sha256", "md5", "sha1").
         *
         * @param path Path to the file to hash
         * @param hash_out Pointer to buffer for the computed hash
         * @param hash_size Size of the hash_out buffer in bytes
         * @param algorithm Name of the hashing algorithm to use
         * @return 0 on success, negative on failure
         */
        int32_t file_hash(const std::string &path, unsigned char *hash_out, size_t hash_size, const std::string &algorithm)
        {
            return fossil_io_filesys_file_hash(path.c_str(), hash_out, hash_size, algorithm.c_str());
        }

        /**
         * @brief Merge two files into a single destination file.
         *
         * Combines the contents of two source files into a single destination file
         * according to the specified merge strategy.
         *
         * @param src1 Path to the first source file
         * @param src2 Path to the second source file
         * @param dest Path to the destination file
         * @param strategy Merge strategy to apply (concatenation, interleave, etc.)
         * @return 0 on success, negative on failure
         */
        int32_t file_merge(const std::string &src1, const std::string &src2, const std::string &dest, uint32_t strategy)
        {
            return fossil_io_filesys_file_merge(src1.c_str(), src2.c_str(), dest.c_str(), strategy);
        }

        /**
         * @brief Split a file into multiple parts of specified size.
         *
         * Divides a file into smaller chunks, creating multiple output files with
         * names prefixed by the given prefix and numbered sequentially.
         *
         * @param path Path to the file to split
         * @param part_size Size of each part in bytes
         * @param prefix Prefix for generated part file names
         * @return 0 on success, negative on failure
         */
        int32_t file_split(const std::string &path, size_t part_size, const std::string &prefix)
        {
            return fossil_io_filesys_file_split(path.c_str(), part_size, prefix.c_str());
        }

        /**
         * @brief Join multiple file parts into a single file.
         *
         * Combines multiple file parts (typically created by file_split) into a
         * single destination file in the specified order.
         *
         * @param parts Array of pointers to part file paths
         * @param count Number of parts in the array
         * @param dest Path to the destination file
         * @return 0 on success, negative on failure
         */
        int32_t file_join(const char **parts, size_t count, const std::string &dest)
        {
            return fossil_io_filesys_file_join(parts, count, dest.c_str());
        }

        /**
         * @brief Compress a file using the specified algorithm.
         *
         * Compresses the source file using the provided compression algorithm
         * (e.g., "gzip", "bzip2") and writes compressed data to the destination.
         *
         * @param src Path to the source file to compress
         * @param dest Path to the destination compressed file
         * @param algorithm Compression algorithm to use
         * @return 0 on success, negative on failure
         */
        int32_t file_compress(const std::string &src, const std::string &dest, const std::string &algorithm)
        {
            return fossil_io_filesys_file_compress(src.c_str(), dest.c_str(), algorithm.c_str());
        }

        /**
         * @brief Decompress a file to its original form.
         *
         * Decompresses a previously compressed file and writes the decompressed
         * data to the destination. Algorithm is typically detected automatically.
         *
         * @param src Path to the compressed source file
         * @param dest Path to the destination decompressed file
         * @return 0 on success, negative on failure
         */
        int32_t file_decompress(const std::string &src, const std::string &dest)
        {
            return fossil_io_filesys_file_decompress(src.c_str(), dest.c_str());
        }

        /**
         * @brief Check if a file is readable.
         *
         * Determines whether the file at the specified path can be opened for reading.
         *
         * @param path Path to the file
         * @return 1 if readable, 0 if not, negative on error
         */
        int32_t file_is_readable(const std::string &path)
        {
            return fossil_io_filesys_file_is_readable(path.c_str());
        }

        /**
         * @brief Check if a file is writable.
         *
         * Determines whether the file at the specified path can be opened for writing.
         *
         * @param path Path to the file
         * @return 1 if writable, 0 if not, negative on error
         */
        int32_t file_is_writable(const std::string &path)
        {
            return fossil_io_filesys_file_is_writable(path.c_str());
        }

        /**
         * @brief Set the permissions of a file.
         *
         * Changes the access permissions of the file at the specified path.
         *
         * @param path Path to the file
         * @param perms Permissions to set
         * @return 0 on success, negative error code on failure
         */
        int32_t file_set_perms(const std::string &path, fossil_io_filesys_perms_t perms)
        {
            return fossil_io_filesys_file_set_perms(path.c_str(), perms);
        }

        /**
         * @brief Set the owner and group of a file.
         *
         * Changes the owner and group of the file at the specified path.
         *
         * @param path Path to the file
         * @param owner New owner name or ID (platform-dependent)
         * @param group New group name or ID (platform-dependent)
         * @return 0 on success, negative error code on failure
         */
        int32_t file_set_owner(const std::string &path, const std::string &owner, const std::string &group)
        {
            return fossil_io_filesys_file_set_owner(path.c_str(), owner.c_str(), group.c_str());
        }

        /**
         * @brief Create a directory at the specified path.
         *
         * Creates a new directory. If recursive is true, also creates any necessary
         * parent directories as needed.
         *
         * @param path Path to the directory to create
         * @param recursive If true, create parent directories if they do not exist
         * @return 0 on success, negative on failure
         */
        int32_t dir_create(const std::string &path, bool recursive = false)
        {
            return fossil_io_filesys_dir_create(path.c_str(), recursive);
        }

        /**
         * @brief List the contents of a directory.
         *
         * Retrieves a list of entries in the specified directory, including their
         * metadata. Results are stored in the provided array up to max_entries.
         *
         * @param path Path to the directory to list
         * @param entries Array to store directory entries (caller-allocated)
         * @param max_entries Maximum number of entries to store
         * @param out_count Pointer to store the actual number of entries found
         * @return 0 on success, negative on failure
         */
        int32_t dir_list(const std::string &path, fossil_io_filesys_obj_t *entries, size_t max_entries, size_t *out_count)
        {
            return fossil_io_filesys_dir_list(path.c_str(), entries, max_entries, out_count);
        }

        /**
         * @brief Walk a directory tree and apply a callback to each entry.
         *
         * Recursively traverses a directory and subdirectories, invoking the provided
         * callback function for each filesystem object encountered.
         *
         * @param path Path to the directory to walk
         * @param callback Function to call for each entry (return 0 to continue, non-zero to stop)
         * @param user_data Pointer to user data to pass to the callback
         * @return 0 on success, negative on failure
         */
        int32_t dir_walk(const std::string &path, int (*callback)(const fossil_io_filesys_obj_t *, void *), void *user_data)
        {
            return fossil_io_filesys_dir_walk(path.c_str(), callback, user_data);
        }

        /**
         * @brief Merge the contents of one directory into another.
         *
         * Merges the contents of the source directory into the destination directory.
         * If overwrite is true, existing files in the destination will be replaced.
         * If recursive is true, subdirectories will also be merged.
         *
         * @param src Path to the source directory
         * @param dest Path to the destination directory
         * @param overwrite If true, overwrite existing files in destination
         * @param recursive If true, merge subdirectories recursively
         * @return 0 on success, negative on failure
         */
        int32_t dir_merge(const std::string &src, const std::string &dest, bool overwrite = false, bool recursive = true)
        {
            return fossil_io_filesys_dir_merge(src.c_str(), dest.c_str(), overwrite, recursive);
        }

        /**
         * @brief Mirror a directory to another location.
         *
         * Creates an exact mirror of the source directory at the destination path.
         * If delete_extras is true, any files in the destination that are not in
         * the source will be deleted.
         *
         * @param src Path to the source directory
         * @param dest Path to the destination directory
         * @param delete_extras If true, delete files in destination not in source
         * @return 0 on success, negative on failure
         */
        int32_t dir_mirror(const std::string &src, const std::string &dest, bool delete_extras = false)
        {
            return fossil_io_filesys_dir_mirror(src.c_str(), dest.c_str(), delete_extras);
        }

        /**
         * @brief Check if a directory exists at the given path.
         *
         * Determines whether a directory exists at the specified path.
         *
         * @param path Path to the directory
         * @return 1 if the directory exists, 0 if not, negative on error
         */
        int32_t dir_exists(const std::string &path)
        {
            return fossil_io_filesys_dir_exists(path.c_str());
        }

        /**
         * @brief Check if a directory is readable.
         *
         * Determines whether the directory at the specified path can be opened for reading.
         *
         * @param path Path to the directory
         * @return 1 if readable, 0 if not, negative on error
         */
        int32_t dir_is_readable(const std::string &path)
        {
            return fossil_io_filesys_dir_is_readable(path.c_str());
        }

        /**
         * @brief Check if a directory is writable.
         *
         * Determines whether the directory at the specified path can be written to.
         *
         * @param path Path to the directory
         * @return 1 if writable, 0 if not, negative on error
         */
        int32_t dir_is_writable(const std::string &path)
        {
            return fossil_io_filesys_dir_is_writable(path.c_str());
        }

        /**
         * @brief Set the permissions of a directory.
         *
         * Changes the access permissions of the directory at the specified path.
         *
         * @param path Path to the directory
         * @param perms Permissions to set
         * @return 0 on success, negative error code on failure
         */
        int32_t dir_set_perms(const std::string &path, fossil_io_filesys_perms_t perms)
        {
            return fossil_io_filesys_dir_set_perms(path.c_str(), perms);
        }

        /**
         * @brief Set the owner and group of a directory.
         *
         * Changes the owner and group of the directory at the specified path.
         *
         * @param path Path to the directory
         * @param owner New owner name or ID (platform-dependent)
         * @param group New group name or ID (platform-dependent)
         * @return 0 on success, negative error code on failure
         */
        int32_t dir_set_owner(const std::string &path, const std::string &owner, const std::string &group)
        {
            return fossil_io_filesys_dir_set_owner(path.c_str(), owner.c_str(), group.c_str());
        }

        /**
         * @brief Create a hard or symbolic link to a file.
         *
         * Creates a new link (hard or symbolic) from the target file to the link path.
         * Symbolic links point to the target; hard links are direct references.
         *
         * @param target Path to the target file
         * @param link_path Path where the link will be created
         * @param symbolic If true, create a symbolic link; else create a hard link
         * @return 0 on success, negative on failure
         */
        int32_t link_create(const std::string &target, const std::string &link_path, bool symbolic = true)
        {
            return fossil_io_filesys_link_create(target.c_str(), link_path.c_str(), symbolic);
        }

        /**
         * @brief Read the target of a symbolic link.
         *
         * Reads the target path of a symbolic link at the specified path,
         * without following intermediate links.
         *
         * @param link_path Path to the symbolic link
         * @param target_out Buffer to store the target path
         * @param max_len Maximum length of the target_out buffer
         * @return 0 on success, negative on failure
         */
        int32_t link_read(const std::string &link_path, char *target_out, size_t max_len)
        {
            return fossil_io_filesys_link_read(link_path.c_str(), target_out, max_len);
        }

        /**
         * @brief Resolve a symbolic link to its ultimate target.
         *
         * Resolves a symbolic link at the specified path to its final target,
         * following any intermediate links if necessary.
         *
         * @param link_path Path to the symbolic link
         * @param target_out Buffer to store the resolved target path
         * @param max_len Maximum length of the target_out buffer
         * @return 0 on success, negative on failure
         */
        int32_t link_resolve(const std::string &link_path, char *target_out, size_t max_len)
        {
            return fossil_io_filesys_link_resolve(link_path.c_str(), target_out, max_len);
        }

        /**
         * @brief Check if a link is symbolic or hard.
         *
         * Determines whether the link at the specified path is a symbolic link
         * or a hard link.
         *
         * @param link_path Path to the link to check
         * @param is_symbolic Pointer to store the result (true if symbolic, false if hard)
         * @return 0 on success, negative on failure
         */
        int32_t link_is_symbolic(const std::string &link_path, bool *is_symbolic)
        {
            return fossil_io_filesys_link_is_symbolic(link_path.c_str(), is_symbolic);
        }

        /**
         * @brief Remove a link (symbolic or hard) at the specified path.
         *
         * Deletes the link at the given path. Does not affect the target file.
         *
         * @param link_path Path to the link to remove
         * @return 0 on success, negative error code on failure
         */
        int32_t link_remove(const std::string &link_path)
        {
            return fossil_io_filesys_link_remove(link_path.c_str());
        }

        /**
         * @brief Check if a link exists at the specified path.
         *
         * Determines whether a link (symbolic or hard) exists at the given path.
         *
         * @param link_path Path to the link to check
         * @return 1 if the link exists, 0 if not, negative on error
         */
        int32_t link_exists(const std::string &link_path)
        {
            return fossil_io_filesys_link_exists(link_path.c_str());
        }

        /**
         * @brief Set the permissions of a link (if supported).
         *
         * Changes the access permissions of the link itself (not the target).
         * On most systems, this is only meaningful for symbolic links and may not be supported.
         *
         * @param link_path Path to the link
         * @param perms Permissions to set
         * @return 0 on success, negative error code on failure
         */
        int32_t link_set_perms(const std::string &link_path, fossil_io_filesys_perms_t perms)
        {
            return fossil_io_filesys_link_set_perms(link_path.c_str(), perms);
        }

        /**
         * @brief Set the owner and group of a link (if supported).
         *
         * Changes the owner and group of the link itself (not the target).
         * On most systems, this is only meaningful for symbolic links and may not be supported.
         *
         * @param link_path Path to the link
         * @param owner New owner name or ID (platform-dependent)
         * @param group New group name or ID (platform-dependent)
         * @return 0 on success, negative error code on failure
         */
        int32_t link_set_owner(const std::string &link_path, const std::string &owner, const std::string &group)
        {
            return fossil_io_filesys_link_set_owner(link_path.c_str(), owner.c_str(), group.c_str());
        }

        /**
         * @brief Begin a filesystem transaction.
         *
         * Starts a new transaction, allowing multiple filesystem operations
         * to be performed atomically. Use commit() or rollback() to finalize.
         *
         * @return 0 on success, negative on failure
         */
        int32_t tx_begin()
        {
            return fossil_io_filesys_tx_begin();
        }

        /**
         * @brief Commit a filesystem transaction.
         *
         * Commits the current transaction, making all changes permanent.
         * All operations since tx_begin() are applied atomically.
         *
         * @return 0 on success, negative on failure
         */
        int32_t tx_commit()
        {
            return fossil_io_filesys_tx_commit();
        }

        /**
         * @brief Rollback a filesystem transaction.
         *
         * Rolls back the current transaction, undoing all changes made
         * since tx_begin() was called.
         *
         * @return 0 on success, negative on failure
         */
        int32_t tx_rollback()
        {
            return fossil_io_filesys_tx_rollback();
        }

        /**
         * @brief Get the current working directory.
         *
         * Retrieves the absolute path of the current working directory and stores
         * it in the provided buffer. Buffer must be large enough for path + null terminator.
         *
         * @param buf Pointer to buffer for the current working directory path
         * @param size Size of the buffer in bytes
         * @return 0 on success, negative on failure
         */
        int32_t getcwd(char *buf, size_t size)
        {
            return fossil_io_filesys_getcwd(buf, size);
        }

        /**
         * @brief Change the current working directory.
         *
         * Changes the process's current working directory to the specified path.
         * All relative paths in subsequent operations are resolved relative to this directory.
         *
         * @param path Path to the directory to change to (absolute or relative)
         * @return 0 on success, negative on failure
         */
        int32_t chdir(const std::string &path)
        {
            return fossil_io_filesys_chdir(path.c_str());
        }

        /**
         * @brief Convert a path to its absolute form.
         *
         * Resolves relative paths to absolute paths, expanding relative components
         * (., .., ~) and removing redundant separators. Symbolic links are not followed.
         *
         * @param path Input path (absolute or relative)
         * @param abs_path Buffer to store the resulting absolute path
         * @param max_len Maximum length of the abs_path buffer in bytes
         * @return 0 on success, negative on failure
         */
        int32_t abspath(const std::string &path, char *abs_path, size_t max_len)
        {
            return fossil_io_filesys_abspath(path.c_str(), abs_path, max_len);
        }

        /**
         * @brief Extract the directory component from a path.
         *
         * Extracts the directory portion of a path string. For example, "/home/user/file.txt"
         * returns "/home/user" without the trailing separator.
         *
         * @param path Input path string
         * @param dir_out Buffer to store the extracted directory name
         * @param max_len Maximum length of the dir_out buffer in bytes
         * @return 0 on success, negative on failure
         */
        int32_t dirname(const std::string &path, char *dir_out, size_t max_len)
        {
            return fossil_io_filesys_dirname(path.c_str(), dir_out, max_len);
        }

        /**
         * @brief Extract the filename component from a path.
         *
         * Extracts the filename (basename) portion of a path string, removing all directory
         * components. For example, "/home/user/file.txt" returns "file.txt".
         *
         * @param path Input path string
         * @param name_out Buffer to store the extracted filename
         * @param max_len Maximum length of the name_out buffer in bytes
         * @return 0 on success, negative on failure
         */
        int32_t basename(const std::string &path, char *name_out, size_t max_len)
        {
            return fossil_io_filesys_basename(path.c_str(), name_out, max_len);
        }

        /**
         * @brief Extract the file extension from a path.
         *
         * Extracts the file extension (suffix) from a filename, including the leading dot.
         * For example, might return ".gz" from "file.tar.gz". If no extension is present,
         * returns an empty string.
         *
         * @param path Input path string
         * @param ext_out Buffer to store the extracted file extension
         * @param max_len Maximum length of the ext_out buffer in bytes
         * @return 0 on success, negative on failure
         */
        int32_t extension(const std::string &path, char *ext_out, size_t max_len)
        {
            return fossil_io_filesys_extension(path.c_str(), ext_out, max_len);
        }

        /**
         * @brief Normalize a file path by converting separators and removing redundant components.
         *
         * This method takes a std::string path, normalizes it by converting all path separators
         * to the platform-specific separator, removing redundant separators, and resolving any
         * relative path components (., ..) where possible. The result is returned as a std::string.
         *
         * @param path Input path string to normalize
         * @return Normalized path string (empty if normalization fails)
         */
        std::string path_normalize(const std::string &path)
        {
            char *normalized = fossil_io_filesys_path_normalize(path.c_str());
            if (!normalized)
                return std::string();
            std::string result(normalized);
            free(normalized);
            return result;
        }

        /**
         * @brief Set the access and modification times of a filesystem object.
         *
         * Updates the access and modification timestamps of the object at the specified path.
         * The created_at timestamp is not modified.
         *
         * @param path Path to the filesystem object to update
         * @param accessed_at New access time (time_t value)
         * @param modified_at New modification time (time_t value)
         * @return 0 on success, negative error code on failure
         */
        int32_t set_times(const std::string &path, time_t accessed_at, time_t modified_at)
        {
            return fossil_io_filesys_set_times(path.c_str(), accessed_at, modified_at);
        }

        /**
         * @brief Convert a filesystem object type enum to a human-readable string.
         *
         * Takes a fossil_io_filesys_type_t value and returns a corresponding string
         * representation (e.g., "file", "directory", "link", "unknown").
         *
         * @param type The filesystem object type to convert
         * @return A string representing the filesystem object type
         */
        const char *type_string(fossil_io_filesys_type_t type)
        {
            return fossil_io_filesys_type_string(type);
        }
    };

} // namespace fossil

#endif

#endif /* FOSSIL_IO_FRAMEWORK_H */

SAMPLE CODE C #

#include "fossil/io/filesys.h"
#include <stdio.h>
#include <string.h>

int main(void)
{
    fossil_io_filesys_file_t file;
    const char *path = "example.txt";

    /* Open file for writing */
    if (fossil_io_filesys_file_open(&file, path, "w") != 0) {
        printf("Failed to open file\n");
        return 1;
    }

    /* Write data */
    const char *text = "Hello from Fossil Filesystem API!\n";
    fossil_io_filesys_file_write(&file, text, 1, strlen(text));

    /* Flush and close */
    fossil_io_filesys_file_flush(&file);
    fossil_io_filesys_file_close(&file);

    /* Read file back */
    if (fossil_io_filesys_file_open(&file, path, "r") != 0) {
        printf("Failed to reopen file\n");
        return 1;
    }

    char buffer[128] = {0};
    size_t read = fossil_io_filesys_file_read(&file, buffer, 1, sizeof(buffer) - 1);

    printf("Read (%zu bytes): %s\n", read, buffer);

    fossil_io_filesys_file_close(&file);

    /* Get file metadata */
    fossil_io_filesys_obj_t info;
    if (fossil_io_filesys_stat(path, &info) == 0) {
        printf("File size: %zu bytes\n", info.size);
        printf("Type: %s\n", fossil_io_filesys_type_string(info.type));
    }

    /* Compute hash */
    unsigned char hash[32];
    if (fossil_io_filesys_file_hash(path, hash, sizeof(hash), "sha256") == 0) {
        printf("SHA256: ");
        for (size_t i = 0; i < sizeof(hash); i++) {
            printf("%02x", hash[i]);
        }
        printf("\n");
    }

    return 0;
}

SAMPLE CODE C++ #

#include "fossil/io/filesys.h"
#include <iostream>
#include <cstring>

using namespace fossil::io;

int main()
{
    Filesys fs;

    const std::string path = "example.txt";

    /* Check existence */
    if (fs.exists(path) == 1)
    {
        std::cout << "File already exists, removing...\n";
        fs.remove(path);
    }

    /* Open and write to file */
    fossil_io_filesys_file_t file{};
    if (fs.file_open(&file, path, "w") != 0)
    {
        std::cerr << "Failed to open file for writing\n";
        return 1;
    }

    std::string text = "Hello from Fossil C++ Filesystem API!\n";
    fs.file_write(&file, text.c_str(), 1, text.size());
    fs.file_flush(&file);
    fs.file_close(&file);

    /* Reopen and read */
    if (fs.file_open(&file, path, "r") != 0)
    {
        std::cerr << "Failed to open file for reading\n";
        return 1;
    }

    char buffer[128] = {0};
    size_t bytes = fs.file_read(&file, buffer, 1, sizeof(buffer) - 1);

    std::cout << "Read (" << bytes << " bytes): " << buffer << "\n";

    fs.file_close(&file);

    /* Get metadata */
    fossil_io_filesys_obj_t info{};
    if (fs.stat(path, &info) == 0)
    {
        std::cout << "Size: " << info.size << " bytes\n";
        std::cout << "Type: " << fs.type_string(info.type) << "\n";
    }

    /* Hash the file */
    unsigned char hash[32];
    if (fs.file_hash(path, hash, sizeof(hash), "sha256") == 0)
    {
        std::cout << "SHA256: ";
        for (unsigned char b : hash)
            printf("%02x", b);
        std::cout << "\n";
    }

    /* Path utilities */
    char abs_path[512];
    if (fs.abspath(path, abs_path, sizeof(abs_path)) == 0)
    {
        std::cout << "Absolute path: " << abs_path << "\n";
    }

    std::string normalized = fs.path_normalize("./example.txt");
    std::cout << "Normalized: " << normalized << "\n";

    return 0;
}

What are your feelings

Updated on March 24, 2026