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