/*

	ax_printf - public domain
	Last update: 2015-02-27 Aaron Miller


	TODO
	
	- Convert to proper single-source header form (like STB)
	- Add proper %g and %e support.
	- Test %f more, and add support for #INF, #NAN, etc.


	LICENSE

	This software is in the public domain. Where that dedication is not
	recognized, you are granted a perpetual, irrevocable license to copy
	and modify this file as you see fit.

*/

#include <stdio.h>
#include <stdlib.h>

/* -------------------------------------------------------------------------- */
/* axpf() (printf) implementation */

#include <errno.h>
#include <assert.h>
#include <stdarg.h>
#include <stddef.h>
#include <string.h>

#ifdef _MSC_VER
typedef signed __int8 int8;
typedef signed __int16 int16;
typedef signed __int32 int32;
typedef signed __int64 int64;
typedef unsigned __int8 uint8;
typedef unsigned __int16 uint16;
typedef unsigned __int32 uint32;
typedef unsigned __int64 uint64;
#else
# include <stdint.h>
typedef int8_t int8;
typedef int16_t int16;
typedef int32_t int32;
typedef int64_t int64;
typedef uint8_t uint8;
typedef uint16_t uint16;
typedef uint32_t uint32;
typedef uint64_t uint64;
#endif

/*

	See: http://w...content-available-to-author-only...s.com/reference/cstdio/printf/
	See: http://w...content-available-to-author-only...x.com/man-page/FreeBSD/9/printf/
	See: http://w...content-available-to-author-only...x.com/man-page/freebsd/3/syslog/

*/

typedef ptrdiff_t( *axpf_write_fn_t )( void *, const char *, const char * );

struct axpf__write_mem_data_
{
	char *p;
	size_t i;
	size_t n;
};

static ptrdiff_t axpf__write_mem_f( void *data, const char *s, const char *e )
{
	struct axpf__write_mem_data_ *md;
	size_t n;

	md = ( struct axpf__write_mem_data_ * )data;

	n = ( size_t )( e - s );
	if( !n ) {
		return 0;
	}

	if( md->i + n > md->n ) {
		n = md->n - md->i;
	}

	memcpy( ( void * )&md->p[ md->i ], ( const void * )s, n );
	return n;
}
static ptrdiff_t axpf__write_fp_f( void *data, const char *s, const char *e )
{
	if( !fwrite( ( const void * )s, ( size_t )( e - s ), 1, ( FILE * )data ) ) {
		return ( ptrdiff_t )-1;
	}

	return ( ptrdiff_t )( e - s );
}

enum
{
	/* "-" Left-justify within the given field width */
	kPF_Left = 1<<0,
	/* "+" Result will be preceeded by either a - or + */
	kPF_Sign = 1<<1,
	/* " " Result will be preceeded by either a - or a space */
	kPF_Space = 1<<2,
	/* "#" Insert an appropriate radix (0, 0x, 0X) */
	kPF_Radix = 1<<3,
	/* "0" Left-pad the number with zeroes instead of spaces */
	kPF_Zero = 1<<4,
	/* "'" Use a digit separator */
	kPF_Group = 1<<5,

	/* [Internal] Printing an array */
	kPF_Array = 1<<11,
	/* [Internal] Uppercase */
	kPF_Upper = 1<<12,
	/* [Internal] Width directive was specified */
	kPF_Width = 1<<13,
	/* [Internal] Precision directive was specified */
	kPF_Precision = 1<<14,
	/* [Internal] Use a lower-case radix (for pointer printing) */
	kPF_Pointer = 1<<15
};
typedef enum
{
	kLS_None,
	kLS_hh,
	kLS_h,
	kLS_l,
	kLS_ll,
	kLS_j,
	kLS_z,
	kLS_t,
	kLS_L,
	
	kLS_I,
	kLS_I32,
	kLS_I64
} axpf__lengthSpecifier_t;

#define AXPF__MAX_ARRAY_PRINT 4
struct axpf__state_
{
	axpf_write_fn_t pfn_write;
	void *write_data;
	size_t num_written;
	int diderror;

	unsigned repeats;
	size_t arraysize;
	char arrayprint[ AXPF__MAX_ARRAY_PRINT ];
	unsigned flags;
	int width;
	int precision;
	axpf__lengthSpecifier_t lenspec;
	unsigned radix;
	va_list args;
	const char *s;
	const char *e;
	const char *p;
};

static char axpf__read( struct axpf__state_ *s )
{
	return s->p < s->e ? *s->p++ : '\0';
}
static char axpf__look( struct axpf__state_ *s )
{
	return s->p < s->e ? *s->p : '\0';
}
static void axpf__skip( struct axpf__state_ *s )
{
	if( s->p < s->e ) {
		s->p++;
	}
}

static int axpf__check( struct axpf__state_ *s, int ch )
{
	if( s->p < s->e && *s->p == ch ) {
		++s->p;
		return 1;
	}

	return 0;
}
static int axpf__checks( struct axpf__state_ *s, const char *p )
{
	if( s->p < s->e && *s->p == *p ) {
		const char *q;

		q = s->p + 1;
		while( *q++ == *++p ) {
			if( !*p ) {
				break;
			}
		}
		if( !*p ) {
			s->p = q;
			return 1;
		}
	}

	return 0;
}

static int axpf__write( struct axpf__state_ *s, const char *p, const char *e )
{
	ptrdiff_t r;

	r = s->pfn_write( s->write_data, p, e );
	if( r == -1 ) {
		s->diderror = 1;
		return 0;
	}

	if( s->num_written + ( size_t )r >= s->num_written ) {
		s->num_written += ( size_t )r;
	} else {
		s->num_written = ( ~( size_t )0 ) - 1;
	}

	return 1;
}
static int axpf__writech( struct axpf__state_ *s, char ch )
{
	char txt[ 2 ];

	txt[ 0 ] = ch;
	txt[ 1 ] = '\0';

	return axpf__write( s, &txt[ 0 ], &txt[ 1 ] );
}

static char *axpf__utoa( char *end, uintmax_t i, unsigned radix, int flags )
{
	static const char *lower = "0123456789abcdefghijklmnopqrstuvwxyz";
	static const char *upper = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";

	const char *digits;
	unsigned groupingdist, groupingbase;
	char groupingchar;
	char *p;

	if( radix < 2 ) { radix = 2; }
	if( radix > 36 ) { radix = 36; }

	digits = ( flags & 1 ) ? upper : lower;

	/* TODO: Grab information from locale */
	groupingbase = ( flags & 4 ) ? 3 : 9999;
	groupingdist = groupingbase + 1;
	groupingchar = ',';

	p = end - 1;

	*p = '\0';

	while( i > 0 ) {
		if( !--groupingdist ) {
			groupingdist = groupingbase;
			*--p = groupingchar;
		}
		*--p = digits[ i%radix ];
		i /= radix;
	}
	if( *p == '\0' && ( ~flags & 2 ) ) {
		*--p = '0';
	}

	return p;
}
static int axpf__dtoax( char *buf, char *outsign, double f, int usescinot,
unsigned maxdigits, unsigned radix )
{
	/*
		ORIGINAL: http://w...content-available-to-author-only...d.com/thread5585.html (by cb30)
	*/
	union {
		int64 i;
		double f;
	} x;
	int64 mantissa, whole, fract;
	int16 exp2;

	( void )usescinot;

	if( !f ) {
		*buf++ = '0';
		*buf++ = '.';
		*buf++ = '0';
		*buf   = '\0';

		return 0;
	}

	x.f = f;
	if( x.i < 0 ) {
		x.i &= ~( 1ULL<<63 );
		*outsign = '-';
	} else {
		*outsign = '+';
	}

	exp2 = ( ( int16 )( ( x.i>>52 ) & 0x7FF ) ) - 1023;
	mantissa = ( x.i & 0x1FFFFFFFFFFFFF ) | 0x10000000000000;
	whole = 0;
	fract = 0;

	if( exp2 >= 63 ) {
		*buf = '\0';
		return 1;
	}

	if( exp2 < -52 ) {
		*buf = '\0';
		return -1;
	}

	if( exp2 >= 52 ) {
		whole = mantissa << ( exp2 - 52 );
	} else if( exp2 >= 0 ) {
		whole = mantissa >> ( 52 - exp2 );
		fract = ( mantissa << ( exp2 + 1 ) ) & 0x1FFFFFFFFFFFFF;
	} else {
		fract = ( mantissa & 0x1FFFFFFFFFFFFF ) >> -( exp2 + 1 );
	}

	if( !whole ) {
		*buf++ = '0';
	} else {
		char tmp[ 32 ];
		char *p;

		p = axpf__utoa( &tmp[ sizeof( tmp ) - 1 ], whole, radix, 0 );
		while( *p != '\0' ) {
			*buf++ = *p++;
		}
	}

	*buf++ = '.';

	if( !fract ) {
		*buf++ = '0';
	} else {
		unsigned numdigits = 0;

		if( maxdigits > 32 ) {
			maxdigits = 32;
		}

		for( numdigits = 0; numdigits < maxdigits; ++numdigits ) {
			fract *= 10;
			*buf++ = ( fract >> 53 ) + '0';
			fract &= 0x1FFFFFFFFFFFFF;
		}
	}

	*buf = '\0';
	return 0;
}

static int axpf__pad( struct axpf__state_ *s, unsigned num, char ch )
{
	static const char spaces[] = "                ";
	static const char zeroes[] = "0000000000000000";
	const char *p;

	p = ( ch == ' ' ? &spaces[0] : ( ch == '0' ? &zeroes[0] : NULL ) );

	if( !p ) {
		while( num-- > 0 ) {
			if( !axpf__writech( s, ch ) ) {
				return 0;
			}
		}

		return 1;
	}

	while( num > 0 ) {
		unsigned n = num;

		if( n > sizeof( spaces ) - 1 ) {
			n = sizeof( spaces ) - 1;
		}

		if( !axpf__write( s, p, p + n ) ) {
			return 0;
		}

		num -= n;
	}

	return 1;
}
static int axpf__write_uintx( struct axpf__state_ *s, uintmax_t i, char sign )
{
	const char *prefix;
	unsigned prefixlen;
	unsigned numberlen, numbersignlen;
	unsigned numpadzeroes;
	unsigned numpadspaces;
	unsigned padinfringe;
	char buf[ 128 ];
	char *p;
	int f;

	f = 0;
	if( s->flags & kPF_Upper ) { f |= 1; }
	if( ( s->flags & kPF_Precision ) && s->precision == 0 ) { f |= 2; }
	if( s->flags & kPF_Group ) { f |= 4; }

	p = axpf__utoa( &buf[ sizeof( buf ) ], i, s->radix, f );

	prefix = NULL;
	prefixlen = 0;
	if( s->flags & ( kPF_Radix | kPF_Pointer ) ) {
		int useupper;

		if( ( s->flags & kPF_Pointer ) || ( ~s->flags & kPF_Upper ) ) {
			useupper = 0;
		} else {
			useupper = 1;
		}

		switch( s->radix ) {
		case 2:
			prefixlen = 2;
			prefix = useupper ? "0B" : "0b";
			break;
		case 8:
			prefixlen = 1;
			prefix = "0";
			break;
		case 16:
			prefixlen = 2;
			prefix = useupper ? "0X" : "0x";
			break;
		default:
			break;
		}
	}
	numberlen = ( unsigned )( size_t )( &buf[ sizeof( buf ) - 1 ] - p );

	if( !sign ) {
		if( s->flags & kPF_Sign ) {
			sign = '+';
		} else if( s->flags & kPF_Space ) {
			sign = ' ';
		}
	}
	numbersignlen = numberlen + +( sign != '\0' );

	numpadzeroes = 0;
	if( ( s->flags & kPF_Precision ) &&
	( unsigned )s->precision > numbersignlen ) {
		numpadzeroes = s->precision - numbersignlen;
	}

	numpadspaces = 0;
	padinfringe = numbersignlen + prefixlen + numpadzeroes;
	if( ( s->flags & kPF_Width ) && ( unsigned )s->width > padinfringe ) {
		if( s->flags & kPF_Zero ) {
			numpadzeroes += s->width - padinfringe;
		} else {
			numpadspaces += s->width - padinfringe;
		}
	}

#if 0 /* debugging */
	printf( "\n[numpadspaces=%u;numpadzeroes=%u;;%s(%i)%s(%i)::%u;%s]\n",
		numpadspaces, numpadzeroes,
		( s->flags & kPF_Width ) ? "w" : "_", s->width,
		( s->flags & kPF_Precision ) ? "p" : "_", s->precision,
		prefixlen, prefix != NULL ? prefix : "(null)" );
#endif

	if( ~s->flags & kPF_Left ) {
		if( !axpf__pad( s, numpadspaces, ' ' ) ) {
			return 0;
		}
	}

	if( sign ) {
		if( !axpf__writech( s, sign ) ) {
			return 0;
		}
	}

	if( prefix != NULL && prefixlen > 0 ) {
		if( !axpf__write( s, prefix, prefix + prefixlen ) ) {
			return 0;
		}
	}
	axpf__pad( s, numpadzeroes, '0' );

	if( !axpf__write( s, p, &buf[ sizeof( buf ) - 1 ] ) ) {
		return 0;
	}

	if( s->flags & kPF_Left ) {
		if( !axpf__pad( s, numpadspaces, ' ' ) ) {
			return 0;
		}
	}

	return 1;
}
static int axpf__write_int( struct axpf__state_ *s, intmax_t i )
{
	while( s->repeats-- > 0 ) {
		if( i < 0 ) {
			if( !axpf__write_uintx( s, ( uintmax_t )-i, '-' ) ) {
				return 0;
			}
		} else if( !axpf__write_uintx( s, ( uintmax_t )i, '\0' ) ) {
			return 0;
		}
	}

	return 1;
}
static int axpf__write_uint( struct axpf__state_ *s, uintmax_t i )
{
	while( s->repeats-- > 0 ) {
		if( !axpf__write_uintx( s, i, '\0' ) ) {
			return 0;
		}
	}

	return 1;
}

static int axpf__write_floatd( struct axpf__state_ *s, double f, char spec )
{
	unsigned maxdigits;
	char buf[ 128 ], *p, *end, *dec;
	char sign;
	int r;

	maxdigits = ( s->flags & kPF_Precision ) ? s->precision : 6;
	sign = '\0';
	p = &buf[ sizeof( buf )/2 ];
	r = axpf__dtoax( p, &sign, ( double )f, 0, maxdigits, s->radix );
	if( r < 0 ) {
		buf[ 0 ] = '#'; buf[ 1 ] = 'S'; buf[ 2 ] = '\0';
		p = &buf[ 0 ];
	} else if( r > 0 ) {
		buf[ 0 ] = '#'; buf[ 1 ] = 'L'; buf[ 2 ] = '\0';
		p = &buf[ 0 ];
	}

	if( sign != '-' && ( ~s->flags & kPF_Sign ) ) {
		sign = '\0';
	}

	end = strchr( p, '\0' );

	if( spec == 'g' ) {
		while( end > &buf[ 0 ] && *( end - 1 ) == '0' ) {
			*--end = '\0';
		}

		if( end > &buf[ 0 ] && *( end - 1 ) == '.' ) {
			*--end = '\0';
		}
	}

	dec = strchr( p, '.' );
	if( !dec ) {
		dec = end;
	}

	if( ( s->flags & kPF_Precision ) && s->precision > 0 && r == 0 ) {
		unsigned numdigits;

		if( *dec != '.' ) {
			dec[ 0 ] = '.';
			dec[ 1 ] = '\0';
			end = &dec[ 1 ];
		}

		numdigits = ( unsigned )( size_t )( end - dec ) - 1;
		while( ( int )numdigits < s->precision && numdigits < 32 ) {
			*end++ = '0';
		}

		*end = '\0';
	}

	if( ( s->flags & kPF_Width ) && s->width > 0 && r == 0 ) {
		unsigned numwidth;
		char ch;

		ch = ( s->flags & kPF_Zero ) ? '0' : ' ';

		numwidth = ( unsigned )( size_t )( dec - p );
		if( ch != '0' && sign != '\0' ) {
			*--p = sign;
			sign = '\0';
			++numwidth;
		}

		while( ( int )numwidth < s->width && numwidth < 32 ) {
			*--p = ch;
			++numwidth;
		}
	}

	if( sign != '\0' ) {
		*--p = sign;
	}

	while( s->repeats-- > 0 ) {
		if( !axpf__write( s, p, end ) ) {
			return 0;
		}
	}

	return 1;
}

static int axpf__write_char( struct axpf__state_ *s, int ch )
{
	while( s->repeats-- > 0 ) {
		if( !axpf__writech( s, ch ) ) {
			return 0;
		}
	}

	return 1;
}
static int axpf__writewch( struct axpf__state_ *s, wchar_t ch )
{
	( void )s;
	( void )ch;

	return 0;
}
static int axpf__write_wchar( struct axpf__state_ *s, wchar_t ch )
{
	while( s->repeats-- > 0 ) {
		if( !axpf__writewch( s, ch ) ) {
			return 0;
		}
	}

	return 1;
}

static int axpf__write_str( struct axpf__state_ *s, const char *p )
{
	const char *e;

	if( !p ) {
		p = "(null)";
		e = p + 6;
	} else if( s->flags & kPF_Precision ) {
		e = &p[ s->precision ];
	} else {
		e = strchr( p, '\0' );
	}

	while( s->repeats-- > 0 ) {
		if( !axpf__write( s, p, e ) ) {
			return 0;
		}
	}

	return 1;
}
static int axpf__write_wstr( struct axpf__state_ *s, const wchar_t *p )
{
	const wchar_t *e;

	if( !p ) {
		p = L"(null)";
		e = p + 6;
	} else if( s->flags & kPF_Precision ) {
		e = &p[ s->precision ];
	} else {
		e = wcschr( p, L'\0' );
	}

	while( s->repeats-- > 0 ) {
		while( p < e ) {
			if( !axpf__writewch( s, *p++ ) ) {
				return 0;
			}
		}
	}

	return 1;
}

static int axpf__write_syserr( struct axpf__state_ *s, int err )
{
	char errbuf[ 128 ];

#if defined( _MSC_VER ) && defined( __STDC_WANT_SECURE_LIB__ )
	if( strerror_s( errbuf, sizeof( errbuf ), err ) != 0 ) {
		errbuf[ 0 ] = '('; errbuf[ 1 ] = 'n'; errbuf[ 2 ] = 'u';
		errbuf[ 3 ] = 'l'; errbuf[ 4 ] = 'l'; errbuf[ 5 ] = ')';
		errbuf[ 6 ] = '\0';
	}
#else
	strncpy( errbuf, strerror( err ), sizeof( errbuf ) - 1 );
#endif
	errbuf[ sizeof( errbuf ) - 1 ] = '\0';

	while( s->repeats-- > 0 ) {
		if( !axpf__write( s, errbuf, strchr( errbuf, '\0' ) ) ) {
			return 0;
		}
	}

	return 1;
}
static int axpf__write_bitfield( struct axpf__state_ *s, int bits,
const char *names )
{
	int needcomma;

	if( !names || !*names ) {
		return 0;
	}

	if( !s->repeats ) {
		return 1;
	}

	s->radix = *( const unsigned char * )names;
	if( s->radix == 1 ) {
		s->radix = 10;
	}

	++names;

	if( !axpf__write_uint( s, ( unsigned )bits ) ) {
		return 0;
	}

	if( !axpf__writech( s, ' ' ) || !axpf__writech( s, '<' ) ) {
		return 0;
	}

	needcomma = 0;
	while( *names && bits ) {
		unsigned bit;

		bit = ( ( unsigned )*names++ ) - 1;
		if( bits & ( 1<<bit ) ) {
			const char *p;

			bits &= ~( 1<<bit );

			if( needcomma ) {
				axpf__writech( s, ',' );
			}

			p = names;
			while( *names > ' ' ) {
				++names;
			}

			if( !axpf__write( s, p, names ) ) {
				return 0;
			}

			needcomma = 1;
		}
	}

	if( !axpf__writech( s, '>' ) ) {
		return 0;
	}

	return 1;
}
static int axpf__write_hex_dump( struct axpf__state_ *s, const unsigned char *p,
const char *delimiter )
{
	const char *delimend;
	unsigned count = 16;
	unsigned n;
	unsigned i;

	if( !s->repeats ) {
		return 1;
	}

	if( !delimiter || !*delimiter ) {
		delimiter = " ";
	}

	delimend = strchr( delimiter, '\0' );
	n = ( unsigned )( size_t )( delimend - delimiter );

	s->radix = 16;

	if( ( s->flags & kPF_Width ) && s->width > 0 ) {
		count = s->width;
	}

	s->flags = kPF_Precision;
	s->precision = 2;
	for( i = 0; i < count; ++i ) {
		if( i > 0 && !axpf__writech( s, delimiter[ ( i - 1 )%n ] ) ) {
			return 0;
		}

		if( !axpf__write_uintx( s, p[ i ], '\0' ) ) {
			return 0;
		}
	}

	return 1;
}

static int axpf__find( struct axpf__state_ *s, int ch, int doflush )
{
	const char *p;
	size_t n;

	n = ( size_t )( s->e - s->p );
	if( !n ) {
		return 0;
	}

	p = ( const char * )memchr( ( const void * )s->p, ch, n );
	if( !p ) {
		return 0;
	}

	if( doflush && s->p != p ) {
		if( !axpf__write( s, s->p, p ) ) {
			return 0;
		}
	}

	s->p = p;
	return 1;
}

static int axpf__getdigit( int ch, unsigned radix )
{
	unsigned rlo, rhi;
	unsigned c;

	rlo = radix < 10 ? radix : 10;
	rhi = radix > 10 ? radix - 10 : 0;

	c = ( unsigned )ch;
	if( c >= '0' && c < '0' + rlo ) {
		return ch - '0';
	}

	if( c >= 'a' && c < 'a' + rhi ) {
		return ch - 'a' + 10;
	}
	if( c >= 'A' && c < 'A' + rhi ) {
		return ch - 'A' + 10;
	}

	return -1;
}

