From 18c7623a687ea3f264a0b66aeccb73fc1fb6f0c4 Mon Sep 17 00:00:00 2001 From: MechSlayer <0jcrespo1996@gmail.com> Date: Fri, 5 Sep 2025 19:45:10 +0200 Subject: [PATCH] feature: add file system API with open, read, write, and directory operations --- Source/DrangPlatform/Include/drang/fs.h | 661 ++++++++++++++++++++++++ 1 file changed, 661 insertions(+) create mode 100644 Source/DrangPlatform/Include/drang/fs.h diff --git a/Source/DrangPlatform/Include/drang/fs.h b/Source/DrangPlatform/Include/drang/fs.h new file mode 100644 index 0000000..b212903 --- /dev/null +++ b/Source/DrangPlatform/Include/drang/fs.h @@ -0,0 +1,661 @@ +#pragma once +#include "platform.h" +#include +#include + +DRANG_BEGIN_DECLS + +/** + * @brief File open mode flags for controlling file access behavior. + * + * These flags can be combined using bitwise OR operations to specify + * the desired file access mode and creation behavior. + * + * @remarks Flags can be combined, for example: + * drang_fs_mode_read | drang_fs_mode_write for read-write access. + * Some combinations may not be valid on all platforms. + */ +typedef enum { + drang_fs_mode_read = 0x01, /**< Open file for reading */ + drang_fs_mode_write = 0x02, /**< Open file for writing */ + drang_fs_mode_append = 0x04, /**< Open file for appending (writes go to end) */ + drang_fs_mode_create = 0x08, /**< Create file if it doesn't exist */ + drang_fs_mode_truncate = 0x10, /**< Truncate file to zero length if it exists */ + drang_fs_mode_exclusive = 0x20 /**< Fail if file already exists (used with create) */ +} drang_fs_mode_t; + +/** + * @brief Origin points for seek operations. + * + * Specifies the reference point for file positioning operations. + */ +typedef enum { + drang_seek_origin_begin = 0, /**< Seek from beginning of file */ + drang_seek_origin_current = 1, /**< Seek from current file position */ + drang_seek_origin_end = 2 /**< Seek from end of file */ +} drang_seek_origin_t; + +/** + * @brief File system entry type classification. + * + * Used to identify the type of file system objects when querying + * file information or reading directory entries. + */ +typedef enum { + drang_fs_type_unknown = 0, /**< Unknown or unsupported file type */ + drang_fs_type_file = 1, /**< Regular file */ + drang_fs_type_directory = 2, /**< Directory */ + drang_fs_type_symlink = 3 /**< Symbolic link */ +} drang_fs_type_t; + +/** + * @brief File system permissions type. + * + * Represents file permissions using Unix-style permission bits. + * Use the DRANG_FS_PERM_* constants to construct permission values. + */ +typedef uint32_t drang_fs_permissions_t; + +/** + * @brief File system timestamp type. + * + * Represents file timestamps as 64-bit values. The specific format + * and epoch may be platform-dependent. + */ +typedef uint64_t drang_fs_time_t; + +/** + * @name File Permission Constants + * @brief Unix-style file permission bit flags. + * + * These constants can be combined using bitwise OR to create + * permission specifications for files and directories. + * @{ + */ +#define DRANG_FS_PERM_USER_READ 0x0400 /**< User read permission */ +#define DRANG_FS_PERM_USER_WRITE 0x0200 /**< User write permission */ +#define DRANG_FS_PERM_USER_EXEC 0x0100 /**< User execute permission */ +#define DRANG_FS_PERM_GROUP_READ 0x0040 /**< Group read permission */ +#define DRANG_FS_PERM_GROUP_WRITE 0x0020 /**< Group write permission */ +#define DRANG_FS_PERM_GROUP_EXEC 0x0010 /**< Group execute permission */ +#define DRANG_FS_PERM_OTHER_READ 0x0004 /**< Other read permission */ +#define DRANG_FS_PERM_OTHER_WRITE 0x0002 /**< Other write permission */ +#define DRANG_FS_PERM_OTHER_EXEC 0x0001 /**< Other execute permission */ +/** @} */ + +/** + * @brief Opaque structure representing an open file handle. + * + * This structure contains platform-specific file handle information + * and should only be manipulated through the provided API functions. + */ +typedef struct drang_fs_file drang_fs_file_t; + +/** + * @brief Opaque structure representing an open directory handle. + * + * This structure contains platform-specific directory handle information + * and should only be manipulated through the provided API functions. + */ +typedef struct drang_fs_directory drang_fs_directory_t; + +/** + * @brief File system metadata structure. + * + * Contains comprehensive information about a file system entry + * including type, size, permissions, and timestamps. + */ +struct drang_fs_stat +{ + drang_fs_type_t type; /**< Type of file system entry */ + uint64_t size; /**< Size in bytes (0 for directories) */ + drang_fs_permissions_t permissions; /**< File permissions */ + drang_fs_time_t created_time; /**< File creation time */ + drang_fs_time_t modified_time; /**< Last modification time */ + drang_fs_time_t accessed_time; /**< Last access time */ +}; + +/** + * @brief Maximum size for relative path names in directory entries. + */ +#define DRANG_FS_MAX_REL_PATH_SIZE (512) + +/** + * @brief Directory entry structure. + * + * Represents a single entry within a directory, containing the + * entry name and type information. + */ +typedef struct fs_dir_entry +{ + char name[DRANG_FS_MAX_REL_PATH_SIZE]; /**< Entry name (null-terminated) */ + drang_fs_type_t type; /**< Entry type */ +} drang_fs_dir_entry_t; + +/** + * @brief Opens a file with specified access mode. + * + * Opens or creates a file at the specified path with the given access mode. + * The file handle must be closed with drang_fs_close() when no longer needed. + * + * @param[in] path Path to the file to open. + * @param[in] mode File access mode flags (combination of drang_fs_mode_t values). + * @param[out] out_file Pointer to store the opened file handle. + * @return 0 on success, negative error code on failure. + * @retval DRANG_EOK Success - file opened successfully. + * @retval DRANG_EINVAL Invalid parameter (path or out_file is NULL). + * @retval DRANG_ENOENT File not found and create flag not set. + * @retval DRANG_EPERM Permission denied. + * @retval DRANG_ENOMEM Insufficient memory to allocate file handle. + * @retval DRANG_EIO I/O error occurred during file opening. + * @retval DRANG_EBUSY File is locked or in use by another process. + * @remarks The returned file handle must be freed with drang_fs_close(). + * File path should use platform-appropriate path separators. + */ +DRANG_PLATFORM_API int drang_fs_open(const char *path, drang_fs_mode_t mode, drang_fs_file_t **out_file); + +/** + * @brief Closes an open file handle. + * + * Closes the file handle and releases associated resources. Any pending + * writes are flushed before closing. + * + * @param[in] file File handle to close, or NULL. + * @return 0 on success, negative error code on failure. + * @retval DRANG_EOK Success - file closed successfully. + * @retval DRANG_EINVAL Invalid file handle. + * @retval DRANG_EIO I/O error occurred during file closing. + * @remarks Safe to call with NULL pointer. File handle becomes invalid after closing. + * Pending writes are automatically flushed before closing. + */ +DRANG_PLATFORM_API int drang_fs_close(drang_fs_file_t *file); + +/** + * @brief Reads data from an open file. + * + * Reads up to count*size bytes from the file into the provided buffer. + * The actual number of bytes read is returned through bytes_read. + * + * @param[in] file Open file handle. + * @param[out] buffer Buffer to receive read data. + * @param[in] count Number of elements to read. + * @param[in] size Size of each element in bytes. + * @param[out] bytes_read Pointer to store actual bytes read (may be NULL). + * @return 0 on success, negative error code on failure. + * @retval DRANG_EOK Success - data read successfully. + * @retval DRANG_EINVAL Invalid parameter (file or buffer is NULL). + * @retval DRANG_EIO I/O error occurred during reading. + * @retval DRANG_EPERM File not opened for reading. + * @remarks Reading beyond end of file is not an error - bytes_read will indicate + * actual bytes read. File position advances by bytes read. + */ +DRANG_PLATFORM_API int drang_fs_read(drang_fs_file_t *file, void *buffer, size_t count, size_t size, size_t *bytes_read); + +/** + * @brief Writes data to an open file. + * + * Writes up to count*size bytes from the buffer to the file. + * The actual number of bytes written is returned through bytes_written. + * + * @param[in] file Open file handle. + * @param[in] buffer Buffer containing data to write. + * @param[in] count Number of elements to write. + * @param[in] size Size of each element in bytes. + * @param[out] bytes_written Pointer to store actual bytes written (may be NULL). + * @return 0 on success, negative error code on failure. + * @retval DRANG_EOK Success - data written successfully. + * @retval DRANG_EINVAL Invalid parameter (file or buffer is NULL). + * @retval DRANG_EIO I/O error occurred during writing. + * @retval DRANG_EPERM File not opened for writing. + * @retval DRANG_ENOMEM Insufficient disk space. + * @remarks Data may be buffered - use drang_fs_flush() to ensure data is written. + * File position advances by bytes written. + */ +DRANG_PLATFORM_API int drang_fs_write( + drang_fs_file_t *file, const void *buffer, size_t count, size_t size, size_t *bytes_written); + +/** + * @brief Sets the file position indicator. + * + * Changes the current file position to the specified offset relative + * to the given origin point. + * + * @param[in] file Open file handle. + * @param[in] offset Byte offset from the origin. + * @param[in] origin Reference point for the offset. + * @return 0 on success, negative error code on failure. + * @retval DRANG_EOK Success - file position set successfully. + * @retval DRANG_EINVAL Invalid parameter (file is NULL or invalid origin). + * @retval DRANG_EIO I/O error occurred during seek operation. + * @remarks Seeking beyond end of file is allowed and may extend the file on write. + * Use drang_fs_tell() to get the current position after seeking. + */ +DRANG_PLATFORM_API int drang_fs_seek(drang_fs_file_t *file, int64_t offset, drang_seek_origin_t origin); + +/** + * @brief Gets the current file position. + * + * Returns the current byte offset of the file position indicator + * from the beginning of the file. + * + * @param[in] file Open file handle. + * @param[out] position Pointer to store the current file position. + * @return 0 on success, negative error code on failure. + * @retval DRANG_EOK Success - position retrieved successfully. + * @retval DRANG_EINVAL Invalid parameter (file or position is NULL). + * @retval DRANG_EIO I/O error occurred during position query. + * @remarks Position is always relative to the beginning of the file. + */ +DRANG_PLATFORM_API int drang_fs_tell(drang_fs_file_t *file, uint64_t *position); + +/** + * @brief Flushes pending writes to storage. + * + * Forces any buffered write operations to be written to the underlying + * storage device. Does not guarantee data persistence across system crashes. + * + * @param[in] file Open file handle. + * @return 0 on success, negative error code on failure. + * @retval DRANG_EOK Success - data flushed successfully. + * @retval DRANG_EINVAL Invalid file handle. + * @retval DRANG_EIO I/O error occurred during flush operation. + * @remarks Does not guarantee data persistence - use platform-specific sync + * operations for crash-safe persistence. + */ +DRANG_PLATFORM_API int drang_fs_flush(drang_fs_file_t *file); + +/** + * @brief Truncates or extends a file to specified size. + * + * Changes the size of the file to the specified length. If the file + * is extended, the new area is filled with zeros. + * + * @param[in] file Open file handle. + * @param[in] size New file size in bytes. + * @return 0 on success, negative error code on failure. + * @retval DRANG_EOK Success - file size changed successfully. + * @retval DRANG_EINVAL Invalid file handle. + * @retval DRANG_EIO I/O error occurred during truncation. + * @retval DRANG_EPERM File not opened for writing. + * @retval DRANG_ENOMEM Insufficient disk space for extension. + * @remarks If extending the file, new bytes are zero-filled. + * File position may be adjusted if beyond new file size. + */ +DRANG_PLATFORM_API int drang_fs_truncate(drang_fs_file_t *file, uint64_t size); + +/** + * @brief Creates a new empty file. + * + * Creates a new file at the specified path with the given permissions. + * Fails if the file already exists. + * + * @param[in] path Path where the file should be created. + * @param[in] permissions Initial file permissions. + * @return 0 on success, negative error code on failure. + * @retval DRANG_EOK Success - file created successfully. + * @retval DRANG_EINVAL Invalid parameter (path is NULL). + * @retval DRANG_ENOENT Parent directory does not exist. + * @retval DRANG_EPERM Permission denied. + * @retval DRANG_EBUSY File already exists. + * @retval DRANG_ENOMEM Insufficient disk space. + * @retval DRANG_EIO I/O error occurred during creation. + * @remarks Use drang_fs_open() with create mode for more control over file creation. + * Created file is initially empty. + */ +DRANG_PLATFORM_API int drang_fs_create(const char *path, drang_fs_permissions_t permissions); + +/** + * @brief Removes a file from the file system. + * + * Deletes the specified file from the file system. The file must not + * be currently open by any process. + * + * @param[in] path Path to the file to remove. + * @return 0 on success, negative error code on failure. + * @retval DRANG_EOK Success - file removed successfully. + * @retval DRANG_EINVAL Invalid parameter (path is NULL). + * @retval DRANG_ENOENT File does not exist. + * @retval DRANG_EPERM Permission denied or file is in use. + * @retval DRANG_EIO I/O error occurred during removal. + * @remarks File must not be open by any process. Use drang_fs_rmdir() for directories. + * This operation is typically not reversible. + */ +DRANG_PLATFORM_API int drang_fs_remove(const char *path); + +/** + * @brief Moves or renames a file. + * + * Moves a file from the old path to the new path. This can be used + * for renaming or moving files within or across directories. + * + * @param[in] old_path Current path of the file. + * @param[in] new_path Destination path for the file. + * @return 0 on success, negative error code on failure. + * @retval DRANG_EOK Success - file moved successfully. + * @retval DRANG_EINVAL Invalid parameter (old_path or new_path is NULL). + * @retval DRANG_ENOENT Source file does not exist. + * @retval DRANG_EPERM Permission denied. + * @retval DRANG_EBUSY File is in use or destination exists. + * @retval DRANG_EIO I/O error occurred during move operation. + * @remarks Moving across different file systems may require copy+delete. + * Destination file is overwritten if it exists and permissions allow. + */ +DRANG_PLATFORM_API int drang_fs_move(const char *old_path, const char *new_path); + +/** + * @brief Checks if a file or directory exists. + * + * Tests whether a file system entry exists at the specified path. + * This function does not distinguish between files and directories. + * + * @param[in] path Path to check for existence. + * @return true if the path exists, false otherwise. + * @remarks This function does not set error codes. Use drang_fs_stat() + * for more detailed information about file system entries. + * Result may be outdated immediately after return due to concurrent access. + */ +DRANG_PLATFORM_API bool drang_fs_exists(const char *path); + +/** + * @brief Retrieves file system metadata. + * + * Gets comprehensive information about a file system entry including + * type, size, permissions, and timestamps. + * + * @param[in] path Path to the file system entry. + * @param[out] out_stat Pointer to structure to receive file information. + * @return 0 on success, negative error code on failure. + * @retval DRANG_EOK Success - file information retrieved successfully. + * @retval DRANG_EINVAL Invalid parameter (path or out_stat is NULL). + * @retval DRANG_ENOENT File does not exist. + * @retval DRANG_EPERM Permission denied. + * @retval DRANG_EIO I/O error occurred during stat operation. + * @remarks Provides more detailed information than drang_fs_exists(). + * Timestamp format and precision may be platform-dependent. + */ +DRANG_PLATFORM_API int drang_fs_stat(const char *path, struct drang_fs_stat *out_stat); + +/** + * @brief Sets file permissions. + * + * Changes the access permissions of the specified file or directory. + * The exact behavior may vary between platforms. + * + * @param[in] path Path to the file or directory. + * @param[in] permissions New permission flags. + * @return 0 on success, negative error code on failure. + * @retval DRANG_EOK Success - permissions set successfully. + * @retval DRANG_EINVAL Invalid parameter (path is NULL). + * @retval DRANG_ENOENT File does not exist. + * @retval DRANG_EPERM Permission denied. + * @retval DRANG_NOTSUP Operation not supported on this platform. + * @retval DRANG_EIO I/O error occurred during permission change. + * @remarks Permission semantics may vary between platforms (Unix vs Windows). + * Some permission combinations may not be supported on all platforms. + */ +DRANG_PLATFORM_API int drang_fs_set_permissions(const char *path, drang_fs_permissions_t permissions); + +/** + * @brief Creates a new directory. + * + * Creates a directory at the specified path with the given permissions. + * Parent directories must already exist. + * + * @param[in] path Path where the directory should be created. + * @param[in] permissions Initial directory permissions. + * @return 0 on success, negative error code on failure. + * @retval DRANG_EOK Success - directory created successfully. + * @retval DRANG_EINVAL Invalid parameter (path is NULL). + * @retval DRANG_ENOENT Parent directory does not exist. + * @retval DRANG_EPERM Permission denied. + * @retval DRANG_EBUSY Directory already exists. + * @retval DRANG_ENOMEM Insufficient disk space. + * @retval DRANG_EIO I/O error occurred during creation. + * @remarks Parent directories must exist. Use recursive creation for nested paths. + * Directory permissions may be modified by system umask. + */ +DRANG_PLATFORM_API int drang_fs_mkdir(const char *path, drang_fs_permissions_t permissions); + +/** + * @brief Removes an empty directory. + * + * Deletes the specified directory from the file system. The directory + * must be empty (contain no files or subdirectories). + * + * @param[in] path Path to the directory to remove. + * @return 0 on success, negative error code on failure. + * @retval DRANG_EOK Success - directory removed successfully. + * @retval DRANG_EINVAL Invalid parameter (path is NULL). + * @retval DRANG_ENOENT Directory does not exist. + * @retval DRANG_EPERM Permission denied. + * @retval DRANG_EBUSY Directory is not empty or in use. + * @retval DRANG_EIO I/O error occurred during removal. + * @remarks Directory must be empty before removal. Use drang_fs_remove() for files. + */ +DRANG_PLATFORM_API int drang_fs_rmdir(const char *path); + +/** + * @brief Opens a directory for reading. + * + * Opens the specified directory for reading its entries. The directory + * handle must be closed with drang_fs_closedir() when no longer needed. + * + * @param[in] path Path to the directory to open. + * @param[out] out_directory Pointer to store the opened directory handle. + * @return 0 on success, negative error code on failure. + * @retval DRANG_EOK Success - directory opened successfully. + * @retval DRANG_EINVAL Invalid parameter (path or out_directory is NULL). + * @retval DRANG_ENOENT Directory does not exist. + * @retval DRANG_EPERM Permission denied. + * @retval DRANG_ENOMEM Insufficient memory to allocate directory handle. + * @retval DRANG_EIO I/O error occurred during directory opening. + * @remarks The returned directory handle must be freed with drang_fs_closedir(). + * Use drang_fs_readdir() to iterate through directory entries. + */ +DRANG_PLATFORM_API int drang_fs_opendir(const char *path, drang_fs_directory_t **out_directory); + +/** + * @brief Reads the next directory entry. + * + * Reads the next entry from an open directory. Returns information + * about the entry including name and type. + * + * @param[in] directory Open directory handle. + * @param[out] out_entry Pointer to structure to receive entry information. + * @return 0 on success, negative error code on failure or end of directory. + * @retval DRANG_EOK Success - entry read successfully. + * @retval DRANG_EINVAL Invalid parameter (directory or out_entry is NULL). + * @retval DRANG_ENOENT No more entries (end of directory reached). + * @retval DRANG_EIO I/O error occurred during reading. + * @remarks Returns DRANG_ENOENT when no more entries are available. + * Entry names "." and ".." will not be included. + */ +DRANG_PLATFORM_API int drang_fs_readdir(drang_fs_directory_t *directory, drang_fs_dir_entry_t *out_entry); + +/** + * @brief Closes an open directory handle. + * + * Closes the directory handle and releases associated resources. + * + * @param[in] directory Directory handle to close, or NULL. + * @return 0 on success, negative error code on failure. + * @retval DRANG_EOK Success - directory closed successfully. + * @retval DRANG_EINVAL Invalid directory handle. + * @retval DRANG_EIO I/O error occurred during directory closing. + * @remarks Safe to call with NULL pointer. Directory handle becomes invalid after closing. + */ +DRANG_PLATFORM_API int drang_fs_closedir(drang_fs_directory_t *directory); + +/** + * @brief Gets the current working directory. + * + * Retrieves the absolute path of the current working directory. + * The path is null-terminated and stored in the provided buffer. + * + * @param[out] buffer Buffer to receive the current working directory path. + * @param[in] size Size of the buffer in bytes. + * @param[out] out_size Pointer to store the actual path length (may be NULL). + * @return 0 on success, negative error code on failure. + * @retval DRANG_EOK Success - current directory retrieved successfully. + * @retval DRANG_EINVAL Invalid parameter (buffer is NULL or size is 0). + * @retval DRANG_ENOMEM Buffer too small to hold the path. + * @retval DRANG_EIO I/O error occurred during path retrieval. + * @remarks If out_size is provided, it contains the required buffer size on DRANG_ENOMEM. + * The returned path is always absolute and null-terminated. + */ +DRANG_PLATFORM_API int drang_fs_get_cwd(char *buffer, size_t size, size_t *out_size); + +/** + * @brief Sets the current working directory. + * + * Changes the current working directory to the specified path. + * Affects the process-wide current directory. + * + * @param[in] path Path to the new working directory. + * @return 0 on success, negative error code on failure. + * @retval DRANG_EOK Success - working directory changed successfully. + * @retval DRANG_EINVAL Invalid parameter (path is NULL). + * @retval DRANG_ENOENT Directory does not exist. + * @retval DRANG_EPERM Permission denied. + * @retval DRANG_EIO I/O error occurred during directory change. + * @remarks Changes the process-wide current working directory. + * Relative paths in subsequent operations will be relative to this directory. + */ +DRANG_PLATFORM_API int drang_fs_set_cwd(const char *path); + +/** + * @brief Joins two path components. + * + * Combines two path components into a single path using the appropriate + * platform-specific path separator. + * + * @param[out] result Buffer to store the joined path. + * @param[in] result_size Size of the result buffer in bytes. + * @param[in] path1 First path component. + * @param[in] path2 Second path component. + * @param[out] out_size Pointer to store the actual result length (may be NULL). + * @return 0 on success, negative error code on failure. + * @retval DRANG_EOK Success - paths joined successfully. + * @retval DRANG_EINVAL Invalid parameter (result, path1, or path2 is NULL). + * @retval DRANG_ENOMEM Result buffer too small. + * @remarks Uses platform-appropriate path separators. If out_size is provided, + * it contains the required buffer size on DRANG_ENOMEM. + * The result is always null-terminated. + */ +DRANG_PLATFORM_API int drang_fs_path_join( + char *result, size_t result_size, const char *path1, const char *path2, size_t *out_size); + +/** + * @brief Joins two path components with explicit lengths. + * + * Combines two path components into a single path using the appropriate + * platform-specific path separator. Path components are specified with + * explicit lengths rather than being null-terminated. + * + * @param[out] result Buffer to store the joined path. + * @param[in] result_size Size of the result buffer in bytes. + * @param[in] path1 First path component. + * @param[in] path1_length Length of the first path component. + * @param[in] path2 Second path component. + * @param[in] path2_length Length of the second path component. + * @param[out] out_size Pointer to store the actual result length (may be NULL). + * @return 0 on success, negative error code on failure. + * @retval DRANG_EOK Success - paths joined successfully. + * @retval DRANG_EINVAL Invalid parameter (result, path1, or path2 is NULL). + * @retval DRANG_ENOMEM Result buffer too small. + * @remarks Useful when working with non-null-terminated path strings. + * The result is always null-terminated. + */ +DRANG_PLATFORM_API int drang_fs_path_join_length(char *result, + size_t result_size, + const char *path1, + size_t path1_length, + const char *path2, + size_t path2_length, + size_t *out_size); + +/** + * @brief Normalizes a file path. + * + * Resolves relative path components (. and ..) and removes redundant + * separators to produce a canonical path representation. + * + * @param[out] result Buffer to store the normalized path. + * @param[in] result_size Size of the result buffer in bytes. + * @param[in] path Path to normalize. + * @param[out] out_size Pointer to store the actual result length (may be NULL). + * @return 0 on success, negative error code on failure. + * @retval DRANG_EOK Success - path normalized successfully. + * @retval DRANG_EINVAL Invalid parameter (result or path is NULL). + * @retval DRANG_ENOMEM Result buffer too small. + * @remarks Resolves . and .. components and removes duplicate separators. + * Does not resolve symbolic links. The result is always null-terminated. + */ +DRANG_PLATFORM_API int drang_fs_path_normalize(char *result, size_t result_size, const char *path, size_t *out_size); + +/** + * @brief Normalizes a file path with explicit length. + * + * Resolves relative path components (. and ..) and removes redundant + * separators to produce a canonical path representation. The input path + * is specified with an explicit length rather than being null-terminated. + * + * @param[out] result Buffer to store the normalized path. + * @param[in] result_size Size of the result buffer in bytes. + * @param[in] path Path to normalize. + * @param[in] path_length Length of the input path. + * @param[out] out_size Pointer to store the actual result length (may be NULL). + * @return 0 on success, negative error code on failure. + * @retval DRANG_EOK Success - path normalized successfully. + * @retval DRANG_EINVAL Invalid parameter (result or path is NULL). + * @retval DRANG_ENOMEM Result buffer too small. + * @remarks Useful when working with non-null-terminated path strings. + * The result is always null-terminated. + */ +DRANG_PLATFORM_API int drang_fs_path_normalize_length( + char *result, size_t result_size, const char *path, size_t path_length, size_t *out_size); + +/** + * @brief Converts a path to absolute form. + * + * Converts a relative or absolute path to a fully qualified absolute path. + * Relative paths are resolved against the current working directory. + * + * @param[out] result Buffer to store the absolute path. + * @param[in] result_size Size of the result buffer in bytes. + * @param[in] path Path to convert to absolute form. + * @param[out] out_size Pointer to store the actual result length (may be NULL). + * @return 0 on success, negative error code on failure. + * @retval DRANG_EOK Success - absolute path created successfully. + * @retval DRANG_EINVAL Invalid parameter (result or path is NULL). + * @retval DRANG_ENOMEM Result buffer too small. + * @retval DRANG_EIO I/O error occurred while resolving current directory. + * @remarks Relative paths are resolved against the current working directory. + * The result is always null-terminated and fully qualified. + */ +DRANG_PLATFORM_API int drang_fs_path_absolute(char *result, size_t result_size, const char *path, size_t *out_size); + +/** + * @brief Converts a path to absolute form with explicit length. + * + * Converts a relative or absolute path to a fully qualified absolute path. + * The input path is specified with an explicit length rather than being + * null-terminated. Relative paths are resolved against the current working directory. + * + * @param[out] result Buffer to store the absolute path. + * @param[in] result_size Size of the result buffer in bytes. + * @param[in] path Path to convert to absolute form. + * @param[in] path_length Length of the input path. + * @param[out] out_size Pointer to store the actual result length (may be NULL). + * @return 0 on success, negative error code on failure. + * @retval DRANG_EOK Success - absolute path created successfully. + * @retval DRANG_EINVAL Invalid parameter (result or path is NULL). + * @retval DRANG_ENOMEM Result buffer too small. + * @retval DRANG_EIO I/O error occurred while resolving current directory. + * @remarks Useful when working with non-null-terminated path strings. + * The result is always null-terminated and fully qualified. + */ +DRANG_PLATFORM_API int drang_fs_path_absolute_length( + char *result, size_t result_size, const char *path, size_t path_length, size_t *out_size); + +DRANG_END_DECLS