netinet/in.h: add IPPROTO_MH
[uclibc-ng.git] / libc / stdio / _vfprintf.c
blobfc5d3ff68fe14bfd6070b84be63eb3b7399d7f66
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! */
35 /* April 1, 2002
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>
41 * May 10, 2002
42 * Remove __isdigit and use new ctype.h version.
43 * Add conditional setting of QUAL_CHARS for size_t and ptrdiff_t.
45 * Aug 16, 2002
46 * Fix two problems that showed up with the python 2.2.1 tests; one
47 * involving %o and one involving %f.
49 * Oct 28, 2002
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
52 * a va_copy.
53 * Make sure each va_copy has a matching va_end, as required by C99.
55 * Nov 4, 2002
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.
63 * Nov 21, 2002
64 * Add *wprintf functions. Currently they don't support floating point
65 * conversions. That will wait until the rewrite of _dtostr.
67 * Aug 1, 2003
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.
71 * Misc bug fixes.
73 * Aug 31, 2003
74 * Fix precision bug for %g conversion specifier when using %f style.
76 * Sep 5, 2003
77 * Implement *s*scanf for the non-buffered stdio case with old_vfprintf.
79 * Sep 23, 2003
80 * vfprintf was not always checking for narrow stream orientation.
83 /* TODO:
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.
91 #include <features.h>
92 #include "_stdio.h"
93 #include <stdlib.h>
94 #include <string.h>
95 #include <stddef.h>
96 #include <ctype.h>
97 #include <limits.h>
98 #include <stdarg.h>
99 #include <assert.h>
100 #include <stdint.h>
101 #include <errno.h>
102 #include <locale.h>
104 #ifdef __UCLIBC_HAS_THREADS__
105 # include <stdio_ext.h>
106 # include <pthread.h>
107 #endif
109 #ifdef __UCLIBC_HAS_WCHAR__
110 # include <wchar.h>
111 #endif
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.
122 #endif
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
129 #endif
131 /**********************************************************************/
133 #ifdef __UCLIBC_HAS_GLIBC_CUSTOM_PRINTF__
134 # define MAX_USER_SPEC 10
135 # define MAX_ARGS_PER_SPEC 5
136 #else
137 # undef MAX_USER_SPEC
138 # define MAX_ARGS_PER_SPEC 1
139 #endif
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
145 #endif
147 #if defined(NL_ARGMAX) && (NL_ARGMAX < 9)
148 # error NL_ARGMAX < 9!
149 #endif
151 #if defined(NL_ARGMAX) && (NL_ARGMAX >= (MAX_ARGS_PER_SPEC + 2))
152 # define MAX_ARGS NL_ARGMAX
153 #else
154 /* N for spec itself, plus 1 each for width and precision */
155 # define MAX_ARGS (MAX_ARGS_PER_SPEC + 2)
156 #endif
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;
167 #endif
169 /**********************************************************************/
171 #define SPEC_FLAGS " +0-#'I"
172 enum {
173 FLAG_SPACE = 0x01,
174 FLAG_PLUS = 0x02, /* must be 2 * FLAG_SPACE */
175 FLAG_ZERO = 0x04,
176 FLAG_MINUS = 0x08, /* must be 2 * FLAG_ZERO */
177 FLAG_HASH = 0x10,
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"
187 enum {
188 CONV_n = 0,
189 CONV_p,
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__
194 CONV_m,
195 #endif
196 CONV_custom0 /* must be last */
199 /* p x X o u d i */
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, \
210 /* C */ PA_WCHAR, \
211 /* S */ PA_WSTRING, \
212 /* c */ PA_CHAR, \
213 /* s */ PA_STRING, \
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), \
223 /* c */ (PA_CHAR), \
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'. */
237 /* #else */
238 /* #if LLONG_MAX != INTMAX_MAX */
239 /* #error fix QUAL_CHARS intmax_t entry 'j'! */
240 /* #endif */
241 /* #endif */
243 #ifdef PDS
244 # error PDS already defined!
245 #endif
246 #ifdef SS
247 # error SS already defined!
248 #endif
249 #ifdef IMS
250 # error IMS already defined!
251 #endif
253 #if PTRDIFF_MAX == INT_MAX
254 # define PDS 0
255 #elif PTRDIFF_MAX == LONG_MAX
256 # define PDS 4
257 #elif defined(LLONG_MAX) && (PTRDIFF_MAX == LLONG_MAX)
258 # define PDS 8
259 #else
260 # error fix QUAL_CHARS ptrdiff_t entry 't'!
261 #endif
263 #if SIZE_MAX == UINT_MAX
264 # define SS 0
265 #elif SIZE_MAX == ULONG_MAX
266 # define SS 4
267 #elif defined(LLONG_MAX) && (SIZE_MAX == ULLONG_MAX)
268 # define SS 8
269 #else
270 # error fix QUAL_CHARS size_t entries 'z', 'Z'!
271 #endif
273 #if INTMAX_MAX == INT_MAX
274 # define IMS 0
275 #elif INTMAX_MAX == LONG_MAX
276 # define IMS 4
277 #elif defined(LLONG_MAX) && (INTMAX_MAX == LLONG_MAX)
278 # define IMS 8
279 #else
280 # error fix QUAL_CHARS intmax_t entry 'j'!
281 #endif
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!!! */\
288 1, 8 \
291 /**********************************************************************/
293 #ifdef __STDIO_VA_ARG_PTR
294 # ifdef __BCC__
295 # define __va_arg_ptr(ap,type) (((type *)(ap += sizeof(type))) - 1)
296 # endif
298 # if 1
299 # ifdef __GNUC__
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))
305 # endif
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))) \
310 # endif
311 # endif
312 #endif /* __STDIO_VA_ARG_PTR */
314 #ifdef __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))))
317 #else
318 typedef union {
319 wchar_t wc;
320 unsigned int u;
321 unsigned long ul;
322 # ifdef ULLONG_MAX
323 unsigned long long ull;
324 # endif
325 # ifdef __UCLIBC_HAS_FLOATS__
326 double d;
327 long double ld;
328 # endif
329 void *p;
330 } argvalue_t;
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))
334 #endif
336 typedef struct {
337 const char *fmtpos; /* TODO: move below struct?? */
338 struct printf_info info;
339 #ifdef NL_ARGMAX
340 int maxposarg; /* > 0 if args are positional, 0 if not, -1 if unknown */
341 #endif
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];
346 va_list arg;
347 #ifdef __va_arg_ptr
348 void *argptr[MAX_ARGS];
349 #else
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];
354 #endif
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)
382 ppfs_t ppfs;
383 size_t i;
384 size_t count = 0;
386 if (_ppfs_init(&ppfs, template) >= 0) {
387 #ifdef NL_ARGMAX
388 if (ppfs.maxposarg > 0) {
389 /* Using positional args. */
390 count = ppfs.maxposarg;
391 if (n > count) {
392 n = count;
394 for (i = 0 ; i < n ; i++) {
395 *argtypes++ = ppfs.argtype[i];
397 } else
398 #endif
400 /* Not using positional args. */
401 while (*template) {
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) {
407 ++count;
408 if (n > 0) {
409 *argtypes++ = PA_INT;
410 --n;
413 if (ppfs.info.prec == INT_MIN) {
414 ++count;
415 if (n > 0) {
416 *argtypes++ = PA_INT;
417 --n;
420 for (i = 0 ; i < ppfs.num_data_args ; i++) {
421 if ((ppfs.argtype[i]) != __PA_NOARG) {
422 ++count;
423 if (n > 0) {
424 *argtypes++ = ppfs.argtype[i];
425 --n;
429 } else {
430 ++template;
436 return count;
439 #endif
441 #endif
442 /**********************************************************************/
443 #ifdef L__ppfs_init
445 int attribute_hidden _ppfs_init(register ppfs_t *ppfs, const char *fmt0)
447 int r;
449 /* First, zero out everything... argnumber[], argtype[], argptr[] */
450 memset(ppfs, 0, sizeof(ppfs_t)); /* TODO: nonportable???? */
451 #ifdef NL_ARGMAX
452 --ppfs->maxposarg; /* set to -1 */
453 #endif
454 ppfs->fmtpos = fmt0;
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.";
461 mbstate_t mbstate;
462 const char *p;
463 mbstate.__mask = 0; /* Initialize the mbstate. */
464 p = fmt0;
465 if (mbsrtowcs(NULL, &p, SIZE_MAX, &mbstate) == ((size_t)(-1))) {
466 ppfs->fmtpos = invalid_mbs;
467 return -1;
470 #endif /* __UCLIBC_HAS_LOCALE__ */
471 /* now set all argtypes to no-arg */
473 #if 1
474 /* TODO - use memset here since already "paid for"? */
475 register int *p = ppfs->argtype;
477 r = MAX_ARGS;
478 do {
479 *p++ = __PA_NOARG;
480 } while (--r);
481 #else
482 /* TODO -- get rid of this?? */
483 register char *p = (char *) ((MAX_ARGS-1) * sizeof(int));
485 do {
486 *((int *)(((char *)ppfs) + ((int)p) + offsetof(ppfs_t,argtype))) = __PA_NOARG;
487 p -= sizeof(int);
488 } while (p);
489 #endif
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;
499 while (*fmt) {
500 if ((*fmt == '%') && (*++fmt != '%')) {
501 ppfs->fmtpos = fmt; /* back up to the '%' */
502 r = _ppfs_parsespec(ppfs);
503 if (r < 0) {
504 return -1;
506 fmt = ppfs->fmtpos; /* update to one past end of spec */
507 } else {
508 ++fmt;
511 ppfs->fmtpos = fmt0; /* rewind */
514 #ifdef NL_ARGMAX
515 /* If we have positional args, make sure we know all the types. */
517 register int *p = ppfs->argtype;
518 r = ppfs->maxposarg;
519 while (--r >= 0) {
520 if ( *p == __PA_NOARG ) { /* missing arg type!!! */
521 return -1;
523 ++p;
526 #endif /* NL_ARGMAX */
528 return 0;
530 #endif
531 /**********************************************************************/
532 #ifdef L__ppfs_prepargs
533 void attribute_hidden _ppfs_prepargs(register ppfs_t *ppfs, va_list arg)
535 int i;
537 va_copy(ppfs->arg, arg);
539 #ifdef NL_ARGMAX
540 i = ppfs->maxposarg; /* init for positional args */
541 if (i > 0) {
542 ppfs->num_data_args = i;
543 ppfs->info.width = ppfs->info.prec = ppfs->maxposarg = 0;
544 _ppfs_setargs(ppfs);
545 ppfs->maxposarg = i;
547 #endif
549 #endif
550 /**********************************************************************/
551 #ifdef L__ppfs_setargs
553 void attribute_hidden _ppfs_setargs(register ppfs_t *ppfs)
555 #ifdef __va_arg_ptr
556 register void **p = ppfs->argptr;
557 #else
558 register argvalue_t *p = ppfs->argvalue;
559 #endif
560 int i;
562 #ifdef NL_ARGMAX
563 if (ppfs->maxposarg == 0) { /* initing for or no pos args */
564 #endif
565 if (ppfs->info.width == INT_MIN) {
566 ppfs->info.width =
567 #ifdef __va_arg_ptr
568 *(int *)
569 #endif
570 GET_VA_ARG(p,u,unsigned int,ppfs->arg);
572 if (ppfs->info.prec == INT_MIN) {
573 ppfs->info.prec =
574 #ifdef __va_arg_ptr
575 *(int *)
576 #endif
577 GET_VA_ARG(p,u,unsigned int,ppfs->arg);
579 i = 0;
580 while (i < ppfs->num_data_args) {
581 switch(ppfs->argtype[i++]) {
582 case (PA_INT|PA_FLAG_LONG_LONG):
583 #ifdef ULLONG_MAX
584 GET_VA_ARG(p,ull,unsigned long long,ppfs->arg);
585 break;
586 #endif
587 case (PA_INT|PA_FLAG_LONG):
588 #if ULONG_MAX != UINT_MAX
589 GET_VA_ARG(p,ul,unsigned long,ppfs->arg);
590 break;
591 #endif
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):
596 case PA_INT:
597 GET_VA_ARG(p,u,unsigned int,ppfs->arg);
598 break;
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);
602 break;
603 #ifdef __UCLIBC_HAS_FLOATS__
604 /* PA_FLOAT */
605 case PA_DOUBLE:
606 GET_VA_ARG(p,d,double,ppfs->arg);
607 break;
608 case (PA_DOUBLE|PA_FLAG_LONG_DOUBLE):
609 GET_VA_ARG(p,ld,long double,ppfs->arg);
610 break;
611 #else /* __UCLIBC_HAS_FLOATS__ */
612 case PA_DOUBLE:
613 case (PA_DOUBLE|PA_FLAG_LONG_DOUBLE):
614 assert(0);
615 continue;
616 #endif /* __UCLIBC_HAS_FLOATS__ */
617 default:
618 /* TODO -- really need to ensure this can't happen */
619 assert(ppfs->argtype[i-1] & PA_FLAG_PTR);
620 case PA_POINTER:
621 case PA_STRING:
622 case PA_WSTRING:
623 GET_VA_ARG(p,p,void *,ppfs->arg);
624 break;
625 case __PA_NOARG:
626 continue;
628 ++p;
630 #ifdef NL_ARGMAX
631 } else {
632 if (ppfs->info.width == INT_MIN) {
633 ppfs->info.width
634 = (int) GET_ARG_VALUE(p + ppfs->argnumber[0] - 1,u,unsigned int);
636 if (ppfs->info.prec == INT_MIN) {
637 ppfs->info.prec
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 = ' ';
650 #if 0
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. */
654 ppfs->info.prec = 1;
656 #endif
658 #endif
659 /**********************************************************************/
660 #ifdef L__ppfs_parsespec
662 /* Notes: argtype differs from glibc for the following:
663 * mine glibc
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 */
684 PA_POINTER,
685 PA_STRING,
686 PA_WSTRING,
687 PA_CHAR,
688 PA_INT|PA_FLAG_SHORT,
689 PA_INT,
690 PA_INT|PA_FLAG_LONG,
691 PA_INT|PA_FLAG_LONG_LONG,
692 PA_WCHAR,
693 #ifdef __UCLIBC_HAS_FLOATS__
694 /* PA_FLOAT, */
695 PA_DOUBLE,
696 PA_DOUBLE|PA_FLAG_LONG_DOUBLE,
697 #endif
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),
710 #ifdef ULLONG_MAX
711 PROMOTED_SIZE_OF(long long),
712 #else
713 PROMOTED_SIZE_OF(long), /* TODO -- is this correct? (above too) */
714 #endif
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),
720 #endif
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]);
729 do {
730 if (*--p == argtype) {
731 break;
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)
740 /* Quick test */
741 if (newtype == __PA_NOARG) {
742 return 0;
744 if ((curtype == __PA_NOARG) || (curtype == newtype)) {
745 return 1;
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);
752 #else
754 #define _is_equal_or_bigger_arg(C,N) (((C) == __PA_NOARG) || ((C) == (N)))
756 #endif
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;
771 int preci;
772 int width;
773 int flags;
774 int dataargtype;
775 int i;
776 int dpoint;
777 #ifdef NL_ARGMAX
778 int maxposarg;
779 #endif
780 int p_m_spec_chars;
781 int n;
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__
791 char buf[32];
792 #endif
794 /* WIDE note: we can test against '%' here since we don't allow */
795 /* WIDE note: other mappings of '%' in the wide char set. */
796 preci = -1;
797 argnumber[0] = 0;
798 argnumber[1] = 0;
799 argtype[0] = __PA_NOARG;
800 argtype[1] = __PA_NOARG;
801 #ifdef NL_ARGMAX
802 maxposarg = ppfs->maxposarg;
803 #endif
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. */
812 width = dpoint = 0;
813 flags = ppfs->info._flags & FLAG_WIDESTREAM;
814 if (flags == 0) {
815 fmt = ppfs->fmtpos;
816 } else {
817 fmt = buf + 1;
818 i = 0;
819 do {
820 buf[i] = (char) (((wchar_t *) ppfs->fmtpos)[i-1]);
821 if (buf[i] != (((wchar_t *) ppfs->fmtpos)[i-1])) {
822 return -1;
824 } while (buf[i++] && (i < sizeof(buf)));
825 buf[sizeof(buf)-1] = 0;
827 #else /* __UCLIBC_HAS_WCHAR__ */
828 width = flags = dpoint = 0;
829 fmt = ppfs->fmtpos;
830 #endif
832 assert(fmt[-1] == '%');
833 assert(fmt[0] != '%');
835 /* Process arg pos and/or flags and/or width and/or precision. */
836 width_precision:
837 p = fmt;
838 if (*fmt == '*') {
839 argtype[-dpoint] = PA_INT;
840 ++fmt;
842 i = 0;
843 while (isdigit(*fmt)) {
844 if (i < INT_MAX / 10
845 || (i == INT_MAX / 10 && (*fmt - '0') <= INT_MAX % 10)) {
846 i = (i * 10) + (*fmt - '0');
847 } else {
848 i = INT_MAX; /* best we can do... */
850 ++fmt;
852 if (p[-1] == '%') { /* Check for a position. */
854 /* TODO: if val not in range, then error */
856 #ifdef NL_ARGMAX
857 if ((*fmt == '$') && (i > 0)) {/* Positional spec. */
858 ++fmt;
859 if (maxposarg == 0) {
860 return -1;
862 argnumber[2] = i;
863 if (argnumber[2] > maxposarg) {
864 maxposarg = i;
866 /* Now fall through to check flags. */
867 } else {
868 if (maxposarg > 0) {
869 # ifdef __UCLIBC_HAS_PRINTF_M_SPEC__
870 if (*fmt == 'm') {
871 goto PREC_WIDTH;
873 # endif /* __UCLIBC_HAS_PRINTF_M_SPEC__ */
874 return -1;
876 maxposarg = 0; /* Possible redundant store, but cuts size. */
878 if ((fmt > p) && (*p != '0')) {
879 goto PREC_WIDTH;
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. */
887 return -1;
890 if ((fmt > p) && (*p != '0')) {
891 goto PREC_WIDTH;
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. */
899 i = 1;
900 p = spec_flags;
902 do {
903 if (*fmt == *p++) {
904 ++fmt;
905 flags |= i;
906 goto restart_flags;
908 i += i; /* Better than i <<= 1 for bcc */
909 } while (*p);
910 i = 0;
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;
922 PREC_WIDTH:
923 if (*p == '*') { /* Prec or width takes an arg. */
924 #ifdef NL_ARGMAX
925 if (maxposarg) {
926 if ((*fmt++ != '$') || (i <= 0)) {
927 /* Using pos args and no $ or invalid arg number. */
928 return -1;
930 argnumber[-dpoint] = i;
931 } else
932 #endif
933 if (++p != fmt) {
934 /* Not using pos args but digits followed *. */
935 return -1;
937 i = INT_MIN;
940 if (!dpoint) {
941 width = i;
942 if (*fmt == '.') {
943 ++fmt;
944 dpoint = -1; /* To use as default precison. */
945 goto width_precision;
947 } else {
948 preci = i;
951 /* Process qualifier. */
952 p = qual_chars;
953 do {
954 if (*fmt == *p) {
955 ++fmt;
956 break;
958 } while (*++p);
959 if ((p - qual_chars < 2) && (*fmt == *p)) {
960 p += ((sizeof(qual_chars)-2) / 2);
961 ++fmt;
963 dataargtype = ((int)(p[(sizeof(qual_chars)-2) / 2])) << 8;
965 /* Process conversion specifier. */
966 if (!*fmt) {
967 return -1;
970 p = spec_chars;
972 do {
973 if (*fmt == *p) {
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;
982 p = spec_ranges-1;
983 while (p_m_spec_chars > *++p) {}
985 i = p - spec_ranges;
986 argtype[2] = (dataargtype | spec_or_mask[i]) & spec_and_mask[i];
987 p = spec_chars;
988 break;
990 } while(*++p);
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;
999 if (!*p) {
1000 #ifdef __UCLIBC_HAS_PRINTF_M_SPEC__
1001 if (*fmt == 'm') {
1002 ppfs->conv_num = CONV_m;
1003 ppfs->num_data_args = 0;
1004 } else
1005 #endif
1007 #ifndef __UCLIBC_HAS_GLIBC_CUSTOM_PRINTF__
1008 return -1; /* Error */
1009 #else
1010 /* Handle custom arg -- WARNING -- overwrites p!!! */
1011 ppfs->conv_num = CONV_custom0;
1012 p = _custom_printf_spec;
1013 while (1) {
1014 if (*p == *fmt) {
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! */
1020 break;
1022 if (++p >= (_custom_printf_spec + MAX_USER_SPEC))
1023 return -1; /* Error */
1025 #endif
1029 #ifdef NL_ARGMAX
1030 if (maxposarg > 0) {
1031 i = 0;
1032 do {
1033 /* Update maxposarg and check that NL_ARGMAX is not exceeded. */
1034 n = ((i <= 2)
1035 ? (ppfs->argnumber[i] = argnumber[i])
1036 : argnumber[2] + (i-2));
1037 if (n > maxposarg) {
1038 maxposarg = n;
1039 if (maxposarg > NL_ARGMAX) {
1040 return -1;
1043 --n;
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);
1049 } else
1050 #endif /* NL_ARGMAX */
1052 ppfs->argnumber[2] = 1;
1053 memcpy(ppfs->argtype, argtype + 2, ppfs->num_data_args * sizeof(int));
1056 #ifdef NL_ARGMAX
1057 ppfs->maxposarg = maxposarg;
1058 #endif
1060 #ifdef __UCLIBC_HAS_WCHAR__
1061 flags = ppfs->info._flags & FLAG_WIDESTREAM;
1062 if (flags == 0) {
1063 ppfs->fmtpos = ++fmt;
1064 } else {
1065 ppfs->fmtpos = (const char *) (((const wchar_t *)(ppfs->fmtpos))
1066 + (fmt - buf) );
1068 #else /* __UCLIBC_HAS_WCHAR__ */
1069 ppfs->fmtpos = ++fmt;
1070 #endif
1072 return ppfs->num_data_args + 2;
1075 #endif
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)
1084 register char *r;
1085 register char *p;
1087 if (spec && (arginfo != NULL)) { /* TODO -- check if spec is valid char */
1088 r = NULL;
1089 p = _custom_printf_spec + MAX_USER_SPEC;
1090 do {
1091 --p;
1092 if (!*p) {
1093 r = p;
1095 #ifdef __BCC__
1096 else /* bcc generates less code with fall-through */
1097 #endif
1098 if (*p == spec) {
1099 r = p;
1100 p = _custom_printf_spec;
1102 } while (p > _custom_printf_spec);
1104 if (r) {
1105 if (handler) {
1106 *r = spec;
1107 _custom_printf_handler[(int)(r - p)] = handler;
1108 _custom_printf_arginfo[(int)(r - p)] = arginfo;
1109 } else {
1110 *r = 0;
1112 return 0;
1114 /* TODO -- if asked to unregister a non-existent spec, return what? */
1116 return -1;
1119 #endif
1121 #endif
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)
1148 size_t r = 0;
1150 if (type & 0x80) { /* Some type of padding needed. */
1151 int buflen = strlen((const char *) buf);
1152 len -= buflen;
1153 if (len > 0) {
1154 r = _charpad(fp, (type & 0x7f), len);
1155 if (r != len) {
1156 return r;
1159 len = buflen;
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!!! */
1182 wchar_t wbuf[64];
1183 mbstate_t mbstate;
1184 size_t todo, r, n;
1186 mbstate.__mask = 0;
1187 todo = wclen;
1189 while (todo) {
1190 r = mbsrtowcs(wbuf, &s,
1191 ((todo <= sizeof(wbuf)/sizeof(wbuf[0]))
1192 ? todo
1193 : sizeof(wbuf)/sizeof(wbuf[0])),
1194 &mbstate);
1195 assert(((ssize_t)r) > 0);
1196 n = _outnwcs(stream, wbuf, r);
1197 todo -= n;
1198 if (n != r) {
1199 break;
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;
1212 size_t r = 0;
1213 int i;
1215 if (type & 0x80) { /* Some type of padding needed */
1216 int buflen = strlen(s);
1217 len -= buflen;
1218 if (len > 0) {
1219 r = _charpad(fp, (type & 0x7f), len);
1220 if (r != len) {
1221 return r;
1224 len = buflen;
1227 if (len > 0) {
1228 i = 0;
1229 do {
1230 #ifdef __LOCALE_C_ONLY
1231 wbuf[i] = s[i];
1232 #else
1234 # ifdef __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__
1235 if (s[i] == ',') {
1236 wbuf[i] = __UCLIBC_CURLOCALE->thousands_sep_wc;
1237 } else
1238 # endif
1239 if (s[i] == '.') {
1240 wbuf[i] = __UCLIBC_CURLOCALE->decimal_point_wc;
1241 } else {
1242 wbuf[i] = s[i];
1244 #endif /* __LOCALE_C_ONLY */
1246 } while (++i < len);
1248 r += OUTNSTR(fp, wbuf, len);
1251 return r;
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.";
1259 int r;
1261 /* First, zero out everything... argnumber[], argtype[], argptr[] */
1262 memset(ppfs, 0, sizeof(ppfs_t)); /* TODO: nonportable???? */
1263 #ifdef NL_ARGMAX
1264 --ppfs->maxposarg; /* set to -1 */
1265 #endif
1266 ppfs->fmtpos = (const char *) fmt0;
1267 ppfs->info._flags = FLAG_WIDESTREAM;
1270 mbstate_t mbstate;
1271 const wchar_t *p;
1272 mbstate.__mask = 0; /* Initialize the mbstate. */
1273 p = fmt0;
1274 if (wcsrtombs(NULL, &p, SIZE_MAX, &mbstate) == ((size_t)(-1))) {
1275 ppfs->fmtpos = (const char *) invalid_wcs;
1276 return -1;
1280 /* now set all argtypes to no-arg */
1282 #if 1
1283 /* TODO - use memset here since already "paid for"? */
1284 register int *p = ppfs->argtype;
1286 r = MAX_ARGS;
1287 do {
1288 *p++ = __PA_NOARG;
1289 } while (--r);
1290 #else
1291 /* TODO -- get rid of this?? */
1292 register char *p = (char *) ((MAX_ARGS-1) * sizeof(int));
1294 do {
1295 *((int *)(((char *)ppfs) + ((int)p) + offsetof(ppfs_t,argtype))) = __PA_NOARG;
1296 p -= sizeof(int);
1297 } while (p);
1298 #endif
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;
1308 while (*fmt) {
1309 if ((*fmt == '%') && (*++fmt != '%')) {
1310 ppfs->fmtpos = (const char *) fmt; /* back up to the '%' */
1311 r = _ppfs_parsespec(ppfs);
1312 if (r < 0) {
1313 return -1;
1315 fmt = (const wchar_t *) ppfs->fmtpos; /* update to one past end of spec */
1316 } else {
1317 ++fmt;
1320 ppfs->fmtpos = (const char *) fmt0; /* rewind */
1323 #ifdef NL_ARGMAX
1324 /* If we have positional args, make sure we know all the types. */
1326 register int *p = ppfs->argtype;
1327 r = ppfs->maxposarg;
1328 while (--r >= 0) {
1329 if ( *p == __PA_NOARG ) { /* missing arg type!!! */
1330 return -1;
1332 ++p;
1335 #endif /* NL_ARGMAX */
1337 return 0;
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... */
1348 FMT_TYPE pad[1];
1350 *pad = padchar;
1351 while (todo && (OUTNSTR(stream, (const char *) pad, 1) == 1)) {
1352 --todo;
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";
1365 /* 0 2 4 6 9 11*/
1366 #else
1367 static const wchar_t prefix[] = L"+\0-\0 \0000x\0000X";
1368 #endif
1369 enum {
1370 PREFIX_PLUS = 0,
1371 PREFIX_MINUS = 2,
1372 PREFIX_SPACE = 4,
1373 PREFIX_LWR_X = 6,
1374 PREFIX_UPR_X = 9,
1375 PREFIX_NONE = 11
1378 #ifdef __va_arg_ptr
1379 const void * const *argptr;
1380 #else
1381 const void * argptr[MAX_ARGS_PER_SPEC];
1382 #endif
1383 int *argtype;
1384 #ifdef __UCLIBC_HAS_WCHAR__
1385 const wchar_t *ws = NULL;
1386 mbstate_t mbstate;
1387 #endif
1388 size_t slen;
1389 #ifdef L__vfprintf_internal
1390 #define SLEN slen
1391 #else
1392 size_t SLEN;
1393 wchar_t wbuf[2];
1394 #endif
1395 int base;
1396 int numpad;
1397 int alphacase;
1398 int numfill = 0; /* TODO: fix */
1399 int prefix_num = PREFIX_NONE;
1400 char padchar = ' ';
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. */
1405 char buf[128];
1407 #ifdef NDEBUG
1408 _ppfs_parsespec(ppfs);
1409 #else
1410 if (_ppfs_parsespec(ppfs) < 0) { /* TODO: just for debugging */
1411 abort();
1413 #endif
1414 _ppfs_setargs(ppfs);
1416 argtype = ppfs->argtype + ppfs->argnumber[2] - 1;
1417 /* Deal with the argptr vs argvalue issue. */
1418 #ifdef __va_arg_ptr
1419 argptr = (const void * const *) ppfs->argptr;
1420 # ifdef NL_ARGMAX
1421 if (ppfs->maxposarg > 0) { /* Using positional args... */
1422 argptr += ppfs->argnumber[2] - 1;
1424 # endif
1425 #else
1426 /* Need to build a local copy... */
1428 register argvalue_t *p = ppfs->argvalue;
1429 int i;
1430 # ifdef NL_ARGMAX
1431 if (ppfs->maxposarg > 0) { /* Using positional args... */
1432 p += ppfs->argnumber[2] - 1;
1434 # endif
1435 for (i = 0 ; i < ppfs->num_data_args ; i++ ) {
1436 argptr[i] = (void *) p++;
1439 #endif
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));
1447 return 0;
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)];
1453 if (base == 10) {
1454 if (PRINT_INFO_FLAG_VAL(&(ppfs->info),group)) {
1455 alphacase = __UIM_GROUP;
1457 if (PRINT_INFO_FLAG_VAL(&(ppfs->info),i18n)) {
1458 alphacase |= 0x80;
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 */
1471 base = -base;
1473 if (ppfs->info.prec < 0) { /* Ignore '0' flag if prec specified. */
1474 padchar = ppfs->info.pad;
1476 s = _uintmaxtostr(buf + sizeof(buf) - 1,
1477 (uintmax_t)
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 */
1481 if (*s == '-') {
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
1494 const char *q = s;
1495 mbstate.__mask = 0; /* Initialize the mbstate. */
1496 SLEN = mbsrtowcs(NULL, &q, 0, &mbstate);
1498 #endif
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);
1511 if (*s == '0') {
1512 if (prefix_num >= PREFIX_LWR_X) {
1513 prefix_num = PREFIX_NONE;
1515 if (ppfs->conv_num == CONV_p) {/* null pointer */
1516 s = "(nil)";
1517 #ifdef L__vfwprintf_internal
1518 SLEN =
1519 #endif
1520 slen = 5;
1521 numfill = 0;
1522 } else if (numfill == 0) { /* if precision 0, no output */
1523 #ifdef L__vfwprintf_internal
1524 SLEN =
1525 #endif
1526 slen = 0;
1529 numfill = ((numfill > SLEN) ? numfill - SLEN : 0);
1530 } else if (ppfs->conv_num <= CONV_A) { /* floating point */
1531 #ifdef __UCLIBC_HAS_FLOATS__
1532 ssize_t nf;
1533 nf = _fpmaxtostr(stream,
1534 (__fpmax_t)
1535 (PRINT_INFO_FLAG_VAL(&(ppfs->info),is_long_double)
1536 ? *(long double *) *argptr
1537 : (long double) (* (double *) *argptr)),
1538 &ppfs->info, FP_OUT );
1539 if (nf < 0) {
1540 return -1;
1542 *count += nf;
1544 return 0;
1545 #else /* __UCLIBC_HAS_FLOATS__ */
1546 return -1; /* TODO -- try to continue? */
1547 #endif
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);
1555 if (!ws) {
1556 goto NULL_STRING;
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)
1565 ? ppfs->info.prec
1566 : SIZE_MAX),
1567 &mbstate);
1568 if (slen == ((size_t)-1)) {
1569 return -1; /* EILSEQ */
1571 } else { /* wide char */
1572 s = buf;
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__ */
1580 return -1;
1581 #endif
1582 } else if (ppfs->conv_num <= CONV_s) { /* char or string */
1583 if (ppfs->conv_num == CONV_s) { /* string */
1584 s = *((char **) (*argptr));
1585 if (s) {
1586 #ifdef __UCLIBC_HAS_PRINTF_M_SPEC__
1587 SET_STRING_LEN:
1588 #endif
1589 slen = strnlen(s, ((ppfs->info.prec >= 0)
1590 ? ppfs->info.prec : SIZE_MAX));
1591 } else {
1592 #ifdef __UCLIBC_HAS_WCHAR__
1593 NULL_STRING:
1594 #endif
1595 s = "(null)";
1596 slen = 6;
1597 /* Use an empty string rather than truncation if precision is too small. */
1598 if (ppfs->info.prec >= 0 && ppfs->info.prec < slen)
1599 slen = 0;
1601 } else { /* char */
1602 s = buf;
1603 *s = (unsigned char)(*((const int *) *argptr));
1604 s[1] = 0;
1605 slen = 1;
1608 #else /* L__vfprintf_internal */
1610 if (ppfs->conv_num == CONV_S) { /* wide string */
1611 ws = *((wchar_t **) (*argptr));
1612 if (!ws) {
1613 goto NULL_STRING;
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));
1619 CHAR_CASE:
1620 ws = wbuf;
1621 wbuf[1] = 0;
1622 SLEN = 1;
1625 } else if (ppfs->conv_num <= CONV_s) { /* char or string */
1627 if (ppfs->conv_num == CONV_s) { /* string */
1628 s = *((char **) (*argptr));
1629 if (s) {
1630 #ifdef __UCLIBC_HAS_PRINTF_M_SPEC__
1631 SET_STRING_LEN:
1632 #endif
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. */
1639 const char *q = s;
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),
1644 &mbstate);
1646 if (SLEN == ((size_t)(-1))) {
1647 return -1; /* EILSEQ */
1649 } else {
1650 NULL_STRING:
1651 s = "(null)";
1652 SLEN = slen = 6;
1653 /* Use an empty string rather than truncation if precision is too small. */
1654 if (ppfs->info.prec >= 0 && ppfs->info.prec < slen)
1655 SLEN = slen = 0;
1657 } else { /* char */
1658 *wbuf = btowc( (unsigned char)(*((const int *) *argptr)) );
1659 goto CHAR_CASE;
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;
1668 #endif
1669 } else {
1670 #ifdef __UCLIBC_HAS_GLIBC_CUSTOM_PRINTF__
1671 assert(ppfs->conv_num == CONV_custom0);
1673 s = _custom_printf_spec;
1674 do {
1675 if (*s == ppfs->info.spec) {
1676 int rv;
1677 /* TODO -- check return value for sanity? */
1678 rv = (*_custom_printf_handler
1679 [(int)(s-_custom_printf_spec)])
1680 (stream, &ppfs->info, argptr);
1681 if (rv < 0) {
1682 return -1;
1684 *count += rv;
1685 return 0;
1687 } while (++s < (_custom_printf_spec + MAX_USER_SPEC));
1688 #endif /* __UCLIBC_HAS_GLIBC_CUSTOM_PRINTF__ */
1689 assert(0);
1690 return -1;
1694 size_t t;
1696 t = SLEN + numfill;
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 */
1704 numfill += numpad;
1705 numpad = 0;
1708 /* Now handle the output itself. */
1709 if (!PRINT_INFO_FLAG_VAL(&(ppfs->info),left)) {
1710 if (_charpad(stream, ' ', numpad) != numpad) {
1711 return -1;
1713 numpad = 0;
1715 OUTPUT(stream, prefix + prefix_num);
1717 if (_charpad(stream, '0', numfill) != numfill) {
1718 return -1;
1721 #ifdef L__vfprintf_internal
1723 # ifdef __UCLIBC_HAS_WCHAR__
1724 if (!ws) {
1725 assert(s);
1726 if (_outnstr(stream, s, slen) != slen) {
1727 return -1;
1729 } else { /* wide string */
1730 size_t t;
1731 mbstate.__mask = 0; /* Initialize the mbstate. */
1732 while (slen) {
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) {
1737 return -1;
1739 slen -= t;
1742 # else /* __UCLIBC_HAS_WCHAR__ */
1743 if (_outnstr(stream, (const unsigned char *) s, slen) != slen) {
1744 return -1;
1746 # endif
1748 #else /* L__vfprintf_internal */
1750 if (!ws) {
1751 assert(s);
1752 if (_outnstr(stream, s, SLEN) != SLEN) {
1753 return -1;
1755 } else {
1756 if (_outnwcs(stream, ws, SLEN) != SLEN) {
1757 return -1;
1761 #endif /* L__vfprintf_internal */
1762 if (_charpad(stream, ' ', numpad) != numpad) {
1763 return -1;
1767 return 0;
1771 int VFPRINTF_internal (FILE * __restrict stream,
1772 const FMT_TYPE * __restrict format,
1773 va_list arg)
1775 ppfs_t ppfs;
1776 int count, r;
1777 register const FMT_TYPE *s;
1779 count = 0;
1780 s = format;
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);
1787 #endif
1788 count = -1;
1789 } else {
1790 _ppfs_prepargs(&ppfs, arg); /* This did a va_copy!!! */
1792 do {
1793 while (*format && (*format != '%')) {
1794 ++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)) {
1800 count = -1;
1801 break;
1803 count += r;
1806 if (!*format) { /* we're done */
1807 break;
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);
1815 if (r < 0) {
1816 count = -1;
1817 break;
1819 s = format = (const FMT_TYPE *) ppfs.fmtpos;
1820 } else { /* %% means literal %, so start new string */
1821 s = ++format;
1822 ++format;
1824 } while (1);
1826 va_end(ppfs.arg); /* Need to clean up after va_copy! */
1829 /* #if defined(L__vfprintf_internal) && defined(__UCLIBC_HAS_WCHAR__) */
1830 /* DONE: */
1831 /* #endif */
1833 return count;
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).
1850 #ifdef L_vfprintf
1851 # define VFPRINTF vfprintf
1852 # define VFPRINTF_internal _vfprintf_internal
1853 # define FMT_TYPE char
1854 #else
1855 # define VFPRINTF vfwprintf
1856 # define VFPRINTF_internal _vfwprintf_internal
1857 # define FMT_TYPE wchar_t
1858 #endif
1860 libc_hidden_proto(VFPRINTF)
1861 int VFPRINTF (FILE * __restrict stream,
1862 const FMT_TYPE * __restrict format,
1863 va_list arg)
1865 int count;
1866 __STDIO_AUTO_THREADLOCK_VAR;
1868 __STDIO_AUTO_THREADLOCK(stream);
1871 #ifdef L_vfprintf
1872 (!__STDIO_STREAM_IS_NARROW_WRITING(stream)
1873 && __STDIO_STREAM_TRANS_TO_WRITE(stream, __FLAG_NARROW))
1874 #else
1875 (!__STDIO_STREAM_IS_WIDE_WRITING(stream)
1876 && __STDIO_STREAM_TRANS_TO_WRITE(stream, __FLAG_WIDE))
1877 #endif
1879 count = -1;
1880 } else {
1881 count = VFPRINTF_internal(stream, format, arg);
1884 __STDIO_AUTO_THREADUNLOCK(stream);
1886 return count;
1888 libc_hidden_def(VFPRINTF)
1889 #endif /* defined(L_vfprintf) || defined(L_vfwprintf) */
1891 /**********************************************************************/