ascii-chat
Binary v0.15.83-53-gb711d8720
Library lib/v0.3.2-701-gb711d8720
Video chat in your terminal
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_setting (const char *arg, void *dest, char **error_msg)
 Parse color setting option (–color flag).
bool parse_utf8_setting (const char *arg, void *dest, char **error_msg)
 Parse UTF-8 setting option (–utf8 flag).
bool parse_color_mode (const char *arg, void *dest, char **error_msg)
 Parse terminal color level option.
bool parse_color_filter (const char *arg, void *dest, char **error_msg)
 Parse color filter option.
bool parse_render_mode (const char *arg, void *dest, char **error_msg)
 Parse render mode option.
bool parse_render_theme (const char *arg, void *dest, char **error_msg)
 Parse render theme option for pixel rendering.
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.
bool parse_port_option (const char *arg, void *dest, char **error_msg)
 Parse and validate port option for CLI.
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.
int parse_mirror_media (const char *arg, void *config, char **remaining, int num_remaining, char **error_msg)
 Parse mirror mode media positional argument.
bool parse_verbose_flag (const char *arg, void *dest, char **error_msg)
 Custom parser for –verbose flag.
bool parse_timestamp (const char *arg, void *dest, char **error_msg)
 Custom parser for –timestamp flag.
bool parse_volume (const char *arg, void *dest, char **error_msg)
 Custom parser for volume options (–volume, –speakers-volume, –microphone-volume).
bool parse_log_file (const char *arg, void *dest, char **error_msg)
 Custom parser for log file paths (–log-file, -L).
bool parse_audio_source (const char *arg, void *dest, char **error_msg)
 Parse visualization source option (–audio-source).
bool parse_audio_capture_source (const char *arg, void *dest, char **error_msg)

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_audio_capture_source()

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

Parse local capture policy option (–audio-capture-source).

Definition at line 1150 of file parsers.c.

1150 {
1151 if (!arg || !dest) {
1152 if (error_msg)
1153 *error_msg = platform_strdup("Internal error: NULL argument or destination");
1154 return false;
1155 }
1156
1159 to_lower(arg, lower, sizeof(lower));
1160 if (strcmp(lower, OPT_AUDIO_CAPTURE_SOURCE_AUTO) == 0)
1162 else if (strcmp(lower, OPT_AUDIO_CAPTURE_SOURCE_MIC) == 0)
1164 else if (strcmp(lower, OPT_AUDIO_CAPTURE_SOURCE_MEDIA) == 0)
1166 else if (strcmp(lower, OPT_AUDIO_CAPTURE_SOURCE_BOTH) == 0)
1168 else if (strcmp(lower, OPT_AUDIO_CAPTURE_SOURCE_REMOTE) == 0)
1170 else {
1171 if (error_msg)
1172 *error_msg = platform_strdup("Audio capture source must be 'auto', 'mic', 'media', 'both', or 'remote'");
1173 return false;
1174 }
1175 return true;
1176}
#define OPT_AUDIO_CAPTURE_SOURCE_BOTH
Definition enums.h:149
#define OPT_AUDIO_CAPTURE_SOURCE_AUTO
Definition enums.h:146
#define OPT_AUDIO_CAPTURE_SOURCE_MEDIA
Definition enums.h:148
#define OPT_AUDIO_CAPTURE_SOURCE_REMOTE
Definition enums.h:150
#define OPT_AUDIO_CAPTURE_SOURCE_MIC
Definition enums.h:147
audio_visualization_source_t source
Definition fft.c:24
audio_capture_source_t
Definition options.h:290
@ AUDIO_CAPTURE_SOURCE_BOTH
Definition options.h:294
@ AUDIO_CAPTURE_SOURCE_REMOTE
Definition options.h:295
@ AUDIO_CAPTURE_SOURCE_MEDIA
Definition options.h:293
@ AUDIO_CAPTURE_SOURCE_MIC
Definition options.h:292
@ AUDIO_CAPTURE_SOURCE_AUTO
Definition options.h:291
#define OPTIONS_PARSING_PARSERS_LOWER_BUFFER_SIZE
Definition parsers.c:8

References AUDIO_CAPTURE_SOURCE_AUTO, AUDIO_CAPTURE_SOURCE_BOTH, AUDIO_CAPTURE_SOURCE_MEDIA, AUDIO_CAPTURE_SOURCE_MIC, AUDIO_CAPTURE_SOURCE_REMOTE, OPT_AUDIO_CAPTURE_SOURCE_AUTO, OPT_AUDIO_CAPTURE_SOURCE_BOTH, OPT_AUDIO_CAPTURE_SOURCE_MEDIA, OPT_AUDIO_CAPTURE_SOURCE_MIC, OPT_AUDIO_CAPTURE_SOURCE_REMOTE, OPTIONS_PARSING_PARSERS_LOWER_BUFFER_SIZE, and source.

◆ parse_audio_source()

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

Parse visualization source option (–audio-source).

Parameters
argString argument (e.g., "all", "call", "mic", "media")
destDestination pointer (audio_source_t*)
error_msgOptional error message output (set on failure)
Returns
true on success, false on error

Valid values: all (the default), call, mic, or media.

Definition at line 1114 of file parsers.c.

1114 {
1115 if (!arg || !dest) {
1116 if (error_msg) {
1117 *error_msg = platform_strdup("Internal error: NULL argument or destination");
1118 }
1119 return false;
1120 }
1121
1122 audio_source_t *audio_source = (audio_source_t *)dest;
1124 to_lower(arg, lower, sizeof(lower));
1125
1126 if (strcmp(lower, OPT_AUDIO_SOURCE_ALL) == 0 || strcmp(lower, OPT_AUDIO_SOURCE_AUTO) == 0 ||
1127 strcmp(lower, "both") == 0) {
1128 *audio_source = AUDIO_SOURCE_ALL;
1129 return true;
1130 }
1131 if (strcmp(lower, OPT_AUDIO_SOURCE_CALL) == 0 || strcmp(lower, "remote") == 0) {
1132 *audio_source = AUDIO_SOURCE_CALL;
1133 return true;
1134 }
1135 if (strcmp(lower, OPT_AUDIO_SOURCE_MIC) == 0) {
1136 *audio_source = AUDIO_SOURCE_MIC;
1137 return true;
1138 }
1139 if (strcmp(lower, OPT_AUDIO_SOURCE_MEDIA) == 0) {
1140 *audio_source = AUDIO_SOURCE_MEDIA;
1141 return true;
1142 }
1143
1144 if (error_msg) {
1145 *error_msg = platform_strdup("Audio source must be 'all', 'call', 'mic', or 'media'");
1146 }
1147 return false;
1148}
#define OPT_AUDIO_SOURCE_CALL
Definition enums.h:140
#define OPT_AUDIO_SOURCE_AUTO
Definition enums.h:143
#define OPT_AUDIO_SOURCE_ALL
Definition enums.h:139
#define OPT_AUDIO_SOURCE_MEDIA
Definition enums.h:142
#define OPT_AUDIO_SOURCE_MIC
Definition enums.h:141
audio_source_t
Audio source selected for visualization.
Definition options.h:276
@ AUDIO_SOURCE_MIC
Definition options.h:282
@ AUDIO_SOURCE_CALL
Definition options.h:280
@ AUDIO_SOURCE_MEDIA
Definition options.h:284
@ AUDIO_SOURCE_ALL
Definition options.h:278

