ascii-chat 0.8.38
Real-time terminal-based video chat with ASCII art conversion
Loading...
Searching...
No Matches
options/parsing/actions.c
Go to the documentation of this file.
1
13#include <ascii-chat/options/actions.h>
14#include <string.h>
15#include <stddef.h>
16
17#include <ascii-chat/asciichat_errno.h>
18#include <ascii-chat/common.h>
19#include <ascii-chat/log/logging.h>
20#include <ascii-chat/network/update_checker.h>
21#include <ascii-chat/options/manpage.h>
22#include <ascii-chat/options/presets.h>
23#include <ascii-chat/options/config.h>
24#include <ascii-chat/options/completions/completions.h>
25#include <ascii-chat/options/schema.h>
26#include <ascii-chat/platform/terminal.h>
27#include <ascii-chat/platform/question.h>
28#include <ascii-chat/platform/stat.h>
29#include <ascii-chat/platform/util.h>
30#include <ascii-chat/version.h>
31#include <ascii-chat/video/webcam/webcam.h>
32#include <ascii-chat/audio/audio.h>
33#include <ascii-chat/util/string.h>
34#include <ascii-chat/util/path.h>
35
36#include <stdio.h>
37#include <stdlib.h>
38#include <string.h>
39
40// ============================================================================
41// Action Exit Helper
42// ============================================================================
43
51static _Noreturn void action_exit(int code) {
52 fflush(NULL);
53 _Exit(code);
54}
55
56// ============================================================================
57// Deferred Action Tracking System
58// ============================================================================
59
66static struct {
67 deferred_action_t action;
68 action_args_t args;
70} g_deferred_action_state = {
71 .action = ACTION_NONE,
72 .args = {0},
73 .has_action = false,
74};
75
76void actions_defer(deferred_action_t action, const action_args_t *args) {
77 // Only remember the first action found
78 if (g_deferred_action_state.has_action) {
79 return; // Action already deferred, ignore subsequent actions
80 }
81
82 g_deferred_action_state.action = action;
83 g_deferred_action_state.has_action = true;
84
85 // Copy arguments if provided
86 if (args) {
87 memcpy(&g_deferred_action_state.args, args, sizeof(action_args_t));
88 } else {
89 memset(&g_deferred_action_state.args, 0, sizeof(action_args_t));
90 }
91}
92
93deferred_action_t actions_get_deferred(void) {
94 return g_deferred_action_state.action;
95}
96
97const action_args_t *actions_get_args(void) {
98 if (!g_deferred_action_state.has_action) {
99 return NULL;
100 }
101 return &g_deferred_action_state.args;
102}
103
104// ============================================================================
105// Webcam Action
106// ============================================================================
107
109 // Defer execution until after options are fully parsed
110 actions_defer(ACTION_LIST_WEBCAMS, NULL);
111}
112
119static void execute_list_webcams(void) {
120 webcam_device_info_t *devices = NULL;
121 unsigned int device_count = 0;
122
123 asciichat_error_t result = webcam_list_devices(&devices, &device_count);
124 if (result != ASCIICHAT_OK) {
125 log_plain_stderr("Error: Failed to enumerate webcam devices");
126 action_exit(ERROR_WEBCAM);
127 }
128
129 if (device_count == 0) {
130 log_plain_stderr("%s", colored_string(LOG_COLOR_ERROR, "No webcam devices found."));
131 } else {
132 log_plain_stderr("%s", colored_string(LOG_COLOR_DEV, "Available Webcam Devices:"));
133 for (unsigned int i = 0; i < device_count; i++) {
134 char index_str[32];
135 safe_snprintf(index_str, sizeof(index_str), "%u", devices[i].index);
136 log_plain_stderr(" %s %s", colored_string(LOG_COLOR_GREY, index_str), devices[i].name);
137 }
138 }
139
141 action_exit(0);
142}
143
144// ============================================================================
145// Audio Device Actions
146// ============================================================================
147
149 // Defer execution until after options are fully parsed
150 actions_defer(ACTION_LIST_MICROPHONES, NULL);
151}
152
159static void execute_list_microphones(void) {
160 audio_device_info_t *devices = NULL;
161 unsigned int device_count = 0;
162
163 asciichat_error_t result = audio_list_input_devices(&devices, &device_count);
164 if (result != ASCIICHAT_OK) {
165 log_plain_stderr("Error: Failed to enumerate audio input devices");
166 action_exit(ERROR_AUDIO);
167 }
168
169 if (device_count == 0) {
170 log_plain_stderr("%s", colored_string(LOG_COLOR_ERROR, "No microphone devices found."));
171 } else {
172 log_plain_stderr("%s", colored_string(LOG_COLOR_DEV, "Available Microphone Devices:"));
173 for (unsigned int i = 0; i < device_count; i++) {
174 char index_str[32];
175 safe_snprintf(index_str, sizeof(index_str), "%d", devices[i].index);
176 char device_line[512];
177 char *line_ptr = device_line;
178 int remaining = sizeof(device_line);
179 safe_snprintf(line_ptr, remaining, " %s %s", colored_string(LOG_COLOR_GREY, index_str), devices[i].name);
180 if (devices[i].is_default_input) {
181 size_t len = strlen(device_line);
182 line_ptr = device_line + len;
183 remaining = sizeof(device_line) - (int)len;
184 safe_snprintf(line_ptr, remaining, " %s", colored_string(LOG_COLOR_INFO, "(default)"));
185 }
186 log_plain_stderr("%s", device_line);
187 }
188 }
189
190 audio_free_device_list(devices);
191 action_exit(0);
192}
193
195 // Defer execution until after options are fully parsed
196 actions_defer(ACTION_LIST_SPEAKERS, NULL);
197}
198
205static void execute_list_speakers(void) {
206 audio_device_info_t *devices = NULL;
207 unsigned int device_count = 0;
208
209 asciichat_error_t result = audio_list_output_devices(&devices, &device_count);
210 if (result != ASCIICHAT_OK) {
211 log_plain_stderr("Error: Failed to enumerate audio output devices");
212 action_exit(ERROR_AUDIO);
213 }
214
215 if (device_count == 0) {
216 log_plain_stderr("%s", colored_string(LOG_COLOR_ERROR, "No speaker devices found."));
217 } else {
218 log_plain_stderr("%s", colored_string(LOG_COLOR_DEV, "Available Speaker Devices:"));
219 for (unsigned int i = 0; i < device_count; i++) {
220 char index_str[32];
221 safe_snprintf(index_str, sizeof(index_str), "%d", devices[i].index);
222 char device_line[512];
223 char *line_ptr = device_line;
224 int remaining = sizeof(device_line);
225 safe_snprintf(line_ptr, remaining, " %s %s", colored_string(LOG_COLOR_GREY, index_str), devices[i].name);
226 if (devices[i].is_default_output) {
227 size_t len = strlen(device_line);
228 line_ptr = device_line + len;
229 remaining = sizeof(device_line) - (int)len;
230 safe_snprintf(line_ptr, remaining, " %s", colored_string(LOG_COLOR_INFO, "(default)"));
231 }
232 log_plain_stderr("%s", device_line);
233 }
234 }
235
236 audio_free_device_list(devices);
237 action_exit(0);
238}
239
240// ============================================================================
241// Terminal Capabilities Action
242// ============================================================================
243
248 terminal_capabilities_t caps = detect_terminal_capabilities();
249
250 // Determine if we should use colored output
251 bool use_colors = terminal_should_color_output(STDOUT_FILENO);
252
253 // Override color level if colors should not be shown
254 if (!use_colors) {
255 caps.color_level = TERM_COLOR_NONE;
256 caps.color_count = 0;
257 caps.capabilities &= ~((uint32_t)(TERM_CAP_COLOR_TRUE | TERM_CAP_COLOR_256 | TERM_CAP_COLOR_16));
258 }
259
260 // Use detected terminal size (immediate execution doesn't have parsed options yet)
261 terminal_size_t size;
262 terminal_get_size(&size);
263 unsigned short width = size.cols;
264 unsigned short height = size.rows;
265
266 log_color_t label_color = use_colors ? LOG_COLOR_GREY : LOG_COLOR_GREY;
267 log_color_t string_color = use_colors ? LOG_COLOR_DEBUG : LOG_COLOR_GREY;
268 log_color_t good_color = use_colors ? LOG_COLOR_INFO : LOG_COLOR_GREY;
269 log_color_t bad_color = use_colors ? LOG_COLOR_ERROR : LOG_COLOR_GREY;
270 log_color_t number_color = use_colors ? LOG_COLOR_FATAL : LOG_COLOR_GREY;
271
272 printf("%s\n", colored_string(LOG_COLOR_WARN, "Terminal Capabilities:"));
273
274 char size_buf[64];
275 snprintf(size_buf, sizeof(size_buf), "%ux%u", width, height);
276 printf(" %s: %s\n", colored_string(label_color, "Terminal Size"), colored_string(number_color, size_buf));
277
278 const char *color_level_name = terminal_color_level_name(caps.color_level);
279 log_color_t color_level_color = (caps.color_level == TERM_COLOR_NONE) ? bad_color : string_color;
280 printf(" %s: %s\n", colored_string(label_color, "Color Level"),
281 colored_string(color_level_color, (char *)color_level_name));
282
283 char colors_buf[64];
284 snprintf(colors_buf, sizeof(colors_buf), "%u", caps.color_count);
285 printf(" %s: %s\n", colored_string(label_color, "Max Colors"), colored_string(number_color, colors_buf));
286
287 log_color_t utf8_color = caps.utf8_support ? good_color : bad_color;
288 printf(" %s: %s\n", colored_string(label_color, "UTF-8 Support"),
289 colored_string(utf8_color, (char *)(caps.utf8_support ? "Yes" : "No")));
290
291 const char *render_mode_str = "unknown";
292 if (caps.render_mode == RENDER_MODE_FOREGROUND) {
293 render_mode_str = "foreground";
294 } else if (caps.render_mode == RENDER_MODE_BACKGROUND) {
295 render_mode_str = "background";
296 } else if (caps.render_mode == RENDER_MODE_HALF_BLOCK) {
297 render_mode_str = "half-block";
298 }
299 printf(" %s: %s\n", colored_string(label_color, "Render Mode"),
300 colored_string(string_color, (char *)render_mode_str));
301 printf(" %s: %s\n", colored_string(label_color, "TERM"), colored_string(string_color, (char *)caps.term_type));
302 printf(" %s: %s\n", colored_string(label_color, "COLORTERM"),
303 colored_string(string_color, (char *)(strlen(caps.colorterm) ? caps.colorterm : "(not set)")));
304
305 log_color_t reliable_color = caps.detection_reliable ? good_color : bad_color;
306 printf(" %s: %s\n", colored_string(label_color, "Detection Reliable"),
307 colored_string(reliable_color, (char *)(caps.detection_reliable ? "Yes" : "No")));
308
309 char bitmask_buf[64];
310 snprintf(bitmask_buf, sizeof(bitmask_buf), "0x%08x", caps.capabilities);
311 printf(" %s: %s\n", colored_string(label_color, "Capabilities Bitmask"), colored_string(number_color, bitmask_buf));
312
313 fflush(stdout);
314 action_exit(0);
315}
316
318 // Defer execution until after options are fully parsed and dimensions updated
319 // This ensures --width and --height flags are properly reflected in the output
320 actions_defer(ACTION_SHOW_CAPABILITIES, NULL);
321}
322
323// ============================================================================
324// Update Check Action
325// ============================================================================
326
331 printf("Checking for updates...\n");
332 update_check_result_t result;
333 asciichat_error_t err = update_check_perform(&result);
334 if (err != ASCIICHAT_OK) {
335 printf("\nFailed to check for updates.\n\n");
336 action_exit(1);
337 }
338 if (result.update_available) {
339 char notification[1024];
340 update_check_format_notification(&result, notification, sizeof(notification));
341 printf("\n%s\n\n", notification);
342 } else {
343 printf("\nYou are already on the latest version: %s (%.8s)\n\n", result.current_version, result.current_sha);
344 }
345 action_exit(0);
346}
347
349 // Defer execution until after options are fully parsed
350 actions_defer(ACTION_CHECK_UPDATE, NULL);
351}
352
363static void execute_show_capabilities(void) {
364 terminal_capabilities_t caps = detect_terminal_capabilities();
365
366 // Determine if we should use colored output
367 bool use_colors = terminal_should_color_output(STDOUT_FILENO);
368
369 // Override color level if colors should not be shown
370 if (!use_colors) {
371 // User requested no colors or output is piped or CLAUDECODE is set
372 caps.color_level = TERM_COLOR_NONE;
373 caps.color_count = 0;
374 caps.capabilities &= ~((uint32_t)(TERM_CAP_COLOR_TRUE | TERM_CAP_COLOR_256 | TERM_CAP_COLOR_16));
375 }
376
377 // Get width and height from parsed options (respects --width and --height flags)
378 const options_t *opts = options_get();
379 unsigned short width = opts ? opts->width : 110;
380 unsigned short height = opts ? opts->height : 70;
381
382 // Color scheme:
383 // - Labels: grey
384 // - Regular string values: blue (DEBUG - cyan/blue)
385 // - Affirmative values (Yes, true): green (INFO)
386 // - Bad/negative values (No, false, error): red (ERROR)
387 // - Numbers and hex: magenta (FATAL)
388 log_color_t label_color = use_colors ? LOG_COLOR_GREY : LOG_COLOR_GREY;
389 log_color_t string_color = use_colors ? LOG_COLOR_DEBUG : LOG_COLOR_GREY;
390 log_color_t good_color = use_colors ? LOG_COLOR_INFO : LOG_COLOR_GREY;
391 log_color_t bad_color = use_colors ? LOG_COLOR_ERROR : LOG_COLOR_GREY;
392 log_color_t number_color = use_colors ? LOG_COLOR_FATAL : LOG_COLOR_GREY;
393
394 printf("%s\n", colored_string(LOG_COLOR_WARN, "Terminal Capabilities:"));
395
396 char size_buf[64];
397 snprintf(size_buf, sizeof(size_buf), "%ux%u", width, height);
398 printf(" %s: %s\n", colored_string(label_color, "Terminal Size"), colored_string(number_color, size_buf));
399
400 // Color Level: green if good, red if none/bad
401 const char *color_level_name = terminal_color_level_name(caps.color_level);
402 log_color_t color_level_color = (caps.color_level == TERM_COLOR_NONE) ? bad_color : string_color;
403 printf(" %s: %s\n", colored_string(label_color, "Color Level"),
404 colored_string(color_level_color, (char *)color_level_name));
405
406 char colors_buf[64];
407 snprintf(colors_buf, sizeof(colors_buf), "%u", caps.color_count);
408 printf(" %s: %s\n", colored_string(label_color, "Max Colors"), colored_string(number_color, colors_buf));
409
410 // UTF-8: green if Yes, red if No
411 log_color_t utf8_color = caps.utf8_support ? good_color : bad_color;
412 printf(" %s: %s\n", colored_string(label_color, "UTF-8 Support"),
413 colored_string(utf8_color, (char *)(caps.utf8_support ? "Yes" : "No")));
414
415 const char *render_mode_str = "unknown";
416 if (caps.render_mode == RENDER_MODE_FOREGROUND) {
417 render_mode_str = "foreground";
418 } else if (caps.render_mode == RENDER_MODE_BACKGROUND) {
419 render_mode_str = "background";
420 } else if (caps.render_mode == RENDER_MODE_HALF_BLOCK) {
421 render_mode_str = "half-block";
422 }
423 printf(" %s: %s\n", colored_string(label_color, "Render Mode"),
424 colored_string(string_color, (char *)render_mode_str));
425 printf(" %s: %s\n", colored_string(label_color, "TERM"), colored_string(string_color, (char *)caps.term_type));
426 printf(" %s: %s\n", colored_string(label_color, "COLORTERM"),
427 colored_string(string_color, (char *)(strlen(caps.colorterm) ? caps.colorterm : "(not set)")));
428
429 // Detection Reliable: green if Yes, red if No
430 log_color_t reliable_color = caps.detection_reliable ? good_color : bad_color;
431 printf(" %s: %s\n", colored_string(label_color, "Detection Reliable"),
432 colored_string(reliable_color, (char *)(caps.detection_reliable ? "Yes" : "No")));
433
434 char bitmask_buf[64];
435 snprintf(bitmask_buf, sizeof(bitmask_buf), "0x%08x", caps.capabilities);
436 printf(" %s: %s\n", colored_string(label_color, "Capabilities Bitmask"), colored_string(number_color, bitmask_buf));
437
438 fflush(stdout);
439
440 action_exit(0);
441}
442
443// ============================================================================
444// Version Action
445// ============================================================================
446
448 bool use_colors = terminal_should_color_output(STDOUT_FILENO);
449 if (use_colors) {
450 printf("%s %s (%s, %s)\n", colored_string(LOG_COLOR_WARN, "ascii-chat"),
451 colored_string(LOG_COLOR_INFO, (char *)ASCII_CHAT_VERSION_FULL),
452 colored_string(LOG_COLOR_DEBUG, (char *)ASCII_CHAT_BUILD_TYPE),
453 colored_string(LOG_COLOR_GREY, (char *)ASCII_CHAT_BUILD_DATE));
454 } else {
455 printf("ascii-chat %s (%s, %s)\n", ASCII_CHAT_VERSION_FULL, ASCII_CHAT_BUILD_TYPE, ASCII_CHAT_BUILD_DATE);
456 }
457
458 action_exit(0);
459}
460
461// ============================================================================
462// Help Actions
463// ============================================================================
464
466 usage(stdout, MODE_SERVER);
467 action_exit(0);
468}
469
471 usage(stdout, MODE_CLIENT);
472 action_exit(0);
473}
474
476 usage(stdout, MODE_MIRROR);
477 action_exit(0);
478}
479
481 usage(stdout, MODE_DISCOVERY_SERVICE);
482 action_exit(0);
483}
484
486 usage(stdout, MODE_DISCOVERY);
487 action_exit(0);
488}
489
490// ============================================================================
491// Man Page Generation Action
492// ============================================================================
493
494void action_create_manpage(const char *output_path) {
495 // Get binary-level config
496 const options_config_t *config = options_preset_unified(NULL, NULL);
497
498 if (!config) {
499 log_plain_stderr("Error: Failed to get binary options config");
500 action_exit(ERROR_FILE_OPERATION);
501 }
502
503 // Determine output path: use provided path, or NULL for stdout
504 const char *path_to_use = NULL;
505 if (output_path && strlen(output_path) > 0 && strcmp(output_path, "-") != 0) {
506 // User provided explicit path - use it
507 path_to_use = output_path;
508 } else {
509 // No path or "-" means stdout
510 path_to_use = NULL;
511 }
512
513 // Generate merged man page from embedded or filesystem resources
514 // Resources are loaded automatically based on build type:
515 // - Production (Release): From embedded binary data
516 // - Development (Debug): From filesystem files
517 asciichat_error_t err =
518 options_config_generate_manpage_merged(config, "ascii-chat", NULL, path_to_use, "Video chat in your terminal");
519
520 if (err != ASCIICHAT_OK) {
521 asciichat_error_context_t err_ctx;
522 if (HAS_ERRNO(&err_ctx)) {
523 log_plain_stderr("Error: %s", err_ctx.context_message);
524 } else {
525 log_plain_stderr("Error: Failed to generate man page");
526 }
527 action_exit(ERROR_FILE_OPERATION);
528 }
529
530 if (path_to_use) {
531 log_plain_stderr("Man page written to: %s", path_to_use);
532 } else {
533 log_plain_stderr("Man page written to stdout");
534 }
535
536 action_exit(0);
537}
538
539// ============================================================================
540// Config Creation Action
541// ============================================================================
542
543void action_create_config(const char *output_path) {
544 // Build the schema first so config_create_default can generate options from it
545 const options_config_t *unified_config = options_preset_unified(NULL, NULL);
546 if (unified_config) {
547 asciichat_error_t schema_build_result = config_schema_build_from_configs(&unified_config, 1);
548 if (schema_build_result != ASCIICHAT_OK) {
549 // Schema build failed, but continue anyway
550 (void)schema_build_result;
551 }
552 // options_config_destroy(unified_config);
553 }
554
555 const char *config_path = NULL;
556
557 // Determine output path: use provided path, or NULL for stdout
558 if (output_path && strlen(output_path) > 0 && strcmp(output_path, "-") != 0) {
559 // User provided explicit path - use it as-is
560 config_path = output_path;
561 } else {
562 // No path or "-" means stdout
563 config_path = NULL;
564 }
565
566 // Create config with default options
567 asciichat_error_t result = config_create_default(config_path);
568 if (result != ASCIICHAT_OK) {
569 asciichat_error_context_t err_ctx;
570 if (HAS_ERRNO(&err_ctx)) {
571 log_plain_stderr("Error creating config: %s", err_ctx.context_message);
572 } else {
573 log_plain_stderr("Error: Failed to create config file");
574 }
575 action_exit(ERROR_CONFIG);
576 }
577
578 if (config_path) {
579 log_plain_stderr("Created default config file at: %s", config_path);
580 } else {
581 log_plain_stderr("Config written to stdout");
582 }
583 action_exit(0);
584}
585
586// ============================================================================
587// Shell Completions Action
588// ============================================================================
589
590void action_completions(const char *shell_name, const char *output_path) {
591 if (!shell_name || strlen(shell_name) == 0) {
592 log_plain_stderr("Error: --completions requires shell name (bash, fish, zsh, powershell)");
593 action_exit(ERROR_USAGE);
594 }
595
596 completion_format_t format = completions_parse_shell_name(shell_name);
597 if (format == COMPLETION_FORMAT_UNKNOWN) {
598 log_plain_stderr("Error: Unknown shell '%s' (supported: bash, fish, zsh, powershell)", shell_name);
599 action_exit(ERROR_USAGE);
600 }
601
602 FILE *output = stdout;
603 bool should_close = false;
604
605 // Determine output: use provided path if given and not "-", otherwise stdout
606 if (output_path && strlen(output_path) > 0 && strcmp(output_path, "-") != 0) {
607 // Check if file already exists and prompt for confirmation
608 struct stat st;
609 if (stat(output_path, &st) == 0) {
610 // File exists - ask user if they want to overwrite
611 log_plain("Completions file already exists: %s", output_path);
612
613 bool overwrite = platform_prompt_yes_no("Overwrite", false); // Default to No
614 if (!overwrite) {
615 log_plain("Completions generation cancelled.");
616 action_exit(0);
617 }
618
619 log_plain("Overwriting existing completions file...");
620 }
621
622 output = platform_fopen(output_path, "w");
623 if (!output) {
624 log_plain_stderr("Error: Failed to open %s for writing", output_path);
625 action_exit(ERROR_FILE_OPERATION);
626 }
627 should_close = true;
628 }
629
630 asciichat_error_t result = completions_generate_for_shell(format, output);
631
632 if (should_close) {
633 fclose(output);
634 }
635
636 if (result != ASCIICHAT_OK) {
637 log_plain_stderr("Error: Failed to generate %s completions", completions_get_shell_name(format));
638 action_exit(ERROR_USAGE);
639 }
640
641 // Silently exit - completions are written (either to file or stdout)
642 action_exit(0);
643}
644
651static void execute_check_update(void) {
652 printf("Checking for updates...\n");
653
654 update_check_result_t result;
655 asciichat_error_t err = update_check_perform(&result);
656
657 if (err != ASCIICHAT_OK) {
658 asciichat_error_context_t ctx;
659 if (HAS_ERRNO(&ctx)) {
660 fprintf(stderr, "Update check failed: %s\n", ctx.context_message);
661 }
662 action_exit(1);
663 }
664
665 // Display results
666 if (result.update_available) {
667 char notification[1024];
668 update_check_format_notification(&result, notification, sizeof(notification));
669 printf("\n%s\n\n", notification);
670 } else {
671 printf("\nYou are already on the latest version: %s (%.8s)\n\n", result.current_version, result.current_sha);
672 }
673
674 action_exit(0);
675}
676
677// ============================================================================
678// Deferred Action Execution
679// ============================================================================
680
682 deferred_action_t action = actions_get_deferred();
683
684 switch (action) {
685 case ACTION_NONE:
686 // No action to execute
687 break;
688
689 case ACTION_LIST_WEBCAMS:
690 execute_list_webcams();
691 break;
692
693 case ACTION_LIST_MICROPHONES:
694 execute_list_microphones();
695 break;
696
697 case ACTION_LIST_SPEAKERS:
698 execute_list_speakers();
699 break;
700
701 case ACTION_SHOW_CAPABILITIES:
702 execute_show_capabilities();
703 break;
704
705 case ACTION_CHECK_UPDATE:
706 execute_check_update();
707 break;
708
709 default:
710 log_warn("Unknown deferred action: %d", action);
711 break;
712 }
713}
completion_format_t completions_parse_shell_name(const char *shell_name)
Definition completions.c:54
asciichat_error_t completions_generate_for_shell(completion_format_t format, FILE *output)
Definition completions.c:20
const char * completions_get_shell_name(completion_format_t format)
Definition completions.c:39
asciichat_error_t config_create_default(const char *config_path)
Definition config.c:1250
void audio_free_device_list(audio_device_info_t *devices)
asciichat_error_t audio_list_input_devices(audio_device_info_t **out_devices, unsigned int *out_count)
asciichat_error_t audio_list_output_devices(audio_device_info_t **out_devices, unsigned int *out_count)
void usage(FILE *desc, asciichat_mode_t mode)
asciichat_error_t options_config_generate_manpage_merged(const options_config_t *config, const char *program_name, const char *mode_name, const char *output_path, const char *brief_description)
void action_check_update_immediate(void)
Execute update check immediately (for early binary-level execution)
const action_args_t * actions_get_args(void)
void action_check_update(void)
deferred_action_t actions_get_deferred(void)
void actions_defer(deferred_action_t action, const action_args_t *args)
bool has_action
void action_help_acds(void)
void action_show_capabilities(void)
void action_help_server(void)
void action_help_discovery(void)
action_args_t args
void action_create_manpage(const char *output_path)
void action_completions(const char *shell_name, const char *output_path)
deferred_action_t action
void action_list_microphones(void)
void action_show_version(void)
void action_help_mirror(void)
void action_create_config(const char *output_path)
void action_list_webcams(void)
void action_list_speakers(void)
void action_help_client(void)
void actions_execute_deferred(void)
void action_show_capabilities_immediate(void)
Execute show capabilities immediately (for early binary-level execution)
bool terminal_should_color_output(int fd)
Determine if color output should be used.
asciichat_error_t terminal_get_size(terminal_size_t *size)
terminal_capabilities_t detect_terminal_capabilities(void)
options_config_t * options_preset_unified(const char *program_name, const char *description)
Build unified options config with ALL options (binary + all modes)
Definition presets.c:51
const options_t * options_get(void)
Definition rcu.c:347
asciichat_error_t config_schema_build_from_configs(const options_config_t **configs, size_t num_configs)
Definition schema.c:242
int safe_snprintf(char *buffer, size_t buffer_size, const char *format,...)
Safe formatted string printing to buffer.
Definition system.c:456
asciichat_error_t update_check_perform(update_check_result_t *result)
void update_check_format_notification(const update_check_result_t *result, char *buffer, size_t buffer_size)
const char * colored_string(log_color_t color, const char *text)
bool platform_prompt_yes_no(const char *question, bool default_yes)
Definition util.c:81
void webcam_free_device_list(webcam_device_info_t *devices)
asciichat_error_t webcam_list_devices(webcam_device_info_t **out_devices, unsigned int *out_count)
FILE * platform_fopen(const char *filename, const char *mode)