summaryrefslogtreecommitdiff
path: root/libc/stdio/printf.c
diff options
context:
space:
mode:
Diffstat (limited to 'libc/stdio/printf.c')
-rw-r--r--libc/stdio/printf.c2033
1 files changed, 2033 insertions, 0 deletions
diff --git a/libc/stdio/printf.c b/libc/stdio/printf.c
new file mode 100644
index 000000000..ad4da445b
--- /dev/null
+++ b/libc/stdio/printf.c
@@ -0,0 +1,2033 @@
+/* Copyright (C) 2002 Manuel Novoa III
+ * My stdio library for linux and (soon) elks.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* This code needs a lot of clean up. Some of that is on hold until uClibc
+ * gets a better configuration system (on Erik's todo list).
+ * The other cleanup will take place during the implementation/integration of
+ * the wide char (un)formatted i/o functions which I'm currently working on.
+ */
+
+/* ATTENTION! ATTENTION! ATTENTION! ATTENTION! ATTENTION!
+ *
+ * This code is currently under development. Also, I plan to port
+ * it to elks which is a 16-bit environment with a fairly limited
+ * compiler. Therefore, please refrain from modifying this code
+ * and, instead, pass any bug-fixes, etc. to me. Thanks. Manuel
+ *
+ * ATTENTION! ATTENTION! ATTENTION! ATTENTION! ATTENTION! */
+
+#define _ISOC99_SOURCE /* for ULLONG primarily... */
+#define _GNU_SOURCE
+#define _STDIO_UTILITY /* We're using _uintmaxtostr. */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stddef.h>
+#include <ctype.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <assert.h>
+#include <stdint.h>
+#include <errno.h>
+
+#define __PRINTF_INFO_NO_BITFIELD
+#include <printf.h>
+
+#ifdef __STDIO_THREADSAFE
+#include <pthread.h>
+#endif /* __STDIO_THREADSAFE */
+
+/**********************************************************************/
+
+#define __STDIO_GLIBC_CUSTOM_PRINTF
+
+/* TODO -- move these to a configuration section? */
+#define MAX_FIELD_WIDTH 4095
+#define MAX_USER_SPEC 10
+#define MAX_POS_ARGS 10
+
+/* TODO - fix the defs below */
+#define MAX_ARGS_PER_SPEC (MAX_POS_ARGS-2)
+
+#if MAX_ARGS_PER_SPEC + 2 > MAX_POS_ARGS
+#define MAX_ARGS MAX_ARGS_PER_SPEC + 2
+#else
+#define MAX_ARGS MAX_POS_ARGS
+#endif
+
+/**********************************************************************/
+
+#define __PA_FLAG_INTMASK \
+ (__PA_FLAG_CHAR|PA_FLAG_SHORT|__PA_FLAG_INT|PA_FLAG_LONG|PA_FLAG_LONG_LONG)
+
+extern printf_function *_custom_printf_handler[MAX_USER_SPEC];
+extern printf_arginfo_function *_custom_printf_arginfo[MAX_USER_SPEC];
+extern char *_custom_printf_spec;
+
+/**********************************************************************/
+
+#define SPEC_FLAGS " +0-#'I"
+enum {
+ FLAG_SPACE = 0x01,
+ FLAG_PLUS = 0x02, /* must be 2 * FLAG_SPACE */
+ FLAG_ZERO = 0x04,
+ FLAG_MINUS = 0x08, /* must be 2 * FLAG_ZERO */
+ FLAG_HASH = 0x10,
+ FLAG_THOUSANDS = 0x20,
+ FLAG_I18N = 0x40 /* only works for d, i, u */
+};
+
+/**********************************************************************/
+
+/* float layout 01234567890123456789 TODO: B?*/
+#define SPEC_CHARS "npxXoudifFeEgGaACScs"
+enum {
+ CONV_n = 0,
+ CONV_p,
+ CONV_x, CONV_X, CONV_o, CONV_u, CONV_d, CONV_i,
+ CONV_f, CONV_F, CONV_e, CONV_E, CONV_g, CONV_G, CONV_a, CONV_A,
+ CONV_C, CONV_S, CONV_c, CONV_s,
+ CONV_m,
+ CONV_custom0 /* must be last */
+};
+
+/* p x X o u d i */
+#define SPEC_BASE { 16, 16, 16, 8, 10, 10, 10 }
+
+#define SPEC_RANGES { CONV_n, CONV_p, CONV_i, CONV_A, \
+ CONV_C, CONV_S, CONV_c, CONV_s, CONV_custom0 }
+
+#define SPEC_OR_MASK { \
+ /* n */ (PA_FLAG_PTR|PA_INT), \
+ /* p */ PA_POINTER, \
+ /* oxXudi */ PA_INT, \
+ /* fFeEgGaA */ PA_DOUBLE, \
+ /* C */ PA_WCHAR, \
+ /* S */ PA_WSTRING, \
+ /* c */ PA_CHAR, \
+ /* s */ PA_STRING, \
+}
+
+#define SPEC_AND_MASK { \
+ /* n */ (PA_FLAG_PTR|__PA_INTMASK), \
+ /* p */ PA_POINTER, \
+ /* oxXudi */ (__PA_INTMASK), \
+ /* fFeEgGaA */ (PA_FLAG_LONG_DOUBLE|PA_DOUBLE), \
+ /* C */ (PA_WCHAR), \
+ /* S */ (PA_WSTRING), \
+ /* c */ (PA_CHAR), \
+ /* s */ (PA_STRING), \
+}
+
+/**********************************************************************/
+/*
+ * In order to ease translation to what arginfo and _print_info._flags expect,
+ * we map: 0:int 1:char 2:longlong 4:long 8:short
+ * and then _flags |= (((q << 7) + q) & 0x701) and argtype |= (_flags & 0x701)
+ */
+
+/* TODO -- Fix the table below to take into account stdint.h. */
+#if PTRDIFF_MAX != INT_MAX
+#error fix QUAL_CHARS ptrdiff_t entry 't'!
+#endif
+#if SIZE_MAX != UINT_MAX
+#error fix QUAL_CHARS size_t entries 'z', 'Z'!
+#endif
+#ifndef LLONG_MAX
+#error fix QUAL_CHARS for no long long! Affects 'L', 'j', 'q', 'll'.
+#else
+#if LLONG_MAX != INTMAX_MAX
+#error fix QUAL_CHARS intmax_t entry 'j'!
+#endif
+#endif
+
+#define QUAL_CHARS { \
+ /* j:(u)intmax_t z:(s)size_t t:ptrdiff_t \0:int */ \
+ /* q:long_long Z:(s)size_t */ \
+ 'h', 'l', 'L', 'j', 'z', 't', 'q', 'Z', 0, \
+ 2, 4, 8, 8, 0, 0, 8, 0, 0, /* TODO -- fix!!! */\
+ 1, 8 \
+}
+
+/**********************************************************************/
+
+#ifdef __STDIO_VA_ARG_PTR
+#ifdef __BCC__
+#define __va_arg_ptr(ap,type) (((type *)(ap += sizeof(type))) - 1)
+#endif
+
+#if 1
+#ifdef __GNUC__
+/* TODO -- need other than for 386 as well! */
+
+#ifndef __va_rounded_size
+#define __va_rounded_size(TYPE) \
+ (((sizeof (TYPE) + sizeof (int) - 1) / sizeof (int)) * sizeof (int))
+#endif
+#define __va_arg_ptr(AP, TYPE) \
+ (AP = (va_list) ((char *) (AP) + __va_rounded_size (TYPE)), \
+ ((void *) ((char *) (AP) - __va_rounded_size (TYPE))))
+#endif
+#endif
+#endif /* __STDIO_VA_ARG_PTR */
+
+#ifdef __va_arg_ptr
+#define GET_VA_ARG(AP,F,TYPE,ARGS) (*(AP) = __va_arg_ptr(ARGS,TYPE))
+#define GET_ARG_VALUE(AP,F,TYPE) (*((TYPE *)(*(AP))))
+#else
+typedef union {
+ wchar_t wc;
+ unsigned int u;
+ unsigned long ul;
+#ifdef ULLONG_MAX
+ unsigned long long ull;
+#endif
+#ifndef __BCC__
+ double d;
+ long double ld;
+#endif
+ void *p;
+} argvalue_t;
+
+#define GET_VA_ARG(AU,F,TYPE,ARGS) (AU->F = va_arg(ARGS,TYPE))
+#define GET_ARG_VALUE(AU,F,TYPE) ((TYPE)((AU)->F))
+#endif
+
+typedef struct {
+ const char *fmtpos; /* TODO: move below struct?? */
+ struct printf_info info;
+ int maxposarg; /* > 0 if args are positional, 0 if not, -1 if unknown */
+ int num_data_args; /* TODO: use sentinal??? */
+ unsigned int conv_num;
+ unsigned char argnumber[4]; /* width | prec | 1st data | unused */
+ int argtype[MAX_ARGS];
+ va_list arg;
+#ifdef __va_arg_ptr
+ void *argptr[MAX_ARGS];
+#else
+ /* While this is wasteful of space in the case where pos args aren't
+ * enabled, it is also needed to support custom printf handlers. */
+ argvalue_t argvalue[MAX_ARGS];
+#endif
+} ppfs_t; /* parse printf format state */
+
+/**********************************************************************/
+
+/* TODO: fix printf to return 0 and set errno if format error. Standard says
+ only returns -1 if sets error indicator for the stream. */
+
+/* TODO -- __isdigit() macro */
+#define __isdigit(c) (((unsigned int)(c - '0')) < 10)
+
+extern size_t _dtostr(FILE * fp, long double x, struct printf_info *info);
+
+#define _outnstr(stream, string, len) _stdio_fwrite(s, len, stream) /* TODO */
+
+extern int _do_one_spec(FILE * __restrict stream, ppfs_t *ppfs, int *count);
+
+extern int _ppfs_init(ppfs_t *ppfs, const char *fmt0); /* validates */
+extern void _ppfs_prepargs(ppfs_t *ppfs, va_list arg); /* sets posargptrs */
+extern void _ppfs_setargs(ppfs_t *ppfs); /* sets argptrs for current spec */
+extern int _ppfs_parsespec(ppfs_t *ppfs); /* parses specifier */
+
+/**********************************************************************/
+#ifdef L_parse_printf_format
+
+/* NOTE: This function differs from the glibc version in that parsing stops
+ * upon encountering an invalid conversion specifier. Since this is the way
+ * my printf functions work, I think it makes sense to do it that way here.
+ * Unfortunately, since glibc sets the return type as size_t, we have no way
+ * of returning that the template is illegal, other than returning 0.
+ */
+
+size_t parse_printf_format(register const char *template,
+ size_t n, int *argtypes)
+{
+ ppfs_t ppfs;
+ size_t i;
+ size_t count = 0;
+
+ if (_ppfs_init(&ppfs, template) >= 0) {
+ if (ppfs.maxposarg > 0) { /* Using positional args. */
+ count = ppfs.maxposarg;
+ if (n > count) {
+ n = count;
+ }
+ for (i = 0 ; i < n ; i++) {
+ *argtypes++ = ppfs.argtype[i];
+ }
+ } else { /* Not using positional args. */
+ while (*template) {
+ if ((*template == '%') && (*++template != '%')) {
+ ppfs.fmtpos = template;
+ _ppfs_parsespec(&ppfs); /* Can't fail. */
+ template = ppfs.fmtpos; /* Update to one past spec end. */
+ if (ppfs.info.width == INT_MIN) {
+ ++count;
+ if (n > 0) {
+ *argtypes++ = PA_INT;
+ --n;
+ }
+ }
+ if (ppfs.info.prec == INT_MIN) {
+ ++count;
+ if (n > 0) {
+ *argtypes++ = PA_INT;
+ --n;
+ }
+ }
+ for (i = 0 ; i < ppfs.num_data_args ; i++) {
+ if ((ppfs.argtype[i]) != __PA_NOARG) {
+ ++count;
+ if (n > 0) {
+ *argtypes++ = ppfs.argtype[i];
+ --n;
+ }
+ }
+ }
+ } else {
+ ++template;
+ }
+ }
+ }
+ }
+
+ return count;
+}
+
+#endif
+/**********************************************************************/
+#ifdef L_vfprintf
+
+/* We only support ascii digits (or their USC equivalent codes) in
+ * precision and width settings in *printf (wide) format strings.
+ * In other words, we don't currently support glibc's 'I' flag.
+ * We do accept it, but it is currently ignored. */
+
+/* TODO -- Implement grouping flag... on hold (locale support). */
+
+int vfprintf(FILE * __restrict stream, register const char * __restrict format,
+ va_list arg)
+{
+ ppfs_t ppfs;
+ int count, r;
+ register const char *s;
+
+ __STDIO_THREADLOCK(stream);
+
+ count = 0;
+ s = format;
+
+ if (_ppfs_init(&ppfs, format) < 0) { /* Bad format string. */
+ _outnstr(stream, format, strlen(format));
+ count = -1;
+ } else {
+ _ppfs_prepargs(&ppfs, arg);
+
+ do {
+ while (*format && (*format != '%')) {
+ ++format;
+ }
+
+ if (format-s) { /* output any literal text in format string */
+ if ( (r = _outnstr(stream, s, format-s)) < 0) {
+ count = -1;
+ break;
+ }
+ count += r;
+ }
+
+ if (!*format) { /* we're done */
+ break;
+ }
+
+ if (format[1] != '%') { /* if we get here, *format == '%' */
+ /* TODO: _do_one_spec needs to know what the output funcs are!!! */
+ ppfs.fmtpos = ++format;
+ /* TODO: check -- should only fail on stream error */
+ if ( (r = _do_one_spec(stream, &ppfs, &count)) < 0) {
+ count = -1;
+ break;
+ }
+ s = format = ppfs.fmtpos;
+ } else { /* %% means literal %, so start new string */
+ s = ++format;
+ ++format;
+ }
+ } while (1);
+ }
+
+ __STDIO_THREADUNLOCK(stream);
+
+ return count;
+}
+#endif
+/**********************************************************************/
+#ifdef L__ppfs_init
+
+int _ppfs_init(register ppfs_t *ppfs, const char *fmt0)
+{
+ int r;
+
+ /* First, zero out everything... argnumber[], argtype[], argptr[] */
+ memset(ppfs, 0, sizeof(ppfs_t)); /* TODO: nonportable???? */
+ --ppfs->maxposarg; /* set to -1 */
+ /* now set all argtypes to no-arg */
+ {
+#if 1
+ /* TODO - use memset here since already "paid for"? */
+ register int *p = ppfs->argtype;
+
+ r = MAX_ARGS;
+ do {
+ *p++ = __PA_NOARG;
+ } while (--r);
+#else
+ /* TODO -- get rid of this?? */
+ register char *p = (char *) ((MAX_ARGS-1) * sizeof(int));
+
+ do {
+ *((int *)(((char *)ppfs) + ((int)p) + offsetof(ppfs_t,argtype))) = __PA_NOARG;
+ p -= sizeof(int);
+ } while (p);
+#endif
+ }
+
+ /*
+ * Run through the entire format string to validate it and initialize
+ * the positional arg numbers (if any).
+ */
+ {
+ register const char *fmt = fmt0;
+
+ while (*fmt) {
+ if ((*fmt == '%') && (*++fmt != '%')) {
+ ppfs->fmtpos = fmt; /* back up to the '%' */
+ if ((r = _ppfs_parsespec(ppfs)) < 0) {
+ return -1;
+ }
+ fmt = ppfs->fmtpos; /* update to one past end of spec */
+ } else {
+ ++fmt;
+ }
+ }
+ ppfs->fmtpos = fmt0; /* rewind */
+ }
+
+ /* If we have positional args, make sure we know all the types. */
+ {
+ register int *p = ppfs->argtype;
+ r = ppfs->maxposarg;
+ while (--r >= 0) {
+ if ( *p == __PA_NOARG ) { /* missing arg type!!! */
+ return -1;
+ }
+ ++p;
+ }
+ }
+
+ return 0;
+}
+#endif
+/**********************************************************************/
+#ifdef L__ppfs_prepargs
+void _ppfs_prepargs(register ppfs_t *ppfs, va_list arg)
+{
+ int i;
+
+ __va_copy(ppfs->arg, arg);
+
+ if ((i = ppfs->maxposarg) > 0) { /* init for positional args */
+ ppfs->num_data_args = i;
+ ppfs->info.width = ppfs->info.prec = ppfs->maxposarg = 0;
+ _ppfs_setargs(ppfs);
+ ppfs->maxposarg = i;
+ }
+}
+#endif
+/**********************************************************************/
+#ifdef L__ppfs_setargs
+
+void _ppfs_setargs(register ppfs_t *ppfs)
+{
+#ifdef __va_arg_ptr
+ register void **p = ppfs->argptr;
+#else
+ register argvalue_t *p = ppfs->argvalue;
+#endif
+ int i;
+
+ if (ppfs->maxposarg == 0) { /* initing for or no pos args */
+ if (ppfs->info.width == INT_MIN) {
+ ppfs->info.width =
+#ifdef __va_arg_ptr
+ *(int *)
+#endif
+ GET_VA_ARG(p,u,unsigned int,ppfs->arg);
+ }
+ if (ppfs->info.prec == INT_MIN) {
+ ppfs->info.prec =
+#ifdef __va_arg_ptr
+ *(int *)
+#endif
+ GET_VA_ARG(p,u,unsigned int,ppfs->arg);
+ }
+ i = 0;
+ while (i < ppfs->num_data_args) {
+ switch(ppfs->argtype[i++]) {
+ case (PA_INT|PA_FLAG_LONG_LONG):
+#ifdef ULLONG_MAX
+ GET_VA_ARG(p,ull,unsigned long long,ppfs->arg);
+ break;
+#endif
+ case (PA_INT|PA_FLAG_LONG):
+#if ULONG_MAX != UINT_MAX
+ GET_VA_ARG(p,ul,unsigned long,ppfs->arg);
+ break;
+#endif
+ case PA_CHAR: /* TODO - be careful */
+ /* ... users could use above and really want below!! */
+ case (PA_INT|__PA_FLAG_CHAR):/* TODO -- translate this!!! */
+ case (PA_INT|PA_FLAG_SHORT):
+ case PA_INT:
+ GET_VA_ARG(p,u,unsigned int,ppfs->arg);
+ break;
+ case PA_WCHAR: /* TODO -- assume int? */
+ /* we're assuming wchar_t is at least an int */
+ GET_VA_ARG(p,wc,wchar_t,ppfs->arg);
+ break;
+#ifndef __BCC__
+ /* PA_FLOAT */
+ case PA_DOUBLE:
+ GET_VA_ARG(p,d,double,ppfs->arg);
+ break;
+ case (PA_DOUBLE|PA_FLAG_LONG_DOUBLE):
+ GET_VA_ARG(p,ld,long double,ppfs->arg);
+ break;
+#else
+ case PA_DOUBLE:
+ case (PA_DOUBLE|PA_FLAG_LONG_DOUBLE):
+ assert(0);
+#endif
+ default:
+ /* TODO -- really need to ensure this can't happen */
+ assert(ppfs->argtype[i-1] & PA_FLAG_PTR);
+ case PA_POINTER:
+ case PA_STRING:
+ case PA_WSTRING:
+ GET_VA_ARG(p,p,void *,ppfs->arg);
+ break;
+ case __PA_NOARG:
+ continue;
+ }
+ ++p;
+ }
+ } else {
+ if (ppfs->info.width == INT_MIN) {
+ ppfs->info.width
+ = (int) GET_ARG_VALUE(p + ppfs->argnumber[0] - 1,u,unsigned int);
+ }
+ if (ppfs->info.prec == INT_MIN) {
+ ppfs->info.prec
+ = (int) GET_ARG_VALUE(p + ppfs->argnumber[1] - 1,u,unsigned int);
+ }
+ }
+
+ /* Now we know the width and precision. */
+ if (ppfs->info.width < 0) {
+ ppfs->info.width = -ppfs->info.width;
+ PRINT_INFO_SET_FLAG(&(ppfs->info),left);
+ PRINT_INFO_CLR_FLAG(&(ppfs->info),space);
+ ppfs->info.pad = ' ';
+ }
+#if 0
+ /* NOTE -- keep neg for now so float knows! */
+ if (ppfs->info.prec < 0) { /* spec says treat as omitted. */
+ /* so use default prec... 1 for everything but floats and strings. */
+ ppfs->info.prec = 1;
+ }
+#endif
+}
+#endif
+/**********************************************************************/
+#ifdef L__ppfs_parsespec
+
+/* Notes: argtype differs from glibc for the following:
+ * mine glibc
+ * lc PA_WCHAR PA_CHAR the standard says %lc means %C
+ * ls PA_WSTRING PA_STRING the standard says %ls means %S
+ * {*}n {*}|PA_FLAG_PTR PA_FLAG_PTR size of n can be qualified
+ */
+
+/* TODO: store positions of positional args */
+
+/* TODO -- WARNING -- assumes aligned on integer boundaries!!! */
+
+/* TODO -- disable if not using positional args!!! */
+#define _OVERLAPPING_DIFFERENT_ARGS
+
+/* TODO -- rethink this -- perhaps we should set to largest type??? */
+
+#ifdef _OVERLAPPING_DIFFERENT_ARGS
+
+#define PROMOTED_SIZE_OF(X) ((sizeof(X) + sizeof(int) - 1) / sizeof(X))
+
+static const short int type_codes[] = {
+ __PA_NOARG, /* must be first entry */
+ PA_POINTER,
+ PA_STRING,
+ PA_WSTRING,
+ PA_CHAR,
+ PA_INT|PA_FLAG_SHORT,
+ PA_INT,
+ PA_INT|PA_FLAG_LONG,
+ PA_INT|PA_FLAG_LONG_LONG,
+ PA_WCHAR,
+#ifndef __BCC__
+ /* PA_FLOAT, */
+ PA_DOUBLE,
+ PA_DOUBLE|PA_FLAG_LONG_DOUBLE,
+#endif
+};
+
+static const unsigned char type_sizes[] = {
+ /* unknown type consumes no arg */
+ 0, /* must be first entry */
+ PROMOTED_SIZE_OF(void *),
+ PROMOTED_SIZE_OF(char *),
+ PROMOTED_SIZE_OF(wchar_t *),
+ PROMOTED_SIZE_OF(char),
+ PROMOTED_SIZE_OF(short),
+ PROMOTED_SIZE_OF(int),
+ PROMOTED_SIZE_OF(long),
+#ifdef ULLONG_MAX
+ PROMOTED_SIZE_OF(long long),
+#else
+ PROMOTED_SIZE_OF(long), /* TODO -- is this correct? (above too) */
+#endif
+ PROMOTED_SIZE_OF(wchar_t),
+#ifndef __BCC__
+ /* PROMOTED_SIZE_OF(float), */
+ PROMOTED_SIZE_OF(double),
+ PROMOTED_SIZE_OF(long double),
+#endif
+};
+
+static int _promoted_size(int argtype)
+{
+ register const short int *p;
+
+ /* note -- since any unrecognized type is treated as a pointer */
+ p = type_codes + sizeof(type_codes)/sizeof(type_codes[0]);
+ do {
+ if (*--p == argtype) {
+ break;
+ }
+ } while (p > type_codes);
+
+ return type_sizes[(int)(p - type_codes)];
+}
+
+static int _is_equal_or_bigger_arg(int curtype, int newtype)
+{
+ /* Quick test */
+ if (newtype == __PA_NOARG) {
+ return 0;
+ }
+ if ((curtype == __PA_NOARG) || (curtype == newtype)) {
+ return 1;
+ }
+ /* Ok... slot is already filled and types are different in name. */
+ /* So, compare promoted sizes of curtype and newtype args. */
+ return _promoted_size(curtype) <= _promoted_size(newtype);
+}
+
+#else
+
+#define _is_equal_or_bigger_arg(C,N) (((C) == __PA_NOARG) || ((C) == (N)))
+
+#endif
+
+/* TODO - do this differently? */
+static char _bss_custom_printf_spec[MAX_USER_SPEC]; /* 0-init'd for us. */
+
+char *_custom_printf_spec = _bss_custom_printf_spec;
+printf_arginfo_function *_custom_printf_arginfo[MAX_USER_SPEC];
+
+extern int _ppfs_parsespec(ppfs_t *ppfs)
+{
+ register const char *fmt;
+ register const char *p;
+ int preci;
+ int width;
+ int flags;
+ int dataargtype;
+ int i;
+ int dpoint;
+ int maxposarg;
+ int p_m_spec_chars;
+ int n;
+ int argtype[MAX_ARGS_PER_SPEC+2];
+ int argnumber[3]; /* width, precision, 1st data arg */
+ unsigned int conv_num;
+ static const char spec_flags[] = SPEC_FLAGS;
+ static const char spec_chars[] = SPEC_CHARS;/* TODO: b? */
+ static const char spec_ranges[] = SPEC_RANGES;
+ static const short spec_or_mask[] = SPEC_OR_MASK;
+ static const short spec_and_mask[] = SPEC_AND_MASK;
+ static const char qual_chars[] = QUAL_CHARS;
+
+ /* WIDE note: we can test against '%' here since we don't allow */
+ /* WIDE note: other mappings of '%' in the wide char set. */
+ preci = -1;
+ width = flags = dpoint = 0;
+ argnumber[0] = 0;
+ argnumber[1] = 0;
+ argtype[0] = __PA_NOARG;
+ argtype[1] = __PA_NOARG;
+ maxposarg = ppfs->maxposarg;
+ fmt = ppfs->fmtpos;
+
+ assert(fmt[-1] == '%');
+ assert(fmt[0] != '%');
+
+ /* Process arg pos and/or flags and/or width and/or precision. */
+ width_precision:
+ p = fmt;
+ if (*fmt == '*') {
+ argtype[-dpoint] = PA_INT;
+ ++fmt;
+ }
+ i = 0;
+ while (__isdigit(*fmt)) {
+ if (i < MAX_FIELD_WIDTH) { /* Avoid overflow. */
+ i = (i * 10) + (*fmt - '0');
+ }
+ ++fmt;
+ }
+ if (p[-1] == '%') { /* Check for a position. */
+
+ /* TODO: if val not in range, then error */
+
+ if ((*fmt == '$') && (i > 0)) {/* Positional spec. */
+ ++fmt;
+ if (maxposarg == 0) {
+ return -1;
+ }
+ if ((argnumber[2] = i) > maxposarg) {
+ maxposarg = i;
+ }
+ /* Now fall through to check flags. */
+ } else {
+ if (maxposarg > 0) {
+ return -1;
+ }
+ maxposarg = 0; /* Possible redundant store, but cuts size. */
+
+ if ((fmt > p) && (*p != '0')) {
+ goto PREC_WIDTH;
+ }
+
+ fmt = p; /* Back up for possible '0's flag. */
+ /* Now fall through to check flags. */
+ }
+
+ restart_flags: /* Process flags. */
+ i = 1;
+ p = spec_flags;
+
+ do {
+ if (*fmt == *p++) {
+ ++fmt;
+ flags |= i;
+ goto restart_flags;
+ }
+ i += i; /* Better than i <<= 1 for bcc */
+ } while (*p);
+ i = 0;
+
+ /* If '+' then ignore ' ', and if '-' then ignore '0'. */
+ /* Note: Need to ignore '0' when prec is an arg with val < 0, */
+ /* but that test needs to wait until the arg is retrieved. */
+ flags &= ~((flags & (FLAG_PLUS|FLAG_MINUS)) >> 1);
+ /* Note: Ignore '0' when prec is specified < 0 too (in printf). */
+
+ if (fmt[-1] != '%') { /* If we've done anything, loop for width. */
+ goto width_precision;
+ }
+ }
+ PREC_WIDTH:
+ if (*p == '*') { /* Prec or width takes an arg. */
+ if (maxposarg) {
+ if ((*fmt++ != '$') || (i <= 0)) {
+ /* Using pos args and no $ or invalid arg number. */
+ return -1;
+ }
+ argnumber[-dpoint] = i;
+ } else if (++p != fmt) {
+ /* Not using pos args but digits followed *. */
+ return -1;
+ }
+ i = INT_MIN;
+ }
+
+ if (!dpoint) {
+ width = i;
+ if (*fmt == '.') {
+ ++fmt;
+ dpoint = -1; /* To use as default precison. */
+ goto width_precision;
+ }
+ } else {
+ preci = i;
+ }
+
+ /* Process qualifier. */
+ p = qual_chars;
+ do {
+ if (*fmt == *p) {
+ ++fmt;
+ break;
+ }
+ } while (*++p);
+ if ((p - qual_chars < 2) && (*fmt == *p)) {
+ p += ((sizeof(qual_chars)-2) / 2);
+ ++fmt;
+ }
+ dataargtype = ((int)(p[(sizeof(qual_chars)-2) / 2])) << 8;
+
+ /* Process conversion specifier. */
+ if (!*fmt) {
+ return -1;
+ }
+
+ p = spec_chars;
+
+ do {
+ if (*fmt == *p) {
+ p_m_spec_chars = p - spec_chars;
+
+ if ((p_m_spec_chars >= CONV_c)
+ && (dataargtype & PA_FLAG_LONG)) {
+ p_m_spec_chars -= 2; /* lc -> C and ls -> S */
+ }
+
+ conv_num = p_m_spec_chars;
+ p = spec_ranges-1;
+ while (p_m_spec_chars > *++p) {}
+
+ i = p - spec_ranges;
+ argtype[2] = (dataargtype | spec_or_mask[i]) & spec_and_mask[i];
+ p = spec_chars;
+ break;
+ }
+ } while(*++p);
+
+ ppfs->info.spec = *fmt;
+ ppfs->info.prec = preci;
+ ppfs->info.width = width;
+ ppfs->info.pad = ((flags & FLAG_ZERO) ? '0' : ' ');
+ ppfs->info._flags = (flags & ~FLAG_ZERO) | (dataargtype & __PA_INTMASK);
+ ppfs->num_data_args = 1;
+
+ if (!*p) {
+#ifdef __STDIO_GLIBC_CUSTOM_PRINTF
+ /* TODO -- gnu %m support build option. */
+ if (*fmt == 'm') {
+ conv_num = CONV_m;
+ ppfs->num_data_args = 0;
+ goto DONE;
+ }
+
+ /* Handle custom arg -- WARNING -- overwrites p!!! */
+ conv_num = CONV_custom0;
+ p = _custom_printf_spec;
+ do {
+ if (*p == *fmt) {
+ if ((ppfs->num_data_args
+ = ((*_custom_printf_arginfo[(int)(p-_custom_printf_spec)])
+ (&(ppfs->info), MAX_ARGS_PER_SPEC, argtype+2)))
+ > MAX_ARGS_PER_SPEC) {
+ break; /* Error -- too many args! */
+ }
+ goto DONE;
+ }
+ } while (++p < (_custom_printf_spec + MAX_USER_SPEC));
+#endif /* __STDIO_GLIBC_CUSTOM_PRINTF */
+ /* Otherwise error. */
+ return -1;
+ }
+
+#ifdef __STDIO_GLIBC_CUSTOM_PRINTF
+ DONE:
+#endif
+
+ if (maxposarg > 0) {
+ i = 0;
+ do {
+ /* Update maxposarg and check that NL_ARGMAX is not exceeded. */
+ n = ((i <= 2)
+ ? (ppfs->argnumber[i] = argnumber[i])
+ : argnumber[2] + (i-2));
+ if (n > maxposarg) {
+ if ((maxposarg = n) > NL_ARGMAX) {
+ return -1;
+ }
+ }
+ --n;
+ /* Record argtype with largest size (current, new). */
+ if (_is_equal_or_bigger_arg(ppfs->argtype[n], argtype[i])) {
+ ppfs->argtype[n] = argtype[i];
+ }
+ } while (++i < ppfs->num_data_args + 2);
+ } else {
+ ppfs->argnumber[2] = 1;
+ memcpy(ppfs->argtype, argtype + 2, ppfs->num_data_args * sizeof(int));
+ }
+
+ ppfs->maxposarg = maxposarg;
+ ppfs->fmtpos = ++fmt;
+ ppfs->conv_num = conv_num;
+
+ return ppfs->num_data_args + 2;
+}
+
+#endif
+/**********************************************************************/
+#ifdef L_register_printf_function
+
+int register_printf_function(int spec, printf_function handler,
+ printf_arginfo_function arginfo)
+{
+ register char *r;
+ register char *p;
+
+ if (spec && (arginfo != NULL)) { /* TODO -- check if spec is valid char */
+ r = NULL;
+ p = _custom_printf_spec + MAX_USER_SPEC;
+ do {
+ --p;
+ if (!*p) {
+ r = p;
+ }
+#ifdef __BCC__
+ else /* bcc generates less code with fall-through */
+#endif
+ if (*p == spec) {
+ r = p;
+ p = _custom_printf_spec;
+ }
+ } while (p > _custom_printf_spec);
+
+ if (r) {
+ if (handler) {
+ *r = spec;
+ _custom_printf_handler[(int)(r - p)] = handler;
+ _custom_printf_arginfo[(int)(r - p)] = arginfo;
+ } else {
+ *r = 0;
+ }
+ return 0;
+ }
+ /* TODO -- if asked to unregister a non-existent spec, return what? */
+ }
+ return -1;
+}
+#endif
+/**********************************************************************/
+#ifdef L__do_one_spec
+
+printf_function *_custom_printf_handler[MAX_USER_SPEC];
+
+extern void _store_inttype(void *dest, int desttype, uintmax_t val);
+extern uintmax_t _load_inttype(int desttype, const void *src, int uflag);
+extern void _charpad(FILE * __restrict stream, char padchar, size_t numpad);
+extern void output(FILE * __restrict stream, const char *s);
+
+#define output(F,S) fputs(S,F)
+
+#undef putc
+void _charpad(FILE * __restrict stream, char padchar, size_t numpad)
+{
+ /* TODO -- Use a buffer to cut down on function calls... */
+ char pad[1];
+
+ *pad = padchar;
+ while (numpad) {
+ _stdio_fwrite(pad, 1, stream);
+ --numpad;
+ }
+}
+
+/* TODO -- Dynamically allocate work space to accomodate stack-poor archs? */
+
+int _do_one_spec(FILE * __restrict stream, register ppfs_t *ppfs, int *count)
+{
+ static const char spec_base[] = SPEC_BASE;
+ static const char prefix[] = "+\0-\0 \0000x\0000X";
+ /* 0 2 4 6 9 11*/
+ enum {
+ PREFIX_PLUS = 0,
+ PREFIX_MINUS = 2,
+ PREFIX_SPACE = 4,
+ PREFIX_LWR_X = 6,
+ PREFIX_UPR_X = 9,
+ PREFIX_NONE = 11
+ };
+
+#ifdef __va_arg_ptr
+ const void * const *argptr;
+#else
+ const void * argptr[MAX_ARGS_PER_SPEC];
+#endif
+ int *argtype;
+ size_t slen;
+ int base;
+ int numpad;
+ int numfill = 0; /* TODO: fix */
+ int prefix_num = PREFIX_NONE;
+ char padchar = ' ';
+ char buf[64];
+
+#ifdef NDEBUG
+ _ppfs_parsespec(ppfs);
+#else
+ if (_ppfs_parsespec(ppfs) < 0) { /* TODO: just for debugging */
+ abort();
+ }
+#endif
+ _ppfs_setargs(ppfs);
+
+ argtype = ppfs->argtype + ppfs->argnumber[2] - 1;
+ /* Deal with the argptr vs argvalue issue. */
+#ifdef __va_arg_ptr
+ argptr = (const void * const *) ppfs->argptr;
+ if (ppfs->maxposarg > 0) { /* Using positional args... */
+ argptr += ppfs->argnumber[2] - 1;
+ }
+#else
+ /* Need to build a local copy... */
+ {
+ register argvalue_t *p = ppfs->argvalue;
+ int i;
+ if (ppfs->maxposarg > 0) { /* Using positional args... */
+ p += ppfs->argnumber[2] - 1;
+ }
+ for (i = 0 ; i < ppfs->num_data_args ; i++ ) {
+ argptr[i] = (void *) p++;
+ }
+ }
+#endif
+ {
+ register char *s;
+
+ if (ppfs->conv_num == CONV_n) {
+ _store_inttype(*(void **)*argptr,
+ ppfs->info._flags & __PA_INTMASK,
+ (intmax_t) (*count));
+ return 0;
+ }
+ if (ppfs->conv_num <= CONV_i) { /* pointer or (un)signed int */
+ base = spec_base[(int)(ppfs->conv_num - CONV_p)];
+ if (ppfs->conv_num <= CONV_u) { /* pointer or unsigned int */
+ if (ppfs->conv_num == CONV_p) { /* pointer */
+ prefix_num = PREFIX_LWR_X;
+ } else { /* unsigned int */
+ }
+ } else { /* signed int */
+ base = -base;
+ }
+ if (ppfs->info.prec < 0) { /* Ignore '0' flag if prec specified. */
+ padchar = ppfs->info.pad;
+ }
+ s = _uintmaxtostr(buf + sizeof(buf) - 1,
+ (uintmax_t)
+ _load_inttype(*argtype & __PA_INTMASK,
+ *argptr, base), base,
+ ((ppfs->conv_num == CONV_X)
+ ? __UIM_UPPER : __UIM_LOWER));
+ if (ppfs->conv_num > CONV_u) { /* signed int */
+ if (*s == '-') {
+ PRINT_INFO_SET_FLAG(&(ppfs->info),showsign);
+ ++s; /* handle '-' in the prefix string */
+ prefix_num = PREFIX_MINUS;
+ } else if (PRINT_INFO_FLAG_VAL(&(ppfs->info),showsign)) {
+ prefix_num = PREFIX_PLUS;
+ } else if (PRINT_INFO_FLAG_VAL(&(ppfs->info),space)) {
+ prefix_num = PREFIX_SPACE;
+ }
+ }
+ slen = (char *)(buf + sizeof(buf) - 1) - s;
+ numfill = ((ppfs->info.prec < 0) ? 1 : ppfs->info.prec);
+ if (PRINT_INFO_FLAG_VAL(&(ppfs->info),alt)) {
+ if (ppfs->conv_num <= CONV_x) { /* x or p */
+ prefix_num = PREFIX_LWR_X;
+ }
+ if (ppfs->conv_num == CONV_X) {
+ prefix_num = PREFIX_UPR_X;
+ }
+ if ((ppfs->conv_num == CONV_o) && (numfill < slen)) {
+ numfill = ((*s == '0') ? 1 : slen + 1);
+ }
+ }
+ if (*s == '0') {
+ if (prefix_num >= PREFIX_LWR_X) {
+ prefix_num = PREFIX_NONE;
+ }
+ if (ppfs->conv_num == CONV_p) {/* null pointer */
+ s = "(nil)";
+ slen = 5;
+ numfill = 0;
+ } else if (numfill == 0) { /* if precision 0, no output */
+ slen = 0;
+ }
+ }
+ numfill = ((numfill > slen) ? numfill - slen : 0);
+ } else if (ppfs->conv_num <= CONV_A) { /* floating point */
+ *count += _dtostr(stream,
+ (PRINT_INFO_FLAG_VAL(&(ppfs->info),is_long_double)
+ ? *(long double *) *argptr
+ : (long double) (* (double *) *argptr)),
+ &ppfs->info);
+ return 0;
+ } else if (ppfs->conv_num <= CONV_S) { /* wide char or string */
+#if 1
+ return -1; /* TODO -- wide */
+#else
+ if (ppfs->conv_num == CONV_S) { /* wide string */
+
+ } else { /* wide char */
+
+ }
+#endif
+ } else if (ppfs->conv_num <= CONV_s) { /* char or string */
+ if (ppfs->conv_num == CONV_s) { /* string */
+ s = *((char **) (*argptr));
+ if (s) {
+ SET_STRING_LEN:
+ slen = strnlen(s, ((ppfs->info.prec >= 0)
+ ? ppfs->info.prec : SIZE_MAX));
+ } else {
+ s = "(null)";
+ slen = 6;
+ }
+ } else { /* char */
+ s = (char *) buf;
+ *s = *((const char *) *argptr);
+ s[1] = 0;
+ slen = 1;
+ }
+ } else if (ppfs->conv_num == CONV_m) {
+ s = strerror(errno);
+ goto SET_STRING_LEN;
+ } else {
+ assert(ppfs->conv_num == CONV_custom0);
+
+ s = _custom_printf_spec;
+ do {
+ if (*s == ppfs->info.spec) {
+ int rv;
+ /* TODO -- check return value for sanity? */
+ rv = (*_custom_printf_handler
+ [(int)(s-_custom_printf_spec)])
+ (stream, &ppfs->info, argptr);
+ if (rv < 0) {
+ return -1;
+ }
+ *count += rv;
+ return 0;
+ }
+ } while (++s < (_custom_printf_spec + MAX_USER_SPEC));
+ assert(0);
+ return -1;
+ }
+
+ {
+ size_t t;
+
+ t = slen + numfill;
+ if (prefix_num != PREFIX_NONE) {
+ t += ((prefix_num < PREFIX_LWR_X) ? 1 : 2);
+ }
+ numpad = ((ppfs->info.width > t) ? (ppfs->info.width - t) : 0);
+ *count += t + numpad;
+ }
+ if (padchar == '0') { /* TODO: check this */
+ numfill += numpad;
+ numpad = 0;
+ }
+
+ /* Now handle the output itself. */
+ if (!PRINT_INFO_FLAG_VAL(&(ppfs->info),left)) {
+ _charpad(stream, ' ', numpad);
+ numpad = 0;
+ }
+ output(stream, prefix + prefix_num);
+ _charpad(stream, '0', numfill);
+ _outnstr(stream, s, slen);
+ _charpad(stream, ' ', numpad);
+ }
+
+ return 0;
+}
+#endif
+/**********************************************************************/
+#ifdef L_vsnprintf
+#ifdef __STDIO_BUFFERS
+int vsnprintf(char *__restrict buf, size_t size,
+ const char * __restrict format, va_list arg)
+{
+ FILE f;
+ int rv;
+
+#ifdef __STDIO_GETC_MACRO
+ f.bufgetc =
+#endif
+ f.bufrpos = f.bufwpos = f.bufstart = buf;
+
+ if (size > SIZE_MAX - (size_t) buf) {
+ size = SIZE_MAX - (size_t) buf;
+ }
+#ifdef __STDIO_PUTC_MACRO
+ f.bufputc =
+#endif
+ f.bufend = buf + size;
+
+#if 0 /* shouldn't be necessary */
+/* #ifdef __STDIO_GLIBC_CUSTOM_STREAMS */
+ f.cookie = &(f.filedes);
+ f.gcs.read = 0;
+ f.gcs.write = 0;
+ f.gcs.seek = 0;
+ f.gcs.close = 0;
+#endif
+ f.filedes = -2; /* for debugging */
+ f.modeflags = (__FLAG_NARROW|__FLAG_WRITEONLY|__FLAG_WRITING);
+
+ rv = vfprintf(&f, format, arg);
+ if (size) {
+ if (f.bufwpos == f.bufend) {
+ --f.bufwpos;
+ }
+ *f.bufwpos = 0;
+ }
+ return rv;
+}
+#else /* __STDIO_BUFFERS */
+#ifdef __STDIO_GLIBC_CUSTOM_STREAMS
+
+typedef struct {
+ size_t pos;
+ size_t len;
+ unsigned char *buf;
+ FILE *fp;
+} __snpf_cookie;
+
+#define COOKIE ((__snpf_cookie *) cookie)
+
+static ssize_t snpf_write(void *cookie, const char *buf, size_t bufsize)
+{
+ size_t count;
+ char *p;
+
+ /* Note: bufsize < SSIZE_MAX because of _stdio_WRITE. */
+
+ if (COOKIE->len > COOKIE->pos) {
+ count = COOKIE->len - COOKIE->pos - 1; /* Leave space for nul. */
+ if (count > bufsize) {
+ count = bufsize;
+ }
+
+ p = COOKIE->buf + COOKIE->pos;
+ while (count) {
+ *p++ = *buf++;
+ --count;
+ }
+ *p = 0;
+ }
+
+ COOKIE->pos += bufsize;
+
+ return bufsize;
+}
+
+#undef COOKIE
+
+int vsnprintf(char *__restrict buf, size_t size,
+ const char * __restrict format, va_list arg)
+{
+ FILE f;
+ __snpf_cookie cookie;
+ int rv;
+
+ cookie.buf = buf;
+ cookie.len = size;
+ cookie.pos = 0;
+ cookie.fp = &f;
+
+ f.cookie = &cookie;
+ f.gcs.write = snpf_write;
+ f.gcs.read = NULL;
+ f.gcs.seek = NULL;
+ f.gcs.close = NULL;
+
+ f.filedes = -1; /* For debugging. */
+ f.modeflags = (__FLAG_NARROW|__FLAG_WRITEONLY|__FLAG_WRITING);
+
+ rv = vfprintf(&f, format, arg);
+
+ return rv;
+}
+
+#else /* __STDIO_GLIBC_CUSTOM_STREAMS */
+#warning skipping vsnprintf since no buffering and no custom streams!
+#endif /* __STDIO_GLIBC_CUSTOM_STREAMS */
+#endif /* __STDIO_BUFFERS */
+#endif
+/**********************************************************************/
+#ifdef L_vdprintf
+int vdprintf(int filedes, const char * __restrict format, va_list arg)
+{
+ FILE f;
+ int rv;
+#ifdef __STDIO_BUFFERS
+ char buf[64]; /* TODO: provide _optional_ buffering? */
+
+#ifdef __STDIO_GETC_MACRO
+ f.bufgetc =
+#endif
+ f.bufrpos = f.bufwpos = f.bufstart = buf;
+#ifdef __STDIO_PUTC_MACRO
+ f.bufputc =
+#endif
+ f.bufend = buf + sizeof(buf);
+#endif /* __STDIO_BUFFERS */
+#ifdef __STDIO_GLIBC_CUSTOM_STREAMS
+ f.cookie = &(f.filedes);
+ f.gcs.read = _cs_read;
+ f.gcs.write = _cs_write;
+ f.gcs.seek = 0; /* The internal seek func handles normals. */
+ f.gcs.close = _cs_close;
+#endif
+ f.filedes = filedes;
+ f.modeflags = (__FLAG_NARROW|__FLAG_WRITEONLY|__FLAG_WRITING);
+
+ rv = vfprintf(&f, format, arg);
+
+ return fflush(&f) ? -1 : rv;
+}
+#endif
+/**********************************************************************/
+#ifdef L_vasprintf
+
+#if !defined(__STDIO_BUFFERS) && !defined(__STDIO_GLIBC_CUSTOM_STREAMS)
+#warning skipping vasprintf since no buffering and no custom streams!
+#else
+
+int vasprintf(char **__restrict buf, const char * __restrict format,
+ va_list arg)
+{
+ /* TODO -- change the default implementation? */
+#ifndef __STDIO_GLIBC_CUSTOM_STREAMS
+ /* This implementation actually calls the printf machinery twice, but only
+ * only does one malloc. This can be a problem though when custom printf
+ * specs or the %m specifier are involved because the results of the
+ * second call might be different from the first. */
+
+ int rv = vsnprintf(NULL, 0, format, arg);
+
+ return (((rv >= 0) && ((*buf = malloc(++rv)) != NULL))
+ ? vsnprintf(*buf, rv, format, arg)
+ : -1);
+#else
+ FILE *f;
+ size_t size;
+ int rv;
+
+ /* TODO - do the memstream stuff inline here to avoid fclose and the openlist? */
+ if ((f = open_memstream(buf, &size)) == NULL) {
+ return -1;
+ }
+ rv = vfprintf(f, format, arg);
+ fclose(f);
+ if (rv < 0) {
+ free(*buf);
+ *buf = NULL;
+ return -1;
+ }
+ return rv;
+#endif
+}
+#endif
+#endif
+/**********************************************************************/
+#ifdef L_vprintf
+int vprintf(const char * __restrict format, va_list arg)
+{
+ return vfprintf(stdout, format, arg);
+}
+#endif
+/**********************************************************************/
+#ifdef L_vsprintf
+
+#if !defined(__STDIO_BUFFERS) && !defined(__STDIO_GLIBC_CUSTOM_STREAMS)
+#warning skipping vsprintf since no buffering and no custom streams!
+#else
+
+int vsprintf(char *__restrict buf, const char * __restrict format,
+ va_list arg)
+{
+ return vsnprintf(buf, SIZE_MAX, format, arg);
+}
+
+#endif
+#endif
+/**********************************************************************/
+#ifdef L_fprintf
+int fprintf(FILE * __restrict stream, const char * __restrict format, ...)
+{
+ va_list arg;
+ int rv;
+
+ va_start(arg, format);
+ rv = vfprintf(stream, format, arg);
+ va_end(arg);
+
+ return rv;
+}
+#endif
+/**********************************************************************/
+#ifdef L_snprintf
+
+#if !defined(__STDIO_BUFFERS) && !defined(__STDIO_GLIBC_CUSTOM_STREAMS)
+#warning skipping snprintf since no buffering and no custom streams!
+#else
+
+int snprintf(char *__restrict buf, size_t size,
+ const char * __restrict format, ...)
+{
+ va_list arg;
+ int rv;
+
+ va_start(arg, format);
+ rv = vsnprintf(buf, size, format, arg);
+ va_end(arg);
+ return rv;
+}
+
+#endif
+#endif
+/**********************************************************************/
+#ifdef L_dprintf
+int dprintf(int filedes, const char * __restrict format, ...)
+{
+ va_list arg;
+ int rv;
+
+ va_start(arg, format);
+ rv = vdprintf(filedes, format, arg);
+ va_end(arg);
+
+ return rv;
+}
+#endif
+/**********************************************************************/
+#ifdef L_asprintf
+
+#if !defined(__STDIO_BUFFERS) && !defined(__STDIO_GLIBC_CUSTOM_STREAMS)
+#warning skipping asprintf and __asprintf since no buffering and no custom streams!
+#else
+
+weak_alias(__asprintf,asprintf)
+
+int __asprintf(char **__restrict buf, const char * __restrict format, ...)
+{
+ va_list arg;
+ int rv;
+
+ va_start(arg, format);
+ rv = vasprintf(buf, format, arg);
+ va_end(arg);
+
+ return rv;
+}
+
+#endif
+#endif
+/**********************************************************************/
+#ifdef L_printf
+int printf(const char * __restrict format, ...)
+{
+ va_list arg;
+ int rv;
+
+ va_start(arg, format);
+ rv = vfprintf(stdout, format, arg);
+ va_end(arg);
+
+ return rv;
+}
+#endif
+/**********************************************************************/
+#ifdef L_sprintf
+
+#if !defined(__STDIO_BUFFERS) && !defined(__STDIO_GLIBC_CUSTOM_STREAMS)
+#warning skipping sprintf since no buffering and no custom streams!
+#else
+
+int sprintf(char *__restrict buf, const char * __restrict format, ...)
+{
+ va_list arg;
+ int rv;
+
+ va_start(arg, format);
+ rv = vsnprintf(buf, SIZE_MAX, format, arg);
+ va_end(arg);
+
+ return rv;
+}
+
+#endif
+#endif
+/**********************************************************************/
+#ifdef L__dtostr
+/*
+ * Copyright (C) 2000, 2001 Manuel Novoa III
+ *
+ * Function: size_t _dtostr(FILE *fp, long double x, struct printf_info *info)
+ *
+ * This was written for uClibc to provide floating point support for
+ * the printf functions. It handles +/- infinity and nan on i386.
+ *
+ * Notes:
+ *
+ * At most MAX_DIGITS significant digits are kept. Any trailing digits
+ * are treated as 0 as they are really just the results of rounding noise
+ * anyway. If you want to do better, use an arbitary precision arithmetic
+ * package. ;-)
+ *
+ * It should also be fairly portable, as not assumptions are made about the
+ * bit-layout of doubles.
+ *
+ * It should be too difficult to convert this to handle long doubles on i386.
+ * For information, see the comments below.
+ *
+ * TODO:
+ * long double and/or float version? (note: for float can trim code some).
+ *
+ * Decrease the size. This is really much bigger than I'd like.
+ */
+
+/*****************************************************************************/
+/* Don't change anything that follows unless you know what you're doing. */
+/*****************************************************************************/
+
+/*
+ * Configuration for the scaling power table. Ignoring denormals, you
+ * should have 2**EXP_TABLE_SIZE >= LDBL_MAX_EXP >= 2**(EXP_TABLE_SIZE-1).
+ * The minimum for standard C is 6. For IEEE 8bit doubles, 9 suffices.
+ * For long doubles on i386, use 13.
+ */
+#define EXP_TABLE_SIZE 13
+
+/*
+ * Set this to the maximum number of digits you want converted.
+ * Conversion is done in blocks of DIGITS_PER_BLOCK (9 by default) digits.
+ * (20) 17 digits suffices to uniquely determine a (long) double on i386.
+ */
+#define MAX_DIGITS 20
+
+/*
+ * Set this to the smallest integer type capable of storing a pointer.
+ */
+#define INT_OR_PTR int
+
+/*
+ * This is really only used to check for infinities. The macro produces
+ * smaller code for i386 and, since this is tested before any floating point
+ * calculations, it doesn't appear to suffer from the excess precision problem
+ * caused by the FPU that strtod had. If it causes problems, call the function
+ * and compile zoicheck.c with -ffloat-store.
+ */
+#define _zero_or_inf_check(x) ( x == (x/4) )
+
+/*
+ * Fairly portable nan check. Bitwise for i386 generated larger code.
+ * If you have a better version, comment this out.
+ */
+#define isnan(x) (x != x)
+
+/*****************************************************************************/
+/* Don't change anything that follows peroid!!! ;-) */
+/*****************************************************************************/
+
+#include <float.h>
+
+/*****************************************************************************/
+
+/*
+ * Set things up for the scaling power table.
+ */
+
+#if EXP_TABLE_SIZE < 6
+#error EXP_TABLE_SIZE should be at least 6 to comply with standards
+#endif
+
+#define EXP_TABLE_MAX (1U<<(EXP_TABLE_SIZE-1))
+
+/*
+ * Only bother checking if this is too small.
+ */
+
+#if LDBL_MAX_10_EXP/2 > EXP_TABLE_MAX
+#error larger EXP_TABLE_SIZE needed
+#endif
+
+/*
+ * With 32 bit ints, we can get 9 digits per block.
+ */
+#define DIGITS_PER_BLOCK 9
+
+#if UINT_MAX >= 4294967295UL
+#define DIGIT_BLOCK_TYPE int
+#define DB_FMT "%.*d"
+#elif LONG_MAX >= 4294967295UL
+#define DIGIT_BLOCK_TYPE long
+#define DB_FMT "%.*ld"
+#else
+#error need at least 32 bit longs
+#endif
+
+/* Maximum number of calls to fnprintf to output double. */
+#define MAX_CALLS 8
+
+/*****************************************************************************/
+
+#define NUM_DIGIT_BLOCKS ((MAX_DIGITS+DIGITS_PER_BLOCK-1)/DIGITS_PER_BLOCK)
+
+/* extra space for '-', '.', 'e+###', and nul */
+#define BUF_SIZE ( 3 + NUM_DIGIT_BLOCKS * DIGITS_PER_BLOCK )
+/*****************************************************************************/
+
+static const char *fmts[] = {
+ "%0*d", "%.*s", ".", "inf", "INF", "nan", "NAN", "%*s"
+};
+
+/*****************************************************************************/
+
+size_t _dtostr(FILE * fp, long double x, struct printf_info *info)
+{
+ long double exp_table[EXP_TABLE_SIZE];
+ long double p10;
+ DIGIT_BLOCK_TYPE digit_block; /* int of at least 32 bits */
+ int i, j;
+ int round, o_exp;
+ int exp, exp_neg;
+ int width, preci;
+ char *s;
+ char *e;
+ char buf[BUF_SIZE];
+ INT_OR_PTR pc_fwi[2*MAX_CALLS];
+ INT_OR_PTR *ppc;
+ char exp_buf[8];
+ char drvr[8];
+ char *pdrvr;
+ int npc;
+ int cnt;
+ char sign_str[2];
+ char o_mode;
+ char mode;
+
+ /* check that INT_OR_PTR is sufficiently large */
+ assert( sizeof(INT_OR_PTR) == sizeof(char *) );
+
+ width = info->width;
+ preci = info->prec;
+ mode = info->spec;
+ if (mode == 'a') {
+ mode = 'g'; /* TODO -- fix */
+ }
+ if (mode == 'A') {
+ mode = 'G'; /* TODO -- fix */
+ }
+
+ if (preci < 0) {
+ preci = 6;
+ }
+
+ *sign_str = '\0';
+ if (PRINT_INFO_FLAG_VAL(info,showsign)) {
+ *sign_str = '+';
+ } else if (PRINT_INFO_FLAG_VAL(info,space)) {
+ *sign_str = ' ';
+ }
+/* *sign_str = flag[FLAG_PLUS]; */
+ *(sign_str+1) = 0;
+ if (isnan(x)) { /* nan check */
+ pdrvr = drvr + 1;
+ *pdrvr++ = 5 + (mode < 'a');
+ pc_fwi[2] = 3;
+ info->pad = ' ';
+/* flag[FLAG_0_PAD] = 0; */
+ goto EXIT_SPECIAL;
+ }
+
+ if (x == 0) { /* handle 0 now to avoid false positive */
+ exp = -1;
+ goto GENERATE_DIGITS;
+ }
+
+ if (x < 0) { /* convert negatives to positives */
+ *sign_str = '-';
+ x = -x;
+ }
+
+ if (_zero_or_inf_check(x)) { /* must be inf since zero handled above */
+ pdrvr = drvr + 1;
+ *pdrvr++ = 3 + + (mode < 'a');
+ pc_fwi[2] = 3;
+ info->pad = ' ';
+/* flag[FLAG_0_PAD] = 0; */
+ goto EXIT_SPECIAL;
+ }
+
+ /* need to build the scaling table */
+ for (i = 0, p10 = 10 ; i < EXP_TABLE_SIZE ; i++) {
+ exp_table[i] = p10;
+ p10 *= p10;
+ }
+
+ exp_neg = 0;
+ if (x < 1e8) { /* do we need to scale up or down? */
+ exp_neg = 1;
+ }
+
+ exp = DIGITS_PER_BLOCK - 1;
+
+ i = EXP_TABLE_SIZE;
+ j = EXP_TABLE_MAX;
+ while ( i-- ) { /* scale x such that 1e8 <= x < 1e9 */
+ if (exp_neg) {
+ if (x * exp_table[i] < 1e9) {
+ x *= exp_table[i];
+ exp -= j;
+ }
+ } else {
+ if (x / exp_table[i] >= 1e8) {
+ x /= exp_table[i];
+ exp += j;
+ }
+ }
+ j >>= 1;
+ }
+ if (x >= 1e9) { /* handle bad rounding case */
+ x /= 10;
+ ++exp;
+ }
+ assert(x < 1e9);
+
+ GENERATE_DIGITS:
+ s = buf + 2; /* leave space for '\0' and '0' */
+#if 1
+#define ONE_E_NINE 1000000000L
+#else
+#define ONE_E_NINE 1e9
+#endif
+ for (i = 0 ; i < NUM_DIGIT_BLOCKS ; ++i ) {
+ digit_block = (DIGIT_BLOCK_TYPE) x;
+ x = (x - digit_block) * ONE_E_NINE;
+ s += sprintf(s, DB_FMT, DIGITS_PER_BLOCK, digit_block);
+ }
+
+ /*************************************************************************/
+
+ *exp_buf = 'e';
+ if (mode < 'a') {
+ *exp_buf = 'E';
+ mode += ('a' - 'A');
+ }
+
+ o_mode = mode;
+
+ round = preci;
+
+ if ((mode == 'g') && (round > 0)){
+ --round;
+ }
+
+ if (mode == 'f') {
+ round += exp;
+ }
+
+ s = buf;
+ *s++ = 0; /* terminator for rounding and 0-triming */
+ *s = '0'; /* space to round */
+
+ i = 0;
+ e = s + MAX_DIGITS + 1;
+ if (round < MAX_DIGITS) {
+ e = s + round + 2;
+ if (*e >= '5') {
+ i = 1;
+ }
+ }
+
+ do { /* handle rounding and trim trailing 0s */
+ *--e += i; /* add the carry */
+ } while ((*e == '0') || (*e > '9'));
+
+ o_exp = exp;
+ if (e <= s) { /* we carried into extra digit */
+ ++o_exp;
+ e = s; /* needed if all 0s */
+ } else {
+ ++s;
+ }
+ *++e = 0; /* ending nul char */
+
+ if ((mode == 'g') && ((o_exp >= -4) && (o_exp <= round))) {
+ mode = 'f';
+ }
+
+ exp = o_exp;
+ if (mode != 'f') {
+ o_exp = 0;
+ }
+
+ if (o_exp < 0) {
+ *--s = '0'; /* fake the first digit */
+ }
+
+ pdrvr = drvr+1;
+ ppc = pc_fwi+2;
+
+ *pdrvr++ = 0;
+ *ppc++ = 1;
+ *ppc++ = (INT_OR_PTR)(*s++ - '0');
+
+ i = e - s; /* total digits */
+ if (o_exp >= 0) {
+ if (o_exp >= i) { /* all digit(s) left of decimal */
+ *pdrvr++ = 1;
+ *ppc++ = i;
+ *ppc++ = (INT_OR_PTR)(s);
+ o_exp -= i;
+ i = 0;
+ if (o_exp>0) { /* have 0s left of decimal */
+ *pdrvr++ = 0;
+ *ppc++ = o_exp;
+ *ppc++ = 0;
+ }
+ } else if (o_exp > 0) { /* decimal between digits */
+ *pdrvr++ = 1;
+ *ppc++ = o_exp;
+ *ppc++ = (INT_OR_PTR)(s);
+ s += o_exp;
+ i -= o_exp;
+ }
+ o_exp = -1;
+ }
+
+ if (PRINT_INFO_FLAG_VAL(info,alt)
+/* flag[FLAG_HASH] */
+ || (i) || ((o_mode != 'g') && (preci > 0))) {
+ *pdrvr++ = 2; /* need decimal */
+ *ppc++ = 1; /* needed for width calc */
+ ppc++;
+ }
+
+ if (++o_exp < 0) { /* have 0s right of decimal */
+ *pdrvr++ = 0;
+ *ppc++ = -o_exp;
+ *ppc++ = 0;
+ }
+ if (i) { /* have digit(s) right of decimal */
+ *pdrvr++ = 1;
+ *ppc++ = i;
+ *ppc++ = (INT_OR_PTR)(s);
+ }
+
+ if (o_mode != 'g') {
+ i -= o_exp;
+ if (i < preci) { /* have 0s right of digits */
+ i = preci - i;
+ *pdrvr++ = 0;
+ *ppc++ = i;
+ *ppc++ = 0;
+ }
+ }
+
+ /* build exponent string */
+ if (mode != 'f') {
+ *pdrvr++ = 1;
+ *ppc++ = sprintf(exp_buf,"%c%+.2d", *exp_buf, exp);
+ *ppc++ = (INT_OR_PTR) exp_buf;
+ }
+
+ EXIT_SPECIAL:
+ npc = pdrvr - drvr;
+ ppc = pc_fwi + 2;
+ for (i=1 ; i< npc ; i++) {
+ width -= *(ppc++);
+ ppc++;
+ }
+ i = 0;
+ if (*sign_str) {
+ i = 1;
+ }
+ width -= i;
+ if (width <= 0) {
+ width = 0;
+ } else {
+ if (PRINT_INFO_FLAG_VAL(info,left)) { /* padding on right */
+/* flag[FLAG_MINUS_LJUSTIFY] */
+ ++npc;
+ *pdrvr++ = 7;
+ *ppc = width;
+ *++ppc = (INT_OR_PTR)("");
+ width = 0;
+ } else if (info->pad == '0') { /* 0 padding */
+/* (flag[FLAG_0_PAD] == '0') */
+ pc_fwi[2] += width;
+ width = 0;
+ }
+ }
+ *drvr = 7;
+ ppc = pc_fwi;
+ *ppc++ = width + i;
+ *ppc = (INT_OR_PTR) sign_str;
+
+ pdrvr = drvr;
+ ppc = pc_fwi;
+ cnt = 0;
+ for (i=0 ; i<npc ; i++) {
+#if 1
+ fprintf(fp, fmts[(int)(*pdrvr++)], (INT_OR_PTR)(*(ppc)),
+ (INT_OR_PTR)(*(ppc+1)));
+#else
+ j = fprintf(fp, fmts[(int)(*pdrvr++)], (INT_OR_PTR)(*(ppc)),
+ (INT_OR_PTR)(*(ppc+1)));
+ assert(j == *ppc);
+#endif
+/* if (size > *ppc) { */
+/* size -= *ppc; */
+/* } */
+ cnt += *ppc; /* to avoid problems if j == -1 */
+ ppc += 2;
+ }
+
+ return cnt;
+}
+#endif
+/**********************************************************************/
+#ifdef L__store_inttype
+/* TODO -- right now, assumes intmax_t is either long or long long */
+
+/* We assume int may be short or long, but short and long are different. */
+
+void _store_inttype(register void *dest, int desttype, uintmax_t val)
+{
+ if (desttype == __PA_FLAG_CHAR) { /* assume char not int */
+ *((unsigned char *) dest) = val;
+ return;
+ }
+#if defined(LLONG_MAX) && (LONG_MAX != LLONG_MAX)
+ if (desttype == PA_FLAG_LONG_LONG) {
+ *((unsigned long long int *) dest) = val;
+ return;
+ }
+#endif /* LLONG_MAX */
+#if SHRT_MAX != INT_MAX
+ if (desttype == PA_FLAG_SHORT) {
+ *((unsigned short int *) dest) = val;
+ return;
+ }
+#endif /* SHRT_MAX */
+#if LONG_MAX != INT_MAX
+ if (desttype == PA_FLAG_LONG) {
+ *((unsigned long int *) dest) = val;
+ return;
+ }
+#endif /* LONG_MAX */
+
+ *((unsigned int *) dest) = val;
+}
+
+#endif
+/**********************************************************************/
+#ifdef L__load_inttype
+
+extern uintmax_t _load_inttype(int desttype, const void *src, int uflag)
+{
+ if (uflag >= 0) { /* unsigned */
+#if LONG_MAX != INT_MAX
+ if (desttype & (PA_FLAG_LONG|PA_FLAG_LONG_LONG)) {
+#ifdef LLONG_MAX
+ if (desttype == PA_FLAG_LONG_LONG) {
+ return *((unsigned long long int *) src);
+ }
+#endif
+ return *((unsigned long int *) src);
+ }
+#else /* LONG_MAX != INT_MAX */
+#ifdef LLONG_MAX
+ if (desttype & PA_FLAG_LONG_LONG) {
+ return *((unsigned long long int *) src);
+ }
+#endif
+#endif /* LONG_MAX != INT_MAX */
+ {
+ unsigned int x;
+ x = *((unsigned int *) src);
+ if (desttype == __PA_FLAG_CHAR) x = (unsigned char) x;
+#if SHRT_MAX != INT_MAX
+ if (desttype == PA_FLAG_SHORT) x = (unsigned short int) x;
+#endif
+ return x;
+ }
+ } else { /* signed */
+#if LONG_MAX != INT_MAX
+ if (desttype & (PA_FLAG_LONG|PA_FLAG_LONG_LONG)) {
+#ifdef LLONG_MAX
+ if (destsize == PA_FLAG_LONG_LONG) {
+ return *((long long int *) src);
+ }
+#endif
+ return *((long int *) src);
+ }
+#else /* LONG_MAX != INT_MAX */
+#ifdef LLONG_MAX
+ if (desttype & PA_FLAG_LONG_LONG) {
+ return *((long long int *) src);
+ }
+#endif
+#endif /* LONG_MAX != INT_MAX */
+ {
+ int x;
+ x = *((int *) src);
+ if (desttype == __PA_FLAG_CHAR) x = (char) x;
+#if SHRT_MAX != INT_MAX
+ if (desttype == PA_FLAG_SHORT) x = (short int) x;
+#endif
+ return x;
+ }
+ }
+}
+
+#endif
+/**********************************************************************/