From 0059a5a88e92171f8d0ad4e0761eade8aadfcec0 Mon Sep 17 00:00:00 2001 From: Johannes Date: Thu, 21 Feb 2019 13:26:33 -0600 Subject: [PATCH] libs/libc/stdio/lib_vsprintf.c and Kconfig: - Numbered arguments now work by using two pass parsing and an argument list. The maximum number of numbered args is determined by CONFIG_LIBC_NL_ARGMAX which is then copied into NL_ARGMAX. - Size of pointer argument ('p') is determined before output. include/limits.h: Define NL_ARGMAX (as well as some of the other 'invariant values' per http://pubs.opengroup.org/onlinepubs/7908799/xsh/limits.h.html) --- include/limits.h | 69 ++- libs/libc/stdio/Kconfig | 16 +- libs/libc/stdio/lib_libvsprintf.c | 763 ++++++++++++++++++++---------- 3 files changed, 596 insertions(+), 252 deletions(-) diff --git a/include/limits.h b/include/limits.h index 6f8a6d7749..a954917654 100644 --- a/include/limits.h +++ b/include/limits.h @@ -1,7 +1,7 @@ /******************************************************************************** * include/limits.h * - * Copyright (C) 2007-2009, 2014 Gregory Nutt. All rights reserved. + * Copyright (C) 2007-2009, 2014, 2019 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without @@ -197,7 +197,7 @@ #define _POSIX_SEM_NSEMS_MAX INT_MAX #define _POSIX_SEM_VALUE_MAX 0x7fff -/* Actual limits. These values may be increased from the POSIX minimum +/* Numerical limits. These values may be increased from the POSIX minimum * values above or made indeterminate */ @@ -230,6 +230,71 @@ #define TIMER_MAX _POSIX_TIMER_MAX #define CLOCKRES_MIN _POSIX_CLOCKRES_MIN +/* Other invariant values */ + +/* CHARCLASS_NAME_MAX + * Maximum number of bytes in a character class name. Minimum Acceptable + * Value: 14 + */ + +#define CHARCLASS_NAME_MAX 14 + +/* Maximum value of digit in calls to the printf() and scanf() functions. + * Minimum Acceptable Value: 9 + */ + +#ifdef CONFIG_LIBC_NUMBERED_ARGS +# ifdef CONFIG_LIBC_NL_ARGMAX +# define NL_ARGMAX CONFIG_LIBC_NL_ARGMAX +# else +# define NL_ARGMAX 9 +# endif +#endif + +/* NL_LANGMAX + * Maximum number of bytes in a LANG name. Minimum Acceptable Value: 14 + */ + +#define NL_LANGMAX 14 + +/* NL_MSGMAX + * Maximum message number. Minimum Acceptable Value: 32 67 + */ + +#define NL_MSGMAX 32767 + +/* NL_NMAX + * Maximum number of bytes in an N-to-1 collation mapping. Minimum + * Acceptable Value: * + */ + +/* NL_SETMAX + * Maximum set number. Minimum Acceptable Value: 255 + */ + +#define NL_SETMAX 255 + +/* NL_TEXTMAX + * Maximum number of bytes in a message string. Minimum Acceptable Value: + * _POSIX2_LINE_MAX + */ + +#define NL_TEXTMAX _POSIX2_LINE_MAX + +/* NZERO + * Default process priority. Minimum Acceptable Value: 20 + */ + +#define NZERO 20 + +/* TMP_MAX + * Minimum number of unique pathnames generated by tmpnam(). Maximum + * number of times an application can call tmpnam() reliably. (LEGACY) + * Minimum Acceptable Value: 10000 + */ + +#define TMP_MAX10000 + /* Required for asynchronous I/O */ #define AIO_LISTIO_MAX _POSIX_AIO_LISTIO_MAX diff --git a/libs/libc/stdio/Kconfig b/libs/libc/stdio/Kconfig index df3f252644..e8e03c179a 100644 --- a/libs/libc/stdio/Kconfig +++ b/libs/libc/stdio/Kconfig @@ -97,16 +97,28 @@ config LIBC_PRINT_MINIMAL config LIBC_NUMBERED_ARGS bool "Enable numbered arguments in printf" default n - depends on (LIBC_FLOATINGPOINT || LIBC_LONG_LONG || !LIBC_PRINT_MINIMAL) && !LIBC_PRINT_LEGACY && EXPERIMENTAL + depends on (LIBC_FLOATINGPOINT || LIBC_LONG_LONG || !LIBC_PRINT_MINIMAL) && !LIBC_PRINT_LEGACY ---help--- Enables support for numbered arguments in printf. printf("%3$s %3$s %1$s %2$s\n", "1", "2", "3"); --> "3 3 1 2" printf("%3$*2$.*1$f\n", 4, 8, 1.234567); --> " 1.2346" - Attention mixing of numbered and traditional arguments in one + Attention: Mixing of numbered and sequential arguments in one format string is not allowed according to POSIX. +config LIBC_NL_ARGMAX + int "Maximum number of numbered arguments for printf" + default 16 + depends on LIBC_NUMBERED_ARGS + ---help--- + Applies only if there are numbered arguments in your format string. + The number of sequential arguments isn't affected. + Will be copied into stdio POSIX macro NL_ARGMAX. + + Attention: Increasing this value will increase stack usage + of printf. + config LIBC_SCANSET bool "Scanset support" default n diff --git a/libs/libc/stdio/lib_libvsprintf.c b/libs/libc/stdio/lib_libvsprintf.c index 33b1f2569c..c24dd5712c 100644 --- a/libs/libc/stdio/lib_libvsprintf.c +++ b/libs/libc/stdio/lib_libvsprintf.c @@ -46,6 +46,7 @@ #include #include #include +#include #include @@ -71,7 +72,7 @@ #if defined(CONFIG_LIBC_FLOATINGPOINT) # define PRINTF_LEVEL PRINTF_FLT -#elif defined(CONFIG_LIB_LONG_LONG) || !defined(CONFIG_LIBC_PRINT_MINIMAL) +#elif defined(CONFIG_LIBC_LONG_LONG) || !defined(CONFIG_LIBC_PRINT_MINIMAL) # define PRINTF_LEVEL PRINTF_STD #else # define PRINTF_LEVEL PRINTF_MIN @@ -122,6 +123,12 @@ # define FL_FLTEXP 0x4000 # define FL_FLTFIX 0x8000 +# define TYPE_INT 1 +# define TYPE_LONG 2 +# define TYPE_LONG_LONG 3 +# define TYPE_DOUBLE 4 +# define TYPE_CHAR_POINTER 5 + #endif /* Support special access to CODE-space strings for Harvard architectures */ @@ -132,201 +139,46 @@ # define fmt_char(fmt) (*(fmt)++) #endif +/**************************************************************************** + * Private Types + ****************************************************************************/ + +#if PRINTF_LEVEL > PRINTF_MIN +struct arg +{ + unsigned char type; + union + { + unsigned int u; + unsigned long ul; +#ifdef CONFIG_LIBC_LONG_LONG + unsigned long long ull; +#endif + double d; + FAR char *cp; + } value; +}; +#endif + /**************************************************************************** * Public Functions ****************************************************************************/ -#if PRINTF_LEVEL <= PRINTF_MIN -int lib_vsprintf(FAR struct lib_outstream_s *stream, - FAR const IPTR char *fmt, va_list ap) +#if PRINTF_LEVEL > PRINTF_MIN +static int vsprintf_internal(FAR struct lib_outstream_s *stream, + FAR struct arg *arglist, int numargs, + FAR const IPTR char *fmt, va_list ap) { - unsigned char c; /* Holds a char from the format string */ - unsigned char flags; - unsigned char buf[11]; /* Size for -1 in octal, without '\0' */ - int total_len = 0; - - for (; ; ) - { - for (; ; ) - { - c = fmt_char(fmt); - if (c == '\0') - { - goto ret; - } - - if (c == '%') - { - c = fmt_char(fmt); - if (c != '%') - { - break; - } - } - - putc(c, stream); - } - - for (flags = 0; !(flags & FL_LONG); /* 'll' will detect as error */ - c = fmt_char(fmt)) - { - if (c != '\0' && strchr(" +-.0123456789h", c) != NULL) - { - continue; - } - - if (c == '#') - { - flags |= FL_ALT; - continue; - } - - if (c == 'l') - { - flags |= FL_LONG; - continue; - } - - break; - } - - /* Only a format character is valid. */ - - if (c && strchr("EFGefg", c)) - { - (void)va_arg(ap, double); - putc('?', stream); - continue; - } - - { - FAR const char *pnt; - - switch (c) - { - case 'c': - putc(va_arg(ap, int), stream); - continue; - - case 'S': - - /* FALLTHROUGH */ - - case 's': - pnt = va_arg(ap, FAR char *); - while ((c = *pnt++) != 0) - { - putc(c, stream); - } - - continue; - } - } - - if (c == 'd' || c == 'i') - { - long x = (flags & FL_LONG) ? va_arg(ap, long) : va_arg(ap, int); - - flags &= ~FL_ALT; - if (x < 0) - { - x = -x; - - /* `putc ('-', stream)' will considarably inlarge stack size. So - * flag is used. - */ - - flags |= FL_NEGATIVE; - } - - c = __ultoa_invert(x, (FAR char *)buf, 10) - (FAR char *)buf; - } - else - { - int base; - - switch (c) - { - case 'u': - flags &= ~FL_ALT; - base = 10; - goto ultoa; - - case 'o': - base = 8; - goto ultoa; - - case 'p': - flags |= FL_ALT; - - /* no break */ - - case 'x': - flags |= (FL_ALTHEX | FL_ALTLWR); - base = 16; - goto ultoa; - - case 'X': - flags |= FL_ALTHEX; - base = 16 | XTOA_UPPER; - - ultoa: - c = __ultoa_invert((flags & FL_LONG) - ? va_arg(ap, unsigned long) - : va_arg(ap, unsigned int), - (FAR char *)buf, base) - (FAR char *)buf; - break; - - default: - goto ret; - } - } - - /* Integer number output. */ - - if ((flags & FL_NEGATIVE) != 0) - { - putc('-', stream); - } - - if ((flags & FL_ALT) != 0 && buf[c - 1] != '0') - { - putc('0', stream); - if ((flags & FL_ALTHEX) != 0) - { -# if FL_ALTLWR != 'x' - 'X' -# error -# endif - putc('X' + (flags & FL_ALTLWR), stream); - } - } - - do - { - putc(buf[--c], stream); - } - while (c); - } - -ret: - return total_len; -} - -#else /* i.e. PRINTF_LEVEL > PRINTF_MIN */ - -int lib_vsprintf(FAR struct lib_outstream_s *stream, - FAR const IPTR char *fmt, va_list ap) -{ - unsigned char c; /* Holds a char from the format string */ + unsigned char c; /* Holds a char from the format string */ uint16_t flags; int width; int prec; union { -#ifdef CONFIG_LIBC_LONG_LONG - unsigned char __buf[22]; /* Size for -1 in octal, without '\0' */ +#if defined (CONFIG_LIBC_LONG_LONG) || (ULONG_MAX > 4294967295UL) + unsigned char __buf[22]; /* Size for -1 in octal, without '\0' */ #else - unsigned char __buf[11]; /* Size for -1 in octal, without '\0' */ + unsigned char __buf[11]; /* Size for -1 in octal, without '\0' */ #endif #if PRINTF_LEVEL >= PRINTF_FLT struct dtoa_s __dtoa; @@ -342,12 +194,9 @@ int lib_vsprintf(FAR struct lib_outstream_s *stream, int total_len = 0; #ifdef CONFIG_LIBC_NUMBERED_ARGS + int argnumber; - va_list work_ap; - va_copy(work_ap, ap); -#else -# define work_ap ap #endif for (; ; ) @@ -369,7 +218,14 @@ int lib_vsprintf(FAR struct lib_outstream_s *stream, } } +#ifdef CONFIG_LIBC_NUMBERED_ARGS + if (stream != NULL) + { + putc(c, stream); + } +#else putc(c, stream); +#endif } flags = 0; @@ -378,7 +234,7 @@ int lib_vsprintf(FAR struct lib_outstream_s *stream, do { - if (flags < FL_WIDTH) + if (flags < FL_ASTERISK) { switch (c) { @@ -412,6 +268,7 @@ int lib_vsprintf(FAR struct lib_outstream_s *stream, { if ((flags & FL_ARGNUMBER) == 0) { + /* No other flag except FL_WIDTH or FL_ZFILL (leading * zeros) and argument number must be at least 1 */ @@ -425,38 +282,48 @@ int lib_vsprintf(FAR struct lib_outstream_s *stream, /* It had been the argument number. */ argnumber = width; - flags |= FL_ARGNUMBER; - width = 0; - flags &= ~(FL_WIDTH | FL_ZFILL); + flags = FL_ARGNUMBER; } else if ((flags & FL_ASTERISK) != 0) { + int index; + flags &= ~FL_ASTERISK; - va_end(work_ap); - va_copy(work_ap, ap); if ((flags & FL_PREC) == 0) { - /* Jump to argument */ - - while (--width) - { - (void)va_arg(work_ap, int); - } - - width = va_arg(work_ap, int); + index = width; } else { - /* Jump to argument */ - - while (--prec) + index = prec; + } + if (index > 0 && index <= numargs) + { + if (stream == NULL) { - (void)va_arg(work_ap, int); + arglist[index-1].type = TYPE_INT; + if (index > total_len) + { + total_len = index; + } } - - prec = va_arg(work_ap, int); + else + { + if ((flags & FL_PREC) == 0) + { + width = (int)arglist[index-1].value.u; + } + else + { + prec = (int)arglist[index-1].value.u; + } + } + } + else + { + goto ret; } } else @@ -494,7 +361,7 @@ int lib_vsprintf(FAR struct lib_outstream_s *stream, if ((flags & FL_PREC) != 0) { - prec = va_arg(work_ap, int); + prec = va_arg(ap, int); if (prec < 0) { prec = 0; @@ -502,7 +369,7 @@ int lib_vsprintf(FAR struct lib_outstream_s *stream, } else { - width = va_arg(work_ap, int); + width = va_arg(ap, int); flags |= FL_WIDTH; if (width < 0) @@ -557,26 +424,86 @@ int lib_vsprintf(FAR struct lib_outstream_s *stream, /* Only a format character is valid. */ -# if 'F' != 'E'+1 || 'G' != 'F'+1 || 'f' != 'e'+1 || 'g' != 'f'+1 -# error -# endif +#if 'F' != 'E'+1 || 'G' != 'F'+1 || 'f' != 'e'+1 || 'g' != 'f'+1 +# error +#endif -# ifdef CONFIG_LIBC_NUMBERED_ARGS - if ((flags & FL_ARGNUMBER) != 0) + if (c == 'p') { - /* Jump to argument */ + /* Determine size of pointer and set flags accordingly */ - va_end(work_ap); - va_copy(work_ap, ap); + flags &= ~(FL_LONG | FL_REPD_TYPE); - while (--argnumber) +#ifdef CONFIG_LIBC_LONG_LONG + if (sizeof(void *) == sizeof(unsigned long long)) { - (void)va_arg(work_ap, int); + flags |= (FL_LONG | FL_REPD_TYPE); + } + else +#endif + if (sizeof(void *) == sizeof(unsigned long)) + { + flags |= FL_LONG; } } -# endif -# if PRINTF_LEVEL >= PRINTF_FLT +#ifdef CONFIG_LIBC_NUMBERED_ARGS + + if ((flags & FL_ARGNUMBER) != 0) + { + if (argnumber > 0 && argnumber <= numargs) + { + if (stream == NULL) + { + if ((c >= 'E' && c <= 'G') + || (c >= 'e' && c <= 'g')) + { + arglist[argnumber-1].type = TYPE_DOUBLE; + } + else if (c == 'i' || c == 'd' || c == 'u' || c == 'p') + { + if ((flags & FL_LONG) == 0) + { + arglist[argnumber-1].type = TYPE_INT; + } + else if ((flags & FL_REPD_TYPE) == 0) + { + arglist[argnumber-1].type = TYPE_LONG; + } + else + { + arglist[argnumber-1].type = TYPE_LONG_LONG; + } + } + else if (c == 'c') + { + arglist[argnumber-1].type = TYPE_INT; + } + else if (c == 's') + { + arglist[argnumber-1].type = TYPE_CHAR_POINTER; + } + + if (argnumber > total_len) + { + total_len = argnumber; + } + continue; /* We do only parsing */ + } + } + else + { + goto ret; + } + } + else if (stream == NULL) + { + continue; /* We do only parsing */ + } + +#endif + +#if PRINTF_LEVEL >= PRINTF_FLT if (c >= 'E' && c <= 'G') { flags |= FL_FLTUPP; @@ -585,6 +512,7 @@ int lib_vsprintf(FAR struct lib_outstream_s *stream, } else if (c >= 'e' && c <= 'g') { + double value; int exp; /* Exponent of master decimal digit */ int n; uint8_t sign; /* Sign character (or 0) */ @@ -594,7 +522,7 @@ int lib_vsprintf(FAR struct lib_outstream_s *stream, flags &= ~FL_FLTUPP; - flt_oper: + flt_oper: ndigs = 0; if ((flags & FL_PREC) == 0) { @@ -626,9 +554,22 @@ int lib_vsprintf(FAR struct lib_outstream_s *stream, ndigs = DTOA_MAX_DIG; } - ndigs = __dtoa_engine(va_arg(work_ap, double), &_dtoa, ndigs, - ndecimal); - exp = _dtoa.exp; +#ifdef CONFIG_LIBC_NUMBERED_ARGS + if ((flags & FL_ARGNUMBER) != 0) + { + value = arglist[argnumber-1].value.d; + } + else + { + value = va_arg(ap, double); + } +#else + value = va_arg(ap, double); +#endif + + ndigs = __dtoa_engine(value, &_dtoa, ndigs, + ndecimal); + exp = _dtoa.exp; sign = 0; if ((_dtoa.flags & DTOA_MINUS) && !(_dtoa.flags & DTOA_NAN)) @@ -677,9 +618,9 @@ int lib_vsprintf(FAR struct lib_outstream_s *stream, p = "nan"; } -# if ('I'-'i' != 'N'-'n') || ('I'-'i' != 'F'-'f') || ('I'-'i' != 'A'-'a') -# error -# endif +# if ('I'-'i' != 'N'-'n') || ('I'-'i' != 'F'-'f') || ('I'-'i' != 'A'-'a') +# error +# endif while ((ndigs = *p) != 0) { if ((flags & FL_FLTUPP) != 0) @@ -792,7 +733,7 @@ int lib_vsprintf(FAR struct lib_outstream_s *stream, * the exponent value */ - n = exp > 0 ? exp : 0; /* Exponent of left digit */ + n = exp > 0 ? exp : 0; /* Exponent of left digit */ do { @@ -828,7 +769,7 @@ int lib_vsprintf(FAR struct lib_outstream_s *stream, } while (1); - if (n == exp && (_dtoa.digits[0] > '5' || + if (n == exp && (_dtoa.digits[0] > '5' || (_dtoa.digits[0] == '5' && !(_dtoa.flags & DTOA_CARRY)))) { out = '1'; @@ -887,28 +828,50 @@ int lib_vsprintf(FAR struct lib_outstream_s *stream, goto tail; } -# else /* to: PRINTF_LEVEL >= PRINTF_FLT */ +#else /* to: PRINTF_LEVEL >= PRINTF_FLT */ if ((c >= 'E' && c <= 'G') || (c >= 'e' && c <= 'g')) { - (void)va_arg(work_ap, double); + (void)va_arg(ap, double); pnt = "*float*"; size = sizeof("*float*") - 1; goto str_lpad; } -# endif +#endif switch (c) { case 'c': - buf[0] = va_arg(work_ap, int); - pnt = (FAR char *)buf; +#ifdef CONFIG_LIBC_NUMBERED_ARGS + if ((flags & FL_ARGNUMBER) != 0) + { + buf[0] = (int)arglist[argnumber-1].value.u; + } + else + { + buf[0] = va_arg(ap, int); + } +#else + buf[0] = va_arg(ap, int); +#endif + pnt = (FAR char *) buf; size = 1; goto str_lpad; case 's': case 'S': - pnt = va_arg(work_ap, FAR char *); +#ifdef CONFIG_LIBC_NUMBERED_ARGS + if ((flags & FL_ARGNUMBER) != 0) + { + pnt = (FAR char *)arglist[argnumber-1].value.cp; + } + else + { + pnt = va_arg(ap, FAR char *); + } +#else + pnt = va_arg(ap, FAR char *); +#endif size = strnlen(pnt, (flags & FL_PREC) ? prec : ~0); str_lpad: @@ -944,17 +907,50 @@ int lib_vsprintf(FAR struct lib_outstream_s *stream, if ((flags & FL_LONG) != 0 && (flags & FL_REPD_TYPE) != 0) { - x = va_arg(work_ap, long long); +#ifdef CONFIG_LIBC_NUMBERED_ARGS + if ((flags & FL_ARGNUMBER) != 0) + { + x = (long long)arglist[argnumber-1].value.ull; + } + else + { + x = va_arg(ap, long long); + } +#else + x = va_arg(ap, long long); +#endif } else #endif if ((flags & FL_LONG) != 0) { - x = va_arg(work_ap, long); +#ifdef CONFIG_LIBC_NUMBERED_ARGS + if ((flags & FL_ARGNUMBER) != 0) + { + x = (long)arglist[argnumber-1].value.ul; + } + else + { + x = va_arg(ap, long); + } +#else + x = va_arg(ap, long); +#endif } else { - x = va_arg(work_ap, int); +#ifdef CONFIG_LIBC_NUMBERED_ARGS + if ((flags & FL_ARGNUMBER) != 0) + { + x = (int)arglist[argnumber-1].value.u; + } + else + { + x = va_arg(ap, int); + } +#else + x = va_arg(ap, int); +#endif if ((flags & FL_SHORT) != 0) { if ((flags & FL_REPD_TYPE) == 0) @@ -994,17 +990,50 @@ int lib_vsprintf(FAR struct lib_outstream_s *stream, if ((flags & FL_LONG) != 0 && (flags & FL_REPD_TYPE) != 0) { - x = va_arg(work_ap, unsigned long long); +#ifdef CONFIG_LIBC_NUMBERED_ARGS + if ((flags & FL_ARGNUMBER) != 0) + { + x = arglist[argnumber-1].value.ull; + } + else + { + x = va_arg(ap, unsigned long long); + } +#else + x = va_arg(ap, unsigned long long); +#endif } else #endif if ((flags & FL_LONG) != 0) { - x = va_arg(work_ap, unsigned long); +#ifdef CONFIG_LIBC_NUMBERED_ARGS + if ((flags & FL_ARGNUMBER) != 0) + { + x = arglist[argnumber-1].value.ul; + } + else + { + x = va_arg(ap, unsigned long); + } +#else + x = va_arg(ap, unsigned long); +#endif } else { - x = va_arg(work_ap, unsigned int); +#ifdef CONFIG_LIBC_NUMBERED_ARGS + if ((flags & FL_ARGNUMBER) != 0) + { + x = (unsigned int)arglist[argnumber-1].value.u; + } + else + { + x = va_arg(ap, unsigned int); + } +#else + x = va_arg(ap, unsigned int); +#endif if ((flags & FL_SHORT) != 0) { if ((flags & FL_REPD_TYPE) == 0) @@ -1163,9 +1192,9 @@ int lib_vsprintf(FAR struct lib_outstream_s *stream, putc(buf[--c], stream); } - tail: +tail: - /* Tail is possible. */ + /* Tail is possible. */ while (width) { @@ -1176,11 +1205,249 @@ int lib_vsprintf(FAR struct lib_outstream_s *stream, ret: -#ifdef CONFIG_LIBC_NUMBERED_ARGS - va_end(work_ap); + return total_len; +} #endif +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +#if PRINTF_LEVEL <= PRINTF_MIN +int lib_vsprintf(FAR struct lib_outstream_s *stream, + FAR const IPTR char *fmt, va_list ap) +{ + unsigned char c; /* Holds a char from the format string */ + unsigned char flags; + unsigned char buf[11]; /* Size for -1 in octal, without '\0' */ + int total_len = 0; + + for (; ; ) + { + for (; ; ) + { + c = fmt_char(fmt); + if (c == '\0') + { + goto ret; + } + + if (c == '%') + { + c = fmt_char(fmt); + if (c != '%') + { + break; + } + } + + putc(c, stream); + } + + for (flags = 0; !(flags & FL_LONG); /* 'll' will detect as error */ + c = fmt_char(fmt)) + { + if (c != '\0' && strchr(" +-.0123456789h", c) != NULL) + { + continue; + } + + if (c == '#') + { + flags |= FL_ALT; + continue; + } + + if (c == 'l') + { + flags |= FL_LONG; + continue; + } + + break; + } + + /* Only a format character is valid. */ + + if (c && strchr("EFGefg", c)) + { + (void)va_arg(ap, double); + putc('?', stream); + continue; + } + + { + FAR const char *pnt; + + switch (c) + { + case 'c': + putc(va_arg(ap, int), stream); + continue; + + case 'S': + + /* FALLTHROUGH */ + + case 's': + pnt = va_arg(ap, FAR char *); + while ((c = *pnt++) != 0) + { + putc(c, stream); + } + + continue; + } + } + + if (c == 'd' || c == 'i') + { + long x = (flags & FL_LONG) ? va_arg(ap, long) : va_arg(ap, int); + + flags &= ~FL_ALT; + if (x < 0) + { + x = -x; + + /* `putc ('-', stream)' will considarably inlarge stack size. So + * flag is used. + */ + + flags |= FL_NEGATIVE; + } + + c = __ultoa_invert(x, (FAR char *)buf, 10) - (FAR char *)buf; + } + else + { + int base; + + switch (c) + { + case 'u': + flags &= ~FL_ALT; + base = 10; + goto ultoa; + + case 'o': + base = 8; + goto ultoa; + + case 'p': + /* Determine size of pointer and set flags accordingly */ + + if (sizeof(FAR void *) == sizeof(unsigned long)) + { + flags |= FL_LONG; + } + else + { + flags &= ~FL_LONG; + } + + flags |= FL_ALT; + + /* no break */ + + case 'x': + flags |= (FL_ALTHEX | FL_ALTLWR); + base = 16; + goto ultoa; + + case 'X': + flags |= FL_ALTHEX; + base = 16 | XTOA_UPPER; + + ultoa: + c = __ultoa_invert((flags & FL_LONG) + ? va_arg(ap, unsigned long) + : va_arg(ap, unsigned int), + (FAR char *)buf, base) - (FAR char *)buf; + break; + + default: + goto ret; + } + } + + /* Integer number output. */ + + if ((flags & FL_NEGATIVE) != 0) + { + putc('-', stream); + } + + if ((flags & FL_ALT) != 0 && buf[c - 1] != '0') + { + putc('0', stream); + if ((flags & FL_ALTHEX) != 0) + { +#if FL_ALTLWR != 'x' - 'X' +# error +#endif + putc('X' + (flags & FL_ALTLWR), stream); + } + } + + do + { + putc(buf[--c], stream); + } + while (c); + } + +ret: return total_len; } +#else /* i.e. PRINTF_LEVEL > PRINTF_MIN */ + +int lib_vsprintf(FAR struct lib_outstream_s *stream, + FAR const IPTR char *fmt, va_list ap) +{ +#ifdef CONFIG_LIBC_NUMBERED_ARGS + int i; + struct arg arglist[NL_ARGMAX]; + int numargs; + + /* We do 2 passes of parsing and fill the arglist between the passes. */ + + numargs = vsprintf_internal(NULL, arglist, NL_ARGMAX, fmt, ap); + + for (i = 0; i < numargs; i++) + { + switch (arglist[i].type) + { + case TYPE_LONG_LONG: +#ifdef CONFIG_LIBC_LONG_LONG + arglist[i].value.ull = va_arg(ap, unsigned long long); + break; +#endif + case TYPE_LONG: + arglist[i].value.ul = va_arg(ap, unsigned long); + break; + + case TYPE_INT: + arglist[i].value.u = va_arg(ap, unsigned int); + break; + + case TYPE_DOUBLE: + arglist[i].value.d = va_arg(ap, double); + break; + + case TYPE_CHAR_POINTER: + arglist[i].value.cp = va_arg(ap, FAR char *); + break; + } + } + + return vsprintf_internal(stream, arglist, numargs, fmt, ap); + +#else + + return vsprintf_internal(stream, NULL, 0, fmt, ap); + +#endif +} + #endif /* PRINTF_LEVEL > PRINTF_MIN */