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

Custom option parsers for enum types. More...

Go to the source code of this file.

Functions

bool parse_color_mode (const char *arg, void *dest, char **error_msg)
 Parse terminal color level option.
 
bool parse_render_mode (const char *arg, void *dest, char **error_msg)
 Parse render mode option.
 
bool parse_palette_type (const char *arg, void *dest, char **error_msg)
 Parse palette type option.
 
bool parse_palette_chars (const char *arg, void *dest, char **error_msg)
 Parse custom palette characters option.
 
bool parse_log_level (const char *arg, void *dest, char **error_msg)
 Parse log level 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.
 
int parse_client_address (const char *arg, void *config, char **remaining, int num_remaining, char **error_msg)
 Parse client address positional argument.
 

Detailed Description

Custom option parsers for enum types.

This module provides custom callback parsers for enum-based options used by the options builder system. Each parser converts a string argument to the appropriate enum value.

Author
Zachary Fogg me@zf.nosp@m.o.gg
Date
December 2025

Definition in file parsers.h.

Function Documentation

◆ parse_client_address()

int parse_client_address ( const char *  arg,
void *  config,
char **  remaining,
int  num_remaining,
char **  error_msg 
)

Parse client address positional argument.

Parameters
argAddress argument in format [address][:port]
configPointer to options struct (must contain address and port fields)
remainingRemaining positional args (unused for client)
num_remainingCount of remaining args
error_msgError message output (set on failure)
Returns
Number of args consumed (always 1), or -1 on error

Client address parsing rules:

  • Parses single argument: [address][:port]
  • IPv6 with brackets and port: [::1]:8080
  • IPv4/hostname with port: 192.168.1.1:8080 or example.com:8080
  • Bare IPv6: ::1 (detected by multiple colons)
  • Bare hostname/IPv4: 192.168.1.1 or example.com
  • IPv4 addresses starting with digit are validated strictly
  • Detects port conflict if –port flag was already used

This parser handles the complex logic of separating address from optional port while correctly handling IPv6 addresses that contain colons.

Example usage with options_builder_add_positional():

builder,
"address",
"[address][:port] - Server address (IPv4, IPv6, or hostname) with optional port",
false, // Not required (defaults to localhost:27224)
);
void options_builder_add_positional(options_builder_t *builder, const char *name, const char *help_text, bool required, const char *section_heading, const char **examples, size_t num_examples, int(*parse_fn)(const char *arg, void *config, char **remaining, int num_remaining, char **error_msg))
Add positional argument descriptor.
Definition builder.c:627
int parse_client_address(const char *arg, void *config, char **remaining, int num_remaining, char **error_msg)
Parse client address positional argument.
Definition parsers.c:393

Implements the client address parsing logic from client.c. Parses [address][:port] with complex IPv6 handling.

Definition at line 393 of file parsers.c.

