ascii-chat 0.8.38
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 asprintf   platform_asprintf
 

Functions

const char * get_option_help_placeholder_str (const option_descriptor_t *desc)
 Get help placeholder string for an option type.
 
int format_option_default_value_str (const option_descriptor_t *desc, char *buf, size_t bufsize)
 Format option default value as a string.
 
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.
 
void ensure_descriptor_capacity (options_builder_t *builder)
 Grow descriptor array if needed.
 
void ensure_dependency_capacity (options_builder_t *builder)
 Grow dependency array if needed.
 
void ensure_positional_arg_capacity (options_builder_t *builder)
 Grow positional arg array if needed.
 
const option_descriptor_t * find_option (const options_config_t *config, const char *long_name)
 Find option descriptor by long name.
 
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)
 
options_builder_t * options_builder_create (size_t struct_size)
 
options_builder_t * options_builder_from_preset (const options_config_t *preset)
 
void options_builder_destroy (options_builder_t *builder)
 
options_config_t * options_builder_build (options_builder_t *builder)
 
void options_config_destroy (options_config_t *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)
 
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_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)
 
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_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 *, char **), const option_metadata_t *metadata)
 
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_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 *, void *, char **), const char *help_text, const char *group, bool required, const char *env_var_name, bool optional_arg, const option_metadata_t *metadata)
 
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)
 
void options_builder_add_descriptor (options_builder_t *builder, const option_descriptor_t *descriptor)
 
void options_builder_set_mode_bitmask (options_builder_t *builder, option_mode_bitmask_t mode_bitmask)
 
void options_builder_set_arg_placeholder (options_builder_t *builder, const char *arg_placeholder)
 
void options_builder_set_enum_values (options_builder_t *builder, const char *option_name, const char **values, const char **descriptions)
 
void options_builder_set_numeric_range (options_builder_t *builder, const char *option_name, int min, int max, int step)
 
void options_builder_set_examples (options_builder_t *builder, const char *option_name, const char **examples)
 
void options_builder_set_input_type (options_builder_t *builder, const char *option_name, option_input_type_t input_type)
 
void options_builder_mark_as_list (options_builder_t *builder, const char *option_name)
 
void options_builder_set_default_value_display (options_builder_t *builder, const char *option_name, const char *default_value)
 
void options_builder_add_dependency_requires (options_builder_t *builder, const char *option_name, const char *depends_on, const char *error_message)
 
void options_builder_add_dependency_conflicts (options_builder_t *builder, const char *option_name, const char *conflicts_with, const char *error_message)
 
void options_builder_add_dependency_implies (options_builder_t *builder, const char *option_name, const char *implies, const char *error_message)
 
void options_builder_add_dependency (options_builder_t *builder, const option_dependency_t *dependency)
 
void options_builder_mark_binary_only (options_builder_t *builder, const char *option_name)
 
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))
 
void options_builder_add_usage (options_builder_t *builder, const char *mode, const char *positional, bool show_options, const char *description)
 
void options_builder_add_example (options_builder_t *builder, uint32_t mode_bitmask, const char *args, const char *description, bool owns_args)
 
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)
 
void options_builder_add_custom_section (options_builder_t *builder, const char *heading, const char *content, option_mode_bitmask_t mode_bitmask)
 
asciichat_error_t options_config_parse_positional (const options_config_t *config, int remaining_argc, char **remaining_argv, void *options_struct)
 
asciichat_error_t options_config_set_defaults (const options_config_t *config, void *options_struct)
 
const option_descriptor_t * find_option_descriptor (const options_config_t *config, const char *opt_name)
 Find option descriptor by short or long name.
 
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)
 
asciichat_error_t options_config_validate (const options_config_t *config, const void *options_struct, char **error_message)
 

Detailed Description

Implementation of options builder API.

Definition in file builder.c.

Macro Definition Documentation

◆ asprintf

#define asprintf   platform_asprintf

Definition at line 25 of file builder.c.

Function Documentation

◆ ensure_dependency_capacity()

void ensure_dependency_capacity ( options_builder_t *  builder)

Grow dependency array if needed.

Definition at line 203 of file builder.c.

203 {
204 if (builder->num_dependencies >= builder->dependency_capacity) {
205 size_t new_capacity = builder->dependency_capacity * 2;
206 option_dependency_t *new_dependencies =
207 SAFE_REALLOC(builder->dependencies, new_capacity * sizeof(option_dependency_t), option_dependency_t *);
208 if (!new_dependencies) {
209 log_fatal("Failed to reallocate dependencies array");
210 return;
211 }
212 builder->dependencies = new_dependencies;
213 builder->dependency_capacity = new_capacity;
214 }
215}

Referenced by options_builder_add_dependency(), options_builder_add_dependency_conflicts(), options_builder_add_dependency_implies(), and options_builder_add_dependency_requires().

◆ ensure_descriptor_capacity()

void ensure_descriptor_capacity ( options_builder_t *  builder)

Grow descriptor array if needed.

Definition at line 186 of file builder.c.

186 {
187 if (builder->num_descriptors >= builder->descriptor_capacity) {
188 size_t new_capacity = builder->descriptor_capacity * 2;
189 option_descriptor_t *new_descriptors =
190 SAFE_REALLOC(builder->descriptors, new_capacity * sizeof(option_descriptor_t), option_descriptor_t *);
191 if (!new_descriptors) {
192 log_fatal("Failed to reallocate descriptors array");
193 return;
194 }
195 builder->descriptors = new_descriptors;
196 builder->descriptor_capacity = new_capacity;
197 }
198}

Referenced by options_builder_add_action(), options_builder_add_bool(), options_builder_add_callback(), options_builder_add_callback_optional(), options_builder_add_callback_with_metadata(), options_builder_add_descriptor(), options_builder_add_double(), options_builder_add_double_with_metadata(), options_builder_add_int(), options_builder_add_int_with_metadata(), and options_builder_add_string().

◆ ensure_positional_arg_capacity()

void ensure_positional_arg_capacity ( options_builder_t *  builder)

Grow positional arg array if needed.

Definition at line 220 of file builder.c.

220 {
221 if (builder->num_positional_args >= builder->positional_arg_capacity) {
222 size_t new_capacity = builder->positional_arg_capacity * 2;
223 if (new_capacity == 0)
224 new_capacity = INITIAL_POSITIONAL_ARG_CAPACITY;
225
226 positional_arg_descriptor_t *new_positional = SAFE_REALLOC(
227 builder->positional_args, new_capacity * sizeof(positional_arg_descriptor_t), positional_arg_descriptor_t *);
228 if (!new_positional) {
229 log_fatal("Failed to reallocate positional_args array");
230 return;
231 }
232 builder->positional_args = new_positional;
233 builder->positional_arg_capacity = new_capacity;
234 }
235}

Referenced by options_builder_add_positional().

◆ find_option()

const option_descriptor_t * find_option ( const options_config_t *  config,
const char *  long_name 
)

Find option descriptor by long name.

Definition at line 317 of file builder.c.

317 {
318 for (size_t i = 0; i < config->num_descriptors; i++) {
319 if (strcmp(config->descriptors[i].long_name, long_name) == 0) {
320 return &config->descriptors[i];
321 }
322 }
323 return NULL;
324}

Referenced by is_option_set().

