tools/nolibc/stdio: add a minimal [vf]printf() implementation
This adds a minimal vfprintf() implementation as well as the commonly used fprintf() and printf() that rely on it. For now the function supports: - formats: %s, %c, %u, %d, %x - modifiers: %l and %ll - unknown chars are considered as modifiers and are ignored It is designed to remain minimalist, despite this printf() is 549 bytes on x86_64. It would be wise not to add too many formats. Signed-off-by: Willy Tarreau <w@1wt.eu> Signed-off-by: Paul E. McKenney <paulmck@kernel.org>
This commit is contained in:
parent
e3e19052d5
commit
7e4346f4a3
|
@ -7,6 +7,8 @@
|
||||||
#ifndef _NOLIBC_STDIO_H
|
#ifndef _NOLIBC_STDIO_H
|
||||||
#define _NOLIBC_STDIO_H
|
#define _NOLIBC_STDIO_H
|
||||||
|
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
#include "std.h"
|
#include "std.h"
|
||||||
#include "arch.h"
|
#include "arch.h"
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
@ -158,4 +160,130 @@ char *fgets(char *s, int size, FILE *stream)
|
||||||
return ofs ? s : NULL;
|
return ofs ? s : NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* minimal vfprintf(). It supports the following formats:
|
||||||
|
* - %[l*]{d,u,c,x}
|
||||||
|
* - %s
|
||||||
|
* - unknown modifiers are ignored.
|
||||||
|
*/
|
||||||
|
static __attribute__((unused))
|
||||||
|
int vfprintf(FILE *stream, const char *fmt, va_list args)
|
||||||
|
{
|
||||||
|
char escape, lpref, c;
|
||||||
|
unsigned long long v;
|
||||||
|
unsigned int written;
|
||||||
|
size_t len, ofs;
|
||||||
|
char tmpbuf[21];
|
||||||
|
const char *outstr;
|
||||||
|
|
||||||
|
written = ofs = escape = lpref = 0;
|
||||||
|
while (1) {
|
||||||
|
c = fmt[ofs++];
|
||||||
|
|
||||||
|
if (escape) {
|
||||||
|
/* we're in an escape sequence, ofs == 1 */
|
||||||
|
escape = 0;
|
||||||
|
if (c == 'c' || c == 'd' || c == 'u' || c == 'x') {
|
||||||
|
if (lpref) {
|
||||||
|
if (lpref > 1)
|
||||||
|
v = va_arg(args, unsigned long long);
|
||||||
|
else
|
||||||
|
v = va_arg(args, unsigned long);
|
||||||
|
} else
|
||||||
|
v = va_arg(args, unsigned int);
|
||||||
|
|
||||||
|
if (c == 'd') {
|
||||||
|
/* sign-extend the value */
|
||||||
|
if (lpref == 0)
|
||||||
|
v = (long long)(int)v;
|
||||||
|
else if (lpref == 1)
|
||||||
|
v = (long long)(long)v;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (c) {
|
||||||
|
case 'd':
|
||||||
|
i64toa_r(v, tmpbuf);
|
||||||
|
break;
|
||||||
|
case 'u':
|
||||||
|
u64toa_r(v, tmpbuf);
|
||||||
|
break;
|
||||||
|
case 'x':
|
||||||
|
u64toh_r(v, tmpbuf);
|
||||||
|
break;
|
||||||
|
default: /* 'c' */
|
||||||
|
tmpbuf[0] = v;
|
||||||
|
tmpbuf[1] = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
outstr = tmpbuf;
|
||||||
|
}
|
||||||
|
else if (c == 's') {
|
||||||
|
outstr = va_arg(args, char *);
|
||||||
|
}
|
||||||
|
else if (c == '%') {
|
||||||
|
/* queue it verbatim */
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* modifiers or final 0 */
|
||||||
|
if (c == 'l') {
|
||||||
|
/* long format prefix, maintain the escape */
|
||||||
|
lpref++;
|
||||||
|
}
|
||||||
|
escape = 1;
|
||||||
|
goto do_escape;
|
||||||
|
}
|
||||||
|
len = strlen(outstr);
|
||||||
|
goto flush_str;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* not an escape sequence */
|
||||||
|
if (c == 0 || c == '%') {
|
||||||
|
/* flush pending data on escape or end */
|
||||||
|
escape = 1;
|
||||||
|
lpref = 0;
|
||||||
|
outstr = fmt;
|
||||||
|
len = ofs - 1;
|
||||||
|
flush_str:
|
||||||
|
if (_fwrite(outstr, len, stream) != 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
written += len;
|
||||||
|
do_escape:
|
||||||
|
if (c == 0)
|
||||||
|
break;
|
||||||
|
fmt += ofs;
|
||||||
|
ofs = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* literal char, just queue it */
|
||||||
|
}
|
||||||
|
return written;
|
||||||
|
}
|
||||||
|
|
||||||
|
static __attribute__((unused))
|
||||||
|
int fprintf(FILE *stream, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list args;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
va_start(args, fmt);
|
||||||
|
ret = vfprintf(stream, fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static __attribute__((unused))
|
||||||
|
int printf(const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list args;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
va_start(args, fmt);
|
||||||
|
ret = vfprintf(stdout, fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* _NOLIBC_STDIO_H */
|
#endif /* _NOLIBC_STDIO_H */
|
||||||
|
|
Loading…
Reference in New Issue