References AUDIO_SOURCE_ALL, AUDIO_SOURCE_CALL, AUDIO_SOURCE_MEDIA, AUDIO_SOURCE_MIC, OPT_AUDIO_SOURCE_ALL, OPT_AUDIO_SOURCE_AUTO, OPT_AUDIO_SOURCE_CALL, OPT_AUDIO_SOURCE_MEDIA, OPT_AUDIO_SOURCE_MIC, and OPTIONS_PARSING_PARSERS_LOWER_BUFFER_SIZE.

◆ 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, option_mode_bitmask_t mode_bitmask, int(*parse_fn)(const char *arg, void *config, char **remaining, int num_remaining, char **error_msg))
Add positional argument descriptor.
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:636

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

Definition at line 636 of file parsers.c.

636 {
637 (void)remaining;
638 (void)num_remaining;
639
640 if (!arg || !config) {
641 if (error_msg) {
642 *error_msg = platform_strdup("Internal error: NULL argument or config");
643 }
644 return -1;
645 }
646
647 log_debug("Processing argument: '%s'", arg);
648
649#ifndef __EMSCRIPTEN__
650 ((options_t *)config)->address_explicit = true;
651#endif
652 // Access address and port fields from options_state struct
653 char *address = (char *)config + offsetof(struct options_state, address);
654 int *port = (int *)((char *)config + offsetof(struct options_state, port));
655
656 // Check for WebSocket URL (ws:// or wss://) FIRST before session string validation
657 // WebSocket URLs are passed through without validation or port extraction
658 if (strncmp(arg, "ws://", 5) == 0 || strncmp(arg, "wss://", 6) == 0) {
659 log_debug("Detected WebSocket URL: %s", arg);
660 SAFE_SNPRINTF(address, OPTIONS_BUFF_SIZE, "%s", arg);
661 // Don't set port - WebSocket transport handles URL parsing internally
662 return 1; // Consumed 1 argument
663 }
664
665 // Explicit transport URLs are resolved by the connection layer.
666 if (strncmp(arg, "tcp://", 6) == 0 || strncmp(arg, "quic://", 7) == 0) {
667 log_debug("Detected transport URL: %s", arg);
668 SAFE_SNPRINTF(address, OPTIONS_BUFF_SIZE, "%s", arg);
669 // The connection layer extracts the port from the URL.
670 return 1; // Consumed 1 argument
671 }
672
673 // Check if this is a session string (format: adjective-noun-noun)
674 // Session strings have exactly 2 hyphens, only lowercase letters, length 5-47
675 bool is_session = is_session_string(arg);
676 log_debug("is_session_string('%s') = %s", arg, is_session ? "true" : "false");
677
678 if (is_session) {
679 // This is a session string, not a server address
680 char *session_string = (char *)config + offsetof(struct options_state, session_string);
681 SAFE_SNPRINTF(session_string, SESSION_STRING_BUFFER_SIZE, "%s", arg);
682 log_debug("Stored session string: %s", arg);
683 return 1; // Consumed 1 arg
684 }
685
686 // Not a session string, parse as server address
687 log_debug("Parsing as server address (not a session string)");
688
689 // Check for port in address (format: address:port or [ipv6]:port)
690 const char *colon = strrchr(arg, ':');
691
692 if (colon != NULL) {
693 // Check if this is IPv6 with port [::1]:port or plain hostname:port
694 if (arg[0] == '[') {
695 // IPv6 with brackets: [address]:port
696 const char *closing_bracket = strchr(arg, ']');
697 if (closing_bracket && closing_bracket < colon) {
698 // Extract address (remove brackets)
699 size_t addr_len = (size_t)(closing_bracket - arg - 1);
700 if (addr_len >= OPTIONS_BUFF_SIZE) {
701 if (error_msg) {
702 *error_msg = platform_strdup("IPv6 address too long");
703 }
704 return -1;
705 }
706 SAFE_SNPRINTF(address, OPTIONS_BUFF_SIZE, "%.*s", (int)addr_len, arg + 1);
707
708 // Extract and validate port
709 const char *port_str = colon + 1;
710 char *endptr;
711 long port_num = strtol(port_str, &endptr, 10);
712 if (*endptr != '\0' || port_num < 1 || port_num > 65535) {
713 if (error_msg) {
715 safe_snprintf(msg, sizeof(msg), "Invalid port number '%s'. Must be 1-65535.", port_str);
716 *error_msg = platform_strdup(msg);
717 }
718 return -1;
719 }
720 *port = (int)port_num;
721 }
722 } else {
723 // Check if it's IPv6 without brackets (no port allowed)
724 // or hostname/IPv4:port
725 size_t colon_count = 0;
726 for (const char *p = arg; *p; p++) {
727 if (*p == ':')
728 colon_count++;
729 }
730
731 if (colon_count == 1) {
732 // Likely hostname:port or IPv4:port
733 size_t addr_len = (size_t)(colon - arg);
734 if (addr_len >= OPTIONS_BUFF_SIZE) {
735 if (error_msg) {
736 *error_msg = platform_strdup("Address too long");
737 }
738 return -1;
739 }
740 SAFE_SNPRINTF(address, OPTIONS_BUFF_SIZE, "%.*s", (int)addr_len, arg);
741
742 // Extract and validate port
743 const char *port_str = colon + 1;
744 char *endptr;
745 long port_num = strtol(port_str, &endptr, 10);
746 if (*endptr != '\0' || port_num < 1 || port_num > 65535) {
747 if (error_msg) {
749 safe_snprintf(msg, sizeof(msg), "Invalid port number '%s'. Must be 1-65535.", port_str);
750 *error_msg = platform_strdup(msg);
751 }
752 return -1;
753 }
754 *port = (int)port_num;
755 } else {
756 // Multiple colons - likely bare IPv6 address
757 SAFE_SNPRINTF(address, OPTIONS_BUFF_SIZE, "%s", arg);
758 }
759 }
760 } else {
761 // No colon - just an address
762 SAFE_SNPRINTF(address, OPTIONS_BUFF_SIZE, "%s", arg);
763 }
764
765 // Validate addresses using comprehensive IPv4/IPv6 detection
766 bool has_dot = strchr(address, '.') != NULL;
767 bool has_colon = strchr(address, ':') != NULL;
768 bool starts_with_digit = address[0] >= '0' && address[0] <= '9';
769
770 // Potential IPv6 address (has colons) - validate as IPv6
771 if (has_colon) {
772 if (!is_valid_ipv6(address)) {
773 if (error_msg) {
775 safe_snprintf(msg, sizeof(msg),
776 "Invalid IPv6 address '%s'.\n"
777 "IPv6 addresses must be valid hex notation with colons.\n"
778 "Examples: ::1, 2001:db8::1, fe80::1\n"
779 "Or use hostnames like example.com",
780 address);
781 *error_msg = platform_strdup(msg);
782 }
783 return -1;
784 }
785 } else if (has_dot && starts_with_digit) {
786 // Potential IPv4 address (has dots and starts with digit) - validate strictly
787 if (!is_valid_ipv4(address)) {
788 if (error_msg) {
790 safe_snprintf(msg, sizeof(msg),
791 "Invalid IPv4 address '%s'.\n"
792 "IPv4 addresses must have exactly 4 octets (0-255) separated by dots.\n"
793 "Examples: 127.0.0.1, 192.168.1.1\n"
794 "For hostnames, use letters: example.com, localhost",
795 address);
796 *error_msg = platform_strdup(msg);
797 }
798 return -1;
799 }
800 }
801 // Otherwise treat as valid hostname (no validation needed)
802
803 // Note: Port conflict checking would require additional state
804 // (checking if --port flag was used). For now, this is a simplified version.
805 // Full implementation would need to track whether port was set via flag.
806
807 log_debug("Set address='%s', port=%d", address[0] ? address : "(empty)", *port);
808
809 return 1; // Consumed 1 arg
810}
bool is_session_string(const char *str)
Check if a string is a valid session string.
#define SAFE_SNPRINTF(buffer, buffer_size,...)
Definition common.h:507
#define log_debug(...)
Log a DEBUG message.
Definition log/log.h:595
struct options_state options_t
Immutable, RCU-published snapshot of the application's runtime configuration.
#define OPTIONS_BUFF_SIZE
Buffer size for option string values.
Definition options.h:310
int safe_snprintf(char *buffer, size_t buffer_size, const char *format,...)
Platform-safe snprintf wrapper.
int is_valid_ipv4(const char *ip)
Check if a string is a valid IPv4 address.
Definition ip.c:32
int is_valid_ipv6(const char *ip)
Check if a string is a valid IPv6 address.
Definition ip.c:63
#define OPTIONS_PARSING_PARSERS_MSG_EXTENDED_BUFFER_SIZE
Definition parsers.c:10
#define OPTIONS_PARSING_PARSERS_MSG_BUFFER_SIZE
Definition parsers.c:9
#define SESSION_STRING_BUFFER_SIZE
Immutable, RCU-published snapshot of the application's runtime configuration.
Definition options.h:979

References is_session_string(), is_valid_ipv4(), is_valid_ipv6(), log_debug, OPTIONS_BUFF_SIZE, OPTIONS_PARSING_PARSERS_MSG_BUFFER_SIZE, OPTIONS_PARSING_PARSERS_MSG_EXTENDED_BUFFER_SIZE, SAFE_SNPRINTF, safe_snprintf(), and SESSION_STRING_BUFFER_SIZE.

Referenced by options_preset_unified().

◆ parse_color_filter()

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

Parse color filter option.

Parameters
argString argument (e.g., "green", "cyan", "pink")
destDestination pointer (color_filter_t*)
error_msgOptional error message output (set on failure)
Returns
true on success, false on error

Valid values: none, black, white, green, magenta, fuchsia, orange, teal, cyan, pink, red, yellow

Definition at line 219 of file parsers.c.

219 {
220 if (!arg || !dest) {
221 if (error_msg) {
222 *error_msg = platform_strdup("Internal error: NULL argument or destination");
223 }
224 return false;
225 }
226
227 color_filter_t *color_filter = (color_filter_t *)dest;
229 to_lower(arg, lower, sizeof(lower));
230
231 // Try to match against all known color filters
232 color_filter_t parsed_filter = color_filter_from_cli_name(lower);
233 if (parsed_filter != COLOR_FILTER_NONE || strcmp(lower, OPT_COLOR_FILTER_NONE) == 0) {
234 *color_filter = parsed_filter;
235 return true;
236 }
237
238 // Invalid value
239 if (error_msg) {
241 safe_snprintf(msg, sizeof(msg),
242 "Invalid color filter '%s'. Valid values: none, black, white, green, magenta, fuchsia, "
243 "orange, teal, cyan, pink, red, yellow",
244 arg);
245 *error_msg = platform_strdup(msg);
246 }
247 return false;
248}
#define OPT_COLOR_FILTER_NONE
Definition enums.h:105
color_filter_t color_filter_from_cli_name(const char *cli_name)
Convert color filter CLI name to enum value.

References color_filter_from_cli_name(), OPT_COLOR_FILTER_NONE, OPTIONS_PARSING_PARSERS_LOWER_BUFFER_SIZE, OPTIONS_PARSING_PARSERS_MSG_BUFFER_SIZE, and safe_snprintf().

◆ 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 160 of file parsers.c.

160 {
161 if (!arg || !dest) {
162 if (error_msg) {
163 *error_msg = platform_strdup("Internal error: NULL argument or destination");
164 }
165 return false;
166 }
167
168 terminal_color_mode_t *color_mode = (terminal_color_mode_t *)dest;
170 to_lower(arg, lower, sizeof(lower));
171
172 // Auto-detect
173 if (strcmp(lower, OPT_COLOR_MODE_AUTO) == 0 || strcmp(lower, "a") == 0) {
174 *color_mode = TERM_COLOR_AUTO;
175 return true;
176 }
177
178 // Monochrome/None
179 if (strcmp(lower, OPT_COLOR_MODE_NONE) == 0 || strcmp(lower, OPT_COLOR_MODE_MONO) == 0 ||
180 strcmp(lower, "monochrome") == 0 || strcmp(lower, "0") == 0) {
181 *color_mode = TERM_COLOR_NONE;
182 return true;
183 }
184
185 // 16-color
186 if (strcmp(lower, OPT_COLOR_MODE_16) == 0 || strcmp(lower, "16color") == 0 || strcmp(lower, "ansi") == 0 ||
187 strcmp(lower, "1") == 0) {
188 *color_mode = TERM_COLOR_16;
189 return true;
190 }
191
192 // 256-color
193 if (strcmp(lower, OPT_COLOR_MODE_256) == 0 || strcmp(lower, "256color") == 0 || strcmp(lower, "2") == 0) {
194 *color_mode = TERM_COLOR_256;
195 return true;
196 }
197
198 // Truecolor
199 if (strcmp(lower, OPT_COLOR_MODE_TRUECOLOR) == 0 || strcmp(lower, "true") == 0 || strcmp(lower, "tc") == 0 ||
200 strcmp(lower, "rgb") == 0 || strcmp(lower, "24bit") == 0 || strcmp(lower, "3") == 0) {
201 *color_mode = TERM_COLOR_TRUECOLOR;
202 return true;
203 }
204
205 // Invalid value - suggest closest match
206 if (error_msg) {
208 const char *suggestion = asciichat_suggest_enum_value("color-mode", arg);
209 if (suggestion) {
210 safe_snprintf(msg, sizeof(msg), "Invalid color mode '%s'. Did you mean '%s'?", arg, suggestion);
211 } else {
212 safe_snprintf(msg, sizeof(msg), "Invalid color mode '%s'. Valid values: auto, none, 16, 256, truecolor", arg);
213 }
214 *error_msg = platform_strdup(msg);
215 }
216 return false;
217}
#define OPT_COLOR_MODE_256
Definition enums.h:101
#define OPT_COLOR_MODE_NONE
Definition enums.h:98
#define OPT_COLOR_MODE_TRUECOLOR
Definition enums.h:102
#define OPT_COLOR_MODE_AUTO
Definition enums.h:97
#define OPT_COLOR_MODE_MONO
Definition enums.h:99
#define OPT_COLOR_MODE_16
Definition enums.h:100
int color_mode
Definition fft.c:25
const char * asciichat_suggest_enum_value(const char *option_name, const char *input)
Suggest closest matching enum value using fuzzy matching.

References asciichat_suggest_enum_value(), color_mode, OPT_COLOR_MODE_16, OPT_COLOR_MODE_256, OPT_COLOR_MODE_AUTO, OPT_COLOR_MODE_MONO, OPT_COLOR_MODE_NONE, OPT_COLOR_MODE_TRUECOLOR, OPTIONS_PARSING_PARSERS_LOWER_BUFFER_SIZE, OPTIONS_PARSING_PARSERS_MSG_BUFFER_SIZE, and safe_snprintf().

◆ parse_color_setting()

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

Parse color setting option (–color flag).

Parameters
argString argument (e.g., "auto", "true", "false")
destDestination pointer (int*, will store color_setting_t value)
error_msgOptional error message output (set on failure)
Returns
true on success, false on error

Valid values:

  • "auto", "a", "0" - Smart detection (COLOR_SETTING_AUTO, default)
  • "true", "yes", "1", "on" - Force colors ON (COLOR_SETTING_TRUE)
  • "false", "no", "-1", "off" - Force colors OFF (COLOR_SETTING_FALSE)

This controls whether colors are enabled ("auto"), always on ("true"), or always off ("false") regardless of TTY detection or environment variables.

Definition at line 110 of file parsers.c.

110 {
111 if (!dest) {
112 if (error_msg) {
113 *error_msg = platform_strdup("Internal error: NULL destination");
114 }
115 return false;
116 }
117
118 // Use generic parser with color setting lookup table
119 // Default to TRUE if no arg provided
120 if (!arg || arg[0] == '\0') {
121 int *color_setting = (int *)dest;
122 *color_setting = COLOR_SETTING_TRUE;
123 return true;
124 }
125
126 return parse_setting_generic(arg, dest, g_color_setting_map, error_msg);
127}
@ COLOR_SETTING_TRUE
Force colors ON: always colorize regardless of TTY/piping/CLAUDECODE.
Definition options.h:247

References COLOR_SETTING_TRUE.

◆ parse_log_file()

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

Custom parser for log file paths (–log-file, -L).

Validates that the log file path is safe:

  • Rejects attempts to write to protected system directories (/etc, /System, /Windows, etc.)
  • Allows overwriting existing ascii-chat log files
  • Allows paths in safe locations (/tmp, /var/log, home directory, cwd, etc.)
  • Returns error message if validation fails

Definition at line 1074 of file parsers.c.

1074 {
1075 if (!arg || !dest) {
1076 if (error_msg) {
1077 *error_msg = platform_strdup("Internal error: NULL argument or destination");
1078 }
1079 return false;
1080 }
1081
1082 if (strcmp(arg, "/dev/null") == 0) {
1083 SAFE_STRNCPY((char *)dest, arg, 255);
1084 ((char *)dest)[255] = '\0';
1085 return true;
1086 }
1087
1088 // Validate and normalize the log file path
1089 char *normalized = NULL;
1091
1092 if (result != ASCIICHAT_OK) {
1093 if (error_msg) {
1095 if (HAS_ERRNO(&err_ctx)) {
1096 *error_msg = platform_strdup(err_ctx.context_message);
1097 } else {
1098 *error_msg = platform_strdup("Log file path validation failed");
1099 }
1100 }
1101 return false;
1102 }
1103
1104 // Copy validated path to destination
1105 char *log_file_buf = (char *)dest;
1106 const size_t max_size = 256;
1107 SAFE_STRNCPY(log_file_buf, normalized, max_size - 1);
1108 log_file_buf[max_size - 1] = '\0';
1109
1110 SAFE_FREE(normalized);
1111 return true;
1112}
#define HAS_ERRNO(context)
#define SAFE_STRNCPY(dst, src, size)
Definition common.h:423
#define SAFE_FREE(ptr)
Definition common.h:377
asciichat_error_t
Error and exit codes - unified status values (0-255).
Definition error_codes.h:44
@ ASCIICHAT_OK
Definition error_codes.h:46
asciichat_error_t path_validate_user_path(const char *input, path_role_t role, char **normalized_out)
Validate and canonicalize a user-supplied filesystem path.
Definition path.c:712
@ PATH_ROLE_LOG_FILE
Definition path.h:320

References ASCIICHAT_OK, asciichat_error_context_t::context_message, HAS_ERRNO, PATH_ROLE_LOG_FILE, path_validate_user_path(), SAFE_FREE, and SAFE_STRNCPY.

◆ 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 399 of file parsers.c.

399 {
400 if (!dest) {
401 if (error_msg) {
402 *error_msg = platform_strdup("Internal error: NULL destination");
403 }
404 return false;
405 }
406
407 log_level_t *log_level = (log_level_t *)dest;
408
409 // If no argument provided, use the default log level (based on build type)
410 if (!arg || arg[0] == '\0') {
411 *log_level = DEFAULT_LOG_LEVEL;
412 return true;
413 }
414
416 to_lower(arg, lower, sizeof(lower));
417
418 // Development level
419 if (strcmp(lower, OPT_LOG_LEVEL_DEV) == 0 || strcmp(lower, "development") == 0 || strcmp(lower, "0") == 0) {
420 *log_level = LOG_DEV;
421 return true;
422 }
423
424 // Debug level
425 if (strcmp(lower, OPT_LOG_LEVEL_DEBUG) == 0 || strcmp(lower, "dbg") == 0 || strcmp(lower, "1") == 0) {
426 *log_level = LOG_DEBUG;
427 return true;
428 }
429
430 // Info level
431 if (strcmp(lower, OPT_LOG_LEVEL_INFO) == 0 || strcmp(lower, "information") == 0 || strcmp(lower, "2") == 0) {
432 *log_level = LOG_INFO;
433 return true;
434 }
435
436 // Warning level
437 if (strcmp(lower, OPT_LOG_LEVEL_WARN) == 0 || strcmp(lower, "warning") == 0 || strcmp(lower, "3") == 0) {
438 *log_level = LOG_WARN;
439 return true;
440 }
441
442 // Error level
443 if (strcmp(lower, OPT_LOG_LEVEL_ERROR) == 0 || strcmp(lower, "err") == 0 || strcmp(lower, "4") == 0) {
444 *log_level = LOG_ERROR;
445 return true;
446 }
447
448 // Fatal level
449 if (strcmp(lower, OPT_LOG_LEVEL_FATAL) == 0 || strcmp(lower, "5") == 0) {
450 *log_level = LOG_FATAL;
451 return true;
452 }
453
454 // Invalid value
455 if (error_msg) {
457 safe_snprintf(msg, sizeof(msg), "Invalid log level '%s'. Valid values: dev, debug, info, warn, error, fatal", arg);
458 *error_msg = platform_strdup(msg);
459 }
460 return false;
461}
#define OPT_LOG_LEVEL_DEV
Definition enums.h:89
#define OPT_LOG_LEVEL_FATAL
Definition enums.h:94
#define OPT_LOG_LEVEL_ERROR
Definition enums.h:93
#define OPT_LOG_LEVEL_INFO
Definition enums.h:91
#define OPT_LOG_LEVEL_DEBUG
Definition enums.h:90
#define OPT_LOG_LEVEL_WARN
Definition enums.h:92
log_level_t
Logging levels enumeration.
Definition types.h:29
#define DEFAULT_LOG_LEVEL
Default log level for debug builds (DEBUG and above).
Definition log/log.h:69
#define LOG_DEV
Definition types.h:38
#define LOG_DEBUG
Definition types.h:39
#define LOG_FATAL
Definition types.h:43
#define LOG_ERROR
Definition types.h:42
#define LOG_WARN
Definition types.h:41
#define LOG_INFO
Definition types.h:40

References DEFAULT_LOG_LEVEL, LOG_DEBUG, LOG_DEV, LOG_ERROR, LOG_FATAL, LOG_INFO, LOG_WARN, OPT_LOG_LEVEL_DEBUG, OPT_LOG_LEVEL_DEV, OPT_LOG_LEVEL_ERROR, OPT_LOG_LEVEL_FATAL, OPT_LOG_LEVEL_INFO, OPT_LOG_LEVEL_WARN, OPTIONS_PARSING_PARSERS_LOWER_BUFFER_SIZE, OPTIONS_PARSING_PARSERS_MSG_BUFFER_SIZE, and safe_snprintf().

◆ parse_mirror_media()

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

Parse mirror mode media positional argument.

Parameters
argFile path or URL argument
configPointer to options struct (must contain media_file and media_url fields)
remainingRemaining positional args (unused)
num_remainingCount of remaining args
error_msgError message output (set on failure, caller must free)
Returns
1 if consumed the argument, -1 on error

Mirror mode media argument parsing rules:

  • Cannot be used together with –file or –url flags (mutually exclusive)
  • Attempts to access argument as a file path first (using platform_access)
  • If file doesn't exist or isn't readable, treats as URL
  • Sets either media_file or media_url field depending on what's detected

Example usage with options_builder_add_positional():

builder,
"file|url",
"(optional) Media file path or URL to stream",
false, // Not required (defaults to webcam)
);
int parse_mirror_media(const char *arg, void *config, char **remaining, int num_remaining, char **error_msg)
Parse mirror mode media positional argument.
Definition parsers.c:825

Parse mirror mode media positional argument.

Logic:

  1. Enforce mutual exclusion with –file and –url flags
  2. Check if argument is an accessible file path first
  3. If not a file, treat as URL (yt-dlp/ffmpeg will handle errors)
  4. Set media_file or media_url accordingly

Definition at line 825 of file parsers.c.

825 {
826 (void)remaining;
827 (void)num_remaining;
828
829 if (!arg || !config) {
830 if (error_msg) {
831 *error_msg = platform_strdup("Internal error: NULL argument or config");
832 }
833 return -1;
834 }
835
836 log_debug("Processing argument: '%s'", arg);
837
838 // Access media_file and media_url fields from options_state struct (same as other positional parsers)
839 char *media_file = (char *)config + offsetof(struct options_state, media_file);
840 char *media_url = (char *)config + offsetof(struct options_state, media_url);
841
842 // Enforce mutual exclusion with --file flag
843 if (media_file[0] != '\0') {
844 if (error_msg) {
845 *error_msg = platform_strdup("cannot use both --file and a positional argument");
846 }
847 return -1;
848 }
849
850 // Enforce mutual exclusion with --url flag
851 if (media_url[0] != '\0') {
852 if (error_msg) {
853 *error_msg = platform_strdup("cannot use both --url and a positional argument");
854 }
855 return -1;
856 }
857
858 // Check if it's an accessible file path (priority 1)
859 if (platform_access(arg, PLATFORM_ACCESS_READ) == 0) {
860 log_debug("Argument '%s' is an accessible file", arg);
861 SAFE_SNPRINTF(media_file, OPTIONS_BUFF_SIZE, "%s", arg);
862 return 1; // Consumed 1 argument
863 }
864
865 // Not a file; treat as URL (yt-dlp/ffmpeg will handle any errors)
866 log_debug("Argument '%s' not found as file; treating as URL", arg);
867 SAFE_SNPRINTF(media_url, OPTIONS_BUFF_SIZE, "%s", arg);
868 return 1; // Consumed 1 argument
869}

References log_debug, OPTIONS_BUFF_SIZE, and SAFE_SNPRINTF.

Referenced by options_preset_unified().

◆ 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 875 of file parsers.c.

875 {
876 if (!arg || !dest) {
877 if (error_msg) {
878 *error_msg = platform_strdup("Internal error: NULL argument or destination");
879 }
880 return false;
881 }
882
883 // The dest pointer points to the palette_custom field in options_t
884 // We need to get the full options_t struct to call parse_palette_chars_option
885 // Since we only have the field pointer, we need to handle this directly
886
887 char *palette_custom = (char *)dest;
888
889 size_t len = strlen(arg);
890 if (len == 0) {
891 if (error_msg) {
892 *error_msg = platform_strdup("Invalid palette-chars: value cannot be empty");
893 }
894 return false;
895 }
896
898 if (error_msg) {
900 safe_snprintf(msg, sizeof(msg), "Invalid palette-chars: too long (%zu chars, max 255)", len);
901 *error_msg = platform_strdup(msg);
902 }
903 return false;
904 }
905
906 // Copy the palette characters
908 palette_custom[OPTIONS_PALETTE_CUSTOM_BUFFER_SIZE - 1] = '\0';
909
910 // Also set the palette type to custom by calculating back to options_t pointer
911 // dest points to options_t.palette_custom, so we can get options_t* using offset arithmetic
912 options_t *opts = (options_t *)((char *)dest - offsetof(options_t, palette_custom));
914 opts->palette_explicit = true;
915
916 return true;
917}
#define OPTIONS_PALETTE_CUSTOM_BUFFER_SIZE
Definition options.h:211
@ PALETTE_CUSTOM
User-defined via –palette-chars.
Definition palette.h:104
bool palette_explicit
Palette explicitly selected.
Definition options.h:1232
palette_type_t palette_type
Selected palette type.
Definition options.h:1229

References OPTIONS_PALETTE_CUSTOM_BUFFER_SIZE, OPTIONS_PARSING_PARSERS_MSG_BUFFER_SIZE, PALETTE_CUSTOM, options_state::palette_explicit, options_state::palette_type, safe_snprintf(), 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 336 of file parsers.c.

336 {
337 if (!arg || !dest) {
338 if (error_msg) {
339 *error_msg = platform_strdup("Internal error: NULL argument or destination");
340 }
341 return false;
342 }
343
344 palette_type_t *palette_type = (palette_type_t *)dest;
346 to_lower(arg, lower, sizeof(lower));
347
348 // Standard palette
349 if (strcmp(lower, OPT_PALETTE_STANDARD) == 0 || strcmp(lower, "std") == 0 || strcmp(lower, "0") == 0) {
350 *palette_type = PALETTE_STANDARD;
351 return true;
352 }
353
354 // Blocks palette
355 if (strcmp(lower, OPT_PALETTE_BLOCKS) == 0 || strcmp(lower, "block") == 0 || strcmp(lower, "1") == 0) {
356 *palette_type = PALETTE_BLOCKS;
357 return true;
358 }
359
360 // Digital palette
361 if (strcmp(lower, OPT_PALETTE_DIGITAL) == 0 || strcmp(lower, "dig") == 0 || strcmp(lower, "2") == 0) {
362 *palette_type = PALETTE_DIGITAL;
363 return true;
364 }
365
366 // Minimal palette
367 if (strcmp(lower, OPT_PALETTE_MINIMAL) == 0 || strcmp(lower, "min") == 0 || strcmp(lower, "3") == 0) {
368 *palette_type = PALETTE_MINIMAL;
369 return true;
370 }
371
372 // Cool palette
373 if (strcmp(lower, OPT_PALETTE_COOL) == 0 || strcmp(lower, "4") == 0) {
374 *palette_type = PALETTE_COOL;
375 return true;
376 }
377
378 // Custom palette
379 if (strcmp(lower, OPT_PALETTE_CUSTOM) == 0 || strcmp(lower, "5") == 0) {
380 *palette_type = PALETTE_CUSTOM;
381 return true;
382 }
383
384 // Invalid value - suggest closest match
385 if (error_msg) {
387 const char *suggestion = asciichat_suggest_enum_value("palette", arg);
388 if (suggestion) {
389 safe_snprintf(msg, sizeof(msg), "Invalid palette type '%s'. Did you mean '%s'?", arg, suggestion);
390 } else {
391 safe_snprintf(msg, sizeof(msg),
392 "Invalid palette type '%s'. Valid values: standard, blocks, digital, minimal, cool, custom", arg);
393 }
394 *error_msg = platform_strdup(msg);
395 }
396 return false;
397}
#define OPT_PALETTE_CUSTOM
Definition enums.h:125
#define OPT_PALETTE_COOL
Definition enums.h:124
#define OPT_PALETTE_MINIMAL
Definition enums.h:123
#define OPT_PALETTE_BLOCKS
Definition enums.h:121
#define OPT_PALETTE_STANDARD
Definition enums.h:120
#define OPT_PALETTE_DIGITAL
Definition enums.h:122
palette_type_t
Built-in palette type enumeration.
Definition palette.h:90
@ PALETTE_BLOCKS
Unicode block characters: " ░░▒▒▓▓██".
Definition palette.h:96
@ PALETTE_COOL
Ascending blocks: " ▁▂▃▄▅▆▇█".
Definition palette.h:102
@ PALETTE_STANDARD
Standard ASCII palette: " ...',;:clodxkO0KXNWM".
Definition palette.h:94
@ PALETTE_DIGITAL
Digital/glitch aesthetic: " -=≡≣▰▱◼".
Definition palette.h:98
@ PALETTE_MINIMAL
Simple ASCII: " .-+*#".
Definition palette.h:100