◆ find_option_descriptor()

const option_descriptor_t * find_option_descriptor ( const options_config_t *  config,
const char *  opt_name 
)

Find option descriptor by short or long name.

Definition at line 1453 of file builder.c.

1453 {
1454 if (!opt_name || opt_name[0] == '\0')
1455 return NULL;
1456
1457 // Handle short options: -x (single dash, single char)
1458 if (opt_name[0] == '-' && opt_name[1] != '-' && opt_name[1] != '\0') {
1459 // This is a short option like -x
1460 char short_char = opt_name[1];
1461 for (size_t i = 0; i < config->num_descriptors; i++) {
1462 if (config->descriptors[i].short_name == short_char) {
1463 return &config->descriptors[i];
1464 }
1465 }
1466 // Short option not found
1467 return NULL;
1468 }
1469
1470 // Handle long options: --name (double dash)
1471 if (opt_name[0] == '-' && opt_name[1] == '-' && opt_name[2] != '\0') {
1472 const char *long_name = opt_name + 2; // Skip the '--'
1473 for (size_t i = 0; i < config->num_descriptors; i++) {
1474 if (strcmp(config->descriptors[i].long_name, long_name) == 0) {
1475 return &config->descriptors[i];
1476 }
1477 }
1478 // Long option not found
1479 return NULL;
1480 }
1481
1482 // Not a recognized option format
1483 return NULL;
1484}

◆ format_option_default_value_str()

int format_option_default_value_str ( const option_descriptor_t *  desc,
char *  buf,
size_t  bufsize 
)

Format option default value as a string.

Parameters
descOption descriptor
bufOutput buffer
bufsizeSize of buffer
Returns
Number of characters written to buf

Definition at line 56 of file builder.c.

56 {
57 if (!desc || !buf || bufsize == 0) {
58 return 0;
59 }
60
61 // For callback options with enums, look up the enum string by matching the default value
62 if (desc->type == OPTION_TYPE_CALLBACK && desc->metadata.enum_values && desc->default_value) {
63 int default_int_val = 0;
64 memcpy(&default_int_val, desc->default_value, sizeof(int));
65
66 // Try to find matching enum value
67 if (desc->metadata.enum_integer_values) {
68 for (size_t i = 0; desc->metadata.enum_values[i] != NULL; i++) {
69 if (desc->metadata.enum_integer_values[i] == default_int_val) {
70 return safe_snprintf(buf, bufsize, "%s", desc->metadata.enum_values[i]);
71 }
72 }
73 } else {
74 // Fallback: assume sequential 0-based indices if integer values not provided
75 // Count enum values to check bounds
76 size_t enum_count = 0;
77 while (desc->metadata.enum_values[enum_count] != NULL) {
78 enum_count++;
79 }
80 if (default_int_val >= 0 && (size_t)default_int_val < enum_count) {
81 return safe_snprintf(buf, bufsize, "%s", desc->metadata.enum_values[default_int_val]);
82 }
83 }
84 }
85
86 // For callback options storing numeric types (int/double/float), format them as numbers
87 if (desc->type == OPTION_TYPE_CALLBACK && desc->default_value && desc->metadata.enum_values == NULL) {
88 // Check if this is a numeric callback by looking for numeric_range metadata
89 // Numeric callbacks have min/max range constraints set (max != 0 indicates a range was set)
90 if (desc->metadata.numeric_range.max != 0 || desc->metadata.numeric_range.min != 0) {
91 // This is a numeric callback option - extract and format the value
92 // Determine if it's int or double based on range (int max is 2147483647)
93 double default_double = 0.0;
94 if (desc->metadata.numeric_range.max <= 2147483647.0 && desc->metadata.numeric_range.min >= -2147483648.0) {
95 // Integer-ranged value (like port 1-65535) - stored as int
96 int default_int = 0;
97 memcpy(&default_int, desc->default_value, sizeof(int));
98 default_double = (double)default_int;
99 } else {
100 // Double-ranged value - stored as double
101 memcpy(&default_double, desc->default_value, sizeof(double));
102 }
103
104 // Format with appropriate precision (remove trailing zeros for integers)
105 char formatted[32];
106 safe_snprintf(formatted, sizeof(formatted), "%.1f", default_double);
107
108 // Remove trailing zeros after decimal point
109 char *dot = strchr(formatted, '.');
110 if (dot) {
111 char *end = formatted + strlen(formatted) - 1;
112 while (end > dot && *end == '0') {
113 end--;
114 }
115 if (*end == '.') {
116 *end = '\0';
117 } else {
118 *(end + 1) = '\0';
119 }
120 }
121
122 return safe_snprintf(buf, bufsize, "%s", formatted);
123 }
124 }
125
126 // For callback options with string defaults (no enum, no numeric range)
127 // Treat default_value as a const char* string pointer
128 if (desc->type == OPTION_TYPE_CALLBACK && desc->default_value && desc->metadata.enum_values == NULL &&
129 desc->metadata.numeric_range.max == 0 && desc->metadata.numeric_range.min == 0) {
130 const char *str_default = (const char *)desc->default_value;
131 if (str_default && str_default[0] != '\0') {
132 return safe_snprintf(buf, bufsize, "%s", str_default);
133 }
134 }
135
136 return options_format_default_value(desc->type, desc->default_value, buf, bufsize);
137}
int options_format_default_value(option_type_t type, const void *default_value, char *buf, size_t bufsize)
int safe_snprintf(char *buffer, size_t buffer_size, const char *format,...)
Safe formatted string printing to buffer.
Definition system.c:456

References options_format_default_value(), and safe_snprintf().

Referenced by options_config_print_options_sections_with_width(), and options_config_print_usage().

◆ get_option_help_placeholder_str()

const char * get_option_help_placeholder_str ( const option_descriptor_t *  desc)

Get help placeholder string for an option type.

Parameters
descOption descriptor
Returns
Pointer to string literal ("INTEGER", "NUMBER", "STRING", "VALUE", "[BOOLEAN]"), custom placeholder, or empty string

Definition at line 38 of file builder.c.

38 {
39 if (!desc) {
40 return "";
41 }
42 // Check for custom placeholder first
43 if (desc->arg_placeholder != NULL) {
44 return desc->arg_placeholder;
45 }
46 return options_get_type_placeholder(desc->type);
47}
const char * options_get_type_placeholder(option_type_t type)

References options_get_type_placeholder().

Referenced by options_config_print_options_sections_with_width(), and options_config_print_usage().

◆ is_option_set()

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 at line 329 of file builder.c.

329 {
330 const option_descriptor_t *desc = find_option(config, option_name);
331 if (!desc)
332 return false;
333
334 const char *base = (const char *)options_struct;
335 const void *field = base + desc->offset;
336
337 // Use handler registry to check if option is set
338 if (desc->type >= 0 && desc->type < (int)(NUM_OPTION_TYPES)) {
339 if (g_builder_handlers[desc->type].is_set) {
340 return g_builder_handlers[desc->type].is_set(field, desc);
341 }
342 }
343
344 return false;
345}
const option_descriptor_t * find_option(const options_config_t *config, const char *long_name)
Find option descriptor by long name.
Definition builder.c:317
const option_builder_handler_t g_builder_handlers[]

References find_option(), and g_builder_handlers.

