ascii-chat 0.11.33
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 1197 of file parsers.c.

1197 {
1198 if (!arg || !dest) {
1199 if (error_msg)
1200 *error_msg = platform_strdup("Internal error: NULL argument or destination");
1201 return false;
1202 }
1203
1205 char lower[32];
1206 to_lower(arg, lower, sizeof(lower));
1207 if (strcmp(lower, OPT_AUDIO_CAPTURE_SOURCE_AUTO) == 0)
1208 *source = AUDIO_CAPTURE_SOURCE_AUTO;
1209 else if (strcmp(lower, OPT_AUDIO_CAPTURE_SOURCE_MIC) == 0)
1210 *source = AUDIO_CAPTURE_SOURCE_MIC;
1211 else if (strcmp(lower, OPT_AUDIO_CAPTURE_SOURCE_MEDIA) == 0)
1213 else if (strcmp(lower, OPT_AUDIO_CAPTURE_SOURCE_BOTH) == 0)
1214 *source = AUDIO_CAPTURE_SOURCE_BOTH;
1215 else if (strcmp(lower, OPT_AUDIO_CAPTURE_SOURCE_REMOTE) == 0)
1217 else {
1218 if (error_msg)
1219 *error_msg = platform_strdup("Audio capture source must be 'auto', 'mic', 'media', 'both', or 'remote'");
1220 return false;
1221 }
1222 return true;
1223}
#define OPT_AUDIO_CAPTURE_SOURCE_BOTH
Definition enums.h:134
#define OPT_AUDIO_CAPTURE_SOURCE_AUTO
Definition enums.h:131
#define OPT_AUDIO_CAPTURE_SOURCE_MEDIA
Definition enums.h:133
#define OPT_AUDIO_CAPTURE_SOURCE_REMOTE
Definition enums.h:135
#define OPT_AUDIO_CAPTURE_SOURCE_MIC
Definition enums.h:132
char * platform_strdup(const char *s)
Duplicate string (strdup replacement)

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, and platform_strdup().

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

1161 {
1162 if (!arg || !dest) {
1163 if (error_msg) {
1164 *error_msg = platform_strdup("Internal error: NULL argument or destination");
1165 }
1166 return false;
1167 }
1168
1169 audio_source_t *audio_source = (audio_source_t *)dest;
1170 char lower[32];
1171 to_lower(arg, lower, sizeof(lower));
1172
1173 if (strcmp(lower, OPT_AUDIO_SOURCE_ALL) == 0 || strcmp(lower, OPT_AUDIO_SOURCE_AUTO) == 0 ||
1174 strcmp(lower, "both") == 0) {
1175 *audio_source = AUDIO_SOURCE_ALL;
1176 return true;
1177 }
1178 if (strcmp(lower, OPT_AUDIO_SOURCE_CALL) == 0 || strcmp(lower, "remote") == 0) {
1179 *audio_source = AUDIO_SOURCE_CALL;
1180 return true;
1181 }
1182 if (strcmp(lower, OPT_AUDIO_SOURCE_MIC) == 0) {
1183 *audio_source = AUDIO_SOURCE_MIC;
1184 return true;
1185 }
1186 if (strcmp(lower, OPT_AUDIO_SOURCE_MEDIA) == 0) {
1187 *audio_source = AUDIO_SOURCE_MEDIA;
1188 return true;
1189 }
1190
1191 if (error_msg) {
1192 *error_msg = platform_strdup("Audio source must be 'all', 'call', 'mic', or 'media'");
1193 }
1194 return false;
1195}
#define OPT_AUDIO_SOURCE_CALL
Definition enums.h:125
#define OPT_AUDIO_SOURCE_AUTO
Definition enums.h:128
#define OPT_AUDIO_SOURCE_ALL
Definition enums.h:124
#define OPT_AUDIO_SOURCE_MEDIA
Definition enums.h:127
#define OPT_AUDIO_SOURCE_MIC
Definition enums.h:126
audio_source_t
Audio source selected for visualization.

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 platform_strdup().

◆ 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.
Definition builder.c:1192
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:685

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

Definition at line 685 of file parsers.c.

