📄 vfprintf.c
字号:
/* 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... */#define _GNU_SOURCE#define _STDIO_UTILITY /* We're using _uintmaxtostr. */#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>/* 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/**********************************************************************//* Deal with pre-C99 compilers. */#ifndef va_copy#ifdef __va_copy#define va_copy(A,B) __va_copy(A,B)#else /* TODO -- maybe create a bits/vacopy.h for arch specific versions * to ensure we get the right behavior? Either that or fall back * on the portable (but costly in size) method of using a va_list *. * That means a pointer derefs in the va_arg() invocations... */#warning Neither va_copy (C99/SUSv3) or __va_copy is defined. Using a simple copy instead. But you should really check that this is appropriate... /* the glibc manual suggests that this will usually suffice when __va_copy doesn't exist. */#define va_copy(A,B) A = B#endif#endif /* va_copy *//**********************************************************************/#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];extern printf_arginfo_function *_custom_printf_arginfo[MAX_USER_SPEC];extern char *_custom_printf_spec;#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))))#elsetypedef 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))#endiftypedef 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_FLOATtypedef void (__fp_outfunc_t)(FILE *fp, intptr_t type, intptr_t len, intptr_t buf);extern size_t _fpmaxtostr(FILE * fp, __fpmax_t x, struct printf_info *info, __fp_outfunc_t fp_outfunc);#endifextern 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 */extern void _store_inttype(void *dest, int desttype, uintmax_t val);extern uintmax_t _load_inttype(int desttype, const void *src, int uflag);/**********************************************************************/#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 */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -