From 3e0df2c22bfd37bc62bf09a01ec498e40d3599de Mon Sep 17 00:00:00 2001
From: Alexander Kanavin <alex.kanavin@gmail.com>
Date: Sat, 22 May 2021 20:26:24 +0200
Subject: [PATCH] add fallback parse_printf_format implementation

Upstream-Status: Inappropriate [musl specific]

Signed-off-by: Emil Renner Berthing <systemd@esmil.dk>
Signed-off-by: Khem Raj <raj.khem@gmail.com>
Signed-off-by: Chen Qi <Qi.Chen@windriver.com>
[rebased for systemd 243]
Signed-off-by: Scott Murray <scott.murray@konsulko.com>

---
 meson.build                              |   1 +
 src/basic/meson.build                    |   5 +
 src/basic/parse-printf-format.c          | 273 +++++++++++++++++++++++
 src/basic/parse-printf-format.h          |  57 +++++
 src/basic/stdio-util.h                   |   2 +-
 src/libsystemd/sd-journal/journal-send.c |   2 +-
 6 files changed, 338 insertions(+), 2 deletions(-)
 create mode 100644 src/basic/parse-printf-format.c
 create mode 100644 src/basic/parse-printf-format.h

--- a/meson.build
+++ b/meson.build
@@ -686,6 +686,7 @@ endif
 foreach header : ['crypt.h',
                   'linux/memfd.h',
                   'linux/vm_sockets.h',
+                  'printf.h',
                   'sys/auxv.h',
                   'valgrind/memcheck.h',
                   'valgrind/valgrind.h',
--- a/src/basic/meson.build
+++ b/src/basic/meson.build
@@ -335,6 +335,11 @@ endforeach
 
 basic_sources += generated_gperf_headers
 
+if conf.get('HAVE_PRINTF_H') != 1
+        basic_sources += [files('parse-printf-format.c')]
+endif
+
+
 ############################################################
 
 arch_list = [
--- /dev/null
+++ b/src/basic/parse-printf-format.c
@@ -0,0 +1,273 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2014 Emil Renner Berthing <systemd@esmil.dk>
+
+  With parts from the musl C library
+  Copyright 2005-2014 Rich Felker, et al.
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stddef.h>
+#include <string.h>
+
+#include "parse-printf-format.h"
+
+static const char *consume_nonarg(const char *fmt)
+{
+        do {
+                if (*fmt == '\0')
+                        return fmt;
+        } while (*fmt++ != '%');
+        return fmt;
+}
+
+static const char *consume_num(const char *fmt)
+{
+        for (;*fmt >= '0' && *fmt <= '9'; fmt++)
+                /* do nothing */;
+        return fmt;
+}
+
+static const char *consume_argn(const char *fmt, size_t *arg)
+{
+        const char *p = fmt;
+        size_t val = 0;
+
+        if (*p < '1' || *p > '9')
+                return fmt;
+        do {
+                val = 10*val + (*p++ - '0');
+        } while (*p >= '0' && *p <= '9');
+
+        if (*p != '$')
+                return fmt;
+        *arg = val;
+        return p+1;
+}
+
+static const char *consume_flags(const char *fmt)
+{
+        while (1) {
+                switch (*fmt) {
+                case '#':
+                case '0':
+                case '-':
+                case ' ':
+                case '+':
+                case '\'':
+                case 'I':
+                        fmt++;
+                        continue;
+                }
+                return fmt;
+        }
+}
+
+enum state {
+        BARE,
+        LPRE,
+        LLPRE,
+        HPRE,
+        HHPRE,
+        BIGLPRE,
+        ZTPRE,
+        JPRE,
+        STOP
+};
+
+enum type {
+        NONE,
+        PTR,
+        INT,
+        UINT,
+        ULLONG,
+        LONG,
+        ULONG,
+        SHORT,
+        USHORT,
+        CHAR,
+        UCHAR,
+        LLONG,
+        SIZET,
+        IMAX,
+        UMAX,
+        PDIFF,
+        UIPTR,
+        DBL,
+        LDBL,
+        MAXTYPE
+};
+
+static const short pa_types[MAXTYPE] = {
+        [NONE]   = PA_INT,
+        [PTR]    = PA_POINTER,
+        [INT]    = PA_INT,
+        [UINT]   = PA_INT,
+        [ULLONG] = PA_INT | PA_FLAG_LONG_LONG,
+        [LONG]   = PA_INT | PA_FLAG_LONG,
+        [ULONG]  = PA_INT | PA_FLAG_LONG,
+        [SHORT]  = PA_INT | PA_FLAG_SHORT,
+        [USHORT] = PA_INT | PA_FLAG_SHORT,
+        [CHAR]   = PA_CHAR,
+        [UCHAR]  = PA_CHAR,
+        [LLONG]  = PA_INT | PA_FLAG_LONG_LONG,
+        [SIZET]  = PA_INT | PA_FLAG_LONG,
+        [IMAX]   = PA_INT | PA_FLAG_LONG_LONG,
+        [UMAX]   = PA_INT | PA_FLAG_LONG_LONG,
+        [PDIFF]  = PA_INT | PA_FLAG_LONG_LONG,
+        [UIPTR]  = PA_INT | PA_FLAG_LONG,
+        [DBL]    = PA_DOUBLE,
+        [LDBL]   = PA_DOUBLE | PA_FLAG_LONG_DOUBLE
+};
+
+#define S(x) [(x)-'A']
+#define E(x) (STOP + (x))
+
+static const unsigned char states[]['z'-'A'+1] = {
+        { /* 0: bare types */
+                S('d') = E(INT), S('i') = E(INT),
+                S('o') = E(UINT),S('u') = E(UINT),S('x') = E(UINT), S('X') = E(UINT),
+                S('e') = E(DBL), S('f') = E(DBL), S('g') = E(DBL),  S('a') = E(DBL),
+                S('E') = E(DBL), S('F') = E(DBL), S('G') = E(DBL),  S('A') = E(DBL),
+                S('c') = E(CHAR),S('C') = E(INT),
+                S('s') = E(PTR), S('S') = E(PTR), S('p') = E(UIPTR),S('n') = E(PTR),
+                S('m') = E(NONE),
+                S('l') = LPRE,   S('h') = HPRE, S('L') = BIGLPRE,
+                S('z') = ZTPRE,  S('j') = JPRE, S('t') = ZTPRE
+        }, { /* 1: l-prefixed */
+                S('d') = E(LONG), S('i') = E(LONG),
+                S('o') = E(ULONG),S('u') = E(ULONG),S('x') = E(ULONG),S('X') = E(ULONG),
+                S('e') = E(DBL),  S('f') = E(DBL),  S('g') = E(DBL),  S('a') = E(DBL),
+                S('E') = E(DBL),  S('F') = E(DBL),  S('G') = E(DBL),  S('A') = E(DBL),
+                S('c') = E(INT),  S('s') = E(PTR),  S('n') = E(PTR),
+                S('l') = LLPRE
+        }, { /* 2: ll-prefixed */
+                S('d') = E(LLONG), S('i') = E(LLONG),
+                S('o') = E(ULLONG),S('u') = E(ULLONG),
+                S('x') = E(ULLONG),S('X') = E(ULLONG),
+                S('n') = E(PTR)
+        }, { /* 3: h-prefixed */
+                S('d') = E(SHORT), S('i') = E(SHORT),
+                S('o') = E(USHORT),S('u') = E(USHORT),
+                S('x') = E(USHORT),S('X') = E(USHORT),
+                S('n') = E(PTR),
+                S('h') = HHPRE
+        }, { /* 4: hh-prefixed */
+                S('d') = E(CHAR), S('i') = E(CHAR),
+                S('o') = E(UCHAR),S('u') = E(UCHAR),
+                S('x') = E(UCHAR),S('X') = E(UCHAR),
+                S('n') = E(PTR)
+        }, { /* 5: L-prefixed */
+                S('e') = E(LDBL),S('f') = E(LDBL),S('g') = E(LDBL), S('a') = E(LDBL),
+                S('E') = E(LDBL),S('F') = E(LDBL),S('G') = E(LDBL), S('A') = E(LDBL),
+                S('n') = E(PTR)
+        }, { /* 6: z- or t-prefixed (assumed to be same size) */
+                S('d') = E(PDIFF),S('i') = E(PDIFF),
+                S('o') = E(SIZET),S('u') = E(SIZET),
+                S('x') = E(SIZET),S('X') = E(SIZET),
+                S('n') = E(PTR)
+        }, { /* 7: j-prefixed */
+                S('d') = E(IMAX), S('i') = E(IMAX),
+                S('o') = E(UMAX), S('u') = E(UMAX),
+                S('x') = E(UMAX), S('X') = E(UMAX),
+                S('n') = E(PTR)
+        }
+};
+
+size_t parse_printf_format(const char *fmt, size_t n, int *types)
+{
+        size_t i = 0;
+        size_t last = 0;
+
+        memset(types, 0, n);
+
+        while (1) {
+                size_t arg;
+                unsigned int state;
+
+                fmt = consume_nonarg(fmt);
+                if (*fmt == '\0')
+                        break;
+                if (*fmt == '%') {
+                        fmt++;
+                        continue;
+                }
+                arg = 0;
+                fmt = consume_argn(fmt, &arg);
+                /* flags */
+                fmt = consume_flags(fmt);
+                /* width */
+                if (*fmt == '*') {
+                        size_t warg = 0;
+                        fmt = consume_argn(fmt+1, &warg);
+                        if (warg == 0)
+                                warg = ++i;
+                        if (warg > last)
+                                last = warg;
+                        if (warg <= n && types[warg-1] == NONE)
+                                types[warg-1] = INT;
+                } else
+                        fmt = consume_num(fmt);
+                /* precision */
+                if (*fmt == '.') {
+                        fmt++;
+                        if (*fmt == '*') {
+                                size_t parg = 0;
+                                fmt = consume_argn(fmt+1, &parg);
+                                if (parg == 0)
+                                        parg = ++i;
+                                if (parg > last)
+                                        last = parg;
+                                if (parg <= n && types[parg-1] == NONE)
+                                        types[parg-1] = INT;
+                        } else {
+                                if (*fmt == '-')
+                                        fmt++;
+                                fmt = consume_num(fmt);
+                        }
+                }
+                /* length modifier and conversion specifier */
+                state = BARE;
+                do {
+                        unsigned char c = *fmt++;
+
+                        if (c < 'A' || c > 'z')
+                                continue;
+                        state = states[state]S(c);
+                        if (state == 0)
+                                continue;
+                } while (state < STOP);
+
+                if (state == E(NONE))
+                        continue;
+
+                if (arg == 0)
+                        arg = ++i;
+                if (arg > last)
+                        last = arg;
+                if (arg <= n)
+                        types[arg-1] = state - STOP;
+        }
+
+        if (last > n)
+                last = n;
+        for (i = 0; i < last; i++)
+                types[i] = pa_types[types[i]];
+
+        return last;
+}
--- /dev/null
+++ b/src/basic/parse-printf-format.h
@@ -0,0 +1,57 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2014 Emil Renner Berthing <systemd@esmil.dk>
+
+  With parts from the GNU C Library
+  Copyright 1991-2014 Free Software Foundation, Inc.
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#pragma once
+
+#include "config.h"
+
+#if HAVE_PRINTF_H
+#include <printf.h>
+#else
+
+#include <stddef.h>
+
+enum {				/* C type: */
+  PA_INT,			/* int */
+  PA_CHAR,			/* int, cast to char */
+  PA_WCHAR,			/* wide char */
+  PA_STRING,			/* const char *, a '\0'-terminated string */
+  PA_WSTRING,			/* const wchar_t *, wide character string */
+  PA_POINTER,			/* void * */
+  PA_FLOAT,			/* float */
+  PA_DOUBLE,			/* double */
+  PA_LAST
+};
+
+/* Flag bits that can be set in a type returned by `parse_printf_format'.  */
+#define	PA_FLAG_MASK		0xff00
+#define	PA_FLAG_LONG_LONG	(1 << 8)
+#define	PA_FLAG_LONG_DOUBLE	PA_FLAG_LONG_LONG
+#define	PA_FLAG_LONG		(1 << 9)
+#define	PA_FLAG_SHORT		(1 << 10)
+#define	PA_FLAG_PTR		(1 << 11)
+
+size_t parse_printf_format(const char *fmt, size_t n, int *types);
+
+#endif /* HAVE_PRINTF_H */
--- a/src/basic/stdio-util.h
+++ b/src/basic/stdio-util.h
@@ -1,13 +1,13 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 #pragma once
 
-#include <printf.h>
 #include <stdarg.h>
 #include <stdio.h>
 #include <sys/types.h>
 
 #include "macro.h"
 #include "memory-util.h"
+#include "parse-printf-format.h"
 
 #define snprintf_ok(buf, len, fmt, ...)                                \
         ({                                                             \
--- a/src/libsystemd/sd-journal/journal-send.c
+++ b/src/libsystemd/sd-journal/journal-send.c
@@ -2,7 +2,6 @@
 
 #include <errno.h>
 #include <fcntl.h>
-#include <printf.h>
 #include <stddef.h>
 #include <sys/un.h>
 #include <unistd.h>
@@ -21,6 +20,7 @@
 #include "stdio-util.h"
 #include "string-util.h"
 #include "tmpfile-util.h"
+#include "parse-printf-format.h"
 
 #define SNDBUF_SIZE (8*1024*1024)
 
