summaryrefslogtreecommitdiff
path: root/tools/include/nolibc
diff options
context:
space:
mode:
authorWilly Tarreau <w@1wt.eu>2022-02-07 19:23:33 +0300
committerPaul E. McKenney <paulmck@kernel.org>2022-04-21 03:05:44 +0300
commit7e4346f4a3a611a6233ba388080c1426545da4fc (patch)
tree45cea3f8e81daf181e88032ee899dafca9c37808 /tools/include/nolibc
parente3e19052d54db8d492b8f82f8516139469bad5d3 (diff)
downloadlinux-7e4346f4a3a611a6233ba388080c1426545da4fc.tar.xz
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>
Diffstat (limited to 'tools/include/nolibc')
-rw-r--r--tools/include/nolibc/stdio.h128
1 files changed, 128 insertions, 0 deletions
diff --git a/tools/include/nolibc/stdio.h b/tools/include/nolibc/stdio.h
index 996bf89a30d2..a73cf24cb68d 100644
--- a/tools/include/nolibc/stdio.h
+++ b/tools/include/nolibc/stdio.h
@@ -7,6 +7,8 @@
#ifndef _NOLIBC_STDIO_H
#define _NOLIBC_STDIO_H
+#include <stdarg.h>
+
#include "std.h"
#include "arch.h"
#include "types.h"
@@ -158,4 +160,130 @@ char *fgets(char *s, int size, FILE *stream)
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 */