14#include <ascii-chat/options/builder/internal.h>
15#include <ascii-chat/options/common.h>
16#include <ascii-chat/util/string.h>
22static bool is_set_bool(
const void *field,
const option_descriptor_t *desc);
23static bool is_set_int(
const void *field,
const option_descriptor_t *desc);
24static bool is_set_string(
const void *field,
const option_descriptor_t *desc);
25static bool is_set_double(
const void *field,
const option_descriptor_t *desc);
26static bool is_set_callback(
const void *field,
const option_descriptor_t *desc);
27static bool is_set_action(
const void *field,
const option_descriptor_t *desc);
29static void apply_env_bool(
void *field,
const char *env_value,
const option_descriptor_t *desc);
30static void apply_env_int(
void *field,
const char *env_value,
const option_descriptor_t *desc);
31static void apply_env_string(
void *field,
const char *env_value,
const option_descriptor_t *desc);
32static void apply_env_double(
void *field,
const char *env_value,
const option_descriptor_t *desc);
33static void apply_env_callback(
void *field,
const char *env_value,
const option_descriptor_t *desc);
34static void apply_env_action(
void *field,
const char *env_value,
const option_descriptor_t *desc);
36static asciichat_error_t apply_cli_bool(
void *field,
const char *opt_value,
const option_descriptor_t *desc);
37static asciichat_error_t apply_cli_int(
void *field,
const char *opt_value,
const option_descriptor_t *desc);
38static asciichat_error_t apply_cli_string(
void *field,
const char *opt_value,
const option_descriptor_t *desc);
39static asciichat_error_t apply_cli_double(
void *field,
const char *opt_value,
const option_descriptor_t *desc);
40static asciichat_error_t apply_cli_callback(
void *field,
const char *opt_value,
const option_descriptor_t *desc);
41static asciichat_error_t apply_cli_action(
void *field,
const char *opt_value,
const option_descriptor_t *desc);
43static void format_help_placeholder_bool(
char *buf,
size_t bufsize);
44static void format_help_placeholder_int(
char *buf,
size_t bufsize);
45static void format_help_placeholder_string(
char *buf,
size_t bufsize);
46static void format_help_placeholder_double(
char *buf,
size_t bufsize);
47static void format_help_placeholder_callback(
char *buf,
size_t bufsize);
48static void format_help_placeholder_action(
char *buf,
size_t bufsize);
55 [OPTION_TYPE_BOOL] = {is_set_bool, apply_env_bool, apply_cli_bool, format_help_placeholder_bool},
56 [OPTION_TYPE_INT] = {is_set_int, apply_env_int, apply_cli_int, format_help_placeholder_int},
57 [OPTION_TYPE_STRING] = {is_set_string, apply_env_string, apply_cli_string, format_help_placeholder_string},
58 [OPTION_TYPE_DOUBLE] = {is_set_double, apply_env_double, apply_cli_double, format_help_placeholder_double},
59 [OPTION_TYPE_CALLBACK] = {is_set_callback, apply_env_callback, apply_cli_callback,
60 format_help_placeholder_callback},
61 [OPTION_TYPE_ACTION] = {is_set_action, apply_env_action, apply_cli_action, format_help_placeholder_action},
63static bool is_set_bool(
const void *field,
const option_descriptor_t *desc) {
65 unsigned char value_byte = 0;
66 memcpy(&value_byte, field, 1);
67 bool value = (value_byte != 0);
69 bool default_val =
false;
70 if (desc && desc->default_value) {
71 unsigned char default_byte = 0;
72 memcpy(&default_byte, desc->default_value, 1);
73 default_val = (default_byte != 0);
75 return value != default_val;
78static bool is_set_int(
const void *field,
const option_descriptor_t *desc) {
79 int value = *(
const int *)field;
80 int default_val = desc->default_value ? *(
const int *)desc->default_value : 0;
81 return value != default_val;
84static bool is_set_string(
const void *field,
const option_descriptor_t *desc) {
85 const char *value = (
const char *)field;
86 const char *default_val = NULL;
87 if (desc->default_value) {
88 default_val = *(
const char *
const *)desc->default_value;
91 return (value && value[0] !=
'\0');
93 return strcmp(value, default_val) != 0;
96static bool is_set_double(
const void *field,
const option_descriptor_t *desc) {
98 double default_val = 0.0;
99 memcpy(&value, field,
sizeof(
double));
100 if (desc->default_value) {
101 memcpy(&default_val, desc->default_value,
sizeof(
double));
103 return value != default_val;
106static bool is_set_callback(
const void *field,
const option_descriptor_t *desc) {
108 const void *value = NULL;
109 memcpy(&value, field,
sizeof(
const void *));
110 return value != NULL;
113static bool is_set_action(
const void *field,
const option_descriptor_t *desc) {
120static void apply_env_bool(
void *field,
const char *env_value,
const option_descriptor_t *desc) {
122 unsigned char current_byte = 0;
123 memcpy(¤t_byte, field, 1);
124 bool current_value = (current_byte != 0);
127 bool default_val =
false;
128 if (desc && desc->default_value) {
129 unsigned char default_byte = 0;
130 memcpy(&default_byte, desc->default_value, 1);
131 default_val = (default_byte != 0);
134 if (current_value != default_val) {
140 value = (strcmp(env_value,
"1") == 0 || strcmp(env_value,
"true") == 0 || strcmp(env_value,
"yes") == 0 ||
141 strcmp(env_value,
"on") == 0);
142 }
else if (desc && desc->default_value) {
143 unsigned char default_byte = 0;
144 memcpy(&default_byte, desc->default_value, 1);
145 value = (default_byte != 0);
148 unsigned char value_byte = value ? 1 : 0;
149 memcpy(field, &value_byte, 1);
152static void apply_env_int(
void *field,
const char *env_value,
const option_descriptor_t *desc) {
153 int current_value = 0;
154 memcpy(¤t_value, field,
sizeof(
int));
155 int default_val = desc->default_value ? *(
const int *)desc->default_value : 0;
156 if (current_value != default_val) {
162 long parsed = strtol(env_value, &endptr, 10);
163 if (*endptr ==
'\0' && parsed >= INT_MIN && parsed <= INT_MAX) {
166 }
else if (desc->default_value) {
167 value = *(
const int *)desc->default_value;
169 memcpy(field, &value,
sizeof(
int));
172static void apply_env_string(
void *field,
const char *env_value,
const option_descriptor_t *desc) {
173 const char *current_value = (
const char *)field;
174 const char *default_val = NULL;
175 if (desc->default_value) {
176 default_val = *(
const char *
const *)desc->default_value;
178 if (current_value && current_value[0] !=
'\0') {
179 if (!default_val || strcmp(current_value, default_val) != 0) {
183 const char *value = NULL;
186 }
else if (desc->default_value) {
187 value = *(
const char *
const *)desc->default_value;
189 if (value && value[0] !=
'\0') {
190 char *dest = (
char *)field;
192 dest[OPTIONS_BUFF_SIZE - 1] =
'\0';
196static void apply_env_double(
void *field,
const char *env_value,
const option_descriptor_t *desc) {
197 double current_value = 0.0;
198 memcpy(¤t_value, field,
sizeof(
double));
199 double default_val = 0.0;
200 if (desc->default_value) {
201 memcpy(&default_val, desc->default_value,
sizeof(
double));
203 if (current_value != default_val) {
209 value = strtod(env_value, &endptr);
210 if (*endptr !=
'\0') {
213 }
else if (desc->default_value) {
214 value = *(
const double *)desc->default_value;
216 memcpy(field, &value,
sizeof(
double));
219static void apply_env_callback(
void *field,
const char *env_value,
const option_descriptor_t *desc) {
226static void apply_env_action(
void *field,
const char *env_value,
const option_descriptor_t *desc) {
237static asciichat_error_t parse_bool_value(
const char *value_str,
bool *out_value,
const option_descriptor_t *desc) {
238 if (!value_str || value_str[0] ==
'\0') {
239 return SET_ERRNO(ERROR_USAGE,
"Option --%s requires a value (true/false, yes/no, 1/0, on/off)",
240 desc ? desc->long_name :
"unknown");
244 if (strcasecmp(value_str,
"true") == 0 || strcasecmp(value_str,
"yes") == 0 || strcasecmp(value_str,
"1") == 0 ||
245 strcasecmp(value_str,
"on") == 0) {
251 if (strcasecmp(value_str,
"false") == 0 || strcasecmp(value_str,
"no") == 0 || strcasecmp(value_str,
"0") == 0 ||
252 strcasecmp(value_str,
"off") == 0) {
258 return SET_ERRNO(ERROR_USAGE,
"Invalid boolean value for --%s: '%s' (use: true/false, yes/no, 1/0, on/off)",
259 desc ? desc->long_name :
"unknown", value_str);
262static asciichat_error_t apply_cli_bool(
void *field,
const char *opt_value,
const option_descriptor_t *desc) {
265 if (opt_value == NULL) {
267 unsigned char current_byte = 0;
268 memcpy(¤t_byte, field, 1);
269 bool current_value = (current_byte != 0);
270 new_value = !current_value;
273 asciichat_error_t parse_result = parse_bool_value(opt_value, &new_value, desc);
274 if (parse_result != ASCIICHAT_OK) {
280 unsigned char new_value_byte = new_value ? 1 : 0;
281 memcpy(field, &new_value_byte, 1);
285static asciichat_error_t apply_cli_int(
void *field,
const char *opt_value,
const option_descriptor_t *desc) {
287 if (!opt_value || opt_value[0] ==
'\0') {
288 return SET_ERRNO(ERROR_USAGE,
"Option --%s requires a numeric value", desc ? desc->long_name :
"unknown");
292 long value = strtol(opt_value, &endptr, 10);
293 if (*endptr !=
'\0' || value < INT_MIN || value > INT_MAX) {
296 int int_value = (int)value;
299 if (desc && desc->metadata.numeric_range.max != 0) {
300 if (int_value < desc->metadata.numeric_range.min || int_value > desc->metadata.numeric_range.max) {
301 return SET_ERRNO(ERROR_USAGE,
"Value %d out of range [%d-%d]", int_value, desc->metadata.numeric_range.min,
302 desc->metadata.numeric_range.max);
306 memcpy(field, &int_value,
sizeof(
int));
310static asciichat_error_t apply_cli_string(
void *field,
const char *opt_value,
const option_descriptor_t *desc) {
312 if (opt_value && opt_value[0] ==
'\0' && desc && desc->long_name) {
313 if (strcmp(desc->long_name,
"key") == 0) {
314 return SET_ERRNO(ERROR_USAGE,
"Option --%s cannot be empty", desc->long_name);
318 char *dest = (
char *)field;
320 dest[OPTIONS_BUFF_SIZE - 1] =
'\0';
324static asciichat_error_t apply_cli_double(
void *field,
const char *opt_value,
const option_descriptor_t *desc) {
327 double value = strtod(opt_value, &endptr);
329 if (endptr == opt_value || *endptr !=
'\0') {
332 memcpy(field, &value,
sizeof(
double));
336static asciichat_error_t apply_cli_callback(
void *field,
const char *opt_value,
const option_descriptor_t *desc) {
337 if (desc->parse_fn) {
338 char *error_msg = NULL;
339 if (!desc->parse_fn(opt_value, field, &error_msg)) {
340 asciichat_error_t err = SET_ERRNO(ERROR_USAGE,
"Parse error: %s", error_msg ? error_msg :
"unknown");
348static asciichat_error_t apply_cli_action(
void *field,
const char *opt_value,
const option_descriptor_t *desc) {
351 if (desc->action_fn) {
358static void format_help_placeholder_bool(
char *buf,
size_t bufsize) {
362static void format_help_placeholder_int(
char *buf,
size_t bufsize) {
366static void format_help_placeholder_string(
char *buf,
size_t bufsize) {
370static void format_help_placeholder_double(
char *buf,
size_t bufsize) {
374static void format_help_placeholder_callback(
char *buf,
size_t bufsize) {
378static void format_help_placeholder_action(
char *buf,
size_t bufsize) {
const option_builder_handler_t g_builder_handlers[]
int safe_snprintf(char *buffer, size_t buffer_size, const char *format,...)
Safe formatted string printing to buffer.