ascii-chat 0.8.38
Real-time terminal-based video chat with ASCII art conversion
Loading...
Searching...
No Matches
system.c File Reference

🔧 Shared cross-platform system utilities (included by posix/system.c and windows/system.c) More...

Go to the source code of this file.

Data Structures

struct  bin_cache_entry_t
 Binary PATH cache entry structure for binary detection caching. More...
 

Macros

#define BIN_SUFFIX   ""
 
#define PLATFORM_MAX_PATH_LENGTH   4096
 

Functions

void platform_cleanup_binary_path_cache (void)
 Cleanup the binary PATH cache.
 
bool platform_is_binary_in_path (const char *bin_name)
 
bool platform_get_executable_path (char *exe_path, size_t path_size)
 Get the path to the current executable.
 
int safe_snprintf (char *buffer, size_t buffer_size, const char *format,...)
 Safe formatted string printing to buffer.
 
int safe_fprintf (FILE *stream, const char *format,...)
 Safe formatted output to file stream.
 
int safe_vsnprintf (char *buffer, size_t buffer_size, const char *format, va_list ap)
 Safe formatted string printing with va_list.
 
void platform_print_backtrace_symbols (const char *label, char **symbols, int count, int skip_frames, int max_frames, backtrace_frame_filter_t filter)
 Print pre-resolved backtrace symbols with colored terminal output and plain log file output.
 

Detailed Description

🔧 Shared cross-platform system utilities (included by posix/system.c and windows/system.c)

Definition in file system.c.

Macro Definition Documentation

◆ BIN_SUFFIX

#define BIN_SUFFIX   ""

Definition at line 25 of file system.c.

◆ PLATFORM_MAX_PATH_LENGTH

#define PLATFORM_MAX_PATH_LENGTH   4096

Maximum path length supported by the operating system

Platform-specific values:

  • Windows: 32767 characters (extended-length path with \?\ prefix)
  • Linux: 4096 bytes (PATH_MAX from limits.h)
  • macOS: 1024 bytes (PATH_MAX from sys/syslimits.h)

Note: Windows legacy MAX_PATH (260) is too restrictive for modern use. We use the extended-length limit instead.

Definition at line 64 of file system.c.

Function Documentation

◆ platform_cleanup_binary_path_cache()

void platform_cleanup_binary_path_cache ( void  )

Cleanup the binary PATH cache.

Definition at line 304 of file system.c.

304 {
305 if (!atomic_load(&g_cache_initialized)) {
306 return;
307 }
308
309 if (g_bin_path_cache) {
310 rwlock_wrlock(&g_cache_rwlock);
311
312 // Free all cached entries using uthash iteration
313 bin_cache_entry_t *entry, *tmp;
314 HASH_ITER(hh, g_bin_path_cache, entry, tmp) {
315 HASH_DELETE(hh, g_bin_path_cache, entry);
316 free_cache_entry(entry);
317 }
318
319 rwlock_wrunlock(&g_cache_rwlock);
320 rwlock_destroy(&g_cache_rwlock);
321 g_bin_path_cache = NULL;
322 }
323
324 atomic_store(&g_cache_initialized, false);
325}
Binary PATH cache entry structure for binary detection caching.
Definition system.c:118

Referenced by server_main().

◆ platform_get_executable_path()

bool platform_get_executable_path ( char *  exe_path,
size_t  path_size 
)

Get the path to the current executable.

Parameters
exe_pathBuffer to store the executable path
path_sizeSize of the buffer
Returns
true on success, false on failure

Definition at line 393 of file system.c.

