ascii-chat 0.11.33
Video chat in your terminal
Loading...
Searching...
No Matches
builder.h File Reference

Options builder API for flexible command-line option configuration. More...

Go to the source code of this file.

Data Structures

struct  option_metadata_t
 Metadata for shell completion generation. More...
 
struct  option_descriptor_t
 Option descriptor. More...
 
struct  usage_descriptor_t
 Usage line descriptor for programmatic USAGE generation. More...
 
struct  example_descriptor_t
 Example descriptor for programmatic EXAMPLES generation. More...
 
struct  help_mode_descriptor_t
 Mode descriptor for programmatic MODES generation (for help output) More...
 
struct  option_dependency_t
 Option dependency. More...
 
struct  positional_arg_descriptor_t
 Positional argument descriptor. More...
 
struct  custom_section_descriptor_t
 Custom help section descriptor. More...
 
struct  options_config_t
 Options configuration. More...
 
struct  options_builder_t
 Options builder. More...
 

Enumerations

enum  option_type_t {
  OPTION_TYPE_BOOL , OPTION_TYPE_INT , OPTION_TYPE_STRING , OPTION_TYPE_DOUBLE ,
  OPTION_TYPE_CALLBACK , OPTION_TYPE_ACTION
}
 Option value types. More...
 
enum  option_input_type_t {
  OPTION_INPUT_NONE , OPTION_INPUT_ENUM , OPTION_INPUT_NUMERIC , OPTION_INPUT_STRING ,
  OPTION_INPUT_FILEPATH , OPTION_INPUT_CHOICE
}
 Completion input type for smart shell completions. More...
 
enum  dependency_type_t { DEPENDENCY_REQUIRES , DEPENDENCY_CONFLICTS , DEPENDENCY_IMPLIES }
 Option dependency types. More...
 

Functions

options_builder_t * options_builder_create (size_t struct_size)
 Create empty options builder.
 
options_builder_t * options_builder_from_preset (const options_config_t *preset)
 Create builder from preset config.
 
void options_builder_destroy (options_builder_t *builder)
 Free options builder.
 
options_config_t * options_builder_build (options_builder_t *builder)
 Build immutable options config.
 
void options_config_destroy (options_config_t *config)
 Free options config.
 
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.
 
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 *options_struct, char **error_msg))
 Add integer option.
 
void options_builder_add_int_with_metadata (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 *options_struct, char **error_msg), const option_metadata_t *metadata)
 Add integer option with metadata (for numeric ranges and examples)
 
void options_builder_add_string (options_builder_t *builder, const char *long_name, char short_name, size_t offset, const char *default_value, const char *help_text, const char *group, bool required, const char *env_var_name, bool(*validate)(const void *options_struct, char **error_msg))
 Add string option.
 
void options_builder_add_double (options_builder_t *builder, const char *long_name, char short_name, size_t offset, double default_value, const char *help_text, const char *group, bool required, const char *env_var_name, bool(*validate)(const void *options_struct, char **error_msg), bool optional_arg)
 Add double/float option.
 
void options_builder_add_double_with_metadata (options_builder_t *builder, const char *long_name, char short_name, size_t offset, double default_value, const char *help_text, const char *group, bool required, const char *env_var_name, bool(*validate)(const void *options_struct, char **error_msg), const option_metadata_t *metadata, bool optional_arg)
 
void options_builder_add_callback (options_builder_t *builder, const char *long_name, char short_name, size_t offset, const void *default_value, size_t value_size, bool(*parse_fn)(const char *arg, void *dest, char **error_msg), const char *help_text, const char *group, bool required, const char *env_var_name)
 Add option with custom callback parser.
 
void options_builder_add_callback_optional (options_builder_t *builder, const char *long_name, char short_name, size_t offset, const void *default_value, size_t value_size, bool(*parse_fn)(const char *arg, void *dest, char **error_msg), const char *help_text, const char *group, bool required, const char *env_var_name, bool optional_arg)
 Add option with custom callback parser that supports optional arguments.
 
void options_builder_add_callback_with_metadata (options_builder_t *builder, const char *long_name, char short_name, size_t offset, const void *default_value, size_t value_size, bool(*parse_fn)(const char *arg, void *dest, char **error_msg), const char *help_text, const char *group, bool required, const char *env_var_name, bool optional_arg, const option_metadata_t *metadata)
 Add callback option with full metadata (for enums, ranges, examples, etc.)
 
void options_builder_add_action (options_builder_t *builder, const char *long_name, char short_name, void(*action_fn)(void), const char *help_text, const char *group)
 Add action option (executes action and may exit)
 
void options_builder_set_mode_bitmask (options_builder_t *builder, option_mode_bitmask_t mode_bitmask)
 Set mode bitmask on the last added option descriptor.
 
void options_builder_set_arg_placeholder (options_builder_t *builder, const char *arg_placeholder)
 Set custom argument placeholder on the last added option descriptor.
 
void options_builder_set_enum_values (options_builder_t *builder, const char *option_name, const char **values, const char **descriptions)
 Set enum values with descriptions for an option.
 
void options_builder_set_numeric_range (options_builder_t *builder, const char *option_name, int min, int max, int step)
 Set numeric range for an option.
 
void options_builder_set_examples (options_builder_t *builder, const char *option_name, const char **examples)
 Set example values for an option.
 
void options_builder_set_input_type (options_builder_t *builder, const char *option_name, option_input_type_t input_type)
 Set input type for an option.
 
void options_builder_mark_as_list (options_builder_t *builder, const char *option_name)
 Mark option as accepting multiple values.
 
void options_builder_set_default_value_display (options_builder_t *builder, const char *option_name, const char *default_value)
 Set default value string in metadata.
 
void options_builder_add_descriptor (options_builder_t *builder, const option_descriptor_t *descriptor)
 Add full option descriptor (advanced)
 
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.
 
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.
 
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.
 
void options_builder_add_dependency (options_builder_t *builder, const option_dependency_t *dependency)
 Add full dependency (advanced)
 
void options_builder_mark_binary_only (options_builder_t *builder, const char *option_name)
 Mark an option as binary-level only (hide from mode-specific help)
 
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.
 
asciichat_error_t options_config_parse_positional (const options_config_t *config, int remaining_argc, char **remaining_argv, void *options_struct)
 Parse positional arguments.
 
void options_builder_add_usage (options_builder_t *builder, const char *mode, const char *positional, bool show_options, const char *description)
 Add usage line descriptor.
 
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.
 
void options_builder_add_example_utility (options_builder_t *builder, uint32_t mode_bitmask, const char *args, const char *description, bool is_utility_command)
 Add an example with utility command support.
 
void options_builder_add_mode (options_builder_t *builder, const char *name, const char *description)
 Add mode descriptor.
 
void options_builder_add_custom_section (options_builder_t *builder, const char *heading, const char *content, option_mode_bitmask_t mode_bitmask)
 Add a custom help section.
 
options_config_t * options_preset_unified (const char *program_name, const char *description)
 Get binary-level options preset.
 
asciichat_error_t options_config_set_defaults (const options_config_t *config, void *options_struct)
 Set default values in options struct.
 
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.
 
asciichat_error_t options_config_validate (const options_config_t *config, const void *options_struct, char **error_message)
 Validate options struct.
 
int options_config_calculate_max_col_width (const options_config_t *config)
 Calculate global max column width for help output alignment.
 
void options_config_print_usage (const options_config_t *config, FILE *stream)
 Print usage/help text.
 
void options_config_print_usage_section (const options_config_t *config, FILE *stream)
 Print only the USAGE section.
 
void options_config_print_options_sections_with_width (const options_config_t *config, FILE *stream, int max_col_width, asciichat_mode_t mode)
 Print everything except the USAGE section.
 
void options_config_print_options_sections (const options_config_t *config, FILE *stream, asciichat_mode_t mode)
 Print everything except the USAGE section (backward compatibility)
 
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 (unified function)
 
void options_struct_destroy (const options_config_t *config, void *options_struct)
 Clean up memory owned by options struct.
 
const char * options_get_type_placeholder (option_type_t type)
 Get placeholder string for option type.
 
int options_format_default_value (option_type_t type, const void *default_value, char *buf, size_t bufsize)
 Format option default value to string.
 
const char * find_similar_option_with_mode (const char *unknown_opt, const options_config_t *config, option_mode_bitmask_t current_mode_bitmask)
 Find similar option across all modes and suggest with mode information.
 

Detailed Description

Options builder API for flexible command-line option configuration.

This module provides a builder pattern for constructing option configurations that can be used by library consumers to create custom tools based on ascii-chat modes or build entirely custom option sets from scratch.

Integration with Registry and RCU

The builder pattern works together with:

  • Registry (registry.h): Centralized option definitions. Use options_registry_add_all_to_builder() to populate builder with registered options.
  • RCU Thread-Safety (rcu.h): Lock-free access to parsed options. Built configs are used by options_init() to parse and publish to RCU.
  • Mode Bitmasks: Each option includes a mode_bitmask indicating which modes it applies to (SERVER, CLIENT, MIRROR, DISCOVERY_SVC, BINARY, etc.).

Features:

  • Three-tier parsing (binary → mode → mode-specific)
  • Required fields with environment variable fallbacks
  • Option dependencies (REQUIRES, CONFLICTS, IMPLIES)
  • Cross-field validation (validators receive full options struct)
  • Automatic string memory management (auto-strdup, cleanup)
  • Grouped help output with semantic coloring
  • Mode-aware option filtering and applicability
  • Smart shell completion metadata (enums, ranges, examples, filepath hints)
  • Preset configs for server/client/mirror/acds modes

Builder Pattern Workflow

CREATE BUILDER
↓
ADD OPTIONS (with mode bitmasks)
↓
ADD DEPENDENCIES (REQUIRES, CONFLICTS, IMPLIES)
↓
BUILD IMMUTABLE CONFIG
↓
PARSE COMMAND LINE (options are mode-filtered)
↓
VALIDATE (required, dependencies, custom validators)
↓
PUBLISH TO RCU (lock-free thread-safe access)

Builder Usage Patterns

Pattern 1: Start from Registry

// Use registered options as base
options_registry_add_all_to_builder(builder); // Add all registered options
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
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
Options builder.
Definition builder.h:441
Options configuration.
Definition builder.h:401
Consolidated options structure.

Pattern 2: Start from Preset

// Start with unified preset (standard multi-mode setup)
options_config_t *preset = options_preset_unified("ascii-chat", "Terminal video chat");
// Add custom options or modify
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

Pattern 3: Custom Builder from Scratch

// Build entirely custom config
options_builder_t *builder = options_builder_create(sizeof(my_options_t));
options_builder_add_bool(builder, "verbose", 'v', offsetof(my_options_t, verbose), ...);
options_builder_add_int(builder, "port", 'p', offsetof(my_options_t, port), ...);
options_builder_set_mode_bitmask(builder, OPTION_MODE_SERVER); // For last added option
void options_builder_set_mode_bitmask(options_builder_t *builder, option_mode_bitmask_t mode_bitmask)
Set mode bitmask on the last added option descriptor.
Definition builder.c:984
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
@ OPTION_MODE_SERVER
Server mode (bit 0)

Mode Bitmask System

Options are filtered based on mode using bitmasks:

  • OPTION_MODE_BINARY: Parsed before mode detection (–help, –version, –log-file)
  • OPTION_MODE_SERVER: Server-only options
  • OPTION_MODE_CLIENT: Client-only options
  • OPTION_MODE_MIRROR: Mirror mode options
  • OPTION_MODE_DISCOVERY_SVC: Discovery service options
  • OPTION_MODE_ALL: Available in all modes

Each option's mode_bitmask field controls where it appears in:

  • Help output (mode-specific or binary-level)
  • Shell completions
  • Parsing (only options matching detected mode are parsed)

Setting Mode Bitmask:

options_builder_add_int(builder, "max-clients", 'm', ...);
options_builder_set_mode_bitmask(builder, OPTION_MODE_SERVER); // Server-only option

Option Types and Parsing

  • OPTION_TYPE_BOOL: Boolean flag (–flag, no value)
  • OPTION_TYPE_INT: Integer with validation and optional range
  • OPTION_TYPE_STRING: String (auto-duplicated, auto-freed)
  • OPTION_TYPE_DOUBLE: Floating point with optional range
  • OPTION_TYPE_CALLBACK: Custom parser for complex types
  • OPTION_TYPE_ACTION: Action that executes immediately (–version, –help)

Option Dependencies

Enforce relationships between options during validation:

  • DEPENDENCY_REQUIRES: If A is set, B must be set (e.g., –tls requires –cert)
  • DEPENDENCY_CONFLICTS: If A is set, B must NOT be set (e.g., –no-crypto conflicts with –key-file)
  • DEPENDENCY_IMPLIES: If A is set, B defaults to true (e.g., –tls implies –secure)

Adding Dependencies:

options_builder_add_dependency_requires(builder, "tls-cert", "tls-enabled", NULL);
options_builder_add_dependency_conflicts(builder, "no-crypto", "key-file", NULL);
options_builder_add_dependency_implies(builder, "secure", "tls-enabled", NULL);
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_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
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

RCU Integration

After building and parsing, options are published to RCU for thread-safe access:

options_config_parse(config, argc, argv, &opts, MODE_DETECTION_RESULT, ...);
options_config_validate(config, &opts, NULL);
// Publish to RCU before spawning worker threads
// Now worker threads can safely read options lock-free
const options_t *current_opts = options_get();
int port = current_opts->port; // No locks needed!
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
const options_t * options_get(void)
Get current options (lock-free read)
Definition rcu.c:496
options_t options_t_new(void)
Initialize options by parsing command-line arguments.
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
See also
registry.h - Central registry of all option definitions
rcu.h - Thread-safe RCU-based access to published Options Module
options.h - Unified Options Module parsing system (uses builder internally)
Author
Zachary Fogg me@zf.nosp@m.o.gg
Date
December 2025

Definition in file builder.h.

Enumeration Type Documentation

◆ dependency_type_t

Option dependency types.

Defines relationships between options.

Enumerator
DEPENDENCY_REQUIRES 

If A is set, B must be set.

DEPENDENCY_CONFLICTS 

If A is set, B must NOT be set.

DEPENDENCY_IMPLIES 

If A is set, B defaults to true/enabled.

Definition at line 228 of file builder.h.