393 {
394 (void)remaining;
395 (void)num_remaining;
396
397 if (!arg || !config) {
398 if (error_msg) {
399 *error_msg = strdup("Internal error: NULL argument or config");
400 }
401 return -1;
402 }
403
404 // Check if this is a session string (format: adjective-noun-noun)
405 // Session strings have exactly 2 hyphens, only lowercase letters, length 1-47
406 if (is_session_string(arg)) {
407 // This is a session string, not a server address
408 char *session_string = (char *)config + offsetof(struct options_state, session_string);
409 SAFE_SNPRINTF(session_string, 64, "%s", arg);
410 log_debug("Detected session string: %s", arg);
411 return 1; // Consumed 1 arg
412 }
413
414 // Not a session string, parse as server address
415 // Access address and port fields from options_state struct
416 char *address = (char *)config + offsetof(struct options_state, address);
417 char *port = (char *)config + offsetof(struct options_state, port);
418
419 // Check for port in address (format: address:port or [ipv6]:port)
420 const char *colon = strrchr(arg, ':');
421
422 if (colon != NULL) {
423 // Check if this is IPv6 with port [::1]:port or plain hostname:port
424 if (arg[0] == '[') {
425 // IPv6 with brackets: [address]:port
426 const char *closing_bracket = strchr(arg, ']');
427 if (closing_bracket && closing_bracket < colon) {
428 // Extract address (remove brackets)
429 size_t addr_len = (size_t)(closing_bracket - arg - 1);
430 if (addr_len >= OPTIONS_BUFF_SIZE) {
431 if (error_msg) {
432 *error_msg = strdup("IPv6 address too long");
433 }
434 return -1;
435 }
436 SAFE_SNPRINTF(address, OPTIONS_BUFF_SIZE, "%.*s", (int)addr_len, arg + 1);
437
438 // Extract and validate port
439 const char *port_str = colon + 1;
440 char *endptr;
441 long port_num = strtol(port_str, &endptr, 10);
442 if (*endptr != '\0' || port_num < 1 || port_num > 65535) {
443 if (error_msg) {
444 char *msg = SAFE_MALLOC(256, char *);
445 if (msg) {
446 snprintf(msg, 256, "Invalid port number '%s'. Must be 1-65535.", port_str);
447 *error_msg = msg;
448 }
449 }
450 return -1;
451 }
452 SAFE_SNPRINTF(port, OPTIONS_BUFF_SIZE, "%s", port_str);
453 }
454 } else {
455 // Check if it's IPv6 without brackets (no port allowed)
456 // or hostname/IPv4:port
457 size_t colon_count = 0;
458 for (const char *p = arg; *p; p++) {
459 if (*p == ':')
460 colon_count++;
461 }
462
463 if (colon_count == 1) {
464 // Likely hostname:port or IPv4:port
465 size_t addr_len = (size_t)(colon - arg);
466 if (addr_len >= OPTIONS_BUFF_SIZE) {
467 if (error_msg) {
468 *error_msg = strdup("Address too long");
469 }
470 return -1;
471 }
472 SAFE_SNPRINTF(address, OPTIONS_BUFF_SIZE, "%.*s", (int)addr_len, arg);
473
474 // Extract and validate port
475 const char *port_str = colon + 1;
476 char *endptr;
477 long port_num = strtol(port_str, &endptr, 10);
478 if (*endptr != '\0' || port_num < 1 || port_num > 65535) {
479 if (error_msg) {
480 char *msg = SAFE_MALLOC(256, char *);
481 if (msg) {
482 snprintf(msg, 256, "Invalid port number '%s'. Must be 1-65535.", port_str);
483 *error_msg = msg;
484 }
485 }
486 return -1;
487 }
488 SAFE_SNPRINTF(port, OPTIONS_BUFF_SIZE, "%s", port_str);
489 } else {
490 // Multiple colons - likely bare IPv6 address
491 SAFE_SNPRINTF(address, OPTIONS_BUFF_SIZE, "%s", arg);
492 }
493 }
494 } else {
495 // No colon - just an address
496 SAFE_SNPRINTF(address, OPTIONS_BUFF_SIZE, "%s", arg);
497 }
498
499 // Validate addresses that contain dots as potential IPv4 addresses
500 // If it has a dot, it's either a valid IPv4 or a hostname with domain
501 bool has_dot = strchr(address, '.') != NULL;
502 bool starts_with_digit = address[0] >= '0' && address[0] <= '9';
503
504 if (has_dot && starts_with_digit) {
505 // Looks like an IPv4 attempt - validate strictly
506 if (!is_valid_ipv4(address)) {
507 if (error_msg) {
508 char *msg = SAFE_MALLOC(512, char *);
509 if (msg) {
510 snprintf(msg, 512,
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",
515 address);
516 *error_msg = msg;
517 }
518 }
519 return -1;
520 }
521 }
522
523 // Note: Port conflict checking would require additional state
524 // (checking if --port flag was used). For now, this is a simplified version.
525 // Full implementation would need to track whether port was set via flag.
526
527 return 1; // Consumed 1 arg
528}
bool is_session_string(const char *str)
Check if a string matches session string pattern.
Definition discovery.c:83
#define SAFE_MALLOC(size, cast)
Definition common.h:208
#define SAFE_SNPRINTF(buffer, buffer_size,...)
Definition common.h:412
#define log_debug(...)
Log a DEBUG message.
#define OPTIONS_BUFF_SIZE
Buffer size for option string values.
Definition options.h:176
int is_valid_ipv4(const char *ip)
Check if a string is a valid IPv4 address.
Definition ip.c:22
Consolidated options structure.
Definition options.h:439