393 {
394 if (!exe_path || path_size == 0) {
395 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters: exe_path=%p, path_size=%zu", (void *)exe_path, path_size);
396 return false;
397 }
398
399#ifdef _WIN32
400 DWORD len = GetModuleFileNameA(NULL, exe_path, (DWORD)path_size);
401 if (len == 0) {
402 SET_ERRNO_SYS(ERROR_INVALID_STATE, "GetModuleFileNameA failed: error code %lu", GetLastError());
403 return false;
404 }
405 if (len >= path_size) {
406 SET_ERRNO(ERROR_BUFFER_OVERFLOW,
407 "Executable path exceeds buffer size (path length >= %zu bytes, buffer size = %zu bytes)", (size_t)len,
408 path_size);
409 return false;
410 }
411 return true;
412
413#elif defined(__linux__)
414 ssize_t len = readlink("/proc/self/exe", exe_path, path_size - 1);
415 if (len < 0) {
416 SET_ERRNO_SYS(ERROR_INVALID_STATE, "readlink(\"/proc/self/exe\") failed: %s", SAFE_STRERROR(errno));
417 return false;
418 }
419 if ((size_t)len >= path_size - 1) {
420 SET_ERRNO(ERROR_BUFFER_OVERFLOW,
421 "Executable path exceeds buffer size (path length >= %zu bytes, buffer size = %zu bytes)", (size_t)len,
422 path_size);
423 return false;
424 }
425 exe_path[len] = '\0';
426 return true;
427
428#elif defined(__APPLE__)
429 uint32_t bufsize = (uint32_t)path_size;
430 int result = _NSGetExecutablePath(exe_path, &bufsize);
431 if (result != 0) {
432 SET_ERRNO(ERROR_BUFFER_OVERFLOW, "_NSGetExecutablePath failed: path requires %u bytes, buffer size = %zu bytes",
433 bufsize, path_size);
434 return false;
435 }
436 return true;
437
438#else
439 SET_ERRNO(ERROR_GENERAL, "Unsupported platform - cannot get executable path");
440 return false;
441#endif
442}

◆ platform_is_binary_in_path()

bool platform_is_binary_in_path ( const char *  bin_name)

Definition at line 331 of file system.c.

331 {
332 if (!bin_name || bin_name[0] == '\0') {
333 return false;
334 }
335
336 // Initialize cache if needed
337 init_cache_once();
338 if (!atomic_load(&g_cache_initialized)) {
339 // Cache initialization failed, check directly (this should never happen)
340 SET_ERRNO(ERROR_INVALID_STATE, "Binary PATH cache not initialized, checking directly (this should never happen)");
341 return check_binary_in_path_uncached(bin_name);
342 }
343
344 // Check cache first
345 rwlock_rdlock(&g_cache_rwlock);
346 bin_cache_entry_t *entry = NULL;
347 HASH_FIND_STR(g_bin_path_cache, bin_name, entry);
348 rwlock_rdunlock(&g_cache_rwlock);
349
350 if (entry) {
351 // Cache hit
352 log_dev("Binary '%s' %s in PATH (%s)", bin_name, colored_string(LOG_COLOR_INFO, "found"),
353 colored_string(LOG_COLOR_WARN, "cached"));
354 return entry->in_path;
355 }
356
357 // Cache miss - check PATH and cache result
358 bool found = check_binary_in_path_uncached(bin_name);
359
360 // Create new cache entry
361 entry = SAFE_MALLOC(sizeof(bin_cache_entry_t), bin_cache_entry_t *);
362 if (!entry) {
363 SET_ERRNO(ERROR_MEMORY, "Failed to allocate cache entry");
364 return found; // Return result without caching
365 }
366
367 entry->bin_name = platform_strdup(bin_name);
368 if (!entry->bin_name) {
369 SET_ERRNO(ERROR_MEMORY, "Failed to duplicate binary name");
370 SAFE_FREE(entry);
371 return found;
372 }
373
374 entry->in_path = found;
375
376 // Add to cache using uthash
377 rwlock_wrlock(&g_cache_rwlock);
378 HASH_ADD_KEYPTR(hh, g_bin_path_cache, entry->bin_name, strlen(entry->bin_name), entry);
379 rwlock_wrunlock(&g_cache_rwlock);
380
381 log_dev("Binary '%s' %s in PATH", bin_name,
382 colored_string(found ? LOG_COLOR_INFO : LOG_COLOR_ERROR, found ? "found" : "NOT found"));
383
384 return found;
385}
char * platform_strdup(const char *s)
bool in_path
Whether binary was found in PATH (true = found, false = not found)
Definition system.c:122
char * bin_name
Binary name string (allocated, owned by cache) - also used as uthash key.
Definition system.c:120
const char * colored_string(log_color_t color, const char *text)

References bin_cache_entry_t::bin_name, colored_string(), bin_cache_entry_t::in_path, and platform_strdup().

◆ platform_print_backtrace_symbols()

void platform_print_backtrace_symbols ( const char *  label,
char **  symbols,
int  count,
int  skip_frames,
int  max_frames,
backtrace_frame_filter_t  filter 
)

