/*************************************************************************
 *
 * $Id: trio.c,v 1.1.1.1 2000/11/13 02:42:50 holsta Exp $
 *
 * Copyright (C) 1998 Bjorn Reese and Daniel Stenberg.
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
 * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE AUTHORS AND
 * CONTRIBUTORS ACCEPT NO RESPONSIBILITY IN ANY CONCEIVABLE MANNER.
 *
 *************************************************************************
 *
 * 1999/07/23 - breese
 *
 *   Fixed the internal representation of numbers from signed to
 *   unsigned. Signed numbers posed a problem for large unsigned
 *   numbers (reported by Tero)
 *
 *   Fixed a tiny bug in trio_vsprintfcat
 *
 * 1999/05/16 - breese
 *
 *   Added size_t support (just waiting for a C9X compliant compiler
 *   to add ptrdiff_t and intmax_t)
 *
 *   Rewrote TrioOutStreamDouble so it does not use the libc sprintf
 *   to emulate floating-point anylonger.
 *
 *   Fixed width, precision, and adjustment for numbers and doubles.
 *
 * 1999/05/06 - breese
 *
 *   Fixed zero padding for %d. Now %d will only zero pad if explicitly
 *   requested to do so with the 0 flag.
 *
 *   Fixed an incorrect while() condition in TrioGetString
 *   (both errors were reported by Tero.)
 *
 * 1999/04/19 - breese
 *
 *   Fixed incorrect zero padding of pointers
 *
 * 1999/03/25 - Daniel & breese
 *
 *   Made it compile under cygwin
 *
 *   Fixed a bug were TrioPreprocess would return an error if no
 *   formatting chars were found (reported by Tero.)
 *
 * 1999/03/19 - breese
 *
 *   Added trio_strerror and TRIO_ERROR_NAME.
 *
 *   Changed the error codes to be positive (as errno)
 *
 *   Fixed two reads of uninitialized memory reported by Purify
 *
 *   Added binary specifiers 'b' and 'B' (like SCO.) ThousandSeparator
 *   can be used to separate nibbles (4 bit)
 *
 *   Renamed all Internal* functions to Trio*, which seems like a
 *   better namespace (even though it is of no practical interest
 *   because these functions are not visible beyond the scope of
 *   this file.)
 *
 * 1999/03/11 - breese
 *
 *   Double references and gaps in the arguments are not allowed (for
 *   the %n$ format) and in both cases an error code is returned.
 *
 * 1999/03/08 - breese
 *
 *   Added InStream and OutStream to the trio_T structure.
 *
 *   Started work on TrioScan.
 *
 *   Return values for errors changed. Two macros to unpack the error
 *   code has been added to the header.
 *
 *   Shortshort (hh) flag added.
 *
 *   %#s also quotes the quote-char now.
 *
 *   Removed the 'errorInFormat' boolean, which isn't used anymore
 *   after the functions bail out with an error instead.
 *
 * 1999/03/04 - Daniel
 *
 *   More than MAX_PARAMETERS parametes will now cause the
 *   TrioPreprocess() function to return error.
 *
 *   Unknown flags and/or specifiers cause errors too.
 *
 * 1999/03/01 - Daniel
 *
 *   Added trio_snprintfcat and trio_vsnprintfcat and the defined
 *   name StrFormatAppendMax. They append a formatted string to the end
 *   of a string.
 *
 *   Define MAX_PARAMETERS to 128 at all times instead of using NL_ARGMAX
 *   when that exists.
 *
 * 1999/02/24 - Daniel
 *
 *   Added platform fixes for Amiga as suggested by Tero Jänkä <tesaja@utu.fi>
 *
 * 1999/01/31 - Daniel
 *
 *   vaprintf did add a zero byte even when it had failed.
 *
 * 1999/01/31 - breese
 *
 *   Cleaned up the code for locale handling and thousand separator
 *
 * 1999/01/28 - Daniel Stenberg
 *
 *   Added trio_aprintf() and trio_vaprintf(). They return an allocated
 *   string.
 *
 * 1999/01/21 - breese
 *
 *   Added thousands separator for numbers
 *
 * 1999/01/09 - breese
 *
 *   Added floating point support for *printf
 *
 * 1998/10/01 - breese
 *
 *   Rewrote the implementation of *printf and *scanf and put all
 *   the code in this file. Extended qualifiers and qualifiers from other
 *   standards were added.
 *
 ************************************************************************/

/*
 * FIXME:
 *  scan
 *  complex numbers? (C9X _Complex)
 *  boolean values? (C9X _Bool)
 *  widechar
 *  j, z, t are quantifiers, not specifiers!!
 *  dprintf [GNU] works as fprintf except it uses a file descriptor
 *   instead of a FILE pointer
 *  trio_printf("%u\n", UINT_MAX) gives a different result than
 *  the system printf() (I am not sure which is most correct)
 */

static const char rcsid[] = "@(#)$Id: trio.c,v 1.1.1.1 2000/11/13 02:42:50 holsta Exp $";

#if defined(__STDC__) && (__STDC_VERSION__ >= 199901L)
# define TRIO_C9X
#endif

#undef TRIO_C9X  /* Not fully done yet */
#define TRIO_BSD
#define TRIO_GNU
#define TRIO_SUN
#define TRIO_UNIX98
#define TRIO_EXTENSION

/* Use __STDC_ISO_10646__ to determine if widechars are used */


#if defined(unix) || defined(__xlC__) /* AIX xlC workaround */
# define PLATFORM_UNIX
#elif defined(AMIGA) && defined(__GNUC__)
# define PLATFORM_UNIX
#endif

#include "trio.h"
#include "strio.h"
#include <ctype.h>
#include <math.h>
#include <limits.h>
#include <float.h>
#include <stdarg.h>
#include <errno.h>
#if defined(TRIO_C9X)
# include <stdint.h>
#endif
#if defined(PLATFORM_UNIX)
# include <locale.h>
# define USE_LOCALE
#endif
#ifndef DEBUG
# define NDEBUG
#endif
#include <assert.h>

#ifndef NULL
# define NULL 0
#endif
#define NIL ((char)0)
#ifndef FALSE
# define FALSE (1 == 0)
# define TRUE (! FALSE)
#endif

/* mincore() can be used for debugging purposes */
#define VALID(x) (NULL != (x))


/*************************************************************************
 * Internal definitions
 */

/* Pack the error code and the position. This is unpacked
 * with TRIO_ERROR_CODE and TRIO_ERROR_POSITION.
 */
#define TRIO_ERROR_RETURN(x,y) (- ((x) + ((y) << 8)))

#if defined(STANDALONE)
# undef printf
#endif

#if !defined(USE_LONGLONG)
# if defined(__GNUC__) && !defined(__STRICT_ANSI__)
#  define USE_LONGLONG
# elif defined(__SUNPRO_C)
#  define USE_LONGLONG
# endif
#endif

#if defined(USE_LONGLONG)
# define LONGLONG long long
# define ULONGLONG unsigned long long
# define LONGDOUBLE long double
#else
# define LONGLONG long
# define ULONGLONG unsigned long
# define LONGDOUBLE double
#endif

#if defined(TRIO_C9X)
# define LONGEST uintmax_t
# define SLONGEST intmax_t
#else
# define LONGEST ULONGLONG
# define SLONGEST LONGLONG
#endif

/*
 * The maximal number of digits are for base 2.
 */
#define MAX_CHARS_IN(x) (sizeof(x) * CHAR_BIT + 1)

enum {
  TYPE_PRINT   = 1,
  TYPE_SCAN    = 2,

  FLAGS_NEW         = 0,
  FLAGS_PARAMETER   = 1,
  FLAGS_SPACE       = 2 * FLAGS_PARAMETER,
  FLAGS_SHOWSIGN    = 2 * FLAGS_SPACE,
  FLAGS_LEFTADJUST  = 2 * FLAGS_SHOWSIGN,
  FLAGS_ALTERNATIVE = 2 * FLAGS_LEFTADJUST,
  FLAGS_SHORT       = 2 * FLAGS_ALTERNATIVE,
  FLAGS_LONG        = 2 * FLAGS_SHORT,
  FLAGS_QUAD        = 2 * FLAGS_LONG,
  FLAGS_LONGDOUBLE  = 2 * FLAGS_QUAD,
  FLAGS_NILPADDING  = 2 * FLAGS_LONGDOUBLE,
  FLAGS_UNSIGNED    = 2 * FLAGS_NILPADDING,
  FLAGS_OCTAL       = 2 * FLAGS_UNSIGNED,
  FLAGS_HEX         = 2 * FLAGS_OCTAL,
  FLAGS_UPPER       = 2 * FLAGS_HEX,
  FLAGS_WIDTH       = 2 * FLAGS_UPPER,
  FLAGS_PRECISION   = 2 * FLAGS_WIDTH,
  FLAGS_FLOAT_E     = 2 * FLAGS_PRECISION,
  FLAGS_FLOAT_G     = 2 * FLAGS_FLOAT_E,
  FLAGS_QUOTE       = 2 * FLAGS_FLOAT_G,
  FLAGS_WIDECHAR    = 2 * FLAGS_QUOTE,
  FLAGS_ALLOC       = 2 * FLAGS_WIDECHAR,
  FLAGS_IGNORE      = 2 * FLAGS_ALLOC,
  FLAGS_SHORTSHORT  = 2 * FLAGS_IGNORE,
  FLAGS_BINARY      = 2 * FLAGS_SHORTSHORT,
  FLAGS_SIZE_T      = 2 * FLAGS_BINARY,
  FLAGS_PTRDIFF_T   = 2 * FLAGS_SIZE_T,
  FLAGS_INTMAX_T    = 2 * FLAGS_PTRDIFF_T,