References is_session_string(), is_valid_ipv4(), log_debug, OPTIONS_BUFF_SIZE, SAFE_MALLOC, and SAFE_SNPRINTF.

Referenced by options_preset_client().

◆ parse_color_mode()

bool parse_color_mode ( const char *  arg,
void *  dest,
char **  error_msg 
)

Parse terminal color level option.

Parameters
argString argument (e.g., "auto", "none", "16", "256", "truecolor")
destDestination pointer (terminal_color_mode_t*)
error_msgOptional error message output (set on failure)
Returns
true on success, false on error

Valid values:

  • "auto", "a" - Auto-detect from terminal
  • "none", "mono", "monochrome", "0" - No color (TERM_COLOR_NONE)
  • "16", "16color", "ansi" - 16-color mode (TERM_COLOR_16)
  • "256", "256color" - 256-color mode (TERM_COLOR_256)
  • "truecolor", "true", "tc", "rgb", "24bit" - Truecolor mode (TERM_COLOR_TRUECOLOR)

Definition at line 75 of file parsers.c.

75 {
76 if (!arg || !dest) {
77 if (error_msg) {
78 *error_msg = strdup("Internal error: NULL argument or destination");
79 }
80 return false;
81 }
82
83 terminal_color_mode_t *color_mode = (terminal_color_mode_t *)dest;
84 char lower[32];
85 to_lower(arg, lower, sizeof(lower));
86
87 // Auto-detect
88 if (strcmp(lower, "auto") == 0 || strcmp(lower, "a") == 0) {
89 *color_mode = TERM_COLOR_AUTO;
90 return true;
91 }
92
93 // Monochrome/None
94 if (strcmp(lower, "none") == 0 || strcmp(lower, "mono") == 0 || strcmp(lower, "monochrome") == 0 ||
95 strcmp(lower, "0") == 0) {
96 *color_mode = TERM_COLOR_NONE;
97 return true;
98 }
99
100 // 16-color
101 if (strcmp(lower, "16") == 0 || strcmp(lower, "16color") == 0 || strcmp(lower, "ansi") == 0 ||
102 strcmp(lower, "1") == 0) {
103 *color_mode = TERM_COLOR_16;
104 return true;
105 }
106
107 // 256-color
108 if (strcmp(lower, "256") == 0 || strcmp(lower, "256color") == 0 || strcmp(lower, "2") == 0) {
109 *color_mode = TERM_COLOR_256;
110 return true;
111 }
112
113 // Truecolor
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) {
116 *color_mode = TERM_COLOR_TRUECOLOR;
117 return true;
118 }
119
120 // Invalid value
121 if (error_msg) {
122 char *msg = SAFE_MALLOC(256, char *);
123 if (msg) {
124 snprintf(msg, 256, "Invalid color mode '%s'. Valid values: auto, none, 16, 256, truecolor", arg);
125 *error_msg = msg;
126 }
127 }
128 return false;
129}
terminal_color_mode_t
Terminal color support levels.
Definition terminal.h:424
@ TERM_COLOR_NONE
No color support (monochrome terminal)
Definition terminal.h:428
@ TERM_COLOR_16
16-color support (standard ANSI colors)
Definition terminal.h:430
@ TERM_COLOR_256
256-color support (extended ANSI palette)
Definition terminal.h:432
@ TERM_COLOR_AUTO
Auto-detect color support from terminal capabilities.
Definition terminal.h:426
@ TERM_COLOR_TRUECOLOR
24-bit truecolor support (RGB colors)
Definition terminal.h:434

References SAFE_MALLOC, TERM_COLOR_16, TERM_COLOR_256, TERM_COLOR_AUTO, TERM_COLOR_NONE, and TERM_COLOR_TRUECOLOR.

◆ parse_log_level()

bool parse_log_level ( const char *  arg,
void *  dest,
char **  error_msg 
)

Parse log level option.

Parameters
argString argument (e.g., "debug", "info", "warn")
destDestination pointer (log_level_t*)
error_msgOptional error message output (set on failure)
Returns
true on success, false on error