685 {
686 (void)remaining;
687 (void)num_remaining;
688
689 if (!arg || !config) {
690 if (error_msg) {
691 *error_msg = platform_strdup("Internal error: NULL argument or config");
692 }
693 return -1;
694 }
695
696 log_debug("parse_client_address: Processing argument: '%s'", arg);
697
698 // Access address and port fields from options_state struct
699 char *address = (char *)config + offsetof(struct options_state, address);
700 int *port = (int *)((char *)config + offsetof(struct options_state, port));
701
702 // Check for WebSocket URL (ws:// or wss://) FIRST before session string validation
703 // WebSocket URLs are passed through without validation or port extraction
704 if (strncmp(arg, "ws://", 5) == 0 || strncmp(arg, "wss://", 6) == 0) {
705 log_debug("Detected WebSocket URL: %s", arg);
706 SAFE_SNPRINTF(address, OPTIONS_BUFF_SIZE, "%s", arg);
707 // For wss://, TLS handles transport encryption - allow ACIP to handle auth independently
708 // For ws://, default to ACIP encryption (no TLS)
709 if (strncmp(arg, "wss://", 6) == 0) {
710 bool *no_encrypt = (bool *)config + offsetof(struct options_state, no_encrypt);
711 *no_encrypt = true;
712 log_debug("Auto-detected wss:// - setting no_encrypt=true (TLS handles transport encryption, ACIP handles auth)");
713 }
714 // Don't set port - WebSocket transport handles URL parsing internally
715 return 1; // Consumed 1 argument
716 }
717
718 // Check for TCP URL (tcp://) - passed through directly to FFmpeg
719 // TCP URLs are used for streaming media from custom TCP servers
720 if (strncmp(arg, "tcp://", 6) == 0) {
721 log_debug("Detected TCP URL: %s", arg);
722 SAFE_SNPRINTF(address, OPTIONS_BUFF_SIZE, "%s", arg);
723 // Don't set port - TCP transport handles URL parsing internally
724 return 1; // Consumed 1 argument
725 }
726
727 // Check if this is a session string (format: adjective-noun-noun)
728 // Session strings have exactly 2 hyphens, only lowercase letters, length 5-47
729 bool is_session = is_session_string(arg);
730 log_debug("parse_client_address: is_session_string('%s') = %s", arg, is_session ? "true" : "false");
731
732 if (is_session) {
733 // This is a session string, not a server address
734 char *session_string = (char *)config + offsetof(struct options_state, session_string);
735 SAFE_SNPRINTF(session_string, SESSION_STRING_BUFFER_SIZE, "%s", arg);
736 log_debug("parse_client_address: Stored session string: %s", arg);
737 return 1; // Consumed 1 arg
738 }
739
740 // Not a session string, parse as server address
741 log_debug("parse_client_address: Parsing as server address (not a session string)");
742
743 // Check for port in address (format: address:port or [ipv6]:port)
744 const char *colon = strrchr(arg, ':');
745
746 if (colon != NULL) {
747 // Check if this is IPv6 with port [::1]:port or plain hostname:port
748 if (arg[0] == '[') {
749 // IPv6 with brackets: [address]:port
750 const char *closing_bracket = strchr(arg, ']');
751 if (closing_bracket && closing_bracket < colon) {
752 // Extract address (remove brackets)
753 size_t addr_len = (size_t)(closing_bracket - arg - 1);
754 if (addr_len >= OPTIONS_BUFF_SIZE) {
755 if (error_msg) {
756 *error_msg = platform_strdup("IPv6 address too long");
757 }
758 return -1;
759 }
760 SAFE_SNPRINTF(address, OPTIONS_BUFF_SIZE, "%.*s", (int)addr_len, arg + 1);
761
762 // Extract and validate port
763 const char *port_str = colon + 1;
764 char *endptr;
765 long port_num = strtol(port_str, &endptr, 10);
766 if (*endptr != '\0' || port_num < 1 || port_num > 65535) {
767 if (error_msg) {
768 char msg[256];
769 safe_snprintf(msg, sizeof(msg), "Invalid port number '%s'. Must be 1-65535.", port_str);
770 *error_msg = platform_strdup(msg);
771 }
772 return -1;
773 }
774 *port = (int)port_num;
775 }
776 } else {
777 // Check if it's IPv6 without brackets (no port allowed)
778 // or hostname/IPv4:port
779 size_t colon_count = 0;
780 for (const char *p = arg; *p; p++) {
781 if (*p == ':')
782 colon_count++;
783 }
784
785 if (colon_count == 1) {
786 // Likely hostname:port or IPv4:port
787 size_t addr_len = (size_t)(colon - arg);
788 if (addr_len >= OPTIONS_BUFF_SIZE) {
789 if (error_msg) {
790 *error_msg = platform_strdup("Address too long");
791 }
792 return -1;
793 }
794 SAFE_SNPRINTF(address, OPTIONS_BUFF_SIZE, "%.*s", (int)addr_len, arg);
795
796 // Extract and validate port
797 const char *port_str = colon + 1;
798 char *endptr;
799 long port_num = strtol(port_str, &endptr, 10);
800 if (*endptr != '\0' || port_num < 1 || port_num > 65535) {
801 if (error_msg) {
802 char msg[256];
803 safe_snprintf(msg, sizeof(msg), "Invalid port number '%s'. Must be 1-65535.", port_str);
804 *error_msg = platform_strdup(msg);
805 }
806 return -1;
807 }
808 *port = (int)port_num;
809 } else {
810 // Multiple colons - likely bare IPv6 address
811 SAFE_SNPRINTF(address, OPTIONS_BUFF_SIZE, "%s", arg);
812 }
813 }
814 } else {
815 // No colon - just an address
816 SAFE_SNPRINTF(address, OPTIONS_BUFF_SIZE, "%s", arg);
817 }
818
819 // Validate addresses using comprehensive IPv4/IPv6 detection
820 bool has_dot = strchr(address, '.') != NULL;
821 bool has_colon = strchr(address, ':') != NULL;
822 bool starts_with_digit = address[0] >= '0' && address[0] <= '9';
823
824 // Potential IPv6 address (has colons) - validate as IPv6
825 if (has_colon) {
826 if (!is_valid_ipv6(address)) {
827 if (error_msg) {
828 char msg[512];
829 safe_snprintf(msg, sizeof(msg),
830 "Invalid IPv6 address '%s'.\n"
831 "IPv6 addresses must be valid hex notation with colons.\n"
832 "Examples: ::1, 2001:db8::1, fe80::1\n"
833 "Or use hostnames like example.com",
834 address);
835 *error_msg = platform_strdup(msg);
836 }
837 return -1;
838 }
839 } else if (has_dot && starts_with_digit) {
840 // Potential IPv4 address (has dots and starts with digit) - validate strictly
841 if (!is_valid_ipv4(address)) {
842 if (error_msg) {
843 char msg[512];
844 safe_snprintf(msg, sizeof(msg),
845 "Invalid IPv4 address '%s'.\n"
846 "IPv4 addresses must have exactly 4 octets (0-255) separated by dots.\n"
847 "Examples: 127.0.0.1, 192.168.1.1\n"
848 "For hostnames, use letters: example.com, localhost",
849 address);
850 *error_msg = platform_strdup(msg);
851 }
852 return -1;
853 }
854 }
855 // Otherwise treat as valid hostname (no validation needed)
856
857 // Note: Port conflict checking would require additional state
858 // (checking if --port flag was used). For now, this is a simplified version.
859 // Full implementation would need to track whether port was set via flag.
860
861 log_debug("parse_client_address: Set address='%s', port=%d", address[0] ? address : "(empty)", *port);
862
863 return 1; // Consumed 1 arg
864}
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:492
#define log_debug(...)
Log a DEBUG message.
Definition log/log.h:548
#define OPTIONS_BUFF_SIZE
Buffer size for option string values.
int safe_snprintf(char *buffer, size_t buffer_size, const char *format,...)
Safe formatted string printing to buffer.
Definition system.c:148
int is_valid_ipv4(const char *ip)
Check if a string is a valid IPv4 address.
Definition ip.c:58
int is_valid_ipv6(const char *ip)
Check if a string is a valid IPv6 address.
Definition ip.c:105
#define SESSION_STRING_BUFFER_SIZE
Consolidated options structure.

