1 /*----------------------------------------------------------------------------
2 Name: xmlBlaster/src/c/util/helper.c
3 Project: xmlBlaster.org
4 Copyright: xmlBlaster.org, see xmlBlaster-LICENSE file
5 Comment: Contains helper functions for string and message manipulation
6 Generic helper code, used by Queue implementation and xmlBlaster client code
7 Don't add any queue specific or xmlBlaster client specific code!
8 Compile: gcc -Wall -g -o helper helper.c -DHELPER_UTIL_MAIN -I..
9 Testsuite: xmlBlaster/testsuite/src/c/TestUtil.c
10 Author: "Marcel Ruff" <xmlBlaster@marcelruff.info>
11 -----------------------------------------------------------------------------*/
12
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <stdarg.h>
16 #include <string.h>
17 #include <ctype.h>
18 #include <assert.h>
19 #include <time.h>
20 #include "helper.h"
21 #include "Timestampc.h"
22
23 #ifdef _ENABLE_STACK_TRACE_
24 # include <execinfo.h>
25 #endif
26
27 #ifdef _WINDOWS
28 # include <Winsock2.h> /* Sleep() */
29 # if XB_USE_PTHREADS
30 # include <pthreads/pthread.h> /* Our pthreads.h: For logging output of thread ID, for Windows and WinCE downloaded from http://sources.redhat.com/pthreads-win32 */
31 # endif
32 #else
33 # include <unistd.h> /* sleep(), only used in main */
34 # include <errno.h> /* errno */
35 # include <sys/time.h> /* sleep with select(), gettimeofday() */
36 # include <sys/types.h> /* sleep with select() */
37 # if XB_USE_PTHREADS
38 # include <pthread.h> /* The original pthreads.h from the OS */
39 # endif
40 # include <inttypes.h> /* PRId64 %lld format specifier */
41 #endif
42
43 #define MICRO_SECS_PER_SECOND 1000000
44 #define NANO_SECS_PER_SECOND MICRO_SECS_PER_SECOND * 1000
45
46 static const char *LOG_TEXT[] = { "NOLOG", "ERROR", "WARN", "INFO", "CALL", "TIME", "TRACE", "DUMP", "PLAIN" };
47 static const int numLOG_TEXT = 9; /* sizeof(LOG_TEXT) returns 36 which is not what we want */
48
49 #define ESC "\033[0m"; /* Reset color to original values */
50 #define BOLD "\033[1m"
51
52 #define RED_BLACK "\033[31;40m"
53 #define GREEN_BLACK "\033[32;40m"
54 #define YELLOW_BLACK "\033[33;40m"
55 #define BLUE_BLACK "\033[34;40m"
56 #define PINK_BLACK "\033[35;40m"
57 #define LTGREEN_BLACK "\033[36;40m"
58 #define WHITE_BLACK "\033[37;40m"
59
60 #define WHITE_RED "\033[37;41m"
61 #define BLACK_RED "\033[30;41m"
62 #define BLACK_GREEN "\033[40;42m"
63 #define BLACK_PINK "\033[40;45m"
64 #define BLACK_LTGREEN "\033[40;46m"
65
66 /* To support colored logging output in xterminals */
67 static const char *LOG_TEXT_ESCAPE[] = {
68 "NOLOG",
69 "\033[31;40mERROR\033[0m",
70 "\033[33;40mWARN\033[0m",
71 "\033[32;40mINFO\033[0m",
72 "\033[34;40mCALL\033[0m",
73 "\033[36;40mTIME\033[0m",
74 "\033[37;40mTRACE\033[0m",
75 "\033[35;40mDUMP\033[0m",
76 "\033[37;40mPLAIN\033[0m"
77 };
78
79 static int vsnprintf0(char *s, size_t size, const char *format, va_list ap);
80
81 /**
82 * Add for GCC compilation: "-rdynamic -export-dynamic -D_ENABLE_STACK_TRACE_"
83 * @return The stack trace, you need to free() it.
84 * Returns NULL if out of memory.
85 */
86 Dll_Export char *getStackTrace(int maxNumOfLines)
87 {
88 #ifdef _ENABLE_STACK_TRACE_
89 int i;
90 void** arr = (void **)calloc(maxNumOfLines, sizeof(void *));
91 if (arr == 0) return (char *)0;
92 {
93 /*
94 > +Currently, the function name and offset can only be obtained on systems
95 > +that use the ELF binary format for programs and libraries.
96 Perhaps a reference to the addr2line program can be added here. It
97 can be used to retrieve symbols even if the -rdynamic flag wasn't
98 passed to the linker, and it should work on non-ELF targets as well.
99 o Under linux, gcc interprets it by setting the
100 "-export-dynamic" option for ld, which has that effect, according
101 to the linux ld manpage.
102
103 o Under IRIX it's ignored, and the program's happy as a clam.
104
105 o Under SunOS-4.1, gcc interprets it by setting the -dc -dp
106 options for ld, which again forces the allocation of the symbol
107 table in the code produced (see ld(1) on a Sun).
108 */
109 int bt = backtrace(arr, maxNumOfLines);
110 char** list = (char **)backtrace_symbols(arr, bt); /* malloc the return pointer, the entries don't need to be freed */
111 char *ret = strcpyAlloc("");
112 for (i=0; i<bt; i++) {
113 if (list[i] != NULL) {
114 strcatAlloc(&ret, list[i]);
115 strcatAlloc(&ret, "\n");
116 }
117 }
118 free(list);
119 free(arr);
120 if (strlen(ret) < 1) {
121 strcatAlloc(&ret, ""); /* Creation of stackTrace failed */
122 }
123 return ret;
124 }
125 #else
126 if (maxNumOfLines > 0) /* to make the compiler happy */
127 return strcpyAlloc("");
128 return strcpyAlloc(""); /* No stack trace provided in this system */
129 #endif
130 }
131
132 #ifndef XMLBLASTER_SLEEP_FALLBACK
133 # define XMLBLASTER_SLEEP_FALLBACK 0 /* Initialize to make icc happy */
134 #endif
135 #ifndef XMLBLASTER_SLEEP_NANO
136 # define XMLBLASTER_SLEEP_NANO 0
137 #endif
138
139 /**
140 * Sleep for given milliseconds, on none real time systems expect ~ 10 millisecs tolerance.
141 */
142 Dll_Export void sleepMillis(long millisecs)
143 {
144 #ifdef _WINDOWS
145 Sleep(millisecs);
146 #elif defined(__IPhoneOS__)
147 usleep((unsigned long)millisecs*1000);
148 #elif XMLBLASTER_SLEEP_FALLBACK /* rounded to seconds */
149 if (millisecs < 1000)
150 millisecs = 1000;
151 sleep(millisecs/1000);
152 #elif XMLBLASTER_SLEEP_NANO
153 TODO:
154 int nanosleep(const struct timespec *rqtp, struct timespec *rmtp);
155 struct timespec
156 {
157 time_t tv_sec; /* seconds */
158 long tv_nsec; /* nanoseconds */
159 };
160 /*
161 usleep() deprecated
162 */
163 /*
164 #include <time.h>
165 void Sleep(clock_t wait)
166 {
167 clock_t goal;
168 goal = wait * (CLOCKS_PER_SEC / 1000);
169 while( goal >= clock())
170 ;
171 }
172 */
173 #else
174 fd_set dummy;
175 struct timeval toWait;
176 int ret;
177
178 FD_ZERO(&dummy);
179 toWait.tv_sec = millisecs / 1000;
180 toWait.tv_usec = (millisecs % 1000) * 1000;
181
182 ret = select(0, &dummy, NULL, NULL, &toWait);
183 if (ret == -1) {
184 printf("[helper.c] ERROR: sleepMillis(%ld) returned errnor %d", millisecs, errno);
185 }
186 #endif
187 }
188
189 #include <wchar.h>
190 /**
191 * Converts the given wide char pwcs to multibyte argv.
192 * <p>Call freeWcsArgv() to free the memory again.</p>
193 * @param pwcs In parameter: Wide char command line arguments
194 * @param argc The number of strings in pwcs
195 * @return argv Is allocated with malloc and holds all given pwcs strings
196 */
197 Dll_Export char **convertWcsArgv(wchar_t **argv_wcs, int argc) {
198 int i;
199 char **argv = (char **)malloc(argc*sizeof(char*));
200 for (i=0; i<argc; i++) {
201 size_t sizeInBytes = 4*(int)wcslen(argv_wcs[i]);
202 argv[i] = (char *)malloc(sizeInBytes*sizeof(char));
203 # if _MSC_VER >= 1400 && !defined(WINCE)
204 {
205 size_t pReturnValue;
206 /*errno_t err = */
207 wcstombs_s(&pReturnValue, argv[i], sizeInBytes, argv_wcs[i], _TRUNCATE);
208 }
209 # else
210 wcstombs(argv[i], argv_wcs[i], sizeInBytes);
211 # endif
212 /*printf("%s ", argv[i]);*/
213 }
214 return argv;
215 }
216
217 /**
218 * Frees the allocated argv from convertWcsArgv().
219 * @param argv The main(argv)
220 * @param argc The number of strings in argv
221 */
222 Dll_Export void freeArgv(char **argv, int argc) {
223 int i;
224 if (argv == 0) return;
225 for (i=0; i<argc; i++) {
226 free(argv[i]);
227 }
228 free(argv);
229 }
230
231 Dll_Export char *strtok_r2(char *src, const char *delim, char **saveptr, const char quotechar) {
232 bool inQuotes = false;
233 int ii, len;
234 char *ptr;
235 if (src != 0)
236 *saveptr = src;
237 ptr = *saveptr;
238 if (ptr == 0)
239 return 0;
240 len = strlen(ptr);
241 for (ii = 0; ii < len; ii++) {
242 char c = ptr[ii];
243 if (quotechar != 0 && c == quotechar) {
244 inQuotes = !inQuotes;
245 if (inQuotes)
246 ptr++; /* strip leading quotechar */
247 else
248 ptr[ii] = 0; /* Remove trailing quotechar */
249 }
250 else if (strchr(delim, c) != 0 && !inQuotes) {
251 ptr[ii] = 0;
252 (*saveptr) = ptr+ii+1;
253 return ptr;
254 }
255 }
256 (*saveptr) = 0;
257 return ptr;
258 }
259
260 /**
261 * Allocates the string with malloc for you.
262 * You need to free it with free()
263 * @param src The text to copy
264 * @return The allocated string or NULL if out of memory
265 */
266 Dll_Export char *strcpyAlloc(const char *src)
267 {
268 char *dest;
269 size_t len;
270 if (src == 0) return (char *)0;
271 len = strlen(src) + 1;
272 dest = (char *)malloc(len*sizeof(char));
273 if (dest == 0) return 0;
274 strncpy0(dest, src, len);
275 return dest;
276 }
277
278 Dll_Export char *strcpyAlloc0(const char *src, const size_t maxLen)
279 {
280 char *dest;
281 size_t len;
282 if (src == 0) return (char *)0;
283 len = strlen(src) + 1;
284 if (len > maxLen) len = maxLen;
285 dest = (char *)malloc(len*sizeof(char));
286 if (dest == 0) return 0;
287 strncpy0(dest, src, len);
288 return dest;
289 }
290
291 /**
292 * Same as strcat but reallocs the 'dest' string
293 * @return The allocated string (*dest) or NULL if out of memory
294 */
295 Dll_Export char *strcatAlloc(char **dest, const char *src)
296 {
297 size_t lenSrc;
298 size_t len;
299 assert(dest != 0);
300 if (src == 0) return (char *)0;
301 lenSrc = strlen(src);
302 len = lenSrc+strlen(*dest)+1;
303 (*dest) = (char *)realloc(*dest, len*sizeof(char));
304 if ((*dest) == 0) return 0;
305 strncat((*dest), src, lenSrc);
306 *((*dest)+len-1) = '\0';
307 return (*dest);
308 }
309
310 /**
311 * Same as strcat but reallocs the 'dest' string
312 * @return The allocated string (*dest) or NULL if out of memory
313 */
314 Dll_Export char *strcatAlloc0(char **dest, const char *src, const size_t maxLen)
315 {
316 size_t lenSrc;
317 size_t len;
318 assert(dest != 0);
319 if (src == 0) return (char *)0;
320 lenSrc = strlen(src);
321 len = lenSrc+strlen(*dest)+1;
322 (*dest) = (char *)realloc(*dest, len*sizeof(char));
323 if ((*dest) == 0) return 0;
324 strncat((*dest), src, lenSrc);
325 if (len > maxLen) {
326 len = maxLen; /* TODO: proper handling: not allocate too much */
327 }
328 *((*dest)+len-1) = '\0';
329 return (*dest);
330 }
331
332 /**
333 * Same as strcpyAlloc but if the given *dest != NULL this old allocation is freed first
334 * @return *dest The allocated string filled with 'src',
335 * you need to free() it when not needed anymore.
336 */
337 Dll_Export char *strcpyRealloc(char **dest, const char *src)
338 {
339 if (*dest != 0)
340 free(*dest);
341 *dest = strcpyAlloc(src);
342 return *dest;
343 }
344
345 /**
346 * Allocates the string with malloc for you, it is always ended with 0.
347 * NOTE: If your given blob or len is 0 an empty string of size 1 is returned
348 * @return The string, never null.
349 * You need to free it with free()
350 */
351 Dll_Export char *strFromBlobAlloc(const char *blob, const size_t len)
352 {
353 char *dest;
354 size_t i;
355 if (blob == 0 || len < 1) {
356 dest = (char *)malloc(1*sizeof(char));
357 if (dest == 0) return 0;
358 *dest = 0;
359 return dest;
360 }
361
362 dest = (char *)malloc((len+1)*sizeof(char));
363 if (dest == 0) return 0;
364 for (i=0; i<len; i++) {
365 dest[i] = (char)blob[i];
366 }
367 dest[len] = '\0';
368 return dest;
369 }
370
371
372 /**
373 * Convert the errnum to a human readable errnoStr.
374 * @param errnoStr Out parameter holding the string
375 * @param sizeInBytes Size of the buffer
376 * @param errnum The error number (errno)
377 */
378 Dll_Export void xb_strerror(char *errnoStr, size_t sizeInBytes, int errnum) {
379 snprintf0(errnoStr, sizeInBytes, "%d", errnum); /* default if string lookup fails */
380 # if defined(WINCE)
381 # elif _MSC_VER >= 1400
382 strerror_s(errnoStr, sizeInBytes, errnum);
383 # elif defined(_LINUX)
384 strerror_r(errnum, errnoStr, sizeInBytes-1); /* glibc > 2. returns a char*, but should return an int */
385 # else
386 {
387 char *p = strerror(errnum);
388 strncpy0(errnoStr, p, sizeInBytes);
389 }
390 # endif
391 }
392
393
394 /**
395 * Guarantees a 0 terminated string
396 * @param to The destination string must be big enough
397 * @param from The source to be copied
398 * @param maxLen (maxLen-1) of 'to' will be filled with a 0,
399 * so effectively only maxLen-1 from 'from' are copied.
400 * @return The destination string 'to'
401 */
402 Dll_Export char *strncpy0(char * const to, const char * const from, const size_t maxLen)
403 {
404 # if defined(WINCE)
405 char *ret=strncpy(to, from, maxLen-1);
406 *(to+maxLen-1) = '\0';
407 return ret;
408 # elif _MSC_VER >= 1400
409 /* errno_t strncpy_s(
410 char *strDest,
411 size_t sizeInBytes,
412 const char *strSource,
413 size_t count
414 ); */
415 errno_t ee = strncpy_s(to, maxLen, from, _TRUNCATE); /*maxLen);*/
416 return to;
417 # else /* MAC OSX calls it strlcpy() */
418 char *ret=strncpy(to, from, maxLen-1);
419 *(to+maxLen-1) = '\0';
420 return ret;
421 # endif
422 }
423
424
425 /**
426 * Guarantees a 0 terminated string
427 * @param to The destination string must be big enough
428 * @param from The source to be appended
429 * @param max Number of characters to append, max-1 will be ended by 0
430 * @return The destination string 'to'
431 */
432 Dll_Export char *strncat0(char * const to, const char * const from, const size_t max)
433 {
434 # if _MSC_VER >= 1400 && !defined(WINCE)
435 /* buffersize of 'to' in bytes */
436 size_t bufferSizeInBytes = strlen(to) + max;
437 errno_t ee = strncat_s(to, bufferSizeInBytes, from, _TRUNCATE);
438 return to;
439 # else /* MAC OSX calls it strlcat() */
440 int oldLen = strlen(to);
441 char *ret=strncat(to, from, max-1);
442 *(to+oldLen+max-1) = '\0';
443 return ret;
444 # endif
445 }
446
447 int vsnprintf0(char *s, size_t size, const char *format, va_list ap) {
448 # if _MSC_VER >= 1400 && !defined(WINCE)
449 errno_t err = vsnprintf_s(s, size, _TRUNCATE, format, ap);
450 if ( err == STRUNCATE ) {
451 printf("truncation occurred %s!\n", format);
452 return 0;
453 }
454 return err;
455 # elif defined(_WINDOWS)
456 return _vsnprintf(s, size, format, ap);
457 # else
458 return vsnprintf(s, size, format, ap);
459 # endif
460 }
461
462 /**
463 * Microsoft introduces the vsnprintf_s()
464 */
465 Dll_Export int snprintf0(char *buffer, size_t sizeOfBuffer, const char *format, ...) {
466 int ret;
467 va_list ap;
468 va_start (ap, format);
469 ret = vsnprintf0(
470 buffer,
471 sizeOfBuffer,
472 format,
473 ap);
474 va_end (ap);
475 return ret;
476 }
477
478 /**
479 * strip leading and trailing spaces of the given string
480 */
481 Dll_Export void trim(char *s)
482 {
483 size_t first=0;
484 size_t len;
485 int i;
486
487 if (s == (char *)0) return;
488
489 len = strlen((char *) s);
490
491 { /* find beginning of text */
492 while (first<len) {
493 if (!isspace((unsigned char)s[first]))
494 break;
495 first++;
496 }
497 }
498
499 if (first>=len) {
500 *s = '\0';
501 return;
502 }
503 else
504 memmove((char *) s, (char *) s+first, strlen(s+first)+1); /* including '\0' */
505
506 for (i=(int)strlen((char *) s)-1; i >= 0; i--)
507 if (!isspace((unsigned char)s[i])) {
508 s[i+1] = '\0';
509 return;
510 }
511 if (i<0) *s = '\0';
512 }
513
514 /**
515 * strip leading spaces of the given string
516 */
517 Dll_Export void trimStart(char *s)
518 {
519 size_t first=0;
520 size_t len;
521
522 if (s == (char *)0) return;
523
524 len = strlen((char *) s);
525
526 { /* find beginning of text */
527 while (first<len) {
528 if (!isspace((unsigned char)s[first]))
529 break;
530 first++;
531 }
532 }
533
534 if (first>=len) {
535 *s = '\0';
536 return;
537 }
538 else
539 memmove((char *) s, (char *) s+first, strlen(s+first)+1); /* including '\0' */
540 }
541
542 /**
543 * strip trailing spaces of the given string
544 */
545 Dll_Export void trimEnd(char *s)
546 {
547 int i;
548 for (i=(int)strlen((char *) s)-1; i >= 0; i--)
549 if (!isspace((unsigned char)s[i])) {
550 s[i+1] = '\0';
551 return;
552 }
553 if (i<0) *s = '\0';
554 }
555
556 Dll_Export
557 bool startsWith(const char * const str, const char * const token) {
558 int i;
559 if (str == 0 || token == 0)
560 return false;
561 for (i = 0; ; i++) {
562 if (token[i] == 0)
563 return true;
564 if (str[i] != token[i])
565 return false;
566 }
567 return false; /* never reached, to make compiler happy */
568 }
569
570 Dll_Export
571 bool endsWith(const char * const str, const char * const token) {
572 int i, count=0, lenStr, len;
573 if (str == 0 || token == 0)
574 return false;
575 lenStr = strlen(str);
576 len = strlen(token);
577 if (lenStr < len)
578 return false;
579 for (i = lenStr - len; i < lenStr; i++, count++) {
580 if (str[i] != token[count])
581 return false;
582 }
583 return true;
584 }
585
586 /**
587 * Converts the given binary data to a more readable string,
588 * the zero bytes are replaced by '*'
589 * @param data The data to convert
590 * @param len The length of the binary data
591 * @return readable is returned, it must be free()'d.
592 * If allocation fails NULL is returned
593 */
594 Dll_Export char *toReadableDump(char *data, size_t len)
595 {
596 char *readable;
597 size_t i;
598 if (data == 0) {
599 return (char *)0;
600 }
601 readable = (char *)malloc((len+1) * sizeof(char));
602 if (readable == (char *)0) return (char *)0;
603 for (i=0; i<len; i++) {
604 if (data[i] == '\0')
605 readable[i] = '*';
606 else
607 readable[i] = data[i];
608 }
609 readable[len] = '\0';
610 return readable;
611 }
612
613 #if defined(XB_USE_PTHREADS)
614 /**
615 * Cast the thread identifier to an long value.
616 * @param t The pthread_t type
617 * @return A unique long, usually the pointer address
618 */
619 unsigned long get_pthread_id(pthread_t t)
620 {
621 # ifdef _WINDOWS
622 return (unsigned long)t.p; /* typedef ptw32_handle_t pthread_t; with struct {void*p; unsigned int x;} */
623 # else
624 /* TODO: Mac OS X: in sys/_types.h: struct _opaque_pthread_t { long __sig; struct __darwin_pthread_handler_rec *__cleanup_stack; char __opaque[__PTHREAD_SIZE__]; }; */
625 int64_t val64 = 0;
626 /*printf("xmlBlaster helper.c pthread_t size=%ud\n", sizeof(pthread_t));*/
627 {
628 val64 = (int64_t)t; /* INT_LEAST64_MAX=9223372036854775807 */
629 if (val64 <= 4294967295U) {
630 /*printf("xmlBlaster helper.c OK\n");*/
631 return (unsigned long)t;
632 }
633 }
634 { /* Intels icc 10.x had problems which i couldn't resolve (2008/11 marcel) */
635 char *p;
636 char buf[56];
637 long val32;
638 /* 2147483647 */
639 /* 3081234112 */
640 printf("xmlBlaster helper.c Warning: stripping pthread_id "PRINTF_PREFIX_INT64_T"\n", val64);
641 /*printf("xmlBlaster helper.c Warning: stripping pthread_id %"PRId64"\n", val64);*/
642 SNPRINTF(buf, 55, PRINTF_PREFIX_INT64_T, val64);
643 /*SNPRINTF(buf, 55, "%"PRId64, val64); PRId64 As 32bit system need "%lld" and 64 bit systems need "%ld" */
644 /*printf("xmlBlaster helper.c Warning: stripping pthread_id string %s\n", buf);*/
645 p = buf + strlen(buf) - 9;
646 sscanf(p, "%ld", &val32);
647 /*printf("xmlBlaster helper.c Warning: stripping pthread_id from %"PRId64" to %ld\n", val64, val32);*/
648 printf("xmlBlaster helper.c Warning: stripping pthread_id from "PRINTF_PREFIX_INT64_T" to %ld\n", val64, val32);
649 return val32;
650 /*return (long)(val64/(1+int64_t(val64 / INT_LEAST32_MAX)));*/
651 }
652 # endif
653 }
654 #endif
655
656 /**
657 * Console helper to get key hit.
658 * New lines are ignored
659 * @param str The text displayed on the console
660 * @return the key hit
661 */
662 Dll_Export char getInputKey(const char *str) {
663 int c = 0;
664 printf("%s >\n", str);
665 do {
666 c = getchar();
667 }
668 while (c == '\n'); /* Ignore enter hits */
669 return (char)c;
670 }
671
672 /**
673 * Default logging output is handled by this method:
674 * All logging is appended a time, the loglevel and the location string.
675 * The logging output is to console.
676 * <p>
677 * If you have your own logging device you need to implement this method
678 * yourself and register it with
679 * </p>
680 * <pre>
681 * xa->log = myXmlBlasterLoggingHandler;
682 * </pre>
683 * @param logUserP User specific location bounced back
684 * @param currLevel The actual log level of the client
685 * @param level The level of this log entry
686 * @param location A string describing the code place
687 * @param fmt The formatting string
688 * @param ... Other variables to log, corresponds to 'fmt'
689 */
690 Dll_Export void xmlBlasterDefaultLogging(void *logUserP, XMLBLASTER_LOG_LEVEL currLevel,
691 XMLBLASTER_LOG_LEVEL level,
692 const char *location, const char *fmt, ...)
693 {
694 /* Guess, we need no more than 200 bytes. */
695 int n, size = 200;
696 char *p = 0;
697 va_list ap;
698 char *stackTrace = 0;
699 # ifdef _WINDOWS
700 const char * const * logText = LOG_TEXT;
701 # else
702 const char * const * logText = LOG_TEXT_ESCAPE;
703 # endif
704 if (logUserP) {} /* To avoid "logUserP was never referenced" compiler warning */
705
706 if (level > currLevel) {
707 return;
708 }
709 if ((p = (char *)malloc ((size_t)size)) == NULL)
710 return;
711
712 if (level <= XMLBLASTER_LOG_ERROR) {
713 stackTrace = getStackTrace(10);
714 }
715
716 for (;;) {
717 /* Try to print in the allocated space. */
718 va_start(ap, fmt);
719 n = vsnprintf0(p, (size_t)size, fmt, ap);
720 va_end(ap);
721 /* If that worked, print the string to console. */
722 if (n > -1 && n < size) {
723 enum { SIZE=128 };
724 char timeStr[SIZE];
725 getCurrentTimeStr(timeStr, SIZE);
726 # if XB_USE_PTHREADS
727 printf("[%s %s %s thread0x%lx] %s %s\n", timeStr, logText[level], location,
728 get_pthread_id(pthread_self()), p,
729 (stackTrace != 0) ? stackTrace : "");
730 # else
731 printf("[%s %s %s] %s %s\n", timeStr, logText[level], location, p,
732 (stackTrace != 0) ? stackTrace : "");
733 # endif
734 free(p);
735 free(stackTrace);
736 return;
737 }
738 /* Else try again with more space. */
739 if (n > -1) /* glibc 2.1 */
740 size = n+1; /* precisely what is needed */
741 else /* glibc 2.0 */
742 size *= 2; /* twice the old size */
743 if ((p = (char *)realloc (p, (size_t)size)) == NULL) {
744 free(stackTrace);
745 return;
746 }
747 }
748 }
749
750 /**
751 * Parses the given string and returns the enum for it.
752 * If logLevelStr is NULL or empty or unknown we return the default log level.
753 * @param logLevelStr The level e.g. "WARN" or "warn" or "2"
754 * @return The enum, e.g. XMLBLASTER_LOG_WARN
755 */
756 Dll_Export XMLBLASTER_LOG_LEVEL parseLogLevel(const char *logLevelStr)
757 {
758 int i;
759 int len = numLOG_TEXT;
760 if (logLevelStr == 0 || *logLevelStr == '\0' ) {
761 return XMLBLASTER_LOG_WARN;
762 }
763 for (i=0; i<len; i++) {
764 # ifdef _WINDOWS
765 if (!strcmp(LOG_TEXT[i], logLevelStr)) {
766 # else
767 if (!strcasecmp(LOG_TEXT[i], logLevelStr)) {
768 # endif
769 return (XMLBLASTER_LOG_LEVEL)i;
770 }
771 }
772 if (strToInt(&i, logLevelStr) == 1)
773 return (XMLBLASTER_LOG_LEVEL)i;
774 return XMLBLASTER_LOG_WARN;
775 }
776
777 /**
778 * @return A human readable log level, e.g. "ERROR"
779 */
780 Dll_Export const char *getLogLevelStr(XMLBLASTER_LOG_LEVEL logLevel)
781 {
782 return LOG_TEXT[logLevel];
783 }
784
785 /**
786 * Check if logging is necessary.
787 * @param currLevel The actual log level of the client
788 * @param level The level of this log entry
789 * @return true If logging is desired
790 */
791 Dll_Export _INLINE_FUNC bool doLog(XMLBLASTER_LOG_LEVEL currLevel, XMLBLASTER_LOG_LEVEL level)
792 {
793 return (currLevel <= level) ? true : false;
794 }
795
796 /**
797 * Embed the given 'embed' into exception->message.
798 * <code>embed</code> and <code>exception</code> may point on the same instance
799 * @param embed An original exception to embed, can be empty in which case it is ignored
800 * @param exception Contains the new exception with embedded old exception errorCode/message
801 */
802 Dll_Export void embedException(ExceptionStruct *exception, const char *newErrorCode, const char *newMessage, const ExceptionStruct *embed)
803 {
804 char embedStr[EXCEPTIONSTRUCT_MESSAGE_LEN];
805 char newErrorCodeTmp[EXCEPTIONSTRUCT_ERRORCODE_LEN];
806 char message[EXCEPTIONSTRUCT_MESSAGE_LEN];
807
808 strncpy0(newErrorCodeTmp, newErrorCode, EXCEPTIONSTRUCT_ERRORCODE_LEN); /* Make temporary copy in case the memory overlaps */
809 if (*embed->errorCode != 0) {
810 SNPRINTF(message, EXCEPTIONSTRUCT_MESSAGE_LEN, "%s {Root cause: %s}", newMessage, getExceptionStr(embedStr, EXCEPTIONSTRUCT_MESSAGE_LEN, embed));
811 }
812 else {
813 SNPRINTF(message, EXCEPTIONSTRUCT_MESSAGE_LEN, "%s", newMessage);
814 }
815 strncpy0(exception->message, message, EXCEPTIONSTRUCT_MESSAGE_LEN);
816 strncpy0(exception->errorCode, newErrorCodeTmp, EXCEPTIONSTRUCT_ERRORCODE_LEN);
817 }
818
819 /**
820 * Should be called on any ExceptionStruct before using it.
821 * Nulls all fields
822 */
823 Dll_Export void initializeExceptionStruct(ExceptionStruct *exception)
824 {
825 exception->remote = false;
826 *exception->errorCode = (char)0;
827 *exception->message = (char)0;
828 }
829
830 /**
831 * Convenience function which returns a formatted exception string.
832 * <pre>
833 * </pre>
834 * @param out The string where the exception is written into, you should allocate at least
835 * EXCEPTIONSTRUCT_ERRORCODE_LEN + EXCEPTIONSTRUCT_MESSAGE_LEN + 64
836 * bytes for it
837 * @param outSize The max size of 'out'
838 * @param exception The exception to dump
839 * @return out
840 */
841 Dll_Export const char *getExceptionStr(char *out, int outSize, const ExceptionStruct *exception)
842 {
843 SNPRINTF(out, (size_t)outSize, "[%s] %s", exception->errorCode, exception->message);
844 return out;
845 }
846
847 /**
848 * Convert a 64 bit integer to a string.
849 * This helper concentrates this conversion to one place to
850 * simplify porting to compilers with no <code>int64_t = long long</code> support
851 * @param buf You need to pass this buffer with at least INT64_STRLEN_MAX=22 bytes of size
852 * @return buf
853 */
854 Dll_Export const char* int64ToStr(char * const buf, int64_t val)
855 {
856 if (buf == 0) return 0;
857 *buf = 0;
858 /* SNPRINTF(buf, INT64_STRLEN_MAX, "%lld", val); The normal sprintf should be safe enough */
859 snprintf0(buf, INT64_STRLEN_MAX, PRINTF_PREFIX_INT64_T, val); /* Returns number of written chars */
860 return buf;
861 }
862
863 /**
864 * Convert a string to a 64 bit integer.
865 * This helper concentrates this conversion to one place to
866 * simplify porting to compilers with no <code>long long</code> support
867 * @param val Your <code>long long</code> which is filled from <code>str</code>
868 * @param str The string to convert, for example "123450000LL"
869 * @return true on success
870 */
871 Dll_Export bool strToInt64(int64_t *val, const char * const str)
872 {
873 if (str == 0 || val == 0) return false;
874 /*str[INT64_STRLEN_MAX-1] = 0; sscanf should be safe enough to handle overflow */
875 /* %lld on UNIX, %I64d on Windows */
876 # if _MSC_VER >= 1400 && !defined(WINCE)
877 return (sscanf_s(str, PRINTF_PREFIX_INT64_T, val) == 1) ? true : false;
878 # else
879 return (sscanf(str, PRINTF_PREFIX_INT64_T, val) == 1) ? true : false;
880 # endif
881 }
882
883 Dll_Export bool strToLong(long *val, const char * const str)
884 {
885 if (str == 0 || val == 0) return false;
886 {
887 int64_t tmp;
888 bool ret = strToInt64(&tmp, str);
889 if (ret == false) return false;
890 *val = (long)tmp;
891 return true;
892 }
893 }
894
895 Dll_Export bool strToInt(int *val, const char * const str)
896 {
897 if (str == 0 || val == 0) return false;
898 {
899 int64_t tmp;
900 bool ret = strToInt64(&tmp, str);
901 if (ret == false) return false;
902 *val = (int)tmp;
903 return true;
904 }
905 }
906
907 Dll_Export bool strToULong(unsigned long *val, const char * const str)
908 {
909 if (str == 0 || val == 0) return false;
910 # if _MSC_VER >= 1400 && !defined(WINCE)
911 return (sscanf_s(str, "%lu", val) == 1) ? true : false;
912 # else
913 return (sscanf(str, "%lu", val) == 1) ? true : false;
914 # endif
915 }
916
917
918 /**
919 * Allocates the string with malloc for you.
920 * You need to free it with free()
921 * @param blob If null it is malloc()'d for you, else the given blob is used to be filled.
922 * @return The given blob (or a new malloc()'d if blob was NULL), the data is 0 terminated.
923 * We return NULL on out of memory.
924 */
925 Dll_Export BlobHolder *blobcpyAlloc(BlobHolder *blob, const char *data, size_t dataLen)
926 {
927 if (blob == 0) {
928 blob = (BlobHolder *)calloc(1, sizeof(BlobHolder));
929 if (blob == 0) return blob;
930 }
931 blob->dataLen = dataLen;
932 blob->data = (char *)malloc((dataLen+1)*sizeof(char));
933 if (blob->data == 0) {
934 free(blob);
935 return (BlobHolder *)0;
936 }
937 *(blob->data + dataLen) = 0;
938 memcpy(blob->data, data, dataLen);
939 return blob;
940 }
941
942 Dll_Export void freeBlobHolder(BlobHolder *blob)
943 {
944 if (blob == 0) return;
945 freeBlobHolderContent(blob);
946 free(blob);
947 }
948
949 /**
950 * free()'s the data in the given blob, does not free the blob itself.
951 * @param blob if NULL we return NULL
952 * @return The given blob
953 */
954 Dll_Export BlobHolder *freeBlobHolderContent(BlobHolder *blob)
955 {
956 if (blob == 0) return 0;
957 if (blob->data != 0) {
958 free(blob->data);
959 blob->data = 0;
960 blob->dataLen = 0;
961 }
962 return blob;
963 }
964
965 /**
966 * Converts the given binary data to a more readable string,
967 * the zero bytes are replaced by '*'
968 * @param blob The binary data
969 * @return readable is returned, it must be free()'d
970 */
971 Dll_Export char *blobDump(BlobHolder *blob)
972 {
973 return toReadableDump(blob->data, blob->dataLen);
974 }
975
976 Dll_Export void freeBlobDump(char *blobDumpP)
977 {
978 free(blobDumpP);
979 }
980
981 # ifdef HELPER_UTIL_MAIN
982 /*
983 * gcc -g -Wall -DHELPER_UTIL_MAIN=1 -I../../ -o helper helper.c -I../
984 */
985 int main()
986 {
987 const long millisecs = 500;
988 const int currLevel = 3;
989 const char *location = __FILE__;
990 const char *p = "OOOO";
991 int i = 3;
992 xmlBlasterDefaultLogging(0, currLevel, XMLBLASTER_LOG_WARN, location, "%s i=%d\n", p, i);
993
994 printf("Sleeping now for %ld millis\n", millisecs);
995 sleepMillis(millisecs);
996 printf("Waiking up after %ld millis\n", millisecs);
997
998 {
999 const char *ptr = " 28316";
1000 char tr[20];
1001 strncpy0(tr, ptr, 20);
1002 trim(tr);
1003 printf("Before '%s' after '%s'\n", ptr, tr);
1004 }
1005 {
1006 const char *ptr = " 28316 ";
1007 char tr[20];
1008 strncpy0(tr, ptr, 20);
1009 trim(tr);
1010 printf("Before '%s' after '%s'\n", ptr, tr);
1011 }
1012 {
1013 ExceptionStruct ex;
1014 strncpy0(ex.errorCode, "Original.cause", EXCEPTIONSTRUCT_ERRORCODE_LEN);
1015 strncpy0(ex.message, "Original message", EXCEPTIONSTRUCT_MESSAGE_LEN);
1016 embedException(&ex, "new.cause", "new message", &ex);
1017 printf("errorCode=%s message=%s\n", ex.errorCode, ex.message);
1018 }
1019 return 0;
1020 }
1021 # endif
syntax highlighted by Code2HTML, v. 0.9.1