From 4e4b02d5513b9930bc9ad737145d2968afd164c5 Mon Sep 17 00:00:00 2001 From: HyperWinX Date: Tue, 2 Sep 2025 11:39:54 +0400 Subject: [PATCH 01/22] Full remake --- cclarify.c | 16 ++++ cclarify.h | 250 ++++++++++++++++++----------------------------------- test.c | 18 +--- 3 files changed, 100 insertions(+), 184 deletions(-) create mode 100644 cclarify.c diff --git a/cclarify.c b/cclarify.c new file mode 100644 index 0000000..1e1c4c9 --- /dev/null +++ b/cclarify.c @@ -0,0 +1,16 @@ +#include +#include + +const char* __clar_descs[] = { + "fatal", + "error", + "warning", + "info", + "debug" +}; + +char* __clar_fmt = NULL; +char* __clar_buf = NULL; +bool __clar_logging_enabled = true; + + diff --git a/cclarify.h b/cclarify.h index 40ce6f0..667d111 100644 --- a/cclarify.h +++ b/cclarify.h @@ -4,6 +4,8 @@ #include #include #include +#include +#include // Some useful macroses #define CRED "\x1b[31m" @@ -12,181 +14,93 @@ #define CRESET "\x1b[0m" //Structs and enums definitions -typedef enum Output{ - FILEO, - STDOUTO, - ALL -} Output; - -typedef enum MsgType{ - INFO, - WARNING, - ERROR -} MsgType; - -typedef struct Clarifier{ - Output outmode; - int cclarify_stdout; - FILE* cclarify_fd; - char* prompt; - void (*write)(struct Clarifier*,char*,MsgType); -} Clarifier; - -// Variables -int cclarify_stdout = 0; -int cclarify_depth = 0; -FILE* cclarify_fd; -Output outmode; -char* prompt = "==>"; -char* assign_strings[] = { - "%s Assigning value \"%u\" to %s, old value: %u", - "%s Assigning value \"%lu\" to %s, old value: %lu\n", - "%s Assigning value \"%d\" to %s, old value: %d\n", - "%s Assigning value \"%ld\" to %s, old value: %ld\n", - "%s Assigning value \"%f\" to %s, old value: %f\n", - "%s Assigning value \"%lf\" to %s, old value: %lf\n", - "%s Assigning value \"%s\" to %s, old value: %s\n", - "%s Assigning value \"%p\" to %s, old value: %p\n" +enum __clar_loglevel : uint8_t { + CLAR_LOG_FATAL = 0, + CLAR_LOG_ERROR = 1, + CLAR_LOG_WARNING = 2, + CLAR_LOG_INFO = 3, + CLAR_LOG_DEBUG = 4 }; -char* display_strings[] = { - "%s Variable \"%s\" value: \"%u\"\n", - "%s Variable \"%s\" value: \"%lu\"\n", - "%s Variable \"%s\" value: \"%d\"\n", - "%s Variable \"%s\" value: \"%ld\"\n", - "%s Variable \"%s\" value: \"%f\"\n", - "%s Variable \"%s\" value: \"%lf\"\n", - "%s Variable \"%s\" value: \"%s\"\n", - "%s Variable \"%s\" value: \"%p\"\n" +struct __clar_logtarget { + FILE* file; + uint8_t output_mask; }; -void log_write(char* msg, MsgType type); - -// Functions -// Dummies -char* u_dummy_assign(){return assign_strings[0];} -char* lu_dummy_assign(){return assign_strings[1];} -char* d_dummy_assign(){return assign_strings[2];} -char* ld_dummy_assign(){return assign_strings[3];} -char* f_dummy_assign(){return assign_strings[4];} -char* lf_dummy_assign(){return assign_strings[5];} -char* s_dummy_assign(){return assign_strings[6];} -char* vp_dummy_assign(){return assign_strings[7];} - -char* u_dummy_display(){return display_strings[0];} -char* lu_dummy_display(){return display_strings[1];} -char* d_dummy_display(){return display_strings[2];} -char* ld_dummy_display(){return display_strings[3];} -char* f_dummy_display(){return display_strings[4];} -char* lf_dummy_display(){return display_strings[5];} -char* s_dummy_display(){return display_strings[6];} -char* vp_dummy_display(){return display_strings[7];} - -char* getcol(MsgType type){ - switch (type){ - case INFO: - return CGREEN; - case WARNING: - return CYELLOW; - case ERROR: - return CRED; - } -} -void write_buf(Clarifier* obj, char* msg, MsgType type){ - write(obj->cclarify_stdout, getcol(type), 5); - write(obj->cclarify_stdout, msg, strlen(msg)); - write(obj->cclarify_stdout, CRESET, 1); -} -void write_fd(Clarifier* obj, char* msg, MsgType type){ - fprintf(obj->cclarify_fd, "%s", msg); -} - -void write_all(Clarifier* obj, char* msg, MsgType type){ - write(obj->cclarify_stdout, getcol(type), 5); - write(obj->cclarify_stdout, msg, strlen(msg)); - write(obj->cclarify_stdout, CRESET, 1); - fprintf(obj->cclarify_fd, "%s", msg); -} +struct Clarifier { + enum __clar_loglevel loglevel; + struct __clar_logtarget logtarget; + const char* format; +}; -void write_data(Clarifier* restrict clar, char* restrict buffer, size_t size, MsgType type, char* restrict format, ...){ - va_list args; - va_start(args, format); - vsnprintf(buffer, size, format, args); - clar->write(clar, buffer, type); - va_end(args); +// Internal data +extern const char* __clar_descs[]; +extern char* __clar_fmt; +extern bool __clar_logging_enabled; + +// API +inline struct Clarifier clar_create_logger( + enum __clar_loglevel level, + struct __clar_logtarget target, + const char* fmt + ) { + return (struct Clarifier){ level, target, fmt }; } -#define GLOBAL_INIT() \ - char cclarify_buffer[512] = {0}; \ - char cclarify_col = 0; \ - char* cclarify_str = 0; -#define CLEARBUF() memset(cclarify_buffer, 0x00, strlen(cclarify_buffer)) -#define ASSIGN(obj, var, val) \ - cclarify_str = _Generic(var, \ - uint8_t: u_dummy_assign, \ - uint16_t: u_dummy_assign, \ - uint32_t: u_dummy_assign, \ - uint64_t: lu_dummy_assign, \ - int8_t: d_dummy_assign, \ - int16_t: d_dummy_assign, \ - int32_t: d_dummy_assign, \ - int64_t: ld_dummy_assign, \ - float: f_dummy_assign, \ - double: lf_dummy_assign, \ - char*: s_dummy_assign, \ - void*: vp_dummy_assign)(); \ - write_data(&obj, cclarify_buffer, sizeof(cclarify_buffer), INFO, cclarify_str, prompt, val, #var, var); \ - var = val; \ - CLEARBUF(); -#define EXEC(obj, func) \ - snprintf(cclarify_buffer, sizeof(cclarify_buffer), "%s Starting execution of function \"%s\"\n", prompt, #func); \ - obj.write(&obj, cclarify_buffer, INFO); \ - cclarify_depth++; \ - CLEARBUF(); \ - func; \ - snprintf(cclarify_buffer, sizeof(cclarify_buffer), "%s Execution of function \"%s\" finished\n", prompt, #func); \ - obj.write(&obj, cclarify_buffer, INFO); \ - cclarify_depth--; \ - CLEARBUF(); -#define MSG(obj, msg, type) \ - snprintf(cclarify_buffer, sizeof(cclarify_buffer), "%s %s\n", prompt, msg); \ - obj.write(&obj, cclarify_buffer, type); \ - CLEARBUF(); -#define DISPLAY(obj, var) \ - cclarify_str = _Generic(var, \ - uint8_t: u_dummy_display, \ - uint16_t: u_dummy_display, \ - uint32_t: u_dummy_display, \ - uint64_t: lu_dummy_display, \ - int8_t: d_dummy_display, \ - int16_t: d_dummy_display, \ - int32_t: d_dummy_display, \ - int64_t: ld_dummy_display, \ - float: f_dummy_display, \ - double: lf_dummy_display, \ - char*: s_dummy_display, \ - void*: vp_dummy_display)(); \ - write_data(&obj, cclarify_buffer, sizeof(cclarify_buffer), INFO, cclarify_str, prompt, #var, var); \ - CLEARBUF(); - -#define init_loggerd(obj, desc) _init_loggerd(&obj, desc); -void _init_loggerd(Clarifier* obj, int descriptor){ - obj->outmode = STDOUTO; - obj->cclarify_stdout = descriptor; - obj->write = &write_buf; +inline struct __clar_logtarget clar_create_logtarget( + const char* filename, + uint8_t outmask + ) { + struct __clar_logtarget target; + target.file = fopen(filename, "w"); + if (!target.file) { + printf("Failed to open file %s to log", filename); + abort(); + } + target.output_mask = outmask; + return target; } -#define init_loggerf(obj, fd) _init_loggerd(&obj, fd); -void _init_loggerf(Clarifier* obj, FILE* fd){ - obj->outmode = FILEO; - obj->cclarify_fd = fd; - obj->write = &write_fd; +#define __CLAR_EXPAND(x) x +#define __CLAR_GET_MACRO(_1, name, ...) name +#define CLAR_INIT(...) __CLAR_EXPAND(__CLAR_GET_MACRO(__VA_ARGS__, __CLAR_INIT2, __CLAR_INIT1)(__VA_ARGS__)) + +#define __CLAR_INIT1() + +#define __CLAR_INIT2(arg) \ + __clar_fmt = strdup(arg); + +void clar_log( + enum __clar_loglevel loglevel, + const char* restrict fmt, + ... + ) { + va_list args; + va_start(args, fmt); + + if (!__clar_fmt) { + vprintf(fmt, args); + return; + } + for (uint32_t i = 0; i < strlen(__clar_fmt); ++i) { + switch (__clar_fmt[i]) { + case '%': + switch (__clar_fmt[i + 1]) { + case 'S': + vprintf(fmt, args); + break; + case 'd': + fputs(__clar_descs[(uint8_t)loglevel], stdout); + break; + case '%': + putchar('%'); + break; + } + ++i; + break; + default: + putchar(__clar_fmt[i]); + break; + } + } } - -#define init_loggerfd(obj, desc, fd) _init_loggerfd(&obj, desc, fd); -void _init_loggerfd(Clarifier* obj, int descriptor, FILE* fd){ - obj->outmode = ALL; - obj->cclarify_stdout = descriptor; - obj->cclarify_fd = fd; - obj->write = &write_all; -} \ No newline at end of file diff --git a/test.c b/test.c index 37051d0..9235d40 100644 --- a/test.c +++ b/test.c @@ -1,20 +1,6 @@ #include "cclarify.h" -void func(){} -int func_assign(){return 1;} - int main(){ - GLOBAL_INIT(); - Clarifier clar; - int dupped = dup(fileno(stdout)); - init_loggerd(clar, dupped); - uint64_t t = 666; - ASSIGN(clar, t, 5); - MSG(clar, "Bruh", INFO); - MSG(clar, "Bruh", WARNING); - MSG(clar, "Bruh", ERROR); - EXEC(clar, func()); - MSG(clar, "Lets assign function return value to variable!", INFO); - ASSIGN(clar, t, func_assign()); - DISPLAY(clar, t); + CLAR_INIT("[%d] %S"); + clar_log(CLAR_LOG_DEBUG, "Test %s %d", "HyperWin", 5); } From e03fc9ae09a37a2c29c6a9fb40d2242fb3794140 Mon Sep 17 00:00:00 2001 From: HyperWinX Date: Sat, 6 Sep 2025 12:39:25 +0400 Subject: [PATCH 02/22] Working basic output --- cclarify.c | 196 +++++++++++++++++++++++++++++++++++++++++++++++++++++ cclarify.h | 83 +++++++++++------------ test.c | 3 + 3 files changed, 240 insertions(+), 42 deletions(-) diff --git a/cclarify.c b/cclarify.c index 1e1c4c9..6659ac6 100644 --- a/cclarify.c +++ b/cclarify.c @@ -1,6 +1,16 @@ +#include <__stdarg_va_list.h> +#include #include +#include #include +#include "cclarify.h" + +#define CRED "\x1b[31m" +#define CYELLOW "\x1b[33m" +#define CRESET "\x1b[0m" + + const char* __clar_descs[] = { "fatal", "error", @@ -9,8 +19,194 @@ const char* __clar_descs[] = { "debug" }; +const char* __clar_colors[] = { + CRED, + CRED, + CYELLOW, + CRESET, + CRESET +}; + +struct Clarifier __clar_default_fmt; + char* __clar_fmt = NULL; char* __clar_buf = NULL; bool __clar_logging_enabled = true; +[[gnu::visibility("hidden")]] +uint32_t __clar_get_fmt_size( + const char* restrict fmt, + va_list* args + ) { + return vsnprintf(NULL, 0, fmt, *args); +} + +[[gnu::visibility("hidden")]] +void sputs( + char* buf, + const char* src, + uint32_t* offset + ) { + uint32_t len = strlen(src); + memcpy(buf, src, len); + *offset += len; +} + +[[gnu::visibility("hidden")]] +void __clar_format( + struct Clarifier* clar, + enum __clar_loglevel loglevel, + char* buf, + char* fmt, + va_list* args + ) { + if (!clar) { + clar = &__clar_default_fmt; + } + + uint32_t offset = 0; + + for (uint32_t i = 0; i < strlen(clar->format); ++i) { + switch (clar->format[i]) { + case '%': + switch (clar->format[i + 1]) { + case 'S': + offset += vsnprintf(buf + offset, 0xFFFF, fmt, *args); + break; + case 'd': + sputs(buf + offset, __clar_colors[(uint8_t)loglevel], &offset); + sputs(buf + offset, __clar_descs[(uint8_t)loglevel], &offset); + sputs(buf + offset, CRESET, &offset); + break; + case '%': + default: + buf[offset++] = '%'; + break; + } + ++i; + break; + default: + buf[offset++] = clar->format[i]; + break; + } + } + buf[offset++] = '\n'; +} + +void __clar_log( + struct Clarifier* clar, + enum __clar_loglevel loglevel, + const char* fmt, + va_list* args + ) { + uint32_t sz = __clar_get_fmt_size(fmt, args) + 2048; + char* buf = alloca(sz); + if (!buf) { + return; + } + memset(buf, 0x00, sz); + + __clar_format(clar, loglevel, buf, fmt, args); + + if (clar->logtarget.output_mask & CLAR_OUT_STDOUT) { + fputs(buf, stdout); + } + if (clar->logtarget.output_mask & CLAR_OUT_FILE && + clar->logtarget.file) { + fwrite(buf, 1, sz, clar->logtarget.file); + } +} + +void clar_log( + enum __clar_loglevel loglevel, + const char* restrict fmt, + ... + ) { + va_list args; + va_start(args, fmt); + + __clar_log(&__clar_default_fmt, loglevel, fmt, &args); + + va_end(args); +} + +void clar_log_fd( + struct Clarifier clar, + const char* restrict fmt, + ... + ) { + va_list args; + va_start(args, fmt); + char* buf = alloca(__clar_get_fmt_size(fmt, &args) + 2048); + va_end(args); + +} + +void clar_debug( + const char* restrict fmt, + ...) { + va_list args; + va_start(args, fmt); + + __clar_log(&__clar_default_fmt, CLAR_LOG_DEBUG, fmt, &args); + + va_end(args); +} + +void clar_info( + const char* restrict fmt, + ...) { + va_list args; + va_start(args, fmt); + + __clar_log(&__clar_default_fmt, CLAR_LOG_INFO, fmt, &args); + + va_end(args); +} + +void clar_warn( + const char* restrict fmt, + ...) { + va_list args; + va_start(args, fmt); + + __clar_log(&__clar_default_fmt, CLAR_LOG_WARNING, fmt, &args); + + va_end(args); +} + +void clar_err( + const char* restrict fmt, + ...) { + va_list args; + va_start(args, fmt); + + __clar_log(&__clar_default_fmt, CLAR_LOG_ERROR, fmt, &args); + + va_end(args); +} + +void clar_fatal( + const char* restrict fmt, + ...) { + va_list args; + va_start(args, fmt); + + __clar_log(&__clar_default_fmt, CLAR_LOG_FATAL, fmt, &args); + + va_end(args); +} + +[[gnu::constructor]] +void __clar_construct() { + __clar_default_fmt.format = strdup("[%d] %S"); + __clar_default_fmt.loglevel = CLAR_LOG_DEBUG; + __clar_default_fmt.logtarget.output_mask = CLAR_OUT_STDOUT; +} +[[gnu::destructor]] +void __clar_destruct() { + if (__clar_default_fmt.format) { + free(__clar_default_fmt.format); + } +} diff --git a/cclarify.h b/cclarify.h index 667d111..13ce137 100644 --- a/cclarify.h +++ b/cclarify.h @@ -7,12 +7,6 @@ #include #include -// Some useful macroses -#define CRED "\x1b[31m" -#define CGREEN "\x1b[32m" -#define CYELLOW "\x1b[33m" -#define CRESET "\x1b[0m" - //Structs and enums definitions enum __clar_loglevel : uint8_t { CLAR_LOG_FATAL = 0, @@ -22,6 +16,11 @@ enum __clar_loglevel : uint8_t { CLAR_LOG_DEBUG = 4 }; +enum __clar_outputmask : uint8_t { + CLAR_OUT_STDOUT = (1 << 0), + CLAR_OUT_FILE = (1 << 1) +}; + struct __clar_logtarget { FILE* file; uint8_t output_mask; @@ -32,11 +31,8 @@ struct Clarifier { struct __clar_logtarget logtarget; const char* format; }; - -// Internal data -extern const char* __clar_descs[]; -extern char* __clar_fmt; -extern bool __clar_logging_enabled; +// Internals +extern struct Clarifier __clar_default_fmt; // API inline struct Clarifier clar_create_logger( @@ -61,6 +57,14 @@ inline struct __clar_logtarget clar_create_logtarget( return target; } +inline void clar_set_format(const char* restrict fmt) { + if (__clar_default_fmt.format) { + free(__clar_default_fmt.format); + __clar_default_fmt.format = NULL; + } + __clar_default_fmt.format = strdup(fmt); +} + #define __CLAR_EXPAND(x) x #define __CLAR_GET_MACRO(_1, name, ...) name #define CLAR_INIT(...) __CLAR_EXPAND(__CLAR_GET_MACRO(__VA_ARGS__, __CLAR_INIT2, __CLAR_INIT1)(__VA_ARGS__)) @@ -68,39 +72,34 @@ inline struct __clar_logtarget clar_create_logtarget( #define __CLAR_INIT1() #define __CLAR_INIT2(arg) \ - __clar_fmt = strdup(arg); + __clar_default_fmt.format = strdup(arg); void clar_log( enum __clar_loglevel loglevel, const char* restrict fmt, - ... - ) { - va_list args; - va_start(args, fmt); + ...); - if (!__clar_fmt) { - vprintf(fmt, args); - return; - } - for (uint32_t i = 0; i < strlen(__clar_fmt); ++i) { - switch (__clar_fmt[i]) { - case '%': - switch (__clar_fmt[i + 1]) { - case 'S': - vprintf(fmt, args); - break; - case 'd': - fputs(__clar_descs[(uint8_t)loglevel], stdout); - break; - case '%': - putchar('%'); - break; - } - ++i; - break; - default: - putchar(__clar_fmt[i]); - break; - } - } -} +void clar_log_fd( + struct Clarifier clar, + const char* restrict fmt, + ...); + +void clar_debug( + const char* restrict fmt, + ...); + +void clar_info( + const char* restrict fmt, + ...); + +void clar_warn( + const char* restrict fmt, + ...); + +void clar_err( + const char* restrict fmt, + ...); + +void clar_fatal( + const char* restrict fmt, + ...); diff --git a/test.c b/test.c index 9235d40..1ca8558 100644 --- a/test.c +++ b/test.c @@ -2,5 +2,8 @@ int main(){ CLAR_INIT("[%d] %S"); + clar_warn("test"); + clar_err("test"); + clar_debug("Test"); clar_log(CLAR_LOG_DEBUG, "Test %s %d", "HyperWin", 5); } From fa92df0833a6bc6b16ba5a2efb021299a694ea17 Mon Sep 17 00:00:00 2001 From: HyperWinX Date: Sat, 6 Sep 2025 14:14:03 +0400 Subject: [PATCH 03/22] Working time format specifiers --- cclarify.c | 108 ++++++++++---------------------------- cclarify.h | 151 +++++++++++++++++++++++++++++++++++++++++++++++------ test.c | 6 ++- 3 files changed, 168 insertions(+), 97 deletions(-) diff --git a/cclarify.c b/cclarify.c index 6659ac6..0df096e 100644 --- a/cclarify.c +++ b/cclarify.c @@ -3,6 +3,7 @@ #include #include #include +#include #include "cclarify.h" @@ -14,7 +15,7 @@ const char* __clar_descs[] = { "fatal", "error", - "warning", + "warn", "info", "debug" }; @@ -65,15 +66,38 @@ void __clar_format( } uint32_t offset = 0; + time_t cur_time = time(NULL); + struct tm* timeinfo = localtime(&cur_time); for (uint32_t i = 0; i < strlen(clar->format); ++i) { switch (clar->format[i]) { case '%': switch (clar->format[i + 1]) { - case 'S': - offset += vsnprintf(buf + offset, 0xFFFF, fmt, *args); + case 'Y': + offset += strftime(buf + offset, 0xFFFF, "%Y", timeinfo); + break; + case 'M': + offset += strftime(buf + offset, 0xFFFF, "%b", timeinfo); break; case 'd': + offset += strftime(buf + offset, 0xFFFF, "%a", timeinfo); + break; + case 'D': + offset += strftime(buf + offset, 0xFFFF, "%d", timeinfo); + break; + case 'H': + offset += strftime(buf + offset, 0xFFFF, "%H", timeinfo); + break; + case 'm': + offset += strftime(buf + offset, 0xFFFF, "%M", timeinfo); + break; + case 's': + offset += strftime(buf + offset, 0xFFFF, "%S", timeinfo); + break; + case 'l': + offset += vsnprintf(buf + offset, 0xFFFF, fmt, *args); + break; + case 'x': sputs(buf + offset, __clar_colors[(uint8_t)loglevel], &offset); sputs(buf + offset, __clar_descs[(uint8_t)loglevel], &offset); sputs(buf + offset, CRESET, &offset); @@ -121,85 +145,11 @@ void clar_log( enum __clar_loglevel loglevel, const char* restrict fmt, ... - ) { - va_list args; - va_start(args, fmt); - - __clar_log(&__clar_default_fmt, loglevel, fmt, &args); - - va_end(args); -} - -void clar_log_fd( - struct Clarifier clar, - const char* restrict fmt, - ... - ) { - va_list args; - va_start(args, fmt); - char* buf = alloca(__clar_get_fmt_size(fmt, &args) + 2048); - va_end(args); - -} - -void clar_debug( - const char* restrict fmt, - ...) { - va_list args; - va_start(args, fmt); - - __clar_log(&__clar_default_fmt, CLAR_LOG_DEBUG, fmt, &args); - - va_end(args); -} - -void clar_info( - const char* restrict fmt, - ...) { - va_list args; - va_start(args, fmt); - - __clar_log(&__clar_default_fmt, CLAR_LOG_INFO, fmt, &args); - - va_end(args); -} - -void clar_warn( - const char* restrict fmt, - ...) { - va_list args; - va_start(args, fmt); - - __clar_log(&__clar_default_fmt, CLAR_LOG_WARNING, fmt, &args); - - va_end(args); -} - -void clar_err( - const char* restrict fmt, - ...) { - va_list args; - va_start(args, fmt); - - __clar_log(&__clar_default_fmt, CLAR_LOG_ERROR, fmt, &args); - - va_end(args); -} - -void clar_fatal( - const char* restrict fmt, - ...) { - va_list args; - va_start(args, fmt); - - __clar_log(&__clar_default_fmt, CLAR_LOG_FATAL, fmt, &args); - - va_end(args); -} +); [[gnu::constructor]] void __clar_construct() { - __clar_default_fmt.format = strdup("[%d] %S"); + __clar_default_fmt.format = strdup("%Y %M %d %D %H:%m:%s [%x] %l"); __clar_default_fmt.loglevel = CLAR_LOG_DEBUG; __clar_default_fmt.logtarget.output_mask = CLAR_OUT_STDOUT; } diff --git a/cclarify.h b/cclarify.h index 13ce137..d483a4e 100644 --- a/cclarify.h +++ b/cclarify.h @@ -33,7 +33,12 @@ struct Clarifier { }; // Internals extern struct Clarifier __clar_default_fmt; - +extern void __clar_log( + struct Clarifier* clar, + enum __clar_loglevel loglevel, + const char* fmt, + va_list* args +); // API inline struct Clarifier clar_create_logger( enum __clar_loglevel level, @@ -74,32 +79,146 @@ inline void clar_set_format(const char* restrict fmt) { #define __CLAR_INIT2(arg) \ __clar_default_fmt.format = strdup(arg); -void clar_log( +static inline void clar_log( enum __clar_loglevel loglevel, const char* restrict fmt, - ...); + ...) { + va_list args; + va_start(args, fmt); -void clar_log_fd( - struct Clarifier clar, + __clar_log(&__clar_default_fmt, loglevel, fmt, &args); + + va_end(args); +} +static inline void clar_log_with( + struct Clarifier* clar, + enum __clar_loglevel loglevel, const char* restrict fmt, - ...); + ... + ) { + va_list args; + va_start(args, fmt); + + __clar_log(&clar, loglevel, fmt, &args); -void clar_debug( + va_end(args); +} + +static inline void clar_debug( const char* restrict fmt, - ...); + ...) { + va_list args; + va_start(args, fmt); -void clar_info( + __clar_log(&__clar_default_fmt, CLAR_LOG_DEBUG, fmt, &args); + + va_end(args); +} + +static inline void clar_debug_with( + struct Clarifier* clar, const char* restrict fmt, - ...); + ... + ) { + va_list args; + va_start(args, fmt); + + __clar_log(clar, CLAR_LOG_DEBUG, fmt, &args); -void clar_warn( + va_end(args); +} + +static inline void clar_info( const char* restrict fmt, - ...); + ...) { + va_list args; + va_start(args, fmt); -void clar_err( + __clar_log(&__clar_default_fmt, CLAR_LOG_INFO, fmt, &args); + + va_end(args); +} +static inline void clar_info_with( + struct Clarifier* clar, const char* restrict fmt, - ...); + ... + ) { + va_list args; + va_start(args, fmt); + + __clar_log(clar, CLAR_LOG_DEBUG, fmt, &args); + + va_end(args); +} + +static inline void clar_warn( + const char* restrict fmt, + ...) { + va_list args; + va_start(args, fmt); + + __clar_log(&__clar_default_fmt, CLAR_LOG_WARNING, fmt, &args); + + va_end(args); +} -void clar_fatal( +static inline void clar_warn_with( + struct Clarifier* clar, const char* restrict fmt, - ...); + ... + ) { + va_list args; + va_start(args, fmt); + + __clar_log(clar, CLAR_LOG_WARNING, fmt, &args); + + va_end(args); +} + +static inline void clar_err( + const char* restrict fmt, + ...) { + va_list args; + va_start(args, fmt); + + __clar_log(&__clar_default_fmt, CLAR_LOG_ERROR, fmt, &args); + + va_end(args); +} + +static inline void clar_err_with( + struct Clarifier* clar, + const char* restrict fmt, + ... + ) { + va_list args; + va_start(args, fmt); + + __clar_log(clar, CLAR_LOG_ERROR, fmt, &args); + + va_end(args); +} + +static inline void clar_fatal( + const char* restrict fmt, + ...) { + va_list args; + va_start(args, fmt); + + __clar_log(&__clar_default_fmt, CLAR_LOG_FATAL, fmt, &args); + + va_end(args); +} + +static inline void clar_fatal_with( + struct Clarifier* clar, + const char* restrict fmt, + ... + ) { + va_list args; + va_start(args, fmt); + + __clar_log(clar, CLAR_LOG_FATAL, fmt, &args); + + va_end(args); +} diff --git a/test.c b/test.c index 1ca8558..def6419 100644 --- a/test.c +++ b/test.c @@ -1,9 +1,11 @@ #include "cclarify.h" int main(){ - CLAR_INIT("[%d] %S"); + CLAR_INIT("%Y %M %d %D %H:%m:%s [%x] %l"); + clar_debug("test"); + clar_info("test"); clar_warn("test"); clar_err("test"); - clar_debug("Test"); + clar_fatal("test"); clar_log(CLAR_LOG_DEBUG, "Test %s %d", "HyperWin", 5); } From 0bf37f795fd054f1345727117bf70f6be8e9a192 Mon Sep 17 00:00:00 2001 From: HyperWinX Date: Sat, 6 Sep 2025 17:15:28 +0400 Subject: [PATCH 04/22] Fix loglevels --- cclarify.c | 30 ++++++++++++++++-------------- cclarify.h | 16 ++++++++-------- test.c | 10 ++++++++++ 3 files changed, 34 insertions(+), 22 deletions(-) diff --git a/cclarify.c b/cclarify.c index 0df096e..4c15e31 100644 --- a/cclarify.c +++ b/cclarify.c @@ -59,7 +59,8 @@ void __clar_format( enum __clar_loglevel loglevel, char* buf, char* fmt, - va_list* args + va_list* args, + bool colors ) { if (!clar) { clar = &__clar_default_fmt; @@ -98,9 +99,13 @@ void __clar_format( offset += vsnprintf(buf + offset, 0xFFFF, fmt, *args); break; case 'x': - sputs(buf + offset, __clar_colors[(uint8_t)loglevel], &offset); - sputs(buf + offset, __clar_descs[(uint8_t)loglevel], &offset); - sputs(buf + offset, CRESET, &offset); + if (colors) { + sputs(buf + offset, __clar_colors[(uint8_t)loglevel], &offset); + sputs(buf + offset, __clar_descs[(uint8_t)loglevel], &offset); + sputs(buf + offset, CRESET, &offset); + } else { + sputs(buf + offset, __clar_descs[(uint8_t)loglevel], &offset); + } break; case '%': default: @@ -123,6 +128,9 @@ void __clar_log( const char* fmt, va_list* args ) { + if (loglevel > clar->loglevel) { + return; + } uint32_t sz = __clar_get_fmt_size(fmt, args) + 2048; char* buf = alloca(sz); if (!buf) { @@ -130,14 +138,14 @@ void __clar_log( } memset(buf, 0x00, sz); - __clar_format(clar, loglevel, buf, fmt, args); - if (clar->logtarget.output_mask & CLAR_OUT_STDOUT) { + __clar_format(clar, loglevel, buf, fmt, args, true); fputs(buf, stdout); } if (clar->logtarget.output_mask & CLAR_OUT_FILE && clar->logtarget.file) { - fwrite(buf, 1, sz, clar->logtarget.file); + __clar_format(clar, loglevel, buf, fmt, args, false); + fwrite(buf, 1, strlen(buf), clar->logtarget.file); } } @@ -149,14 +157,8 @@ void clar_log( [[gnu::constructor]] void __clar_construct() { - __clar_default_fmt.format = strdup("%Y %M %d %D %H:%m:%s [%x] %l"); + __clar_default_fmt.format = "%Y %M %d %D %H:%m:%s [%x] %l"; __clar_default_fmt.loglevel = CLAR_LOG_DEBUG; __clar_default_fmt.logtarget.output_mask = CLAR_OUT_STDOUT; } -[[gnu::destructor]] -void __clar_destruct() { - if (__clar_default_fmt.format) { - free(__clar_default_fmt.format); - } -} diff --git a/cclarify.h b/cclarify.h index d483a4e..37f1dcd 100644 --- a/cclarify.h +++ b/cclarify.h @@ -40,7 +40,7 @@ extern void __clar_log( va_list* args ); // API -inline struct Clarifier clar_create_logger( +static inline struct Clarifier clar_create_logger( enum __clar_loglevel level, struct __clar_logtarget target, const char* fmt @@ -48,7 +48,7 @@ inline struct Clarifier clar_create_logger( return (struct Clarifier){ level, target, fmt }; } -inline struct __clar_logtarget clar_create_logtarget( +static inline struct __clar_logtarget clar_create_logtarget( const char* filename, uint8_t outmask ) { @@ -62,12 +62,12 @@ inline struct __clar_logtarget clar_create_logtarget( return target; } -inline void clar_set_format(const char* restrict fmt) { - if (__clar_default_fmt.format) { - free(__clar_default_fmt.format); - __clar_default_fmt.format = NULL; - } - __clar_default_fmt.format = strdup(fmt); +static inline void clar_set_global_format(const char* restrict fmt) { + __clar_default_fmt.format = fmt; +} + +static inline void clar_set_global_loglevel(enum __clar_loglevel loglevel) { + __clar_default_fmt.loglevel = loglevel; } #define __CLAR_EXPAND(x) x diff --git a/test.c b/test.c index def6419..693deb5 100644 --- a/test.c +++ b/test.c @@ -2,10 +2,20 @@ int main(){ CLAR_INIT("%Y %M %d %D %H:%m:%s [%x] %l"); + struct Clarifier clar = clar_create_logger(CLAR_LOG_DEBUG, clar_create_logtarget("test.log", CLAR_OUT_FILE), "[%x] %l"); clar_debug("test"); clar_info("test"); clar_warn("test"); clar_err("test"); clar_fatal("test"); + clar_fatal_with(&clar, "test"); clar_log(CLAR_LOG_DEBUG, "Test %s %d", "HyperWin", 5); + + clar_debug("I'll try print a message with loglevel lower than the current global loglevel."); + clar_set_global_loglevel(CLAR_LOG_WARNING); + clar_info("Message that should not be seen"); + clar_set_global_loglevel(CLAR_LOG_DEBUG); + clar_debug("See? It works. Same with custom loggers."); + clar.loglevel = CLAR_LOG_WARNING; + clar_info_with(&clar, "You shouldn't see this."); } From fe9c1b8979aa2315562e9748a3df7e84f7cbe2ea Mon Sep 17 00:00:00 2001 From: HyperWinX Date: Sat, 6 Sep 2025 17:50:03 +0400 Subject: [PATCH 05/22] Fixes --- cclarify.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/cclarify.c b/cclarify.c index 4c15e31..e11b4ad 100644 --- a/cclarify.c +++ b/cclarify.c @@ -54,7 +54,7 @@ void sputs( } [[gnu::visibility("hidden")]] -void __clar_format( +uint32_t __clar_format( struct Clarifier* clar, enum __clar_loglevel loglevel, char* buf, @@ -120,6 +120,7 @@ void __clar_format( } } buf[offset++] = '\n'; + return offset; } void __clar_log( @@ -144,8 +145,8 @@ void __clar_log( } if (clar->logtarget.output_mask & CLAR_OUT_FILE && clar->logtarget.file) { - __clar_format(clar, loglevel, buf, fmt, args, false); - fwrite(buf, 1, strlen(buf), clar->logtarget.file); + uint32_t size = __clar_format(clar, loglevel, buf, fmt, args, false); + fwrite(buf, 1, size, clar->logtarget.file); } } From 784e2fc608061e8dc65cc023203595944210cccf Mon Sep 17 00:00:00 2001 From: HyperWinX Date: Sat, 6 Sep 2025 19:30:48 +0400 Subject: [PATCH 06/22] Fixes --- cclarify.c | 6 +++--- cclarify.h | 40 ++++++++++++++++++++++++++-------------- 2 files changed, 29 insertions(+), 17 deletions(-) diff --git a/cclarify.c b/cclarify.c index e11b4ad..03b2a02 100644 --- a/cclarify.c +++ b/cclarify.c @@ -28,7 +28,7 @@ const char* __clar_colors[] = { CRESET }; -struct Clarifier __clar_default_fmt; +struct clarifier __clar_default_fmt; char* __clar_fmt = NULL; char* __clar_buf = NULL; @@ -55,7 +55,7 @@ void sputs( [[gnu::visibility("hidden")]] uint32_t __clar_format( - struct Clarifier* clar, + struct clarifier* clar, enum __clar_loglevel loglevel, char* buf, char* fmt, @@ -124,7 +124,7 @@ uint32_t __clar_format( } void __clar_log( - struct Clarifier* clar, + struct clarifier* clar, enum __clar_loglevel loglevel, const char* fmt, va_list* args diff --git a/cclarify.h b/cclarify.h index 37f1dcd..61aa237 100644 --- a/cclarify.h +++ b/cclarify.h @@ -26,26 +26,26 @@ struct __clar_logtarget { uint8_t output_mask; }; -struct Clarifier { +struct clarifier { enum __clar_loglevel loglevel; struct __clar_logtarget logtarget; const char* format; }; // Internals -extern struct Clarifier __clar_default_fmt; +extern struct clarifier __clar_default_fmt; extern void __clar_log( - struct Clarifier* clar, + struct clarifier* clar, enum __clar_loglevel loglevel, const char* fmt, va_list* args ); // API -static inline struct Clarifier clar_create_logger( +static inline struct clarifier clar_create_logger( enum __clar_loglevel level, struct __clar_logtarget target, const char* fmt ) { - return (struct Clarifier){ level, target, fmt }; + return (struct clarifier){ level, target, fmt }; } static inline struct __clar_logtarget clar_create_logtarget( @@ -55,13 +55,21 @@ static inline struct __clar_logtarget clar_create_logtarget( struct __clar_logtarget target; target.file = fopen(filename, "w"); if (!target.file) { - printf("Failed to open file %s to log", filename); - abort(); + printf("[[cclarify]] Failed to open file %s to log", filename); } target.output_mask = outmask; return target; } +static inline void clar_destroy_logger(struct clarifier* clar) { + if (clar->logtarget.file) { + fclose(clar->logtarget.file); + clar->logtarget.file = NULL; + } + clar->logtarget.output_mask = 0; + clar->format = NULL; +} + static inline void clar_set_global_format(const char* restrict fmt) { __clar_default_fmt.format = fmt; } @@ -70,6 +78,10 @@ static inline void clar_set_global_loglevel(enum __clar_loglevel loglevel) { __clar_default_fmt.loglevel = loglevel; } +static inline void clar_set_global_logger(struct clarifier* clar) { + memcpy(&__clar_default_fmt, clar, sizeof(clarifier)); +} + #define __CLAR_EXPAND(x) x #define __CLAR_GET_MACRO(_1, name, ...) name #define CLAR_INIT(...) __CLAR_EXPAND(__CLAR_GET_MACRO(__VA_ARGS__, __CLAR_INIT2, __CLAR_INIT1)(__VA_ARGS__)) @@ -77,7 +89,7 @@ static inline void clar_set_global_loglevel(enum __clar_loglevel loglevel) { #define __CLAR_INIT1() #define __CLAR_INIT2(arg) \ - __clar_default_fmt.format = strdup(arg); + __clar_default_fmt.format = arg; static inline void clar_log( enum __clar_loglevel loglevel, @@ -91,7 +103,7 @@ static inline void clar_log( va_end(args); } static inline void clar_log_with( - struct Clarifier* clar, + struct clarifier* clar, enum __clar_loglevel loglevel, const char* restrict fmt, ... @@ -116,7 +128,7 @@ static inline void clar_debug( } static inline void clar_debug_with( - struct Clarifier* clar, + struct clarifier* clar, const char* restrict fmt, ... ) { @@ -139,7 +151,7 @@ static inline void clar_info( va_end(args); } static inline void clar_info_with( - struct Clarifier* clar, + struct clarifier* clar, const char* restrict fmt, ... ) { @@ -163,7 +175,7 @@ static inline void clar_warn( } static inline void clar_warn_with( - struct Clarifier* clar, + struct clarifier* clar, const char* restrict fmt, ... ) { @@ -187,7 +199,7 @@ static inline void clar_err( } static inline void clar_err_with( - struct Clarifier* clar, + struct clarifier* clar, const char* restrict fmt, ... ) { @@ -211,7 +223,7 @@ static inline void clar_fatal( } static inline void clar_fatal_with( - struct Clarifier* clar, + struct clarifier* clar, const char* restrict fmt, ... ) { From 14cac50def564fb4cc95eeadae3d8d1cadb07ba2 Mon Sep 17 00:00:00 2001 From: HyperWinX Date: Tue, 9 Sep 2025 07:34:11 +0400 Subject: [PATCH 07/22] Updated structure, added conanfile --- .clang-format | 60 +++++++++++++++++++++++ .clang-tidy | 54 ++++++++++++++++++++ CMakeLists.txt | 10 ++++ Makefile | 4 -- conanfile.py | 53 ++++++++++++++++++++ cclarify.h => include/cclarify/cclarify.h | 29 ++++++++++- cclarify.c => src/cclarify.c | 49 ++++++++++++++++-- test.c | 12 ++++- 8 files changed, 261 insertions(+), 10 deletions(-) create mode 100644 .clang-format create mode 100644 .clang-tidy create mode 100644 CMakeLists.txt delete mode 100644 Makefile create mode 100644 conanfile.py rename cclarify.h => include/cclarify/cclarify.h (82%) rename cclarify.c => src/cclarify.c (66%) diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..c7dfc55 --- /dev/null +++ b/.clang-format @@ -0,0 +1,60 @@ +# Base style configuration +# Inherit all formatting rules from the LLVM style as a starting point. +BasedOnStyle: LLVM + +# Indentation settings +# - IndentWidth: number of spaces per indent level. +# - TabWidth: visual width of tab characters. +# - UseTab: whether to emit real tab characters. +# - NamespaceIndentation: ident namespaces +IndentWidth: 2 +TabWidth: 2 +UseTab: Never +NamespaceIndentation: All + +# Include directives +# - SortIncludes: controls ordering of #include statements. +# - IncludeBlocks: allow empty lines for includes. +SortIncludes: true +IncludeBlocks: Preserve + +# Short constructs on single lines +# Disallow collapsing blocks, loops, cases, ifs, and functions into a single line. +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: None +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false + +# Access modifiers formatting +# Shift class access specifiers (public/protected/private) relative to class indent. +AccessModifierOffset: -2 + +# Brace placement +# Attach opening braces to the control statement or declaration line. +BreakBeforeBraces: Attach + +# Spacing around parentheses and casts +# ControlStatements: space before parens in if/for/while only. +# No extra spaces inside parentheses, angles, container literals, or square brackets. +SpaceBeforeParens: ControlStatements +SpaceAfterCStyleCast: false +SpacesInParentheses: false +SpacesInAngles: false +SpacesInContainerLiterals: false +SpacesInSquareBrackets: false +SpaceBeforeSquareBrackets: false + +# Template keyword spacing +# Always place a space after the 'template' keyword. +SpaceAfterTemplateKeyword: true + +# Line length +# ColumnLimit: 0 disables automatic wrapping; allows unlimited line length. +ColumnLimit: 0 + +# Pointer alignment +# DerivePointerAlignment: ignore inference, use PointerAlignment setting. +# PointerAlignment: align the '*' or '&' to the left with the type. +DerivePointerAlignment: false +PointerAlignment: Left diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 0000000..0cbf6d5 --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,54 @@ +# .clang-tidy + +# Enable broad sets, disable one modernize check explicitly. +Checks: > + clang-diagnostic-*, + clang-analyzer-*, + cppcoreguidelines-*, + modernize-*, + -modernize-use-trailing-return-type + -cppcoreguidelines-avoid-magic-numbers + +# Lint everything. Set a regex (e.g. ^src/|^include/) to scope. +HeaderFilterRegex: '' + +# Use clang-format's "google" style when auto-fixing. +FormatStyle: google + +# Per-check options, grouped for readability. +CheckOptions: + # --- CERT --- + - key: cert-dcl16-c.NewSuffixes + value: 'L;LL;LU;LLU' + - key: cert-oop54-cpp.WarnOnlyIfThisHasSuspiciousField + value: '0' + + # --- CppCoreGuidelines --- + - key: cppcoreguidelines-explicit-virtual-functions.IgnoreDestructors + value: '1' + - key: cppcoreguidelines-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic + value: '1' + + # --- Google Readability --- + - key: google-readability-braces-around-statements.ShortStatementLines + value: '1' + - key: google-readability-function-size.StatementThreshold + value: '800' + - key: google-readability-namespace-comments.ShortNamespaceLines + value: '10' + - key: google-readability-namespace-comments.SpacesBeforeComments + value: '2' + + # --- Modernize --- + - key: modernize-loop-convert.MaxCopySize + value: '16' + - key: modernize-loop-convert.MinConfidence + value: 'reasonable' + - key: modernize-loop-convert.NamingStyle + value: 'CamelCase' + - key: modernize-pass-by-value.IncludeStyle + value: 'llvm' + - key: modernize-replace-auto-ptr.IncludeStyle + value: 'llvm' + - key: modernize-use-nullptr.NullMacros + value: 'NULL' diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..1d714ac --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,10 @@ +cmake_minimum_required(VERSION 3.15) +project(cclarify C) + +add_library(cclarify src/cclarify.c) +target_include_directories(cclarify PUBLIC include) + + + +set_target_properties(cclarify PROPERTIES PUBLIC_HEADER "include/cclarify/cclarify.h") +install(TARGETS cclarify) diff --git a/Makefile b/Makefile deleted file mode 100644 index ba1d632..0000000 --- a/Makefile +++ /dev/null @@ -1,4 +0,0 @@ -all: - @gcc test.c -o test -std=c17 -g - @./test - @rm test diff --git a/conanfile.py b/conanfile.py new file mode 100644 index 0000000..2238bca --- /dev/null +++ b/conanfile.py @@ -0,0 +1,53 @@ +from conan import ConanFile +from conan.tools.cmake import CMakeToolchain, CMake, cmake_layout, CMakeDeps + + +class pkgRecipe(ConanFile): + name = "cclarify" + version = "0.1" + package_type = "library" + + # Optional metadata + license = "GPLv3" + author = "HyperWinX" + url = "https://github.com/HyperWinX/CClarify.git" + description = "Small and powerful logging library for C applications" + topics = ("c", "logging") + + # Binary configuration + settings = "os", "compiler", "build_type", "arch" + options = {"shared": [True, False], "fPIC": [True, False]} + default_options = {"shared": False, "fPIC": True} + + # Sources are located in the same place as this recipe, copy them to the recipe + exports_sources = "CMakeLists.txt", "src/*", "include/*" + + def config_options(self): + if self.settings.os == "Windows": + self.options.rm_safe("fPIC") + + def configure(self): + if self.options.shared: + self.options.rm_safe("fPIC") + + def layout(self): + cmake_layout(self) + + def generate(self): + deps = CMakeDeps(self) + deps.generate() + tc = CMakeToolchain(self) + tc.generate() + + def build(self): + cmake = CMake(self) + cmake.configure() + cmake.build() + + def package(self): + cmake = CMake(self) + cmake.install() + + def package_info(self): + self.cpp_info.libs = ["mypkg"] + diff --git a/cclarify.h b/include/cclarify/cclarify.h similarity index 82% rename from cclarify.h rename to include/cclarify/cclarify.h index 61aa237..1e4339e 100644 --- a/cclarify.h +++ b/include/cclarify/cclarify.h @@ -23,7 +23,11 @@ enum __clar_outputmask : uint8_t { struct __clar_logtarget { FILE* file; + const char* filename; uint8_t output_mask; + uint16_t max_files; + uint32_t max_size; + uint32_t cur_size; }; struct clarifier { @@ -54,6 +58,7 @@ static inline struct __clar_logtarget clar_create_logtarget( ) { struct __clar_logtarget target; target.file = fopen(filename, "w"); + target.filename = filename; if (!target.file) { printf("[[cclarify]] Failed to open file %s to log", filename); } @@ -70,6 +75,19 @@ static inline void clar_destroy_logger(struct clarifier* clar) { clar->format = NULL; } +static inline void clar_destroy_logtarget(struct clarifier* clar) { + if (clar->logtarget.file) { + fclose(clar->logtarget.file); + } + memset(&clar->logtarget, '\0', sizeof(__clar_logtarget)); +} + +static inline void clar_set_global_rotation(const char* filename, uint16_t max_file_count, uint32_t max_size) { + __clar_default_fmt.logtarget.filename = filename; + __clar_default_fmt.logtarget.max_files = max_file_count; + __clar_default_fmt.logtarget.max_size = max_size; +} + static inline void clar_set_global_format(const char* restrict fmt) { __clar_default_fmt.format = fmt; } @@ -79,9 +97,16 @@ static inline void clar_set_global_loglevel(enum __clar_loglevel loglevel) { } static inline void clar_set_global_logger(struct clarifier* clar) { - memcpy(&__clar_default_fmt, clar, sizeof(clarifier)); + memcpy(&__clar_default_fmt, clar, sizeof(struct clarifier)); } +static inline void clar_set_rotation(struct clarifier* clar, const char* filename, uint16_t max_file_count, uint32_t max_size) { + clar->logtarget.filename = filename; + clar->logtarget.max_files = max_file_count; + clar->logtarget.max_size = max_size; +} + + #define __CLAR_EXPAND(x) x #define __CLAR_GET_MACRO(_1, name, ...) name #define CLAR_INIT(...) __CLAR_EXPAND(__CLAR_GET_MACRO(__VA_ARGS__, __CLAR_INIT2, __CLAR_INIT1)(__VA_ARGS__)) @@ -111,7 +136,7 @@ static inline void clar_log_with( va_list args; va_start(args, fmt); - __clar_log(&clar, loglevel, fmt, &args); + __clar_log(clar, loglevel, fmt, &args); va_end(args); } diff --git a/cclarify.c b/src/cclarify.c similarity index 66% rename from cclarify.c rename to src/cclarify.c index 03b2a02..7b78d26 100644 --- a/cclarify.c +++ b/src/cclarify.c @@ -2,10 +2,11 @@ #include #include #include +#include #include #include -#include "cclarify.h" +#include "cclarify/cclarify.h" #define CRED "\x1b[31m" #define CYELLOW "\x1b[33m" @@ -58,7 +59,7 @@ uint32_t __clar_format( struct clarifier* clar, enum __clar_loglevel loglevel, char* buf, - char* fmt, + const char* fmt, va_list* args, bool colors ) { @@ -146,6 +147,44 @@ void __clar_log( if (clar->logtarget.output_mask & CLAR_OUT_FILE && clar->logtarget.file) { uint32_t size = __clar_format(clar, loglevel, buf, fmt, args, false); + if (clar->logtarget.max_size != 0 && clar->logtarget.cur_size + size > clar->logtarget.max_size) { + uint16_t len = strlen(clar->logtarget.filename); + char* buf1 = alloca(len + 8); // Enough for suffix + char* buf2 = alloca(len + 8); + fclose(clar->logtarget.file); + clar->logtarget.file = NULL; + clar->logtarget.cur_size = 0; + for (int32_t i = clar->logtarget.max_files - 1; i >= 0; --i) { + switch (i) { + case 0: + clar->logtarget.file = fopen(clar->logtarget.filename, "w"); + if (!clar->logtarget.file && clar->logtarget.output_mask & CLAR_OUT_STDOUT) { + __clar_format(clar, CLAR_LOG_ERROR, buf, "[[cclarify]] Failed to open log file!", NULL, true); + fputs(buf, stdout); + return; + } + break; + case 1: + snprintf(buf1, len + 8, "%s.%d", clar->logtarget.filename, i); + if (rename(clar->logtarget.filename, buf1) && clar->logtarget.output_mask & CLAR_OUT_STDOUT) { + __clar_format(clar, CLAR_LOG_ERROR, buf, "[[cclarify]] Failed to rotate logs!", NULL, true); + fputs(buf, stdout); + return; + } + break; + default: + snprintf(buf1, len + 8, "%s.%d", clar->logtarget.filename, i); + snprintf(buf2, len + 8, "%s.%d", clar->logtarget.filename, i - 1); + if (rename(buf2, buf1) && clar->logtarget.output_mask & CLAR_OUT_STDOUT) { + __clar_format(clar, CLAR_LOG_ERROR, buf, "[[cclarify]] Failed to rotate logs!", NULL, true); + fputs(buf, stdout); + return; + } + break; + } + } + } + clar->logtarget.cur_size += size; fwrite(buf, 1, size, clar->logtarget.file); } } @@ -156,10 +195,14 @@ void clar_log( ... ); -[[gnu::constructor]] +[[gnu::constructor, gnu::visibility("hidden")]] void __clar_construct() { __clar_default_fmt.format = "%Y %M %d %D %H:%m:%s [%x] %l"; __clar_default_fmt.loglevel = CLAR_LOG_DEBUG; __clar_default_fmt.logtarget.output_mask = CLAR_OUT_STDOUT; } +[[gnu::destructor, gnu::visibility("hidden")]] +void __clar_destroy() { + +} diff --git a/test.c b/test.c index 693deb5..7e67135 100644 --- a/test.c +++ b/test.c @@ -2,7 +2,7 @@ int main(){ CLAR_INIT("%Y %M %d %D %H:%m:%s [%x] %l"); - struct Clarifier clar = clar_create_logger(CLAR_LOG_DEBUG, clar_create_logtarget("test.log", CLAR_OUT_FILE), "[%x] %l"); + struct clarifier clar = clar_create_logger(CLAR_LOG_DEBUG, clar_create_logtarget("test.log", CLAR_OUT_FILE), "[%x] %l"); clar_debug("test"); clar_info("test"); clar_warn("test"); @@ -18,4 +18,14 @@ int main(){ clar_debug("See? It works. Same with custom loggers."); clar.loglevel = CLAR_LOG_WARNING; clar_info_with(&clar, "You shouldn't see this."); + + struct clarifier clar2 = clar_create_logger(CLAR_LOG_DEBUG, clar_create_logtarget("test2.log", CLAR_OUT_FILE), "[%x] %l"); + clar_set_global_logger(&clar2); + clar_set_global_rotation("test2.log", 50, 65536); + + for (uint64_t i = 0; i < 10000000; ++i) { + clar_debug("Test, it: %d", i); + } + + puts("Completed 10.000.000 prints with max file count 50 and max size 64KB"); } From 6380a048f5c84eabd49e2e9698a403492ffb6dfb Mon Sep 17 00:00:00 2001 From: HyperWinX Date: Tue, 9 Sep 2025 09:18:41 +0400 Subject: [PATCH 08/22] Added simple workflow --- .github/workflows/ci.yml | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..62189d3 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,25 @@ +name: CClarify CI + +on: + push: + pull_request: + branches: [master] + +jobs: + build: + strategy: + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + std: [99, 11, 17, 23] + + runs-on: ${{ matrix.os }} + + steps: + - name: Install conan + uses: turtlebrowser/get-conan@main + + - name: Checkout code + uses: actions/checkout@v4 + + - name: build + run: conan build . From 2ca0b2af3903cbfcd4c54f940ad76ea55a100f02 Mon Sep 17 00:00:00 2001 From: HyperWinX Date: Tue, 9 Sep 2025 09:21:39 +0400 Subject: [PATCH 09/22] Fix workflow --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 62189d3..ddc8f3f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,7 +16,7 @@ jobs: steps: - name: Install conan - uses: turtlebrowser/get-conan@main + uses: conan-io/setup-conan@v1 - name: Checkout code uses: actions/checkout@v4 From 64440c510a8917281ecb63a803a1987a1f80ceeb Mon Sep 17 00:00:00 2001 From: HyperWinX Date: Tue, 9 Sep 2025 09:23:00 +0400 Subject: [PATCH 10/22] Exclude header --- src/cclarify.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/cclarify.c b/src/cclarify.c index 7b78d26..fc61ed4 100644 --- a/src/cclarify.c +++ b/src/cclarify.c @@ -1,9 +1,7 @@ -#include <__stdarg_va_list.h> #include #include #include #include -#include #include #include "cclarify/cclarify.h" From 671d254f7035f744aef0fd7acc74994dc2101a79 Mon Sep 17 00:00:00 2001 From: HyperWinX Date: Tue, 9 Sep 2025 09:24:29 +0400 Subject: [PATCH 11/22] Fix struct name --- include/cclarify/cclarify.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/cclarify/cclarify.h b/include/cclarify/cclarify.h index 1e4339e..ba9b02c 100644 --- a/include/cclarify/cclarify.h +++ b/include/cclarify/cclarify.h @@ -79,7 +79,7 @@ static inline void clar_destroy_logtarget(struct clarifier* clar) { if (clar->logtarget.file) { fclose(clar->logtarget.file); } - memset(&clar->logtarget, '\0', sizeof(__clar_logtarget)); + memset(&clar->logtarget, '\0', sizeof(struct __clar_logtarget)); } static inline void clar_set_global_rotation(const char* filename, uint16_t max_file_count, uint32_t max_size) { From f7745d43ea8f6812bfc8886093331ce2235c7370 Mon Sep 17 00:00:00 2001 From: HyperWinX Date: Tue, 9 Sep 2025 09:31:32 +0400 Subject: [PATCH 12/22] Remove unnecessary headers --- include/cclarify/cclarify.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/include/cclarify/cclarify.h b/include/cclarify/cclarify.h index ba9b02c..f0b92d7 100644 --- a/include/cclarify/cclarify.h +++ b/include/cclarify/cclarify.h @@ -1,7 +1,5 @@ #include #include -#include -#include #include #include #include From bc1027bc4a1078852e3d3b2f0ab06d76e9340aa2 Mon Sep 17 00:00:00 2001 From: HyperWinX Date: Tue, 9 Sep 2025 10:09:39 +0400 Subject: [PATCH 13/22] Changes --- CMakeLists.txt | 2 -- conanfile.py | 7 +++++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1d714ac..4aefb4d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,7 +4,5 @@ project(cclarify C) add_library(cclarify src/cclarify.c) target_include_directories(cclarify PUBLIC include) - - set_target_properties(cclarify PROPERTIES PUBLIC_HEADER "include/cclarify/cclarify.h") install(TARGETS cclarify) diff --git a/conanfile.py b/conanfile.py index 2238bca..8ac90c1 100644 --- a/conanfile.py +++ b/conanfile.py @@ -16,8 +16,9 @@ class pkgRecipe(ConanFile): # Binary configuration settings = "os", "compiler", "build_type", "arch" - options = {"shared": [True, False], "fPIC": [True, False]} - default_options = {"shared": False, "fPIC": True} + languages = "C" + options = {"shared": [True, False], "fPIC": [True, False], "std": [99, 11, 17, 23]} + default_options = {"shared": False, "fPIC": True, "std": 17} # Sources are located in the same place as this recipe, copy them to the recipe exports_sources = "CMakeLists.txt", "src/*", "include/*" @@ -37,6 +38,8 @@ def generate(self): deps = CMakeDeps(self) deps.generate() tc = CMakeToolchain(self) + tc.variables["CMAKE_C_STANDARD"] = self.options.std.value + tc.variables["CMAKE_C_STANDARD_REQUIRED"] = "ON" tc.generate() def build(self): From 2294b08d05854234c971ba6f6dc11f40c9b13e11 Mon Sep 17 00:00:00 2001 From: HyperWinX Date: Tue, 9 Sep 2025 10:21:50 +0400 Subject: [PATCH 14/22] Updated workflows --- .github/workflows/ci.yml | 2 +- CMakeLists.txt | 2 ++ conanfile.py | 3 ++- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ddc8f3f..401b55c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,4 +22,4 @@ jobs: uses: actions/checkout@v4 - name: build - run: conan build . + run: conan build . -o std=${{ matrix.std }} diff --git a/CMakeLists.txt b/CMakeLists.txt index 4aefb4d..11b798f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,8 @@ cmake_minimum_required(VERSION 3.15) project(cclarify C) +message(STATUS "Using C standard: ${CMAKE_C_STANDARD}") + add_library(cclarify src/cclarify.c) target_include_directories(cclarify PUBLIC include) diff --git a/conanfile.py b/conanfile.py index 8ac90c1..f734cfc 100644 --- a/conanfile.py +++ b/conanfile.py @@ -38,7 +38,8 @@ def generate(self): deps = CMakeDeps(self) deps.generate() tc = CMakeToolchain(self) - tc.variables["CMAKE_C_STANDARD"] = self.options.std.value + print(self.options.std.value) + tc.variables["CMAKE_C_STANDARD"] = str(self.options.std.value) tc.variables["CMAKE_C_STANDARD_REQUIRED"] = "ON" tc.generate() From 5cd809409da2897a340fe57fe957c972a09dc781 Mon Sep 17 00:00:00 2001 From: HyperWinX Date: Tue, 9 Sep 2025 10:22:18 +0400 Subject: [PATCH 15/22] Removed windows support --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 401b55c..32efa49 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,7 +9,7 @@ jobs: build: strategy: matrix: - os: [ubuntu-latest, windows-latest, macos-latest] + os: [ubuntu-latest, macos-latest] std: [99, 11, 17, 23] runs-on: ${{ matrix.os }} From 5d0bc2db47e3d39a042f4f5278a223f12d791b7a Mon Sep 17 00:00:00 2001 From: HyperWinX Date: Tue, 9 Sep 2025 10:24:21 +0400 Subject: [PATCH 16/22] Removed windows support --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 32efa49..c1d207f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,6 +11,7 @@ jobs: matrix: os: [ubuntu-latest, macos-latest] std: [99, 11, 17, 23] + compiler: [gcc, clang] runs-on: ${{ matrix.os }} From cc14edf8884f2d30b378e878b6e9fa48a169e4ab Mon Sep 17 00:00:00 2001 From: HyperWin <92534013+HyperWinX@users.noreply.github.com> Date: Tue, 9 Sep 2025 13:38:38 +0400 Subject: [PATCH 17/22] Update README.md --- README.md | 98 +++++++++++++++++++++---------------------------------- 1 file changed, 37 insertions(+), 61 deletions(-) diff --git a/README.md b/README.md index 9d12895..d36ae02 100644 --- a/README.md +++ b/README.md @@ -25,72 +25,48 @@ - [About project](#about-cclarify) - [Installation](#installation) - [Usage](#usage) - - [Assign](#assign) - - [Exec](#exec) - - [Msg](#msg) - - [Display](#display) + - [Loglevels](#loglevels) + - [Formatting rules](#formatting-rules) + - [Global formatter](#global-formatter-api) + # About CClarify -CClarify is a small framework for creating advanced logging system. It can write to your custom descriptor, pointing to buffer, and can save everything to file, if you set it to do so. +CClarify is a small logging library, written in pure C, that doesn't use any heap memory. ## Installation -1. Download main header [cclarify.h](cclarify.h) -2. Include it to your source file, where you want to add logging -3. Now you should set up everything. -```c -GLOBAL_INIT(); // Init logger variables -Clarifier clar; // Create logger object -// If you have int descriptor like stdout -init_loggerd(clar, descriptor); - -// If you want it to write logs to file -init_loggerf(clar, fd); - -// If you want it to write logs to both destinations -init_loggerfd(clar, descriptor, fd); -``` -4. You are good to go now! -## Usage -### Assign -If you want to assign some value to variable (custom types are not supported) you should do this: -```c -ASSIGN(clar, variable, value); -``` -It will output something like: -``` -==> Assigning value "value" to "variable", old value: "old_var_val" -``` -### Exec -If you wanna execute some function, and signal if function started or stopped, do this: -```c -EXEC(clar, function()); -``` -It will notify you about started execution: +If you use **Conan**, add the the package: ``` -==> Starting execution of function "function()" -``` -And about end of function execution. -``` -==> Execution of function "function()" finished +hyper-cclarify/ ``` +You can choose a version in **Releases** tab. -### Msg -To display some king of message, you need just to use MSG() function. -```c -// To display green info message, do this -MSG(clar, "Hello World!", INFO); -// Yellow - warning -MSG(clar, "Warning", WARNING); -// Red - error -MSG(clar, "Error", ERROR); -``` +## Usage +### Loglevels +CClarify has the following loglevels: +- **CLAR_LOG_DEBUG** +- **CLAR_LOG_INFO** +- **CLAR_LOG_WARNING** +- **CLAR_LOG_ERROR** +- **CLAR_LOG_FATAL** -### Display -Sometimes you want just to display variable value. Do this: -```c -DISPLAY(clar, variable); -``` -and you will get something like: -``` -==> Variable "variable" value: "420" -``` \ No newline at end of file +**Example**: +If you will set loglevel to **CLAR_LOG_WARNING**, all log calls with priority lower than this (i. e. **CLAR_LOG_INFO** and **CLAR_LOG_DEBUG**) won't print anything at all. + +### Formatting rules +| %Y | Insert current year (for example, **2025**) | +|----|-----------------------------------------------------------------------------------------| +| %M | Insert abbreviated month name (for example, **Sep**) | +| %d | Insert abbreviated day of week name (for example, **Mon**) | +| %D | Insert day of month as a decimal (for example, **09**) | +| %H | Insert hour as a decimal (for example, **13**) | +| %m | Insert minute as a decimal (for example, **32**) | +| %s | Insert second as a decifuncleuncal (for example, **47**) | +| %l | Insert formatted string from log() call | +| %x | Inserts message, specific to current loglevel (applying colors, if writing to terminal) | +| %% | Inserts a single percent | +| % | Inserts a single percent too - if the next character is not a valid format specifier | + +### Global formatter API +`clar_set_global_rotation(const char* filename, uint16_t max_files, uint32_t max_file_size)` - Enables log rotation when you enabled global output to file. Max file size is set in bytes! + +`clar_set_global_format(const char* fmf)` - Sets global format string. Notice, that this isstrinriclsricls. From 9494716e051d96632e0f10f2783f42be46c9c530 Mon Sep 17 00:00:00 2001 From: HyperWin <92534013+HyperWinX@users.noreply.github.com> Date: Tue, 9 Sep 2025 13:53:50 +0400 Subject: [PATCH 18/22] Update README.md --- README.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d36ae02..b7769e8 100644 --- a/README.md +++ b/README.md @@ -67,6 +67,14 @@ If you will set loglevel to **CLAR_LOG_WARNING**, all log calls with priority lo | % | Inserts a single percent too - if the next character is not a valid format specifier | ### Global formatter API -`clar_set_global_rotation(const char* filename, uint16_t max_files, uint32_t max_file_size)` - Enables log rotation when you enabled global output to file. Max file size is set in bytes! +`void clar_set_global_rotation(const char* filename, uint16_t max_files, uint32_t max_file_size)` - Enables log rotation when you enabled global output to file. Max file size is set in bytes. -`clar_set_global_format(const char* fmf)` - Sets global format string. Notice, that this isstrinriclsricls. +`void clar_set_global_format(const char* fmt)` - Sets global format string. Notice, that this is NOT a format string, that is used by libc's *printf functions! See [formatting rules](#formatting-rules) for more. + +`void clar_set_global_loglevel(__clar_loglevel loglevel)` - Sets global loglevel. + +`void clar_log(__clar_loglevel loglevel, const char* fmt, ...)` - Uses global formatter and ged loglevel to log. + +`void formattter and **CLAR_LOG_DEBUG** loglevel to log. + +`void clar_info(__clar_loglevel loglevel, From c50530db41a0386a7f8906d77bfa9f4abb75d759 Mon Sep 17 00:00:00 2001 From: HyperWinX Date: Tue, 9 Sep 2025 14:29:07 +0400 Subject: [PATCH 19/22] Update readme --- README.md | 42 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index b7769e8..0877b89 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,8 @@ - [Usage](#usage) - [Loglevels](#loglevels) - [Formatting rules](#formatting-rules) - - [Global formatter](#global-formatter-api) + - [Global formatter API](#global-formatter-api) + - [Using custom loggers](#using-custom-loggers) # About CClarify @@ -73,8 +74,41 @@ If you will set loglevel to **CLAR_LOG_WARNING**, all log calls with priority lo `void clar_set_global_loglevel(__clar_loglevel loglevel)` - Sets global loglevel. -`void clar_log(__clar_loglevel loglevel, const char* fmt, ...)` - Uses global formatter and ged loglevel to log. +`void clar_log(__clar_loglevel loglevel, const char* fmt, ...)` - Uses global logger and provided loglevel to log. -`void formattter and **CLAR_LOG_DEBUG** loglevel to log. +`void clar_debug(const char* fmt, ...)` - Uses global logger and **CLAR_LOG_DEBUG** loglevel to log. + +`void clar_info(const char* fmt, ...)` - Uses global logger and **CLAR_LOG_INFO** loglevel to log. + +`void clar_warn(const char* fmt, ...)` - Uses global logger and **CLAR_LOG_WARNING** loglevel to log. + +`void clar_error(const char* fmt, ...)` - Uses global logger and **CLAR_LOG_ERROR** loglevel to log. + +`void clar_fatal(const char* fmt, ...)` - Uses global logger and **CLAR_LOG_FATAL** loglevel to log. + +### Using custom loggers + +#### Create custom logger: +``` +struct clarifier clar = clar_create_logger(CLAR_LOG_DEBUG, clar_create_logtarget("test.log", CLAR_OUT_STDOUT | CLAR_OUT_FILE), "[%x] %l"); +``` +What we are doing here: +- Create logger with **CLAR_LOG_DEBUG** loglevel +- With logtarget: + - Write to file test.log + - Write to both file and stdout, i. e. terminal +- With formatting string "[%x] %l" + +#### How to use: +`void clar_log_with(struct clarifier* clar, __clar_loglevel loglevel, const char* fmt, ...)` - Uses custom logger and provided loglevel to log. + +`void clar_debug(struct clarifier* clar, const char* fmt, ...)` - Uses custom logger and **CLAR_LOG_DEBUG** loglevel to log. + +`void clar_info(struct clarifier* clar, const char* fmt, ...)` - Uses custom logger and **CLAR_LOG_INFO** loglevel to log. + +`void clar_warn(struct clarifier* clar, const char* fmt, ...)` - Uses custom logger and **CLAR_LOG_WARNING** loglevel to log. + +`void clar_error(struct clarifier* clar, const char* fmt, ...)` - Uses custom logger and **CLAR_LOG_ERROR** loglevel to log. + +`void clar_fatal(struct clarifier* clar, const char* fmt, ...)` - Uses custom logger and **CLAR_LOG_FATAL** loglevel to log. -`void clar_info(__clar_loglevel loglevel, From 2b1da016272461aaf5506701c2153709615dd162 Mon Sep 17 00:00:00 2001 From: HyperWinX Date: Wed, 10 Sep 2025 21:40:33 +0400 Subject: [PATCH 20/22] Conan changes --- .gitmodules | 3 +++ CMakeLists.txt | 6 ++++++ conanfile.py | 8 ++++++++ dist/munit | 1 + src/cclarify.c | 9 +++------ test_package/CMakeLists.txt | 12 ++++++++++++ test_package/conanfile.py | 26 ++++++++++++++++++++++++++ test_package/src/tests.c | 0 8 files changed, 59 insertions(+), 6 deletions(-) create mode 100644 .gitmodules create mode 160000 dist/munit create mode 100644 test_package/CMakeLists.txt create mode 100644 test_package/conanfile.py create mode 100644 test_package/src/tests.c diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..2711953 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "dist/munit"] + path = dist/munit + url = https://github.com/nemequ/munit diff --git a/CMakeLists.txt b/CMakeLists.txt index 11b798f..1258d99 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,3 +8,9 @@ target_include_directories(cclarify PUBLIC include) set_target_properties(cclarify PROPERTIES PUBLIC_HEADER "include/cclarify/cclarify.h") install(TARGETS cclarify) + +add_executable(cclarify-tests + src/tests.c + ${CMAKE_SOURCE_DIR}/dist/munit/munit.c) +target_include_directories(cclarify-tests ${CMAKE_SOURCE_DIR}/dist/munit) +target_link_libraries(cclarify-tests cclarify) diff --git a/conanfile.py b/conanfile.py index f734cfc..c630676 100644 --- a/conanfile.py +++ b/conanfile.py @@ -23,6 +23,9 @@ class pkgRecipe(ConanFile): # Sources are located in the same place as this recipe, copy them to the recipe exports_sources = "CMakeLists.txt", "src/*", "include/*" + def requirements(self): + self.tested_reference_str = "cclarify/0.1" + def config_options(self): if self.settings.os == "Windows": self.options.rm_safe("fPIC") @@ -48,6 +51,11 @@ def build(self): cmake.configure() cmake.build() + def test(self): + if can_run(self): + cmd = os.path.join(self.cpp.build.bindir, "cclarify-tests") + self.run(cmd, env="conanrun") + def package(self): cmake = CMake(self) cmake.install() diff --git a/dist/munit b/dist/munit new file mode 160000 index 0000000..fbbdf14 --- /dev/null +++ b/dist/munit @@ -0,0 +1 @@ +Subproject commit fbbdf1467eb0d04a6ee465def2e529e4c87f2118 diff --git a/src/cclarify.c b/src/cclarify.c index fc61ed4..11ee962 100644 --- a/src/cclarify.c +++ b/src/cclarify.c @@ -33,7 +33,6 @@ char* __clar_fmt = NULL; char* __clar_buf = NULL; bool __clar_logging_enabled = true; -[[gnu::visibility("hidden")]] uint32_t __clar_get_fmt_size( const char* restrict fmt, va_list* args @@ -41,7 +40,6 @@ uint32_t __clar_get_fmt_size( return vsnprintf(NULL, 0, fmt, *args); } -[[gnu::visibility("hidden")]] void sputs( char* buf, const char* src, @@ -52,7 +50,6 @@ void sputs( *offset += len; } -[[gnu::visibility("hidden")]] uint32_t __clar_format( struct clarifier* clar, enum __clar_loglevel loglevel, @@ -193,14 +190,14 @@ void clar_log( ... ); -[[gnu::constructor, gnu::visibility("hidden")]] +[[gnu::constructor]] void __clar_construct() { __clar_default_fmt.format = "%Y %M %d %D %H:%m:%s [%x] %l"; __clar_default_fmt.loglevel = CLAR_LOG_DEBUG; __clar_default_fmt.logtarget.output_mask = CLAR_OUT_STDOUT; } -[[gnu::destructor, gnu::visibility("hidden")]] +[[gnu::destructor]] void __clar_destroy() { - + clar_destroy_logger(&__clar_default_fmt); } diff --git a/test_package/CMakeLists.txt b/test_package/CMakeLists.txt new file mode 100644 index 0000000..ddebc3c --- /dev/null +++ b/test_package/CMakeLists.txt @@ -0,0 +1,12 @@ +cmake_minimum_required(VERSION 3.15) +project(cclarify_tests CXX) + +find_package(cclarify CONFIG REQUIRED) + +get_target_property(CCLARIFY_SRC_DIR cclarify::cclarify IMPORTED_LOCATION) + +add_executable(cclarify-tests + src/tests.c + ${CCLARIFY_SRC_DIR}/dist/munit/munit.c) +target_include_directories(cclarify-tests ${CCLARIFY_SRC_DIR}/dist/munit) +target_link_libraries(cclarify-tests hello::hello) diff --git a/test_package/conanfile.py b/test_package/conanfile.py new file mode 100644 index 0000000..f426ab5 --- /dev/null +++ b/test_package/conanfile.py @@ -0,0 +1,26 @@ +import os + +from conan import ConanFile +from conan.tools.cmake import CMake, cmake_layout +from conan.tools.build import can_run + + +class CClarify(ConanFile): + settings = "os", "compiler", "build_type", "arch" + generators = "CMakeDeps", "CMakeToolchain" + + def requirements(self): + self.requires(self.tested_reference_str) + + def build(self): + cmake = CMake(self) + cmake.configure() + cmake.build() + + def layout(self): + cmake_layout(self) + + def test(self): + if can_run(self): + cmd = os.path.join(self.cpp.build.bindir, "cclarify-tests") + self.run(cmd, env="conanrun") diff --git a/test_package/src/tests.c b/test_package/src/tests.c new file mode 100644 index 0000000..e69de29 From c34b41a6a79c2f2cbe3cf822b75d20c12491a537 Mon Sep 17 00:00:00 2001 From: HyperWinX Date: Wed, 10 Sep 2025 22:17:37 +0400 Subject: [PATCH 21/22] Fixes --- CMakeLists.txt | 8 +------- conanfile.py | 17 ++--------------- test_package/CMakeLists.txt | 12 +++++------- test_package/conanfile.py | 2 +- 4 files changed, 9 insertions(+), 30 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1258d99..1c507ae 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,10 +7,4 @@ add_library(cclarify src/cclarify.c) target_include_directories(cclarify PUBLIC include) set_target_properties(cclarify PROPERTIES PUBLIC_HEADER "include/cclarify/cclarify.h") -install(TARGETS cclarify) - -add_executable(cclarify-tests - src/tests.c - ${CMAKE_SOURCE_DIR}/dist/munit/munit.c) -target_include_directories(cclarify-tests ${CMAKE_SOURCE_DIR}/dist/munit) -target_link_libraries(cclarify-tests cclarify) +install(TARGETS cclarify) \ No newline at end of file diff --git a/conanfile.py b/conanfile.py index c630676..bd49bdc 100644 --- a/conanfile.py +++ b/conanfile.py @@ -2,30 +2,23 @@ from conan.tools.cmake import CMakeToolchain, CMake, cmake_layout, CMakeDeps -class pkgRecipe(ConanFile): +class CCLarifyRecipe(ConanFile): name = "cclarify" version = "0.1" package_type = "library" - # Optional metadata license = "GPLv3" author = "HyperWinX" url = "https://github.com/HyperWinX/CClarify.git" description = "Small and powerful logging library for C applications" topics = ("c", "logging") - # Binary configuration settings = "os", "compiler", "build_type", "arch" - languages = "C" options = {"shared": [True, False], "fPIC": [True, False], "std": [99, 11, 17, 23]} default_options = {"shared": False, "fPIC": True, "std": 17} - # Sources are located in the same place as this recipe, copy them to the recipe exports_sources = "CMakeLists.txt", "src/*", "include/*" - def requirements(self): - self.tested_reference_str = "cclarify/0.1" - def config_options(self): if self.settings.os == "Windows": self.options.rm_safe("fPIC") @@ -41,7 +34,6 @@ def generate(self): deps = CMakeDeps(self) deps.generate() tc = CMakeToolchain(self) - print(self.options.std.value) tc.variables["CMAKE_C_STANDARD"] = str(self.options.std.value) tc.variables["CMAKE_C_STANDARD_REQUIRED"] = "ON" tc.generate() @@ -51,15 +43,10 @@ def build(self): cmake.configure() cmake.build() - def test(self): - if can_run(self): - cmd = os.path.join(self.cpp.build.bindir, "cclarify-tests") - self.run(cmd, env="conanrun") - def package(self): cmake = CMake(self) cmake.install() def package_info(self): - self.cpp_info.libs = ["mypkg"] + self.cpp_info.libs = ["cclarify"] diff --git a/test_package/CMakeLists.txt b/test_package/CMakeLists.txt index ddebc3c..a2a2480 100644 --- a/test_package/CMakeLists.txt +++ b/test_package/CMakeLists.txt @@ -1,12 +1,10 @@ cmake_minimum_required(VERSION 3.15) -project(cclarify_tests CXX) +project(cclarify_test C) find_package(cclarify CONFIG REQUIRED) -get_target_property(CCLARIFY_SRC_DIR cclarify::cclarify IMPORTED_LOCATION) - add_executable(cclarify-tests - src/tests.c - ${CCLARIFY_SRC_DIR}/dist/munit/munit.c) -target_include_directories(cclarify-tests ${CCLARIFY_SRC_DIR}/dist/munit) -target_link_libraries(cclarify-tests hello::hello) + ${CMAKE_CURRENT_SOURCE_DIR}/../dist/munit/munit.c + ${PROJECT_SOURCE_DIR}/src/tests.c) +target_include_directories(cclarify-tests PUBLIC ${cclarify_SOURCE_DIR}/dist/munit) +target_link_libraries(cclarify-tests cclarify::cclarify) diff --git a/test_package/conanfile.py b/test_package/conanfile.py index f426ab5..aed6f6e 100644 --- a/test_package/conanfile.py +++ b/test_package/conanfile.py @@ -5,7 +5,7 @@ from conan.tools.build import can_run -class CClarify(ConanFile): +class pkgTestConan(ConanFile): settings = "os", "compiler", "build_type", "arch" generators = "CMakeDeps", "CMakeToolchain" From d1864eab8d0f45e45a93b65d0b80e4b09c42c1aa Mon Sep 17 00:00:00 2001 From: HyperWinX Date: Fri, 12 Sep 2025 08:26:31 +0400 Subject: [PATCH 22/22] Final changes, prepare for upload --- .github/workflows/ci.yml | 6 +- CMakeLists.txt | 4 +- README.md | 11 ++ conanfile.py | 4 +- src/cclarify.c | 9 +- test_package/CMakeLists.txt | 9 +- test_package/src/tests.c | 213 ++++++++++++++++++++++++++++++++++++ 7 files changed, 245 insertions(+), 11 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c1d207f..3143a6f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,6 +21,8 @@ jobs: - name: Checkout code uses: actions/checkout@v4 + with: + submodules: true - - name: build - run: conan build . -o std=${{ matrix.std }} + - name: Build & test + run: conan create . -o std=${{ matrix.std }} -s compiler=${{ matrix.compiler }} -s compiler.version=14 -s compiler.libcxx=libstdc++ diff --git a/CMakeLists.txt b/CMakeLists.txt index 1c507ae..87a9706 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.15) -project(cclarify C) +project(hyper-cclarify C) message(STATUS "Using C standard: ${CMAKE_C_STANDARD}") @@ -7,4 +7,4 @@ add_library(cclarify src/cclarify.c) target_include_directories(cclarify PUBLIC include) set_target_properties(cclarify PROPERTIES PUBLIC_HEADER "include/cclarify/cclarify.h") -install(TARGETS cclarify) \ No newline at end of file +install(TARGETS cclarify) diff --git a/README.md b/README.md index 0877b89..3be3d18 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,7 @@ - [About project](#about-cclarify) - [Installation](#installation) - [Usage](#usage) + - [Quickstart](#quickstart) - [Loglevels](#loglevels) - [Formatting rules](#formatting-rules) - [Global formatter API](#global-formatter-api) @@ -42,6 +43,16 @@ hyper-cclarify/ You can choose a version in **Releases** tab. ## Usage +### Quickstart +If you don't need all these fancy things, you can just start logging - it will work! +```c +#include "cclarify.h" + +int main() { + clar_info("Program is running"); +} +``` + ### Loglevels CClarify has the following loglevels: - **CLAR_LOG_DEBUG** diff --git a/conanfile.py b/conanfile.py index bd49bdc..ce3a34d 100644 --- a/conanfile.py +++ b/conanfile.py @@ -3,8 +3,8 @@ class CCLarifyRecipe(ConanFile): - name = "cclarify" - version = "0.1" + name = "hyper-cclarify" + version = "1.0" package_type = "library" license = "GPLv3" diff --git a/src/cclarify.c b/src/cclarify.c index 11ee962..5b58f0d 100644 --- a/src/cclarify.c +++ b/src/cclarify.c @@ -115,7 +115,7 @@ uint32_t __clar_format( break; } } - buf[offset++] = '\n'; + //buf[offset++] = '\n'; return offset; } @@ -138,10 +138,11 @@ void __clar_log( if (clar->logtarget.output_mask & CLAR_OUT_STDOUT) { __clar_format(clar, loglevel, buf, fmt, args, true); fputs(buf, stdout); + fputc('\n', stdout); } if (clar->logtarget.output_mask & CLAR_OUT_FILE && clar->logtarget.file) { - uint32_t size = __clar_format(clar, loglevel, buf, fmt, args, false); + uint32_t size = __clar_format(clar, loglevel, buf, fmt, args, false) + 1; if (clar->logtarget.max_size != 0 && clar->logtarget.cur_size + size > clar->logtarget.max_size) { uint16_t len = strlen(clar->logtarget.filename); char* buf1 = alloca(len + 8); // Enough for suffix @@ -156,6 +157,7 @@ void __clar_log( if (!clar->logtarget.file && clar->logtarget.output_mask & CLAR_OUT_STDOUT) { __clar_format(clar, CLAR_LOG_ERROR, buf, "[[cclarify]] Failed to open log file!", NULL, true); fputs(buf, stdout); + fputc('\n', stdout); return; } break; @@ -164,6 +166,7 @@ void __clar_log( if (rename(clar->logtarget.filename, buf1) && clar->logtarget.output_mask & CLAR_OUT_STDOUT) { __clar_format(clar, CLAR_LOG_ERROR, buf, "[[cclarify]] Failed to rotate logs!", NULL, true); fputs(buf, stdout); + fputc('\n', stdout); return; } break; @@ -173,6 +176,7 @@ void __clar_log( if (rename(buf2, buf1) && clar->logtarget.output_mask & CLAR_OUT_STDOUT) { __clar_format(clar, CLAR_LOG_ERROR, buf, "[[cclarify]] Failed to rotate logs!", NULL, true); fputs(buf, stdout); + fputc('\n', stdout); return; } break; @@ -181,6 +185,7 @@ void __clar_log( } clar->logtarget.cur_size += size; fwrite(buf, 1, size, clar->logtarget.file); + fputc('\n', clar->logtarget.file); } } diff --git a/test_package/CMakeLists.txt b/test_package/CMakeLists.txt index a2a2480..ed3b566 100644 --- a/test_package/CMakeLists.txt +++ b/test_package/CMakeLists.txt @@ -1,10 +1,13 @@ cmake_minimum_required(VERSION 3.15) project(cclarify_test C) -find_package(cclarify CONFIG REQUIRED) +find_package(hyper-cclarify CONFIG REQUIRED) + +set(CMAKE_C_FLAGS "-O0 -g") +set(CMAKE_CXX_FLAGS "-O0 -g") add_executable(cclarify-tests ${CMAKE_CURRENT_SOURCE_DIR}/../dist/munit/munit.c ${PROJECT_SOURCE_DIR}/src/tests.c) -target_include_directories(cclarify-tests PUBLIC ${cclarify_SOURCE_DIR}/dist/munit) -target_link_libraries(cclarify-tests cclarify::cclarify) +target_include_directories(cclarify-tests PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../dist/munit) +target_link_libraries(cclarify-tests hyper-cclarify::hyper-cclarify) diff --git a/test_package/src/tests.c b/test_package/src/tests.c index e69de29..cff5012 100644 --- a/test_package/src/tests.c +++ b/test_package/src/tests.c @@ -0,0 +1,213 @@ +#include +#include +#include + +#include "munit.h" +#include "cclarify.h" + +extern uint32_t __clar_format( + struct clarifier* clar, + enum __clar_loglevel loglevel, + char* buf, + const char* fmt, + va_list* args, + bool colors + ); + +MunitResult clar_year_test(const MunitParameter params[], void* ptr) { + char buf1[256], buf2[256]; + time_t tm; + struct tm* tm_; + + tm = time(NULL); + tm_ = localtime(&tm); + + clar_set_global_format("%Y"); + strftime(buf1, 255, "%Y", tm_); + __clar_format(&__clar_default_fmt, CLAR_LOG_DEBUG, buf2, "", NULL, true); + + munit_assert_string_equal(buf1, buf2); + + return MUNIT_OK; +} + +MunitResult clar_month_test(const MunitParameter params[], void* ptr) { + char buf1[256], buf2[256]; + time_t tm; + struct tm* tm_; + + tm = time(NULL); + tm_ = localtime(&tm); + + clar_set_global_format("%M"); + strftime(buf1, 255, "%b", tm_); + __clar_format(&__clar_default_fmt, CLAR_LOG_DEBUG, buf2, "", NULL, true); + + munit_assert_string_equal(buf1, buf2); + + return MUNIT_OK; +} + +MunitResult clar_day_test(const MunitParameter params[], void* ptr) { + char buf1[256], buf2[256]; + time_t tm; + struct tm* tm_; + + tm = time(NULL); + tm_ = localtime(&tm); + + clar_set_global_format("%d"); + strftime(buf1, 255, "%a", tm_); + __clar_format(&__clar_default_fmt, CLAR_LOG_DEBUG, buf2, "", NULL, true); + + munit_assert_string_equal(buf1, buf2); + + return MUNIT_OK; +} + +MunitResult clar_dof_test(const MunitParameter params[], void* ptr) { + char buf1[256], buf2[256]; + time_t tm; + struct tm* tm_; + + tm = time(NULL); + tm_ = localtime(&tm); + + clar_set_global_format("%D"); + strftime(buf1, 255, "%d", tm_); + __clar_format(&__clar_default_fmt, CLAR_LOG_DEBUG, buf2, "", NULL, true); + + munit_assert_string_equal(buf1, buf2); + + return MUNIT_OK; +} + +MunitResult clar_hour_test(const MunitParameter params[], void* ptr) { + char buf1[256], buf2[256]; + time_t tm; + struct tm* tm_; + + tm = time(NULL); + tm_ = localtime(&tm); + + clar_set_global_format("%H"); + strftime(buf1, 255, "%H", tm_); + __clar_format(&__clar_default_fmt, CLAR_LOG_DEBUG, buf2, "", NULL, true); + + munit_assert_string_equal(buf1, buf2); + + return MUNIT_OK; +} + +MunitResult clar_minute_test(const MunitParameter params[], void* ptr) { + char buf1[256], buf2[256]; + time_t tm; + struct tm* tm_; + + tm = time(NULL); + tm_ = localtime(&tm); + + clar_set_global_format("%m"); + strftime(buf1, 255, "%M", tm_); + __clar_format(&__clar_default_fmt, CLAR_LOG_DEBUG, buf2, "", NULL, true); + + munit_assert_string_equal(buf1, buf2); + + return MUNIT_OK; +} + +MunitResult clar_second_test(const MunitParameter params[], void* ptr) { + char buf1[256], buf2[256]; + time_t tm; + struct tm* tm_; + + tm = time(NULL); + tm_ = localtime(&tm); + + clar_set_global_format("%s"); + strftime(buf1, 255, "%S", tm_); + __clar_format(&__clar_default_fmt, CLAR_LOG_DEBUG, buf2, "", NULL, true); + + munit_assert_string_equal(buf1, buf2); + + return MUNIT_OK; +} + + +MunitTest tests[] = { + { + "cclarify-year-test", + clar_year_test, + NULL, + NULL, + MUNIT_TEST_OPTION_NONE, + NULL + }, + { + "cclarify-month-test", + clar_month_test, + NULL, + NULL, + MUNIT_TEST_OPTION_NONE, + NULL + }, + { + "cclarify-day-test", + clar_day_test, + NULL, + NULL, + MUNIT_TEST_OPTION_NONE, + NULL + }, + { + "cclarify-dof-test", + clar_dof_test, + NULL, + NULL, + MUNIT_TEST_OPTION_NONE, + NULL + }, + { + "cclarify-hour-test", + clar_hour_test, + NULL, + NULL, + MUNIT_TEST_OPTION_NONE, + NULL + }, + { + "cclarify-minute-test", + clar_minute_test, + NULL, + NULL, + MUNIT_TEST_OPTION_NONE, + NULL + }, + { + "cclarify-second-test", + clar_second_test, + NULL, + NULL, + MUNIT_TEST_OPTION_NONE, + NULL + }, + { + NULL, + NULL, + NULL, + NULL, + MUNIT_TEST_OPTION_NONE, NULL + } +}; + +static const MunitSuite test_suite = { + (char*)"", + tests, + NULL, + 128, + MUNIT_SUITE_OPTION_NONE +}; + +int main(int argc, char** argv) { + return munit_suite_main(&test_suite, (void*)"cclarify", argc, argv); +}