16static void to_lower(
const char *src,
char *dst,
size_t max_len) {
18 while (src[i] && i < max_len - 1) {
19 dst[i] = (char)tolower((
unsigned char)src[i]);
45 size_t len = strlen(str);
46 if (len == 0 || len > 47) {
51 if (str[0] ==
'-' || str[len - 1] ==
'-') {
57 for (
size_t i = 0; i < len; i++) {
62 if (i > 0 && str[i - 1] ==
'-') {
65 }
else if (!islower(c)) {
72 return hyphen_count == 2;
78 *error_msg = strdup(
"Internal error: NULL argument or destination");
85 to_lower(arg, lower,
sizeof(lower));
88 if (strcmp(lower,
"auto") == 0 || strcmp(lower,
"a") == 0) {
94 if (strcmp(lower,
"none") == 0 || strcmp(lower,
"mono") == 0 || strcmp(lower,
"monochrome") == 0 ||
95 strcmp(lower,
"0") == 0) {
101 if (strcmp(lower,
"16") == 0 || strcmp(lower,
"16color") == 0 || strcmp(lower,
"ansi") == 0 ||
102 strcmp(lower,
"1") == 0) {
108 if (strcmp(lower,
"256") == 0 || strcmp(lower,
"256color") == 0 || strcmp(lower,
"2") == 0) {
114 if (strcmp(lower,
"truecolor") == 0 || strcmp(lower,
"true") == 0 || strcmp(lower,
"tc") == 0 ||
115 strcmp(lower,
"rgb") == 0 || strcmp(lower,
"24bit") == 0 || strcmp(lower,
"3") == 0) {
124 snprintf(msg, 256,
"Invalid color mode '%s'. Valid values: auto, none, 16, 256, truecolor", arg);
134 *error_msg = strdup(
"Internal error: NULL argument or destination");
141 to_lower(arg, lower,
sizeof(lower));
144 if (strcmp(lower,
"foreground") == 0 || strcmp(lower,
"fg") == 0 || strcmp(lower,
"0") == 0) {
150 if (strcmp(lower,
"background") == 0 || strcmp(lower,
"bg") == 0 || strcmp(lower,
"1") == 0) {
156 if (strcmp(lower,
"half-block") == 0 || strcmp(lower,
"half") == 0 || strcmp(lower,
"hb") == 0 ||
157 strcmp(lower,
"2") == 0) {
166 snprintf(msg, 256,
"Invalid render mode '%s'. Valid values: foreground, background, half-block", arg);
176 *error_msg = strdup(
"Internal error: NULL argument or destination");
183 to_lower(arg, lower,
sizeof(lower));
186 if (strcmp(lower,
"standard") == 0 || strcmp(lower,
"std") == 0 || strcmp(lower,
"0") == 0) {
192 if (strcmp(lower,
"blocks") == 0 || strcmp(lower,
"block") == 0 || strcmp(lower,
"1") == 0) {
198 if (strcmp(lower,
"digital") == 0 || strcmp(lower,
"dig") == 0 || strcmp(lower,
"2") == 0) {
204 if (strcmp(lower,
"minimal") == 0 || strcmp(lower,
"min") == 0 || strcmp(lower,
"3") == 0) {
210 if (strcmp(lower,
"cool") == 0 || strcmp(lower,
"4") == 0) {
216 if (strcmp(lower,
"custom") == 0 || strcmp(lower,
"5") == 0) {
225 snprintf(msg, 256,
"Invalid palette type '%s'. Valid values: standard, blocks, digital, minimal, cool, custom",
236 *error_msg = strdup(
"Internal error: NULL argument or destination");
243 to_lower(arg, lower,
sizeof(lower));
246 if (strcmp(lower,
"dev") == 0 || strcmp(lower,
"development") == 0 || strcmp(lower,
"0") == 0) {
252 if (strcmp(lower,
"debug") == 0 || strcmp(lower,
"dbg") == 0 || strcmp(lower,
"1") == 0) {
258 if (strcmp(lower,
"info") == 0 || strcmp(lower,
"information") == 0 || strcmp(lower,
"2") == 0) {
264 if (strcmp(lower,
"warn") == 0 || strcmp(lower,
"warning") == 0 || strcmp(lower,
"3") == 0) {
270 if (strcmp(lower,
"error") == 0 || strcmp(lower,
"err") == 0 || strcmp(lower,
"4") == 0) {
276 if (strcmp(lower,
"fatal") == 0 || strcmp(lower,
"5") == 0) {
285 snprintf(msg, 256,
"Invalid log level '%s'. Valid values: dev, debug, info, warn, error, fatal", arg);
310 if (!arg || !config) {
312 *error_msg = strdup(
"Internal error: NULL argument or config");
319 char *address = (
char *)config + offsetof(
struct options_state, address);
320 char *address6 = (
char *)config + offsetof(
struct options_state, address6);
324 const char *addr_to_check = arg;
326 addr_to_check = parsed_addr;
333 if (address[0] !=
'\0' && strcmp(address,
"127.0.0.1") != 0 && strcmp(address,
"localhost") != 0) {
338 "Cannot specify multiple IPv4 addresses.\n"
341 address, addr_to_check);
352 if (address6[0] !=
'\0' && strcmp(address6,
"::1") != 0) {
357 "Cannot specify multiple IPv6 addresses.\n"
360 address6, addr_to_check);
373 "Invalid IP address '%s'.\n"
374 "Server bind addresses must be valid IPv4 or IPv6 addresses.\n"
376 " ascii-chat server 0.0.0.0\n"
377 " ascii-chat server ::1\n"
378 " ascii-chat server 0.0.0.0 ::1",
393int parse_client_address(
const char *arg,
void *config,
char **remaining,
int num_remaining,
char **error_msg) {
397 if (!arg || !config) {
399 *error_msg = strdup(
"Internal error: NULL argument or config");
408 char *session_string = (
char *)config + offsetof(
struct options_state, session_string);
410 log_debug(
"Detected session string: %s", arg);
416 char *address = (
char *)config + offsetof(
struct options_state, address);
417 char *port = (
char *)config + offsetof(
struct options_state, port);
420 const char *colon = strrchr(arg,
':');
426 const char *closing_bracket = strchr(arg,
']');
427 if (closing_bracket && closing_bracket < colon) {
429 size_t addr_len = (size_t)(closing_bracket - arg - 1);
432 *error_msg = strdup(
"IPv6 address too long");
439 const char *port_str = colon + 1;
441 long port_num = strtol(port_str, &endptr, 10);
442 if (*endptr !=
'\0' || port_num < 1 || port_num > 65535) {
446 snprintf(msg, 256,
"Invalid port number '%s'. Must be 1-65535.", port_str);
457 size_t colon_count = 0;
458 for (
const char *p = arg; *p; p++) {
463 if (colon_count == 1) {
465 size_t addr_len = (size_t)(colon - arg);
468 *error_msg = strdup(
"Address too long");
475 const char *port_str = colon + 1;
477 long port_num = strtol(port_str, &endptr, 10);
478 if (*endptr !=
'\0' || port_num < 1 || port_num > 65535) {
482 snprintf(msg, 256,
"Invalid port number '%s'. Must be 1-65535.", port_str);
501 bool has_dot = strchr(address,
'.') != NULL;
502 bool starts_with_digit = address[0] >=
'0' && address[0] <=
'9';
504 if (has_dot && starts_with_digit) {
511 "Invalid IPv4 address '%s'.\n"
512 "IPv4 addresses must have exactly 4 octets (0-255) separated by dots.\n"
513 "Examples: 127.0.0.1, 192.168.1.1\n"
514 "For hostnames, use letters: example.com, localhost",
537 *error_msg = strdup(
"Internal error: NULL argument or destination");
546 char *palette_custom = (
char *)dest;
548 if (strlen(arg) >= 256) {
552 snprintf(msg, 256,
"Invalid palette-chars: too long (%zu chars, max 255)", strlen(arg));
561 palette_custom[255] =
'\0';
bool is_session_string(const char *str)
Check if a string matches session string pattern.
#define SAFE_STRNCPY(dst, src, size)
#define SAFE_MALLOC(size, cast)
#define SAFE_SNPRINTF(buffer, buffer_size,...)
log_level_t
Logging levels enumeration.
#define log_debug(...)
Log a DEBUG message.
#define OPTIONS_BUFF_SIZE
Buffer size for option string values.
palette_type_t
Built-in palette type enumeration.
@ PALETTE_BLOCKS
Unicode block characters: " ░░▒▒▓▓██".
@ PALETTE_CUSTOM
User-defined via –palette-chars.
@ PALETTE_COOL
Ascending blocks: " ▁▂▃▄▅▆▇█".
@ PALETTE_STANDARD
Standard ASCII palette: " ...',;:clodxkO0KXNWM".
@ PALETTE_DIGITAL
Digital/glitch aesthetic: " -=≡≣▰▱◼".
@ PALETTE_MINIMAL
Simple ASCII: " .-+*#".
int is_valid_ipv4(const char *ip)
Check if a string is a valid IPv4 address.
int is_valid_ipv6(const char *ip)
Check if a string is a valid IPv6 address.
int parse_ipv6_address(const char *input, char *output, size_t output_size)
Parse IPv6 address, removing brackets if present.
🌍 IP Address Parsing and Formatting Utilities
⚙️ Command-line options parsing and configuration management for ascii-chat
bool parse_render_mode(const char *arg, void *dest, char **error_msg)
Parse render mode option.
int parse_server_bind_address(const char *arg, void *config, char **remaining, int num_remaining, char **error_msg)
Parse server bind address positional argument.
bool parse_palette_type(const char *arg, void *dest, char **error_msg)
Parse palette type option.
bool parse_log_level(const char *arg, void *dest, char **error_msg)
Parse log level option.
int parse_client_address(const char *arg, void *config, char **remaining, int num_remaining, char **error_msg)
Parse client address positional argument.
bool parse_color_mode(const char *arg, void *dest, char **error_msg)
Parse terminal color level option.
bool parse_palette_chars(const char *arg, void *dest, char **error_msg)
Parse custom palette characters option.
Custom option parsers for enum types.
Consolidated options structure.
Common SIMD utilities and structures.