281 {
282
285
286 if (argc < 1 || argv == NULL || argv[0] == NULL) {
287 fprintf(stderr, "Error: Invalid argument vector\n");
288 return 1;
289 }
290
291
292
295 for (int i = 1; i < argc; i++) {
296 if (strcmp(argv[i], "--color") == 0 || strcmp(argv[i], "--color=true") == 0) {
299 break;
300 } else if (strcmp(argv[i], "--color=false") == 0) {
303 break;
304 } else if (strncmp(argv[i], "--color=", 8) == 0) {
306
308 break;
309 }
310 }
311
312
313
314
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++;
327 }
328 }
329
330
331
334 caps = apply_color_mode_override(caps);
335
336
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
347
349
350
351 const char *colorscheme_name = "pastel";
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
360 color_scheme_t scheme;
363 } else {
364
367 }
368 }
369
370
372
373
374
375 int mode_position = -1;
376 for (int i = 1; i < argc; i++) {
377 if (argv[i][0] != '-') {
378
379 mode_position = i;
380 break;
381 }
382
383
384
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
392 if (strchr(opt_name, '=')) {
393 continue;
394 }
395
396
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++;
401 }
402 }
403 }
404
405
406
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
416
417 const char *log_file = "ascii-chat.log";
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
427
428
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
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
448
449
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
466
467
468
469 const char *early_log_file = early_json_format ? NULL : log_file;
471 if (init_result != ASCIICHAT_OK) {
472 return init_result;
473 }
474
475
476
479 }
480
481
482
484
485
486
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
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
513
514 exit(options_result);
515 }
516
517
519 if (!opts) {
520 fprintf(stderr, "Error: Options not initialized\n");
521 return 1;
522 }
523
524
525
526 bool use_json_logging = GET_OPTION(json);
527
528
529
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
543 if (user_specified_log_file) {
544
545 SAFE_STRNCPY(json_filename_buf, final_log_file, sizeof(json_filename_buf) - 1);
546 } else {
547
548
549 size_t len = strlen(final_log_file);
550 if (len > 4 && strcmp(&final_log_file[len - 4], ".log") == 0) {
551
552 SAFE_STRNCPY(json_filename_buf, final_log_file, sizeof(json_filename_buf) - 1);
553
554 strcpy(&json_filename_buf[len - 4], ".json");
555 } else {
556
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
563
564 log_init(NULL, GET_OPTION(log_level),
false,
false);
565 } else {
566
567 log_init(final_log_file, GET_OPTION(log_level),
false,
false);
568 }
569
570
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
582 if (use_json_logging) {
583
584 int json_fd =
platform_open(final_log_file, O_CREAT | O_RDWR | O_TRUNC, FILE_PERM_PRIVATE);
585 if (json_fd >= 0) {
587 } else {
588
589 SET_ERRNO(ERROR_CONFIG, "Failed to open JSON output file: %s", final_log_file);
590 return ERROR_CONFIG;
591 }
592 }
593
594
595
597
598
599
600
601 if (GET_OPTION(quiet) ||
604 }
605
606
607 const char *custom_chars = opts && opts->palette_custom_set ? opts->palette_custom : NULL;
609 FATAL(ERROR_CONFIG, "Failed to apply palette configuration");
610 }
611
612
613#if defined(DEBUG_MEMORY) && !defined(USE_MIMALLOC_DEBUG) && !defined(NDEBUG)
614 debug_memory_set_quiet_mode(GET_OPTION(quiet));
615#endif
616
617
619
620
621
622 if (opts->help) {
623 print_usage(opts->detected_mode);
624 fflush(NULL);
625 _Exit(0);
626 }
627
628 if (opts->version) {
631
632 }
633
634
635
636
639 }
640
641 log_dev("Logging initialized to %s", final_log_file);
642
643
644
645
646
647
648
649#ifndef NDEBUG
650
651 log_debug("Initializing lock debug system...");
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
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
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
681 if (!GET_OPTION(no_check_update)) {
682 update_check_result_t update_result;
684 if (update_err == ASCIICHAT_OK && update_result.update_available) {
685 char notification[1024];
687 log_info("%s", notification);
688
689
691 }
692 }
693
694
695
697
698
699#ifndef NDEBUG
700#ifndef _WIN32
701 platform_signal(SIGUSR1, common_handle_sigusr1);
702#endif
703#endif
704
705
707 if (!mode) {
708 fprintf(stderr, "Error: Mode not found for detected_mode=%d\n", opts->detected_mode);
709 return 1;
710 }
711
712
713
715
716 if (exit_code == ERROR_USAGE) {
717 exit(ERROR_USAGE);
718 }
719
720 return exit_code;
721}
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
ASCIICHAT_API bool g_color_flag_passed
ASCIICHAT_API char ** g_argv
asciichat_error_t asciichat_shared_init(const char *log_file, bool is_client)
void asciichat_shared_destroy(void)
Clean up all shared library subsystems.
asciichat_error_t grep_init(const char *pattern)
asciichat_error_t options_init(int argc, char **argv)
int lock_debug_init(void)
int lock_debug_start_thread(void)
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 setup_signal_handlers(void)
void action_show_version(void)
int apply_palette_config(palette_type_t type, const char *custom_chars)
void options_state_destroy(void)
void options_cleanup_schema(void)
const options_t * options_get(void)
void splash_set_update_notification(const char *notification)
Mode descriptor for string conversion.
mode_entry_point_t entry_point
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)
int platform_open(const char *pathname, int flags,...)