Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/intern
diff options
context:
space:
mode:
authorCampbell Barton <ideasman42@gmail.com>2018-04-01 12:03:25 +0300
committerCampbell Barton <ideasman42@gmail.com>2018-04-01 12:03:25 +0300
commitb65ea517eb932bde950bde51979c6a3fd258efa8 (patch)
tree8f3a291a7e1778bb3af45cdb1d98a621efbd1a7d /intern
parent916c91bd08933d596eaca3e369467daf7964612e (diff)
parent473f17b3d557adbb06b89e0a186be48a0129086d (diff)
Merge branch 'master' into blender2.8
- Undo that changes modes currently asserts, since undo is now screen data. Most likely we will change how object mode and workspaces work since it's not practical/maintainable at the moment. - Removed view_layer from particle settings (wasn't needed and complicated undo).
Diffstat (limited to 'intern')
-rw-r--r--intern/CMakeLists.txt1
-rw-r--r--intern/clog/CLG_log.h211
-rw-r--r--intern/clog/CMakeLists.txt34
-rw-r--r--intern/clog/clog.c583
4 files changed, 829 insertions, 0 deletions
diff --git a/intern/CMakeLists.txt b/intern/CMakeLists.txt
index af3b9296077..4b3ccfc808a 100644
--- a/intern/CMakeLists.txt
+++ b/intern/CMakeLists.txt
@@ -24,6 +24,7 @@
# ***** END GPL LICENSE BLOCK *****
# add_subdirectory(atomic) # header only
+add_subdirectory(clog)
add_subdirectory(string)
add_subdirectory(ghost)
add_subdirectory(guardedalloc)
diff --git a/intern/clog/CLG_log.h b/intern/clog/CLG_log.h
new file mode 100644
index 00000000000..8afa9edd75b
--- /dev/null
+++ b/intern/clog/CLG_log.h
@@ -0,0 +1,211 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef __CLOG_H__
+#define __CLOG_H__
+
+/** \file clog/CLG_log.h
+ * \ingroup clog
+ *
+ * C Logging Library (clog)
+ * ========================
+ *
+ * Usage
+ * -----
+ *
+ * - `CLG_LOGREF_DECLARE_GLOBAL` macro to declare #CLG_LogRef pointers.
+ * - `CLOG_` prefixed macros for logging.
+ *
+ * Identifiers
+ * -----------
+ *
+ * #CLG_LogRef holds an identifier which defines the category of the logger.
+ *
+ * You can define and use identifiers as needed, logging will lazily initialize them.
+ *
+ * By convention lower case dot separated identifiers are used, eg:
+ * `module.sub_module`, this allows filtering by `module.*`,
+ * see #CLG_type_filter_include, #CLG_type_filter_exclude
+ *
+ * There is currently no functionality to remove a category once it's created.
+ *
+ * Severity
+ * --------
+ *
+ * - `INFO`: Simply log events, uses verbosity levels to control how much information to show.
+ * - `WARN`: General warnings (which aren't necessary to show to users).
+ * - `ERROR`: An error we can recover from, should not happen.
+ * - `FATAL`: Similar to assert. This logs the message, then a stack trace and abort.
+ *
+ *
+ * Verbosity Level
+ * ---------------
+ *
+ * Usage:
+ *
+ * - 0: Always show (used for warnings, errors).
+ * Should never get in the way or become annoying.
+ *
+ * - 1: Top level module actions (eg: load a file, create a new window .. etc).
+ *
+ * - 2: Actions within a module (steps which compose an action, but don't flood output).
+ * Running a tool, full data recalculation.
+ *
+ * - 3: Detailed actions which may be of interest when debugging internal logic of a module
+ * These *may* flood the log with details.
+ *
+ * - 4+: May be used for more details than 3, should be avoided but not prevented.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#ifdef __GNUC__
+# define _CLOG_ATTR_NONNULL(args ...) __attribute__((nonnull(args)))
+#else
+# define _CLOG_ATTR_NONNULL(...)
+#endif
+
+#ifdef __GNUC__
+# define _CLOG_ATTR_PRINTF_FORMAT(format_param, dots_param) __attribute__((format(printf, format_param, dots_param)))
+#else
+# define _CLOG_ATTR_PRINTF_FORMAT(format_param, dots_param)
+#endif
+
+#if defined(_MSC_VER) && !defined(__func__)
+# define __func__MSVC
+# define __func__ __FUNCTION__
+#endif
+
+#define STRINGIFY_ARG(x) "" #x
+#define STRINGIFY_APPEND(a, b) "" a #b
+#define STRINGIFY(x) STRINGIFY_APPEND("", x)
+
+struct CLogContext;
+
+/* Don't typedef enums. */
+enum CLG_LogFlag {
+ CLG_FLAG_USE = (1 << 0),
+};
+
+enum CLG_Severity {
+ CLG_SEVERITY_INFO = 0,
+ CLG_SEVERITY_WARN,
+ CLG_SEVERITY_ERROR,
+ CLG_SEVERITY_FATAL,
+};
+#define CLG_SEVERITY_LEN (CLG_SEVERITY_FATAL + 1)
+
+/* Each logger ID has one of these. */
+typedef struct CLG_LogType {
+ struct CLG_LogType *next;
+ char identifier[64];
+ /** FILE output. */
+ struct CLogContext *ctx;
+ /** Control behavior. */
+ int level;
+ enum CLG_LogFlag flag;
+} CLG_LogType;
+
+typedef struct CLG_LogRef {
+ const char *identifier;
+ CLG_LogType *type;
+} CLG_LogRef;
+
+void CLG_log_str(
+ CLG_LogType *lg, enum CLG_Severity severity, const char *file_line, const char *fn,
+ const char *message)
+ _CLOG_ATTR_NONNULL(1, 3, 4, 5);
+void CLG_logf(
+ CLG_LogType *lg, enum CLG_Severity severity, const char *file_line, const char *fn,
+ const char *format, ...)
+ _CLOG_ATTR_NONNULL(1, 3, 4, 5) _CLOG_ATTR_PRINTF_FORMAT(5, 6);
+
+/* Main initializer and distructor (per session, not logger). */
+void CLG_init(void);
+void CLG_exit(void);
+
+void CLG_output_set(void *file_handle);
+void CLG_output_use_basename_set(int value);
+void CLG_fatal_fn_set(void (*fatal_fn)(void *file_handle));
+
+void CLG_type_filter_include(const char *type_filter, int type_filter_len);
+void CLG_type_filter_exclude(const char *type_filter, int type_filter_len);
+
+void CLG_logref_init(CLG_LogRef *clg_ref);
+
+/** Declare outside function, declare as extern in header. */
+#define CLG_LOGREF_DECLARE_GLOBAL(var, id) \
+ static CLG_LogRef _static_ ## var = {id}; \
+ CLG_LogRef *var = &_static_ ## var
+
+/** Initialize struct once. */
+#define CLOG_ENSURE(clg_ref) \
+ ((clg_ref)->type ? (clg_ref)->type : (CLG_logref_init(clg_ref), (clg_ref)->type))
+
+#define CLOG_AT_SEVERITY(clg_ref, severity, verbose_level, ...) { \
+ CLG_LogType *_lg_ty = CLOG_ENSURE(clg_ref); \
+ if (((_lg_ty->flag & CLG_FLAG_USE) && (_lg_ty->level >= verbose_level)) || (severity >= CLG_SEVERITY_WARN)) { \
+ CLG_logf(_lg_ty, severity, __FILE__ ":" STRINGIFY(__LINE__), __func__, __VA_ARGS__); \
+ } \
+} ((void)0)
+
+#define CLOG_STR_AT_SEVERITY(clg_ref, severity, verbose_level, str) { \
+ CLG_LogType *_lg_ty = CLOG_ENSURE(clg_ref); \
+ if (((_lg_ty->flag & CLG_FLAG_USE) && (_lg_ty->level >= verbose_level)) || (severity >= CLG_SEVERITY_WARN)) { \
+ CLG_log_str(lg, severity, __FILE__ ":" STRINGIFY(__LINE__), __func__, str); \
+ } \
+} ((void)0)
+
+#define CLOG_STR_AT_SEVERITY_N(clg_ref, severity, verbose_level, str) { \
+ CLG_LogType *_lg_ty = CLOG_ENSURE(clg_ref); \
+ if (((_lg_ty->flag & CLG_FLAG_USE) && (_lg_ty->level >= verbose_level)) || (severity >= CLG_SEVERITY_WARN)) { \
+ const char *_str = str; \
+ CLG_log_str(_lg_ty, severity, __FILE__ ":" STRINGIFY(__LINE__), __func__, _str); \
+ MEM_freeN((void *)_str); \
+ } \
+} ((void)0)
+
+#define CLOG_INFO(clg_ref, level, ...) CLOG_AT_SEVERITY(clg_ref, CLG_SEVERITY_INFO, level, __VA_ARGS__)
+#define CLOG_WARN(clg_ref, ...) CLOG_AT_SEVERITY(clg_ref, CLG_SEVERITY_WARN, 0, __VA_ARGS__)
+#define CLOG_ERROR(clg_ref, ...) CLOG_AT_SEVERITY(clg_ref, CLG_SEVERITY_ERROR, 0, __VA_ARGS__)
+#define CLOG_FATAL(clg_ref, ...) CLOG_AT_SEVERITY(clg_ref, CLG_SEVERITY_FATAL, 0, __VA_ARGS__)
+
+#define CLOG_STR_INFO(clg_ref, level, ...) CLOG_STR_AT_SEVERITY(clg_ref, CLG_SEVERITY_INFO, level, __VA_ARGS__)
+#define CLOG_STR_WARN(clg_ref, ...) CLOG_STR_AT_SEVERITY(clg_ref, CLG_SEVERITY_WARN, 0, __VA_ARGS__)
+#define CLOG_STR_ERROR(clg_ref, ...) CLOG_STR_AT_SEVERITY(clg_ref, CLG_SEVERITY_ERROR, 0, __VA_ARGS__)
+#define CLOG_STR_FATAL(clg_ref, ...) CLOG_STR_AT_SEVERITY(clg_ref, CLG_SEVERITY_FATAL, 0, __VA_ARGS__)
+
+/* Allocated string which is immediately freed. */
+#define CLOG_STR_INFO_N(clg_ref, level, ...) CLOG_STR_AT_SEVERITY_N(clg_ref, CLG_SEVERITY_INFO, level, __VA_ARGS__)
+#define CLOG_STR_WARN_N(clg_ref, ...) CLOG_STR_AT_SEVERITY_N(clg_ref, CLG_SEVERITY_WARN, 0, __VA_ARGS__)
+#define CLOG_STR_ERROR_N(clg_ref, ...) CLOG_STR_AT_SEVERITY_N(clg_ref, CLG_SEVERITY_ERROR, 0, __VA_ARGS__)
+#define CLOG_STR_FATAL_N(clg_ref, ...) CLOG_STR_AT_SEVERITY_N(clg_ref, CLG_SEVERITY_FATAL, 0, __VA_ARGS__)
+
+#ifdef __func__MSVC
+# undef __func__MSVC
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CLOG_H__ */
diff --git a/intern/clog/CMakeLists.txt b/intern/clog/CMakeLists.txt
new file mode 100644
index 00000000000..8bf7b66f7ec
--- /dev/null
+++ b/intern/clog/CMakeLists.txt
@@ -0,0 +1,34 @@
+# ***** BEGIN GPL LICENSE BLOCK *****
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ***** END GPL LICENSE BLOCK *****
+
+set(INC
+ .
+ ../guardedalloc
+)
+
+set(INC_SYS
+
+)
+
+set(SRC
+ clog.c
+
+ CLG_log.h
+)
+
+blender_add_lib(bf_intern_clog "${SRC}" "${INC}" "${INC_SYS}")
diff --git a/intern/clog/clog.c b/intern/clog/clog.c
new file mode 100644
index 00000000000..dfbd34d341a
--- /dev/null
+++ b/intern/clog/clog.c
@@ -0,0 +1,583 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file clog/clog.c
+ * \ingroup clog
+ */
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <assert.h>
+
+/* For 'isatty' to check for color. */
+#if defined(__unix__)
+# include <unistd.h>
+#endif
+
+/* Only other dependency (could use regular malloc too). */
+#include "MEM_guardedalloc.h"
+
+/* own include. */
+#include "CLG_log.h"
+
+/* Local utility defines */
+#define STREQ(a, b) (strcmp(a, b) == 0)
+#define STREQLEN(a, b, n) (strncmp(a, b, n) == 0)
+
+#ifdef _WIN32
+# define PATHSEP_CHAR '\\'
+#else
+# define PATHSEP_CHAR '/'
+#endif
+
+/* -------------------------------------------------------------------- */
+/** \name Internal Types
+ * \{ */
+
+typedef struct CLG_IDFilter {
+ struct CLG_IDFilter *next;
+ /** Over alloc. */
+ char match[0];
+} CLG_IDFilter;
+
+typedef struct CLogContext {
+ /** Single linked list of types. */
+ CLG_LogType *types;
+ /* exclude, include filters. */
+ CLG_IDFilter *filters[2];
+ bool use_color;
+ bool use_basename;
+
+ /** Borrowed, not owned. */
+ FILE *output;
+
+ /** For new types. */
+ struct {
+ int level;
+ } default_type;
+
+ struct {
+ void (*fatal_fn)(void *file_handle);
+ } callbacks;
+} CLogContext;
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Mini Buffer Functionality
+ *
+ * Use so we can do a single call to write.
+ * \{ */
+
+#define CLOG_BUF_LEN_INIT 512
+
+typedef struct CLogStringBuf {
+ char *data;
+ uint len;
+ uint len_alloc;
+ bool is_alloc;
+} CLogStringBuf;
+
+static void clg_str_init(CLogStringBuf *cstr, char *buf_stack, uint buf_stack_len)
+{
+ cstr->data = buf_stack;
+ cstr->len_alloc = buf_stack_len;
+ cstr->len = 0;
+ cstr->is_alloc = false;
+}
+
+static void clg_str_free(CLogStringBuf *cstr)
+{
+ if (cstr->is_alloc) {
+ MEM_freeN(cstr->data);
+ }
+}
+
+static void clg_str_reserve(CLogStringBuf *cstr, const uint len)
+{
+ if (len > cstr->len_alloc) {
+ cstr->len_alloc *= 2;
+ if (len > cstr->len_alloc) {
+ cstr->len_alloc = len;
+ }
+
+ if (cstr->is_alloc) {
+ cstr->data = MEM_reallocN(cstr->data, cstr->len_alloc);
+ }
+ else {
+ /* Copy the static buffer. */
+ char *data = MEM_mallocN(cstr->len_alloc, __func__);
+ memcpy(data, cstr->data, cstr->len);
+ cstr->data = data;
+ cstr->is_alloc = true;
+ }
+ cstr->len_alloc = len;
+ }
+}
+
+static void clg_str_append_with_len(CLogStringBuf *cstr, const char *str, const uint len)
+{
+ uint len_next = cstr->len + len;
+ clg_str_reserve(cstr, len_next);
+ char *str_dst = cstr->data + cstr->len;
+ memcpy(str_dst, str, len);
+#if 0 /* no need. */
+ str_dst[len] = '\0';
+#endif
+ cstr->len = len_next;
+}
+
+static void clg_str_append(CLogStringBuf *cstr, const char *str)
+{
+ clg_str_append_with_len(cstr, str, strlen(str));
+}
+
+static void clg_str_vappendf(CLogStringBuf *cstr, const char *fmt, va_list args)
+{
+ /* Use limit because windows may use '-1' for a formatting error. */
+ const uint len_max = 65535;
+ uint len_avail = (cstr->len_alloc - cstr->len);
+ if (len_avail == 0) {
+ len_avail = CLOG_BUF_LEN_INIT;
+ clg_str_reserve(cstr, len_avail);
+ }
+ while (true) {
+ va_list args_cpy;
+ va_copy(args_cpy, args);
+ int retval = vsnprintf(cstr->data + cstr->len, len_avail, fmt, args_cpy);
+ va_end(args_cpy);
+ if (retval != -1) {
+ cstr->len += retval;
+ break;
+ }
+ else {
+ len_avail *= 2;
+ if (len_avail >= len_max) {
+ break;
+ }
+ clg_str_reserve(cstr, len_avail);
+ }
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Internal Utilities
+ * \{ */
+
+enum eCLogColor {
+ COLOR_DEFAULT,
+ COLOR_RED,
+ COLOR_GREEN,
+ COLOR_YELLOW,
+
+ COLOR_RESET,
+};
+#define COLOR_LEN (COLOR_RESET + 1)
+
+static const char *clg_color_table[COLOR_LEN] = {NULL};
+
+static void clg_color_table_init(bool use_color)
+{
+ for (int i = 0; i < COLOR_LEN; i++) {
+ clg_color_table[i] = "";
+ }
+ if (use_color) {
+#ifdef _WIN32
+ /* TODO */
+#else
+ clg_color_table[COLOR_DEFAULT] = "\033[1;37m";
+ clg_color_table[COLOR_RED] = "\033[1;31m";
+ clg_color_table[COLOR_GREEN] = "\033[1;32m";
+ clg_color_table[COLOR_YELLOW] = "\033[1;33m";
+ clg_color_table[COLOR_RESET] = "\033[0m";
+#endif
+ }
+}
+
+static const char *clg_severity_str[CLG_SEVERITY_LEN] = {
+ [CLG_SEVERITY_INFO] = "INFO",
+ [CLG_SEVERITY_WARN] = "WARN",
+ [CLG_SEVERITY_ERROR] = "ERROR",
+ [CLG_SEVERITY_FATAL] = "FATAL",
+};
+
+static const char *clg_severity_as_text(enum CLG_Severity severity)
+{
+ bool ok = (unsigned int)severity < CLG_SEVERITY_LEN;
+ assert(ok);
+ if (ok) {
+ return clg_severity_str[severity];
+ }
+ else {
+ return "INVALID_SEVERITY";
+ }
+}
+
+static enum eCLogColor clg_severity_to_color(enum CLG_Severity severity)
+{
+ assert((unsigned int)severity < CLG_SEVERITY_LEN);
+ enum eCLogColor color = COLOR_DEFAULT;
+ switch (severity) {
+ case CLG_SEVERITY_INFO:
+ color = COLOR_DEFAULT;
+ break;
+ case CLG_SEVERITY_WARN:
+ color = COLOR_YELLOW;
+ break;
+ case CLG_SEVERITY_ERROR:
+ case CLG_SEVERITY_FATAL:
+ color = COLOR_RED;
+ break;
+ default:
+ /* should never get here. */
+ assert(false);
+ }
+ return color;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Context Type Access
+ * \{ */
+
+/**
+ * Filter the indentifier based on very basic globbing.
+ * - `foo` exact match of `foo`.
+ * - `foo.bar` exact match for `foo.bar`
+ * - `foo.*` match for `foo` & `foo.bar` & `foo.bar.baz`
+ * - `*` matches everything.
+ */
+static bool clg_ctx_filter_check(CLogContext *ctx, const char *identifier)
+{
+ const int identifier_len = strlen(identifier);
+ for (uint i = 0; i < 2; i++) {
+ const CLG_IDFilter *flt = ctx->filters[i];
+ while (flt != NULL) {
+ const int len = strlen(flt->match);
+ if (STREQ(flt->match, "*") ||
+ ((len == identifier_len) && (STREQ(identifier, flt->match))))
+ {
+ return (bool)i;
+ }
+ if ((len >= 2) && (STREQLEN(".*", &flt->match[len - 2], 2))) {
+ if (((identifier_len == len - 2) && STREQLEN(identifier, flt->match, len - 2)) ||
+ ((identifier_len >= len - 1) && STREQLEN(identifier, flt->match, len - 1)))
+ {
+ return (bool)i;
+ }
+ }
+ flt = flt->next;
+ }
+ }
+ return false;
+}
+
+/**
+ * \note This should never be called per logging call.
+ * Searching is only to get an initial handle.
+ */
+static CLG_LogType *clg_ctx_type_find_by_name(CLogContext *ctx, const char *identifier)
+{
+ for (CLG_LogType *ty = ctx->types; ty; ty = ty->next) {
+ if (STREQ(identifier, ty->identifier)) {
+ return ty;
+ }
+ }
+ return NULL;
+}
+
+static CLG_LogType *clg_ctx_type_register(CLogContext *ctx, const char *identifier)
+{
+ assert(clg_ctx_type_find_by_name(ctx, identifier) == NULL);
+ CLG_LogType *ty = MEM_callocN(sizeof(*ty), __func__);
+ ty->next = ctx->types;
+ ctx->types = ty;
+ strncpy(ty->identifier, identifier, sizeof(ty->identifier) - 1);
+ ty->ctx = ctx;
+ ty->level = ctx->default_type.level;
+
+ if (clg_ctx_filter_check(ctx, ty->identifier)) {
+ ty->flag |= CLG_FLAG_USE;
+ }
+ return ty;
+}
+
+static void clg_ctx_fatal_action(CLogContext *ctx, FILE *file_handle)
+{
+ if (ctx->callbacks.fatal_fn != NULL) {
+ ctx->callbacks.fatal_fn(file_handle);
+ }
+ fflush(file_handle);
+ abort();
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Logging API
+ * \{ */
+
+static void write_severity(CLogStringBuf *cstr, enum CLG_Severity severity, bool use_color)
+{
+ assert((unsigned int)severity < CLG_SEVERITY_LEN);
+ if (use_color) {
+ enum eCLogColor color = clg_severity_to_color(severity);
+ clg_str_append(cstr, clg_color_table[color]);
+ clg_str_append(cstr, clg_severity_as_text(severity));
+ clg_str_append(cstr, clg_color_table[COLOR_RESET]);
+ }
+ else {
+ clg_str_append(cstr, clg_severity_as_text(severity));
+ }
+}
+
+static void write_type(CLogStringBuf *cstr, CLG_LogType *lg)
+{
+ clg_str_append(cstr, " (");
+ clg_str_append(cstr, lg->identifier);
+ clg_str_append(cstr, "): ");
+}
+
+static void write_file_line_fn(CLogStringBuf *cstr, const char *file_line, const char *fn, const bool use_basename)
+{
+ uint file_line_len = strlen(file_line);
+ if (use_basename) {
+ uint file_line_offset = file_line_len;
+ while (file_line_offset-- > 0) {
+ if (file_line[file_line_offset] == PATHSEP_CHAR) {
+ file_line_offset++;
+ break;
+ }
+ }
+ file_line += file_line_offset;
+ file_line_len -= file_line_offset;
+ }
+ clg_str_append_with_len(cstr, file_line, file_line_len);
+
+
+ clg_str_append(cstr, " ");
+ clg_str_append(cstr, fn);
+ clg_str_append(cstr, ": ");
+}
+
+void CLG_log_str(
+ CLG_LogType *lg, enum CLG_Severity severity, const char *file_line, const char *fn,
+ const char *message)
+{
+ CLogStringBuf cstr;
+ char cstr_stack_buf[CLOG_BUF_LEN_INIT];
+ clg_str_init(&cstr, cstr_stack_buf, sizeof(cstr_stack_buf));
+
+ write_severity(&cstr, severity, lg->ctx->use_color);
+ write_type(&cstr, lg);
+
+ {
+ write_file_line_fn(&cstr, file_line, fn, lg->ctx->use_basename);
+ clg_str_append(&cstr, message);
+ }
+ clg_str_append(&cstr, "\n");
+
+ /* could be optional */
+ fwrite(cstr.data, cstr.len, 1, lg->ctx->output);
+ fflush(lg->ctx->output);
+
+ clg_str_free(&cstr);
+
+ if (severity == CLG_SEVERITY_FATAL) {
+ clg_ctx_fatal_action(lg->ctx, lg->ctx->output);
+ }
+}
+
+void CLG_logf(
+ CLG_LogType *lg, enum CLG_Severity severity, const char *file_line, const char *fn,
+ const char *fmt, ...)
+{
+ CLogStringBuf cstr;
+ char cstr_stack_buf[CLOG_BUF_LEN_INIT];
+ clg_str_init(&cstr, cstr_stack_buf, sizeof(cstr_stack_buf));
+
+ write_severity(&cstr, severity, lg->ctx->use_color);
+ write_type(&cstr, lg);
+
+ {
+ write_file_line_fn(&cstr, file_line, fn, lg->ctx->use_basename);
+
+ va_list ap;
+ va_start(ap, fmt);
+ clg_str_vappendf(&cstr, fmt, ap);
+ va_end(ap);
+ }
+ clg_str_append(&cstr, "\n");
+
+ /* could be optional */
+ fwrite(cstr.data, cstr.len, 1, lg->ctx->output);
+ fflush(lg->ctx->output);
+
+ clg_str_free(&cstr);
+
+ if (severity == CLG_SEVERITY_FATAL) {
+ clg_ctx_fatal_action(lg->ctx, lg->ctx->output);
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Logging Context API
+ * \{ */
+
+static void CLG_ctx_output_set(CLogContext *ctx, void *file_handle)
+{
+ ctx->output = file_handle;
+#if defined(__unix__)
+ ctx->use_color = isatty(fileno(file_handle));
+#endif
+}
+
+static void CLG_ctx_output_use_basename_set(CLogContext *ctx, int value)
+{
+ ctx->use_basename = (bool)value;
+}
+
+/** Action on fatal severity. */
+static void CLG_ctx_fatal_fn_set(CLogContext *ctx, void (*fatal_fn)(void *file_handle))
+{
+ ctx->callbacks.fatal_fn = fatal_fn;
+}
+
+static void clg_ctx_type_filter_append(CLG_IDFilter **flt_list, const char *type_match, int type_match_len)
+{
+ if (type_match_len == 0) {
+ return;
+ }
+ CLG_IDFilter *flt = MEM_callocN(sizeof(*flt) + (type_match_len + 1), __func__);
+ flt->next = *flt_list;
+ *flt_list = flt;
+ memcpy(flt->match, type_match, type_match_len);
+ /* no need to null terminate since we calloc'd */
+}
+
+static void CLG_ctx_type_filter_exclude(CLogContext *ctx, const char *type_match, int type_match_len)
+{
+ clg_ctx_type_filter_append(&ctx->filters[0], type_match, type_match_len);
+}
+
+static void CLG_ctx_type_filter_include(CLogContext *ctx, const char *type_match, int type_match_len)
+{
+ clg_ctx_type_filter_append(&ctx->filters[1], type_match, type_match_len);
+}
+
+static CLogContext *CLG_ctx_init(void)
+{
+ CLogContext *ctx = MEM_callocN(sizeof(*ctx), __func__);
+ ctx->use_color = true;
+ ctx->default_type.level = 1;
+ CLG_ctx_output_set(ctx, stdout);
+
+ return ctx;
+}
+
+static void CLG_ctx_free(CLogContext *ctx)
+{
+ while (ctx->types != NULL) {
+ CLG_LogType *item = ctx->types;
+ ctx->types = item->next;
+ MEM_freeN(item);
+ }
+
+ for (uint i = 0; i < 2; i++) {
+ while (ctx->filters[i] != NULL) {
+ CLG_IDFilter *item = ctx->filters[i];
+ ctx->filters[i] = item->next;
+ MEM_freeN(item);
+ }
+ }
+ MEM_freeN(ctx);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Public Logging API
+ *
+ * Currently uses global context.
+ * \{ */
+
+/* We could support multiple at once, for now this seems not needed. */
+struct CLogContext *g_ctx = NULL;
+
+void CLG_init(void)
+{
+ g_ctx = CLG_ctx_init();
+
+ clg_color_table_init(g_ctx->use_color);
+}
+
+void CLG_exit(void)
+{
+ CLG_ctx_free(g_ctx);
+}
+
+void CLG_output_set(void *file_handle)
+{
+ CLG_ctx_output_set(g_ctx, file_handle);
+}
+
+void CLG_output_use_basename_set(int value)
+{
+ CLG_ctx_output_use_basename_set(g_ctx, value);
+}
+
+
+void CLG_fatal_fn_set(void (*fatal_fn)(void *file_handle))
+{
+ CLG_ctx_fatal_fn_set(g_ctx, fatal_fn);
+}
+
+void CLG_type_filter_exclude(const char *type_match, int type_match_len)
+{
+ CLG_ctx_type_filter_exclude(g_ctx, type_match, type_match_len);
+}
+
+void CLG_type_filter_include(const char *type_match, int type_match_len)
+{
+ CLG_ctx_type_filter_include(g_ctx, type_match, type_match_len);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Logging Reference API
+ * Use to avoid lookups each time.
+ * \{ */
+
+void CLG_logref_init(CLG_LogRef *clg_ref)
+{
+ assert(clg_ref->type == NULL);
+ CLG_LogType *clg_ty = clg_ctx_type_find_by_name(g_ctx, clg_ref->identifier);
+ clg_ref->type = clg_ty ? clg_ty : clg_ctx_type_register(g_ctx, clg_ref->identifier);
+}
+
+/** \} */