References asciichat_suggest_enum_value(), OPT_PALETTE_BLOCKS, OPT_PALETTE_COOL, OPT_PALETTE_CUSTOM, OPT_PALETTE_DIGITAL, OPT_PALETTE_MINIMAL, OPT_PALETTE_STANDARD, OPTIONS_PARSING_PARSERS_LOWER_BUFFER_SIZE, OPTIONS_PARSING_PARSERS_MSG_BUFFER_SIZE, PALETTE_BLOCKS, PALETTE_COOL, PALETTE_CUSTOM, PALETTE_DIGITAL, PALETTE_MINIMAL, PALETTE_STANDARD, and safe_snprintf().

◆ parse_port_option()

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

Parse and validate port option for CLI.

Parameters
argPort string to parse
destDestination pointer (int* for port storage)
error_msgError message output (set on failure, caller must free)
Returns
true on success, false on error

Validates port is a number in the range 1-65535.

Definition at line 463 of file parsers.c.

463 {
464 if (!arg || !dest) {
465 if (error_msg) {
466 *error_msg = platform_strdup("Internal error: NULL argument or destination");
467 }
468 return false;
469 }
470
471 int *port_value = (int *)dest;
472 uint16_t port_num;
473
474 // Use the existing parse_port function for validation
475 asciichat_error_t err = parse_port(arg, &port_num);
476 if (err != ASCIICHAT_OK) {
477 if (error_msg) {
479 safe_snprintf(msg, sizeof(msg), "Invalid port '%s'. Port must be a number between 0 and 65535 (0 = auto-assign).",
480 arg);
481 *error_msg = platform_strdup(msg);
482 }
483 return false;
484 }
485
486 // Port zero is useful to low-level socket APIs for ephemeral binding, but
487 // it is not a valid ascii-chat user-facing port and conflicts with the
488 // option registry's 1-65535 contract.
489 if (port_num == 0) {
490 if (error_msg) {
491 *error_msg = platform_strdup("Port must be between 1 and 65535");
492 }
493 return false;
494 }
495
496 *port_value = (int)port_num;
497 return true;
498}
unsigned short uint16_t
Definition common.h:61
asciichat_error_t parse_port(const char *str, uint16_t *out_port)
Parse port number (1-65535) from string.

References ASCIICHAT_OK, OPTIONS_PARSING_PARSERS_MSG_BUFFER_SIZE, parse_port(), and safe_snprintf().