static unsigned axpf__step_uint( struct axpf__state_ *s )
{
	unsigned r = 0;

	for(;;) {
		int n;

		n = axpf__getdigit( axpf__look( s ), 10 );
		if( n < 0 ) {
			break;
		}

		if( r < ( 1L<<( sizeof( int )*8 - 2 ) ) ) {
			r *= 10;
			r += n;
		}
		axpf__skip( s );
	}

	return r;
}
static int axpf__step_int( struct axpf__state_ *s )
{
	int m = 1;

	if( axpf__check( s, '-' ) ) {
		m = -1;
	}

	return m*( int )axpf__step_uint( s );
}
static int axpf__step( struct axpf__state_ *s )
{
	char spec;

	if( !axpf__find( s, '%', 1 ) ) {
		axpf__write( s, s->p, s->e );
		return 0;
	}

	axpf__skip( s );

	if( axpf__check( s, '%' ) ) {
		axpf__writech( s, '%' );
		return 1;
	}

	s->repeats = 1;
	s->arraysize = 0;
	s->arrayprint[ 0 ] = '{';
	s->arrayprint[ 1 ] = ',';
	s->arrayprint[ 2 ] = ' ';
	s->arrayprint[ 3 ] = '}';
	s->flags = 0;
	s->width = 0;
	s->precision = 0;
	s->lenspec = kLS_None;
	s->radix = 10;

	/* repeats */
	if( axpf__check( s, '{' ) ) {
		if( axpf__check( s, '*' ) ) {
			s->repeats = va_arg( s->args, unsigned );
		} else {
			s->repeats = axpf__step_uint( s );
		}

		( void )axpf__check( s, '}' );
	}

	/* array */
	if( axpf__check( s, '[' ) ) {
		unsigned n = 0;

		if( axpf__check( s, '*' ) ) {
			s->arraysize = va_arg( s->args, size_t );
		} else {
			s->arraysize = axpf__step_uint( s );
		}
		s->flags |= kPF_Array;
		
		while( n < AXPF__MAX_ARRAY_PRINT ) {
			if( axpf__check( s, ']' ) ) {
				break;
			}

			s->arrayprint[ n++ ] = axpf__read( s );
		}

		if( n == AXPF__MAX_ARRAY_PRINT ) {
			( void )axpf__check( s, ']' );
		} else if( n > 0 ) {
			/* the last character specified is the closing value */
			s->arrayprint[ AXPF__MAX_ARRAY_PRINT - 1 ] = s->arrayprint[ n - 1 ];

			/* if a space was omitted then don't space elements out */
			if( n < 4 ) {
				s->arrayprint[ 2 ] = '\0';
			}
			
			/* if a delimiter was omitted then assume a comma */
			if( n < 3 ) {
				s->arrayprint[ 1 ] = ',';
			}
		}
	}

	/* flags */
	unsigned fcount;
	do {
		fcount = 0;

		if( axpf__check( s, '-' ) ) {
			s->flags |= kPF_Left;
			++fcount;
		}
		if( axpf__check( s, '+' ) ) {
			s->flags |= kPF_Sign;
			++fcount;
		}
		if( axpf__check( s, ' ' ) ) {
			s->flags |= kPF_Space;
			++fcount;
		}
		if( axpf__check( s, '#' ) ) {
			s->flags |= kPF_Radix;
			++fcount;
		}
		if( axpf__check( s, '0' ) ) {
			s->flags |= kPF_Zero;
			++fcount;
		}
		if( axpf__check( s, '\'' ) ) {
			s->flags |= kPF_Group;
			++fcount;
		}
	} while( fcount > 0 );

	/* width */
	if( axpf__check( s, '*' ) ) {
		s->width = va_arg( s->args, int );
		s->flags |= kPF_Width;
	} else if( axpf__getdigit( axpf__look( s ), 10 ) >= 0 ) {
		s->width = axpf__step_int( s );
		s->flags |= kPF_Width;
	}
	
	/* precision */
	if( axpf__check( s, '.' ) ) {
		if( axpf__check( s, '*' ) ) {
			s->precision = va_arg( s->args, int );
			s->flags |= kPF_Precision;
		} else if( axpf__getdigit( axpf__look( s ), 10 ) >= 0 ) {
			s->precision = axpf__step_int( s );
			s->flags |= kPF_Precision;
		}
	}

	/* length specifier */
	if( axpf__check( s, 'h' ) ) {
		if( axpf__check( s, 'h' ) ) {
			s->lenspec = kLS_hh;
		} else {
			s->lenspec = kLS_h;
		}
	} else if( axpf__check( s, 'l' ) ) {
		if( axpf__check( s, 'l' ) ) {
			s->lenspec = kLS_ll;
		} else {
			s->lenspec = kLS_l;
		}
	} else if( axpf__check( s, 'j' ) ) {
		s->lenspec = kLS_j;
	} else if( axpf__check( s, 'z' ) ) {
		s->lenspec = kLS_z;
	} else if( axpf__check( s, 't' ) ) {
		s->lenspec = kLS_t;
	} else if( axpf__check( s, 'L' ) ) {
		s->lenspec = kLS_L;
	} else if( axpf__check( s, 'q' ) ) {
		s->lenspec = kLS_I64;
	} else if( axpf__check( s, 'w' ) ) {
		s->lenspec = kLS_l;
	} else if( axpf__check( s, 'I' ) ) {
		if( axpf__checks( s, "32" ) ) {
			s->lenspec = kLS_I32;
		} else if( axpf__checks( s, "64" ) ) {
			s->lenspec = kLS_I64;
		} else {
			s->lenspec = kLS_I;
		}
	}

	spec = axpf__read( s );
	if( spec >= 'A' && spec <= 'Z' && spec!='C' && spec!='D' && spec!='S' ) {
		s->flags |= kPF_Upper;
		spec = spec - 'A' + 'a';
	}

	/* arrays require special handling */
	if( s->flags & kPF_Array ) {
		const void *ptrbase;
		unsigned repeats;

		if( spec == 'r' ) {
			s->radix = va_arg( s->args, unsigned int );
		}
		ptrbase = va_arg( s->args, const void * );

		if( spec == 'a' ) {
			spec = 'f';
			s->radix = 16;
		}

		repeats = s->repeats;
		while( repeats-- > 0 ) {
			union {
				const void *p;
				const int *i; const unsigned int *u;
				const signed char *sc; const unsigned char *usc;
				const short int *si; const unsigned short int *usi;
				const long int *li; const unsigned long int *uli;
#ifdef _MSC_VER
				const __int64 *lli; const unsigned __int64 *ulli;
#else
				const long long int *lli; const unsigned long long int *ulli;
#endif
				const intmax_t *im; const uintmax_t *uim;
				const size_t *sz;
				const ptrdiff_t *pd;
				const int32 *i32; const uint32 *ui32;
				const int64 *i64; const uint64 *ui64;
				const float *f;
				const double *d;
				const long double *ld;
				const char *c;
				const wchar_t *wc;
				const char *const *s;
				const wchar_t *const *ws;
			} x;
			size_t i;

			if( !ptrbase ) {
				const char buf[] = "(null)";
				axpf__write( s, buf, buf + sizeof(buf) - 1 );
				continue;
			}

			axpf__writech( s, s->arrayprint[ 0 ] );

			x.p = ptrbase;
			for( i = 0; i < s->arraysize; ++i ) {
				if( i > 0 ) {
					axpf__writech( s, s->arrayprint[ 1 ] );
				}
				if( s->arrayprint[ 2 ] == ' ' ) {
					axpf__writech( s, ' ' );
				}

				s->repeats = 1;
				switch( spec ) {
				case 'd':
				case 'i':
					switch( s->lenspec ) {
#define P_(F_) axpf__write_int( s, x.F_[i] ); break
					case kLS_None:	P_( i );
					case kLS_hh:	P_( sc );
					case kLS_h:		P_( si );
					case kLS_l:
					case kLS_L:		P_( li );
					case kLS_ll:	P_( lli );
					case kLS_j:		P_( im );
					case kLS_z:		P_( sz );
					case kLS_I:
					case kLS_t:		P_( pd );
					case kLS_I32:	P_( i32 );
					case kLS_I64:	P_( i64 );
#undef P_
					}
					break;

				case 'u':
				case 'o':
				case 'x':
				case 'r':
					if( spec == 'o' ) {
						s->radix = 8;
					} else if( spec == 'x' ) {
						s->radix = 16;
					} else if( spec == 'r' ) {
						s->radix = va_arg( s->args, unsigned int );
					}
					
					switch( s->lenspec ) {
#define P_(F_) axpf__write_uint( s, x.F_[i] ); break
					case kLS_None:	P_( u );
					case kLS_hh:	P_( usc );
					case kLS_h:		P_( usi );
					case kLS_l:
					case kLS_L:		P_( uli );
					case kLS_ll:	P_( ulli );
					case kLS_j:		P_( uim );
					case kLS_I:
					case kLS_z:		P_( sz );
					case kLS_t:		P_( pd );
					case kLS_I32:	P_( ui32 );
					case kLS_I64:	P_( ui64 );
#undef P_
					}
					break;
					
				case 'f':
				case 'e':
				case 'g':
					if( s->lenspec == kLS_None ) {
						axpf__write_floatd( s, ( double )x.f[ i ], spec );
					} else if( s->lenspec == kLS_l ) {
						axpf__write_floatd( s, x.d[ i ], spec );
					} else if( s->lenspec == kLS_L ) {
						axpf__write_floatd( s, ( double )x.ld[ i ], spec );
					}
					break;

				case 'c':
				case 'C':
					if( s->lenspec == kLS_None && spec != 'C' ) {
						axpf__write_char( s, x.c[ i ] );
					} else if( s->lenspec == kLS_l || spec == 'C' ) {
						axpf__write_wchar( s, x.wc[ i ] );
					}
					break;

				case 's':
				case 'S':
					if( s->lenspec == kLS_None && spec != 'S' ) {
						axpf__write_str( s, x.s[ i ] );
					} else if( s->lenspec == kLS_l || spec == 'S' ) {
						axpf__write_wstr( s, x.ws[ i ] );
					}
					break;

				case 'p':
					s->radix = 16;
					s->flags |= kPF_Radix | kPF_Pointer | kPF_Precision;
					s->precision = sizeof( void * )*2;
					axpf__write_uint( s, x.sz[ i ] );
					break;
				}
			}

			if( s->arrayprint[ 2 ] == ' ' ) {
				axpf__writech( s, ' ' );
			}

			axpf__writech( s, s->arrayprint[ 3 ] );
		}

		return !s->diderror;
	}

	/* check the specifier */
	switch( spec ) {
	case 'd':
	case 'i':
		if( s->lenspec == kLS_l ) {
			axpf__write_int( s, va_arg( s->args, long int ) );
		} else if( s->lenspec == kLS_ll || s->lenspec == kLS_L ) {
#ifdef _MSC_VER
			axpf__write_int( s, va_arg( s->args, __int64 ) );
#else
			axpf__write_int( s, va_arg( s->args, long long int ) );
#endif
		} else if( s->lenspec == kLS_t || s->lenspec == kLS_I ) {
			axpf__write_int( s, va_arg( s->args, ptrdiff_t ) );
		} else if( s->lenspec == kLS_z ) {
			axpf__write_int( s, va_arg( s->args, size_t ) );
		} else if( s->lenspec == kLS_j ) {
			axpf__write_int( s, va_arg( s->args, intmax_t ) );
		} else if( s->lenspec == kLS_I32 ) {
			axpf__write_int( s, va_arg( s->args, int32 ) );
		} else if( s->lenspec == kLS_I64 ) {
			axpf__write_int( s, va_arg( s->args, int64 ) );
		} else {
			axpf__write_int( s, va_arg( s->args, int ) );
		}
		break;

	case 'u':
	case 'o':
	case 'x':
	case 'r':
		if( spec == 'o' ) {
			s->radix = 8;
		} else if( spec == 'x' ) {
			s->radix = 16;
		} else if( spec == 'r' ) {
			s->radix = va_arg( s->args, unsigned int );
		}
		
		if( s->lenspec == kLS_l ) {
			axpf__write_uint( s, va_arg( s->args, unsigned long int ) );
		} else if( s->lenspec == kLS_ll || s->lenspec == kLS_L ) {
#ifdef _MSC_VER
			axpf__write_uint( s, va_arg( s->args, unsigned __int64 ) );
#else
			axpf__write_uint( s, va_arg( s->args, unsigned long long int ) );
#endif
		} else if( s->lenspec == kLS_t ) {
			axpf__write_uint( s, va_arg( s->args, ptrdiff_t ) );
		} else if( s->lenspec == kLS_z || s->lenspec == kLS_I ) {
			axpf__write_uint( s, va_arg( s->args, size_t ) );
		} else if( s->lenspec == kLS_j ) {
			axpf__write_uint( s, va_arg( s->args, uintmax_t ) );
		} else if( s->lenspec == kLS_I32 ) {
			axpf__write_uint( s, va_arg( s->args, uint32 ) );
		} else if( s->lenspec == kLS_I64 ) {
			axpf__write_uint( s, va_arg( s->args, uint64 ) );
		} else {
			axpf__write_uint( s, va_arg( s->args, unsigned int ) );
		}
		break;

	case 'f':
	case 'e':
	case 'g':
	case 'a':
		if( spec == 'a' ) {
			spec = 'f';
			s->radix = 16;
		}

		if( s->lenspec == kLS_None ) {
			axpf__write_floatd( s, va_arg( s->args, double ), spec );
		} else if( s->lenspec == kLS_l || s->lenspec == kLS_L ) {
			axpf__write_floatd( s, ( double )va_arg( s->args, long double ),
				spec );
		}
		break;

	case 'c':
	case 'C':
		if( s->lenspec == kLS_None && spec != 'C' ) {
			axpf__write_char( s, va_arg( s->args, int ) );
		} else if( s->lenspec == kLS_l || spec == 'C' ) {
			axpf__write_wchar( s, va_arg( s->args, int/*wint_t*/ ) );
		}
		break;

	case 's':
	case 'S':
		if( s->lenspec == kLS_None && spec != 'S' ) {
			axpf__write_str( s, va_arg( s->args, const char * ) );
		} else if( s->lenspec == kLS_l || spec == 'S' ) {
			axpf__write_wstr( s, va_arg( s->args, const wchar_t * ) );
		}
		break;

	case 'p':
		s->radix = 16;
		s->flags |= kPF_Radix | kPF_Pointer | kPF_Precision;
		s->precision = sizeof( void * )*2;
		axpf__write_uint( s, va_arg( s->args, size_t ) );
		break;

	case 'n':
		switch( s->lenspec ) {
#define P_(Ty_) *va_arg( s->args, Ty_ * ) = ( Ty_ )s->num_written; break
		case kLS_None:		P_( int );
		case kLS_hh:		P_( signed char );
		case kLS_h:			P_( short int );
		case kLS_l:
		case kLS_L:			P_( long int );
#ifdef _MSC_VER
		case kLS_ll:		P_( __int64 );
#else
		case kLS_ll:		P_( long long int );
#endif
		case kLS_j:			P_( intmax_t );
		case kLS_z:			P_( size_t );
		case kLS_I:
		case kLS_t:			P_( ptrdiff_t );
		case kLS_I32:		P_( int32 );
		case kLS_I64:		P_( int64 );
#undef P_
		}
		break;

	case 'm':
		axpf__write_syserr( s, s->width != 0 ? s->width : ( int )errno );
		break;

	case 'b':
		{
			int bits;
			const char *names;

			bits = va_arg( s->args, int );
			names = va_arg( s->args, const char * );

			axpf__write_bitfield( s, bits, names );
		}
		break;
	case 'D':
		{
			const unsigned char *data;
			const char *delm;

			data = va_arg( s->args, const unsigned char * );
			delm = va_arg( s->args, const char * );

			axpf__write_hex_dump( s, data, delm );
		}
		break;
	}

	return !s->diderror;
}
static ptrdiff_t axpf__main( struct axpf__state_ *s, const char *fmt,
const char *fmte, va_list args )
{
	s->s = fmt;
	s->p = fmt;
	s->e = !fmte ? strchr( fmt, '\0' ) : fmte;

	s->num_written = 0;
	s->args = args;

	s->diderror = 0;

	while( axpf__step( s ) ) {
	}

	if( s->diderror ) {
		return -1;
	}

	return ( ptrdiff_t )s->num_written;
}

