Implement a much more capable vuprintf()
New support as well as some buggy support fixed.
Still no floating point support if ever that would be desired.
Support (*):
* Flags: '-', '+', ' ', '#', '0'
* Width and precision: 'n', '.n', '*' and '.*'
* Length modifiers: 'hh', 'h', 'j', 'l', 'll', 't', 'z'
* Radix: 'c', 'd', 'i', 'n', 'o', 'p/P', 's', 'u', 'x/X'
(*) Provision exists to switch lesser-used stuff on or off or when
certain functionality isn't desired (bootloader?). The compulsory
radixes are everything but 'o', 'n', 'p/P' and 'x/X' with length
modifiers being optional. The default setup is 'l', 'z', 'c', 'd',
'p/P', 's', 'u', 'x/X'.
* Move fdprintf() to its own file. It was in a strange place.
* Make callers compatible and fix a couple snprintf() bugs while
at it.
Could smush it down in size but I'm gonna get over the binsize
neurosis and just the let optimizer do its thing.
Change-Id: Ibdc613a9b6775802c188b29b9dd46c568c94f7c3
2017-09-08 23:28:02 +00:00
|
|
|
|
/***************************************************************************
|
|
|
|
|
* __________ __ ___.
|
|
|
|
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
|
|
|
|
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
|
|
|
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
|
|
|
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
|
|
|
|
* \/ \/ \/ \/ \/
|
|
|
|
|
* $Id$
|
|
|
|
|
*
|
|
|
|
|
* Copyright (C) 2002 by Gary Czvitkovicz
|
|
|
|
|
* Copyright (C) 2017 by Michael A. Sevakis
|
|
|
|
|
*
|
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
|
* as published by the Free Software Foundation; either version 2
|
|
|
|
|
* of the License, or (at your option) any later version.
|
|
|
|
|
*
|
|
|
|
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
|
|
|
|
* KIND, either express or implied.
|
|
|
|
|
*
|
|
|
|
|
****************************************************************************/
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
|
#include <limits.h>
|
|
|
|
|
#include <string.h>
|
2017-01-21 20:18:31 +00:00
|
|
|
|
#include <stddef.h>
|
2018-01-06 12:17:04 +00:00
|
|
|
|
#include <stdlib.h>
|
Implement a much more capable vuprintf()
New support as well as some buggy support fixed.
Still no floating point support if ever that would be desired.
Support (*):
* Flags: '-', '+', ' ', '#', '0'
* Width and precision: 'n', '.n', '*' and '.*'
* Length modifiers: 'hh', 'h', 'j', 'l', 'll', 't', 'z'
* Radix: 'c', 'd', 'i', 'n', 'o', 'p/P', 's', 'u', 'x/X'
(*) Provision exists to switch lesser-used stuff on or off or when
certain functionality isn't desired (bootloader?). The compulsory
radixes are everything but 'o', 'n', 'p/P' and 'x/X' with length
modifiers being optional. The default setup is 'l', 'z', 'c', 'd',
'p/P', 's', 'u', 'x/X'.
* Move fdprintf() to its own file. It was in a strange place.
* Make callers compatible and fix a couple snprintf() bugs while
at it.
Could smush it down in size but I'm gonna get over the binsize
neurosis and just the let optimizer do its thing.
Change-Id: Ibdc613a9b6775802c188b29b9dd46c568c94f7c3
2017-09-08 23:28:02 +00:00
|
|
|
|
#include "system.h"
|
|
|
|
|
#include "vuprintf.h"
|
2018-01-06 12:17:04 +00:00
|
|
|
|
#include "ap_int.h"
|
Implement a much more capable vuprintf()
New support as well as some buggy support fixed.
Still no floating point support if ever that would be desired.
Support (*):
* Flags: '-', '+', ' ', '#', '0'
* Width and precision: 'n', '.n', '*' and '.*'
* Length modifiers: 'hh', 'h', 'j', 'l', 'll', 't', 'z'
* Radix: 'c', 'd', 'i', 'n', 'o', 'p/P', 's', 'u', 'x/X'
(*) Provision exists to switch lesser-used stuff on or off or when
certain functionality isn't desired (bootloader?). The compulsory
radixes are everything but 'o', 'n', 'p/P' and 'x/X' with length
modifiers being optional. The default setup is 'l', 'z', 'c', 'd',
'p/P', 's', 'u', 'x/X'.
* Move fdprintf() to its own file. It was in a strange place.
* Make callers compatible and fix a couple snprintf() bugs while
at it.
Could smush it down in size but I'm gonna get over the binsize
neurosis and just the let optimizer do its thing.
Change-Id: Ibdc613a9b6775802c188b29b9dd46c568c94f7c3
2017-09-08 23:28:02 +00:00
|
|
|
|
|
2018-05-22 23:07:57 +00:00
|
|
|
|
#ifndef BOOTLOADER
|
2019-07-20 03:20:56 +00:00
|
|
|
|
|
|
|
|
|
/* Only the Quake plugin needs float formatting */
|
|
|
|
|
#if defined(HAVE_LCD_COLOR) && \
|
|
|
|
|
(!defined(LCD_STRIDEFORMAT) || (LCD_STRIDEFORMAT != VERTICAL_STRIDE)) && \
|
|
|
|
|
(PLUGIN_BUFFER_SIZE > 0x14000) && (CONFIG_PLATFORM & PLATFORM_NATIVE) && defined(CPU_ARM)
|
Implement a much more capable vuprintf()
New support as well as some buggy support fixed.
Still no floating point support if ever that would be desired.
Support (*):
* Flags: '-', '+', ' ', '#', '0'
* Width and precision: 'n', '.n', '*' and '.*'
* Length modifiers: 'hh', 'h', 'j', 'l', 'll', 't', 'z'
* Radix: 'c', 'd', 'i', 'n', 'o', 'p/P', 's', 'u', 'x/X'
(*) Provision exists to switch lesser-used stuff on or off or when
certain functionality isn't desired (bootloader?). The compulsory
radixes are everything but 'o', 'n', 'p/P' and 'x/X' with length
modifiers being optional. The default setup is 'l', 'z', 'c', 'd',
'p/P', 's', 'u', 'x/X'.
* Move fdprintf() to its own file. It was in a strange place.
* Make callers compatible and fix a couple snprintf() bugs while
at it.
Could smush it down in size but I'm gonna get over the binsize
neurosis and just the let optimizer do its thing.
Change-Id: Ibdc613a9b6775802c188b29b9dd46c568c94f7c3
2017-09-08 23:28:02 +00:00
|
|
|
|
/* turn everything on */
|
|
|
|
|
#define FMT_LENMOD (0xffffffff)
|
|
|
|
|
#define FMT_RADIX (0xffffffff)
|
|
|
|
|
#endif
|
|
|
|
|
|
2019-07-20 03:20:56 +00:00
|
|
|
|
#endif
|
|
|
|
|
|
Implement a much more capable vuprintf()
New support as well as some buggy support fixed.
Still no floating point support if ever that would be desired.
Support (*):
* Flags: '-', '+', ' ', '#', '0'
* Width and precision: 'n', '.n', '*' and '.*'
* Length modifiers: 'hh', 'h', 'j', 'l', 'll', 't', 'z'
* Radix: 'c', 'd', 'i', 'n', 'o', 'p/P', 's', 'u', 'x/X'
(*) Provision exists to switch lesser-used stuff on or off or when
certain functionality isn't desired (bootloader?). The compulsory
radixes are everything but 'o', 'n', 'p/P' and 'x/X' with length
modifiers being optional. The default setup is 'l', 'z', 'c', 'd',
'p/P', 's', 'u', 'x/X'.
* Move fdprintf() to its own file. It was in a strange place.
* Make callers compatible and fix a couple snprintf() bugs while
at it.
Could smush it down in size but I'm gonna get over the binsize
neurosis and just the let optimizer do its thing.
Change-Id: Ibdc613a9b6775802c188b29b9dd46c568c94f7c3
2017-09-08 23:28:02 +00:00
|
|
|
|
/* these are the defaults if no other preference is given */
|
|
|
|
|
#ifndef FMT_LENMOD
|
|
|
|
|
#define FMT_LENMOD (FMT_LENMOD_l | \
|
|
|
|
|
FMT_LENMOD_z)
|
|
|
|
|
#endif /* FMT_LENMOD */
|
|
|
|
|
|
|
|
|
|
#ifndef FMT_RADIX
|
|
|
|
|
#define FMT_RADIX (FMT_RADIX_c | \
|
|
|
|
|
FMT_RADIX_d | \
|
|
|
|
|
FMT_RADIX_p | \
|
|
|
|
|
FMT_RADIX_s | \
|
|
|
|
|
FMT_RADIX_u | \
|
|
|
|
|
FMT_RADIX_x)
|
|
|
|
|
#endif /* FMT_RADIX */
|
|
|
|
|
|
|
|
|
|
/** Length modifier and radix flags **/
|
|
|
|
|
|
|
|
|
|
/* compulsory length modifiers: NONE
|
|
|
|
|
* however a compatible 'l' or 'll' must be defined if another requires it */
|
|
|
|
|
#define FMT_LENMOD_h 0x001 /* signed/unsigned short (%h<radix>) */
|
|
|
|
|
#define FMT_LENMOD_hh 0x002 /* signed/unsigned char (%hh<radix>) */
|
|
|
|
|
#define FMT_LENMOD_j 0x004 /* intmax_t/uintmax_t (%j<radix>) */
|
|
|
|
|
#define FMT_LENMOD_l 0x008 /* signed/unsigned long (%l<radix>) */
|
|
|
|
|
#define FMT_LENMOD_ll 0x010 /* signed/unsigned long long (%ll<radix>) */
|
|
|
|
|
#define FMT_LENMOD_t 0x020 /* signed/unsigned ptrdiff_t (%t<radix>) */
|
|
|
|
|
#define FMT_LENMOD_z 0x040 /* size_t/ssize_t (%z<radix>) */
|
2018-01-06 12:17:04 +00:00
|
|
|
|
#if 0
|
Implement a much more capable vuprintf()
New support as well as some buggy support fixed.
Still no floating point support if ever that would be desired.
Support (*):
* Flags: '-', '+', ' ', '#', '0'
* Width and precision: 'n', '.n', '*' and '.*'
* Length modifiers: 'hh', 'h', 'j', 'l', 'll', 't', 'z'
* Radix: 'c', 'd', 'i', 'n', 'o', 'p/P', 's', 'u', 'x/X'
(*) Provision exists to switch lesser-used stuff on or off or when
certain functionality isn't desired (bootloader?). The compulsory
radixes are everything but 'o', 'n', 'p/P' and 'x/X' with length
modifiers being optional. The default setup is 'l', 'z', 'c', 'd',
'p/P', 's', 'u', 'x/X'.
* Move fdprintf() to its own file. It was in a strange place.
* Make callers compatible and fix a couple snprintf() bugs while
at it.
Could smush it down in size but I'm gonna get over the binsize
neurosis and just the let optimizer do its thing.
Change-Id: Ibdc613a9b6775802c188b29b9dd46c568c94f7c3
2017-09-08 23:28:02 +00:00
|
|
|
|
#define FMT_LENMOD_L 0x080 /* long double (instead of double) */
|
2018-01-06 12:17:04 +00:00
|
|
|
|
#else
|
|
|
|
|
#define FMT_LENMOD_L 0x000
|
|
|
|
|
#endif
|
Implement a much more capable vuprintf()
New support as well as some buggy support fixed.
Still no floating point support if ever that would be desired.
Support (*):
* Flags: '-', '+', ' ', '#', '0'
* Width and precision: 'n', '.n', '*' and '.*'
* Length modifiers: 'hh', 'h', 'j', 'l', 'll', 't', 'z'
* Radix: 'c', 'd', 'i', 'n', 'o', 'p/P', 's', 'u', 'x/X'
(*) Provision exists to switch lesser-used stuff on or off or when
certain functionality isn't desired (bootloader?). The compulsory
radixes are everything but 'o', 'n', 'p/P' and 'x/X' with length
modifiers being optional. The default setup is 'l', 'z', 'c', 'd',
'p/P', 's', 'u', 'x/X'.
* Move fdprintf() to its own file. It was in a strange place.
* Make callers compatible and fix a couple snprintf() bugs while
at it.
Could smush it down in size but I'm gonna get over the binsize
neurosis and just the let optimizer do its thing.
Change-Id: Ibdc613a9b6775802c188b29b9dd46c568c94f7c3
2017-09-08 23:28:02 +00:00
|
|
|
|
|
|
|
|
|
/* compulsory radixes: c, d, i, u, s */
|
|
|
|
|
#define FMT_RADIX_c 0x001 /* single character (%c) */
|
|
|
|
|
#define FMT_RADIX_d 0x002 /* signed integer type, decimal (%d %i) */
|
|
|
|
|
#define FMT_RADIX_n 0x004 /* bytes output so far (%n) */
|
|
|
|
|
#define FMT_RADIX_o 0x008 /* unsigned integer type, octal (%o) */
|
|
|
|
|
#define FMT_RADIX_p 0x010 /* pointer (%p %P) */
|
|
|
|
|
#define FMT_RADIX_s 0x020 /* string (%s) */
|
|
|
|
|
#define FMT_RADIX_u 0x040 /* unsigned integer type, decimal (%u) */
|
|
|
|
|
#define FMT_RADIX_x 0x080 /* unsigned integer type, hex (%x %X) */
|
|
|
|
|
#define FMT_RADIX_a 0x100 /* hex floating point "[-]0xh.hhhhp<68>d" */
|
|
|
|
|
#define FMT_RADIX_e 0x200 /* floating point with exponent "[-]d.ddde<64>dd" */
|
|
|
|
|
#define FMT_RADIX_f 0x400 /* floating point "[-]ddd.ddd" */
|
|
|
|
|
#define FMT_RADIX_g 0x800 /* floating point exponent or decimal depending
|
|
|
|
|
upon value and precision */
|
|
|
|
|
|
2018-01-06 12:17:04 +00:00
|
|
|
|
/* TODO: 'a' 'A' */
|
|
|
|
|
#define FMT_RADIX_floats (FMT_RADIX_e|FMT_RADIX_f|FMT_RADIX_g)
|
|
|
|
|
|
|
|
|
|
#if (FMT_RADIX & FMT_RADIX_floats)
|
|
|
|
|
/* Assumes IEEE 754 double-precision, native-endian; replace to parse and init
|
|
|
|
|
for some other format */
|
|
|
|
|
#define parse_double parse_ieee754_double
|
|
|
|
|
#define init_double_chunks init_ieee754_double_chunks
|
|
|
|
|
#define format_double_int10 format_ap_int10
|
|
|
|
|
#define format_double_frac10 format_ap_frac10
|
|
|
|
|
#endif
|
|
|
|
|
|
Implement a much more capable vuprintf()
New support as well as some buggy support fixed.
Still no floating point support if ever that would be desired.
Support (*):
* Flags: '-', '+', ' ', '#', '0'
* Width and precision: 'n', '.n', '*' and '.*'
* Length modifiers: 'hh', 'h', 'j', 'l', 'll', 't', 'z'
* Radix: 'c', 'd', 'i', 'n', 'o', 'p/P', 's', 'u', 'x/X'
(*) Provision exists to switch lesser-used stuff on or off or when
certain functionality isn't desired (bootloader?). The compulsory
radixes are everything but 'o', 'n', 'p/P' and 'x/X' with length
modifiers being optional. The default setup is 'l', 'z', 'c', 'd',
'p/P', 's', 'u', 'x/X'.
* Move fdprintf() to its own file. It was in a strange place.
* Make callers compatible and fix a couple snprintf() bugs while
at it.
Could smush it down in size but I'm gonna get over the binsize
neurosis and just the let optimizer do its thing.
Change-Id: Ibdc613a9b6775802c188b29b9dd46c568c94f7c3
2017-09-08 23:28:02 +00:00
|
|
|
|
/* avoid defining redundant functions if two or more types can use the same
|
|
|
|
|
* something not getting a macro means it gets assigned its own value and
|
|
|
|
|
* formatter */
|
|
|
|
|
|
|
|
|
|
/* l */
|
|
|
|
|
#if LONG_MIN == INT_MIN && LONG_MAX == INT_MAX
|
|
|
|
|
#define val_ld val_d
|
|
|
|
|
#define format_ld format_d
|
|
|
|
|
#define branch_fmt_ld branch_fmt_d
|
|
|
|
|
#elif !(FMT_LENMOD & FMT_LENMOD_l) /* unique */
|
|
|
|
|
#define val_ld
|
|
|
|
|
#endif /* LONG_ */
|
|
|
|
|
|
|
|
|
|
#if ULONG_MAX == UINT_MAX
|
|
|
|
|
#define val_lu val_u
|
|
|
|
|
#define format_lu format_u
|
|
|
|
|
#define branch_fmt_lu branch_fmt_u
|
|
|
|
|
#elif !(FMT_LENMOD & FMT_LENMOD_l) /* unique */
|
|
|
|
|
#define val_lu
|
|
|
|
|
#endif /* ULONG_ */
|
|
|
|
|
|
|
|
|
|
/* ll */
|
|
|
|
|
#if LLONG_MIN == INT_MIN && LLONG_MAX == INT_MAX
|
|
|
|
|
#define val_lld val_d
|
|
|
|
|
#define format_lld format_d
|
|
|
|
|
#define branch_fmt_lld branch_fmt_d
|
|
|
|
|
#elif LLONG_MIN == LONG_MIN && LLONG_MAX == LONG_MAX
|
|
|
|
|
#define val_lld val_ld
|
|
|
|
|
#define format_lld format_ld
|
|
|
|
|
#define branch_fmt_lld branch_fmt_ld
|
|
|
|
|
#elif !(FMT_LENMOD & FMT_LENMOD_ll) /* unique */
|
|
|
|
|
#define val_lld
|
|
|
|
|
#endif /* LLONG_ */
|
|
|
|
|
|
|
|
|
|
#if ULLONG_MAX == UINT_MAX
|
|
|
|
|
#define val_llu val_u
|
|
|
|
|
#define format_llu format_u
|
|
|
|
|
#define branch_fmt_llu branch_fmt_u
|
|
|
|
|
#elif ULLONG_MAX == ULONG_MAX
|
|
|
|
|
#define val_llu val_lu
|
|
|
|
|
#define format_llu format_lu
|
|
|
|
|
#define branch_fmt_llu branch_fmt_lu
|
|
|
|
|
#elif !(FMT_LENMOD & FMT_LENMOD_ll) /* unique */
|
|
|
|
|
#define val_llu
|
|
|
|
|
#endif /* ULLONG_ */
|
|
|
|
|
|
|
|
|
|
/* char/short parameter type promotions */
|
|
|
|
|
#define SCHAR_INT_ARG int
|
|
|
|
|
#define UCHAR_INT_ARG int
|
|
|
|
|
#define SSHRT_INT_ARG int
|
|
|
|
|
#if USHRT_MAX == UINT_MAX
|
|
|
|
|
#define USHRT_INT_ARG unsigned int
|
|
|
|
|
#else
|
|
|
|
|
#define USHRT_INT_ARG int
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/* some macros to have conditional work inside macros */
|
|
|
|
|
#if (FMT_LENMOD & FMT_LENMOD_l)
|
|
|
|
|
#define IF_FMT_LENMOD_l(...) __VA_ARGS__
|
|
|
|
|
#else
|
|
|
|
|
#define IF_FMT_LENMOD_l(...)
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#if (FMT_LENMOD & FMT_LENMOD_ll)
|
|
|
|
|
#define IF_FMT_LENMOD_ll(...) __VA_ARGS__
|
|
|
|
|
#else
|
|
|
|
|
#define IF_FMT_LENMOD_ll(...)
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#if (FMT_RADIX & FMT_RADIX_o)
|
|
|
|
|
#define IF_FMT_RADIX_o(...) __VA_ARGS__
|
|
|
|
|
#else
|
|
|
|
|
#define IF_FMT_RADIX_o(...)
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#if (FMT_RADIX & FMT_RADIX_x)
|
|
|
|
|
#define IF_FMT_RADIX_x(...) __VA_ARGS__
|
|
|
|
|
#else
|
|
|
|
|
#define IF_FMT_RADIX_x(...)
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/* synthesize multicharacter constant */
|
|
|
|
|
#define LENMOD2(cv, ch) \
|
|
|
|
|
(((cv) << CHAR_BIT) | (ch))
|
|
|
|
|
|
|
|
|
|
#define LENMOD_NONE 0
|
|
|
|
|
|
|
|
|
|
#if (FMT_LENMOD & FMT_LENMOD_h)
|
|
|
|
|
#define LENMOD_h 'h'
|
|
|
|
|
#endif
|
|
|
|
|
#if (FMT_LENMOD & FMT_LENMOD_hh)
|
|
|
|
|
#define LENMOD_hh LENMOD2('h', 'h') /* 'hh' */
|
|
|
|
|
#endif
|
|
|
|
|
#if (FMT_LENMOD & FMT_LENMOD_j)
|
|
|
|
|
#define LENMOD_j 'j'
|
|
|
|
|
#endif
|
|
|
|
|
#if (FMT_LENMOD & FMT_LENMOD_l)
|
|
|
|
|
#define LENMOD_l 'l'
|
|
|
|
|
#endif
|
|
|
|
|
#if (FMT_LENMOD & FMT_LENMOD_ll)
|
|
|
|
|
#undef FMT_MAX_L
|
|
|
|
|
#define LENMOD_ll LENMOD2('l', 'l') /* 'll' */
|
|
|
|
|
#endif
|
|
|
|
|
#if (FMT_LENMOD & FMT_LENMOD_t)
|
|
|
|
|
#define LENMOD_t 't'
|
|
|
|
|
#endif
|
|
|
|
|
#if (FMT_LENMOD & FMT_LENMOD_z)
|
|
|
|
|
#define LENMOD_z 'z'
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/* select type-compatible length modifier
|
|
|
|
|
* (a bit hacky; it should be range-based) */
|
|
|
|
|
#define LENMOD_INTCOMPAT_SEL(type, signd) \
|
|
|
|
|
({ int __lenmod; \
|
|
|
|
|
size_t __size = sizeof (type); \
|
|
|
|
|
if (__size == ((signd) ? sizeof (int) : \
|
|
|
|
|
sizeof (unsigned int))) { \
|
|
|
|
|
__lenmod = LENMOD_NONE; \
|
|
|
|
|
} \
|
|
|
|
|
else if (__size == ((signd) ? sizeof (long) : \
|
|
|
|
|
sizeof (unsigned long))) { \
|
|
|
|
|
IF_FMT_LENMOD_l(__lenmod = LENMOD_l;) \
|
|
|
|
|
} \
|
|
|
|
|
else if (__size == ((signd) ? sizeof (long long) : \
|
|
|
|
|
sizeof (unsigned long long))) { \
|
|
|
|
|
IF_FMT_LENMOD_ll(__lenmod = LENMOD_ll;) \
|
|
|
|
|
} \
|
|
|
|
|
__lenmod; })
|
|
|
|
|
|
|
|
|
|
/* call formatting function for the compatible integer type */
|
|
|
|
|
#define LENMOD_INTCOMPAT_CALL(inteqv, val, fmt_buf, x, signd) \
|
|
|
|
|
({ const char *__buf; \
|
|
|
|
|
switch (inteqv) { \
|
|
|
|
|
case LENMOD_NONE: \
|
|
|
|
|
__buf = (signd) ? \
|
|
|
|
|
format_d((val), (fmt_buf), (x)) : \
|
|
|
|
|
format_u((val), (fmt_buf), (x)); \
|
|
|
|
|
break; \
|
|
|
|
|
IF_FMT_LENMOD_l( \
|
|
|
|
|
case LENMOD_l: \
|
|
|
|
|
__buf = (signd) ? \
|
|
|
|
|
format_ld((val), (fmt_buf), (x)) : \
|
|
|
|
|
format_lu((val), (fmt_buf), (x)); \
|
|
|
|
|
break; \
|
|
|
|
|
) \
|
|
|
|
|
IF_FMT_LENMOD_ll( \
|
|
|
|
|
case LENMOD_ll: \
|
|
|
|
|
__buf = (signd) ? \
|
|
|
|
|
format_lld((val), (fmt_buf), (x)) : \
|
|
|
|
|
format_llu((val), (fmt_buf), (x)); \
|
|
|
|
|
break; \
|
|
|
|
|
) \
|
|
|
|
|
} \
|
|
|
|
|
__buf; \
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
/* execute formatting branch for the compatible integer type */
|
|
|
|
|
#define LENMOD_INTCOMPAT_BRANCH(inteqv, val, signd) \
|
|
|
|
|
({ switch (inteqv) { \
|
|
|
|
|
case LENMOD_NONE: \
|
|
|
|
|
if (signd) { \
|
|
|
|
|
val_d = (val); \
|
|
|
|
|
goto branch_fmt_d; \
|
|
|
|
|
} \
|
|
|
|
|
else { \
|
|
|
|
|
val_u = (val); \
|
|
|
|
|
goto branch_fmt_u; \
|
|
|
|
|
} \
|
|
|
|
|
IF_FMT_LENMOD_l( \
|
|
|
|
|
case LENMOD_l: \
|
|
|
|
|
if (signd) { \
|
|
|
|
|
val_ld = (val); \
|
|
|
|
|
goto branch_fmt_ld; \
|
|
|
|
|
} \
|
|
|
|
|
else { \
|
|
|
|
|
val_lu = (val); \
|
|
|
|
|
goto branch_fmt_lu; \
|
|
|
|
|
} \
|
|
|
|
|
) \
|
|
|
|
|
IF_FMT_LENMOD_ll( \
|
|
|
|
|
case LENMOD_ll: \
|
|
|
|
|
if (signd) { \
|
|
|
|
|
val_lld = (val); \
|
|
|
|
|
goto branch_fmt_lld; \
|
|
|
|
|
} \
|
|
|
|
|
else { \
|
|
|
|
|
val_llu = (val); \
|
|
|
|
|
goto branch_fmt_llu; \
|
|
|
|
|
} \
|
|
|
|
|
) \
|
|
|
|
|
} \
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
#define CONVERT_RADIX_10_SIGN(val, fmt_buf, p, signchar, type) \
|
|
|
|
|
do { \
|
|
|
|
|
if (val) { \
|
|
|
|
|
unsigned type v; \
|
|
|
|
|
\
|
|
|
|
|
if (val < 0) { \
|
|
|
|
|
v = (typeof (v))-(val + 1) + 1; \
|
|
|
|
|
signchar = '-'; \
|
|
|
|
|
} \
|
|
|
|
|
else { \
|
|
|
|
|
v = val; \
|
|
|
|
|
} \
|
|
|
|
|
\
|
|
|
|
|
do { \
|
|
|
|
|
*--p = (v % 10) + '0'; \
|
|
|
|
|
v /= 10; \
|
|
|
|
|
} while (v); \
|
|
|
|
|
} \
|
|
|
|
|
\
|
|
|
|
|
if (signchar) { \
|
|
|
|
|
p[-1] = signchar; \
|
|
|
|
|
fmt_buf->length = 1; \
|
|
|
|
|
break; \
|
|
|
|
|
} \
|
|
|
|
|
\
|
|
|
|
|
fmt_buf->length = 0; \
|
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
|
|
#define CONVERT_RADIX_8(val, fmt_buf, p) \
|
|
|
|
|
do { \
|
|
|
|
|
if (val) { \
|
|
|
|
|
typeof (val) v = val; \
|
|
|
|
|
\
|
|
|
|
|
do { \
|
|
|
|
|
*--p = (v % 010) + '0'; \
|
|
|
|
|
v /= 010; \
|
|
|
|
|
} while (v); \
|
|
|
|
|
} \
|
|
|
|
|
\
|
|
|
|
|
if (fmt_buf->length) { \
|
|
|
|
|
*--p = '0'; \
|
|
|
|
|
fmt_buf->length = 0; \
|
|
|
|
|
} \
|
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
|
|
#define CONVERT_RADIX_10(val, fmt_buf, p) \
|
|
|
|
|
do { \
|
|
|
|
|
if (val) { \
|
|
|
|
|
typeof (val) v = val; \
|
|
|
|
|
\
|
|
|
|
|
do { \
|
|
|
|
|
*--p = (v % 10) + '0'; \
|
|
|
|
|
v /= 10; \
|
|
|
|
|
} while (v); \
|
|
|
|
|
} \
|
|
|
|
|
\
|
|
|
|
|
fmt_buf->length = 0; \
|
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
|
|
#define CONVERT_RADIX_16(val, fmt_buf, p, x) \
|
|
|
|
|
do { \
|
|
|
|
|
if (val) { \
|
|
|
|
|
const int h = x - 'X' - 0xA \
|
|
|
|
|
+ 'A' - '0'; \
|
|
|
|
|
typeof (val) v = val; \
|
|
|
|
|
\
|
|
|
|
|
do { \
|
|
|
|
|
unsigned int d = v % 0x10; \
|
|
|
|
|
if (d >= 0xA) { \
|
|
|
|
|
d += h; \
|
|
|
|
|
} \
|
|
|
|
|
*--p = d + '0'; \
|
|
|
|
|
v /= 0x10; \
|
|
|
|
|
} while (v); \
|
|
|
|
|
\
|
|
|
|
|
if (fmt_buf->length) { \
|
|
|
|
|
p[-1] = x; \
|
|
|
|
|
p[-2] = '0'; \
|
|
|
|
|
fmt_buf->length = 2; \
|
|
|
|
|
break; \
|
|
|
|
|
} \
|
|
|
|
|
} \
|
|
|
|
|
\
|
|
|
|
|
fmt_buf->length = 0; \
|
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
|
|
#define CONVERT_RADIX_NOSIGN(val, fmt_buf, p, x) \
|
|
|
|
|
switch (x) \
|
|
|
|
|
{ \
|
|
|
|
|
IF_FMT_RADIX_o( case 'o': \
|
|
|
|
|
CONVERT_RADIX_8(val, fmt_buf, p); \
|
|
|
|
|
break; ) \
|
|
|
|
|
case 'u': \
|
|
|
|
|
CONVERT_RADIX_10(val, fmt_buf, p); \
|
|
|
|
|
break; \
|
|
|
|
|
IF_FMT_RADIX_x( default: \
|
|
|
|
|
CONVERT_RADIX_16(val, fmt_buf, p, x); \
|
|
|
|
|
break; ) \
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct fmt_buf {
|
|
|
|
|
const char *fmt_start; /* second character of formatter after '%' */
|
|
|
|
|
size_t length; /* length of formatted text (non-numeric)
|
|
|
|
|
or prefix (numeric) */
|
|
|
|
|
char buf[24]; /* work buffer */
|
|
|
|
|
char bufend[1]; /* buffer end marker and guard '0' */
|
2018-01-06 12:17:04 +00:00
|
|
|
|
#if (FMT_RADIX & FMT_RADIX_floats)
|
|
|
|
|
int lenmod;
|
|
|
|
|
int radixchar;
|
|
|
|
|
int signchar;
|
|
|
|
|
int alignchar;
|
|
|
|
|
int width;
|
|
|
|
|
int precision;
|
|
|
|
|
char *p;
|
|
|
|
|
#endif
|
Implement a much more capable vuprintf()
New support as well as some buggy support fixed.
Still no floating point support if ever that would be desired.
Support (*):
* Flags: '-', '+', ' ', '#', '0'
* Width and precision: 'n', '.n', '*' and '.*'
* Length modifiers: 'hh', 'h', 'j', 'l', 'll', 't', 'z'
* Radix: 'c', 'd', 'i', 'n', 'o', 'p/P', 's', 'u', 'x/X'
(*) Provision exists to switch lesser-used stuff on or off or when
certain functionality isn't desired (bootloader?). The compulsory
radixes are everything but 'o', 'n', 'p/P' and 'x/X' with length
modifiers being optional. The default setup is 'l', 'z', 'c', 'd',
'p/P', 's', 'u', 'x/X'.
* Move fdprintf() to its own file. It was in a strange place.
* Make callers compatible and fix a couple snprintf() bugs while
at it.
Could smush it down in size but I'm gonna get over the binsize
neurosis and just the let optimizer do its thing.
Change-Id: Ibdc613a9b6775802c188b29b9dd46c568c94f7c3
2017-09-08 23:28:02 +00:00
|
|
|
|
};
|
|
|
|
|
|
2018-01-06 12:17:04 +00:00
|
|
|
|
#define PUSHCHAR(ch) \
|
|
|
|
|
({ int __rc = push(userp, (ch)); \
|
|
|
|
|
count += __rc >= 0; \
|
|
|
|
|
if (__rc <= 0) { \
|
|
|
|
|
goto done; \
|
|
|
|
|
} })
|
|
|
|
|
|
Implement a much more capable vuprintf()
New support as well as some buggy support fixed.
Still no floating point support if ever that would be desired.
Support (*):
* Flags: '-', '+', ' ', '#', '0'
* Width and precision: 'n', '.n', '*' and '.*'
* Length modifiers: 'hh', 'h', 'j', 'l', 'll', 't', 'z'
* Radix: 'c', 'd', 'i', 'n', 'o', 'p/P', 's', 'u', 'x/X'
(*) Provision exists to switch lesser-used stuff on or off or when
certain functionality isn't desired (bootloader?). The compulsory
radixes are everything but 'o', 'n', 'p/P' and 'x/X' with length
modifiers being optional. The default setup is 'l', 'z', 'c', 'd',
'p/P', 's', 'u', 'x/X'.
* Move fdprintf() to its own file. It was in a strange place.
* Make callers compatible and fix a couple snprintf() bugs while
at it.
Could smush it down in size but I'm gonna get over the binsize
neurosis and just the let optimizer do its thing.
Change-Id: Ibdc613a9b6775802c188b29b9dd46c568c94f7c3
2017-09-08 23:28:02 +00:00
|
|
|
|
/* %d %i */
|
|
|
|
|
static inline const char * format_d(int val,
|
|
|
|
|
struct fmt_buf *fmt_buf,
|
|
|
|
|
int signchar)
|
|
|
|
|
{
|
|
|
|
|
char *p = fmt_buf->bufend;
|
|
|
|
|
CONVERT_RADIX_10_SIGN(val, fmt_buf, p, signchar, int);
|
|
|
|
|
return p;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* %o %u %x %X */
|
|
|
|
|
static inline const char * format_u(unsigned int val,
|
|
|
|
|
struct fmt_buf *fmt_buf,
|
|
|
|
|
int radixchar)
|
|
|
|
|
{
|
|
|
|
|
char *p = fmt_buf->bufend;
|
|
|
|
|
CONVERT_RADIX_NOSIGN(val, fmt_buf, p, radixchar);
|
|
|
|
|
return p;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if (FMT_LENMOD & FMT_LENMOD_l)
|
|
|
|
|
#ifndef format_ld
|
|
|
|
|
/* %ld %li */
|
|
|
|
|
static inline const char * format_ld(long val,
|
|
|
|
|
struct fmt_buf *fmt_buf,
|
|
|
|
|
int signchar)
|
|
|
|
|
{
|
|
|
|
|
char *p = fmt_buf->bufend;
|
|
|
|
|
CONVERT_RADIX_10_SIGN(val, fmt_buf, p, signchar, long);
|
|
|
|
|
return p;
|
|
|
|
|
}
|
|
|
|
|
#endif /* format_ld */
|
|
|
|
|
|
|
|
|
|
#ifndef format_lu
|
|
|
|
|
/* %lo %lu %lx %lX */
|
|
|
|
|
static inline const char * format_lu(unsigned long val,
|
|
|
|
|
struct fmt_buf *fmt_buf,
|
|
|
|
|
int radixchar)
|
|
|
|
|
{
|
|
|
|
|
char *p = fmt_buf->bufend;
|
|
|
|
|
CONVERT_RADIX_NOSIGN(val, fmt_buf, p, radixchar);
|
|
|
|
|
return p;
|
|
|
|
|
}
|
|
|
|
|
#endif /* format_lu */
|
|
|
|
|
#endif /* FMT_LENMOD_l */
|
|
|
|
|
|
|
|
|
|
#if (FMT_LENMOD & FMT_LENMOD_ll)
|
|
|
|
|
#ifndef format_lld
|
|
|
|
|
/* %lld %lli */
|
|
|
|
|
static inline const char * format_lld(long long val,
|
|
|
|
|
struct fmt_buf *fmt_buf,
|
|
|
|
|
int signchar)
|
|
|
|
|
{
|
|
|
|
|
char *p = fmt_buf->bufend;
|
|
|
|
|
CONVERT_RADIX_10_SIGN(val, fmt_buf, p, signchar, long long);
|
|
|
|
|
return p;
|
|
|
|
|
}
|
|
|
|
|
#endif /* format_lld */
|
|
|
|
|
|
|
|
|
|
#ifndef format_llu
|
|
|
|
|
/* %llo %llu %llx %llX */
|
|
|
|
|
static inline const char * format_llu(unsigned long long val,
|
|
|
|
|
struct fmt_buf *fmt_buf,
|
|
|
|
|
int radixchar)
|
|
|
|
|
{
|
|
|
|
|
char *p = fmt_buf->bufend;
|
|
|
|
|
CONVERT_RADIX_NOSIGN(val, fmt_buf, p, radixchar);
|
|
|
|
|
return p;
|
|
|
|
|
}
|
|
|
|
|
#endif /* format_llu */
|
|
|
|
|
#endif /* FMT_LENMOD_ll */
|
|
|
|
|
|
|
|
|
|
/* %c */
|
|
|
|
|
static inline const char * format_c(int c,
|
|
|
|
|
struct fmt_buf *fmt_buf,
|
|
|
|
|
int lenmod)
|
|
|
|
|
{
|
|
|
|
|
if (lenmod != LENMOD_NONE) {
|
|
|
|
|
return NULL; /* wchar_t support for now */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char *p = fmt_buf->bufend;
|
|
|
|
|
fmt_buf->length = 1;
|
|
|
|
|
*--p = (unsigned char)c;
|
|
|
|
|
return p;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* %s */
|
|
|
|
|
static inline const char * format_s(const void *str,
|
|
|
|
|
struct fmt_buf *fmt_buf,
|
|
|
|
|
int precision,
|
|
|
|
|
int lenmod)
|
|
|
|
|
{
|
|
|
|
|
if (lenmod != LENMOD_NONE) {
|
|
|
|
|
return NULL; /* wchar_t support for now */
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-06 17:33:38 +00:00
|
|
|
|
const char *s = str;
|
|
|
|
|
size_t len;
|
Implement a much more capable vuprintf()
New support as well as some buggy support fixed.
Still no floating point support if ever that would be desired.
Support (*):
* Flags: '-', '+', ' ', '#', '0'
* Width and precision: 'n', '.n', '*' and '.*'
* Length modifiers: 'hh', 'h', 'j', 'l', 'll', 't', 'z'
* Radix: 'c', 'd', 'i', 'n', 'o', 'p/P', 's', 'u', 'x/X'
(*) Provision exists to switch lesser-used stuff on or off or when
certain functionality isn't desired (bootloader?). The compulsory
radixes are everything but 'o', 'n', 'p/P' and 'x/X' with length
modifiers being optional. The default setup is 'l', 'z', 'c', 'd',
'p/P', 's', 'u', 'x/X'.
* Move fdprintf() to its own file. It was in a strange place.
* Make callers compatible and fix a couple snprintf() bugs while
at it.
Could smush it down in size but I'm gonna get over the binsize
neurosis and just the let optimizer do its thing.
Change-Id: Ibdc613a9b6775802c188b29b9dd46c568c94f7c3
2017-09-08 23:28:02 +00:00
|
|
|
|
/* string length may be specified by precision instead of \0-
|
|
|
|
|
terminated; however, don't go past a \0 if one is there */
|
2018-11-06 17:33:38 +00:00
|
|
|
|
if (precision >= 0) {
|
|
|
|
|
const char *nil = memchr(s, '\0', (size_t) precision);
|
|
|
|
|
|
|
|
|
|
if (nil != NULL && (nil - s) < precision)
|
|
|
|
|
len = nil - s;
|
|
|
|
|
else
|
|
|
|
|
len = precision;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
len = strlen(s);
|
Implement a much more capable vuprintf()
New support as well as some buggy support fixed.
Still no floating point support if ever that would be desired.
Support (*):
* Flags: '-', '+', ' ', '#', '0'
* Width and precision: 'n', '.n', '*' and '.*'
* Length modifiers: 'hh', 'h', 'j', 'l', 'll', 't', 'z'
* Radix: 'c', 'd', 'i', 'n', 'o', 'p/P', 's', 'u', 'x/X'
(*) Provision exists to switch lesser-used stuff on or off or when
certain functionality isn't desired (bootloader?). The compulsory
radixes are everything but 'o', 'n', 'p/P' and 'x/X' with length
modifiers being optional. The default setup is 'l', 'z', 'c', 'd',
'p/P', 's', 'u', 'x/X'.
* Move fdprintf() to its own file. It was in a strange place.
* Make callers compatible and fix a couple snprintf() bugs while
at it.
Could smush it down in size but I'm gonna get over the binsize
neurosis and just the let optimizer do its thing.
Change-Id: Ibdc613a9b6775802c188b29b9dd46c568c94f7c3
2017-09-08 23:28:02 +00:00
|
|
|
|
|
|
|
|
|
fmt_buf->length = len;
|
|
|
|
|
return s;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if (FMT_RADIX & FMT_RADIX_n)
|
|
|
|
|
/* %n */
|
|
|
|
|
static inline bool format_n(void *np,
|
|
|
|
|
int count,
|
|
|
|
|
int lenmod)
|
|
|
|
|
{
|
|
|
|
|
if (lenmod != LENMOD_NONE) {
|
|
|
|
|
return false; /* int only for now */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*(int *)np = count;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
#endif /* FMT_RADIX_n */
|
|
|
|
|
|
|
|
|
|
#if (FMT_RADIX & FMT_RADIX_p)
|
|
|
|
|
/* %p %P */
|
|
|
|
|
static inline const char * format_p(const void *p,
|
|
|
|
|
struct fmt_buf *fmt_buf,
|
|
|
|
|
int radixchar,
|
|
|
|
|
bool *numericp)
|
|
|
|
|
{
|
|
|
|
|
if (p) {
|
|
|
|
|
/* format as %#x or %#X */
|
|
|
|
|
*numericp = true;
|
|
|
|
|
radixchar -= 'P' - 'X';
|
|
|
|
|
fmt_buf->length = 2;
|
|
|
|
|
return LENMOD_INTCOMPAT_CALL(LENMOD_INTCOMPAT_SEL(uintptr_t, false),
|
|
|
|
|
(uintptr_t)p, fmt_buf, radixchar, false);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
/* format as %s */
|
|
|
|
|
fmt_buf->length = 5;
|
|
|
|
|
return "(nil)";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif /* FMT_RADIX_p */
|
|
|
|
|
|
2018-01-06 12:17:04 +00:00
|
|
|
|
#if (FMT_RADIX & FMT_RADIX_floats)
|
|
|
|
|
/* find out how many uint32_t chunks need to be allocated, if any
|
|
|
|
|
* if none are needed, finish the init for the number here */
|
|
|
|
|
static long parse_ieee754_double(double f,
|
|
|
|
|
struct ap_int *ia,
|
|
|
|
|
struct ap_int *fa,
|
|
|
|
|
struct fmt_buf *fmt_buf)
|
|
|
|
|
{
|
|
|
|
|
long rc = 0;
|
|
|
|
|
|
|
|
|
|
union {
|
|
|
|
|
double f;
|
|
|
|
|
uint64_t f64;
|
|
|
|
|
} u = { .f = f };
|
|
|
|
|
|
|
|
|
|
int e = ((int)(u.f64 >> 52) & 0x7ff) - 1023; /* -1023..1024 */
|
|
|
|
|
uint64_t mantissa = u.f64 & 0x000fffffffffffffull;
|
|
|
|
|
|
|
|
|
|
if (u.f64 >> 63) {
|
|
|
|
|
fmt_buf->signchar = '-';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (LIKELY(e >= -8 && e <= 63)) { /* -8 to +63 */
|
|
|
|
|
/* integer, fraction and manipulations fit in uint64_t */
|
|
|
|
|
mantissa |= 0x0010000000000000ull;
|
|
|
|
|
ia->numchunks = 0;
|
|
|
|
|
ia->shift = 0;
|
|
|
|
|
fa->numchunks = 0;
|
|
|
|
|
|
|
|
|
|
if (e < 0) { /* -8 to -1 - fraction */
|
|
|
|
|
long fracbits = 52 - e;
|
|
|
|
|
/* int - none */
|
|
|
|
|
ia->len = 0;
|
|
|
|
|
ia->val = 0;
|
|
|
|
|
/* frac */
|
|
|
|
|
fa->len = fracbits - __builtin_ctzll(mantissa);
|
|
|
|
|
fa->shift = fracbits;
|
|
|
|
|
fa->val = mantissa;
|
|
|
|
|
}
|
|
|
|
|
else if (e <= 51) { /* 0 to +51 - integer|fraction */
|
|
|
|
|
long fracbits = 52 - e;
|
|
|
|
|
/* int */
|
|
|
|
|
ia->len = base10exp(e) + 2; /* go up + possibly 1 longer */
|
|
|
|
|
ia->val = mantissa >> fracbits;
|
|
|
|
|
/* frac */
|
|
|
|
|
fa->shift = fracbits;
|
|
|
|
|
fa->val = mantissa ^ (ia->val << fracbits);
|
|
|
|
|
fa->len = fa->val ? fracbits - __builtin_ctzll(mantissa) : 0;
|
|
|
|
|
}
|
|
|
|
|
else { /* +52 to +63 - integer */
|
|
|
|
|
/* int */
|
|
|
|
|
ia->len = base10exp(e) + 2;
|
|
|
|
|
ia->val = mantissa << (e - 52);
|
|
|
|
|
/* frac - none */
|
|
|
|
|
fa->len = 0;
|
|
|
|
|
fa->shift = 0;
|
|
|
|
|
fa->val = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (e < 0) { /* -1023 to -9 - fraction */
|
|
|
|
|
/* int - none */
|
|
|
|
|
ia->numchunks = 0;
|
|
|
|
|
ia->len = 0;
|
|
|
|
|
ia->shift = 0;
|
|
|
|
|
ia->val = 0;
|
|
|
|
|
/* frac - left-justify on bit 31 of the chunk of the MSb */
|
|
|
|
|
if (e >= -1022) { /* normal */
|
|
|
|
|
mantissa |= 0x0010000000000000ull;
|
|
|
|
|
}
|
|
|
|
|
else { /* subnormal (including zero) */
|
|
|
|
|
e = -1022;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (mantissa) {
|
|
|
|
|
long fracbits = 52 - e;
|
|
|
|
|
fa->len = fracbits - __builtin_ctzll(mantissa);
|
|
|
|
|
fa->shift = 31 - ((51 - e) % 32);
|
|
|
|
|
fa->val = mantissa;
|
|
|
|
|
fa->basechunk = (fa->shift + 52) / 32;
|
|
|
|
|
fa->numchunks = (51 - e + fa->shift) / 32 + 1;
|
|
|
|
|
rc = fa->numchunks;
|
|
|
|
|
}
|
|
|
|
|
else { /* zero */
|
|
|
|
|
fa->numchunks = 0;
|
|
|
|
|
fa->len = 0;
|
|
|
|
|
fa->shift = 0;
|
|
|
|
|
fa->val = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (e <= 1023) { /* +64 to +1023 - integer */
|
|
|
|
|
/* int - right-justify on bit 0 of the first chunk */
|
|
|
|
|
ia->val = mantissa | 0x0010000000000000ull;
|
|
|
|
|
ia->len = base10exp(e) + 2;
|
|
|
|
|
ia->shift = (e - 52) % 32;
|
|
|
|
|
ia->basechunk = e / 32;
|
|
|
|
|
ia->numchunks = ia->basechunk + 1;
|
|
|
|
|
rc = ia->numchunks;
|
|
|
|
|
/* frac - none */
|
|
|
|
|
fa->numchunks = 0;
|
|
|
|
|
fa->len = 0;
|
|
|
|
|
fa->shift = 0;
|
|
|
|
|
fa->val = 0;
|
|
|
|
|
}
|
|
|
|
|
else { /* +1024: INF, NAN */
|
|
|
|
|
rc = -1 - !!mantissa;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* construct the arbitrary-precision value in the provided allocation */
|
|
|
|
|
static void init_ieee754_double_chunks(struct ap_int *a,
|
|
|
|
|
uint32_t *a_chunks)
|
|
|
|
|
{
|
|
|
|
|
long basechunk = a->basechunk;
|
|
|
|
|
long shift = a->shift;
|
|
|
|
|
uint64_t val = a->val;
|
|
|
|
|
|
|
|
|
|
a->chunks = a_chunks;
|
|
|
|
|
|
|
|
|
|
memset(a_chunks, 0, a->numchunks*sizeof (uint32_t));
|
|
|
|
|
|
|
|
|
|
if (shift < 12) {
|
|
|
|
|
a_chunks[basechunk - 1] = val << shift;
|
|
|
|
|
a_chunks[basechunk - 0] = val >> (32 - shift);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
a_chunks[basechunk - 2] = val << shift;
|
|
|
|
|
a_chunks[basechunk - 1] = val >> (32 - shift);
|
|
|
|
|
a_chunks[basechunk - 0] = val >> (64 - shift);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* format inf, nan strings */
|
|
|
|
|
static void format_inf_nan(struct fmt_buf *fmt_buf, long type)
|
|
|
|
|
{
|
|
|
|
|
/* certain special values */
|
|
|
|
|
static const char text[2][2][3] =
|
|
|
|
|
{
|
|
|
|
|
{ { 'I', 'N', 'F' }, { 'i', 'n', 'f' } },
|
|
|
|
|
{ { 'N', 'A', 'N' }, { 'n', 'a', 'n' } },
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
char *p = fmt_buf->buf;
|
|
|
|
|
fmt_buf->p = p;
|
|
|
|
|
fmt_buf->length = 3;
|
|
|
|
|
|
|
|
|
|
/* they also have a sign */
|
|
|
|
|
if (fmt_buf->signchar) {
|
|
|
|
|
*p++ = fmt_buf->signchar;
|
|
|
|
|
fmt_buf->length++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
memcpy(p, &text[type][(fmt_buf->radixchar >> 5) & 0x1], 3);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* %e %E %f %F %g %G */
|
|
|
|
|
static int format_double_radix(double f,
|
|
|
|
|
struct fmt_buf *fmt_buf,
|
|
|
|
|
vuprintf_push_cb push,
|
|
|
|
|
void *userp)
|
|
|
|
|
{
|
|
|
|
|
struct ap_int ia, fa;
|
|
|
|
|
long rc = parse_double(f, &ia, &fa, fmt_buf);
|
|
|
|
|
|
|
|
|
|
if (UNLIKELY(rc < 0)) {
|
|
|
|
|
format_inf_nan(fmt_buf, -rc - 1);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int count = 0;
|
|
|
|
|
|
|
|
|
|
/* default precision is 6 for all formats */
|
|
|
|
|
int prec_rem = fmt_buf->precision < 0 ? 6 : fmt_buf->precision;
|
|
|
|
|
|
|
|
|
|
int exp = exp;
|
|
|
|
|
int explen = 0;
|
|
|
|
|
|
|
|
|
|
switch (fmt_buf->radixchar & 0x3)
|
|
|
|
|
{
|
|
|
|
|
case 3: /* %g, %G */
|
|
|
|
|
fmt_buf->precision = prec_rem;
|
|
|
|
|
if (prec_rem) {
|
|
|
|
|
prec_rem--;
|
|
|
|
|
}
|
|
|
|
|
case 1: /* %e, %E */
|
|
|
|
|
explen = 2;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (rc > 0 && ia.numchunks > 0) {
|
|
|
|
|
/* large integer required */
|
|
|
|
|
init_double_chunks(&ia, alloca(rc*sizeof(*ia.chunks)));
|
|
|
|
|
rc = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const int bufoffs = 6; /* log rollover + round rollover + leading zeros (%g) */
|
|
|
|
|
long f_prec = MIN(fa.len, prec_rem + 1);
|
|
|
|
|
char buf[bufoffs + ia.len + f_prec + 1];
|
|
|
|
|
char *p_last = &buf[bufoffs + ia.len];
|
|
|
|
|
char *p_dec = p_last;
|
|
|
|
|
char *p_first = format_double_int10(&ia, p_last);
|
|
|
|
|
|
|
|
|
|
if (explen) {
|
|
|
|
|
if (!ia.val && fa.len) {
|
|
|
|
|
p_first = p_last = &buf[bufoffs];
|
|
|
|
|
f_prec = -f_prec - 1; /* no lead zeros */
|
|
|
|
|
}
|
|
|
|
|
else { /* handles 0e+0 too */
|
|
|
|
|
exp = ia.len - 1;
|
|
|
|
|
|
|
|
|
|
if (exp) {
|
|
|
|
|
prec_rem -= exp;
|
|
|
|
|
|
|
|
|
|
if (prec_rem < 0) {
|
|
|
|
|
p_last += prec_rem + 1;
|
|
|
|
|
f_prec = 0;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
f_prec = MIN(fa.len, prec_rem + 1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
p_dec = p_first + 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (f_prec) {
|
|
|
|
|
if (rc > 0) {
|
|
|
|
|
/* large integer required */
|
|
|
|
|
init_double_chunks(&fa, alloca(rc*sizeof(*fa.chunks)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
p_last = format_double_frac10(&fa, p_last, f_prec);
|
|
|
|
|
|
|
|
|
|
if (f_prec < 0) {
|
|
|
|
|
f_prec = -f_prec - 1;
|
|
|
|
|
exp = f_prec - fa.len;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
prec_rem -= f_prec;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (prec_rem < 0) {
|
|
|
|
|
prec_rem = 0;
|
|
|
|
|
p_last--;
|
|
|
|
|
|
|
|
|
|
if (round_number_string10(p_last, p_last - p_first)) {
|
|
|
|
|
/* carried left */
|
|
|
|
|
p_first--;
|
|
|
|
|
|
|
|
|
|
if (explen) {
|
|
|
|
|
/* slide everything left by 1 */
|
|
|
|
|
exp++;
|
|
|
|
|
p_dec--;
|
|
|
|
|
p_last--;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (explen) {
|
|
|
|
|
if ((fmt_buf->radixchar & 0x3) == 0x3) { /* g, G */
|
|
|
|
|
/* 'g' is some weird crap */
|
|
|
|
|
/* now that the final exponent is known and everything rounded,
|
|
|
|
|
it is possible to decide whether to format similarly to
|
|
|
|
|
'e' or 'f' */
|
|
|
|
|
if (fmt_buf->precision > exp && exp >= -4) { /* P > X >= -4 */
|
|
|
|
|
if (exp >= 0) {
|
|
|
|
|
/* integer digits will be in the buffer */
|
|
|
|
|
p_dec = p_first + exp + 1;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
/* we didn't keep leading zeros and need to regenerate
|
|
|
|
|
them; space was reserved just in case */
|
|
|
|
|
p_first = memset(p_dec + exp - 1, '0', -exp);
|
|
|
|
|
p_dec = p_first + 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* suppress exponent */
|
|
|
|
|
explen = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!fmt_buf->length) {
|
|
|
|
|
/* strip any trailing zeros from the fraction */
|
|
|
|
|
while (p_last > p_dec && p_last[-1] == '0') {
|
|
|
|
|
p_last--;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* suppress trailing precision fill */
|
|
|
|
|
prec_rem = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (explen) {
|
|
|
|
|
/* build exponent string: 'e<>dd' */
|
|
|
|
|
char *p = fmt_buf->bufend;
|
|
|
|
|
int signchar = '+';
|
|
|
|
|
|
|
|
|
|
if (exp < 0) {
|
|
|
|
|
signchar = '-';
|
|
|
|
|
exp = -exp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while (exp || explen < 4) {
|
|
|
|
|
*--p = exp % 10 + '0';
|
|
|
|
|
exp /= 10;
|
|
|
|
|
explen++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*--p = signchar;
|
|
|
|
|
*--p = fmt_buf->radixchar & ~0x2;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int width = fmt_buf->width;
|
|
|
|
|
int point = p_last > p_dec || prec_rem || fmt_buf->length;
|
|
|
|
|
int length = p_last - p_first + !!fmt_buf->signchar + point + explen;
|
|
|
|
|
|
|
|
|
|
if (width) {
|
|
|
|
|
if (width - length <= prec_rem) {
|
|
|
|
|
width = 0;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
width -= length + prec_rem;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
rc = -1;
|
|
|
|
|
|
|
|
|
|
/* left padding */
|
|
|
|
|
if (fmt_buf->alignchar > '0') {
|
|
|
|
|
/* space-padded width -- before sign */
|
|
|
|
|
while (width > 0) {
|
|
|
|
|
PUSHCHAR(' ');
|
|
|
|
|
width--;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (fmt_buf->signchar) {
|
|
|
|
|
PUSHCHAR(fmt_buf->signchar);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (fmt_buf->alignchar == '0') {
|
|
|
|
|
/* zero-padded width -- after sign */
|
|
|
|
|
while (width > 0) {
|
|
|
|
|
PUSHCHAR('0');
|
|
|
|
|
width--;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* integer part */
|
|
|
|
|
while (p_first < p_dec) {
|
|
|
|
|
PUSHCHAR(*p_first++);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* decimal point */
|
|
|
|
|
if (point) {
|
|
|
|
|
PUSHCHAR('.');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* fractional part */
|
|
|
|
|
while (p_first < p_last) {
|
|
|
|
|
PUSHCHAR(*p_first++);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* precision 0-padding */
|
|
|
|
|
while (prec_rem > 0) {
|
|
|
|
|
PUSHCHAR('0');
|
|
|
|
|
prec_rem--;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* exponent */
|
|
|
|
|
if (explen > 0) {
|
|
|
|
|
char *p = fmt_buf->bufend;
|
|
|
|
|
while (explen > 0) {
|
|
|
|
|
PUSHCHAR(p[-explen--]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* right padding */
|
|
|
|
|
while (width > 0) {
|
|
|
|
|
PUSHCHAR(' ');
|
|
|
|
|
width--;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
rc = 1;
|
|
|
|
|
done:
|
|
|
|
|
fmt_buf->length = count;
|
|
|
|
|
return rc;
|
|
|
|
|
}
|
|
|
|
|
#endif /* FMT_RADIX_floats */
|
|
|
|
|
|
Implement a much more capable vuprintf()
New support as well as some buggy support fixed.
Still no floating point support if ever that would be desired.
Support (*):
* Flags: '-', '+', ' ', '#', '0'
* Width and precision: 'n', '.n', '*' and '.*'
* Length modifiers: 'hh', 'h', 'j', 'l', 'll', 't', 'z'
* Radix: 'c', 'd', 'i', 'n', 'o', 'p/P', 's', 'u', 'x/X'
(*) Provision exists to switch lesser-used stuff on or off or when
certain functionality isn't desired (bootloader?). The compulsory
radixes are everything but 'o', 'n', 'p/P' and 'x/X' with length
modifiers being optional. The default setup is 'l', 'z', 'c', 'd',
'p/P', 's', 'u', 'x/X'.
* Move fdprintf() to its own file. It was in a strange place.
* Make callers compatible and fix a couple snprintf() bugs while
at it.
Could smush it down in size but I'm gonna get over the binsize
neurosis and just the let optimizer do its thing.
Change-Id: Ibdc613a9b6775802c188b29b9dd46c568c94f7c3
2017-09-08 23:28:02 +00:00
|
|
|
|
/* parse fixed width or precision field */
|
|
|
|
|
static const char * parse_number_spec(const char *fmt,
|
|
|
|
|
int ch,
|
|
|
|
|
int *out)
|
|
|
|
|
{
|
|
|
|
|
int i = ch - '0';
|
|
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
|
ch = *fmt - '0';
|
|
|
|
|
|
|
|
|
|
if (ch < 0 || ch > 9 || i > INT_MAX / 10 ||
|
|
|
|
|
(i == INT_MAX / 10 && ch > INT_MAX % 10)) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
i = i * 10 + ch;
|
|
|
|
|
fmt++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*out = i;
|
|
|
|
|
return fmt;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int vuprintf(vuprintf_push_cb push, /* call 'push()' for each output letter */
|
|
|
|
|
void *userp,
|
|
|
|
|
const char *fmt,
|
|
|
|
|
va_list ap)
|
|
|
|
|
{
|
|
|
|
|
int count = 0;
|
|
|
|
|
int ch;
|
|
|
|
|
|
|
|
|
|
/* macrofied identifiers share a variable with another */
|
|
|
|
|
unsigned int val_d;
|
|
|
|
|
unsigned int val_u;
|
|
|
|
|
#ifndef val_ld
|
|
|
|
|
unsigned long val_ld;
|
|
|
|
|
#endif
|
|
|
|
|
#ifndef val_lu
|
|
|
|
|
unsigned long val_lu;
|
|
|
|
|
#endif
|
|
|
|
|
#ifndef val_lld
|
|
|
|
|
unsigned long long val_lld;
|
|
|
|
|
#endif
|
|
|
|
|
#ifndef val_llu
|
|
|
|
|
unsigned long long val_llu;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
struct fmt_buf fmt_buf;
|
|
|
|
|
fmt_buf.bufend[0] = '0';
|
|
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
|
while (1) {
|
|
|
|
|
if ((ch = *fmt++) == '\0') {
|
|
|
|
|
goto done;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ch == '%' && (ch = *fmt++) != '%') {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PUSHCHAR(ch);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* set to defaults */
|
|
|
|
|
fmt_buf.fmt_start = fmt;
|
|
|
|
|
|
|
|
|
|
int signchar = 0;
|
|
|
|
|
unsigned int width = 0;
|
|
|
|
|
int lenmod = LENMOD_NONE;
|
|
|
|
|
size_t length = 0;
|
|
|
|
|
size_t pfxlen = 0;
|
|
|
|
|
bool numeric = false;
|
|
|
|
|
int alignchar = '0' + 1;
|
|
|
|
|
int precision = -1;
|
|
|
|
|
const char *buf = NULL;
|
|
|
|
|
|
|
|
|
|
/*** flags ***/
|
|
|
|
|
while (1) {
|
|
|
|
|
switch (ch)
|
|
|
|
|
{
|
|
|
|
|
case ' ': /* <space> before non-negative value (signed conversion) */
|
|
|
|
|
case '+': /* '+' before non-negative value (signed conversion) */
|
|
|
|
|
/* '+' overrides ' ' */
|
|
|
|
|
if (ch > signchar) {
|
|
|
|
|
signchar = ch;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case '-': /* left-justify in field */
|
|
|
|
|
case '0': /* zero-pad to fill field */
|
|
|
|
|
/* '-' overrides '0' */
|
|
|
|
|
if (ch < alignchar) {
|
|
|
|
|
alignchar = ch;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case '#': /* number prefix (nonzero %o:'0' %x/%X:'0x') */
|
|
|
|
|
/* indicate; formatter updates with actual length */
|
|
|
|
|
pfxlen = 1;
|
|
|
|
|
break;
|
|
|
|
|
#if 0
|
|
|
|
|
case '\'': /* digit grouping (non-monetary) */
|
|
|
|
|
break;
|
|
|
|
|
#endif
|
|
|
|
|
default:
|
|
|
|
|
goto flags_done;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ch = *fmt++;
|
|
|
|
|
}
|
|
|
|
|
flags_done:
|
|
|
|
|
|
|
|
|
|
/*** width ***/
|
|
|
|
|
if (ch == '*') {
|
|
|
|
|
/* variable width */
|
|
|
|
|
int w = va_arg(ap, int);
|
|
|
|
|
if (w < 0) {
|
|
|
|
|
/* negative width is width with implied '-' */
|
|
|
|
|
width = (unsigned int)-(w + 1) + 1;
|
|
|
|
|
alignchar = '-';
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
width = w;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ch = *fmt++;
|
|
|
|
|
}
|
|
|
|
|
else if (ch >= '1' && ch <= '9') {
|
|
|
|
|
/* fixed width */
|
|
|
|
|
fmt = parse_number_spec(fmt, ch, &width);
|
|
|
|
|
ch = *fmt++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*** precision ***/
|
|
|
|
|
if (ch == '.') {
|
|
|
|
|
ch = *fmt++;
|
|
|
|
|
|
|
|
|
|
if (ch == '*') {
|
|
|
|
|
/* variable precision; negative precision is ignored */
|
|
|
|
|
precision = va_arg (ap, int);
|
|
|
|
|
ch = *fmt++;
|
|
|
|
|
}
|
|
|
|
|
else if (ch >= '0' && ch <= '9') {
|
|
|
|
|
/* fixed precision */
|
|
|
|
|
fmt = parse_number_spec(fmt, ch, &precision);
|
|
|
|
|
ch = *fmt++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*** length modifier ***/
|
|
|
|
|
#if FMT_LENMOD
|
|
|
|
|
switch (ch)
|
|
|
|
|
{
|
|
|
|
|
#if (FMT_LENMOD & (FMT_LENMOD_h | FMT_LENMOD_hh))
|
|
|
|
|
case 'h':
|
|
|
|
|
#endif
|
|
|
|
|
#if (FMT_LENMOD & FMT_LENMOD_j)
|
|
|
|
|
case 'j':
|
|
|
|
|
#endif
|
|
|
|
|
#if (FMT_LENMOD & (FMT_LENMOD_l | FMT_LENMOD_ll))
|
|
|
|
|
case 'l':
|
|
|
|
|
#endif
|
|
|
|
|
#if (FMT_LENMOD & FMT_LENMOD_t)
|
|
|
|
|
case 't':
|
|
|
|
|
#endif
|
|
|
|
|
#if (FMT_LENMOD & FMT_LENMOD_z)
|
|
|
|
|
case 'z':
|
2018-01-06 12:17:04 +00:00
|
|
|
|
#endif
|
|
|
|
|
#if (FMT_LENMOD & FMT_LENMOD_L)
|
|
|
|
|
case 'L':
|
Implement a much more capable vuprintf()
New support as well as some buggy support fixed.
Still no floating point support if ever that would be desired.
Support (*):
* Flags: '-', '+', ' ', '#', '0'
* Width and precision: 'n', '.n', '*' and '.*'
* Length modifiers: 'hh', 'h', 'j', 'l', 'll', 't', 'z'
* Radix: 'c', 'd', 'i', 'n', 'o', 'p/P', 's', 'u', 'x/X'
(*) Provision exists to switch lesser-used stuff on or off or when
certain functionality isn't desired (bootloader?). The compulsory
radixes are everything but 'o', 'n', 'p/P' and 'x/X' with length
modifiers being optional. The default setup is 'l', 'z', 'c', 'd',
'p/P', 's', 'u', 'x/X'.
* Move fdprintf() to its own file. It was in a strange place.
* Make callers compatible and fix a couple snprintf() bugs while
at it.
Could smush it down in size but I'm gonna get over the binsize
neurosis and just the let optimizer do its thing.
Change-Id: Ibdc613a9b6775802c188b29b9dd46c568c94f7c3
2017-09-08 23:28:02 +00:00
|
|
|
|
#endif
|
|
|
|
|
lenmod = ch;
|
|
|
|
|
ch = *fmt++;
|
|
|
|
|
#if (FMT_LENMOD & (FMT_LENMOD_hh | FMT_LENMOD_ll))
|
|
|
|
|
/* doesn't matter if jj, tt or zz happen; they will be rejected
|
|
|
|
|
by the radix handler */
|
|
|
|
|
if (ch == lenmod) {
|
|
|
|
|
lenmod = LENMOD2(lenmod, ch);
|
|
|
|
|
ch = *fmt++;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
#endif /* FMT_LENMOD */
|
|
|
|
|
|
|
|
|
|
/*** radix ***/
|
|
|
|
|
switch (ch)
|
|
|
|
|
{
|
|
|
|
|
/** non-numeric **/
|
|
|
|
|
case 'c':
|
|
|
|
|
buf = format_c(va_arg(ap, int), &fmt_buf, lenmod);
|
|
|
|
|
break;
|
|
|
|
|
#if (FMT_RADIX & FMT_RADIX_n)
|
|
|
|
|
case 'n':
|
|
|
|
|
if (format_n(va_arg(ap, void *), count, lenmod)) {
|
|
|
|
|
continue; /* no output */
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
#endif
|
|
|
|
|
case 's':
|
|
|
|
|
buf = format_s(va_arg(ap, const void *), &fmt_buf,
|
|
|
|
|
precision, lenmod);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
/** non-integer **/
|
|
|
|
|
#if (FMT_RADIX & FMT_RADIX_p)
|
|
|
|
|
case 'p':
|
|
|
|
|
case 'P':
|
|
|
|
|
buf = format_p(va_arg(ap, void *), &fmt_buf, ch,
|
|
|
|
|
&numeric);
|
|
|
|
|
break;
|
|
|
|
|
#endif
|
|
|
|
|
|
2018-01-06 12:17:04 +00:00
|
|
|
|
#if (FMT_RADIX & FMT_RADIX_floats)
|
|
|
|
|
/* any floats gets all of them (except with 'L' and %a, %A for now) */
|
|
|
|
|
case 'e':
|
|
|
|
|
case 'E':
|
|
|
|
|
case 'f':
|
|
|
|
|
case 'F':
|
|
|
|
|
case 'g':
|
|
|
|
|
case 'G':
|
|
|
|
|
/* LENMOD_L isn't supported for now and will be rejected automatically */
|
|
|
|
|
|
|
|
|
|
/* floating point has very different spec interpretations to other
|
|
|
|
|
formats and requires special handling */
|
|
|
|
|
fmt_buf.length = pfxlen;
|
|
|
|
|
fmt_buf.lenmod = lenmod;
|
|
|
|
|
fmt_buf.radixchar = ch;
|
|
|
|
|
fmt_buf.signchar = signchar;
|
|
|
|
|
fmt_buf.alignchar = alignchar;
|
|
|
|
|
fmt_buf.width = width;
|
|
|
|
|
fmt_buf.precision = precision;
|
|
|
|
|
|
|
|
|
|
ch = format_double_radix(va_arg(ap, double), &fmt_buf, push, userp);
|
|
|
|
|
if (ch) {
|
|
|
|
|
count += fmt_buf.length;
|
|
|
|
|
if (ch > 0) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
goto done;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
buf = fmt_buf.p;
|
|
|
|
|
break;
|
|
|
|
|
#endif
|
|
|
|
|
|
Implement a much more capable vuprintf()
New support as well as some buggy support fixed.
Still no floating point support if ever that would be desired.
Support (*):
* Flags: '-', '+', ' ', '#', '0'
* Width and precision: 'n', '.n', '*' and '.*'
* Length modifiers: 'hh', 'h', 'j', 'l', 'll', 't', 'z'
* Radix: 'c', 'd', 'i', 'n', 'o', 'p/P', 's', 'u', 'x/X'
(*) Provision exists to switch lesser-used stuff on or off or when
certain functionality isn't desired (bootloader?). The compulsory
radixes are everything but 'o', 'n', 'p/P' and 'x/X' with length
modifiers being optional. The default setup is 'l', 'z', 'c', 'd',
'p/P', 's', 'u', 'x/X'.
* Move fdprintf() to its own file. It was in a strange place.
* Make callers compatible and fix a couple snprintf() bugs while
at it.
Could smush it down in size but I'm gonna get over the binsize
neurosis and just the let optimizer do its thing.
Change-Id: Ibdc613a9b6775802c188b29b9dd46c568c94f7c3
2017-09-08 23:28:02 +00:00
|
|
|
|
/** signed integer **/
|
|
|
|
|
case 'd':
|
|
|
|
|
case 'i':
|
|
|
|
|
fmt_buf.length = pfxlen;
|
|
|
|
|
|
|
|
|
|
switch (lenmod)
|
|
|
|
|
{
|
|
|
|
|
case LENMOD_NONE:
|
|
|
|
|
val_d = va_arg(ap, signed int);
|
|
|
|
|
goto branch_fmt_d;
|
|
|
|
|
#if (FMT_LENMOD & FMT_LENMOD_h)
|
|
|
|
|
case LENMOD_h:
|
|
|
|
|
val_d = (signed short)va_arg(ap, SSHRT_INT_ARG);
|
|
|
|
|
goto branch_fmt_d;
|
|
|
|
|
#endif
|
|
|
|
|
#if (FMT_LENMOD & FMT_LENMOD_hh)
|
|
|
|
|
case LENMOD_hh:
|
|
|
|
|
val_d = (signed char)va_arg(ap, SCHAR_INT_ARG);
|
|
|
|
|
goto branch_fmt_d;
|
|
|
|
|
#endif
|
|
|
|
|
#if (FMT_LENMOD & FMT_LENMOD_j)
|
|
|
|
|
case LENMOD_j:
|
|
|
|
|
LENMOD_INTCOMPAT_BRANCH(LENMOD_INTCOMPAT_SEL(intmax_t, true),
|
|
|
|
|
va_arg(ap, intmax_t), true);
|
|
|
|
|
#endif
|
|
|
|
|
#if (FMT_LENMOD & FMT_LENMOD_l)
|
|
|
|
|
case LENMOD_l:
|
|
|
|
|
val_ld = va_arg(ap, signed long);
|
|
|
|
|
goto branch_fmt_ld;
|
|
|
|
|
#endif
|
|
|
|
|
#if (FMT_LENMOD & FMT_LENMOD_ll)
|
|
|
|
|
case LENMOD_ll:
|
|
|
|
|
val_lld = va_arg(ap, signed long long);
|
|
|
|
|
goto branch_fmt_lld;
|
|
|
|
|
#endif
|
|
|
|
|
#if (FMT_LENMOD & FMT_LENMOD_t)
|
|
|
|
|
case LENMOD_t:
|
|
|
|
|
LENMOD_INTCOMPAT_BRANCH(LENMOD_INTCOMPAT_SEL(ptrdiff_t, true),
|
|
|
|
|
va_arg(ap, ptrdiff_t), true);
|
|
|
|
|
#endif
|
|
|
|
|
#if (FMT_LENMOD & FMT_LENMOD_z)
|
|
|
|
|
case LENMOD_z:
|
|
|
|
|
LENMOD_INTCOMPAT_BRANCH(LENMOD_INTCOMPAT_SEL(ssize_t, true),
|
|
|
|
|
va_arg(ap, ssize_t), true);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* macrofied labels share a formatter with another */
|
|
|
|
|
if (0) {
|
|
|
|
|
branch_fmt_d:
|
|
|
|
|
buf = format_d(val_d, &fmt_buf, signchar);
|
|
|
|
|
} else if (0) {
|
|
|
|
|
#ifndef val_ld
|
|
|
|
|
branch_fmt_ld:
|
|
|
|
|
buf = format_ld(val_ld, &fmt_buf, signchar);
|
|
|
|
|
#endif
|
|
|
|
|
} else if (0) {
|
|
|
|
|
#ifndef val_lld
|
|
|
|
|
branch_fmt_lld:
|
|
|
|
|
buf = format_lld(val_lld, &fmt_buf, signchar);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
numeric = true;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
/** unsigned integer **/
|
|
|
|
|
#if (FMT_RADIX & FMT_RADIX_o)
|
|
|
|
|
case 'o':
|
|
|
|
|
#endif
|
|
|
|
|
case 'u':
|
|
|
|
|
#if (FMT_RADIX & FMT_RADIX_x)
|
|
|
|
|
case 'x':
|
|
|
|
|
case 'X':
|
|
|
|
|
#endif
|
|
|
|
|
fmt_buf.length = pfxlen;
|
|
|
|
|
|
|
|
|
|
switch (lenmod)
|
|
|
|
|
{
|
|
|
|
|
case LENMOD_NONE:
|
|
|
|
|
val_u = va_arg(ap, unsigned int);
|
|
|
|
|
goto branch_fmt_u;
|
|
|
|
|
#if (FMT_LENMOD & FMT_LENMOD_h)
|
|
|
|
|
case LENMOD_h:
|
|
|
|
|
val_u = (unsigned short)va_arg(ap, USHRT_INT_ARG);
|
|
|
|
|
goto branch_fmt_u;
|
|
|
|
|
#endif
|
|
|
|
|
#if (FMT_LENMOD & FMT_LENMOD_hh)
|
|
|
|
|
case LENMOD_hh:
|
|
|
|
|
val_u = (unsigned char)va_arg(ap, UCHAR_INT_ARG);
|
|
|
|
|
goto branch_fmt_u;
|
|
|
|
|
#endif
|
|
|
|
|
#if (FMT_LENMOD & FMT_LENMOD_j)
|
|
|
|
|
case LENMOD_j:
|
|
|
|
|
LENMOD_INTCOMPAT_BRANCH(LENMOD_INTCOMPAT_SEL(uintmax_t, false),
|
|
|
|
|
va_arg(ap, uintmax_t), false);
|
|
|
|
|
#endif
|
|
|
|
|
#if (FMT_LENMOD & FMT_LENMOD_l)
|
|
|
|
|
case LENMOD_l:
|
|
|
|
|
val_lu = va_arg(ap, unsigned long);
|
|
|
|
|
goto branch_fmt_lu;
|
|
|
|
|
#endif
|
|
|
|
|
#if (FMT_LENMOD & FMT_LENMOD_ll)
|
|
|
|
|
case LENMOD_ll:
|
|
|
|
|
val_llu = va_arg(ap, unsigned long long);
|
|
|
|
|
goto branch_fmt_llu;
|
|
|
|
|
#endif
|
|
|
|
|
#if (FMT_LENMOD & (FMT_LENMOD_t | FMT_LENMOD_z))
|
|
|
|
|
/* format "uptrdiff_t" as size_t (unless it becomes standard) */
|
|
|
|
|
#if (FMT_LENMOD & FMT_LENMOD_t)
|
|
|
|
|
case LENMOD_t:
|
|
|
|
|
#endif
|
|
|
|
|
#if (FMT_LENMOD & FMT_LENMOD_z)
|
|
|
|
|
case LENMOD_z:
|
|
|
|
|
#endif
|
|
|
|
|
LENMOD_INTCOMPAT_BRANCH(LENMOD_INTCOMPAT_SEL(size_t, false),
|
|
|
|
|
va_arg(ap, size_t), false);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* macrofied labels share a formatter with another */
|
|
|
|
|
if (0) {
|
|
|
|
|
branch_fmt_u:
|
|
|
|
|
buf = format_u(val_u, &fmt_buf, ch);
|
|
|
|
|
} else if (0) {
|
|
|
|
|
#ifndef val_lu
|
|
|
|
|
branch_fmt_lu:
|
|
|
|
|
buf = format_lu(val_lu, &fmt_buf, ch);
|
|
|
|
|
#endif
|
|
|
|
|
} else if (0) {
|
|
|
|
|
#ifndef val_llu
|
|
|
|
|
branch_fmt_llu:
|
|
|
|
|
buf = format_llu(val_llu, &fmt_buf, ch);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
numeric = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (buf) {
|
|
|
|
|
/** padding **/
|
|
|
|
|
if (numeric) {
|
|
|
|
|
/* numeric formats into fmt_buf.buf */
|
|
|
|
|
pfxlen = fmt_buf.length;
|
|
|
|
|
length = fmt_buf.bufend - buf;
|
|
|
|
|
|
|
|
|
|
size_t size = pfxlen + length;
|
|
|
|
|
|
|
|
|
|
if (precision >= 0) {
|
|
|
|
|
/* explicit precision */
|
|
|
|
|
precision -= (int)length;
|
|
|
|
|
|
|
|
|
|
if (precision > 0) {
|
|
|
|
|
size += precision;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
width -= MIN(width, size);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
/* default precision */
|
|
|
|
|
if (!length) {
|
|
|
|
|
length = 1;
|
|
|
|
|
size++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
width -= MIN(width, size);
|
|
|
|
|
|
|
|
|
|
if (alignchar == '0') {
|
|
|
|
|
/* width zero-fill */
|
|
|
|
|
precision = width;
|
|
|
|
|
width = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
/* non-numeric: supress prefix and precision; keep length and
|
|
|
|
|
width */
|
|
|
|
|
pfxlen = 0;
|
|
|
|
|
precision = 0;
|
|
|
|
|
length = fmt_buf.length;
|
|
|
|
|
width -= MIN(width, length);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
/* format not accepted; print it literally */
|
|
|
|
|
buf = fmt_buf.fmt_start - 2;
|
|
|
|
|
length = fmt - buf;
|
|
|
|
|
width = 0;
|
|
|
|
|
pfxlen = 0;
|
|
|
|
|
precision = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** push all the stuff **/
|
|
|
|
|
|
|
|
|
|
if (alignchar != '-') {
|
|
|
|
|
/* left padding */
|
|
|
|
|
while (width > 0) {
|
|
|
|
|
PUSHCHAR(' ');
|
|
|
|
|
width--;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* prefix */
|
|
|
|
|
while (pfxlen > 0) {
|
|
|
|
|
PUSHCHAR(buf[-pfxlen]);
|
|
|
|
|
pfxlen--;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 0-padding */
|
|
|
|
|
while (precision > 0) {
|
|
|
|
|
PUSHCHAR('0');
|
|
|
|
|
precision--;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* field */
|
|
|
|
|
while (length > 0) {
|
|
|
|
|
PUSHCHAR(*buf++);
|
|
|
|
|
length--;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* right padding */
|
|
|
|
|
while (width > 0) {
|
|
|
|
|
PUSHCHAR(' ');
|
|
|
|
|
width--;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
done:
|
|
|
|
|
return count;
|
|
|
|
|
}
|