ascii-chat 0.11.33
Video chat in your terminal
Loading...
Searching...
No Matches
Platform Abstractions

Cross-platform abstractions for threading, sockets, system calls, and hardware access. More...

Files

file  cond.c
 Platform-agnostic condition variable hooks and utilities.
 
file  filesystem.c
 Linux filesystem operations.
 
file  font.c
 Font resolution for Linux: fontconfig name โ†’ absolute .ttf path.
 
file  keepawake.c
 Linux system sleep prevention using systemd-inhibit.
 
file  system.c
 ๐Ÿง Linux system utilities and backtrace symbol resolution
 
file  filesystem.c
 macOS filesystem operations
 
file  font.c
 Font resolution for macOS: CoreText family-name passthrough + bundled fallback.
 
file  keepawake.c
 macOS system sleep prevention using IOKit power assertions
 
file  system.c
 ๐ŸŽ macOS system utilities and backtrace symbol resolution
 
file  mutex.c
 Platform-agnostic mutex hooks and utilities.
 
file  rwlock.c
 Platform-agnostic read-write lock hooks and utilities.
 
file  socket.c
 ๐ŸŒ Common socket utility functions (cross-platform implementations)
 
file  symbols.c
 ๐Ÿ” Symbol resolution cache: llvm-symbolizer/addr2line wrapper with hashtable-backed caching
 
file  system.c
 ๐Ÿ”ง Shared cross-platform system utilities (safe string functions, etc.)
 
file  terminal.c
 Cross-platform unified color detection and terminal capabilities.
 
file  thread.c
 ๐Ÿงต Thread utilities and helpers
 
file  init.c
 Platform initialization for WASM/Emscripten.
 
file  string.c
 String utility functions for WASM/Emscripten.
 
file  actions.c
 Action function stubs for WASM (not needed for mirror mode)
 
file  audio.c
 Audio stubs for WASM (audio not supported in browser environment)
 
file  audio.h
 Stub audio types for WASM builds (uses Web Audio API instead of PortAudio)
 
file  comprehensive.c
 Comprehensive stubs for WASM build - network, encoding, analysis.
 
file  filesystem.c
 File operation stubs for WASM.
 
file  log.c
 Logging utility stubs for WASM.
 
file  manpage.c
 Manpage resource stubs for WASM (not needed for mirror mode)
 
file  misc.c
 Miscellaneous stubs for WASM.
 
file  mode_defaults.c
 Mode-specific default stubs for WASM.
 
file  network.c
 Network function stubs for WASM.
 
file  portaudio.h
 Stub PortAudio header for WASM builds.
 
file  terminal.c
 Terminal function stubs for WASM (functions not needed for mirror mode)
 
file  threading.c
 Threading stubs for WASM.
 
file  util.c
 Utility stubs for WASM/Emscripten (backtrace, localtime)
 
file  video.c
 Video utility stubs for WASM.
 
file  system.c
 System utilities for WASM/Emscripten - includes generic implementations.
 
file  terminal.c
 Terminal abstraction for WASM/Emscripten via EM_JS bridge web.
 
file  threading.c
 Threading abstraction for WASM/Emscripten using pthreads.
 
file  time.c
 Time functions for WASM/Emscripten.
 
file  abstraction.h
 Platform abstraction layer umbrella header providing unified cross-platform API.
 
file  agent.h
 Cross-platform SSH/GPG agent socket discovery.
 
file  api.h
 DLL export/import macros for cross-platform symbol visibility.
 
file  backtrace.h
 Cross-platform backtrace and stack trace capture.
 
file  cond.h
 Cross-platform condition variable interface for ascii-chat.
 
file  errno.h
 Cross-platform error handling utilities.
 
file  filesystem.h
 Cross-platform filesystem operations.
 
file  font.h
 Platform-specific font resolution for render-file output.
 
file  init.h
 Platform initialization and static synchronization helpers.
 
file  internal.h
 Private implementation helpers for platform abstraction layer.
 
file  keyboard.h
 ๐ŸŽฎ Cross-platform keyboard input interface for ascii-chat
 
file  memory.h
 Cross-platform memory allocation utilities.
 
file  mmap.h
 Cross-platform memory-mapped file interface.
 
file  mutex.h
 Cross-platform mutex interface for ascii-chat.
 
file  network.h
 Cross-platform network headers and socket definitions.
 
file  pipe.h
 Cross-platform pipe/agent socket interface for ascii-chat.
 
file  process.h
 Cross-platform process types and execution utilities.
 
file  question.h
 Cross-platform interactive prompting utilities.
 
file  rwlock.h
 Cross-platform read-write lock interface for ascii-chat.
 
file  signal.h
 Cross-platform crash signal and exception handling.
 
file  socket.h
 Cross-platform socket interface for ascii-chat.
 
file  stat.h
 Cross-platform stat and file type checking macros.
 
file  string.h
 Cross-platform string operations.
 
file  symbols.h
 Symbol Resolution Cache for Backtrace Addresses.
 
file  thread.h
 ๐Ÿงต Cross-platform thread interface for ascii-chat
 
file  wasm_console.h
 WASM browser console logging API.
 
file  windows_compat.h
 Wrapper for windows.h with C23 alignment compatibility.
 
file  windows_errno.h
 Windows errno compatibility definitions.
 

Data Structures

struct  symbol_entry_t
 Symbol cache entry structure for address-to-symbol mapping. More...
 
struct  bin_cache_entry_t
 Cache entry for binary PATH lookup. More...
 
struct  cond_t
 Condition variable type (POSIX: pthread_cond_t with debug tracking) More...
 
struct  platform_stat_t
 File type information from stat() More...
 
struct  platform_dir_entry_t
 Directory entry information for platform_dir_foreach() More...
 
struct  config_file_result_t
 Result of a config file search. More...
 
struct  config_file_list_t
 List of config file search results. More...
 
struct  static_mutex_t
 Static mutex structure for global mutexes requiring static initialization. More...
 
struct  static_rwlock_t
 Static reader-writer lock structure for global rwlocks requiring static initialization. More...
 
struct  static_cond_t
 Static condition variable structure for global condition variables requiring static initialization. More...
 
struct  keyboard_line_edit_opts_t
 Options for interactive line editing. More...
 
struct  platform_mmap
 Memory-mapped file handle. More...
 
struct  mutex_t
 Mutex type (POSIX: pthread_mutex_t with debug tracking) More...
 
struct  prompt_opts_t
 Options for text prompts. More...
 
struct  rwlock_t
 Read-write lock type (POSIX: pthread_rwlock_t with debug tracking) More...
 
struct  platform_signal_handler_t
 Signal handler descriptor for bulk registration. More...
 
struct  platform_stderr_redirect_handle_t
 Handle for temporary stderr redirection. More...
 
struct  platform_binary_match_t
 Find which binary(ies) contain a given address. More...
 
struct  terminal_size_t
 Terminal size structure. More...
 
struct  asciichat_thread_wrapper_t
 Internal thread wrapper for automatic cleanup. More...
 

Macros

#define VALIDATE_AGENT_PATH(path, path_out, path_size, context)
 Validate and copy agent socket path.
 
#define ASCIICHAT_API   __attribute__((visibility("default")))
 Export symbols on Unix platforms (Linux, macOS)
 
#define LOG_COND_STATE(cond)   cond_log_state(cond, __FILE__, __LINE__, __func__)
 
#define PLATFORM_O_RDONLY   O_RDONLY
 
#define PLATFORM_O_WRONLY   O_WRONLY
 
#define PLATFORM_O_RDWR   O_RDWR
 
#define PLATFORM_O_CREAT   O_CREAT
 
#define PLATFORM_O_EXCL   O_EXCL
 
#define PLATFORM_O_TRUNC   O_TRUNC
 
#define PLATFORM_O_APPEND   O_APPEND
 
#define PLATFORM_O_BINARY   0
 
#define PATH_DELIM   '/'
 Platform-specific path separator character.
 
#define PATH_SEPARATOR_STR   "/"
 
#define PATH_ENV_SEPARATOR   ":"
 Platform-specific PATH environment variable separator.
 
#define PLATFORM_MAX_PATH_LENGTH   4096
 Maximum path length supported by the operating system.
 
#define PLATFORM_MAX_ENV_VALUE_LENGTH   32768
 Maximum environment variable value length.
 
#define FILE_PERM_PRIVATE   0600
 File permission: Private (owner read/write only)
 
#define DIR_PERM_PRIVATE   0700
 Directory permission: Private (owner read/write/execute only)
 
#define FILE_PERM_PUBLIC_READ   0644
 File permission: Public read, owner write.
 
#define FILE_PERM_MASK   0777
 Permission mask for all permissions.
 
#define PLATFORM_ACCESS_EXISTS   0
 Access modes for platform_access()
 
#define PLATFORM_ACCESS_WRITE   2
 Check if file/directory is writable.
 
#define PLATFORM_ACCESS_READ   4
 Check if file/directory is readable.
 
#define STATIC_MUTEX_INIT   {.mutex.impl = PTHREAD_MUTEX_INITIALIZER, .mutex.name = NULL, .initialized = 1}
 
#define STATIC_RWLOCK_INIT   {.lock.impl = PTHREAD_RWLOCK_INITIALIZER, .lock.name = NULL, .initialized = 1}
 
#define STATIC_COND_INIT   {.cond.impl = PTHREAD_COND_INITIALIZER, .cond.name = NULL, .initialized = 1}
 
#define platform_alloca(size)   alloca(size)
 Platform-specific stack allocation (POSIX)
 
#define LOG_MUTEX_STATE(mutex)   mutex_log_state(mutex, __FILE__, __LINE__, __func__)
 
#define ntohll(x)   htonll(x)
 
#define INVALID_PIPE_VALUE   (-1)
 Invalid pipe value (POSIX: -1)
 
#define PROMPT_OPTS_DEFAULT   (prompt_opts_t){.echo = true, .same_line = false, .mask_char = 0}
 Default prompt options (echo enabled, answer on next line)
 
#define PROMPT_OPTS_PASSWORD   (prompt_opts_t){.echo = false, .same_line = true, .mask_char = '*'}
 Prompt options for password input (no echo, asterisk masking, same line)
 
#define PROMPT_OPTS_INLINE   (prompt_opts_t){.echo = true, .same_line = true, .mask_char = 0}
 Prompt options for inline text input (echo enabled, same line)
 
#define LOG_RWLOCK_STATE(rwlock)   rwlock_log_state(rwlock, __FILE__, __LINE__, __func__)
 
#define INVALID_SOCKET_VALUE   (-1)
 Invalid socket value (POSIX: -1)
 
#define SOCKET_ERROR_WOULDBLOCK   EWOULDBLOCK
 
#define SOCKET_ERROR_INPROGRESS   EINPROGRESS
 
#define SOCKET_ERROR_AGAIN   EAGAIN
 
#define SAFE_IGNORE_PRINTF_RESULT(expr)   ((void)(expr))
 
#define TERMINAL_COLOR_THEME_LIGHT_FG_R   65
 Platform-specific getopt include.
 
#define TERMINAL_COLOR_THEME_LIGHT_FG_G   61
 
#define TERMINAL_COLOR_THEME_LIGHT_FG_B   61
 
#define TERMINAL_COLOR_THEME_DARK_FG_R   204
 Default text color for dark theme (RGB)
 
#define TERMINAL_COLOR_THEME_DARK_FG_G   204
 
#define TERMINAL_COLOR_THEME_DARK_FG_B   204
 
#define TERMINAL_COLOR_THEME_LIGHT_BG_R   255
 Default background color for light theme (RGB)
 
#define TERMINAL_COLOR_THEME_LIGHT_BG_G   255
 
#define TERMINAL_COLOR_THEME_LIGHT_BG_B   255
 
#define TERMINAL_COLOR_THEME_DARK_BG_R   0
 Default background color for dark theme (RGB)
 
#define TERMINAL_COLOR_THEME_DARK_BG_G   0
 
#define TERMINAL_COLOR_THEME_DARK_BG_B   0
 
#define PLATFORM_THREAD_LOCAL   __thread
 Platform-specific thread-local storage declaration.
 
#define EINVAL   22
 
#define ERANGE   34
 
#define ETIMEDOUT   110
 
#define EINTR   4
 
#define EBADF   9
 
#define EAGAIN   11
 
#define EWOULDBLOCK   11
 
#define EPIPE   32
 
#define ECONNREFUSED   111
 
#define ENETUNREACH   101
 
#define EHOSTUNREACH   113
 
#define ECONNRESET   104
 
#define ENOTSOCK   88
 

Typedefs

typedef bool(* platform_dir_foreach_cb) (const platform_dir_entry_t *entry, void *user_data)
 Callback for platform_dir_foreach()
 
typedef struct platform_mmap platform_mmap_t
 Memory-mapped file handle.
 
typedef int pipe_t
 Pipe handle type (POSIX: int file descriptor)
 
typedef struct platform_process platform_process_t
 Opaque process handle.
 
typedef void(* platform_crash_handler_t) (int signal, void *context)
 Callback function type for crash handlers.
 
typedef int socket_t
 Socket handle type (POSIX: int)
 
typedef void(* signal_handler_t) (int)
 Signal handler function type.
 
typedef pthread_t asciichat_thread_t
 Thread handle type (POSIX: pthread_t)
 
typedef pthread_t thread_id_t
 Thread ID type (POSIX: pthread_t)
 
typedef pthread_key_t tls_key_t
 Thread-local storage key type (POSIX: pthread_key_t)
 

Enumerations

enum  keyboard_key_t {
  KEY_NONE = 0 , KEY_ESCAPE = 27 , KEY_SPACE = 32 , KEY_UP = 256 ,
  KEY_DOWN = 257 , KEY_LEFT = 258 , KEY_RIGHT = 259 , KEY_DELETE = 260 ,
  KEY_HOME = 261 , KEY_END = 262 , KEY_CTRL_DELETE = 263 , KEY_MINUS = '-' ,
  KEY_0 = '0' , KEY_C = 'c' , KEY_R = 'r' , KEY_M = 'm' ,
  KEY_F = 'f' , KEY_QUESTION = '?' , KEY_BACKTICK = '`'
}
 Unified keyboard key code enumeration. More...
 
enum  keyboard_line_edit_result_t { LINE_EDIT_CONTINUE , LINE_EDIT_ACCEPTED , LINE_EDIT_CANCELLED , LINE_EDIT_NO_INPUT }
 Result codes for interactive line editing. More...
 

Functions

void cond_on_wait (cond_t *cond, mutex_t *mutex, const char *file, int line, const char *func)
 Hook called when a thread waits on a condition variable.
 
void cond_on_signal (cond_t *cond)
 Hook called when a condition variable is signaled.
 
void cond_on_broadcast (cond_t *cond)
 Hook called when a condition variable is broadcast.
 
int cond_format_state (const cond_t *cond, char *buffer, size_t size)
 Format condition variable timing and state info into buffer.
 
void cond_log_state (const cond_t *cond, const char *file, int line, const char *func)
 Log the state of a condition variable.
 
void mutex_on_lock (mutex_t *mutex)
 Hook called when a mutex is successfully locked.
 
void mutex_on_unlock (mutex_t *mutex)
 Hook called when a mutex is unlocked.
 
void mutex_on_trylock (mutex_t *mutex, bool success)
 Hook called when a mutex trylock is attempted.
 
int mutex_format_state (const mutex_t *mutex, char *buffer, size_t size)
 Format mutex timing and state info into buffer.
 
void mutex_log_state (const mutex_t *mutex, const char *file, int line, const char *func)
 Log the state of a mutex.
 
void rwlock_on_rdlock (rwlock_t *rwlock)
 Hook called when a read lock is successfully acquired.
 
void rwlock_on_wrlock (rwlock_t *rwlock)
 Hook called when a write lock is successfully acquired.
 
void rwlock_on_unlock (rwlock_t *rwlock)
 Hook called when an rwlock is unlocked (read or write)
 
int rwlock_format_state (const rwlock_t *rwlock, char *buffer, size_t size)
 Format rwlock timing and state info into buffer.
 
void rwlock_log_state (const rwlock_t *rwlock, const char *file, int line, const char *func)
 Log the state of a read-write lock.
 
void socket_optimize_for_streaming (socket_t sock)
 Optimize socket for high-throughput video streaming.
 
int platform_get_ssh_agent_socket (char *path_out, size_t path_size)
 
int platform_get_gpg_agent_socket (char *path_out, size_t path_size)
 
int platform_backtrace (void **buffer, int size)
 Get a backtrace of the current call stack.
 
char ** platform_backtrace_symbols (void *const *buffer, int size)
 Convert backtrace addresses to symbol names.
 
void platform_backtrace_symbols_destroy (char **strings)
 Free symbol array from platform_backtrace_symbols()
 
void backtrace_print_simple (int skip_frames)
 Print backtrace of the current call stack.
 
int cond_init (cond_t *cond, const char *name)
 Initialize a condition variable with a name.
 
int cond_destroy (cond_t *cond)
 Destroy a condition variable.
 
int cond_wait_impl (cond_t *cond, mutex_t *mutex)
 Wait on a condition variable (blocking) - implementation function.
 
int cond_timedwait_impl (cond_t *cond, mutex_t *mutex, uint64_t timeout_ns)
 Wait on a condition variable with timeout - implementation function.
 
int cond_signal (cond_t *cond)
 Signal a condition variable (wake one waiting thread)
 
int cond_broadcast (cond_t *cond)
 Broadcast to a condition variable (wake all waiting threads)
 
int debug_sync_cond_wait (cond_t *cond, mutex_t *mutex, const char *file, int line, const char *func)
 
int debug_sync_cond_timedwait (cond_t *cond, mutex_t *mutex, uint64_t timeout_ns, const char *file, int line, const char *func)
 
bool debug_sync_is_initialized (void)
 
void platform_clear_error_state (void)
 
asciichat_error_t platform_stat (const char *path, platform_stat_t *stat_out)
 Get file statistics.
 
int platform_is_regular_file (const char *path)
 Check if a path is a regular file.
 
int platform_is_directory (const char *path)
 Check if a path is a directory.
 
asciichat_error_t platform_mkdir (const char *path, int mode)
 Create a directory.
 
asciichat_error_t platform_mkdir_recursive (const char *path, int mode)
 Create directories recursively (mkdir -p equivalent)
 
asciichat_error_t platform_mkdtemp (char *path_out, size_t path_size, const char *prefix)
 Create a temporary directory with a given prefix.
 
asciichat_error_t platform_rmdir_recursive (const char *path)
 Recursively delete a directory and all its contents.
 
asciichat_error_t platform_dir_foreach (const char *path, platform_dir_foreach_cb callback, void *user_data)
 Iterate over entries in a directory.
 
int platform_create_temp_file (char *path_out, size_t path_size, const char *prefix, int *fd)
 Create a temporary file with a given prefix.
 
int platform_delete_temp_file (const char *path)
 Delete a temporary file.
 
asciichat_error_t platform_temp_file_open (const char *name, const char *path, int *fd_out)
 Open a temporary file for writing.
 
asciichat_error_t platform_truncate_file (const char *path, size_t size)
 Truncate a file to a specific size.
 
bool platform_path_is_absolute (const char *path)
 Check if a path is absolute (not relative)
 
char platform_path_get_separator (void)
 Get the path separator character for current platform.
 
asciichat_error_t platform_path_normalize (const char *input, char *output, size_t output_size)
 Normalize and validate a file path.
 
asciichat_error_t platform_validate_key_file_permissions (const char *key_path)
 Validate that a cryptographic key file has appropriate permissions.
 
asciichat_error_t platform_find_config_file (const char *filename, config_file_list_t *list_out)
 Find config file across multiple standard locations.
 
void config_file_list_destroy (config_file_list_t *list)
 Free config file list resources.
 
const char * platform_get_home_dir (void)
 Get the user's home directory.
 
char * platform_get_config_dir (void)
 Get the application configuration directory.
 
char * platform_get_data_dir (void)
 Get the application data directory.
 
int platform_open (const char *name, const char *pathname, int flags,...)
 Safe file open (open replacement)
 
FILE * platform_fopen (const char *name, const char *filename, const char *mode)
 Safe file open stream (fopen replacement)
 
FILE * platform_tmpfile (void)
 Create a temporary file (tmpfile replacement)
 
FILE * platform_fdopen (const char *name, int fd, const char *mode)
 Convert file descriptor to stream (fdopen replacement)
 
ssize_t platform_read (int fd, void *buf, size_t count)
 Safe file read (read replacement)
 
ssize_t platform_write (int fd, const void *buf, size_t count)
 Safe file write (write replacement)
 
int platform_close (int fd)
 Safe file close (close replacement)
 
int platform_unlink (const char *pathname)
 Delete/unlink file.
 
int platform_chmod (const char *pathname, int mode)
 Change file permissions/mode.
 
const char * platform_path_skip_absolute_prefix (const char *path)
 Skip absolute path prefix (drive letter on Windows)
 
void platform_normalize_path_separators (char *path)
 Normalize path separators for the current platform.
 
int platform_path_strcasecmp (const char *a, const char *b, size_t n)
 Platform-aware path string comparison.
 
const char * file_read_error_message (const char *path)
 Get human-readable error message for file read failure.
 
const char * file_write_error_message (const char *path)
 Get human-readable error message for file write failure.
 
bool file_is_readable (const char *path)
 Check if file is readable.
 
bool file_is_writable (const char *path)
 Check if file is writable.
 
bool platform_get_executable_path (char *exe_path, size_t path_size)
 Get the path to the current executable.
 
bool platform_get_temp_dir (char *temp_dir, size_t path_size)
 Get the system temporary directory path.
 
bool platform_get_cwd (char *cwd, size_t path_size)
 Get the current working directory of the process.
 
int platform_access (const char *path, int mode)
 Check file/directory access permissions.
 
bool platform_is_binary_in_path (const char *bin_name)
 Check if a binary is available in the system PATH.
 
void platform_cleanup_binary_path_cache (void)
 Cleanup the binary PATH cache.
 
int platform_fsync (int fd)
 Synchronize a file descriptor to disk.
 
bool check_binary_in_path_uncached (const char *bin_name)
 Check if a binary is in PATH (uncached, platform-specific implementation)
 
asciichat_error_t platform_init (void)
 Initialize platform-specific subsystems.
 
void platform_destroy (void)
 Cleanup platform-specific subsystems.
 
asciichat_error_t keyboard_init (void)
 Initialize keyboard input system.
 
void keyboard_destroy (void)
 Cleanup keyboard input system and restore terminal.
 
keyboard_key_t keyboard_read_nonblocking (void)
 Read next keyboard input without blocking.
 
keyboard_key_t keyboard_read_with_timeout (uint32_t timeout_ms)
 Read keyboard input with timeout.
 
keyboard_line_edit_result_t keyboard_read_line_interactive (keyboard_line_edit_opts_t *opts)
 Process one keystroke for interactive line editing.
 
void platform_mmap_init (platform_mmap_t *mapping)
 Initialize a platform_mmap_t structure.
 
asciichat_error_t platform_mmap_open (const char *name, const char *path, size_t size, platform_mmap_t *out)
 Memory-map a file for read/write access.
 
void platform_mmap_close (platform_mmap_t *mapping)
 Unmap and close a memory-mapped file.
 
void platform_mmap_sync (platform_mmap_t *mapping, bool async)
 Flush memory-mapped changes to disk.
 
bool platform_mmap_is_valid (const platform_mmap_t *mapping)
 Check if a mapping is currently valid.
 
int debug_sync_mutex_lock (mutex_t *mutex, const char *file_name, int line_number, const char *function_name)
 
int debug_sync_mutex_trylock (mutex_t *mutex, const char *file_name, int line_number, const char *function_name)
 
int debug_sync_mutex_unlock (mutex_t *mutex, const char *file_name, int line_number, const char *function_name)
 
int mutex_init (mutex_t *mutex, const char *name)
 Initialize a mutex with a name.
 
int mutex_destroy (mutex_t *mutex)
 Destroy a mutex.
 
int mutex_lock_impl (mutex_t *mutex)
 Lock a mutex (implementation function)
 
int mutex_trylock_impl (mutex_t *mutex)
 Try to lock a mutex without blocking (implementation function)
 
int mutex_unlock_impl (mutex_t *mutex)
 Unlock a mutex (implementation function)
 
pipe_t platform_pipe_connect (const char *path)
 Connect to an agent via named pipe (Windows) or Unix socket (POSIX)
 
int platform_pipe_close (pipe_t pipe)
 Close a pipe connection.
 
ssize_t platform_pipe_read (pipe_t pipe, void *buf, size_t len)
 Read data from a pipe.
 
ssize_t platform_pipe_write (pipe_t pipe, const void *buf, size_t len)
 Write data to a pipe.
 
bool platform_pipe_is_valid (pipe_t pipe)
 Check if a pipe handle is valid.
 
pid_t platform_get_pid (void)
 Get the current process ID.
 
asciichat_error_t platform_popen (const char *name, const char *command, const char *mode, FILE **out_stream)
 Execute a command and return a file stream for reading/writing.
 
asciichat_error_t platform_pclose (FILE **stream_ptr)
 Close a process stream opened with platform_popen()
 
asciichat_error_t platform_process_spawn (platform_process_t **process_out, const char *path, const char *const *argv, int stdin_fd, int stdout_fd, int stderr_fd)
 Spawn a child process.
 
asciichat_error_t platform_process_wait (platform_process_t *process, int timeout_ms, int *exit_code_out)
 Wait for process to terminate with timeout.
 
bool platform_process_is_alive (platform_process_t *process)
 Check if process is still running.
 
asciichat_error_t platform_process_kill (platform_process_t *process)
 Terminate a process.
 
void platform_process_destroy (platform_process_t *process)
 Free process handle.
 
int platform_prompt_question (const char *prompt, char *buffer, size_t max_len, prompt_opts_t opts)
 Prompt the user for text input.
 
bool platform_prompt_yes_no (const char *prompt, bool default_yes)
 Prompt the user for a yes/no answer.
 
bool platform_is_interactive (void)
 Check if interactive prompting is available.
 
int debug_sync_rwlock_rdlock (rwlock_t *rwlock, const char *file_name, int line_number, const char *function_name)
 
int debug_sync_rwlock_wrlock (rwlock_t *rwlock, const char *file_name, int line_number, const char *function_name)
 
int debug_sync_rwlock_rdunlock (rwlock_t *rwlock, const char *file_name, int line_number, const char *function_name)
 
int debug_sync_rwlock_wrunlock (rwlock_t *rwlock, const char *file_name, int line_number, const char *function_name)
 
int rwlock_init (rwlock_t *lock, const char *name)
 Initialize a read-write lock with a name.
 
int rwlock_destroy (rwlock_t *lock)
 Destroy a read-write lock.
 
int rwlock_init_impl (rwlock_t *lock)
 Initialize a read-write lock (implementation function)
 
int rwlock_destroy_impl (rwlock_t *lock)
 Destroy a read-write lock (implementation function)
 
int rwlock_rdlock_impl (rwlock_t *lock)
 Acquire a read lock (implementation function)
 
int rwlock_wrlock_impl (rwlock_t *lock)
 Acquire a write lock (implementation function)
 
int rwlock_rdunlock_impl (rwlock_t *lock)
 Release a read lock (implementation function)
 
int rwlock_wrunlock_impl (rwlock_t *lock)
 Release a write lock (implementation function)
 
asciichat_error_t platform_install_crash_handler (platform_crash_handler_t handler)
 Install a crash signal handler.
 
asciichat_error_t platform_uninstall_crash_handler (void)
 Uninstall the crash signal handler.
 
const char * platform_signal_name (int signal)
 Get human-readable name for signal number.
 
asciichat_error_t socket_init (void)
 Initialize socket subsystem (required on Windows)
 
void socket_cleanup (void)
 Cleanup socket subsystem.
 
socket_t socket_create (const char *name, int domain, int type, int protocol)
 Create a new named socket.
 
int socket_close (socket_t sock)
 Close a socket.
 
int socket_bind (socket_t sock, const struct sockaddr *addr, socklen_t addrlen)
 Bind a socket to an address.
 
int socket_listen (socket_t sock, int backlog)
 Listen for incoming connections.
 
socket_t socket_accept (socket_t sock, struct sockaddr *addr, socklen_t *addrlen, const char *name)
 Accept an incoming connection.
 
int socket_connect (socket_t sock, const struct sockaddr *addr, socklen_t addrlen)
 Connect to a remote address.
 
ssize_t socket_send (socket_t sock, const void *buf, size_t len, int flags)
 Send data on a socket.
 
ssize_t socket_recv (socket_t sock, void *buf, size_t len, int flags)
 Receive data from a socket.
 
ssize_t socket_sendto (socket_t sock, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen)
 Send data to a specific address (UDP)
 
ssize_t socket_recvfrom (socket_t sock, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen)
 Receive data from a specific address (UDP)
 
int socket_setsockopt (socket_t sock, int level, int optname, const void *optval, socklen_t optlen)
 Set socket option.
 
int socket_getsockopt (socket_t sock, int level, int optname, void *optval, socklen_t *optlen)
 Get socket option.
 
int socket_set_timeout_ns (socket_t sock, uint64_t timeout_ns)
 Set socket send/receive timeout in nanoseconds.
 
int socket_shutdown (socket_t sock, int how)
 Shutdown socket I/O.
 
int socket_getpeername (socket_t sock, struct sockaddr *addr, socklen_t *addrlen)
 Get peer address.
 
int socket_getsockname (socket_t sock, struct sockaddr *addr, socklen_t *addrlen)
 Get socket local address.
 
int socket_set_blocking (socket_t sock)
 Set socket to blocking mode.
 
int socket_set_nonblocking (socket_t sock, bool nonblocking)
 Set socket to non-blocking mode.
 
int socket_set_reuseaddr (socket_t sock, bool reuse)
 Set SO_REUSEADDR socket option.
 
int socket_set_nodelay (socket_t sock, bool nodelay)
 Set TCP_NODELAY socket option (disable Nagle's algorithm)
 
int socket_set_keepalive (socket_t sock, bool keepalive)
 Set SO_KEEPALIVE socket option.
 
int socket_set_keepalive_params (socket_t sock, bool enable, int idle, int interval, int count)
 Set TCP keepalive parameters.
 
int socket_set_linger (socket_t sock, bool enable, int timeout)
 Set SO_LINGER socket option.
 
int socket_set_timeout (socket_t sock, uint64_t timeout_ns)
 Set socket receive and send timeouts.
 
int socket_set_buffer_sizes (socket_t sock, int recv_size, int send_size)
 Set socket buffer sizes.
 
int socket_get_peer_address (socket_t sock, struct sockaddr *addr, socklen_t *addrlen)
 Get peer address (convenience function)
 
int socket_get_error (socket_t sock)
 Get socket-specific error code.
 
int socket_get_last_error (void)
 Get last socket error code.
 
const char * socket_get_error_string (void)
 Get last socket error as string.
 
int socket_poll (struct pollfd *fds, nfds_t nfds, int64_t timeout_ns)
 Poll sockets for events (multiplexed I/O)
 
int socket_select (socket_t max_fd, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout)
 Select sockets for I/O readiness.
 
void socket_fd_zero (fd_set *set)
 Clear an fd_set.
 
void socket_fd_set (socket_t sock, fd_set *set)
 Add a socket to an fd_set.
 
int socket_fd_isset (socket_t sock, fd_set *set)
 Check if a socket is in an fd_set.
 
int socket_get_fd (socket_t sock)
 Get the underlying file descriptor (POSIX compatibility)
 
bool socket_is_valid (socket_t sock)
 Check if a socket handle is valid.
 
bool socket_is_would_block_error (int error_code)
 Check if error code indicates "would block" (non-blocking socket would wait)
 
bool socket_is_connection_reset_error (int error_code)
 Check if error code indicates connection reset.
 
bool socket_is_invalid_socket_error (int error_code)
 Check if error code indicates a closed/invalid socket.
 
bool socket_is_in_progress_error (int error_code)
 Check if error indicates operation in progress (non-blocking connect)
 
int platform_socket_set_timeout (socket_t sock, uint64_t timeout_ns)
 Set send/receive timeout for a socket.
 
int platform_socket_connect_timeout (socket_t sock, const struct sockaddr *addr, socklen_t addr_len, uint64_t timeout_ns)
 Connect to remote address with timeout.
 
int platform_vsnprintf (char *str, size_t size, const char *format, va_list ap)
 Safe variable-argument string formatting.
 
int platform_asprintf (char **strp, const char *format,...)
 Allocate formatted string (asprintf replacement)
 
int platform_vasprintf (char **strp, const char *format, va_list ap)
 Allocate formatted string with va_list (vasprintf replacement)
 
char * platform_strdup (const char *s)
 Duplicate string (strdup replacement)
 
int platform_strcasecmp (const char *s1, const char *s2)
 Case-insensitive string comparison.
 
int platform_strncasecmp (const char *s1, const char *s2, size_t n)
 Case-insensitive string comparison with length limit.
 
char * platform_strtok_r (char *str, const char *delim, char **saveptr)
 Thread-safe string tokenization (strtok_r replacement)
 
size_t platform_strlcpy (char *dst, const char *src, size_t size)
 Safe string copy with size tracking (strlcpy)
 
int platform_strncpy (char *dst, size_t dst_size, const char *src, size_t count)
 Safe string copy with explicit size bounds (strncpy replacement)
 
ssize_t platform_getline (char **lineptr, size_t *n, FILE *stream)
 Cross-platform getline implementation.
 
asciichat_error_t platform_escape_shell_path (const char *path, char *output, size_t output_size)
 Escape a path string for safe shell usage.
 
asciichat_error_t symbol_cache_init (void)
 Initialize the symbol cache.
 
void symbol_cache_destroy (void)
 Clean up the symbol cache and free all resources.
 
const char * symbol_cache_lookup (void *addr)
 Look up a symbol for a given address.
 
bool symbol_cache_insert (void *addr, const char *symbol)
 Insert a symbol into the cache.
 
void symbol_cache_get_stats (uint64_t *hits_out, uint64_t *misses_out, size_t *entries_out)
 Get cache statistics.
 
void symbol_cache_print_stats (void)
 Print cache statistics to logging system.
 
char ** symbol_cache_resolve_batch (void *const *buffer, int size)
 Resolve multiple addresses using addr2line and cache results.
 
void symbol_cache_free_symbols (char **symbols)
 Free symbol array returned by symbol_cache_resolve_batch.
 
void platform_force_exit (int exit_code)
 Forcefully terminate the process immediately without cleanup.
 
void platform_sleep_ms (unsigned int ms)
 Sleep for a specified number of milliseconds.
 
uint64_t platform_get_monotonic_time_us (void)
 Get monotonic time in microseconds.
 
asciichat_error_t platform_request_timer_precision (int precision)
 Request coarse (reduced resolution) timer precision.
 
asciichat_error_t platform_restore_timer_resolution (void)
 Restore default timer precision.
 
asciichat_error_t platform_enable_keepawake (void)
 Enable system sleep prevention (keepawake mode)
 
void platform_disable_keepawake (void)
 Disable system sleep prevention (allow OS to sleep)
 
asciichat_error_t platform_localtime (const time_t *timer, struct tm *result)
 Platform-safe localtime wrapper.
 
asciichat_error_t platform_gtime (const time_t *timer, struct tm *result)
 Platform-safe gmtime wrapper.
 
const char * platform_get_username (void)
 Get the current username.
 
signal_handler_t platform_signal (int sig, signal_handler_t handler)
 Set a signal handler.
 
asciichat_error_t platform_register_signal_handlers (const platform_signal_handler_t *handlers, int count)
 Register multiple signal handlers at once.
 
const char * platform_getenv (const char *name)
 Get an environment variable value.
 
int platform_setenv (const char *name, const char *value)
 Set an environment variable.
 
int platform_unsetenv (const char *name)
 Remove an environment variable.
 
void platform_raise_fd_limit (unsigned int limit)
 Raise the file descriptor limit for the current process.
 
platform_stderr_redirect_handle_t platform_stderr_redirect_to_null (void)
 Redirect stderr to /dev/null temporarily.
 
void platform_stderr_restore (platform_stderr_redirect_handle_t handle)
 Restore stderr from a redirect handle.
 
platform_stderr_redirect_handle_t platform_stdout_stderr_redirect_to_null (void)
 Redirect both stdout and stderr to /dev/null (restorable)
 
void platform_stdout_stderr_restore (platform_stderr_redirect_handle_t handle)
 Restore stdout and stderr after platform_stdout_stderr_redirect_to_null()
 
void platform_stdio_redirect_to_null_permanent (void)
 Permanently redirect stderr and stdout to /dev/null.
 
int safe_snprintf (char *buffer, size_t buffer_size, const char *format,...)
 Platform-safe snprintf wrapper.
 
int safe_fprintf (FILE *stream, const char *format,...)
 Platform-safe fprintf wrapper.
 
int safe_vsnprintf (char *buffer, size_t buffer_size, const char *format, va_list ap)
 Platform-safe vsnprintf wrapper.
 
asciichat_error_t platform_memcpy (void *dest, size_t dest_size, const void *src, size_t count)
 Platform-safe memcpy wrapper.
 
asciichat_error_t platform_memset (void *dest, size_t dest_size, int ch, size_t count)
 Platform-safe memset wrapper.
 
asciichat_error_t platform_memmove (void *dest, size_t dest_size, const void *src, size_t count)
 Platform-safe memmove wrapper.
 
asciichat_error_t platform_strcpy (char *dest, size_t dest_size, const char *src)
 Platform-safe strcpy wrapper.
 
int platform_get_last_error (void)
 Get the last system error code.
 
const char * platform_strerror (int errnum)
 Get human-readable error message for a system error code.
 
asciichat_error_t platform_resolve_hostname_to_ipv4 (const char *hostname, char *ipv4_out, size_t ipv4_out_size)
 Resolve hostname to IPv4 address.
 
asciichat_error_t platform_load_system_ca_certs (char **pem_data_out, size_t *pem_size_out)
 Load system CA certificates for TLS/HTTPS.
 
int platform_execute_subprocess (const char *executable, const char **argv, char *output_buffer, size_t output_size)
 Execute a subprocess and optionally capture its output.
 
size_t platform_write_all (int fd, const void *buf, size_t count)
 Write all bytes to a file descriptor, handling partial writes.
 
int get_binary_file_address_offsets (const void *addr, platform_binary_match_t *matches, int max_matches)
 Get binary that contains address on Linux via /proc/self/maps.
 
asciichat_error_t terminal_get_size (terminal_size_t *size)
 Get terminal size.
 
asciichat_error_t terminal_set_raw_mode (bool enable)
 Set terminal to raw mode.
 
asciichat_error_t terminal_set_echo (bool enable)
 Set terminal echo mode.
 
bool terminal_supports_color (void)
 Check if terminal supports color.
 
bool terminal_supports_unicode (void)
 Check if terminal supports unicode.
 
bool terminal_supports_utf8 (void)
 Check if terminal supports UTF-8.
 
asciichat_error_t terminal_clear_screen (void)
 Clear the terminal screen.
 
asciichat_error_t terminal_move_cursor (int row, int col)
 Move cursor to specified position.
 
asciichat_error_t terminal_move_cursor_relative (int offset)
 Move cursor relative to current position.
 
void terminal_enable_ansi (void)
 Enable ANSI escape sequences.
 
asciichat_error_t terminal_set_buffering (bool line_buffered)
 Set terminal buffering mode.
 
asciichat_error_t terminal_flush (int fd)
 Flush terminal output.
 
asciichat_error_t terminal_get_cursor_position (int *row, int *col)
 Get current cursor position.
 
asciichat_error_t terminal_save_cursor (void)
 Save cursor position.
 
asciichat_error_t terminal_restore_cursor (void)
 Restore saved cursor position.
 
asciichat_error_t terminal_set_title (const char *title)
 Set terminal window title.
 
asciichat_error_t terminal_ring_bell (void)
 Ring terminal bell.
 
asciichat_error_t terminal_cursor_hide (void)
 Hide terminal cursor.
 
asciichat_error_t terminal_cursor_show (void)
 Show terminal cursor.
 
asciichat_error_t terminal_set_scroll_region (int top, int bottom)
 Set scroll region.
 
asciichat_error_t terminal_reset (int fd)
 Reset terminal to default state.
 
asciichat_error_t terminal_cursor_home (int fd)
 Move cursor to home position (top-left)
 
asciichat_error_t terminal_clear_scrollback (int fd)
 Clear terminal scrollback buffer.
 
void * asciichat_thread_wrapper_impl (void *arg)
 Internal thread wrapper function that executes user code with cleanup.
 
int asciichat_thread_create (asciichat_thread_t *thread, const char *name, void *(*func)(void *), void *arg)
 Create a new named thread.
 
int asciichat_thread_join (asciichat_thread_t *thread, void **retval)
 Wait for a thread to complete (blocking)
 
int asciichat_thread_detach (asciichat_thread_t *thread)
 Detach a thread so its resources are reclaimed when it exits.
 
int asciichat_thread_join_timeout (asciichat_thread_t *thread, void **retval, uint64_t timeout_ns)
 Wait for a thread to complete with timeout.
 
void asciichat_thread_exit (void *retval)
 Exit the current thread.
 
thread_id_t asciichat_thread_self (void)
 Get the current thread's ID.
 
int asciichat_thread_equal (thread_id_t t1, thread_id_t t2)
 Compare two thread IDs for equality.
 
uint64_t asciichat_thread_current_id (void)
 Get the current thread's unique numeric ID.
 
bool asciichat_thread_is_initialized (asciichat_thread_t *thread)
 Check if a thread handle has been initialized.
 
void asciichat_thread_init (asciichat_thread_t *thread)
 Initialize a thread handle to an uninitialized state.
 
asciichat_error_t asciichat_thread_set_realtime_priority (void)
 Set the current thread to real-time priority.
 
asciichat_error_t thread_create_or_fail (asciichat_thread_t *thread, void *(*func)(void *), void *arg, const char *thread_name, const char *client_id)
 Create a thread with standardized error handling and logging.
 
uintptr_t asciichat_thread_to_key (asciichat_thread_t thread)
 Convert a thread handle to a uintptr_t registry key.
 
int ascii_tls_key_create (tls_key_t *key, void(*destructor)(void *))
 Create a thread-local storage key.
 
int ascii_tls_key_delete (tls_key_t key)
 Delete a thread-local storage key.
 
void * ascii_tls_get (tls_key_t key)
 Get thread-local value for a key.
 
int ascii_tls_set (tls_key_t key, void *value)
 Set thread-local value for a key.
 

Variables

pthread_rwlock_t rwlock_t::impl
 Underlying POSIX rwlock.
 
const char * rwlock_t::name
 Human-readable name for named registry (all builds)
 
uint64_t rwlock_t::last_rdlock_time_ns
 Timestamp of last read lock acquisition (nanoseconds)
 
uint64_t rwlock_t::last_wrlock_time_ns
 Timestamp of last write lock acquisition (nanoseconds)
 
uint64_t rwlock_t::last_unlock_time_ns
 Timestamp of last unlock (nanoseconds)
 
uintptr_t rwlock_t::write_held_by_key
 Registry key of thread holding write lock (0 if not held)
 
atomic_t rwlock_t::read_lock_count
 Number of threads holding read locks (thread-safe atomic)
 
uint64_t rwlock_t::rdlock_count
 Total read lock acquisitions.
 
uint64_t rwlock_t::wrlock_count
 Total write lock acquisitions.
 
uint64_t rwlock_t::unlock_count
 Total unlocks.
 
int errno
 

Cross-Platform Utility Functions

void platform_sleep_us (unsigned int usec)
 High-precision sleep function with microsecond precision.
 
void platform_sleep_ns (uint64_t ns)
 Platform-safe sleep function with nanosecond precision.
 

Platform Detection Macros

#define PLATFORM_WINDOWS   0
 Platform detection: 1 on Windows, 0 on POSIX.
 
#define PLATFORM_POSIX   1
 Platform detection: 1 on POSIX, 0 on Windows.
 

Compiler Attribute Macros

#define PACKED_STRUCT_BEGIN
 Begin a packed structure (POSIX: no-op, uses attribute)
 
#define PACKED_STRUCT_END
 End a packed structure (POSIX: no-op, uses attribute)
 
#define PACKED_ATTR   __attribute__((packed))
 Packed structure attribute (POSIX: attribute((packed)))
 
#define ALIGNED_ATTR(x)   __attribute__((aligned(x)))
 Memory alignment attribute (POSIX: attribute((aligned)))
 

Utility Macros

#define PLATFORM_BINARY_NAME   "ascii-chat"
 Suppress unused parameter warnings.
 
#define PLATFORM_SHELL_NULL_REDIRECT   "2>/dev/null"
 Shell null device/error redirect for platform.
 

Condition Variable Wait Macros

#define cond_wait(cond, mutex)
 Wait on a condition variable (with debug tracking in debug builds)
 
#define cond_timedwait(cond, mutex, timeout_ns)
 Wait on a condition variable with timeout (with debug tracking in debug builds)
 

Mutex Locking Macros

#define mutex_lock(mutex)    (debug_sync_is_initialized() ? debug_sync_mutex_lock(mutex, __FILE__, __LINE__, __func__) : mutex_lock_impl(mutex))
 Lock a mutex (with debug tracking in debug builds)
 
#define mutex_trylock(mutex)    (debug_sync_is_initialized() ? debug_sync_mutex_trylock(mutex, __FILE__, __LINE__, __func__) : mutex_trylock_impl(mutex))
 Try to lock a mutex without blocking (with debug tracking in debug builds)
 
#define mutex_unlock(mutex)    (debug_sync_is_initialized() ? debug_sync_mutex_unlock(mutex, __FILE__, __LINE__, __func__) : mutex_unlock_impl(mutex))
 Unlock a mutex (with debug tracking in debug builds)
 

Read-Write Lock Macros

#define rwlock_rdlock(lock)    (debug_sync_is_initialized() ? debug_sync_rwlock_rdlock(lock, __FILE__, __LINE__, __func__) : rwlock_rdlock_impl(lock))
 Acquire a read lock (with debug tracking in debug builds)
 
#define rwlock_wrlock(lock)    (debug_sync_is_initialized() ? debug_sync_rwlock_wrlock(lock, __FILE__, __LINE__, __func__) : rwlock_wrlock_impl(lock))
 Acquire a write lock (with debug tracking in debug builds)
 
#define rwlock_rdunlock(lock)    (debug_sync_is_initialized() ? debug_sync_rwlock_rdunlock(lock, __FILE__, __LINE__, __func__) : rwlock_rdunlock_impl(lock))
 Release a read lock (with debug tracking in debug builds)
 
#define rwlock_wrunlock(lock)    (debug_sync_is_initialized() ? debug_sync_rwlock_wrunlock(lock, __FILE__, __LINE__, __func__) : rwlock_wrunlock_impl(lock))
 Release a write lock (with debug tracking in debug builds)
 

Detailed Description

Cross-platform abstractions for threading, sockets, system calls, and hardware access.

Provides platform-independent functions for locating and connecting to:

Author
Zachary Fogg me@zf.nosp@m.o.gg
Date
January 2026

This header defines the ASCIICHAT_API macro used to control symbol visibility in shared libraries (DLLs on Windows, .so/.dylib on Unix).

Windows DLL Behavior:

Unix Behavior:

Usage:

// In header file (extern declaration):
extern ASCIICHAT_API int global_variable;
extern ASCIICHAT_API void my_function(void);
// In source file (definition):
ASCIICHAT_API int global_variable = 42;
ASCIICHAT_API void my_function(void) { ... }
#define ASCIICHAT_API
Export symbols on Unix platforms (Linux, macOS)
Definition api.h:82

CRITICAL: This header must have ZERO dependencies

Author
Zachary Fogg me@zf.nosp@m.o.gg
Date
2025

Platform abstraction for capturing and symbolizing stack traces.

Platform-specific implementations:

Thread safety:

Author
Zachary Fogg me@zf.nosp@m.o.gg
Date
March 2026

This header provides a unified condition variable interface that abstracts platform-specific implementations (Windows Condition Variables vs POSIX pthread condition variables).

The interface provides:

Note
On Windows, uses CONDITION_VARIABLE. On POSIX systems, uses pthread_cond_t.
Condition variables must be used with a mutex (mutex_t) for proper synchronization.
Author
Zachary Fogg me@zf.nosp@m.o.gg
Date
September 2025

Provides platform-independent error handling functions for:

Author
Zachary Fogg me@zf.nosp@m.o.gg
Date
January 2026

Provides unified filesystem operations across Windows and POSIX platforms:

This header abstracts away platform differences in directory management, path handling, and configuration file discovery. All operations are platform-aware and handle differences like path separators, permission models, and standard configuration directories transparently.

Platform-specific behavior:

Thread safety: Most operations are thread-safe under normal conditions. However:

Author
Zachary Fogg me@zf.nosp@m.o.gg
Date
January 2026

This header provides platform initialization functions and static initialization helpers for synchronization primitives that need to work before main().

Author
Zachary Fogg me@zf.nosp@m.o.gg
Date
September 2025

This header contains PRIVATE implementation helpers and macros used ONLY by the platform abstraction layer implementation files.

IMPORTANT: This header must ONLY be included within lib/platform/ files.

Author
Zachary Fogg me@zf.nosp@m.o.gg
Date
September 2025

This header provides unified keyboard input operations for interactive controls during media playback and rendering. Supports both POSIX and Windows platforms with consistent key code mappings.

The interface provides:

Author
Zachary Fogg me@zf.nosp@m.o.gg
Date
January 2026

Provides unified memory allocation abstractions across Windows and POSIX platforms.

Author
Zachary Fogg me@zf.nosp@m.o.gg
Date
January 2026

This header provides a unified interface for memory-mapped files across platforms. Memory-mapped files allow treating file contents as memory, enabling efficient shared state and crash-safe logging.

The interface provides:

Platform implementations:

Author
Zachary Fogg me@zf.nosp@m.o.gg
Date
December 2025

This header provides a unified mutex interface that abstracts platform-specific implementations (Windows Critical Sections vs POSIX pthread mutexes).

The interface provides:

Note
On Windows, uses CRITICAL_SECTION for lightweight synchronization. On POSIX systems, uses pthread_mutex_t.
In debug builds, mutex_lock() and mutex_unlock() macros use lock debugging if enabled. In release builds, they call the implementation directly for zero overhead.
Author
Zachary Fogg me@zf.nosp@m.o.gg
Date
September 2025

Provides unified network header includes across Windows and POSIX platforms.

Author
Zachary Fogg me@zf.nosp@m.o.gg
Date
January 2026

This header provides a unified interface for agent communication (SSH agent, GPG agent) that abstracts platform-specific implementations:

The interface provides:

Note
On Windows, uses named pipes (CreateFileA, ReadFile, WriteFile). On POSIX systems, uses Unix domain sockets (socket, connect, read, write).
Author
Zachary Fogg me@zf.nosp@m.o.gg
Date
October 2025

Provides platform-independent process types and execution functions for running external programs (like ssh-keygen, gpg) and capturing their output.

Windows does not provide pid_t natively, so we typedef it here.

Author
Zachary Fogg me@zf.nosp@m.o.gg
Date
December 2025

Provides interactive prompting functionality across Windows, Linux, and macOS. Supports text input with optional echo, yes/no questions with defaults, and configurable answer placement (same line or next line).

All prompt functions handle terminal locking to prevent log interleaving, check for TTY availability, and support non-interactive mode detection.

Author
Zachary Fogg me@zf.nosp@m.o.gg
Date
December 2025

This header provides a unified read-write lock interface that abstracts platform-specific implementations (Windows SRW Locks vs POSIX pthread read-write locks).

The interface provides:

Note
On Windows, uses SRWLOCK for lightweight synchronization. On POSIX systems, uses pthread_rwlock_t.
In debug builds, rwlock_rdlock(), rwlock_wrlock(), rwlock_rdunlock(), and rwlock_wrunlock() macros use lock debugging if enabled. In release builds, they call the implementation directly for zero overhead.
Author
Zachary Fogg me@zf.nosp@m.o.gg
Date
September 2025

Provides unified crash handler registration across Windows and POSIX platforms. Enables capturing segmentation faults, access violations, aborts, and bus errors with consistent behavior across platforms.

Author
Zachary Fogg me@zf.nosp@m.o.gg
Date
January 2026

Overview

This header provides a unified socket interface that abstracts platform-specific implementations (Windows Winsock2 vs POSIX sockets). It enables the same socket code to work identically on Windows, Linux, and macOS without platform-specific #ifdef blocks in application code.

Key abstractions:

High-Level Interface

The socket interface provides:

Socket Lifecycle

A typical socket workflow follows this pattern:

Server Socket Lifecycle

socket_init() // (Windows: initialize Winsock)
โ†“
socket_create(AF_INET, SOCK_STREAM, 0) // Create listening socket
โ†“
socket_set_reuseaddr(sock, true) // Configure (optional)
โ†“
socket_bind(sock, addr, addrlen) // Bind to address
โ†“
socket_listen(sock, backlog) // Listen for connections
โ†“
loop:
socket_accept(sock, ...) // Accept incoming connection
โ†“
socket_recv(client, ...) // Receive data
socket_send(client, ...) // Send data
โ†“
socket_close(client) // Close client connection
โ†“ (back to loop)
socket_close(sock) // Close server socket
โ†“
socket_cleanup() // Cleanup (Windows: cleanup Winsock)
ssize_t socket_recv(socket_t sock, void *buf, size_t len, int flags)
Receive data from a socket.
void socket_cleanup(void)
Cleanup socket subsystem.
int socket_close(socket_t sock)
Close a socket.
int socket_bind(socket_t sock, const struct sockaddr *addr, socklen_t addrlen)
Bind a socket to an address.
ssize_t socket_send(socket_t sock, const void *buf, size_t len, int flags)
Send data on a socket.
socket_t socket_accept(socket_t sock, struct sockaddr *addr, socklen_t *addrlen, const char *name)
Accept an incoming connection.
int socket_set_reuseaddr(socket_t sock, bool reuse)
Set SO_REUSEADDR socket option.
asciichat_error_t socket_init(void)
Initialize socket subsystem (required on Windows)
socket_t socket_create(const char *name, int domain, int type, int protocol)
Create a new named socket.
int socket_listen(socket_t sock, int backlog)
Listen for incoming connections.
#define true
Definition stdbool.h:62

Client Socket Lifecycle

socket_init() // (Windows: initialize Winsock)
โ†“
socket_create(AF_INET, SOCK_STREAM, 0) // Create socket
โ†“
socket_connect(sock, remote_addr, addrlen) // Connect to server
โ†“
socket_send(sock, data, len, 0) // Send data
โ†“
socket_recv(sock, buffer, len, 0) // Receive data
โ†“
socket_shutdown(sock, SHUT_RDWR) // Shutdown I/O (optional)
โ†“
socket_close(sock) // Close socket
โ†“
socket_cleanup() // Cleanup (Windows: cleanup Winsock)
int socket_shutdown(socket_t sock, int how)
Shutdown socket I/O.
int socket_connect(socket_t sock, const struct sockaddr *addr, socklen_t addrlen)
Connect to a remote address.

IPv4 and IPv6 Support

The socket interface supports both IPv4 and IPv6 through standard struct sockaddr structures. Socket creation requires specifying the address family:

IPv4 (AF_INET):

struct sockaddr_in addr4;
addr4.sin_family = AF_INET;
inet_pton(AF_INET, "127.0.0.1", &addr4.sin_addr);
addr4.sin_port = htons(27224);
socket_t sock = socket_create(AF_INET, SOCK_STREAM, 0);
socket_bind(sock, (struct sockaddr *)&addr4, sizeof(addr4));
int socket_t

IPv6 (AF_INET6):

struct sockaddr_in6 addr6;
addr6.sin6_family = AF_INET6;
inet_pton(AF_INET6, "::1", &addr6.sin6_addr);
addr6.sin6_port = htons(27224);
socket_t sock = socket_create(AF_INET6, SOCK_STREAM, 0);
socket_bind(sock, (struct sockaddr *)&addr6, sizeof(addr6));

Error Handling

All socket functions that return error codes follow a consistent pattern:

Return value conventions:

Error code retrieval:

ssize_t result = socket_recv(sock, buf, len, 0);
if (result < 0) {
int error = socket_get_last_error();
const char *error_str = socket_get_error_string();
log_error("recv failed: %s", error_str);
}
#define log_error(...)
Log an ERROR message.
Definition log/log.h:587
int socket_get_last_error(void)
Get last socket error code.
const char * socket_get_error_string(void)
Get last socket error as string.

Platform-specific error checking helper: Use the socket_is_*_error() family of functions for portable error detection:

Platform-Specific Behavior

Windows (Winsock2)

POSIX (Linux, macOS)

Blocking and Non-Blocking Modes

Sockets are blocking by default. Use socket_set_nonblocking() to enable non-blocking mode:

Blocking socket (default):

socket_t sock = socket_create(AF_INET, SOCK_STREAM, 0);
// socket_recv() will wait indefinitely for data
// socket_connect() will wait indefinitely for connection

Non-blocking socket:

ssize_t result = socket_recv(sock, buf, len, 0);
// No data available, can retry later
}
int socket_set_nonblocking(socket_t sock, bool nonblocking)
Set socket to non-blocking mode.
bool socket_is_would_block_error(int error_code)
Check if error code indicates "would block" (non-blocking socket would wait)

Performance Optimization

Video Streaming Optimization

For high-throughput video streaming, use socket_optimize_for_streaming():

socket_t client = socket_accept(server_sock, NULL, NULL);
// Automatically applies:
// - TCP_NODELAY (disables Nagle's algorithm)
// - Large send/receive buffers
// - Keepalive with tuned parameters
// - Appropriate timeouts
void socket_optimize_for_streaming(socket_t sock)
Optimize socket for high-throughput video streaming.
Definition socket.c:37

Custom Socket Tuning

For specialized use cases, manually configure sockets:

socket_set_nodelay(sock, true); // Disable Nagle's algorithm
socket_set_reuseaddr(sock, true); // Allow rapid rebinding
socket_set_keepalive(sock, true); // Enable TCP keepalive
socket_set_buffer_sizes(sock, 2MB, 2MB); // Large buffers for throughput
socket_set_timeout(sock, 30000000000LL); // 30 second timeout in nanoseconds
int socket_set_buffer_sizes(socket_t sock, int recv_size, int send_size)
Set socket buffer sizes.
int socket_set_timeout(socket_t sock, uint64_t timeout_ns)
Set socket receive and send timeouts.
Definition socket.c:82
int socket_set_nodelay(socket_t sock, bool nodelay)
Set TCP_NODELAY socket option (disable Nagle's algorithm)
int socket_set_keepalive(socket_t sock, bool keepalive)
Set SO_KEEPALIVE socket option.

I/O Multiplexing

Use polling for efficient monitoring of multiple sockets:

Poll API (recommended):

struct pollfd fds[2];
fds[0].fd = socket_get_fd(server_sock);
fds[0].events = POLLIN; // Monitor for incoming connections
fds[1].fd = socket_get_fd(client_sock);
fds[1].events = POLLIN | POLLOUT; // Monitor read and write
int ready = socket_poll(fds, 2, 1000000000LL); // 1 second timeout
if (ready > 0) {
if (fds[0].revents & POLLIN) {
// Server ready to accept
}
if (fds[1].revents & POLLIN) {
// Client ready to read
}
}
int socket_get_fd(socket_t sock)
Get the underlying file descriptor (POSIX compatibility)
int socket_poll(struct pollfd *fds, nfds_t nfds, int64_t timeout_ns)
Poll sockets for events (multiplexed I/O)

Select API (legacy):

fd_set readfds, writefds;
socket_fd_zero(&readfds);
socket_fd_set(server_sock, &readfds);
socket_fd_set(client_sock, &readfds);
struct timeval timeout = {1, 0}; // 1 second
int ready = socket_select(client_sock + 1, &readfds, NULL, NULL, &timeout);
if (ready > 0) {
if (socket_fd_isset(server_sock, &readfds)) {
// Server ready to accept
}
}
void socket_fd_zero(fd_set *set)
Clear an fd_set.
void socket_fd_set(socket_t sock, fd_set *set)
Add a socket to an fd_set.
int socket_select(socket_t max_fd, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout)
Select sockets for I/O readiness.
int socket_fd_isset(socket_t sock, fd_set *set)
Check if a socket is in an fd_set.
Author
Zachary Fogg me@zf.nosp@m.o.gg
Date
September 2025

Provides unified macros for checking file types and properties across Windows and POSIX platforms. Windows requires special handling since it doesn't provide standard POSIX stat macros.

Author
Zachary Fogg me@zf.nosp@m.o.gg
Date
January 2026

Provides platform-independent string manipulation utilities.

Author
Zachary Fogg me@zf.nosp@m.o.gg
Date
January 2026

This header provides a high-performance symbol resolution cache system for converting backtrace addresses to human-readable symbol names. The system caches resolved symbols to avoid expensive addr2line subprocess spawns on every backtrace operation.

CORE FEATURES:

PERFORMANCE BENEFITS:

Symbol resolution without caching requires:

With caching:

ARCHITECTURE:

The symbol cache uses a hashtable to store resolved symbols:

BATCH RESOLUTION:

For efficient backtrace processing:

STATISTICS TRACKING:

The system tracks:

These statistics enable performance monitoring and cache efficiency analysis.

THREAD SAFETY:

Note
The symbol cache significantly improves backtrace performance by avoiding repeated addr2line subprocess spawns for the same addresses.
Cached symbol strings are owned by the cache and should not be freed by callers. The cache manages memory automatically.
addr2line must be available in PATH for batch resolution to work.
Debug symbols must be available in the executable for resolution.
Author
Zachary Fogg me@zf.nosp@m.o.gg
Date
October 2025

This header provides unified system functions including process management, environment variables, TTY operations, and signal handling.

The interface provides:

Author
Zachary Fogg me@zf.nosp@m.o.gg
Date
September 2025

This header provides unified terminal I/O operations including ANSI escape sequences, cursor control, and terminal configuration.

The interface provides:

Platform-specific behavior:

Signal handling (POSIX systems):

Window size detection:

TTY detection and interactive mode:

These functions determine whether interactive features (prompts, animations, padding) should be used. When output is piped or redirected, formatting is disabled to avoid corrupting piped data.

Theme detection:

Thread safety:

Author
Zachary Fogg me@zf.nosp@m.o.gg
Date
September 2025

This header provides a unified thread interface that abstracts platform-specific implementations (Windows threads vs POSIX pthreads).

The interface provides:

Note
On Windows, uses HANDLE for thread representation. On POSIX systems, uses pthread_t.
Author
Zachary Fogg me@zf.nosp@m.o.gg
Date
September 2025

This header provides a single point to include windows.h with proper alignment. The pragma pack ensures Windows SDK types get 8-byte alignment as expected, then immediately restores default packing so application structs are unaffected.

Author
Zachary Fogg me@zf.nosp@m.o.gg
Date
September 2025

This header provides POSIX-style errno constant definitions for Windows compatibility. Windows headers do not define these constants by default, so this header fills the gap to enable cross-platform code.

CORE FEATURES:

WINDOWS COMPATIBILITY:

Windows uses WSA errors for socket operations, but many functions also use standard errno. This header ensures standard errno constants are available on Windows for consistent error handling.

DEFINED CONSTANTS:

This header defines the following errno constants:

Note
This header must be included before any Windows headers that might define or use these constants.
All constants are protected with #ifndef guards to prevent redefinition errors.
On POSIX systems, these constants are typically defined in <errno.h>, but this header can still be included safely.
Author
Zachary Fogg me@zf.nosp@m.o.gg
Date
September 2025

Socket API and Cross-Platform Networking

Architecture

The socket abstraction layer provides a unified, cross-platform interface for TCP/UDP network communication. It abstracts the differences between Windows Winsock2 and POSIX BSD sockets, enabling application code to remain completely platform-agnostic.

Design Principles:

  • Single API: Same function signatures work on Windows, Linux, and macOS
  • Type unification: socket_t abstracts SOCKET (Windows) vs int (POSIX)
  • Error normalization: Consistent error handling across platforms
  • Zero overhead: Inline wrappers compile to direct API calls in release builds
  • Auto-optimization: Accepted sockets are automatically optimized for video streaming

Quick Start

Creating a TCP Server

// Initialize socket subsystem (required on Windows)
// Create listening socket
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(27224);
addr.sin_addr.s_addr = htonl(INADDR_ANY);
socket_t server_sock = socket_create(AF_INET, SOCK_STREAM, 0);
socket_set_reuseaddr(server_sock, true);
socket_bind(server_sock, (struct sockaddr *)&addr, sizeof(addr));
socket_listen(server_sock, 10);
// Accept connection
socket_t client = socket_accept(server_sock, NULL, NULL);
if (socket_is_valid(client)) {
// Automatic optimization for streaming
char buf[4096];
ssize_t received = socket_recv(client, buf, sizeof(buf), 0);
if (received > 0) {
socket_send(client, buf, (size_t)received, 0);
}
socket_close(client);
}
socket_close(server_sock);
bool socket_is_valid(socket_t sock)
Check if a socket handle is valid.
Cross-platform socket interface for ascii-chat.

Creating a TCP Client

// Create and connect socket
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(27224);
inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr);
socket_t sock = socket_create(AF_INET, SOCK_STREAM, 0);
if (socket_connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
log_error("Connection failed: %s", socket_get_error_string());
socket_close(sock);
return;
}
// Send and receive data
socket_send(sock, "Hello", 5, 0);
char buf[1024];
ssize_t received = socket_recv(sock, buf, sizeof(buf) - 1, 0);
if (received > 0) {
buf[received] = '\0';
log_info("Received: %s", buf);
}
#define log_info(...)
Log an INFO message.
Definition log/log.h:561

Core Features

IPv4 and IPv6 Support

The socket API seamlessly supports both IPv4 and IPv6 through standard struct sockaddr_in (IPv4) and struct sockaddr_in6 (IPv6) structures.

IPv4 Example:

struct sockaddr_in addr4;
memset(&addr4, 0, sizeof(addr4));
addr4.sin_family = AF_INET;
addr4.sin_port = htons(27224);
inet_pton(AF_INET, "192.168.1.100", &addr4.sin_addr);
socket_t sock = socket_create(AF_INET, SOCK_STREAM, 0);
socket_bind(sock, (struct sockaddr *)&addr4, sizeof(addr4));

IPv6 Example:

struct sockaddr_in6 addr6;
memset(&addr6, 0, sizeof(addr6));
addr6.sin6_family = AF_INET6;
addr6.sin6_port = htons(27224);
inet_pton(AF_INET6, "::1", &addr6.sin6_addr);
socket_t sock = socket_create(AF_INET6, SOCK_STREAM, 0);
socket_bind(sock, (struct sockaddr *)&addr6, sizeof(addr6));
// Optional: Allow IPv4 connections on IPv6 socket
int v6only = 0;
socket_setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &v6only, sizeof(v6only));
int socket_setsockopt(socket_t sock, int level, int optname, const void *optval, socklen_t optlen)
Set socket option.

Error Handling

All socket functions use consistent error reporting with platform-agnostic error code helpers.

Standard error patterns:

// Socket creation errors
socket_t sock = socket_create(AF_INET, SOCK_STREAM, 0);
if (!socket_is_valid(sock)) {
log_error("Socket creation failed: %s", socket_get_error_string());
return false;
}
// Connection errors
if (socket_connect(sock, &addr, sizeof(addr)) < 0) {
int err = socket_get_last_error();
log_error("Connection failed: %s", socket_get_error_string());
socket_close(sock);
return false;
}
// Send/recv errors
ssize_t result = socket_recv(sock, buf, len, 0);
if (result < 0) {
// Non-blocking socket, no data available
// Connection reset
} else {
log_error("Recv failed: %s", socket_get_error_string());
}
} else if (result == 0) {
// Connection closed gracefully
} else {
// Process received data
}
bool socket_is_connection_reset_error(int error_code)
Check if error code indicates connection reset.

Error detection helpers:

Blocking and Non-Blocking Modes

Sockets are blocking by default. Switch to non-blocking for asynchronous I/O.

Blocking socket (default):

socket_t sock = socket_create(AF_INET, SOCK_STREAM, 0);
// socket_recv() waits indefinitely for data
ssize_t received = socket_recv(sock, buf, len, 0);

Non-blocking socket:

// socket_recv() returns immediately
ssize_t received = socket_recv(sock, buf, len, 0);
if (received > 0) {
// Data available
} else if (received < 0 && socket_is_would_block_error(socket_get_last_error())) {
// No data available, retry later
} else {
// Error
}

Performance Optimization

Video Streaming Optimization

The socket layer provides automatic optimization for high-throughput video streaming:

socket_t client = socket_accept(server_sock, NULL, NULL);
// Automatically applies:
// - Disables Nagle's algorithm (TCP_NODELAY)
// - Sets large send/receive buffers (2MB with fallbacks)
// - Enables keepalive probes
// - Sets appropriate timeouts

Manual tuning for specialized use cases:

socket_set_nodelay(sock, true); // Reduce latency
socket_set_reuseaddr(sock, true); // Quick rebinding
socket_set_keepalive(sock, true); // Detect dead connections
socket_set_buffer_sizes(sock, 2MB, 2MB); // Large buffers for throughput
socket_set_timeout(sock, 30 * 1000000000LL); // 30s nanosecond timeout

TCP Keepalive Configuration

Fine-tune keepalive for different use cases:

// Aggressive keepalive for real-time communication
socket_set_keepalive_params(sock, true, 10, 5, 3);
// idle=10s, interval=5s, count=3 probes before timeout
// Relaxed keepalive for background connections
socket_set_keepalive_params(sock, true, 300, 60, 9);
// idle=5min, interval=1min, count=9 probes
int socket_set_keepalive_params(socket_t sock, bool enable, int idle, int interval, int count)
Set TCP keepalive parameters.

I/O Multiplexing

Use multiplexing to efficiently monitor multiple sockets.

Poll API (Recommended)

More efficient and modern than select() for high-descriptor-count scenarios:

struct pollfd fds[2];
// Monitor server socket for incoming connections
fds[0].fd = socket_get_fd(server_sock);
fds[0].events = POLLIN;
fds[0].revents = 0;
// Monitor client socket for read and write readiness
fds[1].fd = socket_get_fd(client_sock);
fds[1].events = POLLIN | POLLOUT;
fds[1].revents = 0;
// Wait up to 5 seconds
int ready = socket_poll(fds, 2, 5000000000LL);
if (ready < 0) {
// Error
} else if (ready == 0) {
// Timeout
} else {
// Check which sockets are ready
if (fds[0].revents & POLLIN) {
// Server ready to accept
socket_t client = socket_accept(server_sock, NULL, NULL);
}
if (fds[1].revents & POLLIN) {
// Client has data available
}
if (fds[1].revents & POLLOUT) {
// Client socket writable
}
if (fds[1].revents & POLLERR) {
// Client socket error
}
}

Event flags:

  • POLLIN: Data available to read
  • POLLOUT: Socket writable (buffer has space)
  • POLLERR: Socket error condition
  • POLLHUP: Connection closed
  • POLLNVAL: Invalid socket

Select API (Legacy)

The select() API for systems that need it:

fd_set readfds, writefds;
socket_fd_zero(&readfds);
socket_fd_zero(&writefds);
// Add sockets to sets
socket_fd_set(server_sock, &readfds);
socket_fd_set(client_sock, &readfds);
socket_fd_set(client_sock, &writefds);
// Set timeout
struct timeval timeout;
timeout.tv_sec = 5;
timeout.tv_usec = 0;
int ready = socket_select(client_sock + 1, &readfds, &writefds, NULL, &timeout);
if (ready > 0) {
if (socket_fd_isset(server_sock, &readfds)) {
// Server ready to accept
}
if (socket_fd_isset(client_sock, &readfds)) {
// Client ready to read
}
}

Platform-Specific Details

Windows (Winsock2)

Initialization: Windows requires explicit Winsock initialization before socket operations:

if (err != ASCIICHAT_OK) {
log_error("Failed to initialize Winsock");
return false;
}
// ... use sockets ...
socket_cleanup(); // Cleanup when done
asciichat_error_t
Error and exit codes - unified status values (0-255)
Definition error_codes.h:49
@ ASCIICHAT_OK
Definition error_codes.h:51

Type mapping:

  • socket_t โ†” SOCKET
  • INVALID_SOCKET_VALUE โ†” INVALID_SOCKET (~0)
  • socklen_t โ†” int

Error codes:

  • WSAEWOULDBLOCK (non-blocking no-data)
  • WSAEINPROGRESS (operation in progress)
  • WSAECONNRESET (connection reset)

Polling: Uses WSAPoll() on Windows Vista+ for efficiency, falls back to select() on older versions.

POSIX (Linux, macOS)

Initialization: Automatic (socket_init() is a no-op on POSIX):

// On POSIX, this does nothing, but call it for portability
socket_t sock = socket_create(AF_INET, SOCK_STREAM, 0);

Type mapping:

  • socket_t โ†” int (file descriptor)
  • INVALID_SOCKET_VALUE โ†” -1
  • socklen_t โ†” socklen_t (already defined)

Error codes:

  • EAGAIN/EWOULDBLOCK (non-blocking no-data)
  • EINPROGRESS (operation in progress)
  • ECONNRESET (connection reset)

Polling: Uses poll() for efficient I/O multiplexing (preferred over select() for modern systems).

Socket Lifecycle

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ Socket Lifecycle โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
CLIENT SIDE:
โ†“
socket_connect() โ”€โ”€โ†’ (blocks until connected or error)
โ†“
socket_send()/recv()
โ†“
socket_shutdown() (optional)
โ†“
โ†“
CLOSED
SERVER SIDE:
โ†“
โ†“
โ†“
socket_accept() โ”€โ”€โ†’ Returns new socket for client
โ†“ (original socket continues listening)
socket_send()/recv() on accepted socket
โ†“
socket_close() on accepted socket
โ†“
(loop back to accept for next client)
โ†“
socket_close() on server socket
โ†“
CLOSED

Best Practices

Always Check Socket Validity

// โŒ WRONG - Directly comparing to -1 (breaks on Windows)
socket_t sock = socket_create(...);
if (sock == -1) { ... }
// โœ… CORRECT - Use portable function
socket_t sock = socket_create(...);
if (!socket_is_valid(sock)) { ... }

Always Log Error Context

// โŒ POOR
if (!socket_is_valid(sock)) return false;
// โœ… BETTER
if (!socket_is_valid(sock)) {
log_error("Socket creation failed: %s", socket_get_error_string());
return false;
}

Handle Partial Sends

// โŒ WRONG - Assumes full send
socket_send(sock, data, 1000, 0);
// โœ… CORRECT - Handles partial sends
size_t total_sent = 0;
while (total_sent < 1000) {
ssize_t sent = socket_send(sock, (char *)data + total_sent,
1000 - total_sent, 0);
if (sent < 0) { handle_error(); }
total_sent += sent;
}

Non-Blocking I/O Patterns

Always use helper functions for error classification:

ssize_t result = socket_recv(sock, buf, len, 0);
if (result < 0) {
int err = socket_get_last_error();
// Retry later, don't treat as fatal
// Connection lost, clean up
} else {
// Other error
}
}

Integration with ascii-chat

The socket API is used throughout ascii-chat for:

  • Client-server communication for video streaming
  • Multi-client connections to a central server
  • Network protocol layer abstraction
  • Cross-platform TCP and UDP support

See lib/network for higher-level protocol abstraction using sockets.

See also
Platform Abstraction Layer
Network Module
include/ascii-chat/platform/socket.h

Macro Definition Documentation

◆ ALIGNED_ATTR

#define ALIGNED_ATTR (   x)    __attribute__((aligned(x)))

#include <abstraction.h>

Memory alignment attribute (POSIX: attribute((aligned)))

Parameters
xAlignment in bytes (must be power of 2)

Definition at line 192 of file abstraction.h.

◆ ASCIICHAT_API

#define ASCIICHAT_API   __attribute__((visibility("default")))

#include <api.h>

Export symbols on Unix platforms (Linux, macOS)

Uses GCC/Clang visibility attributes to explicitly export symbols. This is needed when building with -fvisibility=hidden for better performance and smaller symbol tables.

Definition at line 82 of file api.h.

◆ cond_timedwait

#define cond_timedwait (   cond,
  mutex,
  timeout_ns 
)

#include <cond.h>

Value:
? debug_sync_cond_timedwait(cond, mutex, timeout_ns, __FILE__, __LINE__, __func__) \
: cond_timedwait_impl(cond, mutex, timeout_ns))
mutex_t mutex
bool debug_sync_is_initialized(void)
Definition sync.c:820
int cond_timedwait_impl(cond_t *cond, mutex_t *mutex, uint64_t timeout_ns)
Wait on a condition variable with timeout - implementation function.
int debug_sync_cond_timedwait(cond_t *cond, mutex_t *mutex, uint64_t timeout_ns, const char *file, int line, const char *func)
Definition sync.c:797

Wait on a condition variable with timeout (with debug tracking in debug builds)

Parameters
condPointer to condition variable to wait on
mutexPointer to mutex that must be locked by the calling thread
timeout_nsTimeout in nanoseconds
Returns
0 if condition was signaled, non-zero on timeout or error

Atomically unlocks the mutex and waits on the condition variable with a timeout. Returns non-zero if the timeout expires before the condition is signaled.

Note
In debug builds, this macro includes deadlock detection if initialized. In release builds, calls the implementation directly for zero overhead.
Warning
The mutex must be locked before calling this function.

Definition at line 300 of file cond.h.

303 : cond_timedwait_impl(cond, mutex, timeout_ns))

◆ cond_wait

#define cond_wait (   cond,
  mutex 
)

#include <cond.h>

Value:
(debug_sync_is_initialized() ? debug_sync_cond_wait(cond, mutex, __FILE__, __LINE__, __func__) \
int debug_sync_cond_wait(cond_t *cond, mutex_t *mutex, const char *file, int line, const char *func)
Definition sync.c:786
int cond_wait_impl(cond_t *cond, mutex_t *mutex)
Wait on a condition variable (blocking) - implementation function.

Wait on a condition variable (with debug tracking in debug builds)

Parameters
condPointer to condition variable to wait on
mutexPointer to mutex that must be locked by the calling thread
Returns
0 on success, non-zero on error

Atomically unlocks the mutex and waits on the condition variable. The mutex must be locked by the calling thread before calling this function. Upon return, the mutex will be locked again.

Note
In debug builds, this macro includes deadlock detection if initialized. In release builds, calls the implementation directly for zero overhead.
Warning
The mutex must be locked before calling this function.

Definition at line 275 of file cond.h.

277 : cond_wait_impl(cond, mutex))

◆ DIR_PERM_PRIVATE

#define DIR_PERM_PRIVATE   0700

#include <filesystem.h>

Directory permission: Private (owner read/write/execute only)

Octal mode 0700: rwx---— Used for private directories like ~/.ascii-chat

Note
On Windows, this is a no-op (Windows uses ACLs instead of POSIX permissions)

Definition at line 199 of file filesystem.h.

◆ EAGAIN

#define EAGAIN   11

#include <windows_errno.h>

Definition at line 81 of file windows_errno.h.

◆ EBADF

#define EBADF   9

#include <windows_errno.h>

Definition at line 77 of file windows_errno.h.

◆ ECONNREFUSED

#define ECONNREFUSED   111

#include <windows_errno.h>

Definition at line 93 of file windows_errno.h.

◆ ECONNRESET

#define ECONNRESET   104

#include <windows_errno.h>

Definition at line 105 of file windows_errno.h.

◆ EHOSTUNREACH

#define EHOSTUNREACH   113

#include <windows_errno.h>

Definition at line 101 of file windows_errno.h.

◆ EINTR

#define EINTR   4

#include <windows_errno.h>

Definition at line 73 of file windows_errno.h.

◆ EINVAL

#define EINVAL   22

#include <windows_errno.h>

Definition at line 61 of file windows_errno.h.

◆ ENETUNREACH

#define ENETUNREACH   101

#include <windows_errno.h>

Definition at line 97 of file windows_errno.h.

◆ ENOTSOCK

#define ENOTSOCK   88

#include <windows_errno.h>

Definition at line 109 of file windows_errno.h.

◆ EPIPE

#define EPIPE   32

#include <windows_errno.h>

Definition at line 89 of file windows_errno.h.

◆ ERANGE

#define ERANGE   34

#include <windows_errno.h>

Definition at line 65 of file windows_errno.h.

◆ ETIMEDOUT

#define ETIMEDOUT   110

#include <windows_errno.h>

Definition at line 69 of file windows_errno.h.

◆ EWOULDBLOCK

#define EWOULDBLOCK   11

#include <windows_errno.h>

Definition at line 85 of file windows_errno.h.

◆ FILE_PERM_MASK

#define FILE_PERM_MASK   0777

#include <filesystem.h>

Permission mask for all permissions.

Octal mode 0777: rwxrwxrwx Used for masking permission bits (e.g., st_mode & 0777)

Definition at line 219 of file filesystem.h.

◆ FILE_PERM_PRIVATE

#define FILE_PERM_PRIVATE   0600

#include <filesystem.h>

File permission: Private (owner read/write only)

Octal mode 0600: rw----— Used for sensitive files like private keys, log files, and configuration files.

Note
On Windows, this is a no-op (Windows uses ACLs instead of POSIX permissions)

Definition at line 187 of file filesystem.h.

◆ FILE_PERM_PUBLIC_READ

#define FILE_PERM_PUBLIC_READ   0644

#include <filesystem.h>

File permission: Public read, owner write.

Octal mode 0644: rw-r–r– Used for files that should be readable by others but only writable by owner.

Definition at line 209 of file filesystem.h.

◆ INVALID_PIPE_VALUE

#define INVALID_PIPE_VALUE   (-1)

#include <pipe.h>

Invalid pipe value (POSIX: -1)

Definition at line 42 of file pipe.h.

◆ INVALID_SOCKET_VALUE

#define INVALID_SOCKET_VALUE   (-1)

#include <socket.h>

Invalid socket value (POSIX: -1)

Definition at line 278 of file socket.h.

◆ LOG_COND_STATE

#define LOG_COND_STATE (   cond)    cond_log_state(cond, __FILE__, __LINE__, __func__)

#include <cond.h>

Definition at line 245 of file cond.h.

◆ LOG_MUTEX_STATE

#define LOG_MUTEX_STATE (   mutex)    mutex_log_state(mutex, __FILE__, __LINE__, __func__)

#include <mutex.h>

Definition at line 175 of file platform/mutex.h.

◆ LOG_RWLOCK_STATE

#define LOG_RWLOCK_STATE (   rwlock)    rwlock_log_state(rwlock, __FILE__, __LINE__, __func__)

#include <rwlock.h>

Definition at line 189 of file rwlock.h.

◆ mutex_lock

#define mutex_lock (   mutex)     (debug_sync_is_initialized() ? debug_sync_mutex_lock(mutex, __FILE__, __LINE__, __func__) : mutex_lock_impl(mutex))

#include <mutex.h>

Lock a mutex (with debug tracking in debug builds)

Parameters
mutexPointer to mutex to lock

Locks the mutex, blocking if necessary until the lock is acquired.

Note
In debug builds, this macro includes lock debugging if initialized. In release builds, calls the implementation directly for zero overhead.

Definition at line 238 of file platform/mutex.h.

int mutex_lock_impl(mutex_t *mutex)
Lock a mutex (implementation function)
Definition threading.c:27

◆ mutex_trylock

#define mutex_trylock (   mutex)     (debug_sync_is_initialized() ? debug_sync_mutex_trylock(mutex, __FILE__, __LINE__, __func__) : mutex_trylock_impl(mutex))

#include <mutex.h>

Try to lock a mutex without blocking (with debug tracking in debug builds)

Parameters
mutexPointer to mutex to try to lock
Returns
0 if lock was acquired, non-zero if mutex was already locked
Note
In debug builds, this macro includes lock debugging if initialized. In release builds, calls the implementation directly for zero overhead.

Definition at line 255 of file platform/mutex.h.

int mutex_trylock_impl(mutex_t *mutex)
Try to lock a mutex without blocking (implementation function)
Definition threading.c:32

◆ mutex_unlock

#define mutex_unlock (   mutex)     (debug_sync_is_initialized() ? debug_sync_mutex_unlock(mutex, __FILE__, __LINE__, __func__) : mutex_unlock_impl(mutex))

#include <mutex.h>

Unlock a mutex (with debug tracking in debug builds)

Parameters
mutexPointer to mutex to unlock

Unlocks the mutex. The mutex must be locked by the current thread.

Note
In debug builds, this macro includes lock debugging if initialized. In release builds, calls the implementation directly for zero overhead.

Definition at line 273 of file platform/mutex.h.

int mutex_unlock_impl(mutex_t *mutex)
Unlock a mutex (implementation function)
Definition threading.c:37

◆ ntohll

#define ntohll (   x)    htonll(x)

#include <network.h>

Definition at line 51 of file platform/network.h.

◆ PACKED_ATTR

#define PACKED_ATTR   __attribute__((packed))

#include <abstraction.h>

Packed structure attribute (POSIX: attribute((packed)))

On POSIX, use this attribute directly on structure definitions.

Example:
typedef struct {
uint8_t field1;
uint16_t field2;
} PACKED_ATTR packed_struct_t;
unsigned short uint16_t
Definition common.h:57
unsigned char uint8_t
Definition common.h:56
#define PACKED_ATTR
Packed structure attribute (POSIX: attribute((packed)))

Definition at line 185 of file abstraction.h.

◆ PACKED_STRUCT_BEGIN

#define PACKED_STRUCT_BEGIN

#include <abstraction.h>

Begin a packed structure (POSIX: no-op, uses attribute)

Definition at line 167 of file abstraction.h.

◆ PACKED_STRUCT_END

#define PACKED_STRUCT_END

#include <abstraction.h>

End a packed structure (POSIX: no-op, uses attribute)

Definition at line 169 of file abstraction.h.

◆ PATH_DELIM

#define PATH_DELIM   '/'

#include <filesystem.h>

Platform-specific path separator character.

  • Windows: '\' (backslash)
  • Unix/POSIX: '/' (forward slash)

Use this constant instead of hardcoding separators or using #ifdef _WIN32.

Note
For string literals, use PATH_SEPARATOR_STR instead.

Definition at line 112 of file filesystem.h.

◆ PATH_ENV_SEPARATOR

#define PATH_ENV_SEPARATOR   ":"

#include <filesystem.h>

Platform-specific PATH environment variable separator.

  • Windows: ";" (semicolon)
  • Unix/POSIX: ":" (colon)

Definition at line 127 of file filesystem.h.

◆ PATH_SEPARATOR_STR

#define PATH_SEPARATOR_STR   "/"

#include <filesystem.h>

Definition at line 113 of file filesystem.h.

◆ PLATFORM_ACCESS_EXISTS

#define PLATFORM_ACCESS_EXISTS   0

#include <filesystem.h>

Access modes for platform_access()

Check if file/directory exists

Definition at line 1030 of file filesystem.h.

◆ PLATFORM_ACCESS_READ

#define PLATFORM_ACCESS_READ   4

#include <filesystem.h>

Check if file/directory is readable.

Definition at line 1032 of file filesystem.h.

◆ PLATFORM_ACCESS_WRITE

#define PLATFORM_ACCESS_WRITE   2

#include <filesystem.h>

Check if file/directory is writable.

Definition at line 1031 of file filesystem.h.

◆ platform_alloca

#define platform_alloca (   size)    alloca(size)

#include <memory.h>

Platform-specific stack allocation (POSIX)

On POSIX, use standard alloca

Definition at line 31 of file platform/memory.h.

◆ PLATFORM_BINARY_NAME

#define PLATFORM_BINARY_NAME   "ascii-chat"

#include <abstraction.h>

Suppress unused parameter warnings.

Parameters
xParameter name to mark as unused

Use this macro to suppress compiler warnings about unused function parameters. Especially useful for callback functions where not all parameters are used.

Example:
void callback(void *data, int flags) {
UNUSED(flags); // Suppress warning about unused 'flags' parameter
process_data(data);
}
Note
This macro casts the parameter to void, which effectively tells the compiler that the parameter is intentionally unused.

Platform-specific binary executable name with extension

Expands to "ascii-chat.exe" on Windows, "ascii-chat" on other platforms. Use this macro instead of hardcoding platform-specific executable names.

Example:
const char *binary_name = PLATFORM_BINARY_NAME; // "ascii-chat.exe" or "ascii-chat"
printf("Usage: %s [options]\n", binary_name);
#define PLATFORM_BINARY_NAME
Suppress unused parameter warnings.

Definition at line 687 of file abstraction.h.

◆ PLATFORM_MAX_ENV_VALUE_LENGTH

#define PLATFORM_MAX_ENV_VALUE_LENGTH   32768

#include <filesystem.h>

Maximum environment variable value length.

Windows theoretically supports 32767 chars per env var value. Unix systems typically have no hard limit per variable, but total environment size is limited (usually ~128KB-2MB).

We use 32KB as a reasonable maximum that handles Windows PATH (which can easily exceed 4KB) while not being excessive.

Definition at line 171 of file filesystem.h.

◆ PLATFORM_MAX_PATH_LENGTH

#define PLATFORM_MAX_PATH_LENGTH   4096

#include <filesystem.h>

Maximum path length supported by the operating system.

Platform-specific values:

  • Windows: 32767 characters (extended-length path with \?\ prefix)
  • Linux: 4096 bytes (PATH_MAX from limits.h)
  • macOS: 1024 bytes (PATH_MAX from sys/syslimits.h)
Note
Windows legacy MAX_PATH (260) is too restrictive for modern use. We use the extended-length limit instead.

Definition at line 158 of file filesystem.h.

◆ PLATFORM_O_APPEND

#define PLATFORM_O_APPEND   O_APPEND

#include <filesystem.h>

Definition at line 88 of file filesystem.h.

◆ PLATFORM_O_BINARY

#define PLATFORM_O_BINARY   0

#include <filesystem.h>

Definition at line 89 of file filesystem.h.

◆ PLATFORM_O_CREAT

#define PLATFORM_O_CREAT   O_CREAT

#include <filesystem.h>

Definition at line 85 of file filesystem.h.

◆ PLATFORM_O_EXCL

#define PLATFORM_O_EXCL   O_EXCL

#include <filesystem.h>

Definition at line 86 of file filesystem.h.

◆ PLATFORM_O_RDONLY

#define PLATFORM_O_RDONLY   O_RDONLY

#include <filesystem.h>

Definition at line 82 of file filesystem.h.

◆ PLATFORM_O_RDWR

#define PLATFORM_O_RDWR   O_RDWR

#include <filesystem.h>

Definition at line 84 of file filesystem.h.

◆ PLATFORM_O_TRUNC

#define PLATFORM_O_TRUNC   O_TRUNC

#include <filesystem.h>

Definition at line 87 of file filesystem.h.

◆ PLATFORM_O_WRONLY

#define PLATFORM_O_WRONLY   O_WRONLY

#include <filesystem.h>

Definition at line 83 of file filesystem.h.

◆ PLATFORM_POSIX

#define PLATFORM_POSIX   1

#include <abstraction.h>

Platform detection: 1 on POSIX, 0 on Windows.

Definition at line 95 of file abstraction.h.

◆ PLATFORM_SHELL_NULL_REDIRECT

#define PLATFORM_SHELL_NULL_REDIRECT   "2>/dev/null"

#include <abstraction.h>

Shell null device/error redirect for platform.

Expands to "2>nul" on Windows, "2>/dev/null" on other platforms. Use this macro when building shell commands that need to redirect stderr.

Example:
char cmd[256];
snprintf(cmd, sizeof(cmd), "gpg --export 0x%s " PLATFORM_SHELL_NULL_REDIRECT, key_id);
system(cmd); // On Windows: gpg --export 0x... 2>nul
// On Unix: gpg --export 0x... 2>/dev/null
#define PLATFORM_SHELL_NULL_REDIRECT
Shell null device/error redirect for platform.

Definition at line 709 of file abstraction.h.

◆ PLATFORM_THREAD_LOCAL

#define PLATFORM_THREAD_LOCAL   __thread

#include <thread.h>

Platform-specific thread-local storage declaration.

Use this macro to declare thread-local storage variables that are initialized once per thread with zero/null value.

Platform-specific behavior:

  • Windows: Uses __declspec(thread)
  • POSIX: Uses __thread
Example:
PLATFORM_THREAD_LOCAL bool g_in_callback = false;
#define PLATFORM_THREAD_LOCAL
Platform-specific thread-local storage declaration.
Note
Not compatible with dynamic TLS (ascii_tls_key_*). Use one or the other.
Static thread-local storage is allocated at program start.
Initialization is zero/null, per language spec.

Definition at line 83 of file include/ascii-chat/platform/thread.h.

◆ PLATFORM_WINDOWS

#define PLATFORM_WINDOWS   0

#include <abstraction.h>

Platform detection: 1 on Windows, 0 on POSIX.

Definition at line 93 of file abstraction.h.

◆ PROMPT_OPTS_DEFAULT

#define PROMPT_OPTS_DEFAULT   (prompt_opts_t){.echo = true, .same_line = false, .mask_char = 0}

#include <question.h>

Default prompt options (echo enabled, answer on next line)

Definition at line 40 of file question.h.

◆ PROMPT_OPTS_INLINE

#define PROMPT_OPTS_INLINE   (prompt_opts_t){.echo = true, .same_line = true, .mask_char = 0}

#include <question.h>

Prompt options for inline text input (echo enabled, same line)

Definition at line 50 of file question.h.

◆ PROMPT_OPTS_PASSWORD

#define PROMPT_OPTS_PASSWORD   (prompt_opts_t){.echo = false, .same_line = true, .mask_char = '*'}

#include <question.h>

Prompt options for password input (no echo, asterisk masking, same line)

Definition at line 45 of file question.h.

◆ rwlock_rdlock

#define rwlock_rdlock (   lock)     (debug_sync_is_initialized() ? debug_sync_rwlock_rdlock(lock, __FILE__, __LINE__, __func__) : rwlock_rdlock_impl(lock))

#include <rwlock.h>

Acquire a read lock (with debug tracking in debug builds)

Parameters
lockPointer to read-write lock

Acquires a shared read lock. Multiple threads can hold read locks simultaneously. Blocks if a write lock is held.

Note
In debug builds, this macro includes lock debugging if initialized. In release builds, calls the implementation directly for zero overhead.

Definition at line 294 of file rwlock.h.

295 : rwlock_rdlock_impl(lock))
int rwlock_rdlock_impl(rwlock_t *lock)
Acquire a read lock (implementation function)
Definition threading.c:70

◆ rwlock_rdunlock

#define rwlock_rdunlock (   lock)     (debug_sync_is_initialized() ? debug_sync_rwlock_rdunlock(lock, __FILE__, __LINE__, __func__) : rwlock_rdunlock_impl(lock))

#include <rwlock.h>

Release a read lock (with debug tracking in debug builds)

Parameters
lockPointer to read-write lock

Releases a shared read lock held by the calling thread.

Note
In debug builds, this macro includes lock debugging if initialized. In release builds, calls the implementation directly for zero overhead.

Definition at line 331 of file rwlock.h.

332 : rwlock_rdunlock_impl(lock))
int rwlock_rdunlock_impl(rwlock_t *lock)
Release a read lock (implementation function)
Definition threading.c:78

◆ rwlock_wrlock

#define rwlock_wrlock (   lock)     (debug_sync_is_initialized() ? debug_sync_rwlock_wrlock(lock, __FILE__, __LINE__, __func__) : rwlock_wrlock_impl(lock))

#include <rwlock.h>

Acquire a write lock (with debug tracking in debug builds)

Parameters
lockPointer to read-write lock

Acquires an exclusive write lock. Only one thread can hold a write lock, and it excludes all read locks. Blocks if any locks are held.

Note
In debug builds, this macro includes lock debugging if initialized. In release builds, calls the implementation directly for zero overhead.

Definition at line 313 of file rwlock.h.

314 : rwlock_wrlock_impl(lock))
int rwlock_wrlock_impl(rwlock_t *lock)
Acquire a write lock (implementation function)
Definition threading.c:74

◆ rwlock_wrunlock

#define rwlock_wrunlock (   lock)     (debug_sync_is_initialized() ? debug_sync_rwlock_wrunlock(lock, __FILE__, __LINE__, __func__) : rwlock_wrunlock_impl(lock))

#include <rwlock.h>

Release a write lock (with debug tracking in debug builds)

Parameters
lockPointer to read-write lock

Releases an exclusive write lock held by the calling thread.

Note
In debug builds, this macro includes lock debugging if initialized. In release builds, calls the implementation directly for zero overhead.

Definition at line 349 of file rwlock.h.

350 : rwlock_wrunlock_impl(lock))
int rwlock_wrunlock_impl(rwlock_t *lock)
Release a write lock (implementation function)
Definition threading.c:82

◆ SAFE_IGNORE_PRINTF_RESULT

#define SAFE_IGNORE_PRINTF_RESULT (   expr)    ((void)(expr))

#include <system.h>

Check return value of snprintf/fprintf and cast to void if needed

This macro satisfies clang-tidy cert-err33-c by explicitly handling the return value of printf family functions.

Definition at line 525 of file system.h.

◆ SOCKET_ERROR_AGAIN

#define SOCKET_ERROR_AGAIN   EAGAIN

#include <socket.h>

Definition at line 1084 of file socket.h.

◆ SOCKET_ERROR_INPROGRESS

#define SOCKET_ERROR_INPROGRESS   EINPROGRESS

#include <socket.h>

Definition at line 1083 of file socket.h.

◆ SOCKET_ERROR_WOULDBLOCK

#define SOCKET_ERROR_WOULDBLOCK   EWOULDBLOCK

#include <socket.h>

Definition at line 1082 of file socket.h.

◆ STATIC_COND_INIT

#define STATIC_COND_INIT   {.cond.impl = PTHREAD_COND_INITIALIZER, .cond.name = NULL, .initialized = 1}

#include <init.h>

Definition at line 109 of file include/ascii-chat/platform/init.h.

◆ STATIC_MUTEX_INIT

#define STATIC_MUTEX_INIT   {.mutex.impl = PTHREAD_MUTEX_INITIALIZER, .mutex.name = NULL, .initialized = 1}

#include <init.h>

Definition at line 107 of file include/ascii-chat/platform/init.h.

◆ STATIC_RWLOCK_INIT

#define STATIC_RWLOCK_INIT   {.lock.impl = PTHREAD_RWLOCK_INITIALIZER, .lock.name = NULL, .initialized = 1}

#include <init.h>

Definition at line 108 of file include/ascii-chat/platform/init.h.

◆ TERMINAL_COLOR_THEME_DARK_BG_B

#define TERMINAL_COLOR_THEME_DARK_BG_B   0

#include <terminal.h>

Definition at line 146 of file terminal.h.

◆ TERMINAL_COLOR_THEME_DARK_BG_G

#define TERMINAL_COLOR_THEME_DARK_BG_G   0

#include <terminal.h>

Definition at line 145 of file terminal.h.

◆ TERMINAL_COLOR_THEME_DARK_BG_R

#define TERMINAL_COLOR_THEME_DARK_BG_R   0

#include <terminal.h>

Default background color for dark theme (RGB)

Used for background in dark/black theme. Black background for dark terminals.

Definition at line 144 of file terminal.h.

◆ TERMINAL_COLOR_THEME_DARK_FG_B

#define TERMINAL_COLOR_THEME_DARK_FG_B   204

#include <terminal.h>

Definition at line 128 of file terminal.h.

◆ TERMINAL_COLOR_THEME_DARK_FG_G

#define TERMINAL_COLOR_THEME_DARK_FG_G   204

#include <terminal.h>

Definition at line 127 of file terminal.h.

◆ TERMINAL_COLOR_THEME_DARK_FG_R

#define TERMINAL_COLOR_THEME_DARK_FG_R   204

#include <terminal.h>

Default text color for dark theme (RGB)

Used for text on dark/black backgrounds. A light neutral color that's readable on dark backgrounds and provides good contrast.

Definition at line 126 of file terminal.h.

◆ TERMINAL_COLOR_THEME_LIGHT_BG_B

#define TERMINAL_COLOR_THEME_LIGHT_BG_B   255

#include <terminal.h>

Definition at line 137 of file terminal.h.

◆ TERMINAL_COLOR_THEME_LIGHT_BG_G

#define TERMINAL_COLOR_THEME_LIGHT_BG_G   255

#include <terminal.h>

Definition at line 136 of file terminal.h.

◆ TERMINAL_COLOR_THEME_LIGHT_BG_R

#define TERMINAL_COLOR_THEME_LIGHT_BG_R   255

#include <terminal.h>

Default background color for light theme (RGB)

Used for background in light/bright theme. White background for light terminals.

Definition at line 135 of file terminal.h.

◆ TERMINAL_COLOR_THEME_LIGHT_FG_B

#define TERMINAL_COLOR_THEME_LIGHT_FG_B   61

#include <terminal.h>

Definition at line 118 of file terminal.h.

◆ TERMINAL_COLOR_THEME_LIGHT_FG_G

#define TERMINAL_COLOR_THEME_LIGHT_FG_G   61

#include <terminal.h>

Definition at line 117 of file terminal.h.

◆ TERMINAL_COLOR_THEME_LIGHT_FG_R

#define TERMINAL_COLOR_THEME_LIGHT_FG_R   65

#include <terminal.h>

Platform-specific getopt include.

Provides unified getopt functionality across platforms:

Default text color for light theme (RGB)

Used for text on light/white backgrounds. A subtle dark blue-grey that's readable on light backgrounds and matches modern terminal color schemes.

Definition at line 116 of file terminal.h.

◆ VALIDATE_AGENT_PATH

#define VALIDATE_AGENT_PATH (   path,
  path_out,
  path_size,
  context 
)

#include <agent.h>

Value:
do { \
if (!path || strlen(path) == 0) { \
log_debug(context " not set"); \
return -1; \
} \
if (strlen(path) >= path_size) { \
log_error(context " path too long"); \
return -1; \
} \
SAFE_STRNCPY(path_out, path, path_size); \
return 0; \
} while (0)

Validate and copy agent socket path.

Parameters
pathSource path string
path_outOutput buffer
path_sizeOutput buffer size
contextDescription for error messages (e.g., "SSH_AUTH_SOCK")

Checks that path is not empty, fits in buffer, and copies it safely. Returns -1 on failure (logs error), returns 0 on success.

Definition at line 35 of file platform/agent.h.

36 { \
37 if (!path || strlen(path) == 0) { \
38 log_debug(context " not set"); \
39 return -1; \
40 } \
41 if (strlen(path) >= path_size) { \
42 log_error(context " path too long"); \
43 return -1; \
44 } \
45 SAFE_STRNCPY(path_out, path, path_size); \
46 return 0; \
47 } while (0)

Typedef Documentation

◆ asciichat_thread_t

typedef pthread_t asciichat_thread_t

#include <thread.h>

Thread handle type (POSIX: pthread_t)

Definition at line 42 of file include/ascii-chat/platform/thread.h.

◆ pipe_t

typedef int pipe_t

#include <pipe.h>

Pipe handle type (POSIX: int file descriptor)

Definition at line 40 of file pipe.h.

◆ platform_crash_handler_t

typedef void(* platform_crash_handler_t) (int signal, void *context)

#include <signal.h>

Callback function type for crash handlers.

Called when a critical signal (segfault, abort, etc.) is caught.

Parameters
signalSignal number (SIGSEGV, SIGABRT, etc.)
contextPlatform-specific context information (can be NULL)
Note
Callback should not perform blocking operations or allocate memory
On some platforms, only limited operations are safe in this callback

Definition at line 38 of file signal.h.

◆ platform_dir_foreach_cb

typedef bool(* platform_dir_foreach_cb) (const platform_dir_entry_t *entry, void *user_data)

#include <filesystem.h>

Callback for platform_dir_foreach()

Parameters
entryDirectory entry information
user_dataUser-provided context pointer
Returns
true to continue iteration, false to stop

Definition at line 420 of file filesystem.h.

◆ platform_mmap_t

#include <mmap.h>

Memory-mapped file handle.

Contains platform-specific handles and mapping information. Do not access members directly; use the platform_mmap_* functions.

◆ platform_process_t

typedef struct platform_process platform_process_t

#include <process.h>

Opaque process handle.

Platform-specific process representation:

  • Windows: PROCESS_INFORMATION-based handle
  • POSIX: PID and status tracking

Definition at line 136 of file process.h.

◆ signal_handler_t

typedef void(* signal_handler_t) (int)

#include <system.h>

Signal handler function type.

Parameters
sigSignal number

Definition at line 52 of file system.h.

◆ socket_t

typedef int socket_t

#include <socket.h>

Socket handle type (POSIX: int)

Definition at line 276 of file socket.h.

◆ thread_id_t

typedef pthread_t thread_id_t

#include <thread.h>

Thread ID type (POSIX: pthread_t)

Definition at line 44 of file include/ascii-chat/platform/thread.h.

◆ tls_key_t

typedef pthread_key_t tls_key_t

#include <thread.h>

Thread-local storage key type (POSIX: pthread_key_t)

Definition at line 46 of file include/ascii-chat/platform/thread.h.

Enumeration Type Documentation

◆ keyboard_key_t

#include <keyboard.h>

Unified keyboard key code enumeration.

Maps keyboard input to unified key codes. Arrow keys and special keys are abstracted across POSIX (escape sequences) and Windows (extended codes).

Return Value Ranges:

  • KEY_NONE (0): No key available
  • KEY_ESCAPE (27): Escape key
  • KEY_SPACE (32): Space bar
  • Arrow keys: KEY_UP/KEY_DOWN/KEY_LEFT/KEY_RIGHT (256-259)
  • Function keys: KEY_DELETE (260), KEY_HOME (261), KEY_END (262), KEY_CTRL_DELETE (263)
  • ASCII/UTF-8 characters: Raw character code (1-26, 33-127)
Note
UTF-8 multibyte sequences are not currently supported in return values. Regular ASCII input (0-127) is returned as-is; all other bytes return KEY_NONE.
Enumerator
KEY_NONE 

No key pressed or no input available.

KEY_ESCAPE 

Escape key (ESC)

KEY_SPACE 

Space bar.

KEY_UP 

Up arrow key.

KEY_DOWN 

Down arrow key.

KEY_LEFT 

Left arrow key.

KEY_RIGHT 

Right arrow key.

KEY_DELETE 

Delete key (forward delete)

KEY_HOME 

Home key (move to start of line)

KEY_END 

End key (move to end of line)

KEY_CTRL_DELETE 

Ctrl+Delete (delete word forward)

KEY_MINUS 

'-' key - toggle FPS counter

KEY_0 

'0' key - toggle matrix rain effect

KEY_C 

'c' key - cycle color modes

KEY_R 

'r' key - cycle render modes

KEY_M 

'm' key - toggle mute

KEY_F 

'f' key - flip webcam

KEY_QUESTION 

'?' key - show help screen

KEY_BACKTICK 

'‘’ key - print lock state (debug builds)

Definition at line 54 of file keyboard.h.

54 {
55 KEY_NONE = 0,
56 KEY_ESCAPE = 27,
57 KEY_SPACE = 32,
58 KEY_UP = 256,
59 KEY_DOWN = 257,
60 KEY_LEFT = 258,
61 KEY_RIGHT = 259,
62 KEY_DELETE = 260,
63 KEY_HOME = 261,
64 KEY_END = 262,
65 KEY_CTRL_DELETE = 263,
66 KEY_MINUS = '-',
67 KEY_0 = '0',
68 KEY_C = 'c',
69 KEY_R = 'r',
70 KEY_M = 'm',
71 KEY_F = 'f',
72 KEY_QUESTION = '?',
73 KEY_BACKTICK = '`',
keyboard_key_t
Unified keyboard key code enumeration.
Definition keyboard.h:54
@ KEY_SPACE
Space bar.
Definition keyboard.h:57
@ KEY_UP
Up arrow key.
Definition keyboard.h:58
@ KEY_M
'm' key - toggle mute
Definition keyboard.h:70
@ KEY_BACKTICK
'โ€˜โ€™ key - print lock state (debug builds)
Definition keyboard.h:73
@ KEY_ESCAPE
Escape key (ESC)
Definition keyboard.h:56
@ KEY_F
'f' key - flip webcam
Definition keyboard.h:71
@ KEY_MINUS
'-' key - toggle FPS counter
Definition keyboard.h:66
@ KEY_LEFT
Left arrow key.
Definition keyboard.h:60
@ KEY_RIGHT
Right arrow key.
Definition keyboard.h:61
@ KEY_0
'0' key - toggle matrix rain effect
Definition keyboard.h:67
@ KEY_NONE
No key pressed or no input available.
Definition keyboard.h:55
@ KEY_C
'c' key - cycle color modes
Definition keyboard.h:68
@ KEY_R
'r' key - cycle render modes
Definition keyboard.h:69
@ KEY_END
End key (move to end of line)
Definition keyboard.h:64
@ KEY_DOWN
Down arrow key.
Definition keyboard.h:59
@ KEY_QUESTION
'?' key - show help screen
Definition keyboard.h:72
@ KEY_HOME
Home key (move to start of line)
Definition keyboard.h:63
@ KEY_DELETE
Delete key (forward delete)
Definition keyboard.h:62
@ KEY_CTRL_DELETE
Ctrl+Delete (delete word forward)
Definition keyboard.h:65

◆ keyboard_line_edit_result_t

#include <keyboard.h>

Result codes for interactive line editing.

Return values for keyboard_read_line_interactive() indicating the current state of the editing session after processing one keystroke.

Enumerator
LINE_EDIT_CONTINUE 

Keep editing (more input needed)

LINE_EDIT_ACCEPTED 

User pressed Enter (accept input)

LINE_EDIT_CANCELLED 

User pressed Escape/Ctrl+C (cancel input)

LINE_EDIT_NO_INPUT 

No key available (non-blocking mode)

Definition at line 212 of file keyboard.h.

212 {
keyboard_line_edit_result_t
Result codes for interactive line editing.
Definition keyboard.h:212
@ LINE_EDIT_ACCEPTED
User pressed Enter (accept input)
Definition keyboard.h:214
@ LINE_EDIT_CANCELLED
User pressed Escape/Ctrl+C (cancel input)
Definition keyboard.h:215
@ LINE_EDIT_CONTINUE
Keep editing (more input needed)
Definition keyboard.h:213
@ LINE_EDIT_NO_INPUT
No key available (non-blocking mode)
Definition keyboard.h:216

Function Documentation

◆ ascii_tls_get()

void * ascii_tls_get ( tls_key_t  key)

#include <thread.h>

Get thread-local value for a key.

Parameters
keyTLS key
Returns
Thread-local value, or NULL if not set

Returns the thread-local value associated with the specified key for the calling thread.

Definition at line 105 of file threading.c.

105 {
106 return pthread_getspecific(key);
107}

Referenced by asciichat_instr_runtime_get(), asciichat_instr_runtime_global_destroy(), and mutex_stack_cleanup_current_thread().

◆ ascii_tls_key_create()

int ascii_tls_key_create ( tls_key_t *  key,
void(*)(void *)  destructor 
)

#include <thread.h>

Create a thread-local storage key.

Parameters
keyPointer to TLS key (output parameter)
destructorOptional destructor function called when thread exits (may be NULL)
Returns
0 on success, non-zero on error

Creates a new TLS key that can be used to store thread-specific data. If a destructor is provided, it will be called with the stored value when a thread terminates (if the value is non-NULL).

Definition at line 97 of file threading.c.

97 {
98 return pthread_key_create(key, destructor);
99}

◆ ascii_tls_key_delete()

int ascii_tls_key_delete ( tls_key_t  key)

#include <thread.h>

Delete a thread-local storage key.

Parameters
keyTLS key to delete
Returns
0 on success, non-zero on error

Deletes the specified TLS key. Does NOT call destructors for existing thread-local values. The caller is responsible for cleanup before deletion.

Definition at line 101 of file threading.c.

101 {
102 return pthread_key_delete(key);
103}

Referenced by asciichat_instr_runtime_global_destroy(), and mutex_stack_cleanup().

◆ ascii_tls_set()

int ascii_tls_set ( tls_key_t  key,
void *  value 
)

#include <thread.h>

Set thread-local value for a key.

Parameters
keyTLS key
valueValue to store
Returns
0 on success, non-zero on error

Associates the specified value with the key for the calling thread.

Definition at line 109 of file threading.c.

109 {
110 return pthread_setspecific(key, value);
111}

Referenced by asciichat_instr_runtime_get(), asciichat_instr_runtime_global_destroy(), and mutex_stack_cleanup_current_thread().

◆ asciichat_thread_create()

int asciichat_thread_create ( asciichat_thread_t *  thread,
const char *  name,
void *(*)(void *)  func,
void *  arg 
)

#include <thread.h>

Create a new named thread.

Parameters
threadPointer to thread handle (output parameter)
nameHuman-readable name for debugging (e.g., "audio_reader")
funcThread function to execute
argArgument to pass to thread function
Returns
0 on success, non-zero on error

Creates a new thread that executes the given function with the provided argument. The thread handle is stored in the thread parameter. The name is registered in the debug named registry for thread identification.

Threads created with this function automatically perform cleanup operations (like mutex stack cleanup) before exiting.

Definition at line 43 of file threading.c.

43 {
44 (void)name; // Unused in WASM - no thread registry
45 return pthread_create(thread, NULL, start_routine, arg);
46}

◆ asciichat_thread_current_id()

uint64_t asciichat_thread_current_id ( void  )

#include <thread.h>

Get the current thread's unique numeric ID.

Returns
Unique thread identifier as a 64-bit unsigned integer

Returns a unique numeric identifier for the current thread. This is more portable than thread_id_t for comparisons.

Definition at line 92 of file threading.c.

92 {
93 return (uint64_t)pthread_self();
94}
unsigned long long uint64_t
Definition common.h:59

Referenced by asciichat_instr_runtime_get(), cond_on_wait(), debug_sync_set_main_thread_id(), log_json_async_safe(), log_json_write(), mutex_on_lock(), mutex_on_trylock(), rwlock_on_unlock(), and rwlock_on_wrlock().

◆ asciichat_thread_detach()

int asciichat_thread_detach ( asciichat_thread_t *  thread)

#include <thread.h>

Detach a thread so its resources are reclaimed when it exits.

Parameters
threadPointer to thread handle
Returns
0 on success, non-zero on failure

Definition at line 52 of file threading.c.

52 {
53 return pthread_detach(*thread);
54}

◆ asciichat_thread_equal()

int asciichat_thread_equal ( thread_id_t  t1,
thread_id_t  t2 
)

#include <thread.h>

Compare two thread IDs for equality.

Parameters
t1First thread ID
t2Second thread ID
Returns
Non-zero if thread IDs are equal, 0 otherwise

Definition at line 60 of file threading.c.

60 {
61 return pthread_equal(t1, t2);
62}

◆ asciichat_thread_exit()

void asciichat_thread_exit ( void *  retval)

#include <thread.h>

Exit the current thread.

Parameters
retvalReturn value to pass to thread joiner (or NULL)

Terminates the calling thread and optionally passes a return value to any thread waiting to join.

◆ asciichat_thread_init()

void asciichat_thread_init ( asciichat_thread_t *  thread)

#include <thread.h>

Initialize a thread handle to an uninitialized state.

Parameters
threadPointer to thread handle

Sets the thread handle to an uninitialized state. Useful for static initialization or resetting a thread handle.

Referenced by discover_session_parallel(), parallel_connect(), and stop_client_render_threads().

◆ asciichat_thread_is_initialized()

bool asciichat_thread_is_initialized ( asciichat_thread_t *  thread)

#include <thread.h>

Check if a thread handle has been initialized.

Parameters
threadPointer to thread handle
Returns
true if thread is initialized, false otherwise

Definition at line 18 of file stubs/threading.c.

18 {
19 (void)thread;
20 return true;
21}

Referenced by discover_session_parallel(), parallel_connect(), remove_client(), session_pipeline_destroy(), stop_client_render_threads(), and stop_client_threads().

◆ asciichat_thread_join()

int asciichat_thread_join ( asciichat_thread_t *  thread,
void **  retval 
)

#include <thread.h>

Wait for a thread to complete (blocking)

Parameters
threadThread handle to wait for
retvalPointer to store thread return value (or NULL to ignore)
Returns
0 on success, non-zero on error

Blocks the calling thread until the specified thread terminates.

Definition at line 48 of file threading.c.

48 {
49 return pthread_join(*thread, retval);
50}

◆ asciichat_thread_join_timeout()

int asciichat_thread_join_timeout ( asciichat_thread_t *  thread,
void **  retval,
uint64_t  timeout_ns 
)

#include <thread.h>

Wait for a thread to complete with timeout.

Parameters
threadThread handle to wait for
retvalPointer to store thread return value (or NULL to ignore)
timeout_nsTimeout in nanoseconds
Returns
0 on success, non-zero on timeout or error

Waits for the specified thread to terminate, with a maximum wait time. Returns non-zero if the timeout expires before the thread completes.

Definition at line 11 of file stubs/threading.c.

11 {
12 (void)thread;
13 (void)retval;
14 (void)timeout_ns;
15 return 0; // Success - no-op in WASM
16}

Referenced by audio_start_thread(), debug_sync_cleanup_thread(), discovery_session_destroy(), ffmpeg_decoder_stop_prefetch(), session_pipeline_destroy(), session_server_like_run(), splash_wait_for_animation(), thread_pool_stop_all(), and update_banner_wait_for_check().

◆ asciichat_thread_self()

thread_id_t asciichat_thread_self ( void  )

#include <thread.h>

Get the current thread's ID.

Returns
Thread ID of the calling thread

Returns a platform-specific thread identifier for the calling thread.

Definition at line 56 of file threading.c.

56 {
57 return pthread_self();
58}

◆ asciichat_thread_set_realtime_priority()

asciichat_error_t asciichat_thread_set_realtime_priority ( void  )

#include <thread.h>

Set the current thread to real-time priority.

Returns
ASCIICHAT_OK on success, error code on failure

Attempts to set the current thread to real-time priority for time-critical operations like audio processing.

Platform-specific implementations:

  • Linux: Uses pthread_setschedparam() with SCHED_FIFO at priority 80
  • macOS: Uses thread_policy_set() with THREAD_TIME_CONSTRAINT_POLICY
  • Windows: Uses SetThreadPriority() with THREAD_PRIORITY_TIME_CRITICAL
Note
On Linux, requires CAP_SYS_NICE capability or rtprio resource limit
On Windows, does not require special privileges
On macOS, requires mach_thread_self() to work

Referenced by audio_set_realtime_priority().

◆ asciichat_thread_to_key()

uintptr_t asciichat_thread_to_key ( asciichat_thread_t  thread)

#include <thread.h>

Convert a thread handle to a uintptr_t registry key.

Parameters
threadThread handle (asciichat_thread_t)
Returns
uintptr_t representation suitable for pointer/ID lookups

Platform-specific conversion for use with registry systems (e.g., named object registry). On POSIX, pthread_t is cast directly to uintptr_t. On Windows, HANDLE is cast directly to uintptr_t.

Convert a thread handle to a uintptr_t registry key.

Parameters
threadThread handle (asciichat_thread_t)
Returns
uintptr_t suitable for registry lookups

Platform-specific conversion. On POSIX, pthread_t is cast directly. On Windows, HANDLE is cast directly.

This function is defined in lib/platform/posix/thread.c or lib/platform/windows/thread.c

Definition at line 87 of file threading.c.

87 {
88 return (uintptr_t)thread;
89}

Referenced by cond_on_wait(), log_template_apply(), mutex_on_lock(), mutex_on_trylock(), named_describe_thread(), rwlock_on_unlock(), and rwlock_on_wrlock().

◆ asciichat_thread_wrapper_impl()

void * asciichat_thread_wrapper_impl ( void *  arg)

#include <thread.h>

Internal thread wrapper function that executes user code with cleanup.

This function is called internally by pthread_create to wrap user threads with automatic cleanup. It calls the user's thread function, then performs cleanup operations (mutex stack cleanup) before exiting.

Parameters
argPointer to asciichat_thread_wrapper_t (allocated by thread_create)
Returns
Return value from user thread function

◆ backtrace_print_simple()

void backtrace_print_simple ( int  skip_frames)

#include <backtrace.h>

Print backtrace of the current call stack.

Captures the current call stack and prints it to stderr using platform_backtrace_symbols(). Useful for debugging crashes and errors.

Parameters
skip_framesNumber of frames to skip from the top (0 = include all)

Definition at line 54 of file platform/wasm/stubs/backtrace.c.

54 {
55 (void)skip_frames;
56 // No-op for WASM
57}

Referenced by main().

◆ check_binary_in_path_uncached()

bool check_binary_in_path_uncached ( const char *  bin_name)

#include <filesystem.h>

Check if a binary is in PATH (uncached, platform-specific implementation)

Internal function called by platform_is_binary_in_path(). Defined in posix/filesystem.c (Unix/Linux/macOS) or windows/filesystem.c (Windows).

Parameters
bin_nameBase name of the binary to search for
Returns
true if binary found and executable, false otherwise

◆ cond_broadcast()

int cond_broadcast ( cond_t *  cond)

#include <cond.h>

Broadcast to a condition variable (wake all waiting threads)

Parameters
condPointer to condition variable to broadcast
Returns
0 on success, non-zero on error

Wakes up all threads that are waiting on the condition variable. If no threads are waiting, the broadcast has no effect.

Definition at line 52 of file stubs/threading.c.

52 {
53 (void)cond;
54 return 0;
55}

Referenced by debug_sync_cond_broadcast(), session_pipeline_destroy(), and thread_pool_stop_all().

◆ cond_destroy()

int cond_destroy ( cond_t *  cond)

#include <cond.h>

Destroy a condition variable.

Parameters
condPointer to condition variable to destroy
Returns
0 on success, non-zero on error

Destroys the condition variable and frees any associated resources. No threads should be waiting on the condition variable when this is called.

Definition at line 42 of file stubs/threading.c.

42 {
43 (void)cond;
44 return 0;
45}

Referenced by acip_webrtc_transport_create(), acip_websocket_client_transport_create(), acip_websocket_server_transport_create(), app_client_destroy(), audio_destroy(), audio_init(), audio_sender_finalize(), discover_session_parallel(), ffmpeg_decoder_create(), ffmpeg_decoder_create_stdin(), ffmpeg_decoder_destroy(), parallel_connect(), remove_client(), and thread_pool_destroy().

◆ cond_format_state()

int cond_format_state ( const cond_t *  cond,
char *  buffer,
size_t  size 
)

#include <cond.c>

Format condition variable timing and state info into buffer.

Parameters
condPointer to the condition variable
bufferOutput buffer
sizeBuffer size
Returns
Number of bytes written

Formats timing (wait/signal/broadcast), waiting threads, and operation counts. Called by cond_log_state() and –sync-state display code.

Definition at line 95 of file cond.c.

95 {
96 if (!cond || !buffer || size == 0)
97 return 0;
98
99 int offset = 0;
100 uint64_t now_ns = time_get_ns();
101
102 char wait_str[64] = "";
103 char signal_str[64] = "";
104 char broadcast_str[64] = "";
105 char waiting_str[64] = "";
106 char count_str[256] = "";
107
108 if (cond->last_wait_time_ns > 0 && cond->last_wait_time_ns <= now_ns) {
109 char elapsed_str[64];
110 time_pretty(now_ns - cond->last_wait_time_ns, -1, elapsed_str, sizeof(elapsed_str));
111 snprintf(wait_str, sizeof(wait_str), "wait=%s", elapsed_str);
112 }
113
114 if (cond->last_signal_time_ns > 0 && cond->last_signal_time_ns <= now_ns) {
115 char elapsed_str[64];
116 time_pretty(now_ns - cond->last_signal_time_ns, -1, elapsed_str, sizeof(elapsed_str));
117 snprintf(signal_str, sizeof(signal_str), "signal=%s", elapsed_str);
118 }
119
120 if (cond->last_broadcast_time_ns > 0 && cond->last_broadcast_time_ns <= now_ns) {
121 char elapsed_str[64];
122 time_pretty(now_ns - cond->last_broadcast_time_ns, -1, elapsed_str, sizeof(elapsed_str));
123 snprintf(broadcast_str, sizeof(broadcast_str), "broadcast=%s", elapsed_str);
124 }
125
126 uint64_t waiting_count = atomic_load_u64(&cond->waiting_count);
127 if (waiting_count > 0) {
128 snprintf(waiting_str, sizeof(waiting_str), "[WAITING=%llu]", (unsigned long long)waiting_count);
129 }
130
131 if (cond->wait_count > 0 || cond->signal_count > 0 || cond->broadcast_count > 0) {
132 snprintf(count_str, sizeof(count_str), "[ops: wait=%llu signal=%llu broadcast=%llu]",
133 (unsigned long long)cond->wait_count, (unsigned long long)cond->signal_count,
134 (unsigned long long)cond->broadcast_count);
135 }
136
137 offset += snprintf(buffer + offset, size - offset, "%s %s %s %s %s", wait_str, signal_str, broadcast_str, waiting_str,
138 count_str);
139 return offset;
140}
uint64_t atomic_load_u64(atomic_t *a)
Atomically load a uint64_t value.
Definition atomic.c:233
uint64_t time_get_ns(void)
Get current monotonic time in nanoseconds.
Definition util/time.c:108
int time_pretty(uint64_t nanoseconds, int decimals, char *buffer, size_t buffer_size)
Format nanoseconds as pretty duration with spaces and configurable precision.
Definition util/time.c:424
uint64_t last_wait_time_ns
Timestamp of last wait (nanoseconds)
Definition cond.h:70
uint64_t broadcast_count
Total broadcast calls.
Definition cond.h:78
uint64_t last_broadcast_time_ns
Timestamp of last broadcast (nanoseconds)
Definition cond.h:69
uint64_t last_signal_time_ns
Timestamp of last signal (nanoseconds)
Definition cond.h:68
atomic_t waiting_count
Number of threads currently waiting (functional - needed for all builds)
Definition cond.h:66
uint64_t signal_count
Total signal calls.
Definition cond.h:77
uint64_t wait_count
Total wait calls.
Definition cond.h:76

References atomic_load_u64(), cond_t::broadcast_count, cond_t::last_broadcast_time_ns, cond_t::last_signal_time_ns, cond_t::last_wait_time_ns, cond_t::signal_count, time_get_ns(), time_pretty(), cond_t::wait_count, and cond_t::waiting_count.

Referenced by cond_log_state().

◆ cond_init()

int cond_init ( cond_t *  cond,
const char *  name 
)

#include <cond.h>

Initialize a condition variable with a name.

Parameters
condPointer to condition variable to initialize
nameHuman-readable name for debugging (e.g., "audio_ready")
Returns
0 on success, non-zero on error

Initializes the condition variable for use. Must be called before any other condition variable operations. The name is stored for debugging and automatically suffixed with a unique counter.

Definition at line 36 of file stubs/threading.c.

36 {
37 (void)cond;
38 (void)name;
39 return 0;
40}

Referenced by acip_webrtc_transport_create(), acip_websocket_client_transport_create(), acip_websocket_server_transport_create(), add_webrtc_client(), app_client_create(), audio_init(), audio_sender_init(), debug_sync_start_thread(), discover_session_parallel(), ffmpeg_decoder_create(), ffmpeg_decoder_create_stdin(), parallel_connect(), and thread_pool_create_with_workers().

◆ cond_log_state()

void cond_log_state ( const cond_t *  cond,
const char *  file,
int  line,
const char *  func 
)

#include <cond.c>

Log the state of a condition variable.

Format and log the current state of a condition variable.

Parameters
condPointer to the condition variable
fileSource file of the caller
lineSource line of the caller
funcSource function of the caller

Formats and logs detailed state information about the condition variable.

Parameters
condPointer to the condition variable
fileSource file of the caller
lineSource line of the caller
funcSource function of the caller

Logs detailed state information about the condition variable (timing, counts, waiting threads) at the specified location. Debug builds only; no-op in release builds.

Definition at line 153 of file cond.c.

153 {
154 if (!cond)
155 return;
156 char buf[512];
157 cond_format_state(cond, buf, sizeof(buf));
158 log_msg(LOG_DEBUG, file, line, func, "cond/state %p: %s", (const void *)cond, buf);
159}
void log_msg(log_level_t level, const char *file, int line, const char *func, const char *fmt,...)
Log a message at a specific level.
Definition log/log.c:1019
int cond_format_state(const cond_t *cond, char *buffer, size_t size)
Format condition variable timing and state info into buffer.
Definition cond.c:95
#define LOG_DEBUG
Definition types.h:39

References cond_format_state(), LOG_DEBUG, and log_msg().

◆ cond_on_broadcast()

void cond_on_broadcast ( cond_t *  cond)

#include <cond.c>

Hook called when a condition variable is broadcast.

Parameters
condPointer to the condition variable being broadcast

Called by platform-specific cond_broadcast() after waking all threads. Records timing and broadcasts count.

Parameters
condPointer to the condition variable being broadcast

Called by platform-specific implementations after waking all threads. Records timing and other diagnostic data (debug builds only).

Definition at line 75 of file cond.c.

75 {
76 if (!cond)
77 return;
79 cond->broadcast_count++;
81}
void atomic_store_u64(atomic_t *a, uint64_t value)
Atomically store a uint64_t value.
Definition atomic.c:241

References atomic_store_u64(), cond_t::broadcast_count, cond_t::last_broadcast_time_ns, time_get_ns(), and cond_t::waiting_count.

◆ cond_on_signal()

void cond_on_signal ( cond_t *  cond)

#include <cond.c>

Hook called when a condition variable is signaled.

Parameters
condPointer to the condition variable being signaled

Called by platform-specific cond_signal() after waking one thread. Records timing and decrements waiting count.

Parameters
condPointer to the condition variable being signaled

Called by platform-specific implementations after waking one thread. Records timing and other diagnostic data (debug builds only).

Definition at line 56 of file cond.c.

56 {
57 if (!cond)
58 return;
60 cond->signal_count++;
61 if (atomic_load_u64(&cond->waiting_count) > 0) {
63 }
64}
uint64_t atomic_fetch_sub_u64(atomic_t *a, uint64_t delta)
Atomically subtract from a uint64_t and return the previous value.
Definition atomic.c:256

References atomic_fetch_sub_u64(), atomic_load_u64(), cond_t::last_signal_time_ns, cond_t::signal_count, time_get_ns(), and cond_t::waiting_count.

◆ cond_on_wait()

void cond_on_wait ( cond_t *  cond,
mutex_t *  mutex,
const char *  file,
int  line,
const char *  func 
)

#include <cond.c>

Hook called when a thread waits on a condition variable.

Parameters
condPointer to the condition variable being waited on
mutexPointer to the associated mutex
fileSource file of the wait callsite (for deadlock detection)
lineSource line of the wait callsite (for deadlock detection)
funcSource function of the wait callsite (for deadlock detection)

Called by platform-specific cond_wait() or cond_timedwait() before blocking. Records timing, callsite information, and increments wait count.

Parameters
condPointer to the condition variable being waited on
mutexPointer to the associated mutex
fileSource file of the wait callsite (for deadlock detection)
lineSource line of the wait callsite (for deadlock detection)
funcSource function of the wait callsite (for deadlock detection)

Called by platform-specific implementations before blocking on wait. Records timing, callsite information, and associated mutex for deadlock detection (debug builds only).

Definition at line 33 of file cond.c.

33 {
34 if (!cond)
35 return;
37 cond->last_wait_mutex = mutex;
38 cond->last_wait_file = file;
39 cond->last_wait_line = line;
40 cond->last_wait_func = func;
42 cond->last_waiting_key = asciichat_thread_to_key(current_thread);
44 cond->wait_count++;
45}
uint64_t atomic_fetch_add_u64(atomic_t *a, uint64_t delta)
Atomically add to a uint64_t and return the previous value.
Definition atomic.c:248
uintptr_t asciichat_thread_to_key(asciichat_thread_t thread)
Convert a thread handle to a registry key.
Definition threading.c:87
uint64_t asciichat_thread_current_id(void)
Get the current thread's unique numeric ID.
Definition threading.c:92
void * asciichat_thread_t
mutex_t * last_wait_mutex
Associated mutex at most recent wait (for deadlock detection)
Definition cond.h:72
uintptr_t last_waiting_key
Registry key of most recent waiter.
Definition cond.h:71
const char * last_wait_file
Callsite file of most recent cond_wait (for deadlock detection)
Definition cond.h:73
int last_wait_line
Callsite line of most recent cond_wait (for deadlock detection)
Definition cond.h:74
const char * last_wait_func
Callsite function of most recent cond_wait (for deadlock detection)
Definition cond.h:75

References asciichat_thread_current_id(), asciichat_thread_to_key(), atomic_fetch_add_u64(), cond_t::last_wait_file, cond_t::last_wait_func, cond_t::last_wait_line, cond_t::last_wait_mutex, cond_t::last_wait_time_ns, cond_t::last_waiting_key, mutex, time_get_ns(), cond_t::wait_count, and cond_t::waiting_count.

◆ cond_signal()

int cond_signal ( cond_t *  cond)

#include <cond.h>

Signal a condition variable (wake one waiting thread)

Parameters
condPointer to condition variable to signal
Returns
0 on success, non-zero on error

Wakes up one thread that is waiting on the condition variable. If no threads are waiting, the signal is lost.

Definition at line 47 of file stubs/threading.c.

47 {
48 (void)cond;
49 return 0;
50}

Referenced by audio_destroy(), audio_sender_cleanup(), audio_stop_duplex(), audio_stop_thread(), client_receive_thread(), debug_sync_cleanup_thread(), debug_sync_cond_signal(), debug_sync_print_backtrace_delayed(), debug_sync_print_state_delayed(), debug_sync_trigger_print(), ffmpeg_decoder_seek_to_timestamp(), and thread_pool_queue_work().

◆ cond_timedwait_impl()

int cond_timedwait_impl ( cond_t *  cond,
mutex_t *  mutex,
uint64_t  timeout_ns 
)

#include <cond.h>

Wait on a condition variable with timeout - implementation function.

Parameters
condPointer to condition variable to wait on
mutexPointer to mutex that must be locked by the calling thread
timeout_msTimeout in milliseconds
Returns
0 if condition was signaled, non-zero on timeout or error

Atomically unlocks the mutex and waits on the condition variable with a timeout. Returns non-zero if the timeout expires before the condition is signaled.

Warning
The mutex must be locked before calling this function.
Note
Use cond_timedwait() macro instead of calling this directly

Definition at line 63 of file stubs/threading.c.

63 {
64 (void)cond;
65 (void)mutex;
66 (void)timeout_ns;
67 return 0;
68}

References mutex.

Referenced by debug_sync_cond_timedwait().

◆ cond_wait_impl()

int cond_wait_impl ( cond_t *  cond,
mutex_t *  mutex 
)

#include <cond.h>

Wait on a condition variable (blocking) - implementation function.

Parameters
condPointer to condition variable to wait on
mutexPointer to mutex that must be locked by the calling thread
Returns
0 on success, non-zero on error

Atomically unlocks the mutex and waits on the condition variable. The mutex must be locked by the calling thread before calling this function. Upon return, the mutex will be locked again.

Warning
The mutex must be locked before calling this function.
Note
Use cond_wait() macro instead of calling this directly

Definition at line 57 of file stubs/threading.c.

57 {
58 (void)cond;
59 (void)mutex;
60 return 0;
61}

References mutex.

Referenced by debug_sync_cond_wait().

◆ config_file_list_destroy()

void config_file_list_destroy ( config_file_list_t *  list)

#include <filesystem.h>

Free config file list resources.

Releases all allocated memory in a config file list result. Safe to call with NULL or empty lists.

Parameters
listPointer to config_file_list_t to free
Note
This function always succeeds; no error checking needed
Safe to call multiple times with the same list
Safe to call with list->count == 0

Definition at line 55 of file wasm/stubs/filesystem.c.

55 {
56 (void)list;
57 // No-op
58}

Referenced by check_known_host(), and config_load_system_and_user().

◆ debug_sync_cond_timedwait()

int debug_sync_cond_timedwait ( cond_t *  cond,
mutex_t *  mutex,
uint64_t  timeout_ns,
const char *  file,
int  line,
const char *  func 
)

#include <cond.h>

Definition at line 797 of file sync.c.

798 {
799 // Same as debug_sync_cond_wait(): skip tracking the atomic unlock/relock.
800 (void)file_name;
801 (void)line_number;
802 (void)function_name;
803 return cond_timedwait_impl(cond, mutex, timeout_ns);
804}

References cond_timedwait_impl(), and mutex.

◆ debug_sync_cond_wait()

int debug_sync_cond_wait ( cond_t *  cond,
mutex_t *  mutex,
const char *  file,
int  line,
const char *  func 
)

#include <cond.h>

Definition at line 786 of file sync.c.

787 {
788 // Note: pthread_cond_wait() atomically releases and re-acquires the mutex,
789 // but this happens at the kernel level. Debug tracking can't monitor this atomic
790 // operation properly, so we skip cond_on_wait() to avoid false deadlock reports.
791 (void)file_name;
792 (void)line_number;
793 (void)function_name;
794 return cond_wait_impl(cond, mutex);
795}

References cond_wait_impl(), and mutex.

◆ debug_sync_is_initialized()

bool debug_sync_is_initialized ( void  )

#include <cond.h>

Definition at line 820 of file sync.c.

820 {
821 return true;
822}

Referenced by stats_logger_thread().

◆ debug_sync_mutex_lock()

int debug_sync_mutex_lock ( mutex_t *  mutex,
const char *  file_name,
int  line_number,
const char *  function_name 
)

#include <mutex.h>

Definition at line 737 of file sync.c.

737 {
738 (void)file_name;
739 (void)line_number;
740 (void)function_name;
741 return mutex_lock_impl(mutex);
742}

References mutex, and mutex_lock_impl().

◆ debug_sync_mutex_trylock()

int debug_sync_mutex_trylock ( mutex_t *  mutex,
const char *  file_name,
int  line_number,
const char *  function_name 
)

#include <mutex.h>

Definition at line 744 of file sync.c.

744 {
745 (void)file_name;
746 (void)line_number;
747 (void)function_name;
749}

References mutex, and mutex_trylock_impl().

◆ debug_sync_mutex_unlock()

int debug_sync_mutex_unlock ( mutex_t *  mutex,
const char *  file_name,
int  line_number,
const char *  function_name 
)

#include <mutex.h>

Definition at line 751 of file sync.c.

751 {
752 (void)file_name;
753 (void)line_number;
754 (void)function_name;
755 return mutex_unlock_impl(mutex);
756}

References mutex, and mutex_unlock_impl().

◆ debug_sync_rwlock_rdlock()

int debug_sync_rwlock_rdlock ( rwlock_t *  rwlock,
const char *  file_name,
int  line_number,
const char *  function_name 
)

#include <rwlock.h>

Definition at line 758 of file sync.c.

758 {
759 (void)file_name;
760 (void)line_number;
761 (void)function_name;
762 return rwlock_rdlock_impl(lock);
763}

References rwlock_rdlock_impl().

◆ debug_sync_rwlock_rdunlock()

int debug_sync_rwlock_rdunlock ( rwlock_t *  rwlock,
const char *  file_name,
int  line_number,
const char *  function_name 
)

#include <rwlock.h>

Definition at line 765 of file sync.c.

765 {
766 (void)file_name;
767 (void)line_number;
768 (void)function_name;
769 return rwlock_rdunlock_impl(lock);
770}

References rwlock_rdunlock_impl().

◆ debug_sync_rwlock_wrlock()

int debug_sync_rwlock_wrlock ( rwlock_t *  rwlock,
const char *  file_name,
int  line_number,
const char *  function_name 
)

#include <rwlock.h>

Definition at line 772 of file sync.c.

772 {
773 (void)file_name;
774 (void)line_number;
775 (void)function_name;
776 return rwlock_wrlock_impl(lock);
777}

References rwlock_wrlock_impl().

◆ debug_sync_rwlock_wrunlock()

int debug_sync_rwlock_wrunlock ( rwlock_t *  rwlock,
const char *  file_name,
int  line_number,
const char *  function_name 
)

#include <rwlock.h>

Definition at line 779 of file sync.c.

779 {
780 (void)file_name;
781 (void)line_number;
782 (void)function_name;
783 return rwlock_wrunlock_impl(lock);
784}

References rwlock_wrunlock_impl().

◆ file_is_readable()

bool file_is_readable ( const char *  path)

#include <filesystem.h>

Check if file is readable.

Tests whether the file exists and can be read by the current process.

Parameters
pathFile path to check
Returns
true if file is readable, false otherwise

◆ file_is_writable()

bool file_is_writable ( const char *  path)

#include <filesystem.h>

Check if file is writable.

Tests whether the file can be written by the current process. Returns true even if file doesn't exist (assumes directory is writable).

Parameters
pathFile path to check
Returns
true if file is writable, false otherwise

◆ file_read_error_message()

const char * file_read_error_message ( const char *  path)

#include <filesystem.h>

Get human-readable error message for file read failure.

Checks errno and provides specific error messages for common cases:

  • ENOENT: File does not exist
  • EACCES: Permission denied
  • EISDIR: Is a directory, not a file
  • Other: Generic error with errno description
Parameters
pathFile path that failed to open
Returns
Static string describing the error (do not free)

Definition at line 24 of file filesystem.c.

24 {
25 switch (errno) {
26 case ENOENT:
27 snprintf(error_msg_buffer, sizeof(error_msg_buffer), "File does not exist: %s", path);
28 break;
29 case EACCES:
30 snprintf(error_msg_buffer, sizeof(error_msg_buffer), "Permission denied (cannot read): %s", path);
31 break;
32 case EISDIR:
33 snprintf(error_msg_buffer, sizeof(error_msg_buffer), "Is a directory, not a file: %s", path);
34 break;
35 default:
36 snprintf(error_msg_buffer, sizeof(error_msg_buffer), "Failed to open for reading: %s (%s)", path,
38 break;
39 }
40 return error_msg_buffer;
41}
const char * platform_strerror(int errnum)
Get human-readable error message for a system error code.
Definition wasm/system.c:46
int errno

References errno, and platform_strerror().

Referenced by update_check_load_cache().

◆ file_write_error_message()

const char * file_write_error_message ( const char *  path)

#include <filesystem.h>

Get human-readable error message for file write failure.

Checks errno and provides specific error messages for common cases:

  • ENOENT: Directory does not exist
  • EACCES: Permission denied
  • EROFS: Read-only filesystem
  • ENOSPC: No space left on device
  • EISDIR: Is a directory, not a file
  • Other: Generic error with errno description
Parameters
pathFile path that failed to open
Returns
Static string describing the error (do not free)

Definition at line 43 of file filesystem.c.

43 {
44 switch (errno) {
45 case ENOENT:
46 snprintf(error_msg_buffer, sizeof(error_msg_buffer), "Directory does not exist: %s", path);
47 break;
48 case EACCES:
49 snprintf(error_msg_buffer, sizeof(error_msg_buffer), "Permission denied (cannot write): %s", path);
50 break;
51 case EROFS:
52 snprintf(error_msg_buffer, sizeof(error_msg_buffer), "Read-only filesystem: %s", path);
53 break;
54 case ENOSPC:
55 snprintf(error_msg_buffer, sizeof(error_msg_buffer), "No space left on device: %s", path);
56 break;
57 case EISDIR:
58 snprintf(error_msg_buffer, sizeof(error_msg_buffer), "Is a directory, not a file: %s", path);
59 break;
60 default:
61 snprintf(error_msg_buffer, sizeof(error_msg_buffer), "Failed to open for writing: %s (%s)", path,
63 break;
64 }
65 return error_msg_buffer;
66}

References errno, and platform_strerror().

Referenced by update_check_save_cache().

◆ get_binary_file_address_offsets()

int get_binary_file_address_offsets ( const void *  addr,
platform_binary_match_t *  matches,
int  max_matches 
)

#include <system.h>

Get binary that contains address on Linux via /proc/self/maps.

Example:
int count = get_binary_file_address_offsets(backtrace_address, matches, 2);
for (int i = 0; i < count; i++) {
// Call llvm-symbolizer with matches[i].path and matches[i].file_offset
printf("Address %p is in %s at offset %lx\n",
backtrace_address, matches[i].path, matches[i].file_offset);
}
int get_binary_file_address_offsets(const void *addr, platform_binary_match_t *matches, int max_matches)
Get binary that contains address on Linux via /proc/self/maps.
Find which binary(ies) contain a given address.
Definition system.h:777

Scans /proc/self/maps to find which loaded binary (exe or .so) contains the given runtime address. Returns the file offset within that binary, which is passed to llvm-symbolizer for symbol resolution.

Parameters
addrRuntime address from backtrace
matchesOutput array for matches (path, offset, is_project_lib)
max_matchesMaximum number of matches to store
Returns
Number of matches found (0, 1, or rarely 2)

Get binary that contains address on Linux via /proc/self/maps.

Iterates through dyld-loaded images to find which one contains the given runtime address. Returns the file offset within that image, which is passed to llvm-symbolizer for symbol resolution.

Parameters
addrRuntime address from backtrace
matchesOutput array for matches (path, offset, is_project_lib)
max_matchesMaximum number of matches to store
Returns
Number of matches found (0, 1, or rarely 2)

Definition at line 26 of file linux/system.c.

26 {
27 int count = 0;
28 uintptr_t addr_int = (uintptr_t)addr;
29
30 FILE *maps = fopen("/proc/self/maps", "r");
31 if (!maps) {
32 return 0;
33 }
34
35 char line[512];
36 while (fgets(line, sizeof(line), maps) && count < max_matches) {
37 char perms[5], device[10], path[PLATFORM_MAX_PATH_LENGTH];
38
39 // Parse: start-end perms offset device inode path
40 // Example: 7f3a2b1c0000-7f3a2b1c1000 r-xp 00000000 08:02 12345678 /usr/lib/libsodium.so.23
41 char *p = line;
42 char *end_ptr;
43
44 uintptr_t start = (uintptr_t)strtoul(p, &end_ptr, 16);
45 if (end_ptr == p || *end_ptr != '-') {
46 continue;
47 }
48 p = end_ptr + 1;
49
50 uintptr_t end = (uintptr_t)strtoul(p, &end_ptr, 16);
51 if (end_ptr == p || *end_ptr != ' ') {
52 continue;
53 }
54 p = end_ptr + 1;
55
56 // Parse perms field (4 chars like "r-xp")
57 if (strlen(p) < 4) {
58 continue;
59 }
60 memcpy(perms, p, 4);
61 perms[4] = '\0';
62 p += 4;
63 if (*p != ' ') {
64 continue;
65 }
66 p++;
67
68 uintptr_t offset = (uintptr_t)strtoul(p, &end_ptr, 16);
69 if (end_ptr == p || *end_ptr != ' ') {
70 continue;
71 }
72 p = end_ptr + 1;
73
74 // Parse device field (like "08:02")
75 char *space = strchr(p, ' ');
76 if (!space || (size_t)(space - p) >= sizeof(device)) {
77 continue;
78 }
79 memcpy(device, p, (size_t)(space - p));
80 device[space - p] = '\0';
81 p = space + 1;
82
83 // Skip whitespace and inode field
84 while (*p == ' ')
85 p++;
86 strtoul(p, &end_ptr, 10);
87 if (end_ptr == p) {
88 continue;
89 }
90
91 // Skip lines without executable flag
92 if (perms[2] != 'x') {
93 continue;
94 }
95
96 // Extract path more robustly by searching for the last whitespace-separated token
97 // /proc/self/maps format: start-end perms offset device inode [path]
98 // The path is optional and starts after the inode field
99 path[0] = '\0';
100
101 // Find the last whitespace and take everything after it as the path
102 // This is more robust than trying to count fields
103 int line_len = strlen(line);
104 int path_start = -1;
105
106 // Scan from the end backwards to find the last non-whitespace character
107 for (int i = line_len - 1; i >= 0; i--) {
108 if (line[i] != ' ' && line[i] != '\t' && line[i] != '\n' && line[i] != '\r') {
109 // Found a non-whitespace char, now find the start of this token
110 path_start = i;
111 while (path_start > 0 && line[path_start - 1] != ' ' && line[path_start - 1] != '\t') {
112 path_start--;
113 }
114 break;
115 }
116 }
117
118 if (path_start > 0 && line[path_start - 1] != '\0') {
119 // Extract the path starting from path_start
120 strncpy(path, &line[path_start], PLATFORM_MAX_PATH_LENGTH - 1);
121 path[PLATFORM_MAX_PATH_LENGTH - 1] = '\0';
122 // Remove trailing whitespace
123 int path_len = strlen(path);
124 while (path_len > 0 && (path[path_len - 1] == '\n' || path[path_len - 1] == '\r')) {
125 path[--path_len] = '\0';
126 }
127 }
128
129 // Check if address falls within this segment
130 if (perms[2] == 'x' && path[0] == '/') {
131 if (addr_int >= start && addr_int < end) {
132 strncpy(matches[count].path, path, PLATFORM_MAX_PATH_LENGTH - 1);
133 matches[count].path[PLATFORM_MAX_PATH_LENGTH - 1] = '\0';
134 matches[count].file_offset = (addr_int - start) + offset;
135 count++;
136 }
137 }
138 }
139
140 fclose(maps);
141 return count;
142}
uintptr_t file_offset
Definition system.h:781
#define PLATFORM_MAX_PATH_LENGTH
Definition system.c:69

References platform_binary_match_t::file_offset, log_debug, platform_binary_match_t::path, and PLATFORM_MAX_PATH_LENGTH.

◆ keyboard_destroy()

void keyboard_destroy ( void  )

#include <keyboard.h>

Cleanup keyboard input system and restore terminal.

Restores terminal to original state (canonical mode with echo enabled). Must be called after keyboard_init() to prevent terminal corruption on program exit.

Platform behavior:

  • POSIX: Restores original termios settings via tcsetattr
  • Windows: Restores original console mode
Note
Safe to call multiple times (no-op if not initialized)
Safe to call even if keyboard_init() failed

Definition at line 81 of file platform/wasm/stubs/actions.c.

81 {
82 // No-op - no keyboard to destroy in WASM
83}

Referenced by asciichat_shared_destroy(), display_cleanup(), session_render_loop(), and ui_status_display_interactive().

◆ keyboard_init()

asciichat_error_t keyboard_init ( void  )

#include <keyboard.h>

Initialize keyboard input system.

Returns
ASCIICHAT_OK on success, or error code on failure

Sets up terminal for keyboard input by enabling raw mode (character-by-character input without line buffering or echo). Must be called before keyboard_read_nonblocking() and paired with keyboard_destroy() for proper terminal restoration.

Errors:

  • ERROR_PLATFORM_INIT: Terminal attribute query or configuration failed
  • ERROR_GENERAL: System call failed (see asciichat_errno for details)

Platform behavior:

  • POSIX: Uses tcgetattr/tcsetattr to set raw mode (ICANON/ECHO disabled)
  • POSIX: Sets stdin to non-blocking mode with fcntl(F_SETFL, O_NONBLOCK)
  • Windows: Uses GetStdHandle(STD_INPUT_HANDLE) and SetConsoleMode()
  • Windows: Disables ENABLE_LINE_INPUT and ENABLE_ECHO_INPUT modes

Error handling:

  • Automatically sets asciichat_errno on failure with context
  • Call HAS_ERRNO() to check for error details after failure
Note
Must be paired with keyboard_destroy() before program exit
Calling multiple times is safe (reference-counted, idempotent)
Terminal state is restored by keyboard_destroy()
Safe to call before or after calling keyboard_read_nonblocking()

Referenced by asciichat_shared_init(), and ui_status_display_interactive().

◆ keyboard_read_line_interactive()

keyboard_line_edit_result_t keyboard_read_line_interactive ( keyboard_line_edit_opts_t *  opts)

#include <keyboard.h>

Process one keystroke for interactive line editing.

Parameters
optsLine editing options (must not be NULL)
Returns
Result code indicating editing status

Non-blocking line editor that processes one keystroke per call. Supports full text editing with cursor movement, character insertion/deletion, and UTF-8 multi-byte sequences.

Supported editing operations:

  • Backspace (8/127): Delete character before cursor
  • Delete (ESC[3~): Delete character at cursor
  • Left/Right arrows: Move cursor
  • Home/End: Jump to start/end of line
  • Enter: Accept input (return LINE_EDIT_ACCEPTED)
  • Escape/Ctrl+C: Cancel input (return LINE_EDIT_CANCELLED)
  • Printable characters: Insert at cursor position
  • UTF-8 multi-byte: Full support for non-ASCII characters

Display behavior:

  • If opts.echo is true, characters are displayed as typed
  • If opts.mask_char is non-zero, characters are masked (e.g., '*' for passwords)
  • If opts.prefix is non-NULL, it's displayed before the input (e.g., "/" for grep)
  • If opts.validator is non-NULL, it's called on every change (for live validation)

Non-blocking design:

  • Returns immediately if no input available (LINE_EDIT_NO_INPUT)
  • Suitable for integration with render loops
  • Call repeatedly in a loop until LINE_EDIT_ACCEPTED or LINE_EDIT_CANCELLED

Errors:

  • Returns LINE_EDIT_NO_INPUT if keyboard not initialized
  • Returns LINE_EDIT_NO_INPUT if opts is NULL or buffer is NULL
Note
Terminal must be in raw mode (call keyboard_init() first)
The buffer is modified in-place as user types
len and cursor are updated to reflect current state
Thread-safe (but only one editing session should be active at a time)
Example
// Interactive grep input
char pattern[256] = {0};
size_t len = 0;
size_t cursor = 0;
.buffer = pattern,
.max_len = sizeof(pattern),
.len = &len,
.cursor = &cursor,
.echo = false, // We render ourselves
.mask_char = 0, // No masking
.prefix = "/", // Show "/" prefix
.validator = validate_pattern // Optional validator
};
while (true) {
switch (result) {
// User pressed Enter - pattern is in buffer
apply_pattern(pattern);
return;
// User pressed Escape - restore previous state
restore_previous();
return;
// Still editing - re-render display
render_input_line(pattern, cursor);
break;
// No input - continue loop
break;
}
}
keyboard_line_edit_result_t keyboard_read_line_interactive(keyboard_line_edit_opts_t *opts)
Process one keystroke for interactive line editing.
Options for interactive line editing.
Definition keyboard.h:192
char * buffer
Input buffer (modified in-place)
Definition keyboard.h:193

Definition at line 72 of file platform/wasm/stubs/actions.c.

72 {
73 (void)opts;
74 return LINE_EDIT_NO_INPUT; // No input available in WASM
75}

References LINE_EDIT_NO_INPUT.

Referenced by log_search_handle_key().

◆ keyboard_read_nonblocking()

keyboard_key_t keyboard_read_nonblocking ( void  )

#include <keyboard.h>

Read next keyboard input without blocking.

Returns
Keyboard key code (keyboard_key_t) or KEY_NONE if no input

Checks for available keyboard input and returns immediately. Returns KEY_NONE if no input is currently available. This is a non-blocking operation suitable for integration into render loops.

Supported input:

  • Arrow keys: KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT
  • Special keys: KEY_ESCAPE, KEY_SPACE
  • ASCII characters: Returned as raw character codes (a-z, A-Z, 0-9, etc.)
  • Control keys: Ctrl+C, Ctrl+Z, and other control sequences (codes 1-31)
  • UTF-8: Not currently supported (non-ASCII bytes return KEY_NONE)

Platform behavior:

  • POSIX: Uses select() with zero timeout on stdin for non-blocking check
  • POSIX: Parses ESC escape sequences for arrow keys (ESC [ A/B/C/D)
  • POSIX: 50ms timeout per escape sequence byte to distinguish ESC key from sequences
  • Windows: Uses _kbhit() and _getch() for non-blocking input
  • Windows: Handles 0xE0 and 0x00 extended key prefixes for arrow keys
  • Windows: Arrow key mappings: 72/up, 80/down, 75/left, 77/right

Return behavior:

  • Returns immediately with KEY_NONE if no input available (non-blocking)
  • Returns KEY_NONE if keyboard_init() was never called (safe, graceful)
  • Returns ASCII character code unchanged (e.g., 'c'=99, 'm'=109, '5'=53)
  • Arrow key sequences are fully consumed before returning
Note
Safe to call without prior keyboard_init() (returns KEY_NONE)
Thread-safe; uses mutex to check initialization state
Not suitable for high-frequency polling (CPU overhead); use in 60 FPS loops
Assumes terminal is in raw mode (set by keyboard_init())

Definition at line 77 of file platform/wasm/stubs/actions.c.

77 {
78 return KEY_NONE; // No input available in WASM
79}

References KEY_NONE.

Referenced by display_render_frame(), session_pipeline_run_main(), session_render_loop(), and ui_status_display_interactive().

◆ keyboard_read_with_timeout()

keyboard_key_t keyboard_read_with_timeout ( uint32_t  timeout_ms)

#include <keyboard.h>

Read keyboard input with timeout.

Parameters
timeout_msTimeout in milliseconds (0 for non-blocking)
Returns
Keyboard key code or KEY_NONE if timeout

Waits up to timeout_ms for keyboard input. Returns immediately if input is available, or after timeout if no input. Use this for event-driven input handling where you want to wait for keypresses.

Referenced by update_banner_show_prompt().

◆ mutex_destroy()

◆ mutex_format_state()

int mutex_format_state ( const mutex_t *  mutex,
char *  buffer,
size_t  size 
)

#include <mutex.c>

Format mutex timing and state info into buffer.

Parameters
mutexPointer to the mutex
bufferOutput buffer
sizeBuffer size
Returns
Number of bytes written

Formats timing (lock/unlock), held-by info, and operation counts. Called by mutex_log_state() and –sync-state display code.

Definition at line 89 of file platform/mutex.c.

89 {
90 if (!mutex || !buffer || size == 0)
91 return 0;
92
93 int offset = 0;
94 uint64_t now_ns = time_get_ns();
95
96 // Format timing information
97 char lock_str[64] = "";
98 char unlock_str[64] = "";
99 char held_str[64] = "";
100 char count_str[256] = "";
101
102 if (mutex->last_lock_time_ns > 0 && mutex->last_lock_time_ns <= now_ns) {
103 char elapsed_str[64];
104 time_pretty(now_ns - mutex->last_lock_time_ns, -1, elapsed_str, sizeof(elapsed_str));
105 snprintf(lock_str, sizeof(lock_str), "lock=%s", elapsed_str);
106 }
107
108 if (mutex->last_unlock_time_ns > 0 && mutex->last_unlock_time_ns <= now_ns) {
109 char elapsed_str[64];
110 time_pretty(now_ns - mutex->last_unlock_time_ns, -1, elapsed_str, sizeof(elapsed_str));
111 snprintf(unlock_str, sizeof(unlock_str), "unlock=%s", elapsed_str);
112 }
113
114 if (mutex->currently_held_by_key != 0) {
115 snprintf(held_str, sizeof(held_str), "[LOCKED_BY=thread.%lu]", (unsigned long)mutex->currently_held_by_key);
116 }
117
118 if (mutex->lock_count > 0 || mutex->unlock_count > 0 || mutex->trylock_count > 0) {
119 snprintf(count_str, sizeof(count_str), "[ops: lock=%llu unlock=%llu trylock=%llu/%llu]",
120 (unsigned long long)mutex->lock_count, (unsigned long long)mutex->unlock_count,
121 (unsigned long long)mutex->trylock_success_count, (unsigned long long)mutex->trylock_count);
122 }
123
124 offset += snprintf(buffer + offset, size - offset, "%s %s %s %s", lock_str, unlock_str, held_str, count_str);
125 return offset;
126}
uintptr_t currently_held_by_key
Registry key of thread holding the lock (0 if free)
uint64_t last_lock_time_ns
Timestamp of last lock acquisition (nanoseconds)
uint64_t unlock_count
Total unlocks.
uint64_t last_unlock_time_ns
Timestamp of last unlock (nanoseconds)
uint64_t trylock_success_count
Successful trylocks.
uint64_t trylock_count
Total trylock attempts.
uint64_t lock_count
Total lock acquisitions.

References mutex_t::currently_held_by_key, mutex_t::last_lock_time_ns, mutex_t::last_unlock_time_ns, mutex_t::lock_count, mutex, time_get_ns(), time_pretty(), mutex_t::trylock_count, mutex_t::trylock_success_count, and mutex_t::unlock_count.

Referenced by mutex_log_state().

◆ mutex_init()

int mutex_init ( mutex_t *  mutex,
const char *  name 
)

◆ mutex_lock_impl()

int mutex_lock_impl ( mutex_t *  mutex)

#include <mutex.h>

Lock a mutex (implementation function)

Parameters
mutexPointer to mutex to lock
Returns
0 on success, non-zero on error
Note
This is the implementation function. Use mutex_lock() macro instead, which includes debug tracking in debug builds.

Definition at line 27 of file threading.c.

27 {
28 (void)mutex;
29 return 0; // Success - no-op
30}

References mutex.

Referenced by debug_sync_mutex_lock().

◆ mutex_log_state()

void mutex_log_state ( const mutex_t *  mutex,
const char *  file,
int  line,
const char *  func 
)

#include <mutex.c>

Log the state of a mutex.

Format and log the current state of a mutex.

Parameters
mutexPointer to the mutex
fileSource file of the caller
lineSource line of the caller
funcSource function of the caller

Formats and logs detailed state information about the mutex.

Parameters
mutexPointer to the mutex
fileSource file of the caller
lineSource line of the caller
funcSource function of the caller

Logs detailed state information about the mutex (timing, counts, held-by info) at the specified location. Debug builds only; no-op in release builds.

Definition at line 139 of file platform/mutex.c.

139 {
140 if (!mutex)
141 return;
142 char buf[512];
143 mutex_format_state(mutex, buf, sizeof(buf));
144 log_msg(LOG_DEBUG, file, line, func, "mutex/state %p: %s", (const void *)mutex, buf);
145}
int mutex_format_state(const mutex_t *mutex, char *buffer, size_t size)
Format mutex timing and state info into buffer.

References LOG_DEBUG, log_msg(), mutex, and mutex_format_state().

◆ mutex_on_lock()

void mutex_on_lock ( mutex_t *  mutex)

#include <mutex.c>

Hook called when a mutex is successfully locked.

Parameters
mutexPointer to the mutex that was locked

Called by platform-specific mutex_lock_impl() after the lock is acquired. Records timing, held-by thread, and increments lock count.

Parameters
mutexPointer to the mutex that was locked

Called by platform-specific implementations after lock acquisition. Records timing and other diagnostic data (debug builds only).

Definition at line 29 of file platform/mutex.c.

References asciichat_thread_current_id(), asciichat_thread_to_key(), mutex_t::currently_held_by_key, mutex_t::last_lock_time_ns, mutex_t::lock_count, mutex, and time_get_ns().

◆ mutex_on_trylock()

void mutex_on_trylock ( mutex_t *  mutex,
bool  success 
)

#include <mutex.c>

Hook called when a mutex trylock is attempted.

Parameters
mutexPointer to the mutex
successWhether the trylock succeeded

Called by platform-specific mutex_trylock_impl() after the attempt. Records timing and success/failure statistics.

Parameters
mutexPointer to the mutex
successWhether the trylock succeeded

Called by platform-specific implementations after trylock attempt. Records timing and success/failure statistics (debug builds only).

Definition at line 65 of file platform/mutex.c.

65 {
66 if (!mutex)
67 return;
69 if (success) {
74 }
75}

References asciichat_thread_current_id(), asciichat_thread_to_key(), mutex_t::currently_held_by_key, mutex_t::last_lock_time_ns, mutex, time_get_ns(), mutex_t::trylock_count, and mutex_t::trylock_success_count.

◆ mutex_on_unlock()

void mutex_on_unlock ( mutex_t *  mutex)

#include <mutex.c>

Hook called when a mutex is unlocked.

Parameters
mutexPointer to the mutex that was unlocked

Called by platform-specific mutex_unlock_impl() before releasing the lock. Records timing and increments unlock count.

Parameters
mutexPointer to the mutex that was unlocked

Called by platform-specific implementations before lock release. Records timing and other diagnostic data (debug builds only).

Definition at line 47 of file platform/mutex.c.

47 {
48 if (!mutex)
49 return;
53}

References mutex_t::currently_held_by_key, mutex_t::last_unlock_time_ns, mutex, time_get_ns(), and mutex_t::unlock_count.

◆ mutex_trylock_impl()

int mutex_trylock_impl ( mutex_t *  mutex)

#include <mutex.h>

Try to lock a mutex without blocking (implementation function)

Parameters
mutexPointer to mutex to try to lock
Returns
0 if lock was acquired, non-zero if mutex was already locked

Attempts to acquire the mutex lock without blocking. Returns immediately whether the lock was acquired or not.

Note
This is the implementation function. Use mutex_trylock() macro instead, which includes debug tracking in debug builds.

Definition at line 32 of file threading.c.

32 {
33 (void)mutex;
34 return 0; // Success - no-op
35}

References mutex.

Referenced by debug_sync_mutex_trylock().

◆ mutex_unlock_impl()

int mutex_unlock_impl ( mutex_t *  mutex)

#include <mutex.h>

Unlock a mutex (implementation function)

Parameters
mutexPointer to mutex to unlock
Returns
0 on success, non-zero on error
Note
This is the implementation function. Use mutex_unlock() macro instead, which includes debug tracking in debug builds.

Definition at line 37 of file threading.c.

37 {
38 (void)mutex;
39 return 0; // Success - no-op
40}

References mutex.

Referenced by debug_sync_mutex_unlock().

◆ platform_access()

int platform_access ( const char *  path,
int  mode 
)

#include <filesystem.h>

Check file/directory access permissions.

Platform-safe wrapper for access() / _access(). Tests whether the calling process has the requested access to the specified path.

Platform-specific implementations:

  • POSIX: Uses access() with F_OK, R_OK, W_OK, X_OK modes
  • Windows: Uses _access() with 0, 2, 4, 6 modes
Parameters
pathFile or directory path to check
modeAccess mode to test (PLATFORM_ACCESS_EXISTS, PLATFORM_ACCESS_WRITE, PLATFORM_ACCESS_READ)
Returns
0 on success (access permitted), -1 on failure (access denied or path doesn't exist)
Note
Thread-safe on all platforms
Does not follow symbolic links on POSIX (uses access() not faccessat())
Returns -1 if path is NULL
Example:
// Directory is writable
}
int platform_access(const char *pathname, int mode)
Check file/directory access permissions.
#define PLATFORM_ACCESS_WRITE
Check if file/directory is writable.

Definition at line 98 of file wasm/stubs/filesystem.c.

98 {
99 (void)pathname;
100 (void)mode;
101 return -1; // Not accessible in WASM
102}

Referenced by get_discovery_database_dir(), get_log_dir(), parse_mirror_media(), and websocket_server_init().

◆ platform_asprintf()

int platform_asprintf ( char **  strp,
const char *  format,
  ... 
)

#include <string.h>

Allocate formatted string (asprintf replacement)

Parameters
strpPointer to string pointer (output parameter for allocated string)
formatPrintf-style format string
...Variable arguments
Returns
Number of characters written (excluding null terminator), or -1 on error

Allocates memory for a formatted string using malloc. The caller must free the allocated string with free().

Definition at line 48 of file platform/wasm/string.c.

48 {
49 // Use standard asprintf (available in WASM/Emscripten)
50 va_list args;
51 va_start(args, fmt);
52 int ret = vasprintf(strp, fmt, args);
53 va_end(args);
54 return ret;
55}
action_args_t args

References args.

◆ platform_backtrace()

int platform_backtrace ( void **  buffer,
int  size 
)

#include <backtrace.h>

Get a backtrace of the current call stack.

Captures return addresses from the current call stack into a provided buffer. Uses platform-specific mechanisms to walk the stack:

  • POSIX: libexecinfo backtrace() or manual frame pointer walking
  • Windows: CaptureStackBackTrace or StackWalk64
Parameters
bufferArray of void* pointers to store return addresses
sizeMaximum number of frames to capture
Returns
Number of frames captured (0 on error or empty stack)
Note
Return addresses are raw pointers; use platform_backtrace_symbols() to convert to names
This is a low-level function; prefer backtrace_capture() from debug/backtrace.h

Definition at line 16 of file util.c.

16 {
17 (void)buffer;
18 (void)size;
19 return 0; // No backtrace support in WASM
20}

Referenced by backtrace_capture().

◆ platform_backtrace_symbols()

char ** platform_backtrace_symbols ( void *const *  buffer,
int  size 
)

#include <backtrace.h>

Convert backtrace addresses to symbol names.

Converts raw return addresses from platform_backtrace() into human-readable symbol information (function names, file paths, line numbers).

Platform-specific resolution strategy:

  • POSIX: Uses addr2line/llvm-symbolizer via symbol_cache_resolve_batch()
  • Windows: Uses DbgHelp (SymFromAddr) with fallback to addr2line cache
Parameters
bufferArray of return addresses from platform_backtrace()
sizeNumber of addresses in buffer
Returns
Null-terminated array of symbol strings, or NULL on error
Note
The returned array must be freed with platform_backtrace_symbols_destroy()
Array is guaranteed to have 'size' valid entries plus a NULL terminator
This is a low-level function; prefer backtrace_symbolize() from debug/backtrace.h

Definition at line 22 of file util.c.

22 {
23 (void)buffer;
24 (void)size;
25 return NULL; // No backtrace symbols in WASM
26}

Referenced by backtrace_symbolize().

◆ platform_backtrace_symbols_destroy()

void platform_backtrace_symbols_destroy ( char **  strings)

#include <backtrace.h>

Free symbol array from platform_backtrace_symbols()

Releases all memory allocated by platform_backtrace_symbols(), including individual symbol strings and the array itself.

Parameters
stringsArray returned by platform_backtrace_symbols(), or NULL
Note
Safe to call with NULL pointer
Must not be called multiple times on same pointer

Definition at line 28 of file util.c.

28 {
29 (void)symbols;
30 // No-op
31}

Referenced by backtrace_t_free().

◆ platform_chmod()

int platform_chmod ( const char *  pathname,
int  mode 
)

#include <filesystem.h>

Change file permissions/mode.

Parameters
pathnameFile path
modeNew file mode (permissions)
Returns
0 on success, -1 on error

Referenced by add_known_host(), and gpg_homedir_create().

◆ platform_cleanup_binary_path_cache()

void platform_cleanup_binary_path_cache ( void  )

#include <filesystem.h>

Cleanup the binary PATH cache.

Frees all cached binary PATH lookup results and destroys the cache. Should be called during program cleanup (e.g., in platform_destroy()).

Note
Thread-safe: Uses internal locking
Safe to call even if cache was never initialized

Definition at line 113 of file filesystem.c.

113 {
114 if (!lifecycle_shutdown(&g_cache_lc)) {
115 return;
116 }
117
118 if (g_bin_path_cache) {
119 rwlock_wrlock(&g_cache_rwlock);
120
121 bin_cache_entry_t *entry, *tmp;
122 HASH_ITER(hh, g_bin_path_cache, entry, tmp) {
123 HASH_DELETE(hh, g_bin_path_cache, entry);
124 free_cache_entry(entry);
125 }
126
127 rwlock_wrunlock(&g_cache_rwlock);
128 rwlock_destroy(&g_cache_rwlock);
129 g_bin_path_cache = NULL;
130 }
131}
#define rwlock_wrunlock(lock)
Release a write lock (with debug tracking in debug builds)
Definition rwlock.h:349
int rwlock_destroy(rwlock_t *lock)
Destroy a read-write lock.
#define rwlock_wrlock(lock)
Acquire a write lock (with debug tracking in debug builds)
Definition rwlock.h:313
bool lifecycle_shutdown(lifecycle_t *lc)
Definition lifecycle.c:108
Cache entry for binary PATH lookup.
Definition filesystem.c:75

References lifecycle_shutdown(), rwlock_destroy(), rwlock_wrlock, and rwlock_wrunlock.

Referenced by asciichat_shared_destroy().

◆ platform_clear_error_state()

void platform_clear_error_state ( void  )

#include <errno.h>

Clear all error state on the current platform.

Windows: Clears WSA errors via WSASetLastError(0). POSIX: Clears system errno.

Definition at line 69 of file util.c.

69 {
70 // No-op - error state is thread-local, nothing to clear in WASM
71}

Referenced by asciichat_clear_errno().

◆ platform_close()

int platform_close ( int  fd)

#include <filesystem.h>

Safe file close (close replacement)

Parameters
fdFile descriptor to close
Returns
0 on success, -1 on error

Definition at line 94 of file wasm/stubs/filesystem.c.

94 {
95 return close(fd);
96}

Referenced by asciichat_instr_runtime_destroy(), check_known_host(), check_known_host_no_identity(), log_destroy(), log_disable_file_output(), log_init(), remove_known_host(), and session_display_destroy().

◆ platform_create_temp_file()

int platform_create_temp_file ( char *  path_out,
size_t  path_size,
const char *  prefix,
int *  fd 
)

#include <filesystem.h>

Create a temporary file with a given prefix.

Windows: Creates file in temp dir via GetTempFileName with process-specific prefix Unix: Creates file via mkstemp with prefix in /tmp

Parameters
path_outBuffer to store the created temp file path (must be at least 256 bytes)
path_sizeSize of path_out buffer
prefixPrefix for the temp file name (e.g., "asc_sig")
fdOutput parameter: on Unix, receives the open file descriptor; on Windows, -1
Returns
0 on success, -1 on error
Note
Caller must close fd on Unix and delete the file on both platforms when done

Referenced by gpg_sign_with_key(), gpg_verify_detached_ed25519(), gpg_verify_signature_with_binary(), and parse_gpg_keys_from_response().

◆ platform_delete_temp_file()

int platform_delete_temp_file ( const char *  path)

#include <filesystem.h>

Delete a temporary file.

Parameters
pathPath to the file to delete
Returns
0 on success, -1 on error

Referenced by gpg_sign_with_key(), gpg_verify_detached_ed25519(), gpg_verify_signature_with_binary(), and parse_gpg_keys_from_response().

◆ platform_destroy()

void platform_destroy ( void  )

#include <init.h>

Cleanup platform-specific subsystems.

Performs cleanup for platform-specific subsystems. Should be called during program shutdown.

Definition at line 15 of file lib/platform/wasm/init.c.

15 {
16 // No cleanup needed for WASM
17}

Referenced by asciichat_shared_destroy(), client_cleanup(), and mirror_cleanup().

◆ platform_dir_foreach()

asciichat_error_t platform_dir_foreach ( const char *  path,
platform_dir_foreach_cb  callback,
void *  user_data 
)

#include <filesystem.h>

Iterate over entries in a directory.

Parameters
pathDirectory path
callbackFunction called for each entry (excluding "." and "..")
user_dataOpaque pointer passed to callback
Returns
ASCIICHAT_OK on success, error code if directory cannot be opened

◆ platform_disable_keepawake()

void platform_disable_keepawake ( void  )

#include <system.h>

Disable system sleep prevention (allow OS to sleep)

Allows the operating system to enter sleep mode. This reverts the effect of platform_enable_keepawake().

Platform-specific implementations:

  • macOS: Releases the IOKit power assertion
  • Linux: Closes the systemd-inhibit file descriptor
  • Windows: Clears all execution state flags
Note
Safe to call even if keepawake was never enabled
Safe to call multiple times

Definition at line 91 of file linux/keepawake.c.

91 {
92#ifdef HAVE_LIBSYSTEMD
93 if (g_inhibit_fd >= 0) {
94 close(g_inhibit_fd);
95 log_debug("Keepawake disabled, closed inhibit fd=%d", g_inhibit_fd);
96 // Unregister from named registry after logging
97 NAMED_UNREGISTER_FD(g_inhibit_fd);
98 g_inhibit_fd = -1;
99 }
100#endif
101}
#define NAMED_UNREGISTER_FD(fd)
Unregister a file descriptor.
#define log_debug(...)
Log a DEBUG message.
Definition log/log.h:548

References log_debug, and NAMED_UNREGISTER_FD.

Referenced by session_client_like_run(), and session_server_like_run().

◆ platform_enable_keepawake()

asciichat_error_t platform_enable_keepawake ( void  )

#include <system.h>

Enable system sleep prevention (keepawake mode)

Returns
ASCIICHAT_OK on success, error code on failure

Prevents the operating system from entering sleep mode while the application is running.

Platform-specific implementations:

  • macOS: Uses IOKit power assertions (IOPMAssertionCreateWithName)
  • Linux: Uses systemd-inhibit if available, gracefully degrades if unavailable
  • Windows: Uses SetThreadExecutionState with ES_SYSTEM_REQUIRED and ES_DISPLAY_REQUIRED
Note
Logs warning and returns OK if platform doesn't support keepawake
Safe to call multiple times (checks for already-enabled state)
Call platform_disable_keepawake() to revert

Definition at line 21 of file linux/keepawake.c.

21 {
22#ifdef HAVE_LIBSYSTEMD
23 // Linux: Use systemd-inhibit if available
24 if (g_inhibit_fd >= 0) {
25 log_debug("Keepawake already enabled");
26 return ASCIICHAT_OK;
27 }
28
29 // Check if systemd is available at runtime (weak symbol)
30 if (sd_bus_default_system == NULL) {
31 log_dev("systemd not available, keepawake not supported");
32 return ASCIICHAT_OK; // Not an error, just unsupported
33 }
34
35 sd_bus *bus = NULL;
36 sd_bus_message *reply = NULL;
37 sd_bus_error error = SD_BUS_ERROR_NULL;
38
39 if (sd_bus_default_system(&bus) < 0) {
40 return SET_ERRNO(ERROR_PLATFORM_INIT, "Failed to connect to system bus");
41 }
42
43 if (!bus) {
44 return SET_ERRNO(ERROR_PLATFORM_INIT, "System bus is NULL");
45 }
46
47 int r = sd_bus_call_method(bus, "org.freedesktop.login1", "/org/freedesktop/login1", "org.freedesktop.login1.Manager",
48 "Inhibit", &error, &reply, "ssss",
49 "sleep:idle", // What to inhibit
50 "ascii-chat", // Who
51 "Video/audio streaming", // Why
52 "block" // Mode
53 );
54
55 if (r < 0) {
56 sd_bus_error_free(&error);
57 sd_bus_unref(bus);
58 return SET_ERRNO(ERROR_PLATFORM_INIT, "Failed to inhibit sleep via systemd");
59 }
60
61 if (!reply) {
62 sd_bus_error_free(&error);
63 sd_bus_unref(bus);
64 return SET_ERRNO(ERROR_PLATFORM_INIT, "systemd inhibit reply is NULL");
65 }
66
67 if (sd_bus_message_read(reply, "h", &g_inhibit_fd) < 0) {
68 sd_bus_message_unref(reply);
69 sd_bus_error_free(&error);
70 sd_bus_unref(bus);
71 return SET_ERRNO(ERROR_PLATFORM_INIT, "Failed to read inhibit fd from systemd reply");
72 }
73
74 sd_bus_message_unref(reply);
75 sd_bus_error_free(&error);
76 sd_bus_unref(bus);
77
78 // Register the inhibit fd with the named registry for debug logging
79 NAMED_REGISTER_FD(g_inhibit_fd, "inhibit");
80
81 log_debug("Keepawake enabled via systemd-inhibit fd=%d", g_inhibit_fd);
82 return ASCIICHAT_OK;
83
84#else
85 // Other POSIX systems: not implemented
86 log_debug("Keepawake not implemented on this platform");
87 return ASCIICHAT_OK;
88#endif
89}
#define NAMED_REGISTER_FD(fd, name)
Register an existing file descriptor.
#define SET_ERRNO(code, context_msg,...)
Set error code with custom context message and log it, returning the error code.
@ ERROR_PLATFORM_INIT
Definition error_codes.h:60
#define log_dev(...)
Log a DEV message (most verbose, development only)
Definition log/log.h:534

References ASCIICHAT_OK, ERROR_PLATFORM_INIT, log_debug, log_dev, NAMED_REGISTER_FD, and SET_ERRNO.

Referenced by session_client_like_run(), and session_server_like_run().

◆ platform_escape_shell_path()

asciichat_error_t platform_escape_shell_path ( const char *  path,
char *  output,
size_t  output_size 
)

#include <string.h>

Escape a path string for safe shell usage.

Escapes a file path according to platform-specific shell rules:

  • Windows: Wraps in double quotes, escapes internal quotes
  • POSIX: Wraps in single quotes (safest approach)
Parameters
pathInput path to escape
outputBuffer to store escaped path
output_sizeSize of output buffer
Returns
ASCIICHAT_OK on success, ERROR_BUFFER_OVERFLOW if buffer too small
Note
If output_size is too small, returns error without modifying buffer
Output string is properly null-terminated
Example:
char escaped[512];
platform_escape_shell_path("/path/to/file.txt", escaped, sizeof(escaped));
// Windows: "C:\path\to\file.txt"
// POSIX: '/path/to/file.txt'
asciichat_error_t platform_escape_shell_path(const char *path, char *output, size_t output_size)
Escape a path string for safe shell usage.

◆ platform_execute_subprocess()

int platform_execute_subprocess ( const char *  executable,
const char **  argv,
char *  output_buffer,
size_t  output_size 
)

#include <system.h>

Execute a subprocess and optionally capture its output.

Parameters
executablePath to executable to run (e.g., "gpg", "/usr/bin/yt-dlp")
argvNULL-terminated array of arguments (argv[0] should be executable name or path)
output_bufferOptional buffer to store captured stdout (NULL to skip output capture)
output_sizeSize of output buffer (ignored if output_buffer is NULL)
Returns
Exit code of the process, or -1 on error

Executes a subprocess using platform-specific mechanisms:

  • POSIX: fork() + execvp() (no output), or popen() (with output)
  • Windows: CreateProcess() (no output), or piped output capture (with output)

If output_buffer is NULL or output_size is 0, stdout and stderr are inherited from the parent process. Otherwise, stdout is captured into output_buffer.

Usage examples:

// No output capture
const char *argv[] = {"gpg", "--version", NULL};
int exit_code = platform_execute_subprocess("gpg", argv, NULL, 0);
// With output capture
char output[1024];
const char *argv[] = {"gpg", "--list-secret-keys", "--with-colons", NULL};
int exit_code = platform_execute_subprocess("gpg", argv, output, sizeof(output));
if (exit_code == 0) {
// Parse output (null-terminated)
}
int platform_execute_subprocess(const char *executable, const char **argv, char *output_buffer, size_t output_size)
Execute a subprocess and optionally capture its output.
Note
When output_buffer is NULL: stdout and stderr inherited, fast fork+exec
When output_buffer is provided: stdout captured, stderr inherited
Output is always null-terminated when captured
Returns -1 if output_buffer is provided but too small for output
More secure than system() as it doesn't invoke a shell

Definition at line 23 of file platform/wasm/stubs/network.c.

23 {
24 (void)executable;
25 (void)argv;
26 (void)output_buffer;
27 (void)output_size;
28 return -1; // No subprocess support in WASM
29}

Referenced by gpg_sign_with_key(), update_check_get_upgrade_suggestion(), and yt_dlp_is_available().

◆ platform_fdopen()

FILE * platform_fdopen ( const char *  name,
int  fd,
const char *  mode 
)

#include <filesystem.h>

Convert file descriptor to stream (fdopen replacement)

Parameters
nameDebug name for the stream (required, e.g., "log_stream")
fdFile descriptor
modeOpen mode string for the stream
Returns
FILE pointer, or NULL on error

Referenced by check_known_host(), check_known_host_no_identity(), remove_known_host(), terminal_fd_reader_create(), and terminal_fd_writer_create().

◆ platform_find_config_file()

asciichat_error_t platform_find_config_file ( const char *  filename,
config_file_list_t *  list_out 
)

#include <filesystem.h>

Find config file across multiple standard locations.

Searches for a config file across platform-specific standard locations and returns ALL existing matches in priority order (highest first).

This allows calling code to implement different merge strategies:

  • Override: use first match (colors.toml)
  • Cascade: load all in reverse order (config.toml)
  • Append: search all for matching entries (known_hosts)

Search Order (Unix/macOS):

  1. ~/.config/ascii-chat/filename (XDG user config)
  2. /opt/homebrew/etc/ascii-chat/filename (macOS Homebrew)
  3. /usr/local/etc/ascii-chat/filename (Unix local)
  4. /etc/ascii-chat/filename (system-wide)

Search Order (Windows):

  1. APPDATA%\ascii-chat\filename (user config)
  2. PROGRAMDATA%\ascii-chat\filename (system-wide)
Parameters
filenameConfig filename (e.g., "config.toml", "colors.toml", "known_hosts")
list_outPointer to config_file_list_t to populate
Returns
ASCIICHAT_OK on success (even if no files found), error code on failure
Note
Caller must free list_out with config_file_list_destroy()
Returns ASCIICHAT_OK even if no files are found (list_out->count == 0)
Files are checked for existence and regular file type
Example (Override semantics - colors.toml):
config_file_list_t list = {0};
if (platform_find_config_file("colors.toml", &list) == ASCIICHAT_OK) {
if (list.count > 0) {
// Use first match (highest priority)
colors_load_from_file(list.files[0].path, scheme);
}
}
asciichat_error_t platform_find_config_file(const char *filename, config_file_list_t *list_out)
Find config file across multiple standard locations.
void config_file_list_destroy(config_file_list_t *list)
Free config file list resources.
List of config file search results.
Definition filesystem.h:595
size_t count
Number of results found.
Definition filesystem.h:597
config_file_result_t * files
Array of results (allocated, must be freed)
Definition filesystem.h:596
char * path
Absolute path to config file (allocated, must be freed)
Definition filesystem.h:583
Example (Cascade semantics - config.toml):
config_file_list_t list = {0};
if (platform_find_config_file("config.toml", &list) == ASCIICHAT_OK) {
// Load configs in reverse order (lowest priority first)
// This allows higher-priority configs to override
for (size_t i = list.count; i > 0; i--) {
config_load_and_apply(list.files[i - 1].path, opts);
}
}
asciichat_error_t config_load_and_apply(asciichat_mode_t detected_mode, const char *config_path, bool strict, options_t *opts)
Main function to load configuration from file and apply to global options.
Definition config.c:1041

Definition at line 45 of file wasm/stubs/filesystem.c.

45 {
46 (void)filename;
47 if (list_out) {
48 list_out->files = NULL;
49 list_out->count = 0;
50 list_out->capacity = 0;
51 }
52 return ASCIICHAT_OK; // No config files in WASM
53}
size_t capacity
Allocated capacity.
Definition filesystem.h:598

References ASCIICHAT_OK, config_file_list_t::capacity, config_file_list_t::count, and config_file_list_t::files.

Referenced by check_known_host(), and config_load_system_and_user().

◆ platform_fopen()

FILE * platform_fopen ( const char *  name,
const char *  filename,
const char *  mode 
)

#include <filesystem.h>

Safe file open stream (fopen replacement)

Parameters
nameDebug name for the file (required, e.g., "config_file")
filenameFile path to open
modeOpen mode string (e.g., "r", "w", "rb")
Returns
FILE pointer, or NULL on error

Definition at line 21 of file wasm/stubs/filesystem.c.

21 {
22 if (!name) {
23 return NULL;
24 }
25 FILE *stream = fopen(filename, mode); // Use standard fopen
26 if (stream) {
27 int fd = fileno(stream);
28 if (fd >= 0) {
29 NAMED_REGISTER_FD(fd, name);
30 }
31 }
32 return stream;
33}

References NAMED_REGISTER_FD.

Referenced by acds_identity_load(), action_completions(), add_known_host(), colorscheme_export_scheme(), config_create_default(), discovery_keys_save_cached(), get_manpage_template(), gpg_sign_with_key(), options_config_generate_manpage_merged(), options_config_generate_manpage_template(), parse_keys_from_file(), parse_manpage_sections(), parse_private_key(), parse_public_key(), parse_ssh_private_key(), update_check_load_cache(), update_check_save_cache(), validate_ssh_key_file(), and wav_writer_open().

◆ platform_force_exit()

void platform_force_exit ( int  exit_code)

#include <system.h>

Forcefully terminate the process immediately without cleanup.

Parameters
exit_codeExit code to return to the operating system

Forcefully terminates the current process immediately without running atexit handlers or cleanup code. Used when normal exit() won't suffice (e.g., handling second Ctrl+C during shutdown).

Platform-specific implementations:

  • Windows: ExitProcess(exit_code)
  • POSIX: _exit(exit_code)
Note
This function does not return. Process is terminated immediately.
Use this sparingly - prefer normal exit() whenever possible.

Definition at line 121 of file misc.c.

121 {
122 (void)exit_code;
123}

Referenced by client_main().

◆ platform_fsync()

int platform_fsync ( int  fd)

#include <filesystem.h>

Synchronize a file descriptor to disk.

Parameters
fdFile descriptor to sync
Returns
0 on success, non-zero on error

Forces all buffered data for the file descriptor to be written to disk.

◆ platform_get_config_dir()

char * platform_get_config_dir ( void  )

#include <filesystem.h>

Get the application configuration directory.

Platform-specific implementation:

  • POSIX: Returns $XDG_CONFIG_HOME/ascii-chat/ (default: ~/.config/ascii-chat/)
  • Windows: Returns APPDATA%\ascii-chat\
Returns
Allocated string with config directory path (including trailing separator), or NULL on error. Caller must free with SAFE_FREE()
Note
The returned string includes a trailing path separator (/ or )
Returns a freshly allocated string each time; caller must free it
Returns NULL if home directory cannot be determined
Example:
char *config_dir = platform_get_config_dir();
if (config_dir) {
// config_dir = "/home/user/.config/ascii-chat/" on Linux
// config_dir = "C:\\Users\\user\\AppData\\Roaming\\ascii-chat\\" on Windows
SAFE_FREE(config_dir);
}
#define SAFE_FREE(ptr)
Definition common.h:376
char * platform_get_config_dir(void)
Get the application configuration directory.
Definition util.c:104

Definition at line 104 of file util.c.

104 {
105 static char config_dir[] = "/config";
106 return config_dir; // WASM virtual filesystem config directory
107}

Referenced by acds_identity_default_path(), and get_config_dir().

◆ platform_get_cwd()

bool platform_get_cwd ( char *  cwd,
size_t  path_size 
)

#include <filesystem.h>

Get the current working directory of the process.

Normalizes the result using platform-specific semantics and does not append a trailing directory separator.

Parameters
cwdBuffer to store the current working directory
path_sizeSize of the buffer in bytes
Returns
true on success, false on failure (buffer too small or API error)

Definition at line 110 of file util.c.

110 {
111 if (!cwd || path_size == 0) {
112 return false;
113 }
114 platform_strlcpy(cwd, "/", path_size);
115 return true;
116}
size_t platform_strlcpy(char *dst, const char *src, size_t size)
Safe string copy with size tracking (strlcpy)

References platform_strlcpy().

Referenced by get_log_dir(), and path_validate_user_path().

◆ platform_get_data_dir()

char * platform_get_data_dir ( void  )

#include <filesystem.h>

Get the application data directory.

Platform-specific implementation:

  • POSIX: Returns $XDG_DATA_HOME/ascii-chat/ (default: ~/.local/share/ascii-chat/)
  • Windows: Returns APPDATA%\ascii-chat\
Returns
Allocated string with data directory path (including trailing separator), or NULL on error. Caller must free() the returned string.
Note
Creates the directory if it doesn't exist
Thread-safe (returns newly allocated string each time)
Example:
char *data_dir = platform_get_data_dir();
if (data_dir) {
// data_dir = "/home/user/.local/share/ascii-chat/" on Linux
// data_dir = "C:\\Users\\user\\AppData\\Roaming\\ascii-chat\\" on Windows
SAFE_FREE(data_dir);
}
char * platform_get_data_dir(void)
Get the application data directory.

Definition at line 104 of file wasm/stubs/filesystem.c.

104 {
105 return NULL; // No data directory in WASM
106}

Referenced by get_data_dir().

◆ platform_get_executable_path()

bool platform_get_executable_path ( char *  exe_path,
size_t  path_size 
)

#include <filesystem.h>

Get the path to the current executable.

Retrieves the full path to the currently running executable using platform-specific methods.

Platform-specific implementations:

  • Windows: GetModuleFileNameA()
  • Linux: readlink("/proc/self/exe")
  • macOS: _NSGetExecutablePath()
Parameters
exe_pathBuffer to store the executable path
path_sizeSize of the buffer
Returns
true on success, false on failure
Note
Thread-safe
Buffer should be PLATFORM_MAX_PATH_LENGTH bytes to support all paths
Example:
char exe_path[PLATFORM_MAX_PATH_LENGTH];
if (platform_get_executable_path(exe_path, sizeof(exe_path))) {
// Use exe_path
}
bool platform_get_executable_path(char *exe_path, size_t path_size)
Get the path to the current executable.
Definition filesystem.c:191
Parameters
exe_pathBuffer to store the executable path
path_sizeSize of the buffer
Returns
true on success, false on failure

Definition at line 191 of file filesystem.c.

191 {
192 if (!exe_path || path_size == 0) {
193 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters: exe_path=%p, path_size=%zu", (void *)exe_path, path_size);
194 return false;
195 }
196
197#ifdef _WIN32
198 DWORD len = GetModuleFileNameA(NULL, exe_path, (DWORD)path_size);
199 if (len == 0) {
200 SET_ERRNO_SYS(ERROR_INVALID_STATE, "GetModuleFileNameA failed: error code %lu", GetLastError());
201 return false;
202 }
203 if (len >= path_size) {
205 "Executable path exceeds buffer size (path length >= %zu bytes, buffer size = %zu bytes)", (size_t)len,
206 path_size);
207 return false;
208 }
209 return true;
210
211#elif defined(__linux__)
212 ssize_t len = readlink("/proc/self/exe", exe_path, path_size - 1);
213 if (len < 0) {
214 SET_ERRNO_SYS(ERROR_INVALID_STATE, "readlink(\"/proc/self/exe\") failed: %s", SAFE_STRERROR(errno));
215 return false;
216 }
217 if ((size_t)len >= path_size - 1) {
219 "Executable path exceeds buffer size (path length >= %zu bytes, buffer size = %zu bytes)", (size_t)len,
220 path_size);
221 return false;
222 }
223 // NOLINTNEXTLINE(clang-analyzer-security.ArrayBound) - len bounded by path_size check above
224 exe_path[len] = '\0';
225 return true;
226
227#elif defined(__APPLE__)
228 uint32_t bufsize = (uint32_t)path_size;
229 int result = _NSGetExecutablePath(exe_path, &bufsize);
230 if (result != 0) {
231 SET_ERRNO(ERROR_BUFFER_OVERFLOW, "_NSGetExecutablePath failed: path requires %u bytes, buffer size = %zu bytes",
232 bufsize, path_size);
233 return false;
234 }
235 return true;
236
237#else
238 SET_ERRNO(ERROR_GENERAL, "Unsupported platform - cannot get executable path");
239 return false;
240#endif
241}
unsigned int uint32_t
Definition common.h:58
#define SAFE_STRERROR(errnum)
Definition common.h:465
#define SET_ERRNO_SYS(code, context_msg,...)
Set error code with custom message and system error context, returning the error code.
@ ERROR_INVALID_STATE
@ ERROR_GENERAL
Definition error_codes.h:52
@ ERROR_INVALID_PARAM
@ ERROR_BUFFER_OVERFLOW

References errno, ERROR_BUFFER_OVERFLOW, ERROR_GENERAL, ERROR_INVALID_PARAM, ERROR_INVALID_STATE, SAFE_STRERROR, SET_ERRNO, and SET_ERRNO_SYS.

◆ platform_get_gpg_agent_socket()

int platform_get_gpg_agent_socket ( char *  path_out,
size_t  path_size 
)

#include <agent.h>

Get the GPG agent socket/named pipe path.

Attempts to use gpgconf first, then falls back to default locations: Windows: APPDATA%\gnupg\S.gpg-agent Unix: $GNUPGHOME/S.gpg-agent or ~/.gnupg/S.gpg-agent

Parameters
path_outBuffer to store path (should be at least 256 bytes)
path_sizeSize of path_out buffer
Returns
0 on success, -1 on error

Referenced by gpg_agent_connect().

◆ platform_get_home_dir()

const char * platform_get_home_dir ( void  )

#include <filesystem.h>

Get the user's home directory.

Platform-specific implementation:

  • POSIX: Returns HOME environment variable
  • Windows: Returns USERPROFILE environment variable (fallback to HOME)
Returns
Pointer to home directory string (not allocated), or NULL if not found
Note
The returned pointer is managed by the system and should not be freed
The returned string is valid only until the next getenv() call

Definition at line 94 of file util.c.

94 {
95 return "/home"; // WASM virtual filesystem home
96}

Referenced by expand_path(), and path_validate_user_path().

◆ platform_get_last_error()

int platform_get_last_error ( void  )

#include <system.h>

Get the last system error code.

Returns the last error code from the operating system. On Windows: Returns GetLastError() On POSIX: Returns errno

Returns
System error code (Windows DWORD cast to int, or errno on POSIX)

Definition at line 89 of file util.c.

89 {
90 return 0; // No system error in WASM
91}

◆ platform_get_monotonic_time_us()

uint64_t platform_get_monotonic_time_us ( void  )

#include <system.h>

Get monotonic time in microseconds.

Returns
Current monotonic time in microseconds

Returns a monotonically increasing time value in microseconds. Useful for measuring elapsed time without being affected by system clock changes. Thread-safe and lock-free.

Platform-specific implementations:

  • Unix/POSIX: Uses CLOCK_MONOTONIC via clock_gettime()
  • Windows: Uses QueryPerformanceCounter()
Note
The absolute value is arbitrary; only differences are meaningful.
Wraps after approximately 584,942 years (uint64_t).

Definition at line 44 of file platform/wasm/time.c.

44 {
45 struct timespec ts;
46 clock_gettime(CLOCK_MONOTONIC, &ts);
47 return (uint64_t)ts.tv_sec * 1000000ULL + (uint64_t)ts.tv_nsec / 1000ULL;
48}

Referenced by backtrace_print(), client_main(), discovery_session_process(), main(), terminal_screen_render(), ui_status_update(), and webrtc_is_gathering_timed_out().

◆ platform_get_pid()

pid_t platform_get_pid ( void  )

#include <process.h>

Get the current process ID.

Returns
Process ID of the calling process

Platform-specific implementations:

  • POSIX: Uses getpid()
  • Windows: Uses _getpid()

Definition at line 61 of file wasm/system.c.

61 {
62 return 1; // WASM runs in browser - no process ID concept
63}

Referenced by asciichat_instr_runtime_get(), and options_state_init().

◆ platform_get_ssh_agent_socket()

int platform_get_ssh_agent_socket ( char *  path_out,
size_t  path_size 
)

#include <agent.h>

Get the SSH agent socket/pipe path.

Windows: Default named pipe "\\\\.\\pipe\\openssh-ssh-agent" or SSH_AUTH_SOCK. Unix: Uses SSH_AUTH_SOCK environment variable.

Parameters
path_outBuffer to store path (should be at least 256 bytes)
path_sizeSize of path_out buffer
Returns
0 on success, -1 on error

◆ platform_get_temp_dir()

bool platform_get_temp_dir ( char *  temp_dir,
size_t  path_size 
)

#include <filesystem.h>

Get the system temporary directory path.

Retrieves the path to the system's temporary directory using platform-specific methods. Verifies the directory exists and is writable.

Platform-specific implementations:

  • Windows: TEMP% or TMP% environment variable, fallback to C:\Temp
  • Linux/macOS: /tmp
Parameters
temp_dirBuffer to store the temporary directory path
path_sizeSize of the buffer
Returns
true on success (directory exists and is writable), false on failure
Note
Thread-safe
Returned path does not include trailing directory separator
Buffer should be at least 256 bytes to support typical paths
Returns false if the directory doesn't exist or lacks write permission
Example:
char temp_dir[256];
if (platform_get_temp_dir(temp_dir, sizeof(temp_dir))) {
// temp_dir is valid and writable
char log_path[512];
snprintf(log_path, sizeof(log_path), "%s/myapp.log", temp_dir);
}
bool platform_get_temp_dir(char *temp_dir, size_t path_size)
Get the system temporary directory path.
Definition util.c:74

Definition at line 74 of file util.c.

74 {
75 if (!temp_dir || path_size == 0) {
76 return false;
77 }
78 platform_strlcpy(temp_dir, "/tmp", path_size);
79 return true;
80}

References platform_strlcpy().

Referenced by audio_analysis_init(), audio_client_init(), client_audio_pipeline_create(), get_log_dir(), and path_validate_user_path().

◆ platform_get_username()

const char * platform_get_username ( void  )

#include <system.h>

Get the current username.

Returns
Pointer to username string (may be static, do not free)

Returns the username of the current user.

Note
The returned string may be a static buffer. Do not modify or free it.

Referenced by server_connection_establish().

◆ platform_getenv()

const char * platform_getenv ( const char *  name)

#include <system.h>

Get an environment variable value.

Parameters
nameEnvironment variable name
Returns
Pointer to value string (or NULL if not set), do not free

Returns the value of the specified environment variable.

Note
The returned string may be a static buffer. Do not modify or free it.

Definition at line 39 of file wasm/system.c.

39 {
40 (void)name;
41 // Environment variables not supported in WASM browser context
42 // Calling getenv() causes "memory access out of bounds" errors
43 return NULL;
44}

Referenced by client_audio_pipeline_process_duplex(), client_crypto_handshake(), crypto_handshake_client_key_exchange(), ed25519_verify_signature(), get_discovery_database_dir(), options_init(), parse_ssh_private_key(), path_validate_user_path(), and prompt_unknown_host().

◆ platform_getline()

ssize_t platform_getline ( char **  lineptr,
size_t *  n,
FILE *  stream 
)

#include <string.h>

Cross-platform getline implementation.

Parameters
lineptrPointer to buffer (can be NULL, will be allocated/reallocated)
nPointer to buffer size
streamFile stream to read from
Returns
Number of characters read (including newline), or -1 on error/EOF

◆ platform_gtime()

asciichat_error_t platform_gtime ( const time_t *  timer,
struct tm *  result 
)

#include <system.h>

Platform-safe gmtime wrapper.

Uses gmtime_s on Windows and gmtime_r on POSIX. Thread-safe on all platforms.

Parameters
timerPointer to time_t value
resultPointer to struct tm to receive result
Returns
ASCIICHAT_OK on success, error code on error

Referenced by asciichat_instr_log_line().

◆ platform_init()

asciichat_error_t platform_init ( void  )

#include <init.h>

Initialize platform-specific subsystems.

Returns
ASCIICHAT_OK on success, error code on failure

Initializes platform-specific subsystems such as Winsock on Windows. Must be called before using any platform-specific functions.

Definition at line 10 of file lib/platform/wasm/init.c.

10 {
11 // No special initialization needed for WASM
12 return ASCIICHAT_OK;
13}

References ASCIICHAT_OK.

Referenced by asciichat_shared_init(), client_init_with_args(), and mirror_init_with_args().

◆ platform_install_crash_handler()

asciichat_error_t platform_install_crash_handler ( platform_crash_handler_t  handler)

#include <signal.h>

Install a crash signal handler.

Registers a callback to be invoked when critical signals are received. Handles platform differences:

  • POSIX: sigaction() for SIGSEGV, SIGABRT, SIGBUS, SIGILL
  • Windows: SetUnhandledExceptionFilter() for access violations, stack overflow
Parameters
handlerCallback function to invoke on crash, or NULL to uninstall
Returns
ASCIICHAT_OK on success, error code on failure
Note
Only one handler can be active at a time; installing a new handler replaces the previous one
Crashes still terminate the process after the handler runs
Signal safety: handlers should only use async-signal-safe functions
Example:
void my_crash_handler(int sig, void *ctx) {
fprintf(stderr, "Caught signal %d\n", sig);
// Perform minimal cleanup, write logs, etc.
}
void platform_install_crash_handler(void)
Install crash handlers for the application.

◆ platform_is_binary_in_path()

bool platform_is_binary_in_path ( const char *  bin_name)

#include <filesystem.h>

Check if a binary is available in the system PATH.

This function checks if the specified binary can be found in the PATH by searching each directory in the PATH environment variable. Results are cached to avoid repeated filesystem checks.

On Windows: Automatically appends .exe if needed, checks with GetFileAttributesA On Unix: Uses access() with X_OK to verify executable permission

Parameters
bin_nameBase name of the binary (e.g., "ssh-keygen", "llvm-symbolizer") On Windows, .exe extension is added automatically if not present
Returns
true if binary is in PATH and executable, false otherwise
Note
Thread-safe: Uses internal locking for cache access
First call for a binary checks filesystem, subsequent calls use cache
No external dependencies (doesn't spawn where/command -v)
Example:
if (platform_is_binary_in_path("ssh-keygen")) {
// Use ssh-keygen
}
bool platform_is_binary_in_path(const char *bin_name)
Check if a binary is available in the system PATH.
Definition filesystem.c:133

Definition at line 133 of file filesystem.c.

133 {
134 if (!bin_name || bin_name[0] == '\0') {
135 return false;
136 }
137
138 init_cache_once();
139 if (!lifecycle_is_initialized(&g_cache_lc)) {
140 SET_ERRNO(ERROR_INVALID_STATE, "Binary PATH cache not initialized, checking directly (this should never happen)");
141 return check_binary_in_path_uncached(bin_name);
142 }
143
144 rwlock_rdlock(&g_cache_rwlock);
145 bin_cache_entry_t *entry = NULL;
146 HASH_FIND_STR(g_bin_path_cache, bin_name, entry);
147 rwlock_rdunlock(&g_cache_rwlock);
148
149 if (entry) {
150 log_dev("Binary '%s' %s in PATH (%s)", bin_name, colored_string(LOG_COLOR_INFO, "found"),
151 colored_string(LOG_COLOR_WARN, "cached"));
152 return entry->in_path;
153 }
154
155 bool found = check_binary_in_path_uncached(bin_name);
156
158 if (!entry) {
159 SET_ERRNO(ERROR_MEMORY, "Failed to allocate cache entry");
160 return found;
161 }
162
163 entry->bin_name = platform_strdup(bin_name);
164 if (!entry->bin_name) {
165 SET_ERRNO(ERROR_MEMORY, "Failed to duplicate binary name");
166 SAFE_FREE(entry);
167 return found;
168 }
169
170 entry->in_path = found;
171
172 rwlock_wrlock(&g_cache_rwlock);
173 HASH_ADD_KEYPTR(hh, g_bin_path_cache, entry->bin_name, strlen(entry->bin_name), entry);
174 rwlock_wrunlock(&g_cache_rwlock);
175
176 char *found_notfound_str =
177 found ? colored_string(LOG_COLOR_INFO, "found") : colored_string(LOG_COLOR_ERROR, "NOT found");
178 log_dev("Binary '%s' %s in PATH", bin_name, found_notfound_str);
179
180 return found;
181}
#define SAFE_MALLOC(size, cast)
Definition common.h:264
@ ERROR_MEMORY
Definition error_codes.h:56
@ LOG_COLOR_ERROR
Definition log/log.h:135
@ LOG_COLOR_INFO
Definition log/log.h:133
@ LOG_COLOR_WARN
Definition log/log.h:134
#define rwlock_rdlock(lock)
Acquire a read lock (with debug tracking in debug builds)
Definition rwlock.h:294
char * platform_strdup(const char *s)
Duplicate string (strdup replacement)
#define rwlock_rdunlock(lock)
Release a read lock (with debug tracking in debug builds)
Definition rwlock.h:331
const char * colored_string(log_color_t color, const char *text)
Build a colored string for terminal output.
bool lifecycle_is_initialized(const lifecycle_t *lc)
Definition lifecycle.c:155
char * bin_name
Binary name string (allocated, owned by cache) - also used as uthash key.
Definition filesystem.c:76
bool in_path
Whether binary was found in PATH (true = found, false = not found)
Definition filesystem.c:77
bool check_binary_in_path_uncached(const char *bin_name)

References bin_cache_entry_t::bin_name, check_binary_in_path_uncached(), colored_string(), ERROR_INVALID_STATE, ERROR_MEMORY, bin_cache_entry_t::in_path, lifecycle_is_initialized(), LOG_COLOR_ERROR, LOG_COLOR_INFO, LOG_COLOR_WARN, log_dev, platform_strdup(), rwlock_rdlock, rwlock_rdunlock, rwlock_wrlock, rwlock_wrunlock, SAFE_FREE, SAFE_MALLOC, and SET_ERRNO.

◆ platform_is_directory()

int platform_is_directory ( const char *  path)

#include <filesystem.h>

Check if a path is a directory.

Convenience function that checks if a path points to a directory. Does not follow symbolic links.

Parameters
pathPath to check
Returns
Non-zero (true) if path is a directory, 0 (false) otherwise
Note
Does not follow symbolic links.
Returns false for regular files, sockets, pipes, etc.
Returns false if the path doesn't exist.

Referenced by discovery_keys_save_cached().

◆ platform_is_interactive()

bool platform_is_interactive ( void  )

#include <question.h>

Check if interactive prompting is available.

Returns
true if stdin is a TTY and interactive prompting is possible

Use this to check before calling prompt functions in contexts where non-interactive operation is acceptable (e.g., scripted usage).

Definition at line 359 of file comprehensive.c.

359 {
360 return false;
361}

Referenced by client_crypto_handshake(), prompt_password(), and prompt_password_simple().

◆ platform_is_regular_file()

int platform_is_regular_file ( const char *  path)

#include <filesystem.h>

Check if a path is a regular file.

Convenience function that checks if a path points to a regular file. Does not follow symbolic links.

Parameters
pathFile path to check
Returns
Non-zero (true) if path is a regular file, 0 (false) otherwise
Note
Does not follow symbolic links.
Returns false for directories, sockets, pipes, etc.
Returns false if the file doesn't exist.

Definition at line 124 of file util.c.

124 {
125 (void)path;
126 return 0; // No file system access in WASM mirror mode
127}

Referenced by discovery_keys_clear_cache(), discovery_keys_load_cached(), and path_validate_user_path().

◆ platform_load_system_ca_certs()

asciichat_error_t platform_load_system_ca_certs ( char **  pem_data_out,
size_t *  pem_size_out 
)

#include <system.h>

Load system CA certificates for TLS/HTTPS.

Loads the operating system's trusted root CA certificates in PEM format. This allows TLS connections to trust the same CAs that the OS trusts.

Platform-specific paths:

  • Linux (Debian/Ubuntu): /etc/ssl/certs/ca-certificates.crt
  • Linux (RHEL/CentOS): /etc/pki/tls/certs/ca-bundle.crt
  • macOS: /etc/ssl/cert.pem or Security framework
  • Windows: Uses CryptoAPI certificate store
Parameters
pem_data_outPointer to receive allocated PEM data (caller must free)
pem_size_outPointer to receive size of PEM data
Returns
ASCIICHAT_OK on success, error code on failure
Note
The caller must free the allocated PEM data with SAFE_FREE() or ALLOC_FREE().
Example:
char* pem_data;
size_t pem_size;
if (platform_load_system_ca_certs(&pem_data, &pem_size) == ASCIICHAT_OK) {
// Use pem_data for TLS verification
SAFE_FREE(pem_data);
}
asciichat_error_t platform_load_system_ca_certs(char **pem_data_out, size_t *pem_size_out)
Load system CA certificates for TLS/HTTPS.

Referenced by https_get().

◆ platform_localtime()

asciichat_error_t platform_localtime ( const time_t *  timer,
struct tm *  result 
)

#include <system.h>

Platform-safe localtime wrapper.

Uses localtime_s on Windows and localtime_r on POSIX. Thread-safe on all platforms.

Parameters
timerPointer to time_t value
resultPointer to struct tm to receive result
Returns
ASCIICHAT_OK on success, error code on error

Definition at line 50 of file util.c.

50 {
51 if (!timer || !result) {
53 }
54 // Use thread-safe localtime_r for both WASM and native builds
55 // IMPORTANT: localtime() is NOT thread-safe and causes deadlocks in threaded WASM builds
56 if (localtime_r(timer, result) != NULL) {
57 return ASCIICHAT_OK;
58 }
60}

References ASCIICHAT_OK, ERROR_INVALID_PARAM, and ERROR_PLATFORM_INIT.

Referenced by asciichat_error_stats_print(), asciichat_print_error_context(), backtrace_print(), get_current_time_formatted(), manpage_fmt_write_title(), and time_format_now().

◆ platform_memcpy()

asciichat_error_t platform_memcpy ( void *  dest,
size_t  dest_size,
const void *  src,
size_t  count 
)

#include <system.h>

Platform-safe memcpy wrapper.

Uses memcpy_s on Windows when available (C11) and memcpy with bounds checking on POSIX. Provides consistent interface across platforms.

Parameters
destDestination buffer
dest_sizeSize of destination buffer
srcSource buffer
countNumber of bytes to copy
Returns
ASCIICHAT_OK on success, error code on error

Definition at line 62 of file platform/wasm/string.c.

62 {
63 if (!dest || !src) {
65 }
66 if (count > dest_size) {
67 return ERROR_INVALID_PARAM; // Buffer overflow protection
68 }
69 memcpy(dest, src, count);
70 return ASCIICHAT_OK;
71}

References ASCIICHAT_OK, and ERROR_INVALID_PARAM.

◆ platform_memmove()

asciichat_error_t platform_memmove ( void *  dest,
size_t  dest_size,
const void *  src,
size_t  count 
)

#include <system.h>

Platform-safe memmove wrapper.

Uses memmove_s on Windows when available (C11) and memmove with bounds checking on POSIX. Handles overlapping memory regions safely.

Parameters
destDestination buffer
dest_sizeSize of destination buffer
srcSource buffer
countNumber of bytes to move
Returns
ASCIICHAT_OK on success, error code on error

◆ platform_memset()

asciichat_error_t platform_memset ( void *  dest,
size_t  dest_size,
int  ch,
size_t  count 
)

#include <system.h>

Platform-safe memset wrapper.

Uses memset_s on Windows when available (C11) and memset with bounds checking on POSIX. Provides consistent interface across platforms.

Parameters
destDestination buffer
dest_sizeSize of destination buffer
chValue to set (cast to unsigned char)
countNumber of bytes to set
Returns
ASCIICHAT_OK on success, error code on error

Definition at line 73 of file platform/wasm/string.c.

73 {
74 if (!dest) {
76 }
77 if (count > dest_size) {
78 return ERROR_INVALID_PARAM; // Buffer overflow protection
79 }
80 memset(dest, ch, count);
81 return ASCIICHAT_OK;
82}

References ASCIICHAT_OK, and ERROR_INVALID_PARAM.

◆ platform_mkdir()

asciichat_error_t platform_mkdir ( const char *  path,
int  mode 
)

#include <filesystem.h>

Create a directory.

Creates a directory with the specified permissions. If the directory already exists, this is not an error.

Platform-specific implementations:

  • POSIX: Uses mkdir() with mode parameter
  • Windows: Uses CreateDirectoryA(), mode is ignored
Parameters
pathDirectory path to create
modeFile permissions (0700 for owner rwx only, ignored on Windows)
Returns
ASCIICHAT_OK on success (or if directory exists), error code on failure
Note
Permissions only apply to parent directories that need to be created.
On Windows, the mode parameter is ignored (uses ACLs).
Returns ASCIICHAT_OK even if the directory already exists.
Example:
if (platform_mkdir("~/.ascii-chat", 0700) == ASCIICHAT_OK) {
// Directory created or already exists
}
asciichat_error_t platform_mkdir(const char *path, int mode)
Create a directory.

Referenced by discovery_keys_save_cached(), and get_log_dir().

◆ platform_mkdir_recursive()

asciichat_error_t platform_mkdir_recursive ( const char *  path,
int  mode 
)

#include <filesystem.h>

Create directories recursively (mkdir -p equivalent)

Creates all parent directories needed for the given path.

Platform-specific implementations:

  • POSIX: Uses mkdir() in a loop for each path component
  • Windows: Uses CreateDirectoryA() in a loop for each path component
Parameters
pathDirectory path to create (may contain parent directories)
modeFile permissions (0700 for owner rwx only, ignored on Windows)
Returns
ASCIICHAT_OK on success, error code on failure
Note
Handles both forward slashes (/) and backslashes () as separators
Safe on Windows drive letters (e.g., C:\path\to\dir)
Returns ASCIICHAT_OK if the directory already exists
Example:
// Create ~/.ascii-chat/config/ and all parent directories
if (platform_mkdir_recursive("~/.ascii-chat/config", 0700) == ASCIICHAT_OK) {
// Directory and parents created or already exist
}
asciichat_error_t platform_mkdir_recursive(const char *path, int mode)
Create directories recursively (mkdir -p equivalent)

Definition at line 39 of file wasm/stubs/filesystem.c.

39 {
40 (void)path;
41 (void)mode;
42 return ERROR_PLATFORM_INIT; // Not implemented for WASM
43}

References ERROR_PLATFORM_INIT.

Referenced by acds_identity_save(), config_create_default(), and get_discovery_database_dir().

◆ platform_mkdtemp()

asciichat_error_t platform_mkdtemp ( char *  path_out,
size_t  path_size,
const char *  prefix 
)

#include <filesystem.h>

Create a temporary directory with a given prefix.

Creates an isolated temporary directory with proper permissions.

Windows: Creates directory in temp dir with process-specific prefix Unix: Creates directory via mkdtemp with prefix in /tmp

Parameters
path_outBuffer to store the created temp directory path (must be at least 256 bytes)
path_sizeSize of path_out buffer
prefixPrefix for the temp directory name (e.g., "ascii-chat-gpg")
Returns
ASCIICHAT_OK on success, error code on failure
Note
Caller must delete the directory when done using platform_rmdir_recursive()
Directory permissions are restricted to 0700 (owner-only access)

Referenced by gpg_homedir_create().

◆ platform_mmap_close()

void platform_mmap_close ( platform_mmap_t *  mapping)

#include <mmap.h>

Unmap and close a memory-mapped file.

Unmaps the memory region and closes the underlying file handle. Safe to call on an already-closed or uninitialized mapping.

Parameters
mappingMapping handle to close
Note
Does not explicitly sync before closing; kernel will flush dirty pages eventually. Call platform_mmap_sync() first if immediate persistence is required.

Referenced by log_mmap_destroy().

◆ platform_mmap_init()

void platform_mmap_init ( platform_mmap_t *  mapping)

#include <mmap.h>

Initialize a platform_mmap_t structure.

Sets all fields to safe initial values. Call before first use.

Parameters
mappingPointer to mapping structure to initialize

Referenced by log_mmap_init().

◆ platform_mmap_is_valid()

bool platform_mmap_is_valid ( const platform_mmap_t *  mapping)

#include <mmap.h>

Check if a mapping is currently valid.

Parameters
mappingMapping handle to check
Returns
true if the mapping is open and usable, false otherwise

◆ platform_mmap_open()

asciichat_error_t platform_mmap_open ( const char *  name,
const char *  path,
size_t  size,
platform_mmap_t *  out 
)

#include <mmap.h>

Memory-map a file for read/write access.

Opens or creates a file and maps it into memory. The file is created if it doesn't exist, and resized to the specified size.

The mapping uses shared mode (MAP_SHARED on POSIX, FILE_MAP_ALL_ACCESS on Windows) so changes are visible to other processes and persist to the file.

Parameters
pathFile path to map (created if doesn't exist)
sizeDesired mapping size in bytes
[out]outOutput mapping handle (must be initialized with platform_mmap_init)
Returns
ASCIICHAT_OK on success, error code on failure
Note
On success, out->addr contains the mapped memory address
Call platform_mmap_close() to unmap and close

Example:

if (platform_mmap_open("/tmp/log.mmap", 1024 * 1024, &mapping) == ASCIICHAT_OK) {
// Use mapping.addr as normal memory
memset(mapping.addr, 0, mapping.size);
}
void platform_mmap_close(platform_mmap_t *mapping)
Unmap and close a memory-mapped file.
asciichat_error_t platform_mmap_open(const char *name, const char *path, size_t size, platform_mmap_t *out)
Memory-map a file for read/write access.
void platform_mmap_init(platform_mmap_t *mapping)
Initialize a platform_mmap_t structure.
Memory-mapped file handle.

Referenced by log_mmap_init().

◆ platform_mmap_sync()

void platform_mmap_sync ( platform_mmap_t *  mapping,
bool  async 
)

#include <mmap.h>

Flush memory-mapped changes to disk.

Requests the kernel to flush any modified pages to the underlying file. This is typically not needed as the kernel flushes automatically, but can be used to ensure data persistence at specific points.

Parameters
mappingMapping handle to sync
asyncIf true, return immediately (async flush). If false, block until flush completes (sync flush).
Note
On crash, unflushed data may be lost. For crash-critical data, call platform_mmap_sync(mapping, false) after important writes.

Referenced by log_mmap_destroy(), log_mmap_rotate(), log_mmap_sync(), and log_mmap_write().

◆ platform_normalize_path_separators()

void platform_normalize_path_separators ( char *  path)

#include <filesystem.h>

Normalize path separators for the current platform.

Converts all path separators to the preferred format for the current platform.

Platform-specific behavior:

  • Windows: Converts forward slashes (/) to backslashes ()
  • Unix: No-op (already uses forward slashes)
Parameters
pathPath string to normalize (modified in-place)
Note
The path is modified in-place
Safe to call with NULL (no-op)

Definition at line 98 of file util.c.

98 {
99 // No-op - WASM uses forward slashes (Unix-style)
100 (void)path;
101}

Referenced by expand_path().

◆ platform_open()

int platform_open ( const char *  name,
const char *  pathname,
int  flags,
  ... 
)

#include <filesystem.h>

Safe file open (open replacement)

Parameters
nameDebug name for the file descriptor (required, e.g., "config_file")
pathnameFile path to open
flagsOpen flags (PLATFORM_O_RDONLY, PLATFORM_O_WRONLY, PLATFORM_O_RDWR, etc.)
...Variable arguments (mode for PLATFORM_O_CREAT)
Returns
File descriptor, or -1 on error

Definition at line 68 of file wasm/stubs/filesystem.c.

68 {
69 if (!name) {
70 return -1;
71 }
72
73 // Handle optional mode parameter for O_CREAT
74 int fd = -1;
75 int mode = 0;
76 if (flags & 0x0200) { // O_CREAT flag value
77 va_list args;
78 va_start(args, flags);
79 mode = va_arg(args, int);
80 va_end(args);
81 fd = open(pathname, flags, mode);
82 } else {
83 fd = open(pathname, flags);
84 }
85
86 if (fd >= 0) {
87 NAMED_REGISTER_FD(fd, name);
88 log_dev("Opened file descriptor %d for %s at path %s", fd, name, pathname);
89 }
90
91 return fd;
92}

References args, log_dev, and NAMED_REGISTER_FD.

Referenced by acds_identity_save(), check_known_host(), check_known_host_no_identity(), log_init(), main(), parse_gpg_keys_from_response(), remove_known_host(), and test_logging_disable().

◆ platform_path_get_separator()

char platform_path_get_separator ( void  )

#include <filesystem.h>

Get the path separator character for current platform.

Returns
'\' on Windows, '/' on POSIX

◆ platform_path_is_absolute()

bool platform_path_is_absolute ( const char *  path)

#include <filesystem.h>

Check if a path is absolute (not relative)

Platform-specific logic:

  • Windows: Checks for drive letter (C:) or UNC path (\server)
  • POSIX: Checks for leading slash (/)
Parameters
pathPath string to check
Returns
true if path is absolute, false if relative or NULL

◆ platform_path_normalize()

asciichat_error_t platform_path_normalize ( const char *  input,
char *  output,
size_t  output_size 
)

#include <filesystem.h>

Normalize and validate a file path.

Converts path to platform-standard format with correct separators and normalization.

Parameters
inputInput path string
outputOutput buffer for normalized path
output_sizeSize of output buffer
Returns
ASCIICHAT_OK on success, error code on failure

◆ platform_path_skip_absolute_prefix()

const char * platform_path_skip_absolute_prefix ( const char *  path)

#include <filesystem.h>

Skip absolute path prefix (drive letter on Windows)

Advances pointer past the absolute path prefix for the current platform.

Platform-specific behavior:

  • Windows: Skips drive letter (e.g., "C:" in "C:\path")
  • Unix: Returns original pointer (no prefix to skip)
Parameters
pathPath string to process (e.g., "C:\path" or "/path")
Returns
Pointer to first character after the prefix, or original path if no prefix
Note
Safe to call with NULL (returns NULL)
Safe to call with relative paths

◆ platform_path_strcasecmp()

int platform_path_strcasecmp ( const char *  a,
const char *  b,
size_t  n 
)

#include <filesystem.h>

Platform-aware path string comparison.

Compares paths with platform-specific rules for case sensitivity.

Platform-specific behavior:

  • Windows: Case-insensitive comparison
  • Unix: Case-sensitive comparison
Parameters
aFirst path string
bSecond path string
nMaximum number of characters to compare
Returns
0 if equal, <0 if a<b, >0 if a>b

Definition at line 119 of file util.c.

119 {
120 return strncmp(a, b, n); // Case-sensitive on WASM
121}

Referenced by path_is_within_base().

◆ platform_pclose()

asciichat_error_t platform_pclose ( FILE **  stream_ptr)

#include <process.h>

Close a process stream opened with platform_popen()

Closes the stream and waits for the process to terminate. Returns the process exit status.

Platform-specific implementations:

  • POSIX: Uses pclose() and waits for process
  • Windows: Uses _pclose() and waits for process
Parameters
stream_ptrPointer to FILE* stream to close
Returns
ASCIICHAT_OK on success, error code on failure
Note
stream_ptr must be a pointer to a FILE* stream obtained from platform_popen().
The FILE* pointer is set to NULL after closing.
Always sets errno context on failure for debugging.

Definition at line 18 of file platform/wasm/stubs/network.c.

18 {
19 (void)stream_ptr;
20 return SET_ERRNO(ERROR_NOT_SUPPORTED, "platform_pclose not supported in WASM");
21}
@ ERROR_NOT_SUPPORTED
Definition error_codes.h:74

References ERROR_NOT_SUPPORTED, and SET_ERRNO.

Referenced by check_gpg_key_expiry(), gpg_get_public_key(), gpg_verify_detached_ed25519(), gpg_verify_signature_with_binary(), parse_gpg_keys_from_response(), and yt_dlp_extract_stream_url().

◆ platform_pipe_close()

int platform_pipe_close ( pipe_t  pipe)

#include <pipe.h>

Close a pipe connection.

Parameters
pipePipe handle to close
Returns
0 on success, non-zero on error

Closes the pipe connection using the appropriate platform-specific function:

  • Windows: CloseHandle()
  • POSIX: close()

Referenced by gpg_agent_connect(), gpg_agent_disconnect(), ssh_agent_add_key(), ssh_agent_has_key(), ssh_agent_is_available(), and ssh_agent_sign().

◆ platform_pipe_connect()

pipe_t platform_pipe_connect ( const char *  path)

#include <pipe.h>

Connect to an agent via named pipe (Windows) or Unix socket (POSIX)

Parameters
pathPath to agent (named pipe path on Windows, socket path on POSIX)
Returns
Pipe handle on success, INVALID_PIPE_VALUE on error

Connects to an agent using the appropriate platform-specific mechanism:

  • Windows: Opens named pipe via CreateFileA
  • POSIX: Connects to Unix domain socket via socket() + connect()
Note
The path format differs by platform:
  • Windows: Named pipe path (e.g., "\\\\.\\pipe\\openssh-ssh-agent")
  • POSIX: Unix socket path (e.g., "/tmp/ssh-XXXXXX/agent.XXXXXX")

Referenced by gpg_agent_connect().

◆ platform_pipe_is_valid()

bool platform_pipe_is_valid ( pipe_t  pipe)

#include <pipe.h>

Check if a pipe handle is valid.

Parameters
pipePipe handle to check
Returns
true if pipe is valid, false otherwise

Referenced by gpg_agent_connect().

◆ platform_pipe_read()

ssize_t platform_pipe_read ( pipe_t  pipe,
void *  buf,
size_t  len 
)

#include <pipe.h>

Read data from a pipe.

Parameters
pipePipe handle to read from
bufBuffer to store read data
lenMaximum number of bytes to read
Returns
Number of bytes read on success, -1 on error, 0 on connection closed

Reads data from the pipe using the appropriate platform-specific function:

  • Windows: ReadFile()
  • POSIX: read()
Note
This function may read fewer bytes than requested (short read). Caller should handle partial reads if needed.

Referenced by gpg_get_public_key(), ssh_agent_add_key(), ssh_agent_has_key(), and ssh_agent_sign().

◆ platform_pipe_write()

ssize_t platform_pipe_write ( pipe_t  pipe,
const void *  buf,
size_t  len 
)

#include <pipe.h>

Write data to a pipe.

Parameters
pipePipe handle to write to
bufData buffer to write
lenNumber of bytes to write
Returns
Number of bytes written on success, -1 on error

Writes data to the pipe using the appropriate platform-specific function:

  • Windows: WriteFile()
  • POSIX: write()
Note
This function may write fewer bytes than requested (short write). Caller should handle partial writes if needed.

Referenced by gpg_get_public_key(), ssh_agent_add_key(), ssh_agent_has_key(), and ssh_agent_sign().

◆ platform_popen()

asciichat_error_t platform_popen ( const char *  name,
const char *  command,
const char *  mode,
FILE **  out_stream 
)

#include <process.h>

Execute a command and return a file stream for reading/writing.

Opens a process for communication, similar to POSIX popen(). Creates a unidirectional pipe to read from or write to the process.

Platform-specific implementations:

  • POSIX: Uses popen()
  • Windows: Uses _popen()
Parameters
commandCommand line to execute (e.g., "ssh-keygen -l -f file.pub")
modeFile stream mode: "r" for reading, "w" for writing
out_streamPointer to receive the FILE* stream
Returns
ASCIICHAT_OK on success, error code on failure
Note
The returned stream must be closed with platform_pclose().
On Windows, the mode parameters are the same as POSIX popen().
Example:
FILE *stream;
if (platform_popen("ssh-keygen -l -f key.pub", "r", &stream) == ASCIICHAT_OK) {
char line[256];
fgets(line, sizeof(line), stream);
platform_pclose(&stream);
}
asciichat_error_t platform_popen(const char *name, const char *command, const char *mode, FILE **out_stream)
Execute a command and return a file stream for reading/writing.
asciichat_error_t platform_pclose(FILE **stream_ptr)
Close a process stream opened with platform_popen()

Definition at line 10 of file platform/wasm/stubs/network.c.

10 {
11 (void)name;
12 (void)command;
13 (void)mode;
14 (void)out_stream;
15 return SET_ERRNO(ERROR_NOT_SUPPORTED, "platform_popen not supported in WASM");
16}

References ERROR_NOT_SUPPORTED, and SET_ERRNO.

Referenced by check_gpg_key_expiry(), gpg_get_public_key(), gpg_verify_detached_ed25519(), gpg_verify_signature_with_binary(), parse_gpg_keys_from_response(), and yt_dlp_extract_stream_url().

◆ platform_process_destroy()

void platform_process_destroy ( platform_process_t *  process)

#include <process.h>

Free process handle.

Releases resources associated with a process handle. Must be called for every process created with platform_process_spawn.

Parameters
processProcess handle to free
Note
Safe to call with NULL

◆ platform_process_is_alive()

bool platform_process_is_alive ( platform_process_t *  process)

#include <process.h>

Check if process is still running.

Parameters
processProcess handle
Returns
true if process is still running, false if terminated
Note
Non-blocking check

◆ platform_process_kill()

asciichat_error_t platform_process_kill ( platform_process_t *  process)

#include <process.h>

Terminate a process.

Forcefully terminates a running process.

Parameters
processProcess handle
Returns
ASCIICHAT_OK on success, error code on failure
Note
Does not wait for termination; use platform_process_wait to wait

◆ platform_process_spawn()

asciichat_error_t platform_process_spawn ( platform_process_t **  process_out,
const char *  path,
const char *const *  argv,
int  stdin_fd,
int  stdout_fd,
int  stderr_fd 
)

#include <process.h>

Spawn a child process.

Creates and starts a new process with the specified command and arguments.

Platform-specific behavior:

  • Windows: Uses CreateProcessA with PROCESS_INFORMATION
  • POSIX: Uses fork() and exec()
Parameters
process_outOutput parameter: process handle (caller must free with platform_process_destroy)
pathPath to executable (can be relative or absolute)
argvArgument array, NULL-terminated (argv[0] should be program name)
stdin_fdFile descriptor for stdin, or -1 to use parent's stdin
stdout_fdFile descriptor for stdout, or -1 to use parent's stdout
stderr_fdFile descriptor for stderr, or -1 to use parent's stderr
Returns
ASCIICHAT_OK on success, error code on failure
Note
argv must be NULL-terminated
File descriptors are duplicated, not closed

◆ platform_process_wait()

asciichat_error_t platform_process_wait ( platform_process_t *  process,
int  timeout_ms,
int *  exit_code_out 
)

#include <process.h>

Wait for process to terminate with timeout.

Waits for a spawned process to complete execution.

Parameters
processProcess handle from platform_process_spawn
timeout_msTimeout in milliseconds, or -1 for infinite wait
exit_code_outOptional output: process exit code
Returns
ASCIICHAT_OK if process exited, ERROR_TIMEOUT if timeout
Note
On Windows, exit code is from GetExitCodeProcess
On POSIX, exit code is from waitpid

◆ platform_prompt_question()

int platform_prompt_question ( const char *  prompt,
char *  buffer,
size_t  max_len,
prompt_opts_t  opts 
)

#include <question.h>

Prompt the user for text input.

Parameters
promptThe prompt message to display
bufferBuffer to store the entered text
max_lenMaximum length of the buffer (including null terminator)
optsPrompt options (use PROMPT_OPTS_DEFAULT, PROMPT_OPTS_PASSWORD, etc.)
Returns
0 on success, -1 on failure or user cancellation (Ctrl+C)

Displays a prompt and reads user input. The prompt format depends on opts:

  • same_line=true: "prompt: " (user types on same line)
  • same_line=false: "prompt:\n> " (user types on next line after "> ")

When echo=false, input is hidden and optionally masked with mask_char.

Note
Acquires terminal lock during prompting to prevent log interleaving.
Returns -1 if stdin is not a TTY (non-interactive mode).

Referenced by prompt_password(), and prompt_password_simple().

◆ platform_prompt_yes_no()

bool platform_prompt_yes_no ( const char *  prompt,
bool  default_yes 
)

#include <question.h>

Prompt the user for a yes/no answer.

Parameters
promptThe question to ask (without the yes/no suffix)
default_yesIf true, default is Yes (Y/n); if false, default is No (y/N)
Returns
true if user answered yes, false if user answered no or on error

Displays a yes/no prompt with the default shown in uppercase:

  • default_yes=true: "prompt (Y/n)? "
  • default_yes=false: "prompt (y/N)? "

Accepts: "yes", "y", "Y" for yes; "no", "n", "N" for no. Empty input (just Enter) returns the default value.

Note
Acquires terminal lock during prompting to prevent log interleaving.
Returns false if stdin is not a TTY (non-interactive mode).

Definition at line 83 of file util.c.

83 {
84 (void)question;
85 return default_yes; // Always return default in WASM
86}

Referenced by action_completions(), client_crypto_handshake(), config_create_default(), discovery_keys_verify_change(), options_config_generate_manpage_merged(), prompt_unknown_host(), and prompt_unknown_host_no_identity().

◆ platform_raise_fd_limit()

void platform_raise_fd_limit ( unsigned int  limit)

#include <system.h>

Raise the file descriptor limit for the current process.

Parameters
limitDesired soft limit (0 = use platform default maximum)

Referenced by session_server_like_run().

◆ platform_read()

ssize_t platform_read ( int  fd,
void *  buf,
size_t  count 
)

#include <filesystem.h>

Safe file read (read replacement)

Parameters
fdFile descriptor
bufBuffer to read into
countNumber of bytes to read
Returns
Number of bytes read, or -1 on error

Definition at line 64 of file wasm/stubs/filesystem.c.

64 {
65 return read(fd, buf, count);
66}

◆ platform_register_signal_handlers()

asciichat_error_t platform_register_signal_handlers ( const platform_signal_handler_t *  handlers,
int  count 
)

#include <system.h>

Register multiple signal handlers at once.

Parameters
handlersArray of signal handler descriptors
countNumber of handlers in the array
Returns
ASCIICHAT_OK on success, error code on failure

Registers multiple signal handlers in one call, reducing repeated #ifdef _WIN32 blocks. On Windows, this handles SIGINT and SIGTERM via console control handlers. On POSIX, uses platform_signal() for each.

Example:
{SIGTERM, sigterm_handler},
{SIGINT, sigint_handler},
};
asciichat_error_t platform_register_signal_handlers(const platform_signal_handler_t *handlers, int count)
Register multiple signal handlers at once.
Signal handler descriptor for bulk registration.
Definition system.h:295

Referenced by setup_signal_handlers().

◆ platform_request_timer_precision()

asciichat_error_t platform_request_timer_precision ( int  precision)

#include <system.h>

Request coarse (reduced resolution) timer precision.

Requests the OS to use coarse timer precision, reducing power consumption and improving system responsiveness at the cost of lower timer accuracy.

Platform-specific implementations:

  • Windows: Calls timeBeginPeriod(1) to reduce timer resolution from ~15ms to ~1ms
  • POSIX: No-op (POSIX systems don't provide this feature)

This is typically called during application startup if you need timer precision (e.g., for real-time audio/video). Must be balanced with platform_restore_timer_resolution().

Parameters
precisionDesired timer precision in milliseconds (typically 1)
Returns
ASCIICHAT_OK on success, error code on failure
Note
On Windows, this increases power consumption slightly
POSIX systems return ASCIICHAT_OK but do nothing
Must call platform_restore_timer_resolution() to restore default behavior

◆ platform_resolve_hostname_to_ipv4()

asciichat_error_t platform_resolve_hostname_to_ipv4 ( const char *  hostname,
char *  ipv4_out,
size_t  ipv4_out_size 
)

#include <system.h>

Resolve hostname to IPv4 address.

Performs DNS resolution to convert a hostname to an IPv4 address string. Handles platform-specific networking initialization and cleanup.

Parameters
hostnameHostname to resolve (e.g., "example.com")
ipv4_outBuffer to store the resolved IPv4 address (e.g., "192.168.1.1")
ipv4_out_sizeSize of the output buffer
Returns
ASCIICHAT_OK on success, error code on failure

Referenced by validate_opt_ip_address().

◆ platform_restore_timer_resolution()

asciichat_error_t platform_restore_timer_resolution ( void  )

#include <system.h>

Restore default timer precision.

Restores the default system timer precision, undoing a previous call to platform_request_timer_precision(). Reduces power consumption.

Platform-specific implementations:

  • Windows: Calls timeEndPeriod(1) to restore default timer resolution
  • POSIX: No-op (POSIX systems don't provide this feature)
Returns
ASCIICHAT_OK on success, error code on failure
Note
Safe to call without a matching platform_request_timer_precision() call
POSIX systems return ASCIICHAT_OK but do nothing

Definition at line 99 of file misc.c.

99 {
100 return ASCIICHAT_OK;
101}

References ASCIICHAT_OK.

Referenced by asciichat_shared_destroy().

◆ platform_rmdir_recursive()

asciichat_error_t platform_rmdir_recursive ( const char *  path)

#include <filesystem.h>

Recursively delete a directory and all its contents.

Safely removes a directory and all files/subdirectories within it. Safe to call on non-existent paths (returns ASCIICHAT_OK, no-op).

Windows: Uses FindFirstFile/DeleteFile/RemoveDirectory Unix: Uses opendir/readdir/rmdir with recursion

Parameters
pathPath to the directory to delete
Returns
ASCIICHAT_OK on success, error code on failure
Note
Path must be a directory, not a file
Safe to call on non-existent paths (returns ASCIICHAT_OK)

Referenced by gpg_homedir_create(), and gpg_homedir_destroy().

◆ platform_setenv()

int platform_setenv ( const char *  name,
const char *  value 
)

#include <system.h>

Set an environment variable.

Parameters
nameEnvironment variable name
valueEnvironment variable value (or NULL to unset)
Returns
0 on success, non-zero on error

Sets or unsets an environment variable.

Definition at line 50 of file wasm/system.c.

50 {
51 (void)name;
52 (void)value;
53 return -1; // Not supported in WASM
54}

Referenced by __attribute__(), and env_pop_prompt_response().

◆ platform_signal()

signal_handler_t platform_signal ( int  sig,
signal_handler_t  handler 
)

#include <system.h>

Set a signal handler.

Parameters
sigSignal number (e.g., SIGINT, SIGTERM)
handlerSignal handler function (or SIG_DFL, SIG_IGN)
Returns
Previous signal handler, or SIG_ERR on error

Registers a signal handler for the specified signal.

Definition at line 115 of file misc.c.

115 {
116 (void)sig;
117 (void)handler;
118 return NULL;
119}

Referenced by client_main(), main(), and session_server_like_run().

◆ platform_signal_name()

const char * platform_signal_name ( int  signal)

#include <signal.h>

Get human-readable name for signal number.

Parameters
signalSignal number (SIGSEGV, SIGABRT, etc.)
Returns
Human-readable signal name (e.g., "SIGSEGV"), or "UNKNOWN_SIGNAL"
Note
Returned string is not allocated; valid for program lifetime

◆ platform_sleep_ms()

void platform_sleep_ms ( unsigned int  ms)

#include <system.h>

Sleep for a specified number of milliseconds.

Parameters
msNumber of milliseconds to sleep

Sleeps the current thread for the specified duration.

Definition at line 24 of file platform/wasm/time.c.

24 {
25 struct timespec ts;
26 ts.tv_sec = ms / 1000;
27 ts.tv_nsec = (ms % 1000) * 1000000;
28 nanosleep(&ts, NULL);
29}

Referenced by acds_server_shutdown(), client_main(), client_receive_thread(), client_video_render_thread(), disconnect_client_for_bad_data(), discovery_session_process(), main(), session_client_like_run(), and ui_mdns_select().

◆ platform_sleep_ns()

void platform_sleep_ns ( uint64_t  ns)

#include <abstraction.h>

Platform-safe sleep function with nanosecond precision.

Parameters
nsSleep duration in nanoseconds

High-precision sleep for timing-critical operations like frame rate limiting and adaptive sleeps. Achieves nanosecond-level accuracy on supported platforms.

Note
On Windows, uses Sleep() with millisecond granularity, converting nanoseconds.
On POSIX, uses nanosleep() for nanosecond precision.
Example:
platform_sleep_ns(16666667); // Sleep for 16.67ms (60 FPS)
void platform_sleep_ns(uint64_t ns)
Platform-safe sleep function with nanosecond precision.

Definition at line 50 of file platform/wasm/time.c.

50 {
51 struct timespec ts;
52 ts.tv_sec = ns / 1000000000ULL;
53 ts.tv_nsec = ns % 1000000000ULL;
54 nanosleep(&ts, NULL);
55}

Referenced by adaptive_sleep_do(), client_audio_render_thread(), client_video_render_thread(), keepalive_stop_thread(), session_capture_sleep_for_fps(), and tcp_client_connect().

◆ platform_sleep_us()

void platform_sleep_us ( unsigned int  usec)

#include <abstraction.h>

High-precision sleep function with microsecond precision.

Parameters
usecNumber of microseconds to sleep

Sleeps the current thread for the specified number of microseconds with high precision. Supports early wakeup via shutdown signaling.

Note
On Windows, Sleep() has ~15ms minimum resolution. This function uses more precise timing mechanisms for microsecond-level accuracy.
On POSIX, uses usleep() or nanosleep() for microsecond precision.
Example:
platform_sleep_us(1000); // Sleep for 1000 microseconds (1 millisecond)
void platform_sleep_us(unsigned int us)
High-precision sleep function with microsecond precision.

Definition at line 31 of file platform/wasm/time.c.

31 {
32 struct timespec ts;
33 ts.tv_sec = us / 1000000;
34 ts.tv_nsec = (us % 1000000) * 1000;
35 nanosleep(&ts, NULL);
36}

Referenced by audio_client_init(), audio_sender_finalize(), audio_stop_thread(), capture_stop_thread(), client_send_thread_func(), connection_attempt_tcp(), discovery_session_process(), platform_write_all(), protocol_start_connection(), protocol_stop_connection(), remove_client(), server_connection_establish(), stats_logger_thread(), and time_sleep_ns().

◆ platform_socket_connect_timeout()

int platform_socket_connect_timeout ( socket_t  sock,
const struct sockaddr *  addr,
socklen_t  addr_len,
uint64_t  timeout_ns 
)

#include <socket.h>

Connect to remote address with timeout.

Attempts to connect to a remote address with an optional timeout.

Platform-specific behavior:

  • Windows: Uses ioctlsocket() to set non-blocking, connect(), then select()
  • POSIX: Uses fcntl() to set non-blocking, connect(), then poll()
Parameters
sockSocket to connect
addrAddress structure to connect to
addr_lenLength of address structure
timeout_msTimeout in milliseconds (0 = infinite wait)
Returns
0 on success, -1 on timeout or error
Note
Socket must be created but not yet connected
After call, socket is set back to blocking mode on success

◆ platform_socket_set_timeout()

int platform_socket_set_timeout ( socket_t  sock,
uint64_t  timeout_ns 
)

#include <socket.h>

Set send/receive timeout for a socket.

Configures the timeout for socket send and receive operations.

Platform-specific behavior:

  • Windows: Uses ioctlsocket() with SO_RCVTIMEO/SO_SNDTIMEO options
  • POSIX: Uses setsockopt() with SO_RCVTIMEO/SO_SNDTIMEO options
Parameters
sockSocket to configure
timeout_msTimeout in milliseconds (0 = blocking, -1 = infinite)
Returns
0 on success, -1 on error
Note
Timeout applies to both send and receive operations
Some platforms may require SO_SNDTIMEO and SO_RCVTIMEO separately

◆ platform_stat()

asciichat_error_t platform_stat ( const char *  path,
platform_stat_t *  stat_out 
)

#include <filesystem.h>

Get file statistics.

Retrieves metadata about a file without following symbolic links.

Platform-specific implementations:

  • POSIX: Uses lstat()
  • Windows: Uses GetFileAttributesExA()
Parameters
pathFile path to stat
stat_outPointer to platform_stat_t to receive results
Returns
ASCIICHAT_OK on success, error code on failure
Note
Does not follow symbolic links (uses lstat on POSIX).
Sets errno context on failure.
Example:
platform_stat_t stat_info;
if (platform_stat("key_file", &stat_info) == ASCIICHAT_OK) {
if (stat_info.is_regular_file) {
// Process the file
}
}
asciichat_error_t platform_stat(const char *path, platform_stat_t *stat_out)
Get file statistics.
File type information from stat()
Definition filesystem.h:232
int is_regular_file
Non-zero if file is a regular file.
Definition filesystem.h:235

◆ platform_stderr_redirect_to_null()

platform_stderr_redirect_handle_t platform_stderr_redirect_to_null ( void  )

#include <system.h>

Redirect stderr to /dev/null temporarily.

This is useful for suppressing noisy warnings from third-party libraries (e.g., PortAudio backend probes) that are harmless but clutter output.

Returns
Handle for restoring stderr, or {-1, -1} on failure
Note
On Windows, this function returns {-1, -1} and has no effect
You must call platform_stderr_restore() with the returned handle to restore stderr

Example:

noisy_third_party_function(); // stderr output suppressed
platform_stderr_restore(handle); // stderr restored
platform_stderr_redirect_handle_t platform_stderr_redirect_to_null(void)
Redirect stderr to /dev/null temporarily.
void platform_stderr_restore(platform_stderr_redirect_handle_t handle)
Restore stderr from a redirect handle.
Handle for temporary stderr redirection.
Definition system.h:375

◆ platform_stderr_restore()

void platform_stderr_restore ( platform_stderr_redirect_handle_t  handle)

#include <system.h>

Restore stderr from a redirect handle.

Restores stderr to its original destination and closes the /dev/null file descriptor.

Parameters
handleHandle returned by platform_stderr_redirect_to_null()
Note
Safe to call with invalid handle (e.g., {-1, -1}) - will do nothing
After calling this, the handle is invalidated and should not be reused

◆ platform_stdio_redirect_to_null_permanent()

void platform_stdio_redirect_to_null_permanent ( void  )

#include <system.h>

Permanently redirect stderr and stdout to /dev/null.

This is used before exit() to prevent cleanup handlers from writing to the console after we've already displayed final messages to the user.

Note
On Windows, this function has no effect
This is a one-way operation - streams cannot be restored

◆ platform_stdout_stderr_redirect_to_null()

platform_stderr_redirect_handle_t platform_stdout_stderr_redirect_to_null ( void  )

#include <system.h>

Redirect both stdout and stderr to /dev/null (restorable)

Suppresses output from both stdout and stderr. This is useful when initializing libraries that may output diagnostic messages that would corrupt terminal rendering.

Returns
Handle to pass to platform_stdout_stderr_restore() to restore streams
Note
Always call platform_stdout_stderr_restore() with the returned handle
The returned handle uses original_fd for stdout, devnull_fd for stderr

◆ platform_stdout_stderr_restore()

void platform_stdout_stderr_restore ( platform_stderr_redirect_handle_t  handle)

#include <system.h>

Restore stdout and stderr after platform_stdout_stderr_redirect_to_null()

Restores both stdout and stderr to their original destinations.

Parameters
handleHandle returned by platform_stdout_stderr_redirect_to_null()
Note
Safe to call with invalid handle - will do nothing

◆ platform_strcasecmp()

int platform_strcasecmp ( const char *  s1,
const char *  s2 
)

#include <string.h>

Case-insensitive string comparison.

Parameters
s1First string
s2Second string
Returns
0 if equal, <0 if s1<s2, >0 if s1>s2 (case-insensitive)

Definition at line 43 of file platform/wasm/string.c.

43 {
44 // Use standard strcasecmp (available in WASM)
45 return strcasecmp(s1, s2);
46}

Referenced by color_filter_from_cli_name(), detect_terminal_background(), validate_opt_log_level(), and validate_opt_reconnect().

◆ platform_strcpy()

asciichat_error_t platform_strcpy ( char *  dest,
size_t  dest_size,
const char *  src 
)

#include <system.h>

Platform-safe strcpy wrapper.

Uses strcpy_s on Windows when available (C11) and strncpy with bounds checking on POSIX. Always null-terminates the destination string.

Parameters
destDestination buffer
dest_sizeSize of destination buffer
srcSource string
Returns
ASCIICHAT_OK on success, error code on error

Definition at line 92 of file platform/wasm/string.c.

92 {
93 if (!dest || !src) {
95 }
96 if (dest_size == 0) {
98 }
99
100 size_t src_len = strlen(src);
101 if (src_len >= dest_size) {
102 return ERROR_INVALID_PARAM;
103 }
104
105 strncpy(dest, src, dest_size - 1);
106 dest[dest_size - 1] = '\0';
107 return ASCIICHAT_OK;
108}

References ASCIICHAT_OK, and ERROR_INVALID_PARAM.

◆ platform_strdup()

◆ platform_strerror()

const char * platform_strerror ( int  errnum)

#include <system.h>

Get human-readable error message for a system error code.

Converts a system error code into a readable error message string. Uses strerror_r on POSIX and FormatMessageA on Windows.

Parameters
errnumSystem error code
Returns
Pointer to error message string (may be static, do not modify or free)
Note
The returned string may be a static buffer. Do not modify or free it.
On POSIX, uses strerror_r with thread-local storage
On Windows, uses FormatMessageA with proper cleanup

Definition at line 46 of file wasm/system.c.

46 {
47 return strerror(errnum);
48}

Referenced by file_read_error_message(), and file_write_error_message().

◆ platform_strlcpy()

size_t platform_strlcpy ( char *  dst,
const char *  src,
size_t  size 
)

#include <string.h>

Safe string copy with size tracking (strlcpy)

Parameters
dstDestination buffer
srcSource string
sizeSize of destination buffer
Returns
Length of source string (before truncation)

Definition at line 13 of file platform/wasm/string.c.

13 {
14 size_t src_len = strlen(src);
15
16 if (size == 0) {
17 return src_len;
18 }
19
20 size_t copy_len = (src_len >= size) ? size - 1 : src_len;
21 memcpy(dst, src, copy_len);
22 dst[copy_len] = '\0';
23
24 return src_len;
25}

Referenced by detect_terminal_capabilities(), platform_get_cwd(), and platform_get_temp_dir().

◆ platform_strncasecmp()

int platform_strncasecmp ( const char *  s1,
const char *  s2,
size_t  n 
)

#include <string.h>

Case-insensitive string comparison with length limit.

Parameters
s1First string
s2Second string
nMaximum number of characters to compare
Returns
0 if equal, <0 if s1<s2, >0 if s1>s2 (case-insensitive)

Definition at line 88 of file platform/wasm/string.c.

88 {
89 return strncasecmp(s1, s2, n);
90}

◆ platform_strncpy()

int platform_strncpy ( char *  dst,
size_t  dst_size,
const char *  src,
size_t  count 
)

#include <string.h>

Safe string copy with explicit size bounds (strncpy replacement)

Parameters
dstDestination buffer
dst_sizeSize of destination buffer
srcSource string
countMaximum number of characters to copy
Returns
0 on success, -1 on overflow/error

Referenced by parse_private_key(), and parse_public_key().

◆ platform_strtok_r()

char * platform_strtok_r ( char *  str,
const char *  delim,
char **  saveptr 
)

#include <string.h>

Thread-safe string tokenization (strtok_r replacement)

Parameters
strString to tokenize (or NULL to continue tokenizing)
delimDelimiter characters
saveptrPointer to save tokenization state
Returns
Pointer to next token, or NULL when no more tokens

Referenced by parse_public_keys(), and sdp_parse().

◆ platform_temp_file_open()

asciichat_error_t platform_temp_file_open ( const char *  name,
const char *  path,
int *  fd_out 
)

#include <filesystem.h>

Open a temporary file for writing.

Platform-aware wrapper that handles the differences between POSIX and Windows temp file opening.

Platform-specific behavior:

Parameters
pathPath to the temporary file (from platform_create_temp_file)
fd_outOutput parameter: file descriptor
Returns
ASCIICHAT_OK on success, error code on failure
Note
Caller must close fd when done
On Windows, platform_create_temp_file returns fd=-1, so this wrapper opens it
On POSIX, platform_create_temp_file already returns valid fd

◆ platform_tmpfile()

FILE * platform_tmpfile ( void  )

#include <filesystem.h>

Create a temporary file (tmpfile replacement)

Returns
FILE pointer, or NULL on error

Definition at line 35 of file wasm/stubs/filesystem.c.

35 {
36 return tmpfile();
37}

Referenced by manpage_parser_parse_memory().

◆ platform_truncate_file()

asciichat_error_t platform_truncate_file ( const char *  path,
size_t  size 
)

#include <filesystem.h>

Truncate a file to a specific size.

Resizes a file to the specified size, removing data if truncating, or padding with zeros if extending (platform-dependent).

Platform-specific behavior:

  • Windows: Uses CreateFileA, SetFilePointerEx, SetEndOfFile
  • POSIX: Uses ftruncate() or truncate()
Parameters
pathPath to the file to truncate
sizeNew file size in bytes
Returns
ASCIICHAT_OK on success, error code on failure
Note
File must be writable

◆ platform_uninstall_crash_handler()

asciichat_error_t platform_uninstall_crash_handler ( void  )

#include <signal.h>

Uninstall the crash signal handler.

Removes any installed crash handler and restores default signal behavior.

Returns
ASCIICHAT_OK on success, error code on failure

◆ platform_unlink()

int platform_unlink ( const char *  pathname)

#include <filesystem.h>

Delete/unlink file.

Parameters
pathnameFile path to delete
Returns
0 on success, -1 on error

Referenced by gpg_verify_detached_ed25519(), and parse_gpg_keys_from_response().

◆ platform_unsetenv()

int platform_unsetenv ( const char *  name)

#include <system.h>

Remove an environment variable.

Parameters
nameVariable name to unset
Returns
0 on success, -1 on failure

Definition at line 56 of file wasm/system.c.

56 {
57 (void)name;
58 return -1; // Not supported in WASM
59}

Referenced by env_pop_prompt_response().

◆ platform_validate_key_file_permissions()

asciichat_error_t platform_validate_key_file_permissions ( const char *  key_path)

#include <filesystem.h>

Validate that a cryptographic key file has appropriate permissions.

Ensures that only the file owner can read the key file, preventing unauthorized access to private cryptographic material.

Platform-specific validation:

  • POSIX: Checks file mode permissions and verifies group/other bits are 0
  • Windows: Checks ACL (Access Control List) to ensure only owner has read access
Parameters
key_pathPath to the key file to validate
Returns
ASCIICHAT_OK if permissions are appropriate, error code if too permissive

Referenced by validate_key_security().

◆ platform_vasprintf()

int platform_vasprintf ( char **  strp,
const char *  format,
va_list  ap 
)

#include <string.h>

Allocate formatted string with va_list (vasprintf replacement)

Parameters
strpPointer to string pointer (output parameter for allocated string)
formatPrintf-style format string
apVariable argument list
Returns
Number of characters written (excluding null terminator), or -1 on error

Referenced by named_register_fmt().

◆ platform_vsnprintf()

int platform_vsnprintf ( char *  str,
size_t  size,
const char *  format,
va_list  ap 
)

#include <string.h>

Safe variable-argument string formatting.

Parameters
strDestination buffer
sizeSize of destination buffer
formatPrintf-style format string
apVariable argument list
Returns
Number of characters written, or negative on error

Definition at line 84 of file platform/wasm/string.c.

84 {
85 return vsnprintf(str, size, format, ap);
86}

Referenced by safe_snprintf(), and safe_vsnprintf().

◆ platform_write()

ssize_t platform_write ( int  fd,
const void *  buf,
size_t  count 
)

#include <filesystem.h>

Safe file write (write replacement)

Platform-safe write function.

Parameters
fdFile descriptor
bufBuffer to write from
countNumber of bytes to write
Returns
Number of bytes written, or -1 on error
Parameters
fdFile descriptor to write to
bufBuffer containing data to write
countNumber of bytes to write
Returns
Number of bytes written on success, -1 on error

Cross-platform write function that handles Windows-specific quirks (e.g., CRLF line endings) and provides consistent behavior across platforms.

Note
On Windows, automatically handles line ending conversion if needed.
On POSIX, equivalent to standard write().

Override platform_write to route STDOUT

Definition at line 72 of file wasm/system.c.

72 {
73 if (!buf || count == 0) {
74 // Don't warn about these - it's a valid case (empty write)
75 return 0;
76 }
77
78 // Use the default write()
79 return write(fd, buf, count);
80}

Referenced by log_json_write(), and platform_write_all().

◆ platform_write_all()

size_t platform_write_all ( int  fd,
const void *  buf,
size_t  count 
)

#include <system.h>

Write all bytes to a file descriptor, handling partial writes.

Parameters
fdFile descriptor to write to
bufBuffer containing data to write
countNumber of bytes to write
Returns
Number of bytes successfully written (may be less than count on retry limit)

Handles partial writes and EAGAIN errors, retrying up to 1000 times before giving up. This ensures that data is fully written even when dealing with non-blocking I/O or interrupted syscalls.

Note
On EAGAIN/EWOULDBLOCK, sleeps 100us before retrying instead of busy-waiting
Returns early on fatal write errors (logs warning but continues retrying)
If NULL buffer or 0 count is passed, returns 0 immediately

Handles partial writes and EAGAIN errors, retrying up to 1000 times before giving up. This ensures that data is fully written even when dealing with non-blocking I/O or interrupted syscalls.

Definition at line 224 of file system.c.

224 {
225 if (!buf || count == 0) {
226 return 0;
227 }
228
229 size_t written_total = 0;
230 int attempts = 0;
231 const int MAX_ATTEMPTS = 1000;
232
233 while (written_total < count && attempts < MAX_ATTEMPTS) {
234 ssize_t result = platform_write(fd, (const char *)buf + written_total, count - written_total);
235
236 if (result > 0) {
237 written_total += (size_t)result;
238 attempts = 0; // Reset attempt counter on successful write
239 } else if (result < 0) {
240 // Handle EAGAIN (non-blocking would-block) with sleep instead of tight loop
241 if (errno == EAGAIN || errno == EWOULDBLOCK) {
242 // Sleep 100us before retrying to avoid busy-waiting and spinning CPU
244 } else {
245 // Other write errors - log and retry
246 log_warn("platform_write_all: write() error on fd=%d (wrote %zu/%zu so far, errno=%d)", fd, written_total,
247 count, errno);
248 }
249 attempts++;
250 } else {
251 // result == 0: no bytes written, retry
252 attempts++;
253 }
254 }
255
256 if (attempts >= MAX_ATTEMPTS && written_total < count) {
257 log_warn("platform_write_all: Hit retry limit on fd=%d: wrote %zu of %zu bytes", fd, written_total, count);
258 }
259
260 return written_total;
261}
#define log_warn(...)
Log a WARN message.
Definition log/log.h:574
#define EWOULDBLOCK
ssize_t platform_write(int fd, const void *buf, size_t count)
Safe file write (write replacement)
Definition wasm/system.c:72
#define EAGAIN

References EAGAIN, errno, EWOULDBLOCK, log_warn, platform_sleep_us(), and platform_write().

Referenced by ascii_write(), config_create_default(), frame_buffer_flush(), keyboard_help_is_active_global(), log_console_impl(), log_json_async_safe(), log_search_render_input_line(), session_client_like_run(), session_display_render_fps_overlay(), session_display_write_ascii(), and session_display_write_raw().

◆ rwlock_destroy()

int rwlock_destroy ( rwlock_t *  lock)

#include <rwlock.h>

Destroy a read-write lock.

Parameters
lockPointer to read-write lock to destroy
Returns
0 on success, non-zero on error

Destroys the read-write lock and frees any associated resources. The lock must not be held by any thread when this is called.

Referenced by lifecycle_reset(), lifecycle_shutdown(), mixer_create(), mixer_destroy(), named_destroy(), platform_cleanup_binary_path_cache(), symbol_cache_destroy(), and tcp_server_destroy().

◆ rwlock_destroy_impl()

int rwlock_destroy_impl ( rwlock_t *  lock)

#include <rwlock.h>

Destroy a read-write lock (implementation function)

Parameters
lockPointer to read-write lock to destroy
Returns
0 on success, non-zero on error
Note
This is the implementation function. Use rwlock_destroy() instead.

◆ rwlock_format_state()

int rwlock_format_state ( const rwlock_t *  rwlock,
char *  buffer,
size_t  size 
)

#include <rwlock.c>

Format rwlock timing and state info into buffer.

Parameters
rwlockPointer to the rwlock
bufferOutput buffer
sizeBuffer size
Returns
Number of bytes written

Formats timing (rdlock/wrlock/unlock), held-by info, and operation counts. Called by rwlock_log_state() and –sync-state display code.

Definition at line 90 of file rwlock.c.

90 {
91 if (!rwlock || !buffer || size == 0)
92 return 0;
93
94 int offset = 0;
95 uint64_t now_ns = time_get_ns();
96
97 char rdlock_str[64] = "";
98 char wrlock_str[64] = "";
99 char unlock_str[64] = "";
100 char held_str[64] = "";
101 char count_str[256] = "";
102
103 if (rwlock->last_rdlock_time_ns > 0 && rwlock->last_rdlock_time_ns <= now_ns) {
104 char elapsed_str[64];
105 time_pretty(now_ns - rwlock->last_rdlock_time_ns, -1, elapsed_str, sizeof(elapsed_str));
106 snprintf(rdlock_str, sizeof(rdlock_str), "rdlock=%s", elapsed_str);
107 }
108
109 if (rwlock->last_wrlock_time_ns > 0 && rwlock->last_wrlock_time_ns <= now_ns) {
110 char elapsed_str[64];
111 time_pretty(now_ns - rwlock->last_wrlock_time_ns, -1, elapsed_str, sizeof(elapsed_str));
112 snprintf(wrlock_str, sizeof(wrlock_str), "wrlock=%s", elapsed_str);
113 }
114
115 if (rwlock->last_unlock_time_ns > 0 && rwlock->last_unlock_time_ns <= now_ns) {
116 char elapsed_str[64];
117 time_pretty(now_ns - rwlock->last_unlock_time_ns, -1, elapsed_str, sizeof(elapsed_str));
118 snprintf(unlock_str, sizeof(unlock_str), "unlock=%s", elapsed_str);
119 }
120
122 if (rwlock->write_held_by_key != 0) {
123 snprintf(held_str, sizeof(held_str), "[WRITE_LOCKED_BY=thread.%lu]", (unsigned long)rwlock->write_held_by_key);
124 } else if (read_count > 0) {
125 snprintf(held_str, sizeof(held_str), "[READ_LOCKED=%llu]", (unsigned long long)read_count);
126 }
127
128 if (rwlock->rdlock_count > 0 || rwlock->wrlock_count > 0 || rwlock->unlock_count > 0) {
129 snprintf(count_str, sizeof(count_str), "[ops: rdlock=%llu wrlock=%llu unlock=%llu]",
130 (unsigned long long)rwlock->rdlock_count, (unsigned long long)rwlock->wrlock_count,
131 (unsigned long long)rwlock->unlock_count);
132 }
133
134 offset += snprintf(buffer + offset, size - offset, "%s %s %s %s %s", rdlock_str, wrlock_str, unlock_str, held_str,
135 count_str);
136 return offset;
137}
uint64_t wrlock_count
Total write lock acquisitions.
Definition rwlock.h:71
uintptr_t write_held_by_key
Registry key of thread holding write lock (0 if not held)
Definition rwlock.h:68
uint64_t last_unlock_time_ns
Timestamp of last unlock (nanoseconds)
Definition rwlock.h:67
uint64_t unlock_count
Total unlocks.
Definition rwlock.h:72
uint64_t last_wrlock_time_ns
Timestamp of last write lock acquisition (nanoseconds)
Definition rwlock.h:66
uint64_t last_rdlock_time_ns
Timestamp of last read lock acquisition (nanoseconds)
Definition rwlock.h:65
uint64_t rdlock_count
Total read lock acquisitions.
Definition rwlock.h:70
atomic_t read_lock_count
Number of threads holding read locks (thread-safe atomic)
Definition rwlock.h:69
rwlock_t rwlock
Read-write lock for thread-safe access (uthash requires external locking)
Definition util/time.c:34

References atomic_load_u64(), rwlock_t::last_rdlock_time_ns, rwlock_t::last_unlock_time_ns, rwlock_t::last_wrlock_time_ns, rwlock_t::rdlock_count, rwlock_t::read_lock_count, rwlock, time_get_ns(), time_pretty(), rwlock_t::unlock_count, rwlock_t::write_held_by_key, and rwlock_t::wrlock_count.

Referenced by rwlock_log_state().

◆ rwlock_init()

int rwlock_init ( rwlock_t *  lock,
const char *  name 
)

#include <rwlock.h>

Initialize a read-write lock with a name.

Parameters
lockPointer to read-write lock to initialize
nameHuman-readable name for debugging (e.g., "client_list_lock")
Returns
0 on success, non-zero on error

Initializes the read-write lock for use. Must be called before any other lock operations. The name is stored for debugging and automatically suffixed with a unique counter.

Definition at line 65 of file threading.c.

65 {
66 (void)name;
67 return pthread_rwlock_init((pthread_rwlock_t *)rwlock, NULL);
68}

References rwlock.

Referenced by lifecycle_init(), mixer_create(), named_init(), symbol_cache_init(), and tcp_server_init().

◆ rwlock_init_impl()

int rwlock_init_impl ( rwlock_t *  lock)

#include <rwlock.h>

Initialize a read-write lock (implementation function)

Parameters
lockPointer to read-write lock to initialize
Returns
0 on success, non-zero on error
Note
This is the implementation function. Use rwlock_init() instead.

◆ rwlock_log_state()

void rwlock_log_state ( const rwlock_t *  rwlock,
const char *  file,
int  line,
const char *  func 
)

#include <rwlock.c>

Log the state of a read-write lock.

Format and log the current state of a read-write lock.

Parameters
rwlockPointer to the rwlock
fileSource file of the caller
lineSource line of the caller
funcSource function of the caller

Formats and logs detailed state information about the rwlock.

Parameters
rwlockPointer to the read-write lock
fileSource file of the caller
lineSource line of the caller
funcSource function of the caller

Logs detailed state information about the rwlock (timing, counts, held-by info) at the specified location. Debug builds only; no-op in release builds.

Definition at line 150 of file rwlock.c.

150 {
151 if (!rwlock)
152 return;
153 char buf[512];
154 rwlock_format_state(rwlock, buf, sizeof(buf));
155 log_msg(LOG_DEBUG, file, line, func, "rwlock/state %p: %s", (const void *)rwlock, buf);
156}
int rwlock_format_state(const rwlock_t *rwlock, char *buffer, size_t size)
Format rwlock timing and state info into buffer.
Definition rwlock.c:90

References LOG_DEBUG, log_msg(), rwlock, and rwlock_format_state().

◆ rwlock_on_rdlock()

void rwlock_on_rdlock ( rwlock_t *  rwlock)

#include <rwlock.c>

Hook called when a read lock is successfully acquired.

Parameters
rwlockPointer to the rwlock that was read-locked

Called by platform-specific rwlock_rdlock_impl() after the read lock is acquired. Records timing, increments current reader count and total rdlock count.

Parameters
rwlockPointer to the rwlock that was read-locked

Called by platform-specific implementations after read lock acquisition. Records timing and other diagnostic data (debug builds only).

Definition at line 29 of file rwlock.c.

References atomic_fetch_add_u64(), rwlock_t::last_rdlock_time_ns, rwlock_t::rdlock_count, rwlock_t::read_lock_count, rwlock, and time_get_ns().

◆ rwlock_on_unlock()

void rwlock_on_unlock ( rwlock_t *  rwlock)

#include <rwlock.c>

Hook called when an rwlock is unlocked (read or write)

Parameters
rwlockPointer to the rwlock that was unlocked

Called by platform-specific rwlock_rdunlock_impl() or rwlock_wrunlock_impl() before releasing the lock. Decrements appropriate reader/writer count and increments total unlock count.

Parameters
rwlockPointer to the rwlock that was unlocked

Called by platform-specific implementations before lock release. Records timing and other diagnostic data (debug builds only).

Definition at line 64 of file rwlock.c.

64 {
65 if (!rwlock)
66 return;
70 uintptr_t current_key = asciichat_thread_to_key(current_thread);
71 if (rwlock->write_held_by_key == current_key) {
73 } else if (atomic_load_u64(&rwlock->read_lock_count) > 0) {
75 }
76}

References asciichat_thread_current_id(), asciichat_thread_to_key(), atomic_fetch_sub_u64(), atomic_load_u64(), rwlock_t::last_unlock_time_ns, rwlock_t::read_lock_count, rwlock, time_get_ns(), rwlock_t::unlock_count, and rwlock_t::write_held_by_key.

◆ rwlock_on_wrlock()

void rwlock_on_wrlock ( rwlock_t *  rwlock)

#include <rwlock.c>

Hook called when a write lock is successfully acquired.

Parameters
rwlockPointer to the rwlock that was write-locked

Called by platform-specific rwlock_wrlock_impl() after the write lock is acquired. Records timing, held-by thread, and increments total wrlock count.

Parameters
rwlockPointer to the rwlock that was write-locked

Called by platform-specific implementations after write lock acquisition. Records timing and other diagnostic data (debug builds only).

Definition at line 46 of file rwlock.c.

References asciichat_thread_current_id(), asciichat_thread_to_key(), rwlock_t::last_wrlock_time_ns, rwlock, time_get_ns(), rwlock_t::write_held_by_key, and rwlock_t::wrlock_count.

◆ rwlock_rdlock_impl()

int rwlock_rdlock_impl ( rwlock_t *  lock)

#include <rwlock.h>

Acquire a read lock (implementation function)

Parameters
lockPointer to read-write lock
Returns
0 on success, non-zero on error

Acquires a shared read lock. Multiple threads can hold read locks simultaneously. Blocks if a write lock is held.

Note
This is the implementation function. Use rwlock_rdlock() macro instead, which includes debug tracking in debug builds.

Definition at line 70 of file threading.c.

70 {
71 return pthread_rwlock_rdlock((pthread_rwlock_t *)rwlock);
72}

References rwlock.

Referenced by debug_sync_rwlock_rdlock().

◆ rwlock_rdunlock_impl()

int rwlock_rdunlock_impl ( rwlock_t *  lock)

#include <rwlock.h>

Release a read lock (implementation function)

Parameters
lockPointer to read-write lock
Returns
0 on success, non-zero on error

Releases a shared read lock held by the calling thread.

Note
This is the implementation function. Use rwlock_rdunlock() macro instead, which includes debug tracking in debug builds.

Definition at line 78 of file threading.c.

78 {
79 return pthread_rwlock_unlock((pthread_rwlock_t *)rwlock);
80}

References rwlock.

Referenced by debug_sync_rwlock_rdunlock().

◆ rwlock_wrlock_impl()

int rwlock_wrlock_impl ( rwlock_t *  lock)

#include <rwlock.h>

Acquire a write lock (implementation function)

Parameters
lockPointer to read-write lock
Returns
0 on success, non-zero on error

Acquires an exclusive write lock. Only one thread can hold a write lock, and it excludes all read locks. Blocks if any locks are held.

Note
This is the implementation function. Use rwlock_wrlock() macro instead, which includes debug tracking in debug builds.

Definition at line 74 of file threading.c.

74 {
75 return pthread_rwlock_wrlock((pthread_rwlock_t *)rwlock);
76}

References rwlock.

Referenced by debug_sync_rwlock_wrlock().

◆ rwlock_wrunlock_impl()

int rwlock_wrunlock_impl ( rwlock_t *  lock)

#include <rwlock.h>

Release a write lock (implementation function)

Parameters
lockPointer to read-write lock
Returns
0 on success, non-zero on error

Releases an exclusive write lock held by the calling thread.

Note
This is the implementation function. Use rwlock_wrunlock() macro instead, which includes debug tracking in debug builds.

Definition at line 82 of file threading.c.

82 {
83 return pthread_rwlock_unlock((pthread_rwlock_t *)rwlock);
84}

References rwlock.

Referenced by debug_sync_rwlock_wrunlock().

◆ safe_fprintf()

int safe_fprintf ( FILE *  stream,
const char *  format,
  ... 
)

#include <system.h>

Platform-safe fprintf wrapper.

Uses fprintf_s on Windows and fprintf on POSIX.

Parameters
streamFile stream
formatFormat string
...Variable arguments
Returns
Number of characters written or negative on error

Platform-safe fprintf wrapper.

Parameters
streamOutput file stream
formatPrintf-style format string
Returns
Number of characters written, or -1 on error

Safely formats and prints output to a file stream. Returns the number of characters written, or -1 on error.

Definition at line 172 of file system.c.

172 {
173 if (!stream || !format) {
174 return -1;
175 }
176
177 va_list args;
178 va_start(args, format);
179 int ret = vfprintf(stream, format, args);
180 va_end(args);
181
182 return ret;
183}

References args.

Referenced by add_known_host(), asciichat_fatal_with_context(), asciichat_print_error_context(), log_init(), log_labeled(), log_msg(), log_plain_msg(), and webcam_print_init_error_help().

◆ safe_snprintf()

int safe_snprintf ( char *  buffer,
size_t  buffer_size,
const char *  format,
  ... 
)

#include <system.h>

Platform-safe snprintf wrapper.

Uses snprintf_s on Windows and snprintf with additional safety on POSIX. Always null-terminates the output buffer.

Parameters
bufferOutput buffer
buffer_sizeSize of output buffer
formatFormat string
...Variable arguments
Returns
Number of characters written (excluding null terminator) or negative on error

Platform-safe snprintf wrapper.

Parameters
bufferOutput buffer
buffer_sizeSize of output buffer
formatPrintf-style format string
Returns
Number of characters written, or -1 on error

Safely formats a string into a buffer with bounds checking. Uses platform_snprintf for cross-platform implementation. Returns the number of characters written (not including null terminator). Returns -1 if buffer is too small.

Definition at line 148 of file system.c.

148 {
149 if (!buffer || !format || buffer_size == 0) {
150 return -1;
151 }
152
153 va_list args;
154 va_start(args, format);
155
156 /* Delegate to platform_snprintf for actual formatting */
157 int ret = platform_vsnprintf(buffer, buffer_size, format, args);
158
159 va_end(args);
160 return ret;
161}
int buffer_size
Size of circular buffer.
Definition grep.c:90
int platform_vsnprintf(char *str, size_t size, const char *format, va_list ap)
Safe variable-argument string formatting.

References args, buffer_size, and platform_vsnprintf().

Referenced by acds_client_connect(), acds_identity_default_path(), acds_identity_fingerprint(), acds_string_generate(), add_client(), asciichat_instr_log_line(), asciichat_instr_log_pc(), audio_analysis_init(), audio_client_init(), backtrace_format(), backtrace_print(), build_github_gpg_url(), build_github_ssh_url(), build_gitlab_gpg_url(), build_gitlab_ssh_url(), capture_init(), check_gpg_key_expiry(), check_known_host(), check_known_host_no_identity(), client_audio_pipeline_create(), colored_string(), colorize_log_message(), colorize_named_string(), colorscheme_compile_scheme(), colorscheme_export_scheme(), config_load_and_apply(), create_client_render_threads(), crypto_get_rekey_status(), crypto_handshake_client_key_exchange(), crypto_handshake_server_auth_challenge(), crypto_handshake_server_start(), discovery_keys_get_cache_path(), discovery_keys_save_cached(), display_mitm_warning(), expand_path(), find_similar_option_with_mode(), format_available_modes(), format_bytes_pretty(), format_gpg_key_display(), format_mode_names(), format_option_default_value_str(), format_public_key(), format_uptime_hms(), get_current_time_formatted(), get_discovery_database_dir(), get_known_hosts_path(), get_log_dir(), get_manpage_template(), gpg_agent_sign(), gpg_get_public_key(), gpg_sign_detached_ed25519(), gpg_verify_detached_ed25519(), gpg_verify_signature(), gpg_verify_signature_with_binary(), https_get(), ice_format_candidate(), log_mmap_write(), log_recolor_plain_entry(), log_template_apply(), manpage_content_generate_environment(), manpage_content_generate_environment_with_manual(), manpage_content_generate_examples(), manpage_content_generate_options(), manpage_content_generate_positional(), manpage_content_generate_usage(), manpage_fmt_write_title(), manpage_merger_generate_synopsis(), manpage_merger_generate_usage(), nat_upnp_get_address(), options_config_calculate_max_col_width(), options_config_print_options_sections_with_width(), options_config_print_usage(), options_format_default_value(), options_init(), options_preset_unified(), options_print_help_for_mode(), parallel_connect(), parse_client_address(), parse_color_filter(), parse_color_mode(), parse_gpg_key(), parse_gpg_key_binary(), parse_gpg_keys_from_response(), parse_log_level(), parse_palette_chars(), parse_palette_type(), parse_port_option(), parse_private_key(), parse_render_mode(), parse_render_theme(), parse_server_bind_address(), parse_ssh_private_key(), path_validate_user_path(), prompt_unknown_host(), prompt_unknown_host_no_identity(), pubkey_to_hex(), query_init(), remove_known_host(), rgb_to_truecolor_ansi(), sdp_generate_answer(), sdp_generate_offer(), session_capture_create(), session_client_like_run(), session_network_capture_create(), session_participant_start_video_capture(), session_server_like_run(), stats_logger_thread(), test_get_binary_path(), time_pretty(), turn_generate_credentials(), and yt_dlp_extract_stream_url().

◆ safe_vsnprintf()

int safe_vsnprintf ( char *  buffer,
size_t  buffer_size,
const char *  format,
va_list  ap 
)

#include <system.h>

Platform-safe vsnprintf wrapper.

Uses the appropriate vsnprintf implementation for the platform. Safely formats a string with va_list argument.

Parameters
bufferOutput buffer
buffer_sizeSize of output buffer
formatFormat string
apVariable argument list
Returns
Number of characters written (excluding null terminator) or negative on error

Platform-safe vsnprintf wrapper.

Parameters
bufferOutput buffer (can be NULL if buffer_size is 0 for size calculation)
buffer_sizeSize of output buffer
formatPrintf-style format string
apVariable argument list
Returns
Number of characters written, or -1 on error

Safely formats a string into a buffer with bounds checking using va_list. Uses platform_vsnprintf for cross-platform implementation. Returns the number of characters written (not including null terminator). Special case: NULL buffer with size 0 returns required buffer size for size calculation. Returns -1 if buffer is too small, format is invalid, or buffer is NULL with size > 0.

Definition at line 199 of file system.c.

199 {
200 if (!format) {
201 return -1;
202 }
203
204 // Allow NULL buffer with size 0 for size calculation (standard vsnprintf behavior)
205 if (!buffer && buffer_size > 0) {
206 return -1; // Non-NULL buffer required when buffer_size > 0
207 }
208
209 /* Delegate to platform_vsnprintf for actual formatting */
210 return platform_vsnprintf(buffer, buffer_size, format, ap);
211}

References buffer_size, and platform_vsnprintf().

Referenced by disconnect_client_for_bad_data(), format_message(), log_file_msg(), log_mmap_write(), log_msg(), and log_plain_msg().

◆ socket_accept()

socket_t socket_accept ( socket_t  sock,
struct sockaddr *  addr,
socklen_t *  addrlen,
const char *  name 
)

#include <socket.h>

Accept an incoming connection.

Parameters
sockListening socket
addrPointer to store peer address (or NULL)
addrlenPointer to address length (input/output)
nameHuman-readable name for the accepted socket (required, e.g., "client_connection:1234")
Returns
New socket handle on success, INVALID_SOCKET_VALUE on error

Accepts an incoming connection and registers it with the named registry for debugging. The name parameter is required and must describe the connection purpose.

Referenced by accept_with_timeout().

◆ socket_bind()

int socket_bind ( socket_t  sock,
const struct sockaddr *  addr,
socklen_t  addrlen 
)

#include <socket.h>

Bind a socket to an address.

Parameters
sockSocket to bind
addrAddress to bind to
addrlenLength of address structure
Returns
0 on success, non-zero on error

◆ socket_cleanup()

void socket_cleanup ( void  )

#include <socket.h>

Cleanup socket subsystem.

Cleans up the socket subsystem. On Windows, this cleans up Winsock. Should be called during program shutdown.

◆ socket_close()

◆ socket_connect()

int socket_connect ( socket_t  sock,
const struct sockaddr *  addr,
socklen_t  addrlen 
)

#include <socket.h>

Connect to a remote address.

Parameters
sockSocket to connect (must be created but not yet connected)
addrRemote address to connect to (struct sockaddr_in for IPv4, struct sockaddr_in6 for IPv6)
addrlenLength of address structure
Returns
0 on success, non-zero on error

Initiates a connection to a remote address. For TCP sockets (SOCK_STREAM), this blocks until the connection succeeds or fails (unless socket is non-blocking).

Typical usage (IPv4):

struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(27224);
inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr);
socket_t sock = socket_create(AF_INET, SOCK_STREAM, 0);
if (socket_connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
log_error("Connection failed: %s", socket_get_error_string());
socket_close(sock);
}

Non-blocking connect: For non-blocking sockets, connect() returns immediately with EINPROGRESS/WSAEINPROGRESS. Use socket_poll() or socket_select() to wait for connection completion:

if (socket_connect(sock, &addr, sizeof(addr)) < 0) {
// Connection in progress, poll for completion
}
}
bool socket_is_in_progress_error(int error_code)
Check if error indicates operation in progress (non-blocking connect)

◆ socket_create()

socket_t socket_create ( const char *  name,
int  domain,
int  type,
int  protocol 
)

#include <socket.h>

Create a new named socket.

Parameters
nameDebug name for the socket
domainSocket domain (AF_INET for IPv4, AF_INET6 for IPv6, AF_UNIX for local)
typeSocket type (SOCK_STREAM for TCP, SOCK_DGRAM for UDP)
protocolProtocol (typically 0 for automatic selection based on domain/type)
Returns
Socket handle on success, INVALID_SOCKET_VALUE on error

Creates a new named socket but does not connect it. Use with socket_bind() and socket_listen() for servers, or socket_connect() for clients. The socket is automatically registered with the debug naming system.

Common domain/type combinations:

  • AF_INET + SOCK_STREAM = TCP over IPv4
  • AF_INET6 + SOCK_STREAM = TCP over IPv6
  • AF_INET + SOCK_DGRAM = UDP over IPv4
  • AF_INET6 + SOCK_DGRAM = UDP over IPv6

Platform-specific notes:

  • Windows: Requires socket_init() to be called first
  • POSIX: No initialization required

Error handling:

socket_t sock = socket_create("my_socket", AF_INET, SOCK_STREAM, 0);
if (!socket_is_valid(sock)) {
log_error("Socket creation failed: %s", socket_get_error_string());
return false;
}

Referenced by tcp_client_connect().

◆ socket_fd_isset()

int socket_fd_isset ( socket_t  sock,
fd_set *  set 
)

#include <socket.h>

Check if a socket is in an fd_set.

Parameters
sockSocket to check
setfd_set to check in
Returns
Non-zero if socket is in set, 0 otherwise

Referenced by connect_with_timeout(), and tcp_server_run().

◆ socket_fd_set()

void socket_fd_set ( socket_t  sock,
fd_set *  set 
)

#include <socket.h>

Add a socket to an fd_set.

Parameters
sockSocket to add
setfd_set to add to

Referenced by connect_with_timeout(), and tcp_server_run().

◆ socket_fd_zero()

void socket_fd_zero ( fd_set *  set)

#include <socket.h>

Clear an fd_set.

Parameters
setfd_set to clear

Referenced by connect_with_timeout(), and tcp_server_run().

◆ socket_get_error()

int socket_get_error ( socket_t  sock)

#include <socket.h>

Get socket-specific error code.

Parameters
sockSocket to query
Returns
Error code (platform-specific)

◆ socket_get_error_string()

const char * socket_get_error_string ( void  )

#include <socket.h>

Get last socket error as string.

Returns
Pointer to error string (may be static, do not free)
Note
The returned string may be a static buffer. Do not modify or free it.

Definition at line 81 of file comprehensive.c.

81 {
82 return "Socket error";
83}

Referenced by accept_with_timeout(), network_error_string(), and tcp_server_run().

◆ socket_get_fd()

int socket_get_fd ( socket_t  sock)

#include <socket.h>

Get the underlying file descriptor (POSIX compatibility)

Parameters
sockSocket handle
Returns
File descriptor value (on POSIX, same as socket handle)

◆ socket_get_last_error()

int socket_get_last_error ( void  )

#include <socket.h>

Get last socket error code.

Returns
Error code (platform-specific)

Definition at line 85 of file comprehensive.c.

85 {
86 return 0;
87}

Referenced by accept_with_timeout(), connect_with_timeout(), and tcp_server_run().

◆ socket_get_peer_address()

int socket_get_peer_address ( socket_t  sock,
struct sockaddr *  addr,
socklen_t *  addrlen 
)

#include <socket.h>

Get peer address (convenience function)

Parameters
sockConnected socket
addrPointer to store peer address
addrlenPointer to address length (input/output)
Returns
0 on success, non-zero on error
Note
This is a convenience wrapper around socket_getpeername().

◆ socket_getpeername()

int socket_getpeername ( socket_t  sock,
struct sockaddr *  addr,
socklen_t *  addrlen 
)

#include <socket.h>

Get peer address.

Parameters
sockConnected socket
addrPointer to store peer address
addrlenPointer to address length (input/output)
Returns
0 on success, non-zero on error

◆ socket_getsockname()

int socket_getsockname ( socket_t  sock,
struct sockaddr *  addr,
socklen_t *  addrlen 
)

#include <socket.h>

Get socket local address.

Parameters
sockSocket to query
addrPointer to store local address
addrlenPointer to address length (input/output)
Returns
0 on success, non-zero on error

◆ socket_getsockopt()

int socket_getsockopt ( socket_t  sock,
int  level,
int  optname,
void *  optval,
socklen_t *  optlen 
)

#include <socket.h>

Get socket option.

Parameters
sockSocket to query
levelOption level (SOL_SOCKET, IPPROTO_TCP, IPPROTO_IPV6, etc.)
optnameOption name (SO_REUSEADDR, TCP_NODELAY, SO_RCVBUF, etc.)
optvalPointer to store option value (type depends on optname)
optlenPointer to option length (on input: buffer size, on output: actual length)
Returns
0 on success, non-zero on error

Retrieves the current value of a socket option. The caller must provide a buffer (optval) and specify its size (optlen). On return, optlen contains the actual size of the option value.

Important implementation detail: optlen is both an input and output parameter:

  • On input: Maximum size of the buffer pointed to by optval
  • On output: Actual size of the option value

Example: Get receive buffer size:

int recv_buf_size;
socklen_t optlen = sizeof(recv_buf_size);
if (socket_getsockopt(sock, SOL_SOCKET, SO_RCVBUF, &recv_buf_size, &optlen) == 0) {
log_info("Receive buffer size: %d bytes", recv_buf_size);
}
int socket_getsockopt(socket_t sock, int level, int optname, void *optval, socklen_t *optlen)
Get socket option.

Platform-specific variations:

  • Windows: Some timeout options use DWORD (milliseconds)
  • POSIX: Some timeout options use struct timeval (seconds + microseconds)
  • Option availability varies by OS (e.g., TCP_KEEPIDLE is Linux-specific)

Referenced by connect_with_timeout().

◆ socket_init()

asciichat_error_t socket_init ( void  )

#include <socket.h>

Initialize socket subsystem (required on Windows)

Returns
ASCIICHAT_OK on success, error code on failure

Initializes the socket subsystem. On Windows, this initializes Winsock. Must be called before any socket operations.

◆ socket_is_connection_reset_error()

bool socket_is_connection_reset_error ( int  error_code)

#include <socket.h>

Check if error code indicates connection reset.

Parameters
error_codePlatform-specific error code (from socket_get_last_error())
Returns
true if error is ECONNRESET (POSIX) or WSAECONNRESET (Windows)

Used to detect when the remote peer forcibly closed the connection. Abstracts platform differences between POSIX and Windows.

◆ socket_is_in_progress_error()

bool socket_is_in_progress_error ( int  error_code)

#include <socket.h>

Check if error indicates operation in progress (non-blocking connect)

Parameters
error_codePlatform-specific error code (from socket_get_last_error())
Returns
true if error is EINPROGRESS (POSIX) or WSAEINPROGRESS (Windows)

Used for non-blocking connect() operations. When connect() is called on a non-blocking socket, it returns immediately with EINPROGRESS/WSAEINPROGRESS if the connection is still being established.

Example:
int result = connect(sock, addr, addrlen);
// Connection in progress - use select/poll to wait
}

Referenced by connect_with_timeout().

◆ socket_is_invalid_socket_error()

bool socket_is_invalid_socket_error ( int  error_code)

#include <socket.h>

Check if error code indicates a closed/invalid socket.

Parameters
error_codePlatform-specific error code (from socket_get_last_error())
Returns
true if error indicates socket is closed or invalid

Detects errors like EBADF (bad file descriptor) on POSIX or WSAENOTSOCK (socket operation on non-socket) on Windows.

Definition at line 76 of file comprehensive.c.

76 {
77 (void)error_code;
78 return false;
79}
asciichat_error_t error_code

References error_code.

Referenced by accept_with_timeout().

◆ socket_is_valid()

bool socket_is_valid ( socket_t  sock)

#include <socket.h>

Check if a socket handle is valid.

Parameters
sockSocket handle to check
Returns
true if socket is valid, false otherwise

Definition at line 33 of file comprehensive.c.

33 {
34 return sock >= 0;
35}

Referenced by packet_send(), tcp_client_close(), tcp_client_connect(), tcp_client_destroy(), tcp_client_shutdown(), and tcp_server_run().

◆ socket_is_would_block_error()

bool socket_is_would_block_error ( int  error_code)

#include <socket.h>

Check if error code indicates "would block" (non-blocking socket would wait)

Parameters
error_codePlatform-specific error code (from socket_get_last_error())
Returns
true if error is EAGAIN/EWOULDBLOCK (POSIX) or WSAEWOULDBLOCK (Windows)

Detects when a non-blocking socket operation needs to be retried later because the operation would have blocked. This is the standard way to implement non-blocking I/O patterns.

Platform abstraction:

  • POSIX: Detects both EAGAIN and EWOULDBLOCK (they're often the same)
  • Windows: Detects WSAEWOULDBLOCK (WSA-prefixed Winsock error)

Typical non-blocking receive pattern:

while (more_data_expected) {
ssize_t result = socket_recv(sock, buf, len, 0);
if (result > 0) {
// Process received data
process_data(buf, (size_t)result);
} else if (result < 0) {
// No data available, check other sockets or wait
break;
// Connection closed abruptly
handle_error();
} else {
// Other error
handle_error();
}
} else {
// result == 0: Connection closed gracefully
break;
}
}

Typical non-blocking send pattern:

size_t total_sent = 0;
while (total_sent < len) {
ssize_t sent = socket_send(sock, (char *)buf + total_sent, len - total_sent, 0);
if (sent > 0) {
total_sent += sent;
} else if (sent < 0) {
// Send buffer full, retry later with poll/select
break;
} else {
// Fatal error
return false;
}
} else {
// sent == 0 shouldn't happen on send, but handle it
break;
}
}

Definition at line 71 of file comprehensive.c.

71 {
72 (void)error_code;
73 return false;
74}

References error_code.

Referenced by connect_with_timeout().

◆ socket_listen()

int socket_listen ( socket_t  sock,
int  backlog 
)

#include <socket.h>

Listen for incoming connections.

Parameters
sockSocket to listen on (must be bound)
backlogMaximum length of the queue of pending connections
Returns
0 on success, non-zero on error

◆ socket_optimize_for_streaming()

void socket_optimize_for_streaming ( socket_t  sock)

#include <socket.c>

Optimize socket for high-throughput video streaming.

Consolidates socket configuration for real-time video streaming:

  • Disables Nagle's algorithm (TCP_NODELAY)
  • Sets large send/receive buffers with automatic fallbacks
  • Enables TCP keepalive
  • Sets send/receive timeouts

This common implementation applies to both POSIX and Windows platforms.

Parameters
sockSocket to configure
sockSocket to optimize

Applies multiple socket optimizations for video streaming:

  • Disables Nagle's algorithm (TCP_NODELAY)
  • Sets large send/receive buffers (2MB with fallbacks to 512KB and 128KB)
  • Enables keepalive
  • Sets timeouts to prevent blocking indefinitely

This function consolidates socket configuration that is needed for real-time video streaming. It gracefully handles buffer size negotiation by falling back to smaller sizes if the OS doesn't support large buffers.

Note
Warnings are logged if individual options fail, but the function continues to apply the remaining options.

Definition at line 37 of file socket.c.

37 {
38 // 1. Disable Nagle's algorithm - CRITICAL for real-time video
39 // TCP_NODELAY ensures data is sent immediately without waiting for ACKs
40 int nodelay = 1;
41 if (socket_setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &nodelay, sizeof(nodelay)) != 0) {
42 log_warn("Failed to disable Nagle's algorithm (TCP_NODELAY) on socket");
43 }
44
45 // 2. Increase send buffer for video streaming (2MB with fallbacks)
46 // Large buffers reduce packet drops during bursty frame transmission
47 int send_buffer = MAX_FRAME_BUFFER_SIZE; // 2MB
48 if (socket_setsockopt(sock, SOL_SOCKET, SO_SNDBUF, &send_buffer, sizeof(send_buffer)) != 0) {
49 send_buffer = 512 * 1024; // 512KB fallback
50 if (socket_setsockopt(sock, SOL_SOCKET, SO_SNDBUF, &send_buffer, sizeof(send_buffer)) != 0) {
51 send_buffer = 128 * 1024; // 128KB fallback
52 socket_setsockopt(sock, SOL_SOCKET, SO_SNDBUF, &send_buffer, sizeof(send_buffer));
53 }
54 }
55
56 // 3. Increase receive buffer (2MB with fallbacks)
57 // Allows buffering of multiple incoming frames before processing
58 int recv_buffer = MAX_FRAME_BUFFER_SIZE; // 2MB
59 if (socket_setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &recv_buffer, sizeof(recv_buffer)) != 0) {
60 recv_buffer = 512 * 1024; // 512KB fallback
61 if (socket_setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &recv_buffer, sizeof(recv_buffer)) != 0) {
62 recv_buffer = 128 * 1024; // 128KB fallback
63 socket_setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &recv_buffer, sizeof(recv_buffer));
64 }
65 }
66
67 // 4. Enable keepalive to detect dead connections
68 int keepalive = 1;
69 socket_setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &keepalive, sizeof(keepalive));
70}
#define MAX_FRAME_BUFFER_SIZE
Maximum frame buffer size including headers and compression.

References log_warn, MAX_FRAME_BUFFER_SIZE, and socket_setsockopt().

◆ socket_poll()

int socket_poll ( struct pollfd *  fds,
nfds_t  nfds,
int64_t  timeout_ns 
)

#include <socket.h>

Poll sockets for events (multiplexed I/O)

Parameters
fdsArray of pollfd structures (contains fd, events, and output revents)
nfdsNumber of file descriptors to poll
timeout_nsTimeout in nanoseconds (0 for immediate return, -1 for infinite, >0 for specific timeout)
Returns
Number of sockets with ready events, 0 on timeout, -1 on error

Monitors multiple sockets for readiness events. This is the recommended way to wait on multiple sockets efficiently, especially in high-performance scenarios.

pollfd structure:

struct pollfd {
socket_t fd; // Socket to monitor
short events; // Requested events (POLLIN, POLLOUT, POLLERR, etc.)
short revents; // Returned events (output, set by socket_poll)
};

Event flags (events/revents):

  • POLLIN: Data available for reading (or new connection on listening socket)
  • POLLOUT: Socket is writable (buffer has space)
  • POLLERR: Error condition
  • POLLHUP: Connection closed by peer
  • POLLNVAL: Invalid socket

Example: Monitor server and client sockets:

struct pollfd fds[2];
fds[0].fd = socket_get_fd(server_sock);
fds[0].events = POLLIN; // Wait for incoming connections
fds[1].fd = socket_get_fd(client_sock);
fds[1].events = POLLIN | POLLOUT; // Wait for read or write readiness
int ready = socket_poll(fds, 2, 5000000000LL); // 5 second timeout
if (ready < 0) {
// Error
} else if (ready == 0) {
// Timeout, no sockets ready
} else {
// Check which sockets are ready
if (fds[0].revents & POLLIN) {
// Server ready to accept
socket_t client = socket_accept(server_sock, NULL, NULL);
}
if (fds[1].revents & POLLIN) {
// Client has data to read
}
if (fds[1].revents & POLLERR) {
// Client socket error
}
}

Platform-specific implementation:

  • POSIX: Uses poll() system call efficiently (O(n) complexity)
  • Windows: Uses WSAPoll() if available (Windows Vista+), falls back to select()

Works with both IPv4 and IPv6: socket_poll() works transparently with both IPv4 and IPv6 sockets created with AF_INET and AF_INET6 respectively.

Definition at line 64 of file comprehensive.c.

64 {
65 (void)fds;
66 (void)nfds;
67 (void)timeout_ns;
68 return 0;
69}

Referenced by accept_with_timeout(), recv_with_timeout(), and send_with_timeout().

◆ socket_recv()

ssize_t socket_recv ( socket_t  sock,
void *  buf,
size_t  len,
int  flags 
)

#include <socket.h>

Receive data from a socket.

Parameters
sockSocket to receive from (must be connected)
bufBuffer to store received data
lenMaximum number of bytes to receive (buffer must be at least len bytes)
flagsSocket flags (typically 0; MSG_DONTWAIT for non-blocking on POSIX)
Returns
Number of bytes received (1 to len) on success, 0 on connection closed, -1 on error

Receives data from a connected socket. This is the primary function for reading data from established connections (TCP streams, connected UDP sockets).

Return value semantics:

  • Returns 0: Connection closed by peer (graceful shutdown)
  • Returns > 0: Data received (1 to len bytes)
  • Returns -1: Error (check socket_get_last_error())

Common error cases:

  • EAGAIN/EWOULDBLOCK on non-blocking socket with no data available
  • ECONNRESET on forcible connection closure
  • EBADF on invalid socket or closed socket

Typical receive loop:

char buffer[4096];
ssize_t received = socket_recv(sock, buffer, sizeof(buffer) - 1, 0);
if (received == 0) {
// Connection closed gracefully
return;
} else if (received < 0) {
// Non-blocking socket, no data available
// Connection reset by peer
} else {
// Other error
}
} else {
// Process received bytes
process_data(buffer, (size_t)received);
}

For UDP (datagram) sockets: Use socket_recvfrom() instead to receive data with source address information.

Definition at line 56 of file comprehensive.c.

56 {
57 (void)sock;
58 (void)buf;
59 (void)len;
60 (void)flags;
61 return -1;
62}

Referenced by recv_with_timeout().

◆ socket_recvfrom()

ssize_t socket_recvfrom ( socket_t  sock,
void *  buf,
size_t  len,
int  flags,
struct sockaddr *  src_addr,
socklen_t *  addrlen 
)

#include <socket.h>

Receive data from a specific address (UDP)

Parameters
sockSocket to receive from
bufBuffer to store received data
lenMaximum number of bytes to receive
flagsSocket flags (typically 0)
src_addrPointer to store source address (or NULL)
addrlenPointer to address length (input/output)
Returns
Number of bytes received on success, -1 on error

◆ socket_select()

int socket_select ( socket_t  max_fd,
fd_set *  readfds,
fd_set *  writefds,
fd_set *  exceptfds,
struct timeval *  timeout 
)

#include <socket.h>

Select sockets for I/O readiness.

Parameters
max_fdHighest file descriptor number (plus 1)
readfdsSet of sockets to check for read readiness (or NULL)
writefdsSet of sockets to check for write readiness (or NULL)
exceptfdsSet of sockets to check for exceptions (or NULL)
timeoutTimeout value (or NULL for infinite)
Returns
Number of ready sockets, -1 on error

Referenced by connect_with_timeout(), and tcp_server_run().

◆ socket_send()

ssize_t socket_send ( socket_t  sock,
const void *  buf,
size_t  len,
int  flags 
)

#include <socket.h>

Send data on a socket.

Parameters
sockSocket to send on (must be connected)
bufData buffer to send
lenNumber of bytes to send
flagsSocket flags (typically 0; MSG_DONTWAIT for non-blocking on POSIX)
Returns
Number of bytes sent (0 to len) on success, -1 on error

Sends data on a connected socket. The return value indicates how many bytes were actually sent, which may be less than requested for non-blocking sockets or when the send buffer is full.

Important semantics:

  • Returns 0 when socket is non-blocking and send buffer is full
  • Returns -1 on error (check socket_get_last_error())
  • Returns 1 to len on success
  • For reliable delivery, loop until all bytes are sent

Error handling:

size_t total_sent = 0;
while (total_sent < len) {
ssize_t sent = socket_send(sock, (char *)buf + total_sent, len - total_sent, 0);
if (sent < 0) {
// Non-blocking socket, buffer full, retry later
break;
} else {
// Connection reset or other fatal error
return false;
}
}
total_sent += sent;
}

Platform-specific notes:

  • Windows: socket_send() is a thin wrapper around send()
  • POSIX: socket_send() is a thin wrapper around send() (or write())
  • Partial sends are normal and should be handled

Definition at line 48 of file comprehensive.c.

48 {
49 (void)sock;
50 (void)buf;
51 (void)len;
52 (void)flags;
53 return -1;
54}

Referenced by add_client(), and send_with_timeout().

◆ socket_sendto()

ssize_t socket_sendto ( socket_t  sock,
const void *  buf,
size_t  len,
int  flags,
const struct sockaddr *  dest_addr,
socklen_t  addrlen 
)

#include <socket.h>

Send data to a specific address (UDP)

Parameters
sockSocket to send on
bufData buffer to send
lenNumber of bytes to send
flagsSocket flags (typically 0)
dest_addrDestination address
addrlenLength of destination address
Returns
Number of bytes sent on success, -1 on error

◆ socket_set_blocking()

int socket_set_blocking ( socket_t  sock)

#include <socket.h>

Set socket to blocking mode.

Parameters
sockSocket to configure
Returns
0 on success, non-zero on error

Referenced by connect_with_timeout().

◆ socket_set_buffer_sizes()

int socket_set_buffer_sizes ( socket_t  sock,
int  recv_size,
int  send_size 
)

#include <socket.h>

Set socket buffer sizes.

Parameters
sockSocket to configure
recv_sizeReceive buffer size in bytes
send_sizeSend buffer size in bytes
Returns
0 on success, non-zero on error

◆ socket_set_keepalive()

int socket_set_keepalive ( socket_t  sock,
bool  keepalive 
)

#include <socket.h>

Set SO_KEEPALIVE socket option.

Parameters
sockSocket to configure
keepalivetrue to enable keepalive, false to disable
Returns
0 on success, non-zero on error

Referenced by server_connection_establish(), and tcp_client_connect().

◆ socket_set_keepalive_params()

int socket_set_keepalive_params ( socket_t  sock,
bool  enable,
int  idle,
int  interval,
int  count 
)

#include <socket.h>

Set TCP keepalive parameters.

Parameters
sockSocket to configure
enableEnable/disable keepalive
idleIdle time before sending first keepalive probe (seconds)
intervalInterval between keepalive probes (seconds)
countNumber of keepalive probes before connection failure
Returns
0 on success, non-zero on error

Referenced by set_socket_keepalive().

◆ socket_set_linger()

int socket_set_linger ( socket_t  sock,
bool  enable,
int  timeout 
)

#include <socket.h>

Set SO_LINGER socket option.

Parameters
sockSocket to configure
enableEnable/disable lingering
timeoutLinger timeout in seconds
Returns
0 on success, non-zero on error

◆ socket_set_nodelay()

int socket_set_nodelay ( socket_t  sock,
bool  nodelay 
)

#include <socket.h>

Set TCP_NODELAY socket option (disable Nagle's algorithm)

Parameters
sockSocket to configure
nodelaytrue to disable Nagle's algorithm, false to enable
Returns
0 on success, non-zero on error

◆ socket_set_nonblocking()

int socket_set_nonblocking ( socket_t  sock,
bool  nonblocking 
)

#include <socket.h>

Set socket to non-blocking mode.

Parameters
sockSocket to configure
nonblockingtrue for non-blocking, false for blocking
Returns
0 on success, non-zero on error

Referenced by set_socket_nonblocking().

◆ socket_set_reuseaddr()

int socket_set_reuseaddr ( socket_t  sock,
bool  reuse 
)

#include <socket.h>

Set SO_REUSEADDR socket option.

Parameters
sockSocket to configure
reusetrue to enable reuse, false to disable
Returns
0 on success, non-zero on error

◆ socket_set_timeout()

int socket_set_timeout ( socket_t  sock,
uint64_t  timeout_ns 
)

#include <socket.h>

Set socket receive and send timeouts.

Parameters
sockSocket to configure
timeout_msTimeout in milliseconds
Returns
0 on success, non-zero on error

Sets both SO_RCVTIMEO (receive timeout) and SO_SNDTIMEO (send timeout) to prevent indefinite blocking on socket operations.

Platform-specific implementations:

  • Windows: Converts nanoseconds to milliseconds for DWORD timeout
  • POSIX: Converts nanoseconds to struct timeval (seconds + microseconds)
Parameters
sockSocket to configure
timeout_msTimeout in milliseconds
Returns
0 on success, non-zero on error

Cross-platform implementation that sets both SO_RCVTIMEO and SO_SNDTIMEO. Platform-specific socket_setsockopt() handles the differences between Windows (DWORD milliseconds) and POSIX (struct timeval).

Definition at line 82 of file socket.c.

82 {
83#ifdef _WIN32
84 // Windows: SO_RCVTIMEO and SO_SNDTIMEO use DWORD (milliseconds)
85 DWORD timeout_val = (DWORD)time_ns_to_ms(timeout_ns);
86 if (socket_setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &timeout_val, sizeof(timeout_val)) != 0) {
87 return -1;
88 }
89 if (socket_setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &timeout_val, sizeof(timeout_val)) != 0) {
90 return -1;
91 }
92#else
93 // POSIX: SO_RCVTIMEO and SO_SNDTIMEO use struct timeval (seconds + microseconds)
94 struct timeval tv;
95 uint64_t us = time_ns_to_us(timeout_ns);
96 tv.tv_sec = (time_t)(us / US_PER_SEC_INT);
97 tv.tv_usec = (suseconds_t)(us % US_PER_SEC_INT);
98 if (socket_setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) != 0) {
99 return -1;
100 }
101 if (socket_setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) != 0) {
102 return -1;
103 }
104#endif
105 return 0;
106}
#define US_PER_SEC_INT
Definition time.h:161

References socket_setsockopt(), and US_PER_SEC_INT.

Referenced by nat_measure_bandwidth().

◆ socket_set_timeout_ns()

int socket_set_timeout_ns ( socket_t  sock,
uint64_t  timeout_ns 
)

#include <socket.h>

Set socket send/receive timeout in nanoseconds.

Parameters
sockSocket to configure
timeout_nsTimeout in nanoseconds (0 to disable timeout)
Returns
0 on success, non-zero on error

Sets both SO_SNDTIMEO and SO_RCVTIMEO socket options. Nanosecond values are converted to platform-specific time units internally.

◆ socket_setsockopt()

int socket_setsockopt ( socket_t  sock,
int  level,
int  optname,
const void *  optval,
socklen_t  optlen 
)

#include <socket.h>

Set socket option.

Parameters
sockSocket to configure
levelOption level (SOL_SOCKET for socket-level, IPPROTO_TCP for TCP, IPPROTO_IPV6 for IPv6, etc.)
optnameOption name (SO_REUSEADDR, TCP_NODELAY, SO_RCVTIMEO, SO_SNDBUF, etc.)
optvalPointer to option value (type depends on optname)
optlenLength of option value
Returns
0 on success, non-zero on error

Sets socket options. Common options are available via convenience functions (socket_set_nodelay(), socket_set_reuseaddr(), etc.), but socket_setsockopt() provides direct access for advanced or platform-specific options.

Common socket-level options (SOL_SOCKET):

  • SO_REUSEADDR: Allow rapid socket rebinding (int, 0 or 1)
  • SO_KEEPALIVE: Enable TCP keepalive probes (int, 0 or 1)
  • SO_RCVBUF: Receive buffer size in bytes (int)
  • SO_SNDBUF: Send buffer size in bytes (int)
  • SO_RCVTIMEO: Receive timeout (struct timeval on POSIX, DWORD ms on Windows)
  • SO_SNDTIMEO: Send timeout (struct timeval on POSIX, DWORD ms on Windows)

Common TCP options (IPPROTO_TCP):

  • TCP_NODELAY: Disable Nagle's algorithm for reduced latency (int, 0 or 1)
  • TCP_KEEPIDLE/TCP_KEEPINTVL/TCP_KEEPCNT: Keepalive parameters (Linux/POSIX)

Common IPv6 options (IPPROTO_IPV6):

  • IPV6_V6ONLY: Restrict to IPv6-only or allow IPv4 too (int, 0 or 1)

Example: Set receive timeout (cross-platform):

#ifdef _WIN32
DWORD timeout_ms = 5000; // 5 second timeout
socket_setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &timeout_ms, sizeof(timeout_ms));
#else
struct timeval timeout = {5, 0}; // 5 seconds, 0 microseconds
socket_setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
#endif

Recommendation: Use convenience functions when available:

Referenced by set_socket_timeout(), socket_configure_buffers(), socket_optimize_for_streaming(), and socket_set_timeout().

◆ socket_shutdown()

int socket_shutdown ( socket_t  sock,
int  how 
)

#include <socket.h>

Shutdown socket I/O.

Parameters
sockSocket to shutdown
howShutdown mode (SHUT_RD, SHUT_WR, SHUT_RDWR)
Returns
0 on success, non-zero on error

Definition at line 42 of file comprehensive.c.

42 {
43 (void)sock;
44 (void)how;
45 return 0;
46}

Referenced by disconnect_client_for_bad_data(), remove_client(), server_connection_shutdown(), and tcp_client_shutdown().

◆ symbol_cache_destroy()

void symbol_cache_destroy ( void  )

#include <symbols.h>

Clean up the symbol cache and free all resources.

Destroys the symbol cache and frees all cached symbols and internal structures. Should be called at application shutdown after all backtraces are complete.

Note
Safe to call multiple times (no-op after first call).
All cached symbol strings are freed automatically.

Definition at line 316 of file symbols.c.

316 {
317 if (!lifecycle_shutdown(&g_symbol_cache_lc)) {
318 return;
319 }
320
321 // Mark as uninitialized FIRST to prevent new inserts during cleanup
322
323 // Acquire write lock to prevent any concurrent operations
324 rwlock_wrlock(&g_symbol_cache_lock);
325
326 // Count entries before freeing for debugging
327 size_t entry_count = HASH_COUNT(g_symbol_cache);
328
329 // Free all symbol entries using HASH_ITER
330 symbol_entry_t *entry = NULL, *tmp = NULL;
331 size_t freed_count = 0;
332 HASH_ITER(hh, g_symbol_cache, entry, tmp) {
333 if (entry) {
334 HASH_DEL(g_symbol_cache, entry);
335 if (entry->symbol) {
336 free(entry->symbol);
337 }
338 free(entry);
339 freed_count++;
340 }
341 }
342
343 // Release lock and destroy rwlock
344 rwlock_wrunlock(&g_symbol_cache_lock);
345 rwlock_destroy(&g_symbol_cache_lock);
346
347 g_symbol_cache = NULL;
348
349 log_dev("Symbol cache cleaned up: %zu entries counted, %zu entries freed (hits=%llu, misses=%llu)", entry_count,
350 freed_count, (unsigned long long)atomic_load_u64(&g_cache_hits),
351 (unsigned long long)atomic_load_u64(&g_cache_misses));
352}
Symbol cache entry structure for address-to-symbol mapping.
Definition symbols.c:108
char * symbol
Resolved symbol string (allocated, owned by cache)
Definition symbols.c:112

References atomic_load_u64(), lifecycle_shutdown(), log_dev, rwlock_destroy(), rwlock_wrlock, rwlock_wrunlock, and symbol_entry_t::symbol.

Referenced by asciichat_shared_destroy().

◆ symbol_cache_free_symbols()

void symbol_cache_free_symbols ( char **  symbols)

#include <symbols.h>

Free symbol array returned by symbol_cache_resolve_batch.

Parameters
symbolsArray of symbol strings (can be NULL)

Frees the array structure returned by symbol_cache_resolve_batch(). This only frees the array itself, not the individual symbol strings (which are owned by the cache).

Note
Safe to call with NULL pointer (no-op).
This function only frees the array structure, not cached symbol strings.

Definition at line 1000 of file symbols.c.

1000 {
1001 if (!symbols) {
1002 return;
1003 }
1004
1005 // The array is NULL-terminated (allocated with size+1, with result[size] = NULL)
1006 // The terminator is a SINGLE NULL at index 'size'
1007 // Failed allocations use the NULL_SENTINEL string "[NULL]" instead of NULL,
1008 // so there are no NULL entries in the middle - only at the terminator
1009 // This makes iteration safe: we can iterate until we find the first NULL (the terminator)
1010
1011 // Iterate through entries, freeing all non-NULL entries until we hit the NULL terminator
1012 for (int i = 0; i < 64; i++) { // Reasonable limit to prevent infinite loop
1013 if (symbols[i] == NULL) {
1014 // Found NULL - this is the terminator, stop here
1015 break;
1016 }
1017
1018 // Found a non-NULL entry - check if it's the sentinel string
1019 // Both regular strings and sentinel strings are allocated (with strdup),
1020 // so we free them all
1021 SAFE_FREE(symbols[i]);
1022 symbols[i] = NULL; // Clear pointer after freeing
1023 }
1024
1025 // Free the array itself
1026 SAFE_FREE(symbols);
1027}

References SAFE_FREE.

◆ symbol_cache_get_stats()

void symbol_cache_get_stats ( uint64_t *  hits_out,
uint64_t *  misses_out,
size_t *  entries_out 
)

#include <symbols.h>

Get cache statistics.

Parameters
hits_outPointer to receive hit count (can be NULL)
misses_outPointer to receive miss count (can be NULL)
entries_outPointer to receive entry count (can be NULL)

Retrieves cumulative statistics from the symbol cache. Useful for performance monitoring and cache efficiency analysis.

Note
All counters are cumulative since cache initialization.
Thread-safe: Can be called from any thread.

Definition at line 435 of file symbols.c.

435 {
436 if (hits_out) {
437 *hits_out = atomic_load_u64(&g_cache_hits);
438 }
439 if (misses_out) {
440 *misses_out = atomic_load_u64(&g_cache_misses);
441 }
442 if (entries_out) {
443 rwlock_rdlock(&g_symbol_cache_lock);
444 *entries_out = HASH_COUNT(g_symbol_cache);
445 rwlock_rdunlock(&g_symbol_cache_lock);
446 }
447}

References atomic_load_u64(), rwlock_rdlock, and rwlock_rdunlock.

◆ symbol_cache_init()

asciichat_error_t symbol_cache_init ( void  )

#include <symbols.h>

Initialize the symbol cache.

Returns
ASCIICHAT_OK on success, error code on failure

Initializes the global symbol cache system. Creates the hashtable and initializes statistics counters. Must be called before using any other symbol cache functions.

Note
Idempotent: Safe to call multiple times (no-op after first call).
Thread-safe: Can be called from any thread during initialization.

Definition at line 291 of file symbols.c.

291 {
292 if (!lifecycle_init(&g_symbol_cache_lc, "symbol_cache")) {
293 return 0; // Already initialized
294 }
295
296 // NOTE: Symbolizer detection is deferred to lazy initialization (symbol_cache_resolve_batch)
297 // to avoid large stack allocations during early initialization in musl static-pie builds.
298 // This prevents __stack_chk_fail() in check_binary_in_path_uncached() which allocates ~4.6KB on stack.
299
300 // Initialize rwlock for thread safety (uthash requires external locking)
301 if (rwlock_init(&g_symbol_cache_lock, "symbol_cache") != 0) {
302 lifecycle_init_abort(&g_symbol_cache_lc);
303 return SET_ERRNO(ERROR_THREAD, "Failed to initialize symbol cache rwlock");
304 }
305
306 // Initialize uthash head to NULL (required)
307 g_symbol_cache = NULL;
308
309 atomic_store_u64(&g_cache_hits, 0);
310 atomic_store_u64(&g_cache_misses, 0);
311
312 log_dev("Symbol cache initialized");
313 return 0;
314}
@ ERROR_THREAD
int rwlock_init(rwlock_t *rwlock, const char *name)
Initialize a read-write lock with a name.
Definition threading.c:65
void lifecycle_init_abort(lifecycle_t *lc)
Definition lifecycle.c:99
bool lifecycle_init(lifecycle_t *lc, const char *name)
Definition lifecycle.c:26

References atomic_store_u64(), ERROR_THREAD, lifecycle_init(), lifecycle_init_abort(), log_dev, rwlock_init(), and SET_ERRNO.

Referenced by asciichat_shared_init().

◆ symbol_cache_insert()

bool symbol_cache_insert ( void *  addr,
const char *  symbol 
)

#include <symbols.h>

Insert a symbol into the cache.

Parameters
addrAddress to cache (must not be NULL)
symbolSymbol string to cache (must not be NULL, will be copied)
Returns
true on success, false on failure (memory allocation error)

Inserts a symbol into the cache for future lookups. The symbol string is copied and owned by the cache. If the address already exists in the cache, the symbol is updated.

Note
Symbol string is copied, so caller can free the original string.
Thread-safe: Can be called from multiple threads simultaneously.
Statistics are updated (entry count, etc.).
Warning
Memory allocation failures return false. Check return value.

Definition at line 377 of file symbols.c.

377 {
378 if (!lifecycle_is_initialized(&g_symbol_cache_lc) || !addr || !symbol) {
379 return false;
380 }
381
382 // Acquire write lock to make the entire operation atomic
383 rwlock_wrlock(&g_symbol_cache_lock);
384
385 // Double-check cache is still initialized after acquiring lock
386 // (cleanup might have marked it uninitialized between our check and lock acquisition)
387 if (!lifecycle_is_initialized(&g_symbol_cache_lc)) {
388 rwlock_wrunlock(&g_symbol_cache_lock);
389 return false;
390 }
391
392 // Check if entry already exists
393 symbol_entry_t *existing = NULL;
394 HASH_FIND_PTR(g_symbol_cache, &addr, existing);
395
396 if (existing) {
397 // Entry exists - update symbol if different
398 if (existing->symbol && strcmp(existing->symbol, symbol) != 0) {
399 // Free old symbol and allocate new one
400 free(existing->symbol);
401 existing->symbol = platform_strdup(symbol);
402 if (!existing->symbol) {
403 rwlock_wrunlock(&g_symbol_cache_lock);
404 return false;
405 }
406 }
407 rwlock_wrunlock(&g_symbol_cache_lock);
408 return true;
409 }
410
411 // Create new entry
412 symbol_entry_t *entry = (symbol_entry_t *)malloc(sizeof(symbol_entry_t));
413 if (!entry) {
414 rwlock_wrunlock(&g_symbol_cache_lock);
415 return false;
416 }
417
418 entry->addr = addr;
419 entry->symbol = platform_strdup(symbol);
420 if (!entry->symbol) {
421 free(entry);
422 rwlock_wrunlock(&g_symbol_cache_lock);
423 return false;
424 }
425
426 // Add to hash table
427 HASH_ADD_PTR(g_symbol_cache, addr, entry);
428
429 // Release lock
430 rwlock_wrunlock(&g_symbol_cache_lock);
431
432 return true;
433}
void * addr
Memory address key (used for hashtable lookup)
Definition symbols.c:110

References symbol_entry_t::addr, lifecycle_is_initialized(), platform_strdup(), rwlock_wrlock, rwlock_wrunlock, and symbol_entry_t::symbol.

Referenced by symbol_cache_resolve_batch().

◆ symbol_cache_lookup()

const char * symbol_cache_lookup ( void *  addr)

#include <symbols.h>

Look up a symbol for a given address.

Parameters
addrAddress to resolve (must not be NULL)
Returns
Cached symbol string, or NULL if not in cache

Performs a hashtable lookup to find the cached symbol for the given address. Returns NULL if the address is not in the cache (cache miss). Use symbol_cache_resolve_batch() to resolve uncached addresses.

Note
This is a fast O(1) lookup operation (hashtable lookup).
Returned string is owned by the cache and should not be freed.
String remains valid until cache is cleaned up.
Thread-safe: Can be called from multiple threads simultaneously.
For batch resolution, use symbol_cache_resolve_batch() which handles both cache lookup and addr2line resolution automatically.

Definition at line 354 of file symbols.c.

354 {
355 if (!lifecycle_is_initialized(&g_symbol_cache_lc) || !addr) {
356 return NULL;
357 }
358
359 rwlock_rdlock(&g_symbol_cache_lock);
360
361 symbol_entry_t *entry = NULL;
362 HASH_FIND_PTR(g_symbol_cache, &addr, entry);
363
364 const char *result = NULL;
365 if (entry) {
366 // Copy string while holding lock to prevent use-after-free
367 result = platform_strdup(entry->symbol);
368 atomic_fetch_add_u64(&g_cache_hits, 1);
369 } else {
370 atomic_fetch_add_u64(&g_cache_misses, 1);
371 }
372
373 rwlock_rdunlock(&g_symbol_cache_lock);
374 return result;
375}

References atomic_fetch_add_u64(), lifecycle_is_initialized(), platform_strdup(), rwlock_rdlock, rwlock_rdunlock, and symbol_entry_t::symbol.

Referenced by symbol_cache_resolve_batch().

◆ symbol_cache_print_stats()

void symbol_cache_print_stats ( void  )

#include <symbols.h>

Print cache statistics to logging system.

Logs detailed statistics from the symbol cache including hit rate, miss count, and entry count. Useful for periodic performance monitoring.

Note
Requires logging system to be initialized.
Statistics are formatted and logged at INFO level.

Definition at line 449 of file symbols.c.

449 {
450 uint64_t hits = atomic_load_u64(&g_cache_hits);
451 uint64_t misses = atomic_load_u64(&g_cache_misses);
452
453 rwlock_rdlock(&g_symbol_cache_lock);
454 size_t entries = HASH_COUNT(g_symbol_cache);
455 rwlock_rdunlock(&g_symbol_cache_lock);
456
457 uint64_t total = hits + misses;
458 double hit_rate = total > 0 ? (100.0 * (double)hits / (double)total) : 0.0;
459
460 log_debug("Symbol Cache Stats: %zu entries, %llu hits, %llu misses (%.1f%% hit rate)", entries,
461 (unsigned long long)hits, (unsigned long long)misses, hit_rate);
462}

References atomic_load_u64(), log_debug, rwlock_rdlock, and rwlock_rdunlock.

◆ symbol_cache_resolve_batch()

char ** symbol_cache_resolve_batch ( void *const *  buffer,
int  size 
)

#include <symbols.h>

Resolve multiple addresses using addr2line and cache results.

Parameters
bufferArray of addresses to resolve (must not be NULL)
sizeNumber of addresses in buffer (must be > 0)
Returns
Array of symbol strings (caller must free with symbol_cache_free_symbols), or NULL on error

Resolves multiple addresses to symbol names using addr2line. For each address:

  1. Checks cache first (fast O(1) lookup)
  2. If not cached, resolves using addr2line subprocess
  3. Caches resolved symbol for future lookups
  4. Returns symbol string in output array

This function is optimized for batch backtrace processing:

  • Checks cache for all addresses first (fast)
  • Resolves only uncached addresses in single addr2line invocation
  • Caches all resolved symbols automatically
Note
Returned array has size elements (one per input address).
Array elements are NULL if symbol resolution failed for that address.
Array elements point to cached symbol strings (owned by cache).
Call symbol_cache_free_symbols() to free the array (not the strings).
addr2line must be available in PATH for resolution to work.
Debug symbols must be available in the executable.
Warning
Caller must free the returned array using symbol_cache_free_symbols(). Do NOT free individual strings (owned by cache).

Definition at line 856 of file symbols.c.

856 {
857 if (size <= 0 || !buffer) {
858 log_error("Invalid parameters: buffer=%p, size=%d", (void *)buffer, size);
859 return NULL;
860 }
861
862 // DO NOT auto-initialize here - causes circular dependency during lock_debug_init()
863 // The cache must be initialized explicitly by platform_init() before use
864 if (!lifecycle_is_initialized(&g_symbol_cache_lc)) {
865 // Cache not initialized - fall back to uncached resolution
866 // This happens during early initialization before platform_init() completes
867 char **result = run_llvm_symbolizer_batch(buffer, size);
868 if (!result) {
869 result = run_addr2line_batch(buffer, size);
870 }
871 return result;
872 }
873
874 // Lazy initialize symbolizer detection on first use (not during symbol_cache_init)
875 // This avoids large stack allocations in musl static-pie during early initialization
876 bool expected = false;
877 if (atomic_cas_bool(&g_symbolizer_detected, &expected, true)) {
878 g_symbolizer_type = detect_symbolizer();
879 }
880
881 // Allocate result array (size + 1 for NULL terminator)
882 // CALLOC zeros the memory, so result[size] is already NULL
883 char **result = SAFE_CALLOC((size_t)(size + 1), sizeof(char *), char **);
884 if (!result) {
885 return NULL;
886 }
887
888 // Ensure NULL terminator is explicitly set (CALLOC already did this, but be explicit)
889 result[size] = NULL;
890
891 // First pass: check cache for all addresses
892 int uncached_count = 0;
893
894 // Allocate arrays for uncached addresses on heap instead of stack
895 // VLAs can cause stack overflow with large backtrace sizes
896 void **uncached_addrs = SAFE_MALLOC((size_t)size * sizeof(void *), void **);
897 int *uncached_indices = SAFE_MALLOC((size_t)size * sizeof(int), int *);
898
899 if (!uncached_addrs || !uncached_indices) {
900 SAFE_FREE(uncached_addrs);
901 SAFE_FREE(uncached_indices);
902 // Return result array with raw addresses as fallback
903 for (int i = 0; i < size; i++) {
904 result[i] = SAFE_MALLOC(32, char *);
905 if (result[i]) {
906 SAFE_SNPRINTF(result[i], 32, "%p", buffer[i]);
907 }
908 }
909 return result;
910 }
911
912 for (int i = 0; i < size; i++) {
913 const char *cached = symbol_cache_lookup(buffer[i]);
914 if (cached) {
915 // Cache hit - symbol_cache_lookup already duplicated the string
916 result[i] = (char *)cached;
917 } else {
918 // Cache miss - track for batch resolution
919 uncached_addrs[uncached_count] = buffer[i];
920 uncached_indices[uncached_count] = i;
921 uncached_count++;
922 }
923 }
924
925 // Second pass: resolve uncached addresses with selected symbolizer
926 if (uncached_count > 0) {
927 char **resolved = NULL;
928
929 // Use the detected symbolizer type
930 switch (g_symbolizer_type) {
931 case SYMBOLIZER_LLVM:
932 resolved = run_llvm_symbolizer_batch(uncached_addrs, uncached_count);
933 break;
935 resolved = run_addr2line_batch(uncached_addrs, uncached_count);
936 break;
937 case SYMBOLIZER_NONE:
938 default:
939 // No symbolizer available - will fall through to raw address handling
940 resolved = NULL;
941 break;
942 }
943
944 if (resolved) {
945 for (int i = 0; i < uncached_count; i++) {
946 int orig_idx = uncached_indices[i];
947 if (resolved[i]) {
948 result[orig_idx] = platform_strdup(resolved[i]);
949 // If strdup failed, use sentinel string instead of NULL
950 if (!result[orig_idx]) {
951 log_error("Failed to duplicate string for result[%d]", orig_idx);
952 result[orig_idx] = platform_strdup(NULL_SENTINEL);
953 }
954 // Only insert into cache if strdup succeeded (and it's not the sentinel)
955 if (result[orig_idx] && strcmp(result[orig_idx], NULL_SENTINEL) != 0) {
956 if (!symbol_cache_insert(uncached_addrs[i], resolved[i])) {
957 log_error("Failed to insert symbol into cache for result[%d]", orig_idx);
958 }
959 }
960 SAFE_FREE(resolved[i]);
961 } else {
962 // resolved[i] is NULL - use sentinel string
963 if (!result[orig_idx]) {
964 result[orig_idx] = platform_strdup(NULL_SENTINEL);
965 }
966 }
967 if (!result[orig_idx]) {
968 log_error("Failed to allocate memory for result[%d]", orig_idx);
969 }
970 }
971
972 SAFE_FREE(resolved);
973
974 } else {
975 // addr2line failed - fill uncached entries with raw addresses or sentinel
976 for (int i = 0; i < uncached_count; i++) {
977 int orig_idx = uncached_indices[i];
978 if (!result[orig_idx]) {
979 result[orig_idx] = SAFE_MALLOC(32, char *);
980 if (result[orig_idx]) {
981 SAFE_SNPRINTF(result[orig_idx], 32, "%p", uncached_addrs[i]);
982 } else {
983 result[orig_idx] = platform_strdup(NULL_SENTINEL);
984 }
985 }
986 if (!result[orig_idx]) {
987 log_error("Failed to allocate memory for result[%d]", orig_idx);
988 }
989 }
990 }
991 }
992
993 // Clean up heap-allocated arrays
994 SAFE_FREE(uncached_addrs);
995 SAFE_FREE(uncached_indices);
996
997 return result;
998}
bool atomic_cas_bool(atomic_t *a, bool *expected, bool new_value)
Atomically compare-and-swap a boolean.
Definition atomic.c:184
#define SAFE_SNPRINTF(buffer, buffer_size,...)
Definition common.h:492
#define SAFE_CALLOC(count, size, cast)
Definition common.h:274
bool symbol_cache_insert(void *addr, const char *symbol)
Insert a symbol into the cache.
Definition symbols.c:377
const char * symbol_cache_lookup(void *addr)
Look up a symbol for a given address.
Definition symbols.c:354
#define NULL_SENTINEL
Definition symbols.c:46
@ SYMBOLIZER_NONE
Definition symbols.c:53
@ SYMBOLIZER_LLVM
Definition symbols.c:54
@ SYMBOLIZER_ADDR2LINE
Definition symbols.c:55

References atomic_cas_bool(), lifecycle_is_initialized(), log_error, NULL_SENTINEL, platform_strdup(), SAFE_CALLOC, SAFE_FREE, SAFE_MALLOC, SAFE_SNPRINTF, symbol_cache_insert(), symbol_cache_lookup(), SYMBOLIZER_ADDR2LINE, SYMBOLIZER_LLVM, and SYMBOLIZER_NONE.

◆ terminal_clear_screen()

asciichat_error_t terminal_clear_screen ( void  )

#include <terminal.h>

Clear the terminal screen.

Returns
ASCIICHAT_OK on success, error code on failure

Clears the terminal screen using ANSI escape sequences (Unix) or Windows Console API. Removes all visible characters and resets cursor position to top-left.

Note
Uses ANSI escape sequence ESC[2J on Unix systems.
On Windows, uses Console API ClearScreen() function.
Screen clearing does not affect scrollback buffer.

Definition at line 222 of file platform/wasm/terminal.c.

222 {
223 // Send ANSI clear screen escape sequence
224 write(STDOUT_FILENO, "\033[2J", 4);
225 return ASCIICHAT_OK;
226}

References ASCIICHAT_OK.

Referenced by session_client_like_run(), session_display_clear(), session_display_write_ascii(), session_handle_keyboard_input(), session_render_loop(), splash_intro_start(), splash_wait_for_animation(), and update_banner_print_instructions().

◆ terminal_clear_scrollback()

asciichat_error_t terminal_clear_scrollback ( int  fd)

#include <terminal.h>

Clear terminal scrollback buffer.

Parameters
fdFile descriptor for terminal (must be valid)
Returns
ASCIICHAT_OK on success, error code on failure

Clears the terminal scrollback buffer (history of previous output). This removes all previous terminal output that can be scrolled back to view. Useful for starting with a clean terminal state.

Note
Scrollback clearing is terminal-dependent.
Some terminals may not support scrollback clearing.
On Windows, clears console screen buffer.

Definition at line 76 of file misc.c.

76 {
77 (void)fd;
78 return ASCIICHAT_OK;
79}

References ASCIICHAT_OK.

Referenced by session_display_write_ascii().

◆ terminal_cursor_hide()

asciichat_error_t terminal_cursor_hide ( void  )

#include <terminal.h>

Hide terminal cursor.

Returns
ASCIICHAT_OK on success, error code on failure

Hides the terminal cursor on stdout. Useful during full-screen ASCII art rendering where cursor flicker is distracting. No-op when not interactive (piped or redirected output).

Uses ESC[?25l. On Windows, tries VT processing first and falls back to SetConsoleCursorInfo for older consoles.

Note
Cursor should be restored with terminal_cursor_show() before exit.

Definition at line 20 of file platform/wasm/stubs/terminal.c.

20 {
21 return ASCIICHAT_OK;
22}

References ASCIICHAT_OK.

Referenced by ascii_write_init(), and session_display_write_ascii().

◆ terminal_cursor_home()

asciichat_error_t terminal_cursor_home ( int  fd)

#include <terminal.h>

Move cursor to home position (top-left)

Parameters
fdFile descriptor for terminal (must be valid)
Returns
ASCIICHAT_OK on success, error code on failure

Moves cursor to home position (row 1, column 1 - top-left corner). Equivalent to terminal_move_cursor(1, 1) but more efficient. Uses ANSI escape sequence ESC[H.

Note
Home position is always row 1, column 1.
Useful for starting new frame rendering at top-left.

Definition at line 228 of file platform/wasm/terminal.c.

228 {
229 (void)fd;
230 // Send ANSI cursor home escape sequence
231 write(STDOUT_FILENO, "\033[H", 3);
232 return ASCIICHAT_OK;
233}

References ASCIICHAT_OK.

Referenced by session_display_clear(), session_display_cursor_home(), session_display_write_ascii(), and splash_wait_for_animation().

◆ terminal_cursor_show()

asciichat_error_t terminal_cursor_show ( void  )

#include <terminal.h>

Show terminal cursor.

Returns
ASCIICHAT_OK on success, error code on failure

Restores the terminal cursor on stdout. No-op when not interactive. Uses ESC[?25h. On Windows, tries VT processing first and falls back to SetConsoleCursorInfo for older consoles.

Definition at line 24 of file platform/wasm/stubs/terminal.c.

24 {
25 return ASCIICHAT_OK;
26}

References ASCIICHAT_OK.

Referenced by ascii_write_destroy(), log_search_enter_mode(), main(), session_display_reset(), session_display_write_ascii(), splash_intro_start(), and terminal_screen_render().

◆ terminal_enable_ansi()

void terminal_enable_ansi ( void  )

#include <terminal.h>

Enable ANSI escape sequences.

On Windows, enables ANSI escape sequence processing in the console. This allows Windows console to interpret ANSI escape codes (colors, cursor movement, etc.) that are normally only available on Unix terminals.

Note
This function is a no-op on Unix systems (ANSI already supported).
On Windows, requires Windows 10 build 1511 or later.
ANSI support is enabled for the current console session.

◆ terminal_flush()

asciichat_error_t terminal_flush ( int  fd)

#include <terminal.h>

Flush terminal output.

Parameters
fdFile descriptor to flush (must be valid file descriptor)
Returns
ASCIICHAT_OK on success, error code on failure

Forces all buffered output to be written to the terminal immediately. Ensures that all pending terminal output is displayed before continuing.

Note
This function calls fsync/flush operations on the file descriptor.
Useful for ensuring output is visible before blocking operations.

Definition at line 163 of file platform/wasm/terminal.c.

163 {
164 (void)fd;
165 return ASCIICHAT_OK;
166}

References ASCIICHAT_OK.

Referenced by ascii_write(), config_create_default(), keyboard_help_render(), session_display_render_fps_overlay(), session_display_reset(), session_display_write_ascii(), session_display_write_raw(), splash_wait_for_animation(), and update_banner_show_prompt().

◆ terminal_get_cursor_position()

asciichat_error_t terminal_get_cursor_position ( int *  row,
int *  col 
)

#include <terminal.h>

Get current cursor position.

Parameters
rowPointer to store row position (must not be NULL, 1-based)
colPointer to store column position (must not be NULL, 1-based)
Returns
ASCIICHAT_OK on success, error code on failure

Queries the terminal for the current cursor position. Uses platform-specific methods to detect cursor location. Positions are returned in 1-based coordinates (row 1, column 1 is top-left).

Note
On failure, output parameters are not modified.
Cursor position detection may not be available on all terminals.

◆ terminal_get_size()

asciichat_error_t terminal_get_size ( terminal_size_t *  size)

#include <terminal.h>

Get terminal size.

Parameters
sizePointer to store terminal size (must not be NULL)
Returns
ASCIICHAT_OK on success, error code on failure

Queries the terminal for its current dimensions (rows and columns). Uses platform-specific methods (ioctl on Unix, Windows Console API).

Note
On failure, size structure is not modified.
Terminal size may change if terminal is resized.

Definition at line 15 of file platform/wasm/stubs/terminal.c.

15 {
16 (void)size;
17 return SET_ERRNO(ERROR_NOT_SUPPORTED, "Terminal operations not supported in WASM");
18}

References ERROR_NOT_SUPPORTED, and SET_ERRNO.

Referenced by action_show_capabilities_immediate(), options_config_print_options_sections_with_width(), options_print_help_for_mode(), sdp_detect_terminal_capabilities(), terminal_get_effective_height(), terminal_get_effective_width(), and terminal_screen_render().

◆ terminal_move_cursor()

asciichat_error_t terminal_move_cursor ( int  row,
int  col 
)

#include <terminal.h>

Move cursor to specified position.

Parameters
rowRow position (1-based, top is row 1)
colColumn position (1-based, left is column 1)
Returns
ASCIICHAT_OK on success, error code on failure

Moves the terminal cursor to the specified row and column position. Uses ANSI escape sequences (Unix) or Windows Console API. Positions are 1-based (top-left is row 1, column 1).

Note
Row 1 is the top row of the terminal.
Column 1 is the leftmost column of the terminal.
Cursor position may be clamped to terminal bounds.

◆ terminal_move_cursor_relative()

asciichat_error_t terminal_move_cursor_relative ( int  offset)

#include <terminal.h>

Move cursor relative to current position.

Parameters
offsetRelative movement: positive = right, negative = left
Returns
ASCIICHAT_OK on success, error code on failure

Moves the cursor left or right by the specified number of columns. Positive offset moves right, negative moves left. Uses ANSI escape sequences: \x1b[nC (right) or \x1b[nD (left).

Note
Silently ignores attempts to move beyond terminal boundaries
This function is a no-op if stdout is not a TTY

Definition at line 235 of file platform/wasm/terminal.c.

235 {
236 if (offset == 0) {
237 return ASCIICHAT_OK;
238 }
239
240 if (offset > 0) {
241 // Move right: \x1b[<n>C
242 char buf[32];
243 int len = snprintf(buf, sizeof(buf), "\033[%dC", offset);
244 if (len > 0 && len < (int)sizeof(buf)) {
245 write(STDOUT_FILENO, buf, len);
246 }
247 } else {
248 // Move left: \x1b[<n>D
249 char buf[32];
250 int len = snprintf(buf, sizeof(buf), "\033[%dD", -offset);
251 if (len > 0 && len < (int)sizeof(buf)) {
252 write(STDOUT_FILENO, buf, len);
253 }
254 }
255
256 return ASCIICHAT_OK;
257}

References ASCIICHAT_OK.

Referenced by log_search_render_input_line().

◆ terminal_reset()

asciichat_error_t terminal_reset ( int  fd)

#include <terminal.h>

Reset terminal to default state.

Parameters
fdFile descriptor for terminal (must be valid)
Returns
ASCIICHAT_OK on success, error code on failure

Resets terminal to default state including:

  • Default colors (foreground/background)
  • Default cursor visibility
  • Default attributes (bold, underline, etc.)
  • Cleared scroll regions

Useful for cleanup before program exit or when resetting terminal state.

Note
This function sends ANSI reset sequence (ESC[0m).
Terminal state is reset immediately.

Definition at line 81 of file misc.c.

81 {
82 (void)fd;
83 return ASCIICHAT_OK;
84}

References ASCIICHAT_OK.

Referenced by session_display_reset(), and session_display_write_ascii().

◆ terminal_restore_cursor()

asciichat_error_t terminal_restore_cursor ( void  )

#include <terminal.h>

Restore saved cursor position.

Returns
ASCIICHAT_OK on success, error code on failure

Restores a previously saved cursor position. Uses ANSI escape sequence ESC[u to restore cursor position. Must be preceded by terminal_save_cursor().

Note
Only restores the most recently saved cursor position.
Some terminals may not support cursor position save/restore.

◆ terminal_ring_bell()

asciichat_error_t terminal_ring_bell ( void  )

#include <terminal.h>

Ring terminal bell.

Returns
ASCIICHAT_OK on success, error code on failure

Rings the terminal bell (beep sound). Uses ANSI escape sequence BEL or platform-specific API to trigger audible notification.

Note
Bell sound depends on terminal/system sound settings.
Some terminals may have bell disabled or silent.

◆ terminal_save_cursor()

asciichat_error_t terminal_save_cursor ( void  )

#include <terminal.h>

Save cursor position.

Returns
ASCIICHAT_OK on success, error code on failure

Saves the current cursor position for later restoration. Uses ANSI escape sequence ESC[s to save cursor position. Restore with terminal_restore_cursor().

Note
Saved position is terminal-specific and not stored in application.
Some terminals may not support cursor position save/restore.

◆ terminal_set_buffering()

asciichat_error_t terminal_set_buffering ( bool  line_buffered)

#include <terminal.h>

Set terminal buffering mode.

Parameters
line_bufferedtrue for line buffering, false for unbuffered
Returns
ASCIICHAT_OK on success, error code on failure

Controls terminal output buffering mode:

  • Line buffering: Output is buffered until newline is written
  • Unbuffered: Output is written immediately (real-time)

Unbuffered mode is useful for real-time ASCII art rendering where immediate output is desired. Line buffering is more efficient for line-based output.

Note
Buffering mode affects stdout/stderr behavior.
Unbuffered mode may reduce performance for large outputs.

◆ terminal_set_echo()

asciichat_error_t terminal_set_echo ( bool  enable)

#include <terminal.h>

Set terminal echo mode.

Parameters
enabletrue to enable echo, false to disable
Returns
ASCIICHAT_OK on success, error code on failure

Controls whether terminal input is echoed back to the display. When echo is disabled, input characters are not displayed (useful for password input or silent key capture).

Note
Echo mode works independently of raw mode.
Disabling echo is useful for password prompts.

Definition at line 207 of file platform/wasm/terminal.c.

207 {
208 // Send ANSI escape sequence for echo control (xterm ignores but passes through)
209 if (!enabled) {
210 write(STDOUT_FILENO, "\033[?25l", 6); // Hide cursor
211 } else {
212 write(STDOUT_FILENO, "\033[?25h", 6); // Show cursor
213 }
214 return ASCIICHAT_OK;
215}
bool enabled
Is filtering active?
Definition grep.c:84

References ASCIICHAT_OK, and enabled.

Referenced by ascii_write_destroy(), and ascii_write_init().

◆ terminal_set_raw_mode()

asciichat_error_t terminal_set_raw_mode ( bool  enable)

#include <terminal.h>

Set terminal to raw mode.

Parameters
enabletrue to enable raw mode, false to disable
Returns
ASCIICHAT_OK on success, error code on failure

Controls terminal raw mode. In raw mode, terminal input is not processed:

  • No line buffering (character-by-character input)
  • No echo (characters not printed)
  • No canonical mode (no line editing)
  • Immediate character availability

Raw mode is useful for real-time input processing (keyboard events, etc.).

Note
Disabling raw mode restores normal terminal behavior.
Raw mode affects only the current terminal session.

◆ terminal_set_scroll_region()

asciichat_error_t terminal_set_scroll_region ( int  top,
int  bottom 
)

#include <terminal.h>

Set scroll region.

Parameters
topTop row of scroll region (1-based, must be > 0)
bottomBottom row of scroll region (1-based, must be >= top)
Returns
ASCIICHAT_OK on success, error code on failure

Defines a scroll region within the terminal. Only the specified row range will scroll when text exceeds the bottom. Uses ANSI escape sequence ESC[top;bottomr. Useful for preserving header/footer regions while allowing content area to scroll.

Note
Scroll region must have top <= bottom.
Setting scroll region to entire terminal clears the restriction.
Some terminals may not support scroll regions.

◆ terminal_set_title()

asciichat_error_t terminal_set_title ( const char *  title)

#include <terminal.h>

Set terminal window title.

Parameters
titleTitle string to set (must not be NULL)
Returns
ASCIICHAT_OK on success, error code on failure

Sets the terminal window title to the specified string. Uses ANSI escape sequence ESC]0;titleBEL or platform-specific API. Title appears in window title bar or terminal tab.

Note
Title is truncated to terminal-specific maximum length.
Some terminals may not support title setting.

◆ terminal_supports_color()

bool terminal_supports_color ( void  )

#include <terminal.h>

Check if terminal supports color.

Returns
true if terminal supports color, false otherwise

Determines whether the terminal supports color output. Checks environment variables ($TERM, $COLORTERM) and terminal type to detect color capabilities.

Note
Returns true if ANY color support is detected (16, 256, or truecolor).
Use detect_terminal_capabilities() for detailed color level detection.

◆ terminal_supports_unicode()

bool terminal_supports_unicode ( void  )

#include <terminal.h>

Check if terminal supports unicode.

Returns
true if terminal supports unicode, false otherwise

Determines whether the terminal supports Unicode character output. Checks locale settings and terminal type for Unicode support.

Note
Unicode support is broader than UTF-8 (includes UTF-16, etc.).
Use terminal_supports_utf8() for UTF-8 specific detection.

◆ terminal_supports_utf8()

bool terminal_supports_utf8 ( void  )

#include <terminal.h>

Check if terminal supports UTF-8.

Returns
true if terminal supports UTF-8, false otherwise

Determines whether the terminal supports UTF-8 encoding. Checks locale settings ($LC_ALL, $LANG) and terminal type for UTF-8 support.

Note
UTF-8 support is required for Unicode palette characters.
Use detect_terminal_capabilities() for comprehensive capability detection.

Definition at line 117 of file platform/wasm/terminal.c.

117 {
118 return true;
119}

Referenced by detect_client_utf8_support(), and options_init().

◆ thread_create_or_fail()

asciichat_error_t thread_create_or_fail ( asciichat_thread_t *  thread,
void *(*)(void *)  func,
void *  arg,
const char *  thread_name,
const char *  client_id 
)

#include <thread.h>

Create a thread with standardized error handling and logging.

Parameters
threadThread handle to fill on success
funcThread function to execute
argArgument to pass to thread function
thread_nameHuman-readable name for logging (e.g., "video_render")
client_idClient ID for error context in logs
Returns
ASCIICHAT_OK on success, ERROR_INVALID_PARAM or ERROR_PLATFORM_INIT on failure

Wraps asciichat_thread_create() with unified error handling and logging. On success, logs at debug level. On failure, uses SET_ERRNO() to record error context and returns:

  • ERROR_INVALID_PARAM if parameters are invalid
  • ERROR_PLATFORM_INIT if thread creation fails
Note
Errors are logged via SET_ERRNO(), use HAS_ERRNO() to check context
thread_name and client_id are used in log messages for debugging

Definition at line 14 of file thread.c.

15 {
16 if (!thread || !func || !thread_name) {
17 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters for thread creation");
18 }
19
20 int result = asciichat_thread_create(thread, thread_name, func, arg);
21 if (result != 0) {
22 return SET_ERRNO(ERROR_PLATFORM_INIT, "Failed to create %s thread for client %u (result=%d)", thread_name,
23 client_id, result);
24 }
25
26 log_debug("Created %s thread for client %u successfully", thread_name, client_id);
27 return ASCIICHAT_OK;
28}
#define asciichat_thread_create(thread_ptr, attr, start_routine, arg)

References ASCIICHAT_OK, asciichat_thread_create, ERROR_INVALID_PARAM, ERROR_PLATFORM_INIT, log_debug, and SET_ERRNO.

Variable Documentation

◆ errno

◆ impl

pthread_rwlock_t rwlock_t::impl

Underlying POSIX rwlock.

Definition at line 62 of file rwlock.h.

◆ last_rdlock_time_ns

uint64_t rwlock_t::last_rdlock_time_ns

Timestamp of last read lock acquisition (nanoseconds)

Definition at line 65 of file rwlock.h.

Referenced by rwlock_format_state(), and rwlock_on_rdlock().

◆ last_unlock_time_ns

uint64_t rwlock_t::last_unlock_time_ns

Timestamp of last unlock (nanoseconds)

Definition at line 67 of file rwlock.h.

Referenced by rwlock_format_state(), and rwlock_on_unlock().

◆ last_wrlock_time_ns

uint64_t rwlock_t::last_wrlock_time_ns

Timestamp of last write lock acquisition (nanoseconds)

Definition at line 66 of file rwlock.h.

Referenced by rwlock_format_state(), and rwlock_on_wrlock().

◆ name

const char* rwlock_t::name

Human-readable name for named registry (all builds)

Definition at line 63 of file rwlock.h.

◆ rdlock_count

uint64_t rwlock_t::rdlock_count

Total read lock acquisitions.

Definition at line 70 of file rwlock.h.

Referenced by rwlock_format_state(), and rwlock_on_rdlock().

◆ read_lock_count

atomic_t rwlock_t::read_lock_count

Number of threads holding read locks (thread-safe atomic)

Definition at line 69 of file rwlock.h.

Referenced by rwlock_format_state(), rwlock_on_rdlock(), and rwlock_on_unlock().

◆ unlock_count

uint64_t rwlock_t::unlock_count

Total unlocks.

Definition at line 72 of file rwlock.h.

Referenced by rwlock_format_state(), and rwlock_on_unlock().

◆ write_held_by_key

uintptr_t rwlock_t::write_held_by_key

Registry key of thread holding write lock (0 if not held)

Definition at line 68 of file rwlock.h.

Referenced by rwlock_format_state(), rwlock_on_unlock(), and rwlock_on_wrlock().

◆ wrlock_count

uint64_t rwlock_t::wrlock_count

Total write lock acquisitions.

Definition at line 71 of file rwlock.h.

Referenced by rwlock_format_state(), and rwlock_on_wrlock().