ascii-chat 0.6.0
Real-time terminal-based video chat with ASCII art conversion
Loading...
Searching...
No Matches
builder.c File Reference

Implementation of options builder API. More...

Go to the source code of this file.

Macros

#define INITIAL_DESCRIPTOR_CAPACITY   32
 
#define INITIAL_DEPENDENCY_CAPACITY   16
 
#define INITIAL_POSITIONAL_ARG_CAPACITY   8
 
#define INITIAL_OWNED_STRINGS_CAPACITY   32
 

Functions

 __attribute__ ((unused))
 Track an owned string for cleanup.
 
options_builder_toptions_builder_create (size_t struct_size)
 Create empty options builder.
 
options_builder_toptions_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_toptions_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 *, char **))
 
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 **))
 
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 *, char **))
 
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 *, void *, char **), const char *help_text, const char *group, bool required, const char *env_var_name)
 
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 *, void *, char **), const char *help_text, const char *group, bool required, const char *env_var_name, bool optional_arg)
 Add callback option with optional argument support.
 
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_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, 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.
 
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, 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.
 
void options_config_print_usage (const options_config_t *config, FILE *stream)
 Print usage/help text.
 
void options_config_cleanup (const options_config_t *config, void *options_struct)
 Clean up memory owned by options struct.
 

Detailed Description

Implementation of options builder API.

Definition in file builder.c.

Macro Definition Documentation

◆ INITIAL_DEPENDENCY_CAPACITY

#define INITIAL_DEPENDENCY_CAPACITY   16

Definition at line 25 of file builder.c.

◆ INITIAL_DESCRIPTOR_CAPACITY

#define INITIAL_DESCRIPTOR_CAPACITY   32

Definition at line 24 of file builder.c.

◆ INITIAL_OWNED_STRINGS_CAPACITY

#define INITIAL_OWNED_STRINGS_CAPACITY   32

Definition at line 27 of file builder.c.

◆ INITIAL_POSITIONAL_ARG_CAPACITY

#define INITIAL_POSITIONAL_ARG_CAPACITY   8

Definition at line 26 of file builder.c.

Function Documentation

◆ __attribute__()

static int __attribute__ ( (unused)  )
inline

Track an owned string for cleanup.

Definition at line 90 of file builder.c.

90 {
91 if (!str)
92 return;
93
94 if (config->num_owned_strings >= config->owned_strings_capacity) {
95 size_t new_capacity = config->owned_strings_capacity * 2;
96 if (new_capacity == 0)
97 new_capacity = INITIAL_OWNED_STRINGS_CAPACITY;
98
99 char **new_owned = SAFE_REALLOC(config->owned_strings, new_capacity * sizeof(char *), char **);
100 if (!new_owned) {
101 log_fatal("Failed to reallocate owned_strings array");
102 return;
103 }
104 config->owned_strings = new_owned;
105 config->owned_strings_capacity = new_capacity;
106 }
107
108 config->owned_strings[config->num_owned_strings++] = str;
109}
#define INITIAL_OWNED_STRINGS_CAPACITY
Definition builder.c:27
#define SAFE_REALLOC(ptr, size, cast)
Definition common.h:228
#define log_fatal(...)
Log a FATAL message.

References INITIAL_OWNED_STRINGS_CAPACITY, log_fatal, options_config_t::num_owned_strings, options_config_t::owned_strings, options_config_t::owned_strings_capacity, and SAFE_REALLOC.

◆ 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 532 of file builder.c.

533 {
534 ensure_descriptor_capacity(builder);
535
536 option_descriptor_t desc = {.long_name = long_name,
537 .short_name = short_name,
538 .type = OPTION_TYPE_ACTION,
539 .offset = 0, // Actions don't store values
540 .help_text = help_text,
541 .group = group,
542 .default_value = NULL,
543 .required = false, // Actions are never required
544 .env_var_name = NULL,
545 .validate = NULL,
546 .parse_fn = NULL,
547 .action_fn = action_fn,
548 .owns_memory = false};
549
550 builder->descriptors[builder->num_descriptors++] = desc;
551}
@ OPTION_TYPE_ACTION
Action that executes and may exit (–list-webcams, etc.)
Definition builder.h:51
Option descriptor.
Definition builder.h:70
const char * long_name
Long option name (e.g., "port")
Definition builder.h:72
option_descriptor_t * descriptors
Dynamic array of descriptors.
Definition builder.h:193
size_t num_descriptors
Current count.
Definition builder.h:194

References options_builder_t::descriptors, option_descriptor_t::long_name, options_builder_t::num_descriptors, and OPTION_TYPE_ACTION.

Referenced by options_preset_acds(), options_preset_client(), options_preset_mirror(), and options_preset_server().

◆ 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 344 of file builder.c.

346 {
347 ensure_descriptor_capacity(builder);
348
349 static bool default_vals[2] = {false, true};
350
351 option_descriptor_t desc = {.long_name = long_name,
352 .short_name = short_name,
353 .type = OPTION_TYPE_BOOL,
354 .offset = offset,
355 .help_text = help_text,
356 .group = group,
357 .default_value = &default_vals[default_value ? 1 : 0],
358 .required = required,
359 .env_var_name = env_var_name,
360 .validate = NULL,
361 .parse_fn = NULL,
362 .owns_memory = false};
363
364 builder->descriptors[builder->num_descriptors++] = desc;
365}
@ OPTION_TYPE_BOOL
Boolean flag (–flag, no value)
Definition builder.h:46

References options_builder_t::descriptors, option_descriptor_t::long_name, options_builder_t::num_descriptors, and OPTION_TYPE_BOOL.