Referenced by options_config_validate().

◆ option_applies_to_mode()

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.

Parameters
descOption descriptor
modeMode to check (use invalid value like -1 for binary help)
for_binary_helpIf true, show binary options; if false, hide them
Returns
true if option should be shown for this mode

Definition at line 150 of file builder.c.

150 {
151 if (!desc) {
152 SET_ERRNO(ERROR_INVALID_PARAM, "Descriptor is NULL");
153 return false;
154 }
155
156 // When for_binary_help is true (i.e., for 'ascii-chat --help'),
157 // show options that apply to the default mode (DISCOVERY) or binary-level options only.
158 // Don't show mode-specific options for other modes (server, client, mirror, discovery-service).
159 if (for_binary_help) {
160 // Binary-level options or options that apply to discovery mode (the default)
161 option_mode_bitmask_t default_modes = OPTION_MODE_BINARY | OPTION_MODE_DISCOVERY;
162 return (desc->mode_bitmask & default_modes) != 0 && !desc->hide_from_binary_help;
163 }
164
165 // For mode-specific help (non-discovery modes), show only options for that mode.
166 // Do not show binary options here unless it also specifically applies to the mode.
167 // The 'mode' parameter is the actual mode (e.g., MODE_CLIENT, MODE_SERVER).
168 if (mode < 0 || mode > MODE_DISCOVERY) { // Use MODE_INVALID as upper bound for valid modes
169 return false;
170 }
171 option_mode_bitmask_t mode_bit = (1 << mode);
172
173 // Check if it's a binary option. If so, only show if it also explicitly applies to this mode.
174 if ((desc->mode_bitmask & OPTION_MODE_BINARY) && !(desc->mode_bitmask & mode_bit)) {
175 return false; // Binary options not shown in mode-specific help unless also mode-specific
176 }
177
178 bool applies = (desc->mode_bitmask & mode_bit) != 0;
179
180 return applies && !desc->hide_from_mode_help;
181}

Referenced by options_config_print_options_sections_with_width(), and options_config_print_usage().

◆ 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 
)

Definition at line 919 of file builder.c.

920 {
922
923 option_descriptor_t desc = {.long_name = long_name,
924 .short_name = short_name,
925 .type = OPTION_TYPE_ACTION,
926 .offset = 0, // Actions don't store values
927 .help_text = help_text,
928 .group = group,
929 .default_value = NULL,
930 .required = false, // Actions are never required
931 .env_var_name = NULL,
932 .validate = NULL,
933 .parse_fn = NULL,
934 .action_fn = action_fn,
935 .owns_memory = false,
936 .hide_from_mode_help = false,
937 .hide_from_binary_help = false,
938 .mode_bitmask = OPTION_MODE_NONE};
939
940 builder->descriptors[builder->num_descriptors++] = desc;
941
942 // Set hide_from_binary_help after adding (so we can check the option name)
943 if (strcmp(long_name, "create-man-page") == 0) {
944 // Always hide from help and man page (this is a development tool)
945 builder->descriptors[builder->num_descriptors - 1].hide_from_binary_help = true;
946 }
947}
void ensure_descriptor_capacity(options_builder_t *builder)
Grow descriptor array if needed.
Definition builder.c:186

References ensure_descriptor_capacity().

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 
)

Definition at line 627 of file builder.c.

629 {
631
632 static bool default_vals[2] = {false, true};
633
634 option_descriptor_t desc = {.long_name = long_name,
635 .short_name = short_name,
636 .type = OPTION_TYPE_BOOL,
637 .offset = offset,
638 .help_text = help_text,
639 .group = group,
640 .default_value = &default_vals[default_value ? 1 : 0],
641 .required = required,
642 .env_var_name = env_var_name,
643 .mode_bitmask = OPTION_MODE_NONE,
644 .validate = NULL,
645 .parse_fn = NULL,
646 .owns_memory = false};
647
648 builder->descriptors[builder->num_descriptors++] = desc;
649}

References ensure_descriptor_capacity().

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

Definition at line 825 of file builder.c.

828 {
829 (void)value_size; // Unused parameter
831
832 option_descriptor_t desc = {.long_name = long_name,
833 .short_name = short_name,
834 .type = OPTION_TYPE_CALLBACK,
835 .offset = offset,
836 .help_text = help_text,
837 .group = group,
838 .default_value = default_value,
839 .required = required,
840 .env_var_name = env_var_name,
841 .validate = NULL,
842 .parse_fn = parse_fn,
843 .owns_memory = false,
844 .optional_arg = false,
845 .mode_bitmask = OPTION_MODE_NONE};
846
847 builder->descriptors[builder->num_descriptors++] = desc;
848}

References ensure_descriptor_capacity().

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

870 {
871 (void)value_size; // Unused parameter
873
874 option_descriptor_t desc = {.long_name = long_name,
875 .short_name = short_name,
876 .type = OPTION_TYPE_CALLBACK,
877 .offset = offset,
878 .help_text = help_text,
879 .group = group,
880 .default_value = default_value,
881 .required = required,
882 .env_var_name = env_var_name,
883 .validate = NULL,
884 .parse_fn = parse_fn,
885 .owns_memory = false,
886 .optional_arg = optional_arg,
887 .mode_bitmask = OPTION_MODE_NONE};
888
889 builder->descriptors[builder->num_descriptors++] = desc;
890}

References ensure_descriptor_capacity().

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

Definition at line 892 of file builder.c.

896 {
897 (void)value_size; // Unused parameter
899
900 option_descriptor_t desc = {.long_name = long_name,
901 .short_name = short_name,
902 .type = OPTION_TYPE_CALLBACK,
903 .offset = offset,
904 .help_text = help_text,
905 .group = group,
906 .default_value = default_value,
907 .required = required,
908 .env_var_name = env_var_name,
909 .validate = NULL,
910 .parse_fn = parse_fn,
911 .owns_memory = false,
912 .optional_arg = optional_arg,
913 .mode_bitmask = OPTION_MODE_NONE,
914 .metadata = metadata ? *metadata : (option_metadata_t){0}};
915
916 builder->descriptors[builder->num_descriptors++] = desc;
917}

References ensure_descriptor_capacity().

Referenced by options_registry_add_all_to_builder().

◆ 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 
)

Definition at line 1295 of file builder.c.

1296 {
1297 if (!builder || !heading || !content)
1298 return;
1299
1300 ensure_custom_section_capacity(builder);
1301
1302 custom_section_descriptor_t section = {.heading = heading, .content = content, .mode_bitmask = mode_bitmask};
1303
1304 builder->custom_sections[builder->num_custom_sections++] = section;
1305}

Referenced by options_preset_unified().

◆ options_builder_add_dependency()

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

Definition at line 1140 of file builder.c.

1140 {
1141 if (!builder || !dependency)
1142 return;
1143
1145 builder->dependencies[builder->num_dependencies++] = *dependency;
1146}
void ensure_dependency_capacity(options_builder_t *builder)
Grow dependency array if needed.
Definition builder.c:203

References ensure_dependency_capacity().

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 
)

Definition at line 1118 of file builder.c.

1119 {
1121
1122 option_dependency_t dep = {.option_name = option_name,
1123 .type = DEPENDENCY_CONFLICTS,
1124 .depends_on = conflicts_with,
1125 .error_message = error_message};
1126
1127 builder->dependencies[builder->num_dependencies++] = dep;
1128}

