diff options
author | Campbell Barton <ideasman42@gmail.com> | 2018-04-01 12:03:25 +0300 |
---|---|---|
committer | Campbell Barton <ideasman42@gmail.com> | 2018-04-01 12:03:25 +0300 |
commit | b65ea517eb932bde950bde51979c6a3fd258efa8 (patch) | |
tree | 8f3a291a7e1778bb3af45cdb1d98a621efbd1a7d /intern | |
parent | 916c91bd08933d596eaca3e369467daf7964612e (diff) | |
parent | 473f17b3d557adbb06b89e0a186be48a0129086d (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.txt | 1 | ||||
-rw-r--r-- | intern/clog/CLG_log.h | 211 | ||||
-rw-r--r-- | intern/clog/CMakeLists.txt | 34 | ||||
-rw-r--r-- | intern/clog/clog.c | 583 |
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); +} + +/** \} */ |