Print pre-resolved backtrace symbols with colored terminal output and plain log file output.

This is a cross-platform function that formats backtrace symbols with:

  • Semantic coloring: grey numbers, blue functions, red unknowns, magenta addresses
  • Two output streams: colored to stderr (terminal), plain to log file
Parameters
labelHeader label (e.g., "Backtrace")
symbolsArray of pre-resolved symbol strings
countNumber of symbols in the array
skip_framesNumber of frames to skip from the start
max_framesMaximum frames to print (0 = unlimited)
filterOptional filter callback to skip specific frames (NULL = no filtering)

Definition at line 535 of file system.c.

536 {
537 if (!symbols || count <= 0) {
538 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters: symbols=%p, count=%d", symbols, count);
539 return;
540 }
541
542 // Calculate frame limits
543 int start = skip_frames;
544 int end = count;
545 if (max_frames > 0 && (start + max_frames) < end) {
546 end = start + max_frames;
547 }
548
549 // Build backtrace in two versions: colored for terminal, plain for log file
550 char colored_buffer[16384] = {0};
551 char plain_buffer[16384] = {0};
552 int colored_offset = 0;
553 int plain_offset = 0;
554
555 // Format log header using logging system's template
556 char log_header_buf[512] = {0};
557 time_t now = time(NULL);
558 struct tm *tm_info = localtime(&now);
559 char timestamp[32];
560 strftime(timestamp, sizeof(timestamp), "%H:%M:%S", tm_info);
561
562 thread_id_t tid = asciichat_thread_self();
563 uint64_t tid_val = (uintptr_t)tid;
564
565 // Get current time in nanoseconds for template formatting
566 uint64_t time_ns = platform_get_monotonic_time_us() * 1000ULL;
567
568 // Try to format header using the logging system's template with color
569 log_template_t *format = log_get_template();
570 if (format) {
571 // Color the label with WARN color for terminal output
572 const char *colored_label_ptr = colored_string(LOG_COLOR_WARN, label);
573 char colored_label_buf[256];
574 strncpy(colored_label_buf, colored_label_ptr, sizeof(colored_label_buf) - 1);
575 colored_label_buf[sizeof(colored_label_buf) - 1] = '\0';
576
577 int len = log_template_apply(format, log_header_buf, sizeof(log_header_buf), LOG_WARN, timestamp, __FILE__,
578 __LINE__, __func__, tid_val, colored_label_buf, true, time_ns);
579 if (len > 0) {
580 // Successfully formatted with logging template
581 colored_offset += safe_snprintf(colored_buffer + colored_offset, sizeof(colored_buffer) - (size_t)colored_offset,
582 "%s\n", log_header_buf);
583 } else {
584 // Fallback: manual formatting if template fails
585 safe_snprintf(log_header_buf, sizeof(log_header_buf), "[%s] [WARN] [tid:%llu] %s: %s", timestamp, tid_val,
586 __func__, label);
587 const char *colored_header_ptr = colored_string(LOG_COLOR_WARN, log_header_buf);
588 char colored_header_buf[512];
589 strncpy(colored_header_buf, colored_header_ptr, sizeof(colored_header_buf) - 1);
590 colored_header_buf[sizeof(colored_header_buf) - 1] = '\0';
591 colored_offset += safe_snprintf(colored_buffer + colored_offset, sizeof(colored_buffer) - (size_t)colored_offset,
592 "%s\n", colored_header_buf);
593 }
594 } else {
595 // Fallback: manual formatting if no template available
596 safe_snprintf(log_header_buf, sizeof(log_header_buf), "[%s] [WARN] [tid:%llu] %s: %s", timestamp, tid_val, __func__,
597 label);
598 const char *colored_header_ptr = colored_string(LOG_COLOR_WARN, log_header_buf);
599 char colored_header_buf[512];
600 strncpy(colored_header_buf, colored_header_ptr, sizeof(colored_header_buf) - 1);
601 colored_header_buf[sizeof(colored_header_buf) - 1] = '\0';
602 colored_offset += safe_snprintf(colored_buffer + colored_offset, sizeof(colored_buffer) - (size_t)colored_offset,
603 "%s\n", colored_header_buf);
604 }
605
606 // Add plain label header for log file
607 plain_offset +=
608 safe_snprintf(plain_buffer + plain_offset, sizeof(plain_buffer) - (size_t)plain_offset, "%s\n", label);
609
610 // Build backtrace frames with colored output for terminal, plain for log
611 int frame_num = 0;
612 for (int i = start; i < end && colored_offset < (int)sizeof(colored_buffer) - 512; i++) {
613 const char *symbol = symbols[i] ? symbols[i] : "???";
614
615 // Skip frame if filter says to
616 if (filter && filter(symbol)) {
617 continue;
618 }
619
620 // Build frame number string
621 char frame_num_str[16];
622 safe_snprintf(frame_num_str, sizeof(frame_num_str), "%d", frame_num);
623
624 // Get colored frame number - copy to temp buffer to avoid rotating buffer issues
625 const char *colored_num_ptr = colored_string(LOG_COLOR_GREY, frame_num_str);
626 char colored_num_buf[256];
627 strncpy(colored_num_buf, colored_num_ptr, sizeof(colored_num_buf) - 1);
628 colored_num_buf[sizeof(colored_num_buf) - 1] = '\0';
629
630 // Parse symbol to extract parts for selective coloring
631 // Format: "[binary_name] function_name() (file:line)"
632 char colored_symbol[2048] = {0};
633 const char *s = symbol;
634 int colored_sym_offset = 0;
635
636 // Color binary name between brackets
637 if (*s == '[') {
638 colored_sym_offset +=
639 safe_snprintf(colored_symbol + colored_sym_offset, sizeof(colored_symbol) - colored_sym_offset, "[");
640 s++;
641 // Find closing bracket
642 const char *bracket_end = strchr(s, ']');
643 if (bracket_end) {
644 int bin_len = bracket_end - s;
645 char bin_name[512];
646 strncpy(bin_name, s, bin_len);
647 bin_name[bin_len] = '\0';
648 const char *colored_bin_ptr = colored_string(LOG_COLOR_DEBUG, bin_name);
649 char colored_bin_buf[512];
650 strncpy(colored_bin_buf, colored_bin_ptr, sizeof(colored_bin_buf) - 1);
651 colored_bin_buf[sizeof(colored_bin_buf) - 1] = '\0';
652 colored_sym_offset += safe_snprintf(colored_symbol + colored_sym_offset,
653 sizeof(colored_symbol) - colored_sym_offset, "%s", colored_bin_buf);
654 colored_sym_offset +=
655 safe_snprintf(colored_symbol + colored_sym_offset, sizeof(colored_symbol) - colored_sym_offset, "] ");
656 s = bracket_end + 1;
657 }
658 }
659
660 // Skip leading spaces after bracket
661 while (*s && *s == ' ')
662 s++;
663
664 // Parse: could be "function() (file:line)" or "file:line (unresolved)"
665 const char *paren_start = strchr(s, '(');
666
667 // Detect format: if there's a colon before the first paren, it's "file:line (description)"
668 const char *colon_pos = strchr(s, ':');
669 if (colon_pos && paren_start && colon_pos < paren_start) {
670 // Format: "file:line (unresolved function)" - rearrange to "(unresolved) (file:line)"
671 // Extract file:line part (trim trailing spaces)
672 int file_len = paren_start - s;
673 while (file_len > 0 && s[file_len - 1] == ' ')
674 file_len--;
675 char file_part[512];
676 strncpy(file_part, s, file_len);
677 file_part[file_len] = '\0';
678
679 // Extract description content (without parens)
680 const char *paren_end = strchr(paren_start, ')');
681 int desc_content_len = paren_end - paren_start - 1; // -1 to skip opening paren
682 char desc_content[512];
683 strncpy(desc_content, paren_start + 1, desc_content_len); // +1 to skip opening paren
684 desc_content[desc_content_len] = '\0';
685
686 const char *colored_desc_ptr = colored_string(LOG_COLOR_ERROR, desc_content);
687 char colored_desc_buf[512];
688 strncpy(colored_desc_buf, colored_desc_ptr, sizeof(colored_desc_buf) - 1);
689 colored_desc_buf[sizeof(colored_desc_buf) - 1] = '\0';
690 colored_sym_offset += safe_snprintf(colored_symbol + colored_sym_offset,
691 sizeof(colored_symbol) - colored_sym_offset, "(%s)", colored_desc_buf);
692
693 // Now color file:line in parens
694 // Skip leading spaces in file_part
695 const char *file_start = file_part;
696 while (*file_start && *file_start == ' ')
697 file_start++;
698
699 char *file_colon = strchr(file_start, ':');
700 if (file_colon) {
701 int filename_len = file_colon - file_start;
702 char filename[512];
703 strncpy(filename, file_start, filename_len);
704 filename[filename_len] = '\0';
705
706 const char *colored_file_ptr = colored_string(LOG_COLOR_DEBUG, filename);
707 char colored_file_buf[512];
708 strncpy(colored_file_buf, colored_file_ptr, sizeof(colored_file_buf) - 1);
709 colored_file_buf[sizeof(colored_file_buf) - 1] = '\0';
710
711 const char *line_num = file_colon + 1;
712 const char *colored_line_ptr = colored_string(LOG_COLOR_GREY, line_num);
713 char colored_line_buf[512];
714 strncpy(colored_line_buf, colored_line_ptr, sizeof(colored_line_buf) - 1);
715 colored_line_buf[sizeof(colored_line_buf) - 1] = '\0';
716
717 colored_sym_offset +=
718 safe_snprintf(colored_symbol + colored_sym_offset, sizeof(colored_symbol) - colored_sym_offset, " (%s:%s)",
719 colored_file_buf, colored_line_buf);
720 }
721 } else if (paren_start) {
722 // Format: "function() (file:line)"
723 int func_len = paren_start - s;
724 char func_name[512];
725 strncpy(func_name, s, func_len);
726 func_name[func_len] = '\0';
727
728 const char *colored_func_ptr = colored_string(LOG_COLOR_DEV, func_name);
729 char colored_func_buf[512];
730 strncpy(colored_func_buf, colored_func_ptr, sizeof(colored_func_buf) - 1);
731 colored_func_buf[sizeof(colored_func_buf) - 1] = '\0';
732 colored_sym_offset += safe_snprintf(colored_symbol + colored_sym_offset,
733 sizeof(colored_symbol) - colored_sym_offset, "%s()", colored_func_buf);
734
735 // Find file:line in second set of parens
736 s = paren_start + 1;
737 while (*s && *s != '(')
738 s++;
739
740 if (*s == '(') {
741 const char *file_paren_end = strchr(s, ')');
742 if (file_paren_end) {
743 colored_sym_offset +=
744 safe_snprintf(colored_symbol + colored_sym_offset, sizeof(colored_symbol) - colored_sym_offset, " (");
745 int file_len = file_paren_end - s - 1;
746 char file_part[512];
747 strncpy(file_part, s + 1, file_len);
748 file_part[file_len] = '\0';
749
750 // Skip leading spaces in file_part
751 const char *file_start = file_part;
752 while (*file_start && *file_start == ' ')
753 file_start++;
754
755 char *colon_pos = strchr(file_start, ':');
756 if (colon_pos) {
757 int filename_len = colon_pos - file_start;
758 char filename[512];
759 strncpy(filename, file_start, filename_len);
760 filename[filename_len] = '\0';
761
762 const char *colored_file_ptr = colored_string(LOG_COLOR_DEBUG, filename);
763 char colored_file_buf[512];
764 strncpy(colored_file_buf, colored_file_ptr, sizeof(colored_file_buf) - 1);
765 colored_file_buf[sizeof(colored_file_buf) - 1] = '\0';
766
767 const char *line_num = colon_pos + 1;
768 const char *colored_line_ptr = colored_string(LOG_COLOR_GREY, line_num);
769 char colored_line_buf[512];
770 strncpy(colored_line_buf, colored_line_ptr, sizeof(colored_line_buf) - 1);
771 colored_line_buf[sizeof(colored_line_buf) - 1] = '\0';
772
773 colored_sym_offset +=
774 safe_snprintf(colored_symbol + colored_sym_offset, sizeof(colored_symbol) - colored_sym_offset, "%s:%s",
775 colored_file_buf, colored_line_buf);
776 }
777 colored_sym_offset +=
778 safe_snprintf(colored_symbol + colored_sym_offset, sizeof(colored_symbol) - colored_sym_offset, ")");
779 }
780 }
781 } else {
782 // No parens, likely a hex address - color with FATAL
783 const char *colored_addr_ptr = colored_string(LOG_COLOR_FATAL, s);
784 char colored_addr_buf[512];
785 strncpy(colored_addr_buf, colored_addr_ptr, sizeof(colored_addr_buf) - 1);
786 colored_addr_buf[sizeof(colored_addr_buf) - 1] = '\0';
787 colored_sym_offset += safe_snprintf(colored_symbol + colored_sym_offset,
788 sizeof(colored_symbol) - colored_sym_offset, "%s", colored_addr_buf);
789 }
790
791 // Format colored buffer: " [colored_num] colored_symbol\n"
792 colored_offset += safe_snprintf(colored_buffer + colored_offset, sizeof(colored_buffer) - (size_t)colored_offset,
793 " [%s] %s\n", colored_num_buf, colored_symbol);
794
795 // Format plain buffer: " [num] symbol\n"
796 plain_offset += safe_snprintf(plain_buffer + plain_offset, sizeof(plain_buffer) - (size_t)plain_offset,
797 " [%d] %s\n", frame_num, symbol);
798 frame_num++;
799 }
800
801 // TODO: Investigate why log_warn() can't be used here. Currently we bypass the logging
802 // system to preserve ANSI color codes, but this means we lose the normal log formatting
803 // (timestamps, log level, etc). Ideally log_warn() should accept a flag to preserve codes.
804 fprintf(stderr, "%s", colored_buffer);
805
806 // Write plain version to log file only (skip stderr since we already printed colored version)
807 log_file_msg("%s", plain_buffer);
808}
int log_template_apply(const log_template_t *format, char *buf, size_t buf_size, log_level_t level, const char *timestamp, const char *file, int line, const char *func, uint64_t tid, const char *message, bool use_colors, uint64_t time_nanoseconds)
Definition log/format.c:455
void log_file_msg(const char *fmt,...)
void * log_get_template(void)
Get the current log format template.
uint64_t platform_get_monotonic_time_us(void)
int safe_snprintf(char *buffer, size_t buffer_size, const char *format,...)
Safe formatted string printing to buffer.
Definition system.c:456
asciichat_thread_t asciichat_thread_self(void)
Definition threading.c:54