ptrdiff_t axfpfev( FILE *fp, const char *fmt, const char *fmte, va_list args )
{
	struct axpf__state_ s;

	s.pfn_write = &axpf__write_fp_f;
	s.write_data = ( void * )fp;

	return axpf__main( &s, fmt, fmte, args );
}
ptrdiff_t axfpfv( FILE *fp, const char *fmt, va_list args )
{
	struct axpf__state_ s;

	s.pfn_write = &axpf__write_fp_f;
	s.write_data = ( void * )fp;

	return axpf__main( &s, fmt, ( const char * )0, args );
}
ptrdiff_t axfpfe( FILE *fp, const char *fmt, const char *fmte, ... )
{
	struct axpf__state_ s;
	ptrdiff_t r;
	va_list args;

	s.pfn_write = &axpf__write_fp_f;
	s.write_data = ( void * )fp;

	va_start( args, fmte );
	r = axpf__main( &s, fmt, fmte, args );
	va_end( args );

	return r;
}
ptrdiff_t axfpf( FILE *fp, const char *fmt, ... )
{
	struct axpf__state_ s;
	ptrdiff_t r;
	va_list args;

	s.pfn_write = &axpf__write_fp_f;
	s.write_data = ( void * )fp;

	va_start( args, fmt );
	r = axpf__main( &s, fmt, ( const char * )0, args );
	va_end( args );

	return r;
}