References ensure_dependency_capacity().

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 
)

Definition at line 1130 of file builder.c.

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

References ensure_dependency_capacity().

◆ 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 
)

Definition at line 1106 of file builder.c.

1107 {
1109
1110 option_dependency_t dep = {.option_name = option_name,
1111 .type = DEPENDENCY_REQUIRES,
1112 .depends_on = depends_on,
1113 .error_message = error_message};
1114
1115 builder->dependencies[builder->num_dependencies++] = dep;
1116}

References ensure_dependency_capacity().

Referenced by options_preset_unified().

◆ options_builder_add_descriptor()

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

Definition at line 949 of file builder.c.

949 {
950 if (!builder || !descriptor) {
951 SET_ERRNO(ERROR_INVALID_PARAM, "Builder is NULL or descriptor is NULL");
952 return;
953 }
954 return;
955
957 builder->descriptors[builder->num_descriptors++] = *descriptor;
958}

References ensure_descriptor_capacity().

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

759 {
761
762 // Increased from 256 to 1024 to support multiple test builders without overflow
763 static double defaults[1024];
764 static size_t num_defaults = 0;
765
766 if (num_defaults >= 1024) {
767 log_error("Too many double options (max 1024)");
768 return;
769 }
770
771 defaults[num_defaults] = default_value;
772
773 option_descriptor_t desc = {.long_name = long_name,
774 .short_name = short_name,
775 .type = OPTION_TYPE_DOUBLE,
776 .offset = offset,
777 .help_text = help_text,
778 .group = group,
779 .default_value = &defaults[num_defaults++],
780 .required = required,
781 .env_var_name = env_var_name,
782 .validate = validate,
783 .parse_fn = NULL,
784 .owns_memory = false,
785 .mode_bitmask = OPTION_MODE_NONE};
786
787 builder->descriptors[builder->num_descriptors++] = desc;
788}

References ensure_descriptor_capacity().

Referenced by options_registry_add_all_to_builder().

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

Definition at line 790 of file builder.c.

794 {
796
797 static double defaults[1024];
798 static size_t num_defaults = 0;
799
800 if (num_defaults >= 1024) {
801 log_error("Too many double options (max 1024)");
802 return;
803 }
804
805 defaults[num_defaults] = default_value;
806
807 option_descriptor_t desc = {.long_name = long_name,
808 .short_name = short_name,
809 .type = OPTION_TYPE_DOUBLE,
810 .offset = offset,
811 .help_text = help_text,
812 .group = group,
813 .default_value = &defaults[num_defaults++],
814 .required = required,
815 .env_var_name = env_var_name,
816 .validate = validate,
817 .parse_fn = NULL,
818 .owns_memory = false,
819 .mode_bitmask = OPTION_MODE_NONE,
820 .metadata = metadata ? *metadata : (option_metadata_t){0}};
821
822 builder->descriptors[builder->num_descriptors++] = desc;
823}

References ensure_descriptor_capacity().

Referenced by options_registry_add_all_to_builder().

◆ 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 
)

Definition at line 1231 of file builder.c.

1232 {
1233 if (!builder || !description)
1234 return;
1235
1236 ensure_example_capacity(builder);
1237
1238 example_descriptor_t example = {.mode_bitmask = mode_bitmask,
1239 .description = description,
1240 .owns_args_memory = owns_args,
1241 .is_utility_command = false};
1242
1243 if (owns_args) {
1244 // If owning, duplicate the string and track it
1245 char *owned_args = platform_strdup(args);
1246 if (!owned_args) {
1247 log_fatal("Failed to duplicate example args string");
1248 return;
1249 }
1250 example.args = owned_args;
1251 track_owned_string_builder(builder, owned_args);
1252 } else {
1253 example.args = args;
1254 }
1255
1256 builder->examples[builder->num_examples++] = example;
1257}
action_args_t args
char * platform_strdup(const char *s)

References args, 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.

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

Definition at line 1265 of file builder.c.

1266 {
1267 if (!builder || !description)
1268 return;
1269
1270 ensure_example_capacity(builder);
1271
1272 example_descriptor_t example = {.mode_bitmask = mode_bitmask,
1273 .description = description,
1274 .owns_args_memory = false,
1275 .is_utility_command = is_utility_command};
1276
1277 if (args) {
1278 example.args = args;
1279 }
1280
1281 builder->examples[builder->num_examples++] = example;
1282}

References args.

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

Definition at line 651 of file builder.c.

653 {
655
656 // Store default value statically
657 static int defaults[256]; // Assume we won't have more than 256 int options
658 static size_t num_defaults = 0;
659
660 if (num_defaults >= 256) {
661 log_error("Too many int options (max 256)");
662 return;
663 }
664
665 defaults[num_defaults] = default_value;
666
667 option_descriptor_t desc = {.long_name = long_name,
668 .short_name = short_name,
669 .type = OPTION_TYPE_INT,
670 .offset = offset,
671 .help_text = help_text,
672 .group = group,
673 .default_value = &defaults[num_defaults++],
674 .required = required,
675 .env_var_name = env_var_name,
676 .validate = validate,
677 .parse_fn = NULL,
678 .owns_memory = false,
679 .mode_bitmask = OPTION_MODE_NONE};
680
681 builder->descriptors[builder->num_descriptors++] = desc;
682}

References ensure_descriptor_capacity().

Referenced by options_registry_add_all_to_builder().

◆ 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 
)

Definition at line 684 of file builder.c.

688 {
690
691 // Store default value statically
692 static int defaults[256]; // Assume we won't have more than 256 int options
693 static size_t num_defaults = 0;
694
695 if (num_defaults >= 256) {
696 log_error("Too many int options (max 256)");
697 return;
698 }
699
700 defaults[num_defaults] = default_value;
701
702 option_descriptor_t desc = {.long_name = long_name,
703 .short_name = short_name,
704 .type = OPTION_TYPE_INT,
705 .offset = offset,
706 .help_text = help_text,
707 .group = group,
708 .default_value = &defaults[num_defaults++],
709 .required = required,
710 .env_var_name = env_var_name,
711 .validate = validate,
712 .parse_fn = NULL,
713 .owns_memory = false,
714 .mode_bitmask = OPTION_MODE_NONE,
715 .metadata = metadata ? *metadata : (option_metadata_t){0}};
716
717 builder->descriptors[builder->num_descriptors++] = desc;
718}

References ensure_descriptor_capacity().

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 
)

Definition at line 1284 of file builder.c.

1284 {
1285 if (!builder || !name || !description)
1286 return;
1287
1288 ensure_mode_capacity(builder);
1289
1290 help_mode_descriptor_t mode = {.name = name, .description = description};
1291
1292 builder->modes[builder->num_modes++] = mode;
1293}

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 
)

Definition at line 1168 of file builder.c.

1172 {
1173 if (!builder || !name || !parse_fn)
1174 return;
1175
1177
1178 positional_arg_descriptor_t pos_arg = {.name = name,
1179 .help_text = help_text,
1180 .required = required,
1181 .section_heading = section_heading,
1182 .examples = examples,
1183 .num_examples = num_examples,
1184 .mode_bitmask = mode_bitmask,
1185 .parse_fn = parse_fn};
1186
1187 builder->positional_args[builder->num_positional_args++] = pos_arg;
1188}
void ensure_positional_arg_capacity(options_builder_t *builder)
Grow positional arg array if needed.
Definition builder.c:220