References asciichat_thread_self(), colored_string(), log_file_msg(), log_get_template(), log_template_apply(), platform_get_monotonic_time_us(), and safe_snprintf().

Referenced by asciichat_fatal_with_context(), and asciichat_print_error_context().

◆ safe_fprintf()

int safe_fprintf ( FILE *  stream,
const char *  format,
  ... 
)

Safe formatted output to file stream.

Parameters
streamOutput file stream
formatPrintf-style format string
Returns
Number of characters written, or -1 on error

Safely formats and prints output to a file stream. Returns the number of characters written, or -1 on error.

Definition at line 480 of file system.c.

480 {
481 if (!stream || !format) {
482 return -1;
483 }
484
485 va_list args;
486 va_start(args, format);
487 int ret = vfprintf(stream, format, args);
488 va_end(args);
489
490 return ret;
491}
action_args_t args

References args.

Referenced by add_known_host(), asciichat_fatal_with_context(), asciichat_print_error_context(), log_init(), log_labeled(), log_msg(), log_plain_msg(), and webcam_print_init_error_help().

◆ safe_snprintf()

int safe_snprintf ( char *  buffer,
size_t  buffer_size,
const char *  format,
  ... 
)

Safe formatted string printing to buffer.

Parameters
bufferOutput buffer
buffer_sizeSize of output buffer
formatPrintf-style format string
Returns
Number of characters written, or -1 on error

