1 /* Copyright (C) 2002-2004 Manuel Novoa III
2 * My stdio library for linux and (soon) elks.
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, see
16 * <http://www.gnu.org/licenses/>.
19 /* This code needs a lot of clean up. Some of that is on hold until uClibc
20 * gets a better configuration system (on Erik's todo list).
21 * The other cleanup will take place during the implementation/integration of
22 * the wide char (un)formatted i/o functions which I'm currently working on.
25 /* ATTENTION! ATTENTION! ATTENTION! ATTENTION! ATTENTION!
27 * This code is currently under development. Also, I plan to port
28 * it to elks which is a 16-bit environment with a fairly limited
29 * compiler. Therefore, please refrain from modifying this code
30 * and, instead, pass any bug-fixes, etc. to me. Thanks. Manuel
32 * ATTENTION! ATTENTION! ATTENTION! ATTENTION! ATTENTION! */
36 * Initialize thread locks for fake files in vsnprintf and vdprintf.
37 * reported by Erik Andersen (andersen@codepoet.com)
38 * Fix an arg promotion handling bug in _do_one_spec for %c.
39 * reported by Ilguiz Latypov <ilatypov@superbt.com>
42 * Remove __isdigit and use new ctype.h version.
43 * Add conditional setting of QUAL_CHARS for size_t and ptrdiff_t.
46 * Fix two problems that showed up with the python 2.2.1 tests; one
47 * involving %o and one involving %f.
50 * Fix a problem in vasprintf (reported by vodz a while back) when built
51 * without custom stream support. In that case, it is necessary to do
53 * Make sure each va_copy has a matching va_end, as required by C99.
56 * Add locale-specific grouping support for integer decimal conversion.
57 * Add locale-specific decimal point support for floating point conversion.
58 * Note: grouping will have to wait for _dtostr() rewrite.
59 * Add printf wchar support for %lc (%C) and %ls (%S).
60 * Require printf format strings to be valid multibyte strings beginning and
61 * ending in their initial shift state, as per the stds.
64 * Add *wprintf functions. Currently they don't support floating point
65 * conversions. That will wait until the rewrite of _dtostr.
68 * Optional hexadecimal float notation support for %a/%A.
69 * Floating point output now works for *wprintf.
70 * Support for glibc locale-specific digit grouping for floats.
74 * Fix precision bug for %g conversion specifier when using %f style.
77 * Implement *s*scanf for the non-buffered stdio case with old_vfprintf.
80 * vfprintf was not always checking for narrow stream orientation.
85 * Should we validate that *printf format strings are valid multibyte
86 * strings in the current locale? ANSI/ISO C99 seems to imply this
87 * and Plauger's printf implementation in his Standard C Library book
88 * treats this as an error.
104 #ifdef __UCLIBC_HAS_THREADS__
105 # include <stdio_ext.h>
106 # include <pthread.h>
109 #ifdef __UCLIBC_HAS_WCHAR__
113 #include <bits/uClibc_uintmaxtostr.h>
114 #include <bits/uClibc_va_copy.h>
116 /* Some older or broken gcc toolchains define LONG_LONG_MAX but not
117 * LLONG_MAX. Since LLONG_MAX is part of the standard, that's what
118 * we use. So complain if we do not have it but should.
120 #if !defined(LLONG_MAX) && defined(LONG_LONG_MAX)
121 #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.
124 #include "_fpmaxtostr.h"
126 #undef __STDIO_HAS_VSNPRINTF
127 #if defined(__STDIO_BUFFERS) || defined(__USE_OLD_VFPRINTF__) || defined(__UCLIBC_HAS_GLIBC_CUSTOM_STREAMS__)
128 # define __STDIO_HAS_VSNPRINTF 1
131 /**********************************************************************/
133 #ifdef __UCLIBC_HAS_GLIBC_CUSTOM_PRINTF__
134 # define MAX_USER_SPEC 10
135 # define MAX_ARGS_PER_SPEC 5
137 # undef MAX_USER_SPEC
138 # define MAX_ARGS_PER_SPEC 1
141 #if MAX_ARGS_PER_SPEC < 1
142 # error MAX_ARGS_PER_SPEC < 1!
143 # undef MAX_ARGS_PER_SPEC
144 # define MAX_ARGS_PER_SPEC 1
147 #if defined(NL_ARGMAX) && (NL_ARGMAX < 9)
148 # error NL_ARGMAX < 9!
151 #if defined(NL_ARGMAX) && (NL_ARGMAX >= (MAX_ARGS_PER_SPEC + 2))
152 # define MAX_ARGS NL_ARGMAX
154 /* N for spec itself, plus 1 each for width and precision */
155 # define MAX_ARGS (MAX_ARGS_PER_SPEC + 2)
158 /**********************************************************************/
160 #define __PA_FLAG_INTMASK \
161 (__PA_FLAG_CHAR|PA_FLAG_SHORT|__PA_FLAG_INT|PA_FLAG_LONG|PA_FLAG_LONG_LONG)
163 #ifdef __UCLIBC_HAS_GLIBC_CUSTOM_PRINTF__
164 extern printf_function _custom_printf_handler
[MAX_USER_SPEC
] attribute_hidden
;
165 extern printf_arginfo_function
*_custom_printf_arginfo
[MAX_USER_SPEC
] attribute_hidden
;
166 extern char *_custom_printf_spec attribute_hidden
;
169 /**********************************************************************/
171 #define SPEC_FLAGS " +0-#'I"
174 FLAG_PLUS
= 0x02, /* must be 2 * FLAG_SPACE */
176 FLAG_MINUS
= 0x08, /* must be 2 * FLAG_ZERO */
178 FLAG_THOUSANDS
= 0x20,
179 FLAG_I18N
= 0x40, /* only works for d, i, u */
180 FLAG_WIDESTREAM
= 0x80
183 /**********************************************************************/
185 /* float layout 01234567890123456789 TODO: B?*/
186 #define SPEC_CHARS "npxXoudifFeEgGaACScs"
190 CONV_x
, CONV_X
, CONV_o
, CONV_u
, CONV_d
, CONV_i
,
191 CONV_f
, CONV_F
, CONV_e
, CONV_E
, CONV_g
, CONV_G
, CONV_a
, CONV_A
,
192 CONV_C
, CONV_S
, CONV_c
, CONV_s
,
193 #ifdef __UCLIBC_HAS_PRINTF_M_SPEC__
196 CONV_custom0
/* must be last */
200 #define SPEC_BASE { 16, 16, 16, 8, 10, 10, 10 }
202 #define SPEC_RANGES { CONV_n, CONV_p, CONV_i, CONV_A, \
203 CONV_C, CONV_S, CONV_c, CONV_s, CONV_custom0 }
205 #define SPEC_OR_MASK { \
206 /* n */ (PA_FLAG_PTR|PA_INT), \
207 /* p */ PA_POINTER, \
208 /* oxXudi */ PA_INT, \
209 /* fFeEgGaA */ PA_DOUBLE, \
211 /* S */ PA_WSTRING, \
216 #define SPEC_AND_MASK { \
217 /* n */ (PA_FLAG_PTR|__PA_INTMASK), \
218 /* p */ PA_POINTER, \
219 /* oxXudi */ (__PA_INTMASK), \
220 /* fFeEgGaA */ (PA_FLAG_LONG_DOUBLE|PA_DOUBLE), \
221 /* C */ (PA_WCHAR), \
222 /* S */ (PA_WSTRING), \
224 /* s */ (PA_STRING), \
227 /**********************************************************************/
229 * In order to ease translation to what arginfo and _print_info._flags expect,
230 * we map: 0:int 1:char 2:longlong 4:long 8:short
231 * and then _flags |= (((q << 7) + q) & 0x701) and argtype |= (_flags & 0x701)
234 /* TODO -- Fix the table below to take into account stdint.h. */
235 /* #ifndef LLONG_MAX */
236 /* #error fix QUAL_CHARS for no long long! Affects 'L', 'j', 'q', 'll'. */
238 /* #if LLONG_MAX != INTMAX_MAX */
239 /* #error fix QUAL_CHARS intmax_t entry 'j'! */
244 # error PDS already defined!
247 # error SS already defined!
250 # error IMS already defined!
253 #if PTRDIFF_MAX == INT_MAX
255 #elif PTRDIFF_MAX == LONG_MAX
257 #elif defined(LLONG_MAX) && (PTRDIFF_MAX == LLONG_MAX)
260 # error fix QUAL_CHARS ptrdiff_t entry 't'!
263 #if SIZE_MAX == UINT_MAX
265 #elif SIZE_MAX == ULONG_MAX
267 #elif defined(LLONG_MAX) && (SIZE_MAX == ULLONG_MAX)
270 # error fix QUAL_CHARS size_t entries 'z', 'Z'!
273 #if INTMAX_MAX == INT_MAX
275 #elif INTMAX_MAX == LONG_MAX
277 #elif defined(LLONG_MAX) && (INTMAX_MAX == LLONG_MAX)
280 # error fix QUAL_CHARS intmax_t entry 'j'!
283 #define QUAL_CHARS { \
284 /* j:(u)intmax_t z:(s)size_t t:ptrdiff_t \0:int */ \
285 /* q:long_long Z:(s)size_t */ \
286 'h', 'l', 'L', 'j', 'z', 't', 'q', 'Z', 0, \
287 2, 4, 8, IMS, SS, PDS, 8, SS, 0, /* TODO -- fix!!! */\
291 /**********************************************************************/
293 #ifdef __STDIO_VA_ARG_PTR
295 # define __va_arg_ptr(ap,type) (((type *)(ap += sizeof(type))) - 1)
300 /* TODO -- need other than for 386 as well! */
302 # ifndef __va_rounded_size
303 # define __va_rounded_size(TYPE) \
304 (((sizeof (TYPE) + sizeof (int) - 1) / sizeof (int)) * sizeof (int))
306 # define __va_arg_ptr(AP, TYPE) \
307 (AP = (va_list) ((char *) (AP) + __va_rounded_size (TYPE)), \
308 ((void *) ((char *) (AP) - __va_rounded_size (TYPE))) \
312 #endif /* __STDIO_VA_ARG_PTR */
315 # define GET_VA_ARG(AP,F,TYPE,ARGS) (*(AP) = __va_arg_ptr(ARGS,TYPE))
316 # define GET_ARG_VALUE(AP,F,TYPE) (*((TYPE *)(*(AP))))
323 unsigned long long ull
;
325 # ifdef __UCLIBC_HAS_FLOATS__
332 # define GET_VA_ARG(AU,F,TYPE,ARGS) (AU->F = va_arg(ARGS,TYPE))
333 # define GET_ARG_VALUE(AU,F,TYPE) ((TYPE)((AU)->F))
337 const char *fmtpos
; /* TODO: move below struct?? */
338 struct printf_info info
;
340 int maxposarg
; /* > 0 if args are positional, 0 if not, -1 if unknown */
342 int num_data_args
; /* TODO: use sentinal??? */
343 unsigned int conv_num
;
344 unsigned char argnumber
[4]; /* width | prec | 1st data | unused */
345 int argtype
[MAX_ARGS
];
348 void *argptr
[MAX_ARGS
];
350 /* if defined(NL_ARGMAX) || defined(__UCLIBC_HAS_GLIBC_CUSTOM_PRINTF__) */
351 /* While this is wasteful of space in the case where pos args aren't
352 * enabled, it is also needed to support custom printf handlers. */
353 argvalue_t argvalue
[MAX_ARGS
];
355 } ppfs_t
; /* parse printf format state */
357 /**********************************************************************/
359 /* TODO: fix printf to return 0 and set errno if format error. Standard says
360 only returns -1 if sets error indicator for the stream. */
362 extern int _ppfs_init(ppfs_t
*ppfs
, const char *fmt0
) attribute_hidden
; /* validates */
363 extern void _ppfs_prepargs(ppfs_t
*ppfs
, va_list arg
) attribute_hidden
; /* sets posargptrs */
364 extern void _ppfs_setargs(ppfs_t
*ppfs
) attribute_hidden
; /* sets argptrs for current spec */
365 extern int _ppfs_parsespec(ppfs_t
*ppfs
) attribute_hidden
; /* parses specifier */
367 /**********************************************************************/
368 #ifdef L_parse_printf_format
370 #ifdef __UCLIBC_HAS_GLIBC_CUSTOM_PRINTF__
372 /* NOTE: This function differs from the glibc version in that parsing stops
373 * upon encountering an invalid conversion specifier. Since this is the way
374 * my printf functions work, I think it makes sense to do it that way here.
375 * Unfortunately, since glibc sets the return type as size_t, we have no way
376 * of returning that the template is illegal, other than returning 0.
379 size_t parse_printf_format(register const char *template,
380 size_t n
, register int *argtypes
)
386 if (_ppfs_init(&ppfs
, template) >= 0) {
388 if (ppfs
.maxposarg
> 0) {
389 /* Using positional args. */
390 count
= ppfs
.maxposarg
;
394 for (i
= 0 ; i
< n
; i
++) {
395 *argtypes
++ = ppfs
.argtype
[i
];
400 /* Not using positional args. */
402 if ((*template == '%') && (*++template != '%')) {
403 ppfs
.fmtpos
= template;
404 _ppfs_parsespec(&ppfs
); /* Can't fail. */
405 template = ppfs
.fmtpos
; /* Update to one past spec end. */
406 if (ppfs
.info
.width
== INT_MIN
) {
409 *argtypes
++ = PA_INT
;
413 if (ppfs
.info
.prec
== INT_MIN
) {
416 *argtypes
++ = PA_INT
;
420 for (i
= 0 ; i
< ppfs
.num_data_args
; i
++) {
421 if ((ppfs
.argtype
[i
]) != __PA_NOARG
) {
424 *argtypes
++ = ppfs
.argtype
[i
];
442 /**********************************************************************/
445 int attribute_hidden
_ppfs_init(register ppfs_t
*ppfs
, const char *fmt0
)
449 /* First, zero out everything... argnumber[], argtype[], argptr[] */
450 memset(ppfs
, 0, sizeof(ppfs_t
)); /* TODO: nonportable???? */
452 --ppfs
->maxposarg
; /* set to -1 */
455 #ifdef __UCLIBC_HAS_LOCALE__
456 /* To support old programs, don't check mb validity if in C locale. */
457 if (__UCLIBC_CURLOCALE
->encoding
!= __ctype_encoding_7_bit
) {
458 /* ANSI/ISO C99 requires format string to be a valid multibyte string
459 * beginning and ending in its initial shift state. */
460 static const char invalid_mbs
[] = "Invalid multibyte format string.";
463 mbstate
.__mask
= 0; /* Initialize the mbstate. */
465 if (mbsrtowcs(NULL
, &p
, SIZE_MAX
, &mbstate
) == ((size_t)(-1))) {
466 ppfs
->fmtpos
= invalid_mbs
;
470 #endif /* __UCLIBC_HAS_LOCALE__ */
471 /* now set all argtypes to no-arg */
474 /* TODO - use memset here since already "paid for"? */
475 register int *p
= ppfs
->argtype
;
482 /* TODO -- get rid of this?? */
483 register char *p
= (char *) ((MAX_ARGS
-1) * sizeof(int));
486 *((int *)(((char *)ppfs
) + ((int)p
) + offsetof(ppfs_t
,argtype
))) = __PA_NOARG
;
493 * Run through the entire format string to validate it and initialize
494 * the positional arg numbers (if any).
497 register const char *fmt
= fmt0
;
500 if ((*fmt
== '%') && (*++fmt
!= '%')) {
501 ppfs
->fmtpos
= fmt
; /* back up to the '%' */
502 r
= _ppfs_parsespec(ppfs
);
506 fmt
= ppfs
->fmtpos
; /* update to one past end of spec */
511 ppfs
->fmtpos
= fmt0
; /* rewind */
515 /* If we have positional args, make sure we know all the types. */
517 register int *p
= ppfs
->argtype
;
520 if ( *p
== __PA_NOARG
) { /* missing arg type!!! */
526 #endif /* NL_ARGMAX */
531 /**********************************************************************/
532 #ifdef L__ppfs_prepargs
533 void attribute_hidden
_ppfs_prepargs(register ppfs_t
*ppfs
, va_list arg
)
537 va_copy(ppfs
->arg
, arg
);
540 i
= ppfs
->maxposarg
; /* init for positional args */
542 ppfs
->num_data_args
= i
;
543 ppfs
->info
.width
= ppfs
->info
.prec
= ppfs
->maxposarg
= 0;
550 /**********************************************************************/
551 #ifdef L__ppfs_setargs
553 void attribute_hidden
_ppfs_setargs(register ppfs_t
*ppfs
)
556 register void **p
= ppfs
->argptr
;
558 register argvalue_t
*p
= ppfs
->argvalue
;
563 if (ppfs
->maxposarg
== 0) { /* initing for or no pos args */
565 if (ppfs
->info
.width
== INT_MIN
) {
570 GET_VA_ARG(p
,u
,unsigned int,ppfs
->arg
);
572 if (ppfs
->info
.prec
== INT_MIN
) {
577 GET_VA_ARG(p
,u
,unsigned int,ppfs
->arg
);
580 while (i
< ppfs
->num_data_args
) {
581 switch(ppfs
->argtype
[i
++]) {
582 case (PA_INT
|PA_FLAG_LONG_LONG
):
584 GET_VA_ARG(p
,ull
,unsigned long long,ppfs
->arg
);
587 case (PA_INT
|PA_FLAG_LONG
):
588 #if ULONG_MAX != UINT_MAX
589 GET_VA_ARG(p
,ul
,unsigned long,ppfs
->arg
);
592 case PA_CHAR
: /* TODO - be careful */
593 /* ... users could use above and really want below!! */
594 case (PA_INT
|__PA_FLAG_CHAR
):/* TODO -- translate this!!! */
595 case (PA_INT
|PA_FLAG_SHORT
):
597 GET_VA_ARG(p
,u
,unsigned int,ppfs
->arg
);
599 case PA_WCHAR
: /* TODO -- assume int? */
600 /* we're assuming wchar_t is at least an int */
601 GET_VA_ARG(p
,wc
,wchar_t,ppfs
->arg
);
603 #ifdef __UCLIBC_HAS_FLOATS__
606 GET_VA_ARG(p
,d
,double,ppfs
->arg
);
608 case (PA_DOUBLE
|PA_FLAG_LONG_DOUBLE
):
609 GET_VA_ARG(p
,ld
,long double,ppfs
->arg
);
611 #else /* __UCLIBC_HAS_FLOATS__ */
613 case (PA_DOUBLE
|PA_FLAG_LONG_DOUBLE
):
616 #endif /* __UCLIBC_HAS_FLOATS__ */
618 /* TODO -- really need to ensure this can't happen */
619 assert(ppfs
->argtype
[i
-1] & PA_FLAG_PTR
);
623 GET_VA_ARG(p
,p
,void *,ppfs
->arg
);
632 if (ppfs
->info
.width
== INT_MIN
) {
634 = (int) GET_ARG_VALUE(p
+ ppfs
->argnumber
[0] - 1,u
,unsigned int);
636 if (ppfs
->info
.prec
== INT_MIN
) {
638 = (int) GET_ARG_VALUE(p
+ ppfs
->argnumber
[1] - 1,u
,unsigned int);
641 #endif /* NL_ARGMAX */
643 /* Now we know the width and precision. */
644 if (ppfs
->info
.width
< 0) {
645 ppfs
->info
.width
= -ppfs
->info
.width
;
646 PRINT_INFO_SET_FLAG(&(ppfs
->info
),left
);
647 PRINT_INFO_CLR_FLAG(&(ppfs
->info
),space
);
648 ppfs
->info
.pad
= ' ';
651 /* NOTE -- keep neg for now so float knows! */
652 if (ppfs
->info
.prec
< 0) { /* spec says treat as omitted. */
653 /* so use default prec... 1 for everything but floats and strings. */
659 /**********************************************************************/
660 #ifdef L__ppfs_parsespec
662 /* Notes: argtype differs from glibc for the following:
664 * lc PA_WCHAR PA_CHAR the standard says %lc means %C
665 * ls PA_WSTRING PA_STRING the standard says %ls means %S
666 * {*}n {*}|PA_FLAG_PTR PA_FLAG_PTR size of n can be qualified
669 /* TODO: store positions of positional args */
671 /* TODO -- WARNING -- assumes aligned on integer boundaries!!! */
673 /* TODO -- disable if not using positional args!!! */
674 #define _OVERLAPPING_DIFFERENT_ARGS
676 /* TODO -- rethink this -- perhaps we should set to largest type??? */
678 #ifdef _OVERLAPPING_DIFFERENT_ARGS
680 #define PROMOTED_SIZE_OF(X) ((sizeof(X) + sizeof(int) - 1) / sizeof(X))
682 static const short int type_codes
[] = {
683 __PA_NOARG
, /* must be first entry */
688 PA_INT
|PA_FLAG_SHORT
,
691 PA_INT
|PA_FLAG_LONG_LONG
,
693 #ifdef __UCLIBC_HAS_FLOATS__
696 PA_DOUBLE
|PA_FLAG_LONG_DOUBLE
,
700 static const unsigned char type_sizes
[] = {
701 /* unknown type consumes no arg */
702 0, /* must be first entry */
703 PROMOTED_SIZE_OF(void *),
704 PROMOTED_SIZE_OF(char *),
705 PROMOTED_SIZE_OF(wchar_t *),
706 PROMOTED_SIZE_OF(char),
707 PROMOTED_SIZE_OF(short),
708 PROMOTED_SIZE_OF(int),
709 PROMOTED_SIZE_OF(long),
711 PROMOTED_SIZE_OF(long long),
713 PROMOTED_SIZE_OF(long), /* TODO -- is this correct? (above too) */
715 PROMOTED_SIZE_OF(wchar_t),
716 #ifdef __UCLIBC_HAS_FLOATS__
717 /* PROMOTED_SIZE_OF(float), */
718 PROMOTED_SIZE_OF(double),
719 PROMOTED_SIZE_OF(long double),
723 static int _promoted_size(int argtype
)
725 register const short int *p
;
727 /* note -- since any unrecognized type is treated as a pointer */
728 p
= type_codes
+ sizeof(type_codes
)/sizeof(type_codes
[0]);
730 if (*--p
== argtype
) {
733 } while (p
> type_codes
);
735 return type_sizes
[(int)(p
- type_codes
)];
738 static int _is_equal_or_bigger_arg(int curtype
, int newtype
)
741 if (newtype
== __PA_NOARG
) {
744 if ((curtype
== __PA_NOARG
) || (curtype
== newtype
)) {
747 /* Ok... slot is already filled and types are different in name. */
748 /* So, compare promoted sizes of curtype and newtype args. */
749 return _promoted_size(curtype
) <= _promoted_size(newtype
);
754 #define _is_equal_or_bigger_arg(C,N) (((C) == __PA_NOARG) || ((C) == (N)))
758 #ifdef __UCLIBC_HAS_GLIBC_CUSTOM_PRINTF__
759 /* TODO - do this differently? */
760 static char _bss_custom_printf_spec
[MAX_USER_SPEC
]; /* 0-init'd for us. */
762 attribute_hidden
char *_custom_printf_spec
= _bss_custom_printf_spec
;
763 attribute_hidden printf_arginfo_function
*_custom_printf_arginfo
[MAX_USER_SPEC
];
764 attribute_hidden printf_function _custom_printf_handler
[MAX_USER_SPEC
];
765 #endif /* __UCLIBC_HAS_GLIBC_CUSTOM_PRINTF__ */
767 int attribute_hidden
_ppfs_parsespec(ppfs_t
*ppfs
)
769 register const char *fmt
;
770 register const char *p
;
782 int argtype
[MAX_ARGS_PER_SPEC
+2];
783 int argnumber
[3]; /* width, precision, 1st data arg */
784 static const char spec_flags
[] = SPEC_FLAGS
;
785 static const char spec_chars
[] = SPEC_CHARS
;/* TODO: b? */
786 static const char spec_ranges
[] = SPEC_RANGES
;
787 static const short spec_or_mask
[] = SPEC_OR_MASK
;
788 static const short spec_and_mask
[] = SPEC_AND_MASK
;
789 static const char qual_chars
[] = QUAL_CHARS
;
790 #ifdef __UCLIBC_HAS_WCHAR__
794 /* WIDE note: we can test against '%' here since we don't allow */
795 /* WIDE note: other mappings of '%' in the wide char set. */
799 argtype
[0] = __PA_NOARG
;
800 argtype
[1] = __PA_NOARG
;
802 maxposarg
= ppfs
->maxposarg
;
805 #ifdef __UCLIBC_HAS_WCHAR__
806 /* This is somewhat lame, but saves a lot of code. If we're dealing with
807 * a wide stream, that means the format is a wchar string. So, copy it
808 * char-by-char into a normal char buffer for processing. Make the buffer
809 * (buf) big enough so that any reasonable format specifier will fit.
810 * While there a legal specifiers that won't, the all involve duplicate
811 * flags or outrageous field widths/precisions. */
813 flags
= ppfs
->info
._flags
& FLAG_WIDESTREAM
;
820 buf
[i
] = (char) (((wchar_t *) ppfs
->fmtpos
)[i
-1]);
821 if (buf
[i
] != (((wchar_t *) ppfs
->fmtpos
)[i
-1])) {
824 } while (buf
[i
++] && (i
< sizeof(buf
)));
825 buf
[sizeof(buf
)-1] = 0;
827 #else /* __UCLIBC_HAS_WCHAR__ */
828 width
= flags
= dpoint
= 0;
832 assert(fmt
[-1] == '%');
833 assert(fmt
[0] != '%');
835 /* Process arg pos and/or flags and/or width and/or precision. */
839 argtype
[-dpoint
] = PA_INT
;
843 while (isdigit(*fmt
)) {
845 || (i
== INT_MAX
/ 10 && (*fmt
- '0') <= INT_MAX
% 10)) {
846 i
= (i
* 10) + (*fmt
- '0');
848 i
= INT_MAX
; /* best we can do... */
852 if (p
[-1] == '%') { /* Check for a position. */
854 /* TODO: if val not in range, then error */
857 if ((*fmt
== '$') && (i
> 0)) {/* Positional spec. */
859 if (maxposarg
== 0) {
863 if (argnumber
[2] > maxposarg
) {
866 /* Now fall through to check flags. */
869 # ifdef __UCLIBC_HAS_PRINTF_M_SPEC__
873 # endif /* __UCLIBC_HAS_PRINTF_M_SPEC__ */
876 maxposarg
= 0; /* Possible redundant store, but cuts size. */
878 if ((fmt
> p
) && (*p
!= '0')) {
882 fmt
= p
; /* Back up for possible '0's flag. */
883 /* Now fall through to check flags. */
885 #else /* NL_ARGMAX */
886 if (*fmt
== '$') { /* Positional spec. */
890 if ((fmt
> p
) && (*p
!= '0')) {
894 fmt
= p
; /* Back up for possible '0's flag. */
895 /* Now fall through to check flags. */
896 #endif /* NL_ARGMAX */
898 restart_flags
: /* Process flags. */
908 i
+= i
; /* Better than i <<= 1 for bcc */
912 /* If '+' then ignore ' ', and if '-' then ignore '0'. */
913 /* Note: Need to ignore '0' when prec is an arg with val < 0, */
914 /* but that test needs to wait until the arg is retrieved. */
915 flags
&= ~((flags
& (FLAG_PLUS
|FLAG_MINUS
)) >> 1);
916 /* Note: Ignore '0' when prec is specified < 0 too (in printf). */
918 if (fmt
[-1] != '%') { /* If we've done anything, loop for width. */
919 goto width_precision
;
923 if (*p
== '*') { /* Prec or width takes an arg. */
926 if ((*fmt
++ != '$') || (i
<= 0)) {
927 /* Using pos args and no $ or invalid arg number. */
930 argnumber
[-dpoint
] = i
;
934 /* Not using pos args but digits followed *. */
944 dpoint
= -1; /* To use as default precison. */
945 goto width_precision
;
951 /* Process qualifier. */
959 if ((p
- qual_chars
< 2) && (*fmt
== *p
)) {
960 p
+= ((sizeof(qual_chars
)-2) / 2);
963 dataargtype
= ((int)(p
[(sizeof(qual_chars
)-2) / 2])) << 8;
965 /* Process conversion specifier. */
974 p_m_spec_chars
= p
- spec_chars
;
976 if ((p_m_spec_chars
>= CONV_c
)
977 && (dataargtype
& PA_FLAG_LONG
)) {
978 p_m_spec_chars
-= 2; /* lc -> C and ls -> S */
981 ppfs
->conv_num
= p_m_spec_chars
;
983 while (p_m_spec_chars
> *++p
) {}
986 argtype
[2] = (dataargtype
| spec_or_mask
[i
]) & spec_and_mask
[i
];
992 ppfs
->info
.spec
= *fmt
;
993 ppfs
->info
.prec
= preci
;
994 ppfs
->info
.width
= width
;
995 ppfs
->info
.pad
= ((flags
& FLAG_ZERO
) ? '0' : ' ');
996 ppfs
->info
._flags
= (flags
& ~FLAG_ZERO
) | (dataargtype
& __PA_INTMASK
);
997 ppfs
->num_data_args
= 1;
1000 #ifdef __UCLIBC_HAS_PRINTF_M_SPEC__
1002 ppfs
->conv_num
= CONV_m
;
1003 ppfs
->num_data_args
= 0;
1007 #ifndef __UCLIBC_HAS_GLIBC_CUSTOM_PRINTF__
1008 return -1; /* Error */
1010 /* Handle custom arg -- WARNING -- overwrites p!!! */
1011 ppfs
->conv_num
= CONV_custom0
;
1012 p
= _custom_printf_spec
;
1015 printf_arginfo_function
*fp
= _custom_printf_arginfo
[(int)(p
- _custom_printf_spec
)];
1016 ppfs
->num_data_args
= fp(&(ppfs
->info
), MAX_ARGS_PER_SPEC
, argtype
+ 2);
1017 if (ppfs
->num_data_args
> MAX_ARGS_PER_SPEC
) {
1018 return -1; /* Error -- too many args! */
1022 if (++p
>= (_custom_printf_spec
+ MAX_USER_SPEC
))
1023 return -1; /* Error */
1030 if (maxposarg
> 0) {
1033 /* Update maxposarg and check that NL_ARGMAX is not exceeded. */
1035 ? (ppfs
->argnumber
[i
] = argnumber
[i
])
1036 : argnumber
[2] + (i
-2));
1037 if (n
> maxposarg
) {
1039 if (maxposarg
> NL_ARGMAX
) {
1044 /* Record argtype with largest size (current, new). */
1045 if (_is_equal_or_bigger_arg(ppfs
->argtype
[n
], argtype
[i
])) {
1046 ppfs
->argtype
[n
] = argtype
[i
];
1048 } while (++i
< ppfs
->num_data_args
+ 2);
1050 #endif /* NL_ARGMAX */
1052 ppfs
->argnumber
[2] = 1;
1053 memcpy(ppfs
->argtype
, argtype
+ 2, ppfs
->num_data_args
* sizeof(int));
1057 ppfs
->maxposarg
= maxposarg
;
1060 #ifdef __UCLIBC_HAS_WCHAR__
1061 flags
= ppfs
->info
._flags
& FLAG_WIDESTREAM
;
1063 ppfs
->fmtpos
= ++fmt
;
1065 ppfs
->fmtpos
= (const char *) (((const wchar_t *)(ppfs
->fmtpos
))
1068 #else /* __UCLIBC_HAS_WCHAR__ */
1069 ppfs
->fmtpos
= ++fmt
;
1072 return ppfs
->num_data_args
+ 2;
1076 /**********************************************************************/
1077 #ifdef L_register_printf_function
1079 #ifdef __UCLIBC_HAS_GLIBC_CUSTOM_PRINTF__
1081 int register_printf_function(int spec
, printf_function handler
,
1082 printf_arginfo_function arginfo
)
1087 if (spec
&& (arginfo
!= NULL
)) { /* TODO -- check if spec is valid char */
1089 p
= _custom_printf_spec
+ MAX_USER_SPEC
;
1096 else /* bcc generates less code with fall-through */
1100 p
= _custom_printf_spec
;
1102 } while (p
> _custom_printf_spec
);
1107 _custom_printf_handler
[(int)(r
- p
)] = handler
;
1108 _custom_printf_arginfo
[(int)(r
- p
)] = arginfo
;
1114 /* TODO -- if asked to unregister a non-existent spec, return what? */
1122 /**********************************************************************/
1123 #if defined(L__vfprintf_internal) || defined(L__vfwprintf_internal)
1125 /* We only support ascii digits (or their USC equivalent codes) in
1126 * precision and width settings in *printf (wide) format strings.
1127 * In other words, we don't currently support glibc's 'I' flag.
1128 * We do accept it, but it is currently ignored. */
1130 static size_t _charpad(FILE * __restrict stream
, int padchar
, size_t numpad
);
1132 #ifdef L__vfprintf_internal
1134 #define VFPRINTF_internal _vfprintf_internal
1135 #define FMT_TYPE char
1136 #define OUTNSTR _outnstr
1137 #define STRLEN strlen
1138 #define _PPFS_init _ppfs_init
1139 #define OUTPUT(F,S) fputs_unlocked(S,F)
1140 /* #define _outnstr(stream, string, len) __stdio_fwrite(string, len, stream) */
1141 #define _outnstr(stream, string, len) ((len > 0) ? __stdio_fwrite((const unsigned char *)(string), len, stream) : 0)
1142 #define FP_OUT _fp_out_narrow
1144 #ifdef __UCLIBC_HAS_FLOATS__
1146 static size_t _fp_out_narrow(FILE *fp
, intptr_t type
, intptr_t len
, intptr_t buf
)
1150 if (type
& 0x80) { /* Some type of padding needed. */
1151 int buflen
= strlen((const char *) buf
);
1154 r
= _charpad(fp
, (type
& 0x7f), len
);
1161 return r
+ OUTNSTR(fp
, (const char *) buf
, len
);
1164 #endif /* __UCLIBC_HAS_FLOATS__ */
1166 #else /* L__vfprintf_internal */
1168 #define VFPRINTF_internal _vfwprintf_internal
1169 #define FMT_TYPE wchar_t
1170 #define OUTNSTR _outnwcs
1171 #define STRLEN wcslen
1172 #define _PPFS_init _ppwfs_init
1173 /* Pulls in fseek: */
1174 #define OUTPUT(F,S) fputws_unlocked(S,F)
1175 /* TODO: #define OUTPUT(F,S) _wstdio_fwrite((S),wcslen(S),(F)) */
1176 #define _outnwcs(stream, wstring, len) _wstdio_fwrite((const wchar_t *)(wstring), len, stream)
1177 #define FP_OUT _fp_out_wide
1179 static size_t _outnstr(FILE *stream
, const char *s
, size_t wclen
)
1181 /* NOTE!!! len here is the number of wchars we want to generate!!! */
1190 r
= mbsrtowcs(wbuf
, &s
,
1191 ((todo
<= sizeof(wbuf
)/sizeof(wbuf
[0]))
1193 : sizeof(wbuf
)/sizeof(wbuf
[0])),
1195 assert(((ssize_t
)r
) > 0);
1196 n
= _outnwcs(stream
, wbuf
, r
);
1203 return wclen
- todo
;
1206 #ifdef __UCLIBC_HAS_FLOATS__
1208 static size_t _fp_out_wide(FILE *fp
, intptr_t type
, intptr_t len
, intptr_t buf
)
1210 wchar_t wbuf
[BUF_SIZE
];
1211 const char *s
= (const char *) buf
;
1215 if (type
& 0x80) { /* Some type of padding needed */
1216 int buflen
= strlen(s
);
1219 r
= _charpad(fp
, (type
& 0x7f), len
);
1230 #ifdef __LOCALE_C_ONLY
1234 # ifdef __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__
1236 wbuf
[i
] = __UCLIBC_CURLOCALE
->thousands_sep_wc
;
1240 wbuf
[i
] = __UCLIBC_CURLOCALE
->decimal_point_wc
;
1244 #endif /* __LOCALE_C_ONLY */
1246 } while (++i
< len
);
1248 r
+= OUTNSTR(fp
, wbuf
, len
);
1254 #endif /* __UCLIBC_HAS_FLOATS__ */
1256 static int _ppwfs_init(register ppfs_t
*ppfs
, const wchar_t *fmt0
)
1258 static const wchar_t invalid_wcs
[] = L
"Invalid wide format string.";
1261 /* First, zero out everything... argnumber[], argtype[], argptr[] */
1262 memset(ppfs
, 0, sizeof(ppfs_t
)); /* TODO: nonportable???? */
1264 --ppfs
->maxposarg
; /* set to -1 */
1266 ppfs
->fmtpos
= (const char *) fmt0
;
1267 ppfs
->info
._flags
= FLAG_WIDESTREAM
;
1272 mbstate
.__mask
= 0; /* Initialize the mbstate. */
1274 if (wcsrtombs(NULL
, &p
, SIZE_MAX
, &mbstate
) == ((size_t)(-1))) {
1275 ppfs
->fmtpos
= (const char *) invalid_wcs
;
1280 /* now set all argtypes to no-arg */
1283 /* TODO - use memset here since already "paid for"? */
1284 register int *p
= ppfs
->argtype
;
1291 /* TODO -- get rid of this?? */
1292 register char *p
= (char *) ((MAX_ARGS
-1) * sizeof(int));
1295 *((int *)(((char *)ppfs
) + ((int)p
) + offsetof(ppfs_t
,argtype
))) = __PA_NOARG
;
1302 * Run through the entire format string to validate it and initialize
1303 * the positional arg numbers (if any).
1306 register const wchar_t *fmt
= fmt0
;
1309 if ((*fmt
== '%') && (*++fmt
!= '%')) {
1310 ppfs
->fmtpos
= (const char *) fmt
; /* back up to the '%' */
1311 r
= _ppfs_parsespec(ppfs
);
1315 fmt
= (const wchar_t *) ppfs
->fmtpos
; /* update to one past end of spec */
1320 ppfs
->fmtpos
= (const char *) fmt0
; /* rewind */
1324 /* If we have positional args, make sure we know all the types. */
1326 register int *p
= ppfs
->argtype
;
1327 r
= ppfs
->maxposarg
;
1329 if ( *p
== __PA_NOARG
) { /* missing arg type!!! */
1335 #endif /* NL_ARGMAX */
1340 #endif /* L__vfprintf_internal */
1343 static size_t _charpad(FILE * __restrict stream
, int padchar
, size_t numpad
)
1345 size_t todo
= numpad
;
1347 /* TODO -- Use a buffer to cut down on function calls... */
1351 while (todo
&& (OUTNSTR(stream
, (const char *) pad
, 1) == 1)) {
1355 return numpad
- todo
;
1358 /* TODO -- Dynamically allocate work space to accomodate stack-poor archs? */
1359 static int _do_one_spec(FILE * __restrict stream
,
1360 register ppfs_t
*ppfs
, int *count
)
1362 static const char spec_base
[] = SPEC_BASE
;
1363 #ifdef L__vfprintf_internal
1364 static const char prefix
[] = "+\0-\0 \0000x\0000X";
1367 static const wchar_t prefix
[] = L
"+\0-\0 \0000x\0000X";
1379 const void * const *argptr
;
1381 const void * argptr
[MAX_ARGS_PER_SPEC
];
1384 #ifdef __UCLIBC_HAS_WCHAR__
1385 const wchar_t *ws
= NULL
;
1389 #ifdef L__vfprintf_internal
1398 int numfill
= 0; /* TODO: fix */
1399 int prefix_num
= PREFIX_NONE
;
1401 /* TODO: buf needs to be big enough for any possible error return strings
1402 * and also for any locale-grouped long long integer strings generated.
1403 * This should be large enough for any of the current archs/locales, but
1404 * eventually this should be handled robustly. */
1408 _ppfs_parsespec(ppfs
);
1410 if (_ppfs_parsespec(ppfs
) < 0) { /* TODO: just for debugging */
1414 _ppfs_setargs(ppfs
);
1416 argtype
= ppfs
->argtype
+ ppfs
->argnumber
[2] - 1;
1417 /* Deal with the argptr vs argvalue issue. */
1419 argptr
= (const void * const *) ppfs
->argptr
;
1421 if (ppfs
->maxposarg
> 0) { /* Using positional args... */
1422 argptr
+= ppfs
->argnumber
[2] - 1;
1426 /* Need to build a local copy... */
1428 register argvalue_t
*p
= ppfs
->argvalue
;
1431 if (ppfs
->maxposarg
> 0) { /* Using positional args... */
1432 p
+= ppfs
->argnumber
[2] - 1;
1435 for (i
= 0 ; i
< ppfs
->num_data_args
; i
++ ) {
1436 argptr
[i
] = (void *) p
++;
1441 register char *s
= NULL
; /* TODO: Should s be unsigned char * ? */
1443 if (ppfs
->conv_num
== CONV_n
) {
1444 _store_inttype(*(void **)*argptr
,
1445 ppfs
->info
._flags
& __PA_INTMASK
,
1446 (intmax_t) (*count
));
1449 if (ppfs
->conv_num
<= CONV_i
) { /* pointer or (un)signed int */
1450 alphacase
= __UIM_LOWER
;
1452 base
= spec_base
[(int)(ppfs
->conv_num
- CONV_p
)];
1454 if (PRINT_INFO_FLAG_VAL(&(ppfs
->info
),group
)) {
1455 alphacase
= __UIM_GROUP
;
1457 if (PRINT_INFO_FLAG_VAL(&(ppfs
->info
),i18n
)) {
1462 if (ppfs
->conv_num
<= CONV_u
) { /* pointer or unsigned int */
1463 if (ppfs
->conv_num
== CONV_X
) {
1464 alphacase
= __UIM_UPPER
;
1466 if (ppfs
->conv_num
== CONV_p
) { /* pointer */
1467 prefix_num
= PREFIX_LWR_X
;
1468 } else { /* unsigned int */
1470 } else { /* signed int */
1473 if (ppfs
->info
.prec
< 0) { /* Ignore '0' flag if prec specified. */
1474 padchar
= ppfs
->info
.pad
;
1476 s
= _uintmaxtostr(buf
+ sizeof(buf
) - 1,
1478 _load_inttype(ppfs
->conv_num
== CONV_p
? PA_FLAG_LONG
: *argtype
& __PA_INTMASK
,
1479 *argptr
, base
), base
, alphacase
);
1480 if (ppfs
->conv_num
> CONV_u
) { /* signed int */
1482 PRINT_INFO_SET_FLAG(&(ppfs
->info
),showsign
);
1483 ++s
; /* handle '-' in the prefix string */
1484 prefix_num
= PREFIX_MINUS
;
1485 } else if (PRINT_INFO_FLAG_VAL(&(ppfs
->info
),showsign
)) {
1486 prefix_num
= PREFIX_PLUS
;
1487 } else if (PRINT_INFO_FLAG_VAL(&(ppfs
->info
),space
)) {
1488 prefix_num
= PREFIX_SPACE
;
1491 slen
= (char *)(buf
+ sizeof(buf
) - 1) - s
;
1492 #ifdef L__vfwprintf_internal
1495 mbstate
.__mask
= 0; /* Initialize the mbstate. */
1496 SLEN
= mbsrtowcs(NULL
, &q
, 0, &mbstate
);
1499 numfill
= ((ppfs
->info
.prec
< 0) ? 1 : ppfs
->info
.prec
);
1500 if (PRINT_INFO_FLAG_VAL(&(ppfs
->info
),alt
)) {
1501 if (ppfs
->conv_num
<= CONV_x
) { /* x or p */
1502 prefix_num
= PREFIX_LWR_X
;
1504 if (ppfs
->conv_num
== CONV_X
) {
1505 prefix_num
= PREFIX_UPR_X
;
1507 if ((ppfs
->conv_num
== CONV_o
) && (numfill
<= SLEN
)) {
1508 numfill
= ((*s
== '0') ? 1 : SLEN
+ 1);
1512 if (prefix_num
>= PREFIX_LWR_X
) {
1513 prefix_num
= PREFIX_NONE
;
1515 if (ppfs
->conv_num
== CONV_p
) {/* null pointer */
1517 #ifdef L__vfwprintf_internal
1522 } else if (numfill
== 0) { /* if precision 0, no output */
1523 #ifdef L__vfwprintf_internal
1529 numfill
= ((numfill
> SLEN
) ? numfill
- SLEN
: 0);
1530 } else if (ppfs
->conv_num
<= CONV_A
) { /* floating point */
1531 #ifdef __UCLIBC_HAS_FLOATS__
1533 nf
= _fpmaxtostr(stream
,
1535 (PRINT_INFO_FLAG_VAL(&(ppfs
->info
),is_long_double
)
1536 ? *(long double *) *argptr
1537 : (long double) (* (double *) *argptr
)),
1538 &ppfs
->info
, FP_OUT
);
1545 #else /* __UCLIBC_HAS_FLOATS__ */
1546 return -1; /* TODO -- try to continue? */
1548 } else if (ppfs
->conv_num
<= CONV_S
) { /* wide char or string */
1549 #ifdef L__vfprintf_internal
1551 #ifdef __UCLIBC_HAS_WCHAR__
1552 mbstate
.__mask
= 0; /* Initialize the mbstate. */
1553 if (ppfs
->conv_num
== CONV_S
) { /* wide string */
1554 ws
= *((const wchar_t **) *argptr
);
1558 /* We use an awful uClibc-specific hack here, passing
1559 * (char*) &ws as the conversion destination. This signals
1560 * uClibc's wcsrtombs that we want a "restricted" length
1561 * such that the mbs fits in a buffer of the specified
1562 * size with no partial conversions. */
1563 slen
= wcsrtombs((char *) &ws
, &ws
, /* Use awful hack! */
1564 ((ppfs
->info
.prec
>= 0)
1568 if (slen
== ((size_t)-1)) {
1569 return -1; /* EILSEQ */
1571 } else { /* wide char */
1573 slen
= wcrtomb(s
, (*((const wchar_t *) *argptr
)), &mbstate
);
1574 if (slen
== ((size_t)-1)) {
1575 return -1; /* EILSEQ */
1577 s
[slen
] = 0; /* TODO - Is this necessary? */
1579 #else /* __UCLIBC_HAS_WCHAR__ */
1582 } else if (ppfs
->conv_num
<= CONV_s
) { /* char or string */
1583 if (ppfs
->conv_num
== CONV_s
) { /* string */
1584 s
= *((char **) (*argptr
));
1586 #ifdef __UCLIBC_HAS_PRINTF_M_SPEC__
1589 slen
= strnlen(s
, ((ppfs
->info
.prec
>= 0)
1590 ? ppfs
->info
.prec
: SIZE_MAX
));
1592 #ifdef __UCLIBC_HAS_WCHAR__
1597 /* Use an empty string rather than truncation if precision is too small. */
1598 if (ppfs
->info
.prec
>= 0 && ppfs
->info
.prec
< slen
)
1603 *s
= (unsigned char)(*((const int *) *argptr
));
1608 #else /* L__vfprintf_internal */
1610 if (ppfs
->conv_num
== CONV_S
) { /* wide string */
1611 ws
= *((wchar_t **) (*argptr
));
1615 SLEN
= wcsnlen(ws
, ((ppfs
->info
.prec
>= 0)
1616 ? ppfs
->info
.prec
: SIZE_MAX
));
1617 } else { /* wide char */
1618 *wbuf
= (wchar_t)(*((const wint_t *) *argptr
));
1625 } else if (ppfs
->conv_num
<= CONV_s
) { /* char or string */
1627 if (ppfs
->conv_num
== CONV_s
) { /* string */
1628 s
= *((char **) (*argptr
));
1630 #ifdef __UCLIBC_HAS_PRINTF_M_SPEC__
1633 /* We use an awful uClibc-specific hack here, passing
1634 * (wchar_t*) &mbstate as the conversion destination.
1635 * This signals uClibc's mbsrtowcs that we want a
1636 * "restricted" length such that the mbs fits in a buffer
1637 * of the specified size with no partial conversions. */
1640 mbstate
.__mask
= 0; /* Initialize the mbstate. */
1641 SLEN
= mbsrtowcs((wchar_t *) &mbstate
, &q
,
1642 ((ppfs
->info
.prec
>= 0)
1643 ? ppfs
->info
.prec
: SIZE_MAX
),
1646 if (SLEN
== ((size_t)(-1))) {
1647 return -1; /* EILSEQ */
1653 /* Use an empty string rather than truncation if precision is too small. */
1654 if (ppfs
->info
.prec
>= 0 && ppfs
->info
.prec
< slen
)
1658 *wbuf
= btowc( (unsigned char)(*((const int *) *argptr
)) );
1662 #endif /* L__vfprintf_internal */
1664 #ifdef __UCLIBC_HAS_PRINTF_M_SPEC__
1665 } else if (ppfs
->conv_num
== CONV_m
) {
1666 s
= __glibc_strerror_r(errno
, buf
, sizeof(buf
));
1667 goto SET_STRING_LEN
;
1670 #ifdef __UCLIBC_HAS_GLIBC_CUSTOM_PRINTF__
1671 assert(ppfs
->conv_num
== CONV_custom0
);
1673 s
= _custom_printf_spec
;
1675 if (*s
== ppfs
->info
.spec
) {
1677 /* TODO -- check return value for sanity? */
1678 rv
= (*_custom_printf_handler
1679 [(int)(s
-_custom_printf_spec
)])
1680 (stream
, &ppfs
->info
, argptr
);
1687 } while (++s
< (_custom_printf_spec
+ MAX_USER_SPEC
));
1688 #endif /* __UCLIBC_HAS_GLIBC_CUSTOM_PRINTF__ */
1697 if (prefix_num
!= PREFIX_NONE
) {
1698 t
+= ((prefix_num
< PREFIX_LWR_X
) ? 1 : 2);
1700 numpad
= ((ppfs
->info
.width
> t
) ? (ppfs
->info
.width
- t
) : 0);
1701 *count
+= t
+ numpad
;
1703 if (padchar
== '0') { /* TODO: check this */
1708 /* Now handle the output itself. */
1709 if (!PRINT_INFO_FLAG_VAL(&(ppfs
->info
),left
)) {
1710 if (_charpad(stream
, ' ', numpad
) != numpad
) {
1715 OUTPUT(stream
, prefix
+ prefix_num
);
1717 if (_charpad(stream
, '0', numfill
) != numfill
) {
1721 #ifdef L__vfprintf_internal
1723 # ifdef __UCLIBC_HAS_WCHAR__
1726 if (_outnstr(stream
, s
, slen
) != slen
) {
1729 } else { /* wide string */
1731 mbstate
.__mask
= 0; /* Initialize the mbstate. */
1733 t
= (slen
<= sizeof(buf
)) ? slen
: sizeof(buf
);
1734 t
= wcsrtombs(buf
, &ws
, t
, &mbstate
);
1735 assert(t
!= ((size_t)(-1)));
1736 if (_outnstr(stream
, buf
, t
) != t
) {
1742 # else /* __UCLIBC_HAS_WCHAR__ */
1743 if (_outnstr(stream
, (const unsigned char *) s
, slen
) != slen
) {
1748 #else /* L__vfprintf_internal */
1752 if (_outnstr(stream
, s
, SLEN
) != SLEN
) {
1756 if (_outnwcs(stream
, ws
, SLEN
) != SLEN
) {
1761 #endif /* L__vfprintf_internal */
1762 if (_charpad(stream
, ' ', numpad
) != numpad
) {
1771 int VFPRINTF_internal (FILE * __restrict stream
,
1772 const FMT_TYPE
* __restrict format
,
1777 register const FMT_TYPE
*s
;
1782 if (_PPFS_init(&ppfs
, format
) < 0) { /* Bad format string. */
1783 OUTNSTR(stream
, (const char *) ppfs
.fmtpos
,
1784 STRLEN((const FMT_TYPE
*)(ppfs
.fmtpos
)));
1785 #if defined(L__vfprintf_internal) && !defined(NDEBUG)
1786 fprintf(stderr
,"\nIMbS: \"%s\"\n\n", format
);
1790 _ppfs_prepargs(&ppfs
, arg
); /* This did a va_copy!!! */
1793 while (*format
&& (*format
!= '%')) {
1797 if (format
- s
) { /* output any literal text in format string */
1798 r
= OUTNSTR(stream
, (const char *) s
, format
- s
);
1799 if (r
!= (format
- s
)) {
1806 if (!*format
) { /* we're done */
1810 if (format
[1] != '%') { /* if we get here, *format == '%' */
1811 /* TODO: _do_one_spec needs to know what the output funcs are!!! */
1812 ppfs
.fmtpos
= (const char *)(++format
);
1813 /* TODO: check -- should only fail on stream error */
1814 r
= _do_one_spec(stream
, &ppfs
, &count
);
1819 s
= format
= (const FMT_TYPE
*) ppfs
.fmtpos
;
1820 } else { /* %% means literal %, so start new string */
1826 va_end(ppfs
.arg
); /* Need to clean up after va_copy! */
1829 /* #if defined(L__vfprintf_internal) && defined(__UCLIBC_HAS_WCHAR__) */
1835 #endif /* defined(L__vfprintf_internal) || defined(L__vfwprintf_internal) */
1838 /**********************************************************************/
1839 #if defined(L_vfprintf) || defined(L_vfwprintf)
1841 /* This is just a wrapper around VFPRINTF_internal.
1842 * Factoring out vfprintf internals allows:
1843 * (1) vdprintf and vsnprintf don't need to setup fake locking,
1844 * (2) __STDIO_STREAM_TRANS_TO_WRITE is not used in vfprintf internals,
1845 * and thus fseek etc is not pulled in by vdprintf and vsnprintf.
1847 * In order to not pull in fseek through fputs, OUTPUT() macro
1848 * is using __stdio_fwrite (TODO: do the same for wide functions).
1851 # define VFPRINTF vfprintf
1852 # define VFPRINTF_internal _vfprintf_internal
1853 # define FMT_TYPE char
1855 # define VFPRINTF vfwprintf
1856 # define VFPRINTF_internal _vfwprintf_internal
1857 # define FMT_TYPE wchar_t
1860 libc_hidden_proto(VFPRINTF
)
1861 int VFPRINTF (FILE * __restrict stream
,
1862 const FMT_TYPE
* __restrict format
,
1866 __STDIO_AUTO_THREADLOCK_VAR
;
1868 __STDIO_AUTO_THREADLOCK(stream
);
1872 (!__STDIO_STREAM_IS_NARROW_WRITING(stream
)
1873 && __STDIO_STREAM_TRANS_TO_WRITE(stream
, __FLAG_NARROW
))
1875 (!__STDIO_STREAM_IS_WIDE_WRITING(stream
)
1876 && __STDIO_STREAM_TRANS_TO_WRITE(stream
, __FLAG_WIDE
))
1881 count
= VFPRINTF_internal(stream
, format
, arg
);
1884 __STDIO_AUTO_THREADUNLOCK(stream
);
1888 libc_hidden_def(VFPRINTF
)
1889 #endif /* defined(L_vfprintf) || defined(L_vfwprintf) */
1891 /**********************************************************************/