Referenced by options_preset_acds(), options_preset_binary(), options_preset_client(), and options_preset_server().

◆ 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 *, void *, char **)  parse_fn,
const char *  help_text,
const char *  group,
bool  required,
const char *  env_var_name 
)

Definition at line 467 of file builder.c.

470 {
471 (void)value_size; // Unused parameter
472 ensure_descriptor_capacity(builder);
473
474 option_descriptor_t desc = {.long_name = long_name,
475 .short_name = short_name,
476 .type = OPTION_TYPE_CALLBACK,
477 .offset = offset,
478 .help_text = help_text,
479 .group = group,
480 .default_value = default_value,
481 .required = required,
482 .env_var_name = env_var_name,
483 .validate = NULL,
484 .parse_fn = parse_fn,
485 .owns_memory = false,
486 .optional_arg = false};
487
488 builder->descriptors[builder->num_descriptors++] = desc;
489}
@ OPTION_TYPE_CALLBACK
Custom parser function.
Definition builder.h:50

References options_builder_t::descriptors, option_descriptor_t::long_name, options_builder_t::num_descriptors, and OPTION_TYPE_CALLBACK.

Referenced by options_preset_acds().

◆ 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 *, void *, char **)  parse_fn,
const char *  help_text,
const char *  group,
bool  required,
const char *  env_var_name,
bool  optional_arg 
)

Add callback option with optional argument support.

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)
optional_argIf true, argument is optional (parser receives NULL if not provided)

Definition at line 507 of file builder.c.

511 {
512 (void)value_size; // Unused parameter
513 ensure_descriptor_capacity(builder);
514
515 option_descriptor_t desc = {.long_name = long_name,
516 .short_name = short_name,
517 .type = OPTION_TYPE_CALLBACK,
518 .offset = offset,
519 .help_text = help_text,
520 .group = group,
521 .default_value = default_value,
522 .required = required,
523 .env_var_name = env_var_name,
524 .validate = NULL,
525 .parse_fn = parse_fn,
526 .owns_memory = false,
527 .optional_arg = optional_arg};
528
529 builder->descriptors[builder->num_descriptors++] = desc;
530}

References options_builder_t::descriptors, option_descriptor_t::long_name, options_builder_t::num_descriptors, and OPTION_TYPE_CALLBACK.

◆ 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 599 of file builder.c.

599 {
600 if (!builder || !dependency)
601 return;
602
603 ensure_dependency_capacity(builder);
604 builder->dependencies[builder->num_dependencies++] = *dependency;
605}
size_t num_dependencies
Current count.
Definition builder.h:198
option_dependency_t * dependencies
Dynamic array of dependencies.
Definition builder.h:197

References options_builder_t::dependencies, 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 577 of file builder.c.

578 {
579 ensure_dependency_capacity(builder);
580
581 option_dependency_t dep = {.option_name = option_name,
582 .type = DEPENDENCY_CONFLICTS,
583 .depends_on = conflicts_with,
584 .error_message = error_message};
585
586 builder->dependencies[builder->num_dependencies++] = dep;
587}
@ DEPENDENCY_CONFLICTS
If A is set, B must NOT be set.
Definition builder.h:61
Option dependency.
Definition builder.h:110
const char * option_name
The option that has the dependency.
Definition builder.h:111

References options_builder_t::dependencies, DEPENDENCY_CONFLICTS, options_builder_t::num_dependencies, and option_dependency_t::option_name.

Referenced by options_preset_client(), and options_preset_server().

◆ 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 589 of file builder.c.

590 {
591 ensure_dependency_capacity(builder);
592
593 option_dependency_t dep = {
594 .option_name = option_name, .type = DEPENDENCY_IMPLIES, .depends_on = implies, .error_message = error_message};
595
596 builder->dependencies[builder->num_dependencies++] = dep;
597}
@ DEPENDENCY_IMPLIES
If A is set, B defaults to true/enabled.
Definition builder.h:62

References options_builder_t::dependencies, DEPENDENCY_IMPLIES, 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 565 of file builder.c.

566 {
567 ensure_dependency_capacity(builder);
568
569 option_dependency_t dep = {.option_name = option_name,
570 .type = DEPENDENCY_REQUIRES,
571 .depends_on = depends_on,
572 .error_message = error_message};
573
574 builder->dependencies[builder->num_dependencies++] = dep;
575}
@ DEPENDENCY_REQUIRES
If A is set, B must be set.
Definition builder.h:60

References options_builder_t::dependencies, DEPENDENCY_REQUIRES, options_builder_t::num_dependencies, and option_dependency_t::option_name.

Referenced by options_preset_client(), and options_preset_mirror().

◆ 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 553 of file builder.c.

553 {
554 if (!builder || !descriptor)
555 return;
556
557 ensure_descriptor_capacity(builder);
558 builder->descriptors[builder->num_descriptors++] = *descriptor;
559}

References options_builder_t::descriptors, and options_builder_t::num_descriptors.

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 *, char **)  validate 
)

Definition at line 436 of file builder.c.

438 {
439 ensure_descriptor_capacity(builder);
440
441 static double defaults[256];
442 static size_t num_defaults = 0;
443
444 if (num_defaults >= 256) {
445 log_error("Too many double options (max 256)");
446 return;
447 }
448
449 defaults[num_defaults] = default_value;
450
451 option_descriptor_t desc = {.long_name = long_name,
452 .short_name = short_name,
453 .type = OPTION_TYPE_DOUBLE,
454 .offset = offset,
455 .help_text = help_text,
456 .group = group,
457 .default_value = &defaults[num_defaults++],
458 .required = required,
459 .env_var_name = env_var_name,
460 .validate = validate,
461 .parse_fn = NULL,
462 .owns_memory = false};
463
464 builder->descriptors[builder->num_descriptors++] = desc;
465}
@ OPTION_TYPE_DOUBLE
Floating point (–ratio 1.5)
Definition builder.h:49
#define log_error(...)
Log an ERROR message.