References ensure_positional_arg_capacity().

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

Definition at line 720 of file builder.c.

722 {
724
725 // Store default string pointer in static storage to avoid stack-use-after-return
726 // Increased from 256 to 1024 to support multiple test builders without overflow
727 static const char *defaults[1024];
728 static size_t num_defaults = 0;
729
730 const void *default_ptr = NULL;
731 if (default_value) {
732 if (num_defaults >= 1024) {
733 log_error("Too many string options (max 1024)");
734 return;
735 }
736 defaults[num_defaults] = default_value;
737 default_ptr = &defaults[num_defaults++];
738 }
739
740 option_descriptor_t desc = {.long_name = long_name,
741 .short_name = short_name,
742 .type = OPTION_TYPE_STRING,
743 .offset = offset,
744 .help_text = help_text,
745 .group = group,
746 .default_value = default_ptr,
747 .required = required,
748 .env_var_name = env_var_name,
749 .validate = validate,
750 .parse_fn = NULL,
751 .owns_memory = true, // Strings are always owned
752 .mode_bitmask = OPTION_MODE_NONE};
753
754 builder->descriptors[builder->num_descriptors++] = desc;
755}

References ensure_descriptor_capacity().

Referenced by options_registry_add_all_to_builder().

◆ 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 
)

Definition at line 1218 of file builder.c.

1219 {
1220 if (!builder || !description)
1221 return;
1222
1223 ensure_usage_line_capacity(builder);
1224
1225 usage_descriptor_t usage = {
1226 .mode = mode, .positional = positional, .show_options = show_options, .description = description};
1227
1228 builder->usage_lines[builder->num_usage_lines++] = usage;
1229}
void usage(FILE *desc, asciichat_mode_t mode)

References usage().

Referenced by options_preset_unified().

◆ options_builder_build()

options_config_t * options_builder_build ( options_builder_t *  builder)

Definition at line 452 of file builder.c.

452 {
453 if (!builder) {
454 SET_ERRNO(ERROR_INVALID_PARAM, "Builder is NULL");
455 return NULL;
456 }
457
458 options_config_t *config = SAFE_MALLOC(sizeof(options_config_t), options_config_t *);
459 if (!config) {
460 SET_ERRNO(ERROR_MEMORY, "Failed to allocate options config");
461 return NULL;
462 }
463
464 // Allocate and copy descriptors
465 config->descriptors = SAFE_MALLOC(builder->num_descriptors * sizeof(option_descriptor_t), option_descriptor_t *);
466 if (!config->descriptors && builder->num_descriptors > 0) {
467 SAFE_FREE(config);
468 SET_ERRNO(ERROR_MEMORY, "Failed to allocate descriptors");
469 return NULL;
470 }
471 memcpy(config->descriptors, builder->descriptors, builder->num_descriptors * sizeof(option_descriptor_t));
472 config->num_descriptors = builder->num_descriptors;
473
474 // Allocate and copy dependencies
475 config->dependencies = SAFE_MALLOC(builder->num_dependencies * sizeof(option_dependency_t), option_dependency_t *);
476 if (!config->dependencies && builder->num_dependencies > 0) {
477 SAFE_FREE(config->descriptors);
478 SAFE_FREE(config);
479 SET_ERRNO(ERROR_MEMORY, "Failed to allocate dependencies");
480 return NULL;
481 }
482 memcpy(config->dependencies, builder->dependencies, builder->num_dependencies * sizeof(option_dependency_t));
483 config->num_dependencies = builder->num_dependencies;
484
485 // Allocate and copy positional args
486 if (builder->num_positional_args > 0) {
487 config->positional_args =
488 SAFE_MALLOC(builder->num_positional_args * sizeof(positional_arg_descriptor_t), positional_arg_descriptor_t *);
489 if (!config->positional_args) {
490 SAFE_FREE(config->descriptors);
491 SAFE_FREE(config->dependencies);
492 SAFE_FREE(config);
493 SET_ERRNO(ERROR_MEMORY, "Failed to allocate positional args");
494 return NULL;
495 }
496 memcpy(config->positional_args, builder->positional_args,
497 builder->num_positional_args * sizeof(positional_arg_descriptor_t));
498 config->num_positional_args = builder->num_positional_args;
499 } else {
500 config->positional_args = NULL;
501 config->num_positional_args = 0;
502 }
503
504 // Allocate and copy usage lines
505 if (builder->num_usage_lines > 0) {
506 config->usage_lines = SAFE_MALLOC(builder->num_usage_lines * sizeof(usage_descriptor_t), usage_descriptor_t *);
507 if (!config->usage_lines) {
508 SAFE_FREE(config->descriptors);
509 SAFE_FREE(config->dependencies);
510 SAFE_FREE(config->positional_args);
511 SAFE_FREE(config);
512 SET_ERRNO(ERROR_MEMORY, "Failed to allocate usage lines");
513 return NULL;
514 }
515 memcpy(config->usage_lines, builder->usage_lines, builder->num_usage_lines * sizeof(usage_descriptor_t));
516 config->num_usage_lines = builder->num_usage_lines;
517 } else {
518 config->usage_lines = NULL;
519 config->num_usage_lines = 0;
520 }
521
522 // Allocate and copy examples
523 if (builder->num_examples > 0) {
524 config->examples = SAFE_MALLOC(builder->num_examples * sizeof(example_descriptor_t), example_descriptor_t *);
525 if (!config->examples) {
526 SAFE_FREE(config->descriptors);
527 SAFE_FREE(config->dependencies);
528 SAFE_FREE(config->positional_args);
529 SAFE_FREE(config->usage_lines);
530 SAFE_FREE(config);
531 SET_ERRNO(ERROR_MEMORY, "Failed to allocate examples");
532 return NULL;
533 }
534 memcpy(config->examples, builder->examples, builder->num_examples * sizeof(example_descriptor_t));
535 config->num_examples = builder->num_examples;
536 } else {
537 config->examples = NULL;
538 config->num_examples = 0;
539 }
540
541 // Allocate and copy modes
542 if (builder->num_modes > 0) {
543 config->modes = SAFE_MALLOC(builder->num_modes * sizeof(help_mode_descriptor_t), help_mode_descriptor_t *);
544 if (!config->modes) {
545 SAFE_FREE(config->descriptors);
546 SAFE_FREE(config->dependencies);
547 SAFE_FREE(config->positional_args);
548 SAFE_FREE(config->usage_lines);
549 SAFE_FREE(config->examples);
550 SAFE_FREE(config);
551 SET_ERRNO(ERROR_MEMORY, "Failed to allocate modes");
552 return NULL;
553 }
554 memcpy(config->modes, builder->modes, builder->num_modes * sizeof(help_mode_descriptor_t));
555 config->num_modes = builder->num_modes;
556 } else {
557 config->modes = NULL;
558 config->num_modes = 0;
559 }
560
561 // Allocate and copy custom sections
562 if (builder->num_custom_sections > 0) {
563 config->custom_sections =
564 SAFE_MALLOC(builder->num_custom_sections * sizeof(custom_section_descriptor_t), custom_section_descriptor_t *);
565 if (!config->custom_sections) {
566 SAFE_FREE(config->descriptors);
567 SAFE_FREE(config->dependencies);
568 SAFE_FREE(config->positional_args);
569 SAFE_FREE(config->usage_lines);
570 SAFE_FREE(config->examples);
571 SAFE_FREE(config->modes);
572 SAFE_FREE(config);
573 SET_ERRNO(ERROR_MEMORY, "Failed to allocate custom sections");
574 return NULL;
575 }
576 memcpy(config->custom_sections, builder->custom_sections,
577 builder->num_custom_sections * sizeof(custom_section_descriptor_t));
578 config->num_custom_sections = builder->num_custom_sections;
579 } else {
580 config->custom_sections = NULL;
581 config->num_custom_sections = 0;
582 }
583
584 config->struct_size = builder->struct_size;
585 config->program_name = builder->program_name;
586 config->description = builder->description;
587
588 // Initialize memory management
589 config->owned_strings = builder->owned_strings_builder;
590 config->num_owned_strings = builder->num_owned_strings_builder;
591 config->owned_strings_capacity = builder->owned_strings_builder_capacity;
592
593 // Clear builder's ownership so it doesn't free them
594 builder->owned_strings_builder = NULL;
595 builder->num_owned_strings_builder = 0;
596 builder->owned_strings_builder_capacity = 0;
597
598 return config;
599}