Safely formats a string into a buffer with bounds checking. Uses platform_snprintf for cross-platform implementation. Returns the number of characters written (not including null terminator). Returns -1 if buffer is too small.

Definition at line 456 of file system.c.

456 {
457 if (!buffer || !format || buffer_size == 0) {
458 return -1;
459 }
460
461 va_list args;
462 va_start(args, format);
463
464 /* Delegate to platform_snprintf for actual formatting */
465 int ret = platform_vsnprintf(buffer, buffer_size, format, args);
466
467 va_end(args);
468 return ret;
469}
int buffer_size
Size of circular buffer.
Definition grep.c:84
int platform_vsnprintf(char *str, size_t size, const char *format, va_list ap)

References args, buffer_size, and platform_vsnprintf().

Referenced by acds_client_connect(), acds_identity_default_path(), acds_identity_fingerprint(), acds_main(), acds_string_generate(), add_client(), add_webrtc_client(), asciichat_instr_log_line(), asciichat_instr_log_pc(), build_github_gpg_url(), build_github_ssh_url(), build_gitlab_gpg_url(), build_gitlab_ssh_url(), capture_init(), check_gpg_key_expiry(), check_known_host(), check_known_host_no_identity(), colored_string(), colorize_log_message(), colorscheme_compile_scheme(), colorscheme_export_scheme(), config_load_and_apply(), create_client_render_threads(), crypto_get_rekey_status(), crypto_handshake_client_key_exchange(), crypto_handshake_server_auth_challenge(), crypto_handshake_server_start(), discovery_keys_fetch_github(), discovery_keys_fetch_gitlab(), discovery_keys_get_cache_path(), discovery_keys_save_cached(), display_mitm_warning(), expand_path(), find_similar_option_with_mode(), format_available_modes(), format_bytes_pretty(), format_duration_ns(), format_gpg_key_display(), format_mode_names(), format_option_default_value_str(), format_public_key(), format_uptime_hms(), get_current_time_formatted(), get_discovery_database_dir(), get_known_hosts_path(), get_log_dir(), get_manpage_template(), gpg_agent_sign(), gpg_get_public_key(), gpg_sign_detached_ed25519(), gpg_sign_with_key(), gpg_verify_detached_ed25519(), gpg_verify_signature(), gpg_verify_signature_with_binary(), https_get(), ice_format_candidate(), image_print_color(), log_mmap_write(), log_recolor_plain_entry(), log_template_apply(), manpage_content_generate_environment(), manpage_content_generate_environment_with_manual(), manpage_content_generate_examples(), manpage_content_generate_options(), manpage_content_generate_positional(), manpage_content_generate_usage(), manpage_fmt_write_title(), manpage_merger_generate_synopsis(), manpage_merger_generate_usage(), nat_upnp_get_address(), options_config_calculate_max_col_width(), options_config_print_options_sections_with_width(), options_config_print_usage(), options_format_default_value(), options_init(), options_preset_unified(), options_print_help_for_mode(), parallel_connect(), parse_client_address(), parse_color_filter(), parse_color_mode(), parse_gpg_key(), parse_gpg_key_binary(), parse_gpg_keys_from_response(), parse_log_level(), parse_palette_chars(), parse_palette_type(), parse_port_option(), parse_private_key(), parse_render_mode(), parse_server_bind_address(), parse_ssh_private_key(), path_validate_user_path(), platform_print_backtrace_symbols(), prompt_unknown_host(), prompt_unknown_host_no_identity(), pubkey_to_hex(), query_init(), remove_known_host(), rgb_to_truecolor_ansi(), sdp_generate_answer(), sdp_generate_offer(), server_main(), session_capture_create(), session_network_capture_create(), stats_logger_thread(), test_get_binary_path(), turn_generate_credentials(), and yt_dlp_extract_stream_url().