  NO_POSITION = -1,
  NO_PRECISION = 0,

  NO_BASE      = 0,
  MAX_BASE     = 36,
  BASE_BINARY  = 2,
  BASE_OCTAL   = 8,
  BASE_DECIMAL = 10,
  BASE_HEX     = 16,
};

#define MAX_PARAMETERS 64

#define FORMAT_UNKNOWN 0
#define FORMAT_INT     1
#define FORMAT_DOUBLE  2
#define FORMAT_CHAR    3
#define FORMAT_STRING  4
#define FORMAT_POINTER 5
#define FORMAT_COUNT   6
#define FORMAT_WIDTH   7
#define FORMAT_GROUP   8
#if defined(TRIO_GNU)
# define FORMAT_ERRNO   10
#endif

#define CHAR_IDENTIFIER '%'
#define CHAR_BACKSLASH '\\'

/* Specifiers */

/*
 * a  Hex-float
 * A  Hex-float
 * c  Character
 * C  Widechar character (wint_t)
 * d  Decimal
 * e  float
 * E  -
 * f  -
 * F  -
 * g  -
 * G  -
 * i  Integer
 * j  intmax_t
 * m  Error message
 * n  Count
 * o  Octal
 * p  Pointer
 * s  String
 * S  Widechar string (wchar_t)
 * t  prtdiff_t
 * u  Unsigned
 * x  Hex
 * X  Hex
 * z  size_t
 * [  Group
 */
#define SPECIFIER_CHAR 'c'
#define SPECIFIER_STRING 's'
#define SPECIFIER_DECIMAL 'd'
#define SPECIFIER_INTEGER 'i'
#define SPECIFIER_UNSIGNED 'u'
#define SPECIFIER_OCTAL 'o'
#define SPECIFIER_HEX 'x'
#define SPECIFIER_HEX_UPPER 'X'
#define SPECIFIER_FLOAT_E 'e'
#define SPECIFIER_FLOAT_E_UPPER 'E'
#define SPECIFIER_FLOAT_F 'f'
#define SPECIFIER_FLOAT_F_UPPER 'F'
#define SPECIFIER_FLOAT_G 'g'
#define SPECIFIER_FLOAT_G_UPPER 'G'
#define SPECIFIER_POINTER 'p'
#define SPECIFIER_GROUP '['
#define SPECIFIER_COUNT 'n'
#if defined(TRIO_UNIX98)
# define SPECIFIER_CHAR_UPPER 'C'
# define SPECIFIER_STRING_UPPER 'S'
#endif
#if defined(TRIO_C9X)
# define SPECIFIER_HEXFLOAT 'a'
# define SPECIFIER_HEXFLOAT_UPPER 'A'
#endif
#if defined(TRIO_GNU)
# define SPECIFIER_ERRNO 'm'
#endif
#if defined(TRIO_EXTENSION)
# define SPECIFIER_BINARY 'b'
# define SPECIFIER_BINARY_UPPER 'B'
#endif

/* Qualifiers */

/*
 * Numbers = d,i,o,u,x,X
 * Float = a,A,e,E,f,F,g,G
 * String = s
 * Char = c
 *
 *
 * 9$ Position
 *      Use the 9th parameter. 9 can be any number between 1 and
 *      the maximal argument
 *
 * 9 Width
 *      Set width to 9. 9 can be any number, but must not be postfixed
 *      by '$'
 *
 * h  Short
 *    Numbers:
 *      (unsigned) short int
 *
 * hh Short short
 *    Numbers:
 *      (unsigned) char
 *
 * l  Long
 *    Numbers:
 *      (unsigned) long int
 *    String:
 *      as the S specifier
 *    Char:
 *      as the C specifier
 *
 * ll Long Long
 *    Numbers:
 *      (unsigned) long long int
 *
 * L  Long Double
 *    Float
 *      long double
 *
 * #  Alternative
 *    Float:
 *      Decimal-point is always present
 *    String:
 *      non-printable characters are handled as \number
 *
 *    Spacing
 *
 * +  Sign
 *
 * -  Alignment
 *
 * .  Precision
 *
 * *  Parameter
 *    print: use parameter
 *    scan: no paramter (ignore)
 *
 * q  Quad
 *
 * Z  size_t
 *
 * w  Widechar
 *
 * '  Thousands/quote
 *    Numbers:
 *      Integer part grouped in thousands
 *    Binary numbers:
 *      Number grouped in nibbles (4 bits)
 *    String:
 *      Quoted string
 *
 * Extensions:
 * <alloc>    = GNU 'a' qualifier
 * <base=n>   = sets base to 'n' (int)
 * <fill=c>   = fill with 'c' (char)
 * <quote>    = quote string
 * <quote=c>  = quote string with 'c' (char)
 */
#define QUALIFIER_POSITION '$'
#define QUALIFIER_SHORT 'h'
#define QUALIFIER_LONG 'l'
#define QUALIFIER_LONG_UPPER 'L'
#define QUALIFIER_ALTERNATIVE '#'
#define QUALIFIER_SPACE ' '
#define QUALIFIER_PLUS '+'
#define QUALIFIER_MINUS '-'
#define QUALIFIER_DOT '.'
#define QUALIFIER_STAR '*'
#if defined(TRIO_C9X)
# define QUALIFIER_SIZE_T 'z'
# define QUALIFIER_PTRDIFF_T 't'
# define QUALIFIER_INTMAX_T 'j'
#endif
#if defined(TRIO_BSD) || defined(TRIO_GNU)
# define QUALIFIER_QUAD 'q'
#endif
#if defined(TRIO_GNU)
# define QUALIFIER_SIZE_T_UPPER 'Z'
#endif
#if defined(TRIO_SUN)
# define QUALIFIER_WIDECHAR 'w'
#endif
#if defined(TRIO_EXTENSION)
# define QUALIFIER_QUOTE '\''
# define QUALIFIER_EXTENSIONBEGIN '<'
# define QUALIFIER_EXTENSIONEND '>'
# define QUALIFIER_EXTENSIONSEPARATOR ','
# define QUALIFIER_EXTENSIONVALUE '='
#endif

typedef struct {
  int type;
  int flags;
  int width;
  size_t precision;
  int base;
  char quote;
  char adjust;
  int indexAfterSpecifier;
  union {
    char *string;
    void *pointer;
    union {
      SLONGEST asSigned;
      LONGEST asUnsigned;
    } num;
    LONGEST *numPointer;
    double doubleNumber;
    double *doublePointer;
    LONGDOUBLE longdoubleNumber;
    LONGDOUBLE *longdoublePointer;
    int errorNumber;
#if defined(QUALIFIER_SIZE_T) || defined(QUALIFIER_SIZE_T_UPPER)
    size_t sizet;
#endif
#if defined(QUALIFIER_PTRDIFF_T)
    ptrdiff_t ptrdifft
#endif
#if defined(QUALIFIER_INTMAX_T)
    intmax_t intmaxt;
#endif
  } data;
} parameter_T;

typedef struct _trio_T {
  size_t amount;
  size_t done;
  size_t max;
  void *location;
  int (*OutStream)(struct _trio_T *, int);
  int (*InStream)(struct _trio_T *, int *);
  int current;
} trio_T;


/*************************************************************************
 * Package scope variables
 */

#if defined(PLATFORM_UNIX)
extern int errno;
#endif

static char globalDecimalPoint[64] = ".";
static char globalThousandSeparator[64] = ",";

