diff options
author | Peter S. Mazinger <ps.m@gmx.net> | 2006-02-13 07:36:40 +0000 |
---|---|---|
committer | Peter S. Mazinger <ps.m@gmx.net> | 2006-02-13 07:36:40 +0000 |
commit | caed8420ef5be27f4b6e2b07a39e21642e6d1ceb (patch) | |
tree | 965f11d1f2103eeda2d09448bbbcbc1848a37855 /libc/stdio/_vfprintf.c | |
parent | 5ba9a00a29564ec74925f9acbba7753b004facf0 (diff) |
More renames for IMA
Diffstat (limited to 'libc/stdio/_vfprintf.c')
-rw-r--r-- | libc/stdio/_vfprintf.c | 1951 |
1 files changed, 1951 insertions, 0 deletions
diff --git a/libc/stdio/_vfprintf.c b/libc/stdio/_vfprintf.c new file mode 100644 index 000000000..a32960f2e --- /dev/null +++ b/libc/stdio/_vfprintf.c @@ -0,0 +1,1951 @@ +/* Copyright (C) 2002-2004 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! */ + + +/* April 1, 2002 + * Initialize thread locks for fake files in vsnprintf and vdprintf. + * reported by Erik Andersen (andersen@codepoet.com) + * Fix an arg promotion handling bug in _do_one_spec for %c. + * reported by Ilguiz Latypov <ilatypov@superbt.com> + * + * May 10, 2002 + * Remove __isdigit and use new ctype.h version. + * Add conditional setting of QUAL_CHARS for size_t and ptrdiff_t. + * + * Aug 16, 2002 + * Fix two problems that showed up with the python 2.2.1 tests; one + * involving %o and one involving %f. + * + * Oct 28, 2002 + * Fix a problem in vasprintf (reported by vodz a while back) when built + * without custom stream support. In that case, it is necessary to do + * a va_copy. + * Make sure each va_copy has a matching va_end, as required by C99. + * + * Nov 4, 2002 + * Add locale-specific grouping support for integer decimal conversion. + * Add locale-specific decimal point support for floating point conversion. + * Note: grouping will have to wait for _dtostr() rewrite. + * Add printf wchar support for %lc (%C) and %ls (%S). + * Require printf format strings to be valid multibyte strings beginning and + * ending in their initial shift state, as per the stds. + * + * Nov 21, 2002 + * Add *wprintf functions. Currently they don't support floating point + * conversions. That will wait until the rewrite of _dtostr. + * + * Aug 1, 2003 + * Optional hexadecimal float notation support for %a/%A. + * Floating point output now works for *wprintf. + * Support for glibc locale-specific digit grouping for floats. + * Misc bug fixes. + * + * Aug 31, 2003 + * Fix precision bug for %g conversion specifier when using %f style. + * + * Sep 5, 2003 + * Implement *s*scanf for the non-buffered stdio case with old_vfprintf. + * + * Sep 23, 2003 + * vfprintf was not always checking for narrow stream orientation. + */ + +/* TODO: + * + * Should we validate that *printf format strings are valid multibyte + * strings in the current locale? ANSI/ISO C99 seems to imply this + * and Plauger's printf implementation in his Standard C Library book + * treats this as an error. + */ + +#define _ISOC99_SOURCE /* for ULLONG primarily... */ +#include <features.h> +#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> +#include <locale.h> + +#define __PRINTF_INFO_NO_BITFIELD +#include <printf.h> + +#ifdef __UCLIBC_HAS_THREADS__ +#include <stdio_ext.h> +#include <pthread.h> +#endif /* __UCLIBC_HAS_THREADS__ */ + +#ifdef __UCLIBC_HAS_WCHAR__ +#include <wchar.h> +#endif /* __UCLIBC_HAS_WCHAR__ */ + +#include <bits/uClibc_uintmaxtostr.h> +#include <bits/uClibc_va_copy.h> + +libc_hidden_proto(memcpy) +libc_hidden_proto(memset) +libc_hidden_proto(strlen) +libc_hidden_proto(strnlen) +libc_hidden_proto(__glibc_strerror_r) +libc_hidden_proto(fputs_unlocked) +libc_hidden_proto(abort) +#ifdef __UCLIBC_HAS_WCHAR__ +libc_hidden_proto(wcslen) +libc_hidden_proto(wcsnlen) +libc_hidden_proto(mbsrtowcs) +libc_hidden_proto(wcsrtombs) +libc_hidden_proto(btowc) +libc_hidden_proto(wcrtomb) +libc_hidden_proto(fputws) +#endif + +/* Some older or broken gcc toolchains define LONG_LONG_MAX but not + * LLONG_MAX. Since LLONG_MAX is part of the standard, that's what + * we use. So complain if we do not have it but should. + */ +#if !defined(LLONG_MAX) && defined(LONG_LONG_MAX) +#error Apparently, LONG_LONG_MAX is defined but LLONG_MAX is not. You need to fix your toolchain headers to support the standard macros for (unsigned) long long. +#endif + +/**********************************************************************/ +/* These provide some control over printf's feature set */ + +/* This is undefined below depeding on uClibc's configuration. */ +#define __STDIO_PRINTF_FLOAT 1 + +/* Now controlled by uClibc_stdio.h. */ +/* #define __UCLIBC_HAS_PRINTF_M_SPEC__ */ + + +/**********************************************************************/ + +#if defined(__UCLIBC__) && !defined(__UCLIBC_HAS_FLOATS__) +#undef __STDIO_PRINTF_FLOAT +#endif + +#ifdef __BCC__ +#undef __STDIO_PRINTF_FLOAT +#endif + +#ifdef __STDIO_PRINTF_FLOAT +#include <float.h> +#include <bits/uClibc_fpmax.h> +#else /* __STDIO_PRINTF_FLOAT */ +#undef L__fpmaxtostr +#endif /* __STDIO_PRINTF_FLOAT */ + + +#undef __STDIO_HAS_VSNPRINTF +#if defined(__STDIO_BUFFERS) || defined(__USE_OLD_VFPRINTF__) || defined(__UCLIBC_HAS_GLIBC_CUSTOM_STREAMS__) +#define __STDIO_HAS_VSNPRINTF 1 +#endif + +/**********************************************************************/ + +/* Now controlled by uClibc_stdio.h. */ +/* #define __UCLIBC_HAS_GLIBC_CUSTOM_PRINTF__ */ + +/* TODO -- move these to a configuration section? */ +#define MAX_FIELD_WIDTH 4095 + +#ifdef __UCLIBC_MJN3_ONLY__ +#ifdef L_register_printf_function +/* emit only once */ +#warning WISHLIST: Make MAX_USER_SPEC configurable? +#warning WISHLIST: Make MAX_ARGS_PER_SPEC configurable? +#endif +#endif /* __UCLIBC_MJN3_ONLY__ */ + +#ifdef __UCLIBC_HAS_GLIBC_CUSTOM_PRINTF__ + +#define MAX_USER_SPEC 10 +#define MAX_ARGS_PER_SPEC 5 + +#else /* __UCLIBC_HAS_GLIBC_CUSTOM_PRINTF__ */ + +#undef MAX_USER_SPEC +#define MAX_ARGS_PER_SPEC 1 + +#endif /* __UCLIBC_HAS_GLIBC_CUSTOM_PRINTF__ */ + +#if MAX_ARGS_PER_SPEC < 1 +#error MAX_ARGS_PER_SPEC < 1! +#undef MAX_ARGS_PER_SPEC +#define MAX_ARGS_PER_SPEC 1 +#endif + +#if defined(NL_ARGMAX) && (NL_ARGMAX < 9) +#error NL_ARGMAX < 9! +#endif + +#if defined(NL_ARGMAX) && (NL_ARGMAX >= (MAX_ARGS_PER_SPEC + 2)) +#define MAX_ARGS NL_ARGMAX +#else +/* N for spec itself, plus 1 each for width and precision */ +#define MAX_ARGS (MAX_ARGS_PER_SPEC + 2) +#endif + +/**********************************************************************/ + +#define __PA_FLAG_INTMASK \ + (__PA_FLAG_CHAR|PA_FLAG_SHORT|__PA_FLAG_INT|PA_FLAG_LONG|PA_FLAG_LONG_LONG) + +#ifdef __UCLIBC_HAS_GLIBC_CUSTOM_PRINTF__ +extern printf_function _custom_printf_handler[MAX_USER_SPEC] attribute_hidden; +extern printf_arginfo_function *_custom_printf_arginfo[MAX_USER_SPEC] attribute_hidden; +extern char *_custom_printf_spec attribute_hidden; +#endif /* __UCLIBC_HAS_GLIBC_CUSTOM_PRINTF__ */ + +/**********************************************************************/ + +#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 */ + FLAG_WIDESTREAM = 0x80 +}; + +/**********************************************************************/ + +/* 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, +#ifdef __UCLIBC_HAS_PRINTF_M_SPEC__ + CONV_m, +#endif + 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. */ +/* #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 */ + +#ifdef PDS +#error PDS already defined! +#endif +#ifdef SS +#error SS already defined! +#endif +#ifdef IMS +#error IMS already defined! +#endif + +#if PTRDIFF_MAX == INT_MAX +#define PDS 0 +#elif PTRDIFF_MAX == LONG_MAX +#define PDS 4 +#elif defined(LLONG_MAX) && (PTRDIFF_MAX == LLONG_MAX) +#define PDS 8 +#else +#error fix QUAL_CHARS ptrdiff_t entry 't'! +#endif + +#if SIZE_MAX == UINT_MAX +#define SS 0 +#elif SIZE_MAX == ULONG_MAX +#define SS 4 +#elif defined(LLONG_MAX) && (SIZE_MAX == ULLONG_MAX) +#define SS 8 +#else +#error fix QUAL_CHARS size_t entries 'z', 'Z'! +#endif + +#if INTMAX_MAX == INT_MAX +#define IMS 0 +#elif INTMAX_MAX == LONG_MAX +#define IMS 4 +#elif defined(LLONG_MAX) && (INTMAX_MAX == LLONG_MAX) +#define IMS 8 +#else +#error fix QUAL_CHARS intmax_t entry 'j'! +#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, IMS, SS, PDS, 8, SS, 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 +#ifdef __STDIO_PRINTF_FLOAT + double d; + long double ld; +#endif /* __STDIO_PRINTF_FLOAT */ + 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; +#ifdef NL_ARGMAX + int maxposarg; /* > 0 if args are positional, 0 if not, -1 if unknown */ +#endif /* NL_ARGMAX */ + 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 +/* if defined(NL_ARGMAX) || defined(__UCLIBC_HAS_GLIBC_CUSTOM_PRINTF__) */ + /* 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. */ + +#ifdef __STDIO_PRINTF_FLOAT +typedef size_t (__fp_outfunc_t)(FILE *fp, intptr_t type, intptr_t len, + intptr_t buf); + +extern ssize_t _fpmaxtostr(FILE * fp, __fpmax_t x, struct printf_info *info, + __fp_outfunc_t fp_outfunc) attribute_hidden; +#endif + +extern int _ppfs_init(ppfs_t *ppfs, const char *fmt0) attribute_hidden; /* validates */ +extern void _ppfs_prepargs(ppfs_t *ppfs, va_list arg) attribute_hidden; /* sets posargptrs */ +extern void _ppfs_setargs(ppfs_t *ppfs) attribute_hidden; /* sets argptrs for current spec */ +extern int _ppfs_parsespec(ppfs_t *ppfs) attribute_hidden; /* parses specifier */ + +extern void _store_inttype(void *dest, int desttype, uintmax_t val) attribute_hidden; +extern uintmax_t _load_inttype(int desttype, const void *src, int uflag) attribute_hidden; + +/**********************************************************************/ +#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, register int *argtypes) +{ + ppfs_t ppfs; + size_t i; + size_t count = 0; + + if (_ppfs_init(&ppfs, template) >= 0) { +#ifdef NL_ARGMAX + 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. */ +#endif /* NL_ARGMAX */ + 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; + } + } +#ifdef NL_ARGMAX + } +#endif /* NL_ARGMAX */ + } + + return count; +} + +#endif +/**********************************************************************/ +#ifdef L__ppfs_init + +#ifdef __UCLIBC_HAS_LOCALE__ +libc_hidden_proto(__global_locale) +#endif + +int attribute_hidden _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???? */ +#ifdef NL_ARGMAX + --ppfs->maxposarg; /* set to -1 */ +#endif /* NL_ARGMAX */ + ppfs->fmtpos = fmt0; +#ifdef __UCLIBC_MJN3_ONLY__ +#warning TODO: Make checking of the format string in C locale an option. +#endif +#ifdef __UCLIBC_HAS_LOCALE__ + /* To support old programs, don't check mb validity if in C locale. */ + if (((__UCLIBC_CURLOCALE_DATA).encoding) != __ctype_encoding_7_bit) { + /* ANSI/ISO C99 requires format string to be a valid multibyte string + * beginning and ending in its initial shift state. */ + static const char invalid_mbs[] = "Invalid multibyte format string."; + mbstate_t mbstate; + const char *p; + mbstate.__mask = 0; /* Initialize the mbstate. */ + p = fmt0; + if (mbsrtowcs(NULL, &p, SIZE_MAX, &mbstate) == ((size_t)(-1))) { + ppfs->fmtpos = invalid_mbs; + return -1; + } + } +#endif /* __UCLIBC_HAS_LOCALE__ */ + /* 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 */ + } + +#ifdef NL_MAX_ARG + /* 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; + } + } +#endif /* NL_MAX_ARG */ + + return 0; +} +#endif +/**********************************************************************/ +#ifdef L__ppfs_prepargs +void attribute_hidden _ppfs_prepargs(register ppfs_t *ppfs, va_list arg) +{ + int i; + + va_copy(ppfs->arg, arg); + +#ifdef NL_ARGMAX + 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 /* NL_ARGMAX */ +} +#endif +/**********************************************************************/ +#ifdef L__ppfs_setargs + +void attribute_hidden _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; + +#ifdef NL_ARGMAX + if (ppfs->maxposarg == 0) { /* initing for or no pos args */ +#endif /* NL_ARGMAX */ + 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; +#ifdef __STDIO_PRINTF_FLOAT + /* 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 /* __STDIO_PRINTF_FLOAT */ + case PA_DOUBLE: + case (PA_DOUBLE|PA_FLAG_LONG_DOUBLE): + assert(0); + continue; +#endif /* __STDIO_PRINTF_FLOAT */ + 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; + } +#ifdef NL_ARGMAX + } 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); + } + } +#endif /* NL_ARGMAX */ + + /* 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 + +#ifdef __UCLIBC_HAS_XLOCALE__ +libc_hidden_proto(__ctype_b_loc) +#else +libc_hidden_proto(__ctype_b) +#endif + +/* 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, +#ifdef __STDIO_PRINTF_FLOAT + /* PA_FLOAT, */ + PA_DOUBLE, + PA_DOUBLE|PA_FLAG_LONG_DOUBLE, +#endif /* __STDIO_PRINTF_FLOAT */ +}; + +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), +#ifdef __STDIO_PRINTF_FLOAT + /* PROMOTED_SIZE_OF(float), */ + PROMOTED_SIZE_OF(double), + PROMOTED_SIZE_OF(long double), +#endif /* __STDIO_PRINTF_FLOAT */ +}; + +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 + +#ifdef __UCLIBC_HAS_GLIBC_CUSTOM_PRINTF__ +/* TODO - do this differently? */ +static char _bss_custom_printf_spec[MAX_USER_SPEC]; /* 0-init'd for us. */ + +attribute_hidden char *_custom_printf_spec = _bss_custom_printf_spec; +attribute_hidden printf_arginfo_function *_custom_printf_arginfo[MAX_USER_SPEC]; +attribute_hidden printf_function _custom_printf_handler[MAX_USER_SPEC]; +#endif /* __UCLIBC_HAS_GLIBC_CUSTOM_PRINTF__ */ + +int attribute_hidden _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; +#ifdef NL_ARGMAX + int maxposarg; +#endif /* NL_ARGMAX */ + int p_m_spec_chars; + int n; + int argtype[MAX_ARGS_PER_SPEC+2]; + int argnumber[3]; /* width, precision, 1st data arg */ + 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; +#ifdef __UCLIBC_HAS_WCHAR__ + char buf[32]; +#endif /* __UCLIBC_HAS_WCHAR__ */ + + /* WIDE note: we can test against '%' here since we don't allow */ + /* WIDE note: other mappings of '%' in the wide char set. */ + preci = -1; + argnumber[0] = 0; + argnumber[1] = 0; + argtype[0] = __PA_NOARG; + argtype[1] = __PA_NOARG; +#ifdef NL_ARGMAX + maxposarg = ppfs->maxposarg; +#endif /* NL_ARGMAX */ + +#ifdef __UCLIBC_HAS_WCHAR__ + /* This is somewhat lame, but saves a lot of code. If we're dealing with + * a wide stream, that means the format is a wchar string. So, copy it + * char-by-char into a normal char buffer for processing. Make the buffer + * (buf) big enough so that any reasonable format specifier will fit. + * While there a legal specifiers that won't, the all involve duplicate + * flags or outrageous field widths/precisions. */ + width = dpoint = 0; + if ((flags = ppfs->info._flags & FLAG_WIDESTREAM) == 0) { + fmt = ppfs->fmtpos; + } else { + fmt = buf + 1; + i = 0; + do { + if ((buf[i] = (char) (((wchar_t *) ppfs->fmtpos)[i-1])) + != (((wchar_t *) ppfs->fmtpos)[i-1]) + ) { + return -1; + } + } while (buf[i++]); + buf[sizeof(buf)-1] = 0; + } +#else /* __UCLIBC_HAS_WCHAR__ */ + width = flags = dpoint = 0; + fmt = ppfs->fmtpos; +#endif /* __UCLIBC_HAS_WCHAR__ */ + + 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 */ + +#ifdef NL_ARGMAX + 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) { +#ifdef __UCLIBC_HAS_PRINTF_M_SPEC__ +#ifdef __UCLIBC_MJN3_ONLY__ +#warning TODO: Support prec and width for %m when positional args used + /* Actually, positional arg processing will fail in general + * for specifiers that don't require an arg. */ +#endif /* __UCLIBC_MJN3_ONLY__ */ + if (*fmt == 'm') { + goto PREC_WIDTH; + } +#endif /* __UCLIBC_HAS_PRINTF_M_SPEC__ */ + 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. */ + } +#else /* NL_ARGMAX */ + if (*fmt == '$') { /* Positional spec. */ + return -1; + } + + if ((fmt > p) && (*p != '0')) { + goto PREC_WIDTH; + } + + fmt = p; /* Back up for possible '0's flag. */ + /* Now fall through to check flags. */ +#endif /* NL_ARGMAX */ + + 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. */ +#ifdef NL_ARGMAX + if (maxposarg) { + if ((*fmt++ != '$') || (i <= 0)) { + /* Using pos args and no $ or invalid arg number. */ + return -1; + } + argnumber[-dpoint] = i; + } else +#endif /* NL_ARGMAX */ + 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 */ + } + + ppfs->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 __UCLIBC_HAS_PRINTF_M_SPEC__ + if (*fmt == 'm') { + ppfs->conv_num = CONV_m; + ppfs->num_data_args = 0; + goto DONE; + } +#endif +#ifdef __UCLIBC_HAS_GLIBC_CUSTOM_PRINTF__ + + /* Handle custom arg -- WARNING -- overwrites p!!! */ + ppfs->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 /* __UCLIBC_HAS_GLIBC_CUSTOM_PRINTF__ */ + /* Otherwise error. */ + return -1; + } + +#if defined(__UCLIBC_HAS_GLIBC_CUSTOM_PRINTF__) || defined(__UCLIBC_HAS_PRINTF_M_SPEC__) + DONE: +#endif + +#ifdef NL_ARGMAX + 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 { +#endif /* NL_ARGMAX */ + ppfs->argnumber[2] = 1; + memcpy(ppfs->argtype, argtype + 2, ppfs->num_data_args * sizeof(int)); |