OSDN Git Service

Replace FSF snail mail address with URLs
[uclinux-h8/uClibc.git] / libc / stdio / _vfprintf.c
1 /*  Copyright (C) 2002-2004     Manuel Novoa III
2  *  My stdio library for linux and (soon) elks.
3  *
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.
8  *
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.
13  *
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/>.
17  */
18
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.
23  */
24
25 /*  ATTENTION!   ATTENTION!   ATTENTION!   ATTENTION!   ATTENTION!
26  *
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
31  *
32  *  ATTENTION!   ATTENTION!   ATTENTION!   ATTENTION!   ATTENTION! */
33
34
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>
40  *
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.
44  *
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.
48  *
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.
54  *
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.
62  *
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.
66  *
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.
72  *
73  * Aug 31, 2003
74  * Fix precision bug for %g conversion specifier when using %f style.
75  *
76  * Sep 5, 2003
77  * Implement *s*scanf for the non-buffered stdio case with old_vfprintf.
78  *
79  * Sep 23, 2003
80  * vfprintf was not always checking for narrow stream orientation.
81  */
82
83 /* TODO:
84  *
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.
89  */
90
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>
103
104 #ifdef __UCLIBC_HAS_THREADS__
105 # include <stdio_ext.h>
106 # include <pthread.h>
107 #endif
108
109 #ifdef __UCLIBC_HAS_WCHAR__
110 # include <wchar.h>
111 #endif
112
113 #include <bits/uClibc_uintmaxtostr.h>
114 #include <bits/uClibc_va_copy.h>
115
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.
119  */
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
123
124 /**********************************************************************/
125 /* These provide some control over printf's feature set */
126
127 /* Now controlled by uClibc_config.h. */
128 /* #define __UCLIBC_HAS_FLOATS__ 1 */
129
130 /* Now controlled by uClibc_config.h. */
131 /* #define __UCLIBC_HAS_PRINTF_M_SPEC__ */
132
133
134 /**********************************************************************/
135
136 #include "_fpmaxtostr.h"
137
138 #undef __STDIO_HAS_VSNPRINTF
139 #if defined(__STDIO_BUFFERS) || defined(__USE_OLD_VFPRINTF__) || defined(__UCLIBC_HAS_GLIBC_CUSTOM_STREAMS__)
140 # define __STDIO_HAS_VSNPRINTF 1
141 #endif
142
143 /**********************************************************************/
144
145 /* Now controlled by uClibc_config.h. */
146 /* #define __UCLIBC_HAS_GLIBC_CUSTOM_PRINTF__ */
147
148 #ifdef __UCLIBC_MJN3_ONLY__
149 # ifdef L_register_printf_function
150 /* emit only once */
151 #  warning WISHLIST: Make MAX_USER_SPEC configurable?
152 #  warning WISHLIST: Make MAX_ARGS_PER_SPEC configurable?
153 # endif
154 #endif
155
156 #ifdef __UCLIBC_HAS_GLIBC_CUSTOM_PRINTF__
157 # define MAX_USER_SPEC       10
158 # define MAX_ARGS_PER_SPEC    5
159 #else
160 # undef MAX_USER_SPEC
161 # define MAX_ARGS_PER_SPEC    1
162 #endif
163
164 #if MAX_ARGS_PER_SPEC < 1
165 # error MAX_ARGS_PER_SPEC < 1!
166 # undef MAX_ARGS_PER_SPEC
167 # define MAX_ARGS_PER_SPEC    1
168 #endif
169
170 #if defined(NL_ARGMAX) && (NL_ARGMAX < 9)
171 # error NL_ARGMAX < 9!
172 #endif
173
174 #if defined(NL_ARGMAX) && (NL_ARGMAX >= (MAX_ARGS_PER_SPEC + 2))
175 # define MAX_ARGS        NL_ARGMAX
176 #else
177 /* N for spec itself, plus 1 each for width and precision */
178 # define MAX_ARGS        (MAX_ARGS_PER_SPEC + 2)
179 #endif
180
181 /**********************************************************************/
182
183 #define __PA_FLAG_INTMASK \
184         (__PA_FLAG_CHAR|PA_FLAG_SHORT|__PA_FLAG_INT|PA_FLAG_LONG|PA_FLAG_LONG_LONG)
185
186 #ifdef __UCLIBC_HAS_GLIBC_CUSTOM_PRINTF__
187 extern printf_function _custom_printf_handler[MAX_USER_SPEC] attribute_hidden;
188 extern printf_arginfo_function *_custom_printf_arginfo[MAX_USER_SPEC] attribute_hidden;
189 extern char *_custom_printf_spec attribute_hidden;
190 #endif
191
192 /**********************************************************************/
193
194 #define SPEC_FLAGS              " +0-#'I"
195 enum {
196         FLAG_SPACE      = 0x01,
197         FLAG_PLUS       = 0x02, /* must be 2 * FLAG_SPACE */
198         FLAG_ZERO       = 0x04,
199         FLAG_MINUS      = 0x08, /* must be 2 * FLAG_ZERO */
200         FLAG_HASH       = 0x10,
201         FLAG_THOUSANDS  = 0x20,
202         FLAG_I18N       = 0x40, /* only works for d, i, u */
203         FLAG_WIDESTREAM = 0x80
204 };
205
206 /**********************************************************************/
207
208 /* float layout          01234567890123456789   TODO: B?*/
209 #define SPEC_CHARS              "npxXoudifFeEgGaACScs"
210 enum {
211         CONV_n = 0,
212         CONV_p,
213         CONV_x, CONV_X, CONV_o, CONV_u, CONV_d, CONV_i,
214         CONV_f, CONV_F, CONV_e, CONV_E, CONV_g, CONV_G, CONV_a, CONV_A,
215         CONV_C, CONV_S, CONV_c, CONV_s,
216 #ifdef __UCLIBC_HAS_PRINTF_M_SPEC__
217         CONV_m,
218 #endif
219         CONV_custom0                            /* must be last */
220 };
221
222 /*                         p   x   X  o   u   d   i */
223 #define SPEC_BASE       { 16, 16, 16, 8, 10, 10, 10 }
224
225 #define SPEC_RANGES     { CONV_n, CONV_p, CONV_i, CONV_A, \
226                           CONV_C, CONV_S, CONV_c, CONV_s, CONV_custom0 }
227
228 #define SPEC_OR_MASK             { \
229         /* n */                 (PA_FLAG_PTR|PA_INT), \
230         /* p */                 PA_POINTER, \
231         /* oxXudi */    PA_INT, \
232         /* fFeEgGaA */  PA_DOUBLE, \
233         /* C */                 PA_WCHAR, \
234         /* S */                 PA_WSTRING, \
235         /* c */                 PA_CHAR, \
236         /* s */                 PA_STRING, \
237 }
238
239 #define SPEC_AND_MASK           { \
240         /* n */                 (PA_FLAG_PTR|__PA_INTMASK), \
241         /* p */                 PA_POINTER, \
242         /* oxXudi */    (__PA_INTMASK), \
243         /* fFeEgGaA */  (PA_FLAG_LONG_DOUBLE|PA_DOUBLE), \
244         /* C */                 (PA_WCHAR), \
245         /* S */                 (PA_WSTRING), \
246         /* c */                 (PA_CHAR), \
247         /* s */                 (PA_STRING), \
248 }
249
250 /**********************************************************************/
251 /*
252  * In order to ease translation to what arginfo and _print_info._flags expect,
253  * we map:  0:int  1:char  2:longlong 4:long  8:short
254  * and then _flags |= (((q << 7) + q) & 0x701) and argtype |= (_flags & 0x701)
255  */
256
257 /* TODO -- Fix the table below to take into account stdint.h. */
258 /*  #ifndef LLONG_MAX */
259 /*  #error fix QUAL_CHARS for no long long!  Affects 'L', 'j', 'q', 'll'. */
260 /*  #else */
261 /*  #if LLONG_MAX != INTMAX_MAX */
262 /*  #error fix QUAL_CHARS intmax_t entry 'j'! */
263 /*  #endif */
264 /*  #endif */
265
266 #ifdef PDS
267 # error PDS already defined!
268 #endif
269 #ifdef SS
270 # error SS already defined!
271 #endif
272 #ifdef IMS
273 # error IMS already defined!
274 #endif
275
276 #if PTRDIFF_MAX == INT_MAX
277 # define PDS            0
278 #elif PTRDIFF_MAX == LONG_MAX
279 # define PDS            4
280 #elif defined(LLONG_MAX) && (PTRDIFF_MAX == LLONG_MAX)
281 # define PDS            8
282 #else
283 # error fix QUAL_CHARS ptrdiff_t entry 't'!
284 #endif
285
286 #if SIZE_MAX == UINT_MAX
287 # define SS             0
288 #elif SIZE_MAX == ULONG_MAX
289 # define SS             4
290 #elif defined(LLONG_MAX) && (SIZE_MAX == ULLONG_MAX)
291 # define SS             8
292 #else
293 # error fix QUAL_CHARS size_t entries 'z', 'Z'!
294 #endif
295
296 #if INTMAX_MAX == INT_MAX
297 # define IMS            0
298 #elif INTMAX_MAX == LONG_MAX
299 # define IMS            4
300 #elif defined(LLONG_MAX) && (INTMAX_MAX == LLONG_MAX)
301 # define IMS            8
302 #else
303 # error fix QUAL_CHARS intmax_t entry 'j'!
304 #endif
305
306 #define QUAL_CHARS              { \
307         /* j:(u)intmax_t z:(s)size_t  t:ptrdiff_t  \0:int */ \
308         /* q:long_long  Z:(s)size_t */ \
309         'h',   'l',  'L',  'j',  'z',  't',  'q', 'Z',  0, \
310          2,     4,    8,  IMS,   SS,  PDS,    8,  SS,   0, /* TODO -- fix!!! */\
311          1,     8 \
312 }
313
314 /**********************************************************************/
315
316 #ifdef __STDIO_VA_ARG_PTR
317 # ifdef __BCC__
318 #  define __va_arg_ptr(ap,type)         (((type *)(ap += sizeof(type))) - 1)
319 # endif
320
321 # if 1
322 #  ifdef __GNUC__
323 /* TODO -- need other than for 386 as well! */
324
325 #   ifndef __va_rounded_size
326 #    define __va_rounded_size(TYPE) \
327         (((sizeof (TYPE) + sizeof (int) - 1) / sizeof (int)) * sizeof (int))
328 #   endif
329 #   define __va_arg_ptr(AP, TYPE)  \
330         (AP = (va_list) ((char *) (AP) + __va_rounded_size (TYPE)),  \
331          ((void *) ((char *) (AP) - __va_rounded_size (TYPE)))  \
332         )
333 #  endif
334 # endif
335 #endif /* __STDIO_VA_ARG_PTR */
336
337 #ifdef __va_arg_ptr
338 # define GET_VA_ARG(AP,F,TYPE,ARGS)     (*(AP) = __va_arg_ptr(ARGS,TYPE))
339 # define GET_ARG_VALUE(AP,F,TYPE)       (*((TYPE *)(*(AP))))
340 #else
341 typedef union {
342         wchar_t wc;
343         unsigned int u;
344         unsigned long ul;
345 # ifdef ULLONG_MAX
346         unsigned long long ull;
347 # endif
348 # ifdef __UCLIBC_HAS_FLOATS__
349         double d;
350         long double ld;
351 # endif
352         void *p;
353 } argvalue_t;
354
355 # define GET_VA_ARG(AU,F,TYPE,ARGS)     (AU->F = va_arg(ARGS,TYPE))
356 # define GET_ARG_VALUE(AU,F,TYPE)       ((TYPE)((AU)->F))
357 #endif
358
359 typedef struct {
360         const char *fmtpos;                     /* TODO: move below struct?? */
361         struct printf_info info;
362 #ifdef NL_ARGMAX
363         int maxposarg;                          /* > 0 if args are positional, 0 if not, -1 if unknown */
364 #endif
365         int num_data_args;                      /* TODO: use sentinal??? */
366         unsigned int conv_num;
367         unsigned char argnumber[4]; /* width | prec | 1st data | unused */
368         int argtype[MAX_ARGS];
369         va_list arg;
370 #ifdef __va_arg_ptr
371         void *argptr[MAX_ARGS];
372 #else
373 /* if defined(NL_ARGMAX) || defined(__UCLIBC_HAS_GLIBC_CUSTOM_PRINTF__) */
374         /* While this is wasteful of space in the case where pos args aren't
375          * enabled, it is also needed to support custom printf handlers. */
376         argvalue_t argvalue[MAX_ARGS];
377 #endif
378 } ppfs_t;                                               /* parse printf format state */
379
380 /**********************************************************************/
381
382 /* TODO: fix printf to return 0 and set errno if format error.  Standard says
383    only returns -1 if sets error indicator for the stream. */
384
385 extern int _ppfs_init(ppfs_t *ppfs, const char *fmt0) attribute_hidden; /* validates */
386 extern void _ppfs_prepargs(ppfs_t *ppfs, va_list arg) attribute_hidden; /* sets posargptrs */
387 extern void _ppfs_setargs(ppfs_t *ppfs) attribute_hidden; /* sets argptrs for current spec */
388 extern int _ppfs_parsespec(ppfs_t *ppfs) attribute_hidden; /* parses specifier */
389
390 /**********************************************************************/
391 #ifdef L_parse_printf_format
392
393 #ifdef __UCLIBC_HAS_GLIBC_CUSTOM_PRINTF__
394
395 /* NOTE: This function differs from the glibc version in that parsing stops
396  * upon encountering an invalid conversion specifier.  Since this is the way
397  * my printf functions work, I think it makes sense to do it that way here.
398  * Unfortunately, since glibc sets the return type as size_t, we have no way
399  * of returning that the template is illegal, other than returning 0.
400  */
401
402 size_t parse_printf_format(register const char *template,
403                                                    size_t n, register int *argtypes)
404 {
405         ppfs_t ppfs;
406         size_t i;
407         size_t count = 0;
408
409         if (_ppfs_init(&ppfs, template) >= 0) {
410 #ifdef NL_ARGMAX
411                 if (ppfs.maxposarg > 0)  {
412                         /* Using positional args. */
413                         count = ppfs.maxposarg;
414                         if (n > count) {
415                                 n = count;
416                         }
417                         for (i = 0 ; i < n ; i++) {
418                                 *argtypes++ = ppfs.argtype[i];
419                         }
420                 } else
421 #endif
422                 {
423                         /* Not using positional args. */
424                         while (*template) {
425                                 if ((*template == '%') && (*++template != '%')) {
426                                         ppfs.fmtpos = template;
427                                         _ppfs_parsespec(&ppfs); /* Can't fail. */
428                                         template = ppfs.fmtpos; /* Update to one past spec end. */
429                                         if (ppfs.info.width == INT_MIN) {
430                                                 ++count;
431                                                 if (n > 0) {
432                                                         *argtypes++ = PA_INT;
433                                                         --n;
434                                                 }
435                                         }
436                                         if (ppfs.info.prec == INT_MIN) {
437                                                 ++count;
438                                                 if (n > 0) {
439                                                         *argtypes++ = PA_INT;
440                                                         --n;
441                                                 }
442                                         }
443                                         for (i = 0 ; i < ppfs.num_data_args ; i++) {
444                                                 if ((ppfs.argtype[i]) != __PA_NOARG) {
445                                                         ++count;
446                                                         if (n > 0) {
447                                                                 *argtypes++ = ppfs.argtype[i];
448                                                                 --n;
449                                                         }
450                                                 }
451                                         }
452                                 } else {
453                                         ++template;
454                                 }
455                         }
456                 }
457         }
458
459         return count;
460 }
461
462 #endif
463
464 #endif
465 /**********************************************************************/
466 #ifdef L__ppfs_init
467
468 int attribute_hidden _ppfs_init(register ppfs_t *ppfs, const char *fmt0)
469 {
470         int r;
471
472         /* First, zero out everything... argnumber[], argtype[], argptr[] */
473         memset(ppfs, 0, sizeof(ppfs_t)); /* TODO: nonportable???? */
474 #ifdef NL_ARGMAX
475         --ppfs->maxposarg;                      /* set to -1 */
476 #endif
477         ppfs->fmtpos = fmt0;
478 #ifdef __UCLIBC_MJN3_ONLY__
479 # warning TODO: Make checking of the format string in C locale an option.
480 #endif
481 #ifdef __UCLIBC_HAS_LOCALE__
482         /* To support old programs, don't check mb validity if in C locale. */
483         if (__UCLIBC_CURLOCALE->encoding != __ctype_encoding_7_bit) {
484                 /* ANSI/ISO C99 requires format string to be a valid multibyte string
485                  * beginning and ending in its initial shift state. */
486                 static const char invalid_mbs[] = "Invalid multibyte format string.";
487                 mbstate_t mbstate;
488                 const char *p;
489                 mbstate.__mask = 0;     /* Initialize the mbstate. */
490                 p = fmt0;
491                 if (mbsrtowcs(NULL, &p, SIZE_MAX, &mbstate) == ((size_t)(-1))) {
492                         ppfs->fmtpos = invalid_mbs;
493                         return -1;
494                 }
495         }
496 #endif /* __UCLIBC_HAS_LOCALE__ */
497         /* now set all argtypes to no-arg */
498         {
499 #if 1
500                 /* TODO - use memset here since already "paid for"? */
501                 register int *p = ppfs->argtype;
502
503                 r = MAX_ARGS;
504                 do {
505                         *p++ = __PA_NOARG;
506                 } while (--r);
507 #else
508                 /* TODO -- get rid of this?? */
509                 register char *p = (char *) ((MAX_ARGS-1) * sizeof(int));
510
511                 do {
512                         *((int *)(((char *)ppfs) + ((int)p) + offsetof(ppfs_t,argtype))) = __PA_NOARG;
513                         p -= sizeof(int);
514                 } while (p);
515 #endif
516         }
517
518         /*
519          * Run through the entire format string to validate it and initialize
520          * the positional arg numbers (if any).
521          */
522         {
523                 register const char *fmt = fmt0;
524
525                 while (*fmt) {
526                         if ((*fmt == '%') && (*++fmt != '%')) {
527                                 ppfs->fmtpos = fmt; /* back up to the '%' */
528                                 r = _ppfs_parsespec(ppfs);
529                                 if (r < 0) {
530                                         return -1;
531                                 }
532                                 fmt = ppfs->fmtpos;     /* update to one past end of spec */
533                         } else {
534                                 ++fmt;
535                         }
536                 }
537                 ppfs->fmtpos = fmt0;            /* rewind */
538         }
539
540 #ifdef NL_ARGMAX
541         /* If we have positional args, make sure we know all the types. */
542         {
543                 register int *p = ppfs->argtype;
544                 r = ppfs->maxposarg;
545                 while (--r >= 0) {
546                         if ( *p == __PA_NOARG ) { /* missing arg type!!! */
547                                 return -1;
548                         }
549                         ++p;
550                 }
551         }
552 #endif /* NL_ARGMAX */
553
554         return 0;
555 }
556 #endif
557 /**********************************************************************/
558 #ifdef L__ppfs_prepargs
559 void attribute_hidden _ppfs_prepargs(register ppfs_t *ppfs, va_list arg)
560 {
561         int i;
562
563         va_copy(ppfs->arg, arg);
564
565 #ifdef NL_ARGMAX
566         i = ppfs->maxposarg; /* init for positional args */
567         if (i > 0) {
568                 ppfs->num_data_args = i;
569                 ppfs->info.width = ppfs->info.prec = ppfs->maxposarg = 0;
570                 _ppfs_setargs(ppfs);
571                 ppfs->maxposarg = i;
572         }
573 #endif
574 }
575 #endif
576 /**********************************************************************/
577 #ifdef L__ppfs_setargs
578
579 void attribute_hidden _ppfs_setargs(register ppfs_t *ppfs)
580 {
581 #ifdef __va_arg_ptr
582         register void **p = ppfs->argptr;
583 #else
584         register argvalue_t *p = ppfs->argvalue;
585 #endif
586         int i;
587
588 #ifdef NL_ARGMAX
589         if (ppfs->maxposarg == 0) {     /* initing for or no pos args */
590 #endif
591                 if (ppfs->info.width == INT_MIN) {
592                         ppfs->info.width =
593 #ifdef __va_arg_ptr
594                                 *(int *)
595 #endif
596                                 GET_VA_ARG(p,u,unsigned int,ppfs->arg);
597                 }
598                 if (ppfs->info.prec == INT_MIN) {
599                         ppfs->info.prec =
600 #ifdef __va_arg_ptr
601                                 *(int *)
602 #endif
603                                 GET_VA_ARG(p,u,unsigned int,ppfs->arg);
604                 }
605                 i = 0;
606                 while (i < ppfs->num_data_args) {
607                         switch(ppfs->argtype[i++]) {
608                                 case (PA_INT|PA_FLAG_LONG_LONG):
609 #ifdef ULLONG_MAX
610                                         GET_VA_ARG(p,ull,unsigned long long,ppfs->arg);
611                                         break;
612 #endif
613                                 case (PA_INT|PA_FLAG_LONG):
614 #if ULONG_MAX != UINT_MAX
615                                         GET_VA_ARG(p,ul,unsigned long,ppfs->arg);
616                                         break;
617 #endif
618                                 case PA_CHAR:   /* TODO - be careful */
619                                         /* ... users could use above and really want below!! */
620                                 case (PA_INT|__PA_FLAG_CHAR):/* TODO -- translate this!!! */
621                                 case (PA_INT|PA_FLAG_SHORT):
622                                 case PA_INT:
623                                         GET_VA_ARG(p,u,unsigned int,ppfs->arg);
624                                         break;
625                                 case PA_WCHAR:  /* TODO -- assume int? */
626                                         /* we're assuming wchar_t is at least an int */
627                                         GET_VA_ARG(p,wc,wchar_t,ppfs->arg);
628                                         break;
629 #ifdef __UCLIBC_HAS_FLOATS__
630                                         /* PA_FLOAT */
631                                 case PA_DOUBLE:
632                                         GET_VA_ARG(p,d,double,ppfs->arg);
633                                         break;
634                                 case (PA_DOUBLE|PA_FLAG_LONG_DOUBLE):
635                                         GET_VA_ARG(p,ld,long double,ppfs->arg);
636                                         break;
637 #else  /* __UCLIBC_HAS_FLOATS__ */
638                                 case PA_DOUBLE:
639                                 case (PA_DOUBLE|PA_FLAG_LONG_DOUBLE):
640                                         assert(0);
641                                         continue;
642 #endif /* __UCLIBC_HAS_FLOATS__ */
643                                 default:
644                                         /* TODO -- really need to ensure this can't happen */
645                                         assert(ppfs->argtype[i-1] & PA_FLAG_PTR);
646                                 case PA_POINTER:
647                                 case PA_STRING:
648                                 case PA_WSTRING:
649                                         GET_VA_ARG(p,p,void *,ppfs->arg);
650                                         break;
651                                 case __PA_NOARG:
652                                         continue;
653                         }
654                         ++p;
655                 }
656 #ifdef NL_ARGMAX
657         } else {
658                 if (ppfs->info.width == INT_MIN) {
659                         ppfs->info.width
660                                 = (int) GET_ARG_VALUE(p + ppfs->argnumber[0] - 1,u,unsigned int);
661                 }
662                 if (ppfs->info.prec == INT_MIN) {
663                         ppfs->info.prec
664                                 = (int) GET_ARG_VALUE(p + ppfs->argnumber[1] - 1,u,unsigned int);
665                 }
666         }
667 #endif /* NL_ARGMAX */
668
669         /* Now we know the width and precision. */
670         if (ppfs->info.width < 0) {
671                 ppfs->info.width = -ppfs->info.width;
672                 PRINT_INFO_SET_FLAG(&(ppfs->info),left);
673                 PRINT_INFO_CLR_FLAG(&(ppfs->info),space);
674                 ppfs->info.pad = ' ';
675         }
676 #if 0
677         /* NOTE -- keep neg for now so float knows! */
678         if (ppfs->info.prec < 0) {      /* spec says treat as omitted. */
679                 /* so use default prec... 1 for everything but floats and strings. */
680                 ppfs->info.prec = 1;
681         }
682 #endif
683 }
684 #endif
685 /**********************************************************************/
686 #ifdef L__ppfs_parsespec
687
688 /* Notes: argtype differs from glibc for the following:
689  *         mine              glibc
690  *  lc     PA_WCHAR          PA_CHAR       the standard says %lc means %C
691  *  ls     PA_WSTRING        PA_STRING     the standard says %ls means %S
692  *  {*}n   {*}|PA_FLAG_PTR   PA_FLAG_PTR   size of n can be qualified
693  */
694
695 /* TODO: store positions of positional args */
696
697 /* TODO -- WARNING -- assumes aligned on integer boundaries!!! */
698
699 /* TODO -- disable if not using positional args!!! */
700 #define _OVERLAPPING_DIFFERENT_ARGS
701
702 /* TODO -- rethink this -- perhaps we should set to largest type??? */
703
704 #ifdef _OVERLAPPING_DIFFERENT_ARGS
705
706 #define PROMOTED_SIZE_OF(X)             ((sizeof(X) + sizeof(int) - 1) / sizeof(X))
707
708 static const short int type_codes[] = {
709         __PA_NOARG,                                     /* must be first entry */
710         PA_POINTER,
711         PA_STRING,
712         PA_WSTRING,
713         PA_CHAR,
714         PA_INT|PA_FLAG_SHORT,
715         PA_INT,
716         PA_INT|PA_FLAG_LONG,
717         PA_INT|PA_FLAG_LONG_LONG,
718         PA_WCHAR,
719 #ifdef __UCLIBC_HAS_FLOATS__
720         /* PA_FLOAT, */
721         PA_DOUBLE,
722         PA_DOUBLE|PA_FLAG_LONG_DOUBLE,
723 #endif
724 };
725
726 static const unsigned char type_sizes[] = {
727         /* unknown type consumes no arg */
728         0,                                                      /* must be first entry */
729         PROMOTED_SIZE_OF(void *),
730         PROMOTED_SIZE_OF(char *),
731         PROMOTED_SIZE_OF(wchar_t *),
732         PROMOTED_SIZE_OF(char),
733         PROMOTED_SIZE_OF(short),
734         PROMOTED_SIZE_OF(int),
735         PROMOTED_SIZE_OF(long),
736 #ifdef ULLONG_MAX
737         PROMOTED_SIZE_OF(long long),
738 #else
739         PROMOTED_SIZE_OF(long),         /* TODO -- is this correct? (above too) */
740 #endif
741         PROMOTED_SIZE_OF(wchar_t),
742 #ifdef __UCLIBC_HAS_FLOATS__
743         /* PROMOTED_SIZE_OF(float), */
744         PROMOTED_SIZE_OF(double),
745         PROMOTED_SIZE_OF(long double),
746 #endif
747 };
748
749 static int _promoted_size(int argtype)
750 {
751         register const short int *p;
752
753         /* note -- since any unrecognized type is treated as a pointer */
754         p = type_codes + sizeof(type_codes)/sizeof(type_codes[0]);
755         do {
756                 if (*--p == argtype) {
757                         break;
758                 }
759         } while (p > type_codes);
760
761         return type_sizes[(int)(p - type_codes)];
762 }
763
764 static int _is_equal_or_bigger_arg(int curtype, int newtype)
765 {
766         /* Quick test */
767         if (newtype == __PA_NOARG) {
768                 return 0;
769         }
770         if ((curtype == __PA_NOARG) || (curtype == newtype)) {
771                 return 1;
772         }
773         /* Ok... slot is already filled and types are different in name. */
774         /* So, compare promoted sizes of curtype and newtype args. */
775         return _promoted_size(curtype) <= _promoted_size(newtype);
776 }
777
778 #else
779
780 #define _is_equal_or_bigger_arg(C,N)    (((C) == __PA_NOARG) || ((C) == (N)))
781
782 #endif
783
784 #ifdef __UCLIBC_HAS_GLIBC_CUSTOM_PRINTF__
785 /* TODO - do this differently? */
786 static char _bss_custom_printf_spec[MAX_USER_SPEC]; /* 0-init'd for us.  */
787
788 attribute_hidden char *_custom_printf_spec = _bss_custom_printf_spec;
789 attribute_hidden printf_arginfo_function *_custom_printf_arginfo[MAX_USER_SPEC];
790 attribute_hidden printf_function _custom_printf_handler[MAX_USER_SPEC];
791 #endif /* __UCLIBC_HAS_GLIBC_CUSTOM_PRINTF__ */
792
793 int attribute_hidden _ppfs_parsespec(ppfs_t *ppfs)
794 {
795         register const char *fmt;
796         register const char *p;
797         int preci;
798         int width;
799         int flags;
800         int dataargtype;
801         int i;
802         int dpoint;
803 #ifdef NL_ARGMAX
804         int maxposarg;
805 #endif
806         int p_m_spec_chars;
807         int n;
808         int argtype[MAX_ARGS_PER_SPEC+2];
809         int argnumber[3];                       /* width, precision, 1st data arg */
810         static const char spec_flags[] = SPEC_FLAGS;
811         static const char spec_chars[] = SPEC_CHARS;/* TODO: b? */
812         static const char spec_ranges[] = SPEC_RANGES;
813         static const short spec_or_mask[] = SPEC_OR_MASK;
814         static const short spec_and_mask[] = SPEC_AND_MASK;
815         static const char qual_chars[] = QUAL_CHARS;
816 #ifdef __UCLIBC_HAS_WCHAR__
817         char buf[32];
818 #endif
819
820         /* WIDE note: we can test against '%' here since we don't allow */
821         /* WIDE note: other mappings of '%' in the wide char set. */
822         preci = -1;
823         argnumber[0] = 0;
824         argnumber[1] = 0;
825         argtype[0] = __PA_NOARG;
826         argtype[1] = __PA_NOARG;
827 #ifdef NL_ARGMAX
828         maxposarg = ppfs->maxposarg;
829 #endif
830
831 #ifdef __UCLIBC_HAS_WCHAR__
832         /* This is somewhat lame, but saves a lot of code.  If we're dealing with
833          * a wide stream, that means the format is a wchar string.  So, copy it
834          * char-by-char into a normal char buffer for processing.  Make the buffer
835          * (buf) big enough so that any reasonable format specifier will fit.
836          * While there a legal specifiers that won't, the all involve duplicate
837          * flags or outrageous field widths/precisions. */
838         width = dpoint = 0;
839         flags = ppfs->info._flags & FLAG_WIDESTREAM;
840         if (flags == 0) {
841                 fmt = ppfs->fmtpos;
842         } else {
843                 fmt = buf + 1;
844                 i = 0;
845                 do {
846                         buf[i] = (char) (((wchar_t *) ppfs->fmtpos)[i-1]);
847                         if (buf[i] != (((wchar_t *) ppfs->fmtpos)[i-1])) {
848                                 return -1;
849                         }
850                 } while (buf[i++] && (i < sizeof(buf)));
851                 buf[sizeof(buf)-1] = 0;
852         }
853 #else  /* __UCLIBC_HAS_WCHAR__ */
854         width = flags = dpoint = 0;
855         fmt = ppfs->fmtpos;
856 #endif
857
858         assert(fmt[-1] == '%');
859         assert(fmt[0] != '%');
860
861         /* Process arg pos and/or flags and/or width and/or precision. */
862  width_precision:
863         p = fmt;
864         if (*fmt == '*') {
865                 argtype[-dpoint] = PA_INT;
866                 ++fmt;
867         }
868         i = 0;
869         while (isdigit(*fmt)) {
870                 if (i < INT_MAX / 10
871                     || (i == INT_MAX / 10 && (*fmt - '0') <= INT_MAX % 10)) {
872                         i = (i * 10) + (*fmt - '0');
873                 } else {
874                         i = INT_MAX; /* best we can do... */
875                 }
876                 ++fmt;
877         }
878         if (p[-1] == '%') { /* Check for a position. */
879
880                 /* TODO: if val not in range, then error */
881
882 #ifdef NL_ARGMAX
883                 if ((*fmt == '$') && (i > 0)) {/* Positional spec. */
884                         ++fmt;
885                         if (maxposarg == 0) {
886                                 return -1;
887                         }
888                         argnumber[2] = i;
889                         if (argnumber[2] > maxposarg) {
890                                 maxposarg = i;
891                         }
892                         /* Now fall through to check flags. */
893                 } else {
894                         if (maxposarg > 0) {
895 # ifdef __UCLIBC_HAS_PRINTF_M_SPEC__
896 #  ifdef __UCLIBC_MJN3_ONLY__
897 #   warning TODO: Support prec and width for %m when positional args used
898                                 /* Actually, positional arg processing will fail in general
899                                  * for specifiers that don't require an arg. */
900 #  endif
901                                 if (*fmt == 'm') {
902                                         goto PREC_WIDTH;
903                                 }
904 # endif /* __UCLIBC_HAS_PRINTF_M_SPEC__ */
905                                 return -1;
906                         }
907                         maxposarg = 0;          /* Possible redundant store, but cuts size. */
908
909                         if ((fmt > p) && (*p != '0')) {
910                                 goto PREC_WIDTH;
911                         }
912
913                         fmt = p;                        /* Back up for possible '0's flag. */
914                         /* Now fall through to check flags. */
915                 }
916 #else  /* NL_ARGMAX */
917                 if (*fmt == '$') {              /* Positional spec. */
918                         return -1;
919                 }
920
921                 if ((fmt > p) && (*p != '0')) {
922                         goto PREC_WIDTH;
923                 }
924
925                 fmt = p;                        /* Back up for possible '0's flag. */
926                 /* Now fall through to check flags. */
927 #endif /* NL_ARGMAX */
928
929         restart_flags:          /* Process flags. */
930                 i = 1;
931                 p = spec_flags;
932
933                 do {
934                         if (*fmt == *p++) {
935                                 ++fmt;
936                                 flags |= i;
937                                 goto restart_flags;
938                         }
939                         i += i;                         /* Better than i <<= 1 for bcc */
940                 } while (*p);
941                 i = 0;
942
943                 /* If '+' then ignore ' ', and if '-' then ignore '0'. */
944                 /* Note: Need to ignore '0' when prec is an arg with val < 0, */
945                 /*       but that test needs to wait until the arg is retrieved. */
946                 flags &= ~((flags & (FLAG_PLUS|FLAG_MINUS)) >> 1);
947                 /* Note: Ignore '0' when prec is specified < 0 too (in printf). */
948
949                 if (fmt[-1] != '%') {   /* If we've done anything, loop for width. */
950                         goto width_precision;
951                 }
952         }
953  PREC_WIDTH:
954         if (*p == '*') {                        /* Prec or width takes an arg. */
955 #ifdef NL_ARGMAX
956                 if (maxposarg) {
957                         if ((*fmt++ != '$') || (i <= 0)) {
958                                 /* Using pos args and no $ or invalid arg number. */
959                                 return -1;
960                         }
961                         argnumber[-dpoint] = i;
962                 } else
963 #endif
964                 if (++p != fmt) {
965                          /* Not using pos args but digits followed *. */
966                         return -1;
967                 }
968                 i = INT_MIN;
969         }
970
971         if (!dpoint) {
972                 width = i;
973                 if (*fmt == '.') {
974                         ++fmt;
975                         dpoint = -1;            /* To use as default precison. */
976                         goto width_precision;
977                 }
978         } else {
979                 preci = i;
980         }
981
982         /* Process qualifier. */
983         p = qual_chars;
984         do {
985                 if (*fmt == *p) {
986                         ++fmt;
987                         break;
988                 }
989         } while (*++p);
990         if ((p - qual_chars < 2) && (*fmt == *p)) {
991                 p += ((sizeof(qual_chars)-2) / 2);
992                 ++fmt;
993         }
994         dataargtype = ((int)(p[(sizeof(qual_chars)-2) / 2])) << 8;
995
996         /* Process conversion specifier. */
997         if (!*fmt) {
998                 return -1;
999         }
1000
1001         p = spec_chars;
1002
1003         do {
1004                 if (*fmt == *p) {
1005                         p_m_spec_chars = p - spec_chars;
1006
1007                         if ((p_m_spec_chars >= CONV_c)
1008                                 && (dataargtype & PA_FLAG_LONG)) {
1009                                 p_m_spec_chars -= 2; /* lc -> C and ls -> S */
1010                         }
1011
1012                         ppfs->conv_num = p_m_spec_chars;
1013                         p = spec_ranges-1;
1014                         while (p_m_spec_chars > *++p) {}
1015
1016                         i = p - spec_ranges;
1017                         argtype[2] = (dataargtype | spec_or_mask[i]) & spec_and_mask[i];
1018                         p = spec_chars;
1019                         break;
1020                 }
1021         } while(*++p);
1022
1023         ppfs->info.spec = *fmt;
1024         ppfs->info.prec = preci;
1025         ppfs->info.width = width;
1026         ppfs->info.pad = ((flags & FLAG_ZERO) ? '0' : ' ');
1027         ppfs->info._flags = (flags & ~FLAG_ZERO) | (dataargtype & __PA_INTMASK);
1028         ppfs->num_data_args = 1;
1029
1030         if (!*p) {
1031 #ifdef __UCLIBC_HAS_PRINTF_M_SPEC__
1032                 if (*fmt == 'm') {
1033                         ppfs->conv_num = CONV_m;
1034                         ppfs->num_data_args = 0;
1035                 } else
1036 #endif
1037                 {
1038 #ifndef __UCLIBC_HAS_GLIBC_CUSTOM_PRINTF__
1039                         return -1;  /* Error */
1040 #else
1041                         /* Handle custom arg -- WARNING -- overwrites p!!! */
1042                         ppfs->conv_num = CONV_custom0;
1043                         p = _custom_printf_spec;
1044                         while (1) {
1045                                 if (*p == *fmt) {
1046                                         printf_arginfo_function *fp = _custom_printf_arginfo[(int)(p - _custom_printf_spec)];
1047                                         ppfs->num_data_args = fp(&(ppfs->info), MAX_ARGS_PER_SPEC, argtype + 2);
1048                                         if (ppfs->num_data_args > MAX_ARGS_PER_SPEC) {
1049                                                 return -1;  /* Error -- too many args! */
1050                                         }
1051                                         break;
1052                                 }
1053                                 if (++p >= (_custom_printf_spec + MAX_USER_SPEC))
1054                                         return -1;  /* Error */
1055                         }
1056 #endif
1057                 }
1058         }
1059
1060 #ifdef NL_ARGMAX
1061         if (maxposarg > 0) {
1062                 i = 0;
1063                 do {
1064                         /* Update maxposarg and check that NL_ARGMAX is not exceeded. */
1065                         n = ((i <= 2)
1066                                  ? (ppfs->argnumber[i] = argnumber[i])
1067                                  : argnumber[2] + (i-2));
1068                         if (n > maxposarg) {
1069                                 maxposarg = n;
1070                                 if (maxposarg > NL_ARGMAX) {
1071                                         return -1;
1072                                 }
1073                         }
1074                         --n;
1075                         /* Record argtype with largest size (current, new). */
1076                         if (_is_equal_or_bigger_arg(ppfs->argtype[n], argtype[i])) {
1077                                 ppfs->argtype[n] = argtype[i];
1078                         }
1079                 } while (++i < ppfs->num_data_args + 2);
1080         } else
1081 #endif /* NL_ARGMAX */
1082         {
1083                 ppfs->argnumber[2] = 1;
1084                 memcpy(ppfs->argtype, argtype + 2, ppfs->num_data_args * sizeof(int));
1085         }
1086
1087 #ifdef NL_ARGMAX
1088         ppfs->maxposarg = maxposarg;
1089 #endif
1090
1091 #ifdef __UCLIBC_HAS_WCHAR__
1092         flags = ppfs->info._flags & FLAG_WIDESTREAM;
1093         if (flags == 0) {
1094                 ppfs->fmtpos = ++fmt;
1095         } else {
1096                 ppfs->fmtpos = (const char *) (((const wchar_t *)(ppfs->fmtpos))
1097                                                                            + (fmt - buf) );
1098         }
1099 #else  /* __UCLIBC_HAS_WCHAR__ */
1100         ppfs->fmtpos = ++fmt;
1101 #endif
1102
1103         return ppfs->num_data_args + 2;
1104 }
1105
1106 #endif
1107 /**********************************************************************/
1108 #ifdef L_register_printf_function
1109
1110 #ifdef __UCLIBC_HAS_GLIBC_CUSTOM_PRINTF__
1111
1112 int register_printf_function(int spec, printf_function handler,
1113                                                          printf_arginfo_function arginfo)
1114 {
1115         register char *r;
1116         register char *p;
1117
1118         if (spec && (arginfo != NULL)) { /* TODO -- check if spec is valid char */
1119                 r = NULL;
1120                 p = _custom_printf_spec + MAX_USER_SPEC;
1121                 do {
1122                         --p;
1123                         if (!*p) {
1124                                 r = p;
1125                         }
1126 #ifdef __BCC__
1127                         else                            /* bcc generates less code with fall-through */
1128 #endif
1129                         if (*p == spec) {
1130                                 r = p;
1131                                 p = _custom_printf_spec;
1132                         }
1133                 } while (p > _custom_printf_spec);
1134
1135                 if (r) {
1136                         if (handler) {
1137                                 *r = spec;
1138                                 _custom_printf_handler[(int)(r - p)] = handler;
1139                                 _custom_printf_arginfo[(int)(r - p)] = arginfo;
1140                         } else {
1141                                 *r = 0;
1142                         }
1143                         return 0;
1144                 }
1145                 /* TODO -- if asked to unregister a non-existent spec, return what? */
1146         }
1147         return -1;
1148 }
1149
1150 #endif
1151
1152 #endif
1153 /**********************************************************************/
1154 #if defined(L__vfprintf_internal) || defined(L__vfwprintf_internal)
1155
1156 /* We only support ascii digits (or their USC equivalent codes) in
1157  * precision and width settings in *printf (wide) format strings.
1158  * In other words, we don't currently support glibc's 'I' flag.
1159  * We do accept it, but it is currently ignored. */
1160
1161 static size_t _charpad(FILE * __restrict stream, int padchar, size_t numpad);
1162
1163 #ifdef L__vfprintf_internal
1164
1165 #define VFPRINTF_internal _vfprintf_internal
1166 #define FMT_TYPE char
1167 #define OUTNSTR _outnstr
1168 #define STRLEN  strlen
1169 #define _PPFS_init _ppfs_init
1170 #define OUTPUT(F,S)                     fputs_unlocked(S,F)
1171 /* #define _outnstr(stream, string, len)        __stdio_fwrite(string, len, stream) */
1172 #define _outnstr(stream, string, len)   ((len > 0) ? __stdio_fwrite((const unsigned char *)(string), len, stream) : 0)
1173 #define FP_OUT _fp_out_narrow
1174
1175 #ifdef __UCLIBC_HAS_FLOATS__
1176
1177 static size_t _fp_out_narrow(FILE *fp, intptr_t type, intptr_t len, intptr_t buf)
1178 {
1179         size_t r = 0;
1180
1181         if (type & 0x80) {                      /* Some type of padding needed. */
1182                 int buflen = strlen((const char *) buf);
1183                 len -= buflen;
1184                 if (len > 0) {
1185                         r = _charpad(fp, (type & 0x7f), len);
1186                         if (r != len) {
1187                                 return r;
1188                         }
1189                 }
1190                 len = buflen;
1191         }
1192         return r + OUTNSTR(fp, (const char *) buf, len);
1193 }
1194
1195 #endif /* __UCLIBC_HAS_FLOATS__ */
1196
1197 #else  /* L__vfprintf_internal */
1198
1199 #define VFPRINTF_internal _vfwprintf_internal
1200 #define FMT_TYPE wchar_t
1201 #define OUTNSTR _outnwcs
1202 #define STRLEN  wcslen
1203 #define _PPFS_init _ppwfs_init
1204 /* Pulls in fseek: */
1205 #define OUTPUT(F,S)                     fputws(S,F)
1206 /* TODO: #define OUTPUT(F,S)            _wstdio_fwrite((S),wcslen(S),(F)) */
1207 #define _outnwcs(stream, wstring, len)  _wstdio_fwrite((const wchar_t *)(wstring), len, stream)
1208 #define FP_OUT _fp_out_wide
1209
1210 static size_t _outnstr(FILE *stream, const char *s, size_t wclen)
1211 {
1212         /* NOTE!!! len here is the number of wchars we want to generate!!! */
1213         wchar_t wbuf[64];
1214         mbstate_t mbstate;
1215         size_t todo, r, n;
1216
1217         mbstate.__mask = 0;
1218         todo = wclen;
1219
1220         while (todo) {
1221                 r = mbsrtowcs(wbuf, &s,
1222                                           ((todo <= sizeof(wbuf)/sizeof(wbuf[0]))
1223                                            ? todo
1224                                            : sizeof(wbuf)/sizeof(wbuf[0])),
1225                                           &mbstate);
1226                 assert(((ssize_t)r) > 0);
1227                 n = _outnwcs(stream, wbuf, r);
1228                 todo -= n;
1229                 if (n != r) {
1230                         break;
1231                 }
1232         }
1233
1234         return wclen - todo;
1235 }
1236
1237 #ifdef __UCLIBC_HAS_FLOATS__
1238
1239 static size_t _fp_out_wide(FILE *fp, intptr_t type, intptr_t len, intptr_t buf)
1240 {
1241         wchar_t wbuf[BUF_SIZE];
1242         const char *s = (const char *) buf;
1243         size_t r = 0;
1244         int i;
1245
1246         if (type & 0x80) {                      /* Some type of padding needed */
1247                 int buflen = strlen(s);
1248                 len -= buflen;
1249                 if (len > 0) {
1250                         r = _charpad(fp, (type & 0x7f), len);
1251                         if (r != len) {
1252                                 return r;
1253                         }
1254                 }
1255                 len = buflen;
1256         }
1257
1258         if (len > 0) {
1259                 i = 0;
1260                 do {
1261 #ifdef __LOCALE_C_ONLY
1262                         wbuf[i] = s[i];
1263 #else
1264
1265 # ifdef __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__
1266                         if (s[i] == ',') {
1267                                 wbuf[i] = __UCLIBC_CURLOCALE->thousands_sep_wc;
1268                         } else
1269 # endif
1270                         if (s[i] == '.') {
1271                                 wbuf[i] = __UCLIBC_CURLOCALE->decimal_point_wc;
1272                         } else {
1273                                 wbuf[i] = s[i];
1274                         }
1275 #endif /* __LOCALE_C_ONLY */
1276
1277                 } while (++i < len);
1278
1279                 r += OUTNSTR(fp, wbuf, len);
1280         }
1281
1282         return r;
1283 }
1284
1285 #endif /* __UCLIBC_HAS_FLOATS__ */
1286
1287 static int _ppwfs_init(register ppfs_t *ppfs, const wchar_t *fmt0)
1288 {
1289         static const wchar_t invalid_wcs[] = L"Invalid wide format string.";
1290         int r;
1291
1292         /* First, zero out everything... argnumber[], argtype[], argptr[] */
1293         memset(ppfs, 0, sizeof(ppfs_t)); /* TODO: nonportable???? */
1294 #ifdef NL_ARGMAX
1295         --ppfs->maxposarg;                      /* set to -1 */
1296 #endif
1297         ppfs->fmtpos = (const char *) fmt0;
1298         ppfs->info._flags = FLAG_WIDESTREAM;
1299
1300         {
1301                 mbstate_t mbstate;
1302                 const wchar_t *p;
1303                 mbstate.__mask = 0;     /* Initialize the mbstate. */
1304                 p = fmt0;
1305                 if (wcsrtombs(NULL, &p, SIZE_MAX, &mbstate) == ((size_t)(-1))) {
1306                         ppfs->fmtpos = (const char *) invalid_wcs;
1307                         return -1;
1308                 }
1309         }
1310
1311         /* now set all argtypes to no-arg */
1312         {
1313 #if 1
1314                 /* TODO - use memset here since already "paid for"? */
1315                 register int *p = ppfs->argtype;
1316
1317                 r = MAX_ARGS;
1318                 do {
1319                         *p++ = __PA_NOARG;
1320                 } while (--r);
1321 #else
1322                 /* TODO -- get rid of this?? */
1323                 register char *p = (char *) ((MAX_ARGS-1) * sizeof(int));
1324
1325                 do {
1326                         *((int *)(((char *)ppfs) + ((int)p) + offsetof(ppfs_t,argtype))) = __PA_NOARG;
1327                         p -= sizeof(int);
1328                 } while (p);
1329 #endif
1330         }
1331
1332         /*
1333          * Run through the entire format string to validate it and initialize
1334          * the positional arg numbers (if any).
1335          */
1336         {
1337                 register const wchar_t *fmt = fmt0;
1338
1339                 while (*fmt) {
1340                         if ((*fmt == '%') && (*++fmt != '%')) {
1341                                 ppfs->fmtpos = (const char *) fmt; /* back up to the '%' */
1342                                 r = _ppfs_parsespec(ppfs);
1343                                 if (r < 0) {
1344                                         return -1;
1345                                 }
1346                                 fmt = (const wchar_t *) ppfs->fmtpos; /* update to one past end of spec */
1347                         } else {
1348                                 ++fmt;
1349                         }
1350                 }
1351                 ppfs->fmtpos = (const char *) fmt0; /* rewind */
1352         }
1353
1354 #ifdef NL_ARGMAX
1355         /* If we have positional args, make sure we know all the types. */
1356         {
1357                 register int *p = ppfs->argtype;
1358                 r = ppfs->maxposarg;
1359                 while (--r >= 0) {
1360                         if ( *p == __PA_NOARG ) { /* missing arg type!!! */
1361                                 return -1;
1362                         }
1363                         ++p;
1364                 }
1365         }
1366 #endif /* NL_ARGMAX */
1367
1368         return 0;
1369 }
1370
1371 #endif /* L__vfprintf_internal */
1372
1373
1374 static size_t _charpad(FILE * __restrict stream, int padchar, size_t numpad)
1375 {
1376         size_t todo = numpad;
1377
1378         /* TODO -- Use a buffer to cut down on function calls... */
1379         FMT_TYPE pad[1];
1380
1381         *pad = padchar;
1382         while (todo && (OUTNSTR(stream, (const char *) pad, 1) == 1)) {
1383                 --todo;
1384         }
1385
1386         return numpad - todo;
1387 }
1388
1389 /* TODO -- Dynamically allocate work space to accomodate stack-poor archs? */
1390 static int _do_one_spec(FILE * __restrict stream,
1391                                                  register ppfs_t *ppfs, int *count)
1392 {
1393         static const char spec_base[] = SPEC_BASE;
1394 #ifdef L__vfprintf_internal
1395         static const char prefix[] = "+\0-\0 \0000x\0000X";
1396         /*                            0  2  4    6     9 11*/
1397 #else
1398         static const wchar_t prefix[] = L"+\0-\0 \0000x\0000X";
1399 #endif
1400         enum {
1401                 PREFIX_PLUS = 0,
1402                 PREFIX_MINUS = 2,
1403                 PREFIX_SPACE = 4,
1404                 PREFIX_LWR_X = 6,
1405                 PREFIX_UPR_X = 9,
1406                 PREFIX_NONE = 11
1407         };
1408
1409 #ifdef __va_arg_ptr
1410         const void * const *argptr;
1411 #else
1412         const void * argptr[MAX_ARGS_PER_SPEC];
1413 #endif
1414         int *argtype;
1415 #ifdef __UCLIBC_HAS_WCHAR__
1416         const wchar_t *ws = NULL;
1417         mbstate_t mbstate;
1418 #endif
1419         size_t slen;
1420 #ifdef L__vfprintf_internal
1421 #define SLEN slen
1422 #else
1423         size_t SLEN;
1424         wchar_t wbuf[2];
1425 #endif
1426         int base;
1427         int numpad;
1428         int alphacase;
1429         int numfill = 0;                        /* TODO: fix */
1430         int prefix_num = PREFIX_NONE;
1431         char padchar = ' ';
1432 #ifdef __UCLIBC_MJN3_ONLY__
1433 #warning TODO: Determine appropriate buf size.
1434 #endif
1435         /* TODO: buf needs to be big enough for any possible error return strings
1436          * and also for any locale-grouped long long integer strings generated.
1437          * This should be large enough for any of the current archs/locales, but
1438          * eventually this should be handled robustly. */
1439         char buf[128];
1440
1441 #ifdef NDEBUG
1442         _ppfs_parsespec(ppfs);
1443 #else
1444         if (_ppfs_parsespec(ppfs) < 0) { /* TODO: just for debugging */
1445                 abort();
1446         }
1447 #endif
1448         _ppfs_setargs(ppfs);
1449
1450         argtype = ppfs->argtype + ppfs->argnumber[2] - 1;
1451         /* Deal with the argptr vs argvalue issue. */
1452 #ifdef __va_arg_ptr
1453         argptr = (const void * const *) ppfs->argptr;
1454 # ifdef NL_ARGMAX
1455         if (ppfs->maxposarg > 0) {      /* Using positional args... */
1456                 argptr += ppfs->argnumber[2] - 1;
1457         }
1458 # endif
1459 #else
1460         /* Need to build a local copy... */
1461         {
1462                 register argvalue_t *p = ppfs->argvalue;
1463                 int i;
1464 # ifdef NL_ARGMAX
1465                 if (ppfs->maxposarg > 0) {      /* Using positional args... */
1466                         p += ppfs->argnumber[2] - 1;
1467                 }
1468 # endif
1469                 for (i = 0 ; i < ppfs->num_data_args ; i++ ) {
1470                         argptr[i] = (void *) p++;
1471                 }
1472         }
1473 #endif
1474         {
1475                 register char *s = NULL; /* TODO: Should s be unsigned char * ? */
1476
1477                 if (ppfs->conv_num == CONV_n) {
1478                         _store_inttype(*(void **)*argptr,
1479                                                    ppfs->info._flags & __PA_INTMASK,
1480                                                    (intmax_t) (*count));
1481                         return 0;
1482                 }
1483                 if (ppfs->conv_num <= CONV_i) { /* pointer or (un)signed int */
1484                         alphacase = __UIM_LOWER;
1485
1486 #ifdef __UCLIBC_MJN3_ONLY__
1487 #ifdef L__vfprintf_internal
1488 #warning CONSIDER: Should we ignore these flags if stub locale?  What about custom specs?
1489 #endif
1490 #endif
1491                         base = spec_base[(int)(ppfs->conv_num - CONV_p)];
1492                         if (base == 10) {
1493                                 if (PRINT_INFO_FLAG_VAL(&(ppfs->info),group)) {
1494                                         alphacase = __UIM_GROUP;
1495                                 }
1496                                 if (PRINT_INFO_FLAG_VAL(&(ppfs->info),i18n)) {
1497                                         alphacase |= 0x80;
1498                                 }
1499                         }
1500
1501                         if (ppfs->conv_num <= CONV_u) { /* pointer or unsigned int */
1502                                 if (ppfs->conv_num == CONV_X) {
1503                                         alphacase = __UIM_UPPER;
1504                                 }
1505                                 if (ppfs->conv_num == CONV_p) { /* pointer */
1506                                         prefix_num = PREFIX_LWR_X;
1507                                 } else {                /* unsigned int */
1508                                 }
1509                         } else {                        /* signed int */
1510                                 base = -base;
1511                         }
1512                         if (ppfs->info.prec < 0) { /* Ignore '0' flag if prec specified. */
1513                                 padchar = ppfs->info.pad;
1514                         }
1515 #ifdef __UCLIBC_MJN3_ONLY__
1516 #ifdef L__vfprintf_internal
1517 #warning CONSIDER: If using outdigits and/or grouping, how should we interpret precision?
1518 #endif
1519 #endif
1520                         s = _uintmaxtostr(buf + sizeof(buf) - 1,
1521                                                           (uintmax_t)
1522                                                           _load_inttype(ppfs->conv_num == CONV_p ? PA_FLAG_LONG : *argtype & __PA_INTMASK,
1523                                                                                         *argptr, base), base, alphacase);
1524                         if (ppfs->conv_num > CONV_u) { /* signed int */
1525                                 if (*s == '-') {
1526                                         PRINT_INFO_SET_FLAG(&(ppfs->info),showsign);
1527                                         ++s;            /* handle '-' in the prefix string */
1528                                         prefix_num = PREFIX_MINUS;
1529                                 } else if (PRINT_INFO_FLAG_VAL(&(ppfs->info),showsign)) {
1530                                         prefix_num = PREFIX_PLUS;
1531                                 } else if (PRINT_INFO_FLAG_VAL(&(ppfs->info),space)) {
1532                                         prefix_num = PREFIX_SPACE;
1533                                 }
1534                         }
1535                         slen = (char *)(buf + sizeof(buf) - 1) - s;
1536 #ifdef L__vfwprintf_internal
1537                         {
1538                                 const char *q = s;
1539                                 mbstate.__mask = 0; /* Initialize the mbstate. */
1540                                 SLEN = mbsrtowcs(NULL, &q, 0, &mbstate);
1541                         }
1542 #endif
1543                         numfill = ((ppfs->info.prec < 0) ? 1 : ppfs->info.prec);
1544                         if (PRINT_INFO_FLAG_VAL(&(ppfs->info),alt)) {
1545                                 if (ppfs->conv_num <= CONV_x) { /* x or p */
1546                                         prefix_num = PREFIX_LWR_X;
1547                                 }
1548                                 if (ppfs->conv_num == CONV_X) {
1549                                         prefix_num = PREFIX_UPR_X;
1550                                 }
1551                                 if ((ppfs->conv_num == CONV_o) && (numfill <= SLEN)) {
1552                                         numfill = ((*s == '0') ? 1 : SLEN + 1);
1553                                 }
1554                         }
1555                         if (*s == '0') {
1556                                 if (prefix_num >= PREFIX_LWR_X) {
1557                                         prefix_num = PREFIX_NONE;
1558                                 }
1559                                 if (ppfs->conv_num == CONV_p) {/* null pointer */
1560                                         s = "(nil)";
1561 #ifdef L__vfwprintf_internal
1562                                         SLEN =
1563 #endif
1564                                         slen = 5;
1565                                         numfill = 0;
1566                                 } else if (numfill == 0) {      /* if precision 0, no output */
1567 #ifdef L__vfwprintf_internal
1568                                         SLEN =
1569 #endif
1570                                         slen = 0;
1571                                 }
1572                         }
1573                         numfill = ((numfill > SLEN) ? numfill - SLEN : 0);
1574                 } else if (ppfs->conv_num <= CONV_A) {  /* floating point */
1575 #ifdef __UCLIBC_HAS_FLOATS__
1576                         ssize_t nf;
1577                         nf = _fpmaxtostr(stream,
1578                                                          (__fpmax_t)
1579                                                          (PRINT_INFO_FLAG_VAL(&(ppfs->info),is_long_double)
1580                                                           ? *(long double *) *argptr
1581                                                           : (long double) (* (double *) *argptr)),
1582                                                          &ppfs->info, FP_OUT );
1583                         if (nf < 0) {
1584                                 return -1;
1585                         }
1586                         *count += nf;
1587
1588                         return 0;
1589 #else  /* __UCLIBC_HAS_FLOATS__ */
1590                         return -1;                      /* TODO -- try to continue? */
1591 #endif
1592                 } else if (ppfs->conv_num <= CONV_S) {  /* wide char or string */
1593 #ifdef L__vfprintf_internal
1594
1595 #ifdef __UCLIBC_HAS_WCHAR__
1596                         mbstate.__mask = 0;     /* Initialize the mbstate. */
1597                         if (ppfs->conv_num == CONV_S) { /* wide string */
1598                                 ws = *((const wchar_t **) *argptr);
1599                                 if (!ws) {
1600                                         goto NULL_STRING;
1601                                 }
1602                                 /* We use an awful uClibc-specific hack here, passing
1603                                  * (char*) &ws as the conversion destination.  This signals
1604                                  * uClibc's wcsrtombs that we want a "restricted" length
1605                                  * such that the mbs fits in a buffer of the specified
1606                                  * size with no partial conversions. */
1607                                 slen = wcsrtombs((char *) &ws, &ws, /* Use awful hack! */
1608                                                         ((ppfs->info.prec >= 0)
1609                                                          ? ppfs->info.prec
1610                                                          : SIZE_MAX),
1611                                                         &mbstate);
1612                                 if (slen == ((size_t)-1)) {
1613                                         return -1;      /* EILSEQ */
1614                                 }
1615                         } else {                        /* wide char */
1616                                 s = buf;
1617                                 slen = wcrtomb(s, (*((const wchar_t *) *argptr)), &mbstate);
1618                                 if (slen == ((size_t)-1)) {
1619                                         return -1;      /* EILSEQ */
1620                                 }
1621                                 s[slen] = 0;    /* TODO - Is this necessary? */
1622                         }
1623 #else  /* __UCLIBC_HAS_WCHAR__ */
1624                         return -1;
1625 #endif
1626                 } 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                                         slen = strnlen(s, ((ppfs->info.prec >= 0)
1634                                                                            ? ppfs->info.prec : SIZE_MAX));
1635                                 } else {
1636 #ifdef __UCLIBC_HAS_WCHAR__
1637                                 NULL_STRING:
1638 #endif
1639                                         s = "(null)";
1640                                         slen = 6;
1641                                         /* Use an empty string rather than truncation if precision is too small. */
1642                                         if (ppfs->info.prec >= 0 && ppfs->info.prec < slen)
1643                                                 slen = 0;
1644                                 }
1645                         } else {                        /* char */
1646                                 s = buf;
1647                                 *s = (unsigned char)(*((const int *) *argptr));
1648                                 s[1] = 0;
1649                                 slen = 1;
1650                         }
1651
1652 #else  /* L__vfprintf_internal */
1653
1654                         if (ppfs->conv_num == CONV_S) { /* wide string */
1655                                 ws = *((wchar_t **) (*argptr));
1656                                 if (!ws) {
1657                                         goto NULL_STRING;
1658                                 }
1659                                 SLEN = wcsnlen(ws, ((ppfs->info.prec >= 0)
1660                                                                         ? ppfs->info.prec : SIZE_MAX));
1661                         } else {                        /* wide char */
1662                                 *wbuf = (wchar_t)(*((const wint_t *) *argptr));
1663                         CHAR_CASE:
1664                                 ws = wbuf;
1665                                 wbuf[1] = 0;
1666                                 SLEN = 1;
1667                         }
1668
1669                 } else if (ppfs->conv_num <= CONV_s) {  /* char or string */
1670
1671                         if (ppfs->conv_num == CONV_s) { /* string */
1672 #ifdef __UCLIBC_MJN3_ONLY__
1673 #warning TODO: Fix %s for _vfwprintf_internal... output upto illegal sequence?
1674 #endif
1675                                 s = *((char **) (*argptr));
1676                                 if (s) {
1677 #ifdef __UCLIBC_HAS_PRINTF_M_SPEC__
1678                                 SET_STRING_LEN:
1679 #endif
1680                                         /* We use an awful uClibc-specific hack here, passing
1681                                          * (wchar_t*) &mbstate as the conversion destination.
1682                                          *  This signals uClibc's mbsrtowcs that we want a
1683                                          * "restricted" length such that the mbs fits in a buffer
1684                                          * of the specified size with no partial conversions. */
1685                                         {
1686                                                 const char *q = s;
1687                                                 mbstate.__mask = 0;     /* Initialize the mbstate. */
1688                                                 SLEN = mbsrtowcs((wchar_t *) &mbstate, &q,
1689                                                                                  ((ppfs->info.prec >= 0)
1690                                                                                   ? ppfs->info.prec : SIZE_MAX),
1691                                                                                  &mbstate);
1692                                         }
1693                                         if (SLEN == ((size_t)(-1))) {
1694                                                 return -1;      /* EILSEQ */
1695                                         }
1696                                 } else {
1697                                 NULL_STRING:
1698                                         s = "(null)";
1699                                         SLEN = slen = 6;
1700                                         /* Use an empty string rather than truncation if precision is too small. */
1701                                         if (ppfs->info.prec >= 0 && ppfs->info.prec < slen)
1702                                                 SLEN = slen = 0;
1703                                 }
1704                         } else {                        /* char */
1705                                 *wbuf = btowc( (unsigned char)(*((const int *) *argptr)) );
1706                                 goto CHAR_CASE;
1707                         }
1708
1709 #endif /* L__vfprintf_internal */
1710
1711 #ifdef __UCLIBC_HAS_PRINTF_M_SPEC__
1712                 } else if (ppfs->conv_num == CONV_m) {
1713                         s = __glibc_strerror_r(errno, buf, sizeof(buf));
1714                         goto SET_STRING_LEN;
1715 #endif
1716                 } else {
1717 #ifdef __UCLIBC_HAS_GLIBC_CUSTOM_PRINTF__
1718                         assert(ppfs->conv_num == CONV_custom0);
1719
1720                         s = _custom_printf_spec;
1721                         do {
1722                                 if (*s == ppfs->info.spec) {
1723                                         int rv;
1724                                         /* TODO -- check return value for sanity? */
1725                                         rv = (*_custom_printf_handler
1726                                                   [(int)(s-_custom_printf_spec)])
1727                                                 (stream, &ppfs->info, argptr);
1728                                         if (rv < 0) {
1729                                                 return -1;
1730                                         }
1731                                         *count += rv;
1732                                         return 0;
1733                                 }
1734                         } while (++s < (_custom_printf_spec + MAX_USER_SPEC));
1735 #endif /* __UCLIBC_HAS_GLIBC_CUSTOM_PRINTF__ */
1736                         assert(0);
1737                         return -1;
1738                 }
1739
1740 #ifdef __UCLIBC_MJN3_ONLY__
1741 #ifdef L__vfprintf_internal
1742 #warning CONSIDER: If using outdigits and/or grouping, how should we pad?
1743 #endif
1744 #endif
1745                 {
1746                         size_t t;
1747
1748                         t = SLEN + numfill;
1749                         if (prefix_num != PREFIX_NONE) {
1750                                 t += ((prefix_num < PREFIX_LWR_X) ? 1 : 2);
1751                         }
1752                         numpad = ((ppfs->info.width > t) ? (ppfs->info.width - t) : 0);
1753                         *count += t + numpad;
1754                 }
1755                 if (padchar == '0') { /* TODO: check this */
1756                         numfill += numpad;
1757                         numpad = 0;
1758                 }
1759
1760                 /* Now handle the output itself. */
1761                 if (!PRINT_INFO_FLAG_VAL(&(ppfs->info),left)) {
1762                         if (_charpad(stream, ' ', numpad) != numpad) {
1763                                 return -1;
1764                         }
1765                         numpad = 0;
1766                 }
1767                 OUTPUT(stream, prefix + prefix_num);
1768
1769                 if (_charpad(stream, '0', numfill) != numfill) {
1770                         return -1;
1771                 }
1772
1773 #ifdef L__vfprintf_internal
1774
1775 # ifdef __UCLIBC_HAS_WCHAR__
1776                 if (!ws) {
1777                         assert(s);
1778                         if (_outnstr(stream, s, slen) != slen) {
1779                                 return -1;
1780                         }
1781                 } else {                                /* wide string */
1782                         size_t t;
1783                         mbstate.__mask = 0;     /* Initialize the mbstate. */
1784                         while (slen) {
1785                                 t = (slen <= sizeof(buf)) ? slen : sizeof(buf);
1786                                 t = wcsrtombs(buf, &ws, t, &mbstate);
1787                                 assert(t != ((size_t)(-1)));
1788                                 if (_outnstr(stream, buf, t) != t) {
1789                                         return -1;
1790                                 }
1791                                 slen -= t;
1792                         }
1793                 }
1794 # else  /* __UCLIBC_HAS_WCHAR__ */
1795                 if (_outnstr(stream, (const unsigned char *) s, slen) != slen) {
1796                         return -1;
1797                 }
1798 # endif
1799
1800 #else  /* L__vfprintf_internal */
1801
1802                 if (!ws) {
1803                         assert(s);
1804                         if (_outnstr(stream, s, SLEN) != SLEN) {
1805                                 return -1;
1806                         }
1807                 } else {
1808                         if (_outnwcs(stream, ws, SLEN) != SLEN) {
1809                                 return -1;
1810                         }
1811                 }
1812
1813 #endif /* L__vfprintf_internal */
1814                 if (_charpad(stream, ' ', numpad) != numpad) {
1815                         return -1;
1816                 }
1817         }
1818
1819         return 0;
1820 }
1821
1822
1823 int VFPRINTF_internal (FILE * __restrict stream,
1824                           const FMT_TYPE * __restrict format,
1825                           va_list arg)
1826 {
1827         ppfs_t ppfs;
1828         int count, r;
1829         register const FMT_TYPE *s;
1830
1831         count = 0;
1832         s = format;
1833
1834         if (_PPFS_init(&ppfs, format) < 0) {    /* Bad format string. */
1835                 OUTNSTR(stream, (const char *) ppfs.fmtpos,
1836                                 STRLEN((const FMT_TYPE *)(ppfs.fmtpos)));
1837 #if defined(L__vfprintf_internal) && !defined(NDEBUG)
1838                 fprintf(stderr,"\nIMbS: \"%s\"\n\n", format);
1839 #endif
1840                 count = -1;
1841         } else {
1842                 _ppfs_prepargs(&ppfs, arg);     /* This did a va_copy!!! */
1843
1844                 do {
1845                         while (*format && (*format != '%')) {
1846                                 ++format;
1847                         }
1848
1849                         if (format - s) {       /* output any literal text in format string */
1850                                 r = OUTNSTR(stream, (const char *) s, format - s);
1851                                 if (r != (format - s)) {
1852                                         count = -1;
1853                                         break;
1854                                 }
1855                                 count += r;
1856                         }
1857
1858                         if (!*format) {                 /* we're done */
1859                                 break;
1860                         }
1861
1862                         if (format[1] != '%') { /* if we get here, *format == '%' */
1863                                 /* TODO: _do_one_spec needs to know what the output funcs are!!! */
1864                                 ppfs.fmtpos = (const char *)(++format);
1865                                 /* TODO: check -- should only fail on stream error */
1866                                 r = _do_one_spec(stream, &ppfs, &count);
1867                                 if (r < 0) {
1868                                         count = -1;
1869                                         break;
1870                                 }
1871                                 s = format = (const FMT_TYPE *) ppfs.fmtpos;
1872                         } else {                        /* %% means literal %, so start new string */
1873                                 s = ++format;
1874                                 ++format;
1875                         }
1876                 } while (1);
1877
1878                 va_end(ppfs.arg);               /* Need to clean up after va_copy! */
1879         }
1880
1881 /* #if defined(L__vfprintf_internal) && defined(__UCLIBC_HAS_WCHAR__) */
1882 /*  DONE: */
1883 /* #endif */
1884
1885         return count;
1886 }
1887 #endif /* defined(L__vfprintf_internal) || defined(L__vfwprintf_internal) */
1888
1889
1890 /**********************************************************************/
1891 #if defined(L_vfprintf) || defined(L_vfwprintf)
1892
1893 /* This is just a wrapper around VFPRINTF_internal.
1894  * Factoring out vfprintf internals allows:
1895  * (1) vdprintf and vsnprintf don't need to setup fake locking,
1896  * (2) __STDIO_STREAM_TRANS_TO_WRITE is not used in vfprintf internals,
1897  * and thus fseek etc is not pulled in by vdprintf and vsnprintf.
1898  *
1899  * In order to not pull in fseek through fputs, OUTPUT() macro
1900  * is using __stdio_fwrite (TODO: do the same for wide functions).
1901  */
1902 #ifdef L_vfprintf
1903 # define VFPRINTF vfprintf
1904 # define VFPRINTF_internal _vfprintf_internal
1905 # define FMT_TYPE char
1906 #else
1907 # define VFPRINTF vfwprintf
1908 # define VFPRINTF_internal _vfwprintf_internal
1909 # define FMT_TYPE wchar_t
1910 #endif
1911
1912 libc_hidden_proto(VFPRINTF)
1913 int VFPRINTF (FILE * __restrict stream,
1914                           const FMT_TYPE * __restrict format,
1915                           va_list arg)
1916 {
1917         int count;
1918         __STDIO_AUTO_THREADLOCK_VAR;
1919
1920         __STDIO_AUTO_THREADLOCK(stream);
1921
1922         if
1923 #ifdef L_vfprintf
1924         (!__STDIO_STREAM_IS_NARROW_WRITING(stream)
1925          && __STDIO_STREAM_TRANS_TO_WRITE(stream, __FLAG_NARROW))
1926 #else
1927         (!__STDIO_STREAM_IS_WIDE_WRITING(stream)
1928          && __STDIO_STREAM_TRANS_TO_WRITE(stream, __FLAG_WIDE))
1929 #endif
1930         {
1931                 count = -1;
1932         } else {
1933                 count = VFPRINTF_internal(stream, format, arg);
1934         }
1935
1936         __STDIO_AUTO_THREADUNLOCK(stream);
1937
1938         return count;
1939 }
1940 libc_hidden_def(VFPRINTF)
1941 #endif /* defined(L_vfprintf) || defined(L_vfwprintf) */
1942
1943 /**********************************************************************/