/*************************************************************************
*
* $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