Referenced by options_builder_generate_manpage_template(), and options_preset_unified().

◆ options_builder_create()

options_builder_t * options_builder_create ( size_t  struct_size)

Definition at line 351 of file builder.c.

351 {
352 options_builder_t *builder = SAFE_MALLOC(sizeof(options_builder_t), options_builder_t *);
353 if (!builder) {
354 SET_ERRNO(ERROR_MEMORY, "Failed to allocate options builder");
355 return NULL;
356 }
357
358 builder->descriptors = SAFE_MALLOC(INITIAL_DESCRIPTOR_CAPACITY * sizeof(option_descriptor_t), option_descriptor_t *);
359 if (!builder->descriptors) {
360 free(builder);
361 SET_ERRNO(ERROR_MEMORY, "Failed to allocate descriptors array");
362 return NULL;
363 }
364
365 builder->dependencies = SAFE_MALLOC(INITIAL_DEPENDENCY_CAPACITY * sizeof(option_dependency_t), option_dependency_t *);
366 if (!builder->dependencies) {
367 SAFE_FREE(builder->descriptors);
368 SAFE_FREE(builder);
369 SET_ERRNO(ERROR_MEMORY, "Failed to allocate dependencies array");
370 return NULL;
371 }
372
373 builder->num_descriptors = 0;
374 builder->descriptor_capacity = INITIAL_DESCRIPTOR_CAPACITY;
375 builder->num_dependencies = 0;
376 builder->dependency_capacity = INITIAL_DEPENDENCY_CAPACITY;
377 builder->positional_args = NULL;
378 builder->num_positional_args = 0;
379 builder->positional_arg_capacity = 0;
380 builder->usage_lines = NULL;
381 builder->num_usage_lines = 0;
382 builder->usage_line_capacity = 0;
383 builder->examples = NULL;
384 builder->num_examples = 0;
385 builder->example_capacity = 0;
386 builder->modes = NULL;
387 builder->num_modes = 0;
388 builder->mode_capacity = 0;
389 builder->custom_sections = NULL;
390 builder->num_custom_sections = 0;
391 builder->custom_section_capacity = 0;
392 builder->struct_size = struct_size;
393 builder->program_name = NULL;
394 builder->description = NULL;
395 builder->owned_strings_builder = NULL;
396 builder->num_owned_strings_builder = 0;
397 builder->owned_strings_builder_capacity = 0;
398
399 return builder;
400}

Referenced by options_builder_from_preset(), and options_preset_unified().

◆ options_builder_destroy()

void options_builder_destroy ( options_builder_t *  builder)

Definition at line 437 of file builder.c.

437 {
438 if (!builder)
439 return;
440
441 SAFE_FREE(builder->descriptors);
442 SAFE_FREE(builder->dependencies);
443 SAFE_FREE(builder->positional_args);
444 SAFE_FREE(builder->usage_lines);
445 SAFE_FREE(builder->examples);
446 SAFE_FREE(builder->modes);
447 SAFE_FREE(builder->custom_sections);
448 SAFE_FREE(builder->owned_strings_builder); // Free the builder's owned strings
449 SAFE_FREE(builder);
450}

Referenced by options_preset_unified().

◆ options_builder_from_preset()

options_builder_t * options_builder_from_preset ( const options_config_t *  preset)

Definition at line 402 of file builder.c.

402 {
403 if (!preset) {
404 SET_ERRNO(ERROR_INVALID_PARAM, "Preset config is NULL");
405 return NULL;
406 }
407
408 options_builder_t *builder = options_builder_create(preset->struct_size);
409 if (!builder) {
410 return NULL;
411 }
412
413 builder->program_name = preset->program_name;
414 builder->description = preset->description;
415
416 // Copy all descriptors
417 for (size_t i = 0; i < preset->num_descriptors; i++) {
418 options_builder_add_descriptor(builder, &preset->descriptors[i]);
419 }
420
421 // Copy all dependencies
422 for (size_t i = 0; i < preset->num_dependencies; i++) {
423 options_builder_add_dependency(builder, &preset->dependencies[i]);
424 }
425
426 // Copy all positional arguments
427 for (size_t i = 0; i < preset->num_positional_args; i++) {
428 const positional_arg_descriptor_t *pos_arg = &preset->positional_args[i];
429 options_builder_add_positional(builder, pos_arg->name, pos_arg->help_text, pos_arg->required,
430 pos_arg->section_heading, pos_arg->examples, pos_arg->num_examples,
431 pos_arg->mode_bitmask, pos_arg->parse_fn);
432 }
433
434 return builder;
435}
void options_builder_add_dependency(options_builder_t *builder, const option_dependency_t *dependency)
Definition builder.c:1140
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))
Definition builder.c:1168
void options_builder_add_descriptor(options_builder_t *builder, const option_descriptor_t *descriptor)
Definition builder.c:949
options_builder_t * options_builder_create(size_t struct_size)
Definition builder.c:351

References options_builder_add_dependency(), options_builder_add_descriptor(), options_builder_add_positional(), and options_builder_create().

◆ options_builder_mark_as_list()

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

Definition at line 1069 of file builder.c.

1069 {
1070 if (!builder || !option_name) {
1071 SET_ERRNO(ERROR_INVALID_PARAM, "Builder or option_name is NULL");
1072 return;
1073 }
1074
1075 int idx = find_descriptor_in_builder(builder, option_name);
1076 if (idx < 0) {
1077 SET_ERRNO(ERROR_INVALID_PARAM, "Option '%s' not found in builder", option_name);
1078 return;
1079 }
1080
1081 option_descriptor_t *desc = &builder->descriptors[idx];
1082 desc->metadata.is_list = true;
1083}

◆ options_builder_mark_binary_only()

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

Definition at line 1148 of file builder.c.