Valid values:

  • "dev", "development", "0" - Development level (LOG_DEV)
  • "debug", "dbg", "1" - Debug level (LOG_DEBUG)
  • "info", "information", "2" - Info level (LOG_INFO)
  • "warn", "warning", "3" - Warning level (LOG_WARN)
  • "error", "err", "4" - Error level (LOG_ERROR)
  • "fatal", "5" - Fatal level (LOG_FATAL)

Definition at line 233 of file parsers.c.

233 {
234 if (!arg || !dest) {
235 if (error_msg) {
236 *error_msg = strdup("Internal error: NULL argument or destination");
237 }
238 return false;
239 }
240
241 log_level_t *log_level = (log_level_t *)dest;
242 char lower[32];
243 to_lower(arg, lower, sizeof(lower));
244
245 // Development level
246 if (strcmp(lower, "dev") == 0 || strcmp(lower, "development") == 0 || strcmp(lower, "0") == 0) {
247 *log_level = LOG_DEV;
248 return true;
249 }
250
251 // Debug level
252 if (strcmp(lower, "debug") == 0 || strcmp(lower, "dbg") == 0 || strcmp(lower, "1") == 0) {
253 *log_level = LOG_DEBUG;
254 return true;
255 }
256
257 // Info level
258 if (strcmp(lower, "info") == 0 || strcmp(lower, "information") == 0 || strcmp(lower, "2") == 0) {
259 *log_level = LOG_INFO;
260 return true;
261 }
262
263 // Warning level
264 if (strcmp(lower, "warn") == 0 || strcmp(lower, "warning") == 0 || strcmp(lower, "3") == 0) {
265 *log_level = LOG_WARN;
266 return true;
267 }
268
269 // Error level
270 if (strcmp(lower, "error") == 0 || strcmp(lower, "err") == 0 || strcmp(lower, "4") == 0) {
271 *log_level = LOG_ERROR;
272 return true;
273 }
274
275 // Fatal level
276 if (strcmp(lower, "fatal") == 0 || strcmp(lower, "5") == 0) {
277 *log_level = LOG_FATAL;
278 return true;
279 }
280
281 // Invalid value
282 if (error_msg) {
283 char *msg = SAFE_MALLOC(256, char *);
284 if (msg) {
285 snprintf(msg, 256, "Invalid log level '%s'. Valid values: dev, debug, info, warn, error, fatal", arg);
286 *error_msg = msg;
287 }
288 }
289 return false;
290}
log_level_t
Logging levels enumeration.
Definition log/logging.h:59
@ LOG_ERROR
Definition log/logging.h:64
@ LOG_INFO
Definition log/logging.h:62
@ LOG_DEBUG
Definition log/logging.h:61
@ LOG_FATAL
Definition log/logging.h:65
@ LOG_WARN
Definition log/logging.h:63
@ LOG_DEV
Definition log/logging.h:60

References LOG_DEBUG, LOG_DEV, LOG_ERROR, LOG_FATAL, LOG_INFO, LOG_WARN, and SAFE_MALLOC.

Referenced by options_preset_acds().

◆ parse_palette_chars()

bool parse_palette_chars ( const char *  arg,
void *  dest,
char **  error_msg 
)

Parse custom palette characters option.

Parameters
argString of characters ordered from darkest to brightest
destDestination pointer (char[256])
error_msgOptional error message output (set on failure)
Returns
true on success, false on error

The characters should be ordered from darkest (leftmost) to brightest (rightmost). Maximum length is 255 characters.

Example: " .:-=+*#%@"

Definition at line 534 of file parsers.c.

