text: /*
   * lib.c - Library functions.
   *
   * Written by
   *  Andreas Boose <viceteam@t-online.de>
   *
   * This file is part of VICE, the Versatile Commodore Emulator.
   * See README for copyright notice.
   *
   *  This program is free software; you can redistribute it and/or modify
   *  it under the terms of the GNU General Public License as published by
   *  the Free Software Foundation; either version 2 of the License, or
   *  (at your option) any later version.
   *
   *  This program is distributed in the hope that it will be useful,
   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   *  GNU General Public License for more details.
   *
   *  You should have received a copy of the GNU General Public License
   *  along with this program; if not, write to the Free Software
   *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
   *  02111-1307  USA.
   *
   */
  
  #include "vice.h"
  
  
  #include <stdarg.h>
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
  
  #ifdef AMIGA_SUPPORT
  #ifndef __USE_INLINE__
  #define __USE_INLINE__
  #endif
  #endif
  
  #if defined(AMIGA_SUPPORT) || defined(__VBCC__)
  #include <proto/exec.h>
  #ifndef AMIGA_SUPPORT
  #define AMIGA_SUPPORT
  #endif
  #endif
  
  #include "types.h"
  
  #ifdef DEBUG
  /* enable memory debugging */
  #define LIB_DEBUG
  /* enable pinpointing of memory leaks, don't forget to enable in lib.h */
  #define LIB_DEBUG_PINPOINT
  #ifdef __GNUC__
  /* #define LIB_DEBUG_CALLER */
  #endif
  #endif
  
  #ifdef LIB_DEBUG
  #define LIB_DEBUG_SIZE  0x10000
  #define LIB_DEBUG_GUARD 0x1000
  #define LIB_DEBUG_TOPMAX 50
  
  static unsigned int lib_debug_initialized = 0;
  
  #ifdef LIB_DEBUG_PINPOINT
  static char *lib_debug_filename[LIB_DEBUG_SIZE];
  static unsigned int lib_debug_line[LIB_DEBUG_SIZE];
  static char *lib_debug_top_filename[LIB_DEBUG_TOPMAX];
  static unsigned int lib_debug_top_line[LIB_DEBUG_TOPMAX];
  static char *lib_debug_pinpoint_filename;
  static unsigned int lib_debug_pinpoint_line=0;
  #endif
  
  static void *lib_debug_address[LIB_DEBUG_SIZE];
  static unsigned int lib_debug_size[LIB_DEBUG_SIZE];
  static unsigned int lib_debug_top_size[LIB_DEBUG_TOPMAX];
  static unsigned int lib_debug_current_total = 0;
  static unsigned int lib_debug_max_total = 0;
  #ifdef LIB_DEBUG_CALLER
  static void *lib_debug_caller[LIB_DEBUG_SIZE];
  #endif
  
  #if LIB_DEBUG_GUARD > 0
  static char *lib_debug_guard_base[LIB_DEBUG_SIZE];
  static unsigned int lib_debug_guard_size[LIB_DEBUG_SIZE];
  #endif
  
  #ifdef LIB_DEBUG_CALLER
  static void *lib_debug_func_level1(void)
  {
      return __builtin_return_address(1 + 1);
  }
  
  static void *lib_debug_func_level2(void)
  {
      return __builtin_return_address(2 + 1);
  }
  
  static void *lib_debug_func_level3(void)
  {
      return __builtin_return_address(3 + 1);
  }
  #endif
  
  static void lib_debug_init(void)
  {
      memset(lib_debug_address, 0, sizeof(lib_debug_address));
  #ifdef LIB_DEBUG_CALLER
      memset(lib_debug_caller, 0, sizeof(lib_debug_caller));
  #endif
  #if LIB_DEBUG_GUARD > 0
      memset(lib_debug_guard_base, 0, sizeof(lib_debug_guard_base));
  #endif
  #ifdef LIB_DEBUG_PINPOINT
      memset(lib_debug_line, 0, sizeof(lib_debug_line));
      memset(lib_debug_top_size, 0, sizeof(lib_debug_top_size));
  #endif
      lib_debug_initialized = 1;
  }
  
  static void lib_debug_add_top(char *filename, unsigned int line, unsigned int size)
  {
      unsigned int index, i;
      for (index = 0; index < LIB_DEBUG_TOPMAX; index++) {
          if (size > lib_debug_top_size[index]) {
  #if 1
              for (i = (LIB_DEBUG_TOPMAX - 1); i > index; i--) {
                  lib_debug_top_size[i] = lib_debug_top_size[i - 1];
                  lib_debug_top_line[i] = lib_debug_top_line[i - 1];
                  lib_debug_top_filename[i] = lib_debug_top_filename[i - 1];
              }
  #endif
              lib_debug_top_size[index] = size;
              lib_debug_top_line[index] = line;
              lib_debug_top_filename[index] = filename;
              break;
          }
      }
  }
  
  static void lib_debug_alloc(void *ptr, size_t size, int level)
  {
      unsigned int index;
  #ifdef LIB_DEBUG_CALLER
      void *func = NULL;
  #endif
  
      if (!lib_debug_initialized)
          lib_debug_init();
  
      index = 0;
  
      while (index < LIB_DEBUG_SIZE && lib_debug_address[index] != NULL) {
          index++;
      }
  
      if (index == LIB_DEBUG_SIZE) {
          printf("Error: lib_debug_alloc(): Out of debug address slots. (increase LIB_DEBUG_SIZE!)\n");
          return;
      }
  
  #ifdef LIB_DEBUG_CALLER
      switch (level) {
        case 1:
          func = lib_debug_func_level1();
          break;
        case 2:
          func = lib_debug_func_level2();
          break;
        case 3:
          func = lib_debug_func_level3();
          break;
      }
      lib_debug_caller[index] = func;
  #endif
  
  #if 0
      if (ptr == (void *)0x85c2c80)
          *(int *)0 = 0;
  #endif
  #if 0
      printf("lib_debug_alloc(): Alloc address %p size %i slot %i from %p.\n",
             ptr, size, index, func);
  #endif
      lib_debug_address[index] = ptr;
      lib_debug_size[index] = (unsigned int)size;
  #ifdef LIB_DEBUG_PINPOINT
      lib_debug_filename[index]=lib_debug_pinpoint_filename;
      lib_debug_line[index]=lib_debug_pinpoint_line;
      lib_debug_add_top(lib_debug_pinpoint_filename, lib_debug_pinpoint_line, (unsigned int)size);
      lib_debug_pinpoint_line=0;
  #endif
      lib_debug_current_total += (unsigned int)size;
      if (lib_debug_current_total > lib_debug_max_total) {
          lib_debug_max_total = lib_debug_current_total;
      }
  }
  
  static void lib_debug_free(void *ptr, unsigned int level, unsigned int fill)
  {
      unsigned int index;
  #ifdef LIB_DEBUG_CALLER
      void *func = NULL;
  #endif
  
      if (ptr == NULL) {
          return;
      }
  
      index = 0;
  
      while (index < LIB_DEBUG_SIZE && lib_debug_address[index] != ptr) {
          index++;
      }
  
      if (index == LIB_DEBUG_SIZE) {
  #if 0
          printf("lib_debug_free(): Cannot find debug address!\n");
  #endif
          return;
      }
  
  #ifdef LIB_DEBUG_CALLER
      switch (level) {
        case 1:
          func = lib_debug_func_level1();
          break;
        case 2:
          func = lib_debug_func_level2();
          break;
        case 3:
          func = lib_debug_func_level3();
          break;
      }
  #endif
  
  #if 0
      printf("lib_debug_free(): Free address %p size %i slot %i from %p.\n",
             ptr, lib_debug_size[index], index, func);
  #endif
  
      if (fill)
          memset(ptr, 0xdd, lib_debug_size[index]);
  
      lib_debug_address[index] = NULL;
      lib_debug_current_total -= lib_debug_size[index];
  }
  
  /*-----------------------------------------------------------------------*/
  
  #if LIB_DEBUG_GUARD > 0
  static void lib_debug_guard_add(char *ptr, unsigned int size)
  {
      unsigned int index;
  
      if (!lib_debug_initialized) {
          lib_debug_init();
      }
  
      index = 0;
  
      /* find free slot */
      while (index < LIB_DEBUG_SIZE && lib_debug_guard_base[index] != NULL) {
          index++;
      }
  
      if (index == LIB_DEBUG_SIZE) {
          printf("Error: lib_debug_guard_add(): Out of debug address slots. (increase LIB_DEBUG_SIZE)\n");
          return;
      }
  #if 0
      printf("ADD BASE %p SLOT %d SIZE %d\n", ptr, index, size);
  #endif
      lib_debug_guard_base[index] = ptr;
      lib_debug_guard_size[index] = (unsigned int)size;
  
      memset(ptr, 0x55, LIB_DEBUG_GUARD);
      memset(ptr + LIB_DEBUG_GUARD + size, 0x55, LIB_DEBUG_GUARD);
  }
  
  static void lib_debug_guard_remove(char *ptr)
  {
      unsigned int index;
      unsigned int i;
  
      index = 0;
  
      /* find matching slot */
      while (index < LIB_DEBUG_SIZE
          && lib_debug_guard_base[index] != (ptr - LIB_DEBUG_GUARD)) {
          index++;
      }
  
      if (index == LIB_DEBUG_SIZE) {
  #ifdef LIB_DEBUG_PINPOINT
          printf("%s:%d: ", lib_debug_pinpoint_filename, lib_debug_pinpoint_line);
  #endif
          printf("Error: lib_debug_guard_remove(): Cannot find debug address %p! (make sure to use functions from lib.h)\n",
                 ptr - LIB_DEBUG_GUARD);
          return;
      }
  
      for (i = 0; i < LIB_DEBUG_GUARD; i++) {
          if (*(ptr - LIB_DEBUG_GUARD + i) != 0x55) {
  #ifdef LIB_DEBUG_PINPOINT
              printf("%s:%d: ", lib_debug_pinpoint_filename, lib_debug_pinpoint_line);
  #endif
              printf("Error: Memory corruption in lower part of base %p!\n", ptr - LIB_DEBUG_GUARD);
              break;
          }
      }
      for (i = 0; i < LIB_DEBUG_GUARD; i++) {
          if (*(ptr + lib_debug_guard_size[index] + i) != 0x55) {
  #ifdef LIB_DEBUG_PINPOINT
              printf("%s:%d: ", lib_debug_pinpoint_filename, lib_debug_pinpoint_line);
  #endif
              printf("Error: Memory corruption in higher part of base %p!\n", ptr - LIB_DEBUG_GUARD);
              break;
          }
      }
  #if 0
      printf("REM BASE %p SLOT %d\n", ptr - LIB_DEBUG_GUARD, index);
  #endif
      lib_debug_guard_base[index] = NULL;
  }
  
  static unsigned int lib_debug_guard_size_get(char *ptr)
  {
      unsigned int index;
  
      index = 0;
  
      while (index < LIB_DEBUG_SIZE
          && lib_debug_guard_base[index] != (ptr - LIB_DEBUG_GUARD)) {
          index++;
      }
  
      if (index == LIB_DEBUG_SIZE) {
  #ifdef LIB_DEBUG_PINPOINT
          printf("%s:%d: ", lib_debug_pinpoint_filename, lib_debug_pinpoint_line);
  #endif
          printf("Error: lib_debug_guard_size(): Cannot find debug address %p!\n",
                 ptr - LIB_DEBUG_GUARD);
          return 0;
      }
  
      return lib_debug_guard_size[index];
  }
  #endif
  
  static void *lib_debug_libc_malloc(size_t size)
  {
  #if LIB_DEBUG_GUARD > 0
      char *ptr;
  
      ptr = (char *)malloc(size + 2 * LIB_DEBUG_GUARD);
      lib_debug_guard_add(ptr, (unsigned int)size);
  
      return (void *)(ptr + LIB_DEBUG_GUARD);
  #else
      return malloc(size);
  #endif
  }
  
  static void *lib_debug_libc_calloc(size_t nmemb, size_t size)
  {
  #if LIB_DEBUG_GUARD > 0
      char *ptr;
  
      ptr = (char *)malloc(nmemb * size + 2 * LIB_DEBUG_GUARD);
      lib_debug_guard_add(ptr, (unsigned int)(nmemb * size));
  
      memset(ptr + LIB_DEBUG_GUARD, 0, nmemb * size);
  
      return (void *)(ptr + LIB_DEBUG_GUARD);
  #else
      return calloc(nmemb, size);
  #endif
  }
  
  static void lib_debug_libc_free(void *ptr)
  {
  #if LIB_DEBUG_GUARD > 0
      if (ptr != NULL) {
          lib_debug_guard_remove((char *)ptr);
          free((char *)ptr - LIB_DEBUG_GUARD);
      } else {
  #ifdef LIB_DEBUG_PINPOINT
          printf("%s:%d: ", lib_debug_pinpoint_filename, lib_debug_pinpoint_line);
  #endif
          printf("Warning: Pointer passed to lib_debug_libc_free is NULL.\n");
      }
  #else
      free(ptr);
  #endif
  }
  
  static void *lib_debug_libc_realloc(void *ptr, size_t size)
  {
  #if LIB_DEBUG_GUARD > 0
      char *new_ptr = NULL;
  
      if (size > 0) {
          new_ptr = lib_debug_libc_malloc(size);
  
          if (ptr != NULL) {
              size_t old_size;
  
              old_size = (size_t)lib_debug_guard_size_get((char *)ptr);
  
              if (size >= old_size) {
                  memcpy(new_ptr, ptr, old_size);
              } else {
                  memcpy(new_ptr, ptr, size);
              }
          }
      }
  
      if (ptr != NULL)
          lib_debug_libc_free(ptr);
  
      return (void *)new_ptr;
  #else
      return realloc(ptr, size);
  #endif
  }
  #endif
  
  /*-----------------------------------------------------------------------*/
  /*
  static void printsize(unsigned int size)
  {
      if (size > (1024 * 1024)) {
          printf("%dMiB", size / (1024 * 1024));
      } else if (size > (1024)) {
          printf("%dKiB", size / (1024));
      } else {
          printf("%dB", size);
      }
  }*/
  
  void lib_debug_check(void)
  {
  #ifdef LIB_DEBUG
      unsigned int index, count;
      unsigned int leakbytes;
  
      count = 0;
      leakbytes = 0;
  
      for (index = 0; index < LIB_DEBUG_SIZE; index++) {
          if (lib_debug_address[index] != NULL) {
              count++;
  #ifdef LIB_DEBUG_PINPOINT
              printf("%s:%d: ", lib_debug_filename[index], lib_debug_line[index]);
  #endif
              printf("Warning: Memory block allocated here was not free'd (Memory leak with size 0x%x at %p).",
                     lib_debug_size[index], lib_debug_address[index]);
  #ifdef LIB_DEBUG_CALLER
              printf("(called from %p)", lib_debug_caller[index]);
  #endif
              printf("\n");
              leakbytes += lib_debug_size[index];
  #if LIB_DEBUG_GUARD > 0
              lib_debug_guard_remove((char *)lib_debug_address[index]);
  #endif
          }
      }
      printf("Total memory leaks: %d Bytes leaked: 0x%x\n", count, leakbytes);
      printf("max. total memory that was allocated: 0x%x bytes. (", lib_debug_max_total);
      printsize(lib_debug_max_total);
      printf(")\n");
  
  #ifdef LIB_DEBUG_PINPOINT
      printf("\nTop %d largest allocated blocks:\n", LIB_DEBUG_TOPMAX);
      for (index = 0; index < LIB_DEBUG_TOPMAX; index++) {
          if (lib_debug_top_size[index]) {
              printf("%8x bytes (", lib_debug_top_size[index]);
              printsize(lib_debug_top_size[index]);
              printf(") allocated at %s:%d\n", lib_debug_top_filename[index], lib_debug_top_line[index]);
          }
      }
  #endif
  
  #endif
  }
  
  /*-----------------------------------------------------------------------*/
  
  void *lib_malloc(size_t size)
  {
  #ifdef LIB_DEBUG
      void *ptr = lib_debug_libc_malloc(size);
  #else
      void *ptr = malloc(size);
  #endif
  
  #ifndef __OS2__
      if (ptr == NULL && size > 0) {
          exit(-1);
      }
  #endif
  #ifdef LIB_DEBUG
      lib_debug_alloc(ptr, size, 3);
  #endif
  
      if (ptr) {
          memset(ptr, 0, size);
      }
  
      return ptr;
  }
  
  #ifdef AMIGA_SUPPORT
  void *lib_AllocVec(unsigned long size, unsigned long attributes)
  {
  #ifdef LIB_DEBUG
      void *ptr;
  
      if (attributes & MEMF_CLEAR) {
          ptr = lib_debug_libc_calloc(1, size);
      } else {
          ptr = lib_debug_libc_malloc(size);
      }
  #else
      void *ptr = AllocVec(size, attributes);
  #endif
  
  #ifndef __OS2__
      if (ptr == NULL && size > 0) {
          exit(-1);
      }
  #endif
  #ifdef LIB_DEBUG
      lib_debug_alloc(ptr, size, 1);
  #endif
  
      return ptr;
  }
  
  void *lib_AllocMem(unsigned long size, unsigned long attributes)
  {
  #ifdef LIB_DEBUG
      void *ptr;
  
      if (attributes & MEMF_CLEAR)
          ptr = lib_debug_libc_calloc(1, size);
      else
          ptr = lib_debug_libc_malloc(size);
  #else
      void *ptr = AllocMem(size, attributes);
  #endif
  
  #ifndef __OS2__
      if (ptr == NULL && size > 0)
          exit(-1);
  #endif
  #ifdef LIB_DEBUG
      lib_debug_alloc(ptr, size, 1);
  #endif
  
      return ptr;
  }
  #endif
  
  /* Like calloc, but abort if not enough memory is available.  */
  void *lib_calloc(size_t nmemb, size_t size)
  {
  #ifdef LIB_DEBUG
      void *ptr = lib_debug_libc_calloc(nmemb, size);
  #else
      void *ptr = calloc(nmemb, size);
  #endif
  
  #ifndef __OS2__
      if (ptr == NULL && (size * nmemb) > 0)
          exit(-1);
  #endif
  #ifdef LIB_DEBUG
      lib_debug_alloc(ptr, size * nmemb, 1);
  #endif
  
      return ptr;
  }
  
  /* Like realloc, but abort if not enough memory is available.  */
  void *lib_realloc(void *ptr, size_t size)
  {
  #ifdef LIB_DEBUG
      void *new_ptr = lib_debug_libc_realloc(ptr, size);
  #else
      void *new_ptr = realloc(ptr, size);
  #endif
  
  #ifndef __OS2__
      if (new_ptr == NULL)
          exit(-1);
  #endif
  #ifdef LIB_DEBUG
      lib_debug_free(ptr, 1, 0);
      lib_debug_alloc(new_ptr, size, 1);
  #endif
  
      return new_ptr;
  }
  
  void lib_free(const void *constptr)
  {
      void * ptr = (void*) constptr;
  #ifdef LIB_DEBUG
      lib_debug_free(ptr, 1, 1);
  #endif
  
  #ifdef LIB_DEBUG
      lib_debug_libc_free(ptr);
  #else
      free(ptr);
  #endif
  }
  
  #ifdef AMIGA_SUPPORT
  void lib_FreeVec(void *ptr)
  {
  #ifdef LIB_DEBUG
      lib_debug_free(ptr, 1, 1);
      lib_debug_libc_free(ptr);
  #else
      FreeVec(ptr);
  #endif
  }
  
  void lib_FreeMem(void *ptr, unsigned long size)
  {
  #ifdef LIB_DEBUG
      lib_debug_free(ptr, 1, 1);
      lib_debug_libc_free(ptr);
  #else
      FreeMem(ptr, size);
  #endif
  }
  #endif
  
  /*-----------------------------------------------------------------------*/
  
  /* Malloc enough space for `str', copy `str' into it and return its
     address.  */
  char *lib_stralloc(const char *str)
  {
      size_t size;
      char *ptr;
  
      if (str == NULL) {
          exit(-1);
      }
  
      size = strlen(str) + 1;
      ptr = lib_malloc(size);
  
      memcpy(ptr, str, size);
      return ptr;
  }
  
  #ifdef HAVE_WORKING_VSNPRINTF
  
  /* taken shamelessly from printf(3) man page of the Linux Programmer's Manual */
  
  char *lib_mvsprintf(const char *fmt, va_list args)
  {
      /* Guess we need no more than 100 bytes. */
      int n, size = 100;
      char *p, *np;
  
      if ((p = lib_malloc (size)) == NULL) {
          return NULL;
      }
  
      while (1) {
          /* Try to print in the allocated space. */
          n = vsnprintf (p, size, fmt, args /* ap */);
  
          /* If that worked, return the string. */
          if (n > -1 && n < size) {
              return p;
          }
  
          /* Else try again with more space. */
          if (n > -1) {     /* glibc 2.1 and C99 */
              size = n + 1; /* precisely what is needed */
          }
          else {            /* glibc 2.0 */
              size *= 2;    /* twice the old size */
          }
  
          if ((np = lib_realloc (p, size)) == NULL) {
              lib_free(p);
              return NULL;
          }
          else {
              p = np;
          }
      }
  }
  
  #else
  
  /* xmsprintf() is like sprintf() but lib_malloc's the buffer by itself.  */
  
  #define xmvsprintf_is_digit(c) ((c) >= '0' && (c) <= '9')
  
  static int xmvsprintf_skip_atoi(const char **s)
  {
      int i = 0;
  
      while (xmvsprintf_is_digit(**s)) {
          i = i * 10 + *((*s)++) - '0';
      }
      return i;
  }
  
  #define ZEROPAD 1               /* pad with zero */
  #define SIGN    2               /* unsigned/signed long */
  #define PLUS    4               /* show plus */
  #define SPACE   8               /* space if plus */
  #define LEFT    16              /* left justified */
  #define SPECIAL 32              /* 0x */
  #define LARGE   64              /* use 'ABCDEF' instead of 'abcdef' */
  
  static inline int xmvsprintf_do_div(long *n, unsigned int base)
  {
      int res;
  
      res = ((unsigned long)*n) % (unsigned)base;
      *n = ((unsigned long)*n) / (unsigned)base;
      return res;
  }
  
  static size_t xmvsprintf_strnlen(const char * s, size_t count)
  {
      const char *sc;
  
      for (sc = s; count-- && *sc != '\0'; ++sc)
          /* nothing */;
      return sc - s;
  }
  
  static void xmvsprintf_add(char **buf, unsigned int *bufsize,
                             unsigned int *position, char write)
  {
      if (*position == *bufsize) {
          *bufsize *= 2;
          *buf = lib_realloc(*buf, *bufsize);
      }
      (*buf)[*position] = write;
      *position += 1;
  }
  
  static void xmvsprintf_number(char **buf, unsigned int *bufsize,
                                unsigned int *position, long num, int base,
                                int size, int precision, int type)
  {
      char c, sign, tmp[66];
      const char *digits = "0123456789abcdefghijklmnopqrstuvwxyz";
      int i;
  
      if (type & LARGE)
          digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
      if (type & LEFT)
          type &= ~ZEROPAD;
      if (base < 2 || base > 36)
          return;
      c = (type & ZEROPAD) ? '0' : ' ';
      sign = 0;
      if (type & SIGN) {
          if (num < 0) {
              sign = '-';
              num = -num;
              size--;
          } else if (type & PLUS) {
              sign = '+';
              size--;
          } else if (type & SPACE) {
              sign = ' ';
              size--;
          }
      }
      if (type & SPECIAL) {
          if (base == 16)
              size -= 2;
          else if (base == 8)
              size--;
      }
  
      i = 0;
      if (num == 0)
          tmp[i++] = '0';
      else while (num != 0)
          tmp[i++] = digits[xmvsprintf_do_div(&num, base)];
      if (i > precision)
          precision = i;
          size -= precision;
      if (!(type & (ZEROPAD + LEFT)))
          while (size-->0)
              xmvsprintf_add(buf, bufsize, position, ' ');
      if (sign)
          xmvsprintf_add(buf, bufsize, position, sign);
      if (type & SPECIAL) {
          if (base == 8)
              xmvsprintf_add(buf, bufsize, position, '0');
          else if (base == 16) {
              xmvsprintf_add(buf, bufsize, position, '0');
              xmvsprintf_add(buf, bufsize, position, digits[33]);
          }
      }
      if (!(type & LEFT))
          while (size-- > 0)
              xmvsprintf_add(buf, bufsize, position, c);
      while (i < precision--)
          xmvsprintf_add(buf, bufsize, position, '0');
      while (i-- > 0)
          xmvsprintf_add(buf, bufsize, position, tmp[i]);
      while (size-- > 0)
          xmvsprintf_add(buf, bufsize, position, ' ');
  }
  
  char *lib_mvsprintf(const char *fmt, va_list args)
  {
      char *buf;
      unsigned int position, bufsize;
  
  
      size_t len;
      int i, base;
      unsigned long num;
      const char *s;
  
      int flags;        /* flags to number() */
      int field_width;  /* width of output field */
      int precision;    /* min. # of digits for integers; max
                           number of chars for from string */
      int qualifier;    /* 'h', 'l', or 'L' for integer fields */
  
      /* Setup the initial buffer.  */
      buf = lib_malloc(10);
      position = 0;
      bufsize = 10;
  
      if(fmt == NULL) {
      	*buf = 0;
      	return buf;
      }
  
      for ( ; *fmt ; ++fmt) {
          if (*fmt != '%') {
              xmvsprintf_add(&buf, &bufsize, &position, *fmt);
              continue;
          }
  
          /* process flags */
          flags = 0;
  repeat:
          ++fmt;  /* this also skips first '%' */
          switch (*fmt) {
            case '-':
              flags |= LEFT;
              goto repeat;
            case '+':
              flags |= PLUS;
              goto repeat;
            case ' ':
              flags |= SPACE;
              goto repeat;
            case '#':
              flags |= SPECIAL;
              goto repeat;
            case '0':
              flags |= ZEROPAD;
              goto repeat;
          }
  
          /* get field width */
          field_width = -1;
          if (xmvsprintf_is_digit(*fmt))
              field_width = xmvsprintf_skip_atoi(&fmt);
          else if (*fmt == '*') {
              ++fmt;
              /* it's the next argument */
              field_width = va_arg(args, int);
              if (field_width < 0) {
                  field_width = -field_width;
                  flags |= LEFT;
              }
          }
  
          /* get the precision */
          precision = -1;
          if (*fmt == '.') {
              ++fmt;
              if (xmvsprintf_is_digit(*fmt))
                  precision = xmvsprintf_skip_atoi(&fmt);
              else if (*fmt == '*') {
                  ++fmt;
                   /* it's the next argument */
                   precision = va_arg(args, int);
              }
              if (precision < 0)
                  precision = 0;
          }
  
          /* get the conversion qualifier */
          qualifier = -1;
          if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L') {
              qualifier = *fmt;
              ++fmt;
          }
  
          /* default base */
          base = 10;
  
          switch (*fmt) {
            case 'c':
              if (!(flags & LEFT))
                  while (--field_width > 0)
                      xmvsprintf_add(&buf, &bufsize, &position, ' ');
              xmvsprintf_add(&buf, &bufsize, &position,
                             (unsigned char) va_arg(args, int));
              while (--field_width > 0)
                  xmvsprintf_add(&buf, &bufsize, &position, ' ');
              continue;
  
            case 's':
              s = va_arg(args, char *);
              if (!s)
                  s = "<NULL>";
  
              len = xmvsprintf_strnlen(s, precision);
  
              if (!(flags & LEFT))
                  while (field_width > 0 && len < field_width--)
                      xmvsprintf_add(&buf, &bufsize, &position, ' ');
              for (i = 0; i < len; ++i)
                  xmvsprintf_add(&buf, &bufsize, &position, *s++);
              while (field_width > 0 && len < field_width--)
                  xmvsprintf_add(&buf, &bufsize, &position, ' ');
              continue;
  
            case 'p':
              if (field_width == -1) {
                  field_width = 2*sizeof(void *);
                  flags |= ZEROPAD;
              }
              xmvsprintf_number(&buf, &bufsize, &position,
                                vice_ptr_to_uint(va_arg(args, void *)), 16,
                                field_width, precision, flags);
              continue;
  
            case '%':
              xmvsprintf_add(&buf, &bufsize, &position, '%');
              continue;
  
            /* integer number formats - set up the flags and "break" */
            case 'o':
              base = 8;
              break;
            case 'X':
              flags |= LARGE;
            case 'x':
              base = 16;
              break;
            case 'd':
            case 'i':
              flags |= SIGN;
            case 'u':
              break;
  
            default:
              xmvsprintf_add(&buf, &bufsize, &position, '%');
              if (*fmt)
                  xmvsprintf_add(&buf, &bufsize, &position, *fmt);
              else
                  --fmt;
              continue;
          }
          if (qualifier == 'l')
              num = va_arg(args, unsigned long);
          else if (qualifier == 'h') {
              num = (unsigned short) va_arg(args, int);
              if (flags & SIGN)
              num = (short) num;
          } else if (flags & SIGN)
              num = va_arg(args, int);
          else
              num = va_arg(args, unsigned int);
          xmvsprintf_number(&buf, &bufsize, &position, num, base, field_width,
                            precision, flags);
      }
      xmvsprintf_add(&buf, &bufsize, &position, '\0');
  
      /* Trim buffer to final size.  */
      buf = lib_realloc(buf, strlen(buf) + 1);
  
      return buf;
  }
  #endif /* #ifdef HAVE_WORKING_VSNPRINTF */
  
  char *lib_msprintf(const char *fmt, ...)
  {
      va_list args;
      char *buf;
  
      //if(fmt == NULL) return "";
  
      va_start(args, fmt);
      buf = lib_mvsprintf(fmt, args);
      va_end(args);
  
      return buf;
  }
  
  #ifdef LIB_DEBUG_PINPOINT
  void *lib_malloc_pinpoint(size_t size, char *name, unsigned int line)
  {
    lib_debug_pinpoint_filename=name;
    lib_debug_pinpoint_line=line;
    return lib_malloc(size);
  }
  
  void lib_free_pinpoint(void *p, char *name, unsigned int line)
  {
    lib_debug_pinpoint_filename=name;
    lib_debug_pinpoint_line=line;
    lib_free(p);
  }
  
  void *lib_calloc_pinpoint(size_t nmemb, size_t size, char *name, unsigned int line)
  {
    lib_debug_pinpoint_filename=name;
    lib_debug_pinpoint_line=line;
    return lib_calloc(nmemb, size);
  }
  
  void *lib_realloc_pinpoint(void *p, size_t size, char *name, unsigned int line)
  {
    lib_debug_pinpoint_filename=name;
    lib_debug_pinpoint_line=line;
    return lib_realloc(p, size);
  }
  
  char *lib_stralloc_pinpoint(const char *str, char *name, unsigned int line)
  {
    lib_debug_pinpoint_filename=name;
    lib_debug_pinpoint_line=line;
    return lib_stralloc(str);
  }
  
  #ifdef AMIGA_SUPPORT
  void *lib_AllocVec_pinpoint(unsigned long size, unsigned long attributes, char *name, unsigned int line)
  {
    lib_debug_pinpoint_filename=name;
    lib_debug_pinpoint_line=line;
    return lib_AllocVec(size, attributes);
  }
  
  void lib_FreeVec_pinpoint(void *ptr, char *name, unsigned int line)
  {
    lib_debug_pinpoint_filename=name;
    lib_debug_pinpoint_line=line;
    return lib_FreeVec(ptr);
  }
  
  void *lib_AllocMem_pinpoint(unsigned long size, unsigned long attributes, char *name, unsigned int line)
  {
    lib_debug_pinpoint_filename=name;
    lib_debug_pinpoint_line=line;
    return lib_AllocMem(size, attributes);
  }
  
  void lib_FreeMem_pinpoint(void *ptr, unsigned long size, char *name, unsigned int line)
  {
    lib_debug_pinpoint_filename=name;
    lib_debug_pinpoint_line=line;
    return lib_FreeMem(ptr, size);
  }
  #endif
  
  #endif
  