1148 {
1149 if (!builder || !option_name)
1150 return;
1151
1152 // Find the option by name and mark it
1153 for (size_t i = 0; i < builder->num_descriptors; i++) {
1154 if (strcmp(builder->descriptors[i].long_name, option_name) == 0) {
1155 builder->descriptors[i].hide_from_mode_help = true;
1156 return;
1157 }
1158 }
1159
1160 // Option not found - this is a programming error
1161 log_warn("Attempted to mark non-existent option '%s' as binary-only", option_name);
1162}

◆ options_builder_set_arg_placeholder()

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

Definition at line 968 of file builder.c.

968 {
969 if (!builder || builder->num_descriptors == 0) {
970 SET_ERRNO(ERROR_INVALID_STATE, "Builder is NULL or has no descriptors");
971 return;
972 }
973 builder->descriptors[builder->num_descriptors - 1].arg_placeholder = arg_placeholder;
974}

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 
)

Definition at line 1085 of file builder.c.

1086 {
1087 if (!builder || !option_name) {
1088 SET_ERRNO(ERROR_INVALID_PARAM, "Builder or option_name is NULL");
1089 return;
1090 }
1091
1092 int idx = find_descriptor_in_builder(builder, option_name);
1093 if (idx < 0) {
1094 SET_ERRNO(ERROR_INVALID_PARAM, "Option '%s' not found in builder", option_name);
1095 return;
1096 }
1097
1098 option_descriptor_t *desc = &builder->descriptors[idx];
1099 desc->metadata.default_value = default_value;
1100}

◆ 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 
)

Definition at line 998 of file builder.c.

999 {
1000 if (!builder || !option_name || !values || !descriptions) {
1001 SET_ERRNO(ERROR_INVALID_PARAM, "Builder or arguments are NULL");
1002 return;
1003 }
1004
1005 int idx = find_descriptor_in_builder(builder, option_name);
1006 if (idx < 0) {
1007 SET_ERRNO(ERROR_INVALID_PARAM, "Option '%s' not found in builder", option_name);
1008 return;
1009 }
1010
1011 option_descriptor_t *desc = &builder->descriptors[idx];
1012 desc->metadata.enum_values = values;
1013 desc->metadata.enum_descriptions = descriptions;
1014}

◆ options_builder_set_examples()

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

Definition at line 1035 of file builder.c.

1035 {
1036 if (!builder || !option_name || !examples) {
1037 SET_ERRNO(ERROR_INVALID_PARAM, "Builder or arguments are NULL");
1038 return;
1039 }
1040
1041 int idx = find_descriptor_in_builder(builder, option_name);
1042 if (idx < 0) {
1043 SET_ERRNO(ERROR_INVALID_PARAM, "Option '%s' not found in builder", option_name);
1044 return;
1045 }
1046
1047 option_descriptor_t *desc = &builder->descriptors[idx];
1048 desc->metadata.examples = examples;
1049 // Note: examples array must be null-terminated
1050}

◆ 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 
)

Definition at line 1052 of file builder.c.

1053 {
1054 if (!builder || !option_name) {
1055 SET_ERRNO(ERROR_INVALID_PARAM, "Builder or option_name is NULL");
1056 return;
1057 }
1058
1059 int idx = find_descriptor_in_builder(builder, option_name);
1060 if (idx < 0) {
1061 SET_ERRNO(ERROR_INVALID_PARAM, "Option '%s' not found in builder", option_name);
1062 return;
1063 }
1064
1065 option_descriptor_t *desc = &builder->descriptors[idx];
1066 desc->metadata.input_type = input_type;
1067}

◆ options_builder_set_mode_bitmask()

void options_builder_set_mode_bitmask ( options_builder_t *  builder,
option_mode_bitmask_t  mode_bitmask 
)

Definition at line 960 of file builder.c.

960 {
961 if (!builder || builder->num_descriptors == 0) {
962 SET_ERRNO(ERROR_INVALID_STATE, "Builder is NULL or has no descriptors");
963 return;
964 }
965 builder->descriptors[builder->num_descriptors - 1].mode_bitmask = mode_bitmask;
966}

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 
)

Definition at line 1016 of file builder.c.

1017 {
1018 if (!builder || !option_name) {
1019 SET_ERRNO(ERROR_INVALID_PARAM, "Builder or option_name is NULL");
1020 return;
1021 }
1022
1023 int idx = find_descriptor_in_builder(builder, option_name);
1024 if (idx < 0) {
1025 SET_ERRNO(ERROR_INVALID_PARAM, "Option '%s' not found in builder", option_name);
1026 return;
1027 }
1028
1029 option_descriptor_t *desc = &builder->descriptors[idx];
1030 desc->metadata.numeric_range.min = min;
1031 desc->metadata.numeric_range.max = max;
1032 desc->metadata.numeric_range.step = step;
1033}

◆ options_config_destroy()

void options_config_destroy ( options_config_t *  config)

Definition at line 601 of file builder.c.

601 {
602 if (!config)
603 return;
604
605 SAFE_FREE(config->descriptors);
606 SAFE_FREE(config->dependencies);
607 SAFE_FREE(config->positional_args);
608 SAFE_FREE(config->usage_lines);
609 SAFE_FREE(config->examples);
610 SAFE_FREE(config->modes);
611 SAFE_FREE(config->custom_sections);
612
613 // Free all owned strings before freeing the array
614 // Use SAFE_FREE because platform_strdup uses SAFE_MALLOC (mimalloc on Windows)
615 for (size_t i = 0; i < config->num_owned_strings; i++) {
616 SAFE_FREE(config->owned_strings[i]);
617 }
618 SAFE_FREE(config->owned_strings);
619
620 SAFE_FREE(config);
621}

Referenced by 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 
)

Definition at line 1750 of file builder.c.

1752 {
1753 if (!config || !options_struct) {
1754 return SET_ERRNO(ERROR_INVALID_PARAM, "Config or options struct is NULL");
1755 }
1756
1757 // Use the new unified parser that handles mixed positional and flag arguments
1758 asciichat_error_t result = options_config_parse_unified(config, argc, argv, options_struct, detected_mode);
1759 if (result != ASCIICHAT_OK) {
1760 return result;
1761 }
1762
1763 // Since unified parser handles all arguments, there are no remaining args
1764 // (backward compatibility: set remaining_argc to 0)
1765 if (remaining_argc) {
1766 *remaining_argc = 0;
1767 }
1768 if (remaining_argv) {
1769 *remaining_argv = NULL;
1770 }
1771
1772 return ASCIICHAT_OK;
1773}

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 
)

Definition at line 1307 of file builder.c.