534 {
535 if (!arg || !dest) {
536 if (error_msg) {
537 *error_msg = strdup("Internal error: NULL argument or destination");
538 }
539 return false;
540 }
541
542 // The dest pointer points to the palette_custom field in options_t
543 // We need to get the full options_t struct to call parse_palette_chars_option
544 // Since we only have the field pointer, we need to handle this directly
545
546 char *palette_custom = (char *)dest;
547
548 if (strlen(arg) >= 256) {
549 if (error_msg) {
550 char *msg = SAFE_MALLOC(256, char *);
551 if (msg) {
552 snprintf(msg, 256, "Invalid palette-chars: too long (%zu chars, max 255)", strlen(arg));
553 *error_msg = msg;
554 }
555 }
556 return false;
557 }
558
559 // Copy the palette characters
560 SAFE_STRNCPY(palette_custom, arg, 256);
561 palette_custom[255] = '\0';
562
563 // Also set the palette type to custom
564 // Note: This is a simplification - ideally we'd have access to the full options_t struct
565 // to set palette_custom_set and palette_type, but the callback interface doesn't provide that.
566 // The palette_type should be handled separately or via a dependency.
567
568 return true;
569}
#define SAFE_STRNCPY(dst, src, size)
Definition common.h:358

References SAFE_MALLOC, and SAFE_STRNCPY.

◆ parse_palette_type()

bool parse_palette_type ( const char *  arg,
void *  dest,
char **  error_msg 
)

Parse palette type option.

Parameters
argString argument (e.g., "standard", "blocks", "custom")
destDestination pointer (palette_type_t*)
error_msgOptional error message output (set on failure)
Returns
true on success, false on error

Valid values:

  • "standard", "std", "0" - Standard palette (PALETTE_STANDARD)
  • "blocks", "block", "1" - Blocks palette (PALETTE_BLOCKS)
  • "digital", "dig", "2" - Digital palette (PALETTE_DIGITAL)
  • "minimal", "min", "3" - Minimal palette (PALETTE_MINIMAL)
  • "cool", "4" - Cool palette (PALETTE_COOL)
  • "custom", "5" - Custom palette (PALETTE_CUSTOM)

Definition at line 173 of file parsers.c.

173 {
174 if (!arg || !dest) {
175 if (error_msg) {
176 *error_msg = strdup("Internal error: NULL argument or destination");
177 }
178 return false;
179 }
180
181 palette_type_t *palette_type = (palette_type_t *)dest;
182 char lower[32];
183 to_lower(arg, lower, sizeof(lower));
184
185 // Standard palette
186 if (strcmp(lower, "standard") == 0 || strcmp(lower, "std") == 0 || strcmp(lower, "0") == 0) {
187 *palette_type = PALETTE_STANDARD;
188 return true;
189 }
190
191 // Blocks palette
192 if (strcmp(lower, "blocks") == 0 || strcmp(lower, "block") == 0 || strcmp(lower, "1") == 0) {
193 *palette_type = PALETTE_BLOCKS;
194 return true;
195 }
196
197 // Digital palette
198 if (strcmp(lower, "digital") == 0 || strcmp(lower, "dig") == 0 || strcmp(lower, "2") == 0) {
199 *palette_type = PALETTE_DIGITAL;
200 return true;
201 }
202
203 // Minimal palette
204 if (strcmp(lower, "minimal") == 0 || strcmp(lower, "min") == 0 || strcmp(lower, "3") == 0) {
205 *palette_type = PALETTE_MINIMAL;
206 return true;
207 }
208
209 // Cool palette
210 if (strcmp(lower, "cool") == 0 || strcmp(lower, "4") == 0) {
211 *palette_type = PALETTE_COOL;
212 return true;
213 }
214
215 // Custom palette
216 if (strcmp(lower, "custom") == 0 || strcmp(lower, "5") == 0) {
217 *palette_type = PALETTE_CUSTOM;
218 return true;
219 }
220
221 // Invalid value
222 if (error_msg) {
223 char *msg = SAFE_MALLOC(256, char *);
224 if (msg) {
225 snprintf(msg, 256, "Invalid palette type '%s'. Valid values: standard, blocks, digital, minimal, cool, custom",
226 arg);
227 *error_msg = msg;
228 }
229 }
230 return false;
231}
palette_type_t
Built-in palette type enumeration.
Definition palette.h:84
@ PALETTE_BLOCKS
Unicode block characters: " ░░▒▒▓▓██".
Definition palette.h:88
@ PALETTE_CUSTOM
User-defined via –palette-chars.
Definition palette.h:96
@ PALETTE_COOL
Ascending blocks: " ▁▂▃▄▅▆▇█".
Definition palette.h:94
@ PALETTE_STANDARD
Standard ASCII palette: " ...',;:clodxkO0KXNWM".
Definition palette.h:86
@ PALETTE_DIGITAL
Digital/glitch aesthetic: " -=≡≣▰▱◼".
Definition palette.h:90
@ PALETTE_MINIMAL
Simple ASCII: " .-+*#".
Definition palette.h:92