228 {
dependency_type_t
Option dependency types.
Definition builder.h:228
@ DEPENDENCY_IMPLIES
If A is set, B defaults to true/enabled.
Definition builder.h:231
@ DEPENDENCY_CONFLICTS
If A is set, B must NOT be set.
Definition builder.h:230
@ DEPENDENCY_REQUIRES
If A is set, B must be set.
Definition builder.h:229

◆ option_input_type_t

Completion input type for smart shell completions.

Specifies the kind of input an option expects, used to generate better completions with value suggestions, ranges, and examples.

Enumerator
OPTION_INPUT_NONE 

No input (boolean flag)

OPTION_INPUT_ENUM 

Choose from fixed set of enum values.

OPTION_INPUT_NUMERIC 

Numeric value with optional min/max/step.

OPTION_INPUT_STRING 

Free-form text input.

OPTION_INPUT_FILEPATH 

File path completion.

OPTION_INPUT_CHOICE 

Dynamic choice (e.g., from system)

Definition at line 176 of file builder.h.

176 {
option_input_type_t
Completion input type for smart shell completions.
Definition builder.h:176
@ OPTION_INPUT_STRING
Free-form text input.
Definition builder.h:180
@ OPTION_INPUT_CHOICE
Dynamic choice (e.g., from system)
Definition builder.h:182
@ OPTION_INPUT_NUMERIC
Numeric value with optional min/max/step.
Definition builder.h:179
@ OPTION_INPUT_NONE
No input (boolean flag)
Definition builder.h:177
@ OPTION_INPUT_FILEPATH
File path completion.
Definition builder.h:181
@ OPTION_INPUT_ENUM
Choose from fixed set of enum values.
Definition builder.h:178

◆ option_type_t

Option value types.

Defines the type of value an option accepts.

Enumerator
OPTION_TYPE_BOOL 

Boolean flag (–flag, no value)

OPTION_TYPE_INT 

Integer value (–count 42)

OPTION_TYPE_STRING 

String value (–name foo)

OPTION_TYPE_DOUBLE 

Floating point (–ratio 1.5)

OPTION_TYPE_CALLBACK 

Custom parser function.

OPTION_TYPE_ACTION 

Action that executes and may exit (–list-webcams, etc.)

Definition at line 161 of file builder.h.

161 {
option_type_t
Option value types.
Definition builder.h:161
@ OPTION_TYPE_INT
Integer value (–count 42)
Definition builder.h:163
@ OPTION_TYPE_DOUBLE
Floating point (–ratio 1.5)
Definition builder.h:165
@ OPTION_TYPE_STRING
String value (–name foo)
Definition builder.h:164
@ OPTION_TYPE_ACTION
Action that executes and may exit (–list-webcams, etc.)
Definition builder.h:167
@ OPTION_TYPE_CALLBACK
Custom parser function.
Definition builder.h:166
@ OPTION_TYPE_BOOL
Boolean flag (–flag, no value)
Definition builder.h:162

Function Documentation

◆ find_similar_option_with_mode()

const char * find_similar_option_with_mode ( const char *  unknown_opt,
const options_config_t *  config,
option_mode_bitmask_t  current_mode_bitmask 
)

Find similar option across all modes and suggest with mode information.

Searches for options matching the unknown option using Levenshtein distance. If found and available in a different mode, suggests it with mode information. For discovery mode, displays it as "the default mode (ascii-chat)".

Parameters
unknown_optUnknown option name (with – prefix)
configOptions configuration (has all descriptors across modes)
current_mode_bitmaskCurrent mode bitmask for filtering
Returns
Formatted suggestion with mode info, or NULL if no good match

Example:

const char *suggestion = find_similar_option_with_mode("--prot", config, current_mode);
if (suggestion) {
log_error("%s", suggestion); // "Did you mean '--port' (available in server mode)?"
}
#define log_error(...)
Log an ERROR message.
Definition log/log.h:587
const char * find_similar_option_with_mode(const char *unknown_opt, const options_config_t *config, option_mode_bitmask_t current_mode_bitmask)
Find similar option across all modes and suggest with mode information.

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

109 {
110 if (!unknown_opt || !config) {
111 return NULL;
112 }
113
114 // Extract the option name without dashes
115 const char *opt_name = unknown_opt;
116 if (strncmp(opt_name, "--", 2) == 0) {
117 opt_name += 2;
118 } else if (strncmp(opt_name, "-", 1) == 0) {
119 opt_name += 1;
120 } else {
121 return NULL; // Not an option format
122 }
123
124 const option_descriptor_t *best_match = NULL;
125 size_t best_distance = SIZE_MAX;
126
127 // Search through all descriptors
128 for (size_t i = 0; i < config->num_descriptors; i++) {
129 const option_descriptor_t *desc = &config->descriptors[i];
130 if (!desc->long_name)
131 continue;
132
133 // Calculate distance to the long name
134 size_t dist = levenshtein(opt_name, desc->long_name);
135 if (dist < best_distance) {
136 best_distance = dist;
137 best_match = desc;
138 }
139 }
140
141 // Only suggest if the distance is within our threshold
142 if (best_distance > LEVENSHTEIN_SUGGESTION_THRESHOLD || !best_match) {
143 return NULL;
144 }
145
146 // Check if the option is not available in current mode
147 bool available_in_current_mode = (best_match->mode_bitmask & current_mode_bitmask) != 0;
148
149 static char suggestion[256];
150 if (available_in_current_mode) {
151 // Option exists but user typed it wrong - just suggest the correct spelling
152 safe_snprintf(suggestion, sizeof(suggestion), "Did you mean '--%s'?", best_match->long_name);
153 } else {
154 // Option exists but in a different mode - show all available modes
155 const char *modes_str = format_available_modes(best_match->mode_bitmask);
156 safe_snprintf(suggestion, sizeof(suggestion), "Did you mean '--%s' (available in modes: %s)?",
157 best_match->long_name, modes_str);
158 }
159
160 return suggestion;
161}
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
int safe_snprintf(char *buffer, size_t buffer_size, const char *format,...)
Safe formatted string printing to buffer.
Definition system.c:148
const char * format_available_modes(option_mode_bitmask_t mode_bitmask)
Format all available modes for an option as comma-separated list.
Option descriptor.
Definition builder.h:241
option_mode_bitmask_t mode_bitmask
Which modes this option applies to.
Definition builder.h:278
const char * long_name
Long option name (e.g., "port")
Definition builder.h:243
option_descriptor_t * descriptors
Array of option descriptors.
Definition builder.h:402
size_t num_descriptors
Number of descriptors.
Definition builder.h:403

References options_config_t::descriptors, format_available_modes(), levenshtein(), LEVENSHTEIN_SUGGESTION_THRESHOLD, option_descriptor_t::long_name, option_descriptor_t::mode_bitmask, options_config_t::num_descriptors, and safe_snprintf().

◆ options_builder_add_action()

void options_builder_add_action ( options_builder_t *  builder,
const char *  long_name,
char  short_name,
void(*)(void)  action_fn,
const char *  help_text,
const char *  group 
)

Add action option (executes action and may exit)

Action options execute a callback function when encountered during parsing. The callback may exit the program (e.g., –list-webcams, –version).

Parameters
builderBuilder to add to
long_nameLong option name
short_nameShort option char (or '\0')
action_fnAction callback to execute
help_textHelp description
groupGroup name for help

Definition at line 943 of file builder.c.

944 {
946
947 option_descriptor_t desc = {.long_name = long_name,
948 .short_name = short_name,
949 .type = OPTION_TYPE_ACTION,
950 .offset = 0, // Actions don't store values
951 .help_text = help_text,
952 .group = group,
953 .default_value = NULL,
954 .required = false, // Actions are never required
955 .env_var_name = NULL,
956 .validate = NULL,
957 .parse_fn = NULL,
958 .action_fn = action_fn,
959 .owns_memory = false,
960 .hide_from_mode_help = false,
961 .hide_from_binary_help = false,
962 .mode_bitmask = OPTION_MODE_NONE};
963
964 builder->descriptors[builder->num_descriptors++] = desc;
965
966 // Set hide_from_binary_help after adding (so we can check the option name)
967 if (strcmp(long_name, "create-man-page") == 0) {
968 // Always hide from help and man page (this is a development tool)
969 builder->descriptors[builder->num_descriptors - 1].hide_from_binary_help = true;
970 }
971}
void ensure_descriptor_capacity(options_builder_t *builder)
Grow descriptor array if needed.
Definition builder.c:213
@ OPTION_MODE_NONE
No modes (invalid)
bool hide_from_binary_help
If true, don't show in binary-level help (e.g., in release builds)
Definition builder.h:255
option_descriptor_t * descriptors
Dynamic array of descriptors.
Definition builder.h:442
size_t num_descriptors
Current count.
Definition builder.h:443

References options_builder_t::descriptors, ensure_descriptor_capacity(), option_descriptor_t::hide_from_binary_help, option_descriptor_t::long_name, options_builder_t::num_descriptors, OPTION_MODE_NONE, and OPTION_TYPE_ACTION.

Referenced by options_registry_add_all_to_builder().

◆ options_builder_add_bool()

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.

Parameters
builderBuilder to add to
long_nameLong option name (e.g., "verbose")
short_nameShort option char (e.g., 'v', or '\0' if none)
offsetoffsetof(struct, field)
default_valueDefault value
help_textHelp description
groupGroup name for help (e.g., "OUTPUT OPTIONS")
requiredIf true, option must be provided
env_var_nameEnvironment variable fallback (or NULL)

Definition at line 657 of file builder.c.

659 {
661
662 static bool default_vals[2] = {false, true};
663
664 option_descriptor_t desc = {.long_name = long_name,
665 .short_name = short_name,
666 .type = OPTION_TYPE_BOOL,
667 .offset = offset,
668 .help_text = help_text,
669 .group = group,
670 .default_value = &default_vals[default_value ? 1 : 0],
671 .required = required,
672 .env_var_name = env_var_name,
673 .mode_bitmask = OPTION_MODE_NONE,
674 .validate = NULL,
675 .parse_fn = NULL,
676 .owns_memory = false};
677
678 builder->descriptors[builder->num_descriptors++] = desc;
679}

References options_builder_t::descriptors, ensure_descriptor_capacity(), option_descriptor_t::long_name, options_builder_t::num_descriptors, OPTION_MODE_NONE, and OPTION_TYPE_BOOL.

Referenced by options_registry_add_all_to_builder().

◆ options_builder_add_callback()

void options_builder_add_callback ( options_builder_t *  builder,
const char *  long_name,
char  short_name,
size_t  offset,
const void *  default_value,
size_t  value_size,
bool(*)(const char *arg, void *dest, char **error_msg)  parse_fn,
const char *  help_text,
const char *  group,
bool  required,
const char *  env_var_name 
)

Add option with custom callback parser.

Parameters
builderBuilder to add to
long_nameLong option name
short_nameShort option char (or '\0')
offsetoffsetof(struct, field)
default_valuePointer to default value (or NULL)
value_sizesizeof(field_type)
parse_fnCustom parser function
help_textHelp description
groupGroup name for help
requiredIf true, option must be provided
env_var_nameEnvironment variable fallback (or NULL)

◆ options_builder_add_callback_optional()

void options_builder_add_callback_optional ( options_builder_t *  builder,
const char *  long_name,
char  short_name,
size_t  offset,
const void *  default_value,
size_t  value_size,
bool(*)(const char *arg, void *dest, char **error_msg)  parse_fn,
const char *  help_text,
const char *  group,
bool  required,
const char *  env_var_name,
bool  optional_arg 
)

Add option with custom callback parser that supports optional arguments.

Allows the callback to receive NULL if no argument is provided (when next arg is a flag).

Parameters
builderBuilder to add to
long_nameLong option name
short_nameShort option char (or '\0')
offsetoffsetof(struct, field)
default_valuePointer to default value (or NULL)
value_sizesizeof(field_type)
parse_fnCustom parser function (receives NULL if arg not provided)
help_textHelp description
groupGroup name for help
requiredIf true, option must be provided
env_var_nameEnvironment variable fallback (or NULL)
optional_argIf true, argument is optional for this callback

◆ options_builder_add_callback_with_metadata()

void options_builder_add_callback_with_metadata ( options_builder_t *  builder,
const char *  long_name,
char  short_name,
size_t  offset,
const void *  default_value,
size_t  value_size,
bool(*)(const char *arg, void *dest, char **error_msg)  parse_fn,
const char *  help_text,
const char *  group,
bool  required,
const char *  env_var_name,
bool  optional_arg,
const option_metadata_t *  metadata 
)

Add callback option with full metadata (for enums, ranges, examples, etc.)

Adds a callback-based option with complete metadata information for help and shell completions.

Parameters
builderBuilder to add to
long_nameLong option name
short_nameShort option char (or '\0')
offsetoffsetof(struct, field)
default_valuePointer to default value
value_sizesizeof(field_type)
parse_fnCustom parser function
help_textHelp description
groupGroup name for help
requiredIf true, option must be provided
env_var_nameEnvironment variable fallback (or NULL)
optional_argIf true, argument is optional for this callback
metadataCompletion metadata (enums, ranges, examples, etc.)

◆ options_builder_add_custom_section()

void options_builder_add_custom_section ( options_builder_t *  builder,
const char *  heading,
const char *  content,
option_mode_bitmask_t  mode_bitmask 
)

Add a custom help section.

Adds a custom section to help output that appears after EXAMPLES but before OPTIONS.

Parameters
[in]builderOptions builder
[in]headingSection heading (e.g., "INTERACTIVE CONTROLS")
[in]contentSection content
[in]mode_bitmaskWhich modes show this section (use MODE_BITMASK_* constants)

Definition at line 1319 of file builder.c.

1320 {
1321 if (!builder || !heading || !content)
1322 return;
1323
1324 ensure_custom_section_capacity(builder);
1325
1326 custom_section_descriptor_t section = {.heading = heading, .content = content, .mode_bitmask = mode_bitmask};
1327
1328 builder->custom_sections[builder->num_custom_sections++] = section;
1329}
Custom help section descriptor.
Definition builder.h:389
const char * heading
Section heading (e.g., "INTERACTIVE CONTROLS")
Definition builder.h:390
size_t num_custom_sections
Current count.
Definition builder.h:467
custom_section_descriptor_t * custom_sections
Dynamic array of custom sections.
Definition builder.h:466

References options_builder_t::custom_sections, custom_section_descriptor_t::heading, and options_builder_t::num_custom_sections.

Referenced by options_preset_unified().

◆ options_builder_add_dependency()

void options_builder_add_dependency ( options_builder_t *  builder,
const option_dependency_t *  dependency 
)

Add full dependency (advanced)

Parameters
builderBuilder to add to
dependencyDependency to copy

Definition at line 1164 of file builder.c.

1164 {
1165 if (!builder || !dependency)
1166 return;
1167
1169 builder->dependencies[builder->num_dependencies++] = *dependency;
1170}
void ensure_dependency_capacity(options_builder_t *builder)
Grow dependency array if needed.
Definition builder.c:230
size_t num_dependencies
Current count.
Definition builder.h:447
option_dependency_t * dependencies
Dynamic array of dependencies.
Definition builder.h:446

References options_builder_t::dependencies, ensure_dependency_capacity(), and options_builder_t::num_dependencies.

Referenced by options_builder_from_preset().

◆ options_builder_add_dependency_conflicts()

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.

Parameters
builderBuilder to add to
option_nameOption that has the conflict
conflicts_withOption it conflicts with
error_messageCustom error message (or NULL for default)

Definition at line 1142 of file builder.c.

1143 {
1145
1146 option_dependency_t dep = {.option_name = option_name,
1147 .type = DEPENDENCY_CONFLICTS,
1148 .depends_on = conflicts_with,
1149 .error_message = error_message};
1150
1151 builder->dependencies[builder->num_dependencies++] = dep;
1152}
Option dependency.
Definition builder.h:331
const char * option_name
The option that has the dependency.
Definition builder.h:332

References options_builder_t::dependencies, DEPENDENCY_CONFLICTS, ensure_dependency_capacity(), options_builder_t::num_dependencies, and option_dependency_t::option_name.

Referenced by options_preset_unified().

◆ options_builder_add_dependency_implies()

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.

Parameters
builderBuilder to add to
option_nameOption that implies another
impliesOption that is implied
error_messageCustom message (rarely used, can be NULL)

Definition at line 1154 of file builder.c.

1155 {
1157
1158 option_dependency_t dep = {
1159 .option_name = option_name, .type = DEPENDENCY_IMPLIES, .depends_on = implies, .error_message = error_message};
1160
1161 builder->dependencies[builder->num_dependencies++] = dep;
1162}

References options_builder_t::dependencies, DEPENDENCY_IMPLIES, ensure_dependency_capacity(), options_builder_t::num_dependencies, and option_dependency_t::option_name.

◆ options_builder_add_dependency_requires()

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.

Parameters
builderBuilder to add to
option_nameOption that has the dependency
depends_onOption it depends on
error_messageCustom error message (or NULL for default)

Definition at line 1130 of file builder.c.

1131 {
1133
1134 option_dependency_t dep = {.option_name = option_name,
1135 .type = DEPENDENCY_REQUIRES,
1136 .depends_on = depends_on,
1137 .error_message = error_message};
1138
1139 builder->dependencies[builder->num_dependencies++] = dep;
1140}

References options_builder_t::dependencies, DEPENDENCY_REQUIRES, ensure_dependency_capacity(), options_builder_t::num_dependencies, and option_dependency_t::option_name.

Referenced by options_preset_unified().

◆ options_builder_add_descriptor()

void options_builder_add_descriptor ( options_builder_t *  builder,
const option_descriptor_t *  descriptor 
)

Add full option descriptor (advanced)

Parameters
builderBuilder to add to
descriptorDescriptor to copy

Definition at line 973 of file builder.c.

973 {
974 if (!builder || !descriptor) {
975 SET_ERRNO(ERROR_INVALID_PARAM, "Builder is NULL or descriptor is NULL");
976 return;
977 }
978 return;
979
981 builder->descriptors[builder->num_descriptors++] = *descriptor;
982}
#define SET_ERRNO(code, context_msg,...)
Set error code with custom context message and log it, returning the error code.
@ ERROR_INVALID_PARAM

References options_builder_t::descriptors, ensure_descriptor_capacity(), ERROR_INVALID_PARAM, options_builder_t::num_descriptors, and SET_ERRNO.

Referenced by options_builder_from_preset().

◆ options_builder_add_double()

void options_builder_add_double ( options_builder_t *  builder,
const char *  long_name,
char  short_name,
size_t  offset,
double  default_value,
const char *  help_text,
const char *  group,
bool  required,
const char *  env_var_name,
bool(*)(const void *options_struct, char **error_msg)  validate,
bool  optional_arg 
)

Add double/float option.

Parameters
builderBuilder to add to
long_nameLong option name
short_nameShort option char (or '\0')
offsetoffsetof(struct, field)
default_valueDefault value
help_textHelp description
groupGroup name for help
requiredIf true, option must be provided
env_var_nameEnvironment variable fallback (or NULL)
validateValidator function receiving full struct (or NULL)

◆ options_builder_add_double_with_metadata()

void options_builder_add_double_with_metadata ( options_builder_t *  builder,
const char *  long_name,
char  short_name,
size_t  offset,
double  default_value,
const char *  help_text,
const char *  group,
bool  required,
const char *  env_var_name,
bool(*)(const void *options_struct, char **error_msg)  validate,
const option_metadata_t *  metadata,
bool  optional_arg 
)

◆ options_builder_add_example()

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.

Adds an example command to be printed in the EXAMPLES section. Components are colored separately during printing:

  • mode: magenta
  • args: green
Parameters
builderBuilder instance
modeMode name (NULL or "server", "client", "mirror", etc.)
argsArguments (NULL or "example.com", "swift-river-mountain", etc.)
descriptionHelp text for this example

Example:

"Start new session");
options_builder_add_example(b, "server", NULL,
"Run as dedicated server");
options_builder_add_example(b, "client", "example.com",
"Connect to specific server");
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

Add an example to the help output

Parameters
builderBuilder instance
mode_bitmaskBitmask of modes (OPTION_MODE_SERVER | OPTION_MODE_CLIENT, etc.) Use OPTION_MODE_BINARY for binary-level examples
argsArguments (NULL or "example.com", etc.)
descriptionHelp text for this example
owns_argsIf true, duplicate and track the args string

Definition at line 1255 of file builder.c.

1256 {
1257 if (!builder || !description)
1258 return;
1259
1260 ensure_example_capacity(builder);
1261
1262 example_descriptor_t example = {.mode_bitmask = mode_bitmask,
1263 .description = description,
1264 .owns_args_memory = owns_args,
1265 .is_utility_command = false};
1266
1267 if (owns_args) {
1268 // If owning, duplicate the string and track it
1269 char *owned_args = platform_strdup(args);
1270 if (!owned_args) {
1271 log_fatal("Failed to duplicate example args string");
1272 return;
1273 }
1274 example.args = owned_args;
1275 track_owned_string_builder(builder, owned_args);
1276 } else {
1277 example.args = args;
1278 }
1279
1280 builder->examples[builder->num_examples++] = example;
1281}
#define log_fatal(...)
Log a FATAL message.
Definition log/log.h:599
char * platform_strdup(const char *s)
Duplicate string (strdup replacement)
action_args_t args
Example descriptor for programmatic EXAMPLES generation.
Definition builder.h:310
uint32_t mode_bitmask
Bitmask of modes (OPTION_MODE_SERVER, OPTION_MODE_CLIENT, etc.)
Definition builder.h:311
const char * args
Command-line arguments part of the example.
Definition builder.h:312
example_descriptor_t * examples
Dynamic array of examples.
Definition builder.h:458
size_t num_examples
Current count.
Definition builder.h:459

References example_descriptor_t::args, args, options_builder_t::examples, log_fatal, example_descriptor_t::mode_bitmask, options_builder_t::num_examples, and platform_strdup().

Referenced by options_preset_unified().

◆ options_builder_add_example_utility()

void options_builder_add_example_utility ( options_builder_t *  builder,
uint32_t  mode_bitmask,
const char *  args,
const char *  description,
bool  is_utility_command 
)

Add an example with utility command support.

Utility commands (like pbpaste, cat, etc.) that shouldn't be prepended with program name or mode in help output.

Parameters
builderBuilder instance
mode_bitmaskBitmask of modes (OPTION_MODE_MIRROR, etc.)
argsArguments (e.g., "pbpaste | cat -", "cat video.avi | ascii-chat mirror -f '-'")
descriptionHelp text for this example
is_utility_commandMust be true for utility commands

Allows marking utility commands (like pbpaste, grep, etc.) that shouldn't be prepended with program name in help output.

Definition at line 1289 of file builder.c.

1290 {
1291 if (!builder || !description)
1292 return;
1293
1294 ensure_example_capacity(builder);
1295
1296 example_descriptor_t example = {.mode_bitmask = mode_bitmask,
1297 .description = description,
1298 .owns_args_memory = false,
1299 .is_utility_command = is_utility_command};
1300
1301 if (args) {
1302 example.args = args;
1303 }
1304
1305 builder->examples[builder->num_examples++] = example;
1306}

References example_descriptor_t::args, args, options_builder_t::examples, example_descriptor_t::mode_bitmask, and options_builder_t::num_examples.

Referenced by options_preset_unified().

◆ options_builder_add_int()

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(*)(const void *options_struct, char **error_msg)  validate 
)

Add integer option.

Parameters
builderBuilder to add to
long_nameLong option name
short_nameShort option char (or '\0')
offsetoffsetof(struct, field)
default_valueDefault value
help_textHelp description
groupGroup name for help
requiredIf true, option must be provided
env_var_nameEnvironment variable fallback (or NULL)
validateValidator function receiving full struct (or NULL)

◆ options_builder_add_int_with_metadata()

void options_builder_add_int_with_metadata ( 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(*)(const void *options_struct, char **error_msg)  validate,
const option_metadata_t *  metadata 
)

Add integer option with metadata (for numeric ranges and examples)

Like options_builder_add_int() but also includes metadata for numeric ranges, examples, and completions.

Parameters
metadataPointer to option_metadata_t with numeric_range, examples, etc. (can be NULL)

Definition at line 710 of file builder.c.

714 {
716
717 if (g_int_defaults_counter >= 256) {
718 log_error("Too many int options (max 256)");
719 return;
720 }
721
722 g_int_defaults[g_int_defaults_counter] = default_value;
723
724 option_descriptor_t desc = {.long_name = long_name,
725 .short_name = short_name,
726 .type = OPTION_TYPE_INT,
727 .offset = offset,
728 .help_text = help_text,
729 .group = group,
730 .default_value = &g_int_defaults[g_int_defaults_counter++],
731 .required = required,
732 .env_var_name = env_var_name,
733 .validate = validate,
734 .parse_fn = NULL,
735 .owns_memory = false,
736 .mode_bitmask = OPTION_MODE_NONE,
737 .metadata = metadata ? *metadata : (option_metadata_t){0}};
738
739 builder->descriptors[builder->num_descriptors++] = desc;
740}
Metadata for shell completion generation.
Definition builder.h:192

References options_builder_t::descriptors, ensure_descriptor_capacity(), log_error, option_descriptor_t::long_name, options_builder_t::num_descriptors, OPTION_MODE_NONE, and OPTION_TYPE_INT.

Referenced by options_registry_add_all_to_builder().

◆ options_builder_add_mode()

void options_builder_add_mode ( options_builder_t *  builder,
const char *  name,
const char *  description 
)

Add mode descriptor.

Adds a mode to be printed in the MODES section. The mode name is colored magenta during printing.

Parameters
builderBuilder instance
nameMode name (e.g., "server", "client", "mirror")
descriptionMode description

Example:

options_builder_add_mode(b, "server", "Run as multi-client video chat server");
options_builder_add_mode(b, "client", "Run as video chat client (connect to server)");
options_builder_add_mode(b, "mirror", "View local webcam as ASCII art (no server)");
void options_builder_add_mode(options_builder_t *builder, const char *name, const char *description)
Add mode descriptor.
Definition builder.c:1308

Definition at line 1308 of file builder.c.

1308 {
1309 if (!builder || !name || !description)
1310 return;
1311
1312 ensure_mode_capacity(builder);
1313
1314 help_mode_descriptor_t mode = {.name = name, .description = description};
1315
1316 builder->modes[builder->num_modes++] = mode;
1317}
Mode descriptor for programmatic MODES generation (for help output)
Definition builder.h:321
const char * name
Mode name (e.g., "server", "client")
Definition builder.h:322
help_mode_descriptor_t * modes
Dynamic array of modes.
Definition builder.h:462
size_t num_modes
Current count.
Definition builder.h:463

References options_builder_t::modes, help_mode_descriptor_t::name, and options_builder_t::num_modes.

Referenced by options_preset_unified().

◆ options_builder_add_positional()

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(*)(const char *arg, void *config, char **remaining, int num_remaining, char **error_msg)  parse_fn 
)

Add positional argument descriptor.

Positional arguments are parsed after getopt finishes, from the remaining argv elements that don't start with '-'. They are parsed in the order they are added to the builder.

Example (client mode):

builder,
"address",
"[address][:port] - Server address (IPv4, IPv6, or hostname) with optional port",
false, // Not required (defaults to localhost)
parse_client_address_arg
);
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

Example (server mode):

