/* Copyright (c) 2001-2009 by SoftIntegration, Inc. All Rights Reserved */
#include <stdio.h>
#include <stdbool.h> 
#include <stdarg.h> 

int snprintf (char *str, size_t n, const char *fmt, ...) {
  /* this local function does not count the str length for for computational array */
  unsigned int printf_string_upper_bound (char* format, va_list args) {
  unsigned int len = 1;
  while (*format)
    {
      _Bool long_int = false;
      _Bool extra_long = false;
      char c;

      c = *format++;

      if (c == '%')
	{
	  _Bool done = false;

	  while (*format && !done)
	    {
	      switch (*format++)
		{
		  char *string_arg;

		case '*':
		  len += va_arg (args, int);
		  break;
		case '1':
		case '2':
		case '3':
		case '4':
		case '5':
		case '6':
		case '7':
		case '8':
		case '9':
		  /* add specified format length, since it might exceed the
		   * size we assume it to have.
		   */
		  format -= 1;
		  len += strtol (format, (char**) &format, 10);
		  break;
		case 'h':
		  /* ignore short int flag, since all args have at least the
		   * same size as an int
		   */
		  break;
		case 'l':
		  if (long_int)
		    extra_long = true; /* linux specific */
		  else
		    long_int = true;
		  break;
		case 'q':
		case 'L':
		  long_int = true;
		  extra_long = true;
		  break;
		case 's':
		  string_arg = va_arg (args, char *);
		  if (string_arg)
		    len += strlen (string_arg);
		  else
		    {
		      /* add enough padding to hold "(null)" identifier */
		      len += 16;
		    }
		  done = true;
		  break;
		case 'd':
		case 'i':
		case 'o':
		case 'u':
		case 'x':
		case 'X':
#ifdef	G_HAVE_GINT64
		  if (extra_long)
		    (void) va_arg (args, gint64);
		  else
#endif	/* G_HAVE_GINT64 */
		    {
		      if (long_int)
			(void) va_arg (args, long);
		      else
			(void) va_arg (args, int);
		    }
		  len += extra_long ? 64 : 32;
		  done = true;
		  break;
		case 'D':
		case 'O':
		case 'U':
		  (void) va_arg (args, long);
		  len += 32;
		  done = true;
		  break;
		case 'e':
		case 'E':
		case 'f':
		case 'g':
#ifdef HAVE_LONG_DOUBLE
		  if (extra_long)
		    (void) va_arg (args, long double);
		  else
#endif	/* HAVE_LONG_DOUBLE */
		    (void) va_arg (args, double);
		  len += extra_long ? 128 : 64;
		  done = true;
		  break;
		case 'c':
		  (void) va_arg (args, int);
		  len += 1;
		  done = true;
		  break;
		case 'p':
		case 'n':
		  (void) va_arg (args, void*);
		  len += 32;
		  done = true;
		  break;
		case '%':
		  len += 1;
		  done = true;
		  break;
		default:
		  /* ignore unknow/invalid flags */
		  break;
		}
	    }
	}
      else
	len += 1;
    }
    return len;
  }

  char *printed;
  va_list args;

  va_start (args, fmt);
  printed = malloc (sizeof(char)*printf_string_upper_bound (fmt, args));
  vsprintf (printed, fmt, args);
  va_end (args);
  strncpy (str, printed, n);
  str[n-1] = '\0';
  free (printed);
  return strlen (str);
}