References is_session_string(), is_valid_ipv4(), is_valid_ipv6(), log_debug, OPTIONS_BUFF_SIZE, platform_strdup(), 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 269 of file parsers.c.

269 {
270 if (!arg || !dest) {
271 if (error_msg) {
272 *error_msg = platform_strdup("Internal error: NULL argument or destination");
273 }
274 return false;
275 }
276
277 color_filter_t *color_filter = (color_filter_t *)dest;
278 char lower[32];
279 to_lower(arg, lower, sizeof(lower));
280
281 // Try to match against all known color filters
282 *color_filter = color_filter_from_cli_name(lower);
283 if (*color_filter != COLOR_FILTER_NONE || strcmp(lower, OPT_COLOR_FILTER_NONE) == 0) {
284 return true;
285 }
286
287 // Invalid value
288 if (error_msg) {
289 char msg[256];
290 safe_snprintf(msg, sizeof(msg),
291 "Invalid color filter '%s'. Valid values: none, black, white, green, magenta, fuchsia, "
292 "orange, teal, cyan, pink, red, yellow",
293 arg);
294 *error_msg = platform_strdup(msg);
295 }
296 return false;
297}
#define OPT_COLOR_FILTER_NONE
Definition enums.h:90
color_filter_t color_filter_from_cli_name(const char *cli_name)
Convert color filter CLI name to enum value.
color_filter_t
Monochromatic color filter enumeration.
Definition terminal.h:599
@ COLOR_FILTER_NONE
No filtering (default)
Definition terminal.h:601

References color_filter_from_cli_name(), COLOR_FILTER_NONE, OPT_COLOR_FILTER_NONE, platform_strdup(), 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 210 of file parsers.c.

210 {
211 if (!arg || !dest) {
212 if (error_msg) {
213 *error_msg = platform_strdup("Internal error: NULL argument or destination");
214 }
215 return false;
216 }
217
218 terminal_color_mode_t *color_mode = (terminal_color_mode_t *)dest;
219 char lower[32];
220 to_lower(arg, lower, sizeof(lower));
221
222 // Auto-detect
223 if (strcmp(lower, OPT_COLOR_MODE_AUTO) == 0 || strcmp(lower, "a") == 0) {
224 *color_mode = TERM_COLOR_AUTO;
225 return true;
226 }
227
228 // Monochrome/None
229 if (strcmp(lower, OPT_COLOR_MODE_NONE) == 0 || strcmp(lower, "mono") == 0 || strcmp(lower, "monochrome") == 0 ||
230 strcmp(lower, "0") == 0) {
231 *color_mode = TERM_COLOR_NONE;
232 return true;
233 }
234
235 // 16-color
236 if (strcmp(lower, OPT_COLOR_MODE_16) == 0 || strcmp(lower, "16color") == 0 || strcmp(lower, "ansi") == 0 ||
237 strcmp(lower, "1") == 0) {
238 *color_mode = TERM_COLOR_16;
239 return true;
240 }
241
242 // 256-color
243 if (strcmp(lower, OPT_COLOR_MODE_256) == 0 || strcmp(lower, "256color") == 0 || strcmp(lower, "2") == 0) {
244 *color_mode = TERM_COLOR_256;
245 return true;
246 }
247
248 // Truecolor
249 if (strcmp(lower, OPT_COLOR_MODE_TRUECOLOR) == 0 || strcmp(lower, "true") == 0 || strcmp(lower, "tc") == 0 ||
250 strcmp(lower, "rgb") == 0 || strcmp(lower, "24bit") == 0 || strcmp(lower, "3") == 0) {
251 *color_mode = TERM_COLOR_TRUECOLOR;
252 return true;
253 }
254
255 // Invalid value - suggest closest match
256 if (error_msg) {
257 char msg[256];
258 const char *suggestion = asciichat_suggest_enum_value("color-mode", arg);
259 if (suggestion) {
260 safe_snprintf(msg, sizeof(msg), "Invalid color mode '%s'. Did you mean '%s'?", arg, suggestion);
261 } else {
262 safe_snprintf(msg, sizeof(msg), "Invalid color mode '%s'. Valid values: auto, none, 16, 256, truecolor", arg);
263 }
264 *error_msg = platform_strdup(msg);
265 }
266 return false;
267}
#define OPT_COLOR_MODE_256
Definition enums.h:86
#define OPT_COLOR_MODE_NONE
Definition enums.h:84
#define OPT_COLOR_MODE_TRUECOLOR
Definition enums.h:87
#define OPT_COLOR_MODE_AUTO
Definition enums.h:83
#define OPT_COLOR_MODE_16
Definition enums.h:85
const char * asciichat_suggest_enum_value(const char *option_name, const char *input)
Suggest closest matching enum value using fuzzy matching.
terminal_color_mode_t
Terminal color support levels.
Definition terminal.h:578
@ TERM_COLOR_NONE
No color support (monochrome terminal)
Definition terminal.h:582
@ TERM_COLOR_16
16-color support (standard ANSI colors)
Definition terminal.h:584
@ TERM_COLOR_256
256-color support (extended ANSI palette)
Definition terminal.h:586
@ TERM_COLOR_AUTO
Auto-detect color support from terminal capabilities.
Definition terminal.h:580
@ TERM_COLOR_TRUECOLOR
24-bit truecolor support (RGB colors)
Definition terminal.h:588