builder,
"bind-addr",
"Bind address (IPv4 or IPv6) - can specify 0-2 addresses",
false, // Not required (defaults to localhost dual-stack)
parse_server_bind_addr
);
Parameters
builderBuilder to add to
nameName for help text (e.g., "address")
help_textDescription for usage message
requiredIf true, this positional arg must be provided
section_headingOptional section heading for examples (e.g., "ADDRESS FORMATS")
examplesOptional array of example strings with descriptions
num_examplesNumber of examples in array
mode_bitmaskWhich modes this positional arg applies to
parse_fnCustom parser (receives arg, config, remaining args, error_msg) Returns number of args consumed (usually 1), or -1 on error

Definition at line 1192 of file builder.c.

1196 {
1197 if (!builder || !name || !parse_fn)
1198 return;
1199
1201
1202 positional_arg_descriptor_t pos_arg = {.name = name,
1203 .help_text = help_text,
1204 .required = required,
1205 .section_heading = section_heading,
1206 .examples = examples,
1207 .num_examples = num_examples,
1208 .mode_bitmask = mode_bitmask,
1209 .parse_fn = parse_fn};
1210
1211 builder->positional_args[builder->num_positional_args++] = pos_arg;
1212}
void ensure_positional_arg_capacity(options_builder_t *builder)
Grow positional arg array if needed.
Definition builder.c:247
positional_arg_descriptor_t * positional_args
Dynamic array of positional args.
Definition builder.h:450
size_t num_positional_args
Current count.
Definition builder.h:451
Positional argument descriptor.
Definition builder.h:357
const char * name
Name for help text (e.g., "address", "bind-addr")
Definition builder.h:358

References ensure_positional_arg_capacity(), positional_arg_descriptor_t::name, options_builder_t::num_positional_args, and options_builder_t::positional_args.

Referenced by options_builder_from_preset(), and options_preset_unified().

◆ options_builder_add_string()

void options_builder_add_string ( options_builder_t *  builder,
const char *  long_name,
char  short_name,
size_t  offset,
const char *  default_value,
const char *  help_text,
const char *  group,
bool  required,
const char *  env_var_name,
bool(*)(const void *options_struct, char **error_msg)  validate 
)

Add string option.

Strings are automatically strdup'd during parsing and freed during cleanup.

Parameters
builderBuilder to add to
long_nameLong option name
short_nameShort option char (or '\0')
offsetoffsetof(struct, field)
default_valueDefault value (will be strdup'd, or NULL)
help_textHelp description
groupGroup name for help
requiredIf true, option must be provided
env_var_nameEnvironment variable fallback (or NULL)
validateValidator function receiving full struct (or NULL)

◆ options_builder_add_usage()

void options_builder_add_usage ( options_builder_t *  builder,
const char *  mode,
const char *  positional,
bool  show_options,
const char *  description 
)

Add usage line descriptor.

Adds a usage line to be printed in the USAGE section. Components are colored separately during printing:

  • mode: magenta
  • positional: green
  • "[options...]": yellow
Parameters
builderBuilder instance
modeMode name (NULL for binary-level, or "server", "<mode>", etc.)
positionalPositional args (NULL or "[bind-addr]", "<session-string>", etc.)
show_optionsTrue to append "[options...]" or "[mode-options...]"
descriptionHelp text for this usage pattern

Example:

options_builder_add_usage(b, NULL, NULL, true,
"Start a new session");
options_builder_add_usage(b, NULL, "<session-string>", true,
"Join an existing session");
options_builder_add_usage(b, "<mode>", NULL, true,
"Run in a specific mode");
void options_builder_add_usage(options_builder_t *builder, const char *mode, const char *positional, bool show_options, const char *description)
Add usage line descriptor.
Definition builder.c:1242

Definition at line 1242 of file builder.c.

1243 {
1244 if (!builder || !description)
1245 return;
1246
1247 ensure_usage_line_capacity(builder);
1248
1250 .mode = mode, .positional = positional, .show_options = show_options, .description = description};
1251
1252 builder->usage_lines[builder->num_usage_lines++] = usage;
1253}
void usage(FILE *desc, asciichat_mode_t mode)
Print usage information for a specific mode.
size_t num_usage_lines
Current count.
Definition builder.h:455
usage_descriptor_t * usage_lines
Dynamic array of usage lines.
Definition builder.h:454
Usage line descriptor for programmatic USAGE generation.
Definition builder.h:296
const char * mode
NULL or mode name (e.g., "server") or "<mode>" placeholder.
Definition builder.h:297

References usage_descriptor_t::mode, options_builder_t::num_usage_lines, usage(), and options_builder_t::usage_lines.

Referenced by options_preset_unified().

◆ options_builder_build()

options_config_t * options_builder_build ( options_builder_t *  builder)

Build immutable options config.

Creates final config from builder. Builder is NOT consumed - you must still call options_builder_destroy().

Parameters
builderBuilder with options and dependencies
Returns
Immutable config (must be freed with options_config_destroy)

Definition at line 482 of file builder.c.