ptrdiff_t axerrfev( const char *fmt, const char *fmte, va_list args )
{
	struct axpf__state_ s;

	s.pfn_write = &axpf__write_fp_f;
	s.write_data = ( void * )stderr;

	return axpf__main( &s, fmt, fmte, args );
}
ptrdiff_t axerrfv( const char *fmt, va_list args )
{
	struct axpf__state_ s;

	s.pfn_write = &axpf__write_fp_f;
	s.write_data = ( void * )stderr;

	return axpf__main( &s, fmt, ( const char * )0, args );
}
ptrdiff_t axerrfe( const char *fmt, const char *fmte, ... )
{
	struct axpf__state_ s;
	ptrdiff_t r;
	va_list args;

	s.pfn_write = &axpf__write_fp_f;
	s.write_data = ( void * )stderr;

	va_start( args, fmte );
	r = axpf__main( &s, fmt, fmte, args );
	va_end( args );

	return r;
}
ptrdiff_t axerrf( const char *fmt, ... )
{
	struct axpf__state_ s;
	ptrdiff_t r;
	va_list args;

	s.pfn_write = &axpf__write_fp_f;
	s.write_data = ( void * )stderr;

	va_start( args, fmt );
	r = axpf__main( &s, fmt, ( const char * )0, args );
	va_end( args );

	return r;
}

