mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2024-12-01 01:55:41 +00:00
424 lines
14 KiB
C
424 lines
14 KiB
C
|
#include <stdarg.h>
|
||
|
#include <string.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <stdio.h>
|
||
|
#ifdef _WIN32
|
||
|
#include <windows.h>
|
||
|
#else
|
||
|
#include <unistd.h>
|
||
|
#endif
|
||
|
#ifdef __ANDROID__
|
||
|
#include <android/log.h>
|
||
|
#endif
|
||
|
|
||
|
#ifndef CLOG_LOG_TO_STDIO
|
||
|
#ifdef __ANDROID__
|
||
|
#define CLOG_LOG_TO_STDIO 0
|
||
|
#else
|
||
|
#define CLOG_LOG_TO_STDIO 1
|
||
|
#endif
|
||
|
#endif
|
||
|
|
||
|
#include <clog.h>
|
||
|
|
||
|
|
||
|
/* Messages up to this size are formatted entirely on-stack, and don't allocate heap memory */
|
||
|
#define CLOG_STACK_BUFFER_SIZE 1024
|
||
|
|
||
|
#define CLOG_FATAL_PREFIX "Fatal error: "
|
||
|
#define CLOG_FATAL_PREFIX_LENGTH 13
|
||
|
#define CLOG_FATAL_PREFIX_FORMAT "Fatal error in %s: "
|
||
|
#define CLOG_ERROR_PREFIX "Error: "
|
||
|
#define CLOG_ERROR_PREFIX_LENGTH 7
|
||
|
#define CLOG_ERROR_PREFIX_FORMAT "Error in %s: "
|
||
|
#define CLOG_WARNING_PREFIX "Warning: "
|
||
|
#define CLOG_WARNING_PREFIX_LENGTH 9
|
||
|
#define CLOG_WARNING_PREFIX_FORMAT "Warning in %s: "
|
||
|
#define CLOG_INFO_PREFIX "Note: "
|
||
|
#define CLOG_INFO_PREFIX_LENGTH 6
|
||
|
#define CLOG_INFO_PREFIX_FORMAT "Note (%s): "
|
||
|
#define CLOG_DEBUG_PREFIX "Debug: "
|
||
|
#define CLOG_DEBUG_PREFIX_LENGTH 7
|
||
|
#define CLOG_DEBUG_PREFIX_FORMAT "Debug (%s): "
|
||
|
#define CLOG_SUFFIX_LENGTH 1
|
||
|
|
||
|
void clog_vlog_fatal(const char* module, const char* format, va_list args) {
|
||
|
#if defined(__ANDROID__) && !CLOG_LOG_TO_STDIO
|
||
|
__android_log_vprint(ANDROID_LOG_FATAL, module, format, args);
|
||
|
#else
|
||
|
char stack_buffer[CLOG_STACK_BUFFER_SIZE];
|
||
|
char* heap_buffer = NULL;
|
||
|
char* out_buffer = &stack_buffer[0];
|
||
|
|
||
|
/* The first call to vsnprintf will clobber args, thus need a copy in case a second vsnprintf call is needed */
|
||
|
va_list args_copy;
|
||
|
va_copy(args_copy, args);
|
||
|
|
||
|
int prefix_chars = CLOG_FATAL_PREFIX_LENGTH;
|
||
|
if (module == NULL) {
|
||
|
memcpy(stack_buffer, CLOG_FATAL_PREFIX, CLOG_FATAL_PREFIX_LENGTH);
|
||
|
} else {
|
||
|
prefix_chars = snprintf(stack_buffer, CLOG_STACK_BUFFER_SIZE, CLOG_FATAL_PREFIX_FORMAT, module);
|
||
|
if (prefix_chars < 0) {
|
||
|
/* Format error in prefix (possible if prefix is modified): skip prefix and continue as if nothing happened. */
|
||
|
prefix_chars = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int format_chars;
|
||
|
if (prefix_chars + CLOG_SUFFIX_LENGTH >= CLOG_STACK_BUFFER_SIZE) {
|
||
|
/*
|
||
|
* Prefix + suffix alone would overflow the on-stack buffer, thus need to use on-heap buffer.
|
||
|
* Do not even try to format the string into on-stack buffer.
|
||
|
*/
|
||
|
format_chars = vsnprintf(NULL, 0, format, args);
|
||
|
} else {
|
||
|
format_chars =
|
||
|
vsnprintf(
|
||
|
&stack_buffer[prefix_chars],
|
||
|
CLOG_STACK_BUFFER_SIZE - prefix_chars - CLOG_SUFFIX_LENGTH,
|
||
|
format,
|
||
|
args);
|
||
|
}
|
||
|
if (format_chars < 0) {
|
||
|
/* Format error in the message: silently ignore this particular message. */
|
||
|
goto cleanup;
|
||
|
}
|
||
|
if (prefix_chars + format_chars + CLOG_SUFFIX_LENGTH > CLOG_STACK_BUFFER_SIZE) {
|
||
|
/* Allocate a buffer on heap, and vsnprintf to this buffer */
|
||
|
heap_buffer = malloc(prefix_chars + format_chars + CLOG_SUFFIX_LENGTH);
|
||
|
if (heap_buffer == NULL) {
|
||
|
goto cleanup;
|
||
|
}
|
||
|
|
||
|
if (prefix_chars > CLOG_STACK_BUFFER_SIZE) {
|
||
|
/* Prefix didn't fit into on-stack buffer, re-format it again to on-heap buffer */
|
||
|
snprintf(heap_buffer, prefix_chars + 1 /* for '\0'-terminator */, CLOG_FATAL_PREFIX_FORMAT, module);
|
||
|
} else {
|
||
|
/* Copy pre-formatted prefix from on-stack buffer to on-heap buffer */
|
||
|
memcpy(heap_buffer, stack_buffer, prefix_chars);
|
||
|
}
|
||
|
vsnprintf(heap_buffer + prefix_chars, format_chars + CLOG_SUFFIX_LENGTH, format, args_copy);
|
||
|
out_buffer = heap_buffer;
|
||
|
}
|
||
|
out_buffer[prefix_chars + format_chars] = '\n';
|
||
|
#ifdef _WIN32
|
||
|
DWORD bytes_written;
|
||
|
WriteFile(
|
||
|
GetStdHandle(STD_ERROR_HANDLE),
|
||
|
out_buffer, prefix_chars + format_chars + CLOG_SUFFIX_LENGTH,
|
||
|
&bytes_written, NULL);
|
||
|
#else
|
||
|
write(STDERR_FILENO, out_buffer, prefix_chars + format_chars + CLOG_SUFFIX_LENGTH);
|
||
|
#endif
|
||
|
|
||
|
cleanup:
|
||
|
free(heap_buffer);
|
||
|
va_end(args_copy);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
void clog_vlog_error(const char* module, const char* format, va_list args) {
|
||
|
#if defined(__ANDROID__) && !CLOG_LOG_TO_STDIO
|
||
|
__android_log_vprint(ANDROID_LOG_ERROR, module, format, args);
|
||
|
#else
|
||
|
char stack_buffer[CLOG_STACK_BUFFER_SIZE];
|
||
|
char* heap_buffer = NULL;
|
||
|
char* out_buffer = &stack_buffer[0];
|
||
|
|
||
|
/* The first call to vsnprintf will clobber args, thus need a copy in case a second vsnprintf call is needed */
|
||
|
va_list args_copy;
|
||
|
va_copy(args_copy, args);
|
||
|
|
||
|
int prefix_chars = CLOG_ERROR_PREFIX_LENGTH;
|
||
|
if (module == NULL) {
|
||
|
memcpy(stack_buffer, CLOG_ERROR_PREFIX, CLOG_ERROR_PREFIX_LENGTH);
|
||
|
} else {
|
||
|
prefix_chars = snprintf(stack_buffer, CLOG_STACK_BUFFER_SIZE, CLOG_ERROR_PREFIX_FORMAT, module);
|
||
|
if (prefix_chars < 0) {
|
||
|
/* Format error in prefix (possible if prefix is modified): skip prefix and continue as if nothing happened. */
|
||
|
prefix_chars = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int format_chars;
|
||
|
if (prefix_chars + CLOG_SUFFIX_LENGTH >= CLOG_STACK_BUFFER_SIZE) {
|
||
|
/*
|
||
|
* Prefix + suffix alone would overflow the on-stack buffer, thus need to use on-heap buffer.
|
||
|
* Do not even try to format the string into on-stack buffer.
|
||
|
*/
|
||
|
format_chars = vsnprintf(NULL, 0, format, args);
|
||
|
} else {
|
||
|
format_chars =
|
||
|
vsnprintf(
|
||
|
&stack_buffer[prefix_chars],
|
||
|
CLOG_STACK_BUFFER_SIZE - prefix_chars - CLOG_SUFFIX_LENGTH,
|
||
|
format,
|
||
|
args);
|
||
|
}
|
||
|
if (format_chars < 0) {
|
||
|
/* Format error in the message: silently ignore this particular message. */
|
||
|
goto cleanup;
|
||
|
}
|
||
|
if (prefix_chars + format_chars + CLOG_SUFFIX_LENGTH > CLOG_STACK_BUFFER_SIZE) {
|
||
|
/* Allocate a buffer on heap, and vsnprintf to this buffer */
|
||
|
heap_buffer = malloc(prefix_chars + format_chars + CLOG_SUFFIX_LENGTH);
|
||
|
if (heap_buffer == NULL) {
|
||
|
goto cleanup;
|
||
|
}
|
||
|
|
||
|
if (prefix_chars > CLOG_STACK_BUFFER_SIZE) {
|
||
|
/* Prefix didn't fit into on-stack buffer, re-format it again to on-heap buffer */
|
||
|
snprintf(heap_buffer, prefix_chars + 1 /* for '\0'-terminator */, CLOG_ERROR_PREFIX_FORMAT, module);
|
||
|
} else {
|
||
|
/* Copy pre-formatted prefix from on-stack buffer to on-heap buffer */
|
||
|
memcpy(heap_buffer, stack_buffer, prefix_chars);
|
||
|
}
|
||
|
vsnprintf(heap_buffer + prefix_chars, format_chars + CLOG_SUFFIX_LENGTH, format, args_copy);
|
||
|
out_buffer = heap_buffer;
|
||
|
}
|
||
|
out_buffer[prefix_chars + format_chars] = '\n';
|
||
|
#ifdef _WIN32
|
||
|
DWORD bytes_written;
|
||
|
WriteFile(
|
||
|
GetStdHandle(STD_ERROR_HANDLE),
|
||
|
out_buffer, prefix_chars + format_chars + CLOG_SUFFIX_LENGTH,
|
||
|
&bytes_written, NULL);
|
||
|
#else
|
||
|
write(STDERR_FILENO, out_buffer, prefix_chars + format_chars + CLOG_SUFFIX_LENGTH);
|
||
|
#endif
|
||
|
|
||
|
cleanup:
|
||
|
free(heap_buffer);
|
||
|
va_end(args_copy);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
void clog_vlog_warning(const char* module, const char* format, va_list args) {
|
||
|
#if defined(__ANDROID__) && !CLOG_LOG_TO_STDIO
|
||
|
__android_log_vprint(ANDROID_LOG_WARN, module, format, args);
|
||
|
#else
|
||
|
char stack_buffer[CLOG_STACK_BUFFER_SIZE];
|
||
|
char* heap_buffer = NULL;
|
||
|
char* out_buffer = &stack_buffer[0];
|
||
|
|
||
|
/* The first call to vsnprintf will clobber args, thus need a copy in case a second vsnprintf call is needed */
|
||
|
va_list args_copy;
|
||
|
va_copy(args_copy, args);
|
||
|
|
||
|
int prefix_chars = CLOG_WARNING_PREFIX_LENGTH;
|
||
|
if (module == NULL) {
|
||
|
memcpy(stack_buffer, CLOG_WARNING_PREFIX, CLOG_WARNING_PREFIX_LENGTH);
|
||
|
} else {
|
||
|
prefix_chars = snprintf(stack_buffer, CLOG_STACK_BUFFER_SIZE, CLOG_WARNING_PREFIX_FORMAT, module);
|
||
|
if (prefix_chars < 0) {
|
||
|
/* Format error in prefix (possible if prefix is modified): skip prefix and continue as if nothing happened. */
|
||
|
prefix_chars = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int format_chars;
|
||
|
if (prefix_chars + CLOG_SUFFIX_LENGTH >= CLOG_STACK_BUFFER_SIZE) {
|
||
|
/*
|
||
|
* Prefix + suffix alone would overflow the on-stack buffer, thus need to use on-heap buffer.
|
||
|
* Do not even try to format the string into on-stack buffer.
|
||
|
*/
|
||
|
format_chars = vsnprintf(NULL, 0, format, args);
|
||
|
} else {
|
||
|
format_chars =
|
||
|
vsnprintf(
|
||
|
&stack_buffer[prefix_chars],
|
||
|
CLOG_STACK_BUFFER_SIZE - prefix_chars - CLOG_SUFFIX_LENGTH,
|
||
|
format,
|
||
|
args);
|
||
|
}
|
||
|
if (format_chars < 0) {
|
||
|
/* Format error in the message: silently ignore this particular message. */
|
||
|
goto cleanup;
|
||
|
}
|
||
|
if (prefix_chars + format_chars + CLOG_SUFFIX_LENGTH > CLOG_STACK_BUFFER_SIZE) {
|
||
|
/* Allocate a buffer on heap, and vsnprintf to this buffer */
|
||
|
heap_buffer = malloc(prefix_chars + format_chars + CLOG_SUFFIX_LENGTH);
|
||
|
if (heap_buffer == NULL) {
|
||
|
goto cleanup;
|
||
|
}
|
||
|
|
||
|
if (prefix_chars > CLOG_STACK_BUFFER_SIZE) {
|
||
|
/* Prefix didn't fit into on-stack buffer, re-format it again to on-heap buffer */
|
||
|
snprintf(heap_buffer, prefix_chars + 1 /* for '\0'-terminator */, CLOG_WARNING_PREFIX_FORMAT, module);
|
||
|
} else {
|
||
|
/* Copy pre-formatted prefix from on-stack buffer to on-heap buffer */
|
||
|
memcpy(heap_buffer, stack_buffer, prefix_chars);
|
||
|
}
|
||
|
vsnprintf(heap_buffer + prefix_chars, format_chars + CLOG_SUFFIX_LENGTH, format, args_copy);
|
||
|
out_buffer = heap_buffer;
|
||
|
}
|
||
|
out_buffer[prefix_chars + format_chars] = '\n';
|
||
|
#ifdef _WIN32
|
||
|
DWORD bytes_written;
|
||
|
WriteFile(
|
||
|
GetStdHandle(STD_ERROR_HANDLE),
|
||
|
out_buffer, prefix_chars + format_chars + CLOG_SUFFIX_LENGTH,
|
||
|
&bytes_written, NULL);
|
||
|
#else
|
||
|
write(STDERR_FILENO, out_buffer, prefix_chars + format_chars + CLOG_SUFFIX_LENGTH);
|
||
|
#endif
|
||
|
|
||
|
cleanup:
|
||
|
free(heap_buffer);
|
||
|
va_end(args_copy);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
void clog_vlog_info(const char* module, const char* format, va_list args) {
|
||
|
#if defined(__ANDROID__) && !CLOG_LOG_TO_STDIO
|
||
|
__android_log_vprint(ANDROID_LOG_INFO, module, format, args);
|
||
|
#else
|
||
|
char stack_buffer[CLOG_STACK_BUFFER_SIZE];
|
||
|
char* heap_buffer = NULL;
|
||
|
char* out_buffer = &stack_buffer[0];
|
||
|
|
||
|
/* The first call to vsnprintf will clobber args, thus need a copy in case a second vsnprintf call is needed */
|
||
|
va_list args_copy;
|
||
|
va_copy(args_copy, args);
|
||
|
|
||
|
int prefix_chars = CLOG_INFO_PREFIX_LENGTH;
|
||
|
if (module == NULL) {
|
||
|
memcpy(stack_buffer, CLOG_INFO_PREFIX, CLOG_INFO_PREFIX_LENGTH);
|
||
|
} else {
|
||
|
prefix_chars = snprintf(stack_buffer, CLOG_STACK_BUFFER_SIZE, CLOG_INFO_PREFIX_FORMAT, module);
|
||
|
if (prefix_chars < 0) {
|
||
|
/* Format error in prefix (possible if prefix is modified): skip prefix and continue as if nothing happened. */
|
||
|
prefix_chars = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int format_chars;
|
||
|
if (prefix_chars + CLOG_SUFFIX_LENGTH >= CLOG_STACK_BUFFER_SIZE) {
|
||
|
/*
|
||
|
* Prefix + suffix alone would overflow the on-stack buffer, thus need to use on-heap buffer.
|
||
|
* Do not even try to format the string into on-stack buffer.
|
||
|
*/
|
||
|
format_chars = vsnprintf(NULL, 0, format, args);
|
||
|
} else {
|
||
|
format_chars =
|
||
|
vsnprintf(
|
||
|
&stack_buffer[prefix_chars],
|
||
|
CLOG_STACK_BUFFER_SIZE - prefix_chars - CLOG_SUFFIX_LENGTH,
|
||
|
format,
|
||
|
args);
|
||
|
}
|
||
|
if (format_chars < 0) {
|
||
|
/* Format error in the message: silently ignore this particular message. */
|
||
|
goto cleanup;
|
||
|
}
|
||
|
if (prefix_chars + format_chars + CLOG_SUFFIX_LENGTH > CLOG_STACK_BUFFER_SIZE) {
|
||
|
/* Allocate a buffer on heap, and vsnprintf to this buffer */
|
||
|
heap_buffer = malloc(prefix_chars + format_chars + CLOG_SUFFIX_LENGTH);
|
||
|
if (heap_buffer == NULL) {
|
||
|
goto cleanup;
|
||
|
}
|
||
|
|
||
|
if (prefix_chars > CLOG_STACK_BUFFER_SIZE) {
|
||
|
/* Prefix didn't fit into on-stack buffer, re-format it again to on-heap buffer */
|
||
|
snprintf(heap_buffer, prefix_chars + 1 /* for '\0'-terminator */, CLOG_INFO_PREFIX_FORMAT, module);
|
||
|
} else {
|
||
|
/* Copy pre-formatted prefix from on-stack buffer to on-heap buffer */
|
||
|
memcpy(heap_buffer, stack_buffer, prefix_chars);
|
||
|
}
|
||
|
vsnprintf(heap_buffer + prefix_chars, format_chars + CLOG_SUFFIX_LENGTH, format, args_copy);
|
||
|
out_buffer = heap_buffer;
|
||
|
}
|
||
|
out_buffer[prefix_chars + format_chars] = '\n';
|
||
|
#ifdef _WIN32
|
||
|
DWORD bytes_written;
|
||
|
WriteFile(
|
||
|
GetStdHandle(STD_OUTPUT_HANDLE),
|
||
|
out_buffer, prefix_chars + format_chars + CLOG_SUFFIX_LENGTH,
|
||
|
&bytes_written, NULL);
|
||
|
#else
|
||
|
write(STDOUT_FILENO, out_buffer, prefix_chars + format_chars + CLOG_SUFFIX_LENGTH);
|
||
|
#endif
|
||
|
|
||
|
cleanup:
|
||
|
free(heap_buffer);
|
||
|
va_end(args_copy);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
void clog_vlog_debug(const char* module, const char* format, va_list args) {
|
||
|
#if defined(__ANDROID__) && !CLOG_LOG_TO_STDIO
|
||
|
__android_log_vprint(ANDROID_LOG_DEBUG, module, format, args);
|
||
|
#else
|
||
|
char stack_buffer[CLOG_STACK_BUFFER_SIZE];
|
||
|
char* heap_buffer = NULL;
|
||
|
char* out_buffer = &stack_buffer[0];
|
||
|
|
||
|
/* The first call to vsnprintf will clobber args, thus need a copy in case a second vsnprintf call is needed */
|
||
|
va_list args_copy;
|
||
|
va_copy(args_copy, args);
|
||
|
|
||
|
int prefix_chars = CLOG_DEBUG_PREFIX_LENGTH;
|
||
|
if (module == NULL) {
|
||
|
memcpy(stack_buffer, CLOG_DEBUG_PREFIX, CLOG_DEBUG_PREFIX_LENGTH);
|
||
|
} else {
|
||
|
prefix_chars = snprintf(stack_buffer, CLOG_STACK_BUFFER_SIZE, CLOG_DEBUG_PREFIX_FORMAT, module);
|
||
|
if (prefix_chars < 0) {
|
||
|
/* Format error in prefix (possible if prefix is modified): skip prefix and continue as if nothing happened. */
|
||
|
prefix_chars = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int format_chars;
|
||
|
if (prefix_chars + CLOG_SUFFIX_LENGTH >= CLOG_STACK_BUFFER_SIZE) {
|
||
|
/*
|
||
|
* Prefix + suffix alone would overflow the on-stack buffer, thus need to use on-heap buffer.
|
||
|
* Do not even try to format the string into on-stack buffer.
|
||
|
*/
|
||
|
format_chars = vsnprintf(NULL, 0, format, args);
|
||
|
} else {
|
||
|
format_chars =
|
||
|
vsnprintf(
|
||
|
&stack_buffer[prefix_chars],
|
||
|
CLOG_STACK_BUFFER_SIZE - prefix_chars - CLOG_SUFFIX_LENGTH,
|
||
|
format,
|
||
|
args);
|
||
|
}
|
||
|
if (format_chars < 0) {
|
||
|
/* Format error in the message: silently ignore this particular message. */
|
||
|
goto cleanup;
|
||
|
}
|
||
|
if (prefix_chars + format_chars + CLOG_SUFFIX_LENGTH > CLOG_STACK_BUFFER_SIZE) {
|
||
|
/* Allocate a buffer on heap, and vsnprintf to this buffer */
|
||
|
heap_buffer = malloc(prefix_chars + format_chars + CLOG_SUFFIX_LENGTH);
|
||
|
if (heap_buffer == NULL) {
|
||
|
goto cleanup;
|
||
|
}
|
||
|
|
||
|
if (prefix_chars > CLOG_STACK_BUFFER_SIZE) {
|
||
|
/* Prefix didn't fit into on-stack buffer, re-format it again to on-heap buffer */
|
||
|
snprintf(heap_buffer, prefix_chars + 1 /* for '\0'-terminator */, CLOG_DEBUG_PREFIX_FORMAT, module);
|
||
|
} else {
|
||
|
/* Copy pre-formatted prefix from on-stack buffer to on-heap buffer */
|
||
|
memcpy(heap_buffer, stack_buffer, prefix_chars);
|
||
|
}
|
||
|
vsnprintf(heap_buffer + prefix_chars, format_chars + CLOG_SUFFIX_LENGTH, format, args_copy);
|
||
|
out_buffer = heap_buffer;
|
||
|
}
|
||
|
out_buffer[prefix_chars + format_chars] = '\n';
|
||
|
#ifdef _WIN32
|
||
|
DWORD bytes_written;
|
||
|
WriteFile(
|
||
|
GetStdHandle(STD_OUTPUT_HANDLE),
|
||
|
out_buffer, prefix_chars + format_chars + CLOG_SUFFIX_LENGTH,
|
||
|
&bytes_written, NULL);
|
||
|
#else
|
||
|
write(STDOUT_FILENO, out_buffer, prefix_chars + format_chars + CLOG_SUFFIX_LENGTH);
|
||
|
#endif
|
||
|
|
||
|
cleanup:
|
||
|
free(heap_buffer);
|
||
|
va_end(args_copy);
|
||
|
#endif
|
||
|
}
|