482 {
483 if (!builder) {
484 SET_ERRNO(ERROR_INVALID_PARAM, "Builder is NULL");
485 return NULL;
486 }
487
489 if (!config) {
490 SET_ERRNO(ERROR_MEMORY, "Failed to allocate options config");
491 return NULL;
492 }
493
494 // Allocate and copy descriptors
496 if (!config->descriptors && builder->num_descriptors > 0) {
497 SAFE_FREE(config);
498 SET_ERRNO(ERROR_MEMORY, "Failed to allocate descriptors");
499 return NULL;
500 }
501 memcpy(config->descriptors, builder->descriptors, builder->num_descriptors * sizeof(option_descriptor_t));
502 config->num_descriptors = builder->num_descriptors;
503
504 // Allocate and copy dependencies
506 if (!config->dependencies && builder->num_dependencies > 0) {
507 SAFE_FREE(config->descriptors);
508 SAFE_FREE(config);
509 SET_ERRNO(ERROR_MEMORY, "Failed to allocate dependencies");
510 return NULL;
511 }
512 memcpy(config->dependencies, builder->dependencies, builder->num_dependencies * sizeof(option_dependency_t));
513 config->num_dependencies = builder->num_dependencies;
514
515 // Allocate and copy positional args
516 if (builder->num_positional_args > 0) {
517 config->positional_args =
519 if (!config->positional_args) {
520 SAFE_FREE(config->descriptors);
521 SAFE_FREE(config->dependencies);
522 SAFE_FREE(config);
523 SET_ERRNO(ERROR_MEMORY, "Failed to allocate positional args");
524 return NULL;
525 }
526 memcpy(config->positional_args, builder->positional_args,
528 config->num_positional_args = builder->num_positional_args;
529 } else {
530 config->positional_args = NULL;
531 config->num_positional_args = 0;
532 }
533
534 // Allocate and copy usage lines
535 if (builder->num_usage_lines > 0) {
537 if (!config->usage_lines) {
538 SAFE_FREE(config->descriptors);
539 SAFE_FREE(config->dependencies);
540 SAFE_FREE(config->positional_args);
541 SAFE_FREE(config);
542 SET_ERRNO(ERROR_MEMORY, "Failed to allocate usage lines");
543 return NULL;
544 }
545 memcpy(config->usage_lines, builder->usage_lines, builder->num_usage_lines * sizeof(usage_descriptor_t));
546 config->num_usage_lines = builder->num_usage_lines;
547 } else {
548 config->usage_lines = NULL;
549 config->num_usage_lines = 0;
550 }
551
552 // Allocate and copy examples
553 if (builder->num_examples > 0) {
555 if (!config->examples) {
556 SAFE_FREE(config->descriptors);
557 SAFE_FREE(config->dependencies);
558 SAFE_FREE(config->positional_args);
559 SAFE_FREE(config->usage_lines);
560 SAFE_FREE(config);
561 SET_ERRNO(ERROR_MEMORY, "Failed to allocate examples");
562 return NULL;
563 }
564 memcpy(config->examples, builder->examples, builder->num_examples * sizeof(example_descriptor_t));
565 config->num_examples = builder->num_examples;
566 } else {
567 config->examples = NULL;
568 config->num_examples = 0;
569 }
570
571 // Allocate and copy modes
572 if (builder->num_modes > 0) {
574 if (!config->modes) {
575 SAFE_FREE(config->descriptors);
576 SAFE_FREE(config->dependencies);
577 SAFE_FREE(config->positional_args);
578 SAFE_FREE(config->usage_lines);
579 SAFE_FREE(config->examples);
580 SAFE_FREE(config);
581 SET_ERRNO(ERROR_MEMORY, "Failed to allocate modes");
582 return NULL;
583 }
584 memcpy(config->modes, builder->modes, builder->num_modes * sizeof(help_mode_descriptor_t));
585 config->num_modes = builder->num_modes;
586 } else {
587 config->modes = NULL;
588 config->num_modes = 0;
589 }
590
591 // Allocate and copy custom sections
592 if (builder->num_custom_sections > 0) {
593 config->custom_sections =
595 if (!config->custom_sections) {
596 SAFE_FREE(config->descriptors);
597 SAFE_FREE(config->dependencies);
598 SAFE_FREE(config->positional_args);
599 SAFE_FREE(config->usage_lines);
600 SAFE_FREE(config->examples);
601 SAFE_FREE(config->modes);
602 SAFE_FREE(config);
603 SET_ERRNO(ERROR_MEMORY, "Failed to allocate custom sections");
604 return NULL;
605 }
606 memcpy(config->custom_sections, builder->custom_sections,
608 config->num_custom_sections = builder->num_custom_sections;
609 } else {
610 config->custom_sections = NULL;
611 config->num_custom_sections = 0;
612 }
613
614 config->struct_size = builder->struct_size;
615 config->program_name = builder->program_name;
616 config->description = builder->description;
617
618 // Initialize memory management
619 config->owned_strings = builder->owned_strings_builder;
622
623 // Clear builder's ownership so it doesn't free them
624 builder->owned_strings_builder = NULL;
625 builder->num_owned_strings_builder = 0;
627
628 return config;
629}
#define SAFE_FREE(ptr)
Definition common.h:376
#define SAFE_MALLOC(size, cast)
Definition common.h:264
@ ERROR_MEMORY
Definition error_codes.h:56
size_t owned_strings_builder_capacity
Definition builder.h:477
const char * program_name
Definition builder.h:471
const char * description
Definition builder.h:472
char ** owned_strings_builder
Definition builder.h:475
size_t num_owned_strings_builder
Definition builder.h:476
size_t struct_size
Definition builder.h:470
positional_arg_descriptor_t * positional_args
Array of positional argument descriptors.
Definition builder.h:408
size_t num_custom_sections
Number of custom sections.
Definition builder.h:426
size_t num_usage_lines
Number of usage lines.
Definition builder.h:417
option_dependency_t * dependencies
Array of dependencies.
Definition builder.h:405
custom_section_descriptor_t * custom_sections
Array of custom help sections.
Definition builder.h:425
size_t num_modes
Number of modes.
Definition builder.h:423
const char * program_name
For usage header.
Definition builder.h:412
size_t num_positional_args
Number of positional arguments.
Definition builder.h:409
char ** owned_strings
Strdup'd strings to free on cleanup.
Definition builder.h:429
size_t owned_strings_capacity
Allocated capacity.
Definition builder.h:431
size_t num_examples
Number of examples.
Definition builder.h:420
size_t num_dependencies
Number of dependencies.
Definition builder.h:406
const char * description
For usage header.
Definition builder.h:413
help_mode_descriptor_t * modes
Array of mode descriptors.
Definition builder.h:422
usage_descriptor_t * usage_lines
Array of usage line descriptors.
Definition builder.h:416
example_descriptor_t * examples
Array of example descriptors.
Definition builder.h:419
size_t struct_size
sizeof(options_t) for bounds checking
Definition builder.h:411
size_t num_owned_strings
Number of owned strings.
Definition builder.h:430

References options_config_t::custom_sections, options_builder_t::custom_sections, options_config_t::dependencies, options_builder_t::dependencies, options_config_t::description, options_builder_t::description, options_config_t::descriptors, options_builder_t::descriptors, ERROR_INVALID_PARAM, ERROR_MEMORY, options_config_t::examples, options_builder_t::examples, options_config_t::modes, options_builder_t::modes, options_config_t::num_custom_sections, options_builder_t::num_custom_sections, options_config_t::num_dependencies, options_builder_t::num_dependencies, options_config_t::num_descriptors, options_builder_t::num_descriptors, options_config_t::num_examples, options_builder_t::num_examples, options_config_t::num_modes, options_builder_t::num_modes, options_config_t::num_owned_strings, options_builder_t::num_owned_strings_builder, options_config_t::num_positional_args, options_builder_t::num_positional_args, options_config_t::num_usage_lines, options_builder_t::num_usage_lines, options_config_t::owned_strings, options_builder_t::owned_strings_builder, options_builder_t::owned_strings_builder_capacity, options_config_t::owned_strings_capacity, options_config_t::positional_args, options_builder_t::positional_args, options_config_t::program_name, options_builder_t::program_name, SAFE_FREE, SAFE_MALLOC, SET_ERRNO, options_config_t::struct_size, options_builder_t::struct_size, options_config_t::usage_lines, and options_builder_t::usage_lines.

Referenced by options_builder_generate_manpage_template(), and options_preset_unified().

◆ options_builder_create()

options_builder_t * options_builder_create ( size_t  struct_size)

Create empty options builder.

Parameters
struct_sizesizeof(your_options_t) for the target options struct
Returns
New builder (must be freed with options_builder_destroy)

Definition at line 378 of file builder.c.

378 {
379 // Reset int defaults counter for new builder
380 g_int_defaults_counter = 0;
381
383 if (!builder) {
384 SET_ERRNO(ERROR_MEMORY, "Failed to allocate options builder");
385 return NULL;
386 }
387
389 if (!builder->descriptors) {
390 free(builder);
391 SET_ERRNO(ERROR_MEMORY, "Failed to allocate descriptors array");
392 return NULL;
393 }
394
396 if (!builder->dependencies) {
397 SAFE_FREE(builder->descriptors);
398 SAFE_FREE(builder);
399 SET_ERRNO(ERROR_MEMORY, "Failed to allocate dependencies array");
400 return NULL;
401 }
402
403 builder->num_descriptors = 0;
405 builder->num_dependencies = 0;
407 builder->positional_args = NULL;
408 builder->num_positional_args = 0;
409 builder->positional_arg_capacity = 0;
410 builder->usage_lines = NULL;
411 builder->num_usage_lines = 0;
412 builder->usage_line_capacity = 0;
413 builder->examples = NULL;
414 builder->num_examples = 0;
415 builder->example_capacity = 0;
416 builder->modes = NULL;
417 builder->num_modes = 0;
418 builder->mode_capacity = 0;
419 builder->custom_sections = NULL;
420 builder->num_custom_sections = 0;
421 builder->custom_section_capacity = 0;
422 builder->struct_size = struct_size;
423 builder->program_name = NULL;
424 builder->description = NULL;
425 builder->owned_strings_builder = NULL;
426 builder->num_owned_strings_builder = 0;
428
429 return builder;
430}
#define INITIAL_DEPENDENCY_CAPACITY
#define INITIAL_DESCRIPTOR_CAPACITY
size_t descriptor_capacity
Allocated capacity.
Definition builder.h:444
size_t mode_capacity
Allocated capacity.
Definition builder.h:464
size_t example_capacity
Allocated capacity.
Definition builder.h:460
size_t positional_arg_capacity
Allocated capacity.
Definition builder.h:452
size_t dependency_capacity
Allocated capacity.
Definition builder.h:448
size_t custom_section_capacity
Allocated capacity.
Definition builder.h:468
size_t usage_line_capacity
Allocated capacity.
Definition builder.h:456

References options_builder_t::custom_section_capacity, options_builder_t::custom_sections, options_builder_t::dependencies, options_builder_t::dependency_capacity, options_builder_t::description, options_builder_t::descriptor_capacity, options_builder_t::descriptors, ERROR_MEMORY, options_builder_t::example_capacity, options_builder_t::examples, INITIAL_DEPENDENCY_CAPACITY, INITIAL_DESCRIPTOR_CAPACITY, options_builder_t::mode_capacity, options_builder_t::modes, options_builder_t::num_custom_sections, options_builder_t::num_dependencies, options_builder_t::num_descriptors, options_builder_t::num_examples, options_builder_t::num_modes, options_builder_t::num_owned_strings_builder, options_builder_t::num_positional_args, options_builder_t::num_usage_lines, options_builder_t::owned_strings_builder, options_builder_t::owned_strings_builder_capacity, options_builder_t::positional_arg_capacity, options_builder_t::positional_args, options_builder_t::program_name, SAFE_FREE, SAFE_MALLOC, SET_ERRNO, options_builder_t::struct_size, options_builder_t::usage_line_capacity, and options_builder_t::usage_lines.

Referenced by options_builder_from_preset(), and options_preset_unified().

◆ options_builder_destroy()

void options_builder_destroy ( options_builder_t *  builder)

Free options builder.

Parameters
builderBuilder to free (can be NULL)

Definition at line 467 of file builder.c.

467 {
468 if (!builder)
469 return;
470
471 SAFE_FREE(builder->descriptors);
472 SAFE_FREE(builder->dependencies);
473 SAFE_FREE(builder->positional_args);
474 SAFE_FREE(builder->usage_lines);
475 SAFE_FREE(builder->examples);
476 SAFE_FREE(builder->modes);
477 SAFE_FREE(builder->custom_sections);
478 SAFE_FREE(builder->owned_strings_builder); // Free the builder's owned strings
479 SAFE_FREE(builder);
480}

References options_builder_t::custom_sections, options_builder_t::dependencies, options_builder_t::descriptors, options_builder_t::examples, options_builder_t::modes, options_builder_t::owned_strings_builder, options_builder_t::positional_args, SAFE_FREE, and options_builder_t::usage_lines.

Referenced by options_preset_unified().

◆ options_builder_from_preset()

options_builder_t * options_builder_from_preset ( const options_config_t *  preset)

Create builder from preset config.

Copies all descriptors and dependencies from the preset.

Parameters
presetPreset configuration to copy
Returns
New builder (must be freed with options_builder_destroy)

Definition at line 432 of file builder.c.

432 {
433 if (!preset) {
434 SET_ERRNO(ERROR_INVALID_PARAM, "Preset config is NULL");
435 return NULL;
436 }
437
439 if (!builder) {
440 return NULL;
441 }
442
443 builder->program_name = preset->program_name;
444 builder->description = preset->description;
445
446 // Copy all descriptors
447 for (size_t i = 0; i < preset->num_descriptors; i++) {
448 options_builder_add_descriptor(builder, &preset->descriptors[i]);
449 }
450
451 // Copy all dependencies
452 for (size_t i = 0; i < preset->num_dependencies; i++) {
453 options_builder_add_dependency(builder, &preset->dependencies[i]);
454 }
455
456 // Copy all positional arguments
457 for (size_t i = 0; i < preset->num_positional_args; i++) {
458 const positional_arg_descriptor_t *pos_arg = &preset->positional_args[i];
459 options_builder_add_positional(builder, pos_arg->name, pos_arg->help_text, pos_arg->required,
460 pos_arg->section_heading, pos_arg->examples, pos_arg->num_examples,
461 pos_arg->mode_bitmask, pos_arg->parse_fn);
462 }
463
464 return builder;
465}
void options_builder_add_dependency(options_builder_t *builder, const option_dependency_t *dependency)
Add full dependency (advanced)
Definition builder.c:1164
void options_builder_add_descriptor(options_builder_t *builder, const option_descriptor_t *descriptor)
Add full option descriptor (advanced)
Definition builder.c:973
const char * section_heading
Section heading for examples (e.g., "ADDRESS FORMATS")
Definition builder.h:363
bool required
If true, this positional arg must be provided.
Definition builder.h:360
const char * help_text
Description for usage message.
Definition builder.h:359
int(* parse_fn)(const char *arg, void *config, char **remaining, int num_remaining, char **error_msg)
Definition builder.h:377
option_mode_bitmask_t mode_bitmask
Which modes this positional arg applies to.
Definition builder.h:380
const char ** examples
Array of example strings with descriptions.
Definition builder.h:364
size_t num_examples
Number of examples.
Definition builder.h:365

References options_config_t::dependencies, options_config_t::description, options_builder_t::description, options_config_t::descriptors, ERROR_INVALID_PARAM, positional_arg_descriptor_t::examples, positional_arg_descriptor_t::help_text, positional_arg_descriptor_t::mode_bitmask, positional_arg_descriptor_t::name, options_config_t::num_dependencies, options_config_t::num_descriptors, positional_arg_descriptor_t::num_examples, options_config_t::num_positional_args, options_builder_add_dependency(), options_builder_add_descriptor(), options_builder_add_positional(), options_builder_create(), positional_arg_descriptor_t::parse_fn, options_config_t::positional_args, options_config_t::program_name, options_builder_t::program_name, positional_arg_descriptor_t::required, positional_arg_descriptor_t::section_heading, SET_ERRNO, and options_config_t::struct_size.

◆ options_builder_mark_as_list()

void options_builder_mark_as_list ( options_builder_t *  builder,
const char *  option_name 
)

Mark option as accepting multiple values.

Indicates the option accepts comma-separated or space-separated values.

Parameters
builderOptions builder
option_nameLong name of option to mark

Example:

options_builder_mark_as_list(builder, "stun-servers"); // Accepts comma-separated URLs
void options_builder_mark_as_list(options_builder_t *builder, const char *option_name)
Mark option as accepting multiple values.
Definition builder.c:1093

Definition at line 1093 of file builder.c.

1093 {
1094 if (!builder || !option_name) {
1095 SET_ERRNO(ERROR_INVALID_PARAM, "Builder or option_name is NULL");
1096 return;
1097 }
1098
1099 int idx = find_descriptor_in_builder(builder, option_name);
1100 if (idx < 0) {
1101 SET_ERRNO(ERROR_INVALID_PARAM, "Option '%s' not found in builder", option_name);
1102 return;
1103 }
1104
1105 option_descriptor_t *desc = &builder->descriptors[idx];
1106 desc->metadata.is_list = true;
1107}
option_metadata_t metadata
Metadata for shell completions (enums, ranges, examples, etc.)
Definition builder.h:281
bool is_list
If true, option accepts multiple comma-separated or space-separated values.
Definition builder.h:217

References options_builder_t::descriptors, ERROR_INVALID_PARAM, option_metadata_t::is_list, option_descriptor_t::metadata, and SET_ERRNO.

◆ options_builder_mark_binary_only()

void options_builder_mark_binary_only ( options_builder_t *  builder,
const char *  option_name 
)

Mark an option as binary-level only (hide from mode-specific help)

Binary-level options are still parsed by mode-specific parsers (so they work anywhere in the command line), but they don't appear in mode-specific –help. They should only be documented in the top-level binary help.

Parameters
builderBuilder containing the option
option_nameLong name of the option to mark

Example:

options_builder_add_string(b, "log-file", 'L', ...);
void options_builder_mark_binary_only(options_builder_t *builder, const char *option_name)
Mark an option as binary-level only (hide from mode-specific help)
Definition builder.c:1172
void options_builder_add_string(options_builder_t *builder, const char *long_name, char short_name, size_t offset, const char *default_value, const char *help_text, const char *group, bool required, const char *env_var_name, bool(*validate)(const void *, char **))
Definition builder.c:742

Definition at line 1172 of file builder.c.

1172 {
1173 if (!builder || !option_name)
1174 return;
1175
1176 // Find the option by name and mark it
1177 for (size_t i = 0; i < builder->num_descriptors; i++) {
1178 if (strcmp(builder->descriptors[i].long_name, option_name) == 0) {
1179 builder->descriptors[i].hide_from_mode_help = true;
1180 return;
1181 }
1182 }
1183
1184 // Option not found - this is a programming error
1185 log_warn("Attempted to mark non-existent option '%s' as binary-only", option_name);
1186}
#define log_warn(...)
Log a WARN message.
Definition log/log.h:574
bool hide_from_mode_help
If true, don't show in mode-specific help (binary-level only)
Definition builder.h:254

References options_builder_t::descriptors, option_descriptor_t::hide_from_mode_help, log_warn, option_descriptor_t::long_name, and options_builder_t::num_descriptors.

◆ options_builder_set_arg_placeholder()

void options_builder_set_arg_placeholder ( options_builder_t *  builder,
const char *  arg_placeholder 
)

Set custom argument placeholder on the last added option descriptor.

Sets the arg_placeholder field on the most recently added option descriptor. This allows customizing the placeholder text shown in help (e.g., "SHELL [FILE]" instead of "STR").

Parameters
builderOptions builder
arg_placeholderCustom placeholder text (e.g., "SHELL [FILE]"), or NULL to use type-based placeholder

Definition at line 992 of file builder.c.

992 {
993 if (!builder || builder->num_descriptors == 0) {
994 SET_ERRNO(ERROR_INVALID_STATE, "Builder is NULL or has no descriptors");
995 return;
996 }
997 builder->descriptors[builder->num_descriptors - 1].arg_placeholder = arg_placeholder;
998}
@ ERROR_INVALID_STATE
const char * arg_placeholder
Custom argument placeholder (e.g., "SHELL [FILE]" instead of "STR")
Definition builder.h:253

References option_descriptor_t::arg_placeholder, options_builder_t::descriptors, ERROR_INVALID_STATE, options_builder_t::num_descriptors, and SET_ERRNO.

Referenced by options_registry_add_all_to_builder().

◆ options_builder_set_default_value_display()

void options_builder_set_default_value_display ( options_builder_t *  builder,
const char *  option_name,
const char *  default_value 
)

Set default value string in metadata.

Sets the default value string for display in completions (informational, separate from the descriptor's default_value which is used for parsing).

Parameters
builderOptions builder
option_nameLong name of option to set metadata for
default_valueDefault value as string for display

Definition at line 1109 of file builder.c.

1110 {
1111 if (!builder || !option_name) {
1112 SET_ERRNO(ERROR_INVALID_PARAM, "Builder or option_name is NULL");
1113 return;
1114 }
1115
1116 int idx = find_descriptor_in_builder(builder, option_name);
1117 if (idx < 0) {
1118 SET_ERRNO(ERROR_INVALID_PARAM, "Option '%s' not found in builder", option_name);
1119 return;
1120 }
1121
1122 option_descriptor_t *desc = &builder->descriptors[idx];
1123 desc->metadata.default_value = default_value;
1124}
const char * default_value
Default value as string for display.
Definition builder.h:211

References option_metadata_t::default_value, options_builder_t::descriptors, ERROR_INVALID_PARAM, option_descriptor_t::metadata, and SET_ERRNO.

◆ options_builder_set_enum_values()

void options_builder_set_enum_values ( options_builder_t *  builder,
const char *  option_name,
const char **  values,
const char **  descriptions 
)

Set enum values with descriptions for an option.

Populates the metadata with enum values and descriptions for shell completion. Both arrays must have the same length.

Parameters
builderOptions builder
option_nameLong name of option to set metadata for
valuesArray of enum value strings (e.g., {"auto", "none", "16", "256", "truecolor"})
descriptionsArray of descriptions parallel to values
countNumber of values/descriptions

Example:

const char *color_values[] = {"auto", "none", "16", "256", "truecolor"};
const char *color_descs[] = {
"Auto-detect from terminal",
"Monochrome only",
"16 colors (ANSI)",
"256 colors (xterm)",
"24-bit truecolor (modern terminals)"
};
options_builder_set_enum_values(builder, "color-mode", color_values, color_descs, 5);
void options_builder_set_enum_values(options_builder_t *builder, const char *option_name, const char **values, const char **descriptions)
Set enum values with descriptions for an option.
Definition builder.c:1022
void options_builder_set_input_type(options_builder_t *builder, const char *option_name, option_input_type_t input_type)
Set input type for an option.
Definition builder.c:1076

Definition at line 1022 of file builder.c.

1023 {
1024 if (!builder || !option_name || !values || !descriptions) {
1025 SET_ERRNO(ERROR_INVALID_PARAM, "Builder or arguments are NULL");
1026 return;
1027 }
1028
1029 int idx = find_descriptor_in_builder(builder, option_name);
1030 if (idx < 0) {
1031 SET_ERRNO(ERROR_INVALID_PARAM, "Option '%s' not found in builder", option_name);
1032 return;
1033 }
1034
1035 option_descriptor_t *desc = &builder->descriptors[idx];
1036 desc->metadata.enum_values = values;
1037 desc->metadata.enum_descriptions = descriptions;
1038}
const char ** enum_descriptions
Descriptions parallel to enum_values (null-terminated, e.g., "Auto-detect from terminal")
Definition builder.h:197
const char ** enum_values
Enum value strings (null-terminated, e.g., {"auto", "none", "16", "256", "truecolor",...
Definition builder.h:195

References options_builder_t::descriptors, option_metadata_t::enum_descriptions, option_metadata_t::enum_values, ERROR_INVALID_PARAM, option_descriptor_t::metadata, and SET_ERRNO.

◆ options_builder_set_examples()

void options_builder_set_examples ( options_builder_t *  builder,
const char *  option_name,
const char **  examples 
)

Set example values for an option.

Provides example values or command invocations for help display and completion suggestions.

Parameters
builderOptions builder
option_nameLong name of option to set metadata for
examplesArray of example strings (must be null-terminated, may be values or full commands)

Example:

const char *fps_examples[] = {"30", "60", "144", NULL};
options_builder_set_examples(builder, "fps", fps_examples);
void options_builder_set_examples(options_builder_t *builder, const char *option_name, const char **examples)
Set example values for an option.
Definition builder.c:1059

Definition at line 1059 of file builder.c.

1059 {
1060 if (!builder || !option_name || !examples) {
1061 SET_ERRNO(ERROR_INVALID_PARAM, "Builder or arguments are NULL");
1062 return;
1063 }
1064
1065 int idx = find_descriptor_in_builder(builder, option_name);
1066 if (idx < 0) {
1067 SET_ERRNO(ERROR_INVALID_PARAM, "Option '%s' not found in builder", option_name);
1068 return;
1069 }
1070
1071 option_descriptor_t *desc = &builder->descriptors[idx];
1072 desc->metadata.examples = examples;
1073 // Note: examples array must be null-terminated
1074}
const char ** examples
Example values or command invocations (null-terminated array)
Definition builder.h:208

References options_builder_t::descriptors, ERROR_INVALID_PARAM, option_metadata_t::examples, option_descriptor_t::metadata, and SET_ERRNO.

◆ options_builder_set_input_type()

void options_builder_set_input_type ( options_builder_t *  builder,
const char *  option_name,
option_input_type_t  input_type 
)

Set input type for an option.

Specifies what kind of input the option expects (enum, numeric, filepath, etc.) for generating appropriate shell completions.

Parameters
builderOptions builder
option_nameLong name of option to set metadata for
input_typeThe input type (OPTION_INPUT_ENUM, OPTION_INPUT_NUMERIC, etc.)

Example:

Definition at line 1076 of file builder.c.

1077 {
1078 if (!builder || !option_name) {
1079 SET_ERRNO(ERROR_INVALID_PARAM, "Builder or option_name is NULL");
1080 return;
1081 }
1082
1083 int idx = find_descriptor_in_builder(builder, option_name);
1084 if (idx < 0) {
1085 SET_ERRNO(ERROR_INVALID_PARAM, "Option '%s' not found in builder", option_name);
1086 return;
1087 }
1088
1089 option_descriptor_t *desc = &builder->descriptors[idx];
1090 desc->metadata.input_type = input_type;
1091}
option_input_type_t input_type
What kind of input this option expects.
Definition builder.h:214

References options_builder_t::descriptors, ERROR_INVALID_PARAM, option_metadata_t::input_type, option_descriptor_t::metadata, and SET_ERRNO.

◆ options_builder_set_mode_bitmask()

void options_builder_set_mode_bitmask ( options_builder_t *  builder,
option_mode_bitmask_t  mode_bitmask 
)

Set mode bitmask on the last added option descriptor.

Sets the mode_bitmask field on the most recently added option descriptor. This allows setting mode applicability after adding an option.

Parameters
builderOptions builder
mode_bitmaskBitmask indicating which modes this option applies to

Definition at line 984 of file builder.c.

984 {
985 if (!builder || builder->num_descriptors == 0) {
986 SET_ERRNO(ERROR_INVALID_STATE, "Builder is NULL or has no descriptors");
987 return;
988 }
989 builder->descriptors[builder->num_descriptors - 1].mode_bitmask = mode_bitmask;
990}

References options_builder_t::descriptors, ERROR_INVALID_STATE, option_descriptor_t::mode_bitmask, options_builder_t::num_descriptors, and SET_ERRNO.

Referenced by options_registry_add_all_to_builder().

◆ options_builder_set_numeric_range()

void options_builder_set_numeric_range ( options_builder_t *  builder,
const char *  option_name,
int  min,
int  max,
int  step 
)

Set numeric range for an option.

Specifies minimum, maximum, and optional step for numeric input completions.

Parameters
builderOptions builder
option_nameLong name of option to set metadata for
minMinimum value (0 = no limit)
maxMaximum value (0 = no limit)
stepStep size (0 = continuous, no step)

Example:

options_builder_set_numeric_range(builder, "compression-level", 1, 9, 1);
void options_builder_set_numeric_range(options_builder_t *builder, const char *option_name, int min, int max, int step)
Set numeric range for an option.
Definition builder.c:1040

Definition at line 1040 of file builder.c.

1041 {
1042 if (!builder || !option_name) {
1043 SET_ERRNO(ERROR_INVALID_PARAM, "Builder or option_name is NULL");
1044 return;
1045 }
1046
1047 int idx = find_descriptor_in_builder(builder, option_name);
1048 if (idx < 0) {
1049 SET_ERRNO(ERROR_INVALID_PARAM, "Option '%s' not found in builder", option_name);
1050 return;
1051 }
1052
1053 option_descriptor_t *desc = &builder->descriptors[idx];
1054 desc->metadata.numeric_range.min = min;
1055 desc->metadata.numeric_range.max = max;
1056 desc->metadata.numeric_range.step = step;
1057}
int max
Maximum value (or 0 if no limit)
Definition builder.h:203
int min
Minimum value (or 0 if no limit)
Definition builder.h:202
int step
Step size (0 = no step, continuous)
Definition builder.h:204
struct option_metadata_t::@19 numeric_range

References options_builder_t::descriptors, ERROR_INVALID_PARAM, option_metadata_t::max, option_descriptor_t::metadata, option_metadata_t::min, option_metadata_t::numeric_range, SET_ERRNO, and option_metadata_t::step.

◆ options_config_calculate_max_col_width()

int options_config_calculate_max_col_width ( const options_config_t *  config)

Calculate global max column width for help output alignment.

Calculates the maximum width needed for proper alignment across all help sections (USAGE, EXAMPLES, OPTIONS, MODES).

Parameters
configOptions configuration
Returns
Maximum column width needed for alignment

Calculate global max column width for help output alignment.

Calculates the maximum width needed for proper alignment across USAGE, EXAMPLES, OPTIONS, and MODES sections.

Definition at line 77 of file help.c.

77 {
78 if (!config)
79 return 0;
80
81 const char *binary_name = PLATFORM_BINARY_NAME;
82
83 int max_col_width = 0;
84 char temp_buf[BUFFER_SIZE_MEDIUM];
85
86 // Check USAGE entries (capped at 45 chars for max first column)
87 for (size_t i = 0; i < config->num_usage_lines; i++) {
88 const usage_descriptor_t *usage = &config->usage_lines[i];
89 int len = 0;
90
91 len += safe_snprintf(temp_buf + len, sizeof(temp_buf) - len, "%s", binary_name);
92
93 if (usage->mode) {
94 const char *colored_mode = colored_string(LOG_COLOR_FATAL, usage->mode);
95 len += safe_snprintf(temp_buf + len, sizeof(temp_buf) - len, " %s", colored_mode);
96 }
97
98 if (usage->positional) {
99 const char *colored_pos = colored_string(LOG_COLOR_INFO, usage->positional);
100 len += safe_snprintf(temp_buf + len, sizeof(temp_buf) - len, " %s", colored_pos);
101 }
102
103 if (usage->show_options) {
104 const char *options_text =
105 (usage->mode && strcmp(usage->mode, "<mode>") == 0) ? "[mode-options...]" : "[options...]";
106 const char *colored_opts = colored_string(LOG_COLOR_WARN, options_text);
107 len += safe_snprintf(temp_buf + len, sizeof(temp_buf) - len, " %s", colored_opts);
108 }
109
110 int w = utf8_display_width(temp_buf);
111 if (w > LAYOUT_COLUMN_WIDTH) {
113 }
114 if (w > max_col_width)
115 max_col_width = w;
116 }
117
118 // Check EXAMPLES entries
119 for (size_t i = 0; i < config->num_examples; i++) {
120 const example_descriptor_t *example = &config->examples[i];
121 int len = 0;
122
123 // Only prepend program name if this is not a utility command
124 if (!example->is_utility_command) {
125 len += safe_snprintf(temp_buf + len, sizeof(temp_buf) - len, "%s", binary_name);
126
127 // Programmatically add mode name based on mode_bitmask
128 const char *mode_name = get_mode_name_from_bitmask(example->mode_bitmask);
129 if (mode_name) {
130 len += safe_snprintf(temp_buf + len, sizeof(temp_buf) - len, " %s", mode_name);
131 }
132 }
133
134 if (example->args) {
135 const char *colored_args = colored_string(LOG_COLOR_INFO, example->args);
136 len += safe_snprintf(temp_buf + len, sizeof(temp_buf) - len, " %s", colored_args);
137 }
138
139 int w = utf8_display_width(temp_buf);
140 if (w > LAYOUT_COLUMN_WIDTH) {
142 }
143 if (w > max_col_width)
144 max_col_width = w;
145 }
146
147 // Check MODES entries (capped at 45 chars)
148 for (size_t i = 0; i < config->num_modes; i++) {
149 const char *colored_name = colored_string(LOG_COLOR_FATAL, config->modes[i].name);
150 int w = utf8_display_width(colored_name);
151 if (w > LAYOUT_COLUMN_WIDTH) {
153 }
154 if (w > max_col_width)
155 max_col_width = w;
156 }
157
158 // Check OPTIONS entries (from descriptors)
159 for (size_t i = 0; i < config->num_descriptors; i++) {
160 const option_descriptor_t *desc = &config->descriptors[i];
161 if (desc->hide_from_mode_help || desc->hide_from_binary_help || !desc->group)
162 continue;
163
164 // Build option display string with separate coloring for short and long flags
165 char opts_buf[BUFFER_SIZE_SMALL];
166 if (desc->short_name && desc->short_name != '\0') {
167 char short_flag[16];
168 safe_snprintf(short_flag, sizeof(short_flag), "-%c", desc->short_name);
169 char long_flag[BUFFER_SIZE_SMALL];
170 safe_snprintf(long_flag, sizeof(long_flag), "--%s", desc->long_name);
171 // Color short flag, add comma, color long flag
172 safe_snprintf(opts_buf, sizeof(opts_buf), "%s, %s", colored_string(LOG_COLOR_WARN, short_flag),
173 colored_string(LOG_COLOR_WARN, long_flag));
174 } else {
175 char long_flag[BUFFER_SIZE_SMALL];
176 safe_snprintf(long_flag, sizeof(long_flag), "--%s", desc->long_name);
177 safe_snprintf(opts_buf, sizeof(opts_buf), "%s", colored_string(LOG_COLOR_WARN, long_flag));
178 }
179 const char *colored_opts = opts_buf;
180
181 int w = utf8_display_width(colored_opts);
182 if (w > LAYOUT_COLUMN_WIDTH) {
184 }
185 if (w > max_col_width)
186 max_col_width = w;
187 }
188
189 // Enforce maximum column width of 45 characters
190 if (max_col_width > 45) {
191 max_col_width = 45;
192 }
193
194 return max_col_width;
195}
#define BUFFER_SIZE_SMALL
Small buffer size (256 bytes)
#define BUFFER_SIZE_MEDIUM
Medium buffer size (512 bytes)
@ LOG_COLOR_FATAL
Definition log/log.h:136
@ LOG_COLOR_INFO
Definition log/log.h:133
@ LOG_COLOR_WARN
Definition log/log.h:134
#define PLATFORM_BINARY_NAME
Suppress unused parameter warnings.
const char * colored_string(log_color_t color, const char *text)
Build a colored string for terminal output.
int utf8_display_width(const char *str)
Calculate terminal display width of a UTF-8 string.
Definition utf8.c:46
#define LAYOUT_COLUMN_WIDTH
Definition layout.h:19
bool is_utility_command
True if this is a utility command (don't prepend program name)
Definition builder.h:315
const char * group
Group name for help sections (e.g., "NETWORK OPTIONS")
Definition builder.h:252
char short_name
Short option char (e.g., 'p', or '\0' if none)
Definition builder.h:244

References example_descriptor_t::args, BUFFER_SIZE_MEDIUM, BUFFER_SIZE_SMALL, colored_string(), options_config_t::descriptors, options_config_t::examples, option_descriptor_t::group, option_descriptor_t::hide_from_binary_help, option_descriptor_t::hide_from_mode_help, example_descriptor_t::is_utility_command, LAYOUT_COLUMN_WIDTH, LOG_COLOR_FATAL, LOG_COLOR_INFO, LOG_COLOR_WARN, option_descriptor_t::long_name, example_descriptor_t::mode_bitmask, options_config_t::modes, help_mode_descriptor_t::name, options_config_t::num_descriptors, options_config_t::num_examples, options_config_t::num_modes, options_config_t::num_usage_lines, PLATFORM_BINARY_NAME, safe_snprintf(), option_descriptor_t::short_name, usage(), options_config_t::usage_lines, and utf8_display_width().

Referenced by options_config_print_options_sections_with_width(), and options_config_print_usage_section().

◆ options_config_destroy()

void options_config_destroy ( options_config_t *  config)

Free options config.

Frees the config structure. Does NOT free strings in the options struct - use options_config_destroy() for that.

Parameters
configConfig to free (can be NULL)

Definition at line 631 of file builder.c.

631 {
632 if (!config)
633 return;
634
635 SAFE_FREE(config->descriptors);
636 SAFE_FREE(config->dependencies);
637 SAFE_FREE(config->positional_args);
638 SAFE_FREE(config->usage_lines);
639 SAFE_FREE(config->examples);
640 SAFE_FREE(config->modes);
641 SAFE_FREE(config->custom_sections);
642
643 // Free all owned strings before freeing the array
644 // Use SAFE_FREE because platform_strdup uses SAFE_MALLOC (mimalloc on Windows)
645 for (size_t i = 0; i < config->num_owned_strings; i++) {
646 SAFE_FREE(config->owned_strings[i]);
647 }
648 SAFE_FREE(config->owned_strings);
649
650 SAFE_FREE(config);
651}

References options_config_t::custom_sections, options_config_t::dependencies, options_config_t::descriptors, options_config_t::examples, options_config_t::modes, options_config_t::num_owned_strings, options_config_t::owned_strings, options_config_t::positional_args, SAFE_FREE, and options_config_t::usage_lines.

Referenced by config_create_default(), options_init(), and usage().

◆ options_config_parse()

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.

Parses argv using the option descriptors. Strings are automatically strdup'd. Environment variables are checked for missing options.

Parameters
configOptions configuration
argcArgument count
argvArgument vector
options_structOptions struct to fill
remaining_argcOptional: receives count of non-option args
remaining_argvOptional: receives array of non-option args
Returns
ASCIICHAT_OK on success, ERROR_USAGE on parse errors

Definition at line 1815 of file builder.c.

1817 {
1818 if (!config || !options_struct) {
1819 return SET_ERRNO(ERROR_INVALID_PARAM, "Config or options struct is NULL");
1820 }
1821
1822 // Use the new unified parser that handles mixed positional and flag arguments
1823 asciichat_error_t result = options_config_parse_unified(config, argc, argv, options_struct, detected_mode);
1824 if (result != ASCIICHAT_OK) {
1825 return result;
1826 }
1827
1828 // Since unified parser handles all arguments, there are no remaining args
1829 // (backward compatibility: set remaining_argc to 0)
1830 if (remaining_argc) {
1831 *remaining_argc = 0;
1832 }
1833 if (remaining_argv) {
1834 *remaining_argv = NULL;
1835 }
1836
1837 return ASCIICHAT_OK;
1838}
asciichat_error_t
Error and exit codes - unified status values (0-255)
Definition error_codes.h:49
@ ASCIICHAT_OK
Definition error_codes.h:51

References ASCIICHAT_OK, ERROR_INVALID_PARAM, and SET_ERRNO.

Referenced by options_init().

◆ options_config_parse_positional()

asciichat_error_t options_config_parse_positional ( const options_config_t *  config,
int  remaining_argc,
char **  remaining_argv,
void *  options_struct 
)

Parse positional arguments.

Parses positional arguments from remaining_argv after getopt processing. Calls each positional arg descriptor's parse_fn in order.

This is called automatically by options_config_parse(), but can also be called separately if you want custom control over the parsing flow.

Parameters
configOptions configuration
remaining_argcCount of remaining args
remaining_argvArray of remaining args
options_structOptions struct to fill
Returns
ASCIICHAT_OK on success, ERROR_USAGE on parse errors

Definition at line 1331 of file builder.c.

1332 {
1333 if (!config || !options_struct) {
1334 return SET_ERRNO(ERROR_INVALID_PARAM, "Config or options struct is NULL");
1335 }
1336
1337 if (remaining_argc < 0 || (remaining_argc > 0 && !remaining_argv)) {
1338 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid remaining args");
1339 }
1340
1341 // No positional args defined - but we got some positional arguments
1342 if (config->num_positional_args == 0 && remaining_argc > 0) {
1343 log_error("Error: Unexpected positional argument '%s'", remaining_argv[0]);
1344 return ERROR_USAGE;
1345 }
1346
1347 // Check if required positional args are missing
1348 for (size_t i = 0; i < config->num_positional_args; i++) {
1349 const positional_arg_descriptor_t *pos_arg = &config->positional_args[i];
1350 if (pos_arg->required && remaining_argc == 0) {
1351 log_error("Error: Missing required positional argument '%s'", pos_arg->name);
1352 if (pos_arg->help_text) {
1353 log_error(" %s", pos_arg->help_text);
1354 }
1355 return ERROR_USAGE;
1356 }
1357 }
1358
1359 // Parse positional arguments
1360 int arg_index = 0;
1361 for (size_t i = 0; i < config->num_positional_args && arg_index < remaining_argc; i++) {
1362 const positional_arg_descriptor_t *pos_arg = &config->positional_args[i];
1363
1364 const char *arg = remaining_argv[arg_index];
1365 char **remaining = (arg_index + 1 < remaining_argc) ? &remaining_argv[arg_index + 1] : NULL;
1366 int num_remaining = remaining_argc - arg_index - 1;
1367 char *error_msg = NULL;
1368
1369 // Call custom parser
1370 int consumed = pos_arg->parse_fn(arg, options_struct, remaining, num_remaining, &error_msg);
1371
1372 if (consumed < 0) {
1373 // Parse error
1374 if (error_msg) {
1375 log_error("Error parsing positional argument '%s': %s", pos_arg->name, error_msg);
1376 free(error_msg);
1377 } else {
1378 log_error("Error parsing positional argument '%s': %s", pos_arg->name, arg);
1379 }
1380 return ERROR_USAGE;
1381 }
1382
1383 // Advance by consumed args (usually 1, but can be more for multi-arg parsers)
1384 arg_index += consumed;
1385 }
1386
1387 // Check for extra unconsumed positional arguments
1388 if (arg_index < remaining_argc) {
1389 log_error("Error: Unexpected positional argument '%s'", remaining_argv[arg_index]);
1390 return ERROR_USAGE;
1391 }
1392
1393 return ASCIICHAT_OK;
1394}
@ ERROR_USAGE
Definition error_codes.h:53

References ASCIICHAT_OK, ERROR_INVALID_PARAM, ERROR_USAGE, positional_arg_descriptor_t::help_text, log_error, positional_arg_descriptor_t::name, options_config_t::num_positional_args, positional_arg_descriptor_t::parse_fn, options_config_t::positional_args, positional_arg_descriptor_t::required, and SET_ERRNO.

◆ options_config_print_options_sections()

void options_config_print_options_sections ( const options_config_t *  config,
FILE *  stream,
asciichat_mode_t  mode 
)

Print everything except the USAGE section (backward compatibility)

Wrapper for options_config_print_options_sections_with_width with auto-calculation.

Parameters
configOptions configuration
streamOutput stream (stdout or stderr)
modeOptional mode to filter by (if provided, filters by mode_bitmask)

Print everything except the USAGE section (backward compatibility)

Calls options_config_print_options_sections_with_width with auto-calculation.

Definition at line 1085 of file help.c.

1085 {
1086 options_config_print_options_sections_with_width(config, stream, 0, mode);
1087}
void options_config_print_options_sections_with_width(const options_config_t *config, FILE *stream, int max_col_width, asciichat_mode_t mode)
Print everything except the USAGE section.
Definition help.c:890

References options_config_print_options_sections_with_width().

◆ options_config_print_options_sections_with_width()

void options_config_print_options_sections_with_width ( const options_config_t *  config,
FILE *  stream,
int  max_col_width,
asciichat_mode_t  mode 
)

Print everything except the USAGE section.

Prints MODES, MODE-OPTIONS, EXAMPLES, and OPTIONS sections. Used with options_config_print_usage_section to allow custom content in between.

Parameters
configOptions configuration (should be unified preset with all options)
streamOutput stream (stdout or stderr)
max_col_widthOptional pre-calculated column width (0 = auto-calculate)
modeOptional mode to filter by (if MODE_SERVER, MODE_CLIENT, etc., filters by mode_bitmask) If NULL or invalid, shows all options (for binary-level help)

Prints MODES, MODE-OPTIONS, EXAMPLES, and OPTIONS sections. Used with options_config_print_usage_section to allow custom content in between.

Definition at line 890 of file help.c.

891 {
892 if (!config || !stream) {
893 SET_ERRNO(ERROR_INVALID_PARAM, "Config or stream is NULL");
894 return;
895 }
896
897 // Detect terminal width - try actual terminal size first, fallback to COLUMNS env var
898 int term_width = 80;
899 terminal_size_t term_size;
900 if (terminal_get_size(&term_size) == ASCIICHAT_OK && term_size.cols > 40) {
901 term_width = term_size.cols;
902 } else {
903 const char *cols_env = SAFE_GETENV("COLUMNS");
904 if (cols_env) {
905 char *endptr;
906 errno = 0;
907 long cols = strtol(cols_env, &endptr, 10);
908 if (*endptr == '\0' && errno == 0 && cols > 40 && cols <= INT_MAX)
909 term_width = (int)cols;
910 else if (*endptr != '\0' || errno != 0 || cols <= 40)
911 log_error("options_config_print_*: Invalid COLUMNS value: %s", cols_env);
912 }
913 }
914
915 // Calculate column width if not provided
916 if (max_col_width <= 0) {
917 max_col_width = options_config_calculate_max_col_width(config);
918 }
919
920 // CAP max_col_width: 86 if terminal wide, otherwise 45 for narrow first column
921 int max_col_cap = (term_width > 170) ? 86 : 45;
922 if (max_col_width > max_col_cap) {
923 max_col_width = max_col_cap;
924 }
925
926 // Determine if this is binary-level help
927 // Discovery mode IS the binary-level help (shows binary options + discovery options)
928 // Other modes show only mode-specific options
929 bool for_binary_help = (mode == MODE_DISCOVERY);
930 // Build list of unique groups in order of first appearance
931 const char **unique_groups = SAFE_MALLOC(config->num_descriptors * sizeof(const char *), const char **);
932 size_t num_unique_groups = 0;
933
934 // For binary-level help, we now use a two-pass system to order groups,
935 // ensuring binary-level option groups appear before discovery-mode groups.
936 if (for_binary_help) {
937 // Pass 1: Add GENERAL first, then LOGGING if present
938 bool general_added = false;
939 bool logging_added = false;
940 for (size_t i = 0; i < config->num_descriptors; i++) {
941 const option_descriptor_t *desc = &config->descriptors[i];
942 if (desc->group) {
943 if (!general_added && strcmp(desc->group, "GENERAL") == 0) {
944 unique_groups[num_unique_groups++] = "GENERAL";
945 general_added = true;
946 }
947 if (!logging_added && strcmp(desc->group, "LOGGING") == 0) {
948 unique_groups[num_unique_groups++] = "LOGGING";
949 logging_added = true;
950 }
951 if (general_added && logging_added) {
952 break;
953 }
954 }
955 }
956
957 // Pass 2: Collect all other groups that apply for binary help (all modes), if not already added.
958 for (size_t i = 0; i < config->num_descriptors; i++) {
959 const option_descriptor_t *desc = &config->descriptors[i];
960 // An option applies if option_applies_to_mode says so for binary help (which checks OPTION_MODE_ALL)
961 if (option_applies_to_mode(desc, mode, for_binary_help) && desc->group) {
962 // Skip GENERAL and LOGGING groups if we already added them (for binary help)
963 if (strcmp(desc->group, "GENERAL") == 0 || strcmp(desc->group, "LOGGING") == 0) {
964 continue;
965 }
966
967 bool group_exists = false;
968 for (size_t j = 0; j < num_unique_groups; j++) {
969 if (strcmp(unique_groups[j], desc->group) == 0) {
970 group_exists = true;
971 break;
972 }
973 }
974 if (!group_exists) {
975 unique_groups[num_unique_groups++] = desc->group;
976 }
977 } else if (desc->group) {
978 }
979 }
980 } else {
981 // Original logic for other modes
982 for (size_t i = 0; i < config->num_descriptors; i++) {
983 const option_descriptor_t *desc = &config->descriptors[i];
984 if (option_applies_to_mode(desc, mode, for_binary_help) && desc->group) {
985 bool group_exists = false;
986 for (size_t j = 0; j < num_unique_groups; j++) {
987 if (strcmp(unique_groups[j], desc->group) == 0) {
988 group_exists = true;
989 break;
990 }
991 }
992 if (!group_exists) {
993 unique_groups[num_unique_groups++] = desc->group;
994 }
995 }
996 }
997 }
998
999 // Print options grouped by category
1000 for (size_t gi = 0; gi < num_unique_groups; gi++) {
1001 const char *current_group = unique_groups[gi];
1002 // Add newline before each group except the first
1003 if (gi > 0) {
1004 fprintf(stream, "\n");
1005 }
1006 fprintf(stream, "%s\n", colored_string(LOG_COLOR_DEBUG, current_group));
1007
1008 for (size_t i = 0; i < config->num_descriptors; i++) {
1009 const option_descriptor_t *desc = &config->descriptors[i];
1010 if (!option_applies_to_mode(desc, mode, for_binary_help) || !desc->group ||
1011 strcmp(desc->group, current_group) != 0) {
1012 continue;
1013 }
1014
1015 // Build option string (flag part) with separate coloring for short and long flags
1016 char colored_option_str[BUFFER_SIZE_MEDIUM] = "";
1017 int colored_len = 0;
1018
1019 // Short name and long name with separate coloring
1020 if (desc->short_name) {
1021 char short_flag[16];
1022 safe_snprintf(short_flag, sizeof(short_flag), "-%c", desc->short_name);
1023 char long_flag[BUFFER_SIZE_SMALL];
1024 safe_snprintf(long_flag, sizeof(long_flag), "--%s", desc->long_name);
1025 // Color short flag, plain comma-space, color long flag
1026 colored_len +=
1027 safe_snprintf(colored_option_str + colored_len, sizeof(colored_option_str) - colored_len, "%s, %s",
1029 } else {
1030 char long_flag[BUFFER_SIZE_SMALL];
1031 safe_snprintf(long_flag, sizeof(long_flag), "--%s", desc->long_name);
1032 colored_len += safe_snprintf(colored_option_str + colored_len, sizeof(colored_option_str) - colored_len, "%s",
1033 colored_string(LOG_COLOR_WARN, long_flag));
1034 }
1035
1036 // Value placeholder (colored green)
1037 if (desc->type != OPTION_TYPE_BOOL && desc->type != OPTION_TYPE_ACTION) {
1038 colored_len += safe_snprintf(colored_option_str + colored_len, sizeof(colored_option_str) - colored_len, " ");
1039 const char *placeholder = get_option_help_placeholder_str(desc);
1040 if (placeholder[0] != '\0') {
1041 colored_len += safe_snprintf(colored_option_str + colored_len, sizeof(colored_option_str) - colored_len, "%s",
1042 colored_string(LOG_COLOR_INFO, placeholder));
1043 }
1044 }
1045
1046 // Build description with defaults and env vars
1047 char desc_str[1024] = "";
1048 int desc_len = 0;
1049
1050 if (desc->help_text) {
1051 desc_len += safe_snprintf(desc_str + desc_len, sizeof(desc_str) - desc_len, "%s", desc->help_text);
1052 }
1053
1054 // Skip adding default if the description already mentions it
1055 bool description_has_default =
1056 desc->help_text && (strstr(desc->help_text, "(default:") || strstr(desc->help_text, "=default)"));
1057
1058 if (desc->default_value && !description_has_default) {
1059 char default_buf[256];
1060 int default_len = format_option_default_value_str(desc, default_buf, sizeof(default_buf));
1061 if (default_len > 0) {
1062 desc_len +=
1063 safe_snprintf(desc_str + desc_len, sizeof(desc_str) - desc_len, " (%s %s)",
1064 colored_string(LOG_COLOR_FATAL, "default:"), colored_string(LOG_COLOR_FATAL, default_buf));
1065 }
1066 }
1067
1068 if (desc->required) {
1069 desc_len += safe_snprintf(desc_str + desc_len, sizeof(desc_str) - desc_len, " [REQUIRED]");
1070 }
1071
1072 layout_print_two_column_row(stream, colored_option_str, desc_str, max_col_width, term_width, 2);
1073 }
1074 }
1075
1076 // Cleanup
1077 SAFE_FREE(unique_groups);
1078}
const char * get_option_help_placeholder_str(const option_descriptor_t *desc)
Get help placeholder string for an option type.
Definition builder.c:43
int format_option_default_value_str(const option_descriptor_t *desc, char *buf, size_t bufsize)
Format option default value as a string.
Definition builder.c:83
bool option_applies_to_mode(const option_descriptor_t *desc, asciichat_mode_t mode, bool for_binary_help)
Check if an option applies to a specific mode using bitmask.
Definition builder.c:177
#define SAFE_GETENV(name)
Definition common.h:434
@ LOG_COLOR_DEBUG
Definition log/log.h:132
@ MODE_DISCOVERY
Discovery mode - participant that can dynamically become host.
asciichat_error_t terminal_get_size(terminal_size_t *size)
Get terminal size.
int errno
int options_config_calculate_max_col_width(const options_config_t *config)
Calculate global max column width across all help sections.
Definition help.c:77
void layout_print_two_column_row(FILE *stream, const char *first_column, const char *second_column, int first_col_len, int term_width, int continuation_indent_extra)
Print two-column row with automatic wrapping.
Definition layout.c:220
const char * help_text
Description for –help.
Definition builder.h:251
const void * default_value
Pointer to default value (or NULL if required)
Definition builder.h:258
bool required
If true, option must be provided.
Definition builder.h:259
option_type_t type
Value type.
Definition builder.h:247
Terminal size structure.
Definition terminal.h:163
int cols
Number of columns (width) in terminal.
Definition terminal.h:165

References ASCIICHAT_OK, BUFFER_SIZE_MEDIUM, BUFFER_SIZE_SMALL, colored_string(), terminal_size_t::cols, option_descriptor_t::default_value, options_config_t::descriptors, errno, ERROR_INVALID_PARAM, format_option_default_value_str(), get_option_help_placeholder_str(), option_descriptor_t::group, option_descriptor_t::help_text, layout_print_two_column_row(), LOG_COLOR_DEBUG, LOG_COLOR_FATAL, LOG_COLOR_INFO, LOG_COLOR_WARN, log_error, option_descriptor_t::long_name, MODE_DISCOVERY, options_config_t::num_descriptors, option_applies_to_mode(), OPTION_TYPE_ACTION, OPTION_TYPE_BOOL, options_config_calculate_max_col_width(), option_descriptor_t::required, SAFE_FREE, SAFE_GETENV, SAFE_MALLOC, safe_snprintf(), SET_ERRNO, option_descriptor_t::short_name, terminal_get_size(), and option_descriptor_t::type.

Referenced by options_config_print_options_sections(), and options_print_help_for_mode().

◆ options_config_print_usage()

void options_config_print_usage ( const options_config_t *  config,
FILE *  stream 
)

Print usage/help text.

Generates formatted help with grouped options.

Parameters
configOptions configuration
streamOutput stream (stdout or stderr)

Definition at line 706 of file help.c.

706 {
707 if (!config || !stream)
708 return;
709
710 // Detect terminal width from COLUMNS env var or use default
711 int term_width = 80;
712 const char *cols_env = SAFE_GETENV("COLUMNS");
713 if (cols_env) {
714 char *endptr;
715 errno = 0;
716 long cols = strtol(cols_env, &endptr, 10);
717 if (*endptr == '\0' && errno == 0 && cols > 40 && cols <= INT_MAX)
718 term_width = (int)cols;
719 else if (*endptr != '\0' || errno != 0 || cols <= 40)
720 log_error("options_config_print_binary_help: Invalid COLUMNS value: %s", cols_env);
721 }
722
723 // Binary-level help uses MODE_DISCOVERY internally
725 bool for_binary_help = true;
726
727 // Calculate per-section column widths (each section is independent, capped at 75)
728 int usage_max_col_width = calculate_section_max_col_width(config, "usage", mode, for_binary_help);
729 int modes_max_col_width = calculate_section_max_col_width(config, "modes", mode, for_binary_help);
730 int examples_max_col_width = calculate_section_max_col_width(config, "examples", mode, for_binary_help);
731 int options_max_col_width = calculate_section_max_col_width(config, "options", mode, for_binary_help);
732
733 // Print programmatically generated sections (USAGE, MODES, MODE-OPTIONS, EXAMPLES)
734 print_usage_section(config, stream, term_width, usage_max_col_width);
735 print_modes_section(config, stream, term_width, modes_max_col_width);
736 print_mode_options_section(stream, term_width, 40); // Keep reasonable width for mode-options
737 print_examples_section(config, stream, term_width, examples_max_col_width, MODE_SERVER, true);
738
739 // Build list of unique groups in order of first appearance
740
741 const char **unique_groups = SAFE_MALLOC(config->num_descriptors * sizeof(const char *), const char **);
742 size_t num_unique_groups = 0;
743
744 for (size_t i = 0; i < config->num_descriptors; i++) {
745 const option_descriptor_t *desc = &config->descriptors[i];
746 // Filter by mode_bitmask - for binary help, show binary options
747 if (!option_applies_to_mode(desc, MODE_SERVER, for_binary_help) || !desc->group) {
748 continue;
749 }
750
751 // Check if this group is already in the list
752 bool group_exists = false;
753 for (size_t j = 0; j < num_unique_groups; j++) {
754 if (unique_groups[j] && strcmp(unique_groups[j], desc->group) == 0) {
755 group_exists = true;
756 break;
757 }
758 }
759
760 // Add new group to list
761 if (!group_exists && num_unique_groups < config->num_descriptors) {
762 unique_groups[num_unique_groups++] = desc->group;
763 }
764 }
765
766 // Print options grouped by group name
767 for (size_t g = 0; g < num_unique_groups; g++) {
768 const char *current_group = unique_groups[g];
769 // Only add leading newline for groups after the first one
770 if (g > 0) {
771 fprintf(stream, "\n");
772 }
773 fprintf(stream, "%s\n", colored_string(LOG_COLOR_DEBUG, current_group));
774
775 // Print all options in this group
776 for (size_t i = 0; i < config->num_descriptors; i++) {
777 const option_descriptor_t *desc = &config->descriptors[i];
778
779 // Skip if not in current group or if doesn't apply to mode
780 if (!option_applies_to_mode(desc, MODE_SERVER, for_binary_help) || !desc->group ||
781 strcmp(desc->group, current_group) != 0) {
782 continue;
783 }
784
785 // Build option string with separate coloring for short and long flags
786 char option_str[BUFFER_SIZE_MEDIUM] = "";
787 int option_len = 0;
788
789 // Short name and long name with separate coloring
790 if (desc->short_name) {
791 char short_flag[16];
792 safe_snprintf(short_flag, sizeof(short_flag), "-%c", desc->short_name);
793 char long_flag[BUFFER_SIZE_SMALL];
794 safe_snprintf(long_flag, sizeof(long_flag), "--%s", desc->long_name);
795 // Color short flag, plain comma-space, color long flag
796 option_len +=
797 safe_snprintf(option_str + option_len, sizeof(option_str) - option_len, "%s, %s",
799 } else {
800 char long_flag[BUFFER_SIZE_SMALL];
801 safe_snprintf(long_flag, sizeof(long_flag), "--%s", desc->long_name);
802 option_len += safe_snprintf(option_str + option_len, sizeof(option_str) - option_len, "%s",
803 colored_string(LOG_COLOR_WARN, long_flag));
804 }
805
806 // Value placeholder (colored green)
807 if (desc->type != OPTION_TYPE_BOOL && desc->type != OPTION_TYPE_ACTION) {
808 option_len += safe_snprintf(option_str + option_len, sizeof(option_str) - option_len, " ");
809 const char *placeholder = get_option_help_placeholder_str(desc);
810 if (placeholder[0] != '\0') {
811 option_len += safe_snprintf(option_str + option_len, sizeof(option_str) - option_len, "%s",
812 colored_string(LOG_COLOR_INFO, placeholder));
813 }
814 }
815
816 // Build description string (plain text, colors applied when printing)
817 char desc_str[BUFFER_SIZE_MEDIUM] = "";
818 int desc_len = 0;
819
820 if (desc->help_text) {
821 desc_len += safe_snprintf(desc_str + desc_len, sizeof(desc_str) - desc_len, "%s", desc->help_text);
822 }
823
824 // Skip adding default if the description already mentions it
825 bool description_has_default =
826 desc->help_text && (strstr(desc->help_text, "(default:") || strstr(desc->help_text, "=default)"));
827
828 if (desc->default_value && !description_has_default) {
829 char default_buf[256];
830 int default_len = format_option_default_value_str(desc, default_buf, sizeof(default_buf));
831 if (default_len > 0) {
832 desc_len +=
833 safe_snprintf(desc_str + desc_len, sizeof(desc_str) - desc_len, " (%s %s)",
835 }
836 }
837
838 if (desc->required) {
839 desc_len += safe_snprintf(desc_str + desc_len, sizeof(desc_str) - desc_len, " [REQUIRED]");
840 }
841
842 // Use layout function with section-specific column width for consistent alignment
843 // option_str already contains colored_string() results, so pass it directly
844 layout_print_two_column_row(stream, option_str, desc_str, options_max_col_width, term_width, 2);
845 }
846 }
847
848 // Cleanup
849 SAFE_FREE(unique_groups);
850
851 fprintf(stream, "\n");
852}
asciichat_mode_t
Mode type for options parsing.
@ MODE_SERVER
Server mode - network server options.

References BUFFER_SIZE_MEDIUM, BUFFER_SIZE_SMALL, colored_string(), option_descriptor_t::default_value, options_config_t::descriptors, errno, format_option_default_value_str(), get_option_help_placeholder_str(), option_descriptor_t::group, option_descriptor_t::help_text, layout_print_two_column_row(), LOG_COLOR_DEBUG, LOG_COLOR_FATAL, LOG_COLOR_INFO, LOG_COLOR_WARN, log_error, option_descriptor_t::long_name, MODE_DISCOVERY, MODE_SERVER, options_config_t::num_descriptors, option_applies_to_mode(), OPTION_TYPE_ACTION, OPTION_TYPE_BOOL, option_descriptor_t::required, SAFE_FREE, SAFE_GETENV, SAFE_MALLOC, safe_snprintf(), option_descriptor_t::short_name, and option_descriptor_t::type.

◆ options_config_print_usage_section()

void options_config_print_usage_section ( const options_config_t *  config,
FILE *  stream 
)

Print only the USAGE section.

Prints just the USAGE section. Useful for inserting other content (like positional argument examples) between USAGE and other sections.

Parameters
configOptions configuration
streamOutput stream (stdout or stderr)

Splits usage printing from other sections to allow custom content (like positional argument examples) to be inserted between USAGE and other sections.

Definition at line 860 of file help.c.

860 {
861 if (!config || !stream)
862 return;
863
864 // Detect terminal width from COLUMNS env var or use default
865 int term_width = 80;
866 const char *cols_env = SAFE_GETENV("COLUMNS");
867 if (cols_env) {
868 char *endptr;
869 errno = 0;
870 long cols = strtol(cols_env, &endptr, 10);
871 if (*endptr == '\0' && errno == 0 && cols > 40 && cols <= INT_MAX)
872 term_width = (int)cols;
873 else if (*endptr != '\0' || errno != 0 || cols <= 40)
874 log_error("options_config_print_binary_help: Invalid COLUMNS value: %s", cols_env);
875 }
876
877 // Calculate global max column width across all sections for consistent alignment
878 int max_col_width = options_config_calculate_max_col_width(config);
879
880 // Print only USAGE section
881 print_usage_section(config, stream, term_width, max_col_width);
882}

References errno, log_error, options_config_calculate_max_col_width(), and SAFE_GETENV.

◆ options_config_set_defaults()

asciichat_error_t options_config_set_defaults ( const options_config_t *  config,
void *  options_struct 
)

Set default values in options struct.

Sets defaults from descriptors, checking environment variables for options with env_var_name set.

Parameters
configOptions configuration
options_structOptions struct to initialize
Returns
ASCIICHAT_OK on success, error code on failure

Definition at line 1400 of file builder.c.

1400 {
1401 if (!config || !options_struct) {
1402 return SET_ERRNO(ERROR_INVALID_PARAM, "Config or options struct is NULL");
1403 }
1404
1405 char *base = (char *)options_struct;
1406
1407 for (size_t i = 0; i < config->num_descriptors; i++) {
1408 const option_descriptor_t *desc = &config->descriptors[i];
1409 void *field = base + desc->offset;
1410
1411 // Check environment variable first (primary)
1412 const char *env_value = NULL;
1413 if (desc->env_var_name) {
1414 env_value = SAFE_GETENV(desc->env_var_name);
1415 }
1416 // Check shorter alias names as fallback if primary not set
1417 if (!env_value) {
1418 if (desc->env_var_name && strcmp(desc->env_var_name, "ASCII_CHAT_PORT") == 0) {
1419 env_value = SAFE_GETENV("PORT");
1420 } else if (desc->env_var_name && strcmp(desc->env_var_name, "ASCII_CHAT_WEBSOCKET_PORT") == 0) {
1421 env_value = SAFE_GETENV("WS_PORT");
1422 }
1423 }
1424
1425 // Use handler registry to apply environment variables and defaults
1426 if (desc->type >= 0 && desc->type < (int)(NUM_OPTION_TYPES)) {
1427 if (g_builder_handlers[desc->type].apply_env) {
1428 g_builder_handlers[desc->type].apply_env(field, env_value, desc);
1429 }
1430 } else if (desc->type == OPTION_TYPE_CALLBACK && desc->parse_fn && desc->default_value) {
1431 // Special handling for callbacks with parse_fn
1432 char *error_msg = NULL;
1433 desc->parse_fn(NULL, field, &error_msg);
1434 if (error_msg) {
1435 free(error_msg);
1436 }
1437 }
1438 }
1439
1440 return ASCIICHAT_OK;
1441}
const option_builder_handler_t g_builder_handlers[]
#define NUM_OPTION_TYPES
void(* apply_env)(void *field, const char *env_value, const option_descriptor_t *desc)
size_t offset
offsetof(struct, field) - where to store value
Definition builder.h:248
const char * env_var_name
Environment variable fallback (or NULL)
Definition builder.h:260
bool(* parse_fn)(const char *arg, void *dest, char **error_msg)
Definition builder.h:266

References option_builder_handler_t::apply_env, ASCIICHAT_OK, option_descriptor_t::default_value, options_config_t::descriptors, option_descriptor_t::env_var_name, ERROR_INVALID_PARAM, g_builder_handlers, options_config_t::num_descriptors, NUM_OPTION_TYPES, option_descriptor_t::offset, OPTION_TYPE_CALLBACK, option_descriptor_t::parse_fn, SAFE_GETENV, SET_ERRNO, and option_descriptor_t::type.

Referenced by options_init().

◆ options_config_validate()

asciichat_error_t options_config_validate ( const options_config_t *  config,
const void *  options_struct,
char **  error_message 
)

Validate options struct.

Checks:

  • Required fields are set
  • Dependencies are satisfied
  • Custom validators pass
Parameters
configOptions configuration
options_structOptions struct to validate
error_messageOptional: receives detailed error message (must free)
Returns
ASCIICHAT_OK if valid, error code otherwise

Definition at line 1840 of file builder.c.

1841 {
1842 if (!config || !options_struct) {
1843 return SET_ERRNO(ERROR_INVALID_PARAM, "Config or options struct is NULL");
1844 }
1845
1846 const char *base = (const char *)options_struct;
1847
1848 // Check required fields
1849 for (size_t i = 0; i < config->num_descriptors; i++) {
1850 const option_descriptor_t *desc = &config->descriptors[i];
1851 if (!desc->required)
1852 continue;
1853
1854 const void *field = base + desc->offset;
1855 // Use handler registry to check if option is set (for required field validation)
1856 bool is_set = true;
1857 if (desc->type >= 0 && desc->type < (int)(NUM_OPTION_TYPES)) {
1858 if (g_builder_handlers[desc->type].is_set) {
1859 is_set = g_builder_handlers[desc->type].is_set(field, desc);
1860 }
1861 }
1862
1863 if (!is_set) {
1864 if (error_message) {
1865 int asprintf_result;
1866 if (desc->env_var_name) {
1867 asprintf_result = asprintf(error_message, "Required option --%s is not set (set %s env var or use --%s)",
1868 desc->long_name, desc->env_var_name, desc->long_name);
1869 } else {
1870 asprintf_result = asprintf(error_message, "Required option --%s is not set", desc->long_name);
1871 }
1872 if (asprintf_result < 0) {
1873 log_error("Failed to format error message for missing required option --%s", desc->long_name);
1874 *error_message = NULL;
1875 }
1876 }
1877 return ERROR_USAGE;
1878 }
1879 }
1880
1881 // Check dependencies
1882 for (size_t i = 0; i < config->num_dependencies; i++) {
1883 const option_dependency_t *dep = &config->dependencies[i];
1884
1885 bool option_is_set = is_option_set(config, options_struct, dep->option_name);
1886 bool depends_is_set = is_option_set(config, options_struct, dep->depends_on);
1887
1888 switch (dep->type) {
1890 if (option_is_set && !depends_is_set) {
1891 if (error_message) {
1892 if (dep->error_message) {
1893 *error_message = platform_strdup(dep->error_message);
1894 } else {
1895 int asprintf_result =
1896 asprintf(error_message, "Option --%s requires --%s to be set", dep->option_name, dep->depends_on);
1897 if (asprintf_result < 0) {
1898 log_error("Failed to format error message for option dependency: --%s requires --%s", dep->option_name,
1899 dep->depends_on);
1900 *error_message = NULL;
1901 }
1902 }
1903 }
1904 return ERROR_USAGE;
1905 }
1906 break;
1907
1909 if (option_is_set && depends_is_set) {
1910 if (error_message) {
1911 if (dep->error_message) {
1912 *error_message = platform_strdup(dep->error_message);
1913 } else {
1914 int asprintf_result =
1915 asprintf(error_message, "Option --%s conflicts with --%s", dep->option_name, dep->depends_on);
1916 if (asprintf_result < 0) {
1917 log_error("Failed to format error message for option conflict: --%s conflicts with --%s",
1918 dep->option_name, dep->depends_on);
1919 *error_message = NULL;
1920 }
1921 }
1922 }
1923 return ERROR_USAGE;
1924 }
1925 break;
1926
1927 case DEPENDENCY_IMPLIES:
1928 // Implies is handled during parsing, not validation
1929 break;
1930 }
1931 }
1932
1933 // Run custom validators
1934 for (size_t i = 0; i < config->num_descriptors; i++) {
1935 const option_descriptor_t *desc = &config->descriptors[i];
1936 if (!desc->validate)
1937 continue;
1938
1939 char *custom_error = NULL;
1940 if (!desc->validate(options_struct, &custom_error)) {
1941 if (error_message) {
1942 *error_message = custom_error;
1943 } else {
1944 free(custom_error);
1945 }
1946 return ERROR_USAGE;
1947 }
1948 }
1949
1950 // Cross-field validation: Check for conflicting color options
1951 // Cannot use --color with --color-mode none
1952 const options_t *opts = (const options_t *)options_struct;
1953 if (opts->color && opts->color_mode == TERM_COLOR_NONE) {
1954 if (error_message) {
1955 int asprintf_result =
1956 asprintf(error_message, "Option --color cannot be used with --color-mode=none (conflicting color settings)");
1957 if (asprintf_result < 0) {
1958 log_error("Failed to format error message for color option conflict");
1959 *error_message = NULL;
1960 }
1961 }
1962 return ERROR_USAGE;
1963 }
1964
1965 return ASCIICHAT_OK;
1966}
bool is_option_set(const options_config_t *config, const void *options_struct, const char *option_name)
Check if an option is set (has non-default value)
Definition builder.c:356
#define asprintf
Definition builder.c:24
bool is_set
bool(* is_set)(const void *field, const option_descriptor_t *desc)
const char * depends_on
The option it depends on.
Definition builder.h:334
const char * error_message
Custom error message (optional)
Definition builder.h:335
dependency_type_t type
Type of dependency.
Definition builder.h:333
bool(* validate)(const void *options_struct, char **error_msg)
Definition builder.h:263
terminal_color_mode_t color_mode
Color mode (auto/none/16/256/truecolor)
int color
Color setting (COLOR_SETTING_AUTO/TRUE/FALSE)
@ TERM_COLOR_NONE
No color support (monochrome terminal)
Definition terminal.h:582

References ASCIICHAT_OK, asprintf, options_state::color, options_state::color_mode, options_config_t::dependencies, DEPENDENCY_CONFLICTS, DEPENDENCY_IMPLIES, DEPENDENCY_REQUIRES, option_dependency_t::depends_on, options_config_t::descriptors, option_descriptor_t::env_var_name, ERROR_INVALID_PARAM, option_dependency_t::error_message, ERROR_USAGE, g_builder_handlers, is_option_set(), option_builder_handler_t::is_set, is_set, log_error, option_descriptor_t::long_name, options_config_t::num_dependencies, options_config_t::num_descriptors, NUM_OPTION_TYPES, option_descriptor_t::offset, option_dependency_t::option_name, platform_strdup(), option_descriptor_t::required, SET_ERRNO, TERM_COLOR_NONE, option_descriptor_t::type, option_dependency_t::type, and option_descriptor_t::validate.

Referenced by validate_options_and_report().

◆ options_format_default_value()

int options_format_default_value ( option_type_t  type,
const void *  default_value,
char *  buf,
size_t  bufsize 
)

Format option default value to string.

Formats default value according to option type:

  • BOOL: "true" or "false"
  • INT: "%d" format
  • STRING: raw string (caller applies escaping if needed)
  • DOUBLE: "%.2f" format
Parameters
typeOption type enum value
default_valuePointer to default value (type-specific)
bufOutput buffer
bufsizeSize of output buffer
Returns
Number of characters written, or 0 on error

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

576 {
577 if (!default_value || !buf || bufsize == 0) {
578 return 0;
579 }
580
581 switch (type) {
582 case OPTION_TYPE_BOOL:
583 return safe_snprintf(buf, bufsize, "%s", *(const bool *)default_value ? "true" : "false");
584 case OPTION_TYPE_INT: {
585 int int_val = 0;
586 memcpy(&int_val, default_value, sizeof(int));
587 return safe_snprintf(buf, bufsize, "%d", int_val);
588 }
590 return safe_snprintf(buf, bufsize, "%s", *(const char *const *)default_value);
591 case OPTION_TYPE_DOUBLE: {
592 double double_val = 0.0;
593 memcpy(&double_val, default_value, sizeof(double));
594 return safe_snprintf(buf, bufsize, "%.2f", double_val);
595 }
596 default:
597 // OPTION_TYPE_CALLBACK and OPTION_TYPE_ACTION don't have defaults to display
598 return 0;
599 }
600}

References OPTION_TYPE_BOOL, OPTION_TYPE_DOUBLE, OPTION_TYPE_INT, OPTION_TYPE_STRING, and safe_snprintf().

Referenced by format_option_default_value_str(), and manpage_content_generate_options().

◆ options_get_type_placeholder()

const char * options_get_type_placeholder ( option_type_t  type)

Get placeholder string for option type.

Returns standardized placeholder strings for use in help text and man pages:

  • INT/DOUBLE → "NUM"
  • STRING → "STR"
  • CALLBACK → "VAL"
  • BOOL/ACTION → "" (empty string)
Parameters
typeOption type enum value
Returns
Pointer to string literal (never NULL, may be empty)

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

558 {
559 switch (type) {
560 case OPTION_TYPE_INT:
561 return "INTEGER";
563 return "NUMBER";
565 return "STRING";
567 return "VALUE";
568 case OPTION_TYPE_BOOL:
569 return "BOOLEAN";
571 default:
572 return "";
573 }
574}

References OPTION_TYPE_ACTION, OPTION_TYPE_BOOL, OPTION_TYPE_CALLBACK, OPTION_TYPE_DOUBLE, OPTION_TYPE_INT, and OPTION_TYPE_STRING.

Referenced by get_option_help_placeholder_str(), and manpage_content_generate_options().

◆ options_preset_unified()

options_config_t * options_preset_unified ( const char *  program_name,
const char *  description 
)

Get binary-level options preset.

Binary options are parsed BEFORE mode selection. Includes: –help, –version, –log-file, –log-level, etc.

Parameters
program_nameOptional program name (defaults to "ascii-chat")
descriptionOptional program description
Returns
Preset config (caller must free after use)

Get binary-level options preset.

This is the single source of truth for all options. Each option has a mode_bitmask indicating which modes it applies to. The config includes all options, and validation happens after parsing based on detected mode.

Definition at line 51 of file presets.c.

51 {
53 if (!b) {
54 SET_ERRNO(ERROR_MEMORY, "Failed to create options builder");
55 return NULL;
56 }
57
58 b->program_name = program_name ? program_name : "ascii-chat";
59 b->description = description ? description : "Video chat in your terminal";
60
61 // Add ALL options from registry (binary + all modes)
63 if (err != ASCIICHAT_OK) {
65 SET_ERRNO(err, "Failed to add all options to builder");
66 return NULL;
67 }
68
69 // Add positional arguments for each mode
70 // These allow parsing of positional arguments like "192.168.1.1" for client mode
71 // and "[bind-address]" for server mode
72
73 // Generate random session strings for examples
74 // Use static buffers so they persist after the function returns
75 static char session_buf1[SESSION_STRING_BUFFER_SIZE];
76 static char session_buf2[SESSION_STRING_BUFFER_SIZE];
77 static char session_buf3[SESSION_STRING_BUFFER_SIZE];
78 static char session_buf4[SESSION_STRING_BUFFER_SIZE];
79 static char session_buf5[SESSION_STRING_BUFFER_SIZE];
80 static char session_buf6[SESSION_STRING_BUFFER_SIZE];
81 static char session_buf7[SESSION_STRING_BUFFER_SIZE];
82 static char session_buf8[SESSION_STRING_BUFFER_SIZE];
83 static char session_buf9[SESSION_STRING_BUFFER_SIZE];
84 static char session_buf10[SESSION_STRING_BUFFER_SIZE];
85
86 // Fallback strings if generation fails
87 char *example_session_string1 = "adjective-noun-noun";
88 char *example_session_string2 = "adjective-noun-noun";
89 char *example_session_string3 = "adjective-noun-noun";
90 char *example_session_string4 = "adjective-noun-noun";
91 char *example_session_string5 = "adjective-noun-noun";
92 char *example_session_string6 = "adjective-noun-noun";
93 char *example_session_string7 = "adjective-noun-noun";
94 char *example_session_string8 = "adjective-noun-noun";
95 char *example_session_string9 = "adjective-noun-noun";
96 char *example_session_string10 = "adjective-noun-noun";
97
98 // Generate session strings for examples (sodium_init called as needed by acds_string_generate)
99 acds_string_generate(session_buf1, sizeof(session_buf1));
100 if (session_buf1[0] != '\0') {
101 example_session_string1 = session_buf1;
102 }
103 acds_string_generate(session_buf2, sizeof(session_buf2));
104 if (session_buf2[0] != '\0') {
105 example_session_string2 = session_buf2;
106 }
107 acds_string_generate(session_buf3, sizeof(session_buf3));
108 if (session_buf3[0] != '\0') {
109 example_session_string3 = session_buf3;
110 }
111 acds_string_generate(session_buf4, sizeof(session_buf4));
112 if (session_buf4[0] != '\0') {
113 example_session_string4 = session_buf4;
114 }
115 acds_string_generate(session_buf5, sizeof(session_buf5));
116 if (session_buf5[0] != '\0') {
117 example_session_string5 = session_buf5;
118 }
119 acds_string_generate(session_buf6, sizeof(session_buf6));
120 if (session_buf6[0] != '\0') {
121 example_session_string6 = session_buf6;
122 }
123 acds_string_generate(session_buf7, sizeof(session_buf7));
124 if (session_buf7[0] != '\0') {
125 example_session_string7 = session_buf7;
126 }
127 acds_string_generate(session_buf8, sizeof(session_buf8));
128 if (session_buf8[0] != '\0') {
129 example_session_string8 = session_buf8;
130 }
131 acds_string_generate(session_buf9, sizeof(session_buf9));
132 if (session_buf9[0] != '\0') {
133 example_session_string9 = session_buf9;
134 }
135 acds_string_generate(session_buf10, sizeof(session_buf10));
136 if (session_buf10[0] != '\0') {
137 example_session_string10 = session_buf10;
138 }
139
140 // Build session string examples dynamically for discovery mode
141 // These appear at the beginning of the examples section, right after "start new session"
142 static char example_buf1[SESSION_STRING_BUFFER_SIZE];
143 static char example_buf2[SESSION_STRING_BUFFER_SIZE];
144 static char example_buf3[SESSION_STRING_BUFFER_SIZE];
145 static char example_buf4[SESSION_STRING_BUFFER_SIZE];
146 static char example_buf5[SESSION_STRING_BUFFER_SIZE];
147 static char example_buf6[SESSION_STRING_BUFFER_SIZE];
148 static char example_buf7[SESSION_STRING_BUFFER_SIZE];
149 static char example_buf8[SESSION_STRING_BUFFER_SIZE];
150 static char example_buf9[SESSION_STRING_BUFFER_SIZE];
151 static char example_buf10[SESSION_STRING_BUFFER_SIZE];
152
153 safe_snprintf(example_buf1, sizeof(example_buf1), "%s", example_session_string1);
154 safe_snprintf(example_buf2, sizeof(example_buf2), "%s", example_session_string2);
155 safe_snprintf(example_buf3, sizeof(example_buf3), "%s", example_session_string3);
156 safe_snprintf(example_buf4, sizeof(example_buf4), "%s", example_session_string4);
157 safe_snprintf(example_buf5, sizeof(example_buf5), "%s", example_session_string5);
158 safe_snprintf(example_buf6, sizeof(example_buf6), "%s", example_session_string6);
159 safe_snprintf(example_buf7, sizeof(example_buf7), "%s", example_session_string7);
160 safe_snprintf(example_buf8, sizeof(example_buf8), "%s", example_session_string8);
161 safe_snprintf(example_buf9, sizeof(example_buf9), "%s", example_session_string9);
162 safe_snprintf(example_buf10, sizeof(example_buf10), "%s", example_session_string10);
163
164 // Client mode: [address] - can be IP, hostname, hostname:port, or WebSocket URL
165 static const char *client_examples[] = {"localhost",
166 "ascii-chat.com",
167 "0.0.0.0",
168 "::",
169 "192.168.1.1:8080",
170 "[2001:db8::42]:27224",
171 "233.27.48.203:27224",
172 "62fb:759e:2bce:21d7:9e5d:13f8:3c11:5084:27224",
173 "ws://example.com:8080",
174 "wss://secure.example.com:443"};
175 // Discovery mode: [session-string] - session string or empty to start new session
176 // Use simple static examples for positional arguments section (dynamic strings shown in examples section)
177 static const char *discovery_examples[] = {
178 "(empty/unset) start new session. generates a session string to give someone to connect to you.",
179 (const char *)example_buf7, (const char *)example_buf8, (const char *)example_buf9, (const char *)example_buf10};
181 b, "session-string", "(optional) Random three words in format adjective-noun-noun that connect you to a call.",
182 false, "POSITIONAL ARGUMENTS", discovery_examples, ARRAY_SIZE(discovery_examples), OPTION_MODE_DISCOVERY,
184
185 // Server and Discovery Service modes: [bind-address] [bind-address] - can be IP or hostname, up to 2 for IPv4/IPv6
186 static const char *server_examples[] = {"localhost",
187 "ascii-chat.com",
188 "0.0.0.0",
189 "::",
190 "234.50.188.236",
191 "9631:54e7:5b5c:80dc:0f62:1f01:7ccf:5512",
192 "105.137.19.11 3a08:7276:ccb4:7b31:e934:5330:9b3a:9598",
193 "::1 192.168.1.100"};
194 options_builder_add_positional(b, "bind-address",
195 "(optional) 0-2 addresses for a server to bind to, one IPv4 and the other IPv6.",
196 false, "POSITIONAL ARGUMENTS", server_examples, ARRAY_SIZE(server_examples),
198
199 options_builder_add_positional(b, "address", "(optional) Server address for client to connect to.", false,
200 "POSITIONAL ARGUMENTS", client_examples, ARRAY_SIZE(client_examples),
202
203 // Mirror mode: [file|url] - file path or URL to stream
204 static const char *mirror_media_examples[] = {"/path/to/video.mp4", "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
205 "https://example.com/stream.m3u8"};
206 options_builder_add_positional(b, "file|url",
207 "(optional) Media file path or URL to stream. If omitted, uses webcam. "
208 "Cannot be combined with --file or --url.",
209 false, "POSITIONAL ARGUMENTS", mirror_media_examples,
210 ARRAY_SIZE(mirror_media_examples), OPTION_MODE_MIRROR, parse_mirror_media);
211
212 // Add usage lines for all modes
213 options_builder_add_usage(b, NULL, NULL, true, "Start a new session (share the session string)");
214 options_builder_add_usage(b, NULL, "<session-string>", true, "Join an existing session");
215 options_builder_add_usage(b, NULL, "<mode>", true, "Run in a specific mode");
216 options_builder_add_usage(b, "server", "[bind-address] [bind-address]", true,
217 "Start server (can specify 0-2 bind addresses, one IPv4 and the other IPv6)");
218 options_builder_add_usage(b, "client", "[address]", true, "Connect to server (defaults to localhost:27224)");
219 options_builder_add_usage(b, "mirror", "[file|url]", true, "View local webcam or media file or URL as ASCII art");
220 options_builder_add_usage(b, "discovery-service", "[bind-address] [bind-address]", true,
221 "Start discovery service (can specify 0-2 bind addresses, one IPv4 and the other IPv6)");
222 options_builder_add_usage(b, NULL, "[mode] --help", false, "Show help for a specific mode");
223
224 // Add examples for binary-level help and discovery mode
225 options_builder_add_example(b, OPTION_MODE_BINARY, NULL, "Start new session (share the session string)", false);
226 options_builder_add_example(b, OPTION_MODE_BINARY, example_buf1, "Join a session using the session string", true);
227
228 // Build combined examples with session string + flags
229 static char combined_buf1[256];
230 static char combined_buf2[256];
231 static char combined_buf3[256];
232 static char combined_buf4[256];
233 static char combined_buf5[256];
234 safe_snprintf(combined_buf1, sizeof(combined_buf1), "%s --file video.mp4", example_buf3);
235 safe_snprintf(combined_buf2, sizeof(combined_buf2), "%s --url 'https://www.youtube.com/watch?v=dQw4w9WgXcQ'",
236 example_buf4);
237 safe_snprintf(combined_buf3, sizeof(combined_buf3), "%s -f -", example_buf5);
238 safe_snprintf(combined_buf4, sizeof(combined_buf4), "%s --palette-chars '@%%#*+=-:. '", example_buf6);
239 safe_snprintf(combined_buf5, sizeof(combined_buf5), "%s --discovery-service --discovery-service-port 27225",
240 example_buf2);
241
242 options_builder_add_example(b, OPTION_MODE_BINARY, combined_buf1, "Join session and stream from local video file",
243 false);
244 options_builder_add_example(b, OPTION_MODE_BINARY, combined_buf2, "Join session and stream from YouTube video",
245 false);
246 options_builder_add_example(b, OPTION_MODE_BINARY, combined_buf3, "Join session and stream media from stdin", false);
247 options_builder_add_example(b, OPTION_MODE_BINARY, combined_buf4, "Join session with custom ASCII palette characters",
248 false);
249 options_builder_add_example(b, OPTION_MODE_BINARY, combined_buf5, "Join session via custom discovery server", false);
250
251 // Add examples for server-like modes (server + discovery-service)
252 options_builder_add_example(b, OPTION_MODE_SERVER_LIKE, NULL, "Start on localhost (127.0.0.1 and ::1)", false);
253 options_builder_add_example(b, OPTION_MODE_SERVER_LIKE, "0.0.0.0", "Start on all IPv4 interfaces", false);
255 "0.0.0.0 ::", "Start on all IPv4 and IPv6 interfaces (dual-stack)", false);
256 options_builder_add_example(b, OPTION_MODE_SERVER_LIKE, "--port 8080", "Start on custom port", false);
257
258 // Server-specific examples
259 options_builder_add_example(b, OPTION_MODE_SERVER, "--key ~/.ssh/id_ed25519 --discovery",
260 "Start with identity key and discovery registration", false);
261
262 // Add examples for client-like modes (client + mirror)
263 options_builder_add_example(b, OPTION_MODE_CLIENT_LIKE, "--url 'https://youtu.be/7ynHVGCehoM'",
264 "Stream from YouTube URL (also supports RTSP, HTTP, and HTTPS URLs)", false);
265 options_builder_add_example(b, OPTION_MODE_CLIENT_LIKE, "--url 'https://www.twitch.tv/ludwig'",
266 "Stream Ludwig from videogames on Twitch", false);
267 options_builder_add_example(b, OPTION_MODE_CLIENT_LIKE, "-f video.mp4", "Stream from local video file", false);
268 options_builder_add_example(b, OPTION_MODE_CLIENT_LIKE, "--palette-chars '@%#*+=-:. '",
269 "Custom palette characters to use. UTF-8 is allowed.", false);
271 b, OPTION_MODE_CLIENT_LIKE, "--snapshot",
272 "Print ascii art for --snapshot-delay's value of seconds then print the last frame and exit. "
273 "In snapshot mode, --width, --height, and --color are NOT autodetected when piping stdin in or redirecting "
274 "output.",
275 false);
276 options_builder_add_example(b, OPTION_MODE_CLIENT_LIKE, "--color-filter cyan --palette cool",
277 "Apply cyan color filter and cool palette", false);
278
279 // Client-specific examples
280 options_builder_add_example(b, OPTION_MODE_CLIENT, "example.com", "Connect to remote server", false);
281 options_builder_add_example(b, OPTION_MODE_CLIENT, "example.com:8080", "Connect to remote server on custom port",
282 false);
283 options_builder_add_example(b, OPTION_MODE_CLIENT, "--color-mode mono --render-mode half-block --width 120",
284 "Connect with custom display options", false);
285
286 // Mirror-specific examples (unique to mirror mode)
288 b, OPTION_MODE_MIRROR, NULL,
289 "View the webcam or files or URLs as ASCII art. Like client mode but without network connectivity or a server.",
290 false);
291 options_builder_add_example(b, OPTION_MODE_MIRROR, "--color-mode mono", "View webcam in black and white", false);
292 options_builder_add_example(b, OPTION_MODE_MIRROR, "--color-filter green",
293 "View webcam with green monochromatic color filter", false);
294 options_builder_add_example(b, OPTION_MODE_MIRROR, "--matrix --color-filter rainbow",
295 "Matrix rain effect with rainbow colors cycling over 3.5s", false);
297 "Stream media from stdin (cat file.gif | ascii-chat mirror -f '-')", false);
298 options_builder_add_example_utility(b, OPTION_MODE_MIRROR, "cat video.avi | ascii-chat mirror -f '-' -l -s 00:30",
299 "Stream .avi from stdin, looped, seeking to 00:30", true);
300 options_builder_add_example(b, OPTION_MODE_MIRROR, "--file video.mov --seek 22:10",
301 "Start playback at exactly 22:10 (also works with --url)", false);
302 options_builder_add_example(b, OPTION_MODE_MIRROR, "-f 'https://youtu.be/LS9W8SO-Two' -S -D 0 -s 5:12",
303 "Print a single frame from a YouTube video at exactly 5:12 and exit", false);
304 options_builder_add_example(b, OPTION_MODE_MIRROR, "-S -D 0 | tee frame.txt | pbcopy",
305 "Capture single ASCII frame to clipboard (macOS) and file", false);
307 "View ASCII frame from clipboard (macOS)", true);
308
309 // Discovery-service specific examples
310 options_builder_add_example(b, OPTION_MODE_DISCOVERY_SVC, "--require-server-identity --require-client-identity",
311 "Enforce identity verification for all parties", false);
312
313 // Add mode descriptions
315 b, "default",
316 "When the ascii-chat binary is used without a mode, it operates as either client or server automatically and "
317 "uses the discovery-service to make peer connections with session strings");
318 options_builder_add_mode(b, "server", "Run as multi-client video chat server");
319 options_builder_add_mode(b, "client", "Run as video chat client (connect to server)");
320 options_builder_add_mode(b, "mirror", "View local media as ASCII art (no server)");
321 options_builder_add_mode(b, "discovery-service", "Secure P2P session signalling");
322
323 // Add custom help sections for interactive modes (client, mirror, and discovery)
324 options_builder_add_custom_section(b, "KEYBINDINGS",
325 "Available in ascii-chat client, mirror, and discovery modes. "
326 "While rendering, press '?' to display a keyboard shortcuts help menu showing:\n"
327 " - Available keybindings (?, Space, arrows, m, c, f, r)\n"
328 " - Current settings (volume, color mode, audio status, etc.)",
330
331 // Add environment variables section (all modes)
333 b, "ENVIRONMENT",
334 "All command-line flags that accept values have corresponding environment variables.\n"
335 " Format: ASCII_CHAT_<FLAG_NAME> where FLAG_NAME is uppercase with hyphens replaced by underscores\n"
336 " Example: --color-filter maps to ASCII_CHAT_COLOR_FILTER\n"
337 "\n"
338 " Configuration precedence (lowest to highest):\n"
339 " 1. Config file values (~/.ascii-chat/config.toml)\n"
340 " 2. Environment variables (ASCII_CHAT_*)\n"
341 " 3. Command-line flags (--flag-name)\n"
342 "\n"
343 " Additional environment variables are documented in the ascii-chat(1) man page.",
345
346 // Add common dependencies (these will be validated after parsing)
347 // Note: Dependencies are validated at runtime, so we add them here for documentation
348
349 // ============================================================================
350 // Media Source Conflicts
351 // ============================================================================
352 // URL conflicts: --url cannot be used with --file or --loop
354 "Option --url cannot be used with --file (--url takes priority)");
356 b, "url", "loop", "Option --url cannot be used with --loop (network streams cannot be looped)");
357
358 // ============================================================================
359 // Encryption & Authentication Conflicts
360 // ============================================================================
361 // Cannot use both --encrypt and --no-encrypt
362 options_builder_add_dependency_conflicts(b, "no-encrypt", "encrypt", "Cannot use --no-encrypt with --encrypt");
363 options_builder_add_dependency_conflicts(b, "no-encrypt", "key",
364 "Cannot use --no-encrypt with --key (key requires encryption)");
365 options_builder_add_dependency_conflicts(b, "no-encrypt", "password",
366 "Cannot use --no-encrypt with --password (password requires encryption)");
368 b, "no-encrypt", "client-keys",
369 "Cannot use --no-encrypt with --client-keys (key validation requires encryption)");
371 b, "no-encrypt", "server-key", "Cannot use --no-encrypt with --server-key (key validation requires encryption)");
372
373 // Cannot use --no-auth with authentication material (--key, --password, --client-keys, --server-key)
375 "Cannot use --no-auth with --key (key requires authentication)");
376 options_builder_add_dependency_conflicts(b, "no-auth", "password",
377 "Cannot use --no-auth with --password (password requires authentication)");
379 b, "no-auth", "client-keys", "Cannot use --no-auth with --client-keys (key list requires authentication)");
381 b, "no-auth", "server-key", "Cannot use --no-auth with --server-key (verification requires authentication)");
382
383 // Cannot use --key with --server-key (both server-side key options)
385 b, "key", "server-key",
386 "Cannot use --key with --server-key (--key is server identity, --server-key is client-side)");
387
388 // ============================================================================
389 // Compression Conflicts
390 // ============================================================================
391 // Cannot use --no-compress with --compression-level
392 options_builder_add_dependency_conflicts(b, "no-compress", "compression-level",
393 "Cannot use --no-compress with --compression-level");
394
395 // ============================================================================
396 // Audio Encoding Conflicts
397 // ============================================================================
398 // Cannot use both --encode-audio and --no-encode-audio
399 options_builder_add_dependency_conflicts(b, "encode-audio", "no-encode-audio",
400 "Cannot use both --encode-audio and --no-encode-audio");
401
402 // ============================================================================
403 // Display & Screen Conflicts
404 // ============================================================================
405 options_builder_add_dependency_conflicts(b, "waveform", "matrix",
406 "Option --waveform cannot be used with --matrix");
407 options_builder_add_dependency_conflicts(b, "fft", "matrix", "Option --fft cannot be used with --matrix");
408 options_builder_add_dependency_conflicts(b, "waveform", "fft",
409 "Options --waveform and --fft cannot be used together");
410 options_builder_add_dependency_conflicts(b, "waveform", "audio",
411 "Option --waveform requires audio; remove --audio=false");
412 options_builder_add_dependency_conflicts(b, "fft", "audio", "Option --fft requires audio; remove --audio=false");
413 options_builder_add_dependency_conflicts(b, "waveform", "stretch",
414 "Option --waveform cannot be used with --stretch");
415 options_builder_add_dependency_conflicts(b, "fft", "stretch", "Option --fft cannot be used with --stretch");
416
417 // ============================================================================
418 // Requirements (dependencies that must be satisfied)
419 // ============================================================================
420 // --snapshot-delay requires --snapshot
421 options_builder_add_dependency_requires(b, "snapshot-delay", "snapshot",
422 "Option --snapshot-delay requires --snapshot");
423
424 // --loop requires --file (can't loop network streams)
425 options_builder_add_dependency_requires(b, "loop", "file", "Option --loop requires --file");
426
427 const options_config_t *config = options_builder_build(b);
429 return config;
430}
void options_builder_add_custom_section(options_builder_t *builder, const char *heading, const char *content, option_mode_bitmask_t mode_bitmask)
Add a custom help section.
Definition builder.c:1319
void options_builder_destroy(options_builder_t *builder)
Free options builder.
Definition builder.c:467
void options_builder_add_example_utility(options_builder_t *builder, uint32_t mode_bitmask, const char *args, const char *description, bool is_utility_command)
Add an example with utility command support.
Definition builder.c:1289
asciichat_error_t acds_string_generate(char *output, size_t output_size)
Generate random session string.
#define OPTION_MODE_CLIENT_LIKE
#define OPTION_MODE_SERVER_LIKE
Mode group macros for examples.
@ OPTION_MODE_ALL
All modes + binary.
@ OPTION_MODE_BINARY
Binary-level options (parsed before mode detection)
@ OPTION_MODE_CLIENT
Client mode (bit 1)
@ OPTION_MODE_DISCOVERY
Discovery mode (bit 4)
@ OPTION_MODE_MIRROR
Mirror mode (bit 2)
@ OPTION_MODE_DISCOVERY_SVC
Discovery server mode (bit 3)
int parse_server_bind_address(const char *arg, void *config, char **remaining, int num_remaining, char **error_msg)
Parse server bind address positional argument.
Definition parsers.c:563
int parse_mirror_media(const char *arg, void *config, char **remaining, int num_remaining, char **error_msg)
Parse mirror mode positional argument (file path or URL)
Definition parsers.c:879
int parse_client_address(const char *arg, void *config, char **remaining, int num_remaining, char **error_msg)
Parse client address positional argument.
Definition parsers.c:685
#define SESSION_STRING_BUFFER_SIZE