1308 {
1309 if (!config || !options_struct) {
1310 return SET_ERRNO(ERROR_INVALID_PARAM, "Config or options struct is NULL");
1311 }
1312
1313 if (remaining_argc < 0 || (remaining_argc > 0 && !remaining_argv)) {
1314 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid remaining args");
1315 }
1316
1317 // No positional args defined - but we got some positional arguments
1318 if (config->num_positional_args == 0 && remaining_argc > 0) {
1319 log_error("Error: Unexpected positional argument '%s'", remaining_argv[0]);
1320 return ERROR_USAGE;
1321 }
1322
1323 // Check if required positional args are missing
1324 for (size_t i = 0; i < config->num_positional_args; i++) {
1325 const positional_arg_descriptor_t *pos_arg = &config->positional_args[i];
1326 if (pos_arg->required && remaining_argc == 0) {
1327 log_error("Error: Missing required positional argument '%s'", pos_arg->name);
1328 if (pos_arg->help_text) {
1329 log_error(" %s", pos_arg->help_text);
1330 }
1331 return ERROR_USAGE;
1332 }
1333 }
1334
1335 // Parse positional arguments
1336 int arg_index = 0;
1337 for (size_t i = 0; i < config->num_positional_args && arg_index < remaining_argc; i++) {
1338 const positional_arg_descriptor_t *pos_arg = &config->positional_args[i];
1339
1340 const char *arg = remaining_argv[arg_index];
1341 char **remaining = (arg_index + 1 < remaining_argc) ? &remaining_argv[arg_index + 1] : NULL;
1342 int num_remaining = remaining_argc - arg_index - 1;
1343 char *error_msg = NULL;
1344
1345 // Call custom parser
1346 int consumed = pos_arg->parse_fn(arg, options_struct, remaining, num_remaining, &error_msg);
1347
1348 if (consumed < 0) {
1349 // Parse error
1350 if (error_msg) {
1351 log_error("Error parsing positional argument '%s': %s", pos_arg->name, error_msg);
1352 free(error_msg);
1353 } else {
1354 log_error("Error parsing positional argument '%s': %s", pos_arg->name, arg);
1355 }
1356 return ERROR_USAGE;
1357 }
1358
1359 // Advance by consumed args (usually 1, but can be more for multi-arg parsers)
1360 arg_index += consumed;
1361 }
1362
1363 // Check for extra unconsumed positional arguments
1364 if (arg_index < remaining_argc) {
1365 log_error("Error: Unexpected positional argument '%s'", remaining_argv[arg_index]);
1366 return ERROR_USAGE;
1367 }
1368
1369 return ASCIICHAT_OK;
1370}

◆ options_config_set_defaults()

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

Definition at line 1376 of file builder.c.

1376 {
1377 if (!config || !options_struct) {
1378 return SET_ERRNO(ERROR_INVALID_PARAM, "Config or options struct is NULL");
1379 }
1380
1381 char *base = (char *)options_struct;
1382
1383 for (size_t i = 0; i < config->num_descriptors; i++) {
1384 const option_descriptor_t *desc = &config->descriptors[i];
1385 void *field = base + desc->offset;
1386
1387 // Check environment variable first
1388 const char *env_value = NULL;
1389 if (desc->env_var_name) {
1390 env_value = SAFE_GETENV(desc->env_var_name);
1391 }
1392
1393 // Use handler registry to apply environment variables and defaults
1394 if (desc->type >= 0 && desc->type < (int)(NUM_OPTION_TYPES)) {
1395 if (g_builder_handlers[desc->type].apply_env) {
1396 g_builder_handlers[desc->type].apply_env(field, env_value, desc);
1397 }
1398 } else if (desc->type == OPTION_TYPE_CALLBACK && desc->parse_fn && desc->default_value) {
1399 // Special handling for callbacks with parse_fn
1400 char *error_msg = NULL;
1401 desc->parse_fn(NULL, field, &error_msg);
1402 if (error_msg) {
1403 free(error_msg);
1404 }
1405 }
1406 }
1407
1408 return ASCIICHAT_OK;
1409}

References g_builder_handlers.

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 
)

Definition at line 1775 of file builder.c.

1776 {
1777 if (!config || !options_struct) {
1778 return SET_ERRNO(ERROR_INVALID_PARAM, "Config or options struct is NULL");
1779 }
1780
1781 const char *base = (const char *)options_struct;
1782
1783 // Check required fields
1784 for (size_t i = 0; i < config->num_descriptors; i++) {
1785 const option_descriptor_t *desc = &config->descriptors[i];
1786 if (!desc->required)
1787 continue;
1788
1789 const void *field = base + desc->offset;
1790 // Use handler registry to check if option is set (for required field validation)
1791 bool is_set = true;
1792 if (desc->type >= 0 && desc->type < (int)(NUM_OPTION_TYPES)) {
1793 if (g_builder_handlers[desc->type].is_set) {
1794 is_set = g_builder_handlers[desc->type].is_set(field, desc);
1795 }
1796 }
1797
1798 if (!is_set) {
1799 if (error_message) {
1800 if (desc->env_var_name) {
1801 asprintf(error_message, "Required option --%s is not set (set %s env var or use --%s)", desc->long_name,
1802 desc->env_var_name, desc->long_name);
1803 } else {
1804 asprintf(error_message, "Required option --%s is not set", desc->long_name);
1805 }
1806 }
1807 return ERROR_USAGE;
1808 }
1809 }
1810
1811 // Check dependencies
1812 for (size_t i = 0; i < config->num_dependencies; i++) {
1813 const option_dependency_t *dep = &config->dependencies[i];
1814
1815 bool option_is_set = is_option_set(config, options_struct, dep->option_name);
1816 bool depends_is_set = is_option_set(config, options_struct, dep->depends_on);
1817
1818 switch (dep->type) {
1819 case DEPENDENCY_REQUIRES:
1820 if (option_is_set && !depends_is_set) {
1821 if (error_message) {
1822 if (dep->error_message) {
1823 *error_message = platform_strdup(dep->error_message);
1824 } else {
1825 asprintf(error_message, "Option --%s requires --%s to be set", dep->option_name, dep->depends_on);
1826 }
1827 }
1828 return ERROR_USAGE;
1829 }
1830 break;
1831
1832 case DEPENDENCY_CONFLICTS:
1833 if (option_is_set && depends_is_set) {
1834 if (error_message) {
1835 if (dep->error_message) {
1836 *error_message = platform_strdup(dep->error_message);
1837 } else {
1838 asprintf(error_message, "Option --%s conflicts with --%s", dep->option_name, dep->depends_on);
1839 }
1840 }
1841 return ERROR_USAGE;
1842 }
1843 break;
1844
1845 case DEPENDENCY_IMPLIES:
1846 // Implies is handled during parsing, not validation
1847 break;
1848 }
1849 }
1850
1851 // Run custom validators
1852 for (size_t i = 0; i < config->num_descriptors; i++) {
1853 const option_descriptor_t *desc = &config->descriptors[i];
1854 if (!desc->validate)
1855 continue;
1856
1857 char *custom_error = NULL;
1858 if (!desc->validate(options_struct, &custom_error)) {
1859 if (error_message) {
1860 *error_message = custom_error;
1861 } else {
1862 free(custom_error);
1863 }
1864 return ERROR_USAGE;
1865 }
1866 }
1867
1868 // Cross-field validation: Check for conflicting color options
1869 // Cannot use --color with --color-mode none
1870 const options_t *opts = (const options_t *)options_struct;
1871 if (opts->color && opts->color_mode == TERM_COLOR_NONE) {
1872 if (error_message) {
1873 asprintf(error_message, "Option --color cannot be used with --color-mode=none (conflicting color settings)");
1874 }
1875 return ERROR_USAGE;
1876 }
1877
1878 return ASCIICHAT_OK;
1879}
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:329
#define asprintf
Definition builder.c:25
bool is_set

References asprintf, g_builder_handlers, is_option_set(), is_set, and platform_strdup().

Referenced by validate_options_and_report().