◆ safe_vsnprintf()

int safe_vsnprintf ( char *  buffer,
size_t  buffer_size,
const char *  format,
va_list  ap 
)

Safe formatted string printing with va_list.

Parameters
bufferOutput buffer (can be NULL if buffer_size is 0 for size calculation)
buffer_sizeSize of output buffer
formatPrintf-style format string
apVariable argument list
Returns
Number of characters written, or -1 on error

Safely formats a string into a buffer with bounds checking using va_list. Uses platform_vsnprintf for cross-platform implementation. Returns the number of characters written (not including null terminator). Special case: NULL buffer with size 0 returns required buffer size for size calculation. Returns -1 if buffer is too small, format is invalid, or buffer is NULL with size > 0.

Definition at line 507 of file system.c.

507 {
508 if (!format) {
509 return -1;
510 }
511
512 // Allow NULL buffer with size 0 for size calculation (standard vsnprintf behavior)
513 if (!buffer && buffer_size > 0) {
514 return -1; // Non-NULL buffer required when buffer_size > 0
515 }
516
517 /* Delegate to platform_vsnprintf for actual formatting */
518 return platform_vsnprintf(buffer, buffer_size, format, ap);
519}

References buffer_size, and platform_vsnprintf().

Referenced by disconnect_client_for_bad_data(), format_message(), log_file_msg(), log_mmap_write(), log_msg(), log_plain_msg(), and str_printf().