References PALETTE_BLOCKS, PALETTE_COOL, PALETTE_CUSTOM, PALETTE_DIGITAL, PALETTE_MINIMAL, PALETTE_STANDARD, and SAFE_MALLOC.

◆ parse_render_mode()

bool parse_render_mode ( const char *  arg,
void *  dest,
char **  error_msg 
)

Parse render mode option.

Parameters
argString argument (e.g., "foreground", "background", "half-block")
destDestination pointer (render_mode_t*)
error_msgOptional error message output (set on failure)
Returns
true on success, false on error

Valid values:

  • "foreground", "fg", "0" - Foreground mode (RENDER_MODE_FOREGROUND)
  • "background", "bg", "1" - Background mode (RENDER_MODE_BACKGROUND)
  • "half-block", "half", "hb", "2" - Half-block mode (RENDER_MODE_HALF_BLOCK)

Definition at line 131 of file parsers.c.

131 {
132 if (!arg || !dest) {
133 if (error_msg) {
134 *error_msg = strdup("Internal error: NULL argument or destination");
135 }
136 return false;
137 }
138
139 render_mode_t *render_mode = (render_mode_t *)dest;
140 char lower[32];
141 to_lower(arg, lower, sizeof(lower));
142
143 // Foreground mode
144 if (strcmp(lower, "foreground") == 0 || strcmp(lower, "fg") == 0 || strcmp(lower, "0") == 0) {
145 *render_mode = RENDER_MODE_FOREGROUND;
146 return true;
147 }
148
149 // Background mode
150 if (strcmp(lower, "background") == 0 || strcmp(lower, "bg") == 0 || strcmp(lower, "1") == 0) {
151 *render_mode = RENDER_MODE_BACKGROUND;
152 return true;
153 }
154
155 // Half-block mode
156 if (strcmp(lower, "half-block") == 0 || strcmp(lower, "half") == 0 || strcmp(lower, "hb") == 0 ||
157 strcmp(lower, "2") == 0) {
158 *render_mode = RENDER_MODE_HALF_BLOCK;
159 return true;
160 }
161
162 // Invalid value
163 if (error_msg) {
164 char *msg = SAFE_MALLOC(256, char *);
165 if (msg) {
166 snprintf(msg, 256, "Invalid render mode '%s'. Valid values: foreground, background, half-block", arg);
167 *error_msg = msg;
168 }
169 }
170 return false;
171}
render_mode_t
Render mode preferences.
Definition terminal.h:467
@ RENDER_MODE_FOREGROUND
Foreground colors only (text color)
Definition terminal.h:469
@ RENDER_MODE_BACKGROUND
Background colors (block colors)
Definition terminal.h:471
@ RENDER_MODE_HALF_BLOCK
Unicode half-block characters (mixed foreground/background)
Definition terminal.h:473

References RENDER_MODE_BACKGROUND, RENDER_MODE_FOREGROUND, RENDER_MODE_HALF_BLOCK, and SAFE_MALLOC.

◆ parse_server_bind_address()

int parse_server_bind_address ( const char *  arg,
void *  config,
char **  remaining,
int  num_remaining,
char **  error_msg 
)

Parse server bind address positional argument.

Parameters
argCurrent bind address argument
configPointer to options struct (must contain address/address6 fields)
remainingRemaining positional args (for multi-arg parsing)
num_remainingCount of remaining args
error_msgError message output (set on failure)
Returns
Number of args consumed (0-2), or -1 on error

Server bind address parsing rules:

  • 0 args total: Uses defaults (127.0.0.1 + ::1 for dual-stack localhost)
  • 1 arg: Single IPv4 OR IPv6 bind address
  • 2 args: One IPv4 AND one IPv6 bind address (order-independent)
  • Cannot specify multiple addresses of the same type
  • IPv6 addresses can be wrapped in brackets (e.g., [::1])