References asciichat_suggest_enum_value(), OPT_COLOR_MODE_16, OPT_COLOR_MODE_256, OPT_COLOR_MODE_AUTO, OPT_COLOR_MODE_NONE, OPT_COLOR_MODE_TRUECOLOR, platform_strdup(), safe_snprintf(), TERM_COLOR_16, TERM_COLOR_256, TERM_COLOR_AUTO, TERM_COLOR_NONE, and TERM_COLOR_TRUECOLOR.

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

160 {
161 if (!dest) {
162 if (error_msg) {
163 *error_msg = platform_strdup("Internal error: NULL destination");
164 }
165 return false;
166 }
167
168 // Use generic parser with color setting lookup table
169 // Default to TRUE if no arg provided
170 if (!arg || arg[0] == '\0') {
171 int *color_setting = (int *)dest;
172 *color_setting = COLOR_SETTING_TRUE;
173 return true;
174 }
175
176 return parse_setting_generic(arg, dest, g_color_setting_map, error_msg);
177}
@ COLOR_SETTING_TRUE
Force colors ON: always colorize regardless of TTY/piping/CLAUDECODE.

References COLOR_SETTING_TRUE, and platform_strdup().

Referenced by options_init().

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

1127 {
1128 if (!arg || !dest) {
1129 if (error_msg) {
1130 *error_msg = platform_strdup("Internal error: NULL argument or destination");
1131 }
1132 return false;
1133 }
1134
1135 // Validate and normalize the log file path
1136 char *normalized = NULL;
1138
1139 if (result != ASCIICHAT_OK) {
1140 if (error_msg) {
1142 if (HAS_ERRNO(&err_ctx)) {
1143 *error_msg = platform_strdup(err_ctx.context_message);
1144 } else {
1145 *error_msg = platform_strdup("Log file path validation failed");
1146 }
1147 }
1148 return false;
1149 }
1150
1151 // Copy validated path to destination
1152 char *log_file_buf = (char *)dest;
1153 const size_t max_size = 256;
1154 SAFE_STRNCPY(log_file_buf, normalized, max_size - 1);
1155 log_file_buf[max_size - 1] = '\0';
1156
1157 SAFE_FREE(normalized);
1158 return true;
1159}
#define SAFE_STRNCPY(dst, src, size)
Definition common.h:414
#define SAFE_FREE(ptr)
Definition common.h:376
#define HAS_ERRNO(var)
Check if an error occurred and get full context.
asciichat_error_t
Error and exit codes - unified status values (0-255)
Definition error_codes.h:49
@ ASCIICHAT_OK
Definition error_codes.h:51
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:1020
@ PATH_ROLE_LOG_FILE
Definition path.h:318
Error context structure.
char * context_message
Optional custom message (dynamically allocated, owned by system)

References ASCIICHAT_OK, asciichat_error_context_t::context_message, HAS_ERRNO, PATH_ROLE_LOG_FILE, path_validate_user_path(), platform_strdup(), 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 448 of file parsers.c.

448 {
449 if (!dest) {
450 if (error_msg) {
451 *error_msg = platform_strdup("Internal error: NULL destination");
452 }
453 return false;
454 }
455
456 log_level_t *log_level = (log_level_t *)dest;
457
458 // If no argument provided, use the default log level (based on build type)
459 if (!arg || arg[0] == '\0') {
460 *log_level = DEFAULT_LOG_LEVEL;
461 return true;
462 }
463
464 char lower[32];
465 to_lower(arg, lower, sizeof(lower));
466
467 // Development level
468 if (strcmp(lower, OPT_LOG_LEVEL_DEV) == 0 || strcmp(lower, "development") == 0 || strcmp(lower, "0") == 0) {
469 *log_level = LOG_DEV;
470 return true;
471 }
472
473 // Debug level
474 if (strcmp(lower, OPT_LOG_LEVEL_DEBUG) == 0 || strcmp(lower, "dbg") == 0 || strcmp(lower, "1") == 0) {
475 *log_level = LOG_DEBUG;
476 return true;
477 }
478
479 // Info level
480 if (strcmp(lower, OPT_LOG_LEVEL_INFO) == 0 || strcmp(lower, "information") == 0 || strcmp(lower, "2") == 0) {
481 *log_level = LOG_INFO;
482 return true;
483 }
484
485 // Warning level
486 if (strcmp(lower, OPT_LOG_LEVEL_WARN) == 0 || strcmp(lower, "warning") == 0 || strcmp(lower, "3") == 0) {
487 *log_level = LOG_WARN;
488 return true;
489 }
490
491 // Error level
492 if (strcmp(lower, OPT_LOG_LEVEL_ERROR) == 0 || strcmp(lower, "err") == 0 || strcmp(lower, "4") == 0) {
493 *log_level = LOG_ERROR;
494 return true;
495 }
496
497 // Fatal level
498 if (strcmp(lower, OPT_LOG_LEVEL_FATAL) == 0 || strcmp(lower, "5") == 0) {
499 *log_level = LOG_FATAL;
500 return true;
501 }
502
503 // Invalid value
504 if (error_msg) {
505 char msg[256];
506 safe_snprintf(msg, sizeof(msg), "Invalid log level '%s'. Valid values: dev, debug, info, warn, error, fatal", arg);
507 *error_msg = platform_strdup(msg);
508 }
509 return false;
510}
#define OPT_LOG_LEVEL_DEV
Definition enums.h:75
#define OPT_LOG_LEVEL_FATAL
Definition enums.h:80
#define OPT_LOG_LEVEL_ERROR
Definition enums.h:79
#define OPT_LOG_LEVEL_INFO
Definition enums.h:77
#define OPT_LOG_LEVEL_DEBUG
Definition enums.h:76
#define OPT_LOG_LEVEL_WARN
Definition enums.h:78
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:65
#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, platform_strdup(), and safe_snprintf().

Referenced by options_init().

◆ 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 positional argument (file path or URL)
Definition parsers.c:879

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