ptrdiff_t axpfev( const char *fmt, const char *fmte, va_list args )
{
	struct axpf__state_ s;

	s.pfn_write = &axpf__write_fp_f;
	s.write_data = ( void * )stdout;

	return axpf__main( &s, fmt, fmte, args );
}
ptrdiff_t axpfv( const char *fmt, va_list args )
{
	struct axpf__state_ s;

	s.pfn_write = &axpf__write_fp_f;
	s.write_data = ( void * )stdout;

	return axpf__main( &s, fmt, ( const char * )0, args );
}
ptrdiff_t axpfe( const char *fmt, const char *fmte, ... )
{
	struct axpf__state_ s;
	ptrdiff_t r;
	va_list args;

	s.pfn_write = &axpf__write_fp_f;
	s.write_data = ( void * )stdout;

	va_start( args, fmte );
	r = axpf__main( &s, fmt, fmte, args );
	va_end( args );

	return r;
}
ptrdiff_t axpf( const char *fmt, ... )
{
	struct axpf__state_ s;
	ptrdiff_t r;
	va_list args;

	s.pfn_write = &axpf__write_fp_f;
	s.write_data = ( void * )stdout;

	va_start( args, fmt );
	r = axpf__main( &s, fmt, ( const char * )0, args );
	va_end( args );

	return r;
}

ptrdiff_t axspfev( char *buf, size_t nbuf, const char *fmt, const char *fmte,
va_list args )
{
	struct axpf__write_mem_data_ m;
	struct axpf__state_ s;
	ptrdiff_t r;

	m.p = buf;
	m.i = 0;
	m.n = nbuf;

	s.pfn_write = &axpf__write_mem_f;
	s.write_data = ( void * )&m;

	r = axpf__main( &s, fmt, fmte, args );
	if( m.n > 0 ) {
		m.p[ m.i < m.n ? m.i : m.n - 1 ] = '\0';
	}

	return r;
}
ptrdiff_t axspfv( char *buf, size_t nbuf, const char *fmt, va_list args )
{
	return axspfev( buf, nbuf, fmt, ( const char * )0, args );
}
ptrdiff_t axspfe( char *buf, size_t nbuf, const char *fmt, const char *fmte,
... )
{
	ptrdiff_t r;
	va_list args;

	va_start( args, fmte );
	r = axspfev( buf, nbuf, fmt, fmte, args );
	va_end( args );

	return r;
}
ptrdiff_t axspf( char *buf, size_t nbuf, const char *fmt, ... )
{
	ptrdiff_t r;
	va_list args;

	va_start( args, fmt );
	r = axspfev( buf, nbuf, fmt, ( const char * )0, args );
	va_end( args );

	return r;
}

#ifdef __cplusplus
template< size_t tMaxBuf >
ptrdiff_t axspfev( char( &buf )[ tMaxBuf ], const char *fmt, const char *fmte,
va_list args )
{
	return axspfev( buf, tMaxBuf, fmt, fmte, args );
}
template< size_t tMaxBuf >
ptrdiff_t axspfv( char( &buf )[ tMaxBuf ], const char *fmt, va_list args )
{
	return axspfev( buf, tMaxBuf, fmt, ( const char * )0, args );
}
template< size_t tMaxBuf >
ptrdiff_t axspfe( char( &buf )[ tMaxBuf ], const char *fmt, const char *fmte,
... )
{
	ptrdiff_t r;
	va_list args;

	va_start( args, fmte );
	r = axspfev( buf, tMaxBuf, fmt, fmte, args );
	va_end( args );

	return r;
}
template< size_t tMaxBuf >
ptrdiff_t axspf( char( &buf )[ tMaxBuf ], const char *fmt, ... )
{
	ptrdiff_t r;
	va_list args;

	va_start( args, fmt );
	r = axspfev( buf, tMaxBuf, fmt, ( const char * )0, args );
	va_end( args );

	return r;
}
#endif

/* -------------------------------------------------------------------------- */

int main()
{
	float arr_f[ 4 ];
	int arr_i[ 4 ];

	arr_i[ 0 ] = 1;
	arr_i[ 1 ] = 2;
	arr_i[ 2 ] = 3;
	arr_i[ 3 ] = 4;

	axpf( "Hello, world!\n" );
	axpf( "Test string: \"%s\"\n", "This is a test" );
	axpf( "Test integer: %d\n", 100 );
	axpf( "Test signed integer: %d\n", -123 );
	axpf( "Test hex: %X\n", 0xFACE );
	axpf( "Test hex two: %#X\n", 0xFACE );
	axpf( "Test pointer: %p\n", ( const void * )"Yo" );
	axpf( "Hex: %4D\n", "ABCD", ":" );
	axpf( "Bits: %b\n", 3, "\20\3Three\2Two\1One" );
	axpf( "String: %.*s\n", 4, "Hey!XXXXXXXX" );
	axpf( "String: %.2s\n", "YoXXXXX" );
	axpf( "Errno: %2m\n" ); /* No such file or directory */
	axpf( "Grouping: %'d ;; %'d ;; %'d ;; %'d\n", 12345, 1234, 123, 1234567 );
	axpf( "Binary: %r\n", 2, 10 );
	axpf( "Binary-pre: %#r\n", 2, 127 );
	axpf( "Repeat: %{5}s\n", "A" );
	axpf( "Repeat: %{*}sYo\n", 4, " " );
	axpf( "Repeat: %{1}i\n", 1234 );
	axpf( "Repeat: %{0}i\n", 1234 );
	axpf( "Array: %[3]d\n", &arr_i[0] );
	axpf( "Array: %[*]d\n", 4, arr_i );
	axpf( "Repeat-array: %{3}[2]d\n", arr_i );

	arr_i[ 0 ] = 1234;
	arr_i[ 1 ] = 5678;
	arr_i[ 2 ] = 1593;
	axpf( "Custom-array: %[3<;>]'d\n", arr_i );

	axpf( "Float: %f\n", 3.1415926535 );

	arr_f[ 0 ] = -1.5f;
	arr_f[ 1 ] = 95.235f;
	arr_f[ 2 ] = -127.127f;

	axpf( "Float-array: %[3].2f\n", arr_f );

	return EXIT_SUCCESS;
}