References options_builder_t::descriptors, log_error, option_descriptor_t::long_name, options_builder_t::num_descriptors, and OPTION_TYPE_DOUBLE.

◆ 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 *, char **)  validate 
)

Definition at line 367 of file builder.c.

369 {
370 ensure_descriptor_capacity(builder);
371
372 // Store default value statically
373 static int defaults[256]; // Assume we won't have more than 256 int options
374 static size_t num_defaults = 0;
375
376 if (num_defaults >= 256) {
377 log_error("Too many int options (max 256)");
378 return;
379 }
380
381 defaults[num_defaults] = default_value;
382
383 option_descriptor_t desc = {.long_name = long_name,
384 .short_name = short_name,
385 .type = OPTION_TYPE_INT,
386 .offset = offset,
387 .help_text = help_text,
388 .group = group,
389 .default_value = &defaults[num_defaults++],
390 .required = required,
391 .env_var_name = env_var_name,
392 .validate = validate,
393 .parse_fn = NULL,
394 .owns_memory = false};
395
396 builder->descriptors[builder->num_descriptors++] = desc;
397}
@ OPTION_TYPE_INT
Integer value (–count 42)
Definition builder.h:47

References options_builder_t::descriptors, log_error, option_descriptor_t::long_name, options_builder_t::num_descriptors, and OPTION_TYPE_INT.

Referenced by options_preset_client(), and options_preset_server().

◆ 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,
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, int(*parse_fn)(const char *arg, void *config, char **remaining, int num_remaining, char **error_msg))
Add positional argument descriptor.
Definition builder.c:627

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
parse_fnCustom parser (receives arg, config, remaining args, error_msg) Returns number of args consumed (usually 1), or -1 on error

Definition at line 627 of file builder.c.

630 {
631 if (!builder || !name || !parse_fn)
632 return;
633
634 ensure_positional_arg_capacity(builder);
635
636 positional_arg_descriptor_t pos_arg = {.name = name,
637 .help_text = help_text,
638 .required = required,
639 .section_heading = section_heading,
640 .examples = examples,
641 .num_examples = num_examples,
642 .parse_fn = parse_fn};
643
644 builder->positional_args[builder->num_positional_args++] = pos_arg;
645}
positional_arg_descriptor_t * positional_args
Dynamic array of positional args.
Definition builder.h:201
size_t num_positional_args
Current count.
Definition builder.h:202
Positional argument descriptor.
Definition builder.h:136
const char * name
Name for help text (e.g., "address", "bind-addr")
Definition builder.h:137

References positional_arg_descriptor_t::name, options_builder_t::num_positional_args, and options_builder_t::positional_args.

Referenced by options_builder_from_preset(), options_preset_acds(), options_preset_client(), and options_preset_server().

◆ 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 *, char **)  validate 
)

Definition at line 399 of file builder.c.

401 {
402 ensure_descriptor_capacity(builder);
403
404 // Store default string pointer in static storage to avoid stack-use-after-return
405 static const char *defaults[256];
406 static size_t num_defaults = 0;
407
408 const void *default_ptr = NULL;
409 if (default_value) {
410 if (num_defaults >= 256) {
411 log_error("Too many string options (max 256)");
412 return;
413 }
414 defaults[num_defaults] = default_value;
415 default_ptr = &defaults[num_defaults++];
416 }
417
418 option_descriptor_t desc = {
419 .long_name = long_name,
420 .short_name = short_name,
421 .type = OPTION_TYPE_STRING,
422 .offset = offset,
423 .help_text = help_text,
424 .group = group,
425 .default_value = default_ptr,
426 .required = required,
427 .env_var_name = env_var_name,
428 .validate = validate,
429 .parse_fn = NULL,
430 .owns_memory = true // Strings are always owned
431 };
432
433 builder->descriptors[builder->num_descriptors++] = desc;
434}
@ OPTION_TYPE_STRING
String value (–name foo)
Definition builder.h:48

References options_builder_t::descriptors, log_error, option_descriptor_t::long_name, options_builder_t::num_descriptors, and OPTION_TYPE_STRING.

Referenced by options_preset_acds(), options_preset_client(), and options_preset_server().

◆ 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 265 of file builder.c.