static const char digitsLower[] = "0123456789abcdefghijklmnopqrstuvwxyz";
static const char digitsUpper[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";

static const char null[] = "(nil)";

static const char extensionFill[] = "fill";
static const size_t extensionFillSize = sizeof(extensionFill) - 1;
static const char extensionAlloc[] = "alloc";
static const size_t extensionAllocSize = sizeof(extensionAlloc) - 1;
static const char extensionBase[] = "base";
static const size_t extensionBaseSize = sizeof(extensionBase) - 1;
static const char extensionQuote[] = "quote";
static const size_t extensionQuoteSize = sizeof(extensionQuote) - 1;


/*************************************************************************
 * TrioIsQualifier [private]
 *
 * Description:
 *  Remember to add all new qualifiers (except QUALIFIER_POSITION) to
 *  this function.
 */
static int
TrioIsQualifier(char ch)
{
  /* QUALIFIER_POSITION is not included */
  switch (ch)
    {
    case '0': case '1': case '2': case '3': case '4':
    case '5': case '6': case '7': case '8': case '9':
    case QUALIFIER_PLUS:
    case QUALIFIER_MINUS:
    case QUALIFIER_SPACE:
    case QUALIFIER_DOT:
    case QUALIFIER_STAR:
    case QUALIFIER_ALTERNATIVE:
    case QUALIFIER_QUOTE:
    case QUALIFIER_SHORT:
    case QUALIFIER_LONG:
    case QUALIFIER_LONG_UPPER:
#if defined(QUALIFIER_SIZE_T)
    case QUALIFIER_SIZE_T:
#endif
#if defined(QUALIFIER_PTRDIFF_T)
    case QUALIFIER_PTRDIFF_T:
#endif
#if defined(QUALIFIER_INTMAX_T)
    case QUALIFIER_INTMAX_T:
#endif
#if defined(QUALIFIER_QUAD)
    case QUALIFIER_QUAD:
#endif
#if defined(QUALIFIER_SIZE_T_UPPER)
    case QUALIFIER_SIZE_T_UPPER:
#endif
#if defined(QUALIFIER_WIDECHAR)
    case QUALIFIER_WIDECHAR:
#endif
#if defined(QUALIFIER_EXTENSIONBEGIN)
    case QUALIFIER_EXTENSIONBEGIN:
#endif
      return TRUE;
    default:
      return FALSE;
    }
}

/*************************************************************************
 * TrioGetPosition [private]
 *
 * Get the %n$ position.
 */
static int
TrioGetPosition(char *format, int *indexPointer)
{
  int number = 0;
  int index = *indexPointer;

  while (isdigit((int)format[index]))
    {
      number *= 10;
      number += (int)(format[index] - '0');
      index++;
    }
  if ((number != 0) && (QUALIFIER_POSITION == format[index++]))
    {
      *indexPointer = index;
      return number;
    }
  return NO_POSITION;
}

/*************************************************************************
 * 
 */
#if defined(STANDALONE)
static void
TrioDumpParameter(parameter_T elem)
{
  switch (elem.type)
    {
    case FORMAT_STRING:
      printf("type = string\n");
      break;
    case FORMAT_POINTER:
      printf("type = pointer\n");
      break;
    case FORMAT_CHAR:
      printf("type = char\n");
      break;
    case FORMAT_INT:
      printf("type = int\n");
      break;
    case FORMAT_COUNT:
      printf("type = count\n");
      break;
    case FORMAT_DOUBLE:
      printf("type = double\n");
      break;
    case FORMAT_WIDTH:
      printf("type = width\n");
      break;
#if defined(FORMAT_ERRNO)
    case FORMAT_ERRNO:
      printf("type = errno\n");
      break;
#endif
    default:
      printf("type = %d\n", elem.type);
      break;
    }
  printf("flags = 0x%08x =", elem.flags);
  if (elem.flags == FLAGS_NEW)
    printf(" none");
  else
    {
      if (elem.flags & FLAGS_PARAMETER)
	printf(" parameter");
      if (elem.flags & FLAGS_SPACE)
	printf(" space");
      if (elem.flags & FLAGS_SHOWSIGN)
	printf(" showsign");
      if (elem.flags & FLAGS_LEFTADJUST)
	printf(" leftadjust");
      if (elem.flags & FLAGS_ALTERNATIVE)
	printf(" alternative");
      if (elem.flags & FLAGS_SHORT)
	printf(" short");
      if (elem.flags & FLAGS_SHORTSHORT)
	printf(" shortshort");
      if (elem.flags & FLAGS_LONG)
	printf(" long");
      if (elem.flags & FLAGS_QUAD)
	printf(" quad");
      if (elem.flags & FLAGS_LONGDOUBLE)
	printf(" longdouble");
      if (elem.flags & FLAGS_NILPADDING)
	printf(" nilpadding");
      if (elem.flags & FLAGS_UNSIGNED)
	printf(" unsigned");
      if (elem.flags & FLAGS_OCTAL)
	printf(" octal");
      if (elem.flags & FLAGS_HEX)
	printf(" hex");
      if (elem.flags & FLAGS_UPPER)
	printf(" upper");
      if (elem.flags & FLAGS_WIDTH)
	printf(" width");
      if (elem.flags & FLAGS_PRECISION)
	printf(" precision");
      if (elem.flags & FLAGS_FLOAT_E)
	printf(" float_e");
      if (elem.flags & FLAGS_FLOAT_G)
	printf(" float_g");
      if (elem.flags & FLAGS_QUOTE)
	printf(" quote");
      if (elem.flags & FLAGS_WIDECHAR)
	printf(" widechar");
      if (elem.flags & FLAGS_BINARY)
	printf(" binary");
    }
  printf("\n");
  if (elem.flags & FLAGS_QUOTE)
    printf("quote = %c\n", elem.quote);
}
#endif

/*************************************************************************
 * TrioPreprocess [private]
 *
 * Description:
 *  Parse the format string
 */
static int
TrioPreprocess(int type,
	       char *format,
	       parameter_T *parameters,
	       va_list arglist)
{
  int flags;
  int parameterPosition = 0;
  int currentParam;
  int maxParam = -1;
  int insideExtension;
  int width;
  int precision;
  char adjust;
  char quote;
  char ch;
  int i = -1;
  int base;
  int index;
  int work;
  char *tmpformat;
  unsigned short usedEntries[MAX_PARAMETERS];

  /*
   * 'parameters' is not initialized, but we need to know
   * which entries we used.
   */
  memset(usedEntries, 0, sizeof(usedEntries));

  index = 0;
  while (format[index])
    {
      if (CHAR_IDENTIFIER == format[index++])
	{
	  if (CHAR_IDENTIFIER == format[index])
	    {
	      index++;
	      continue; /* while */
	    }

	  flags = FLAGS_NEW;
	  insideExtension = FALSE;
	  currentParam = TrioGetPosition(format, &index);
	  if (NO_POSITION == currentParam)
	    {
	      /* We have got no positional, get the next counter */
	      currentParam = parameterPosition;
	    }
	  else
	    {
	      /*
	       * 'currentParam' is decreased by 1, because N$ starts
	       * from 1, whereas the array it is indexing starts from 0.
	       */
	      currentParam--;
	    }
          if(currentParam >= MAX_PARAMETERS)
	    {
	      /* bail out completely to make the error more obvious */
	      return TRIO_ERROR_RETURN(TRIO_ETOOMANY, index);
	    }

	  if (currentParam > maxParam)
	    maxParam = currentParam;

	  parameterPosition++;

	  /* Default values */
	  width = 0;
	  precision = 0;
	  base = NO_BASE;
	  adjust = NIL;
	  quote = '\"';

	  while (TrioIsQualifier(format[index])
		 || insideExtension)
	    {
	      ch = format[index++];

#if defined(TRIO_EXTENSION)
	      if (insideExtension)
		{
		  if (QUALIFIER_EXTENSIONSEPARATOR == ch)
		    {
		      ch = QUALIFIER_EXTENSIONBEGIN;
		    }
		  else
		    {
		      insideExtension = FALSE;
		    }
		}
	      if (QUALIFIER_EXTENSIONBEGIN == ch)
		{
		  /* Parse extended format */
		  insideExtension = TRUE;
		  work = index;

		  switch (format[work])
		    {
		    case 'a':
		      if (StrEqualMax(extensionAlloc, extensionAllocSize,
				      &format[work]))
			{
			  flags |= FLAGS_ALLOC;
			  work += extensionAllocSize;
			}
		      break;

		    case 'b':
		      if (StrEqualMax(extensionBase, extensionBaseSize,
				      &format[work]))
			{
			  work += extensionBaseSize;
			  if (QUALIFIER_EXTENSIONVALUE == format[work])
			    {
			      work++;
			      base = StrToLong(&format[work], &tmpformat, 10);
			      work += (int)(tmpformat - &format[work]);
			      if (base > MAX_BASE)
				return TRIO_ERROR_RETURN(TRIO_EINVAL, index);
			    }
			}
		      break;

		    case 'f':
		      if (StrEqualMax(extensionFill, extensionFillSize,
				      &format[work]))
			{
			  work += extensionFillSize;
			  if (QUALIFIER_EXTENSIONVALUE == format[work])
			    {
			      work++;
			      adjust = format[work++];
			    }
			}

		    case 'q':
		      if (StrEqualMax(extensionQuote, extensionQuoteSize,
				      &format[work]))
			{
			  flags |= FLAGS_QUOTE;
			  work += extensionQuoteSize;
			  if (QUALIFIER_EXTENSIONVALUE == format[work])
			    {
			      work++;
			      quote = format[work++];
			    }
			}
		      break;

		    default:
		      break;
		    }
		  
		  if (QUALIFIER_EXTENSIONEND == work[format])
		    {
		      insideExtension = FALSE;
		      index = ++work;
		    }
		}
#endif /* defined(TRIO_EXTENSION) */

	      switch (ch)
		{
#if defined(TRIO_EXTENSION)
		case QUALIFIER_EXTENSIONBEGIN:
		case QUALIFIER_EXTENSIONSEPARATOR:
		  /* Everything is fine, but ignore */
		  break;
#endif
		case QUALIFIER_SPACE:
		  flags |= FLAGS_SPACE;
		  break;

		case QUALIFIER_PLUS:
		  flags |= FLAGS_SHOWSIGN;
		  break;

		case QUALIFIER_MINUS:
		  flags |= FLAGS_LEFTADJUST;
		  flags &= ~FLAGS_NILPADDING;
		  adjust = ' ';
		  break;

		case QUALIFIER_ALTERNATIVE:
		  flags |= FLAGS_ALTERNATIVE;
		  break;

		case QUALIFIER_QUOTE:
		  flags |= FLAGS_QUOTE;
		  break;

		case QUALIFIER_DOT:
		  flags |= FLAGS_PRECISION;
		  if (QUALIFIER_STAR == format[index])
		    {
		      flags |= FLAGS_PARAMETER;
		      index++;
		      precision = TrioGetPosition(format, &index);
		      if (NO_POSITION == precision)
			precision = currentParam;
		      else
			precision--;

		      currentParam = precision + 1;
		      if (currentParam > maxParam)
			maxParam = currentParam;
		      parameterPosition++;
		    }
		  else
		    {
		      precision = StrToLong(&format[index], &tmpformat, 10);
		      index = (int)(tmpformat - format);
		    }
		  break; /* QUALIFIER_DOT */

		case QUALIFIER_STAR:
		  /* This has different meanings for print and scan */
		  if (TYPE_PRINT == type)
		    {
		      flags |= (FLAGS_WIDTH | FLAGS_PARAMETER);

		      width = TrioGetPosition(format, &index);
		      if (NO_POSITION == width)
			width = currentParam;
		      else
			width--;

		      currentParam = width + 1;
		      if (currentParam > maxParam)
			maxParam = currentParam;
		      parameterPosition++;
		    }
		  else
		    {
		      flags |= FLAGS_IGNORE;
		    }

		  break; /* QUALIFIER_STAR */

		case '0':
		  if (! (flags & FLAGS_LEFTADJUST))
		    flags |= FLAGS_NILPADDING;
		  /* FALLTHROUGH */
		case '1': case '2': case '3': case '4':
		case '5': case '6': case '7': case '8': case '9':
		  flags |= FLAGS_WIDTH;
		  /* &format[index - 1] is used to "rewind" the read
		   * character from format
		   */
		  width = StrToLong(&format[index - 1], &tmpformat, 10);
		  index = (int)(tmpformat - format);
		  break;

		case QUALIFIER_SHORT:
		  if (flags & FLAGS_SHORT)
		    flags |= FLAGS_SHORTSHORT;
		  else
		    flags |= FLAGS_SHORT;
		  break;

		case QUALIFIER_LONG:
		  if (flags & FLAGS_LONG)
		    flags |= FLAGS_QUAD;
		  else
		    flags |= FLAGS_LONG;
		  break;

		case QUALIFIER_LONG_UPPER:
		  flags |= FLAGS_LONGDOUBLE;
		  break;

#if defined(QUALIFIER_SIZE_T)
		case QUALIFIER_SIZE_T:
		  break;
#endif

#if defined(QUALIFIER_PTRDIFF_T)
		case QUALIFIER_PTRDIFF_T:
		  break;
#endif

#if defined(QUALIFIER_INTMAX_T)
		case QUALIFIER_INTMAX_T:
		  break;
#endif

#if defined(QUALIFIER_QUAD)
		case QUALIFIER_QUAD:
		  flags |= FLAGS_QUAD;
		  break;
#endif

#if defined(QUALIFIER_WIDECHAR)
		case QUALIFIER_WIDECHAR:
		  flags |= FLAGS_WIDECHAR;
		  break;
#endif

#if defined(QUALIFIER_SIZE_T_UPPER)
		case QUALIFIER_SIZE_T_UPPER:
		  break;
#endif

		default:
		  /* bail out completely to make the error more obvious */
                  return TRIO_ERROR_RETURN(TRIO_EINVAL, index);
		}
	    } /* while qualifier */

	  switch (format[index++])
	    {
#if defined(SPECIFIER_CHAR_UPPER)
	    case SPECIFIER_CHAR_UPPER:
	      flags |= FLAGS_LONG;
	      /* FALLTHROUGH */
#endif
	    case SPECIFIER_CHAR:
	      parameters[currentParam].type = FORMAT_CHAR;
	      break;

#if defined(SPECIFIER_STRING_UPPER)
	    case SPECIFIER_STRING_UPPER:
	      flags |= FLAGS_LONG;
	      /* FALLTHROUGH */
#endif
	    case SPECIFIER_STRING:
	      parameters[currentParam].type = FORMAT_STRING;
	      break;

	    case SPECIFIER_GROUP:
	      if (TYPE_SCAN == type)
		{
		  parameters[currentParam].type = FORMAT_GROUP;
		  /* The rest must be read here */
		}
	      break;

	    case SPECIFIER_DECIMAL:
	    case SPECIFIER_INTEGER:
	      parameters[currentParam].type = FORMAT_INT;
	      break;

	    case SPECIFIER_UNSIGNED:
	      flags |= FLAGS_UNSIGNED;
	      parameters[currentParam].type = FORMAT_INT;
	      break;

	    case SPECIFIER_OCTAL:
	      flags |= FLAGS_OCTAL;
	      parameters[currentParam].type = FORMAT_INT;
	      break;

	    case SPECIFIER_BINARY_UPPER:
	      flags |= FLAGS_UPPER;
	      /* FALLTHROUGH */
	    case SPECIFIER_BINARY:
	      flags |= FLAGS_BINARY | FLAGS_NILPADDING;
	      parameters[currentParam].type = FORMAT_INT;
	      break;

	    case SPECIFIER_HEX_UPPER:
	      flags |= FLAGS_UPPER;
	      /* FALLTHROUGH */
	    case SPECIFIER_HEX:
	      flags |= FLAGS_HEX;
	      parameters[currentParam].type = FORMAT_INT;
	      break;

	    case SPECIFIER_FLOAT_E_UPPER:
	      flags |= FLAGS_UPPER;
	      /* FALLTHROUGH */
	    case SPECIFIER_FLOAT_E:
	      flags |= FLAGS_FLOAT_E;
	      parameters[currentParam].type = FORMAT_DOUBLE;
	      break;

	    case SPECIFIER_FLOAT_G_UPPER:
	      flags |= FLAGS_UPPER;
	      /* FALLTHROUGH */
	    case SPECIFIER_FLOAT_G:
	      flags |= FLAGS_FLOAT_G;
	      parameters[currentParam].type = FORMAT_DOUBLE;
	      break;

	    case SPECIFIER_FLOAT_F_UPPER:
	      flags |= FLAGS_UPPER;
	      /* FALLTHROUGH */
	    case SPECIFIER_FLOAT_F:
	      parameters[currentParam].type = FORMAT_DOUBLE;
	      break;

	    case SPECIFIER_POINTER:
	      parameters[currentParam].type = FORMAT_POINTER;
	      break;

	    case SPECIFIER_COUNT:
	      parameters[currentParam].type = FORMAT_COUNT;
	      break;

#if defined(SPECIFIER_HEXFLOAT)
# if defined(SPECIFIER_HEXFLOAT_UPPER)
	    case SPECIFIER_HEXFLOAT_UPPER:
	      flags |= FLAGS_UPPER;
	      /* FALLTHROUGH */
# endif
	    case SPECIFIER_HEXFLOAT:
	      flags |= FLAGS_HEX;
	      parameters[currentParam].type = FORMAT_DOUBLE;
	      break;
#endif

#if defined(FORMAT_ERRNO)
	    case SPECIFIER_ERRNO:
	      parameters[currentParam].type = FORMAT_ERRNO;
	      break;
#endif

	    default:
	      /* bail out completely to make the error more obvious */
              return TRIO_ERROR_RETURN(TRIO_EINVAL, index);
	    }

	  /*  Count the number of times this entry has been used */
	  usedEntries[currentParam] += 1;
	  
	  parameters[currentParam].indexAfterSpecifier = index;
	  parameters[currentParam].flags = flags;
	  parameters[currentParam].width = width;
	  parameters[currentParam].base = base;
	  parameters[currentParam].quote = quote;
	  parameters[currentParam].adjust = adjust;

	  if (flags & FLAGS_PARAMETER)
	    {
	      if (flags & FLAGS_WIDTH)
		i = width;
	      else if (flags & FLAGS_PRECISION)
		i = precision;
	      else
		i = MAX_PARAMETERS; /* Indicate error */

	      if (MAX_PARAMETERS != i)
		{
		  usedEntries[i] += 1;
	  
		  parameters[currentParam].width = i;
		  parameters[i].type = FORMAT_WIDTH;
		  parameters[i].indexAfterSpecifier = index;
		  parameters[i].flags = flags;
		  parameters[i].precision = parameters[i].width = 0;
		  parameters[i].adjust = NIL;
		  parameters[i].quote = NIL;
		}
	    }
	  else
	    {
	      parameters[currentParam].width = width;
	      parameters[currentParam].precision = precision;
	    }

	} /* if identifier */
    } /* while format characters left */

  for (i = 0; i <= maxParam; i++)
    {
      if (usedEntries[i] != 1)
	{
	  if (usedEntries[i] == 0) /* gap detected */
	    return TRIO_ERROR_RETURN(TRIO_EGAP, 0);
	  else /* double references detected */
	    return TRIO_ERROR_RETURN(TRIO_EDBLREF, 0);
	}
#if defined(STANDALONE)
      TrioDumpParameter(parameters[i]);
#endif
      if (parameters[i].flags & FLAGS_IGNORE)
	continue; /* for all arguments */

      /* The stack arguments are read according to ANSI C89
       * default argument promotions:
       *
       *  char           = int
       *  short          = int
       *  unsigned char  = unsigned int
       *  unsigned short = unsigned int
       *  float          = double
       * FIXME: remember to use unsigned
       *
       * In addition to the ANSI C89 these types are read (the
       * default argument promotions of C9X has not been
       * considered yet)
       *
       *  long long
       *  long double
       *  size_t
       *  ptrdiff_t
       *  intmax_t
       */
      switch (parameters[i].type)
	{
	case FORMAT_GROUP:
	case FORMAT_STRING:
	  parameters[i].data.string = va_arg(arglist, char *);
	  break;

	case FORMAT_POINTER:
	case FORMAT_COUNT:
	case FORMAT_UNKNOWN:
	  parameters[i].data.pointer = va_arg(arglist, void *);
	  break;

	case FORMAT_CHAR:
	case FORMAT_INT:
	  if (TYPE_SCAN == type)
	    {
#if defined(QUALIFIER_SIZE_T) || defined(QUALIFIER_SIZE_T_UPPER)
	      if (parameters[i].flags & FLAGS_SIZE_T)
		parameters[i].data.sizet = va_arg(arglist, size_t);
	      else
#endif
		parameters[i].data.numPointer = (LONGEST *)va_arg(arglist, void *);
#if 0
	      if (parameters[i].flags & FLAGS_QUAD)
		parameters[i].data.numPointer = (LONGEST *)va_arg(arglist, ULONGLONG *);
	      else if (parameters[i].flags & FLAGS_LONG)
		parameters[i].data.numPointer = (LONGEST *)va_arg(arglist, long *);
	      else
		parameters[i].data.numPointer = (LONGEST *)va_arg(arglist, int *);
#endif
	    }
	  else
	    {
#if defined(QUALIFIER_SIZE_T) || defined(QUALIFIER_SIZE_T_UPPER)
	      if (parameters[i].flags & FLAGS_SIZE_T)
		parameters[i].data.sizet = va_arg(arglist, size_t);
	      else
#endif
	      if (parameters[i].flags & FLAGS_QUAD)
		parameters[i].data.num.asUnsigned = (LONGEST)va_arg(arglist, ULONGLONG);
	      else if (parameters[i].flags & FLAGS_LONG)
		parameters[i].data.num.asUnsigned = (LONGEST)va_arg(arglist, long);
	      else
		parameters[i].data.num.asUnsigned = (LONGEST)va_arg(arglist, int);
	    }
	  break;

	case FORMAT_WIDTH:
	  /* 	  parameters[i].type = FORMAT_INT; */
	  parameters[i].data.num.asUnsigned = (LONGEST)va_arg(arglist, int);
	  break;

	case FORMAT_DOUBLE:
	  if (TYPE_SCAN == type)
	    {
	      if (parameters[i].flags & FLAGS_LONG)
		parameters[i].data.longdoublePointer = va_arg(arglist, LONGDOUBLE *);
	      else
		parameters[i].data.doublePointer = va_arg(arglist, double *);
	    }
	  else
	    {
	      if (parameters[i].flags & FLAGS_LONG)
		parameters[i].data.longdoubleNumber = va_arg(arglist, LONGDOUBLE);
	      else
		parameters[i].data.longdoubleNumber = (LONGDOUBLE)va_arg(arglist, double);
	    }
	  break;


#if defined(FORMAT_ERRNO)
	case FORMAT_ERRNO:
	  parameters[i].data.errorNumber = errno;
	  break;
#endif

	default:
	  break;
	}
    } /* for all specifiers */
  return i;
}

/*************************************************************************
 * TrioOutStreamNumber [private]
 *
 * Description:
 *  Output a number.
 *  The complexity of this function is a result of the complexity
 *  of the dependencies of the flags.
 *
 * FIXME:
 *  buffer: what if StrLength(globalThousandSeparator) is zero?
 */
static void
TrioOutStreamNumber(trio_T *self,
		    LONGEST number,
		    int flags,
		    int width,
		    int precision,
		    int base)
{
  int isNegative;
  char buffer[MAX_CHARS_IN(LONGEST) * StrLength(globalThousandSeparator)];
  char *bufferend = &buffer[sizeof(buffer) - 1];
  char *pointer;
  const char *digits;
  int i;
  int length;
  char *p;
  int charsPerThousand;

  assert(VALID(self));
  assert(VALID(self->OutStream));
  assert(base > NO_BASE && base <= MAX_BASE);

  digits = (flags & FLAGS_UPPER) ? digitsUpper : digitsLower;

  /* Binary and hexadecimal numbers are grouped in fours */
  charsPerThousand = ((base == BASE_BINARY) || (base == BASE_HEX))
    ? 4 : 3;
  
  if (flags & FLAGS_UNSIGNED)
    isNegative = FALSE;
  else if (isNegative = (((SLONGEST)number) < 0))
    number = -number;

  if (flags & FLAGS_QUAD)
    number &= (ULONGLONG)-1;
  else if (flags & FLAGS_LONG)
    number &= (unsigned long)-1;
  else
    number &= (unsigned int)-1;
  
  /* Build number */
  pointer = bufferend;
  *pointer-- = NIL;
  i = 0;
  do
    {
      i++;
      *pointer-- = digits[number % base];
      number /= base;
      if ((number) && (flags & FLAGS_QUOTE) && (i % charsPerThousand == 0))
	{
	  /*
	   * We are building the number from the least significant
	   * to the most significant digit, so we have to copy the
	   * thousand separator backwards
	   */
	  length = StrLength(globalThousandSeparator);
	  if (((int)(pointer - buffer) - length) > 0)
	    {
	      p = &globalThousandSeparator[length - 1];
	      while (length-- > 0)
		*pointer-- = *p--;
	    }
	}
    }
  while (number);

  /* Adjust width */
  width -= (bufferend - pointer) - 1;

  /* Adjust precision */
  if (NO_PRECISION != precision)
    {
      precision -= (bufferend - pointer) - 1;
      if ((flags & FLAGS_ALTERNATIVE) || (precision > width))
	flags |= FLAGS_NILPADDING;
    }

  /* Adjust width further */
  if (isNegative || (flags & FLAGS_SHOWSIGN) || (flags & FLAGS_SPACE))
    width--;
  if (flags & FLAGS_ALTERNATIVE)
    {
      switch (base)
	{
	case BASE_BINARY:
	case BASE_HEX:
	  width -= 2;
	  break;
	case BASE_OCTAL:
	  width--;
	  break;
	default:
	  break;
	}
    }

  /* Output prefixes spaces if needed */
  if (! ((flags & FLAGS_LEFTADJUST) ||
	 ((flags & FLAGS_NILPADDING) && (precision == NO_PRECISION))))
    {
      while (width-- > precision)
	self->OutStream(self, ' ');
    }

  /* width has been adjusted for signs and alternatives */
  if (isNegative)
    self->OutStream(self, '-');
  else if (flags & FLAGS_SHOWSIGN)
    self->OutStream(self, '+');
  else if (flags & FLAGS_SPACE)
    self->OutStream(self, ' ');

  if (flags & FLAGS_ALTERNATIVE)
    {
      switch (base)
	{
	case BASE_BINARY:
	  self->OutStream(self, '0');
	  self->OutStream(self, (flags & FLAGS_UPPER) ? 'B' : 'b');
	  break;

	case BASE_OCTAL:
	  self->OutStream(self, '0');
	  break;

	case BASE_HEX:
	  self->OutStream(self, '0');
	  self->OutStream(self, (flags & FLAGS_UPPER) ? 'X' : 'x');
	  break;

	default:
	  break;
	} /* switch base */
    }

  /* Output prefixed zero padding if needed */
  if (flags & FLAGS_NILPADDING)
    {
      if (precision == NO_PRECISION)
	precision = width;
      while (precision-- > 0)
	{
	  self->OutStream(self, '0');
	  width--;
	}
    }

  /* Output the number itself */
  while (*(++pointer))
    {
      self->OutStream(self, *pointer);
    }

  /* Output trailing spaces if needed */
  if (flags & FLAGS_LEFTADJUST)
    {
      while (width-- > 0)
	self->OutStream(self, ' ');
    }
}

/*************************************************************************
 * TrioOutStreamDouble [private]
 *
 * FIXME:
 *  INF & NAN
 */
static void
TrioOutStreamDouble(trio_T *self,
		    LONGDOUBLE longdoubleNumber,
		    int flags,
		    int width,
		    int precision)
{
  int i;
  int charsPerThousand;
  int length;
  double number = (double)longdoubleNumber;
  double integerPart;
  double fractionPart;
  double precisionPower;
  int exponent;
  unsigned int uExponent;
  int base;
  double dblBase;
  int isNegative;
  int isExponentNegative;
  int doFraction;
  const char *digits;
  char *p;
  char integerBuffer[MAX_CHARS_IN(double)];
  char *integerBufferEnd = &integerBuffer[sizeof(integerBuffer) - 1];
  char *integerPointer;
  char fractionBuffer[MAX_CHARS_IN(double)];
  char *fractionBufferEnd = &fractionBuffer[sizeof(fractionBuffer) - 1];
  char *fractionPointer;
  char exponentBuffer[MAX_CHARS_IN(double)];
  char *exponentBufferEnd = &exponentBuffer[sizeof(exponentBuffer) - 1];
  char *exponentPointer;
  
  assert(VALID(self));
  assert(VALID(self->OutStream));

  digits = (flags & FLAGS_UPPER) ? digitsUpper : digitsLower;
  base = (flags & FLAGS_HEX) ? BASE_HEX : BASE_DECIMAL;
  dblBase = (double)base;
  charsPerThousand = (base == BASE_HEX) ? 4 : 3;
  
  if (precision == NO_PRECISION)
    precision = FLT_DIG;
  precisionPower = pow(10.0, (double)precision);

  if (flags & (FLAGS_FLOAT_G | FLAGS_HEX))
    {
      if ((number < 1.0e-4) ||
	  (number > precisionPower))
	flags |= FLAGS_FLOAT_E;
    }

  if (flags & FLAGS_FLOAT_E)
    {
      /* Scale the number */
      exponent = (int)floor(log10(number));
      number /= pow(10.0, (double)exponent);
      isExponentNegative = (exponent < 0);
      uExponent = (isExponentNegative) ? -exponent : exponent;
      /* No thousand separators */
      flags &= ~FLAGS_QUOTE;
    }

  /* Calculate the integer and fraction parts */
  fractionPart = modf(number, &integerPart);
  fractionPart *= precisionPower;
  if (fractionPart - floor(fractionPart) >= 0.5)
    fractionPart += 1.0;
  fractionPart = floor(fractionPart);
  
  isNegative = (integerPart < 0.0);
  if (isNegative)
    integerPart = -integerPart;
  
  /*
   * LONGLONG cannot hold an arbitrarily sized double integer
   * part, so we cannot reuse TrioOutStreamNumber here. Instead
   * we build the integer part manually.
   */
  integerPointer = integerBufferEnd;
  *integerPointer-- = NIL;
  i = 0;
  do {
    i++;
    *integerPointer-- = digits[(int)fmod(integerPart, dblBase)];
    integerPart /= dblBase;
    (void)modf(integerPart, &integerPart);
    if ( !(flags & FLAGS_FLOAT_E) &&
	 (integerPart > DBL_EPSILON) &&
	 (flags & FLAGS_QUOTE) &&
	 (i % charsPerThousand == 0))
      {
	/* See comment for TrioOutStreamNumber */
	length = StrLength(globalThousandSeparator);
	if (((int)(integerPointer - integerBuffer) - length) > 0)
	  {
	    p = &globalThousandSeparator[length - 1];
	    while (length-- > 0)
	      *integerPointer-- = *p--;
	  }
      }
  } while (integerPart > DBL_EPSILON);

  width -= (integerBufferEnd - integerPointer) - 1;
  
  fractionPointer = fractionBufferEnd;
  *fractionPointer-- = NIL;
  while (fractionPart > DBL_EPSILON)
    {
      *fractionPointer-- = digits[(int)fmod(fractionPart, dblBase)];
      fractionPart /= dblBase;
      (void)modf(fractionPart, &fractionPart);
    }

  width -= (fractionBufferEnd - fractionPointer) - 1;
  doFraction = ((fractionBufferEnd - fractionPointer) > 1);
  if (doFraction || (flags & FLAGS_ALTERNATIVE))
    width -= StrLength(globalDecimalPoint);

  if (flags & FLAGS_FLOAT_E)
    {
      exponentPointer = exponentBufferEnd;
      *exponentPointer-- = NIL;
      do {
	*exponentPointer-- = digits[uExponent % base];
	uExponent /= base;
      } while (uExponent);

      /* +2 are the exponent char (e, p) and sign */
      width -= (exponentBufferEnd - exponentPointer) - 1 + 2;
    }
  
  if (isNegative)
    self->OutStream(self, '-');
  else if (flags & FLAGS_SHOWSIGN)
    self->OutStream(self, '+');
  if (flags & FLAGS_HEX)
    {
      self->OutStream(self, '0');
      self->OutStream(self, (flags & FLAGS_UPPER) ? 'X' : 'x');
      width -= 2;
    }
  
  /* Output prefixed spaces or zero padding */
  if ((flags & FLAGS_NILPADDING) || !(flags & FLAGS_LEFTADJUST))
    {
      while (width-- > 0)
	self->OutStream(self, (flags & FLAGS_NILPADDING) ? '0' : ' ');
    }
  
  /* Output the integer part */
  while (*(++integerPointer))
    self->OutStream(self, *integerPointer);

  /* Output the fraction part */
  if (doFraction || (flags & FLAGS_ALTERNATIVE))
    {
      char *decimalpoint = globalDecimalPoint;
      while (*decimalpoint)
	self->OutStream(self, *decimalpoint++);
      
      if (doFraction)
	{
	  /* zero padding? */
	  while (*(++fractionPointer))
	    self->OutStream(self, *fractionPointer);
	}
    }
  
  /* Output the exponent */
  if (flags & FLAGS_FLOAT_E)
    {
      self->OutStream(self,
		      (flags & FLAGS_HEX)
		      ? ((flags & FLAGS_UPPER) ? 'P' : 'p')
		      : ((flags & FLAGS_UPPER) ? 'E' : 'e'));
      self->OutStream(self, (isExponentNegative) ? '-' : '+');
      while (*(++exponentPointer))
	self->OutStream(self, *exponentPointer);
    }
  
  /* Output trailing spaces if needed */
  if (flags & FLAGS_LEFTADJUST)
    {
      while (width-- > 0)
	self->OutStream(self, ' ');
    }
}

/*************************************************************************
 * TrioOutStreamString [private]
 *
 * Description:
 *  Output a string
 */
static void
TrioOutStreamString(trio_T *self,
		    char *string,
		    int flags,
		    int width,
		    int precision,
		    int quote,
		    int adjust)
{
  int length;
  int ch;

  assert(VALID(self));
  assert(VALID(self->OutStream));

  if (string == NULL)
    {
      string = (char *)null;
      length = sizeof(null) - 1;
      /* Disable quoting for the null pointer */
      flags &= (~FLAGS_QUOTE);
      width = 0;
    }
  else
    {
      length = StrLength(string);
    }
  if ((NO_PRECISION != precision) &&
      (precision < length))
    {
      length = precision;
    }
  width -= length;

  if (flags & FLAGS_QUOTE)
    self->OutStream(self, quote);

  if (! (flags & FLAGS_LEFTADJUST))
    {
      while (width-- > 0)
	self->OutStream(self, adjust);
    }

  while (length-- > 0)
    {
      ch = *string++;
      if (flags & FLAGS_ALTERNATIVE)
	{
	  if (! (isprint(ch) || isspace(ch)))
	    {
	      /* Non-printable characters are converted to \number */
	      self->OutStream(self, CHAR_BACKSLASH);
	      TrioOutStreamNumber(self, (ULONGLONG)ch,
				      FLAGS_UNSIGNED | FLAGS_NILPADDING,
				      3, 3, BASE_OCTAL);
	    }
	  else if (ch == CHAR_BACKSLASH)
	    {
	      self->OutStream(self, CHAR_BACKSLASH);
	      self->OutStream(self, CHAR_BACKSLASH);
	    }
	  else
	    {
	      self->OutStream(self, ch);
	    }
	}
      else
	{
	  self->OutStream(self, ch);
	}
    }

  if (flags & FLAGS_LEFTADJUST)
    {
      while (width-- > 0)
	self->OutStream(self, adjust);
    }
  if (flags & FLAGS_QUOTE)
    self->OutStream(self, quote);
}

/*************************************************************************
 * TrioFilePutChar [private]
 */
static int
TrioFilePutChar(trio_T *self, int output)
{
  FILE *fd = (FILE *)self->location;

  assert(VALID(self));
  assert(VALID(fd));

  self->amount++;
  self->done++;
  return fputc(output, fd);
}

/*************************************************************************
 * TrioStoreBuffer [private]
 */
static int
TrioStoreBuffer(trio_T *self, int output)
{
  char **buffer = (char **)self->location;

  assert(VALID(self));
  assert(VALID(buffer));

  **buffer = (char)output;
  (*buffer)++;
  self->amount++;
  self->done++;
  return output; /* act like fputc() ! */
}

/*************************************************************************
 * TrioStoreBufferMax [private]
 */
static int
TrioStoreBufferMax(trio_T *self, int output)
{
  char **buffer;

  assert(VALID(self));
  buffer = (char **)self->location;
  assert(VALID(buffer));

  if (self->amount < self->max)
    {
      **buffer = (char)output;
      (*buffer)++;
      self->done++;
    }
  self->amount++;
  return output;
}

/*************************************************************************
 * TrioFormat [private]
 *
 * Description:
 *  This is the main engine for formatting output
 */
static int
TrioFormat(void *destination,
	   size_t destinationSize,
	   int (*OutStream)(trio_T *, int),
	   const char *format,
	   va_list args)
{
#if defined(USE_LOCALE)
  static struct lconv *localeValues = NULL;
#endif
  int rc;
  parameter_T parameters[MAX_PARAMETERS];
  trio_T internalData;
  trio_T *data;
  int i;
  char *string;
  void *pointer;
  int flags;
  int width; /* must be signed */
  int precision;
  int base;
  char adjust;
  char quote;
  int index;
  ULONGLONG number;

  assert(VALID(OutStream));
  assert(VALID(format));
  assert(VALID(args));

  memset(&internalData, 0, sizeof(internalData));
  data = &internalData;
  data->OutStream = OutStream;
  data->location = destination;
  data->max = destinationSize;

#if defined(USE_LOCALE)
  if (NULL == localeValues)
    {
      localeValues = (struct lconv *)localeconv();
      if (StrLength(localeValues->decimal_point) > 0)
	{
	  StrCopyMax(globalDecimalPoint,
		     sizeof(globalDecimalPoint),
		     localeValues->decimal_point);
	}
      if (StrLength(localeValues->thousands_sep) > 0)
	{
	  StrCopyMax(globalThousandSeparator,
		     sizeof(globalThousandSeparator),
		     localeValues->thousands_sep);
	}
    }
#endif

  rc = TrioPreprocess(TYPE_PRINT, (char *)format, parameters, args);
  if (rc < 0)
    return rc;

  index = 0;
  i = 0;
  while (format[index])
    {
      if (CHAR_IDENTIFIER == format[index])
	{
	  if (CHAR_IDENTIFIER == format[index + 1])
	    {
	      OutStream(data, CHAR_IDENTIFIER);
	      index += 2;
	    }
	  else
	    {
	      flags = parameters[i].flags;
	      adjust = parameters[i].adjust;
	      if (NIL == adjust)
		adjust = ' ';
	      quote = parameters[i].quote;
	      base = parameters[i].base;

	      /* Find width */
	      width = parameters[i].width;
	      if ((flags & (FLAGS_WIDTH | FLAGS_PARAMETER)) == (FLAGS_WIDTH | FLAGS_PARAMETER))
		{
		  /* Get with as parameter instead */
		  width = (int)parameters[width].data.num.asSigned;
		}
	      /* Find precision */
	      if (flags & FLAGS_PRECISION)
		{
		  precision = parameters[i].precision;
		  if (flags & FLAGS_PARAMETER)
		    {
		      precision = (int)parameters[precision].data.num.asSigned;
		    }
		}
	      else
		{
		  precision = NO_PRECISION;
		}
	      if (flags & FLAGS_PARAMETER)
		i++;

	      switch (parameters[i].type)
		{
		case FORMAT_CHAR:
		  if (flags & FLAGS_QUOTE)
		    OutStream(data, quote);
		  if (! (flags & FLAGS_LEFTADJUST))
		    {
		      while (--width > 0)
			OutStream(data, adjust);
		    }

		  OutStream(data, (char)parameters[i].data.num.asSigned);

		  if (flags & FLAGS_LEFTADJUST)
		    {
		      while(--width > 0)
			OutStream(data, adjust);
		    }
		  if (flags & FLAGS_QUOTE)
		    OutStream(data, quote);

		  break; /* FORMAT_CHAR */

		case FORMAT_INT:
		  if (0 == base)
		    {
		      base = (flags & FLAGS_OCTAL) ? BASE_OCTAL
			: ( (flags & FLAGS_HEX) ? BASE_HEX
			    : ( (flags & FLAGS_BINARY) ? BASE_BINARY
				: BASE_DECIMAL) );
		    }
#if defined(QUALIFIER_SIZE_T) || defined(QUALIFIER_SIZE_T_UPPER)
		  if (flags & FLAGS_SIZE_T)
		    number = parameters[i].data.sizet;
		  else
#endif
		    number = parameters[i].data.num.asUnsigned;
#if 0
		  if (flags & FLAGS_QUAD)
		    number = (ULONGLONG)parameters[i].data.quadNumber;
		  else if (flags & FLAGS_LONG)
		    number = (ULONGLONG)parameters[i].data.longNumber;
		  else
		    number = (ULONGLONG)parameters[i].data.number;
#endif		  
		  TrioOutStreamNumber(data,
				      number,
				      flags,
				      width,
				      precision,
				      base);

		  break; /* FORMAT_INT */

		case FORMAT_DOUBLE:
		  TrioOutStreamDouble(data,
				      parameters[i].data.longdoubleNumber,
				      flags,
				      width,
				      precision);
		  break; /* FORMAT_DOUBLE */

		case FORMAT_STRING:
		  TrioOutStreamString(data,
				      parameters[i].data.string,
				      flags,
				      width,
				      precision,
				      quote,
				      adjust);
		  break; /* FORMAT_STRING */

		case FORMAT_POINTER:
		  pointer = parameters[i].data.pointer;
		  if (NULL == pointer)
		    {
		      string = (char *)null;
		      while (*string)
			OutStream(data, *string++);
		    }
		  else
		    {
		      /* The following line may give a compiler warning,
		       * but it is ok.
		       * For C9X we could use intptr_t instead.
		       */
		      number = (ULONGLONG)(parameters[i].data.pointer);
		      /* Shrink to size of pointer */
		      number &= (ULONGLONG)-1;
		      flags |= (FLAGS_UNSIGNED | FLAGS_HEX |
				FLAGS_ALTERNATIVE | FLAGS_NILPADDING);
		      /* The number of bits in a hex ciffer is 4 */
		      width = (sizeof("0x") - 1)
			+ sizeof(void *) * CHAR_BIT / 4;
		      TrioOutStreamNumber(data,
					  number,
					  flags,
					  width,
					  precision,
					  BASE_HEX);
		    }
		  break; /* FORMAT_POINTER */

		case FORMAT_COUNT:
		  pointer = parameters[i].data.pointer;
		  if (NULL != pointer)
		    {
#if defined(QUALIFIER_SIZE_T) || defined(QUALIFIER_SIZE_T_UPPER)
		      if (flags & FLAGS_SIZE_T)
			*(size_t *)pointer = (size_t)data->done;
		      else
#endif
		      if (flags & FLAGS_QUAD)
			{
			  *(ULONGLONG int *)pointer = (ULONGLONG)data->done;
			}
		      else if (flags & FLAGS_LONG)
			{
			  *(long int *)pointer = (long int)data->done;
			}
		      else if (flags & FLAGS_SHORT)
			{
			  *(short int *)pointer = (short int)data->done;
			}
		      else
			{
			  *(int *)pointer = (int)data->done;
			}
		    }
		  break; /* FORMAT_COUNT */

		case FORMAT_WIDTH:
		  break; /* FORMAT_WIDTH */

#if defined(FORMAT_ERRNO)
		case FORMAT_ERRNO:
		  string = (char *)StrError(parameters[i].data.errorNumber);
		  if (string)
		    {
		      TrioOutStreamString(data,
					  string,
					  flags,
					  width,
					  precision,
					  quote,
					  adjust);
		    }
		  else
		    {
		      OutStream(data, '#');
		      TrioOutStreamNumber(data,
					  parameters[i].data.errorNumber,
					  flags,
					  width,
					  precision,
					  BASE_DECIMAL);
		    }
		  break; /* FORMAT_ERRNO */
#endif

		default:
		  break;
		} /* switch parameter type */

	      /* Prepare for next */
	      index = parameters[i].indexAfterSpecifier;
#if 0
	      /* And apply sanity check */
	      if (NULL == format)
		break; /* while */
#endif
	      i++;
	    }
	}
      else /* not identifier */
	{
	  OutStream(data, format[index++]);
	}
    }

  return data->done;
}

/*************************************************************************
 * trio_printf
 */
int
trio_printf(const char *format, ...)
{
  int rc;
  va_list args;

  assert(VALID(format));
  
  va_start(args, format);
  rc = TrioFormat(stdout, 0, TrioFilePutChar, format, args);
  va_end(args);
  return rc;
}

/*************************************************************************
 * trio_vprintf
 */
int
trio_vprintf(const char *format, va_list args)
{
  assert(VALID(format));
  assert(VALID(args));

  return TrioFormat(stdout, 0, TrioFilePutChar, format, args);
}

/*************************************************************************
 * trio_fprintf
 */
int
trio_fprintf(FILE *fd, const char *format, ...)
{
  int rc;
  va_list args;

  assert(VALID(fd));
  assert(VALID(format));
  
  va_start(args, format);
  rc = TrioFormat(fd, 0, TrioFilePutChar, format, args);
  va_end(args);
  return rc;
}

/*************************************************************************
 * trio_fprintf
 */
int
trio_vfprintf(FILE *fd, const char *format, va_list args)
{
  assert(VALID(fd));
  assert(VALID(format));
  assert(VALID(args));
  
  return TrioFormat(fd, 0, TrioFilePutChar, format, args);
}

/*************************************************************************
 * trio_sprintf
 */
int
trio_sprintf(char *buffer, const char *format, ...)
{
  int rc;
  va_list args;

  assert(VALID(buffer));
  assert(VALID(format));
  
  va_start(args, format);
  rc = TrioFormat(&buffer, 0, TrioStoreBuffer, format, args);
  *buffer = NIL; /* Terminate with NIL character */
  va_end(args);
  return rc;
}

/*************************************************************************
 * trio_vsprintf
 */
int
trio_vsprintf(char *buffer, const char *format, va_list args)
{
  int rc;

  assert(VALID(buffer));
  assert(VALID(format));
  assert(VALID(args));

  rc = TrioFormat(&buffer, 0, TrioStoreBuffer, format, args);
  *buffer = NIL;
  return rc;
}

/*************************************************************************
 * trio_snprintf
 */
int
trio_snprintf(char *buffer, size_t bufferSize, const char *format, ...)
{
  int rc;
  va_list args;

  assert(VALID(buffer));
  assert(bufferSize > 0);
  assert(VALID(format));

  va_start(args, format);
  rc = TrioFormat(&buffer, bufferSize - 1, TrioStoreBufferMax,
		      format, args);
  *buffer = NIL;
  va_end(args);
  return rc;
}

/*************************************************************************
 * trio_vsnprintf
 */
int
trio_vsnprintf(char *buffer, size_t bufferSize, const char *format,
	       va_list args)
{
  int rc;

  assert(VALID(buffer));
  assert(VALID(format));
  assert(VALID(args));

  rc = TrioFormat(&buffer, bufferSize - 1, TrioStoreBufferMax,
			 format, args);
  *buffer = NIL;
  return rc;
}

/*************************************************************************
 * trio_snprintfcat
 * Appends the new string to the dest string overwriting the '\0' character
 * at the end of dest.
 */
int trio_snprintfcat(char *buffer, size_t bufferSize, const char *format, ...)
{
  int rc;
  va_list args;
  size_t buf_len;

  va_start(args, format);

  assert(VALID(buffer));
  assert(VALID(format));

  buf_len = strlen(buffer);

  buffer = &buffer[buf_len];

  rc = TrioFormat(&buffer, bufferSize - 1 - buf_len,
		  TrioStoreBufferMax, format, args);
  va_end(args);
  *buffer = NIL;
  return rc;
}

/*************************************************************************
 * trio_vsnprintfcat
 * Appends the new string to the dest string overwriting the '\0' character
 * at the end of dest.
 */
int trio_vsnprintfcat(char *buffer, size_t bufferSize, const char *format,
                      va_list args)
{
  int rc;
  size_t buf_len;
  assert(VALID(buffer));
  assert(VALID(format));

  buf_len = strlen(buffer);
  buffer = &buffer[buf_len];
  rc = TrioFormat(&buffer, bufferSize - 1 - buf_len,
		  TrioStoreBufferMax, format, args);
  *buffer = NIL;
  return rc;
}

/*************************************************************************
 * trio_aprintf
 */

struct dynamicBuffer {
  char *buffer; /* allocated buffer */
  size_t len;   /* length of string */
  size_t alloc; /* length of alloc */
};

/* fputc() look-alike */
static int
TrioStoreDynamicBuffer(trio_T *self, int output)
{
  struct dynamicBuffer *infop;

  assert(VALID(self));
  infop = (struct dynamicBuffer *)self->location;
  assert(VALID(infop));

  if (infop->buffer == NULL) {
    infop->buffer=(char *)malloc(32);
    if (infop->buffer == NULL)
      return -1; /* fail */
    infop->alloc = 32;
    infop->len =0;
  }
  else if (infop->len + 1 >= infop->alloc) {
    char *newptr;

    newptr = (char *)realloc(infop->buffer, infop->alloc * 2);

    if (newptr == NULL) {
      return -1;
    }
    infop->buffer = newptr;
    infop->alloc *= 2;
  }

  infop->buffer[ infop->len ] = output;
  infop->len++;

  return output; /* fputc() returns like this on success */
}

char *trio_aprintf(const char *format, ...)
{
  va_list args; /* argument pointer */
  int rc;
  struct dynamicBuffer info;

  assert(VALID(format));
  
  info.buffer = NULL;
  info.len = 0;
  info.alloc = 0;

  va_start(args, format);
  rc = TrioFormat(&info, 0, TrioStoreDynamicBuffer, format, args);
  va_end(args);
  if(info.len) {
    info.buffer[info.len] = 0; /* we terminate this with a zero byte */
    return info.buffer;
  }
  else
    return NULL;
}

char *trio_vaprintf(const char *format, va_list args)
{
  int rc;
  struct dynamicBuffer info;

  assert(VALID(format));
  assert(VALID(args));
  
  info.buffer = NULL;
  info.len = 0;
  info.alloc = 0;

  rc = TrioFormat(&info, 0, TrioStoreDynamicBuffer, format, args);
  if(info.len) {
    info.buffer[info.len] = 0; /* we terminate this with a zero byte */
    return info.buffer;
  }
  else
    return NULL;
}

/*************************************************************************
 * TrioSkipWhitespaces [private]
 */
static int
TrioSkipWhitespaces(trio_T *self)
{
  int ch;
  
  self->InStream(self, &ch);
  while (isspace(ch))
    {
      self->InStream(self, &ch);
    }
  return ch;
}

/*************************************************************************
 * TrioGetNumber [private]
 */
static int
TrioGetNumber(trio_T *self, int *target)
{
  int number = 0;

  assert(VALID(self));
  assert(VALID(self->InStream));
  
  while (isdigit(self->current))
    {
      number *= 10;
      number += (self->current - '0');
      self->InStream(self, NULL);
    }
  if (target)
    *target = number;
  return TRUE;
}

/*************************************************************************
 * TrioGetString [private]
 */
static int
TrioGetString(trio_T *self, char *target, int max)
{
  assert(VALID(self));
  assert(VALID(self->InStream));
  assert(VALID(target));
  
  while (! ((self->current == NIL) || isspace(self->current)))
    {
      if (target)
	*target++ = self->current;
      self->InStream(self, NULL);
    }
  if (target)
    *target = NIL;
  return TRUE;
}

/*************************************************************************
 * TrioGetDouble [private]
 *
 * FIXME: add hex-float format
 */
static int
TrioGetDouble(trio_T *self, double *doublePointer, int flags)
{
  int ch;
  char doubleString[512] = "";
  int index = 0;

  /* Read entire double number from stream.
   * This is needed because StrToDouble requires
   * a string, but InStream can be anything else.
   */
  ch = self->current;
  if ((ch == '+') || (ch == '-'))
    {
      doubleString[index++] = ch;
      self->InStream(self, &ch);
    }
  while (isdigit(ch))
    {
      /* Integer part */
      doubleString[index++] = ch;
      self->InStream(self, &ch);
    }
  if (ch == '.')
    {
      /* Decimal part */
      doubleString[index++] = ch;
      self->InStream(self, &ch);
      while (isdigit(ch))
	{
	  doubleString[index++] = ch;
	  self->InStream(self, &ch);
	}
      if ((ch == 'e') || (ch == 'E'))
	{
	  /* Exponent */
	  doubleString[index++] = ch;
	  self->InStream(self, &ch);
	  if ((ch == '+') || (ch == '-'))
	    {
	      doubleString[index++] = ch;
	      self->InStream(self, &ch);
	    }
	  while (isdigit(ch))
	    {
	      doubleString[index++] = ch;
	      self->InStream(self, &ch);
	    }
	}
    }

  if (*doubleString == NIL)
    return FALSE;
  
  if (flags & FLAGS_LONGDOUBLE)
/*     *longdoublePointer = StrToLongDouble()*/;
  else
    {
      *doublePointer = StrToDouble(doubleString, NULL);
    }
  return TRUE;
}

/*************************************************************************
 * TrioInStreamString [private]
 */
static int
TrioInStreamString(trio_T *self, int *intPointer)
{
  char **buffer;

  assert(VALID(self));
  assert(VALID(self->InStream));
  assert(VALID(self->location));

  buffer = (char **)self->location;
  self->current = **buffer;
  (*buffer)++;
  self->amount++;
  
  if (VALID(intPointer))
    {
      *intPointer = self->current;
    }
  /* Terminating zero is converted to End-Of-File */
  return ('\0' == self->current) ? EOF : self->current;
}

/*************************************************************************
 * TrioScan [private]
 */
static int
TrioScan(void *source,
	 size_t sourceSize,
	 int (*InStream)(trio_T *, int *),
	 const char *format,
	 va_list args)
{
#if defined(USE_LOCALE)
  static struct lconv *localeValues = NULL;
#endif
  int rc;
  parameter_T parameters[MAX_PARAMETERS];
  trio_T internalData;
  trio_T *data;
  int ch;
  int i = 0;
  int index;
  int flags;

  assert(VALID(InStream));
  assert(VALID(format));
  assert(VALID(args));

  memset(&internalData, 0, sizeof(internalData));
  data = &internalData;
  data->InStream = InStream;
  data->location = source;
  data->max = sourceSize;

#if defined(USE_LOCALE)
  if (NULL == localeValues)
    {
      localeValues = (struct lconv *)localeconv();
      if (StrLength(localeValues->decimal_point) > 0)
	{
	  StrCopyMax(globalDecimalPoint,
		     sizeof(globalDecimalPoint),
		     localeValues->decimal_point);
	}
      if (StrLength(localeValues->thousands_sep) > 0)
	{
	  StrCopyMax(globalThousandSeparator,
		     sizeof(globalThousandSeparator),
		     localeValues->thousands_sep);
	}
    }
#endif
  
  rc = TrioPreprocess(TYPE_SCAN, (char *)format, parameters, args);
  if (rc < 0)
    return rc;

  index = 0;
  data->InStream(data, &ch);
  
  while (format[index])
    {
      if (EOF == ch)
	return data->amount;
      
      if (CHAR_IDENTIFIER == format[index])
	{
	  if (CHAR_IDENTIFIER == format[index + 1])
	    {
	      /* Two %'s in format matches one % in input stream */
	      if (CHAR_IDENTIFIER == ch)
		{
		  index += 2;
		  continue; /* while format chars left */
		}
	      else
		return TRIO_ERROR_RETURN(TRIO_EINVAL, index);
	    }

	  flags = parameters[i].flags;
	  switch (parameters[i].type)
	    {
	    case FORMAT_INT:
	      TrioGetNumber(data,
			    (flags & FLAGS_IGNORE) ? NULL
			    : (void *)(parameters[i].data.numPointer));
	      break;
	    case FORMAT_STRING:
	      TrioGetString(data,
			    (flags & FLAGS_IGNORE) ? NULL
			    : parameters[i].data.string,
			    0);
	      break;
	    case FORMAT_DOUBLE:
		TrioGetDouble(data,
			      (flags & FLAGS_IGNORE) ? NULL
			      : parameters[i].data.doublePointer,
			      parameters[i].flags);
	      break;
	    default:
	      return TRIO_ERROR_RETURN(TRIO_EINVAL, index);
	    }
	  data->done++;
	  index = parameters[i].indexAfterSpecifier;
	  i++;
	}
      else /* Not an % identifier */
	{
	  if (isspace((int)format[index]))
	    {
	      /* Whitespaces may match any amount of whitespaces */
	      TrioSkipWhitespaces(data);
	      ch = data->current;
	    }
	  else if (ch == format[index])
	    {
	      data->InStream(data, &ch);
	    }
	  else
	    return TRIO_ERROR_RETURN(TRIO_EINVAL, index);
	  
	  index++;
	}
    }
  return data->done;
}

/*************************************************************************
 * trio_sscanf
 */
int
trio_sscanf(char *buffer, const char *format, ...)
{
  int rc;
  va_list args;

  assert(VALID(buffer));
  assert(VALID(format));
  
  va_start(args, format);
  rc = TrioScan(&buffer, 0, TrioInStreamString, format, args);
  va_end(args);
  return rc;
}

/*************************************************************************
 * trio_strerror [public]
 */
const char *trio_strerror(int errorcode)
{
  switch (TRIO_ERROR_CODE(errorcode))
    {
    case TRIO_EOF:
      return "End of file";
    case TRIO_EINVAL:
      return "Invalid argument";
    case TRIO_ETOOMANY:
      return "Too many arguments";
    case TRIO_EDBLREF:
      return "Double reference";
    case TRIO_EGAP:
      return "Reference gap";
    default:
      return "Unknown";
    }
}


syntax highlighted by Code2HTML, v. 0.9.1