This parser can consume 0-2 arguments depending on what's provided. It tracks internal state to ensure only one IPv4 and one IPv6 are specified.

Example usage with options_builder_add_positional():

builder,
"bind-address",
"IPv4 or IPv6 bind address (can specify 0-2 addresses)",
false, // Not required (defaults to localhost)
);
int parse_server_bind_address(const char *arg, void *config, char **remaining, int num_remaining, char **error_msg)
Parse server bind address positional argument.
Definition parsers.c:306

Implements the server bind address parsing logic from server.c. Can consume 1 argument per call, handling IPv4 or IPv6 bind addresses. The positional arg system will call this multiple times for multiple args.

Definition at line 306 of file parsers.c.

306 {
307 (void)remaining; // Unused - we consume one arg at a time
308 (void)num_remaining;
309
310 if (!arg || !config) {
311 if (error_msg) {
312 *error_msg = strdup("Internal error: NULL argument or config");
313 }
314 return -1;
315 }
316
317 // Assume config struct has address and address6 fields (OPTIONS_BUFF_SIZE each)
318 // This is a simplified version that assumes standard options_state layout
319 char *address = (char *)config + offsetof(struct options_state, address);
320 char *address6 = (char *)config + offsetof(struct options_state, address6);
321
322 // Parse IPv6 address (remove brackets if present)
323 char parsed_addr[OPTIONS_BUFF_SIZE];
324 const char *addr_to_check = arg;
325 if (parse_ipv6_address(arg, parsed_addr, sizeof(parsed_addr)) == 0) {
326 addr_to_check = parsed_addr;
327 }
328
329 // Check if it's IPv4 or IPv6
330 if (is_valid_ipv4(addr_to_check)) {
331 // Check if we already have a non-default IPv4 address
332 // Allow overwriting defaults (127.0.0.1, localhost)
333 if (address[0] != '\0' && strcmp(address, "127.0.0.1") != 0 && strcmp(address, "localhost") != 0) {
334 if (error_msg) {
335 char *msg = SAFE_MALLOC(256, char *);
336 if (msg) {
337 snprintf(msg, 256,
338 "Cannot specify multiple IPv4 addresses.\n"
339 "Already have: %s\n"
340 "Cannot add: %s",
341 address, addr_to_check);
342 *error_msg = msg;
343 }
344 }
345 return -1;
346 }
347 SAFE_SNPRINTF(address, OPTIONS_BUFF_SIZE, "%s", addr_to_check);
348 return 1; // Consumed 1 arg
349 } else if (is_valid_ipv6(addr_to_check)) {
350 // Check if we already have a non-default IPv6 address
351 // Allow overwriting default (::1)
352 if (address6[0] != '\0' && strcmp(address6, "::1") != 0) {
353 if (error_msg) {
354 char *msg = SAFE_MALLOC(256, char *);
355 if (msg) {
356 snprintf(msg, 256,
357 "Cannot specify multiple IPv6 addresses.\n"
358 "Already have: %s\n"
359 "Cannot add: %s",
360 address6, addr_to_check);
361 *error_msg = msg;
362 }
363 }
364 return -1;
365 }
366 SAFE_SNPRINTF(address6, OPTIONS_BUFF_SIZE, "%s", addr_to_check);
367 return 1; // Consumed 1 arg
368 } else {
369 if (error_msg) {
370 char *msg = SAFE_MALLOC(512, char *);
371 if (msg) {
372 snprintf(msg, 512,
373 "Invalid IP address '%s'.\n"
374 "Server bind addresses must be valid IPv4 or IPv6 addresses.\n"
375 "Examples:\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",
379 arg);
380 *error_msg = msg;
381 }
382 }
383 return -1;
384 }
385}
int is_valid_ipv6(const char *ip)
Check if a string is a valid IPv6 address.
Definition ip.c:77
int parse_ipv6_address(const char *input, char *output, size_t output_size)
Parse IPv6 address, removing brackets if present.
Definition ip.c:167

References is_valid_ipv4(), is_valid_ipv6(), OPTIONS_BUFF_SIZE, parse_ipv6_address(), SAFE_MALLOC, and SAFE_SNPRINTF.

Referenced by options_preset_acds(), and options_preset_server().