7#include <ascii-chat/options/builder/internal.h>
8#include <ascii-chat/options/builder.h>
9#include <ascii-chat/options/common.h>
10#include <ascii-chat/common.h>
11#include <ascii-chat/log/logging.h>
12#include <ascii-chat/options/layout.h>
13#include <ascii-chat/platform/abstraction.h>
14#include <ascii-chat/platform/terminal.h>
15#include <ascii-chat/asciichat_errno.h>
16#include <ascii-chat/util/utf8.h>
17#include <ascii-chat/util/string.h>
23#include <ascii-chat/platform/util.h>
24#include <ascii-chat/platform/terminal.h>
25#define asprintf platform_asprintf
43 if (desc->arg_placeholder != NULL) {
44 return desc->arg_placeholder;
57 if (!desc || !buf || bufsize == 0) {
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));
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]);
76 size_t enum_count = 0;
77 while (desc->metadata.enum_values[enum_count] != NULL) {
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]);
87 if (desc->type == OPTION_TYPE_CALLBACK && desc->default_value && desc->metadata.enum_values == NULL) {
90 if (desc->metadata.numeric_range.max != 0 || desc->metadata.numeric_range.min != 0) {
93 double default_double = 0.0;
94 if (desc->metadata.numeric_range.max <= 2147483647.0 && desc->metadata.numeric_range.min >= -2147483648.0) {
97 memcpy(&default_int, desc->default_value,
sizeof(
int));
98 default_double = (double)default_int;
101 memcpy(&default_double, desc->default_value,
sizeof(
double));
106 safe_snprintf(formatted,
sizeof(formatted),
"%.1f", default_double);
109 char *dot = strchr(formatted,
'.');
111 char *end = formatted + strlen(formatted) - 1;
112 while (end > dot && *end ==
'0') {
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') {
152 SET_ERRNO(ERROR_INVALID_PARAM,
"Descriptor is NULL");
159 if (for_binary_help) {
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;
168 if (mode < 0 || mode > MODE_DISCOVERY) {
171 option_mode_bitmask_t mode_bit = (1 << mode);
174 if ((desc->mode_bitmask & OPTION_MODE_BINARY) && !(desc->mode_bitmask & mode_bit)) {
178 bool applies = (desc->mode_bitmask & mode_bit) != 0;
180 return applies && !desc->hide_from_mode_help;
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");
195 builder->descriptors = new_descriptors;
196 builder->descriptor_capacity = new_capacity;
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");
212 builder->dependencies = new_dependencies;
213 builder->dependency_capacity = new_capacity;
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;
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");
232 builder->positional_args = new_positional;
233 builder->positional_arg_capacity = new_capacity;
240static void ensure_usage_line_capacity(options_builder_t *builder) {
241 if (builder->num_usage_lines >= builder->usage_line_capacity) {
242 size_t new_capacity = builder->usage_line_capacity * 2;
243 if (new_capacity == 0)
244 new_capacity = INITIAL_DESCRIPTOR_CAPACITY;
246 usage_descriptor_t *new_usage =
247 SAFE_REALLOC(builder->usage_lines, new_capacity *
sizeof(usage_descriptor_t), usage_descriptor_t *);
249 log_fatal(
"Failed to reallocate usage_lines array");
252 builder->usage_lines = new_usage;
253 builder->usage_line_capacity = new_capacity;
260static void ensure_example_capacity(options_builder_t *builder) {
261 if (builder->num_examples >= builder->example_capacity) {
262 size_t new_capacity = builder->example_capacity * 2;
263 if (new_capacity == 0)
264 new_capacity = INITIAL_DESCRIPTOR_CAPACITY;
266 example_descriptor_t *new_examples =
267 SAFE_REALLOC(builder->examples, new_capacity *
sizeof(example_descriptor_t), example_descriptor_t *);
269 log_fatal(
"Failed to reallocate examples array");
272 builder->examples = new_examples;
273 builder->example_capacity = new_capacity;
280static void ensure_mode_capacity(options_builder_t *builder) {
281 if (builder->num_modes >= builder->mode_capacity) {
282 size_t new_capacity = builder->mode_capacity * 2;
283 if (new_capacity == 0)
284 new_capacity = INITIAL_DESCRIPTOR_CAPACITY;
286 help_mode_descriptor_t *new_modes =
287 SAFE_REALLOC(builder->modes, new_capacity *
sizeof(help_mode_descriptor_t), help_mode_descriptor_t *);
289 log_fatal(
"Failed to reallocate modes array");
292 builder->modes = new_modes;
293 builder->mode_capacity = new_capacity;
297static void ensure_custom_section_capacity(options_builder_t *builder) {
298 if (builder->num_custom_sections >= builder->custom_section_capacity) {
299 size_t new_capacity = builder->custom_section_capacity * 2;
300 if (new_capacity == 0)
301 new_capacity = INITIAL_DESCRIPTOR_CAPACITY;
303 custom_section_descriptor_t *new_sections = SAFE_REALLOC(
304 builder->custom_sections, new_capacity *
sizeof(custom_section_descriptor_t), custom_section_descriptor_t *);
306 log_fatal(
"Failed to reallocate custom sections array");
309 builder->custom_sections = new_sections;
310 builder->custom_section_capacity = new_capacity;
317const option_descriptor_t *
find_option(
const options_config_t *config,
const char *long_name) {
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];
329bool is_option_set(
const options_config_t *config,
const void *options_struct,
const char *option_name) {
330 const option_descriptor_t *desc =
find_option(config, option_name);
334 const char *base = (
const char *)options_struct;
335 const void *field = base + desc->offset;
338 if (desc->type >= 0 && desc->type < (
int)(NUM_OPTION_TYPES)) {
352 options_builder_t *builder = SAFE_MALLOC(
sizeof(options_builder_t), options_builder_t *);
354 SET_ERRNO(ERROR_MEMORY,
"Failed to allocate options builder");
358 builder->descriptors = SAFE_MALLOC(INITIAL_DESCRIPTOR_CAPACITY *
sizeof(option_descriptor_t), option_descriptor_t *);
359 if (!builder->descriptors) {
361 SET_ERRNO(ERROR_MEMORY,
"Failed to allocate descriptors array");
365 builder->dependencies = SAFE_MALLOC(INITIAL_DEPENDENCY_CAPACITY *
sizeof(option_dependency_t), option_dependency_t *);
366 if (!builder->dependencies) {
367 SAFE_FREE(builder->descriptors);
369 SET_ERRNO(ERROR_MEMORY,
"Failed to allocate dependencies array");
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;
404 SET_ERRNO(ERROR_INVALID_PARAM,
"Preset config is NULL");
413 builder->program_name = preset->program_name;
414 builder->description = preset->description;
417 for (
size_t i = 0; i < preset->num_descriptors; i++) {
422 for (
size_t i = 0; i < preset->num_dependencies; i++) {
427 for (
size_t i = 0; i < preset->num_positional_args; i++) {
428 const positional_arg_descriptor_t *pos_arg = &preset->positional_args[i];
430 pos_arg->section_heading, pos_arg->examples, pos_arg->num_examples,
431 pos_arg->mode_bitmask, pos_arg->parse_fn);
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);
454 SET_ERRNO(ERROR_INVALID_PARAM,
"Builder is NULL");
458 options_config_t *config = SAFE_MALLOC(
sizeof(options_config_t), options_config_t *);
460 SET_ERRNO(ERROR_MEMORY,
"Failed to allocate options config");
465 config->descriptors = SAFE_MALLOC(builder->num_descriptors *
sizeof(option_descriptor_t), option_descriptor_t *);
466 if (!config->descriptors && builder->num_descriptors > 0) {
468 SET_ERRNO(ERROR_MEMORY,
"Failed to allocate descriptors");
471 memcpy(config->descriptors, builder->descriptors, builder->num_descriptors *
sizeof(option_descriptor_t));
472 config->num_descriptors = builder->num_descriptors;
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);
479 SET_ERRNO(ERROR_MEMORY,
"Failed to allocate dependencies");
482 memcpy(config->dependencies, builder->dependencies, builder->num_dependencies *
sizeof(option_dependency_t));
483 config->num_dependencies = builder->num_dependencies;
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);
493 SET_ERRNO(ERROR_MEMORY,
"Failed to allocate positional args");
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;
500 config->positional_args = NULL;
501 config->num_positional_args = 0;
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);
512 SET_ERRNO(ERROR_MEMORY,
"Failed to allocate usage lines");
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;
518 config->usage_lines = NULL;
519 config->num_usage_lines = 0;
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);
531 SET_ERRNO(ERROR_MEMORY,
"Failed to allocate examples");
534 memcpy(config->examples, builder->examples, builder->num_examples *
sizeof(example_descriptor_t));
535 config->num_examples = builder->num_examples;
537 config->examples = NULL;
538 config->num_examples = 0;
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);
551 SET_ERRNO(ERROR_MEMORY,
"Failed to allocate modes");
554 memcpy(config->modes, builder->modes, builder->num_modes *
sizeof(help_mode_descriptor_t));
555 config->num_modes = builder->num_modes;
557 config->modes = NULL;
558 config->num_modes = 0;
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);
573 SET_ERRNO(ERROR_MEMORY,
"Failed to allocate custom sections");
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;
580 config->custom_sections = NULL;
581 config->num_custom_sections = 0;
584 config->struct_size = builder->struct_size;
585 config->program_name = builder->program_name;
586 config->description = builder->description;
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;
594 builder->owned_strings_builder = NULL;
595 builder->num_owned_strings_builder = 0;
596 builder->owned_strings_builder_capacity = 0;
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);
615 for (
size_t i = 0; i < config->num_owned_strings; i++) {
616 SAFE_FREE(config->owned_strings[i]);
618 SAFE_FREE(config->owned_strings);
628 bool default_value,
const char *help_text,
const char *group,
bool required,
629 const char *env_var_name) {
632 static bool default_vals[2] = {
false,
true};
634 option_descriptor_t desc = {.long_name = long_name,
635 .short_name = short_name,
636 .type = OPTION_TYPE_BOOL,
638 .help_text = help_text,
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,
646 .owns_memory =
false};
648 builder->descriptors[builder->num_descriptors++] = desc;
652 int default_value,
const char *help_text,
const char *group,
bool required,
653 const char *env_var_name,
bool (*validate)(
const void *,
char **)) {
657 static int defaults[256];
658 static size_t num_defaults = 0;
660 if (num_defaults >= 256) {
661 log_error(
"Too many int options (max 256)");
665 defaults[num_defaults] = default_value;
667 option_descriptor_t desc = {.long_name = long_name,
668 .short_name = short_name,
669 .type = OPTION_TYPE_INT,
671 .help_text = help_text,
673 .default_value = &defaults[num_defaults++],
674 .required = required,
675 .env_var_name = env_var_name,
676 .validate = validate,
678 .owns_memory =
false,
679 .mode_bitmask = OPTION_MODE_NONE};
681 builder->descriptors[builder->num_descriptors++] = desc;
685 size_t offset,
int default_value,
const char *help_text,
const char *group,
686 bool required,
const char *env_var_name,
687 bool (*validate)(
const void *options_struct,
char **error_msg),
688 const option_metadata_t *metadata) {
692 static int defaults[256];
693 static size_t num_defaults = 0;
695 if (num_defaults >= 256) {
696 log_error(
"Too many int options (max 256)");
700 defaults[num_defaults] = default_value;
702 option_descriptor_t desc = {.long_name = long_name,
703 .short_name = short_name,
704 .type = OPTION_TYPE_INT,
706 .help_text = help_text,
708 .default_value = &defaults[num_defaults++],
709 .required = required,
710 .env_var_name = env_var_name,
711 .validate = validate,
713 .owns_memory =
false,
714 .mode_bitmask = OPTION_MODE_NONE,
715 .metadata = metadata ? *metadata : (option_metadata_t){0}};
717 builder->descriptors[builder->num_descriptors++] = desc;
721 const char *default_value,
const char *help_text,
const char *group,
bool required,
722 const char *env_var_name,
bool (*validate)(
const void *,
char **)) {
727 static const char *defaults[1024];
728 static size_t num_defaults = 0;
730 const void *default_ptr = NULL;
732 if (num_defaults >= 1024) {
733 log_error(
"Too many string options (max 1024)");
736 defaults[num_defaults] = default_value;
737 default_ptr = &defaults[num_defaults++];
740 option_descriptor_t desc = {.long_name = long_name,
741 .short_name = short_name,
742 .type = OPTION_TYPE_STRING,
744 .help_text = help_text,
746 .default_value = default_ptr,
747 .required = required,
748 .env_var_name = env_var_name,
749 .validate = validate,
752 .mode_bitmask = OPTION_MODE_NONE};
754 builder->descriptors[builder->num_descriptors++] = desc;
758 double default_value,
const char *help_text,
const char *group,
bool required,
759 const char *env_var_name,
bool (*validate)(
const void *,
char **)) {
763 static double defaults[1024];
764 static size_t num_defaults = 0;
766 if (num_defaults >= 1024) {
767 log_error(
"Too many double options (max 1024)");
771 defaults[num_defaults] = default_value;
773 option_descriptor_t desc = {.long_name = long_name,
774 .short_name = short_name,
775 .type = OPTION_TYPE_DOUBLE,
777 .help_text = help_text,
779 .default_value = &defaults[num_defaults++],
780 .required = required,
781 .env_var_name = env_var_name,
782 .validate = validate,
784 .owns_memory =
false,
785 .mode_bitmask = OPTION_MODE_NONE};
787 builder->descriptors[builder->num_descriptors++] = desc;
791 size_t offset,
double default_value,
const char *help_text,
792 const char *group,
bool required,
const char *env_var_name,
793 bool (*validate)(
const void *,
char **),
794 const option_metadata_t *metadata) {
797 static double defaults[1024];
798 static size_t num_defaults = 0;
800 if (num_defaults >= 1024) {
801 log_error(
"Too many double options (max 1024)");
805 defaults[num_defaults] = default_value;
807 option_descriptor_t desc = {.long_name = long_name,
808 .short_name = short_name,
809 .type = OPTION_TYPE_DOUBLE,
811 .help_text = help_text,
813 .default_value = &defaults[num_defaults++],
814 .required = required,
815 .env_var_name = env_var_name,
816 .validate = validate,
818 .owns_memory =
false,
819 .mode_bitmask = OPTION_MODE_NONE,
820 .metadata = metadata ? *metadata : (option_metadata_t){0}};
822 builder->descriptors[builder->num_descriptors++] = desc;
826 const void *default_value,
size_t value_size,
827 bool (*parse_fn)(
const char *,
void *,
char **),
const char *help_text,
828 const char *group,
bool required,
const char *env_var_name) {
832 option_descriptor_t desc = {.long_name = long_name,
833 .short_name = short_name,
834 .type = OPTION_TYPE_CALLBACK,
836 .help_text = help_text,
838 .default_value = default_value,
839 .required = required,
840 .env_var_name = env_var_name,
842 .parse_fn = parse_fn,
843 .owns_memory =
false,
844 .optional_arg =
false,
845 .mode_bitmask = OPTION_MODE_NONE};
847 builder->descriptors[builder->num_descriptors++] = desc;
867 size_t offset,
const void *default_value,
size_t value_size,
868 bool (*parse_fn)(
const char *,
void *,
char **),
const char *help_text,
869 const char *group,
bool required,
const char *env_var_name,
874 option_descriptor_t desc = {.long_name = long_name,
875 .short_name = short_name,
876 .type = OPTION_TYPE_CALLBACK,
878 .help_text = help_text,
880 .default_value = default_value,
881 .required = required,
882 .env_var_name = env_var_name,
884 .parse_fn = parse_fn,
885 .owns_memory =
false,
886 .optional_arg = optional_arg,
887 .mode_bitmask = OPTION_MODE_NONE};
889 builder->descriptors[builder->num_descriptors++] = desc;
893 size_t offset,
const void *default_value,
size_t value_size,
894 bool (*parse_fn)(
const char *,
void *,
char **),
const char *help_text,
895 const char *group,
bool required,
const char *env_var_name,
896 bool optional_arg,
const option_metadata_t *metadata) {
900 option_descriptor_t desc = {.long_name = long_name,
901 .short_name = short_name,
902 .type = OPTION_TYPE_CALLBACK,
904 .help_text = help_text,
906 .default_value = default_value,
907 .required = required,
908 .env_var_name = env_var_name,
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}};
916 builder->descriptors[builder->num_descriptors++] = desc;
920 void (*action_fn)(
void),
const char *help_text,
const char *group) {
923 option_descriptor_t desc = {.long_name = long_name,
924 .short_name = short_name,
925 .type = OPTION_TYPE_ACTION,
927 .help_text = help_text,
929 .default_value = NULL,
931 .env_var_name = 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};
940 builder->descriptors[builder->num_descriptors++] = desc;
943 if (strcmp(long_name,
"create-man-page") == 0) {
945 builder->descriptors[builder->num_descriptors - 1].hide_from_binary_help =
true;
950 if (!builder || !descriptor) {
951 SET_ERRNO(ERROR_INVALID_PARAM,
"Builder is NULL or descriptor is NULL");
957 builder->descriptors[builder->num_descriptors++] = *descriptor;
961 if (!builder || builder->num_descriptors == 0) {
962 SET_ERRNO(ERROR_INVALID_STATE,
"Builder is NULL or has no descriptors");
965 builder->descriptors[builder->num_descriptors - 1].mode_bitmask = mode_bitmask;
969 if (!builder || builder->num_descriptors == 0) {
970 SET_ERRNO(ERROR_INVALID_STATE,
"Builder is NULL or has no descriptors");
973 builder->descriptors[builder->num_descriptors - 1].arg_placeholder = arg_placeholder;
986static int find_descriptor_in_builder(
const options_builder_t *builder,
const char *option_name) {
987 if (!builder || !option_name) {
990 for (
size_t i = 0; i < builder->num_descriptors; i++) {
991 if (builder->descriptors[i].long_name && strcmp(builder->descriptors[i].long_name, option_name) == 0) {
999 const char **descriptions) {
1000 if (!builder || !option_name || !values || !descriptions) {
1001 SET_ERRNO(ERROR_INVALID_PARAM,
"Builder or arguments are NULL");
1005 int idx = find_descriptor_in_builder(builder, option_name);
1007 SET_ERRNO(ERROR_INVALID_PARAM,
"Option '%s' not found in builder", option_name);
1011 option_descriptor_t *desc = &builder->descriptors[idx];
1012 desc->metadata.enum_values = values;
1013 desc->metadata.enum_descriptions = descriptions;
1018 if (!builder || !option_name) {
1019 SET_ERRNO(ERROR_INVALID_PARAM,
"Builder or option_name is NULL");
1023 int idx = find_descriptor_in_builder(builder, option_name);
1025 SET_ERRNO(ERROR_INVALID_PARAM,
"Option '%s' not found in builder", option_name);
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;
1036 if (!builder || !option_name || !examples) {
1037 SET_ERRNO(ERROR_INVALID_PARAM,
"Builder or arguments are NULL");
1041 int idx = find_descriptor_in_builder(builder, option_name);
1043 SET_ERRNO(ERROR_INVALID_PARAM,
"Option '%s' not found in builder", option_name);
1047 option_descriptor_t *desc = &builder->descriptors[idx];
1048 desc->metadata.examples = examples;
1053 option_input_type_t input_type) {
1054 if (!builder || !option_name) {
1055 SET_ERRNO(ERROR_INVALID_PARAM,
"Builder or option_name is NULL");
1059 int idx = find_descriptor_in_builder(builder, option_name);
1061 SET_ERRNO(ERROR_INVALID_PARAM,
"Option '%s' not found in builder", option_name);
1065 option_descriptor_t *desc = &builder->descriptors[idx];
1066 desc->metadata.input_type = input_type;
1070 if (!builder || !option_name) {
1071 SET_ERRNO(ERROR_INVALID_PARAM,
"Builder or option_name is NULL");
1075 int idx = find_descriptor_in_builder(builder, option_name);
1077 SET_ERRNO(ERROR_INVALID_PARAM,
"Option '%s' not found in builder", option_name);
1081 option_descriptor_t *desc = &builder->descriptors[idx];
1082 desc->metadata.is_list =
true;
1086 const char *default_value) {
1087 if (!builder || !option_name) {
1088 SET_ERRNO(ERROR_INVALID_PARAM,
"Builder or option_name is NULL");
1092 int idx = find_descriptor_in_builder(builder, option_name);
1094 SET_ERRNO(ERROR_INVALID_PARAM,
"Option '%s' not found in builder", option_name);
1098 option_descriptor_t *desc = &builder->descriptors[idx];
1099 desc->metadata.default_value = default_value;
1107 const char *depends_on,
const char *error_message) {
1110 option_dependency_t dep = {.option_name = option_name,
1111 .type = DEPENDENCY_REQUIRES,
1112 .depends_on = depends_on,
1113 .error_message = error_message};
1115 builder->dependencies[builder->num_dependencies++] = dep;
1119 const char *conflicts_with,
const char *error_message) {
1122 option_dependency_t dep = {.option_name = option_name,
1123 .type = DEPENDENCY_CONFLICTS,
1124 .depends_on = conflicts_with,
1125 .error_message = error_message};
1127 builder->dependencies[builder->num_dependencies++] = dep;
1131 const char *error_message) {
1134 option_dependency_t dep = {
1135 .option_name = option_name, .type = DEPENDENCY_IMPLIES, .depends_on = implies, .error_message = error_message};
1137 builder->dependencies[builder->num_dependencies++] = dep;
1141 if (!builder || !dependency)
1145 builder->dependencies[builder->num_dependencies++] = *dependency;
1149 if (!builder || !option_name)
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;
1161 log_warn(
"Attempted to mark non-existent option '%s' as binary-only", option_name);
1169 const char *section_heading,
const char **examples,
size_t num_examples,
1170 option_mode_bitmask_t mode_bitmask,
1171 int (*parse_fn)(
const char *arg,
void *config,
char **remaining,
int num_remaining,
1172 char **error_msg)) {
1173 if (!builder || !name || !parse_fn)
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};
1187 builder->positional_args[builder->num_positional_args++] = pos_arg;
1197static void track_owned_string_builder(options_builder_t *builder,
char *str) {
1201 if (builder->num_owned_strings_builder >= builder->owned_strings_builder_capacity) {
1202 size_t new_capacity = builder->owned_strings_builder_capacity * 2;
1203 if (new_capacity == 0)
1204 new_capacity = INITIAL_OWNED_STRINGS_CAPACITY;
1206 char **new_owned = SAFE_REALLOC(builder->owned_strings_builder, new_capacity *
sizeof(
char *),
char **);
1208 log_fatal(
"Failed to reallocate builder owned_strings array");
1211 builder->owned_strings_builder = new_owned;
1212 builder->owned_strings_builder_capacity = new_capacity;
1215 builder->owned_strings_builder[builder->num_owned_strings_builder++] = str;
1219 const char *description) {
1220 if (!builder || !description)
1223 ensure_usage_line_capacity(builder);
1225 usage_descriptor_t
usage = {
1226 .mode = mode, .positional = positional, .show_options = show_options, .description = description};
1228 builder->usage_lines[builder->num_usage_lines++] =
usage;
1232 const char *description,
bool owns_args) {
1233 if (!builder || !description)
1236 ensure_example_capacity(builder);
1238 example_descriptor_t example = {.mode_bitmask = mode_bitmask,
1239 .description = description,
1240 .owns_args_memory = owns_args,
1241 .is_utility_command =
false};
1247 log_fatal(
"Failed to duplicate example args string");
1250 example.args = owned_args;
1251 track_owned_string_builder(builder, owned_args);
1253 example.args =
args;
1256 builder->examples[builder->num_examples++] = example;
1266 const char *description,
bool is_utility_command) {
1267 if (!builder || !description)
1270 ensure_example_capacity(builder);
1272 example_descriptor_t example = {.mode_bitmask = mode_bitmask,
1273 .description = description,
1274 .owns_args_memory =
false,
1275 .is_utility_command = is_utility_command};
1278 example.args =
args;
1281 builder->examples[builder->num_examples++] = example;
1285 if (!builder || !name || !description)
1288 ensure_mode_capacity(builder);
1290 help_mode_descriptor_t mode = {.name = name, .description = description};
1292 builder->modes[builder->num_modes++] = mode;
1296 option_mode_bitmask_t mode_bitmask) {
1297 if (!builder || !heading || !content)
1300 ensure_custom_section_capacity(builder);
1302 custom_section_descriptor_t section = {.heading = heading, .content = content, .mode_bitmask = mode_bitmask};
1304 builder->custom_sections[builder->num_custom_sections++] = section;
1308 char **remaining_argv,
void *options_struct) {
1309 if (!config || !options_struct) {
1310 return SET_ERRNO(ERROR_INVALID_PARAM,
"Config or options struct is NULL");
1313 if (remaining_argc < 0 || (remaining_argc > 0 && !remaining_argv)) {
1314 return SET_ERRNO(ERROR_INVALID_PARAM,
"Invalid remaining args");
1318 if (config->num_positional_args == 0 && remaining_argc > 0) {
1319 log_error(
"Error: Unexpected positional argument '%s'", remaining_argv[0]);
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);
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];
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;
1346 int consumed = pos_arg->parse_fn(arg, options_struct, remaining, num_remaining, &error_msg);
1351 log_error(
"Error parsing positional argument '%s': %s", pos_arg->name, error_msg);
1354 log_error(
"Error parsing positional argument '%s': %s", pos_arg->name, arg);
1360 arg_index += consumed;
1364 if (arg_index < remaining_argc) {
1365 log_error(
"Error: Unexpected positional argument '%s'", remaining_argv[arg_index]);
1369 return ASCIICHAT_OK;
1377 if (!config || !options_struct) {
1378 return SET_ERRNO(ERROR_INVALID_PARAM,
"Config or options struct is NULL");
1381 char *base = (
char *)options_struct;
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;
1388 const char *env_value = NULL;
1389 if (desc->env_var_name) {
1390 env_value = SAFE_GETENV(desc->env_var_name);
1394 if (desc->type >= 0 && desc->type < (
int)(NUM_OPTION_TYPES)) {
1398 }
else if (desc->type == OPTION_TYPE_CALLBACK && desc->parse_fn && desc->default_value) {
1400 char *error_msg = NULL;
1401 desc->parse_fn(NULL, field, &error_msg);
1408 return ASCIICHAT_OK;
1420static bool is_mode_keyword(
const char *arg) {
1421 if (!arg || arg[0] ==
'\0')
1425 static const char *mode_keywords[] = {
"server",
"client",
"mirror",
"acds",
"discovery",
"discovery-service", NULL};
1427 for (
int i = 0; mode_keywords[i] != NULL; i++) {
1428 if (strcmp(arg, mode_keywords[i]) == 0) {
1444static bool is_flag_argument(
const char *arg) {
1445 if (!arg || arg[0] ==
'\0')
1447 return arg[0] ==
'-' || is_mode_keyword(arg);
1454 if (!opt_name || opt_name[0] ==
'\0')
1458 if (opt_name[0] ==
'-' && opt_name[1] !=
'-' && opt_name[1] !=
'\0') {
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];
1471 if (opt_name[0] ==
'-' && opt_name[1] ==
'-' && opt_name[2] !=
'\0') {
1472 const char *long_name = opt_name + 2;
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];
1501static asciichat_error_t parse_single_flag_with_mode(
const options_config_t *config,
char **argv,
int argv_index,
1502 int argc,
void *options_struct, option_mode_bitmask_t mode_bitmask,
1503 int *consumed_count) {
1504 *consumed_count = 1;
1506 if (argv_index < 0 || argv_index >= argc) {
1507 return SET_ERRNO(ERROR_INVALID_PARAM,
"argv_index %d out of bounds [0, %d)", argv_index, argc);
1510 const char *arg = argv[argv_index];
1513 if (strcmp(arg,
"--grep") == 0) {
1515 if (argv_index + 1 < argc) {
1516 *consumed_count = 2;
1518 return ASCIICHAT_OK;
1520 char *long_opt_value = NULL;
1521 char *equals = NULL;
1522 char *arg_copy = NULL;
1523 const char *arg_for_lookup = arg;
1526 if (strncmp(arg,
"--", 2) == 0) {
1527 equals = strchr(arg,
'=');
1529 long_opt_value = equals + 1;
1533 size_t arg_len = equals - arg;
1534 arg_copy = SAFE_MALLOC(arg_len + 1,
char *);
1536 memcpy(arg_copy, arg, arg_len);
1537 arg_copy[arg_len] =
'\0';
1538 arg_for_lookup = arg_copy;
1540 return ERROR_MEMORY;
1551 log_plain_stderr(
"Unknown option: %s. %s", arg_for_lookup, suggestion);
1553 log_plain_stderr(
"Unknown option: %s", arg_for_lookup);
1555 SAFE_FREE(arg_copy);
1562 if (desc->mode_bitmask != 0 && desc->mode_bitmask != OPTION_MODE_BINARY &&
1563 !(desc->mode_bitmask & OPTION_MODE_BINARY)) {
1565 if (!(desc->mode_bitmask & mode_bitmask)) {
1568 SAFE_FREE(arg_copy);
1569 return SET_ERRNO(ERROR_USAGE,
"Option %s is not available in this mode. Available in modes: %s", arg,
1574 void *field = (
char *)options_struct + desc->offset;
1575 const char *opt_value = NULL;
1578 if (desc->type != OPTION_TYPE_BOOL && desc->type != OPTION_TYPE_ACTION) {
1579 if (long_opt_value) {
1581 opt_value = long_opt_value;
1582 }
else if (argv_index + 1 < argc && !is_flag_argument(argv[argv_index + 1])) {
1584 opt_value = argv[argv_index + 1];
1585 *consumed_count = 2;
1589 if (!desc->optional_arg) {
1592 return SET_ERRNO(ERROR_USAGE,
"Option %s requires a value", arg);
1600 if (desc->type < NUM_OPTION_TYPES) {
1603 if (desc->type == OPTION_TYPE_BOOL || desc->type == OPTION_TYPE_ACTION) {
1605 asciichat_error_t result = handler->apply_cli(field, NULL, desc);
1606 if (result != ASCIICHAT_OK) {
1613 asciichat_error_t result = handler->apply_cli(field, opt_value, desc);
1614 if (result != ASCIICHAT_OK) {
1615 SAFE_FREE(arg_copy);
1620 SAFE_FREE(arg_copy);
1621 return SET_ERRNO(ERROR_USAGE,
"Invalid option type for %s", arg);
1624 SAFE_FREE(arg_copy);
1625 return ASCIICHAT_OK;
1642static asciichat_error_t options_config_parse_unified(
const options_config_t *config,
int argc,
char **argv,
1643 void *options_struct, option_mode_bitmask_t detected_mode) {
1644 if (!config || !options_struct) {
1645 return SET_ERRNO(ERROR_INVALID_PARAM,
"Config or options struct is NULL");
1649 char **positional_args = SAFE_MALLOC(argc *
sizeof(
char *),
char **);
1650 if (!positional_args) {
1651 return SET_ERRNO(ERROR_MEMORY,
"Failed to allocate positional args buffer");
1653 int positional_count = 0;
1654 bool end_of_options =
false;
1657 for (
int i = 1; i < argc; i++) {
1658 const char *arg = argv[i];
1661 if (!end_of_options && strcmp(arg,
"--") == 0) {
1662 end_of_options =
true;
1668 if (end_of_options || (!is_flag_argument(arg) && !is_mode_keyword(arg))) {
1669 positional_args[positional_count++] = argv[i];
1674 if (is_mode_keyword(arg)) {
1680 asciichat_error_t err =
1681 parse_single_flag_with_mode(config, argv, i, argc, options_struct, detected_mode, &consumed);
1682 if (err != ASCIICHAT_OK) {
1683 SAFE_FREE(positional_args);
1688 i += (consumed - 1);
1692 if (config->num_positional_args > 0) {
1695 for (
size_t pos_idx = 0; pos_idx < config->num_positional_args && arg_index < positional_count; pos_idx++) {
1696 const positional_arg_descriptor_t *pos_arg = &config->positional_args[pos_idx];
1699 if (pos_arg->mode_bitmask != 0 && !(pos_arg->mode_bitmask & detected_mode)) {
1703 const char *arg = positional_args[arg_index];
1704 char **remaining = (arg_index + 1 < positional_count) ? &positional_args[arg_index + 1] : NULL;
1705 int num_remaining = positional_count - arg_index - 1;
1706 char *error_msg = NULL;
1708 int consumed = pos_arg->parse_fn(arg, options_struct, remaining, num_remaining, &error_msg);
1711 log_error(
"Error parsing positional argument '%s': %s", pos_arg->name, error_msg ? error_msg : arg);
1713 SAFE_FREE(positional_args);
1717 arg_index += consumed;
1721 if (arg_index < positional_count) {
1722 log_error(
"Error: Unexpected positional argument '%s'", positional_args[arg_index]);
1723 SAFE_FREE(positional_args);
1728 for (
size_t i = 0; i < config->num_positional_args; i++) {
1729 const positional_arg_descriptor_t *pos_arg = &config->positional_args[i];
1730 if (pos_arg->required && i >= (
size_t)arg_index) {
1731 log_error(
"Error: Missing required positional argument '%s'", pos_arg->name);
1732 if (pos_arg->help_text) {
1733 log_error(
" %s", pos_arg->help_text);
1735 SAFE_FREE(positional_args);
1739 }
else if (positional_count > 0) {
1741 log_error(
"Error: Unexpected positional argument '%s'", positional_args[0]);
1742 SAFE_FREE(positional_args);
1746 SAFE_FREE(positional_args);
1747 return ASCIICHAT_OK;
1751 option_mode_bitmask_t detected_mode,
int *remaining_argc,
1752 char ***remaining_argv) {
1753 if (!config || !options_struct) {
1754 return SET_ERRNO(ERROR_INVALID_PARAM,
"Config or options struct is NULL");
1758 asciichat_error_t result = options_config_parse_unified(config, argc, argv, options_struct, detected_mode);
1759 if (result != ASCIICHAT_OK) {
1765 if (remaining_argc) {
1766 *remaining_argc = 0;
1768 if (remaining_argv) {
1769 *remaining_argv = NULL;
1772 return ASCIICHAT_OK;
1776 char **error_message) {
1777 if (!config || !options_struct) {
1778 return SET_ERRNO(ERROR_INVALID_PARAM,
"Config or options struct is NULL");
1781 const char *base = (
const char *)options_struct;
1784 for (
size_t i = 0; i < config->num_descriptors; i++) {
1785 const option_descriptor_t *desc = &config->descriptors[i];
1786 if (!desc->required)
1789 const void *field = base + desc->offset;
1792 if (desc->type >= 0 && desc->type < (
int)(NUM_OPTION_TYPES)) {
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);
1804 asprintf(error_message,
"Required option --%s is not set", desc->long_name);
1812 for (
size_t i = 0; i < config->num_dependencies; i++) {
1813 const option_dependency_t *dep = &config->dependencies[i];
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);
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) {
1825 asprintf(error_message,
"Option --%s requires --%s to be set", dep->option_name, dep->depends_on);
1832 case DEPENDENCY_CONFLICTS:
1833 if (option_is_set && depends_is_set) {
1834 if (error_message) {
1835 if (dep->error_message) {
1838 asprintf(error_message,
"Option --%s conflicts with --%s", dep->option_name, dep->depends_on);
1845 case DEPENDENCY_IMPLIES:
1852 for (
size_t i = 0; i < config->num_descriptors; i++) {
1853 const option_descriptor_t *desc = &config->descriptors[i];
1854 if (!desc->validate)
1857 char *custom_error = NULL;
1858 if (!desc->validate(options_struct, &custom_error)) {
1859 if (error_message) {
1860 *error_message = custom_error;
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)");
1878 return ASCIICHAT_OK;
const char * get_option_help_placeholder_str(const option_descriptor_t *desc)
Get help placeholder string for an option type.
void options_builder_add_dependency(options_builder_t *builder, const option_dependency_t *dependency)
void options_builder_set_numeric_range(options_builder_t *builder, const char *option_name, int min, int max, int step)
void options_builder_mark_binary_only(options_builder_t *builder, const char *option_name)
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_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 **))
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)
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)
void options_builder_set_arg_placeholder(options_builder_t *builder, const char *arg_placeholder)
void ensure_positional_arg_capacity(options_builder_t *builder)
Grow positional arg array if needed.
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_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_set_mode_bitmask(options_builder_t *builder, option_mode_bitmask_t mode_bitmask)
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_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)
options_builder_t * options_builder_from_preset(const options_config_t *preset)
const option_descriptor_t * find_option(const options_config_t *config, const char *long_name)
Find option descriptor by long name.
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_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_dependency_conflicts(options_builder_t *builder, const char *option_name, const char *conflicts_with, const char *error_message)
asciichat_error_t options_config_validate(const options_config_t *config, const void *options_struct, char **error_message)
asciichat_error_t options_config_parse_positional(const options_config_t *config, int remaining_argc, char **remaining_argv, void *options_struct)
void options_builder_add_mode(options_builder_t *builder, const char *name, const char *description)
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_set_default_value_display(options_builder_t *builder, const char *option_name, const char *default_value)
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_set_defaults(const options_config_t *config, void *options_struct)
int format_option_default_value_str(const option_descriptor_t *desc, char *buf, size_t bufsize)
Format option default value as a string.
void options_builder_set_enum_values(options_builder_t *builder, const char *option_name, const char **values, const char **descriptions)
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_dependency_implies(options_builder_t *builder, const char *option_name, const char *implies, const char *error_message)
void options_builder_add_descriptor(options_builder_t *builder, const option_descriptor_t *descriptor)
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 ensure_dependency_capacity(options_builder_t *builder)
Grow dependency array if needed.
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 **))
const option_descriptor_t * find_option_descriptor(const options_config_t *config, const char *opt_name)
Find option descriptor by short or long name.
void options_builder_mark_as_list(options_builder_t *builder, const char *option_name)
void options_builder_set_examples(options_builder_t *builder, const char *option_name, const char **examples)
void options_builder_destroy(options_builder_t *builder)
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_config_destroy(options_config_t *config)
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 **))
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.
options_config_t * options_builder_build(options_builder_t *builder)
void options_builder_set_input_type(options_builder_t *builder, const char *option_name, option_input_type_t input_type)
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.
options_builder_t * options_builder_create(size_t struct_size)
const option_builder_handler_t g_builder_handlers[]
int options_format_default_value(option_type_t type, const void *default_value, char *buf, size_t bufsize)
const char * format_available_modes(option_mode_bitmask_t mode_bitmask)
void usage(FILE *desc, asciichat_mode_t mode)
const char * find_similar_option_with_mode(const char *unknown_opt, const options_config_t *config, option_mode_bitmask_t current_mode_bitmask)
const char * options_get_type_placeholder(option_type_t type)
int safe_snprintf(char *buffer, size_t buffer_size, const char *format,...)
Safe formatted string printing to buffer.