dlg.c (21039B)
1 // Copyright (c) 2019 nyorain 2 // Distributed under the Boost Software License, Version 1.0. 3 // See accompanying file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt 4 5 #define _XOPEN_SOURCE 600 6 #define _POSIX_C_SOURCE 200809L 7 #define _WIN32_WINNT 0x0600 8 9 // Needed on windows so that we can use sprintf without warning. 10 #define _CRT_SECURE_NO_WARNINGS 11 12 #include <dlg/output.h> 13 #include <dlg/dlg.h> 14 #include <wchar.h> 15 #include <time.h> 16 #include <stdio.h> 17 #include <stdlib.h> 18 #include <string.h> 19 20 const char* const dlg_reset_sequence = "\033[0m"; 21 const struct dlg_style dlg_default_output_styles[] = { 22 {dlg_text_style_italic, dlg_color_green, dlg_color_none}, 23 {dlg_text_style_dim, dlg_color_gray, dlg_color_none}, 24 {dlg_text_style_none, dlg_color_cyan, dlg_color_none}, 25 {dlg_text_style_none, dlg_color_yellow, dlg_color_none}, 26 {dlg_text_style_none, dlg_color_red, dlg_color_none}, 27 {dlg_text_style_bold, dlg_color_red, dlg_color_none} 28 }; 29 30 static void* xalloc(size_t size) { 31 void* ret = calloc(size, 1); 32 if(!ret) fprintf(stderr, "dlg: calloc returned NULL, probably crashing (size: %zu)\n", size); 33 return ret; 34 } 35 36 static void* xrealloc(void* ptr, size_t size) { 37 void* ret = realloc(ptr, size); 38 if(!ret) fprintf(stderr, "dlg: realloc returned NULL, probably crashing (size: %zu)\n", size); 39 return ret; 40 } 41 42 struct dlg_tag_func_pair { 43 const char* tag; 44 const char* func; 45 }; 46 47 struct dlg_data { 48 const char** tags; // vec 49 struct dlg_tag_func_pair* pairs; // vec 50 char* buffer; 51 size_t buffer_size; 52 }; 53 54 static dlg_handler g_handler = dlg_default_output; 55 static void* g_data = NULL; 56 57 static void dlg_free_data(void* data); 58 static struct dlg_data* dlg_create_data(void); 59 60 // platform-specific 61 #if defined(__unix__) || defined(__unix) || defined(__linux__) || defined(__APPLE__) || defined(__MACH__) 62 #define DLG_OS_UNIX 63 #include <unistd.h> 64 #include <pthread.h> 65 #include <sys/time.h> 66 67 static pthread_key_t dlg_data_key; 68 69 static void dlg_main_cleanup(void) { 70 void* data = pthread_getspecific(dlg_data_key); 71 if(data) { 72 dlg_free_data(data); 73 pthread_setspecific(dlg_data_key, NULL); 74 } 75 } 76 77 static void init_data_key(void) { 78 pthread_key_create(&dlg_data_key, dlg_free_data); 79 atexit(dlg_main_cleanup); 80 } 81 82 static struct dlg_data* dlg_data(void) { 83 static pthread_once_t key_once = PTHREAD_ONCE_INIT; 84 pthread_once(&key_once, init_data_key); 85 86 void* data = pthread_getspecific(dlg_data_key); 87 if(!data) { 88 data = dlg_create_data(); 89 pthread_setspecific(dlg_data_key, data); 90 } 91 92 return (struct dlg_data*) data; 93 } 94 95 static void lock_file(FILE* file) { 96 flockfile(file); 97 } 98 99 static void unlock_file(FILE* file) { 100 funlockfile(file); 101 } 102 103 bool dlg_is_tty(FILE* stream) { 104 return isatty(fileno(stream)); 105 } 106 107 static unsigned get_msecs(void) { 108 struct timeval tv; 109 gettimeofday(&tv, NULL); 110 return tv.tv_usec; 111 } 112 113 // platform switch -- end unix 114 #elif defined(WIN32) || defined(_WIN32) || defined(_WIN64) 115 #define DLG_OS_WIN 116 #define WIN32_LEAN_AND_MEAN 117 #define DEFINE_CONSOLEV2_PROPERTIES 118 #include <windows.h> 119 #include <io.h> 120 121 // thanks for nothing, microsoft 122 #ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING 123 #define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004 124 #endif 125 126 // the max buffer size we will convert on the stack 127 #define DLG_MAX_STACK_BUF_SIZE 1024 128 129 static void WINAPI dlg_fls_destructor(void* data) { 130 dlg_free_data(data); 131 } 132 133 // TODO: error handling 134 static BOOL CALLBACK dlg_init_fls(PINIT_ONCE io, void* param, void** lpContext) { 135 (void) io; 136 (void) param; 137 **((DWORD**) lpContext) = FlsAlloc(dlg_fls_destructor); 138 return true; 139 } 140 141 static struct dlg_data* dlg_data(void) { 142 static INIT_ONCE init_once = INIT_ONCE_STATIC_INIT; 143 static DWORD fls = 0; 144 void* flsp = (void*) &fls; 145 InitOnceExecuteOnce(&init_once, dlg_init_fls, NULL, &flsp); 146 void* data = FlsGetValue(fls); 147 if(!data) { 148 data = dlg_create_data(); 149 FlsSetValue(fls, data); 150 } 151 152 return (struct dlg_data*) data; 153 } 154 155 static void lock_file(FILE* file) { 156 _lock_file(file); 157 } 158 159 static void unlock_file(FILE* file) { 160 _unlock_file(file); 161 } 162 163 bool dlg_is_tty(FILE* stream) { 164 return _isatty(_fileno(stream)); 165 } 166 167 #ifdef DLG_WIN_CONSOLE 168 static bool init_ansi_console(void) { 169 HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE); 170 HANDLE err = GetStdHandle(STD_ERROR_HANDLE); 171 if(out == INVALID_HANDLE_VALUE || err == INVALID_HANDLE_VALUE) 172 return false; 173 174 DWORD outMode, errMode; 175 if(!GetConsoleMode(out, &outMode) || !GetConsoleMode(err, &errMode)) 176 return false; 177 178 outMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; 179 errMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; 180 if(!SetConsoleMode(out, outMode) || !SetConsoleMode(out, errMode)) 181 return false; 182 183 return true; 184 } 185 186 static bool win_write_heap(void* handle, int needed, const char* format, va_list args) { 187 char* buf1 = xalloc(3 * needed + 3 + (needed % 2)); 188 wchar_t* buf2 = (wchar_t*) (buf1 + needed + 1 + (needed % 2)); 189 vsnprintf(buf1, needed + 1, format, args); 190 needed = MultiByteToWideChar(CP_UTF8, 0, buf1, needed, buf2, needed + 1); 191 bool ret = (needed != 0 && WriteConsoleW(handle, buf2, needed, NULL, NULL) != 0); 192 free(buf1); 193 return ret; 194 } 195 196 static bool win_write_stack(void* handle, int needed, const char* format, va_list args) { 197 char buf1[DLG_MAX_STACK_BUF_SIZE]; 198 wchar_t buf2[DLG_MAX_STACK_BUF_SIZE]; 199 vsnprintf(buf1, needed + 1, format, args); 200 needed = MultiByteToWideChar(CP_UTF8, 0, buf1, needed, buf2, needed + 1); 201 return (needed != 0 && WriteConsoleW(handle, buf2, needed, NULL, NULL) != 0); 202 } 203 #endif // DLG_WIN_CONSOLE 204 205 static unsigned get_msecs() { 206 SYSTEMTIME st; 207 GetSystemTime(&st); 208 return st.wMilliseconds; 209 } 210 211 #else // platform switch -- end windows 212 #error Cannot determine platform (needed for color and utf-8 and stuff) 213 #endif 214 215 // general 216 void dlg_escape_sequence(struct dlg_style style, char buf[12]) { 217 int nums[3]; 218 unsigned int count = 0; 219 220 if(style.fg != dlg_color_none) { 221 nums[count++] = style.fg + 30; 222 } 223 224 if(style.bg != dlg_color_none) { 225 nums[count++] = style.fg + 40; 226 } 227 228 if(style.style != dlg_text_style_none) { 229 nums[count++] = style.style; 230 } 231 232 switch(count) { 233 case 1: snprintf(buf, 12, "\033[%dm", nums[0]); break; 234 case 2: snprintf(buf, 12, "\033[%d;%dm", nums[0], nums[1]); break; 235 case 3: snprintf(buf, 12, "\033[%d;%d;%dm", nums[0], nums[1], nums[2]); break; 236 default: buf[0] = '\0'; break; 237 } 238 } 239 240 int dlg_vfprintf(FILE* stream, const char* format, va_list args) { 241 #if defined(DLG_OS_WIN) && defined(DLG_WIN_CONSOLE) 242 void* handle = NULL; 243 if(stream == stdout) { 244 handle = GetStdHandle(STD_OUTPUT_HANDLE); 245 } else if(stream == stderr) { 246 handle = GetStdHandle(STD_ERROR_HANDLE); 247 } 248 249 if(handle) { 250 va_list args_copy; 251 va_copy(args_copy, args); 252 int needed = vsnprintf(NULL, 0, format, args_copy); 253 va_end(args_copy); 254 255 if(needed < 0) { 256 return needed; 257 } 258 259 // We don't allocate too much on the stack 260 // but we also don't want to call alloc every logging call 261 // or use another cached buffer 262 if(needed >= DLG_MAX_STACK_BUF_SIZE) { 263 if(win_write_heap(handle, needed, format, args)) { 264 return needed; 265 } 266 } else { 267 if(win_write_stack(handle, needed, format, args)) { 268 return needed; 269 } 270 } 271 } 272 #endif 273 274 return vfprintf(stream, format, args); 275 } 276 277 int dlg_fprintf(FILE* stream, const char* format, ...) { 278 va_list args; 279 va_start(args, format); 280 int ret = dlg_vfprintf(stream, format, args); 281 va_end(args); 282 return ret; 283 } 284 285 int dlg_styled_fprintf(FILE* stream, struct dlg_style style, const char* format, ...) { 286 char buf[12]; 287 dlg_escape_sequence(style, buf); 288 289 fprintf(stream, "%s", buf); 290 va_list args; 291 va_start(args, format); 292 int ret = dlg_vfprintf(stream, format, args); 293 va_end(args); 294 fprintf(stream, "%s", dlg_reset_sequence); 295 return ret; 296 } 297 298 void dlg_generic_output(dlg_generic_output_handler output, void* data, 299 unsigned int features, const struct dlg_origin* origin, const char* string, 300 const struct dlg_style styles[6]) { 301 // We never print any dynamic content below so we can be sure at compile 302 // time that a buffer of size 64 is large enough. 303 char format_buf[64]; 304 char* format = format_buf; 305 306 if(features & dlg_output_style) { 307 format += sprintf(format, "%%s"); 308 } 309 310 if(features & (dlg_output_time | dlg_output_file_line | dlg_output_tags | dlg_output_func)) { 311 format += sprintf(format, "["); 312 } 313 314 bool first_meta = true; 315 if(features & dlg_output_time) { 316 format += sprintf(format, "%%h"); 317 first_meta = false; 318 } 319 320 if(features & dlg_output_time_msecs) { 321 if(!first_meta) { 322 format += sprintf(format, ":"); 323 } 324 325 format += sprintf(format, "%%m"); 326 first_meta = false; 327 } 328 329 if(features & dlg_output_file_line) { 330 if(!first_meta) { 331 format += sprintf(format, " "); 332 } 333 334 format += sprintf(format, "%%o"); 335 first_meta = false; 336 } 337 338 if(features & dlg_output_func) { 339 if(!first_meta) { 340 format += sprintf(format, " "); 341 } 342 343 format += sprintf(format, "%%f"); 344 first_meta = false; 345 } 346 347 if(features & dlg_output_tags) { 348 if(!first_meta) { 349 format += sprintf(format, " "); 350 } 351 352 format += sprintf(format, "{%%t}"); 353 first_meta = false; 354 } 355 356 if(features & (dlg_output_time | dlg_output_file_line | dlg_output_tags | dlg_output_func)) { 357 format += sprintf(format, "] "); 358 } 359 360 format += sprintf(format, "%%c"); 361 362 if(features & dlg_output_newline) { 363 format += sprintf(format, "\n"); 364 } 365 366 *format = '\0'; 367 dlg_generic_outputf(output, data, format_buf, origin, string, styles); 368 } 369 370 void dlg_generic_outputf(dlg_generic_output_handler output, void* data, 371 const char* format_string, const struct dlg_origin* origin, const char* string, 372 const struct dlg_style styles[6]) { 373 bool reset_style = false; 374 for(const char* it = format_string; *it; it++) { 375 if(*it != '%') { 376 output(data, "%c", *it); 377 continue; 378 } 379 380 char next = *(it + 1); // must be valid since *it is not '\0' 381 if(next == 'h') { 382 time_t t = time(NULL); 383 struct tm tm_info; 384 385 #ifdef DLG_OS_WIN 386 if(localtime_s(&tm_info, &t)) { 387 #else 388 if(!localtime_r(&t, &tm_info)) { 389 #endif 390 output(data, "<DATE ERROR>"); 391 } else { 392 char timebuf[32]; 393 strftime(timebuf, sizeof(timebuf), "%H:%M:%S", &tm_info); 394 output(data, "%s", timebuf); 395 } 396 it++; 397 } else if(next == 'm') { 398 output(data, "%06d", get_msecs()); 399 it++; 400 } else if(next == 't') { 401 bool first_tag = true; 402 for(const char** tags = origin->tags; *tags; ++tags) { 403 if(!first_tag) { 404 output(data, ", "); 405 } 406 407 output(data, "%s", *tags); 408 first_tag = false; 409 } 410 ++it; 411 } else if(next == 'f') { 412 output(data, "%s", origin->func); 413 ++it; 414 } else if(next == 'o') { 415 output(data, "%s:%u", origin->file, origin->line); 416 ++it; 417 } else if(next == 's') { 418 char buf[12]; 419 dlg_escape_sequence(styles[origin->level], buf); 420 output(data, "%s", buf); 421 reset_style = true; 422 ++it; 423 } else if(next == 'r') { 424 output(data, "%s", dlg_reset_sequence); 425 reset_style = false; 426 ++it; 427 } else if(next == 'c') { 428 if(origin->expr && string) { 429 output(data, "assertion '%s' failed: '%s'", origin->expr, string); 430 } else if(origin->expr) { 431 output(data, "assertion '%s' failed", origin->expr); 432 } else if(string) { 433 output(data, "%s", string); 434 } 435 ++it; 436 } else if(next == '%') { 437 output(data, "%s", "%"); 438 ++it; 439 } else { 440 // in this case it's a '%' without known format specifier following 441 output(data, "%s", "%"); 442 } 443 } 444 445 if(reset_style) { 446 output(data, "%s", dlg_reset_sequence); 447 } 448 } 449 450 struct buf { 451 char* buf; 452 size_t* size; 453 }; 454 455 static void print_size(void* size, const char* format, ...) { 456 va_list args; 457 va_start(args, format); 458 459 int ret = vsnprintf(NULL, 0, format, args); 460 va_end(args); 461 462 if(ret > 0) { 463 *((size_t*) size) += ret; 464 } 465 } 466 467 static void print_buf(void* dbuf, const char* format, ...) { 468 struct buf* buf = (struct buf*) dbuf; 469 va_list args; 470 va_start(args, format); 471 472 int printed = vsnprintf(buf->buf, *buf->size, format, args); 473 va_end(args); 474 475 if(printed > 0) { 476 *buf->size -= printed; 477 buf->buf += printed; 478 } 479 } 480 481 void dlg_generic_output_buf(char* buf, size_t* size, unsigned int features, 482 const struct dlg_origin* origin, const char* string, 483 const struct dlg_style styles[6]) { 484 if(buf) { 485 struct buf mbuf; 486 mbuf.buf = buf; 487 mbuf.size = size; 488 dlg_generic_output(print_buf, &mbuf, features, origin, string, styles); 489 } else { 490 *size = 0; 491 dlg_generic_output(print_size, size, features, origin, string, styles); 492 } 493 } 494 495 void dlg_generic_outputf_buf(char* buf, size_t* size, const char* format_string, 496 const struct dlg_origin* origin, const char* string, 497 const struct dlg_style styles[6]) { 498 if(buf) { 499 struct buf mbuf; 500 mbuf.buf = buf; 501 mbuf.size = size; 502 dlg_generic_outputf(print_buf, &mbuf, format_string, origin, string, styles); 503 } else { 504 *size = 0; 505 dlg_generic_outputf(print_size, size, format_string, origin, string, styles); 506 } 507 } 508 509 static void print_stream(void* stream, const char* format, ...) { 510 va_list args; 511 va_start(args, format); 512 dlg_vfprintf((FILE*) stream, format, args); 513 va_end(args); 514 } 515 516 void dlg_generic_output_stream(FILE* stream, unsigned int features, 517 const struct dlg_origin* origin, const char* string, 518 const struct dlg_style styles[6]) { 519 stream = stream ? stream : stdout; 520 if(features & dlg_output_threadsafe) { 521 lock_file(stream); 522 } 523 524 dlg_generic_output(print_stream, stream, features, origin, string, styles); 525 if(features & dlg_output_threadsafe) { 526 unlock_file(stream); 527 } 528 } 529 530 void dlg_generic_outputf_stream(FILE* stream, const char* format_string, 531 const struct dlg_origin* origin, const char* string, 532 const struct dlg_style styles[6], bool lock_stream) { 533 stream = stream ? stream : stdout; 534 if(lock_stream) { 535 lock_file(stream); 536 } 537 538 dlg_generic_outputf(print_stream, stream, format_string, origin, string, styles); 539 if(lock_stream) { 540 unlock_file(stream); 541 } 542 } 543 544 void dlg_default_output(const struct dlg_origin* origin, const char* string, void* data) { 545 FILE* stream = data ? (FILE*) data : stdout; 546 unsigned int features = dlg_output_file_line | 547 dlg_output_newline | 548 dlg_output_threadsafe; 549 550 #ifdef DLG_DEFAULT_OUTPUT_ALWAYS_COLOR 551 dlg_win_init_ansi(); 552 features |= dlg_output_style; 553 #else 554 if(dlg_is_tty(stream) && dlg_win_init_ansi()) { 555 features |= dlg_output_style; 556 } 557 #endif 558 559 dlg_generic_output_stream(stream, features, origin, string, dlg_default_output_styles); 560 fflush(stream); 561 } 562 563 bool dlg_win_init_ansi(void) { 564 #if defined(DLG_OS_WIN) && defined(DLG_WIN_CONSOLE) 565 // TODO: use init once 566 static volatile LONG status = 0; 567 LONG res = InterlockedCompareExchange(&status, 1, 0); 568 if(res == 0) { // not initialized 569 InterlockedExchange(&status, 3 + init_ansi_console()); 570 } 571 572 while(status == 1); // currently initialized in another thread, spinlock 573 return (status == 4); 574 #else 575 return true; 576 #endif 577 } 578 579 // small dynamic vec/array implementation 580 // Since the macros vec_init and vec_add[c]/vec_push might 581 // change the pointers value it must not be referenced somewhere else. 582 #define vec__raw(vec) (((unsigned int*) vec) - 2) 583 584 static void* vec_do_create(unsigned int typesize, unsigned int cap, unsigned int size) { 585 unsigned long a = (size > cap) ? size : cap; 586 void* ptr = xalloc(2 * sizeof(unsigned int) + a * typesize); 587 unsigned int* begin = (unsigned int*) ptr; 588 begin[0] = size * typesize; 589 begin[1] = a * typesize; 590 return begin + 2; 591 } 592 593 // NOTE: can be more efficient if we are allowed to reorder vector 594 static void vec_do_erase(void* vec, unsigned int pos, unsigned int size) { 595 unsigned int* begin = vec__raw(vec); 596 begin[0] -= size; 597 char* buf = (char*) vec; 598 memcpy(buf + pos, buf + pos + size, size); 599 } 600 601 static void* vec_do_add(void** vec, unsigned int size) { 602 unsigned int* begin = vec__raw(*vec); 603 unsigned int needed = begin[0] + size; 604 if(needed >= begin[1]) { 605 void* ptr = xrealloc(begin, sizeof(unsigned int) * 2 + needed * 2); 606 begin = (unsigned int*) ptr; 607 begin[1] = needed * 2; 608 (*vec) = begin + 2; 609 } 610 611 void* ptr = ((char*) (*vec)) + begin[0]; 612 begin[0] += size; 613 return ptr; 614 } 615 616 #define vec_create(type, size) (type*) vec_do_create(sizeof(type), size * 2, size) 617 #define vec_create_reserve(type, size, capacity) (type*) vec_do_create(sizeof(type), capcity, size) 618 #define vec_init(array, size) array = vec_do_create(sizeof(*array), size * 2, size) 619 #define vec_init_reserve(array, size, capacity) *((void**) &array) = vec_do_create(sizeof(*array), capacity, size) 620 #define vec_free(vec) (free((vec) ? vec__raw(vec) : NULL), vec = NULL) 621 #define vec_erase_range(vec, pos, count) vec_do_erase(vec, pos * sizeof(*vec), count * sizeof(*vec)) 622 #define vec_erase(vec, pos) vec_do_erase(vec, pos * sizeof(*vec), sizeof(*vec)) 623 #define vec_size(vec) (vec__raw(vec)[0] / sizeof(*vec)) 624 #define vec_capacity(vec) (vec_raw(vec)[1] / sizeof(*vec)) 625 #define vec_add(vec) vec_do_add((void**) &vec, sizeof(*vec)) 626 #define vec_addc(vec, count) (vec_do_add((void**) &vec, sizeof(*vec) * count)) 627 #define vec_push(vec, value) (vec_do_add((void**) &vec, sizeof(*vec)), vec_last(vec) = (value)) 628 #define vec_pop(vec) (vec__raw(vec)[0] -= sizeof(*vec)) 629 #define vec_popc(vec, count) (vec__raw(vec)[0] -= sizeof(*vec) * count) 630 #define vec_clear(vec) (vec__raw(vec)[0] = 0) 631 #define vec_last(vec) (vec[vec_size(vec) - 1]) 632 633 static struct dlg_data* dlg_create_data(void) { 634 struct dlg_data* data = (struct dlg_data*) xalloc(sizeof(struct dlg_data)); 635 vec_init_reserve(data->tags, 0, 20); 636 vec_init_reserve(data->pairs, 0, 20); 637 data->buffer_size = 100; 638 data->buffer = (char*) xalloc(data->buffer_size); 639 return data; 640 } 641 642 static void dlg_free_data(void* ddata) { 643 struct dlg_data* data = (struct dlg_data*) ddata; 644 if(data) { 645 vec_free(data->pairs); 646 vec_free(data->tags); 647 free(data->buffer); 648 free(data); 649 } 650 } 651 652 void dlg_add_tag(const char* tag, const char* func) { 653 struct dlg_data* data = dlg_data(); 654 struct dlg_tag_func_pair* pair = 655 (struct dlg_tag_func_pair*) vec_add(data->pairs); 656 pair->tag = tag; 657 pair->func = func; 658 } 659 660 bool dlg_remove_tag(const char* tag, const char* func) { 661 struct dlg_data* data = dlg_data(); 662 for(unsigned int i = 0; i < vec_size(data->pairs); ++i) { 663 if(data->pairs[i].func == func && data->pairs[i].tag == tag) { 664 vec_erase(data->pairs, i); 665 return true; 666 } 667 } 668 669 return false; 670 } 671 672 char** dlg_thread_buffer(size_t** size) { 673 struct dlg_data* data = dlg_data(); 674 if(size) { 675 *size = &data->buffer_size; 676 } 677 return &data->buffer; 678 } 679 680 void dlg_set_handler(dlg_handler handler, void* data) { 681 g_handler = handler; 682 g_data = data; 683 } 684 685 dlg_handler dlg_get_handler(void** data) { 686 *data = g_data; 687 return g_handler; 688 } 689 690 const char* dlg__printf_format(const char* str, ...) { 691 va_list vlist; 692 va_start(vlist, str); 693 694 va_list vlistcopy; 695 va_copy(vlistcopy, vlist); 696 int needed = vsnprintf(NULL, 0, str, vlist); 697 if(needed < 0) { 698 printf("dlg__printf_format: invalid format given\n"); 699 va_end(vlist); 700 va_end(vlistcopy); 701 return NULL; 702 } 703 704 va_end(vlist); 705 706 size_t* buf_size; 707 char** buf = dlg_thread_buffer(&buf_size); 708 if(*buf_size <= (unsigned int) needed) { 709 *buf_size = (needed + 1) * 2; 710 *buf = (char*) xrealloc(*buf, *buf_size); 711 } 712 713 vsnprintf(*buf, *buf_size, str, vlistcopy); 714 va_end(vlistcopy); 715 716 return *buf; 717 } 718 719 void dlg__do_log(enum dlg_level lvl, const char* const* tags, const char* file, int line, 720 const char* func, const char* string, const char* expr) { 721 struct dlg_data* data = dlg_data(); 722 unsigned int tag_count = 0; 723 724 // push default tags 725 while(tags[tag_count]) { 726 vec_push(data->tags, tags[tag_count++]); 727 } 728 729 // push current global tags 730 for(size_t i = 0; i < vec_size(data->pairs); ++i) { 731 const struct dlg_tag_func_pair pair = data->pairs[i]; 732 if(pair.func == NULL || !strcmp(pair.func, func)) { 733 vec_push(data->tags, pair.tag); 734 } 735 } 736 737 // push call-specific tags, skip first terminating NULL 738 ++tag_count; 739 while(tags[tag_count]) { 740 vec_push(data->tags, tags[tag_count++]); 741 } 742 743 vec_push(data->tags, NULL); // terminating NULL 744 struct dlg_origin origin; 745 origin.level = lvl; 746 origin.file = file; 747 origin.line = line; 748 origin.func = func; 749 origin.expr = expr; 750 origin.tags = data->tags; 751 752 g_handler(&origin, string, g_data); 753 vec_clear(data->tags); 754 } 755 756 #ifdef _MSC_VER 757 // shitty msvc compatbility 758 // meson gives us sane paths (separated by '/') while on MSVC, 759 // __FILE__ contains a '\\' separator. 760 static bool path_same(char a, char b) { 761 return (a == b) || 762 (a == '/' && b == '\\') || 763 (a == '\\' && b == '/'); 764 } 765 #else 766 767 static inline bool path_same(char a, char b) { 768 return a == b; 769 } 770 771 #endif 772 773 const char* dlg__strip_root_path(const char* file, const char* base) { 774 if(!file) { 775 return NULL; 776 } 777 778 const char* saved = file; 779 if(*file == '.') { // relative path detected 780 while(*(++file) == '.' || *file == '/' || *file == '\\'); 781 if(*file == '\0') { // weird case: purely relative path without file 782 return saved; 783 } 784 785 return file; 786 } 787 788 // strip base from file if it is given 789 if(base) { 790 char fn = *file; 791 char bn = *base; 792 while(bn != '\0' && path_same(fn, bn)) { 793 fn = *(++file); 794 bn = *(++base); 795 } 796 797 if(fn == '\0' || bn != '\0') { // weird case: base isn't prefix of file 798 return saved; 799 } 800 } 801 802 return file; 803 }