◆ 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 (terminal_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 250 of file parsers.c.

250 {
251 if (!arg || !dest) {
252 if (error_msg) {
253 *error_msg = platform_strdup("Internal error: NULL argument or destination");
254 }
255 return false;
256 }
257
258 terminal_render_mode_t *render_mode = (terminal_render_mode_t *)dest;
260 to_lower(arg, lower, sizeof(lower));
261
262 // Foreground mode
263 if (strcmp(lower, OPT_RENDER_MODE_FOREGROUND) == 0 || strcmp(lower, OPT_RENDER_MODE_FG) == 0 ||
264 strcmp(lower, "0") == 0) {
265 *render_mode = RENDER_MODE_FOREGROUND;
266 return true;
267 }
268
269 // Background mode
270 if (strcmp(lower, OPT_RENDER_MODE_BACKGROUND) == 0 || strcmp(lower, OPT_RENDER_MODE_BG) == 0 ||
271 strcmp(lower, "1") == 0) {
272 *render_mode = RENDER_MODE_BACKGROUND;
273 return true;
274 }
275
276 // Half-block mode
277 if (strcmp(lower, OPT_RENDER_MODE_HALF_BLOCK) == 0 || strcmp(lower, "hb") == 0 || strcmp(lower, "2") == 0) {
278 *render_mode = RENDER_MODE_HALF_BLOCK;
279 return true;
280 }
281
282 // Invalid value - suggest closest match
283 if (error_msg) {
285 const char *suggestion = asciichat_suggest_enum_value("render-mode", arg);
286 if (suggestion) {
287 safe_snprintf(msg, sizeof(msg), "Invalid render mode '%s'. Did you mean '%s'?", arg, suggestion);
288 } else {
289 safe_snprintf(msg, sizeof(msg), "Invalid render mode '%s'. Valid values: foreground, background, half-block",
290 arg);
291 }
292 *error_msg = platform_strdup(msg);
293 }
294 return false;
295}
#define OPT_RENDER_MODE_BG
Definition enums.h:131
#define OPT_RENDER_MODE_FOREGROUND
Definition enums.h:128
#define OPT_RENDER_MODE_HALF_BLOCK
Definition enums.h:132
#define OPT_RENDER_MODE_FG
Definition enums.h:129
#define OPT_RENDER_MODE_BACKGROUND
Definition enums.h:130

References asciichat_suggest_enum_value(), OPT_RENDER_MODE_BACKGROUND, OPT_RENDER_MODE_BG, OPT_RENDER_MODE_FG, OPT_RENDER_MODE_FOREGROUND, OPT_RENDER_MODE_HALF_BLOCK, OPTIONS_PARSING_PARSERS_LOWER_BUFFER_SIZE, OPTIONS_PARSING_PARSERS_MSG_BUFFER_SIZE, and safe_snprintf().

◆ parse_render_theme()

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

Parse render theme option for pixel rendering.

Parameters
argString argument (e.g., "dark", "light", "auto")
destDestination pointer (int*, will store term_renderer_theme_t value)
error_msgOptional error message output (set on failure)
Returns
true on success, false on error

Controls the rendering theme when using pixel output. The theme determines color palette for ANSI text rendering.

Valid values:

  • "dark", "0" - Dark theme: dark background, light text (TERM_RENDERER_THEME_DARK)
  • "light", "1" - Light theme: light background, dark text (TERM_RENDERER_THEME_LIGHT)
  • "auto", "2" - Auto-detect theme from terminal (TERM_RENDERER_THEME_AUTO, default)

Definition at line 297 of file parsers.c.

297 {
298 if (!arg || !dest) {
299 if (error_msg) {
300 *error_msg = platform_strdup("Internal error: NULL argument or destination");
301 }
302 return false;
303 }
304
305 int *theme = (int *)dest;
307 to_lower(arg, lower, sizeof(lower));
308
309 // Dark theme
310 if (strcmp(lower, OPT_RENDER_THEME_DARK) == 0 || strcmp(lower, "0") == 0) {
311 *theme = 0; // TERM_RENDERER_THEME_DARK
312 return true;
313 }
314
315 // Light theme
316 if (strcmp(lower, OPT_RENDER_THEME_LIGHT) == 0 || strcmp(lower, "1") == 0) {
317 *theme = 1; // TERM_RENDERER_THEME_LIGHT
318 return true;
319 }
320
321 // Auto theme
322 if (strcmp(lower, OPT_VALUE_AUTO) == 0 || strcmp(lower, "2") == 0) {
323 *theme = 2; // TERM_RENDERER_THEME_AUTO
324 return true;
325 }
326
327 // Invalid value
328 if (error_msg) {
330 safe_snprintf(msg, sizeof(msg), "Invalid render theme '%s'. Valid values: dark, light, auto", arg);
331 *error_msg = platform_strdup(msg);
332 }
333 return false;
334}
#define OPT_RENDER_THEME_LIGHT
Definition enums.h:85
#define OPT_RENDER_THEME_DARK
Definition enums.h:84
#define OPT_VALUE_AUTO
Definition enums.h:77

References OPT_RENDER_THEME_DARK, OPT_RENDER_THEME_LIGHT, OPT_VALUE_AUTO, OPTIONS_PARSING_PARSERS_LOWER_BUFFER_SIZE, OPTIONS_PARSING_PARSERS_MSG_BUFFER_SIZE, and safe_snprintf().

◆ 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:514

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 514 of file parsers.c.

514 {
515 if (!arg || !config) {
516 if (error_msg) {
517 *error_msg = platform_strdup("Internal error: NULL argument or config");
518 }
519 return -1;
520 }
521
522 // Assume config struct has address and address6 fields (OPTIONS_BUFF_SIZE each)
523 // This is a simplified version that assumes standard options_state layout
524 char *address = (char *)config + offsetof(struct options_state, address);
525 char *address6 = (char *)config + offsetof(struct options_state, address6);
526
527 int consumed = 0;
528
529 // Parse first argument (IPv4 or IPv6)
530 char parsed_addr[OPTIONS_BUFF_SIZE];
531 const char *addr_to_check = arg;
532 if (parse_ipv6_address(arg, parsed_addr, sizeof(parsed_addr)) == 0) {
533 addr_to_check = parsed_addr;
534 }
535
536 // Check if it's IPv4 or IPv6
537 if (is_valid_ipv4(addr_to_check)) {
538 // Check if we already have a non-default IPv4 address
539 // Allow overwriting defaults (localhost, 0.0.0.0)
540 if (address[0] != '\0' && !is_localhost_ipv4(address) && strcmp(address, "localhost") != 0 &&
541 strcmp(address, "0.0.0.0") != 0) {
542 if (error_msg) {
544 safe_snprintf(msg, sizeof(msg),
545 "Cannot specify multiple IPv4 addresses.\n"
546 "Already have: %s\n"
547 "Cannot add: %s",
548 address, addr_to_check);
549 *error_msg = platform_strdup(msg);
550 }
551 return -1;
552 }
553 SAFE_SNPRINTF(address, OPTIONS_BUFF_SIZE, "%s", addr_to_check);
554 consumed = 1;
555 } else if (is_valid_ipv6(addr_to_check)) {
556 // Check if we already have a non-default IPv6 address
557 // Allow overwriting default (::1)
558 if (address6[0] != '\0' && !is_localhost_ipv6(address6)) {
559 if (error_msg) {
561 safe_snprintf(msg, sizeof(msg),
562 "Cannot specify multiple IPv6 addresses.\n"
563 "Already have: %s\n"
564 "Cannot add: %s",
565 address6, addr_to_check);
566 *error_msg = platform_strdup(msg);
567 }
568 return -1;
569 }
570 SAFE_SNPRINTF(address6, OPTIONS_BUFF_SIZE, "%s", addr_to_check);
571 consumed = 1;
572 } else {
573 if (error_msg) {
575 safe_snprintf(msg, sizeof(msg),
576 "Invalid IP address '%s'.\n"
577 "Server bind addresses must be valid IPv4 or IPv6 addresses.\n"
578 "Examples:\n"
579 " ascii-chat server 0.0.0.0\n"
580 " ascii-chat server ::1\n"
581 " ascii-chat server 0.0.0.0 ::1",
582 arg);
583 *error_msg = platform_strdup(msg);
584 }
585 return -1;
586 }
587
588 // Try to parse second address if available
589 if (remaining && num_remaining > 0 && remaining[0]) {
590 const char *second_arg = remaining[0];
591 memset(parsed_addr, 0, sizeof(parsed_addr));
592 addr_to_check = second_arg;
593 if (parse_ipv6_address(second_arg, parsed_addr, sizeof(parsed_addr)) == 0) {
594 addr_to_check = parsed_addr;
595 }
596
597 if (is_valid_ipv4(addr_to_check)) {
598 // Second is IPv4
599 if (address[0] != '\0' && !is_localhost_ipv4(address) && strcmp(address, "localhost") != 0 &&
600 strcmp(address, "0.0.0.0") != 0) {
601 // Already have an IPv4, can't add another
602 return consumed;
603 }
604 if (is_valid_ipv4(arg)) {
605 // First was also IPv4, can't have two IPv4s
606 return consumed;
607 }
608 // First was IPv6, second is IPv4 - accept both
609 SAFE_SNPRINTF(address, OPTIONS_BUFF_SIZE, "%s", addr_to_check);
610 consumed = 2;
611 } else if (is_valid_ipv6(addr_to_check)) {
612 // Second is IPv6
613 if (address6[0] != '\0' && !is_localhost_ipv6(address6)) {
614 // Already have an IPv6, can't add another
615 return consumed;
616 }
617 if (is_valid_ipv6(arg)) {
618 // First was also IPv6, can't have two IPv6s
619 return consumed;
620 }
621 // First was IPv4, second is IPv6 - accept both
622 SAFE_SNPRINTF(address6, OPTIONS_BUFF_SIZE, "%s", addr_to_check);
623 consumed = 2;
624 }
625 }
626
627 return consumed;
628}
int is_localhost_ipv6(const char *ip)
Check if IPv6 address is localhost.
Definition ip.c:1239
int is_localhost_ipv4(const char *ip)
Check if IPv4 address is localhost.
Definition ip.c:1218
int parse_ipv6_address(const char *input, char *output, size_t output_size)
Parse IPv6 address, removing brackets if present.
Definition ip.c:116

References is_localhost_ipv4(), is_localhost_ipv6(), is_valid_ipv4(), is_valid_ipv6(), OPTIONS_BUFF_SIZE, OPTIONS_PARSING_PARSERS_MSG_BUFFER_SIZE, OPTIONS_PARSING_PARSERS_MSG_EXTENDED_BUFFER_SIZE, parse_ipv6_address(), SAFE_SNPRINTF, and safe_snprintf().

Referenced by options_preset_unified().

◆ parse_timestamp()

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

Custom parser for –timestamp flag.

Allows –timestamp to work both as a flag with an argument. Sets media_seek_timestamp to the provided timestamp in seconds.

Custom parser for –timestamp flag.

Accepts both "hh:mm:ss.ms" format and plain seconds format. Examples:

  • "30" = 30 seconds
  • "30.5" = 30.5 seconds
  • "1:30" = 1 minute 30 seconds (90 seconds)
  • "1:30.5" = 1 minute 30.5 seconds (90.5 seconds)
  • "0:1:30.5" = 1 minute 30.5 seconds (90.5 seconds)
  • "1:2:30.5" = 1 hour 2 minutes 30.5 seconds (3750.5 seconds)

Definition at line 957 of file parsers.c.

957 {
958 if (!arg || arg[0] == '\0') {
959 if (error_msg) {
960 *error_msg = platform_strdup("--seek requires a timestamp argument");
961 }
962 return false;
963 }
964
965 double *timestamp = (double *)dest;
966 char *endptr;
967 long strtol_result;
968
969 // Count colons to determine format
970 int colon_count = 0;
971 for (const char *p = arg; *p; p++) {
972 if (*p == ':')
973 colon_count++;
974 }
975
976 if (colon_count == 0) {
977 // Plain seconds format: "30" or "30.5"
978 *timestamp = strtod(arg, &endptr);
979 if (*endptr != '\0' || *timestamp < 0.0) {
980 if (error_msg) {
981 *error_msg = platform_strdup("Invalid timestamp: expected non-negative seconds");
982 }
983 return false;
984 }
985 return true;
986 } else if (colon_count == 1) {
987 // MM:SS or MM:SS.ms format
988 strtol_result = strtol(arg, &endptr, 10);
989 if (*endptr != ':' || strtol_result < 0) {
990 if (error_msg) {
991 *error_msg = platform_strdup("Invalid timestamp: expected MM:SS or MM:SS.ms format");
992 }
993 return false;
994 }
995 long minutes = strtol_result;
996 double seconds = strtod(endptr + 1, &endptr);
997 if (*endptr != '\0' && *endptr != '.' && *endptr != '\0') {
998 if (error_msg) {
999 *error_msg = platform_strdup("Invalid timestamp: expected MM:SS or MM:SS.ms format");
1000 }
1001 return false;
1002 }
1003 *timestamp = minutes * 60.0 + seconds;
1004 return true;
1005 } else if (colon_count == 2) {
1006 // HH:MM:SS or HH:MM:SS.ms format
1007 strtol_result = strtol(arg, &endptr, 10);
1008 if (*endptr != ':' || strtol_result < 0) {
1009 if (error_msg) {
1010 *error_msg = platform_strdup("Invalid timestamp: expected HH:MM:SS or HH:MM:SS.ms format");
1011 }
1012 return false;
1013 }
1014 long hours = strtol_result;
1015
1016 strtol_result = strtol(endptr + 1, &endptr, 10);
1017 if (*endptr != ':' || strtol_result < 0 || strtol_result >= 60) {
1018 if (error_msg) {
1019 *error_msg = platform_strdup("Invalid timestamp: minutes must be 0-59");
1020 }
1021 return false;
1022 }
1023 long minutes = strtol_result;
1024
1025 double seconds = strtod(endptr + 1, &endptr);
1026 if (*endptr != '\0') {
1027 if (error_msg) {
1028 *error_msg = platform_strdup("Invalid timestamp: expected HH:MM:SS or HH:MM:SS.ms format");
1029 }
1030 return false;
1031 }
1032 *timestamp = hours * (double)SEC_PER_HOUR + minutes * (double)SEC_PER_MIN + seconds;
1033 return true;
1034 } else {
1035 if (error_msg) {
1036 *error_msg = platform_strdup("Invalid timestamp format: too many colons");
1037 }
1038 return false;
1039 }
1040}
#define SEC_PER_HOUR
Definition time.h:180
#define SEC_PER_MIN
Definition time.h:179

References SEC_PER_HOUR, and SEC_PER_MIN.

◆ parse_utf8_setting()

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

Parse UTF-8 setting option (–utf8 flag).

Parameters
argString argument (e.g., "auto", "true", "false"), or NULL/empty for default
destDestination pointer (int*, will store utf8_setting_t value)
error_msgOptional error message output (set on failure)
Returns
true on success, false on error

Valid values:

  • "auto", "a", "0" - Smart detection (UTF8_SETTING_AUTO, default)
  • "true", "yes", "1", "on" - Force UTF-8 ON (UTF8_SETTING_TRUE)
  • "false", "no", "-1", "off" - Force UTF-8 OFF (UTF8_SETTING_FALSE)

When no argument is provided (–utf8 with no value), defaults to UTF8_SETTING_TRUE. This controls whether UTF-8 is auto-detected ("auto"), always on ("true"), or always off ("false") regardless of terminal capability detection.

Definition at line 141 of file parsers.c.

141 {
142 if (!dest) {
143 if (error_msg) {
144 *error_msg = platform_strdup("Internal error: NULL destination");
145 }
146 return false;
147 }
148
149 // Use generic parser with UTF-8 setting lookup table
150 // Default to TRUE if no arg provided
151 if (!arg || arg[0] == '\0') {
152 int *utf8_setting = (int *)dest;
153 *utf8_setting = UTF8_SETTING_TRUE;
154 return true;
155 }
156
157 return parse_setting_generic(arg, dest, g_utf8_setting_map, error_msg);
158}
@ UTF8_SETTING_TRUE
Force UTF-8 ON: always use UTF-8 regardless of terminal capability.
Definition options.h:266

References UTF8_SETTING_TRUE.

◆ parse_verbose_flag()

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

Custom parser for –verbose flag.

Allows –verbose to work both as a flag (without argument) and with an optional count argument. Increments verbose_level each time called.

Definition at line 919 of file parsers.c.

919 {
920 (void)error_msg; // Unused but required by function signature
921
922 // If arg is NULL or starts with a flag, just increment
923 // Otherwise try to parse as integer count
924 unsigned short int *verbose_level = (unsigned short int *)dest;
925
926 if (!arg || arg[0] == '\0') {
927 // No argument provided, just increment
928 (*verbose_level)++;
929 return true;
930 }
931
932 // Try to parse as integer count
933 char *endptr;
934 long value = strtol(arg, &endptr, 10);
935 if (*endptr == '\0' && value >= 0 && value <= 100) {
936 *verbose_level = (unsigned short int)value;
937 return true;
938 }
939
940 // If it didn't parse as int, treat as flag increment
941 (*verbose_level)++;
942 return true;
943}

◆ parse_volume()

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

Custom parser for volume options (–volume, –speakers-volume, –microphone-volume).

Validates that the volume is a float value between 0.0 and 1.0. Sets the destination float to the parsed volume value.

Definition at line 1042 of file parsers.c.

1042 {
1043 if (!arg || !dest) {
1044 if (error_msg) {
1045 *error_msg = platform_strdup("Internal error: NULL argument or destination");
1046 }
1047 return false;
1048 }
1049
1050 float *volume = (float *)dest;
1051 char *endptr;
1052 float val = strtof(arg, &endptr);
1053
1054 if (*endptr != '\0' || arg == endptr) {
1055 if (error_msg) {
1056 *error_msg = platform_strdup("Invalid volume value. Must be a number between 0.0 and 1.0");
1057 }
1058 return false;
1059 }
1060
1061 if (val < 0.0f || val > 1.0f) {
1062 if (error_msg) {
1064 SAFE_SNPRINTF(buf, sizeof(buf), "Volume must be between 0.0 and 1.0 (got %.2f)", val);
1065 *error_msg = platform_strdup(buf);
1066 }
1067 return false;
1068 }
1069
1070 *volume = val;
1071 return true;
1072}
#define OPTIONS_PARSING_PARSERS_BUF_BUFFER_SIZE
Definition parsers.c:11

References OPTIONS_PARSING_PARSERS_BUF_BUFFER_SIZE, and SAFE_SNPRINTF.