◆ options_print_help_for_mode()

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 (unified function)

This is the single unified function for all help output (binary level and all modes). It handles all layout logic, terminal detection, and section printing.

Parameters
configOptions config with all options
modeMode to show help for (-1 for binary-level help)
program_nameFull program name (e.g., "ascii-chat server")
descriptionBrief description
descOutput file stream

Print help for a specific mode or binary level (unified function)

This is the single unified function for all help output (binary level and all modes). It handles common layout logic, terminal detection, and section printing.

Parameters
configOptions config with all options (binary + all modes)
modeMode to show help for (use -1 for binary-level help)
program_nameFull program name with mode (e.g., "ascii-chat server")
descriptionBrief description of the mode/binary
descOutput file stream (usually stdout)

Definition at line 1105 of file help.c.

1106 {
1107 if (!config || !desc) {
1108 if (desc) {
1109 fprintf(desc, "Error: Failed to create options config\n");
1110 } else {
1111 SET_ERRNO(ERROR_INVALID_PARAM, "Config or desc is NULL");
1112 }
1113 return;
1114 }
1115
1116 // Detect terminal width early so we can decide whether to show ASCII art
1117 int term_width = 80;
1118 terminal_size_t term_size;
1119 if (terminal_get_size(&term_size) == ASCIICHAT_OK && term_size.cols > 40) {
1120 term_width = term_size.cols;
1121 } else {
1122 const char *cols_env = SAFE_GETENV("COLUMNS");
1123 if (cols_env) {
1124 char *endptr;
1125 errno = 0;
1126 long cols = strtol(cols_env, &endptr, 10);
1127 if (*endptr == '\0' && errno == 0 && cols > 40 && cols <= INT_MAX)
1128 term_width = (int)cols;
1129 else if (*endptr != '\0' || errno != 0 || cols <= 40)
1130 log_error("options_config_print_*: Invalid COLUMNS value: %s", cols_env);
1131 }
1132 }
1133
1134 // Print ASCII art logo only if terminal is wide enough (ASCII art is ~52 chars wide)
1135 if (term_width >= 60) {
1136 (void)fprintf(desc, " __ _ ___ ___(_|_) ___| |__ __ _| |_ \n");
1137 (void)fprintf(desc, " / _` / __|/ __| | |_____ / __| '_ \\ / _` | __|\n");
1138 (void)fprintf(desc, "| (_| \\__ \\ (__| | |_____| (__| | | | (_| | |_ \n");
1139 (void)fprintf(desc, " \\__,_|___/\\___|_|_| \\___|_| |_|\\__,_|\\__|\n");
1140 (void)fprintf(desc, "\n");
1141 }
1142
1143 // Print program name and description (color mode name magenta if it's a mode-specific help)
1144 if (program_name) {
1145 const char *space = strchr(program_name, ' ');
1146 if (space && mode >= 0) {
1147 // Mode-specific help: color the mode name
1148 int binary_len = space - program_name;
1149 (void)fprintf(desc, "%.*s %s - %s\n\n", binary_len, program_name, colored_string(LOG_COLOR_FATAL, space + 1),
1150 description);
1151 } else {
1152 // Binary-level help: use colored_string for the program name too
1153 (void)fprintf(desc, "%s - %s\n\n", colored_string(LOG_COLOR_FATAL, program_name), description);
1154 }
1155 }
1156
1157 // Print project links
1158 print_project_links(desc);
1159 (void)fprintf(desc, "\n");
1160
1161 // Determine if this is binary-level help (called for 'ascii-chat --help')
1162 // Binary help uses MODE_DISCOVERY as the mode value
1163 bool for_binary_help = (mode == MODE_DISCOVERY);
1164
1165 // Calculate column widths for USAGE upfront (before printing) for alignment
1166 int usage_max_col_width = calculate_section_max_col_width(config, "usage", mode, for_binary_help);
1167 // Use USAGE width for both USAGE and EXAMPLES sections for consistent alignment
1168 // (don't let long examples push column too far right)
1169 int usage_examples_col_width = usage_max_col_width;
1170
1171 // Print USAGE section (with section-specific column width and mode filtering)
1172 fprintf(desc, "%s\n", colored_string(LOG_COLOR_DEBUG, "USAGE"));
1173 if (config->num_usage_lines > 0) {
1174 // Get mode name for filtering usage lines
1175 const char *mode_name = NULL;
1176 switch (mode) {
1177 case MODE_SERVER:
1178 mode_name = "server";
1179 break;
1180 case MODE_CLIENT:
1181 mode_name = "client";
1182 break;
1183 case MODE_MIRROR:
1184 mode_name = "mirror";
1185 break;
1187 mode_name = "discovery-service";
1188 break;
1189 case MODE_DISCOVERY:
1190 default:
1191 mode_name = NULL; // Binary help shows all usage lines
1192 break;
1193 }
1194
1195 for (size_t i = 0; i < config->num_usage_lines; i++) {
1196 const usage_descriptor_t *usage = &config->usage_lines[i];
1197
1198 // Filter usage lines by mode
1199 if (!for_binary_help) {
1200 // For mode-specific help, show the current mode's usage line and the generic [mode] --help line
1201 bool is_current_mode = (usage->mode && mode_name && strcmp(usage->mode, mode_name) == 0);
1202 bool is_generic_help_line =
1203 (!usage->mode && usage->positional && strcmp(usage->positional, "[mode] --help") == 0);
1204
1205 if (!is_current_mode && !is_generic_help_line) {
1206 continue;
1207 }
1208 }
1209
1210 char usage_buf[BUFFER_SIZE_MEDIUM];
1211 int len = 0;
1212
1213 len += safe_snprintf(usage_buf + len, sizeof(usage_buf) - len, "ascii-chat");
1214
1215 // For mode-specific help, use [mode] placeholder when showing --help usage
1216 if (usage->mode) {
1217 if (!for_binary_help && usage->positional && strcmp(usage->positional, "--help") == 0) {
1218 // Mode-specific help: show [mode] --help as generic placeholder
1219 len +=
1220 safe_snprintf(usage_buf + len, sizeof(usage_buf) - len, " %s", colored_string(LOG_COLOR_FATAL, "[mode]"));
1221 } else {
1222 // Binary help or non-help usage: show actual mode name
1223 len += safe_snprintf(usage_buf + len, sizeof(usage_buf) - len, " %s",
1225 }
1226 }
1227
1228 if (usage->positional) {
1229 len += safe_snprintf(usage_buf + len, sizeof(usage_buf) - len, " %s",
1230 colored_string(LOG_COLOR_INFO, usage->positional));
1231 }
1232
1233 if (usage->show_options) {
1234 const char *options_text =
1235 (usage->mode && strcmp(usage->mode, "<mode>") == 0) ? "[mode-options...]" : "[options...]";
1236 len += safe_snprintf(usage_buf + len, sizeof(usage_buf) - len, " %s",
1237 colored_string(LOG_COLOR_WARN, options_text));
1238 }
1239
1240 layout_print_two_column_row(desc, usage_buf, usage->description, usage_examples_col_width, term_width, 0);
1241 }
1242 }
1243 fprintf(desc, "\n");
1244
1245 // Print MODES section (only for binary-level help)
1246 if (for_binary_help && config->num_modes > 0) {
1247 int modes_max_col_width = calculate_section_max_col_width(config, "modes", mode, for_binary_help);
1248 print_modes_section(config, desc, term_width, modes_max_col_width);
1249 }
1250
1251 // Print positional argument examples (with mode filtering and section-specific column width)
1252 if (config->num_positional_args > 0) {
1253 // First, check if any positional args apply to this mode
1254 option_mode_bitmask_t current_mode_bitmask = 1U << mode;
1255 bool has_applicable_positional_args = false;
1256
1257 for (size_t pa_idx = 0; pa_idx < config->num_positional_args; pa_idx++) {
1258 const positional_arg_descriptor_t *pos_arg = &config->positional_args[pa_idx];
1259
1260 // Filter by mode_bitmask (matching the parsing code logic)
1261 if (pos_arg->mode_bitmask != 0 && !(pos_arg->mode_bitmask & current_mode_bitmask)) {
1262 continue;
1263 }
1264
1265 if (pos_arg->section_heading && pos_arg->examples && pos_arg->num_examples > 0) {
1266 has_applicable_positional_args = true;
1267 break;
1268 }
1269 }
1270
1271 // Only print the section if there are applicable positional args
1272 if (has_applicable_positional_args) {
1273 int positional_max_col_width = calculate_section_max_col_width(config, "positional", mode, false);
1274
1275 for (size_t pa_idx = 0; pa_idx < config->num_positional_args; pa_idx++) {
1276 const positional_arg_descriptor_t *pos_arg = &config->positional_args[pa_idx];
1277
1278 // Filter by mode_bitmask (matching the parsing code logic)
1279 if (pos_arg->mode_bitmask != 0 && !(pos_arg->mode_bitmask & current_mode_bitmask)) {
1280 continue;
1281 }
1282
1283 if (pos_arg->section_heading && pos_arg->examples && pos_arg->num_examples > 0) {
1284 (void)fprintf(desc, "%s\n", colored_string(LOG_COLOR_DEBUG, pos_arg->section_heading));
1285
1286 for (size_t i = 0; i < pos_arg->num_examples; i++) {
1287 const char *example = pos_arg->examples[i];
1288 const char *p = example;
1289 const char *desc_start = NULL;
1290
1291 while (*p == ' ')
1292 p++;
1293 const char *first_part = p;
1294
1295 while (*p && !(*p == ' ' && *(p + 1) == ' '))
1296 p++;
1297 int first_len_bytes = (int)(p - first_part);
1298
1299 while (*p == ' ')
1300 p++;
1301 if (*p) {
1302 desc_start = p;
1303 }
1304
1305 char colored_first_part[256];
1306 safe_snprintf(colored_first_part, sizeof(colored_first_part), "%.*s", first_len_bytes, first_part);
1307 char colored_result[512];
1308 safe_snprintf(colored_result, sizeof(colored_result), "%s",
1309 colored_string(LOG_COLOR_INFO, colored_first_part));
1310
1311 layout_print_two_column_row(desc, colored_result, desc_start ? desc_start : "", positional_max_col_width,
1312 term_width, 0);
1313 }
1314 (void)fprintf(desc, "\n");
1315 }
1316 }
1317 }
1318 }
1319
1320 // Print EXAMPLES section (using USAGE column width for alignment)
1321 print_examples_section(config, desc, term_width, usage_examples_col_width, mode, for_binary_help);
1322
1323 // Print custom sections (after EXAMPLES, before OPTIONS)
1324 if (config->num_custom_sections > 0) {
1325 option_mode_bitmask_t current_mode_bitmask = 1U << mode;
1326 for (size_t i = 0; i < config->num_custom_sections; i++) {
1327 const custom_section_descriptor_t *section = &config->custom_sections[i];
1328
1329 // Filter by mode_bitmask
1330 if (section->mode_bitmask != 0 && !(section->mode_bitmask & current_mode_bitmask)) {
1331 continue;
1332 }
1333
1334 if (section->heading) {
1335 fprintf(desc, "%s\n", colored_string(LOG_COLOR_DEBUG, section->heading));
1336 }
1337
1338 if (section->content) {
1339 // Special handling for KEYBINDINGS section: colorize keybindings and wrap to 70 chars
1340 if (section->heading && strcmp(section->heading, "KEYBINDINGS") == 0) {
1341 // Build colored version with proper keybinding colorization
1342 char colored_output[2048] = "";
1343 const char *src = section->content;
1344 char *dst = colored_output;
1345 size_t remaining = sizeof(colored_output) - 1;
1346
1347 while (*src && remaining > 0) {
1348 // Colorize ? that don't end sentences
1349 if (*src == '?' && *(src + 1) != '\n' && *(src + 1) != '\0') {
1350 const char *colored = colored_string(LOG_COLOR_FATAL, "?");
1351 size_t len = strlen(colored);
1352 if (len <= remaining) {
1353 memcpy(dst, colored, len);
1354 dst += len;
1355 remaining -= len;
1356 src += 1;
1357 } else {
1358 break;
1359 }
1360 } else if (strncmp(src, "Space", 5) == 0 &&
1361 (src > section->content && (*(src - 1) == ',' || *(src - 1) == ' '))) {
1362 const char *colored = colored_string(LOG_COLOR_FATAL, "Space");
1363 size_t len = strlen(colored);
1364 if (len <= remaining) {
1365 memcpy(dst, colored, len);
1366 dst += len;
1367 remaining -= len;
1368 src += 5;
1369 } else {
1370 break;
1371 }
1372 } else if (strncmp(src, "arrows", 6) == 0 &&
1373 (src > section->content && (*(src - 1) == ',' || *(src - 1) == ' '))) {
1374 const char *colored = colored_string(LOG_COLOR_FATAL, "arrows");
1375 size_t len = strlen(colored);
1376 if (len <= remaining) {
1377 memcpy(dst, colored, len);
1378 dst += len;
1379 remaining -= len;
1380 src += 6;
1381 } else {
1382 break;
1383 }
1384 } else if (*src == 'm' && (src > section->content && (*(src - 1) == ',' || *(src - 1) == ' ')) &&
1385 (*(src + 1) == ',' || *(src + 1) == ')')) {
1386 const char *colored = colored_string(LOG_COLOR_FATAL, "m");
1387 size_t len = strlen(colored);
1388 if (len <= remaining) {
1389 memcpy(dst, colored, len);
1390 dst += len;
1391 remaining -= len;
1392 src += 1;
1393 } else {
1394 break;
1395 }
1396 } else if (*src == 'c' && (src > section->content && (*(src - 1) == ',' || *(src - 1) == ' ')) &&
1397 (*(src + 1) == ',' || *(src + 1) == ')')) {
1398 const char *colored = colored_string(LOG_COLOR_FATAL, "c");
1399 size_t len = strlen(colored);
1400 if (len <= remaining) {
1401 memcpy(dst, colored, len);
1402 dst += len;
1403 remaining -= len;
1404 src += 1;
1405 } else {
1406 break;
1407 }
1408 } else if (*src == 'f' && (src > section->content && (*(src - 1) == ',' || *(src - 1) == ' ')) &&
1409 (*(src + 1) == ',' || *(src + 1) == ')')) {
1410 const char *colored = colored_string(LOG_COLOR_FATAL, "f");
1411 size_t len = strlen(colored);
1412 if (len <= remaining) {
1413 memcpy(dst, colored, len);
1414 dst += len;
1415 remaining -= len;
1416 src += 1;
1417 } else {
1418 break;
1419 }
1420 } else if (*src == 'r' && (src > section->content && (*(src - 1) == ',' || *(src - 1) == ' ')) &&
1421 (*(src + 1) == ')')) {
1422 const char *colored = colored_string(LOG_COLOR_FATAL, "r");
1423 size_t len = strlen(colored);
1424 if (len <= remaining) {
1425 memcpy(dst, colored, len);
1426 dst += len;
1427 remaining -= len;
1428 src += 1;
1429 } else {
1430 break;
1431 }
1432 } else {
1433 *dst++ = *src++;
1434 remaining--;
1435 }
1436 }
1437 *dst = '\0';
1438 // Print with 2-space indent, wrapping at min(terminal width, 90)
1439 fprintf(desc, " ");
1440 int keybindings_wrap_width = term_width < 90 ? term_width : 90;
1441 layout_print_wrapped_description(desc, colored_output, 2, keybindings_wrap_width, 0);
1442 fprintf(desc, "\n");
1443 } else {
1444 fprintf(desc, "%s\n", section->content);
1445 }
1446 }
1447
1448 fprintf(desc, "\n");
1449 }
1450 }
1451
1452 // Print options sections (with section-specific column width for options)
1453 int options_max_col_width = calculate_section_max_col_width(config, "options", mode, for_binary_help);
1454 options_config_print_options_sections_with_width(config, desc, options_max_col_width, mode);
1455}
option_mode_bitmask_t
Option mode bitmask.
@ MODE_CLIENT
Client mode - network client options.
@ MODE_DISCOVERY_SERVICE
Discovery server mode - session management and WebRTC signaling.
@ MODE_MIRROR
Mirror mode - local webcam viewing (no network)
void layout_print_wrapped_description(FILE *stream, const char *text, int indent_width, int term_width, int continuation_indent_extra)
Print text with wrapping and proper indentation.
Definition layout.c:98
void print_project_links(FILE *desc)
Print project links with link emoji and colored styling.
option_mode_bitmask_t mode_bitmask
Which modes show this section.
Definition builder.h:392
const char * content
Section content.
Definition builder.h:391

References ASCIICHAT_OK, BUFFER_SIZE_MEDIUM, colored_string(), terminal_size_t::cols, custom_section_descriptor_t::content, options_config_t::custom_sections, errno, ERROR_INVALID_PARAM, positional_arg_descriptor_t::examples, custom_section_descriptor_t::heading, layout_print_two_column_row(), layout_print_wrapped_description(), LOG_COLOR_DEBUG, LOG_COLOR_FATAL, LOG_COLOR_INFO, LOG_COLOR_WARN, log_error, positional_arg_descriptor_t::mode_bitmask, custom_section_descriptor_t::mode_bitmask, MODE_CLIENT, MODE_DISCOVERY, MODE_DISCOVERY_SERVICE, MODE_MIRROR, MODE_SERVER, options_config_t::num_custom_sections, positional_arg_descriptor_t::num_examples, options_config_t::num_modes, options_config_t::num_positional_args, options_config_t::num_usage_lines, options_config_print_options_sections_with_width(), options_config_t::positional_args, print_project_links(), SAFE_GETENV, safe_snprintf(), positional_arg_descriptor_t::section_heading, SET_ERRNO, terminal_get_size(), usage(), and options_config_t::usage_lines.

Referenced by usage().

◆ options_struct_destroy()

void options_struct_destroy ( const options_config_t *  config,
void *  options_struct 
)

Clean up memory owned by options struct.

Frees all auto-duplicated strings and sets pointers to NULL. Call this before freeing the options struct.

Parameters
configOptions configuration
options_structOptions struct to clean up

Definition at line 1457 of file help.c.

1457 {
1458 if (!config || !options_struct)
1459 return;
1460
1461 // Free all owned strings
1462 for (size_t i = 0; i < config->num_owned_strings; i++) {
1463 free(config->owned_strings[i]);
1464 }
1465
1466 // Reset owned strings tracking
1467 ((options_config_t *)config)->num_owned_strings = 0;
1468
1469 // NULL out string fields
1470 char *base = (char *)options_struct;
1471 for (size_t i = 0; i < config->num_descriptors; i++) {
1472 const option_descriptor_t *desc = &config->descriptors[i];
1473 if (desc->type == OPTION_TYPE_STRING && desc->owns_memory) {
1474 char **field = (char **)(base + desc->offset);
1475 *field = NULL;
1476 }
1477 }
1478}
bool owns_memory
If true, strings are strdup'd and freed on cleanup.
Definition builder.h:272

References options_config_t::descriptors, options_config_t::num_descriptors, options_config_t::num_owned_strings, option_descriptor_t::offset, OPTION_TYPE_STRING, options_config_t::owned_strings, option_descriptor_t::owns_memory, and option_descriptor_t::type.