879 {
880 (void)remaining;
881 (void)num_remaining;
882
883 if (!arg || !config) {
884 if (error_msg) {
885 *error_msg = platform_strdup("Internal error: NULL argument or config");
886 }
887 return -1;
888 }
889
890 log_debug("parse_mirror_media: Processing argument: '%s'", arg);
891
892 // Access media_file and media_url fields from options_state struct (same as other positional parsers)
893 char *media_file = (char *)config + offsetof(struct options_state, media_file);
894 char *media_url = (char *)config + offsetof(struct options_state, media_url);
895
896 // Enforce mutual exclusion with --file flag
897 if (media_file[0] != '\0') {
898 if (error_msg) {
899 *error_msg = platform_strdup("cannot use both --file and a positional argument");
900 }
901 return -1;
902 }
903
904 // Enforce mutual exclusion with --url flag
905 if (media_url[0] != '\0') {
906 if (error_msg) {
907 *error_msg = platform_strdup("cannot use both --url and a positional argument");
908 }
909 return -1;
910 }
911
912 // Check if it's an accessible file path (priority 1)
913 if (platform_access(arg, PLATFORM_ACCESS_READ) == 0) {
914 log_debug("Argument '%s' is an accessible file", arg);
915 SAFE_SNPRINTF(media_file, OPTIONS_BUFF_SIZE, "%s", arg);
916 return 1; // Consumed 1 argument
917 }
918
919 // Not a file; treat as URL (yt-dlp/ffmpeg will handle any errors)
920 log_debug("Argument '%s' not found as file; treating as URL", arg);
921 SAFE_SNPRINTF(media_url, OPTIONS_BUFF_SIZE, "%s", arg);
922 return 1; // Consumed 1 argument
923}
int platform_access(const char *pathname, int mode)
Check file/directory access permissions.
#define PLATFORM_ACCESS_READ
Check if file/directory is readable.

References log_debug, OPTIONS_BUFF_SIZE, platform_access(), PLATFORM_ACCESS_READ, platform_strdup(), 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 929 of file parsers.c.

929 {
930 if (!arg || !dest) {
931 if (error_msg) {
932 *error_msg = platform_strdup("Internal error: NULL argument or destination");
933 }
934 return false;
935 }
936
937 // The dest pointer points to the palette_custom field in options_t
938 // We need to get the full options_t struct to call parse_palette_chars_option
939 // Since we only have the field pointer, we need to handle this directly
940
941 char *palette_custom = (char *)dest;
942
943 size_t len = strlen(arg);
944 if (len == 0) {
945 if (error_msg) {
946 *error_msg = platform_strdup("Invalid palette-chars: value cannot be empty");
947 }
948 return false;
949 }
950
951 if (len >= 256) {
952 if (error_msg) {
953 char msg[256];
954 safe_snprintf(msg, sizeof(msg), "Invalid palette-chars: too long (%zu chars, max 255)", len);
955 *error_msg = platform_strdup(msg);
956 }
957 return false;
958 }
959
960 // Copy the palette characters
961 SAFE_STRNCPY(palette_custom, arg, 256);
962 palette_custom[255] = '\0';
963
964 // Also set the palette type to custom by calculating back to options_t pointer
965 // dest points to options_t.palette_custom, so we can get options_t* using offset arithmetic
966 options_t *opts = (options_t *)((char *)dest - offsetof(options_t, palette_custom));
968
969 return true;
970}
@ PALETTE_CUSTOM
User-defined via –palette-chars.
Definition palette.h:98
palette_type_t palette_type
Selected palette type.

References PALETTE_CUSTOM, options_state::palette_type, platform_strdup(), 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 385 of file parsers.c.

385 {
386 if (!arg || !dest) {
387 if (error_msg) {
388 *error_msg = platform_strdup("Internal error: NULL argument or destination");
389 }
390 return false;
391 }
392
393 palette_type_t *palette_type = (palette_type_t *)dest;
394 char lower[32];
395 to_lower(arg, lower, sizeof(lower));
396
397 // Standard palette
398 if (strcmp(lower, OPT_PALETTE_STANDARD) == 0 || strcmp(lower, "std") == 0 || strcmp(lower, "0") == 0) {
399 *palette_type = PALETTE_STANDARD;
400 return true;
401 }
402
403 // Blocks palette
404 if (strcmp(lower, OPT_PALETTE_BLOCKS) == 0 || strcmp(lower, "block") == 0 || strcmp(lower, "1") == 0) {
405 *palette_type = PALETTE_BLOCKS;
406 return true;
407 }
408
409 // Digital palette
410 if (strcmp(lower, OPT_PALETTE_DIGITAL) == 0 || strcmp(lower, "dig") == 0 || strcmp(lower, "2") == 0) {
411 *palette_type = PALETTE_DIGITAL;
412 return true;
413 }
414
415 // Minimal palette
416 if (strcmp(lower, OPT_PALETTE_MINIMAL) == 0 || strcmp(lower, "min") == 0 || strcmp(lower, "3") == 0) {
417 *palette_type = PALETTE_MINIMAL;
418 return true;
419 }
420
421 // Cool palette
422 if (strcmp(lower, OPT_PALETTE_COOL) == 0 || strcmp(lower, "4") == 0) {
423 *palette_type = PALETTE_COOL;
424 return true;
425 }
426
427 // Custom palette
428 if (strcmp(lower, OPT_PALETTE_CUSTOM) == 0 || strcmp(lower, "5") == 0) {
429 *palette_type = PALETTE_CUSTOM;
430 return true;
431 }
432
433 // Invalid value - suggest closest match
434 if (error_msg) {
435 char msg[256];
436 const char *suggestion = asciichat_suggest_enum_value("palette", arg);
437 if (suggestion) {
438 safe_snprintf(msg, sizeof(msg), "Invalid palette type '%s'. Did you mean '%s'?", arg, suggestion);
439 } else {
440 safe_snprintf(msg, sizeof(msg),
441 "Invalid palette type '%s'. Valid values: standard, blocks, digital, minimal, cool, custom", arg);
442 }
443 *error_msg = platform_strdup(msg);
444 }
445 return false;
446}
#define OPT_PALETTE_CUSTOM
Definition enums.h:110
#define OPT_PALETTE_COOL
Definition enums.h:109
#define OPT_PALETTE_MINIMAL
Definition enums.h:108
#define OPT_PALETTE_BLOCKS
Definition enums.h:106
#define OPT_PALETTE_STANDARD
Definition enums.h:105
#define OPT_PALETTE_DIGITAL
Definition enums.h:107
palette_type_t
Built-in palette type enumeration.
Definition palette.h:84
@ PALETTE_BLOCKS
Unicode block characters: " ░░▒▒▓▓██".
Definition palette.h:90
@ PALETTE_COOL
Ascending blocks: " ▁▂▃▄▅▆▇█".
Definition palette.h:96
@ PALETTE_STANDARD
Standard ASCII palette: " ...',;:clodxkO0KXNWM".
Definition palette.h:88
@ PALETTE_DIGITAL
Digital/glitch aesthetic: " -=≡≣▰▱◼".
Definition palette.h:92
@ PALETTE_MINIMAL
Simple ASCII: " .-+*#".
Definition palette.h:94

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

