ascii-chat 0.8.38
Real-time terminal-based video chat with ASCII art conversion
Loading...
Searching...
No Matches
main.c
Go to the documentation of this file.
1
19#include <stdio.h>
20#include <stdlib.h>
21#include <string.h>
22#include <stdbool.h>
23#include <stdatomic.h>
24
25// Mode-specific entry points
26#include "server/main.h"
27#include "client/main.h"
28#include "mirror/main.h"
30#include "discovery/main.h"
31#include <ascii-chat/discovery/session.h>
32
33// Global exit API (exported in main.h)
34#include "main.h"
35
36// Utilities
37#include <ascii-chat/util/utf8.h>
38#include <ascii-chat/util/time.h>
39#include <ascii-chat/util/string.h>
40
41// Common headers for version info and initialization
42#include <ascii-chat/common.h>
43#include <ascii-chat/version.h>
44#include <ascii-chat/options/options.h>
45#include <ascii-chat/platform/system.h>
46#include <ascii-chat/options/common.h>
47#include <ascii-chat/options/rcu.h>
48#include <ascii-chat/options/builder.h>
49#include <ascii-chat/options/actions.h>
50#include <ascii-chat/options/colorscheme.h>
51#include <ascii-chat/log/logging.h>
52#include <ascii-chat/log/json.h>
53#include <ascii-chat/log/grep.h>
54#include <ascii-chat/platform/terminal.h>
55#include <ascii-chat/util/path.h>
56#include <ascii-chat/options/colorscheme.h>
57#include <ascii-chat/network/update_checker.h>
58#include <ascii-chat/ui/splash.h>
59
60#ifndef NDEBUG
61#include <ascii-chat/asciichat_errno.h>
62#include <ascii-chat/debug/lock.h>
63#endif
64
65#ifndef _WIN32
66#include <signal.h>
67#endif
68
69/* ============================================================================
70 * Constants and Configuration
71 * ============================================================================ */
72
73#define APP_NAME "ascii-chat"
74#define VERSION ASCII_CHAT_VERSION_FULL
75
76/* ============================================================================
77 * Global Application Exit State (Centralized Signal Handling)
78 * ============================================================================ */
79
81static atomic_bool g_app_should_exit = false;
82
84static void (*g_interrupt_callback)(void) = NULL;
85
86/* ============================================================================
87 * Global Exit API Implementation (from main.h)
88 * ============================================================================ */
89
90bool should_exit(void) {
91 return atomic_load(&g_app_should_exit);
92}
93
94void signal_exit(void) {
95 atomic_store(&g_app_should_exit, true);
96 void (*cb)(void) = g_interrupt_callback;
97 if (cb) {
98 cb();
99 }
100}
101
102void set_interrupt_callback(void (*cb)(void)) {
103 g_interrupt_callback = cb;
104}
105
106/* ============================================================================
107 * Signal Handlers
108 * ============================================================================ */
109
110#ifndef _WIN32
115static void handle_sigterm(int sig) {
116 (void)sig;
117 // Use log_console for SIGTERM - it's async-signal-safe
118 log_console(LOG_INFO, "SIGTERM received - shutting down");
119 signal_exit();
120}
121#endif
122
127static bool console_ctrl_handler(console_ctrl_event_t event) {
128 if (event != CONSOLE_CTRL_C && event != CONSOLE_CTRL_BREAK && event != CONSOLE_CLOSE) {
129 return false;
130 }
131
132 // Double Ctrl+C forces immediate exit
133 static _Atomic int ctrl_c_count = 0;
134 if (atomic_fetch_add(&ctrl_c_count, 1) + 1 > 1) {
135 platform_force_exit(1);
136 }
137
138 // Note: Don't use log_console() here - on Unix, this is called from SIGINT context
139 // where SAFE_MALLOC may be holding its mutex, causing deadlock. Windows runs this
140 // in a separate thread so it would be safe there, but we keep both paths identical
141 // for simplicity. The shutdown message will appear from normal thread context.
142 signal_exit();
143 return true;
144}
145
146/* ============================================================================
147 * Debug Signal Handlers (all modes)
148 * ============================================================================ */
149
150#ifndef NDEBUG
151#ifndef _WIN32
156static void common_handle_sigusr1(int sig) {
157 (void)sig;
159}
160#endif
161#endif
162
168 platform_set_console_ctrl_handler(console_ctrl_handler);
169
170#ifndef _WIN32
171 platform_signal_handler_t handlers[] = {
172 {SIGTERM, handle_sigterm},
173 {SIGPIPE, SIG_IGN},
174 };
175 platform_register_signal_handlers(handlers, 2);
176#endif
177}
178
179/* ============================================================================
180 * Mode Registration Table
181 * ============================================================================ */
182
183typedef int (*mode_entry_point_t)(void);
184
185typedef struct {
186 const char *name;
187 const char *description;
190
191static const mode_descriptor_t g_mode_table[] = {
192 {
193 .name = "server",
194 .description = "Run as multi-client video chat server",
195 .entry_point = server_main,
196 },
197 {
198 .name = "client",
199 .description = "Run as video chat client (connect to server)",
200 .entry_point = client_main,
201 },
202 {
203 .name = "mirror",
204 .description = "View local webcam as ASCII art (no server)",
205 .entry_point = mirror_main,
206 },
207 {
208 .name = "discovery-service",
209 .description = "Secure P2P session signalling",
210 .entry_point = acds_main,
211 },
212 {.name = NULL, .description = NULL, .entry_point = NULL},
213};
214
215/* ============================================================================
216 * Help and Usage Functions
217 * ============================================================================ */
218
219static void print_usage(asciichat_mode_t mode) {
220 // Use the new usage() function from common.c which handles mode-specific help properly
221 usage(stdout, mode);
222}
223
224// Discovery mode is implicit (no keyword) so it has a separate descriptor
225static const mode_descriptor_t g_discovery_mode = {
226 .name = "discovery",
227 .description = "P2P session with automatic host negotiation",
228 .entry_point = discovery_main,
229};
230
247static const mode_descriptor_t *g_mode_lookup[] = {
248 [MODE_SERVER] = &g_mode_table[0], // server_main
249 [MODE_CLIENT] = &g_mode_table[1], // client_main
250 [MODE_MIRROR] = &g_mode_table[2], // mirror_main
251 [MODE_DISCOVERY_SERVICE] = &g_mode_table[3], // acds_main
252 [MODE_DISCOVERY] = &g_discovery_mode, // discovery_main (implicit mode)
253 [MODE_INVALID] = NULL, // Invalid: no descriptor
254};
255
265static const mode_descriptor_t *find_mode(asciichat_mode_t mode) {
266 // Bounds check: ensure mode is within valid range (0 to MODE_INVALID)
267 if (mode < MODE_SERVER || mode > MODE_INVALID) {
268 return NULL;
269 }
270 return g_mode_lookup[mode];
271}
272
273/* ============================================================================
274 * Helper Functions for Post-Options Processing
275 * ============================================================================ */
276
277/* ============================================================================
278 * Main Entry Point
279 * ============================================================================ */
280
281int main(int argc, char *argv[]) {
282 // Set global argc/argv for early argv inspection (e.g., in terminal.c)
283 g_argc = argc;
284 g_argv = argv;
285 // Validate basic argument structure
286 if (argc < 1 || argv == NULL || argv[0] == NULL) {
287 fprintf(stderr, "Error: Invalid argument vector\n");
288 return 1;
289 }
290
291 // VERY FIRST: Scan for --color BEFORE ANY logging initialization
292 // This sets global flags that persist through cleanup, enabling --color to force colors
293 extern bool g_color_flag_passed;
294 extern bool g_color_flag_value;
295 for (int i = 1; i < argc; i++) {
296 if (strcmp(argv[i], "--color") == 0 || strcmp(argv[i], "--color=true") == 0) {
297 g_color_flag_passed = true;
298 g_color_flag_value = true;
299 break;
300 } else if (strcmp(argv[i], "--color=false") == 0) {
301 g_color_flag_passed = true;
302 g_color_flag_value = false;
303 break;
304 } else if (strncmp(argv[i], "--color=", 8) == 0) {
305 g_color_flag_passed = true;
306 // Default to true for any other --color=X value
307 g_color_flag_value = true;
308 break;
309 }
310 }
311
312 // SECOND: Scan for --grep BEFORE ANY logging initialization
313 // This ensures ALL logs (including from shared_init) can be filtered
314 // Supports multiple --grep patterns (ORed together)
315 for (int i = 1; i < argc - 1; i++) {
316 if (strcmp(argv[i], "--grep") == 0) {
317 const char *pattern = argv[i + 1];
318 asciichat_error_t filter_result = grep_init(pattern);
319 if (filter_result != ASCIICHAT_OK) {
320 fprintf(stderr,
321 "ERROR: Invalid --grep pattern or invalid flags: \"%s\" - use /pattern/flags format (e.g., "
322 "\"/query/ig\" or \"/literal/F\")\n",
323 pattern);
324 return 1;
325 }
326 i++; // Skip the pattern argument
327 }
328 }
329
330 // Detect terminal capabilities early so colored help output works
331 // Logging will be initialized by asciichat_shared_init() before options_init()
333 terminal_capabilities_t caps = detect_terminal_capabilities();
334 caps = apply_color_mode_override(caps);
335
336 // Warn if Release build was built from dirty working tree
337#if ASCII_CHAT_GIT_IS_DIRTY
338 if (strcmp(ASCII_CHAT_BUILD_TYPE, "Release") == 0) {
339 fprintf(stderr, "⚠️ WARNING: This Release build was compiled from a dirty git working tree!\n");
340 fprintf(stderr, " Git commit: %s (dirty)\n", ASCII_CHAT_GIT_COMMIT_HASH);
341 fprintf(stderr, " Build date: %s\n", ASCII_CHAT_BUILD_DATE);
342 fprintf(stderr, " For reproducible builds, commit or stash changes before building.\n\n");
343 }
344#endif
345
346 // Load color scheme early (from config files and CLI) before logging initialization
347 // This allows logging to use the correct colors from the start
349
350 // Parse --color-scheme from argv early to set logging colors for help output
351 const char *colorscheme_name = "pastel"; // default
352 for (int i = 1; i < argc - 1; i++) {
353 if (strcmp(argv[i], "--color-scheme") == 0) {
354 colorscheme_name = argv[i + 1];
355 break;
356 }
357 }
358
359 // Load and apply colorscheme to logging BEFORE options_init() so help gets colors
360 color_scheme_t scheme;
361 if (colorscheme_load_builtin(colorscheme_name, &scheme) == ASCIICHAT_OK) {
362 log_set_color_scheme(&scheme);
363 } else {
364 // Fall back to default pastel if scheme not found
365 if (colorscheme_load_builtin("pastel", &scheme) == ASCIICHAT_OK) {
366 log_set_color_scheme(&scheme);
367 }
368 }
369
370 // Initialize logging colors so they're ready for help output
372
373 // EARLY PARSE: Find the mode position (first positional argument)
374 // Binary-level options must appear BEFORE the mode
375 int mode_position = -1;
376 for (int i = 1; i < argc; i++) {
377 if (argv[i][0] != '-') {
378 // Found a positional argument - this is the mode or session string
379 mode_position = i;
380 break;
381 }
382
383 // Skip option and its argument if needed
384 // Check if this is an option that takes a required argument
385 const char *arg = argv[i];
386 const char *opt_name = arg;
387 if (arg[0] == '-') {
388 opt_name = arg + (arg[1] == '-' ? 2 : 1);
389 }
390
391 // Handle --option=value format
392 if (strchr(opt_name, '=')) {
393 continue; // Value is part of this arg, no need to skip next
394 }
395
396 // Options that take required arguments
397 if (strcmp(arg, "--log-file") == 0 || strcmp(arg, "-L") == 0 || strcmp(arg, "--log-level") == 0 ||
398 strcmp(arg, "--config") == 0 || strcmp(arg, "--color-scheme") == 0 || strcmp(arg, "--log-template") == 0) {
399 if (i + 1 < argc) {
400 i++; // Skip the argument value
401 }
402 }
403 }
404
405 // EARLY PARSE: Determine mode from argv to know if this is client-like mode
406 // The first positional argument (after options) is the mode: client, server, mirror, discovery, acds
407 bool is_client_like_mode = false;
408 if (mode_position > 0) {
409 const char *first_arg = argv[mode_position];
410 if (strcmp(first_arg, "client") == 0 || strcmp(first_arg, "mirror") == 0 || strcmp(first_arg, "discovery") == 0) {
411 is_client_like_mode = true;
412 }
413 }
414
415 // EARLY PARSE: Extract log file from argv (--log-file or -L)
416 // Must appear BEFORE the mode
417 const char *log_file = "ascii-chat.log"; // default
418 int max_search = (mode_position > 0) ? mode_position : argc;
419 for (int i = 1; i < max_search - 1; i++) {
420 if ((strcmp(argv[i], "--log-file") == 0 || strcmp(argv[i], "-L") == 0)) {
421 log_file = argv[i + 1];
422 break;
423 }
424 }
425
426 // EARLY PARSE: Check for --json (JSON logging format)
427 // If JSON format is enabled, we'll use the JSON filename during early init
428 // --json MUST appear BEFORE the mode
429 bool early_json_format = false;
430 if (mode_position > 0) {
431 for (int i = 1; i < mode_position; i++) {
432 if (strcmp(argv[i], "--json") == 0) {
433 early_json_format = true;
434 break;
435 }
436 }
437 } else {
438 // If no mode found, search entire argv
439 for (int i = 1; i < argc; i++) {
440 if (strcmp(argv[i], "--json") == 0) {
441 early_json_format = true;
442 break;
443 }
444 }
445 }
446
447 // EARLY PARSE: Extract log template from argv (--log-template)
448 // Note: This is for format templates, different from --log-format which is the output format
449 // Binary-level options must appear BEFORE the mode
450 const char *early_log_template = NULL;
451 bool early_log_template_console_only = false;
452 for (int i = 1; i < max_search - 1; i++) {
453 if (strcmp(argv[i], "--log-template") == 0) {
454 early_log_template = argv[i + 1];
455 break;
456 }
457 }
458 for (int i = 1; i < max_search; i++) {
459 if (strcmp(argv[i], "--log-format-console-only") == 0) {
460 early_log_template_console_only = true;
461 break;
462 }
463 }
464
465 // Initialize shared subsystems BEFORE options_init()
466 // This ensures options parsing can use properly configured logging with colors
467 // If JSON format is requested, don't write text logs to file
468 // All logs will be JSON formatted later once options_init() runs
469 const char *early_log_file = early_json_format ? NULL : log_file;
470 asciichat_error_t init_result = asciichat_shared_init(early_log_file, is_client_like_mode);
471 if (init_result != ASCIICHAT_OK) {
472 return init_result;
473 }
474
475 // Route logs to stderr if stdout is piped (MUST happen early, before options_init logs)
476 // This keeps stdout clean for data output (e.g., --snapshot mode piped to file)
479 }
480
481 // Register cleanup of shared subsystems to run on normal exit
482 // Library code doesn't call atexit() - the application is responsible
483 (void)atexit(asciichat_shared_destroy);
484
485 // SECRET: Check for --backtrace (debug builds only) BEFORE options_init()
486 // Prints a backtrace and exits immediately - useful for debugging hangs
487#ifndef NDEBUG
488 for (int i = 1; i < argc; i++) {
489 if (strcmp(argv[i], "--backtrace") == 0) {
490 log_info("=== Backtrace at startup ===");
492 log_info("=== End Backtrace ===");
494 return 0;
495 }
496 }
497#endif
498
499 // NOW parse all options - can use logging with colors!
500 asciichat_error_t options_result = options_init(argc, argv);
501 if (options_result != ASCIICHAT_OK) {
502 asciichat_error_context_t error_ctx;
503 if (HAS_ERRNO(&error_ctx)) {
504 fprintf(stderr, "Error: %s\n", error_ctx.context_message);
505 } else {
506 fprintf(stderr, "Error: Failed to initialize options\n");
507 }
508 (void)fflush(stderr);
509
510 // Clean up options state before exiting
513
514 exit(options_result);
515 }
516
517 // Get parsed options
518 const options_t *opts = options_get();
519 if (!opts) {
520 fprintf(stderr, "Error: Options not initialized\n");
521 return 1;
522 }
523
524 // Determine final log file path (use mode-specific default from options if available)
525 // Determine output format early to decide on filename and logging strategy
526 bool use_json_logging = GET_OPTION(json);
527
528 // Determine the log filename
529 // Check if user explicitly passed --log-file (not just the mode-specific default from options_init)
530 bool user_specified_log_file = false;
531 for (int i = 1; i < argc - 1; i++) {
532 if (strcmp(argv[i], "--log-file") == 0 || strcmp(argv[i], "-L") == 0) {
533 user_specified_log_file = true;
534 break;
535 }
536 }
537
538 const char *final_log_file = opts->log_file;
539 char json_filename_buf[256];
540
541 if (use_json_logging) {
542 // When JSON logging is enabled, determine JSON output filename
543 if (user_specified_log_file) {
544 // User explicitly specified a log file - use it exactly for JSON output
545 SAFE_STRNCPY(json_filename_buf, final_log_file, sizeof(json_filename_buf) - 1);
546 } else {
547 // Using default: replace .log with .json in the mode-specific default
548 // e.g., server.log -> server.json, mirror.log -> mirror.json, etc.
549 size_t len = strlen(final_log_file);
550 if (len > 4 && strcmp(&final_log_file[len - 4], ".log") == 0) {
551 // File ends with .log - replace with .json
552 SAFE_STRNCPY(json_filename_buf, final_log_file, sizeof(json_filename_buf) - 1);
553 // Replace .log with .json
554 strcpy(&json_filename_buf[len - 4], ".json");
555 } else {
556 // File doesn't end with .log - just append .json
557 SAFE_STRNCPY(json_filename_buf, final_log_file, sizeof(json_filename_buf) - 1);
558 strncat(json_filename_buf, ".json", sizeof(json_filename_buf) - strlen(json_filename_buf) - 1);
559 }
560 }
561 final_log_file = json_filename_buf;
562 // For JSON mode: Initialize logging without file (text will be disabled)
563 // We'll set up JSON output separately below
564 log_init(NULL, GET_OPTION(log_level), false, false);
565 } else {
566 // Text logging mode: use the file from options (which is mode-specific default or user-specified)
567 log_init(final_log_file, GET_OPTION(log_level), false, false);
568 }
569
570 // Apply custom log template if specified (use early parsed value if available, otherwise use options)
571 const char *final_format = early_log_template ? early_log_template : GET_OPTION(log_template);
572 bool final_format_console_only =
573 early_log_template ? early_log_template_console_only : GET_OPTION(log_format_console_only);
574 if (final_format && final_format[0] != '\0') {
575 asciichat_error_t fmt_result = log_set_format(final_format, final_format_console_only);
576 if (fmt_result != ASCIICHAT_OK) {
577 log_error("Failed to apply custom log format");
578 }
579 }
580
581 // Configure JSON output if requested
582 if (use_json_logging) {
583 // Open the JSON file for output
584 int json_fd = platform_open(final_log_file, O_CREAT | O_RDWR | O_TRUNC, FILE_PERM_PRIVATE);
585 if (json_fd >= 0) {
586 log_set_json_output(json_fd);
587 } else {
588 // Failed to open JSON file - report error and exit
589 SET_ERRNO(ERROR_CONFIG, "Failed to open JSON output file: %s", final_log_file);
590 return ERROR_CONFIG;
591 }
592 }
593
594 // Initialize colors now that logging is fully initialized
595 // This must happen after log_init() since log_init_colors() checks if g_log.initialized
597
598 // Apply quiet mode - disables terminal output
599 // Status screen mode only disables terminal output if terminal is interactive
600 // In non-interactive mode (piped output), logs go to stdout/stderr normally
601 if (GET_OPTION(quiet) ||
602 (opts->detected_mode == MODE_SERVER && GET_OPTION(status_screen) && terminal_is_interactive())) {
604 }
605
606 // Initialize palette based on command line options
607 const char *custom_chars = opts && opts->palette_custom_set ? opts->palette_custom : NULL;
608 if (apply_palette_config(GET_OPTION(palette_type), custom_chars) != 0) {
609 FATAL(ERROR_CONFIG, "Failed to apply palette configuration");
610 }
611
612 // Set quiet mode for memory debugging
613#if defined(DEBUG_MEMORY) && !defined(USE_MIMALLOC_DEBUG) && !defined(NDEBUG)
614 debug_memory_set_quiet_mode(GET_OPTION(quiet));
615#endif
616
617 // Truncate log if it's already too large
619
620 // Handle --help and --version (these are detected and flagged by options_init)
621 // Terminal capabilities already initialized before options_init() at startup
622 if (opts->help) {
623 print_usage(opts->detected_mode);
624 fflush(NULL);
625 _Exit(0);
626 }
627
628 if (opts->version) {
631 // action_show_version() calls _Exit(), so we don't reach here
632 }
633
634 // For server mode with status screen: disable terminal output only if interactive
635 // In non-interactive mode (piped output), logs go to stdout/stderr normally
636 // The status screen (when shown) will capture and display logs in its buffer instead
637 if (opts->detected_mode == MODE_SERVER && opts->status_screen && terminal_is_interactive()) {
639 }
640
641 log_dev("Logging initialized to %s", final_log_file);
642
643 // Note: We do NOT auto-disable colors when stdout appears to be piped, because:
644 // 1. Tools like ripgrep can display ANSI colors when piped
645 // 2. Sandboxed/containerized environments may report false positives for isatty()
646 // 3. Users can explicitly disable colors with --color=false if needed
647 // Color behavior is now fully controlled by --color and --color-mode options.
648
649#ifndef NDEBUG
650 // Initialize lock debugging system after logging is fully set up
651 log_debug("Initializing lock debug system...");
652 int lock_debug_result = lock_debug_init();
653 if (lock_debug_result != 0) {
654 LOG_ERRNO_IF_SET("Lock debug system initialization failed");
655 FATAL(ERROR_PLATFORM_INIT, "Lock debug system initialization failed");
656 }
657 log_debug("Lock debug system initialized successfully");
658
659 // Start lock debug thread in all modes (not just server)
660 if (lock_debug_start_thread() != 0) {
661 LOG_ERRNO_IF_SET("Lock debug thread startup failed");
662 FATAL(ERROR_THREAD, "Lock debug thread startup failed");
663 }
664 log_debug("Lock debug thread started");
665
666#ifndef _WIN32
667 // Register SIGUSR1 to trigger lock state printing in all modes
668 platform_signal(SIGUSR1, common_handle_sigusr1);
669#endif
670#endif
671
672 if (opts->fps > 0) {
673 if (opts->fps < 1 || opts->fps > 144) {
674 log_warn("FPS value %d out of range (1-144), using default", opts->fps);
675 } else {
676 log_debug("FPS set from command line: %d", opts->fps);
677 }
678 }
679
680 // Automatic update check at startup (once per week maximum)
681 if (!GET_OPTION(no_check_update)) {
682 update_check_result_t update_result;
683 asciichat_error_t update_err = update_check_startup(&update_result);
684 if (update_err == ASCIICHAT_OK && update_result.update_available) {
685 char notification[1024];
686 update_check_format_notification(&update_result, notification, sizeof(notification));
687 log_info("%s", notification);
688
689 // Set update notification for splash/status screens
690 splash_set_update_notification(notification);
691 }
692 }
693
694 // Set up global signal handlers BEFORE mode dispatch
695 // All modes use the same centralized exit mechanism
697
698 // Register SIGUSR1 for lock debugging in debug builds (uses shared handler)
699#ifndef NDEBUG
700#ifndef _WIN32
701 platform_signal(SIGUSR1, common_handle_sigusr1);
702#endif
703#endif
704
705 // Find and dispatch to mode entry point
706 const mode_descriptor_t *mode = find_mode(opts->detected_mode);
707 if (!mode) {
708 fprintf(stderr, "Error: Mode not found for detected_mode=%d\n", opts->detected_mode);
709 return 1;
710 }
711
712 // Call the mode-specific entry point
713 // Mode entry points use options_get() to access parsed options
714 int exit_code = mode->entry_point();
715
716 if (exit_code == ERROR_USAGE) {
717 exit(ERROR_USAGE);
718 }
719
720 return exit_code;
721}
int client_main(void)
Client mode entry point for unified binary.
ascii-chat Client Mode Entry Point Header
asciichat_error_t options_colorscheme_init_early(int argc, const char *const argv[])
Initialize color scheme early (before logging)
asciichat_error_t colorscheme_load_builtin(const char *name, color_scheme_t *scheme)
ASCIICHAT_API bool g_color_flag_value
Definition common.c:50
ASCIICHAT_API bool g_color_flag_passed
Definition common.c:49
ASCIICHAT_API char ** g_argv
Definition common.c:46
asciichat_error_t asciichat_shared_init(const char *log_file, bool is_client)
Definition common.c:109
ASCIICHAT_API int g_argc
Definition common.c:45
void asciichat_shared_destroy(void)
Clean up all shared library subsystems.
Definition common.c:164
int acds_main(void)
ACDS (discovery-service mode) entry point.
int discovery_main(void)
Run discovery mode main loop.
ascii-chat Discovery Mode Entry Point Header
asciichat_error_t grep_init(const char *pattern)
Definition grep.c:549
asciichat_error_t options_init(int argc, char **argv)
int lock_debug_init(void)
Definition lock.c:1364
void lock_debug_trigger_print(void)
Definition lock.c:1384
int lock_debug_start_thread(void)
Definition lock.c:1367
void log_truncate_if_large(void)
void log_init_colors(void)
void log_set_force_stderr(bool enabled)
void log_init(const char *filename, log_level_t level, bool force_stderr, bool use_mmap)
asciichat_error_t log_set_format(const char *format_str, bool console_only)
void log_set_json_output(int fd)
void log_set_terminal_output(bool enabled)
void log_set_color_scheme(const color_scheme_t *scheme)
void log_redetect_terminal_capabilities(void)
void signal_exit(void)
Definition main.c:94
int main(int argc, char *argv[])
Definition main.c:281
void setup_signal_handlers(void)
Definition main.c:167
int(* mode_entry_point_t)(void)
Definition main.c:183
bool should_exit(void)
Definition main.c:90
void set_interrupt_callback(void(*cb)(void))
Definition main.c:102
int mirror_main(void)
Run mirror mode main loop.
ascii-chat Mirror Mode Interface
void usage(FILE *desc, asciichat_mode_t mode)
void action_show_version(void)
int apply_palette_config(palette_type_t type, const char *custom_chars)
Definition palette.c:250
bool terminal_is_interactive(void)
bool terminal_should_force_stderr(void)
terminal_capabilities_t detect_terminal_capabilities(void)
void options_state_destroy(void)
Definition rcu.c:309
void options_cleanup_schema(void)
Definition rcu.c:343
const options_t * options_get(void)
Definition rcu.c:347
int server_main(void)
Server mode entry point for unified binary.
ascii-chat Server Mode Entry Point Header
void splash_set_update_notification(const char *notification)
Definition splash.c:612
Mode descriptor for string conversion.
mode_entry_point_t entry_point
Definition main.c:188
asciichat_error_t update_check_startup(update_check_result_t *result)
void update_check_format_notification(const update_check_result_t *result, char *buffer, size_t buffer_size)
void platform_print_backtrace(int skip_frames)
Definition util.c:42
int platform_open(const char *pathname, int flags,...)