#include "c4/error.hpp"
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#define C4_LOGF_ERR(...) fprintf(stderr, __VA_ARGS__); fflush(stderr)
#define C4_LOGF_WARN(...) fprintf(stderr, __VA_ARGS__); fflush(stderr)
#define C4_LOGP(msg, ...) printf(msg)
#if defined(C4_XBOX) || (defined(C4_WIN) && defined(C4_MSVC))
# include "c4/windows.hpp"
#elif defined(C4_PS4)
# include <libdbg.h>
#elif defined(C4_UNIX) || defined(C4_LINUX)
# include <sys/stat.h>
# include <cstring>
# include <fcntl.h>
#elif defined(C4_MACOS) || defined(C4_IOS)
# include <assert.h>
# include <stdbool.h>
# include <sys/types.h>
# include <sys/sysctl.h>
#endif
#if defined(C4_UNIX) || defined(C4_LINUX) || defined(C4_MACOS) || defined(C4_IOS)
# include <unistd.h>
#endif
#if defined(C4_EXCEPTIONS_ENABLED) && defined(C4_ERROR_THROWS_EXCEPTION)
# include <exception>
#endif
#ifdef __clang__
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wformat-nonliteral"
# pragma clang diagnostic ignored "-Wold-style-cast"
#elif defined(__GNUC__)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wformat-nonliteral"
# pragma GCC diagnostic ignored "-Wold-style-cast"
#endif
namespace c4 {
static error_flags s_error_flags = ON_ERROR_DEFAULTS;
static error_callback_type s_error_callback = nullptr;
error_flags get_error_flags()
{
return s_error_flags;
}
void set_error_flags(error_flags flags)
{
s_error_flags = flags;
}
error_callback_type get_error_callback()
{
return s_error_callback;
}
void set_error_callback(error_callback_type cb)
{
s_error_callback = cb;
}
void handle_error(srcloc where, const char *fmt, ...)
{
char buf[1024];
size_t msglen = 0;
if(s_error_flags & (ON_ERROR_LOG|ON_ERROR_CALLBACK))
{
va_list args;
va_start(args, fmt);
int ilen = vsnprintf(buf, sizeof(buf), fmt, args);
va_end(args);
msglen = ilen >= 0 && ilen < (int)sizeof(buf) ? static_cast<size_t>(ilen) : sizeof(buf)-1;
}
if(s_error_flags & ON_ERROR_LOG)
{
C4_LOGF_ERR("\n");
#if defined(C4_ERROR_SHOWS_FILELINE) && defined(C4_ERROR_SHOWS_FUNC)
C4_LOGF_ERR("%s:%d: ERROR: %s\n", where.file, where.line, buf);
C4_LOGF_ERR("%s:%d: ERROR here: %s\n", where.file, where.line, where.func);
#elif defined(C4_ERROR_SHOWS_FILELINE)
C4_LOGF_ERR("%s:%d: ERROR: %s\n", where.file, where.line, buf);
#elif ! defined(C4_ERROR_SHOWS_FUNC)
C4_LOGF_ERR("ERROR: %s\n", buf);
#endif
}
if(s_error_flags & ON_ERROR_CALLBACK)
{
if(s_error_callback)
{
s_error_callback(buf, msglen);
}
}
if(s_error_flags & ON_ERROR_ABORT)
{
abort();
}
if(s_error_flags & ON_ERROR_THROW)
{
#if defined(C4_EXCEPTIONS_ENABLED) && defined(C4_ERROR_THROWS_EXCEPTION)
throw Exception(buf);
#else
abort();
#endif
}
}
void handle_warning(srcloc where, const char *fmt, ...)
{
va_list args;
char buf[1024];
va_start(args, fmt);
vsnprintf(buf, sizeof(buf), fmt, args);
va_end(args);
C4_LOGF_WARN("\n");
#if defined(C4_ERROR_SHOWS_FILELINE) && defined(C4_ERROR_SHOWS_FUNC)
C4_LOGF_WARN("%s:%d: WARNING: %s\n", where.file, where.line, buf);
C4_LOGF_WARN("%s:%d: WARNING: here: %s\n", where.file, where.line, where.func);
#elif defined(C4_ERROR_SHOWS_FILELINE)
C4_LOGF_WARN("%s:%d: WARNING: %s\n", where.file, where.line, buf);
#elif ! defined(C4_ERROR_SHOWS_FUNC)
C4_LOGF_WARN("WARNING: %s\n", buf);
#endif
}
bool is_debugger_attached()
{
#if defined(C4_UNIX) || defined(C4_LINUX)
static bool first_call = true;
static bool first_call_result = false;
if(first_call)
{
first_call = false;
char buf[1024] = "";
int status_fd = open("/proc/self/status", O_RDONLY);
if (status_fd == -1)
{
return 0;
}
ssize_t num_read = ::read(status_fd, buf, sizeof(buf));
if (num_read > 0)
{
static const char TracerPid[] = "TracerPid:";
char *tracer_pid;
if(num_read < 1024)
{
buf[num_read] = 0;
}
tracer_pid = strstr(buf, TracerPid);
if (tracer_pid)
{
first_call_result = !!::atoi(tracer_pid + sizeof(TracerPid) - 1);
}
}
}
return first_call_result;
#elif defined(C4_PS4)
return (sceDbgIsDebuggerAttached() != 0);
#elif defined(C4_XBOX) || (defined(C4_WIN) && defined(C4_MSVC))
return IsDebuggerPresent() != 0;
#elif defined(C4_MACOS) || defined(C4_IOS)
int junk;
int mib[4];
struct kinfo_proc info;
size_t size;
info.kp_proc.p_flag = 0;
mib[0] = CTL_KERN;
mib[1] = KERN_PROC;
mib[2] = KERN_PROC_PID;
mib[3] = getpid();
size = sizeof(info);
junk = sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0);
assert(junk == 0);
return ((info.kp_proc.p_flag & P_TRACED) != 0);
#else
return false;
#endif
}
}
#ifdef __clang__
# pragma clang diagnostic pop
#elif defined(__GNUC__)
# pragma GCC diagnostic pop
#endif