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
This commit is contained in:
parent
52af55eee8
commit
5c9688961e
|
@ -204,7 +204,7 @@ common/fileobj_mgr.c
|
|||
common/dircache.c
|
||||
#endif /* HAVE_DIRCACHE */
|
||||
common/pathfuncs.c
|
||||
common/format.c
|
||||
common/fdprintf.c
|
||||
common/linked_list.c
|
||||
common/strcasecmp.c
|
||||
common/strcasestr.c
|
||||
|
@ -214,6 +214,7 @@ common/strlcpy.c
|
|||
common/structec.c
|
||||
common/timefuncs.c
|
||||
common/unicode.c
|
||||
common/vuprintf.c
|
||||
|
||||
/* Display */
|
||||
scroll_engine.c
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
/***************************************************************************
|
||||
* __________ __ ___.
|
||||
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||||
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||
* \/ \/ \/ \/ \/
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (C) 2002 by Felix Arends
|
||||
*
|
||||
* 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 <limits.h>
|
||||
#include "file.h"
|
||||
#include "vuprintf.h"
|
||||
|
||||
struct for_fprintf {
|
||||
int fd; /* where to store it */
|
||||
int rem; /* amount remaining */
|
||||
};
|
||||
|
||||
static int fprfunc(void *pr, int letter)
|
||||
{
|
||||
struct for_fprintf *fpr = (struct for_fprintf *)pr;
|
||||
|
||||
/* TODO: add a small buffer to reduce write() calls */
|
||||
if (write(fpr->fd, &(char){ letter }, 1) > 0) {
|
||||
return --fpr->rem;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int fdprintf(int fd, const char *fmt, ...)
|
||||
{
|
||||
int bytes;
|
||||
struct for_fprintf fpr;
|
||||
va_list ap;
|
||||
|
||||
fpr.fd = fd;
|
||||
fpr.rem = INT_MAX;
|
||||
|
||||
va_start(ap, fmt);
|
||||
bytes = vuprintf(fprfunc, &fpr, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
return bytes;
|
||||
}
|
|
@ -1,267 +0,0 @@
|
|||
/***************************************************************************
|
||||
* __________ __ ___.
|
||||
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||||
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||
* \/ \/ \/ \/ \/
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (C) 2002 by Gary Czvitkovicz
|
||||
*
|
||||
* 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 <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
#include "file.h"
|
||||
#include "format.h"
|
||||
|
||||
static const char hexdigit[] = "0123456789ABCDEF";
|
||||
|
||||
void format(
|
||||
/* call 'push()' for each output letter */
|
||||
int (*push)(void *userp, unsigned char data),
|
||||
void *userp,
|
||||
const char *fmt,
|
||||
va_list ap)
|
||||
{
|
||||
char *str;
|
||||
char tmpbuf[12], pad;
|
||||
int ch, width, val, sign, precision;
|
||||
long lval, lsign;
|
||||
unsigned int uval;
|
||||
unsigned long ulval;
|
||||
size_t uszval;
|
||||
ssize_t szval, szsign;
|
||||
bool ok = true;
|
||||
|
||||
tmpbuf[sizeof tmpbuf - 1] = '\0';
|
||||
|
||||
while ((ch = *fmt++) != '\0' && ok)
|
||||
{
|
||||
if (ch == '%')
|
||||
{
|
||||
ch = *fmt++;
|
||||
pad = ' ';
|
||||
if (ch == '0')
|
||||
pad = '0';
|
||||
|
||||
width = 0;
|
||||
while (ch >= '0' && ch <= '9')
|
||||
{
|
||||
width = 10*width + ch - '0';
|
||||
ch = *fmt++;
|
||||
}
|
||||
|
||||
precision = 0;
|
||||
if(ch == '.')
|
||||
{
|
||||
ch = *fmt++;
|
||||
while (ch >= '0' && ch <= '9')
|
||||
{
|
||||
precision = 10*precision + ch - '0';
|
||||
ch = *fmt++;
|
||||
}
|
||||
} else {
|
||||
precision = INT_MAX;
|
||||
}
|
||||
|
||||
str = tmpbuf + sizeof tmpbuf - 1;
|
||||
switch (ch)
|
||||
{
|
||||
case 'c':
|
||||
*--str = va_arg (ap, int);
|
||||
break;
|
||||
|
||||
case 's':
|
||||
str = va_arg (ap, char*);
|
||||
break;
|
||||
|
||||
case 'd':
|
||||
val = sign = va_arg (ap, int);
|
||||
if (val < 0)
|
||||
val = -val;
|
||||
do
|
||||
{
|
||||
*--str = (val % 10) + '0';
|
||||
val /= 10;
|
||||
}
|
||||
while (val > 0);
|
||||
if (sign < 0)
|
||||
*--str = '-';
|
||||
break;
|
||||
|
||||
case 'u':
|
||||
uval = va_arg(ap, unsigned int);
|
||||
do
|
||||
{
|
||||
*--str = (uval % 10) + '0';
|
||||
uval /= 10;
|
||||
}
|
||||
while (uval > 0);
|
||||
break;
|
||||
case 'p':
|
||||
case 'P':
|
||||
/* for pointers prepend 0x and act like 'X' */
|
||||
push(userp, '0');
|
||||
push(userp, 'x');
|
||||
/* fall through */
|
||||
case 'x':
|
||||
case 'X':
|
||||
pad='0';
|
||||
uval = va_arg (ap, int);
|
||||
do
|
||||
{
|
||||
*--str = hexdigit[uval & 0xf];
|
||||
uval >>= 4;
|
||||
}
|
||||
while (uval);
|
||||
break;
|
||||
|
||||
case 'l':
|
||||
ch = *fmt++;
|
||||
switch(ch) {
|
||||
case 'x':
|
||||
case 'X':
|
||||
pad='0';
|
||||
ulval = va_arg (ap, long);
|
||||
do
|
||||
{
|
||||
*--str = hexdigit[ulval & 0xf];
|
||||
ulval >>= 4;
|
||||
}
|
||||
while (ulval);
|
||||
break;
|
||||
case 'd':
|
||||
lval = lsign = va_arg (ap, long);
|
||||
if (lval < 0)
|
||||
lval = -lval;
|
||||
do
|
||||
{
|
||||
*--str = (lval % 10) + '0';
|
||||
lval /= 10;
|
||||
}
|
||||
while (lval > 0);
|
||||
if (lsign < 0)
|
||||
*--str = '-';
|
||||
break;
|
||||
|
||||
case 'u':
|
||||
ulval = va_arg(ap, unsigned long);
|
||||
do
|
||||
{
|
||||
*--str = (ulval % 10) + '0';
|
||||
ulval /= 10;
|
||||
}
|
||||
while (ulval > 0);
|
||||
break;
|
||||
|
||||
default:
|
||||
*--str = 'l';
|
||||
*--str = ch;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'z':
|
||||
ch = *fmt++;
|
||||
switch(ch) {
|
||||
case 'd':
|
||||
szval = szsign = va_arg (ap, ssize_t);
|
||||
if (szval < 0)
|
||||
szval = -szval;
|
||||
do
|
||||
{
|
||||
*--str = (szval % 10) + '0';
|
||||
szval /= 10;
|
||||
}
|
||||
while (szval > 0);
|
||||
if (szsign < 0)
|
||||
*--str = '-';
|
||||
break;
|
||||
|
||||
case 'u':
|
||||
uszval = va_arg(ap, size_t);
|
||||
do
|
||||
{
|
||||
*--str = (uszval % 10) + '0';
|
||||
uszval /= 10;
|
||||
}
|
||||
while (uszval > 0);
|
||||
break;
|
||||
|
||||
default:
|
||||
*--str = 'z';
|
||||
*--str = ch;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
*--str = ch;
|
||||
break;
|
||||
}
|
||||
|
||||
if (width > 0)
|
||||
{
|
||||
width -= strlen (str);
|
||||
while (width-- > 0 && ok)
|
||||
ok=push(userp, pad);
|
||||
}
|
||||
while (*str != '\0' && ok && precision--)
|
||||
ok=push(userp, *str++);
|
||||
}
|
||||
else
|
||||
ok=push(userp, ch);
|
||||
}
|
||||
}
|
||||
|
||||
struct for_fprintf {
|
||||
int fd; /* where to store it */
|
||||
int bytes; /* amount stored */
|
||||
};
|
||||
|
||||
static int fprfunc(void *pr, unsigned char letter)
|
||||
{
|
||||
struct for_fprintf *fpr = (struct for_fprintf *)pr;
|
||||
int rc = write(fpr->fd, &letter, 1);
|
||||
|
||||
if(rc > 0) {
|
||||
fpr->bytes++; /* count them */
|
||||
return true; /* we are ok */
|
||||
}
|
||||
|
||||
return false; /* failure */
|
||||
}
|
||||
|
||||
|
||||
int fdprintf(int fd, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
struct for_fprintf fpr;
|
||||
|
||||
fpr.fd=fd;
|
||||
fpr.bytes=0;
|
||||
|
||||
va_start(ap, fmt);
|
||||
format(fprfunc, &fpr, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
return fpr.bytes; /* return 0 on error */
|
||||
}
|
||||
|
||||
void vuprintf(int (*push)(void *userp, unsigned char data), void *userp, const char *fmt, va_list ap)
|
||||
{
|
||||
format(push, userp, fmt, ap);
|
||||
}
|
|
@ -0,0 +1,974 @@
|
|||
/***************************************************************************
|
||||
* __________ __ ___.
|
||||
* 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>
|
||||
#include "system.h"
|
||||
#include "vuprintf.h"
|
||||
|
||||
#if 0
|
||||
/* turn everything on */
|
||||
#define FMT_LENMOD (0xffffffff)
|
||||
#define FMT_RADIX (0xffffffff)
|
||||
#endif
|
||||
|
||||
/* 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>) */
|
||||
#define FMT_LENMOD_L 0x080 /* long double (instead of double) */
|
||||
|
||||
/* 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±d" */
|
||||
#define FMT_RADIX_e 0x200 /* floating point with exponent "[-]d.ddde±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 */
|
||||
|
||||
/* 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' */
|
||||
};
|
||||
|
||||
/* %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 */
|
||||
}
|
||||
|
||||
/* string length may be specified by precision instead of \0-
|
||||
terminated; however, don't go past a \0 if one is there */
|
||||
const char *s = str;
|
||||
size_t len = precision >= 0 ? precision : -1;
|
||||
|
||||
const char *nil = memchr(s, '\0', len);
|
||||
if (nil) {
|
||||
len = nil - s;
|
||||
}
|
||||
|
||||
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 */
|
||||
|
||||
/* 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)
|
||||
{
|
||||
#define PUSHCHAR(ch) \
|
||||
do { \
|
||||
int __ch = (ch); \
|
||||
int __rc = push(userp, __ch); \
|
||||
count += __rc >= 0; \
|
||||
if (__rc <= 0) { \
|
||||
goto done; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
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':
|
||||
#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
|
||||
|
||||
/** 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;
|
||||
}
|
|
@ -19,19 +19,27 @@
|
|||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef __FORMAT_H__
|
||||
#define __FORMAT_H__
|
||||
#ifndef __VUPRINTF_H__
|
||||
#define __VUPRINTF_H__
|
||||
|
||||
void format(
|
||||
/* call 'push()' for each output letter */
|
||||
int (*push)(void *userp, unsigned char data),
|
||||
void *userp,
|
||||
const char *fmt,
|
||||
va_list ap);
|
||||
#include <stdarg.h>
|
||||
|
||||
/* callback function is called for every output character (byte) with userp and
|
||||
* should return 0 when ch is a char other than '\0' that should stop printing */
|
||||
void vuprintf(int (*push)(void *userp, unsigned char data),
|
||||
void *userp, const char *fmt, va_list ap);
|
||||
/* callback function is called for every output character (byte) in the
|
||||
* output with userp
|
||||
*
|
||||
* it must return > 0 to continue (increments counter)
|
||||
* it may return 0 to stop (increments counter)
|
||||
* it may return < 0 to stop (does not increment counter)
|
||||
* a zero in the format string stops (does not increment counter)
|
||||
*
|
||||
* caller is reponsible for stopping formatting in order to keep the return
|
||||
* value from overflowing (assuming they have a reason to care)
|
||||
*/
|
||||
typedef int (* vuprintf_push_cb)(void *userp, int c);
|
||||
|
||||
#endif /* __FORMAT_H__ */
|
||||
/*
|
||||
* returns the number of times push() was called and returned >= 0
|
||||
*/
|
||||
int vuprintf(vuprintf_push_cb push, void *userp, const char *fmt, va_list ap);
|
||||
|
||||
#endif /* __VUPRINTF_H__ */
|
|
@ -18,74 +18,94 @@
|
|||
* KIND, either express or implied.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/*
|
||||
* Minimal printf and snprintf formatting functions
|
||||
*
|
||||
* These support %c %s %d and %x
|
||||
* Field width and zero-padding flag only
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <limits.h>
|
||||
#include "format.h"
|
||||
#include <errno.h>
|
||||
#include "vuprintf.h"
|
||||
|
||||
/* ALSA library requires a more advanced snprintf, so let's not
|
||||
override it in simulator for Linux. Note that Cygwin requires
|
||||
our snprintf or it produces garbled output after a while. */
|
||||
|
||||
struct for_snprintf {
|
||||
unsigned char *ptr; /* where to store it */
|
||||
size_t bytes; /* amount already stored */
|
||||
size_t max; /* max amount to store */
|
||||
char *ptr; /* where to store it */
|
||||
int rem; /* unwritten buffer remaining */
|
||||
};
|
||||
|
||||
static int sprfunc(void *ptr, unsigned char letter)
|
||||
static int sprfunc(void *ptr, int letter)
|
||||
{
|
||||
struct for_snprintf *pr = (struct for_snprintf *)ptr;
|
||||
if(pr->bytes < pr->max) {
|
||||
*pr->ptr = letter;
|
||||
pr->ptr++;
|
||||
pr->bytes++;
|
||||
return true;
|
||||
|
||||
if (pr->rem > 0) {
|
||||
if (--pr->rem == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
*pr->ptr++ = letter;
|
||||
return 1;
|
||||
}
|
||||
return false; /* filled buffer */
|
||||
}
|
||||
else {
|
||||
if (pr->rem == -INT_MAX) {
|
||||
pr->rem = 1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
int snprintf(char *buf, size_t size, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
struct for_snprintf pr;
|
||||
|
||||
pr.ptr = (unsigned char *)buf;
|
||||
pr.bytes = 0;
|
||||
pr.max = size;
|
||||
|
||||
va_start(ap, fmt);
|
||||
format(sprfunc, &pr, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
/* make sure it ends with a trailing zero */
|
||||
pr.ptr[(pr.bytes < pr.max) ? 0 : -1] = '\0';
|
||||
|
||||
return pr.bytes;
|
||||
--pr->rem;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
int vsnprintf(char *buf, size_t size, const char *fmt, va_list ap)
|
||||
{
|
||||
struct for_snprintf pr;
|
||||
if (size <= INT_MAX) {
|
||||
int bytes;
|
||||
struct for_snprintf pr;
|
||||
|
||||
pr.ptr = (unsigned char *)buf;
|
||||
pr.bytes = 0;
|
||||
pr.max = size;
|
||||
pr.ptr = buf;
|
||||
pr.rem = size;
|
||||
|
||||
format(sprfunc, &pr, fmt, ap);
|
||||
bytes = vuprintf(sprfunc, &pr, fmt, ap);
|
||||
|
||||
/* make sure it ends with a trailing zero */
|
||||
pr.ptr[(pr.bytes < pr.max) ? 0 : -1] = '\0';
|
||||
|
||||
return pr.bytes;
|
||||
if (size) {
|
||||
*pr.ptr = '\0';
|
||||
}
|
||||
else if (pr.rem > 0) {
|
||||
goto overflow;
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
overflow:
|
||||
errno = EOVERFLOW;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int snprintf(char *buf, size_t size, const char *fmt, ...)
|
||||
{
|
||||
if (size <= INT_MAX) {
|
||||
int bytes;
|
||||
struct for_snprintf pr;
|
||||
va_list ap;
|
||||
|
||||
pr.ptr = buf;
|
||||
pr.rem = size;
|
||||
|
||||
va_start(ap, fmt);
|
||||
bytes = vuprintf(sprfunc, &pr, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
if (size) {
|
||||
*pr.ptr = '\0';
|
||||
}
|
||||
else if (pr.rem > 0) {
|
||||
goto overflow;
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
overflow:
|
||||
errno = EOVERFLOW;
|
||||
return -1;
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@
|
|||
#endif
|
||||
#include "logf.h"
|
||||
#include "serial.h"
|
||||
#include "format.h"
|
||||
#include "vuprintf.h"
|
||||
|
||||
#ifdef HAVE_USBSTACK
|
||||
#include "usb_core.h"
|
||||
|
@ -193,7 +193,7 @@ static void check_logfindex(void)
|
|||
}
|
||||
}
|
||||
|
||||
static int logf_push(void *userp, unsigned char c)
|
||||
static int logf_push(void *userp, int c)
|
||||
{
|
||||
(void)userp;
|
||||
|
||||
|
@ -210,7 +210,7 @@ static int logf_push(void *userp, unsigned char c)
|
|||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void _logf(const char *fmt, ...)
|
||||
|
@ -310,7 +310,7 @@ void logf_panic_dump(int *y)
|
|||
#endif
|
||||
|
||||
#ifdef ROCKBOX_HAS_LOGDISKF
|
||||
static int logdiskf_push(void *userp, unsigned char c)
|
||||
static int logdiskf_push(void *userp, int c)
|
||||
{
|
||||
(void)userp;
|
||||
|
||||
|
@ -319,11 +319,11 @@ static int logdiskf_push(void *userp, unsigned char c)
|
|||
{
|
||||
strcpy(&logdiskfbuffer[logdiskfindex-8], "LOGFULL");
|
||||
logdiskfindex=MAX_LOGDISKF_SIZE;
|
||||
return false;
|
||||
return 0;
|
||||
}
|
||||
logdiskfbuffer[logdiskfindex++] = c;
|
||||
|
||||
return true;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void flush_buffer(void);
|
||||
|
|
|
@ -60,7 +60,7 @@ void tx_writec(unsigned char c)
|
|||
}
|
||||
|
||||
|
||||
static int uart_push(void *user_data, unsigned char ch)
|
||||
static int uart_push(void *user_data, int ch)
|
||||
{
|
||||
(void)user_data;
|
||||
|
||||
|
|
Loading…
Reference in New Issue