265 {
266 if (!builder) {
267 SET_ERRNO(ERROR_INVALID_PARAM, "Builder is NULL");
268 return NULL;
269 }
270
272 if (!config) {
273 SET_ERRNO(ERROR_MEMORY, "Failed to allocate options config");
274 return NULL;
275 }
276
277 // Allocate and copy descriptors
279 if (!config->descriptors && builder->num_descriptors > 0) {
280 SAFE_FREE(config);
281 SET_ERRNO(ERROR_MEMORY, "Failed to allocate descriptors");
282 return NULL;
283 }
284 memcpy(config->descriptors, builder->descriptors, builder->num_descriptors * sizeof(option_descriptor_t));
285 config->num_descriptors = builder->num_descriptors;
286
287 // Allocate and copy dependencies
289 if (!config->dependencies && builder->num_dependencies > 0) {
290 SAFE_FREE(config->descriptors);
291 SAFE_FREE(config);
292 SET_ERRNO(ERROR_MEMORY, "Failed to allocate dependencies");
293 return NULL;
294 }
295 memcpy(config->dependencies, builder->dependencies, builder->num_dependencies * sizeof(option_dependency_t));
296 config->num_dependencies = builder->num_dependencies;
297
298 // Allocate and copy positional args
299 if (builder->num_positional_args > 0) {
300 config->positional_args =
302 if (!config->positional_args) {
303 SAFE_FREE(config->descriptors);
304 SAFE_FREE(config->dependencies);
305 SAFE_FREE(config);
306 SET_ERRNO(ERROR_MEMORY, "Failed to allocate positional args");
307 return NULL;
308 }
309 memcpy(config->positional_args, builder->positional_args,
311 config->num_positional_args = builder->num_positional_args;
312 } else {
313 config->positional_args = NULL;
314 config->num_positional_args = 0;
315 }
316
317 config->struct_size = builder->struct_size;
318 config->program_name = builder->program_name;
319 config->description = builder->description;
320
321 // Initialize memory management
322 config->owned_strings = NULL;
323 config->num_owned_strings = 0;
324 config->owned_strings_capacity = 0;
325
326 return config;
327}
#define SAFE_FREE(ptr)
Definition common.h:320
#define SAFE_MALLOC(size, cast)
Definition common.h:208
#define SET_ERRNO(code, context_msg,...)
Set error code with custom context message and log it.
@ ERROR_MEMORY
Definition error_codes.h:53
@ ERROR_INVALID_PARAM
const char * program_name
Program name for usage.
Definition builder.h:206
const char * description
Program description for usage.
Definition builder.h:207
size_t struct_size
Target struct size.
Definition builder.h:205
Options configuration.
Definition builder.h:165
positional_arg_descriptor_t * positional_args
Array of positional argument descriptors.
Definition builder.h:172
option_descriptor_t * descriptors
Array of option descriptors.
Definition builder.h:166
option_dependency_t * dependencies
Array of dependencies.
Definition builder.h:169
const char * program_name
For usage header.
Definition builder.h:176
size_t num_positional_args
Number of positional arguments.
Definition builder.h:173
size_t num_descriptors
Number of descriptors.
Definition builder.h:167
char ** owned_strings
Strdup'd strings to free on cleanup.
Definition builder.h:180
size_t owned_strings_capacity
Allocated capacity.
Definition builder.h:182
size_t num_dependencies
Number of dependencies.
Definition builder.h:170
const char * description
For usage header.
Definition builder.h:177
size_t struct_size
sizeof(options_t) for bounds checking
Definition builder.h:175
size_t num_owned_strings
Number of owned strings.
Definition builder.h:181

References 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::num_dependencies, options_builder_t::num_dependencies, options_config_t::num_descriptors, options_builder_t::num_descriptors, options_config_t::num_owned_strings, options_config_t::num_positional_args, options_builder_t::num_positional_args, options_config_t::owned_strings, 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, and options_builder_t::struct_size.

Referenced by options_preset_acds(), options_preset_binary(), options_preset_client(), options_preset_mirror(), and options_preset_server().

◆ 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 184 of file builder.c.

184 {
186 if (!builder) {
187 SET_ERRNO(ERROR_MEMORY, "Failed to allocate options builder");
188 return NULL;
189 }
190
192 if (!builder->descriptors) {
193 free(builder);
194 SET_ERRNO(ERROR_MEMORY, "Failed to allocate descriptors array");
195 return NULL;
196 }
197
199 if (!builder->dependencies) {
200 SAFE_FREE(builder->descriptors);
201 SAFE_FREE(builder);
202 SET_ERRNO(ERROR_MEMORY, "Failed to allocate dependencies array");
203 return NULL;
204 }
205
206 builder->num_descriptors = 0;
208 builder->num_dependencies = 0;
210 builder->positional_args = NULL;
211 builder->num_positional_args = 0;
212 builder->positional_arg_capacity = 0;
213 builder->struct_size = struct_size;
214 builder->program_name = NULL;
215 builder->description = NULL;
216
217 return builder;
218}
#define INITIAL_DEPENDENCY_CAPACITY
Definition builder.c:25
#define INITIAL_DESCRIPTOR_CAPACITY
Definition builder.c:24
Options builder.
Definition builder.h:192
size_t descriptor_capacity
Allocated capacity.
Definition builder.h:195
size_t positional_arg_capacity
Allocated capacity.
Definition builder.h:203
size_t dependency_capacity
Allocated capacity.
Definition builder.h:199

References options_builder_t::dependencies, options_builder_t::dependency_capacity, options_builder_t::description, options_builder_t::descriptor_capacity, options_builder_t::descriptors, ERROR_MEMORY, INITIAL_DEPENDENCY_CAPACITY, INITIAL_DESCRIPTOR_CAPACITY, options_builder_t::num_dependencies, options_builder_t::num_descriptors, options_builder_t::num_positional_args, options_builder_t::positional_arg_capacity, options_builder_t::positional_args, options_builder_t::program_name, SAFE_FREE, SAFE_MALLOC, SET_ERRNO, and options_builder_t::struct_size.

Referenced by options_builder_from_preset(), options_preset_acds(), options_preset_binary(), options_preset_client(), options_preset_mirror(), and options_preset_server().

◆ options_builder_destroy()

void options_builder_destroy ( options_builder_t builder)

Free options builder.

Parameters
builderBuilder to free (can be NULL)

Definition at line 255 of file builder.c.

255 {
256 if (!builder)
257 return;
258
259 SAFE_FREE(builder->descriptors);
260 SAFE_FREE(builder->dependencies);
261 SAFE_FREE(builder->positional_args);
262 SAFE_FREE(builder);
263}

References options_builder_t::dependencies, options_builder_t::descriptors, options_builder_t::positional_args, and SAFE_FREE.

Referenced by options_preset_acds(), options_preset_binary(), options_preset_client(), options_preset_mirror(), and options_preset_server().

◆ 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 220 of file builder.c.

220 {
221 if (!preset) {
222 SET_ERRNO(ERROR_INVALID_PARAM, "Preset config is NULL");
223 return NULL;
224 }
225
227 if (!builder) {
228 return NULL;
229 }
230
231 builder->program_name = preset->program_name;
232 builder->description = preset->description;
233
234 // Copy all descriptors
235 for (size_t i = 0; i < preset->num_descriptors; i++) {
236 options_builder_add_descriptor(builder, &preset->descriptors[i]);
237 }
238
239 // Copy all dependencies
240 for (size_t i = 0; i < preset->num_dependencies; i++) {
241 options_builder_add_dependency(builder, &preset->dependencies[i]);
242 }
243
244 // Copy all positional arguments
245 for (size_t i = 0; i < preset->num_positional_args; i++) {
246 const positional_arg_descriptor_t *pos_arg = &preset->positional_args[i];
247 options_builder_add_positional(builder, pos_arg->name, pos_arg->help_text, pos_arg->required,
248 pos_arg->section_heading, pos_arg->examples, pos_arg->num_examples,
249 pos_arg->parse_fn);
250 }
251
252 return builder;
253}
void options_builder_add_dependency(options_builder_t *builder, const option_dependency_t *dependency)
Add full dependency (advanced)
Definition builder.c:599
void options_builder_add_descriptor(options_builder_t *builder, const option_descriptor_t *descriptor)
Add full option descriptor (advanced)
Definition builder.c:553
options_builder_t * options_builder_create(size_t struct_size)
Create empty options builder.
Definition builder.c:184
const char * section_heading
Section heading for examples (e.g., "ADDRESS FORMATS")
Definition builder.h:142
bool required
If true, this positional arg must be provided.
Definition builder.h:139
const char * help_text
Description for usage message.
Definition builder.h:138
int(* parse_fn)(const char *arg, void *config, char **remaining, int num_remaining, char **error_msg)
Definition builder.h:156
const char ** examples
Array of example strings with descriptions.
Definition builder.h:143
size_t num_examples
Number of examples.
Definition builder.h:144

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::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_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:607
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:399

Definition at line 607 of file builder.c.

607 {
608 if (!builder || !option_name)
609 return;
610
611 // Find the option by name and mark it
612 for (size_t i = 0; i < builder->num_descriptors; i++) {
613 if (strcmp(builder->descriptors[i].long_name, option_name) == 0) {
614 builder->descriptors[i].hide_from_mode_help = true;
615 return;
616 }
617 }
618
619 // Option not found - this is a programming error
620 log_warn("Attempted to mark non-existent option '%s' as binary-only", option_name);
621}
#define log_warn(...)
Log a WARN message.
bool hide_from_mode_help
If true, don't show in mode-specific help (binary-level only)
Definition builder.h:82

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_config_cleanup()

void options_config_cleanup ( 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 1345 of file builder.c.

1345 {
1346 if (!config || !options_struct)
1347 return;
1348
1349 // Free all owned strings
1350 for (size_t i = 0; i < config->num_owned_strings; i++) {
1351 free(config->owned_strings[i]);
1352 }
1353
1354 // Reset owned strings tracking
1355 ((options_config_t *)config)->num_owned_strings = 0;
1356
1357 // NULL out string fields
1358 char *base = (char *)options_struct;
1359 for (size_t i = 0; i < config->num_descriptors; i++) {
1360 const option_descriptor_t *desc = &config->descriptors[i];
1361 if (desc->type == OPTION_TYPE_STRING && desc->owns_memory) {
1362 char **field = (char **)(base + desc->offset);
1363 *field = NULL;
1364 }
1365 }
1366}
size_t offset
offsetof(struct, field) - where to store value
Definition builder.h:77
bool owns_memory
If true, strings are strdup'd and freed on cleanup.
Definition builder.h:99
option_type_t type
Value type.
Definition builder.h:76

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.

◆ 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_cleanup() for that.

Parameters
configConfig to free (can be NULL)

Definition at line 329 of file builder.c.

329 {
330 if (!config)
331 return;
332
333 SAFE_FREE(config->descriptors);
334 SAFE_FREE(config->dependencies);
335 SAFE_FREE(config->positional_args);
336 SAFE_FREE(config->owned_strings);
337 SAFE_FREE(config);
338}

References options_config_t::dependencies, options_config_t::descriptors, options_config_t::owned_strings, options_config_t::positional_args, and SAFE_FREE.

Referenced by parse_acds_options(), parse_client_options(), parse_mirror_options(), parse_server_options(), usage_acds(), usage_client(), usage_mirror(), and usage_server().

◆ options_config_parse()

asciichat_error_t options_config_parse ( const options_config_t config,
int  argc,
char **  argv,
void *  options_struct,
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 1101 of file builder.c.

1102 {
1103 if (!config || !options_struct) {
1104 return SET_ERRNO(ERROR_INVALID_PARAM, "Config or options struct is NULL");
1105 }
1106
1107 // Use the new unified parser that handles mixed positional and flag arguments
1108 asciichat_error_t result = options_config_parse_unified(config, argc, argv, options_struct);
1109 if (result != ASCIICHAT_OK) {
1110 return result;
1111 }
1112
1113 // Since unified parser handles all arguments, there are no remaining args
1114 // (backward compatibility: set remaining_argc to 0)
1115 if (remaining_argc) {
1116 *remaining_argc = 0;
1117 }
1118 if (remaining_argv) {
1119 *remaining_argv = NULL;
1120 }
1121
1122 return ASCIICHAT_OK;
1123}
asciichat_error_t
Error and exit codes - unified status values (0-255)
Definition error_codes.h:46
@ ASCIICHAT_OK
Definition error_codes.h:48

References ASCIICHAT_OK, ERROR_INVALID_PARAM, and SET_ERRNO.

Referenced by parse_acds_options(), parse_client_options(), parse_mirror_options(), and parse_server_options().

◆ 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 647 of file builder.c.

648 {
649 if (!config || !options_struct) {
650 return SET_ERRNO(ERROR_INVALID_PARAM, "Config or options struct is NULL");
651 }
652
653 if (remaining_argc < 0 || (remaining_argc > 0 && !remaining_argv)) {
654 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid remaining args");
655 }
656
657 // No positional args defined - but we got some positional arguments
658 if (config->num_positional_args == 0 && remaining_argc > 0) {
659 (void)fprintf(stderr, "Error: Unexpected positional argument '%s'\n", remaining_argv[0]);
660 return ERROR_USAGE;
661 }
662
663 // Check if required positional args are missing
664 for (size_t i = 0; i < config->num_positional_args; i++) {
665 const positional_arg_descriptor_t *pos_arg = &config->positional_args[i];
666 if (pos_arg->required && remaining_argc == 0) {
667 (void)fprintf(stderr, "Error: Missing required positional argument '%s'\n", pos_arg->name);
668 if (pos_arg->help_text) {
669 (void)fprintf(stderr, " %s\n", pos_arg->help_text);
670 }
671 return ERROR_USAGE;
672 }
673 }
674
675 // Parse positional arguments
676 int arg_index = 0;
677 for (size_t i = 0; i < config->num_positional_args && arg_index < remaining_argc; i++) {
678 const positional_arg_descriptor_t *pos_arg = &config->positional_args[i];
679
680 const char *arg = remaining_argv[arg_index];
681 char **remaining = (arg_index + 1 < remaining_argc) ? &remaining_argv[arg_index + 1] : NULL;
682 int num_remaining = remaining_argc - arg_index - 1;
683 char *error_msg = NULL;
684
685 // Call custom parser
686 int consumed = pos_arg->parse_fn(arg, options_struct, remaining, num_remaining, &error_msg);
687
688 if (consumed < 0) {
689 // Parse error
690 if (error_msg) {
691 (void)fprintf(stderr, "Error parsing positional argument '%s': %s\n", pos_arg->name, error_msg);
692 free(error_msg);
693 } else {
694 (void)fprintf(stderr, "Error parsing positional argument '%s': %s\n", pos_arg->name, arg);
695 }
696 return ERROR_USAGE;
697 }
698
699 // Advance by consumed args (usually 1, but can be more for multi-arg parsers)
700 arg_index += consumed;
701 }
702
703 // Check for extra unconsumed positional arguments
704 if (arg_index < remaining_argc) {
705 (void)fprintf(stderr, "Error: Unexpected positional argument '%s'\n", remaining_argv[arg_index]);
706 return ERROR_USAGE;
707 }
708
709 return ASCIICHAT_OK;
710}
@ ERROR_USAGE
Definition error_codes.h:50

References ASCIICHAT_OK, ERROR_INVALID_PARAM, ERROR_USAGE, positional_arg_descriptor_t::help_text, 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_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 1236 of file builder.c.

1236 {
1237 if (!config || !stream)
1238 return;
1239
1240 // Print header
1241 if (config->program_name) {
1242 fprintf(stream, "USAGE: %s [OPTIONS]\n\n", config->program_name);
1243 }
1244 if (config->description) {
1245 fprintf(stream, "%s\n\n", config->description);
1246 }
1247
1248 // Group options by group name
1249 const char *current_group = NULL;
1250
1251 for (size_t i = 0; i < config->num_descriptors; i++) {
1252 const option_descriptor_t *desc = &config->descriptors[i];
1253
1254 // Skip binary-level options (they're shown in top-level help only)
1255 if (desc->hide_from_mode_help) {
1256 continue;
1257 }
1258
1259 // Print group header if changed
1260 if (desc->group && (!current_group || strcmp(current_group, desc->group) != 0)) {
1261 fprintf(stream, "\n%s:\n", desc->group);
1262 current_group = desc->group;
1263 }
1264
1265 // Print option
1266 fprintf(stream, " ");
1267 if (desc->short_name) {
1268 fprintf(stream, "-%c, ", desc->short_name);
1269 } else {
1270 fprintf(stream, " ");
1271 }
1272
1273 fprintf(stream, "--%s", desc->long_name);
1274
1275 // Print value placeholder
1276 if (desc->type != OPTION_TYPE_BOOL && desc->type != OPTION_TYPE_ACTION) {
1277 fprintf(stream, " ");
1278 switch (desc->type) {
1279 case OPTION_TYPE_INT:
1280 fprintf(stream, "NUM");
1281 break;
1282 case OPTION_TYPE_STRING:
1283 fprintf(stream, "STR");
1284 break;
1285 case OPTION_TYPE_DOUBLE:
1286 fprintf(stream, "NUM");
1287 break;
1289 fprintf(stream, "VAL");
1290 break;
1291 default:
1292 break;
1293 }
1294 }
1295
1296 // Print description
1297 if (desc->help_text) {
1298 fprintf(stream, " %s", desc->help_text);
1299 }
1300
1301 // Print default if exists
1302 if (desc->default_value && desc->type != OPTION_TYPE_CALLBACK) {
1303 fprintf(stream, " (default: ");
1304 switch (desc->type) {
1305 case OPTION_TYPE_BOOL:
1306 fprintf(stream, "%s", *(const bool *)desc->default_value ? "true" : "false");
1307 break;
1308 case OPTION_TYPE_INT: {
1309 int int_val = 0;
1310 memcpy(&int_val, desc->default_value, sizeof(int));
1311 fprintf(stream, "%d", int_val);
1312 break;
1313 }
1314 case OPTION_TYPE_STRING:
1315 fprintf(stream, "%s", *(const char *const *)desc->default_value);
1316 break;
1317 case OPTION_TYPE_DOUBLE: {
1318 double double_val = 0.0;
1319 memcpy(&double_val, desc->default_value, sizeof(double));
1320 fprintf(stream, "%.2f", double_val);
1321 break;
1322 }
1323 default:
1324 break;
1325 }
1326 fprintf(stream, ")");
1327 }
1328
1329 // Mark as required
1330 if (desc->required) {
1331 fprintf(stream, " [REQUIRED]");
1332 }
1333
1334 // Print env var
1335 if (desc->env_var_name) {
1336 fprintf(stream, " (env: %s)", desc->env_var_name);
1337 }
1338
1339 fprintf(stream, "\n");
1340 }
1341
1342 fprintf(stream, "\n");
1343}
const char * help_text
Description for –help.
Definition builder.h:80
const char * env_var_name
Environment variable fallback (or NULL)
Definition builder.h:87
const char * group
Group name for help sections (e.g., "NETWORK OPTIONS")
Definition builder.h:81
const void * default_value
Pointer to default value (or NULL if required)
Definition builder.h:85
bool required
If true, option must be provided.
Definition builder.h:86
char short_name
Short option char (e.g., 'p', or '\0' if none)
Definition builder.h:73

References option_descriptor_t::default_value, options_config_t::description, options_config_t::descriptors, option_descriptor_t::env_var_name, option_descriptor_t::group, option_descriptor_t::help_text, option_descriptor_t::hide_from_mode_help, option_descriptor_t::long_name, options_config_t::num_descriptors, OPTION_TYPE_ACTION, OPTION_TYPE_BOOL, OPTION_TYPE_CALLBACK, OPTION_TYPE_DOUBLE, OPTION_TYPE_INT, OPTION_TYPE_STRING, options_config_t::program_name, option_descriptor_t::required, option_descriptor_t::short_name, and option_descriptor_t::type.

Referenced by usage_acds(), usage_client(), usage_mirror(), and usage_server().

◆ 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 716 of file builder.c.

716 {
717 if (!config || !options_struct) {
718 return SET_ERRNO(ERROR_INVALID_PARAM, "Config or options struct is NULL");
719 }
720
721 char *base = (char *)options_struct;
722
723 for (size_t i = 0; i < config->num_descriptors; i++) {
724 const option_descriptor_t *desc = &config->descriptors[i];
725 void *field = base + desc->offset;
726
727 // Check environment variable first
728 const char *env_value = NULL;
729 if (desc->env_var_name) {
730 env_value = SAFE_GETENV(desc->env_var_name);
731 }
732
733 switch (desc->type) {
734 case OPTION_TYPE_BOOL: {
735 bool value = false;
736 if (env_value) {
737 // Parse env var as bool
738 value = (strcmp(env_value, "1") == 0 || strcmp(env_value, "true") == 0 || strcmp(env_value, "yes") == 0 ||
739 strcmp(env_value, "on") == 0);
740 } else if (desc->default_value) {
741 value = *(const bool *)desc->default_value;
742 }
743 *(bool *)field = value;
744 break;
745 }
746
747 case OPTION_TYPE_INT: {
748 int value = 0;
749 if (env_value) {
750 // Parse env var as int
751 char *endptr;
752 long parsed = strtol(env_value, &endptr, 10);
753 if (*endptr == '\0' && parsed >= INT_MIN && parsed <= INT_MAX) {
754 value = (int)parsed;
755 }
756 } else if (desc->default_value) {
757 value = *(const int *)desc->default_value;
758 }
759 // Use memcpy to handle potentially misaligned memory safely
760 memcpy(field, &value, sizeof(int));
761 break;
762 }
763
764 case OPTION_TYPE_STRING: {
765 const char *value = NULL;
766 if (env_value) {
767 value = env_value;
768 } else if (desc->default_value) {
769 value = *(const char *const *)desc->default_value;
770 }
771
772 // Copy into fixed-size buffer (same as parsing logic in options_config_parse)
773 // String fields in options_t are char[OPTIONS_BUFF_SIZE] arrays, not pointers
774 if (value && value[0] != '\0') {
775 char *dest = (char *)field;
776 snprintf(dest, OPTIONS_BUFF_SIZE, "%s", value);
777 dest[OPTIONS_BUFF_SIZE - 1] = '\0'; // Ensure null termination
778 }
779 break;
780 }
781
782 case OPTION_TYPE_DOUBLE: {
783 double value = 0.0;
784 if (env_value) {
785 char *endptr;
786 value = strtod(env_value, &endptr);
787 if (*endptr != '\0') {
788 value = 0.0; // Parse failed
789 }
790 } else if (desc->default_value) {
791 value = *(const double *)desc->default_value;
792 }
793 // Use memcpy to handle potentially misaligned memory safely
794 memcpy(field, &value, sizeof(double));
795 break;
796 }
797
799 // Callbacks don't have automatic defaults in set_defaults
800 // Defaults are applied during parsing via the parse_fn callback
801 // Note: value_size is not stored in descriptor, so we can't memcpy safely
802 break;
803
805 // Actions don't store defaults - they execute when encountered
806 break;
807 }
808 }
809
810 return ASCIICHAT_OK;
811}
#define SAFE_GETENV(name)
Definition common.h:378
#define OPTIONS_BUFF_SIZE
Buffer size for option string values.
Definition options.h:176

References ASCIICHAT_OK, option_descriptor_t::default_value, options_config_t::descriptors, option_descriptor_t::env_var_name, ERROR_INVALID_PARAM, options_config_t::num_descriptors, option_descriptor_t::offset, OPTION_TYPE_ACTION, OPTION_TYPE_BOOL, OPTION_TYPE_CALLBACK, OPTION_TYPE_DOUBLE, OPTION_TYPE_INT, OPTION_TYPE_STRING, OPTIONS_BUFF_SIZE, SAFE_GETENV, SET_ERRNO, and option_descriptor_t::type.

Referenced by parse_acds_options(), parse_client_options(), parse_mirror_options(), and parse_server_options().

◆ 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 1125 of file builder.c.

1126 {
1127 if (!config || !options_struct) {
1128 return SET_ERRNO(ERROR_INVALID_PARAM, "Config or options struct is NULL");
1129 }
1130
1131 const char *base = (const char *)options_struct;
1132
1133 // Check required fields
1134 for (size_t i = 0; i < config->num_descriptors; i++) {
1135 const option_descriptor_t *desc = &config->descriptors[i];
1136 if (!desc->required)
1137 continue;
1138
1139 const void *field = base + desc->offset;
1140 bool is_set = false;
1141
1142 switch (desc->type) {
1143 case OPTION_TYPE_BOOL:
1144 is_set = true; // Bools are always set
1145 break;
1146 case OPTION_TYPE_INT:
1147 is_set = true; // Ints are always set
1148 break;
1149 case OPTION_TYPE_STRING:
1150 is_set = (*(const char *const *)field != NULL);
1151 break;
1152 case OPTION_TYPE_DOUBLE:
1153 is_set = true; // Doubles are always set
1154 break;
1156 is_set = (*(const void *const *)field != NULL);
1157 break;
1158 case OPTION_TYPE_ACTION:
1159 is_set = true; // Actions are never required
1160 break;
1161 }
1162
1163 if (!is_set) {
1164 if (error_message) {
1165 if (desc->env_var_name) {
1166 asprintf(error_message, "Required option --%s is not set (set %s env var or use --%s)", desc->long_name,
1167 desc->env_var_name, desc->long_name);
1168 } else {
1169 asprintf(error_message, "Required option --%s is not set", desc->long_name);
1170 }
1171 }
1172 return ERROR_USAGE;
1173 }
1174 }
1175
1176 // Check dependencies
1177 for (size_t i = 0; i < config->num_dependencies; i++) {
1178 const option_dependency_t *dep = &config->dependencies[i];
1179
1180 bool option_is_set = is_option_set(config, options_struct, dep->option_name);
1181 bool depends_is_set = is_option_set(config, options_struct, dep->depends_on);
1182
1183 switch (dep->type) {
1185 if (option_is_set && !depends_is_set) {
1186 if (error_message) {
1187 if (dep->error_message) {
1188 *error_message = strdup(dep->error_message);
1189 } else {
1190 asprintf(error_message, "Option --%s requires --%s to be set", dep->option_name, dep->depends_on);
1191 }
1192 }
1193 return ERROR_USAGE;
1194 }
1195 break;
1196
1198 if (option_is_set && depends_is_set) {
1199 if (error_message) {
1200 if (dep->error_message) {
1201 *error_message = strdup(dep->error_message);
1202 } else {
1203 asprintf(error_message, "Option --%s conflicts with --%s", dep->option_name, dep->depends_on);
1204 }
1205 }
1206 return ERROR_USAGE;
1207 }
1208 break;
1209
1210 case DEPENDENCY_IMPLIES:
1211 // Implies is handled during parsing, not validation
1212 break;
1213 }
1214 }
1215
1216 // Run custom validators
1217 for (size_t i = 0; i < config->num_descriptors; i++) {
1218 const option_descriptor_t *desc = &config->descriptors[i];
1219 if (!desc->validate)
1220 continue;
1221
1222 char *custom_error = NULL;
1223 if (!desc->validate(options_struct, &custom_error)) {
1224 if (error_message) {
1225 *error_message = custom_error;
1226 } else {
1227 free(custom_error);
1228 }
1229 return ERROR_USAGE;
1230 }
1231 }
1232
1233 return ASCIICHAT_OK;
1234}
bool is_set
const char * depends_on
The option it depends on.
Definition builder.h:113
const char * error_message
Custom error message (optional)
Definition builder.h:114
dependency_type_t type
Type of dependency.
Definition builder.h:112
bool(* validate)(const void *options_struct, char **error_msg)
Definition builder.h:90

References ASCIICHAT_OK, 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, is_set, option_descriptor_t::long_name, options_config_t::num_dependencies, options_config_t::num_descriptors, option_descriptor_t::offset, option_dependency_t::option_name, OPTION_TYPE_ACTION, OPTION_TYPE_BOOL, OPTION_TYPE_CALLBACK, OPTION_TYPE_DOUBLE, OPTION_TYPE_INT, OPTION_TYPE_STRING, option_descriptor_t::required, SET_ERRNO, option_descriptor_t::type, option_dependency_t::type, and option_descriptor_t::validate.