ascii-chat 0.11.33
Video chat in your terminal
Loading...
Searching...
No Matches
Options Module

⚙️ Command-line option parsing with unified mode detection, builder API, and RCU-based thread-safe access More...

Files

file  builder.c
 Implementation of options builder API.
 
file  handlers.c
 Type handler implementations for builder operations.
 
file  help.c
 Help text generation and formatting for options.
 
file  colorscheme.c
 Color scheme management implementation and early initialization.
 
file  common.c
 Common utilities and helpers for option parsing.
 
file  bash.c
 Bash shell completion script generator.
 
file  completions.c
 Implementation of shell completion generation from options registry.
 
file  fish.c
 Fish shell completion script generator.
 
file  powershell.c
 PowerShell completion script generator.
 
file  zsh.c
 Zsh shell completion script generator with category grouping.
 
file  presets.c
 Preset option configurations for ascii-chat modes.
 
file  schema.c
 Schema metadata registry for config file options.
 
file  enums.c
 Option enum value implementation with dynamic enum-to-string mapping.
 
file  help_api.c
 Public API for retrieving option help text.
 
file  manpage.c
 Man page generation using modular architecture.
 
file  options.c
 ⚙️ Main entry point for unified options parsing with mode detection
 
file  actions.c
 Action option callbacks for ascii-chat.
 
file  parsers.c
 Custom option parsers implementation.
 
file  audio.c
 Audio processing options.
 
file  configuration.c
 Configuration file options.
 
file  core.c
 Internal helper functions for registry implementation.
 
file  database.c
 Discovery service database options.
 
file  debug.c
 Debug options registry (backtrace, sync-state)
 
file  display.c
 Display layout and rendering options.
 
file  general.c
 General options.
 
file  logging.c
 Logging category options.
 
file  media.c
 Media file and stream options.
 
 
file  mode_defaults.c
 Mode-aware default value getters for options.
 
file  network.c
 Network protocol options.
 
file  public_api.c
 Public API functions for the options registry.
 
file  registry.c
 Master registry composition - combines all category arrays.
 
file  security.c
 Security and authentication options.
 
file  terminal.c
 Terminal display options.
 
file  webcam.c
 Webcam capture options.
 
file  strings.c
 Centralized enum and mode string conversion - single source of truth.
 
file  actions.h
 Action option callbacks for ascii-chat.
 
file  builder.h
 Options builder API for flexible command-line option configuration.
 
file  internal.h
 Internal declarations for builder implementation.
 
file  common.h
 Common utilities and helpers for option parsing across all modes.
 
file  bash.h
 Bash shell completion generator.
 
file  completions.h
 Auto-generated shell completions from options registry.
 
file  fish.h
 Fish shell completion generator.
 
file  powershell.h
 PowerShell completion generator.
 
file  zsh.h
 Zsh shell completion generator.
 
file  enums.h
 Option enum value definitions - single source of truth.
 
file  explicit.h
 Check if an option was explicitly set via command-line.
 
file  groups.h
 Option group definitions for composable mode presets.
 
file  levenshtein.h
 Levenshtein distance algorithm for fuzzy string matching.
 
file  manpage.h
 Man page template generation from options builder.
 
file  options.h
 ⚙️ Unified options parsing system for ascii-chat with builder pattern and lock-free access
 
file  parsers.h
 Custom option parsers for enum types.
 
file  presets.h
 Preset option configurations for ascii-chat modes.
 
file  registry.h
 Central registry of all command-line options with mode applicability.
 
file  categories.h
 Extern declarations for all category entry arrays.
 
file  common.h
 Shared structures and macros for registry implementation.
 
file  core.h
 Internal helper functions and data for registry implementation.
 
 
file  mode_defaults.h
 Mode-aware default value getters and mode metadata.
 
file  schema.h
 Schema metadata for config file options.
 
file  strings.h
 Centralized enum and mode string conversion API.
 
file  validation.h
 Validation functions for options parsing.
 

Data Structures

struct  options_state
 Consolidated options structure. More...
 

Macros

#define MODE_SERVER_GROUPS
 Mode group presets.
 
#define LEVENSHTEIN_SUGGESTION_THRESHOLD   4
 Maximum edit distance to suggest an option.
 
#define COLOR_MODE_AUTO   TERM_COLOR_AUTO
 Backward compatibility aliases for color mode enum values.
 
#define COLOR_MODE_NONE   TERM_COLOR_NONE
 Monochrome mode.
 
#define COLOR_MODE_16   TERM_COLOR_16
 16-color mode (alias)
 
#define COLOR_MODE_16_COLOR   TERM_COLOR_16
 16-color mode (full name)
 
#define COLOR_MODE_256   TERM_COLOR_256
 256-color mode (alias)
 
#define COLOR_MODE_256_COLOR   TERM_COLOR_256
 256-color mode (full name)
 
#define COLOR_MODE_TRUECOLOR   TERM_COLOR_TRUECOLOR
 24-bit truecolor mode
 
#define OPT_WEBRTC_DEFAULT   true
 Default WebRTC mode flag (true = P2P WebRTC, false = direct TCP)
 
#define OPT_PREFER_WEBRTC_DEFAULT   false
 Default prefer WebRTC flag (false = try direct TCP first)
 
#define OPT_NO_WEBRTC_DEFAULT   false
 Default no WebRTC flag (false = WebRTC enabled)
 
#define OPT_WEBRTC_SKIP_STUN_DEFAULT   false
 Default WebRTC skip STUN flag (false = use STUN)
 
#define OPT_WEBRTC_DISABLE_TURN_DEFAULT   false
 Default WebRTC disable TURN flag (false = use TURN)
 
#define OPT_WEBRTC_SKIP_HOST_DEFAULT   false
 Default WebRTC skip host candidates flag (false = use host candidates)
 
#define OPT_WEBRTC_ICE_TIMEOUT_MS_DEFAULT   10000
 Default WebRTC ICE gathering timeout in milliseconds (10 seconds)
 
#define OPT_WEBRTC_RECONNECT_ATTEMPTS_DEFAULT   3
 Default WebRTC reconnection attempts (3 = try initial + 3 retries)
 
#define OPT_TURN_USERNAME_DEFAULT   ""
 Default TURN username (empty = use ACDS credentials)
 
#define OPT_TURN_CREDENTIAL_DEFAULT   ""
 Default TURN credential (empty = use ACDS credentials)
 
#define OPT_AUDIO_ENABLED_DEFAULT   true
 Default audio enabled flag (true = audio enabled by default)
 
#define OPT_AUDIO_SOURCE_DEFAULT   AUDIO_SOURCE_ALL
 Visualize all available sources by default.
 
#define OPT_AUDIO_CAPTURE_SOURCE_DEFAULT   AUDIO_CAPTURE_SOURCE_AUTO
 Preserve automatic local audio capture selection by default.
 
#define OPT_MICROPHONE_INDEX_DEFAULT   (-1)
 Default microphone device index (-1 means system default)
 
#define OPT_SPEAKERS_INDEX_DEFAULT   (-1)
 Default speakers device index (-1 means system default)
 
#define OPT_MICROPHONE_SENSITIVITY_DEFAULT   1.0
 Default microphone sensitivity (1.0 = normal volume)
 
#define OPT_SPEAKERS_VOLUME_DEFAULT   1.0
 Default speakers volume (1.0 = normal volume)
 
#define OPT_AUDIO_ANALYSIS_ENABLED_DEFAULT   false
 Default audio analysis enabled flag.
 
#define OPT_AUDIO_NO_PLAYBACK_DEFAULT   false
 Default audio playback flag (false = enable playback)
 
#define OPT_ENCODE_AUDIO_DEFAULT   true
 Default audio encoding state (true = Opus encoding enabled)
 
#define OPT_ENCRYPT_ENABLED_DEFAULT   true
 Default encrypt enabled flag (true = encryption required)
 
#define OPT_NO_ENCRYPT_DEFAULT   false
 Default no encrypt flag (false = allow encryption)
 
#define OPT_NO_AUTH_DEFAULT   false
 Default no auth flag (false = allow authentication)
 
#define OPT_REQUIRE_SERVER_VERIFY_DEFAULT   false
 Default require server verify flag (false = not required)
 
#define OPT_REQUIRE_CLIENT_VERIFY_DEFAULT   false
 Default require client verify flag (false = not required)
 
#define OPT_STRING_EMPTY_DEFAULT   ""
 Default empty string for string option fields that have no configured value.
 
#define OPT_RENDER_FILE_DEFAULT   ""
 
#define OPT_RENDER_THEME_DEFAULT   0
 
#define OPT_RENDER_FONT_DEFAULT   ""
 
#define OPT_RENDER_FONT_SIZE_DEFAULT   12.0
 
#define OPTION_MODE_SERVER_LIKE   (OPTION_MODE_SERVER | OPTION_MODE_DISCOVERY_SVC)
 Mode group macros for examples.
 
#define OPTION_MODE_CLIENT_LIKE   (OPTION_MODE_CLIENT | OPTION_MODE_MIRROR | OPTION_MODE_DISCOVERY)
 
#define OPTION_MODE_NETWORKED    (OPTION_MODE_SERVER | OPTION_MODE_CLIENT | OPTION_MODE_DISCOVERY_SVC | OPTION_MODE_DISCOVERY)
 
#define GET_OPTION(field)
 Safely get a specific option field (lock-free read)
 

Typedefs

typedef struct options_state options_t
 Consolidated options structure.
 

Enumerations

enum  option_group_t {
  OPT_GROUP_NONE = 0 , OPT_GROUP_BINARY = (1 << 0) , OPT_GROUP_TERMINAL = (1 << 1) , OPT_GROUP_NETWORK = (1 << 2) ,
  OPT_GROUP_WEBCAM = (1 << 3) , OPT_GROUP_DISPLAY = (1 << 4) , OPT_GROUP_AUDIO = (1 << 5) , OPT_GROUP_SNAPSHOT = (1 << 6) ,
  OPT_GROUP_CRYPTO = (1 << 7) , OPT_GROUP_COMPRESSION = (1 << 8) , OPT_GROUP_ACDS = (1 << 9) , OPT_GROUP_MEDIA = (1 << 10) ,
  OPT_GROUP_SERVER = (1 << 11) , OPT_GROUP_CLIENT = (1 << 12) , OPT_GROUP_WEBRTC = (1 << 13)
}
 Option group identifiers. More...
 
enum  color_setting_t { COLOR_SETTING_AUTO = 0 , COLOR_SETTING_TRUE = 1 , COLOR_SETTING_FALSE = -1 }
 Color output setting (–color flag values) More...
 
enum  utf8_setting_t { UTF8_SETTING_AUTO = 0 , UTF8_SETTING_TRUE = 1 , UTF8_SETTING_FALSE = -1 }
 UTF-8 support setting (–utf8 flag values) More...
 
enum  audio_source_t {
  AUDIO_SOURCE_ALL = 0 , AUDIO_SOURCE_CALL = 1 , AUDIO_SOURCE_MIC = 2 , AUDIO_SOURCE_MEDIA = 3 ,
  AUDIO_SOURCE_AUTO = AUDIO_SOURCE_ALL
}
 Audio source selected for visualization. More...
 
enum  audio_capture_source_t {
  AUDIO_CAPTURE_SOURCE_AUTO = 0 , AUDIO_CAPTURE_SOURCE_MIC = 1 , AUDIO_CAPTURE_SOURCE_MEDIA = 2 , AUDIO_CAPTURE_SOURCE_BOTH = 3 ,
  AUDIO_CAPTURE_SOURCE_REMOTE = 4
}
 
enum  asciichat_mode_t {
  MODE_SERVER , MODE_CLIENT , MODE_MIRROR , MODE_DISCOVERY_SERVICE ,
  MODE_DISCOVERY , MODE_INVALID
}
 Mode type for options parsing. More...
 
enum  option_mode_bitmask_t {
  OPTION_MODE_NONE = 0 , OPTION_MODE_SERVER = (1 << MODE_SERVER) , OPTION_MODE_CLIENT = (1 << MODE_CLIENT) , OPTION_MODE_MIRROR = (1 << MODE_MIRROR) ,
  OPTION_MODE_DISCOVERY_SVC = (1 << MODE_DISCOVERY_SERVICE) , OPTION_MODE_DISCOVERY = (1 << MODE_DISCOVERY) , OPTION_MODE_BINARY = 0x100 , OPTION_MODE_ALL = 0x1F | 0x100
}
 Option mode bitmask. More...
 

Functions

size_t levenshtein (const char *a, const char *b)
 Calculate Levenshtein distance between two strings.
 
size_t levenshtein_n (const char *a, const size_t length, const char *b, const size_t bLength)
 Calculate Levenshtein distance with explicit string lengths.
 
const char * levenshtein_find_similar (const char *unknown, const char *const *candidates)
 Find the most similar string from a NULL-terminated array.
 
asciichat_error_t options_config_generate_manpage_template (const options_config_t *config, const char *program_name, const char *mode_name, const char *output_path, const char *brief_description)
 Generate a man page template from options builder configuration.
 
asciichat_error_t options_builder_generate_manpage_template (options_builder_t *builder, const char *program_name, const char *mode_name, const char *output_path, const char *brief_description)
 Generate man page template from builder (before build())
 
asciichat_error_t options_config_generate_manpage_merged (const options_config_t *config, const char *program_name, const char *mode_name, const char *output_path, const char *brief_description)
 Generate merged man page template preserving manual content.
 
parsed_section_t * parse_manpage_sections (const char *filepath, size_t *num_sections)
 Parse existing man page template to extract sections.
 
void free_parsed_sections (parsed_section_t *sections, size_t num_sections)
 Free parsed sections array.
 
const parsed_section_t * find_section (const parsed_section_t *sections, size_t num_sections, const char *section_name)
 Find section by name.
 
asciichat_error_t options_config_generate_final_manpage (const char *template_path, const char *output_path, const char *version_string, const char *content_file_path)
 Generate final man page (.1) from template (.1.in) with version substitution and optional content file.
 
const char * escape_groff_special (const char *str)
 Escape special groff/troff characters in text.
 
const char * format_mode_names (option_mode_bitmask_t bitmask)
 Format option mode names as human-readable string.
 
const options_t * options_get (void)
 Get current options (lock-free read)
 
asciichat_error_t options_set_int (const char *field_name, int value)
 Check if an option was explicitly set via command-line.
 
asciichat_error_t options_set_bool (const char *field_name, bool value)
 
asciichat_error_t options_set_string (const char *field_name, const char *value)
 
asciichat_error_t options_set_double (const char *field_name, double value)
 
const char * options_get_help_text (asciichat_mode_t mode, const char *option_name)
 Get help text for an option in a specific mode.
 
bool has_action_flag (void)
 Check if an action flag was detected.
 

Variables

const float weight_red
 Red weight for luminance calculation.
 
const float weight_green
 Green weight for luminance calculation.
 
const float weight_blue
 Blue weight for luminance calculation.
 
unsigned short int RED []
 Red channel lookup table.
 
unsigned short int GREEN []
 Green channel lookup table.
 
unsigned short int BLUE []
 Blue channel lookup table.
 
unsigned short int GRAY []
 Grayscale lookup table.
 

Utility Functions

int strtoint_safe (const char *str)
 Safely parse string to integer with validation.
 

Option Parsing Functions

options_t options_t_new (void)
 Initialize options by parsing command-line arguments.
 
asciichat_error_t options_init (int argc, char **argv)
 Initialize options by parsing command-line arguments with unified mode detection.
 
void usage (FILE *stream, asciichat_mode_t mode)
 Print usage information for a specific mode.
 

Dimension Update Functions

void update_dimensions_to_terminal_size (options_t *opts)
 Update dimensions to match current terminal size.
 
void update_dimensions_for_full_height (options_t *opts)
 Update dimensions to use full terminal height while maintaining aspect ratio.
 

Validation Functions

int validate_opt_port (const char *value_str, char *error_msg, size_t error_msg_size)
 Validate port number (1-65535)
 
int validate_opt_positive_int (const char *value_str, char *error_msg, size_t error_msg_size)
 Validate positive integer.
 
int validate_opt_non_negative_int (const char *value_str, char *error_msg, size_t error_msg_size)
 Validate non-negative integer.
 
int validate_opt_color_mode (const char *value_str, char *error_msg, size_t error_msg_size)
 Validate color mode string.
 
int validate_opt_render_mode (const char *value_str, char *error_msg, size_t error_msg_size)
 Validate render mode string.
 
int validate_opt_palette (const char *value_str, char *error_msg, size_t error_msg_size)
 Validate palette type string.
 
int validate_opt_log_level (const char *value_str, char *error_msg, size_t error_msg_size)
 Validate log level string.
 
int validate_opt_ip_address (const char *value_str, char *parsed_address, size_t address_size, bool is_client, char *error_msg, size_t error_msg_size)
 Validate IP address or hostname.
 
float validate_opt_float_non_negative (const char *value_str, char *error_msg, size_t error_msg_size)
 Validate non-negative float value.
 
float validate_opt_volume (const char *value_str, char *error_msg, size_t error_msg_size)
 Validate volume value (0.0-1.0)
 
int validate_opt_fps (const char *value_str, char *error_msg, size_t error_msg_size)
 Validate FPS value (1-144)
 
int validate_opt_max_clients (const char *value_str, char *error_msg, size_t error_msg_size)
 Validate max clients value (1-32)
 
int validate_opt_compression_level (const char *value_str, char *error_msg, size_t error_msg_size)
 Validate compression level (1-9)
 
int validate_opt_reconnect (const char *value_str, char *error_msg, size_t error_msg_size)
 Validate reconnect value.
 
int validate_opt_device_index (const char *value_str, char *error_msg, size_t error_msg_size)
 Validate device index (-1 for default, 0+ for specific device)
 
int validate_opt_password (const char *value_str, char *error_msg, size_t error_msg_size)
 Validate password (8-256 characters)
 
int options_collect_identity_keys (options_t *opts, int argc, char *argv[])
 Collect multiple –key flags into identity_keys array.
 
bool is_remote_key_path (const char *key_path)
 Check if a key path is a remote/virtual key (not a local file)
 

Configuration Constants

#define OPTIONS_BUFF_SIZE   256
 Buffer size for option string values.
 
#define MAX_IDENTITY_KEYS   32
 Maximum number of identity keys that can be loaded (for multi-key support)
 
#define OPT_HELP_DEFAULT   false
 Default help flag (false = don't show help)
 
#define OPT_VERSION_DEFAULT   false
 Default version flag (false = don't show version)
 
#define OPT_SPLASH_DEFAULT   true
 Default splash screen flag (true = show splash, false = hide splash)
 
#define OPT_SPLASH_SCREEN_EXPLICITLY_SET_DEFAULT   false
 Default splash screen explicitly set flag (false = use default splash setting)
 
#define OPT_STATUS_SCREEN_DEFAULT   true
 Default status screen flag (true = show status, false = hide status)
 
#define OPT_STATUS_SCREEN_EXPLICITLY_SET_DEFAULT   false
 Default status screen explicitly set flag (false = use default status screen setting)
 
#define OPT_ENABLE_KEEPAWAKE_DEFAULT   false
 Default keep-awake (prevent system sleep) flag (false = normal system sleep)
 
#define OPT_DISABLE_KEEPAWAKE_DEFAULT   false
 Default disable keep-awake flag (false = allow system sleep prevention)
 
#define OPT_NO_CHECK_UPDATE_DEFAULT   false
 Default no-check-update flag (false = check for updates enabled)
 
#define OPT_QUIET_DEFAULT   false
 Default quiet mode flag (false = logging enabled)
 
#define OPT_VERBOSE_LEVEL_DEFAULT   0
 Default verbose level (0 = not verbose)
 
#define OPT_LOG_LEVEL_DEFAULT   LOG_INFO
 Default log level (LOG_INFO)
 
#define OPT_GREP_PATTERN_DEFAULT   ""
 Default grep pattern (empty = no filtering)
 
#define OPT_JSON_DEFAULT   false
 Default JSON logging flag (false = text output)
 
#define OPT_COLOR_SCHEME_NAME_DEFAULT   "pastel"
 Default color scheme name (pastel)
 
#define OPT_LOG_TEMPLATE_DEFAULT_RELEASE
 Default log format string - release mode (simple format with timestamp, level, and message)
 
#define OPT_LOG_TEMPLATE_DEFAULT_DEBUG
 Default log format string - debug mode (verbose with thread name, file relative path, line, and function)
 
#define OPT_LOG_TEMPLATE_DEFAULT   OPT_LOG_TEMPLATE_DEFAULT_DEBUG
 Default log template string (selected based on build mode)
 
#define OPT_LOG_FORMAT_OUTPUT_DEFAULT   LOG_OUTPUT_TEXT
 Default log format output type (TEXT = human-readable, JSON = structured)
 
#define OPT_LOG_FORMAT_CONSOLE_DEFAULT   false
 Default log format console only flag (false = use default format everywhere)
 
#define OPT_WIDTH_DEFAULT   110
 Default terminal width in characters.
 
#define OPT_HEIGHT_DEFAULT   70
 Default terminal height in characters.
 
#define OPT_AUTO_WIDTH_DEFAULT   true
 Default auto-detect width flag (true = auto-detect from terminal)
 
#define OPT_AUTO_HEIGHT_DEFAULT   true
 Default auto-detect height flag (true = auto-detect from terminal)
 
#define OPT_COLOR_MODE_DEFAULT   COLOR_MODE_AUTO
 Default color mode (auto-detect terminal capabilities)
 
#define OPT_LIST_WEBCAMS_DEFAULT   false
 Default list webcams flag (false = don't list and exit)
 
#define OPT_LIST_MICROPHONES_DEFAULT   false
 Default list microphones flag (false = don't list and exit)
 
#define OPT_LIST_SPEAKERS_DEFAULT   false
 Default list speakers flag (false = don't list and exit)
 
#define OPT_SHOW_CAPABILITIES_DEFAULT   false
 Default show terminal capabilities flag.
 
#define OPT_FORCE_UTF8_DEFAULT   UTF8_SETTING_AUTO
 Default force UTF-8 support setting (auto-detect)
 
#define OPT_STRIP_ANSI_DEFAULT   false
 Default strip ANSI escape sequences flag.
 
#define OPT_COLOR_DEFAULT   COLOR_SETTING_AUTO
 Default color setting (COLOR_SETTING_AUTO = smart detection)
 
#define OPT_COLOR_FILTER_DEFAULT   COLOR_FILTER_NONE
 Default color filter (none - no filtering)
 
#define OPT_RENDER_MODE_DEFAULT   RENDER_MODE_FOREGROUND
 Default render mode (foreground characters only)
 
#define OPT_PALETTE_TYPE_DEFAULT   PALETTE_STANDARD
 Default palette type (standard ASCII art)
 
#define OPT_PALETTE_CUSTOM_SET_DEFAULT   false
 Default custom palette set flag (false = not set)
 
#define OPT_STRETCH_DEFAULT   false
 Default allow aspect ratio distortion flag.
 
#define OPT_FPS_DEFAULT   60
 Default FPS (frames per second)
 
#define OPT_SNAPSHOT_MODE_DEFAULT   false
 Default snapshot mode flag (false = continuous)
 
#define OPT_SNAPSHOT_DELAY_DEFAULT   3.0f
 Default snapshot delay in seconds.
 
#define OPT_MATRIX_RAIN_DEFAULT   false
 Default Matrix rain effect flag (false = disabled)
 
#define OPT_FPS_COUNTER_DEFAULT   false
 Default FPS counter overlay flag (false = disabled)
 
#define OPT_FLIP_X_DEFAULT   false
 Default horizontal flip state (true = horizontally flipped) macOS webcams default to flipped (mirrored), other platforms default to normal.
 
#define OPT_FLIP_Y_DEFAULT   false
 Default vertical flip state (false = no vertical flip)
 
#define OPT_WEBCAM_INDEX_DEFAULT   0
 Default webcam device index.
 
#define OPT_TEST_PATTERN_DEFAULT   false
 Default test pattern mode (false = use actual webcam)
 
#define OPT_NO_AUDIO_MIXER_DEFAULT   false
 Default no audio mixer flag (false = enable mixer)
 
#define OPT_MEDIA_LOOP_DEFAULT   false
 Default loop media flag (false = play once)
 
#define OPT_MEDIA_FROM_STDIN_DEFAULT   false
 Default media from stdin flag (false = not reading from stdin)
 
#define OPT_MEDIA_SEEK_TIMESTAMP_DEFAULT   0.0
 Default media seek timestamp (start from beginning)
 
#define OPT_PAUSE_DEFAULT   false
 Default pause media flag (false = play immediately)
 
#define OPT_ADDRESS_DEFAULT   "localhost"
 Default server address for client connections.
 
#define OPT_ADDRESS6_DEFAULT   "::1"
 Default IPv6 server address.
 
#define OPT_ENDPOINT_DISCOVERY_SERVICE   "localhost"
 Default discovery service endpoint (what to connect to by default)
 
#define OPT_PORT_DEFAULT   "27224"
 Default TCP port for client/server communication (string)
 
#define OPT_PORT_INT_DEFAULT   27224
 Default TCP port for client/server communication (integer)
 
#define OPT_WEBSOCKET_PORT_SERVER_DEFAULT   27226
 Default WebSocket port for server mode (integer)
 
#define OPT_WEBSOCKET_PORT_ACDS_DEFAULT   27227
 Default WebSocket port for discovery-service mode (integer)
 
#define OPT_MAX_CLIENTS_DEFAULT   9
 Default maximum concurrent clients (server only)
 
#define OPT_RECONNECT_ATTEMPTS_DEFAULT   (-1)
 Default reconnect attempts (-1 means auto/infinite)
 
#define OPT_COMPRESSION_LEVEL_DEFAULT   3
 Default compression level (1-9)
 
#define OPT_NO_COMPRESS_DEFAULT   false
 Default no compression flag (false = enable compression)
 
#define OPT_ACDS_DEFAULT   false
 Default ACDS registration flag (false = disabled)
 
#define OPT_ACDS_PORT_INT_DEFAULT   27225
 Default ACDS discovery service port (integer)
 
#define OPT_ACDS_PORT_DEFAULT   "27225"
 Default ACDS discovery service port (string)
 
#define OPT_ACDS_EXPOSE_IP_DEFAULT   false
 Default ACDS expose IP flag (false = private by default)
 
#define OPT_ACDS_INSECURE_DEFAULT   false
 Default ACDS insecure mode flag (false = verify server)
 
#define OPT_REQUIRE_SERVER_IDENTITY_DEFAULT   false
 Default require-server-identity setting for ACDS.
 
#define OPT_REQUIRE_CLIENT_IDENTITY_DEFAULT   false
 Default require-client-identity setting for ACDS.
 
#define OPT_LAN_DISCOVERY_DEFAULT   false
 Default LAN discovery flag (false = discovery disabled)
 
#define OPT_NO_MDNS_ADVERTISE_DEFAULT   false
 Default no mDNS advertise flag (false = advertise enabled)
 
#define OPT_ENABLE_UPNP_DEFAULT   false
 Default enable UPnP flag (false = UPnP disabled)
 

Network Endpoint Defaults (WebRTC)

Centralized definitions for STUN/TURN servers

#define OPT_ENDPOINT_STUN_PRIMARY   "stun:stun.ascii-chat.com:3478"
 Primary STUN server (ascii-chat hosted)
 
#define OPT_ENDPOINT_STUN_FALLBACK   "stun:stun.l.google.com:19302"
 Fallback STUN server (Google public STUN)
 
#define OPT_ENDPOINT_STUN_SERVERS_DEFAULT   OPT_ENDPOINT_STUN_PRIMARY "," OPT_ENDPOINT_STUN_FALLBACK
 Default STUN servers (comma-separated list)
 
#define OPT_ENDPOINT_TURN_PRIMARY   "turn:turn.ascii-chat.com:3478"
 Primary TURN server (ascii-chat hosted)
 
#define OPT_ENDPOINT_TURN_SERVERS_DEFAULT   OPT_ENDPOINT_TURN_PRIMARY
 Default TURN servers (comma-separated list)
 
#define OPT_STUN_SERVER_HOST_PRIMARY   "stun.ascii-chat.com"
 STUN server hostname only (without protocol/port)
 
#define OPT_STUN_SERVER_PORT_PRIMARY   3478
 STUN server port for primary server.
 
#define OPT_STUN_SERVER_HOST_FALLBACK   "stun.l.google.com"
 Fallback STUN server hostname only.
 
#define OPT_STUN_SERVER_PORT_FALLBACK   19302
 Fallback STUN server port.
 
#define OPT_TURN_SERVER_HOST   "turn.ascii-chat.com"
 TURN server hostname only.
 
#define OPT_TURN_SERVER_PORT   3478
 TURN server port.
 
#define OPT_STUN_SERVERS_DEFAULT   OPT_ENDPOINT_STUN_SERVERS_DEFAULT
 Default STUN server URLs (comma-separated)
 
#define OPT_TURN_SERVERS_DEFAULT   OPT_ENDPOINT_TURN_SERVERS_DEFAULT
 Default TURN server URLs (comma-separated)
 

Detailed Description

⚙️ Command-line option parsing with unified mode detection, builder API, and RCU-based thread-safe access

MIT licensed. Copyright (c) 2015 Titus Wormer titus.nosp@m.worm.nosp@m.er@gm.nosp@m.ail..nosp@m.com From: https://github.com/wooorm/levenshtein.c

This module provides comprehensive command-line argument parsing, configuration management, and unified options state for ascii-chat with support for multiple modes (server, client, mirror, discovery service). The system unifies several layers:

Architecture Overview:

The options system is built in layers from bottom to top:

  1. Option Descriptors (registry.c): Single-source-of-truth definitions of all options with metadata (long name, short name, mode bitmask, defaults, validators, etc.)
  2. Builder Pattern (builder.h): Flexible API for programmatically constructing option configurations. Supports mode-specific options, dependencies, and custom validators.
  3. Presets (presets.h): Pre-built configurations for common modes (unified, server, client, mirror, discovery service). Use options_preset_unified() for the standard multi-mode setup.
  4. Unified Parsing (options.c): Single entry point options_init() that:
    • Detects mode from command-line arguments
    • Parses binary-level options (–help, –version, –log-file)
    • Parses mode-specific options
    • Validates and applies defaults
    • Publishes options via RCU for lock-free access
  5. RCU Thread-Safety (rcu.h): Lock-free read access to options after initialization:
    • Use GET_OPTION(field) macro to safely read options from any thread
    • Use options_get() to get pointer to full options_t struct
    • Use options_set_*() functions for thread-safe updates

Design Philosophy:

Usage Pattern:

#include "options.h"
int main(int argc, char **argv) {
// Initialize options (detects mode, parses args, validates)
asciichat_error_t err = options_init(argc, argv);
if (err != ASCIICHAT_OK) {
// Could be ERROR_USAGE (invalid options) or others
return 1;
}
// Lock-free read access to options (works from any thread)
const char *addr = GET_OPTION(address);
int port = GET_OPTION(port);
bool audio = GET_OPTION(audio_enabled);
asciichat_mode_t mode = GET_OPTION(detected_mode);
// Or get full options_t pointer for multi-field access
const options_t *opts = options_get();
printf("Mode: %d, Dimensions: %dx%d\n",
opts->detected_mode, opts->width, opts->height);
// Thread-safe updates (if needed at runtime)
options_set_int("width", 120);
options_set_bool("audio_enabled", true);
return 0;
}
asciichat_error_t
Error and exit codes - unified status values (0-255)
Definition error_codes.h:49
@ ASCIICHAT_OK
Definition error_codes.h:51
#define GET_OPTION(field)
Safely get a specific option field (lock-free read)
asciichat_mode_t
Mode type for options parsing.
asciichat_error_t options_set_int(const char *field_name, int value)
Check if an option was explicitly set via command-line.
Definition rcu.c:761
const options_t * options_get(void)
Get current options (lock-free read)
Definition rcu.c:496
asciichat_error_t options_init(int argc, char **argv)
Initialize options by parsing command-line arguments with unified mode detection.
asciichat_error_t options_set_bool(const char *field_name, bool value)
Definition rcu.c:969
int main(int argc, char *argv[])
Definition main.c:472
Consolidated options structure.
asciichat_mode_t detected_mode
Mode detected from command-line arguments.
int height
Terminal height in characters (int for OPTION_TYPE_INT compat)
int width
Terminal width in characters (int for OPTION_TYPE_INT compat)

Option Lifecycle:

  1. Definition: Options defined in registry.c with metadata and mode bitmasks
  2. Builder Creation: Use options_preset_unified() to create builder with presets
  3. Initialization: Call options_init(argc, argv) once at program startup
    • Mode detection from argv
    • Binary-level option parsing
    • Mode-specific parsing
    • Validation and defaults
    • RCU publishing
  4. Access: Lock-free reads via GET_OPTION() from any thread
  5. Updates (optional): Thread-safe runtime updates via options_set_*()

Mode-Specific Behavior:

Options include a mode_bitmask that indicates which modes they apply to:

Thread Safety:

Builder API for Custom Configurations:

If you need a custom option set (not the standard unified preset), use the builder API:

// Create empty builder
// Add options from registry (filtered by mode)
// Or add custom options
.long_name = "my-option",
.short_name = 'm',
.offset = offsetof(options_t, my_field),
.mode_bitmask = OPTION_MODE_CLIENT,
// ... other fields ...
};
// Build immutable config
// Parse command line with custom config
options_config_parse_args(config, argc, argv, &my_opts);
// Publish to RCU for lock-free access
void options_builder_add_descriptor(options_builder_t *builder, const option_descriptor_t *descriptor)
Add full option descriptor (advanced)
Definition builder.c:973
options_config_t * options_builder_build(options_builder_t *builder)
Build immutable options config.
Definition builder.c:482
options_builder_t * options_builder_create(size_t struct_size)
Create empty options builder.
Definition builder.c:378
@ OPTION_TYPE_STRING
String value (–name foo)
Definition builder.h:164
options_t options_t_new(void)
Initialize options by parsing command-line arguments.
@ OPTION_MODE_CLIENT
Client mode (bit 1)
asciichat_error_t options_registry_add_all_to_builder(options_builder_t *builder)
Add all options from registry to builder.
Definition public_api.c:20
asciichat_error_t options_state_init(void)
Initialize RCU options system.
Definition rcu.c:370
asciichat_error_t options_state_set(const options_t *opts)
Set options from a parsed options struct.
Definition rcu.c:439
Option descriptor.
Definition builder.h:241
const char * long_name
Long option name (e.g., "port")
Definition builder.h:243
Options builder.
Definition builder.h:441
Options configuration.
Definition builder.h:401

Command-Line Syntax:

Supported formats:

Option Categories:

Organized by functionality for help display:

Default Values:

All options have sensible OPT_*_DEFAULT values:

Note
All options are stored in the options_t struct, replacing scattered globals
Options are parsed once at startup via options_init()
Most options remain constant after parsing (read-only)
Dynamic updates possible via options_set_*() for specific fields
Access via GET_OPTION() macro is lock-free and thread-safe
See also
options_init() - Main entry point for parsing
options_get() - Get pointer to current Options Module struct
GET_OPTION() - Convenience macro for reading single fields
builder.h - Builder API for custom configurations
registry.h - Central registry of all Options Module
rcu.h - RCU-based thread-safe access
presets.h - Pre-built configurations
Author
Zachary Fogg me@zf.nosp@m.o.gg
Date
January 2026

This header provides validation functions used during command-line option parsing and configuration file loading. These functions validate user input and provide detailed error messages for invalid values.

All validation functions follow a consistent pattern:

Note
These functions are used by both options.c and config.c

Options System Architecture

Overview

The ascii-chat options system is a comprehensive command-line argument parsing and configuration management framework built on three key components:
  1. Registry (registry.h): Central single-source-of-truth for all option definitions
  2. Builder (builder.h): Flexible API for constructing option configurations
  3. RCU Thread-Safety (rcu.h): Lock-free read access to options from any thread
Together, these provide:

  • Mode-aware parsing: Automatically filter options by detected mode
  • Type-safe defaults: All options have sensible defaults (OPT_*_DEFAULT constants)
  • Validation: Required fields, dependencies, cross-field validators, custom validators
  • Automatic memory management: Strings are auto-duplicated and auto-freed
  • Lock-free thread safety: Read options from 60fps render threads without locking
  • Shell completion support: Metadata for enum values, numeric ranges, examples
  • Grouped help output: Organized by category with semantic coloring
  • Environment variable fallbacks: Options can fall back to env vars

Architecture Diagram

┌─────────────────────────────────────────────────────────────────────────┐
│ COMMAND LINE ARGUMENTS │
│ ./ascii-chat server │
└──────────────────────────┬──────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────┐
│ MODE DETECTION (main.c) │
│ Detects: server / client / mirror │
│ Sets: detected_mode in options_t │
└──────────────────┬────────────────────┘
│
┌──────────────────▼──────────────────────────────────┐
│ REGISTRY LOOKUP (registry.h) │
│ ┌─────────────────────────────────────────────┐ │
│ │ options_registry_t global_registry[] = { │ │
│ │ { .long_name = "port", .mode = SERVER, │ │
│ │ .type = INT, .default = 27224, ...}, │ │
│ │ { .long_name = "color", .mode = ALL, │ │
│ │ .type = CALLBACK, ... }, │ │
│ │ ... │ │
│ │ } │ │
│ └─────────────────────────────────────────────┘ │
│ │
│ Lookup Functions: │
│ - find_by_name("port") │
│ - find_by_short('p') │
│ - get_for_mode(MODE_SERVER) → filters by mode │
│ - get_for_display(MODE_SERVER, true) → help- │
│ aware filtering │
└──────────────────┬───────────────────────────────────┘
│
┌──────────────────▼──────────────────────────────────┐
│ BUILDER CONSTRUCTION (builder.h) │
│ │
│ Step 1: Create builder │
│ ┌──────────────────────────────────────────────┐ │
│ │ builder = options_builder_create( │ │
│ │ sizeof(options_t)) │ │
│ └──────────────────────────────────────────────┘ │
│ │
│ Step 2: Populate from registry │
│ ┌──────────────────────────────────────────────┐ │
│ │ builder) // Copies all registered opts │ │
│ └──────────────────────────────────────────────┘ │
│ │
│ Step 3: Build immutable config │
│ ┌──────────────────────────────────────────────┐ │
│ │ config = options_builder_build(builder) │ │
│ │ // Creates options_config_t with all state │ │
│ │ // Both arrays and descriptors locked/locked │ │
│ └──────────────────────────────────────────────┘ │
└──────────────────┬───────────────────────────────────┘
│
┌──────────────────▼──────────────────────────────────┐
│ COMMAND-LINE PARSING (options.c) │
│ │
│ Step 1: Set defaults from descriptors │
│ ┌──────────────────────────────────────────────┐ │
│ │ options_config_set_defaults(config, │ │
│ │ &options_struct) │ │
│ │ // Fills options_t with defaults │ │
│ │ // Checks env vars for missing options │ │
│ └──────────────────────────────────────────────┘ │
│ │
│ Step 2: Parse command-line arguments │
│ ┌──────────────────────────────────────────────┐ │
│ │ options_config_parse(config, argc, argv, │ │
│ │ &options_struct, MODE_DETECTED, ...) │ │
│ │ // Uses mode_bitmask to filter options │ │
│ │ // Applies custom parsers (OPTION_CALLBACK) │ │
│ │ // Auto-duplicates strings │ │
│ └──────────────────────────────────────────────┘ │
│ │
│ Step 3: Validate parsed options │
│ ┌──────────────────────────────────────────────┐ │
│ │ options_config_validate(config, │ │
│ │ &options_struct, &error_msg) │ │
│ │ // Check required fields set │ │
│ │ // Validate dependencies (REQUIRES, │ │
│ │ // CONFLICTS, IMPLIES) │ │
│ │ // Call custom validators │ │
│ └──────────────────────────────────────────────┘ │
│ │
│ Step 4: Positional argument parsing │
│ ┌──────────────────────────────────────────────┐ │
│ │ options_config_parse_positional(config, │ │
│ │ remaining_argc, remaining_argv, &opts) │ │
│ │ // Parses non-option args (client address, │ │
│ │ // session string, bind address, etc.) │ │
│ └──────────────────────────────────────────────┘ │
└──────────────────┬───────────────────────────────────┘
│
┌──────────────────▼──────────────────────────────────┐
│ RCU THREAD-SAFE PUBLISHING (rcu.h) │
│ │
│ Step 1: Initialize RCU system │
│ ┌──────────────────────────────────────────────┐ │
│ │ options_state_init() │ │
│ │ // Allocates atomic pointer structure │ │
│ └──────────────────────────────────────────────┘ │
│ │
│ Step 2: Publish parsed options │
│ ┌──────────────────────────────────────────────┐ │
│ │ options_state_set(&options_struct) │ │
│ │ // Atomically swaps pointer to new options │ │
│ │ // OLD: options readers see old version │ │
│ │ // NEW: options readers see new version │ │
│ │ // Defers freeing old struct (grace period) │ │
│ └──────────────────────────────────────────────┘ │
│ │
│ Step 3: Spawn worker threads (safe to read) │
│ ┌──────────────────────────────────────────────┐ │
│ │ thread_pool_init() │ │
│ │ // Video render (60fps) │ │
│ │ // Audio playback (172fps) │ │
│ │ // Network packet handling │ │
│ └──────────────────────────────────────────────┘ │
└──────────────────┬───────────────────────────────────┘
│
┌──────────────────▼──────────────────────────────────┐
│ LOCK-FREE READING (60fps video thread) │
│ │
│ ┌──────────────────────────────────────────────┐ │
│ │ // Render thread (no locks, no blocking) │ │
│ │ const options_t *opts = options_get(); │ │
│ │ int width = opts->width; │ │
│ │ int height = opts->height; │ │
│ │ color_mode_t color = opts->color_mode; │ │
│ │ char *address = opts->server_address; │ │
│ │ │ │
│ │ // Or use convenience macro: │ │
│ │ const options_t *opts = GET_OPTION(width); │ │
│ │ │ │
│ │ // No contention, no cache line bouncing │ │
│ │ // Single atomic load (~1-2ns latency) │ │
│ │ // Consistent snapshot (no partial updates) │ │
│ └──────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────┘
asciichat_error_t options_config_parse(const options_config_t *config, int argc, char **argv, void *options_struct, option_mode_bitmask_t detected_mode, int *remaining_argc, char ***remaining_argv)
Parse command-line arguments.
Definition builder.c:1815
asciichat_error_t options_config_validate(const options_config_t *config, const void *options_struct, char **error_message)
Validate options struct.
Definition builder.c:1840
asciichat_error_t options_config_parse_positional(const options_config_t *config, int remaining_argc, char **remaining_argv, void *options_struct)
Parse positional arguments.
Definition builder.c:1331
asciichat_error_t options_config_set_defaults(const options_config_t *config, void *options_struct)
Set default values in options struct.
Definition builder.c:1400
@ MODE_SERVER
Server mode - network server options.
#define true
Definition stdbool.h:62

Component Details

1. Registry (registry.h)

Purpose: Single source of truth for all option definitionsKey Data Structure:

typedef struct {
const char *long_name; // "port"
char short_name; // 'p'
option_type_t type; // INT, STRING, BOOL, CALLBACK, ACTION
size_t offset; // offsetof(options_t, port)
const void *default_value; // &(int){27224}
bool required; // true/false
const char *env_var_name; // "PORT"
bool (*validate)(...); // Custom validator
option_mode_bitmask_t mode_bitmask; // SERVER | CLIENT | MIRROR
option_metadata_t metadata; // Enums, ranges, examples
// ... more fields
option_type_t
Option value types.
Definition builder.h:161
option_mode_bitmask_t
Option mode bitmask.
#define bool
Definition stdbool.h:61
Metadata for shell completion generation.
Definition builder.h:192
Registry Global Array (registry.c):

static const option_descriptor_t options_registry[] = {
{
.long_name = "port",
.short_name = 'p',
.type = OPTION_TYPE_INT,
.offset = offsetof(options_t, port),
.default_value = &(int){27224},
.help_text = "Server port",
.group = "NETWORK OPTIONS",
.mode_bitmask = OPTION_MODE_SERVER,
.validate = validate_port,
.metadata = {
.input_type = OPTION_INPUT_NUMERIC,
.numeric_range = {.min = 1, .max = 65535, .step = 1},
.examples = (const char *[]){
"27224", "8080", "3000", NULL
}
}
},
// ... more options ...
};
@ OPTION_INPUT_NUMERIC
Numeric value with optional min/max/step.
Definition builder.h:179
@ OPTION_TYPE_INT
Integer value (–count 42)
Definition builder.h:163
@ OPTION_MODE_SERVER
Server mode (bit 0)
Key Functions:

  • options_registry_find_by_name("port") → Returns option descriptor
  • options_registry_get_for_mode(MODE_SERVER, &count) → Filters by mode
  • options_registry_add_all_to_builder(builder) → Populates builder
  • options_registry_get_metadata("port") → Gets completion metadata
Advantages:

  • No duplication (each option defined once)
  • Type-safe defaults (constants, not magic numbers)
  • Consistent across all modes
  • Easy to add new options
  • Facilitates shell completions

2. Builder (builder.h)

Purpose: Flexible API for constructing option configurationsKey Data Structure:

typedef struct {
option_descriptor_t *descriptors; // Dynamic array
size_t num_descriptors;
size_t descriptor_capacity;
option_dependency_t *dependencies; // Dynamic array
size_t num_dependencies;
size_t dependency_capacity;
// ... more arrays for positional args, examples, modes, sections
Option dependency.
Definition builder.h:331
Builder Workflow:

// 1. Create empty builder
// 2. Populate from registry OR add custom options
// 3. Add dependencies (optional)
builder, "tls-cert", "tls-enabled", NULL);
// 4. Add positional arguments (optional)
options_builder_add_positional(builder, "address", ...);
// 5. Add examples (optional, for help)
"Run as server");
// 6. Build immutable config
// 7. Free builder (config is independent)
void options_builder_add_example(options_builder_t *builder, uint32_t mode_bitmask, const char *args, const char *description, bool owns_args)
Add example descriptor.
Definition builder.c:1255
void options_builder_add_dependency_requires(options_builder_t *builder, const char *option_name, const char *depends_on, const char *error_message)
Add dependency: if option_name is set, depends_on must be set.
Definition builder.c:1130
void options_builder_add_positional(options_builder_t *builder, const char *name, const char *help_text, bool required, const char *section_heading, const char **examples, size_t num_examples, option_mode_bitmask_t mode_bitmask, int(*parse_fn)(const char *arg, void *config, char **remaining, int num_remaining, char **error_msg))
Add positional argument descriptor.
Definition builder.c:1192
void options_builder_destroy(options_builder_t *builder)
Free options builder.
Definition builder.c:467
Key Functions:

Building Options: Three ApproachesApproach 1: From Registry (Most Common)

Approach 2: From Preset (Pre-built configurations)

options_config_t *preset = options_preset_unified("ascii-chat", "...");
// Modify if needed
options_builder_t * options_builder_from_preset(const options_config_t *preset)
Create builder from preset config.
Definition builder.c:432
options_config_t * options_preset_unified(const char *program_name, const char *description)
Build unified options config with ALL options (binary + all modes)
Definition presets.c:51
Approach 3: Custom from Scratch

options_builder_t *b = options_builder_create(sizeof(my_options_t));
options_builder_add_bool(b, "verbose", 'v', offsetof(...), ...);
options_builder_add_int(b, "port", 'p', offsetof(...), ...);
// ... add more options
void options_builder_add_bool(options_builder_t *builder, const char *long_name, char short_name, size_t offset, bool default_value, const char *help_text, const char *group, bool required, const char *env_var_name)
Add boolean flag option.
Definition builder.c:657
void options_builder_add_int(options_builder_t *builder, const char *long_name, char short_name, size_t offset, int default_value, const char *help_text, const char *group, bool required, const char *env_var_name, bool(*validate)(const void *, char **))
Definition builder.c:681

3. RCU Thread-Safety (rcu.h)

Purpose: Lock-free read access to options from any threadKey Concept: Read-Copy-Update PatternRCU is perfect for ascii-chat because:

  • Read-Heavy: Options accessed constantly (60fps video, 172fps audio)
  • Write-Rare: Options set at startup, rarely changed
  • Lock-Free Reads: Zero contention, no blocking, ~1-2ns latency
How RCU Works:
Global atomic pointer to options_t
┌────────────────────────────────────────────────┐
│ atomic<const options_t*> g_options_ptr │
└────────────────────────────────────────────────┘
│
├─→ [options_t] ← Current (readers see this)
│ {
│ width: 120,
│ height: 60,
│ color_mode: TRUECOLOR,
│ port: 27224,
│ ...
│ }
When updating options:
1. Allocate new options_t struct
2. Copy current values
3. Apply modifications
4. Atomic swap pointer
5. Defer freeing old struct
Timeline:
────────────────────────────────────────────
OLD readers: [Old]─┐
├─[Swap]─→ [New]
NEW readers: └────────→ [New]
FREE: Defer until all old readers done
struct options_state options_t
Consolidated options structure.
Data Structures:

// Opaque internal structure (rcu.c)
typedef struct {
atomic<const options_t*> ptr; // Atomic pointer
mutex_t update_lock; // Serializes writers
// ... grace period tracking ...
} options_state_t;
Mutex type (POSIX: pthread_mutex_t with debug tracking)
API:

// Initialization (main thread, before spawning workers)
options_state_set(&parsed_options)
// Reading (worker threads, lock-free)
const options_t *opts = options_get(); // 1-2ns atomic load
int width = opts->width;
// Convenience macro (same as above)
int width = GET_OPTION(width);
// Cleanup (program exit)
void options_state_destroy(void)
Shutdown RCU options system.
Definition rcu.c:461
Key Features:

  • Lock-Free: No mutexes, spinlocks, or blocking
  • Wait-Free: Guaranteed to complete in bounded time
  • Fallback Defaults: Returns static defaults before init or after destroy
  • Atomic Semantics: acquire/release for proper memory ordering
  • Immutable Snapshots: Readers always see consistent state

Mode Bitmask System

Options are filtered by mode using bitmasks. This enables:

  • Mode-Specific Options: Only server can use –max-clients
  • Mode-Aware Help: Client help doesn't show server options
  • Automatic Filtering: Parser only accepts relevant options
  • Shell Completions: Only suggest options for current mode
Mode Values:

OPTION_MODE_BINARY // Parsed before mode detection
OPTION_MODE_SERVER // Server-only
OPTION_MODE_CLIENT // Client-only
OPTION_MODE_MIRROR // Mirror mode (local preview)
OPTION_MODE_DISCOVERY_SVC // Discovery service (ACDS)
OPTION_MODE_ALL // All modes (combine with |)
@ OPTION_MODE_ALL
All modes + binary.
@ OPTION_MODE_BINARY
Binary-level options (parsed before mode detection)
@ OPTION_MODE_MIRROR
Mirror mode (bit 2)
@ OPTION_MODE_DISCOVERY_SVC
Discovery server mode (bit 3)
Examples:

// Server-only option
.mode_bitmask = OPTION_MODE_SERVER
// Available in client and mirror
// Binary-level (before mode detection)
.mode_bitmask = OPTION_MODE_BINARY
// Available everywhere
.mode_bitmask = OPTION_MODE_ALL
Mode Filtering Workflow:

1. User: ./ascii-chat client example.com
2. Mode Detection: Detects MODE_CLIENT
3. Registry Filter: Get all options with CLIENT bit set
4. Parser: Only parses CLIENT options (--color, --audio, --snapshot)
5. Help: Only shows CLIENT options in --help
6. Completions: Only suggests CLIENT options
@ MODE_CLIENT
Client mode - network client options.

Option Dependencies

Options can express relationships that are validated:REQUIRES: If A is set, B must be set

builder, "tls-cert", "tls-enabled",
"TLS certificate requires TLS to be enabled");
CONFLICTS: If A is set, B must NOT be set

builder, "no-crypto", "key-file",
"Cannot use key-file with crypto disabled");
void options_builder_add_dependency_conflicts(options_builder_t *builder, const char *option_name, const char *conflicts_with, const char *error_message)
Add anti-dependency: if option_name is set, conflicts_with must NOT be set.
Definition builder.c:1142
IMPLIES: If A is set, B defaults to true

builder, "tls-enabled", "secure", NULL);
void options_builder_add_dependency_implies(options_builder_t *builder, const char *option_name, const char *implies, const char *error_message)
Add implication: if option_name is set, implies defaults to true.
Definition builder.c:1154
Dependencies are validated in options_config_validate() after parsing.

Complete Example: Server Mode

int main(int argc, char **argv) {
// 1. Initialize RCU system
if (err != ASCIICHAT_OK) return 1;
// 2. Create builder and populate from registry
// 3. Parse command line
err = options_config_parse(config, argc, argv, &opts,
MODE_DETECTED, NULL, NULL);
if (err != ASCIICHAT_OK) {
options_config_print_usage(config, stderr);
return 1;
}
// 4. Validate options
char *error_msg = NULL;
err = options_config_validate(config, &opts, &error_msg);
if (err != ASCIICHAT_OK) {
log_error("Option validation failed: %s", error_msg);
SAFE_FREE(error_msg);
return 1;
}
// 5. Publish to RCU (before spawning threads)
err = options_state_set(&opts);
if (err != ASCIICHAT_OK) return 1;
// 6. Now safe to spawn worker threads
server_run(); // Uses options_get() or GET_OPTION() for lock-free access
// 7. Cleanup
options_struct_destroy(config, &opts);
return 0;
}
void options_config_destroy(options_config_t *config)
Free options config.
Definition builder.c:631
#define SAFE_FREE(ptr)
Definition common.h:376
#define log_error(...)
Log an ERROR message.
Definition log/log.h:587
void options_config_print_usage(const options_config_t *config, FILE *stream)
Print usage/help text.
Definition help.c:706
void options_struct_destroy(const options_config_t *config, void *options_struct)
Clean up memory owned by options struct.
Definition help.c:1457

Design Patterns and Best Practices

Pattern 1: Lock-Free Reading (Render Thread)

// Video render thread (60fps, no locks)
void render_frame_thread(void) {
while (running) {
// Safe lock-free read (~1-2ns atomic load)
const options_t *opts = options_get();
// Use options from consistent snapshot
framebuffer_t fb = render_frame(opts->width, opts->height);
apply_color_mode(&fb, opts->color_mode);
display_frame(&fb);
usleep(16667); // ~60fps
}
}
Frame buffer structure for managing video frames.
Definition ringbuffer.h:436
terminal_color_mode_t color_mode
Color mode (auto/none/16/256/truecolor)

Pattern 2: Runtime Option Updates

// Main thread updates option (e.g., terminal resized)
void on_terminal_resize(int new_width, int new_height) {
// Create new options struct with updated values
options_t new_opts = *options_get(); // Copy current
new_opts.width = new_width;
new_opts.height = new_height;
// Publish atomically
options_state_set(&new_opts);
// Render threads now see new dimensions (lock-free)
}

Pattern 3: Mode-Aware Option Filtering

// Get only options for current mode
void show_mode_specific_help(asciichat_mode_t mode) {
size_t num_opts;
const option_descriptor_t *opts =
for (size_t i = 0; i < num_opts; i++) {
if (opts[i].hide_from_mode_help) continue; // Skip binary-only
printf(" --%s %s\n", opts[i].long_name,
opts[i].help_text);
}
}
const option_descriptor_t * options_registry_get_for_mode(asciichat_mode_t mode, size_t *num_options)
Get all options for a specific mode.
Definition public_api.c:218

Pattern 4: Custom Option with Validation

In registry.c:

bool validate_port(const void *options_struct, char **error_msg) {
const options_t *opts = (const options_t *)options_struct;
if (opts->port < 1 || opts->port > 65535) {
*error_msg = strdup("Port must be between 1 and 65535");
return false;
}
return true;
}
// In registry array:
{
.long_name = "port",
.short_name = 'p',
.type = OPTION_TYPE_INT,
.offset = offsetof(options_t, port),
.default_value = &(int){27224},
.help_text = "Server port (1-65535)",
.group = "NETWORK OPTIONS",
.mode_bitmask = OPTION_MODE_SERVER,
.validate = validate_port, // Custom validator
}

Thread Safety Summary

Phase Operation Thread Safety
Startup Parse args Main thread only
Startup Validate Main thread only
Startup Publish (options_state_set) Main thread only
Runtime Read (options_get) All threads, lock-free
Runtime Update One writer, readers see atomic update
Shutdown Destroy Main thread only

Performance Characteristics

Operation Latency Threads Notes
options_get() ~1-2ns Lock-free Single atomic load
GET_OPTION(field) ~1-2ns Lock-free Convenience macro
Parse (100 options) ~10μs Main thread One-time startup
Validate ~1μs Main thread One-time startup
options_state_set ~100ns Serialized Defers free old struct

Summary

The options system provides:

  • Registry: Single definition of all options
  • Builder: Flexible configuration construction
  • RCU: Lock-free read access from any thread
  • Mode Bitmasks: Automatic mode-aware filtering
  • Type Safety: Default constants, no magic numbers
  • Validation: Required fields, dependencies, cross-field validators
  • Memory Safety: Auto-duplicated strings, auto-freed
  • Shell Completions: Metadata for rich completions
  • Grouped Help: Organized by category with colors
All components work together to provide a comprehensive, type-safe, thread-safe command-line option handling system.
See also
registry.h - Central registry of option definitions
builder.h - Builder API for option configurations
rcu.h - RCU-based thread-safe access
options.h - Unified Options Module parsing system

Macro Definition Documentation

◆ COLOR_MODE_16

#define COLOR_MODE_16   TERM_COLOR_16

#include <options.h>

16-color mode (alias)

Definition at line 215 of file include/ascii-chat/options/options.h.

◆ COLOR_MODE_16_COLOR

#define COLOR_MODE_16_COLOR   TERM_COLOR_16

#include <options.h>

16-color mode (full name)

Definition at line 216 of file include/ascii-chat/options/options.h.

◆ COLOR_MODE_256

#define COLOR_MODE_256   TERM_COLOR_256

#include <options.h>

256-color mode (alias)

Definition at line 217 of file include/ascii-chat/options/options.h.

◆ COLOR_MODE_256_COLOR

#define COLOR_MODE_256_COLOR   TERM_COLOR_256

#include <options.h>

256-color mode (full name)

Definition at line 218 of file include/ascii-chat/options/options.h.

◆ COLOR_MODE_AUTO

#define COLOR_MODE_AUTO   TERM_COLOR_AUTO

#include <options.h>

Backward compatibility aliases for color mode enum values.

Auto-detect color support

Definition at line 213 of file include/ascii-chat/options/options.h.

◆ COLOR_MODE_NONE

#define COLOR_MODE_NONE   TERM_COLOR_NONE

#include <options.h>

Monochrome mode.

Definition at line 214 of file include/ascii-chat/options/options.h.

◆ COLOR_MODE_TRUECOLOR

#define COLOR_MODE_TRUECOLOR   TERM_COLOR_TRUECOLOR

#include <options.h>

24-bit truecolor mode

Definition at line 219 of file include/ascii-chat/options/options.h.

◆ GET_OPTION

#define GET_OPTION (   field)

#include <options.h>

Value:
({ \
const options_t *_opts = options_get(); \
if (!_opts) { \
log_warn("GET_OPTION(" #field ") called but options not initialized"); \
} \
static typeof(((options_t *)0)->field) _default = {0}; \
(_opts ? (_opts->field) : _default); \
})

Safely get a specific option field (lock-free read)

Convenience macro for accessing individual option fields without storing the entire options pointer. Includes NULL check with warning log for safety.

Usage Examples:

// Simple field access
const char *addr = GET_OPTION(address6);
int width = GET_OPTION(width);
bool flip_x = GET_OPTION(flip_x);
// In expressions
if (GET_OPTION(encrypt_enabled)) {
// encryption is enabled
}
// Function arguments
connect_to_server(GET_OPTION(address), GET_OPTION(port));

Design: This macro eliminates the need to store const options_t *opts pointers around the codebase, reducing clutter and making code more readable.

Safety: If options_get() returns NULL (shouldn't happen after initialization), the macro will log a warning and return a zero-initialized field.

Performance: Equivalent cost to direct options_get()->field access.

Parameters
fieldThe field name to access (e.g., address, port, width, etc.)
Returns
The value of the requested field
Note
Must be called after options_init() has completed
Zero-initialized fields are returned if options pointer is somehow NULL

Definition at line 1235 of file include/ascii-chat/options/options.h.

1236 { \
1237 const options_t *_opts = options_get(); \
1238 if (!_opts) { \
1239 log_warn("GET_OPTION(" #field ") called but options not initialized"); \
1240 } \
1241 static typeof(((options_t *)0)->field) _default = {0}; \
1242 (_opts ? (_opts->field) : _default); \
1243 })

◆ LEVENSHTEIN_SUGGESTION_THRESHOLD

#define LEVENSHTEIN_SUGGESTION_THRESHOLD   4

#include <levenshtein.h>

Maximum edit distance to suggest an option.

Threshold of 4 catches common typos including:

  • Single character errors (e.g., port-forwad → port-forwarding: distance 4)
  • Transpositions (e.g., colo-mod → color-mode: distance 2)
  • Short option typos (e.g., prot → port: distance 2) While still filtering out completely unrelated options.

Definition at line 32 of file levenshtein.h.

◆ MAX_IDENTITY_KEYS

#define MAX_IDENTITY_KEYS   32

#include <options.h>

Maximum number of identity keys that can be loaded (for multi-key support)

Definition at line 301 of file include/ascii-chat/options/options.h.

◆ MODE_SERVER_GROUPS

#define MODE_SERVER_GROUPS

#include <groups.h>

Value:
@ OPT_GROUP_CRYPTO
Encryption and authentication options.
Definition groups.h:59
@ OPT_GROUP_ACDS
ACDS discovery service options.
Definition groups.h:61
@ OPT_GROUP_BINARY
Binary-level options (help, version, logging)
Definition groups.h:52
@ OPT_GROUP_COMPRESSION
Compression options.
Definition groups.h:60
@ OPT_GROUP_NETWORK
Network options (address, port, reconnect)
Definition groups.h:54
@ OPT_GROUP_SERVER
Server-specific options (max_clients, client-keys)
Definition groups.h:63
@ OPT_GROUP_WEBRTC
WebRTC connectivity options (STUN/TURN)
Definition groups.h:65

Mode group presets.

Predefined combinations of option groups for each mode. These define which options are available in each mode. Server mode options: network binding, crypto, compression, ACDS

Definition at line 78 of file groups.h.

◆ OPT_ACDS_DEFAULT

#define OPT_ACDS_DEFAULT   false

#include <options.h>

Default ACDS registration flag (false = disabled)

Definition at line 577 of file include/ascii-chat/options/options.h.

◆ OPT_ACDS_EXPOSE_IP_DEFAULT

#define OPT_ACDS_EXPOSE_IP_DEFAULT   false

#include <options.h>

Default ACDS expose IP flag (false = private by default)

Definition at line 586 of file include/ascii-chat/options/options.h.

◆ OPT_ACDS_INSECURE_DEFAULT

#define OPT_ACDS_INSECURE_DEFAULT   false

#include <options.h>

Default ACDS insecure mode flag (false = verify server)

Definition at line 589 of file include/ascii-chat/options/options.h.

◆ OPT_ACDS_PORT_DEFAULT

#define OPT_ACDS_PORT_DEFAULT   "27225"

#include <options.h>

Default ACDS discovery service port (string)

Definition at line 583 of file include/ascii-chat/options/options.h.

◆ OPT_ACDS_PORT_INT_DEFAULT

#define OPT_ACDS_PORT_INT_DEFAULT   27225

#include <options.h>

Default ACDS discovery service port (integer)

Definition at line 580 of file include/ascii-chat/options/options.h.

◆ OPT_ADDRESS6_DEFAULT

#define OPT_ADDRESS6_DEFAULT   "::1"

#include <options.h>

Default IPv6 server address.

Definition at line 533 of file include/ascii-chat/options/options.h.

◆ OPT_ADDRESS_DEFAULT

#define OPT_ADDRESS_DEFAULT   "localhost"

#include <options.h>

Default server address for client connections.

Definition at line 530 of file include/ascii-chat/options/options.h.

◆ OPT_AUDIO_ANALYSIS_ENABLED_DEFAULT

#define OPT_AUDIO_ANALYSIS_ENABLED_DEFAULT   false

#include <options.h>

Default audio analysis enabled flag.

Definition at line 709 of file include/ascii-chat/options/options.h.

◆ OPT_AUDIO_CAPTURE_SOURCE_DEFAULT

#define OPT_AUDIO_CAPTURE_SOURCE_DEFAULT   AUDIO_CAPTURE_SOURCE_AUTO

#include <options.h>

Preserve automatic local audio capture selection by default.

Definition at line 694 of file include/ascii-chat/options/options.h.

◆ OPT_AUDIO_ENABLED_DEFAULT

#define OPT_AUDIO_ENABLED_DEFAULT   true

#include <options.h>

Default audio enabled flag (true = audio enabled by default)

Definition at line 689 of file include/ascii-chat/options/options.h.

◆ OPT_AUDIO_NO_PLAYBACK_DEFAULT

#define OPT_AUDIO_NO_PLAYBACK_DEFAULT   false

#include <options.h>

Default audio playback flag (false = enable playback)

Definition at line 712 of file include/ascii-chat/options/options.h.

◆ OPT_AUDIO_SOURCE_DEFAULT

#define OPT_AUDIO_SOURCE_DEFAULT   AUDIO_SOURCE_ALL

#include <options.h>

Visualize all available sources by default.

Definition at line 692 of file include/ascii-chat/options/options.h.

◆ OPT_AUTO_HEIGHT_DEFAULT

#define OPT_AUTO_HEIGHT_DEFAULT   true

#include <options.h>

Default auto-detect height flag (true = auto-detect from terminal)

Definition at line 414 of file include/ascii-chat/options/options.h.

◆ OPT_AUTO_WIDTH_DEFAULT

#define OPT_AUTO_WIDTH_DEFAULT   true

#include <options.h>

Default auto-detect width flag (true = auto-detect from terminal)

Definition at line 411 of file include/ascii-chat/options/options.h.

◆ OPT_COLOR_DEFAULT

#define OPT_COLOR_DEFAULT   COLOR_SETTING_AUTO

#include <options.h>

Default color setting (COLOR_SETTING_AUTO = smart detection)

Definition at line 438 of file include/ascii-chat/options/options.h.

◆ OPT_COLOR_FILTER_DEFAULT

#define OPT_COLOR_FILTER_DEFAULT   COLOR_FILTER_NONE

#include <options.h>

Default color filter (none - no filtering)

Definition at line 445 of file include/ascii-chat/options/options.h.

◆ OPT_COLOR_MODE_DEFAULT

#define OPT_COLOR_MODE_DEFAULT   COLOR_MODE_AUTO

#include <options.h>

Default color mode (auto-detect terminal capabilities)

Definition at line 417 of file include/ascii-chat/options/options.h.

◆ OPT_COLOR_SCHEME_NAME_DEFAULT

#define OPT_COLOR_SCHEME_NAME_DEFAULT   "pastel"

#include <options.h>

Default color scheme name (pastel)

Definition at line 354 of file include/ascii-chat/options/options.h.

◆ OPT_COMPRESSION_LEVEL_DEFAULT

#define OPT_COMPRESSION_LEVEL_DEFAULT   3

#include <options.h>

Default compression level (1-9)

Definition at line 571 of file include/ascii-chat/options/options.h.

◆ OPT_DISABLE_KEEPAWAKE_DEFAULT

#define OPT_DISABLE_KEEPAWAKE_DEFAULT   false

#include <options.h>

Default disable keep-awake flag (false = allow system sleep prevention)

Definition at line 329 of file include/ascii-chat/options/options.h.

◆ OPT_ENABLE_KEEPAWAKE_DEFAULT

#define OPT_ENABLE_KEEPAWAKE_DEFAULT   false

#include <options.h>

Default keep-awake (prevent system sleep) flag (false = normal system sleep)

Definition at line 326 of file include/ascii-chat/options/options.h.

◆ OPT_ENABLE_UPNP_DEFAULT

#define OPT_ENABLE_UPNP_DEFAULT   false

#include <options.h>

Default enable UPnP flag (false = UPnP disabled)

Definition at line 604 of file include/ascii-chat/options/options.h.

◆ OPT_ENCODE_AUDIO_DEFAULT

#define OPT_ENCODE_AUDIO_DEFAULT   true

#include <options.h>

Default audio encoding state (true = Opus encoding enabled)

Definition at line 715 of file include/ascii-chat/options/options.h.

◆ OPT_ENCRYPT_ENABLED_DEFAULT

#define OPT_ENCRYPT_ENABLED_DEFAULT   true

#include <options.h>

Default encrypt enabled flag (true = encryption required)

Definition at line 722 of file include/ascii-chat/options/options.h.

◆ OPT_ENDPOINT_DISCOVERY_SERVICE

#define OPT_ENDPOINT_DISCOVERY_SERVICE   "localhost"

#include <options.h>

Default discovery service endpoint (what to connect to by default)

Production: connects to official server by default Debug: connects to localhost by default for local testing

Note
This is different from is_official_server() in crypto/discovery_keys.c which determines which servers have automatic HTTPS key trust. In debug builds:
  • DEFAULT ENDPOINT (this constant): localhost
  • AUTO-TRUST ENDPOINTS (discovery_keys.c): localhost AND official server

Definition at line 549 of file include/ascii-chat/options/options.h.

◆ OPT_ENDPOINT_STUN_FALLBACK

#define OPT_ENDPOINT_STUN_FALLBACK   "stun:stun.l.google.com:19302"

#include <options.h>

Fallback STUN server (Google public STUN)

Definition at line 617 of file include/ascii-chat/options/options.h.

◆ OPT_ENDPOINT_STUN_PRIMARY

#define OPT_ENDPOINT_STUN_PRIMARY   "stun:stun.ascii-chat.com:3478"

#include <options.h>

Primary STUN server (ascii-chat hosted)

Definition at line 614 of file include/ascii-chat/options/options.h.

◆ OPT_ENDPOINT_STUN_SERVERS_DEFAULT

#define OPT_ENDPOINT_STUN_SERVERS_DEFAULT   OPT_ENDPOINT_STUN_PRIMARY "," OPT_ENDPOINT_STUN_FALLBACK

#include <options.h>

Default STUN servers (comma-separated list)

Definition at line 620 of file include/ascii-chat/options/options.h.

◆ OPT_ENDPOINT_TURN_PRIMARY

#define OPT_ENDPOINT_TURN_PRIMARY   "turn:turn.ascii-chat.com:3478"

#include <options.h>

Primary TURN server (ascii-chat hosted)

Definition at line 623 of file include/ascii-chat/options/options.h.

◆ OPT_ENDPOINT_TURN_SERVERS_DEFAULT

#define OPT_ENDPOINT_TURN_SERVERS_DEFAULT   OPT_ENDPOINT_TURN_PRIMARY

#include <options.h>

Default TURN servers (comma-separated list)

Definition at line 626 of file include/ascii-chat/options/options.h.

◆ OPT_FLIP_X_DEFAULT

#define OPT_FLIP_X_DEFAULT   false

#include <options.h>

Default horizontal flip state (true = horizontally flipped) macOS webcams default to flipped (mirrored), other platforms default to normal.

Definition at line 490 of file include/ascii-chat/options/options.h.

◆ OPT_FLIP_Y_DEFAULT

#define OPT_FLIP_Y_DEFAULT   false

#include <options.h>

Default vertical flip state (false = no vertical flip)

Definition at line 494 of file include/ascii-chat/options/options.h.

◆ OPT_FORCE_UTF8_DEFAULT

#define OPT_FORCE_UTF8_DEFAULT   UTF8_SETTING_AUTO

#include <options.h>

Default force UTF-8 support setting (auto-detect)

Definition at line 432 of file include/ascii-chat/options/options.h.

◆ OPT_FPS_COUNTER_DEFAULT

#define OPT_FPS_COUNTER_DEFAULT   false

#include <options.h>

Default FPS counter overlay flag (false = disabled)

Definition at line 483 of file include/ascii-chat/options/options.h.

◆ OPT_FPS_DEFAULT

#define OPT_FPS_DEFAULT   60

#include <options.h>

Default FPS (frames per second)

Definition at line 460 of file include/ascii-chat/options/options.h.

◆ OPT_GREP_PATTERN_DEFAULT

#define OPT_GREP_PATTERN_DEFAULT   ""

#include <options.h>

Default grep pattern (empty = no filtering)

Definition at line 348 of file include/ascii-chat/options/options.h.

◆ OPT_HEIGHT_DEFAULT

#define OPT_HEIGHT_DEFAULT   70

#include <options.h>

Default terminal height in characters.

Default height used when terminal size cannot be detected or when auto-detection is disabled. This default provides a reasonable size for ASCII art display.

Note
This is used as a fallback if --height is not specified and auto-detection fails.

Definition at line 408 of file include/ascii-chat/options/options.h.

◆ OPT_HELP_DEFAULT

#define OPT_HELP_DEFAULT   false

#include <options.h>

Default help flag (false = don't show help)

Definition at line 308 of file include/ascii-chat/options/options.h.

◆ OPT_JSON_DEFAULT

#define OPT_JSON_DEFAULT   false

#include <options.h>

Default JSON logging flag (false = text output)

Definition at line 351 of file include/ascii-chat/options/options.h.

◆ OPT_LAN_DISCOVERY_DEFAULT

#define OPT_LAN_DISCOVERY_DEFAULT   false

#include <options.h>

Default LAN discovery flag (false = discovery disabled)

Definition at line 598 of file include/ascii-chat/options/options.h.

◆ OPT_LIST_MICROPHONES_DEFAULT

#define OPT_LIST_MICROPHONES_DEFAULT   false

#include <options.h>

Default list microphones flag (false = don't list and exit)

Definition at line 423 of file include/ascii-chat/options/options.h.

◆ OPT_LIST_SPEAKERS_DEFAULT

#define OPT_LIST_SPEAKERS_DEFAULT   false

#include <options.h>

Default list speakers flag (false = don't list and exit)

Definition at line 426 of file include/ascii-chat/options/options.h.

◆ OPT_LIST_WEBCAMS_DEFAULT

#define OPT_LIST_WEBCAMS_DEFAULT   false

#include <options.h>

Default list webcams flag (false = don't list and exit)

Definition at line 420 of file include/ascii-chat/options/options.h.

◆ OPT_LOG_FORMAT_CONSOLE_DEFAULT

#define OPT_LOG_FORMAT_CONSOLE_DEFAULT   false

#include <options.h>

Default log format console only flag (false = use default format everywhere)

Definition at line 382 of file include/ascii-chat/options/options.h.

◆ OPT_LOG_FORMAT_OUTPUT_DEFAULT

#define OPT_LOG_FORMAT_OUTPUT_DEFAULT   LOG_OUTPUT_TEXT

#include <options.h>

Default log format output type (TEXT = human-readable, JSON = structured)

Definition at line 379 of file include/ascii-chat/options/options.h.

◆ OPT_LOG_LEVEL_DEFAULT

#define OPT_LOG_LEVEL_DEFAULT   LOG_INFO

#include <options.h>

Default log level (LOG_INFO)

Definition at line 345 of file include/ascii-chat/options/options.h.

◆ OPT_LOG_TEMPLATE_DEFAULT

#define OPT_LOG_TEMPLATE_DEFAULT   OPT_LOG_TEMPLATE_DEFAULT_DEBUG

#include <options.h>

Default log template string (selected based on build mode)

Release builds use simple format. Debug builds use verbose format with thread ID, file path, line, and function. The get_default_log_template() function returns this value via the options system.

Definition at line 375 of file include/ascii-chat/options/options.h.

◆ OPT_LOG_TEMPLATE_DEFAULT_DEBUG

#define OPT_LOG_TEMPLATE_DEFAULT_DEBUG

#include <options.h>

Value:
"[%color(*, %H):%color(*, %M):%color(*, %S).%color(*, %ms)] [%color(*, %level_aligned)] " \
"[thread/%color(GREY, %tname)] %color(DEBUG, %file_relative):%color(GREY, %line)@%color(DEV, %func)(): " \
"%colored_message"

Default log format string - debug mode (verbose with thread name, file relative path, line, and function)

Definition at line 362 of file include/ascii-chat/options/options.h.

363 :%color(*, %M):%color(*, %S).%color(*, %ms)] [%color(*, %level_aligned)] " \
364 "[thread/%color(GREY, %tname)] %color(DEBUG, %file_relative):%color(GREY, %line)@%color(DEV, %func)(): " \
365 "%colored_message"

◆ OPT_LOG_TEMPLATE_DEFAULT_RELEASE

#define OPT_LOG_TEMPLATE_DEFAULT_RELEASE

#include <options.h>

Value:
"[%color(*, %H):%color(*, %M):%color(*, %S).%color(*, %ms)] [%color(*, %level_aligned)] " \
"%colored_message"

Default log format string - release mode (simple format with timestamp, level, and message)

Definition at line 357 of file include/ascii-chat/options/options.h.

358 :%color(*, %M):%color(*, %S).%color(*, %ms)] [%color(*, %level_aligned)] " \
359 "%colored_message"

◆ OPT_MATRIX_RAIN_DEFAULT

#define OPT_MATRIX_RAIN_DEFAULT   false

#include <options.h>

Default Matrix rain effect flag (false = disabled)

Definition at line 480 of file include/ascii-chat/options/options.h.

◆ OPT_MAX_CLIENTS_DEFAULT

#define OPT_MAX_CLIENTS_DEFAULT   9

#include <options.h>

Default maximum concurrent clients (server only)

Definition at line 565 of file include/ascii-chat/options/options.h.

◆ OPT_MEDIA_FROM_STDIN_DEFAULT

#define OPT_MEDIA_FROM_STDIN_DEFAULT   false

#include <options.h>

Default media from stdin flag (false = not reading from stdin)

Definition at line 517 of file include/ascii-chat/options/options.h.

◆ OPT_MEDIA_LOOP_DEFAULT

#define OPT_MEDIA_LOOP_DEFAULT   false

#include <options.h>

Default loop media flag (false = play once)

Definition at line 514 of file include/ascii-chat/options/options.h.

◆ OPT_MEDIA_SEEK_TIMESTAMP_DEFAULT

#define OPT_MEDIA_SEEK_TIMESTAMP_DEFAULT   0.0

#include <options.h>

Default media seek timestamp (start from beginning)

Definition at line 520 of file include/ascii-chat/options/options.h.

◆ OPT_MICROPHONE_INDEX_DEFAULT

#define OPT_MICROPHONE_INDEX_DEFAULT   (-1)

#include <options.h>

Default microphone device index (-1 means system default)

Definition at line 697 of file include/ascii-chat/options/options.h.

◆ OPT_MICROPHONE_SENSITIVITY_DEFAULT

#define OPT_MICROPHONE_SENSITIVITY_DEFAULT   1.0

#include <options.h>

Default microphone sensitivity (1.0 = normal volume)

Definition at line 703 of file include/ascii-chat/options/options.h.

◆ OPT_NO_AUDIO_MIXER_DEFAULT

#define OPT_NO_AUDIO_MIXER_DEFAULT   false

#include <options.h>

Default no audio mixer flag (false = enable mixer)

Definition at line 507 of file include/ascii-chat/options/options.h.

◆ OPT_NO_AUTH_DEFAULT

#define OPT_NO_AUTH_DEFAULT   false

#include <options.h>

Default no auth flag (false = allow authentication)

Definition at line 728 of file include/ascii-chat/options/options.h.

◆ OPT_NO_CHECK_UPDATE_DEFAULT

#define OPT_NO_CHECK_UPDATE_DEFAULT   false

#include <options.h>

Default no-check-update flag (false = check for updates enabled)

Definition at line 332 of file include/ascii-chat/options/options.h.

◆ OPT_NO_COMPRESS_DEFAULT

#define OPT_NO_COMPRESS_DEFAULT   false

#include <options.h>

Default no compression flag (false = enable compression)

Definition at line 574 of file include/ascii-chat/options/options.h.

◆ OPT_NO_ENCRYPT_DEFAULT

#define OPT_NO_ENCRYPT_DEFAULT   false

#include <options.h>

Default no encrypt flag (false = allow encryption)

Definition at line 725 of file include/ascii-chat/options/options.h.

◆ OPT_NO_MDNS_ADVERTISE_DEFAULT

#define OPT_NO_MDNS_ADVERTISE_DEFAULT   false

#include <options.h>

Default no mDNS advertise flag (false = advertise enabled)

Definition at line 601 of file include/ascii-chat/options/options.h.

◆ OPT_NO_WEBRTC_DEFAULT

#define OPT_NO_WEBRTC_DEFAULT   false

#include <options.h>

Default no WebRTC flag (false = WebRTC enabled)

Definition at line 661 of file include/ascii-chat/options/options.h.

◆ OPT_PALETTE_CUSTOM_SET_DEFAULT

#define OPT_PALETTE_CUSTOM_SET_DEFAULT   false

#include <options.h>

Default custom palette set flag (false = not set)

Definition at line 454 of file include/ascii-chat/options/options.h.

◆ OPT_PALETTE_TYPE_DEFAULT

#define OPT_PALETTE_TYPE_DEFAULT   PALETTE_STANDARD

#include <options.h>

Default palette type (standard ASCII art)

Definition at line 451 of file include/ascii-chat/options/options.h.

◆ OPT_PAUSE_DEFAULT

#define OPT_PAUSE_DEFAULT   false

#include <options.h>

Default pause media flag (false = play immediately)

Definition at line 523 of file include/ascii-chat/options/options.h.

◆ OPT_PORT_DEFAULT

#define OPT_PORT_DEFAULT   "27224"

#include <options.h>

Default TCP port for client/server communication (string)

Definition at line 553 of file include/ascii-chat/options/options.h.

◆ OPT_PORT_INT_DEFAULT

#define OPT_PORT_INT_DEFAULT   27224

#include <options.h>

Default TCP port for client/server communication (integer)

Definition at line 556 of file include/ascii-chat/options/options.h.

◆ OPT_PREFER_WEBRTC_DEFAULT

#define OPT_PREFER_WEBRTC_DEFAULT   false

#include <options.h>

Default prefer WebRTC flag (false = try direct TCP first)

Definition at line 658 of file include/ascii-chat/options/options.h.

◆ OPT_QUIET_DEFAULT

#define OPT_QUIET_DEFAULT   false

#include <options.h>

Default quiet mode flag (false = logging enabled)

Definition at line 339 of file include/ascii-chat/options/options.h.

◆ OPT_RECONNECT_ATTEMPTS_DEFAULT

#define OPT_RECONNECT_ATTEMPTS_DEFAULT   (-1)

#include <options.h>

Default reconnect attempts (-1 means auto/infinite)

Definition at line 568 of file include/ascii-chat/options/options.h.

◆ OPT_RENDER_FILE_DEFAULT

#define OPT_RENDER_FILE_DEFAULT   ""

#include <options.h>

Definition at line 758 of file include/ascii-chat/options/options.h.

◆ OPT_RENDER_FONT_DEFAULT

#define OPT_RENDER_FONT_DEFAULT   ""

#include <options.h>

Definition at line 760 of file include/ascii-chat/options/options.h.

◆ OPT_RENDER_FONT_SIZE_DEFAULT

#define OPT_RENDER_FONT_SIZE_DEFAULT   12.0

#include <options.h>

Definition at line 761 of file include/ascii-chat/options/options.h.

◆ OPT_RENDER_MODE_DEFAULT

#define OPT_RENDER_MODE_DEFAULT   RENDER_MODE_FOREGROUND

#include <options.h>

Default render mode (foreground characters only)

Definition at line 448 of file include/ascii-chat/options/options.h.

◆ OPT_RENDER_THEME_DEFAULT

#define OPT_RENDER_THEME_DEFAULT   0

#include <options.h>

Definition at line 759 of file include/ascii-chat/options/options.h.

◆ OPT_REQUIRE_CLIENT_IDENTITY_DEFAULT

#define OPT_REQUIRE_CLIENT_IDENTITY_DEFAULT   false

#include <options.h>

Default require-client-identity setting for ACDS.

Definition at line 595 of file include/ascii-chat/options/options.h.

◆ OPT_REQUIRE_CLIENT_VERIFY_DEFAULT

#define OPT_REQUIRE_CLIENT_VERIFY_DEFAULT   false

#include <options.h>

Default require client verify flag (false = not required)

Definition at line 734 of file include/ascii-chat/options/options.h.

◆ OPT_REQUIRE_SERVER_IDENTITY_DEFAULT

#define OPT_REQUIRE_SERVER_IDENTITY_DEFAULT   false

#include <options.h>

Default require-server-identity setting for ACDS.

Definition at line 592 of file include/ascii-chat/options/options.h.

◆ OPT_REQUIRE_SERVER_VERIFY_DEFAULT

#define OPT_REQUIRE_SERVER_VERIFY_DEFAULT   false

#include <options.h>

Default require server verify flag (false = not required)

Definition at line 731 of file include/ascii-chat/options/options.h.

◆ OPT_SHOW_CAPABILITIES_DEFAULT

#define OPT_SHOW_CAPABILITIES_DEFAULT   false

#include <options.h>

Default show terminal capabilities flag.

Definition at line 429 of file include/ascii-chat/options/options.h.

◆ OPT_SNAPSHOT_DELAY_DEFAULT

#define OPT_SNAPSHOT_DELAY_DEFAULT   3.0f

#include <options.h>

Default snapshot delay in seconds.

Default delay for snapshot mode before exiting. macOS webcams show pure black first then fade up into a real color image over a few seconds, so we use a longer delay on macOS.

Definition at line 476 of file include/ascii-chat/options/options.h.

◆ OPT_SNAPSHOT_MODE_DEFAULT

#define OPT_SNAPSHOT_MODE_DEFAULT   false

#include <options.h>

Default snapshot mode flag (false = continuous)

Definition at line 463 of file include/ascii-chat/options/options.h.

◆ OPT_SPEAKERS_INDEX_DEFAULT

#define OPT_SPEAKERS_INDEX_DEFAULT   (-1)

#include <options.h>

Default speakers device index (-1 means system default)

Definition at line 700 of file include/ascii-chat/options/options.h.

◆ OPT_SPEAKERS_VOLUME_DEFAULT

#define OPT_SPEAKERS_VOLUME_DEFAULT   1.0

#include <options.h>

Default speakers volume (1.0 = normal volume)

Definition at line 706 of file include/ascii-chat/options/options.h.

◆ OPT_SPLASH_DEFAULT

#define OPT_SPLASH_DEFAULT   true

#include <options.h>

Default splash screen flag (true = show splash, false = hide splash)

Definition at line 314 of file include/ascii-chat/options/options.h.

◆ OPT_SPLASH_SCREEN_EXPLICITLY_SET_DEFAULT

#define OPT_SPLASH_SCREEN_EXPLICITLY_SET_DEFAULT   false

#include <options.h>

Default splash screen explicitly set flag (false = use default splash setting)

Definition at line 317 of file include/ascii-chat/options/options.h.

◆ OPT_STATUS_SCREEN_DEFAULT

#define OPT_STATUS_SCREEN_DEFAULT   true

#include <options.h>

Default status screen flag (true = show status, false = hide status)

Definition at line 320 of file include/ascii-chat/options/options.h.

◆ OPT_STATUS_SCREEN_EXPLICITLY_SET_DEFAULT

#define OPT_STATUS_SCREEN_EXPLICITLY_SET_DEFAULT   false

#include <options.h>

Default status screen explicitly set flag (false = use default status screen setting)

Definition at line 323 of file include/ascii-chat/options/options.h.

◆ OPT_STRETCH_DEFAULT

#define OPT_STRETCH_DEFAULT   false

#include <options.h>

Default allow aspect ratio distortion flag.

Definition at line 457 of file include/ascii-chat/options/options.h.

◆ OPT_STRING_EMPTY_DEFAULT

#define OPT_STRING_EMPTY_DEFAULT   ""

#include <options.h>

Default empty string for string option fields that have no configured value.

Definition at line 753 of file include/ascii-chat/options/options.h.

◆ OPT_STRIP_ANSI_DEFAULT

#define OPT_STRIP_ANSI_DEFAULT   false

#include <options.h>

Default strip ANSI escape sequences flag.

Definition at line 435 of file include/ascii-chat/options/options.h.

◆ OPT_STUN_SERVER_HOST_FALLBACK

#define OPT_STUN_SERVER_HOST_FALLBACK   "stun.l.google.com"

#include <options.h>

Fallback STUN server hostname only.

Definition at line 635 of file include/ascii-chat/options/options.h.

◆ OPT_STUN_SERVER_HOST_PRIMARY

#define OPT_STUN_SERVER_HOST_PRIMARY   "stun.ascii-chat.com"

#include <options.h>

STUN server hostname only (without protocol/port)

Definition at line 629 of file include/ascii-chat/options/options.h.

◆ OPT_STUN_SERVER_PORT_FALLBACK

#define OPT_STUN_SERVER_PORT_FALLBACK   19302

#include <options.h>

Fallback STUN server port.

Definition at line 638 of file include/ascii-chat/options/options.h.

◆ OPT_STUN_SERVER_PORT_PRIMARY

#define OPT_STUN_SERVER_PORT_PRIMARY   3478

#include <options.h>

STUN server port for primary server.

Definition at line 632 of file include/ascii-chat/options/options.h.

◆ OPT_STUN_SERVERS_DEFAULT

#define OPT_STUN_SERVERS_DEFAULT   OPT_ENDPOINT_STUN_SERVERS_DEFAULT

#include <options.h>

Default STUN server URLs (comma-separated)

Definition at line 647 of file include/ascii-chat/options/options.h.

◆ OPT_TEST_PATTERN_DEFAULT

#define OPT_TEST_PATTERN_DEFAULT   false

#include <options.h>

Default test pattern mode (false = use actual webcam)

Definition at line 504 of file include/ascii-chat/options/options.h.

◆ OPT_TURN_CREDENTIAL_DEFAULT

#define OPT_TURN_CREDENTIAL_DEFAULT   ""

#include <options.h>

Default TURN credential (empty = use ACDS credentials)

Definition at line 682 of file include/ascii-chat/options/options.h.

◆ OPT_TURN_SERVER_HOST

#define OPT_TURN_SERVER_HOST   "turn.ascii-chat.com"

#include <options.h>

TURN server hostname only.

Definition at line 641 of file include/ascii-chat/options/options.h.

◆ OPT_TURN_SERVER_PORT

#define OPT_TURN_SERVER_PORT   3478

#include <options.h>

TURN server port.

Definition at line 644 of file include/ascii-chat/options/options.h.

◆ OPT_TURN_SERVERS_DEFAULT

#define OPT_TURN_SERVERS_DEFAULT   OPT_ENDPOINT_TURN_SERVERS_DEFAULT

#include <options.h>

Default TURN server URLs (comma-separated)

Definition at line 650 of file include/ascii-chat/options/options.h.

◆ OPT_TURN_USERNAME_DEFAULT

#define OPT_TURN_USERNAME_DEFAULT   ""

#include <options.h>

Default TURN username (empty = use ACDS credentials)

Definition at line 679 of file include/ascii-chat/options/options.h.

◆ OPT_VERBOSE_LEVEL_DEFAULT

#define OPT_VERBOSE_LEVEL_DEFAULT   0

#include <options.h>

Default verbose level (0 = not verbose)

Definition at line 342 of file include/ascii-chat/options/options.h.

◆ OPT_VERSION_DEFAULT

#define OPT_VERSION_DEFAULT   false

#include <options.h>

Default version flag (false = don't show version)

Definition at line 311 of file include/ascii-chat/options/options.h.

◆ OPT_WEBCAM_INDEX_DEFAULT

#define OPT_WEBCAM_INDEX_DEFAULT   0

#include <options.h>

Default webcam device index.

Definition at line 501 of file include/ascii-chat/options/options.h.

◆ OPT_WEBRTC_DEFAULT

#define OPT_WEBRTC_DEFAULT   true

#include <options.h>

Default WebRTC mode flag (true = P2P WebRTC, false = direct TCP)

Definition at line 655 of file include/ascii-chat/options/options.h.

◆ OPT_WEBRTC_DISABLE_TURN_DEFAULT

#define OPT_WEBRTC_DISABLE_TURN_DEFAULT   false

#include <options.h>

Default WebRTC disable TURN flag (false = use TURN)

Definition at line 667 of file include/ascii-chat/options/options.h.

◆ OPT_WEBRTC_ICE_TIMEOUT_MS_DEFAULT

#define OPT_WEBRTC_ICE_TIMEOUT_MS_DEFAULT   10000

#include <options.h>

Default WebRTC ICE gathering timeout in milliseconds (10 seconds)

Definition at line 673 of file include/ascii-chat/options/options.h.

◆ OPT_WEBRTC_RECONNECT_ATTEMPTS_DEFAULT

#define OPT_WEBRTC_RECONNECT_ATTEMPTS_DEFAULT   3

#include <options.h>

Default WebRTC reconnection attempts (3 = try initial + 3 retries)

Definition at line 676 of file include/ascii-chat/options/options.h.

◆ OPT_WEBRTC_SKIP_HOST_DEFAULT

#define OPT_WEBRTC_SKIP_HOST_DEFAULT   false

#include <options.h>

Default WebRTC skip host candidates flag (false = use host candidates)

Definition at line 670 of file include/ascii-chat/options/options.h.

◆ OPT_WEBRTC_SKIP_STUN_DEFAULT

#define OPT_WEBRTC_SKIP_STUN_DEFAULT   false

#include <options.h>

Default WebRTC skip STUN flag (false = use STUN)

Definition at line 664 of file include/ascii-chat/options/options.h.

◆ OPT_WEBSOCKET_PORT_ACDS_DEFAULT

#define OPT_WEBSOCKET_PORT_ACDS_DEFAULT   27227

#include <options.h>

Default WebSocket port for discovery-service mode (integer)

Definition at line 562 of file include/ascii-chat/options/options.h.

◆ OPT_WEBSOCKET_PORT_SERVER_DEFAULT

#define OPT_WEBSOCKET_PORT_SERVER_DEFAULT   27226

#include <options.h>

Default WebSocket port for server mode (integer)

Definition at line 559 of file include/ascii-chat/options/options.h.

◆ OPT_WIDTH_DEFAULT

#define OPT_WIDTH_DEFAULT   110

#include <options.h>

Default terminal width in characters.

Default width used when terminal size cannot be detected or when auto-detection is disabled. This default provides a reasonable size for ASCII art display.

Note
This is used as a fallback if --width is not specified and auto-detection fails.

Definition at line 397 of file include/ascii-chat/options/options.h.

◆ OPTION_MODE_CLIENT_LIKE

#define OPTION_MODE_CLIENT_LIKE   (OPTION_MODE_CLIENT | OPTION_MODE_MIRROR | OPTION_MODE_DISCOVERY)

#include <options.h>

Definition at line 940 of file include/ascii-chat/options/options.h.

◆ OPTION_MODE_NETWORKED

#include <options.h>

Definition at line 941 of file include/ascii-chat/options/options.h.

◆ OPTION_MODE_SERVER_LIKE

#define OPTION_MODE_SERVER_LIKE   (OPTION_MODE_SERVER | OPTION_MODE_DISCOVERY_SVC)

#include <options.h>

Mode group macros for examples.

These macros combine multiple mode bits to allow examples to apply to logical groups of modes (e.g., server-like modes, client-like modes).

Definition at line 939 of file include/ascii-chat/options/options.h.

◆ OPTIONS_BUFF_SIZE

#define OPTIONS_BUFF_SIZE   256

#include <options.h>

Buffer size for option string values.

Maximum size for string-based options (e.g., addresses, file paths, passwords). Used for arrays that store option values.

Definition at line 298 of file include/ascii-chat/options/options.h.

Typedef Documentation

◆ options_t

typedef struct options_state options_t

#include <options.h>

Consolidated options structure.

All options from the scattered extern globals are now in a single struct. This struct is immutable once published via RCU - modifications create a new copy.

Enumeration Type Documentation

◆ asciichat_mode_t

#include <options.h>

Mode type for options parsing.

Determines which set of options to use when parsing command-line arguments.

Enumerator
MODE_SERVER 

Server mode - network server options.

MODE_CLIENT 

Client mode - network client options.

MODE_MIRROR 

Mirror mode - local webcam viewing (no network)

MODE_DISCOVERY_SERVICE 

Discovery server mode - session management and WebRTC signaling.

MODE_DISCOVERY 

Discovery mode - participant that can dynamically become host.

MODE_INVALID 

Invalid mode.

Definition at line 907 of file include/ascii-chat/options/options.h.

907 {
@ MODE_DISCOVERY_SERVICE
Discovery server mode - session management and WebRTC signaling.
@ MODE_INVALID
Invalid mode.
@ MODE_MIRROR
Mirror mode - local webcam viewing (no network)
@ MODE_DISCOVERY
Discovery mode - participant that can dynamically become host.

◆ audio_capture_source_t

#include <options.h>

Audio source policy for local capture and media playback.

Enumerator
AUDIO_CAPTURE_SOURCE_AUTO 
AUDIO_CAPTURE_SOURCE_MIC 
AUDIO_CAPTURE_SOURCE_MEDIA 
AUDIO_CAPTURE_SOURCE_BOTH 
AUDIO_CAPTURE_SOURCE_REMOTE 

Definition at line 278 of file include/ascii-chat/options/options.h.

◆ audio_source_t

#include <options.h>

Audio source selected for visualization.

Enumerator
AUDIO_SOURCE_ALL 

Combine local microphone, local media, and audio received from other participants

AUDIO_SOURCE_CALL 

Audio received from other participants in the call

AUDIO_SOURCE_MIC 

Local microphone input

AUDIO_SOURCE_MEDIA 

Local media playback

AUDIO_SOURCE_AUTO 

Backward-compatible spelling for all available visualization sources

Definition at line 264 of file include/ascii-chat/options/options.h.

◆ color_setting_t

#include <options.h>

Color output setting (–color flag values)

Enumeration for the –color option which controls color output behavior:

  • COLOR_SETTING_AUTO: Smart detection (default) - colors if TTY, not piping, not CLAUDECODE
  • COLOR_SETTING_TRUE: Force colors ON - always colorize regardless of TTY/piping/CLAUDECODE
  • COLOR_SETTING_FALSE: Force colors OFF - disable all colors and logging colors
Enumerator
COLOR_SETTING_AUTO 

Smart detection (default): colors if TTY and not CLAUDECODE and not piping.

COLOR_SETTING_TRUE 

Force colors ON: always colorize regardless of TTY/piping/CLAUDECODE.

COLOR_SETTING_FALSE 

Force colors OFF: disable all colors and logging colors.

Definition at line 231 of file include/ascii-chat/options/options.h.

231 {
color_setting_t
Color output setting (–color flag values)
@ COLOR_SETTING_TRUE
Force colors ON: always colorize regardless of TTY/piping/CLAUDECODE.
@ COLOR_SETTING_FALSE
Force colors OFF: disable all colors and logging colors.
@ COLOR_SETTING_AUTO
Smart detection (default): colors if TTY and not CLAUDECODE and not piping.

◆ option_group_t

#include <groups.h>

Option group identifiers.

Bit flags that identify groups of related options. These can be combined to specify which option groups a mode supports.

Enumerator
OPT_GROUP_NONE 

No groups.

OPT_GROUP_BINARY 

Binary-level options (help, version, logging)

OPT_GROUP_TERMINAL 

Terminal dimension options (width, height)

OPT_GROUP_NETWORK 

Network options (address, port, reconnect)

OPT_GROUP_WEBCAM 

Webcam options (device, flip, test pattern)

OPT_GROUP_DISPLAY 

Display options (color mode, palette, render mode)

OPT_GROUP_AUDIO 

Audio options (enable, devices, volume)

OPT_GROUP_SNAPSHOT 

Snapshot mode options.

OPT_GROUP_CRYPTO 

Encryption and authentication options.

OPT_GROUP_COMPRESSION 

Compression options.

OPT_GROUP_ACDS 

ACDS discovery service options.

OPT_GROUP_MEDIA 

Media file streaming options.

OPT_GROUP_SERVER 

Server-specific options (max_clients, client-keys)

OPT_GROUP_CLIENT 

Client-specific options (reconnect, server-key)

OPT_GROUP_WEBRTC 

WebRTC connectivity options (STUN/TURN)

Definition at line 50 of file groups.h.

50 {
51 OPT_GROUP_NONE = 0,
52 OPT_GROUP_BINARY = (1 << 0),
53 OPT_GROUP_TERMINAL = (1 << 1),
54 OPT_GROUP_NETWORK = (1 << 2),
55 OPT_GROUP_WEBCAM = (1 << 3),
56 OPT_GROUP_DISPLAY = (1 << 4),
57 OPT_GROUP_AUDIO = (1 << 5),
58 OPT_GROUP_SNAPSHOT = (1 << 6),
59 OPT_GROUP_CRYPTO = (1 << 7),
60 OPT_GROUP_COMPRESSION = (1 << 8),
61 OPT_GROUP_ACDS = (1 << 9),
62 OPT_GROUP_MEDIA = (1 << 10),
63 OPT_GROUP_SERVER = (1 << 11),
64 OPT_GROUP_CLIENT = (1 << 12),
65 OPT_GROUP_WEBRTC = (1 << 13),
option_group_t
Option group identifiers.
Definition groups.h:50
@ OPT_GROUP_DISPLAY
Display options (color mode, palette, render mode)
Definition groups.h:56
@ OPT_GROUP_CLIENT
Client-specific options (reconnect, server-key)
Definition groups.h:64
@ OPT_GROUP_AUDIO
Audio options (enable, devices, volume)
Definition groups.h:57
@ OPT_GROUP_SNAPSHOT
Snapshot mode options.
Definition groups.h:58
@ OPT_GROUP_TERMINAL
Terminal dimension options (width, height)
Definition groups.h:53
@ OPT_GROUP_NONE
No groups.
Definition groups.h:51
@ OPT_GROUP_WEBCAM
Webcam options (device, flip, test pattern)
Definition groups.h:55
@ OPT_GROUP_MEDIA
Media file streaming options.
Definition groups.h:62

◆ option_mode_bitmask_t

#include <options.h>

Option mode bitmask.

Indicates which modes an option applies to. Options can apply to multiple modes by combining bitmasks with bitwise OR.

Enumerator
OPTION_MODE_NONE 

No modes (invalid)

OPTION_MODE_SERVER 

Server mode (bit 0)

OPTION_MODE_CLIENT 

Client mode (bit 1)

OPTION_MODE_MIRROR 

Mirror mode (bit 2)

OPTION_MODE_DISCOVERY_SVC 

Discovery server mode (bit 3)

OPTION_MODE_DISCOVERY 

Discovery mode (bit 4)

OPTION_MODE_BINARY 

Binary-level options (parsed before mode detection)

OPTION_MODE_ALL 

All modes + binary.

Definition at line 922 of file include/ascii-chat/options/options.h.

922 {
923 OPTION_MODE_NONE = 0,
929 OPTION_MODE_BINARY = 0x100,
930 OPTION_MODE_ALL = 0x1F | 0x100
@ OPTION_MODE_DISCOVERY
Discovery mode (bit 4)
@ OPTION_MODE_NONE
No modes (invalid)

◆ utf8_setting_t

#include <options.h>

UTF-8 support setting (–utf8 flag values)

Enumeration for the –utf8 option which controls UTF-8 support behavior:

  • UTF8_SETTING_AUTO: Smart detection (default) - UTF-8 if terminal supports it
  • UTF8_SETTING_TRUE: Force UTF-8 ON - always use UTF-8 regardless of terminal capability
  • UTF8_SETTING_FALSE: Force UTF-8 OFF - disable UTF-8 support
Enumerator
UTF8_SETTING_AUTO 

Smart detection (default): UTF-8 if terminal supports it.

UTF8_SETTING_TRUE 

Force UTF-8 ON: always use UTF-8 regardless of terminal capability.

UTF8_SETTING_FALSE 

Force UTF-8 OFF: disable UTF-8 support.

Definition at line 250 of file include/ascii-chat/options/options.h.

250 {
utf8_setting_t
UTF-8 support setting (–utf8 flag values)
@ UTF8_SETTING_AUTO
Smart detection (default): UTF-8 if terminal supports it.
@ UTF8_SETTING_FALSE
Force UTF-8 OFF: disable UTF-8 support.
@ UTF8_SETTING_TRUE
Force UTF-8 ON: always use UTF-8 regardless of terminal capability.

Function Documentation

◆ escape_groff_special()

const char * escape_groff_special ( const char *  str)

#include <manpage.h>

Escape special groff/troff characters in text.

Converts special characters (-, \, etc.) to groff escape sequences so they display correctly in man pages.

Parameters
strInput string to escape
Returns
Escaped string (static buffer, valid until next call)
Note
Result is stored in static buffer, do not free

Definition at line 38 of file options/manpage.c.

38 {
39 return str ? str : "";
40}

Referenced by manpage_content_generate_examples(), manpage_content_generate_options(), manpage_content_generate_positional(), and manpage_content_generate_usage().

◆ find_section()

const parsed_section_t * find_section ( const parsed_section_t *  sections,
size_t  num_sections,
const char *  section_name 
)

#include <manpage.h>

Find section by name.

Parameters
sectionsArray of parsed sections
num_sectionsNumber of sections
section_nameSection name to find (case-sensitive)
Returns
Pointer to section, or NULL if not found

Definition at line 709 of file options/manpage.c.

709 {
710 return manpage_parser_find_section(sections, num_sections, section_name);
711}
const parsed_section_t * manpage_parser_find_section(const parsed_section_t *sections, size_t count, const char *section_name)
Find section by name.
Definition parser.c:400

References manpage_parser_find_section().

◆ format_mode_names()

const char * format_mode_names ( option_mode_bitmask_t  bitmask)

#include <manpage.h>

Format option mode names as human-readable string.

Converts bitmask of modes into a readable format for man pages. For example: MODE_BINARY | MODE_SERVER -> "binary, server"

Parameters
bitmaskBitmask of option_mode_t values
Returns
Formatted string (static buffer, valid until next call)
Note
Result is stored in static buffer, do not free

Definition at line 42 of file options/manpage.c.

42 {
43 if (mode_bitmask == 0) {
44 return NULL;
45 }
46
47 if ((mode_bitmask & OPTION_MODE_BINARY) && !(mode_bitmask & 0x1F)) {
48 return "global";
49 }
50
51 option_mode_bitmask_t user_modes_mask = (1 << MODE_SERVER) | (1 << MODE_CLIENT) | (1 << MODE_MIRROR) |
53 if ((mode_bitmask & user_modes_mask) == user_modes_mask) {
54 // If all user modes are set, check if binary-level is also set
55 if (mode_bitmask & OPTION_MODE_BINARY) {
56 return "global, client, server, mirror, discovery-service";
57 }
58 return "all modes";
59 }
60
61 static char mode_str[256];
62 mode_str[0] = '\0';
63 int pos = 0;
64
65 // Add global if binary-level is set
66 if (mode_bitmask & OPTION_MODE_BINARY) {
67 pos += safe_snprintf(mode_str + pos, sizeof(mode_str) - pos, "global");
68 }
69
70 // List modes in order: default, client, server, mirror, discovery-service
71 if (mode_bitmask & (1 << MODE_DISCOVERY)) {
72 pos += safe_snprintf(mode_str + pos, sizeof(mode_str) - pos, "%sdefault", pos > 0 ? ", " : "");
73 }
74 if (mode_bitmask & (1 << MODE_CLIENT)) {
75 pos += safe_snprintf(mode_str + pos, sizeof(mode_str) - pos, "%sclient", pos > 0 ? ", " : "");
76 }
77 if (mode_bitmask & (1 << MODE_SERVER)) {
78 pos += safe_snprintf(mode_str + pos, sizeof(mode_str) - pos, "%sserver", pos > 0 ? ", " : "");
79 }
80 if (mode_bitmask & (1 << MODE_MIRROR)) {
81 pos += safe_snprintf(mode_str + pos, sizeof(mode_str) - pos, "%smirror", pos > 0 ? ", " : "");
82 }
83 if (mode_bitmask & (1 << MODE_DISCOVERY_SERVICE)) {
84 pos += safe_snprintf(mode_str + pos, sizeof(mode_str) - pos, "%sdiscovery-service", pos > 0 ? ", " : "");
85 }
86
87 return pos > 0 ? mode_str : NULL;
88}
int safe_snprintf(char *buffer, size_t buffer_size, const char *format,...)
Safe formatted string printing to buffer.
Definition system.c:148

References MODE_CLIENT, MODE_DISCOVERY, MODE_DISCOVERY_SERVICE, MODE_MIRROR, MODE_SERVER, OPTION_MODE_BINARY, and safe_snprintf().

Referenced by manpage_content_generate_options().

◆ free_parsed_sections()

void free_parsed_sections ( parsed_section_t *  sections,
size_t  num_sections 
)

#include <manpage.h>

Free parsed sections array.

Parameters
sectionsArray of parsed sections (can be NULL)
num_sectionsNumber of sections

Definition at line 705 of file options/manpage.c.

705 {
706 manpage_parser_free_sections(sections, num_sections);
707}
void manpage_parser_free_sections(parsed_section_t *sections, size_t count)
Free parsed sections array.
Definition parser.c:383

References manpage_parser_free_sections().

◆ has_action_flag()

bool has_action_flag ( void  )

#include <options.h>

Check if an action flag was detected.

Returns
true if an action flag was passed (–show-capabilities, –list-webcams, etc.)

Used by action implementations to enable output logging temporarily. When an action is passed, the logging system is disabled before the action runs to keep output clean. Actions can use this to re-enable logging if needed.

Returns
true if an action flag was passed

Definition at line 136 of file lib/options/options.c.

136 {
137 return g_action_flag;
138}

◆ is_remote_key_path()

bool is_remote_key_path ( const char *  key_path)

#include <validation.h>

Check if a key path is a remote/virtual key (not a local file)

Parameters
key_pathKey path to check
Returns
true if the key is remote (github:, gitlab:, gpg:, http://, https://), false otherwise

Remote keys are fetched at runtime and don't need file existence validation.

Definition at line 595 of file validation.c.

595 {
596 if (!key_path || key_path[0] == '\0') {
597 return false;
598 }
599 return (strncmp(key_path, "github:", 7) == 0 || strncmp(key_path, "gitlab:", 7) == 0 ||
600 strncmp(key_path, "gpg:", 4) == 0 || strncmp(key_path, "http://", 7) == 0 ||
601 strncmp(key_path, "https://", 8) == 0);
602}

Referenced by options_collect_identity_keys(), and options_init().

◆ levenshtein()

size_t levenshtein ( const char *  a,
const char *  b 
)

#include <levenshtein.h>

Calculate Levenshtein distance between two strings.

The Levenshtein distance is the minimum number of single-character edits (insertions, deletions, or substitutions) required to change one string into the other.

Parameters
aFirst string
bSecond string
Returns
Edit distance, or SIZE_MAX on error
See also
https://en.wikipedia.org/wiki/Levenshtein_distance

Definition at line 74 of file levenshtein.c.

74 {
75 if (!a || !b) {
76 return SIZE_MAX;
77 }
78
79 // Count UTF-8 characters instead of bytes
80 size_t char_count_a = utf8_char_count(a);
81 size_t char_count_b = utf8_char_count(b);
82
83 if (char_count_a == SIZE_MAX || char_count_b == SIZE_MAX) {
84 return SIZE_MAX; // Invalid UTF-8 in one of the strings
85 }
86
87 // Convert strings to codepoint arrays for UTF-8-aware comparison
88 if (char_count_a == 0) {
89 return char_count_b;
90 }
91 if (char_count_b == 0) {
92 return char_count_a;
93 }
94
95 uint32_t *codepoints_a = SAFE_MALLOC(char_count_a * sizeof(uint32_t), uint32_t *);
96 uint32_t *codepoints_b = SAFE_MALLOC(char_count_b * sizeof(uint32_t), uint32_t *);
97
98 if (!codepoints_a || !codepoints_b) {
99 SAFE_FREE(codepoints_a);
100 SAFE_FREE(codepoints_b);
101 return SIZE_MAX;
102 }
103
104 size_t decoded_a = utf8_to_codepoints(a, codepoints_a, char_count_a);
105 size_t decoded_b = utf8_to_codepoints(b, codepoints_b, char_count_b);
106
107 if (decoded_a == SIZE_MAX || decoded_b == SIZE_MAX) {
108 SAFE_FREE(codepoints_a);
109 SAFE_FREE(codepoints_b);
110 return SIZE_MAX;
111 }
112
113 // Compute Levenshtein distance on codepoint arrays
114 size_t *cache = SAFE_CALLOC(char_count_a * sizeof(size_t), 1, size_t *);
115 if (!cache) {
116 SAFE_FREE(codepoints_a);
117 SAFE_FREE(codepoints_b);
118 return SIZE_MAX;
119 }
120
121 // Initialize cache
122 for (size_t i = 0; i < char_count_a; i++) {
123 cache[i] = i + 1;
124 }
125
126 size_t distance = 0;
127 size_t result = 0;
128 for (size_t b_idx = 0; b_idx < char_count_b; b_idx++) {
129 uint32_t b_code = codepoints_b[b_idx];
130 result = distance = b_idx;
131
132 for (size_t a_idx = 0; a_idx < char_count_a; a_idx++) {
133 uint32_t a_code = codepoints_a[a_idx];
134 size_t b_distance = (a_code == b_code) ? distance : distance + 1;
135 distance = cache[a_idx];
136
137 cache[a_idx] = result = (distance > result) ? ((b_distance > result) ? result + 1 : b_distance)
138 : ((b_distance > distance) ? distance + 1 : b_distance);
139 }
140 }
141
142 SAFE_FREE(cache);
143 SAFE_FREE(codepoints_a);
144 SAFE_FREE(codepoints_b);
145
146 return result;
147}
unsigned int uint32_t
Definition common.h:58
#define SAFE_MALLOC(size, cast)
Definition common.h:264
#define SAFE_CALLOC(count, size, cast)
Definition common.h:274
size_t utf8_to_codepoints(const char *str, uint32_t *out_codepoints, size_t max_codepoints)
Convert UTF-8 string to array of Unicode codepoints.
Definition utf8.c:185
size_t utf8_char_count(const char *str)
Count UTF-8 characters (not bytes)
Definition utf8.c:136

References SAFE_CALLOC, SAFE_FREE, SAFE_MALLOC, utf8_char_count(), and utf8_to_codepoints().

Referenced by find_similar_option(), find_similar_option_with_mode(), and levenshtein_find_similar().

◆ levenshtein_find_similar()

const char * levenshtein_find_similar ( const char *  unknown,
const char *const *  candidates 
)

#include <levenshtein.h>

Find the most similar string from a NULL-terminated array.

Searches through an array of candidate strings to find the one most similar to the input string, using Levenshtein distance.

Parameters
unknownThe string to match against
candidatesNULL-terminated array of candidate strings
Returns
Best matching string, or NULL if no match within threshold

Definition at line 149 of file levenshtein.c.

149 {
150 if (!unknown || !candidates) {
151 return NULL;
152 }
153
154 const char *best_match = NULL;
155 size_t best_distance = SIZE_MAX;
156
157 for (int i = 0; candidates[i] != NULL; i++) {
158 size_t dist = levenshtein(unknown, candidates[i]);
159 if (dist < best_distance) {
160 best_distance = dist;
161 best_match = candidates[i];
162 }
163 }
164
165 // Only suggest if the distance is within our threshold
166 if (best_distance <= LEVENSHTEIN_SUGGESTION_THRESHOLD) {
167 return best_match;
168 }
169
170 return NULL;
171}
size_t levenshtein(const char *a, const char *b)
Calculate Levenshtein distance between two strings.
Definition levenshtein.c:74
#define LEVENSHTEIN_SUGGESTION_THRESHOLD
Maximum edit distance to suggest an option.
Definition levenshtein.h:32

References levenshtein(), and LEVENSHTEIN_SUGGESTION_THRESHOLD.

Referenced by asciichat_suggest_enum_value(), and asciichat_suggest_mode().

◆ levenshtein_n()

size_t levenshtein_n ( const char *  a,
const size_t  length,
const char *  b,
const size_t  bLength 
)

#include <levenshtein.h>

Calculate Levenshtein distance with explicit string lengths.

Parameters
aFirst string
lengthLength of first string
bSecond string
bLengthLength of second string
Returns
Edit distance, or SIZE_MAX on error

Definition at line 22 of file levenshtein.c.

22 {
23 // Shortcut optimizations / degenerate cases.
24 if (a == b) {
25 return 0;
26 }
27
28 if (length == 0) {
29 return bLength;
30 }
31
32 if (bLength == 0) {
33 return length;
34 }
35
36 size_t *cache = SAFE_CALLOC(length, sizeof(size_t), size_t *);
37 if (!cache) {
38 return SIZE_MAX; // Allocation failed
39 }
40
41 size_t index = 0;
42 size_t bIndex = 0;
43 size_t distance;
44 size_t bDistance;
45 size_t result = 0;
46 char code;
47
48 // initialize the vector.
49 while (index < length) {
50 cache[index] = index + 1;
51 index++;
52 }
53
54 // Loop.
55 while (bIndex < bLength) {
56 code = b[bIndex];
57 result = distance = bIndex++;
58
59 for (index = 0; index < length; index++) {
60 bDistance = code == a[index] ? distance : distance + 1;
61 distance = cache[index];
62
63 cache[index] = result = distance > result ? bDistance > result ? result + 1 : bDistance
64 : bDistance > distance ? distance + 1
65 : bDistance;
66 }
67 }
68
69 SAFE_FREE(cache);
70
71 return result;
72}

References SAFE_CALLOC, and SAFE_FREE.

◆ options_builder_generate_manpage_template()

asciichat_error_t options_builder_generate_manpage_template ( options_builder_t *  builder,
const char *  program_name,
const char *  mode_name,
const char *  output_path,
const char *  brief_description 
)

#include <manpage.h>

Generate man page template from builder (before build())

Convenience wrapper that builds the config from the builder, generates the template, and cleans up.

Parameters
builderOptions builder
program_nameProgram name
mode_nameMode name (or NULL)
output_pathOutput file path
brief_descriptionBrief description
Returns
ASCIICHAT_OK on success, error code otherwise

Definition at line 217 of file options/manpage.c.

219 {
220 if (!builder || !program_name || !brief_description) {
221 return SET_ERRNO(ERROR_INVALID_PARAM, "Missing required parameters");
222 }
223
224 const options_config_t *config = options_builder_build(builder);
225 if (!config) {
226 return SET_ERRNO(ERROR_CONFIG, "Failed to build options configuration");
227 }
228
229 return options_config_generate_manpage_template(config, program_name, mode_name, output_path, brief_description);
230}
#define SET_ERRNO(code, context_msg,...)
Set error code with custom context message and log it, returning the error code.
@ ERROR_CONFIG
Definition error_codes.h:57
@ ERROR_INVALID_PARAM
asciichat_error_t options_config_generate_manpage_template(const options_config_t *config, const char *program_name, const char *mode_name, const char *output_path, const char *brief_description)
Generate a man page template from options builder configuration.

References ERROR_CONFIG, ERROR_INVALID_PARAM, options_builder_build(), options_config_generate_manpage_template(), and SET_ERRNO.

◆ options_collect_identity_keys()

int options_collect_identity_keys ( options_t *  opts,
int  argc,
char *  argv[] 
)

#include <validation.h>

Collect multiple –key flags into identity_keys array.

Parameters
optsOptions structure to populate
argcArgument count from main()
argvArgument vector from main()
Returns
Number of keys collected on success, -1 on error

Scans command-line arguments for all –key/-K flags and populates both opts->encrypt_key (first key, for compatibility) and opts->identity_keys[] array with all keys. Also sets opts->num_identity_keys.

This enables multi-key support: servers can load multiple identity keys (e.g., both SSH and GPG) and select the appropriate one during handshake based on what the client expects.

Note
Must be called after options_parse() but before key loading
Supports up to MAX_IDENTITY_KEYS (32) keys

Collect multiple –key flags into identity_keys array

Scans argv for all –key or -K flags and populates:

  • opts->encrypt_key with the first key (backward compatibility)
  • opts->identity_keys[] with all keys
  • opts->num_identity_keys with count

This enables multi-key support for servers/ACDS that need to present different identity keys (SSH, GPG) based on client expectations.

Definition at line 527 of file validation.c.

527 {
528 if (!opts || !argv) {
529 log_error("options_collect_identity_keys: Invalid arguments");
530 return -1;
531 }
532
533 size_t key_count = 0;
534
535 // Scan argv for all --key or -K flags
536 for (int i = 1; i < argc && key_count < MAX_IDENTITY_KEYS; i++) {
537 const char *arg = argv[i];
538
539 // Check if this is a --key or -K flag
540 bool is_key_flag = false;
541 const char *key_value = NULL;
542
543 if (strcmp(arg, "--key") == 0 || strcmp(arg, "-K") == 0) {
544 // Next argument is the key path
545 if (i + 1 < argc) {
546 key_value = argv[i + 1];
547 is_key_flag = true;
548 i++; // Skip the value argument
549 }
550 } else if (strncmp(arg, "--key=", 6) == 0) {
551 // --key=value format
552 key_value = arg + 6;
553 is_key_flag = true;
554 }
555
556 if (is_key_flag && key_value && strlen(key_value) > 0) {
557 // Validate that local key files exist (skip for remote/virtual keys)
558 if (!is_remote_key_path(key_value)) {
559 struct stat st;
560 if (stat(key_value, &st) != 0) {
561 log_error("Key file not found: %s", key_value);
562 SET_ERRNO(ERROR_CRYPTO_KEY, "Key file not found: %s", key_value);
563 return -1;
564 }
565 // Check if it's a regular file (S_IFREG) - works on both Windows and Unix
566 if ((st.st_mode & S_IFMT) != S_IFREG) {
567 log_error("Key path is not a regular file: %s", key_value);
568 SET_ERRNO(ERROR_CRYPTO_KEY, "Key path is not a regular file: %s", key_value);
569 return -1;
570 }
571 }
572
573 // Store in identity_keys array
574 SAFE_STRNCPY(opts->identity_keys[key_count], key_value, OPTIONS_BUFF_SIZE);
575
576 // First key also goes into encrypt_key for backward compatibility
577 if (key_count == 0) {
578 SAFE_STRNCPY(opts->encrypt_key, key_value, OPTIONS_BUFF_SIZE);
579 }
580
581 key_count++;
582 log_debug("Collected identity key #%zu: %s", key_count, key_value);
583 }
584 }
585
586 opts->num_identity_keys = key_count;
587
588 if (key_count > 0) {
589 log_info("Collected %zu identity key(s) for multi-key support", key_count);
590 }
591
592 return (int)key_count;
593}
#define SAFE_STRNCPY(dst, src, size)
Definition common.h:414
@ ERROR_CRYPTO_KEY
Definition error_codes.h:97
#define log_info(...)
Log an INFO message.
Definition log/log.h:561
#define log_debug(...)
Log a DEBUG message.
Definition log/log.h:548
#define MAX_IDENTITY_KEYS
Maximum number of identity keys that can be loaded (for multi-key support)
#define OPTIONS_BUFF_SIZE
Buffer size for option string values.
bool is_remote_key_path(const char *key_path)
Check if a key path is a remote/virtual key (not a local file)
Definition validation.c:595
size_t num_identity_keys
Number of identity keys loaded (0 means single-key mode using encrypt_key)
char identity_keys[32][256]
All identity keys (populated when –key is used multiple times)
char encrypt_key[256]
SSH/GPG key file path (first –key flag, kept for compatibility)

References options_state::encrypt_key, ERROR_CRYPTO_KEY, options_state::identity_keys, is_remote_key_path(), log_debug, log_error, log_info, MAX_IDENTITY_KEYS, options_state::num_identity_keys, OPTIONS_BUFF_SIZE, SAFE_STRNCPY, and SET_ERRNO.

Referenced by options_init().

◆ options_config_generate_final_manpage()

asciichat_error_t options_config_generate_final_manpage ( const char *  template_path,
const char *  output_path,
const char *  version_string,
const char *  content_file_path 
)

#include <manpage.h>

Generate final man page (.1) from template (.1.in) with version substitution and optional content file.

Parameters
template_pathPath to input template (.1.in file)
output_pathPath to output man page (.1 file)
version_stringVersion string to substitute for @PROJECT_VERSION@
content_file_pathOptional path to file containing additional content to insert (NULL if none)
Returns
ASCIICHAT_OK on success, error code on failure
Note
This performs the same substitution as ConfigureManPage.cmake
If content_file_path is provided, its content is parsed and merged into appropriate sections
Only available in debug builds (when NDEBUG is not defined)

Definition at line 714 of file options/manpage.c.

715 {
716 (void)template_path;
717 (void)output_path;
718 (void)version_string;
719 (void)content_file_path;
720 return SET_ERRNO(ERROR_CONFIG, "Not implemented in refactored version");
721}

References ERROR_CONFIG, and SET_ERRNO.

◆ options_config_generate_manpage_merged()

asciichat_error_t options_config_generate_manpage_merged ( const options_config_t *  config,
const char *  program_name,
const char *  mode_name,
const char *  output_path,
const char *  brief_description 
)

#include <manpage.h>

Generate merged man page template preserving manual content.

Parses existing template (if exists) and merges auto-generated content with manual content based on section markers:

  • AUTO sections: Fully regenerated from builder
  • MANUAL sections: Preserved exactly as-is
  • MERGE sections: Intelligently merged (e.g., ENVIRONMENT vars)
Parameters
configFinalized options configuration
program_nameProgram name (e.g., "ascii-chat")
mode_nameMode name (e.g., "server", "client", or NULL)
output_pathFile path to write merged template to
brief_descriptionOne-line program description
existing_template_pathPath to existing template (NULL if none)
Returns
ASCIICHAT_OK on success, error code on failure
Note
If existing_template_path is NULL, generates fresh template
Unmarked sections default to MANUAL (preserved)

Generate merged man page from options builder and embedded resources

Generates a merged man page by combining auto-generated content from the options builder with manual sections from embedded resources. Automatically selects between embedded resources (production builds) and filesystem resources (development builds).

Resource Loading:

  • Template: Loads ascii-chat.1.in template from embedded resources or filesystem
  • Content: Loads ascii-chat.1.content sections from embedded resources or filesystem
  • Merging: Intelligently merges AUTO, MANUAL, and MERGE-marked sections
Parameters
configFinalized options configuration
program_nameProgram name (e.g., "ascii-chat")
mode_nameMode name (e.g., "server", "client", or NULL for binary-level)
output_pathOutput file path, or NULL to write to stdout
brief_descriptionOne-line program description
Returns
ASCIICHAT_OK on success, error code on failure
Note
If output_path is NULL, writes to stdout
Resources are loaded automatically from embedded or filesystem based on build type
Deprecated:
The old signature with explicit file paths is no longer supported. Resources are now loaded automatically from embedded resources.

Definition at line 232 of file options/manpage.c.

234 {
235 (void)mode_name; // Not used
236 (void)program_name; // Not used
237 (void)brief_description; // Not used
238
239 if (!config) {
240 return SET_ERRNO(ERROR_INVALID_PARAM, "config is required for man page generation");
241 }
242
243 FILE *f = NULL;
244 bool should_close = false;
245
246 if (output_path && strlen(output_path) > 0 && strcmp(output_path, "-") != 0) {
247 // Check if file already exists and prompt for confirmation
248 struct stat st;
249 if (stat(output_path, &st) == 0) {
250 // File exists - ask user if they want to overwrite
251 log_plain("Man page file already exists: %s", output_path);
252
253 bool overwrite = platform_prompt_yes_no("Overwrite", false); // Default to No
254 if (!overwrite) {
255 log_plain("Man page generation cancelled.");
256 return SET_ERRNO(ERROR_FILE_OPERATION, "User cancelled overwrite");
257 }
258
259 log_plain("Overwriting existing man page file...");
260 }
261
262 f = platform_fopen("file_stream", output_path, "w");
263 if (!f) {
264 return SET_ERRNO_SYS(ERROR_CONFIG, "Failed to open output file: %s", output_path);
265 }
266 should_close = true;
267 } else {
268 f = stdout;
269 }
270
271 // Load template resources
272 manpage_resources_t resources;
273 memset(&resources, 0, sizeof(resources));
275 if (err != ASCIICHAT_OK) {
276 if (should_close)
277 fclose(f);
278 return err;
279 }
280
281 if (!manpage_resources_is_valid(&resources)) {
282 if (should_close)
283 fclose(f);
284 manpage_resources_destroy(&resources);
285 return SET_ERRNO(ERROR_CONFIG, "Man page resources are not valid");
286 }
287
288 // Process template and merge AUTO sections with generated content
289 const char *template_content = resources.template_content;
290 const char *p = template_content;
291
292 bool in_auto_section = false;
293 char current_auto_section[128] = "";
294 bool found_section_header = false;
295
296 // Track MERGE sections
297 bool in_merge_section = false;
298 bool merge_content_generated = false;
299 char current_merge_section[64] = {0};
300
301 // For ENVIRONMENT MERGE section: collect manual variables
302 const char **manual_env_vars = NULL;
303 const char **manual_env_descs = NULL;
304 size_t manual_env_count = 0;
305 size_t manual_env_capacity = 0;
306
307 while (*p) {
308 // Find next line
309 const char *line_end = strchr(p, '\n');
310 if (!line_end) {
311 // Last line without newline
312 if (!in_auto_section && !in_merge_section) {
313 fputs(p, f);
314 }
315 break;
316 }
317
318 size_t line_len = (size_t)(line_end - p);
319
320 // Check for MERGE-START marker (only in current line)
321 bool has_merge_start = false;
322 if (strstr(p, "MERGE-START:") != NULL && strstr(p, "MERGE-START:") < line_end) {
323 has_merge_start = true;
324 }
325
326 if (has_merge_start) {
327 in_merge_section = true;
328 merge_content_generated = false;
329 manual_env_count = 0; // Reset manual variable collection
330 manual_env_capacity = 0;
331
332 // Extract section name (e.g., "ENVIRONMENT" from "MERGE-START: ENVIRONMENT")
333 const char *section_start = strstr(p, "MERGE-START:");
334 if (section_start && section_start < line_end) {
335 section_start += strlen("MERGE-START:");
336 while (*section_start && section_start < line_end && isspace(*section_start))
337 section_start++;
338
339 const char *section_name_end = section_start;
340 // Advance until we reach line_end (the newline position)
341 while (section_name_end < line_end) {
342 section_name_end++;
343 }
344
345 // Trim trailing whitespace
346 while (section_name_end > section_start && isspace(*(section_name_end - 1))) {
347 section_name_end--;
348 }
349
350 size_t section_name_len = (size_t)(section_name_end - section_start);
351 if (section_name_len > 0 && section_name_len < sizeof(current_merge_section)) {
352 // Use memcpy instead of SAFE_STRNCPY because source is not null-terminated
353 memcpy(current_merge_section, section_start, section_name_len);
354 current_merge_section[section_name_len] = '\0';
355 }
356 }
357
358 // Do NOT write the MERGE-START marker line - it's an internal control marker
359 // The content will be generated when MERGE-END is encountered
360
361 // For ENVIRONMENT MERGE sections, find and preserve the .SH header
362 // (skipping over any marker comment lines that might be in between)
363 if (strcmp(current_merge_section, "ENVIRONMENT") == 0) {
364 const char *search_line = line_end + 1;
365 bool found_header = false;
366 while (*search_line && !found_header) {
367 const char *search_line_end = strchr(search_line, '\n');
368 if (!search_line_end)
369 break;
370
371 // Check if this is the .SH header
372 if (strncmp(search_line, ".SH ", 4) == 0) {
373 // Write the .SH header line
374 size_t header_len = (size_t)(search_line_end - search_line);
375 fwrite(search_line, 1, header_len + 1, f);
376 p = search_line_end + 1;
377 found_header = true;
378 break;
379 }
380
381 // Skip marker comment lines (.\" MANUAL-*, .\" MERGE-*, etc)
382 if (strncmp(search_line, ".\\\" ", 4) == 0) {
383 search_line = search_line_end + 1;
384 continue; // Keep searching
385 }
386
387 // If we hit a non-marker, non-.SH line, stop searching
388 break;
389 }
390 if (found_header) {
391 continue; // Skip the "Skip to next line" at line 359
392 }
393 }
394
395 // Skip to next line
396 p = line_end + 1;
397 continue;
398
399 } else if (in_merge_section && strstr(p, "MERGE-END:") != NULL && strstr(p, "MERGE-END:") < line_end) {
400 // Before writing MERGE-END, generate content if not already done
401 if (!merge_content_generated) {
402 if (strcmp(current_merge_section, "ENVIRONMENT") == 0) {
403 log_debug("[MANPAGE] Generating ENVIRONMENT with %zu manual + %zu auto variables", manual_env_count,
404 config->num_descriptors);
405 char *env_content = manpage_content_generate_environment_with_manual(config, manual_env_vars,
406 manual_env_count, manual_env_descs);
407 if (env_content && *env_content != '\0') {
408 log_debug("[MANPAGE] Writing ENVIRONMENT content: %zu bytes", strlen(env_content));
409 fprintf(f, "%s", env_content);
410 } else {
411 log_warn("[MANPAGE] ENVIRONMENT content is empty!");
412 }
414 }
415 merge_content_generated = true;
416 }
417
418 in_merge_section = false;
419 // Do NOT write the MERGE-END marker line - it's an internal control marker
420 memset(current_merge_section, 0, sizeof(current_merge_section));
421
422 // Free collected manual variables (strings first, then arrays)
423 for (size_t i = 0; i < manual_env_count; i++) {
424 if (manual_env_vars && manual_env_vars[i]) {
425 char *var_name = (char *)manual_env_vars[i];
426 SAFE_FREE(var_name);
427 }
428 if (manual_env_descs && manual_env_descs[i]) {
429 char *var_desc = (char *)manual_env_descs[i];
430 SAFE_FREE(var_desc);
431 }
432 }
433 if (manual_env_vars) {
434 SAFE_FREE(manual_env_vars);
435 manual_env_vars = NULL;
436 }
437 if (manual_env_descs) {
438 SAFE_FREE(manual_env_descs);
439 manual_env_descs = NULL;
440 }
441 manual_env_count = 0;
442 manual_env_capacity = 0;
443
444 p = line_end + 1;
445 continue;
446
447 } else if (in_merge_section) {
448 // Within MERGE section: collect manual environment variables for ENVIRONMENT section
449 if (strcmp(current_merge_section, "ENVIRONMENT") == 0) {
450 // Check if line starts with ".TP" or ".B " (groff markers)
451 bool is_tp_marker = (line_len >= 3 && strncmp(p, ".TP", 3) == 0 && (line_len == 3 || isspace(p[3])));
452 bool is_b_marker = (line_len >= 3 && strncmp(p, ".B ", 3) == 0);
453
454 if (is_b_marker) {
455 // Extract variable name after ".B "
456 const char *var_start = p + 3;
457 while (*var_start && var_start < line_end && isspace(*var_start))
458 var_start++;
459
460 const char *var_end = var_start;
461 while (var_end < line_end && *var_end && *var_end != '\n') {
462 var_end++;
463 }
464
465 size_t var_len = (size_t)(var_end - var_start);
466 if (var_len > 0) {
467 char *var_name = SAFE_MALLOC(var_len + 1, char *);
468 memcpy(var_name, var_start, var_len);
469 var_name[var_len] = '\0';
470
471 // Trim trailing whitespace
472 while (var_len > 0 && isspace(var_name[var_len - 1])) {
473 var_name[--var_len] = '\0';
474 }
475
476 // Store the variable name
477 if (manual_env_count >= manual_env_capacity) {
478 manual_env_capacity = manual_env_capacity == 0 ? 16 : manual_env_capacity * 2;
479 manual_env_vars =
480 SAFE_REALLOC(manual_env_vars, manual_env_capacity * sizeof(const char *), const char **);
481 manual_env_descs =
482 SAFE_REALLOC(manual_env_descs, manual_env_capacity * sizeof(const char *), const char **);
483 }
484
485 manual_env_vars[manual_env_count] = var_name;
486 manual_env_descs[manual_env_count] = NULL; // Will be set from next line(s)
487 manual_env_count++;
488 }
489 } else if (manual_env_count > 0 && !is_tp_marker && !is_b_marker) {
490 // This is likely a description line following a ".B var_name" line
491 // Set description for the last collected variable
492 const char *desc_start = p;
493 while (*desc_start && desc_start < line_end && isspace(*desc_start))
494 desc_start++;
495
496 size_t desc_len = (size_t)(line_end - desc_start);
497 if (desc_len > 0 && manual_env_descs[manual_env_count - 1] == NULL) {
498 char *desc = SAFE_MALLOC(desc_len + 1, char *);
499 memcpy(desc, desc_start, desc_len);
500 desc[desc_len] = '\0';
501
502 // Trim trailing whitespace
503 while (desc_len > 0 && isspace(desc[desc_len - 1])) {
504 desc[--desc_len] = '\0';
505 }
506
507 if (desc_len > 0) {
508 manual_env_descs[manual_env_count - 1] = desc;
509 } else {
510 SAFE_FREE(desc);
511 }
512 }
513 }
514 // For ENVIRONMENT MERGE sections, do NOT write template content
515 // All content will be generated at MERGE-END
516 p = line_end + 1;
517 continue;
518 } else {
519 // For other MERGE sections (if any), write template content as-is
520 fwrite(p, 1, line_len + 1, f);
521 p = line_end + 1;
522 continue;
523 }
524 }
525
526 // Check for AUTO-START marker (only in current line)
527 bool has_auto_start = false;
528 const char *temp = p;
529 while (temp < line_end) {
530 if (strstr(temp, "AUTO-START:") != NULL) {
531 const char *found = strstr(temp, "AUTO-START:");
532 if (found < line_end) {
533 has_auto_start = true;
534 break;
535 }
536 // strstr() found it but it's beyond this line, so keep looking
537 temp = found + 1;
538 } else {
539 break;
540 }
541 }
542
543 if (has_auto_start) {
544 in_auto_section = true;
545 found_section_header = false;
546
547 // Extract section name (e.g., "SYNOPSIS" from "AUTO-START: SYNOPSIS")
548 const char *section_start = strstr(p, "AUTO-START:");
549 if (section_start && section_start < line_end) {
550 section_start += strlen("AUTO-START:");
551 while (*section_start && section_start < line_end && isspace(*section_start))
552 section_start++;
553
554 const char *section_name_end = section_start;
555 // Extract section name: everything from section_start until line_end
556 // Do NOT use a condition based on '\n' - just advance until we reach line_end
557 while (section_name_end < line_end) {
558 section_name_end++;
559 }
560
561 // Now trim trailing whitespace from the section name
562 while (section_name_end > section_start && isspace(*(section_name_end - 1))) {
563 section_name_end--;
564 }
565
566 size_t section_name_len = (size_t)(section_name_end - section_start);
567 if (section_name_len > 0 && section_name_len < sizeof(current_auto_section)) {
568 // Use memcpy instead of SAFE_STRNCPY because source is not null-terminated
569 // SAFE_STRNCPY uses strlcpy which only copies size-1 bytes
570 memcpy(current_auto_section, section_start, section_name_len);
571 current_auto_section[section_name_len] = '\0';
572 log_debug("[MANPAGE] Found AUTO-START section: '%s' (%zu bytes)", current_auto_section, section_name_len);
573 }
574 }
575
576 // Don't write the AUTO-START marker line - it's an internal control comment
577
578 } else if (strstr(p, "AUTO-END:") != NULL && strstr(p, "AUTO-END:") < line_end) {
579 in_auto_section = false;
580 // Don't write the AUTO-END marker line - it's an internal control comment
581 memset(current_auto_section, 0, sizeof(current_auto_section));
582 found_section_header = false;
583
584 } else if (in_auto_section) {
585 // Preserve comment lines that come before the section header
586 if (!found_section_header && strstr(p, ".\\\"") != NULL && strstr(p, ".\\\"") < line_end) {
587 // Skip comment lines in AUTO sections that contain "auto-generated" text
588 if (strstr(p, "auto-generated") == NULL) {
589 // Non-marker comment lines can be preserved
590 fwrite(p, 1, line_len + 1, f);
591 }
592 // Skip comment lines with "auto-generated" marker text
593 } else if (!found_section_header && strstr(p, ".SH ") != NULL && strstr(p, ".SH ") < line_end) {
594 // If this is the section header line (.SH ...), write it and generate content
595 fwrite(p, 1, line_len + 1, f);
596 found_section_header = true;
597
598 // Generate content for this AUTO section
599 if (strcmp(current_auto_section, "SYNOPSIS") == 0) {
600 log_debug("[MANPAGE] Generating SYNOPSIS section");
601 char *synopsis_content = NULL;
602 size_t synopsis_len = 0;
603 asciichat_error_t gen_err = manpage_merger_generate_synopsis(NULL, &synopsis_content, &synopsis_len);
604 log_debug("[MANPAGE] SYNOPSIS: err=%d, len=%zu", gen_err, synopsis_len);
605 if (gen_err == ASCIICHAT_OK && synopsis_content && synopsis_len > 0) {
606 fprintf(f, "%s", synopsis_content);
607 manpage_merger_free_content(synopsis_content);
608 }
609 } else if (strcmp(current_auto_section, "POSITIONAL ARGUMENTS") == 0) {
610 log_debug("[MANPAGE] Generating POSITIONAL ARGUMENTS (config has %zu args)", config->num_positional_args);
611 char *pos_content = manpage_content_generate_positional(config);
612 if (pos_content) {
613 size_t pos_len = strlen(pos_content);
614 log_debug("[MANPAGE] POSITIONAL ARGUMENTS: %zu bytes", pos_len);
615 if (*pos_content != '\0') {
616 fprintf(f, "%s", pos_content);
617 }
618 }
620 } else if (strcmp(current_auto_section, "USAGE") == 0) {
621 log_debug("[MANPAGE] Generating USAGE (config has %zu usage lines)", config->num_usage_lines);
622 char *usage_content = NULL;
623 size_t usage_len = 0;
624 asciichat_error_t usage_err = manpage_merger_generate_usage(config, &usage_content, &usage_len);
625 log_debug("[MANPAGE] USAGE: err=%d, len=%zu", usage_err, usage_len);
626 if (usage_err == ASCIICHAT_OK && usage_content && usage_len > 0) {
627 fprintf(f, "%s", usage_content);
628 manpage_merger_free_content(usage_content);
629 }
630 } else if (strcmp(current_auto_section, "EXAMPLES") == 0) {
631 log_debug("[MANPAGE] Generating EXAMPLES (config has %zu examples)", config->num_examples);
632 char *examples_content = manpage_content_generate_examples(config);
633 if (examples_content) {
634 size_t ex_len = strlen(examples_content);
635 log_debug("[MANPAGE] EXAMPLES: %zu bytes", ex_len);
636 if (*examples_content != '\0') {
637 fprintf(f, "%s", examples_content);
638 }
639 }
640 manpage_content_free_examples(examples_content);
641 } else if (strcmp(current_auto_section, "OPTIONS") == 0) {
642 log_debug("[MANPAGE] Generating OPTIONS (config has %zu descriptors)", config->num_descriptors);
643 char *options_content = manpage_content_generate_options(config);
644 if (options_content) {
645 size_t opt_len = strlen(options_content);
646 log_debug("[MANPAGE] OPTIONS: %zu bytes", opt_len);
647 if (*options_content != '\0') {
648 fprintf(f, "%s", options_content);
649 }
650 }
651 manpage_content_free_options(options_content);
652 }
653 }
654 // Otherwise skip old content between section header and AUTO-END
655 } else {
656 // Write all manual content (not in AUTO section)
657 // Skip marker comment lines: .\" AUTO-*, .\" MANUAL-*, .\" MERGE-*
658 // These are internal build-time control comments
659 bool is_marker_comment = (strstr(p, ".\\\" AUTO-") != NULL && strstr(p, ".\\\" AUTO-") < line_end) ||
660 (strstr(p, ".\\\" MANUAL-") != NULL && strstr(p, ".\\\" MANUAL-") < line_end) ||
661 (strstr(p, ".\\\" MERGE-") != NULL && strstr(p, ".\\\" MERGE-") < line_end);
662 if (!is_marker_comment) {
663 fwrite(p, 1, line_len + 1, f);
664 }
665 }
666
667 // Move to next line
668 p = line_end + 1;
669 }
670
671 manpage_resources_destroy(&resources);
672
673 fflush(f);
674 if (should_close) {
675 fclose(f);
676 }
677
678 log_debug("Generated merged man page to %s", output_path ? output_path : "stdout");
679 return ASCIICHAT_OK;
680}
char * manpage_content_generate_environment_with_manual(const options_config_t *config, const char **manual_vars, size_t manual_count, const char **manual_descs)
Generate ENVIRONMENT section with manual and auto variables.
Definition environment.c:74
void manpage_content_free_environment(char *content)
Free generated environment content.
char * manpage_content_generate_examples(const options_config_t *config)
Generate EXAMPLES section content.
Definition examples.c:79
void manpage_content_free_examples(char *content)
Free generated examples content.
Definition examples.c:147
#define SAFE_REALLOC(ptr, size, cast)
Definition common.h:284
#define SET_ERRNO_SYS(code, context_msg,...)
Set error code with custom message and system error context, returning the error code.
@ ERROR_FILE_OPERATION
#define log_warn(...)
Log a WARN message.
Definition log/log.h:574
#define log_plain(...)
Plain logging - writes to both log file and stderr without timestamps or log levels.
Definition log/log.h:618
bool platform_prompt_yes_no(const char *question, bool default_yes)
Prompt the user for a yes/no answer.
Definition util.c:83
FILE * platform_fopen(const char *name, const char *filename, const char *mode)
Safe file open stream (fopen replacement)
char * manpage_content_generate_options(const options_config_t *config)
Generate OPTIONS section content.
void manpage_content_free_options(char *content)
Free generated options content.
asciichat_error_t manpage_merger_generate_usage(const options_config_t *config, char **out_content, size_t *out_len)
Get auto-generated usage content.
Definition merger.c:79
void manpage_merger_free_content(char *content)
Free merged section content.
Definition merger.c:179
asciichat_error_t manpage_merger_generate_synopsis(const char *mode_name, char **out_content, size_t *out_len)
Get auto-generated synopsis content.
Definition merger.c:137
void manpage_content_free_positional(char *content)
Free generated positional content.
Definition positional.c:74
char * manpage_content_generate_positional(const options_config_t *config)
Generate POSITIONAL ARGUMENTS section content.
Definition positional.c:15
void manpage_resources_destroy(manpage_resources_t *resources)
Cleanup allocated man page resources.
Definition resources.c:151
asciichat_error_t manpage_resources_load(manpage_resources_t *resources)
Load man page resources from embedded or filesystem sources.
Definition resources.c:107
bool manpage_resources_is_valid(const manpage_resources_t *resources)
Check if resources are available.
Definition resources.c:180
Man page resource container.
Definition resources.h:22
const char * template_content
Template file content (.1.in)
Definition resources.h:23
size_t num_usage_lines
Number of usage lines.
Definition builder.h:417
size_t num_positional_args
Number of positional arguments.
Definition builder.h:409
size_t num_descriptors
Number of descriptors.
Definition builder.h:403
size_t num_examples
Number of examples.
Definition builder.h:420

References ASCIICHAT_OK, ERROR_CONFIG, ERROR_FILE_OPERATION, ERROR_INVALID_PARAM, log_debug, log_plain, log_warn, manpage_content_free_environment(), manpage_content_free_examples(), manpage_content_free_options(), manpage_content_free_positional(), manpage_content_generate_environment_with_manual(), manpage_content_generate_examples(), manpage_content_generate_options(), manpage_content_generate_positional(), manpage_merger_free_content(), manpage_merger_generate_synopsis(), manpage_merger_generate_usage(), manpage_resources_destroy(), manpage_resources_is_valid(), manpage_resources_load(), options_config_t::num_descriptors, options_config_t::num_examples, options_config_t::num_positional_args, options_config_t::num_usage_lines, platform_fopen(), platform_prompt_yes_no(), SAFE_FREE, SAFE_MALLOC, SAFE_REALLOC, SET_ERRNO, SET_ERRNO_SYS, and manpage_resources_t::template_content.

Referenced by action_create_manpage(), and options_init().

◆ options_config_generate_manpage_template()

asciichat_error_t options_config_generate_manpage_template ( const options_config_t *  config,
const char *  program_name,
const char *  mode_name,
const char *  output_path,
const char *  brief_description 
)

#include <manpage.h>

Generate a man page template from options builder configuration.

Creates a man page template at the specified path with auto-generated sections:

  • .TH (title/header)
  • NAME (auto: program-name — brief description)
  • SYNOPSIS (auto: generated from usage descriptors)
  • USAGE (auto: generated from usage descriptors with descriptions)
  • OPTIONS (auto: generated from option descriptors, grouped by category)
  • EXAMPLES (auto: generated from example descriptors)
  • POSITIONAL ARGUMENTS (auto: generated from positional arg descriptors)
  • ENVIRONMENT VARIABLES (auto: extracted from options with env_var_name set)

Manual sections provided as placeholders:

  • DESCRIPTION (manual: add program overview)
  • FILES (manual: add configuration files)
  • NOTES (manual: add additional notes)
  • BUGS (manual: add known bugs)
  • AUTHOR (manual: add author information)
  • SEE ALSO (manual: add related commands)

Format Notes:

  • Output is in groff/troff format suitable for man command
  • Section headers use .SH directive
  • Bold/italic/constant formatting uses .B, .I, .C directives
  • Options are formatted with short form (-x) and long form (–long-name)

Example Usage:

options_builder_t *builder = options_builder_create(sizeof(server_options_t));
// ... add options ...
builder,
"ascii-chat",
"server",
"ascii-chat-server.1",
"Interactive terminal-based video chat"
);
if (err != ASCIICHAT_OK) {
fprintf(stderr, "Failed to generate man page\n");
}
asciichat_error_t options_builder_generate_manpage_template(options_builder_t *builder, const char *program_name, const char *mode_name, const char *output_path, const char *brief_description)
Generate man page template from builder (before build())
Parameters
configFinalized options configuration (from options_builder_build)
program_nameProgram name (e.g., "ascii-chat")
mode_nameMode name (e.g., "server", "client", or NULL for binary-level)
output_pathFile path to write man page template to
brief_descriptionOne-line program description
Returns
ASCIICHAT_OK on success, ERROR_USAGE or ERROR_FILE on failure
Note
Creates or overwrites the file at output_path
Brief description should be short and start with lowercase
Man section number is auto-determined (1 for user commands, 5 for files)

Definition at line 94 of file options/manpage.c.

96 {
97 if (!config || !program_name || !brief_description) {
98 return SET_ERRNO(ERROR_INVALID_PARAM, "Missing required parameters for man page generation");
99 }
100
101 FILE *f = NULL;
102 bool should_close = false;
103
104 if (output_path) {
105 f = platform_fopen("file_stream", output_path, "w");
106 if (!f) {
107 return SET_ERRNO_SYS(ERROR_CONFIG, "Failed to open output file: %s", output_path);
108 }
109 should_close = true;
110 } else {
111 f = stdout;
112 }
113
114 // Write title/header
115 manpage_fmt_write_title(f, program_name, mode_name, brief_description);
116
117 // Write SYNOPSIS
118 char *synopsis_content = NULL;
119 size_t synopsis_len = 0;
120 asciichat_error_t err = manpage_merger_generate_synopsis(mode_name, &synopsis_content, &synopsis_len);
121 if (err == ASCIICHAT_OK && synopsis_content && synopsis_len > 0) {
122 fprintf(f, "%s", synopsis_content);
123 manpage_merger_free_content(synopsis_content);
124 }
125
126 // Write POSITIONAL ARGUMENTS if present
127 if (config->num_positional_args > 0) {
128 char *pos_content = manpage_content_generate_positional(config);
129 if (pos_content && *pos_content != '\0') {
130 fprintf(f, "%s", pos_content);
131 }
133 }
134
135 // Write DESCRIPTION
136 manpage_fmt_write_section(f, "DESCRIPTION");
137 fprintf(f, ".B ascii-chat\nis a terminal-based video chat application that converts webcam video to ASCII\n");
138 fprintf(f, "art in real-time. It enables video chat directly in your terminal, whether you're\n");
139 fprintf(f, "using a local console, a remote SSH session, or any terminal emulator.\n");
141
142 // Write USAGE
143 char *usage_content = NULL;
144 size_t usage_len = 0;
145 err = manpage_merger_generate_usage(config, &usage_content, &usage_len);
146 if (err == ASCIICHAT_OK && usage_content && usage_len > 0) {
147 fprintf(f, ".SH USAGE\n%s", usage_content);
148 manpage_merger_free_content(usage_content);
149 }
150
151 // Write OPTIONS
152 if (config->num_descriptors > 0) {
153 char *options_content = manpage_content_generate_options(config);
154 if (options_content && *options_content != '\0') {
155 fprintf(f, "%s", options_content);
156 }
157 manpage_content_free_options(options_content);
158 }
159
160 // Write EXAMPLES if present
161 if (config->num_examples > 0) {
162 char *examples_content = manpage_content_generate_examples(config);
163 if (examples_content && *examples_content != '\0') {
164 fprintf(f, "%s", examples_content);
165 }
166 manpage_content_free_examples(examples_content);
167 }
168
169 // Write ENVIRONMENT if present
170 bool has_env_vars = false;
171 for (size_t i = 0; i < config->num_descriptors; i++) {
172 if (config->descriptors[i].env_var_name) {
173 has_env_vars = true;
174 break;
175 }
176 }
177
178 if (has_env_vars) {
179 char *env_content = manpage_content_generate_environment(config);
180 if (env_content && *env_content != '\0') {
181 fprintf(f, "%s", env_content);
182 }
184 }
185
186 // Write placeholder manual sections
187 manpage_fmt_write_section(f, "FILES");
188 fprintf(f, ".I ~/.ascii-chat/config.toml\n");
189 fprintf(f, "User configuration file\n");
191
192 manpage_fmt_write_section(f, "NOTES");
193 fprintf(f, "For more information and examples, visit the project repository.\n");
195
196 manpage_fmt_write_section(f, "BUGS");
197 fprintf(f, "Report bugs at the project issue tracker.\n");
199
200 manpage_fmt_write_section(f, "AUTHOR");
201 fprintf(f, "Contributed by the ascii-chat community.\n");
203
204 manpage_fmt_write_section(f, "SEE ALSO");
205 fprintf(f, ".B man(1),\n");
206 fprintf(f, ".B groff_man(7)\n");
208
209 if (should_close) {
210 fclose(f);
211 }
212
213 log_debug("Generated man page template to %s", output_path ? output_path : "stdout");
214 return ASCIICHAT_OK;
215}
char * manpage_content_generate_environment(const options_config_t *config)
Generate ENVIRONMENT VARIABLES section content.
Definition environment.c:14
void manpage_fmt_write_title(FILE *f, const char *program_name, const char *mode_name, const char *brief_description)
Write groff title/header (.TH directive)
Definition formatter.c:75
void manpage_fmt_write_section(FILE *f, const char *section_name)
Write a section header directive.
Definition formatter.c:28
void manpage_fmt_write_blank_line(FILE *f)
Write a blank line (for spacing between sections)
Definition formatter.c:35
const char * env_var_name
Environment variable fallback (or NULL)
Definition builder.h:260
option_descriptor_t * descriptors
Array of option descriptors.
Definition builder.h:402

References ASCIICHAT_OK, options_config_t::descriptors, option_descriptor_t::env_var_name, ERROR_CONFIG, ERROR_INVALID_PARAM, log_debug, manpage_content_free_environment(), manpage_content_free_examples(), manpage_content_free_options(), manpage_content_free_positional(), manpage_content_generate_environment(), manpage_content_generate_examples(), manpage_content_generate_options(), manpage_content_generate_positional(), manpage_fmt_write_blank_line(), manpage_fmt_write_section(), manpage_fmt_write_title(), manpage_merger_free_content(), manpage_merger_generate_synopsis(), manpage_merger_generate_usage(), options_config_t::num_descriptors, options_config_t::num_examples, options_config_t::num_positional_args, platform_fopen(), SET_ERRNO, and SET_ERRNO_SYS.

Referenced by options_builder_generate_manpage_template().

◆ options_get()

const options_t * options_get ( void  )

#include <options.h>

Get current options (lock-free read)

Get pointer to current options struct (lock-free, thread-safe)

Returns a pointer to the current options struct. This pointer is guaranteed to remain valid for the lifetime of your function (no one will free it under you).

Performance: Single atomic pointer load (~1-2ns on modern CPUs)

Returns
Pointer to current options (never NULL after options_init())

Returns a pointer to the currently published options struct or a safe static default if options haven't been initialized yet (before options_state_init()) or after options have been destroyed (options_state_destroy()).

This function is specifically designed to be safe to call:

  • Before initialization: Early startup code gets sensible defaults
  • During normal operation: Lock-free atomic load with no blocking
  • During cleanup: atexit handlers and shutdown code get fallback defaults
  • After destruction: Code after options_state_destroy() still works

Return Behavior

Returns pointer to either:

  1. Published dynamic options (after options_state_init() and before options_state_destroy())
  2. Static fallback defaults (before init or after destroy)

The static fallback ensures:

  • Never NULL: Code never crashes on NULL pointer dereference
  • Sensible defaults: All OPT_*_DEFAULT constants properly initialized
  • Static lifetime: Outlives all dynamically allocated options structs
  • No cleanup needed: Static memory never freed
  • Thread-safe: Immutable const data, safe to read from any thread

Usage Example

// Safe before options_state_init()
const options_t *opts = options_get();
printf("Default width: %d\n", opts->width);
// After initialization
options_state_set(&parsed_opts);
opts = options_get(); // Returns published struct
// Safe after options_state_destroy()
opts = options_get(); // Returns static defaults again
// Can still safely call GET_OPTION() in atexit handlers
Returns
Pointer to options_t struct (guaranteed never NULL)
Note
Thread-safe: Lock-free atomic load with acquire semantics
Lock-free: No mutexes or blocking calls
Fast: Single atomic instruction (~1-2ns latency)
Fallback support: Returns static defaults when not initialized or after destroy
See also
options_state_init() - Initializes Options Module, publishes to RCU
options_state_destroy() - Clears Options Module, returns fallback to defaults
GET_OPTION() - Convenience macro for reading single fields

Definition at line 496 of file rcu.c.

496 {
497 // Lock-free read with acquire semantics
498 // Guarantees we see all writes made before the pointer was published
499 options_t *current = atomic_ptr_load(&g_options);
500
501 // If options not yet published or after destruction, return safe static default.
502 // This is a critical fallback that:
503 // 1. Allows early startup code (before options_state_init) to work safely
504 // 2. Allows atexit handlers and cleanup code to call GET_OPTION() safely
505 // 3. Never crashes due to NULL pointer access
506 // 4. Provides sensible defaults for all option fields
507 //
508 // The static default options are:
509 // - Allocated once at startup (static storage)
510 // - Never freed (outlives all dynamically allocated options)
511 // - Thread-safe to read (immutable const data)
512 // - Complete with all OPT_*_DEFAULT values matching options_t_new()
513 if (!current) {
514 return (const options_t *)&g_default_options;
515 }
516
517 return current;
518}
void * atomic_ptr_load(atomic_ptr_t *a)
Atomically load a pointer.
Definition atomic.c:272

References atomic_ptr_load().

Referenced by client_crypto_init(), client_main(), discovery_session_create(), discovery_session_process(), log_set_terminal_output(), main(), nat_detect_quality(), options_set_bool(), options_set_double(), options_set_int(), options_set_string(), server_crypto_handshake(), session_settings_from_options(), terminal_screen_render(), and threaded_send_terminal_size_with_auto_detect().

◆ options_get_help_text()

const char * options_get_help_text ( asciichat_mode_t  mode,
const char *  option_name 
)

#include <options.h>

Get help text for an option in a specific mode.

Parameters
modeThe mode context (MODE_SERVER, MODE_CLIENT, MODE_MIRROR, etc.)
option_nameThe long name of the option (e.g., "color-mode", "fps")
Returns
Help text string (const, do not modify), or NULL if option doesn't apply to mode

Searches the options registry for the given option name and mode combination. Returns the help text if the option applies to the mode, or NULL otherwise.

Example:

const char *help = options_get_help_text(MODE_CLIENT, "color-mode");
if (help) {
printf("Help: %s\n", help);
} else {
printf("Option not applicable to client mode\n");
}
const char * options_get_help_text(asciichat_mode_t mode, const char *option_name)
Get help text for an option in a specific mode.
Definition help_api.c:27
Note
Returned pointer is valid for the lifetime of the program (points to static data)
Thread-safe: reads only static registry data
Parameters
modeThe mode context (MODE_SERVER, MODE_CLIENT, MODE_MIRROR, etc.)
option_nameThe long name of the option (e.g., "color-mode", "fps")
Returns
Help text string, or NULL if option doesn't apply to this mode

Searches the options registry for the given option name and checks if it applies to the requested mode. If it does, returns the help text. If the option doesn't exist or doesn't apply to the mode, returns NULL.

Definition at line 27 of file help_api.c.

27 {
28 if (!option_name || !option_name[0]) {
29 return NULL;
30 }
31
32 // Get the registry (initializes size if needed)
34
35 // Search through all registered options
36 for (size_t i = 0; i < g_registry_size; i++) {
37 const registry_entry_t *entry = &g_options_registry[i];
38
39 // Skip empty entries or entries without a long name
40 if (!entry->long_name || !entry->long_name[0]) {
41 continue;
42 }
43
44 // Check if this is the option we're looking for
45 if (strcmp(entry->long_name, option_name) != 0) {
46 continue;
47 }
48
49 // Check if this option applies to the requested mode
50 // Convert mode to bitmask for comparison
51 // Validate mode is in valid range (0-4) to avoid undefined behavior in bit shift
52 if (mode < 0 || mode >= MODE_INVALID) {
53 continue; // Skip invalid mode
54 }
55 uint32_t mode_bitmask = (1 << mode) | OPTION_MODE_BINARY;
56
57 if ((entry->mode_bitmask & mode_bitmask) == 0) {
58 // Option doesn't apply to this mode
59 return NULL;
60 }
61
62 // Return the help text (or empty string if not set)
63 return entry->help_text ? entry->help_text : "";
64 }
65
66 // Option not found
67 return NULL;
68}
void registry_init_size(void)
Initialize registry size and metadata.
Definition core.c:108
registry_entry_t g_options_registry[2048]
Definition registry.c:51
size_t g_registry_size
Definition registry.c:53
Registry entry - stores option definition with mode bitmask and metadata.
const char * long_name
option_mode_bitmask_t mode_bitmask
const char * help_text

References g_options_registry, g_registry_size, registry_entry_t::help_text, registry_entry_t::long_name, registry_entry_t::mode_bitmask, MODE_INVALID, OPTION_MODE_BINARY, and registry_init_size().

Referenced by get_help_text().

◆ options_init()

asciichat_error_t options_init ( int  argc,
char **  argv 
)

#include <options.h>

Initialize options by parsing command-line arguments with unified mode detection.

Parameters
argcArgument count from main()
argvArgument vector from main()
Returns
ASCIICHAT_OK on success, ERROR_USAGE on parse errors

Main Entry Point for the options system. This function:

  1. Mode Detection: Analyzes argv to detect requested mode:
    • Looks for mode keywords: "server", "client", "mirror", "acds", etc.
    • Checks for session string pattern (word-word-word) → client mode
    • Falls back to showing help if no mode specified
  2. Binary-Level Parsing: Processes global options before mode detection:
    • --help / -h → prints help and exits
    • --version / -v → prints version and exits
    • --verbose / -V → sets verbose level (stackable)
    • --quiet / -q → sets quiet mode
    • --log-file / -L → redirects logs to file
    • --log-level → sets log verbosity level
  3. Mode-Specific Parsing: Uses mode-specific parser for detected mode:
    • Parses mode-specific options (server, client, mirror, acds)
    • Each mode has different set of supported options
    • Options are validated with mode bitmasks
  4. Configuration File Loading (if –config specified):
    • Loads TOML config file
    • Merges config file settings with command-line options
    • Command-line takes precedence over config file
  5. Validation and Defaults:
    • Applies default values to unspecified options
    • Validates numeric ranges, file existence, formats
    • Performs cross-field validation (dependencies, conflicts)
    • Checks mode-specific constraints
  6. RCU Publishing:
    • Initializes RCU state if not already done
    • Publishes options struct for lock-free thread-safe access
    • After this point, GET_OPTION() and options_get() are valid

Detected Mode Storage: The detected mode is stored in options_t->detected_mode and accessible via:

asciichat_mode_t mode = GET_OPTION(detected_mode);

Mode Detection Examples:

# Mode keywords (explicit)
./ascii-chat server --port 8080 # MODE_SERVER
./ascii-chat client example.com:9000 # MODE_CLIENT
./ascii-chat mirror --color # MODE_MIRROR
./ascii-chat acds --port 27225 # MODE_DISCOVERY_SERVICE
# Session strings (implicit client mode)
./ascii-chat word-word-word # MODE_CLIENT (session string)
# Help/version (special handling)
./ascii-chat --help # Shows help, may exit(0)
./ascii-chat --version # Shows version, may exit(0)

Return Values:

  • ASCIICHAT_OK: Parsing succeeded normally
  • ERROR_USAGE: Parse error occurred (usage info printed to stderr)
  • Other error codes: Unexpected errors during initialization

Special Handling for –help and –version: These flags are handled specially and may cause early exit via exit(0). The function will still return ASCIICHAT_OK in these cases for compatibility.

Environment Variables: Some options can fall back to environment variables:

  • WEBCAM_DISABLED: Enable test pattern when set to "1", "true", etc.
  • ASCII_CHAT_KEY_PASSWORD: Fallback for encrypted key passphrases
  • SSH_AUTH_SOCK: For SSH agent key access

Typical Usage:

int main(int argc, char **argv) {
asciichat_error_t err = options_init(argc, argv);
if (err != ASCIICHAT_OK) {
// Could be ERROR_USAGE (invalid options) or other errors
// usage() already printed to stderr for ERROR_USAGE
return 1;
}
// Options now available via GET_OPTION() and options_get()
asciichat_mode_t mode = GET_OPTION(detected_mode);
const char *addr = GET_OPTION(address);
// ... application code ...
return 0;
}
Note
Must be called once at program startup before accessing options
Should be called before creating worker threads
Global option variables initialized by this function
Returns ERROR_USAGE for invalid options (usage already printed)
–help and –version may exit directly via exit(0)
After return, options accessible via GET_OPTION() macro

Initialize all command-line options from argv and environment variables

Main entry point for the options parsing system. This function:

  • Detects the mode from the first positional argument (server, client, mirror, etc.)
  • Parses binary-level options (–help, –version, –log-file, etc.)
  • Parses mode-specific options
  • Validates cross-field option dependencies
  • Initializes defaults from OPT_*_DEFAULT defines
  • Publishes options via RCU for thread-safe lock-free access

Option Processing Order:

  1. Binary-level options (parsed from all args regardless of mode)
  2. Mode detection (from first positional argument)
  3. Mode-specific options (from remaining args filtered by mode)
  4. Defaults initialization (for unspecified options)
  5. Cross-field validation (dependencies between options)
  6. RCU publication (make available to all threads)

Return Values:

  • ASCIICHAT_OK - Initialization successful
  • ERROR_USAGE - Invalid usage (help/usage already printed to stdout)
  • Other errors - Check errno context via HAS_ERRNO()

Special Behavior:

  • –help causes help text to print and exit(0) (never returns)
  • –version causes version to print and exit(0) (never returns)
Parameters
argcArgument count from main()
argvArgument vector from main()
Returns
ASCIICHAT_OK on success, ERROR_USAGE on parse error, other on fatal error
Note
Global option variables initialized by this function
Returns ERROR_USAGE for invalid options (usage already printed)
–help and –version may exit directly via exit(0)
After return, options accessible via GET_OPTION() macro

Definition at line 857 of file lib/options/options.c.

857 {
858 log_debug("options_init: starting with argc=%d, argv[0]=%s, argv[1]=%s", argc,
859 (argv && argc > 0 && argv[0]) ? argv[0] : "(null)", (argv && argc > 1 && argv[1]) ? argv[1] : "N/A");
860 // NOTE: --grep filter is initialized in main.c BEFORE any logging starts
861 // This allows ALL logs (including from shared_init) to be filtered
862 // Validate arguments (safety check for tests)
863 if (argc < 0 || argc > 128) {
864 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid argc: %d", argc);
865 }
866 if (argv == NULL) {
867 return SET_ERRNO(ERROR_INVALID_PARAM, "argv is NULL");
868 }
869 // Validate all argv elements are non-NULL up to argc
870 for (int i = 0; i < argc; i++) {
871 if (argv[i] == NULL) {
872 return SET_ERRNO(ERROR_INVALID_PARAM, "argv[%d] is NULL (argc=%d)", i, argc);
873 }
874 }
875 // Initialize RCU options system (must be done before any threads start)
876 // This must happen FIRST, before any other initialization
877 asciichat_error_t rcu_init_result = options_state_init();
878 if (rcu_init_result != ASCIICHAT_OK) {
879 return rcu_init_result;
880 }
881
882 // ========================================================================
883 // STAGE 1: Mode Detection and Binary-Level Option Handling
884 // ========================================================================
885
886 // Check for binary-level actions FIRST (before mode detection)
887 // These actions may take arguments, so we need to check them before mode detection
888 bool show_version = false;
889 bool create_config = false;
890 bool create_manpage = false;
891 bool has_action = false; // Track if any action flag is present
892 const char *config_create_path = NULL;
893 const char *manpage_create_path = NULL;
894
895 // ========================================================================
896 // STAGE 1A: Quick scan for action flags FIRST (they bypass mode detection)
897 // ========================================================================
898 // Quick scan for action flags (they may have arguments)
899 // This must happen BEFORE logging initialization so we can suppress logs before shared_init()
900 // Also scan for --quiet / -q so we can suppress logging from the start
901 // Also scan for --color early so it affects help output colors
902 bool user_quiet = false;
903 int parsed_color_setting = COLOR_SETTING_AUTO; // Store parsed color value until opts is created
904 bool color_setting_found = false;
905 bool check_update_flag_seen = false;
906 bool no_check_update_flag_seen = false;
907
908 // FIRST: Scan entire argv for --help (special case - works before OR after mode)
909 for (int i = 1; i < argc; i++) {
910 if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) {
911 // Scan backwards to find if a mode was specified before --help
912 asciichat_mode_t help_mode = MODE_DISCOVERY; // Default to discovery mode (binary-level help)
913 for (int j = i - 1; j >= 1; j--) {
914 if (argv[j][0] != '-') {
915 if (strcmp(argv[j], "server") == 0) {
916 help_mode = MODE_SERVER;
917 } else if (strcmp(argv[j], "client") == 0) {
918 help_mode = MODE_CLIENT;
919 } else if (strcmp(argv[j], "mirror") == 0) {
920 help_mode = MODE_MIRROR;
921 } else if (strcmp(argv[j], "discovery-service") == 0) {
922 help_mode = MODE_DISCOVERY_SERVICE;
923 } else if (strcmp(argv[j], "discovery") == 0) {
924 help_mode = MODE_DISCOVERY;
925 }
926 break; // Found first non-flag argument
927 }
928 }
929
930 // Show help for the detected mode (or binary-level if no mode)
931 usage(stdout, help_mode);
932 fflush(NULL);
933 _Exit(0);
934 }
935 }
936
937 // THEN: Scan for other binary-level actions (stops at mode name)
938 for (int i = 1; i < argc; i++) {
939 // Stop scanning at mode name - binary-level options must come before the mode
940 if (argv[i][0] != '-') {
941 bool is_mode =
942 (strcmp(argv[i], "server") == 0 || strcmp(argv[i], "client") == 0 || strcmp(argv[i], "mirror") == 0 ||
943 strcmp(argv[i], "discovery") == 0 || strcmp(argv[i], "discovery-service") == 0);
944 if (is_mode) {
945 break; // Stop processing at mode name
946 }
947 }
948 if (argv[i][0] == '-') {
949 // Handle --quiet, -q, --quiet=value formats
950 bool temp_quiet = false;
951 if (parse_binary_bool_arg(argv[i], &temp_quiet, "quiet", 'q')) {
952 if (temp_quiet) {
953 user_quiet = true;
954 }
955 }
956 // Validate --log-level and --log-file require arguments
957 if (strcmp(argv[i], "--log-level") == 0) {
958 if (i + 1 >= argc || argv[i + 1][0] == '-') {
959 log_error("--log-level requires a value (dev, debug, info, warn, error, fatal)");
960 return ERROR_USAGE;
961 }
962 i++; // Skip the argument in this loop
963 }
964 if (strcmp(argv[i], "-L") == 0 || strcmp(argv[i], "--log-file") == 0) {
965 if (i + 1 >= argc || argv[i + 1][0] == '-') {
966 log_error("%s requires a file path", argv[i]);
967 return ERROR_USAGE;
968 }
969 i++; // Skip the argument in this loop
970 }
971 // --help is handled in the first scan loop above (works before OR after mode)
972 // Parse --color early so it affects help output colors
973 if (strcmp(argv[i], "--color") == 0) {
974 if (i + 1 < argc && argv[i + 1][0] != '-') {
975 // Check if next arg is a mode name (not a color value)
976 const char *next_arg = argv[i + 1];
977 bool is_mode =
978 (strcmp(next_arg, "server") == 0 || strcmp(next_arg, "client") == 0 || strcmp(next_arg, "mirror") == 0 ||
979 strcmp(next_arg, "discovery") == 0 || strcmp(next_arg, "discovery-service") == 0);
980
981 if (is_mode) {
982 // Next arg is a mode, not a color value - default to true
983 parsed_color_setting = COLOR_SETTING_TRUE;
984 color_setting_found = true;
985 } else {
986 // Try to parse as color value
987 char *error_msg = NULL;
988 if (parse_color_setting(next_arg, &parsed_color_setting, &error_msg)) {
989 color_setting_found = true;
990 i++; // Skip the setting argument
991 } else {
992 if (error_msg) {
993 log_error("Error parsing --color: %s", error_msg);
994 free(error_msg);
995 }
996 }
997 }
998 } else {
999 // --color without argument defaults to true (enable colors)
1000 parsed_color_setting = COLOR_SETTING_TRUE;
1001 color_setting_found = true;
1002 }
1003 }
1004 // Check for --color=value format
1005 if (strncmp(argv[i], "--color=", 8) == 0) {
1006 const char *value = argv[i] + 8;
1007 char *error_msg = NULL;
1008 if (parse_color_setting(value, &parsed_color_setting, &error_msg)) {
1009 color_setting_found = true;
1010 } else {
1011 if (error_msg) {
1012 log_error("Error parsing --color: %s", error_msg);
1013 free(error_msg);
1014 }
1015 }
1016 }
1017 if (strcmp(argv[i], "--version") == 0 || strcmp(argv[i], "-v") == 0) {
1018 show_version = true;
1019 has_action = true;
1020 break;
1021 }
1022 if (strcmp(argv[i], "--check-update") == 0) {
1023 check_update_flag_seen = true;
1024 if (no_check_update_flag_seen) {
1025 log_error("Cannot specify both --check-update and --no-check-update");
1026 return ERROR_USAGE;
1027 }
1028 has_action = true;
1030 // action_check_update_immediate() calls _Exit(), so we don't reach here
1031 break;
1032 }
1033 if (strcmp(argv[i], "--no-check-update") == 0) {
1034 no_check_update_flag_seen = true;
1035 if (check_update_flag_seen) {
1036 log_error("Cannot specify both --check-update and --no-check-update");
1037 return ERROR_USAGE;
1038 }
1039 // Flag will be parsed normally later
1040 }
1041 if (strcmp(argv[i], "--config-create") == 0) {
1042 create_config = true;
1043 has_action = true;
1044 // Check for optional [FILE] argument
1045 if (i + 1 < argc && argv[i + 1][0] != '-') {
1046 config_create_path = argv[i + 1];
1047 i++; // Consume the file path argument
1048 }
1049 break;
1050 }
1051 if (strcmp(argv[i], "--man-page-create") == 0) {
1052 create_manpage = true;
1053 has_action = true;
1054 // Check for optional [FILE] argument
1055 if (i + 1 < argc && argv[i + 1][0] != '-') {
1056 manpage_create_path = argv[i + 1];
1057 i++; // Consume the file path argument
1058 }
1059 break;
1060 }
1061 if (strcmp(argv[i], "--completions") == 0) {
1062 has_action = true;
1063 // Handle --completions: generate shell completion scripts
1064 if (i + 1 < argc && argv[i + 1][0] != '-') {
1065 const char *shell_name = argv[i + 1];
1066 i++; // Consume shell name
1067
1068 // Check for optional output file
1069 const char *output_file = NULL;
1070 if (i + 1 < argc && argv[i + 1][0] != '-') {
1071 output_file = argv[i + 1];
1072 i++; // Consume output file
1073 }
1074
1075 action_completions(shell_name, output_file);
1076 // action_completions() calls _Exit(), so we don't reach here
1077 } else {
1078 log_error("--completions requires shell name (bash, fish, zsh, powershell)");
1079 return ERROR_USAGE;
1080 }
1081 break; // Unreachable, but for clarity
1082 }
1083 if (strcmp(argv[i], "--list-webcams") == 0) {
1084 has_action = true;
1086 // action_list_webcams() calls _Exit(), so we don't reach here
1087 break;
1088 }
1089 if (strcmp(argv[i], "--list-microphones") == 0) {
1090 has_action = true;
1092 // action_list_microphones() calls _Exit(), so we don't reach here
1093 break;
1094 }
1095 if (strcmp(argv[i], "--list-speakers") == 0) {
1096 has_action = true;
1098 // action_list_speakers() calls _Exit(), so we don't reach here
1099 break;
1100 }
1101 // Check for --show-capabilities (binary-level action)
1102 if (strcmp(argv[i], "--show-capabilities") == 0) {
1103 has_action = true;
1105 // action_show_capabilities_immediate() calls _Exit(), so we don't reach here
1106 break;
1107 }
1108 }
1109 }
1110 // Store action flag globally for use during cleanup
1111 set_action_flag(has_action);
1112 // ========================================================================
1113 // STAGE 1B: DO MODE DETECTION EARLY (needed for log_init)
1114 // ========================================================================
1115 asciichat_mode_t detected_mode = MODE_DISCOVERY; // Default mode
1116 char detected_session_string[SESSION_STRING_BUFFER_SIZE] = {0};
1117 int mode_index = -1;
1118
1119 asciichat_error_t mode_detect_result =
1120 options_detect_mode(argc, argv, &detected_mode, detected_session_string, &mode_index);
1121 if (mode_detect_result != ASCIICHAT_OK) {
1122 return mode_detect_result;
1123 }
1124
1125 // VALIDATE: Binary-level options must appear BEFORE the mode
1126 // Check if any binary-level options appear after the mode position
1127 if (mode_index > 0) {
1128 for (int i = mode_index + 1; i < argc; i++) {
1129 if (argv[i][0] == '-') {
1130 bool takes_arg = false;
1131 bool takes_optional_arg = false;
1132
1133 if (is_binary_level_option_with_args(argv[i], &takes_arg, &takes_optional_arg)) {
1134 return SET_ERRNO(ERROR_USAGE, "Binary-level option '%s' must appear before the mode '%s', not after it",
1135 argv[i], argv[mode_index]);
1136 }
1137 }
1138 }
1139 }
1140 // ========================================================================
1141 // STAGE 1C: Initialize logging EARLY (before any log_dev calls)
1142 // ========================================================================
1143 // Create local options struct and initialize with defaults
1144 options_t opts = options_t_new(); // Initialize with all defaults
1145 opts.no_check_update = no_check_update_flag_seen;
1146 opts.detected_mode = detected_mode;
1147 char *log_filename = options_get_log_filepath(detected_mode, opts);
1148 SAFE_SNPRINTF(opts.log_file, OPTIONS_BUFF_SIZE, "%s", log_filename);
1149 // Note: log_init() is called earlier in asciichat_shared_init() and will be
1150 // reconfigured with the actual log level and file in main.c after options are fully parsed
1151 // NOTE: --color detection now happens in src/main.c BEFORE asciichat_shared_init()
1152 // This ensures g_color_flag_passed and g_color_flag_value are set before any logging.
1153 //
1154 // NOTE: Timer system and shared subsystems are initialized by src/main.c
1155 // via asciichat_shared_init() BEFORE options_init() is called.
1156 // This allows options_init() to use properly configured logging.
1157
1158 // If an action flag is detected OR user passed --quiet, silence logs for clean output
1159 if (user_quiet || has_action) {
1160 log_set_terminal_output(false); // Suppress console logging for clean action output
1161 }
1162 // Apply parsed color setting from STAGE 1A
1163 if (color_setting_found) {
1164 opts.color = parsed_color_setting;
1165 } else {
1166 opts.color = OPT_COLOR_DEFAULT; // Apply default
1167 }
1168
1169 // If we found version/config-create/create-manpage, handle them immediately (before mode detection)
1170 if (show_version || create_config || create_manpage) {
1171 if (show_version) {
1172 opts.version = true;
1173 options_state_set(&opts);
1174 return ASCIICHAT_OK;
1175 } else if (create_config) {
1176 // Build the schema first so config_create_default can generate options from it
1177 const options_config_t *unified_config = options_preset_unified(NULL, NULL);
1178 if (unified_config) {
1179 asciichat_error_t schema_build_result = config_schema_build_from_configs(&unified_config, 1);
1180 if (schema_build_result != ASCIICHAT_OK) {
1181 // Schema build failed, but continue anyway
1182 (void)schema_build_result;
1183 }
1184 options_config_destroy(unified_config);
1185 }
1186
1187 // Call action handler which handles all output and prompts properly
1188 action_create_config(config_create_path);
1189 // action_create_config() calls _Exit(), so we don't reach here
1190 } else if (create_manpage) {
1191 // Call action handler which handles all output and prompts properly
1192 action_create_manpage(manpage_create_path);
1193 // action_create_manpage() calls _Exit(), so we don't reach here
1194 }
1195 }
1196
1197 // Mode detection and logging already initialized early in STAGE 1B/1C above
1198 // (moved earlier to ensure logging is available before any log_dev() calls)
1199
1200 // Check for binary-level options that can appear before or after mode
1201 // Search entire argv to find --quiet, --log-file, --log-level, -V, etc.
1202 // These are documented as binary-level options that can appear anywhere
1203 for (int i = 1; i < argc; i++) {
1204 if (argv[i][0] == '-') {
1205 // Handle -V and --verbose (stackable verbosity)
1206 if (strcmp(argv[i], "-V") == 0 || strcmp(argv[i], "--verbose") == 0) {
1207 // Check if next argument is a number (optional argument)
1208 if (i + 1 < argc && argv[i + 1][0] != '-') {
1209 // Try to parse as integer count
1210 char *endptr;
1211 long value = strtol(argv[i + 1], &endptr, 10);
1212 if (*endptr == '\0' && value >= 0 && value <= 100) {
1213 opts.verbose_level = (unsigned short int)value;
1214 i++; // Skip next argument
1215 continue;
1216 }
1217 }
1218 // No valid number, just increment
1219 opts.verbose_level++;
1220 }
1221 // Handle binary-level boolean options using the abstraction function
1222 // -q/--quiet, --json, --log-format-console all use the same parser
1223 if (parse_binary_bool_arg(argv[i], &opts.quiet, "quiet", 'q')) {
1224 continue;
1225 }
1226 if (parse_binary_bool_arg(argv[i], &opts.json, "json", '\0')) {
1227 continue;
1228 }
1229 if (parse_binary_bool_arg(argv[i], &opts.log_format_console_only, "log-format-console", '\0')) {
1230 continue;
1231 }
1232 // Handle --log-level LEVEL (set log threshold)
1233 if (strcmp(argv[i], "--log-level") == 0) {
1234 if (i + 1 >= argc) {
1235 log_error("--log-level requires a value (dev, debug, info, warn, error, fatal)");
1236 return ERROR_USAGE;
1237 }
1238 if (argv[i + 1][0] == '-') {
1239 log_error("--log-level requires a value (dev, debug, info, warn, error, fatal)");
1240 return ERROR_USAGE;
1241 }
1242 char *error_msg = NULL;
1243 if (parse_log_level(argv[i + 1], &opts.log_level, &error_msg)) {
1244 i++; // Skip the level argument
1245 } else {
1246 if (error_msg) {
1247 log_error("%s", error_msg);
1248 free(error_msg);
1249 } else {
1250 log_error("invalid log level value: %s", argv[i + 1]);
1251 }
1252 return ERROR_USAGE;
1253 }
1254 }
1255 // Handle -L and --log-file FILE (set log file path)
1256 if ((strcmp(argv[i], "-L") == 0 || strcmp(argv[i], "--log-file") == 0)) {
1257 if (i + 1 >= argc) {
1258 log_error("%s requires a file path", argv[i]);
1259 return ERROR_USAGE;
1260 }
1261 if (argv[i + 1][0] == '-') {
1262 log_error("%s requires a file path", argv[i]);
1263 return ERROR_USAGE;
1264 }
1265 SAFE_STRNCPY(opts.log_file, argv[i + 1], sizeof(opts.log_file));
1266 i++; // Skip the file argument
1267 }
1268 // NOTE: --color is now parsed in STAGE 1A (early, before help processing)
1269 // to ensure help output colors are applied correctly
1270 }
1271 }
1272
1273 if (show_version) {
1274 // Show binary-level version from src/main.c
1275 opts.version = true;
1276 options_state_set(&opts);
1277 return ASCIICHAT_OK;
1278 }
1279
1280 if (create_config) {
1281 // Handle --config-create: create default config file and exit
1282 // Use provided path or default to user config location
1283 char config_path[PLATFORM_MAX_PATH_LENGTH];
1284 if (config_create_path) {
1285 SAFE_STRNCPY(config_path, config_create_path, sizeof(config_path));
1286 } else {
1287 // Use default config path: ~/.ascii-chat/config.toml
1288 char *config_dir = get_config_dir();
1289 if (!config_dir) {
1290 log_error("Error: Failed to determine default config directory");
1291 return ERROR_CONFIG;
1292 }
1293 safe_snprintf(config_path, sizeof(config_path), "%sconfig.toml", config_dir);
1294 SAFE_FREE(config_dir);
1295 }
1296
1297 // Create config with default options
1298 asciichat_error_t result = config_create_default(config_path);
1299 if (result != ASCIICHAT_OK) {
1301 if (HAS_ERRNO(&err_ctx)) {
1302 log_error("Error creating config: %s", err_ctx.context_message);
1303 } else {
1304 log_error("Error: Failed to create config file at %s", config_path);
1305 }
1306 return result;
1307 }
1308
1309 log_plain("Created default config file at: %s", config_path);
1310 return ASCIICHAT_OK; // Return successfully after creating config
1311 }
1312
1313 if (create_manpage) {
1314 // Handle --create-man-page-template: generate merged man page template
1315 // The .1.in file is the existing template to read from (not the output)
1316 const char *existing_template_path = "share/man/man1/ascii-chat.1.in";
1317 options_config_t *config = options_preset_unified(NULL, NULL);
1318 if (!config) {
1319 log_error("Error: Failed to get binary options config");
1320 return ERROR_MEMORY;
1321 }
1322
1323 // Generate merged man page from embedded or filesystem resources
1324 // (existing_template_path and manpage_content_file parameters are no longer supported)
1325 asciichat_error_t err = options_config_generate_manpage_merged(config, "ascii-chat", NULL, existing_template_path,
1326 "Video chat in your terminal");
1327
1328 options_config_destroy(config);
1329
1330 if (err != ASCIICHAT_OK) {
1332 if (HAS_ERRNO(&err_ctx)) {
1333 log_error("%s", err_ctx.context_message);
1334 } else {
1335 log_error("Error: Failed to generate man page");
1336 }
1337 return err;
1338 }
1339
1340 log_plain("Generated man page: %s", existing_template_path);
1341 return ASCIICHAT_OK; // Return successfully after generating man page
1342 }
1343
1344 // ========================================================================
1345 // STAGE 2: Build argv for mode-specific parsing
1346 // ========================================================================
1347 // If mode was found, build argv with only arguments after the mode
1348 // If mode_index == -1, filter out binary-level options from all arguments
1349 int mode_argc = argc;
1350 char **mode_argv = (char **)argv;
1351 char **allocated_mode_argv = NULL; // Track if we need to free mode_argv
1352
1353 if (mode_index == -1) {
1354 // No explicit mode - filter binary-level options from entire argv
1355 int max_mode_argc = argc; // Worst case: no binary opts skipped
1356
1357 if (max_mode_argc > 256) {
1358 return SET_ERRNO(ERROR_INVALID_PARAM, "Too many arguments: %d", max_mode_argc);
1359 }
1360
1361 char **new_mode_argv = SAFE_MALLOC((size_t)(max_mode_argc + 1) * sizeof(char *), char **);
1362 if (!new_mode_argv) {
1363 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate mode_argv");
1364 }
1365 allocated_mode_argv = new_mode_argv;
1366
1367 new_mode_argv[0] = argv[0]; // Copy program name
1368
1369 // Copy all args except binary-level options
1370 int new_argv_idx = 1;
1371 for (int i = 1; i < argc; i++) {
1372 bool takes_arg = false;
1373 bool takes_optional_arg = false;
1374
1375 // Check if this is a binary-level option
1376 if (is_binary_level_option_with_args(argv[i], &takes_arg, &takes_optional_arg)) {
1377 // Skip argument if needed
1378 // NOLINTNEXTLINE(bugprone-branch-clone)
1379 if (takes_arg && i + 1 < argc) {
1380 i++; // Skip required argument
1381 } else if (takes_optional_arg && i + 1 < argc && argv[i + 1][0] != '-') {
1382 i++; // Skip optional argument
1383 }
1384 continue;
1385 }
1386 // Not a binary option, copy to mode_argv
1387 new_mode_argv[new_argv_idx++] = argv[i];
1388 }
1389
1390 mode_argc = new_argv_idx;
1391 new_mode_argv[mode_argc] = NULL;
1392 mode_argv = new_mode_argv;
1393 } else if (mode_index != -1) {
1394 // Mode found at position mode_index
1395 // Build new argv: [program_name, args_before_mode (no binary opts)..., args_after_mode...]
1396 // Binary-level options are not passed to mode-specific parsers
1397
1398 int args_after_mode = argc - mode_index - 1;
1399 // We'll calculate final argc after skipping binary options
1400 int max_mode_argc = 1 + (mode_index - 1) + args_after_mode; // Worst case: no binary opts skipped
1401
1402 if (max_mode_argc > 256) {
1403 return SET_ERRNO(ERROR_INVALID_PARAM, "Too many arguments: %d", max_mode_argc);
1404 }
1405
1406 // Allocate max_mode_argc+1 to accommodate NULL terminator
1407 char **new_mode_argv = SAFE_MALLOC((size_t)(max_mode_argc + 1) * sizeof(char *), char **);
1408 if (!new_mode_argv) {
1409 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate mode_argv");
1410 }
1411 allocated_mode_argv = new_mode_argv; // Track allocation for cleanup
1412
1413 // Copy: [program_name, args_before_mode (excluding binary opts)..., args_after_mode...]
1414 if (mode_index == 0) {
1415 // Mode is at argv[0], use "ascii-chat" as program name
1416 new_mode_argv[0] = "ascii-chat";
1417 } else {
1418 new_mode_argv[0] = argv[0];
1419 }
1420
1421 // Copy args before mode, skipping binary-level options
1422 int new_argv_idx = 1;
1423 for (int i = 1; i < mode_index; i++) {
1424 bool takes_arg = false;
1425 bool takes_optional_arg = false;
1426
1427 // Check if this is a binary-level option
1428 if (is_binary_level_option_with_args(argv[i], &takes_arg, &takes_optional_arg)) {
1429 // Skip argument if needed
1430 if (i + 1 < mode_index && (takes_arg || (takes_optional_arg && argv[i + 1][0] != '-'))) {
1431 i++; // Skip argument
1432 }
1433 continue;
1434 }
1435 // Not a binary option, copy to mode_argv
1436 new_mode_argv[new_argv_idx++] = argv[i];
1437 }
1438 // Copy args after mode, filtering out any binary-level options (they shouldn't appear here)
1439 int args_after_mode_idx = 0;
1440 for (int i = mode_index + 1; i < argc; i++) {
1441 bool takes_arg = false;
1442 bool takes_optional_arg = false;
1443
1444 // Check if this is a binary-level option (shouldn't appear after mode)
1445 if (is_binary_level_option_with_args(argv[i], &takes_arg, &takes_optional_arg)) {
1446 // Skip binary-level option and its argument if needed
1447 if (i + 1 < argc && (takes_arg || (takes_optional_arg && argv[i + 1][0] != '-'))) {
1448 i++; // Skip argument
1449 }
1450 continue; // Skip this option
1451 }
1452 // Not a binary option, copy to mode_argv
1453 new_mode_argv[new_argv_idx + args_after_mode_idx++] = argv[i];
1454 }
1455 // Calculate actual argc (program + filtered args before + filtered args after)
1456 mode_argc = new_argv_idx + args_after_mode_idx;
1457 new_mode_argv[mode_argc] = NULL;
1458
1459 mode_argv = new_mode_argv;
1460 }
1461
1462 // ========================================================================
1463 // STAGE 3: Set Mode-Specific Defaults
1464 // ========================================================================
1465 // Initialize all defaults using options_t_new_preserve_binary() to keep binary-level
1466 // options (--quiet, --verbose, --log-level, etc.) from being reset
1467 opts = options_t_new_preserve_binary(&opts);
1468 // If a session string was detected during mode detection, restore it after options reset
1469 // This ensures discovery mode knows which session to join
1470 if (detected_session_string[0] != '\0') {
1471 SAFE_STRNCPY(opts.session_string, detected_session_string, sizeof(opts.session_string));
1472 log_info("options_init: Detected session string from argv: '%s'", detected_session_string);
1473 }
1474
1475 // Encryption options default to disabled/empty
1476 opts.no_encrypt = 0;
1477 opts.encrypt_key[0] = '\0';
1478 opts.password[0] = '\0';
1479 opts.encrypt_keyfile[0] = '\0';
1480 opts.server_key[0] = '\0';
1481 opts.client_keys[0] = '\0';
1482 opts.palette_custom[0] = '\0';
1483
1484 // Set different default addresses for different modes
1485 if (detected_mode == MODE_CLIENT || detected_mode == MODE_MIRROR || detected_mode == MODE_DISCOVERY) {
1486 // Client/Mirror/Discovery: connects to localhost by default (discovery uses ACDS to find server)
1487 SAFE_SNPRINTF(opts.address, OPTIONS_BUFF_SIZE, "localhost");
1488 opts.address6[0] = '\0'; // Client doesn't use address6
1489 } else if (detected_mode == MODE_SERVER) {
1490 // Server: binds to 127.0.0.1 (IPv4) and ::1 (IPv6) by default
1491 SAFE_SNPRINTF(opts.address, OPTIONS_BUFF_SIZE, "127.0.0.1");
1492 // address6 is now a positional argument, not an option
1493 opts.address6[0] = '\0';
1494 } else if (detected_mode == MODE_DISCOVERY_SERVICE) {
1495 // ACDS: binds to all interfaces by default
1496 SAFE_SNPRINTF(opts.address, OPTIONS_BUFF_SIZE, "0.0.0.0");
1497 opts.address6[0] = '\0';
1498 }
1499
1500 // ========================================================================
1501 // STAGE 4: Build Dynamic Schema from Unified Options Config
1502 // ========================================================================
1503 // Build the config schema dynamically from the unified config
1504 // This generates TOML keys, CLI flags, categories, and types from builder data
1505 const options_config_t *unified_config = options_preset_unified(NULL, NULL);
1506 if (unified_config) {
1507 asciichat_error_t schema_build_result = config_schema_build_from_configs(&unified_config, 1);
1508 if (schema_build_result != ASCIICHAT_OK) {
1509 // Schema build failed, but continue with static schema as fallback
1510 (void)schema_build_result;
1511 } else {
1512 }
1513 options_config_destroy(unified_config);
1514 } else {
1515 }
1516
1517 // ========================================================================
1518 // STAGE 5: Load Configuration Files
1519 // ========================================================================
1520 // Extract binary-level options BEFORE config loading (config may reset them)
1521 binary_level_opts_t binary_before_config = extract_binary_level(&opts);
1522 // Publish options to RCU before config loading so that config logs get proper colors
1523 // from the parsed --color setting (e.g., --color=true)
1524 asciichat_error_t config_publish_result = options_state_set(&opts);
1525 (void)config_publish_result; // Suppress unused variable warning
1526 // Load config files - now uses detected_mode directly for bitmask validation
1527 // Save flip_x and flip_y as they should not be reset by config files
1528 // Also save encryption settings - they should only be controlled via CLI, not config file
1529 bool saved_flip_x_from_config = opts.flip_x;
1530 bool saved_flip_y_from_config = opts.flip_y;
1531 bool saved_encrypt_enabled = opts.encrypt_enabled;
1532 asciichat_error_t config_result = config_load_system_and_user(detected_mode, false, &opts);
1533 (void)config_result; // Continue with defaults and CLI parsing regardless of result
1534 bool fps_set_by_config = opts.fps != OPT_FPS_DEFAULT;
1535 // Restore binary-level options (don't let config override command-line options)
1536 restore_binary_level(&opts, &binary_before_config);
1537
1538 // Restore flip_x and flip_y - config shouldn't override the defaults
1539 opts.flip_x = saved_flip_x_from_config;
1540 opts.flip_y = saved_flip_y_from_config;
1541
1542 // Restore encrypt_enabled - it should only be set via CLI, not config file
1543 // The config can set key/password which auto-enables encryption, but the encrypt_enabled
1544 // flag itself should stay at its default unless the user explicitly passes --encrypt
1545 opts.encrypt_enabled = saved_encrypt_enabled;
1546
1547 // ========================================================================
1548 // STAGE 6: Parse Command-Line Arguments (Unified)
1549 // ========================================================================
1550 // Extract binary-level options BEFORE applying unified defaults
1551 // (which might override them from config files)
1552 binary_level_opts_t binary_before_defaults = extract_binary_level(&opts);
1553 asciichat_mode_t mode_saved_for_parsing = detected_mode; // CRITICAL: Save before defaults reset
1554 // Get unified config
1555 options_config_t *config = options_preset_unified(NULL, NULL);
1556 if (!config) {
1557 SAFE_FREE(allocated_mode_argv);
1558 return SET_ERRNO(ERROR_CONFIG, "Failed to create options configuration");
1559 }
1560 int remaining_argc;
1561 char **remaining_argv;
1562
1563 // Save flip_x and flip_y before applying defaults (should not be reset by defaults)
1564 bool saved_flip_x = opts.flip_x;
1565 bool saved_flip_y = opts.flip_y;
1566 // Apply defaults from unified config
1567 asciichat_error_t defaults_result = options_config_set_defaults(config, &opts);
1568 if (defaults_result != ASCIICHAT_OK) {
1569 options_config_destroy(config);
1570 SAFE_FREE(allocated_mode_argv);
1571 return defaults_result;
1572 }
1573 // Restore binary-level options (they should never be overridden by defaults)
1574 restore_binary_level(&opts, &binary_before_defaults);
1575
1576 // Restore flip_x and flip_y - they should keep the values from options_t_new()
1577 // unless explicitly set by the user (but defaults shouldn't override them)
1578 opts.flip_x = saved_flip_x;
1579 opts.flip_y = saved_flip_y;
1580
1581 // Restore detected_mode before parsing so mode validation works.
1582 opts.detected_mode = mode_saved_for_parsing;
1583
1584 // Save flip_x and flip_y before parsing - they should not be reset by the parser
1585 bool saved_flip_x_for_parse = opts.flip_x;
1586 bool saved_flip_y_for_parse = opts.flip_y;
1587 // Note: json is already saved via extract_binary_level/restore_binary_level mechanism
1588 // Parse mode-specific arguments
1589 option_mode_bitmask_t mode_bitmask = (1 << mode_saved_for_parsing);
1590 log_debug("About to parse options_config with mode_bitmask=%u, mode_argc=%d", mode_bitmask, mode_argc);
1591 asciichat_error_t result =
1592 options_config_parse(config, mode_argc, mode_argv, &opts, mode_bitmask, &remaining_argc, &remaining_argv);
1593 log_debug("options_config_parse returned: %d", result);
1594 // Restore flip_x and flip_y - they should keep their values unless explicitly overridden
1595 opts.flip_x = saved_flip_x_for_parse;
1596 opts.flip_y = saved_flip_y_for_parse;
1597 // json is already restored via the call to options_state_set which calls restore_binary_level
1598 if (result != ASCIICHAT_OK) {
1599 options_config_destroy(config);
1600 SAFE_FREE(allocated_mode_argv);
1601 // Convert ERROR_CONFIG to ERROR_USAGE for command-line parsing errors
1602 if (result == ERROR_CONFIG) {
1604 const char *error_detail = "invalid options";
1605 if (HAS_ERRNO(&err_ctx)) {
1606 error_detail = err_ctx.context_message;
1607 }
1608 return SET_ERRNO(ERROR_USAGE, "Failed to parse options: %s", error_detail);
1609 }
1610 return result;
1611 }
1612
1613 bool fps_set_by_cli = false;
1614 for (int i = 0; i < mode_argc; i++) {
1615 if (mode_argv[i] &&
1616 (strcmp(mode_argv[i], "--fps") == 0 || strncmp(mode_argv[i], "--fps=", strlen("--fps=")) == 0)) {
1617 fps_set_by_cli = true;
1618 break;
1619 }
1620 }
1621 const char *fps_env = SAFE_GETENV("ASCII_CHAT_FPS");
1622 opts.fps_explicitly_set = fps_set_by_config || fps_set_by_cli || (fps_env && fps_env[0] != '\0');
1623
1624 // ========================================================================
1625 // STAGE 6.5: Publish Parsed Options Early
1626 // ========================================================================
1627 // Publish options to RCU as soon as they're parsed
1628 // This ensures GET_OPTION() works during cleanup even if validation fails
1629 log_debug("Publishing parsed options to RCU before validation");
1630 asciichat_error_t early_publish = options_state_set(&opts);
1631 if (early_publish != ASCIICHAT_OK) {
1632 log_error("Failed to publish parsed options to RCU state early");
1633 options_config_destroy(config);
1634 SAFE_FREE(allocated_mode_argv);
1635 return early_publish;
1636 }
1637 log_debug("Successfully published options to RCU");
1638
1639 // Auto-enable custom palette if palette-chars was set
1640 if (opts.palette_custom[0] != '\0') {
1641 // palette-chars was set - always use PALETTE_CUSTOM (overrides any explicit --palette setting)
1643 opts.palette_custom_set = true;
1644 log_debug("Set PALETTE_CUSTOM because --palette-chars was provided");
1645
1646 // Validate palette characters for UTF-8 correctness
1647 if (!utf8_is_valid(opts.palette_custom)) {
1648 log_error("Error: --palette-chars contains invalid UTF-8 sequences");
1649 options_config_destroy(config);
1650 SAFE_FREE(allocated_mode_argv);
1651 return option_error_invalid();
1652 }
1653
1654 // Check if palette contains non-ASCII characters
1655 bool has_non_ascii = !utf8_is_ascii_only(opts.palette_custom);
1656 if (has_non_ascii) {
1657 // Non-ASCII characters require UTF-8 support
1658 // Check if UTF-8 is explicitly disabled or unavailable
1659 bool utf8_disabled = (opts.force_utf8 == COLOR_SETTING_FALSE);
1660 bool utf8_auto_unavailable = (opts.force_utf8 == COLOR_SETTING_AUTO && !terminal_supports_utf8());
1661
1662 if (utf8_disabled) {
1663 log_error("Error: --palette-chars contains non-ASCII characters but --utf8=false was specified");
1664 log_error(" Remove --utf8=false or use ASCII-only palette characters");
1665 options_config_destroy(config);
1666 SAFE_FREE(allocated_mode_argv);
1667 return option_error_invalid();
1668 }
1669
1670 if (utf8_auto_unavailable) {
1671 log_error("Error: --palette-chars contains non-ASCII characters but terminal does not support UTF-8");
1672 log_error(" Use --utf8=true to force UTF-8 mode or use ASCII-only palette characters");
1673 options_config_destroy(config);
1674 SAFE_FREE(allocated_mode_argv);
1675 return option_error_invalid();
1676 }
1677 }
1678 }
1679
1680 // Auto-enable encryption if key was provided
1681 if (opts.encrypt_key[0] != '\0') {
1682 // A key may be a local file, a remote reference, or inline key material.
1683 // Only validate filesystem paths when the value is actually path-like;
1684 // inline SSH/base64/password keys must not be rejected by stat().
1685 bool key_is_inline = !path_looks_like_path(opts.encrypt_key);
1686 if (!is_remote_key_path(opts.encrypt_key) && !key_is_inline) {
1687 struct stat st;
1688 if (stat(opts.encrypt_key, &st) != 0) {
1689 log_error("Key file not found: %s", opts.encrypt_key);
1690 options_config_destroy(config);
1691 SAFE_FREE(allocated_mode_argv);
1692 return SET_ERRNO(ERROR_CRYPTO_KEY, "Key file not found: %s", opts.encrypt_key);
1693 }
1694 if ((st.st_mode & S_IFMT) != S_IFREG) {
1695 log_error("Key path is not a regular file: %s", opts.encrypt_key);
1696 options_config_destroy(config);
1697 SAFE_FREE(allocated_mode_argv);
1698 return SET_ERRNO(ERROR_CRYPTO_KEY, "Key path is not a regular file: %s", opts.encrypt_key);
1699 }
1700 }
1701 opts.encrypt_enabled = 1;
1702 log_debug("Auto-enabled encryption because --key was provided");
1703 }
1704
1705 // Color filter validation and auto-enable
1706 if (opts.color_filter != COLOR_FILTER_NONE) {
1707 // Color filter requires color to be enabled
1708 if (opts.color == COLOR_SETTING_FALSE) {
1709 log_error("Error: --color-filter cannot be used with --color=false");
1710 options_config_destroy(config);
1711 SAFE_FREE(allocated_mode_argv);
1712 return option_error_invalid();
1713 }
1714 // Auto-enable color when color filter is specified
1716 log_debug("Auto-enabled color because --color-filter was provided");
1717 }
1718
1719 // Detect if splash or status_screen were explicitly set on command line
1720 for (int i = 0; i < mode_argc; i++) {
1721 if (mode_argv[i] &&
1722 (strcmp(mode_argv[i], "--splash-screen") == 0 || strncmp(mode_argv[i], "--splash-screen=", 16) == 0)) {
1723 // Parse the value if provided (--splash-screen=true/false)
1724 bool splash_value = true; // Default to true for plain --splash-screen
1725 if (strncmp(mode_argv[i], "--splash-screen=", 16) == 0) {
1726 const char *value = mode_argv[i] + 16;
1727 if (strcasecmp(value, "false") == 0 || strcasecmp(value, "no") == 0 || strcasecmp(value, "0") == 0 ||
1728 strcasecmp(value, "off") == 0) {
1729 splash_value = false;
1730 }
1731 }
1732 opts.splash_screen = splash_value;
1733 opts.splash_screen_explicitly_set = true;
1734 }
1735 if (mode_argv[i] &&
1736 (strcmp(mode_argv[i], "--status-screen") == 0 || strncmp(mode_argv[i], "--status-screen=", 16) == 0)) {
1737 // Parse the value if provided (--status-screen=true/false)
1738 bool status_value = true; // Default to true for plain --status-screen
1739 if (strncmp(mode_argv[i], "--status-screen=", 16) == 0) {
1740 const char *value = mode_argv[i] + 16;
1741 if (strcasecmp(value, "false") == 0 || strcasecmp(value, "no") == 0 || strcasecmp(value, "0") == 0 ||
1742 strcasecmp(value, "off") == 0) {
1743 status_value = false;
1744 }
1745 }
1746 opts.status_screen = status_value;
1747 opts.status_screen_explicitly_set = true;
1748 }
1749#ifndef NDEBUG
1750 if (mode_argv[i] &&
1751 (strcmp(mode_argv[i], "--sync-state") == 0 || strncmp(mode_argv[i], "--sync-state=", 13) == 0)) {
1753 if (strcmp(mode_argv[i], "--sync-state") == 0) {
1754 if (i + 1 < mode_argc) {
1755 char *endptr;
1756 double val = strtod(mode_argv[i + 1], &endptr);
1757 if (endptr != mode_argv[i + 1] && val > 0.0) {
1758 opts.debug_sync_state_time = val;
1759 i++;
1760 }
1761 }
1762 } else {
1763 const char *value_str = mode_argv[i] + 13;
1764 if (value_str[0] != '\0') {
1765 char *endptr;
1766 double val = strtod(value_str, &endptr);
1767 if (endptr != value_str && val > 0.0) {
1768 opts.debug_sync_state_time = val;
1769 }
1770 }
1771 }
1772 }
1773 if (mode_argv[i] && (strcmp(mode_argv[i], "--backtrace") == 0 || strncmp(mode_argv[i], "--backtrace=", 12) == 0)) {
1775 if (strcmp(mode_argv[i], "--backtrace") == 0) {
1776 if (i + 1 < mode_argc) {
1777 char *endptr;
1778 double val = strtod(mode_argv[i + 1], &endptr);
1779 if (endptr != mode_argv[i + 1] && val > 0.0) {
1780 opts.debug_backtrace_time = val;
1781 i++;
1782 }
1783 }
1784 } else {
1785 const char *value_str = mode_argv[i] + 12;
1786 if (value_str[0] != '\0') {
1787 char *endptr;
1788 double val = strtod(value_str, &endptr);
1789 if (endptr != value_str && val > 0.0) {
1790 opts.debug_backtrace_time = val;
1791 }
1792 }
1793 }
1794 }
1795 if (mode_argv[i] &&
1796 (strcmp(mode_argv[i], "--memory-report") == 0 || strncmp(mode_argv[i], "--memory-report=", 16) == 0)) {
1798 if (strcmp(mode_argv[i], "--memory-report") == 0) {
1799 if (i + 1 < mode_argc) {
1800 char *endptr;
1801 double val = strtod(mode_argv[i + 1], &endptr);
1802 if (endptr != mode_argv[i + 1] && val > 0.0) {
1804 i++;
1805 }
1806 }
1807 } else {
1808 const char *value_str = mode_argv[i] + 16;
1809 if (value_str[0] != '\0') {
1810 char *endptr;
1811 double val = strtod(value_str, &endptr);
1812 if (endptr != value_str && val > 0.0) {
1814 }
1815 }
1816 }
1817 }
1818#endif
1819 }
1820
1821 // Auto-disable splash when grep is used (since it's one-time startup screen)
1822 // UNLESS it was explicitly set by the user
1823 // Status screen is now compatible with --grep since we support auto-loading patterns
1824 bool grep_was_provided = false;
1825 for (int i = 0; i < mode_argc; i++) {
1826 if (mode_argv[i] && (strcmp(mode_argv[i], "--grep") == 0 || strncmp(mode_argv[i], "--grep=", 7) == 0)) {
1827 grep_was_provided = true;
1828 break;
1829 }
1830 }
1831
1832 if (grep_was_provided) {
1833 if (!opts.splash_screen_explicitly_set) {
1834 opts.splash_screen = false;
1835 log_debug("Auto-disabled splash because --grep was provided");
1836 }
1837 }
1838
1839 // Auto-disable splash and status screens when terminal is non-interactive or CLAUDECODE is set
1840 // This ensures clean output when running under LLM automation or non-interactive shells
1841 bool is_terminal_interactive = terminal_is_interactive();
1842 bool is_claudecode_set = platform_getenv("CLAUDECODE") != NULL;
1843 bool should_auto_disable_screens = !is_terminal_interactive || is_claudecode_set;
1844
1845 if (should_auto_disable_screens) {
1846 if (!opts.splash_screen_explicitly_set) {
1847 opts.splash_screen = false;
1848 log_debug("Auto-disabled splash (non-interactive terminal or CLAUDECODE set)");
1849 }
1850 if (!opts.status_screen_explicitly_set) {
1851 opts.status_screen = false;
1852 log_debug("Auto-disabled status screen (non-interactive terminal or CLAUDECODE set)");
1853 }
1854 }
1855
1856// Validate all string options contain valid UTF-8
1857// This prevents crashes and corruption from invalid UTF-8 in any option
1858// DISABLED IN RELEASE BUILDS: utf8_is_valid() has performance issues with musl
1859#ifndef NDEBUG
1860 const char *string_fields[][2] = {{"address", opts.address},
1861 {"address6", opts.address6},
1862 {"encrypt_key", opts.encrypt_key},
1863 {"encrypt_keyfile", opts.encrypt_keyfile},
1864 {"server_key", opts.server_key},
1865 {"client_keys", opts.client_keys},
1866 {"discovery_server", opts.discovery_server},
1867 {"discovery_service_key", opts.discovery_service_key},
1868 {"discovery_database_path", opts.discovery_database_path},
1869 {"log_file", opts.log_file},
1870 {"media_file", opts.media_file},
1871 {"palette_custom", opts.palette_custom},
1872 {"stun_servers", opts.stun_servers},
1873 {"turn_servers", opts.turn_servers},
1874 {"turn_username", opts.turn_username},
1875 {"turn_credential", opts.turn_credential},
1876 {"turn_secret", opts.turn_secret},
1877 {"session_string", opts.session_string},
1878 {NULL, NULL}};
1879
1880 for (int i = 0; string_fields[i][0] != NULL; i++) {
1881 const char *field_name = string_fields[i][0];
1882 const char *field_value = string_fields[i][1];
1883
1884 // Skip empty strings
1885 if (!field_value || field_value[0] == '\0') {
1886 continue;
1887 }
1888
1889 // Validate UTF-8
1890 if (!utf8_is_valid(field_value)) {
1891 log_error("Error: Option --%s contains invalid UTF-8 sequences", field_name);
1892 log_error(" Value: %s", field_value);
1893 options_config_destroy(config);
1894 SAFE_FREE(allocated_mode_argv);
1895 return option_error_invalid();
1896 }
1897 }
1898#endif
1899 // Validate options
1900 log_info("★ VALIDATE_OPTIONS_AND_REPORT: About to call");
1901 result = validate_options_and_report(config, &opts);
1902 log_info("★ VALIDATE_OPTIONS_AND_REPORT: Returned with result=%d", result);
1903 if (result != ASCIICHAT_OK) {
1904 options_config_destroy(config);
1905 SAFE_FREE(allocated_mode_argv);
1906 return result;
1907 }
1908 // Check for unexpected remaining arguments
1909 if (remaining_argc > 0) {
1910 log_error("Error: Unexpected arguments after options:");
1911 for (int i = 0; i < remaining_argc; i++) {
1912 log_error(" %s", remaining_argv[i]);
1913 }
1914 options_config_destroy(config);
1915 SAFE_FREE(allocated_mode_argv);
1916 return option_error_invalid();
1917 }
1918
1919 // Mode-specific post-processing
1920 // Apply mode-specific defaults (port, websocket-port)
1921 log_info("★ APPLY_MODE_SPECIFIC: About to call");
1923 log_info("★ APPLY_MODE_SPECIFIC: Done");
1924 log_dev("Applied mode-specific defaults: port=%d, websocket_port=%d", opts.port, opts.websocket_port);
1925
1926 if (detected_mode == MODE_DISCOVERY_SERVICE) {
1927 // Set default paths if not specified
1928 log_info("★ DISCOVERY_SERVICE: Handling discovery service defaults");
1929 if (opts.discovery_database_path[0] == '\0') {
1930 // Database: Try system-wide location first, fall back to user directories
1931 // Preference: /usr/local/var/ascii-chat/ > ~/.local/share/ascii-chat/ > ~/.config/ascii-chat/
1932 log_info("★ DISCOVERY_SERVICE: About to call get_discovery_database_dir()");
1933 char *db_dir = get_discovery_database_dir();
1934 log_info("★ DISCOVERY_SERVICE: get_discovery_database_dir() returned %p", (void *)db_dir);
1935 if (!db_dir) {
1936 options_config_destroy(config);
1937 SAFE_FREE(allocated_mode_argv);
1938 return SET_ERRNO(ERROR_CONFIG, "Failed to get database directory (tried system and user locations)");
1939 }
1940 safe_snprintf(opts.discovery_database_path, sizeof(opts.discovery_database_path), "%sdiscovery.db", db_dir);
1941 SAFE_FREE(db_dir);
1942 }
1943 }
1944
1945 log_info("★ OPTIONS_CONFIG_DESTROY: About to call");
1946 options_config_destroy(config);
1947 log_info("★ OPTIONS_CONFIG_DESTROY: Done");
1948
1949 // ========================================================================
1950 // STAGE 7: Post-Processing & Validation
1951 // ========================================================================
1952
1953 log_info("★ STAGE 7: Starting post-processing");
1954
1955 // Collect multiple --key flags for multi-key support (server/ACDS only)
1956 // This enables servers to load both SSH and GPG keys and select the right one
1957 // during handshake based on what the client expects
1958 if (detected_mode == MODE_SERVER || detected_mode == MODE_DISCOVERY_SERVICE) {
1959 log_info("★ STAGE 7.1: About to collect identity keys");
1960 int num_keys = options_collect_identity_keys(&opts, argc, argv);
1961 log_info("★ STAGE 7.1: options_collect_identity_keys returned %d", num_keys);
1962 if (num_keys < 0) {
1963 SAFE_FREE(allocated_mode_argv);
1964 return SET_ERRNO(ERROR_INVALID_PARAM, "Failed to collect identity keys");
1965 }
1966 // num_keys == 0 is OK (no --key flags provided)
1967 }
1968
1969 log_info("★ STAGE 7.2: About to update dimensions");
1970
1971 // After parsing command line options, update dimensions
1972 // First set any auto dimensions to terminal size, then apply full height logic
1973
1974 // Clear auto flags if dimensions were explicitly parsed from options
1975 // This must happen before update_dimensions_to_terminal_size to prevent auto-detected
1976 // values from overwriting user-specified dimensions
1977 // Note: width=0 and height=0 are special values meaning "auto-detect", so don't clear auto flags for those
1978 if (opts.width != OPT_WIDTH_DEFAULT && opts.width != 0) {
1979 opts.auto_width = false;
1980 }
1981 if (opts.height != OPT_HEIGHT_DEFAULT && opts.height != 0) {
1982 opts.auto_height = false;
1983 }
1985 js_log_options("BEFORE_UPDATE_DIMS");
1986 js_log_options("CALLING_UPDATE_DIM");
1988 js_log_options("AFTER_UPDATE_DIM");
1989
1990 js_log_options("L11");
1991 js_log_options("L12");
1992 js_log_options("L13");
1993 js_log_options("L14");
1994 js_log_options("L15");
1995 js_log_options("CHECKPOINT B");
1996
1997 // SKIP: Check WEBCAM_DISABLED environment variable to enable test pattern mode
1998 // TODO: Debug why logging here causes hang
1999 // const char *webcam_disabled = SAFE_GETENV("WEBCAM_DISABLED");
2000 // if (webcam_disabled &&
2001 // (strcmp(webcam_disabled, "1") == 0 || platform_strcasecmp(webcam_disabled, "true") == 0 ||
2002 // platform_strcasecmp(webcam_disabled, "yes") == 0 || platform_strcasecmp(webcam_disabled, "on") == 0)) {
2003 // opts.test_pattern = true;
2004 // }
2005
2006 js_log_options("CHECKPOINT C");
2007
2008 // Apply --no-compress interaction with audio encoding
2009 js_log_options("Before --no-compress check");
2010 if (opts.no_compress) {
2011 js_log_options("In no_compress block");
2012 opts.encode_audio = false;
2013 js_log_options("Set encode_audio to false");
2014 log_debug("--no-compress set: disabling audio encoding");
2015 js_log_options("After log_debug in no_compress");
2016 }
2017 js_log_options("After --no-compress check");
2018
2019 // Set media_from_stdin flag if media_file is "-"
2020 js_log_options("Before media_from_stdin check");
2021 if (opts.media_file[0] != '\0' && strcmp(opts.media_file, "-") == 0) {
2022 js_log_options("In media_from_stdin block");
2023 opts.media_from_stdin = true;
2024 js_log_options("Set media_from_stdin to true");
2025 log_debug("Media file set to stdin");
2026 js_log_options("After log_debug in media_from_stdin");
2027 }
2028 js_log_options("After media_from_stdin check");
2029
2030 // Validate --seek option
2031 js_log_options("Before --seek validation");
2032 js_log_options("Checking media_seek_timestamp");
2033 if (opts.media_seek_timestamp > 0.0) {
2034 js_log_options("In seek validation block");
2035 // Can't seek stdin
2036 if (opts.media_from_stdin) {
2037 js_log_options("Seek error: media_from_stdin");
2038 log_error("--seek cannot be used with stdin (--file -)");
2039 SAFE_FREE(allocated_mode_argv);
2040 return ERROR_INVALID_PARAM;
2041 }
2042
2043 // Require --file or --url
2044 js_log_options("Checking media_file and media_url for seek");
2045 if (opts.media_file[0] == '\0' && opts.media_url[0] == '\0') {
2046 js_log_options("Seek error: no media_file or media_url");
2047 log_error("--seek requires --file or --url");
2048 SAFE_FREE(allocated_mode_argv);
2049 return ERROR_INVALID_PARAM;
2050 }
2051 js_log_options("Seek validation passed");
2052 }
2053 js_log_options("After --seek validation");
2054
2055 // Validate --pause option
2056 js_log_options("Before --pause validation");
2057 js_log_options("Checking pause flag");
2058 if (opts.pause) {
2059 js_log_options("In pause validation block");
2060 // Require --file or --url (not webcam, not test pattern)
2061 if (opts.media_file[0] == '\0' && opts.media_url[0] == '\0') {
2062 js_log_options("Pause error: no media_file or media_url");
2063 log_error("--pause requires --file or --url");
2064 SAFE_FREE(allocated_mode_argv);
2065 return ERROR_INVALID_PARAM;
2066 }
2067 js_log_options("Pause validation passed");
2068 }
2069 js_log_options("After --pause validation");
2070
2071 js_log_options("Before media_url check");
2072 js_log_options("Checking media_url[0]");
2073 if (opts.media_url[0] != '\0') {
2074 js_log_options("Entering media_url block");
2075 // URL must be a valid HTTP(S) URL (YouTube URLs are HTTPS URLs)
2076 js_log_options("About to call url_is_valid");
2077 js_log_options("Before url_is_valid call");
2078 if (!url_is_valid(opts.media_url)) {
2079 js_log_options("url_is_valid returned false");
2080 log_error("--url must be a valid HTTP(S) URL: %s", opts.media_url);
2081 SAFE_FREE(allocated_mode_argv);
2082 return ERROR_INVALID_PARAM;
2083 }
2084 js_log_options("url_is_valid returned true");
2085 js_log_options("url_is_valid returned, before strstr check");
2086
2087 // Normalize bare URLs by prepending http:// if not present
2088 js_log_options("Before strstr check");
2089 if (!strstr(opts.media_url, "://")) {
2090 js_log_options("URL needs normalization");
2091 char normalized_url[2048];
2092 js_log_options("Before snprintf");
2093 int result = snprintf(normalized_url, sizeof(normalized_url), "http://%s", opts.media_url);
2094 js_log_options("After snprintf");
2095 if (result > 0 && result < (int)sizeof(normalized_url)) {
2096 js_log_options("snprintf successful, before SAFE_STRNCPY");
2097 SAFE_STRNCPY(opts.media_url, normalized_url, sizeof(opts.media_url));
2098 js_log_options("URL normalized");
2099 } else {
2100 js_log_options("snprintf failed");
2101 log_error("Failed to normalize URL (too long): %s", opts.media_url);
2102 SAFE_FREE(allocated_mode_argv);
2103 return ERROR_INVALID_PARAM;
2104 }
2105 }
2106 js_log_options("After normalization check");
2107 }
2108 js_log_options("After media_url block");
2109
2110 // ========================================================================
2111 // STAGE 8: Publish to RCU
2112 // ========================================================================
2113
2114 // Adjust flip_x default: if --file or --url is provided, default flip_x to false
2115 // (file/URL content is typically not horizontally flipped, unlike webcam)
2116 js_log_options("Before flip_x check");
2117 if ((opts.media_file[0] != '\0' || opts.media_url[0] != '\0') && opts.flip_x) {
2118 js_log_options("In flip_x adjustment");
2119 opts.flip_x = false;
2120 js_log_options("flip_x set to false");
2121 log_debug("flip_x auto-adjusted to false for media file/URL");
2122 js_log_options("After log_debug for flip_x");
2123 }
2124 js_log_options("After flip_x check");
2125
2126 // Save the quiet flag before publishing (RCU will be cleaned up before memory report runs)
2127#if defined(DEBUG_MEMORY) && !defined(USE_MIMALLOC_DEBUG) && !defined(NDEBUG)
2128 js_log_options("Before quiet_for_memory_report assignment");
2129 bool quiet_for_memory_report = opts.quiet;
2130 js_log_options("After quiet_for_memory_report assignment");
2131#endif
2132
2133 // Publish parsed options to RCU state (replaces options_state_populate_from_globals)
2134 // This makes the options visible to all threads via lock-free reads
2135 js_log_options("STAGE 8 RCU: Before options_state_set");
2136 js_log_options("Before log_info PERF");
2137 js_log_options("After log_info PERF");
2138 js_log_options("Calling options_state_set...");
2139 js_log_options("Before options_state_set call");
2140 asciichat_error_t publish_result = options_state_set(&opts);
2141 js_log_options("After options_state_set call");
2142 js_log_options("options_state_set returned");
2143 js_log_options("Before log_info options_state_set done");
2144 js_log_options("After log_info options_state_set done");
2145 js_log_options("Before publish_result check");
2146 if (publish_result != ASCIICHAT_OK) {
2147 js_log_options("publish_result not OK");
2148 log_error("Failed to publish parsed options to RCU state: %d", publish_result);
2149 SAFE_FREE(allocated_mode_argv);
2150 return publish_result;
2151 }
2152 js_log_options("publish_result is OK");
2153 js_log_options("options_state_set successful");
2154
2155 // Now update debug memory quiet mode with the saved quiet value
2156#if defined(DEBUG_MEMORY) && !defined(USE_MIMALLOC_DEBUG) && !defined(NDEBUG)
2157 debug_memory_set_quiet_mode(quiet_for_memory_report);
2158#endif
2159
2160 // ========================================================================
2161 // Apply color scheme to logging
2162 // ========================================================================
2163 // Now that options are parsed, set and apply the selected color scheme to logging
2164 js_log_options("STAGE 8 ColorScheme: Checking color_scheme_name");
2165 js_log_options("Before color_scheme_name[0] check");
2166 if (opts.color_scheme_name[0] != '\0') {
2167 js_log_options("Color scheme name is set");
2168 js_log_options("Before colorscheme_set_active_scheme");
2169 js_log_options("Before colorscheme_set_active_scheme call");
2171 js_log_options("After colorscheme_set_active_scheme call");
2172 js_log_options("colorscheme_set_active_scheme returned");
2173 js_log_options("Before log_info colorscheme done");
2174 js_log_options("After log_info colorscheme done");
2175 js_log_options("Before scheme_result check");
2176 if (scheme_result == ASCIICHAT_OK) {
2177 js_log_options("scheme_result is OK");
2178 js_log_options("Before colorscheme_get_active_scheme");
2180 js_log_options("After colorscheme_get_active_scheme");
2181 js_log_options("Before scheme null check");
2182 if (scheme) {
2183 js_log_options("scheme is not null");
2184 js_log_options("Before log_set_color_scheme");
2185 log_set_color_scheme(scheme);
2186 js_log_options("After log_set_color_scheme");
2187 js_log_options("Color scheme applied");
2188 js_log_options("Before log_debug color scheme");
2189 log_debug("Color scheme applied: %s", opts.color_scheme_name);
2190 js_log_options("After log_debug color scheme");
2191 } else {
2192 js_log_options("scheme is null");
2193 js_log_options("No scheme returned from colorscheme_get_active_scheme");
2194 }
2195 } else {
2196 log_warn("Failed to apply color scheme: %s", opts.color_scheme_name);
2197 }
2198 }
2199
2200 // ========================================================================
2201 // STAGE 8: Execute deferred actions (after all options parsed and published)
2202 // ========================================================================
2203 // Deferred actions (--list-webcams, --list-microphones, --list-speakers, --show-capabilities)
2204 // are executed here after all options are fully parsed and published via RCU.
2205 // This ensures action output reflects the final parsed state (e.g., final dimensions
2206 // for --show-capabilities).
2207 // Re-enable terminal output so deferred actions can print their results.
2208 // It was disabled earlier (STAGE 1C) to suppress log noise during option parsing.
2209 if (has_action) {
2211 }
2213 SAFE_FREE(allocated_mode_argv);
2214 return ASCIICHAT_OK;
2215}
const color_scheme_t * colorscheme_get_active_scheme(void)
Get currently active color scheme.
asciichat_error_t colorscheme_set_active_scheme(const char *name)
Set the active color scheme.
void debug_memory_set_quiet_mode(bool quiet)
#define SAFE_GETENV(name)
Definition common.h:434
#define SAFE_SNPRINTF(buffer, buffer_size,...)
Definition common.h:492
asciichat_error_t config_load_system_and_user(asciichat_mode_t detected_mode, bool strict, options_t *opts)
Load system config first, then user config (user config overrides system)
Definition config.c:1547
asciichat_error_t config_create_default(const char *config_path)
Create default configuration file with all default values.
Definition config.c:1254
#define HAS_ERRNO(var)
Check if an error occurred and get full context.
@ ERROR_MEMORY
Definition error_codes.h:56
@ ERROR_USAGE
Definition error_codes.h:53
#define log_dev(...)
Log a DEV message (most verbose, development only)
Definition log/log.h:534
void log_set_terminal_output(bool enabled)
Control stderr output to terminal.
Definition log/log.c:700
void log_set_color_scheme(const color_scheme_t *scheme)
Set the color scheme for logging output.
Definition log/log.c:1728
#define OPT_COLOR_DEFAULT
Default color setting (COLOR_SETTING_AUTO = smart detection)
void update_dimensions_to_terminal_size(options_t *opts)
Update dimensions to current terminal size.
void usage(FILE *desc, asciichat_mode_t mode)
Print usage information for a specific mode.
#define OPT_HEIGHT_DEFAULT
Default terminal height in characters.
#define OPT_FPS_DEFAULT
Default FPS (frames per second)
int options_collect_identity_keys(options_t *opts, int argc, char *argv[])
Collect multiple –key flags into identity_keys array.
Definition validation.c:527
asciichat_error_t options_config_generate_manpage_merged(const options_config_t *config, const char *program_name, const char *mode_name, const char *output_path, const char *brief_description)
Generate merged man page template preserving manual content.
void update_dimensions_for_full_height(options_t *opts)
Update dimensions for full-height mode.
#define OPT_WIDTH_DEFAULT
Default terminal width in characters.
@ PALETTE_CUSTOM
User-defined via –palette-chars.
Definition palette.h:98
bool terminal_supports_utf8(void)
Check if terminal supports UTF-8.
const char * platform_getenv(const char *name)
Get an environment variable value.
Definition wasm/system.c:39
bool path_looks_like_path(const char *value)
Determine if a string is likely intended to reference the filesystem.
Definition path.c:809
bool utf8_is_ascii_only(const char *str)
Check if a string contains only ASCII characters.
Definition utf8.c:165
bool utf8_is_valid(const char *str)
Check if a string is valid UTF-8.
Definition utf8.c:156
bool url_is_valid(const char *url)
Fast URL validation using production-grade regex (HTTP/HTTPS/WebSocket/TCP)
Definition url.c:81
char * get_discovery_database_dir(void)
Get discovery service database directory with Homebrew-aware system-wide fallback.
Definition path.c:624
char * get_config_dir(void)
Get configuration directory path with XDG_CONFIG_HOME support.
Definition path.c:527
options_t options_t_new_preserve_binary(const options_t *source)
Create new options struct, preserving binary-level fields from source.
asciichat_error_t validate_options_and_report(const void *config, const void *opts)
Validate options and report errors to stderr.
void action_check_update_immediate(void)
Execute update check immediately (for early binary-level execution)
bool has_action
void action_create_manpage(const char *output_path)
Generate man page template from options builder.
void action_completions(const char *shell_name, const char *output_path)
Generate shell completions and output to stdout or file.
void action_list_microphones(void)
List available microphone devices and exit.
void action_create_config(const char *output_path)
Create default configuration file and exit.
void action_list_webcams(void)
List available webcam devices and exit.
void action_list_speakers(void)
List available speaker devices and exit.
void actions_execute_deferred(void)
Execute the deferred action (if any)
void action_show_capabilities_immediate(void)
Execute show capabilities immediately (for early binary-level execution)
void apply_mode_specific_defaults(options_t *opts)
Apply mode-specific defaults to an options struct after mode detection.
bool parse_color_setting(const char *arg, void *dest, char **error_msg)
Parse color setting option (–color flag)
Definition parsers.c:160
bool parse_log_level(const char *arg, void *dest, char **error_msg)
Parse log level option.
Definition parsers.c:448
bool terminal_is_interactive(void)
Check if the session is fully interactive.
asciichat_error_t config_schema_build_from_configs(const options_config_t **configs, size_t num_configs)
Build schema dynamically from options builder configs.
Definition schema.c:240
#define SESSION_STRING_BUFFER_SIZE
Error context structure.
char * context_message
Optional custom message (dynamically allocated, owned by system)
Create a new options_t struct with all defaults set.
Color scheme definition.
Definition colorscheme.h:62
bool splash_screen_explicitly_set
True if splash screen was explicitly set by user.
char media_url[256]
Network URL (HTTP/HTTPS/YouTube/RTSP) - takes priority over media_file.
bool encrypt_enabled
Enable encryption.
char turn_servers[256]
ACDS: Comma-separated list of TURN server URLs.
int force_utf8
UTF-8 support setting (auto/true/false)
bool debug_backtrace_time_explicit
True if –backtrace was explicitly provided.
double debug_memory_report_interval
Interval in seconds for periodic memory reports (debug builds only)
bool debug_sync_state_time_explicit
True if –debug-state was explicitly provided.
bool fps_explicitly_set
True if FPS was explicitly configured by the user.
double debug_sync_state_time
Time parameter for –sync-state option (debug builds only)
char password[256]
Password string.
int websocket_port
WebSocket server port (server/discovery-service only)
bool pause
Start playback paused (toggle with spacebar)
bool json
Enable JSON logging (–json flag)
char stun_servers[256]
ACDS: Comma-separated list of STUN server URLs.
bool debug_memory_report_interval_explicit
True if –memory-report was explicitly provided.
bool log_format_console_only
Apply log format only to console output.
bool no_encrypt
Disable encryption (opt-out)
bool auto_height
Auto-detect height from terminal.
log_level_t log_level
Log level threshold.
char turn_credential[256]
ACDS: Credential/password for TURN authentication.
char palette_custom[256]
Custom palette characters.
bool status_screen
Show status screen (default: true = show, use –no-status-screen to hide)
bool palette_custom_set
True if custom palette was set.
bool splash_screen
Show splash screen (default: true = show, use –no-splash-screen to hide)
char discovery_database_path[256]
~/.ascii-chat/discovery.db)
char server_key[256]
Expected server public key (client)
char encrypt_keyfile[256]
Alternative key file path.
bool version
Show version information.
bool status_screen_explicitly_set
True if status_screen was explicitly set by user.
bool no_compress
Disable compression entirely.
char address6[256]
IPv6 bind address (server only)
color_filter_t color_filter
Monochromatic color filter (none/black/white/green/etc)
bool quiet
Quiet mode (suppress logs)
int fps
Target framerate (1-144, default: 60)
bool media_from_stdin
Reading from stdin (detected from "--file -")
char discovery_server[256]
discovery server address (default: 127.0.0.1)
double debug_backtrace_time
Time parameter for –backtrace option (debug builds only)
palette_type_t palette_type
Selected palette type.
bool flip_y
Flip video vertically (Y-axis)
char color_scheme_name[64]
Color scheme name (e.g., "pastel", "nord")
char turn_secret[256]
ACDS: Shared secret for dynamic TURN credential generation (HMAC-SHA1)
char discovery_service_key[256]
HTTPS URL)
bool no_check_update
Disable automatic update checks (default: false = checks enabled)
bool encode_audio
Enable Opus audio encoding.
char media_file[256]
Media file path or "-" for stdin.
int color
Color setting (COLOR_SETTING_AUTO/TRUE/FALSE)
char turn_username[256]
ACDS: Username for TURN authentication.
char log_file[256]
Log file path.
unsigned short int verbose_level
Verbosity level (stackable -V)
char address[256]
Server address (client) or bind address (server)
bool flip_x
Flip video horizontally (X-axis). Ignored for webcam on macOS.
bool auto_width
Auto-detect width from terminal.
char client_keys[256]
Allowed client keys (server)
double media_seek_timestamp
Seek to timestamp in seconds before playback.
#define PLATFORM_MAX_PATH_LENGTH
Definition system.c:69
@ COLOR_FILTER_NONE
No filtering (default)
Definition terminal.h:601

References action_check_update_immediate(), action_completions(), action_create_config(), action_create_manpage(), action_list_microphones(), action_list_speakers(), action_list_webcams(), action_show_capabilities_immediate(), actions_execute_deferred(), options_state::address, options_state::address6, apply_mode_specific_defaults(), ASCIICHAT_OK, options_state::auto_height, options_state::auto_width, options_state::client_keys, options_state::color, options_state::color_filter, COLOR_FILTER_NONE, options_state::color_scheme_name, COLOR_SETTING_AUTO, COLOR_SETTING_FALSE, COLOR_SETTING_TRUE, colorscheme_get_active_scheme(), colorscheme_set_active_scheme(), config_create_default(), config_load_system_and_user(), config_schema_build_from_configs(), asciichat_error_context_t::context_message, options_state::debug_backtrace_time, options_state::debug_backtrace_time_explicit, options_state::debug_memory_report_interval, options_state::debug_memory_report_interval_explicit, debug_memory_set_quiet_mode(), options_state::debug_sync_state_time, options_state::debug_sync_state_time_explicit, options_state::detected_mode, options_state::discovery_database_path, options_state::discovery_server, options_state::discovery_service_key, options_state::encode_audio, options_state::encrypt_enabled, options_state::encrypt_key, options_state::encrypt_keyfile, ERROR_CONFIG, ERROR_CRYPTO_KEY, ERROR_INVALID_PARAM, ERROR_MEMORY, ERROR_USAGE, options_state::flip_x, options_state::flip_y, options_state::force_utf8, options_state::fps, options_state::fps_explicitly_set, get_config_dir(), get_discovery_database_dir(), has_action, HAS_ERRNO, options_state::height, is_remote_key_path(), options_state::json, log_debug, log_dev, log_error, options_state::log_file, options_state::log_format_console_only, log_info, options_state::log_level, log_plain, log_set_color_scheme(), log_set_terminal_output(), log_warn, options_state::media_file, options_state::media_from_stdin, options_state::media_seek_timestamp, options_state::media_url, MODE_CLIENT, MODE_DISCOVERY, MODE_DISCOVERY_SERVICE, MODE_MIRROR, MODE_SERVER, options_state::no_check_update, options_state::no_compress, options_state::no_encrypt, OPT_COLOR_DEFAULT, OPT_FPS_DEFAULT, OPT_HEIGHT_DEFAULT, OPT_WIDTH_DEFAULT, OPTIONS_BUFF_SIZE, options_collect_identity_keys(), options_config_destroy(), options_config_generate_manpage_merged(), options_config_parse(), options_config_set_defaults(), options_preset_unified(), options_state_init(), options_state_set(), options_t_new(), options_t_new_preserve_binary(), options_state::palette_custom, PALETTE_CUSTOM, options_state::palette_custom_set, options_state::palette_type, parse_color_setting(), parse_log_level(), options_state::password, path_looks_like_path(), options_state::pause, platform_getenv(), PLATFORM_MAX_PATH_LENGTH, options_state::port, options_state::quiet, SAFE_FREE, SAFE_GETENV, SAFE_MALLOC, SAFE_SNPRINTF, safe_snprintf(), SAFE_STRNCPY, options_state::server_key, options_state::session_string, SESSION_STRING_BUFFER_SIZE, SET_ERRNO, options_state::splash_screen, options_state::splash_screen_explicitly_set, options_state::status_screen, options_state::status_screen_explicitly_set, options_state::stun_servers, terminal_is_interactive(), terminal_supports_utf8(), options_state::turn_credential, options_state::turn_secret, options_state::turn_servers, options_state::turn_username, update_dimensions_for_full_height(), update_dimensions_to_terminal_size(), url_is_valid(), usage(), utf8_is_ascii_only(), utf8_is_valid(), validate_options_and_report(), options_state::verbose_level, options_state::version, options_state::websocket_port, and options_state::width.

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

◆ options_set_bool()

asciichat_error_t options_set_bool ( const char *  field_name,
bool  value 
)

#include <options.h>

Definition at line 969 of file rcu.c.

969 {
970 if (!field_name) {
971 SET_ERRNO(ERROR_INVALID_PARAM, "field_name is NULL");
972 return ERROR_INVALID_PARAM;
973 }
974
975 // Validate field exists
976 if (strcmp(field_name, "no_compress") != 0 && strcmp(field_name, "encode_audio") != 0 &&
977 strcmp(field_name, "flip_x") != 0 && strcmp(field_name, "flip_y") != 0 &&
978 strcmp(field_name, "test_pattern") != 0 && strcmp(field_name, "no_audio_mixer") != 0 &&
979 strcmp(field_name, "show_capabilities") != 0 && strcmp(field_name, "force_utf8") != 0 &&
980 strcmp(field_name, "audio_enabled") != 0 && strcmp(field_name, "audio_analysis_enabled") != 0 &&
981 strcmp(field_name, "audio_no_playback") != 0 && strcmp(field_name, "stretch") != 0 &&
982 strcmp(field_name, "snapshot_mode") != 0 && strcmp(field_name, "strip_ansi") != 0 &&
983 strcmp(field_name, "quiet") != 0 && strcmp(field_name, "encrypt_enabled") != 0 &&
984 strcmp(field_name, "no_encrypt") != 0 && strcmp(field_name, "discovery") != 0 &&
985 strcmp(field_name, "discovery_expose_ip") != 0 && strcmp(field_name, "discovery_insecure") != 0 &&
986 strcmp(field_name, "webrtc") != 0 && strcmp(field_name, "lan_discovery") != 0 &&
987 strcmp(field_name, "no_mdns_advertise") != 0 && strcmp(field_name, "prefer_webrtc") != 0 &&
988 strcmp(field_name, "no_webrtc") != 0 && strcmp(field_name, "webrtc_skip_stun") != 0 &&
989 strcmp(field_name, "webrtc_relay_only") != 0 && strcmp(field_name, "webrtc_disable_turn") != 0 &&
990 strcmp(field_name, "enable_upnp") != 0 && strcmp(field_name, "require_server_identity") != 0 &&
991 strcmp(field_name, "require_client_identity") != 0 && strcmp(field_name, "require_server_verify") != 0 &&
992 strcmp(field_name, "require_client_verify") != 0 && strcmp(field_name, "palette_custom_set") != 0 &&
993 strcmp(field_name, "media_loop") != 0 && strcmp(field_name, "media_from_stdin") != 0 &&
994 strcmp(field_name, "auto_width") != 0 && strcmp(field_name, "auto_height") != 0 &&
995 strcmp(field_name, "splash_screen") != 0 && strcmp(field_name, "status_screen") != 0 &&
996 strcmp(field_name, "matrix_rain") != 0 && strcmp(field_name, "fps_counter") != 0 &&
997 strcmp(field_name, "waveform") != 0 && strcmp(field_name, "fft") != 0) {
998 SET_ERRNO(ERROR_INVALID_PARAM, "Unknown boolean field: %s", field_name);
999 return ERROR_INVALID_PARAM;
1000 }
1001
1002 // Pre-validate against registry metadata (for cross-field validators)
1003 {
1004 const options_t *cur = options_get();
1005
1006 // Skip RCU update if value is already set
1007 bool cur_value = false;
1008 if (strcmp(field_name, "no_compress") == 0)
1009 cur_value = cur->no_compress;
1010 else if (strcmp(field_name, "encode_audio") == 0)
1011 cur_value = cur->encode_audio;
1012 else if (strcmp(field_name, "flip_x") == 0)
1013 cur_value = cur->flip_x;
1014 else if (strcmp(field_name, "flip_y") == 0)
1015 cur_value = cur->flip_y;
1016 else if (strcmp(field_name, "test_pattern") == 0)
1017 cur_value = cur->test_pattern;
1018 else if (strcmp(field_name, "matrix_rain") == 0)
1019 cur_value = cur->matrix_rain;
1020 else if (strcmp(field_name, "waveform") == 0)
1021 cur_value = cur->waveform;
1022 else if (strcmp(field_name, "fft") == 0)
1023 cur_value = cur->fft;
1024 else if (strcmp(field_name, "fps_counter") == 0)
1025 cur_value = cur->fps_counter;
1026 else if (strcmp(field_name, "splash_screen") == 0)
1027 cur_value = cur->splash_screen;
1028 else if (strcmp(field_name, "status_screen") == 0)
1029 cur_value = cur->status_screen;
1030 // For less common fields, always update (safe default)
1031 else
1032 cur_value = !value;
1033 if (cur_value == value)
1034 return ASCIICHAT_OK;
1035
1036 options_t temp = *cur;
1037 // Manually set the field in temp to validate
1038 if (strcmp(field_name, "no_compress") == 0)
1039 temp.no_compress = value;
1040 else if (strcmp(field_name, "encode_audio") == 0)
1041 temp.encode_audio = value;
1042 else if (strcmp(field_name, "flip_x") == 0)
1043 temp.flip_x = value;
1044 else if (strcmp(field_name, "flip_y") == 0)
1045 temp.flip_y = value;
1046 else if (strcmp(field_name, "test_pattern") == 0)
1047 temp.test_pattern = value;
1048 else if (strcmp(field_name, "no_audio_mixer") == 0)
1049 temp.no_audio_mixer = value;
1050 else if (strcmp(field_name, "show_capabilities") == 0)
1051 temp.show_capabilities = value;
1052 else if (strcmp(field_name, "force_utf8") == 0)
1053 temp.force_utf8 = value;
1054 else if (strcmp(field_name, "audio_enabled") == 0)
1055 temp.audio_enabled = value;
1056 else if (strcmp(field_name, "audio_analysis_enabled") == 0)
1057 temp.audio_analysis_enabled = value;
1058 else if (strcmp(field_name, "audio_no_playback") == 0)
1059 temp.audio_no_playback = value;
1060 else if (strcmp(field_name, "stretch") == 0)
1061 temp.stretch = value;
1062 else if (strcmp(field_name, "snapshot_mode") == 0)
1063 temp.snapshot_mode = value;
1064 else if (strcmp(field_name, "strip_ansi") == 0)
1065 temp.strip_ansi = value;
1066 else if (strcmp(field_name, "quiet") == 0)
1067 temp.quiet = value;
1068 else if (strcmp(field_name, "encrypt_enabled") == 0)
1069 temp.encrypt_enabled = value;
1070 else if (strcmp(field_name, "no_encrypt") == 0)
1071 temp.no_encrypt = value;
1072 else if (strcmp(field_name, "discovery") == 0)
1073 temp.discovery = value;
1074 else if (strcmp(field_name, "discovery_expose_ip") == 0)
1075 temp.discovery_expose_ip = value;
1076 else if (strcmp(field_name, "discovery_insecure") == 0)
1077 temp.discovery_insecure = value;
1078 else if (strcmp(field_name, "webrtc") == 0)
1079 temp.webrtc = value;
1080 else if (strcmp(field_name, "lan_discovery") == 0)
1081 temp.lan_discovery = value;
1082 else if (strcmp(field_name, "no_mdns_advertise") == 0)
1083 temp.no_mdns_advertise = value;
1084 else if (strcmp(field_name, "prefer_webrtc") == 0)
1085 temp.prefer_webrtc = value;
1086 else if (strcmp(field_name, "no_webrtc") == 0)
1087 temp.no_webrtc = value;
1088 else if (strcmp(field_name, "webrtc_skip_stun") == 0)
1089 temp.webrtc_skip_stun = value;
1090 else if (strcmp(field_name, "webrtc_relay_only") == 0)
1091 temp.webrtc_relay_only = value;
1092 else if (strcmp(field_name, "webrtc_disable_turn") == 0)
1093 temp.webrtc_disable_turn = value;
1094 else if (strcmp(field_name, "enable_upnp") == 0)
1095 temp.enable_upnp = value;
1096 else if (strcmp(field_name, "require_server_identity") == 0)
1097 temp.require_server_identity = value;
1098 else if (strcmp(field_name, "require_client_identity") == 0)
1099 temp.require_client_identity = value;
1100 else if (strcmp(field_name, "require_server_verify") == 0)
1101 temp.require_server_verify = value;
1102 else if (strcmp(field_name, "require_client_verify") == 0)
1103 temp.require_client_verify = value;
1104 else if (strcmp(field_name, "palette_custom_set") == 0)
1105 temp.palette_custom_set = value;
1106 else if (strcmp(field_name, "media_loop") == 0)
1107 temp.media_loop = value;
1108 else if (strcmp(field_name, "media_from_stdin") == 0)
1109 temp.media_from_stdin = value;
1110 else if (strcmp(field_name, "auto_width") == 0)
1111 temp.auto_width = value;
1112 else if (strcmp(field_name, "auto_height") == 0)
1113 temp.auto_height = value;
1114 else if (strcmp(field_name, "splash_screen") == 0)
1115 temp.splash_screen = value;
1116 else if (strcmp(field_name, "status_screen") == 0)
1117 temp.status_screen = value;
1118 else if (strcmp(field_name, "matrix_rain") == 0)
1119 temp.matrix_rain = value;
1120 else if (strcmp(field_name, "waveform") == 0)
1121 temp.waveform = value;
1122 else if (strcmp(field_name, "fft") == 0)
1123 temp.fft = value;
1124 else if (strcmp(field_name, "fps_counter") == 0)
1125 temp.fps_counter = value;
1126
1127 asciichat_error_t err = rcu_validate_field(field_name, &temp);
1128 if (err != ASCIICHAT_OK)
1129 return err;
1130 }
1131
1132 bool_field_ctx_t ctx = {.field_name = field_name, .value = value};
1133 return options_update(bool_field_updater, &ctx);
1134}
Context for boolean field updates in RCU updater callback.
Definition rcu.c:872
const char * field_name
Name of the field to update.
Definition rcu.c:873
bool snapshot_mode
Snapshot mode (one frame and exit)
bool webrtc
Enable WebRTC mode for discovery session (default: true, P2P WebRTC)
bool discovery_expose_ip
ACDS: explicitly allow public IP disclosure without verification (opt-in)
bool discovery
Enable discovery session registration (default: false)
bool webrtc_relay_only
–webrtc-relay-only: Require TURN relay candidates
bool fft
Replace the video image with a live audio frequency spectrogram.
bool show_capabilities
Show terminal capabilities and exit.
bool no_mdns_advertise
Disable mDNS service advertisement (server only)
bool fps_counter
Show FPS counter overlay in top-right corner.
bool strip_ansi
Strip ANSI escape sequences.
bool audio_analysis_enabled
Enable audio analysis (debug)
bool enable_upnp
Enable UPnP/NAT-PMP port mapping for direct TCP (opt-in via –upnp)
bool webrtc_skip_stun
–webrtc-skip-stun: Skip Stage 2 (STUN), go to TURN
bool lan_discovery
Enable LAN service discovery via mDNS (client only)
bool audio_enabled
Enable audio streaming.
bool no_webrtc
–no-webrtc: Disable WebRTC, use Direct TCP only
bool matrix_rain
Matrix digital rain effect (false = disabled)
bool webrtc_disable_turn
–webrtc-disable-turn: Disable Stage 3 (TURN), use STUN only
bool audio_no_playback
Disable speaker playback (debug)
bool require_server_identity
ACDS: require servers to provide signed Ed25519 identity.
bool no_audio_mixer
Disable audio mixer (debug)
bool prefer_webrtc
–prefer-webrtc: Try WebRTC before Direct TCP
bool stretch
Allow aspect ratio distortion.
bool waveform
Replace the video image with a live audio waveform.
bool test_pattern
Use test pattern instead of webcam.
bool require_client_verify
Client: only connect to servers whose identity was verified by ACDS.
bool require_server_verify
Server: only accept clients who verified via ACDS.
bool media_loop
Loop media file playback.
bool require_client_identity
ACDS: require clients to provide signed Ed25519 identity.
bool discovery_insecure
ACDS: skip server key verification (MITM-vulnerable, requires explicit opt-in)

References ASCIICHAT_OK, options_state::audio_analysis_enabled, options_state::audio_enabled, options_state::audio_no_playback, options_state::auto_height, options_state::auto_width, options_state::discovery, options_state::discovery_expose_ip, options_state::discovery_insecure, options_state::enable_upnp, options_state::encode_audio, options_state::encrypt_enabled, ERROR_INVALID_PARAM, options_state::fft, bool_field_ctx_t::field_name, options_state::flip_x, options_state::flip_y, options_state::force_utf8, options_state::fps_counter, options_state::lan_discovery, options_state::matrix_rain, options_state::media_from_stdin, options_state::media_loop, options_state::no_audio_mixer, options_state::no_compress, options_state::no_encrypt, options_state::no_mdns_advertise, options_state::no_webrtc, options_get(), options_state::palette_custom_set, options_state::prefer_webrtc, options_state::quiet, options_state::require_client_identity, options_state::require_client_verify, options_state::require_server_identity, options_state::require_server_verify, SET_ERRNO, options_state::show_capabilities, options_state::snapshot_mode, options_state::splash_screen, options_state::status_screen, options_state::stretch, options_state::strip_ansi, options_state::test_pattern, options_state::waveform, options_state::webrtc, options_state::webrtc_disable_turn, options_state::webrtc_relay_only, and options_state::webrtc_skip_stun.

Referenced by client_main(), session_handle_keyboard_input(), set_fft(), set_flip_x(), set_flip_y(), set_matrix_rain(), and set_waveform().

◆ options_set_double()

asciichat_error_t options_set_double ( const char *  field_name,
double  value 
)

#include <options.h>

Definition at line 1283 of file rcu.c.

1283 {
1284 if (!field_name) {
1285 SET_ERRNO(ERROR_INVALID_PARAM, "field_name is NULL");
1286 return ERROR_INVALID_PARAM;
1287 }
1288
1289 // Normalize option names to internal field names
1290 const char *internal_name = field_name;
1291 if (strcmp(field_name, "microphone-volume") == 0 || strcmp(field_name, "ivolume") == 0) {
1292 internal_name = "microphone_sensitivity";
1293 } else if (strcmp(field_name, "speakers-volume") == 0 || strcmp(field_name, "volume") == 0) {
1294 internal_name = "speakers_volume";
1295 }
1296
1297 if (strcmp(internal_name, "snapshot_delay") != 0 && strcmp(internal_name, "microphone_sensitivity") != 0 &&
1298 strcmp(internal_name, "speakers_volume") != 0) {
1299 SET_ERRNO(ERROR_INVALID_PARAM, "Unknown double field: %s", field_name);
1300 return ERROR_INVALID_PARAM;
1301 }
1302
1303 // Pre-validate against registry metadata (if available)
1304 {
1305 const options_t *cur = options_get();
1306 options_t temp = *cur;
1307 // Manually set the field in temp to validate
1308 if (strcmp(internal_name, "snapshot_delay") == 0)
1309 temp.snapshot_delay = value;
1310 else if (strcmp(internal_name, "microphone_sensitivity") == 0)
1311 temp.microphone_sensitivity = value;
1312 else if (strcmp(internal_name, "speakers_volume") == 0)
1313 temp.speakers_volume = value;
1314
1315 asciichat_error_t err = rcu_validate_field(internal_name, &temp);
1316 if (err != ASCIICHAT_OK)
1317 return err;
1318 }
1319
1320 double_field_ctx_t ctx = {.field_name = internal_name, .value = value};
1321 return options_update(double_field_updater, &ctx);
1322}
Context for double field updates in RCU updater callback.
Definition rcu.c:1268
const char * field_name
Name of the field to update.
Definition rcu.c:1269
float speakers_volume
Speaker volume multiplier (0.0-1.0, default 1.0)
float microphone_sensitivity
Microphone volume multiplier (0.0-1.0, default 1.0)
double snapshot_delay
Snapshot delay in seconds.

References ASCIICHAT_OK, ERROR_INVALID_PARAM, double_field_ctx_t::field_name, options_state::microphone_sensitivity, options_get(), SET_ERRNO, options_state::snapshot_delay, and options_state::speakers_volume.

Referenced by session_handle_keyboard_input().

◆ options_set_int()

asciichat_error_t options_set_int ( const char *  field_name,
int  value 
)

#include <options.h>

Check if an option was explicitly set via command-line.

Convenience macro for checking if a specific option was explicitly provided by the user via command-line arguments (as opposed to using a default value).

Usage Examples:

// Check if user explicitly provided --width
if (IS_OPTION_EXPLICIT("width")) {
printf("User set width explicitly\n");
}
// Use in conditional logic
if (!IS_OPTION_EXPLICIT("color") && terminal_is_dark_mode()) {
// Color not set, apply dark mode defaults
}
#define IS_OPTION_EXPLICIT(name, opts)
Definition explicit.h:57
Parameters
option_nameThe long name of the option (e.g., "width", "port", "color")
Returns
true if option was explicitly set, false otherwise
Note
Option name must be a string literal matching the long_name from registry
Case-sensitive: must match exact option name
No locks needed - lock-free read via atomic pointer

Set a single option field (thread-safe, RCU-based)

Convenience function for updating a single field in the options struct. Uses RCU (copy-on-write) internally for thread-safe updates.

Example:

options_set_int("width", 120);
options_set_int("port", 8080);
options_set_bool("audio_enabled", true);

Thread Safety: Multiple writers are serialized with a mutex. Readers are never blocked (lock-free reads via GET_OPTION).

Definition at line 761 of file rcu.c.

761 {
762 if (!field_name) {
763 SET_ERRNO(ERROR_INVALID_PARAM, "field_name is NULL");
764 return ERROR_INVALID_PARAM;
765 }
766
767 if (strcmp(field_name, "width") != 0 && strcmp(field_name, "height") != 0 && strcmp(field_name, "max_clients") != 0 &&
768 strcmp(field_name, "compression_level") != 0 && strcmp(field_name, "reconnect_attempts") != 0 &&
769 strcmp(field_name, "microphone_index") != 0 && strcmp(field_name, "speakers_index") != 0 &&
770 strcmp(field_name, "discovery_port") != 0 && strcmp(field_name, "port") != 0 && strcmp(field_name, "fps") != 0 &&
771 strcmp(field_name, "color_mode") != 0 && strcmp(field_name, "color_filter") != 0 &&
772 strcmp(field_name, "render_mode") != 0 && strcmp(field_name, "log_level") != 0 &&
773 strcmp(field_name, "palette_type") != 0) {
774 SET_ERRNO(ERROR_INVALID_PARAM, "Unknown integer field: %s", field_name);
775 return ERROR_INVALID_PARAM;
776 }
777
778 // Keep the state API's public invariants explicit even when the registry
779 // has not yet been populated by the command-line parser.
780 if (strcmp(field_name, "port") == 0 && (value < 1 || value > 65535)) {
781 return SET_ERRNO(ERROR_INVALID_PARAM, "Option 'port' value %d out of range [1, 65535]", value);
782 }
783 if (strcmp(field_name, "color_mode") == 0 && value == TERM_COLOR_AUTO) {
784 return SET_ERRNO(ERROR_INVALID_PARAM, "Option 'color_mode' value %d is not valid for the state API", value);
785 }
786
787 // Pre-validate against registry metadata
788 {
789 const options_t *cur = options_get();
790
791 // Skip RCU update if value is already set (avoids unnecessary alloc+deferred-free)
792 int cur_value = 0;
793 if (strcmp(field_name, "width") == 0)
794 cur_value = cur->width;
795 else if (strcmp(field_name, "height") == 0)
796 cur_value = cur->height;
797 else if (strcmp(field_name, "max_clients") == 0)
798 cur_value = cur->max_clients;
799 else if (strcmp(field_name, "compression_level") == 0)
800 cur_value = cur->compression_level;
801 else if (strcmp(field_name, "reconnect_attempts") == 0)
802 cur_value = cur->reconnect_attempts;
803 else if (strcmp(field_name, "microphone_index") == 0)
804 cur_value = cur->microphone_index;
805 else if (strcmp(field_name, "speakers_index") == 0)
806 cur_value = cur->speakers_index;
807 else if (strcmp(field_name, "discovery_port") == 0)
808 cur_value = cur->discovery_port;
809 else if (strcmp(field_name, "port") == 0)
810 cur_value = cur->port;
811 else if (strcmp(field_name, "fps") == 0)
812 cur_value = cur->fps;
813 else if (strcmp(field_name, "color_mode") == 0)
814 cur_value = (int)cur->color_mode;
815 else if (strcmp(field_name, "color_filter") == 0)
816 cur_value = (int)cur->color_filter;
817 else if (strcmp(field_name, "render_mode") == 0)
818 cur_value = (int)cur->render_mode;
819 else if (strcmp(field_name, "log_level") == 0)
820 cur_value = (int)cur->log_level;
821 else if (strcmp(field_name, "palette_type") == 0)
822 cur_value = (int)cur->palette_type;
823 if (cur_value == value)
824 return ASCIICHAT_OK;
825
826 options_t temp = *cur;
827 // Manually set the field in temp to validate
828 if (strcmp(field_name, "width") == 0)
829 temp.width = value;
830 else if (strcmp(field_name, "height") == 0)
831 temp.height = value;
832 else if (strcmp(field_name, "max_clients") == 0)
833 temp.max_clients = value;
834 else if (strcmp(field_name, "compression_level") == 0)
835 temp.compression_level = value;
836 else if (strcmp(field_name, "reconnect_attempts") == 0)
837 temp.reconnect_attempts = value;
838 else if (strcmp(field_name, "microphone_index") == 0)
839 temp.microphone_index = value;
840 else if (strcmp(field_name, "speakers_index") == 0)
841 temp.speakers_index = value;
842 else if (strcmp(field_name, "discovery_port") == 0)
843 temp.discovery_port = value;
844 else if (strcmp(field_name, "port") == 0)
845 temp.port = value;
846 else if (strcmp(field_name, "fps") == 0)
847 temp.fps = value;
848 else if (strcmp(field_name, "color_mode") == 0)
849 temp.color_mode = (terminal_color_mode_t)value;
850 else if (strcmp(field_name, "color_filter") == 0)
851 temp.color_filter = (color_filter_t)value;
852 else if (strcmp(field_name, "render_mode") == 0)
853 temp.render_mode = (render_mode_t)value;
854 else if (strcmp(field_name, "log_level") == 0)
855 temp.log_level = (log_level_t)value;
856 else if (strcmp(field_name, "palette_type") == 0)
857 temp.palette_type = (palette_type_t)value;
858
859 asciichat_error_t err = rcu_validate_field(field_name, &temp);
860 if (err != ASCIICHAT_OK)
861 return err;
862 }
863
864 int_field_ctx_t ctx = {.field_name = field_name, .value = value};
865 asciichat_error_t update_result = options_update(int_field_updater, &ctx);
866 return update_result;
867}
log_level_t
Logging levels enumeration.
Definition types.h:29
palette_type_t
Built-in palette type enumeration.
Definition palette.h:84
Context for integer field updates in RCU updater callback.
Definition rcu.c:722
const char * field_name
Name of the field to update.
Definition rcu.c:723
int discovery_port
discovery server port (default: 27225)
int compression_level
zstd compression level (1-9)
int microphone_index
Microphone device index (-1 = default)
int max_clients
Maximum concurrent clients (server only)
render_mode_t render_mode
Render mode (foreground/background/half-block)
int speakers_index
Speakers device index (-1 = default)
int reconnect_attempts
Number of reconnection attempts (-1=infinite, 0=none)
color_filter_t
Monochromatic color filter enumeration.
Definition terminal.h:599
render_mode_t
Render mode preferences.
Definition terminal.h:662
terminal_color_mode_t
Terminal color support levels.
Definition terminal.h:578
@ TERM_COLOR_AUTO
Auto-detect color support from terminal capabilities.
Definition terminal.h:580

References ASCIICHAT_OK, options_state::color_filter, options_state::color_mode, options_state::compression_level, options_state::discovery_port, ERROR_INVALID_PARAM, int_field_ctx_t::field_name, options_state::fps, options_state::height, options_state::log_level, options_state::max_clients, options_state::microphone_index, options_get(), options_state::palette_type, options_state::port, options_state::reconnect_attempts, options_state::render_mode, SET_ERRNO, options_state::speakers_index, TERM_COLOR_AUTO, and options_state::width.

Referenced by session_handle_keyboard_input(), session_render_loop(), session_settings_apply_to_options(), set_color_filter(), set_color_mode(), set_height(), set_palette(), set_render_mode(), set_target_fps(), and set_width().

◆ options_set_string()

asciichat_error_t options_set_string ( const char *  field_name,
const char *  value 
)

#include <options.h>

Definition at line 1186 of file rcu.c.

1186 {
1187 if (!field_name) {
1188 SET_ERRNO(ERROR_INVALID_PARAM, "field_name is NULL");
1189 return ERROR_INVALID_PARAM;
1190 }
1191
1192 if (!value) {
1193 SET_ERRNO(ERROR_INVALID_PARAM, "value is NULL");
1194 return ERROR_INVALID_PARAM;
1195 }
1196
1197 // Validate field exists
1198 if (strcmp(field_name, "address") != 0 && strcmp(field_name, "address6") != 0 &&
1199 strcmp(field_name, "encrypt_key") != 0 && strcmp(field_name, "password") != 0 &&
1200 strcmp(field_name, "encrypt_keyfile") != 0 && strcmp(field_name, "server_key") != 0 &&
1201 strcmp(field_name, "client_keys") != 0 && strcmp(field_name, "discovery_server") != 0 &&
1202 strcmp(field_name, "discovery_service_key") != 0 && strcmp(field_name, "discovery_database_path") != 0 &&
1203 strcmp(field_name, "log_file") != 0 && strcmp(field_name, "media_file") != 0 &&
1204 strcmp(field_name, "palette_custom") != 0 && strcmp(field_name, "stun_servers") != 0 &&
1205 strcmp(field_name, "turn_servers") != 0 && strcmp(field_name, "turn_username") != 0 &&
1206 strcmp(field_name, "turn_credential") != 0 && strcmp(field_name, "turn_secret") != 0 &&
1207 strcmp(field_name, "session_string") != 0) {
1208 SET_ERRNO(ERROR_INVALID_PARAM, "Unknown string field: %s", field_name);
1209 return ERROR_INVALID_PARAM;
1210 }
1211
1212 // Pre-validate against registry metadata (for cross-field validators)
1213 {
1214 const options_t *cur = options_get();
1215 options_t temp = *cur;
1216 // Manually set the field in temp to validate
1217 if (strcmp(field_name, "address") == 0)
1218 SAFE_STRNCPY(temp.address, value, sizeof(temp.address));
1219 else if (strcmp(field_name, "address6") == 0)
1220 SAFE_STRNCPY(temp.address6, value, sizeof(temp.address6));
1221 else if (strcmp(field_name, "encrypt_key") == 0)
1222 SAFE_STRNCPY(temp.encrypt_key, value, sizeof(temp.encrypt_key));
1223 else if (strcmp(field_name, "password") == 0)
1224 SAFE_STRNCPY(temp.password, value, sizeof(temp.password));
1225 else if (strcmp(field_name, "encrypt_keyfile") == 0)
1226 SAFE_STRNCPY(temp.encrypt_keyfile, value, sizeof(temp.encrypt_keyfile));
1227 else if (strcmp(field_name, "server_key") == 0)
1228 SAFE_STRNCPY(temp.server_key, value, sizeof(temp.server_key));
1229 else if (strcmp(field_name, "client_keys") == 0)
1230 SAFE_STRNCPY(temp.client_keys, value, sizeof(temp.client_keys));
1231 else if (strcmp(field_name, "discovery_server") == 0)
1232 SAFE_STRNCPY(temp.discovery_server, value, sizeof(temp.discovery_server));
1233 else if (strcmp(field_name, "discovery_service_key") == 0)
1234 SAFE_STRNCPY(temp.discovery_service_key, value, sizeof(temp.discovery_service_key));
1235 else if (strcmp(field_name, "discovery_database_path") == 0)
1237 else if (strcmp(field_name, "log_file") == 0)
1238 SAFE_STRNCPY(temp.log_file, value, sizeof(temp.log_file));
1239 else if (strcmp(field_name, "media_file") == 0)
1240 SAFE_STRNCPY(temp.media_file, value, sizeof(temp.media_file));
1241 else if (strcmp(field_name, "palette_custom") == 0)
1242 SAFE_STRNCPY(temp.palette_custom, value, sizeof(temp.palette_custom));
1243 else if (strcmp(field_name, "stun_servers") == 0)
1244 SAFE_STRNCPY(temp.stun_servers, value, sizeof(temp.stun_servers));
1245 else if (strcmp(field_name, "turn_servers") == 0)
1246 SAFE_STRNCPY(temp.turn_servers, value, sizeof(temp.turn_servers));
1247 else if (strcmp(field_name, "turn_username") == 0)
1248 SAFE_STRNCPY(temp.turn_username, value, sizeof(temp.turn_username));
1249 else if (strcmp(field_name, "turn_credential") == 0)
1250 SAFE_STRNCPY(temp.turn_credential, value, sizeof(temp.turn_credential));
1251 else if (strcmp(field_name, "turn_secret") == 0)
1252 SAFE_STRNCPY(temp.turn_secret, value, sizeof(temp.turn_secret));
1253 else if (strcmp(field_name, "session_string") == 0)
1254 SAFE_STRNCPY(temp.session_string, value, sizeof(temp.session_string));
1255
1256 asciichat_error_t err = rcu_validate_field(field_name, &temp);
1257 if (err != ASCIICHAT_OK)
1258 return err;
1259 }
1260
1261 string_field_ctx_t ctx = {.field_name = field_name, .value = value};
1262 return options_update(string_field_updater, &ctx);
1263}
Context for string field updates in RCU updater callback.
Definition rcu.c:1139
const char * field_name
Name of the field to update.
Definition rcu.c:1140

References options_state::address, options_state::address6, ASCIICHAT_OK, options_state::client_keys, options_state::discovery_database_path, options_state::discovery_server, options_state::discovery_service_key, options_state::encrypt_key, options_state::encrypt_keyfile, ERROR_INVALID_PARAM, string_field_ctx_t::field_name, options_state::log_file, options_state::media_file, options_get(), options_state::palette_custom, options_state::password, SAFE_STRNCPY, options_state::server_key, options_state::session_string, SET_ERRNO, options_state::stun_servers, options_state::turn_credential, options_state::turn_secret, options_state::turn_servers, and options_state::turn_username.

Referenced by set_palette_chars().

◆ options_t_new()

options_t options_t_new ( void  )

#include <options.h>

Initialize options by parsing command-line arguments.

Parameters
argcArgument count from main()
argvArgument vector from main()
is_clienttrue if parsing client options, false for server options
Returns
ASCIICHAT_OK on success, ERROR_USAGE on parse errors

Parses command-line arguments and initializes all option global variables. This function must be called once at program startup before accessing any options.

Parsing Process:

  1. Parse command-line arguments using getopt() (POSIX-compliant)
  2. Validate option values (ranges, file existence, formats)
  3. Apply default values for unspecified options
  4. Perform mode-specific validation (client vs server)
  5. Initialize global option variables

Special Return Values:

  • ASCIICHAT_OK: Parsing succeeded (normal case)
  • ASCIICHAT_OK: Also returned for --help and --version (after printing info)
  • ERROR_USAGE: Parse error or invalid option (usage info should be printed)

Mode-Specific Behavior:

  • Client mode (is_client = true): Parses client-specific options (color mode, webcam, snapshot mode, etc.)
  • Server mode (is_client = false): Parses server-specific options (bind address, client keys whitelist, etc.)

Validation:

  • Numeric ranges (e.g., port 1-65535, webcam index >= 0)
  • File existence (key files, log files)
  • Format correctness (IP addresses, port numbers)
  • Mode compatibility (rejects client-only options in server mode)

Default Value Application: After parsing, unspecified options are set to defaults:

  • Terminal dimensions: Auto-detect or use OPT_WIDTH_DEFAULT/OPT_HEIGHT_DEFAULT
  • Network: localhost:27224
  • Webcam: Index 0 (first device)
  • Color mode: Auto-detect
  • Encryption: Enabled if keys found, disabled otherwise

Environment Variables: The following environment variables are checked during option parsing:

  • WEBCAM_DISABLED: When set to "1", "true", "yes", or "on", automatically enables test pattern mode (opt_test_pattern = true). Useful for CI/CD environments and testing without a physical webcam.
Example:
int main(int argc, char **argv) {
// Parse options (client mode)
asciichat_error_t err = options_init(argc, argv, true);
if (err != ASCIICHAT_OK) {
if (err == ERROR_USAGE) {
usage(stderr, true); // Print usage
}
return 1;
}
// Options are now available via global variables
printf("Connecting to %s:%s\n", opt_address, opt_port);
return 0;
}
Note
Must be called once at program startup before accessing options
Global option variables are initialized by this function
Returns ERROR_USAGE for invalid options (caller should print usage)
--help and --version cause early exit (function still returns ASCIICHAT_OK)

Create a new options_t struct with all defaults set

Initializes an options_t struct with all fields set to their default values from OPT_*_DEFAULT defines. This function is used internally by options_init() to ensure consistent default initialization before command-line parsing.

Behavior:

  • All numeric fields set to their OPT_*_DEFAULT values
  • All boolean fields set to their OPT_*_DEFAULT values
  • All string fields initialized with their default values
  • detected_mode set to MODE_INVALID (overwritten during parsing)

Usage: Typically called by options_init() internally. Can be used by custom code creating builder-based parsers for consistent defaults.

Returns
A new options_t struct with all defaults applied (stack-allocated)
Note
Returns a stack-allocated struct (caller should use immediately or copy)
All fields initialized to their OPT_*_DEFAULT constant values
Does not allocate memory (all fields are static arrays or primitives)

Definition at line 526 of file lib/options/options.c.

526 {
527 options_t opts;
528
529 // Zero-initialize all fields first
530 memset(&opts, 0, sizeof(opts));
531
532 // ============================================================================
533 // GENERAL CATEGORY - General-purpose options
534 // ============================================================================
535 opts.help = OPT_HELP_DEFAULT;
541 opts.fps_explicitly_set = false;
545
546 // ============================================================================
547 // LOGGING CATEGORY - Binary-level logging and output control options
548 // ============================================================================
549 // log_file is mode-dependent at startup and intentionally left empty here
553 opts.grep_pattern[0] = '\0'; // Explicitly ensure grep_pattern is empty
554 opts.json = OPT_JSON_DEFAULT;
556 // log_template and log_format are set by get_default_log_template()
557
558 // ============================================================================
559 // TERMINAL CATEGORY - Terminal display options
560 // ============================================================================
570
571 // ============================================================================
572 // DISPLAY CATEGORY - Display and rendering options
573 // ============================================================================
577 // palette_custom is already zeroed by memset
580 opts.fps = OPT_FPS_DEFAULT;
584 opts.waveform = false;
585 opts.fft = false;
588
589 // ============================================================================
590 // WEBCAM CATEGORY - Webcam and capture device options
591 // ============================================================================
595
596 // ============================================================================
597 // MEDIA CATEGORY - Media file streaming and playback options
598 // ============================================================================
599 // media_file is already zeroed by memset
600 // media_url is already zeroed by memset
605 // yt_dlp_options is already zeroed by memset
606
607 // Render-to-file options
608 // render_file is already zeroed by memset
610 // render_font is already zeroed by memset
612
613 // ============================================================================
614 // NETWORK CATEGORY - Network connectivity and protocol options
615 // ============================================================================
616 SAFE_STRNCPY(opts.address, OPT_ADDRESS_DEFAULT, sizeof(opts.address));
624 // Discovery Service (ACDS) Options
632 // discovery_service_key is already zeroed by memset
633 opts.discovery_database_path[0] = '\0'; // Explicitly ensure discovery_database_path is empty
634 // LAN Discovery & WebRTC Options
638 // WebRTC Mode & Strategy Options
643 opts.webrtc_relay_only = false;
652 // turn_secret is already zeroed by memset
653
654 // ============================================================================
655 // AUDIO CATEGORY - Audio capture, playback and processing options
656 // ============================================================================
667
668 // ============================================================================
669 // SECURITY CATEGORY - Encryption and authentication options
670 // ============================================================================
672 // encrypt_key is already zeroed by memset
673 // password is already zeroed by memset
674 // encrypt_keyfile is already zeroed by memset
677 // server_key is already zeroed by memset
678 // client_keys is already zeroed by memset
679 // identity_keys array is already zeroed by memset
680 opts.num_identity_keys = 0;
683
684 // ============================================================================
685 // CONFIGURATION CATEGORY - Application configuration options
686 // ============================================================================
687 // config_file is already zeroed by memset
688
689 // ============================================================================
690 // DATABASE CATEGORY - Database and persistent storage options
691 // ============================================================================
692 // discovery_database_path is already zeroed by memset (set above in NETWORK)
693
694 // ============================================================================
695 // Internal/System Fields
696 // ============================================================================
697 // session_string is already zeroed by memset
698 // detected_mode is already zeroed by memset
699
700 return opts;
701}
#define OPT_STRIP_ANSI_DEFAULT
Default strip ANSI escape sequences flag.
#define OPT_WEBRTC_SKIP_STUN_DEFAULT
Default WebRTC skip STUN flag (false = use STUN)
#define OPT_ACDS_EXPOSE_IP_DEFAULT
Default ACDS expose IP flag (false = private by default)
#define OPT_ENDPOINT_DISCOVERY_SERVICE
Default discovery service endpoint (what to connect to by default)
#define OPT_NO_AUDIO_MIXER_DEFAULT
Default no audio mixer flag (false = enable mixer)
#define OPT_FLIP_Y_DEFAULT
Default vertical flip state (false = no vertical flip)
#define OPT_SHOW_CAPABILITIES_DEFAULT
Default show terminal capabilities flag.
#define OPT_NO_ENCRYPT_DEFAULT
Default no encrypt flag (false = allow encryption)
#define OPT_QUIET_DEFAULT
Default quiet mode flag (false = logging enabled)
#define OPT_MICROPHONE_INDEX_DEFAULT
Default microphone device index (-1 means system default)
#define OPT_TURN_SERVERS_DEFAULT
Default TURN server URLs (comma-separated)
#define OPT_ENCODE_AUDIO_DEFAULT
Default audio encoding state (true = Opus encoding enabled)
#define OPT_COLOR_MODE_DEFAULT
Default color mode (auto-detect terminal capabilities)
#define OPT_PALETTE_TYPE_DEFAULT
Default palette type (standard ASCII art)
#define OPT_RECONNECT_ATTEMPTS_DEFAULT
Default reconnect attempts (-1 means auto/infinite)
#define OPT_SPEAKERS_VOLUME_DEFAULT
Default speakers volume (1.0 = normal volume)
#define OPT_NO_AUTH_DEFAULT
Default no auth flag (false = allow authentication)
#define OPT_TEST_PATTERN_DEFAULT
Default test pattern mode (false = use actual webcam)
#define OPT_WEBRTC_ICE_TIMEOUT_MS_DEFAULT
Default WebRTC ICE gathering timeout in milliseconds (10 seconds)
#define OPT_COMPRESSION_LEVEL_DEFAULT
Default compression level (1-9)
#define OPT_ENCRYPT_ENABLED_DEFAULT
Default encrypt enabled flag (true = encryption required)
#define OPT_REQUIRE_SERVER_IDENTITY_DEFAULT
Default require-server-identity setting for ACDS.
#define OPT_ENABLE_KEEPAWAKE_DEFAULT
Default keep-awake (prevent system sleep) flag (false = normal system sleep)
#define OPT_RENDER_MODE_DEFAULT
Default render mode (foreground characters only)
#define OPT_ACDS_PORT_INT_DEFAULT
Default ACDS discovery service port (integer)
#define OPT_AUDIO_ENABLED_DEFAULT
Default audio enabled flag (true = audio enabled by default)
#define OPT_MATRIX_RAIN_DEFAULT
Default Matrix rain effect flag (false = disabled)
#define OPT_FORCE_UTF8_DEFAULT
Default force UTF-8 support setting (auto-detect)
#define OPT_STUN_SERVERS_DEFAULT
Default STUN server URLs (comma-separated)
#define OPT_HELP_DEFAULT
Default help flag (false = don't show help)
#define OPT_ADDRESS6_DEFAULT
Default IPv6 server address.
#define OPT_RENDER_FONT_SIZE_DEFAULT
#define OPT_WEBRTC_RECONNECT_ATTEMPTS_DEFAULT
Default WebRTC reconnection attempts (3 = try initial + 3 retries)
#define OPT_SPLASH_DEFAULT
Default splash screen flag (true = show splash, false = hide splash)
#define OPT_RENDER_THEME_DEFAULT
#define OPT_MEDIA_FROM_STDIN_DEFAULT
Default media from stdin flag (false = not reading from stdin)
#define OPT_AUDIO_ANALYSIS_ENABLED_DEFAULT
Default audio analysis enabled flag.
#define OPT_VERSION_DEFAULT
Default version flag (false = don't show version)
#define OPT_ACDS_DEFAULT
Default ACDS registration flag (false = disabled)
#define OPT_NO_COMPRESS_DEFAULT
Default no compression flag (false = enable compression)
#define OPT_AUTO_HEIGHT_DEFAULT
Default auto-detect height flag (true = auto-detect from terminal)
#define OPT_REQUIRE_CLIENT_VERIFY_DEFAULT
Default require client verify flag (false = not required)
#define OPT_AUDIO_NO_PLAYBACK_DEFAULT
Default audio playback flag (false = enable playback)
#define OPT_PAUSE_DEFAULT
Default pause media flag (false = play immediately)
#define OPT_REQUIRE_SERVER_VERIFY_DEFAULT
Default require server verify flag (false = not required)
#define OPT_NO_MDNS_ADVERTISE_DEFAULT
Default no mDNS advertise flag (false = advertise enabled)
#define OPT_AUDIO_SOURCE_DEFAULT
Visualize all available sources by default.
#define OPT_STATUS_SCREEN_EXPLICITLY_SET_DEFAULT
Default status screen explicitly set flag (false = use default status screen setting)
#define OPT_COLOR_FILTER_DEFAULT
Default color filter (none - no filtering)
#define OPT_WEBCAM_INDEX_DEFAULT
Default webcam device index.
#define OPT_SNAPSHOT_MODE_DEFAULT
Default snapshot mode flag (false = continuous)
#define OPT_COLOR_SCHEME_NAME_DEFAULT
Default color scheme name (pastel)
#define OPT_MEDIA_LOOP_DEFAULT
Default loop media flag (false = play once)
#define OPT_NO_CHECK_UPDATE_DEFAULT
Default no-check-update flag (false = check for updates enabled)
#define OPT_STRETCH_DEFAULT
Default allow aspect ratio distortion flag.
#define OPT_STATUS_SCREEN_DEFAULT
Default status screen flag (true = show status, false = hide status)
#define OPT_LAN_DISCOVERY_DEFAULT
Default LAN discovery flag (false = discovery disabled)
#define OPT_AUTO_WIDTH_DEFAULT
Default auto-detect width flag (true = auto-detect from terminal)
#define OPT_JSON_DEFAULT
Default JSON logging flag (false = text output)
#define OPT_ACDS_INSECURE_DEFAULT
Default ACDS insecure mode flag (false = verify server)
#define OPT_WEBRTC_DEFAULT
Default WebRTC mode flag (true = P2P WebRTC, false = direct TCP)
#define OPT_REQUIRE_CLIENT_IDENTITY_DEFAULT
Default require-client-identity setting for ACDS.
#define OPT_DISABLE_KEEPAWAKE_DEFAULT
Default disable keep-awake flag (false = allow system sleep prevention)
#define OPT_ENABLE_UPNP_DEFAULT
Default enable UPnP flag (false = UPnP disabled)
#define OPT_TURN_CREDENTIAL_DEFAULT
Default TURN credential (empty = use ACDS credentials)
#define OPT_WEBRTC_SKIP_HOST_DEFAULT
Default WebRTC skip host candidates flag (false = use host candidates)
#define OPT_VERBOSE_LEVEL_DEFAULT
Default verbose level (0 = not verbose)
#define OPT_SNAPSHOT_DELAY_DEFAULT
Default snapshot delay in seconds.
#define OPT_LOG_LEVEL_DEFAULT
Default log level (LOG_INFO)
#define OPT_MICROPHONE_SENSITIVITY_DEFAULT
Default microphone sensitivity (1.0 = normal volume)
#define OPT_FLIP_X_DEFAULT
Default horizontal flip state (true = horizontally flipped) macOS webcams default to flipped (mirrore...
#define OPT_PORT_INT_DEFAULT
Default TCP port for client/server communication (integer)
#define OPT_SPLASH_SCREEN_EXPLICITLY_SET_DEFAULT
Default splash screen explicitly set flag (false = use default splash setting)
#define OPT_MAX_CLIENTS_DEFAULT
Default maximum concurrent clients (server only)
#define OPT_PREFER_WEBRTC_DEFAULT
Default prefer WebRTC flag (false = try direct TCP first)
#define OPT_WEBRTC_DISABLE_TURN_DEFAULT
Default WebRTC disable TURN flag (false = use TURN)
#define OPT_NO_WEBRTC_DEFAULT
Default no WebRTC flag (false = WebRTC enabled)
#define OPT_ADDRESS_DEFAULT
Default server address for client connections.
#define OPT_AUDIO_CAPTURE_SOURCE_DEFAULT
Preserve automatic local audio capture selection by default.
#define OPT_PALETTE_CUSTOM_SET_DEFAULT
Default custom palette set flag (false = not set)
#define OPT_TURN_USERNAME_DEFAULT
Default TURN username (empty = use ACDS credentials)
#define OPT_SPEAKERS_INDEX_DEFAULT
Default speakers device index (-1 means system default)
#define OPT_WEBSOCKET_PORT_SERVER_DEFAULT
Default WebSocket port for server mode (integer)
#define OPT_MEDIA_SEEK_TIMESTAMP_DEFAULT
Default media seek timestamp (start from beginning)
audio_capture_source_t audio_capture_source
Local capture/media selection policy.
int webrtc_ice_timeout_ms
–webrtc-ice-timeout: ICE gathering timeout in milliseconds (default: 10000)
bool disable_keepawake
Explicitly disable system sleep prevention (allow OS to sleep)
bool enable_keepawake
Explicitly enable system sleep prevention.
audio_source_t audio_source
Visualization source (all/call/mic/media)
bool no_auth
Disable authentication layer (–no-auth)
bool webrtc_skip_host
–webrtc-skip-host: Skip host candidates, force STUN/TURN only
int webcam_index
Webcam device index (0 = first)
int webrtc_reconnect_attempts
–webrtc-reconnect-attempts: Number of retry attempts (default: 3)
int render_theme
0=dark 1=light 2=auto
char grep_pattern[256]
PCRE2 regex for log filtering.
double render_font_size
Font size in points (default 12.0, supports e.g. 10.5)

References options_state::address, options_state::address6, options_state::audio_analysis_enabled, options_state::audio_capture_source, options_state::audio_enabled, options_state::audio_no_playback, options_state::audio_source, options_state::auto_height, options_state::auto_width, options_state::color, options_state::color_filter, options_state::color_mode, options_state::color_scheme_name, options_state::compression_level, options_state::disable_keepawake, options_state::discovery, options_state::discovery_database_path, options_state::discovery_expose_ip, options_state::discovery_insecure, options_state::discovery_port, options_state::discovery_server, options_state::enable_keepawake, options_state::enable_upnp, options_state::encode_audio, options_state::encrypt_enabled, options_state::fft, options_state::flip_x, options_state::flip_y, options_state::force_utf8, options_state::fps, options_state::fps_explicitly_set, options_state::grep_pattern, options_state::height, options_state::help, options_state::json, options_state::lan_discovery, options_state::log_level, options_state::matrix_rain, options_state::max_clients, options_state::media_from_stdin, options_state::media_loop, options_state::media_seek_timestamp, options_state::microphone_index, options_state::microphone_sensitivity, options_state::no_audio_mixer, options_state::no_auth, options_state::no_check_update, options_state::no_compress, options_state::no_encrypt, options_state::no_mdns_advertise, options_state::no_webrtc, options_state::num_identity_keys, OPT_ACDS_DEFAULT, OPT_ACDS_EXPOSE_IP_DEFAULT, OPT_ACDS_INSECURE_DEFAULT, OPT_ACDS_PORT_INT_DEFAULT, OPT_ADDRESS6_DEFAULT, OPT_ADDRESS_DEFAULT, OPT_AUDIO_ANALYSIS_ENABLED_DEFAULT, OPT_AUDIO_CAPTURE_SOURCE_DEFAULT, OPT_AUDIO_ENABLED_DEFAULT, OPT_AUDIO_NO_PLAYBACK_DEFAULT, OPT_AUDIO_SOURCE_DEFAULT, OPT_AUTO_HEIGHT_DEFAULT, OPT_AUTO_WIDTH_DEFAULT, OPT_COLOR_DEFAULT, OPT_COLOR_FILTER_DEFAULT, OPT_COLOR_MODE_DEFAULT, OPT_COLOR_SCHEME_NAME_DEFAULT, OPT_COMPRESSION_LEVEL_DEFAULT, OPT_DISABLE_KEEPAWAKE_DEFAULT, OPT_ENABLE_KEEPAWAKE_DEFAULT, OPT_ENABLE_UPNP_DEFAULT, OPT_ENCODE_AUDIO_DEFAULT, OPT_ENCRYPT_ENABLED_DEFAULT, OPT_ENDPOINT_DISCOVERY_SERVICE, OPT_FLIP_X_DEFAULT, OPT_FLIP_Y_DEFAULT, OPT_FORCE_UTF8_DEFAULT, OPT_FPS_DEFAULT, OPT_HEIGHT_DEFAULT, OPT_HELP_DEFAULT, OPT_JSON_DEFAULT, OPT_LAN_DISCOVERY_DEFAULT, OPT_LOG_LEVEL_DEFAULT, OPT_MATRIX_RAIN_DEFAULT, OPT_MAX_CLIENTS_DEFAULT, OPT_MEDIA_FROM_STDIN_DEFAULT, OPT_MEDIA_LOOP_DEFAULT, OPT_MEDIA_SEEK_TIMESTAMP_DEFAULT, OPT_MICROPHONE_INDEX_DEFAULT, OPT_MICROPHONE_SENSITIVITY_DEFAULT, OPT_NO_AUDIO_MIXER_DEFAULT, OPT_NO_AUTH_DEFAULT, OPT_NO_CHECK_UPDATE_DEFAULT, OPT_NO_COMPRESS_DEFAULT, OPT_NO_ENCRYPT_DEFAULT, OPT_NO_MDNS_ADVERTISE_DEFAULT, OPT_NO_WEBRTC_DEFAULT, OPT_PALETTE_CUSTOM_SET_DEFAULT, OPT_PALETTE_TYPE_DEFAULT, OPT_PAUSE_DEFAULT, OPT_PORT_INT_DEFAULT, OPT_PREFER_WEBRTC_DEFAULT, OPT_QUIET_DEFAULT, OPT_RECONNECT_ATTEMPTS_DEFAULT, OPT_RENDER_FONT_SIZE_DEFAULT, OPT_RENDER_MODE_DEFAULT, OPT_RENDER_THEME_DEFAULT, OPT_REQUIRE_CLIENT_IDENTITY_DEFAULT, OPT_REQUIRE_CLIENT_VERIFY_DEFAULT, OPT_REQUIRE_SERVER_IDENTITY_DEFAULT, OPT_REQUIRE_SERVER_VERIFY_DEFAULT, OPT_SHOW_CAPABILITIES_DEFAULT, OPT_SNAPSHOT_DELAY_DEFAULT, OPT_SNAPSHOT_MODE_DEFAULT, OPT_SPEAKERS_INDEX_DEFAULT, OPT_SPEAKERS_VOLUME_DEFAULT, OPT_SPLASH_DEFAULT, OPT_SPLASH_SCREEN_EXPLICITLY_SET_DEFAULT, OPT_STATUS_SCREEN_DEFAULT, OPT_STATUS_SCREEN_EXPLICITLY_SET_DEFAULT, OPT_STRETCH_DEFAULT, OPT_STRIP_ANSI_DEFAULT, OPT_STUN_SERVERS_DEFAULT, OPT_TEST_PATTERN_DEFAULT, OPT_TURN_CREDENTIAL_DEFAULT, OPT_TURN_SERVERS_DEFAULT, OPT_TURN_USERNAME_DEFAULT, OPT_VERBOSE_LEVEL_DEFAULT, OPT_VERSION_DEFAULT, OPT_WEBCAM_INDEX_DEFAULT, OPT_WEBRTC_DEFAULT, OPT_WEBRTC_DISABLE_TURN_DEFAULT, OPT_WEBRTC_ICE_TIMEOUT_MS_DEFAULT, OPT_WEBRTC_RECONNECT_ATTEMPTS_DEFAULT, OPT_WEBRTC_SKIP_HOST_DEFAULT, OPT_WEBRTC_SKIP_STUN_DEFAULT, OPT_WEBSOCKET_PORT_SERVER_DEFAULT, OPT_WIDTH_DEFAULT, options_state::palette_custom_set, options_state::palette_type, options_state::pause, options_state::port, options_state::prefer_webrtc, options_state::quiet, options_state::reconnect_attempts, options_state::render_font_size, options_state::render_mode, options_state::render_theme, options_state::require_client_identity, options_state::require_client_verify, options_state::require_server_identity, options_state::require_server_verify, SAFE_STRNCPY, options_state::show_capabilities, options_state::snapshot_delay, options_state::snapshot_mode, options_state::speakers_index, options_state::speakers_volume, options_state::splash_screen, options_state::splash_screen_explicitly_set, options_state::status_screen, options_state::status_screen_explicitly_set, options_state::stretch, options_state::strip_ansi, options_state::stun_servers, options_state::test_pattern, options_state::turn_credential, options_state::turn_servers, options_state::turn_username, options_state::verbose_level, options_state::version, options_state::waveform, options_state::webcam_index, options_state::webrtc, options_state::webrtc_disable_turn, options_state::webrtc_ice_timeout_ms, options_state::webrtc_reconnect_attempts, options_state::webrtc_relay_only, options_state::webrtc_skip_host, options_state::webrtc_skip_stun, options_state::websocket_port, and options_state::width.

Referenced by config_create_default(), options_init(), options_state_init(), and options_t_new_preserve_binary().

◆ parse_manpage_sections()

parsed_section_t * parse_manpage_sections ( const char *  filepath,
size_t *  num_sections 
)

#include <manpage.h>

Parse existing man page template to extract sections.

Parameters
filepathPath to existing man page template (.1.in file)
num_sectionsOutput: number of sections found
Returns
Array of parsed sections (caller must free with free_parsed_sections)
NULL on error

Definition at line 682 of file options/manpage.c.

682 {
683 if (!filepath || !num_sections) {
684 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters");
685 return NULL;
686 }
687
688 FILE *f = platform_fopen("file_stream", filepath, "r");
689 if (!f) {
690 SET_ERRNO_SYS(ERROR_CONFIG, "Failed to open file: %s", filepath);
691 return NULL;
692 }
693
694 parsed_section_t *sections = NULL;
695 asciichat_error_t err = manpage_parser_parse_file(f, &sections, num_sections);
696 fclose(f);
697
698 if (err != ASCIICHAT_OK) {
699 return NULL;
700 }
701
702 return sections;
703}
asciichat_error_t manpage_parser_parse_file(FILE *f, parsed_section_t **out_sections, size_t *out_count)
Parse man page sections from a FILE handle.
Definition parser.c:339
Parsed section information.
Definition manpage.h:38

References ASCIICHAT_OK, ERROR_CONFIG, ERROR_INVALID_PARAM, manpage_parser_parse_file(), platform_fopen(), SET_ERRNO, and SET_ERRNO_SYS.

◆ strtoint_safe()

int strtoint_safe ( const char *  str)

#include <options.h>

Safely parse string to integer with validation.

Parameters
strString to parse (must not be NULL)
Returns
Integer value on success, INT_MIN on error

Parses a string to an integer with comprehensive validation:

  • Validates that string is not NULL or empty
  • Performs base-10 conversion using strtol()
  • Checks for partial conversions (characters left unconverted)
  • Validates result is within int range (INT_MIN to INT_MAX)
  • Returns INT_MIN on any error condition

This function is used internally by the options parser to safely convert command-line argument strings to integer values with proper error handling.

Note
Returns INT_MIN on error (which is distinguishable from valid negative values since INT_MIN is a valid integer, but unlikely to be used as an option value). The options parser checks for INT_MIN to detect parse errors.
Thread-safe: Uses only local variables, no static state.
Example:
const char *arg = "80";
int width = strtoint_safe(arg);
if (width == INT_MIN) {
// Parse error
} else {
// Valid integer: width == 80
}
int strtoint_safe(const char *str)
Safely parse string to integer with validation.

Parses a string to integer using parse_int32() with full range checking. Returns INT_MIN on error (NULL input, empty string, invalid format, out of range).

Parameters
strString to parse
Returns
Parsed integer value, or INT_MIN on error
Warning
INT_MIN is used as error sentinel, so cannot represent INT_MIN value

Example:

int val = strtoint_safe(optarg);
if (val == INT_MIN) {
fprintf(stderr, "Invalid integer: %s\n", optarg);
}

Definition at line 164 of file options/common.c.

164 {
165 if (!str || *str == '\0') {
166 return INT_MIN; // Error: NULL or empty string
167 }
168
169 int32_t result = 0;
170 // Use safe parsing utility with full int32 range validation
171 if (parse_int32(str, &result, INT_MIN, INT_MAX) != ASCIICHAT_OK) {
172 return INT_MIN; // Error: invalid input or out of range
173 }
174
175 return (int)result;
176}
asciichat_error_t parse_int32(const char *str, int32_t *out_value, int32_t min_value, int32_t max_value)
Parse signed 32-bit integer with range validation.

References ASCIICHAT_OK, and parse_int32().

Referenced by validate_fps_opt(), validate_opt_device_index(), validate_opt_reconnect(), and validate_positive_int_opt().

◆ update_dimensions_for_full_height()

void update_dimensions_for_full_height ( options_t *  opts)

#include <options.h>

Update dimensions to use full terminal height while maintaining aspect ratio.

Adjusts width and height to use the full terminal height while preserving the aspect ratio of the current dimensions. Useful for maximizing vertical space utilization.

Calculation:

  • Gets full terminal height via update_dimensions_to_terminal_size()
  • Calculates aspect ratio from current dimensions
  • Adjusts width to match new height while preserving ratio
  • Updates opts->width and opts->height

Example:

  • Current dimensions: 80×30 (aspect ratio 2.67)
  • Terminal size: 120×40
  • Result: 107×40 (maintains 2.67 ratio, uses full 40 height)

Usage:

// User presses key to maximize vertical space
// Redraw video feed with new dimensions
Parameters
optsPointer to options_t struct to update (must not be NULL)
Note
Updates opts->width and opts->height
Queries terminal size internally
Preserves aspect ratio of original dimensions
Useful for dynamic terminal resizing

Update dimensions to use full terminal height while maintaining aspect ratio.

Sets opt_height to terminal height when auto-detected. Used during initialization to maximize vertical space usage.

Behavior:

  • Both auto: Set both width and height to terminal size
  • Only height auto: Set height to terminal height
  • Only width auto: Set width to terminal width
  • Neither auto: No change
Note
Does not use log_debug because logging may not be initialized yet
Fails silently if terminal size detection fails (keeps defaults)

Example:

// During options_init():
}
bool auto_height
bool auto_width

Definition at line 606 of file options/common.c.

606 {
607 if (!opts) {
608 return;
609 }
610
611 unsigned short int term_width, term_height;
612
613 // Note: Logging is not available during options_init, so we can't use log_debug here
614 asciichat_error_t result = get_terminal_size(&term_width, &term_height);
615
616 if (result == ASCIICHAT_OK) {
617 // If both dimensions are auto, set height to terminal height and let
618 // aspect_ratio calculate width
619 if (opts->auto_height && opts->auto_width) {
620 opts->height = term_height;
621 opts->width = term_width; // Also set width when both are auto
622 }
623 // If only height is auto, use full terminal height
624 else if (opts->auto_height) {
625 opts->height = term_height;
626 }
627 // If only width is auto, use full terminal width
628 else if (opts->auto_width) {
629 opts->width = term_width;
630 }
631 } else {
632 // Terminal size detection failed, but we can still continue with defaults
633 }
634}
asciichat_error_t get_terminal_size(unsigned short int *width, unsigned short int *height)
Get terminal size with multiple fallback methods.

References ASCIICHAT_OK, options_state::auto_height, options_state::auto_width, get_terminal_size(), options_state::height, and options_state::width.

Referenced by options_init().

◆ update_dimensions_to_terminal_size()

void update_dimensions_to_terminal_size ( options_t *  opts)

#include <options.h>

Update dimensions to match current terminal size.

Queries the current terminal for its size and updates width/height in the options struct. Uses platform-specific terminal size detection APIs.

Platform Implementations:

  • POSIX (Linux/macOS): TIOCGWINSZ ioctl on stdout
  • Windows: Console API GetConsoleScreenBufferInfo
  • Fallback: Environment variables ($COLUMNS, $LINES)
  • Final Fallback: Default constants (OPT_WIDTH_DEFAULT, OPT_HEIGHT_DEFAULT)

Usage:

  • Call during terminal resize (e.g., POSIX SIGWINCH handler)
  • Call to refresh dimensions when window size changes
  • Call when auto-detect is enabled and dimensions need updating

Example:

void handle_sigwinch(int sig) {
// Redraw UI with new dimensions
}
Parameters
optsPointer to options_t struct to update (must not be NULL)
Note
Updates opts->width and opts->height directly
Uses platform-specific APIs for accuracy
Handles all failure modes with sensible fallbacks
Safe to call from signal handlers (POSIX)

Update dimensions to match current terminal size.

Updates opt_width and opt_height to current terminal size for auto-detected dimensions. Used after logging is initialized (can use log_debug).

Behavior:

  • auto_width: Set width to terminal width
  • auto_height: Set height to terminal height
  • Neither: No change
Note
Logs debug messages about dimension updates
Logs debug message if terminal size detection fails

Example:

// After logging initialization:
log_info("Terminal dimensions: %dx%d", opt_width, opt_height);

Definition at line 636 of file options/common.c.

636 {
637 if (!opts) {
638 return;
639 }
640
641 unsigned short int term_width, term_height;
642 // Get current terminal size (get_terminal_size already handles ioctl first, then $COLUMNS/$LINES fallback)
643 asciichat_error_t terminal_result = get_terminal_size(&term_width, &term_height);
644 if (terminal_result == ASCIICHAT_OK) {
645 // Use INFO level so this is visible without -v flag (important for debugging dimension issues)
646 log_dev("Terminal size detected: %ux%u (auto_width=%d, auto_height=%d)", term_width, term_height, opts->auto_width,
647 opts->auto_height);
648 if (opts->auto_width) {
649 opts->width = term_width;
650 log_debug("Auto-width: set width to %u", opts->width);
651 }
652 if (opts->auto_height) {
653 opts->height = term_height;
654 log_debug("Auto-height: set height to %u", opts->height);
655 }
656 log_debug("Final dimensions: %ux%u", opts->width, opts->height);
657 } else {
658 // Terminal detection failed - keep the default values set in options_init()
659 log_warn("TERMINAL_DETECT_FAIL: Could not detect terminal size, using defaults: %ux%u", opts->width, opts->height);
660 }
661}

References ASCIICHAT_OK, options_state::auto_height, options_state::auto_width, get_terminal_size(), options_state::height, log_debug, log_dev, log_warn, and options_state::width.

Referenced by options_init().

◆ usage()

void usage ( FILE *  stream,
asciichat_mode_t  mode 
)

#include <options.h>

Print usage information for a specific mode.

Parameters
streamFile stream to write to (typically stdout or stderr)
modeMode to show usage for (MODE_SERVER, MODE_CLIENT, MODE_MIRROR, etc.)

Generates and prints comprehensive help text for the requested mode.

Output Sections:

  • Program synopsis and description
  • Mode-specific usage syntax
  • All available options with descriptions
  • Options grouped by category (Network, Display, Encryption, etc.)
  • Option defaults and possible values
  • Common usage examples

Options Displayed:

  • Shows only options applicable to the requested mode
  • Includes both short and long forms (e.g., -p, --port)
  • Shows mode bitmask to indicate applicability to other modes
  • Marks required options where applicable
  • Shows default values where available

Typical Usage:

// In error handling
if (options_init(argc, argv) == ERROR_USAGE) {
asciichat_mode_t mode = GET_OPTION(detected_mode);
usage(stderr, mode);
return 1;
}
// For manual help display
usage(stdout, MODE_SERVER);
Note
Called automatically by options_init() for –help
Mode must be valid (MODE_SERVER, MODE_CLIENT, etc.)
Output formatted for 80-column terminals
Options grouped by functionality for readability

Definition at line 684 of file options/common.c.

684 {
685 if (!desc) {
686 return;
687 }
688
689 // Find mode metadata
690 const mode_metadata_t *metadata = NULL;
691 for (size_t i = 0; i < sizeof(mode_info) / sizeof(mode_info[0]); i++) {
692 if (mode_info[i].mode == mode) {
693 metadata = &mode_info[i];
694 break;
695 }
696 }
697
698 if (!metadata) {
699 (void)fprintf(desc, "error: Unknown mode\n");
700 return;
701 }
702
703 // Get unified config
704 const options_config_t *config = options_preset_unified(metadata->program_name, metadata->description);
705 if (!config) {
706 (void)fprintf(desc, "Error: Failed to create options config\n");
707 return;
708 }
709
710 options_print_help_for_mode(config, mode, metadata->program_name, metadata->description, desc);
712}
void options_print_help_for_mode(const options_config_t *config, asciichat_mode_t mode, const char *program_name, const char *description, FILE *desc)
Print help for a specific mode or binary level.
Definition help.c:1105
Mode metadata for usage display.
const char * program_name
Program name shown in usage.
const char * description
One-line description of mode.

References mode_metadata_t::description, options_config_destroy(), options_preset_unified(), options_print_help_for_mode(), and mode_metadata_t::program_name.

Referenced by action_help_acds(), action_help_client(), action_help_discovery(), action_help_mirror(), action_help_server(), manpage_content_generate_usage(), manpage_merger_generate_usage(), options_builder_add_usage(), options_config_calculate_max_col_width(), options_init(), and options_print_help_for_mode().

◆ validate_opt_color_mode()

int validate_opt_color_mode ( const char *  value_str,
char *  error_msg,
size_t  error_msg_size 
)

#include <validation.h>

Validate color mode string.

Parameters
value_strColor mode value as string
error_msgBuffer for error message (can be NULL)
error_msg_sizeSize of error message buffer
Returns
Parsed color mode enum value on success, -1 on error

Valid values: auto, none, mono, 16, 16color, 256, 256color, truecolor, 24bit

Validate color mode string Returns parsed color mode on success, -1 on error

Definition at line 138 of file validation.c.

138 {
139 if (!value_str) {
140 if (error_msg) {
141 SAFE_SNPRINTF(error_msg, error_msg_size, "Color mode value is required");
142 }
143 return -1;
144 }
145
146 if (strcmp(value_str, "auto") == 0) {
147 return COLOR_MODE_AUTO;
148 }
149 if (strcmp(value_str, "none") == 0 || strcmp(value_str, "mono") == 0) {
150 return COLOR_MODE_NONE;
151 }
152 if (strcmp(value_str, "16") == 0 || strcmp(value_str, "16color") == 0) {
153 return COLOR_MODE_16_COLOR;
154 }
155 if (strcmp(value_str, "256") == 0 || strcmp(value_str, "256color") == 0) {
157 }
158 if (strcmp(value_str, "truecolor") == 0 || strcmp(value_str, "24bit") == 0) {
160 }
161 if (error_msg) {
162 SAFE_SNPRINTF(error_msg, error_msg_size,
163 "Invalid color mode '%s'. Valid modes: auto, none, mono, 16, 256, truecolor", value_str);
164 }
165 return -1;
166}
#define COLOR_MODE_16_COLOR
16-color mode (full name)
#define COLOR_MODE_256_COLOR
256-color mode (full name)
#define COLOR_MODE_TRUECOLOR
24-bit truecolor mode
#define COLOR_MODE_AUTO
Backward compatibility aliases for color mode enum values.
#define COLOR_MODE_NONE
Monochrome mode.

References COLOR_MODE_16_COLOR, COLOR_MODE_256_COLOR, COLOR_MODE_AUTO, COLOR_MODE_NONE, COLOR_MODE_TRUECOLOR, and SAFE_SNPRINTF.

◆ validate_opt_compression_level()

int validate_opt_compression_level ( const char *  value_str,
char *  error_msg,
size_t  error_msg_size 
)

#include <validation.h>

Validate compression level (1-9)

Parameters
value_strCompression level as string
error_msgBuffer for error message (can be NULL)
error_msg_sizeSize of error message buffer
Returns
Parsed level on success, -1 on error

Validates zstd compression level in range 1-9.

Validate compression level (1-9) Returns parsed value on success, -1 on error

Definition at line 387 of file validation.c.

387 {
388 int result = validate_int_range(value_str, 1, 9, "Compression level", error_msg, error_msg_size);
389 return (result == INT_MIN) ? -1 : result;
390}

◆ validate_opt_device_index()

int validate_opt_device_index ( const char *  value_str,
char *  error_msg,
size_t  error_msg_size 
)

#include <validation.h>

Validate device index (-1 for default, 0+ for specific device)

Parameters
value_strDevice index as string
error_msgBuffer for error message (can be NULL)
error_msg_sizeSize of error message buffer
Returns
Parsed index on success, INT_MIN on error

Used for microphone-index, speakers-index, webcam-index. -1 means use system default device.

Validate device index (-1 for default, or 0+ for specific device) Returns parsed value on success, INT_MIN on error

Definition at line 456 of file validation.c.

456 {
457 if (!value_str || strlen(value_str) == 0) {
458 if (error_msg) {
459 SAFE_SNPRINTF(error_msg, error_msg_size, "Device index value is required");
460 }
461 return INT_MIN;
462 }
463
464 int index = strtoint_safe(value_str);
465 if (index == INT_MIN) {
466 if (error_msg) {
467 SAFE_SNPRINTF(error_msg, error_msg_size,
468 "Invalid device index '%s'. Must be -1 (default) or a non-negative integer.", value_str);
469 }
470 return INT_MIN;
471 }
472
473 // -1 is valid (system default), otherwise must be >= 0
474 if (index < -1) {
475 if (error_msg) {
476 SAFE_SNPRINTF(error_msg, error_msg_size,
477 "Invalid device index '%d'. Must be -1 (default) or a non-negative integer.", index);
478 }
479 return INT_MIN;
480 }
481 return index;
482}

References SAFE_SNPRINTF, and strtoint_safe().

Referenced by validate_webcam_index().

◆ validate_opt_float_non_negative()

float validate_opt_float_non_negative ( const char *  value_str,
char *  error_msg,
size_t  error_msg_size 
)

#include <validation.h>

Validate non-negative float value.

Parameters
value_strFloat value as string
error_msgBuffer for error message (can be NULL)
error_msg_sizeSize of error message buffer
Returns
Parsed float value on success, -1.0f on error

Validates that the string is a valid non-negative floating-point number.

Validate float value (non-negative) Returns parsed value on success, returns -1.0f on error (caller must check)

Definition at line 320 of file validation.c.

320 {
321 if (!value_str || strlen(value_str) == 0) {
322 if (error_msg) {
323 SAFE_SNPRINTF(error_msg, error_msg_size, "Value is required");
324 }
325 return -1.0f;
326 }
327
328 char *endptr;
329 float val = strtof(value_str, &endptr);
330 if (*endptr != '\0' || value_str == endptr) {
331 if (error_msg) {
332 SAFE_SNPRINTF(error_msg, error_msg_size, "Invalid float value '%s'. Must be a number.", value_str);
333 }
334 return -1.0f;
335 }
336 if (val < 0.0f) {
337 if (error_msg) {
338 SAFE_SNPRINTF(error_msg, error_msg_size, "Value must be non-negative (got %.2f)", val);
339 }
340 return -1.0f;
341 }
342 return val;
343}

References SAFE_SNPRINTF.

◆ validate_opt_fps()

int validate_opt_fps ( const char *  value_str,
char *  error_msg,
size_t  error_msg_size 
)

#include <validation.h>

Validate FPS value (1-144)

Parameters
value_strFPS value as string
error_msgBuffer for error message (can be NULL)
error_msg_sizeSize of error message buffer
Returns
Parsed FPS value on success, -1 on error

Validates that the FPS is in the valid range of 1-144.

Validate FPS value (1-144) Returns parsed value on success, -1 on error

Definition at line 396 of file validation.c.

396 {
397 int result = validate_int_range(value_str, 1, 144, "FPS", error_msg, error_msg_size);
398 return (result == INT_MIN) ? -1 : result;
399}

◆ validate_opt_ip_address()

int validate_opt_ip_address ( const char *  value_str,
char *  parsed_address,
size_t  address_size,
bool  is_client,
char *  error_msg,
size_t  error_msg_size 
)

#include <validation.h>

Validate IP address or hostname.

Parameters
value_strIP address or hostname as string
parsed_addressBuffer to store resolved/parsed address
address_sizeSize of parsed_address buffer
is_clientTrue if client mode (for error messages)
error_msgBuffer for error message (can be NULL)
error_msg_sizeSize of error message buffer
Returns
0 on success, -1 on error

Validates IPv4, IPv6 addresses, or resolves hostname to IP.

Validate IP address or hostname Returns 0 on success, -1 on error Sets parsed_address on success (resolved if hostname)

Definition at line 268 of file validation.c.

269 {
270 (void)is_client; // Parameter not used but kept for API consistency
271 if (!value_str || strlen(value_str) == 0) {
272 if (error_msg) {
273 SAFE_SNPRINTF(error_msg, error_msg_size, "Address value is required");
274 }
275 return -1;
276 }
277
278 // Parse IPv6 address (remove brackets if present)
279 char parsed_addr[OPTIONS_BUFF_SIZE];
280 if (parse_ipv6_address(value_str, parsed_addr, sizeof(parsed_addr)) == 0) {
281 value_str = parsed_addr;
282 }
283
284 // Check if it's a valid IPv4 address
285 if (is_valid_ipv4(value_str)) {
286 SAFE_SNPRINTF(parsed_address, address_size, "%s", value_str);
287 return 0;
288 }
289 // Check if it's a valid IPv6 address
290 if (is_valid_ipv6(value_str)) {
291 SAFE_SNPRINTF(parsed_address, address_size, "%s", value_str);
292 return 0;
293 }
294 // Check if it looks like an invalid IP (has dots but not valid IPv4 format)
295 if (strchr(value_str, '.') != NULL) {
296 if (error_msg) {
297 SAFE_SNPRINTF(error_msg, error_msg_size,
298 "Invalid IP address format '%s'. IPv4 addresses must have exactly 4 octets.", value_str);
299 }
300 return -1;
301 }
302
303 // Otherwise, try to resolve as hostname
304 char resolved_ip[OPTIONS_BUFF_SIZE];
305 if (platform_resolve_hostname_to_ipv4(value_str, resolved_ip, sizeof(resolved_ip)) == 0) {
306 SAFE_SNPRINTF(parsed_address, address_size, "%s", resolved_ip);
307 return 0;
308 } else {
309 if (error_msg) {
310 SAFE_SNPRINTF(error_msg, error_msg_size, "Failed to resolve hostname '%s' to IP address.", value_str);
311 }
312 return -1;
313 }
314}
asciichat_error_t platform_resolve_hostname_to_ipv4(const char *hostname, char *ipv4_out, size_t ipv4_out_size)
Resolve hostname to IPv4 address.
int is_valid_ipv4(const char *ip)
Check if a string is a valid IPv4 address.
Definition ip.c:58
int is_valid_ipv6(const char *ip)
Check if a string is a valid IPv6 address.
Definition ip.c:105
int parse_ipv6_address(const char *input, char *output, size_t output_size)
Parse IPv6 address, removing brackets if present.
Definition ip.c:158

References is_valid_ipv4(), is_valid_ipv6(), OPTIONS_BUFF_SIZE, parse_ipv6_address(), platform_resolve_hostname_to_ipv4(), and SAFE_SNPRINTF.

◆ validate_opt_log_level()

int validate_opt_log_level ( const char *  value_str,
char *  error_msg,
size_t  error_msg_size 
)

#include <validation.h>

Validate log level string.

Parameters
value_strLog level as string
error_msgBuffer for error message (can be NULL)
error_msg_sizeSize of error message buffer
Returns
Parsed log level enum value on success, -1 on error

Valid values (case-insensitive): dev, debug, info, warn, error, fatal

Validate log level string Returns parsed log level on success, -1 on error

Definition at line 234 of file validation.c.

234 {
235 if (!value_str) {
236 if (error_msg) {
237 SAFE_SNPRINTF(error_msg, error_msg_size, "Log level value is required");
238 }
239 return -1;
240 }
241
242 if (platform_strcasecmp(value_str, "dev") == 0) {
243 return LOG_DEV;
244 } else if (platform_strcasecmp(value_str, "debug") == 0) {
245 return LOG_DEBUG;
246 } else if (platform_strcasecmp(value_str, "info") == 0) {
247 return LOG_INFO;
248 } else if (platform_strcasecmp(value_str, "warn") == 0) {
249 return LOG_WARN;
250 } else if (platform_strcasecmp(value_str, "error") == 0) {
251 return LOG_ERROR;
252 } else if (platform_strcasecmp(value_str, "fatal") == 0) {
253 return LOG_FATAL;
254 } else {
255 if (error_msg) {
256 SAFE_SNPRINTF(error_msg, error_msg_size,
257 "Invalid log level '%s'. Valid levels: dev, debug, info, warn, error, fatal", value_str);
258 }
259 return -1;
260 }
261}
int platform_strcasecmp(const char *s1, const char *s2)
Case-insensitive string comparison.
#define LOG_DEV
Definition types.h:38
#define LOG_DEBUG
Definition types.h:39
#define LOG_FATAL
Definition types.h:43
#define LOG_ERROR
Definition types.h:42
#define LOG_WARN
Definition types.h:41
#define LOG_INFO
Definition types.h:40

References LOG_DEBUG, LOG_DEV, LOG_ERROR, LOG_FATAL, LOG_INFO, LOG_WARN, platform_strcasecmp(), and SAFE_SNPRINTF.

Referenced by parse_log_level_option().

◆ validate_opt_max_clients()

int validate_opt_max_clients ( const char *  value_str,
char *  error_msg,
size_t  error_msg_size 
)

#include <validation.h>

Validate max clients value (1-32)

Parameters
value_strMax clients value as string
error_msgBuffer for error message (can be NULL)
error_msg_sizeSize of error message buffer
Returns
Parsed value on success, -1 on error

Validates that the max clients is in the valid range of 1-32.

Validate max clients (1-32) Returns parsed value on success, -1 on error

Definition at line 378 of file validation.c.

378 {
379 int result = validate_int_range(value_str, 1, 32, "Max clients", error_msg, error_msg_size);
380 return (result == INT_MIN) ? -1 : result;
381}

◆ validate_opt_non_negative_int()

int validate_opt_non_negative_int ( const char *  value_str,
char *  error_msg,
size_t  error_msg_size 
)

#include <validation.h>

Validate non-negative integer.

Parameters
value_strInteger value as string
error_msgBuffer for error message (can be NULL)
error_msg_sizeSize of error message buffer
Returns
Parsed value on success, -1 on error

Validates that the string is a non-negative integer (>= 0).

Validate non-negative integer Returns parsed value on success, -1 on error

Definition at line 129 of file validation.c.

129 {
130 int result = validate_int_range(value_str, 0, INT_MAX, "Value", error_msg, error_msg_size);
131 return (result == INT_MIN) ? -1 : result;
132}

◆ validate_opt_palette()

int validate_opt_palette ( const char *  value_str,
char *  error_msg,
size_t  error_msg_size 
)

#include <validation.h>

Validate palette type string.

Parameters
value_strPalette type as string
error_msgBuffer for error message (can be NULL)
error_msg_sizeSize of error message buffer
Returns
Parsed palette type enum value on success, -1 on error

Valid values: standard, blocks, digital, minimal, cool, custom

Validate palette type string Returns parsed palette type on success, -1 on error

Definition at line 200 of file validation.c.

200 {
201 if (!value_str) {
202 if (error_msg) {
203 SAFE_SNPRINTF(error_msg, error_msg_size, "Palette value is required");
204 }
205 return -1;
206 }
207
208 if (strcmp(value_str, "standard") == 0) {
209 return PALETTE_STANDARD;
210 } else if (strcmp(value_str, "blocks") == 0) {
211 return PALETTE_BLOCKS;
212 } else if (strcmp(value_str, "digital") == 0) {
213 return PALETTE_DIGITAL;
214 } else if (strcmp(value_str, "minimal") == 0) {
215 return PALETTE_MINIMAL;
216 } else if (strcmp(value_str, "cool") == 0) {
217 return PALETTE_COOL;
218 } else if (strcmp(value_str, "custom") == 0) {
219 return PALETTE_CUSTOM;
220 } else {
221 if (error_msg) {
222 SAFE_SNPRINTF(error_msg, error_msg_size,
223 "Invalid palette '%s'. Valid palettes: standard, blocks, digital, minimal, cool, custom",
224 value_str);
225 }
226 return -1;
227 }
228}
@ PALETTE_BLOCKS
Unicode block characters: " ░░▒▒▓▓██".
Definition palette.h:90
@ PALETTE_COOL
Ascending blocks: " ▁▂▃▄▅▆▇█".
Definition palette.h:96
@ PALETTE_STANDARD
Standard ASCII palette: " ...',;:clodxkO0KXNWM".
Definition palette.h:88
@ PALETTE_DIGITAL
Digital/glitch aesthetic: " -=≡≣▰▱◼".
Definition palette.h:92
@ PALETTE_MINIMAL
Simple ASCII: " .-+*#".
Definition palette.h:94

References PALETTE_BLOCKS, PALETTE_COOL, PALETTE_CUSTOM, PALETTE_DIGITAL, PALETTE_MINIMAL, PALETTE_STANDARD, and SAFE_SNPRINTF.

◆ validate_opt_password()

int validate_opt_password ( const char *  value_str,
char *  error_msg,
size_t  error_msg_size 
)

#include <validation.h>

Validate password (8-256 characters)

Parameters
value_strPassword string
error_msgBuffer for error message (can be NULL)
error_msg_sizeSize of error message buffer
Returns
0 on success, -1 on error

Validates password length is 8-256 characters and contains no null bytes.

Validate password (8-256 characters, no null bytes) Returns 0 on success, -1 on error

Definition at line 488 of file validation.c.

488 {
489 if (!value_str) {
490 if (error_msg) {
491 SAFE_SNPRINTF(error_msg, error_msg_size, "Password value is required");
492 }
493 return -1;
494 }
495
496 size_t len = strlen(value_str);
497 if (len < 8) {
498 if (error_msg) {
499 SAFE_SNPRINTF(error_msg, error_msg_size, "Password too short (%zu chars). Must be at least 8 characters.", len);
500 }
501 return -1;
502 }
503 if (len > 256) {
504 if (error_msg) {
505 SAFE_SNPRINTF(error_msg, error_msg_size, "Password too long (%zu chars). Must be at most 256 characters.", len);
506 }
507 return -1;
508 }
509
510 // Note: No need to check for embedded null bytes - strlen() already stopped at the first null,
511 // so by definition there are no null bytes within [0, len).
512
513 return 0;
514}

References SAFE_SNPRINTF.

◆ validate_opt_port()

int validate_opt_port ( const char *  value_str,
char *  error_msg,
size_t  error_msg_size 
)

#include <validation.h>

Validate port number (1-65535)

Parameters
value_strPort number as string
error_msgBuffer for error message (can be NULL)
error_msg_sizeSize of error message buffer
Returns
0 on success, -1 on error

Validates that the port string is a valid number in the range 1-65535.

Validate port number (1-65535) Returns 0 on success, non-zero on error

Definition at line 73 of file validation.c.

73 {
74 if (!value_str || strlen(value_str) == 0) {
75 if (error_msg) {
76 SAFE_SNPRINTF(error_msg, error_msg_size, "Port value is required");
77 }
78 return -1;
79 }
80
81 // Use safe integer parsing with range validation
82 uint16_t port_num;
83 if (parse_port(value_str, &port_num) != ASCIICHAT_OK) {
84 if (error_msg) {
85 SAFE_SNPRINTF(error_msg, error_msg_size, "Invalid port value '%s'. Port must be a number between 1 and 65535.",
86 value_str);
87 }
88 return -1;
89 }
90 return 0;
91}
unsigned short uint16_t
Definition common.h:57
asciichat_error_t parse_port(const char *str, uint16_t *out_port)
Parse port number (1-65535) from string.

References ASCIICHAT_OK, parse_port(), and SAFE_SNPRINTF.

◆ validate_opt_positive_int()

int validate_opt_positive_int ( const char *  value_str,
char *  error_msg,
size_t  error_msg_size 
)

#include <validation.h>

Validate positive integer.

Parameters
value_strInteger value as string
error_msgBuffer for error message (can be NULL)
error_msg_sizeSize of error message buffer
Returns
Parsed value on success, -1 on error

Validates that the string is a positive integer (> 0).

Validate positive integer Returns parsed value on success, -1 on error

Definition at line 120 of file validation.c.

120 {
121 int result = validate_int_range(value_str, 1, INT_MAX, "Value", error_msg, error_msg_size);
122 return (result == INT_MIN) ? -1 : result;
123}

◆ validate_opt_reconnect()

int validate_opt_reconnect ( const char *  value_str,
char *  error_msg,
size_t  error_msg_size 
)

#include <validation.h>

Validate reconnect value.

Parameters
value_strReconnect value as string ("off", "auto", or 0-999)
error_msgBuffer for error message (can be NULL)
error_msg_sizeSize of error message buffer
Returns
0 for off, -1 for auto, 1-999 for count, INT_MIN on error

Valid values:

  • "off" or "0": No reconnection (returns 0)
  • "auto" or "-1": Unlimited reconnection (returns -1)
  • "1" to "999": Retry count (returns the count)

Validate reconnect value (off, auto, 0, -1, or 1-999) Returns: 0 for "off" (no retries) -1 for "auto" (unlimited retries) 1-999 for specific retry count INT_MIN on parse error

Definition at line 409 of file validation.c.

409 {
410 if (!value_str || strlen(value_str) == 0) {
411 if (error_msg) {
412 SAFE_SNPRINTF(error_msg, error_msg_size, "Reconnect value is required");
413 }
414 return INT_MIN;
415 }
416
417 // Check for string values first
418 if (platform_strcasecmp(value_str, "off") == 0) {
419 return 0; // No retries
420 }
421 if (platform_strcasecmp(value_str, "auto") == 0) {
422 return -1; // Unlimited retries
423 }
424
425 // Parse as integer
426 int val = strtoint_safe(value_str);
427 if (val == INT_MIN) {
428 if (error_msg) {
429 SAFE_SNPRINTF(error_msg, error_msg_size, "Invalid reconnect value '%s'. Use 'off', 'auto', or a number 0-999.",
430 value_str);
431 }
432 return INT_MIN;
433 }
434
435 // 0 means off, -1 means auto, 1-999 is valid range
436 if (val == 0) {
437 return 0; // No retries
438 }
439 if (val == -1) {
440 return -1; // Unlimited retries
441 }
442 if (val < 1 || val > 999) {
443 if (error_msg) {
444 SAFE_SNPRINTF(error_msg, error_msg_size, "Invalid reconnect count '%s'. Must be 'off', 'auto', or 1-999.",
445 value_str);
446 }
447 return INT_MIN;
448 }
449 return val;
450}

References platform_strcasecmp(), SAFE_SNPRINTF, and strtoint_safe().

◆ validate_opt_render_mode()

int validate_opt_render_mode ( const char *  value_str,
char *  error_msg,
size_t  error_msg_size 
)

#include <validation.h>

Validate render mode string.

Parameters
value_strRender mode value as string
error_msgBuffer for error message (can be NULL)
error_msg_sizeSize of error message buffer
Returns
Parsed render mode enum value on success, -1 on error

Valid values: foreground, fg, background, bg, half-block, halfblock

Validate render mode string Returns parsed render mode on success, -1 on error

Definition at line 172 of file validation.c.

172 {
173 if (!value_str) {
174 if (error_msg) {
175 SAFE_SNPRINTF(error_msg, error_msg_size, "Render mode value is required");
176 }
177 return -1;
178 }
179
180 if (strcmp(value_str, "foreground") == 0 || strcmp(value_str, "fg") == 0) {
182 }
183 if (strcmp(value_str, "background") == 0 || strcmp(value_str, "bg") == 0) {
185 }
186 if (strcmp(value_str, "half-block") == 0 || strcmp(value_str, "halfblock") == 0) {
188 }
189 if (error_msg) {
190 SAFE_SNPRINTF(error_msg, error_msg_size,
191 "Invalid render mode '%s'. Valid modes: foreground, background, half-block", value_str);
192 }
193 return -1;
194}
@ RENDER_MODE_FOREGROUND
Foreground colors only (text color)
Definition terminal.h:664
@ RENDER_MODE_BACKGROUND
Background colors (block colors)
Definition terminal.h:666
@ RENDER_MODE_HALF_BLOCK
Unicode half-block characters (mixed foreground/background)
Definition terminal.h:668

References RENDER_MODE_BACKGROUND, RENDER_MODE_FOREGROUND, RENDER_MODE_HALF_BLOCK, and SAFE_SNPRINTF.

◆ validate_opt_volume()

float validate_opt_volume ( const char *  value_str,
char *  error_msg,
size_t  error_msg_size 
)

#include <validation.h>

Validate volume value (0.0-1.0)

Parameters
value_strVolume value as string
error_msgBuffer for error message (can be NULL)
error_msg_sizeSize of error message buffer
Returns
Parsed volume value on success, -1.0f on error

Validates that the string is a valid volume in the range 0.0 to 1.0. Used for –volume, –microphone-volume, and similar audio options.

Validate volume value (0.0-1.0) Returns parsed value on success, -1.0f on error

Definition at line 349 of file validation.c.

349 {
350 if (!value_str || strlen(value_str) == 0) {
351 if (error_msg) {
352 SAFE_SNPRINTF(error_msg, error_msg_size, "Volume value is required");
353 }
354 return -1.0f;
355 }
356
357 char *endptr;
358 float val = strtof(value_str, &endptr);
359 if (*endptr != '\0' || value_str == endptr) {
360 if (error_msg) {
361 SAFE_SNPRINTF(error_msg, error_msg_size, "Invalid volume value '%s'. Must be a number.", value_str);
362 }
363 return -1.0f;
364 }
365 if (val < 0.0f || val > 1.0f) {
366 if (error_msg) {
367 SAFE_SNPRINTF(error_msg, error_msg_size, "Volume must be between 0.0 and 1.0 (got %.2f)", val);
368 }
369 return -1.0f;
370 }
371 return val;
372}

References SAFE_SNPRINTF.

Variable Documentation

◆ BLUE

unsigned short int BLUE[]
extern

#include <options.h>

Blue channel lookup table.

Lookup table for blue channel values. See RED[] for details.

Definition at line 378 of file options/common.c.

Referenced by precalc_rgb_palettes().

◆ GRAY

unsigned short int GRAY[]
extern

#include <options.h>

Grayscale lookup table.

Lookup table for grayscale values. Used for monochrome ASCII conversion when color information is not needed or available.

Definition at line 379 of file options/common.c.

Referenced by precalc_rgb_palettes().

◆ GREEN

unsigned short int GREEN[]
extern

#include <options.h>

Green channel lookup table.

Lookup table for green channel values. See RED[] for details.

Definition at line 377 of file options/common.c.

Referenced by precalc_rgb_palettes().

◆ RED

unsigned short int RED[]
extern

#include <options.h>

Red channel lookup table.

Lookup table for red channel values. Used for efficient color-to-ASCII character mapping. Precomputed table for fast palette lookups.

Usage: These lookup tables are used internally by the ASCII conversion algorithm for efficient color mapping.

Note
These are internal implementation details
Tables are precomputed based on palette and color mode
Access these tables via palette functions, not directly

Definition at line 376 of file options/common.c.

Referenced by precalc_rgb_palettes().

◆ weight_blue

const float weight_blue
extern

#include <options.h>

Blue weight for luminance calculation.

Weight for blue channel in luminance calculation. See weight_red for details.

Default: Typically 0.114 (standard ITU-R BT.601 weights)

Definition at line 372 of file options/common.c.

Referenced by server_main().

◆ weight_green

const float weight_green
extern

#include <options.h>

Green weight for luminance calculation.

Weight for green channel in luminance calculation. See weight_red for details.

Default: Typically 0.587 (standard ITU-R BT.601 weights)

Definition at line 371 of file options/common.c.

Referenced by server_main().

◆ weight_red

const float weight_red
extern

#include <options.h>

Red weight for luminance calculation.

Weight for red channel in luminance calculation.

Definition at line 370 of file options/common.c.

Referenced by server_main().