Referenced by set_palette().

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

512 {
513 if (!arg || !dest) {
514 if (error_msg) {
515 *error_msg = platform_strdup("Internal error: NULL argument or destination");
516 }
517 return false;
518 }
519
520 int *port_value = (int *)dest;
521 uint16_t port_num;
522
523 // Use the existing parse_port function for validation
524 asciichat_error_t err = parse_port(arg, &port_num);
525 if (err != ASCIICHAT_OK) {
526 if (error_msg) {
527 char msg[256];
528 safe_snprintf(msg, sizeof(msg), "Invalid port '%s'. Port must be a number between 0 and 65535 (0 = auto-assign).",
529 arg);
530 *error_msg = platform_strdup(msg);
531 }
532 return false;
533 }
534
535 // Port zero is useful to low-level socket APIs for ephemeral binding, but
536 // it is not a valid ascii-chat user-facing port and conflicts with the
537 // option registry's 1-65535 contract.
538 if (port_num == 0) {
539 if (error_msg) {
540 *error_msg = platform_strdup("Port must be between 1 and 65535");
541 }
542 return false;
543 }
544
545 *port_value = (int)port_num;
546 return true;
547}
unsigned short uint16_t
Definition common.h:57
asciichat_error_t parse_port(const char *str, uint16_t *out_port)
Parse port number (1-65535) from string.

References ASCIICHAT_OK, parse_port(), platform_strdup(), 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 (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 299 of file parsers.c.

299 {
300 if (!arg || !dest) {
301 if (error_msg) {
302 *error_msg = platform_strdup("Internal error: NULL argument or destination");
303 }
304 return false;
305 }
306
307 render_mode_t *render_mode = (render_mode_t *)dest;
308 char lower[32];
309 to_lower(arg, lower, sizeof(lower));
310
311 // Foreground mode
312 if (strcmp(lower, OPT_RENDER_MODE_FOREGROUND) == 0 || strcmp(lower, OPT_RENDER_MODE_FG) == 0 ||
313 strcmp(lower, "0") == 0) {
314 *render_mode = RENDER_MODE_FOREGROUND;
315 return true;
316 }
317
318 // Background mode
319 if (strcmp(lower, OPT_RENDER_MODE_BACKGROUND) == 0 || strcmp(lower, OPT_RENDER_MODE_BG) == 0 ||
320 strcmp(lower, "1") == 0) {
321 *render_mode = RENDER_MODE_BACKGROUND;
322 return true;
323 }
324
325 // Half-block mode
326 if (strcmp(lower, OPT_RENDER_MODE_HALF_BLOCK) == 0 || strcmp(lower, "hb") == 0 || strcmp(lower, "2") == 0) {
327 *render_mode = RENDER_MODE_HALF_BLOCK;
328 return true;
329 }
330
331 // Invalid value - suggest closest match
332 if (error_msg) {
333 char msg[256];
334 const char *suggestion = asciichat_suggest_enum_value("render-mode", arg);
335 if (suggestion) {
336 safe_snprintf(msg, sizeof(msg), "Invalid render mode '%s'. Did you mean '%s'?", arg, suggestion);
337 } else {
338 safe_snprintf(msg, sizeof(msg), "Invalid render mode '%s'. Valid values: foreground, background, half-block",
339 arg);
340 }
341 *error_msg = platform_strdup(msg);
342 }
343 return false;
344}
#define OPT_RENDER_MODE_BG
Definition enums.h:116
#define OPT_RENDER_MODE_FOREGROUND
Definition enums.h:113
#define OPT_RENDER_MODE_HALF_BLOCK
Definition enums.h:117
#define OPT_RENDER_MODE_FG
Definition enums.h:114
#define OPT_RENDER_MODE_BACKGROUND
Definition enums.h:115
render_mode_t
Render mode preferences.
Definition terminal.h:662
@ RENDER_MODE_FOREGROUND
Foreground colors only (text color)
Definition terminal.h:664
@ RENDER_MODE_BACKGROUND
Background colors (block colors)
Definition terminal.h:666
@ RENDER_MODE_HALF_BLOCK
Unicode half-block characters (mixed foreground/background)
Definition terminal.h:668

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, platform_strdup(), RENDER_MODE_BACKGROUND, RENDER_MODE_FOREGROUND, RENDER_MODE_HALF_BLOCK, 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 346 of file parsers.c.

346 {
347 if (!arg || !dest) {
348 if (error_msg) {
349 *error_msg = platform_strdup("Internal error: NULL argument or destination");
350 }
351 return false;
352 }
353
354 int *theme = (int *)dest;
355 char lower[32];
356 to_lower(arg, lower, sizeof(lower));
357
358 // Dark theme
359 if (strcmp(lower, "dark") == 0 || strcmp(lower, "0") == 0) {
360 *theme = 0; // TERM_RENDERER_THEME_DARK
361 return true;
362 }
363
364 // Light theme
365 if (strcmp(lower, "light") == 0 || strcmp(lower, "1") == 0) {
366 *theme = 1; // TERM_RENDERER_THEME_LIGHT
367 return true;
368 }
369
370 // Auto theme
371 if (strcmp(lower, "auto") == 0 || strcmp(lower, "2") == 0) {
372 *theme = 2; // TERM_RENDERER_THEME_AUTO
373 return true;
374 }
375
376 // Invalid value
377 if (error_msg) {
378 char msg[256];
379 safe_snprintf(msg, sizeof(msg), "Invalid render theme '%s'. Valid values: dark, light, auto", arg);
380 *error_msg = platform_strdup(msg);
381 }
382 return false;
383}

References platform_strdup(), 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:563

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

563 {
564 if (!arg || !config) {
565 if (error_msg) {
566 *error_msg = platform_strdup("Internal error: NULL argument or config");
567 }
568 return -1;
569 }
570
571 // Assume config struct has address and address6 fields (OPTIONS_BUFF_SIZE each)
572 // This is a simplified version that assumes standard options_state layout
573 char *address = (char *)config + offsetof(struct options_state, address);
574 char *address6 = (char *)config + offsetof(struct options_state, address6);
575
576 int consumed = 0;
577
578 // Parse first argument (IPv4 or IPv6)
579 char parsed_addr[OPTIONS_BUFF_SIZE];
580 const char *addr_to_check = arg;
581 if (parse_ipv6_address(arg, parsed_addr, sizeof(parsed_addr)) == 0) {
582 addr_to_check = parsed_addr;
583 }
584
585 // Check if it's IPv4 or IPv6
586 if (is_valid_ipv4(addr_to_check)) {
587 // Check if we already have a non-default IPv4 address
588 // Allow overwriting defaults (localhost, 0.0.0.0)
589 if (address[0] != '\0' && !is_localhost_ipv4(address) && strcmp(address, "localhost") != 0 &&
590 strcmp(address, "0.0.0.0") != 0) {
591 if (error_msg) {
592 char msg[256];
593 safe_snprintf(msg, sizeof(msg),
594 "Cannot specify multiple IPv4 addresses.\n"
595 "Already have: %s\n"
596 "Cannot add: %s",
597 address, addr_to_check);
598 *error_msg = platform_strdup(msg);
599 }
600 return -1;
601 }
602 SAFE_SNPRINTF(address, OPTIONS_BUFF_SIZE, "%s", addr_to_check);
603 consumed = 1;
604 } else if (is_valid_ipv6(addr_to_check)) {
605 // Check if we already have a non-default IPv6 address
606 // Allow overwriting default (::1)
607 if (address6[0] != '\0' && !is_localhost_ipv6(address6)) {
608 if (error_msg) {
609 char msg[256];
610 safe_snprintf(msg, sizeof(msg),
611 "Cannot specify multiple IPv6 addresses.\n"
612 "Already have: %s\n"
613 "Cannot add: %s",
614 address6, addr_to_check);
615 *error_msg = platform_strdup(msg);
616 }
617 return -1;
618 }
619 SAFE_SNPRINTF(address6, OPTIONS_BUFF_SIZE, "%s", addr_to_check);
620 consumed = 1;
621 } else {
622 if (error_msg) {
623 char msg[512];
624 safe_snprintf(msg, sizeof(msg),
625 "Invalid IP address '%s'.\n"
626 "Server bind addresses must be valid IPv4 or IPv6 addresses.\n"
627 "Examples:\n"
628 " ascii-chat server 0.0.0.0\n"
629 " ascii-chat server ::1\n"
630 " ascii-chat server 0.0.0.0 ::1",
631 arg);
632 *error_msg = platform_strdup(msg);
633 }
634 return -1;
635 }
636
637 // Try to parse second address if available
638 if (remaining && num_remaining > 0 && remaining[0]) {
639 const char *second_arg = remaining[0];
640 memset(parsed_addr, 0, sizeof(parsed_addr));
641 addr_to_check = second_arg;
642 if (parse_ipv6_address(second_arg, parsed_addr, sizeof(parsed_addr)) == 0) {
643 addr_to_check = parsed_addr;
644 }
645
646 if (is_valid_ipv4(addr_to_check)) {
647 // Second is IPv4
648 if (address[0] != '\0' && !is_localhost_ipv4(address) && strcmp(address, "localhost") != 0 &&
649 strcmp(address, "0.0.0.0") != 0) {
650 // Already have an IPv4, can't add another
651 return consumed;
652 }
653 if (is_valid_ipv4(arg)) {
654 // First was also IPv4, can't have two IPv4s
655 return consumed;
656 }
657 // First was IPv6, second is IPv4 - accept both
658 SAFE_SNPRINTF(address, OPTIONS_BUFF_SIZE, "%s", addr_to_check);
659 consumed = 2;
660 } else if (is_valid_ipv6(addr_to_check)) {
661 // Second is IPv6
662 if (address6[0] != '\0' && !is_localhost_ipv6(address6)) {
663 // Already have an IPv6, can't add another
664 return consumed;
665 }
666 if (is_valid_ipv6(arg)) {
667 // First was also IPv6, can't have two IPv6s
668 return consumed;
669 }
670 // First was IPv4, second is IPv6 - accept both
671 SAFE_SNPRINTF(address6, OPTIONS_BUFF_SIZE, "%s", addr_to_check);
672 consumed = 2;
673 }
674 }
675
676 return consumed;
677}
int is_localhost_ipv6(const char *ip)
Check if IPv6 address is localhost.
Definition ip.c:1320
int is_localhost_ipv4(const char *ip)
Check if IPv4 address is localhost.
Definition ip.c:1299
int parse_ipv6_address(const char *input, char *output, size_t output_size)
Parse IPv6 address, removing brackets if present.
Definition ip.c:158

References is_localhost_ipv4(), is_localhost_ipv6(), is_valid_ipv4(), is_valid_ipv6(), OPTIONS_BUFF_SIZE, parse_ipv6_address(), platform_strdup(), 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 1010 of file parsers.c.

1010 {
1011 if (!arg || arg[0] == '\0') {
1012 if (error_msg) {
1013 *error_msg = platform_strdup("--seek requires a timestamp argument");
1014 }
1015 return false;
1016 }
1017
1018 double *timestamp = (double *)dest;
1019 char *endptr;
1020 long strtol_result;
1021
1022 // Count colons to determine format
1023 int colon_count = 0;
1024 for (const char *p = arg; *p; p++) {
1025 if (*p == ':')
1026 colon_count++;
1027 }
1028
1029 if (colon_count == 0) {
1030 // Plain seconds format: "30" or "30.5"
1031 *timestamp = strtod(arg, &endptr);
1032 if (*endptr != '\0' || *timestamp < 0.0) {
1033 if (error_msg) {
1034 *error_msg = platform_strdup("Invalid timestamp: expected non-negative seconds");
1035 }
1036 return false;
1037 }
1038 return true;
1039 } else if (colon_count == 1) {
1040 // MM:SS or MM:SS.ms format
1041 strtol_result = strtol(arg, &endptr, 10);
1042 if (*endptr != ':' || strtol_result < 0) {
1043 if (error_msg) {
1044 *error_msg = platform_strdup("Invalid timestamp: expected MM:SS or MM:SS.ms format");
1045 }
1046 return false;
1047 }
1048 long minutes = strtol_result;
1049 double seconds = strtod(endptr + 1, &endptr);
1050 if (*endptr != '\0' && *endptr != '.' && *endptr != '\0') {
1051 if (error_msg) {
1052 *error_msg = platform_strdup("Invalid timestamp: expected MM:SS or MM:SS.ms format");
1053 }
1054 return false;
1055 }
1056 *timestamp = minutes * 60.0 + seconds;
1057 return true;
1058 } else if (colon_count == 2) {
1059 // HH:MM:SS or HH:MM:SS.ms format
1060 strtol_result = strtol(arg, &endptr, 10);
1061 if (*endptr != ':' || strtol_result < 0) {
1062 if (error_msg) {
1063 *error_msg = platform_strdup("Invalid timestamp: expected HH:MM:SS or HH:MM:SS.ms format");
1064 }
1065 return false;
1066 }
1067 long hours = strtol_result;
1068
1069 strtol_result = strtol(endptr + 1, &endptr, 10);
1070 if (*endptr != ':' || strtol_result < 0 || strtol_result >= 60) {
1071 if (error_msg) {
1072 *error_msg = platform_strdup("Invalid timestamp: minutes must be 0-59");
1073 }
1074 return false;
1075 }
1076 long minutes = strtol_result;
1077
1078 double seconds = strtod(endptr + 1, &endptr);
1079 if (*endptr != '\0') {
1080 if (error_msg) {
1081 *error_msg = platform_strdup("Invalid timestamp: expected HH:MM:SS or HH:MM:SS.ms format");
1082 }
1083 return false;
1084 }
1085 *timestamp = hours * (double)SEC_PER_HOUR + minutes * (double)SEC_PER_MIN + seconds;
1086 return true;
1087 } else {
1088 if (error_msg) {
1089 *error_msg = platform_strdup("Invalid timestamp format: too many colons");
1090 }
1091 return false;
1092 }
1093}
#define SEC_PER_HOUR
Definition time.h:168
#define SEC_PER_MIN
Definition time.h:167

References platform_strdup(), 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 191 of file parsers.c.

191 {
192 if (!dest) {
193 if (error_msg) {
194 *error_msg = platform_strdup("Internal error: NULL destination");
195 }
196 return false;
197 }
198
199 // Use generic parser with UTF-8 setting lookup table
200 // Default to TRUE if no arg provided
201 if (!arg || arg[0] == '\0') {
202 int *utf8_setting = (int *)dest;
203 *utf8_setting = UTF8_SETTING_TRUE;
204 return true;
205 }
206
207 return parse_setting_generic(arg, dest, g_utf8_setting_map, error_msg);
208}
@ UTF8_SETTING_TRUE
Force UTF-8 ON: always use UTF-8 regardless of terminal capability.

References platform_strdup(), and 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 972 of file parsers.c.

972 {
973 (void)error_msg; // Unused but required by function signature
974
975 // If arg is NULL or starts with a flag, just increment
976 // Otherwise try to parse as integer count
977 unsigned short int *verbose_level = (unsigned short int *)dest;
978
979 if (!arg || arg[0] == '\0') {
980 // No argument provided, just increment
981 (*verbose_level)++;
982 return true;
983 }
984
985 // Try to parse as integer count
986 char *endptr;
987 long value = strtol(arg, &endptr, 10);
988 if (*endptr == '\0' && value >= 0 && value <= 100) {
989 *verbose_level = (unsigned short int)value;
990 return true;
991 }
992
993 // If it didn't parse as int, treat as flag increment
994 (*verbose_level)++;
995 return true;
996}

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

1095 {
1096 if (!arg || !dest) {
1097 if (error_msg) {
1098 *error_msg = platform_strdup("Internal error: NULL argument or destination");
1099 }
1100 return false;
1101 }
1102
1103 float *volume = (float *)dest;
1104 char *endptr;
1105 float val = strtof(arg, &endptr);
1106
1107 if (*endptr != '\0' || arg == endptr) {
1108 if (error_msg) {
1109 *error_msg = platform_strdup("Invalid volume value. Must be a number between 0.0 and 1.0");
1110 }
1111 return false;
1112 }
1113
1114 if (val < 0.0f || val > 1.0f) {
1115 if (error_msg) {
1116 char buf[256];
1117 SAFE_SNPRINTF(buf, sizeof(buf), "Volume must be between 0.0 and 1.0 (got %.2f)", val);
1118 *error_msg = platform_strdup(buf);
1119 }
1120 return false;
1121 }
1122
1123 *volume = val;
1124 return true;
1125}

References platform_strdup(), and SAFE_SNPRINTF.