[systemd-devel] [RFC 16/25] add fallback parse_printf_format implementation
Tom Gundersen
teg at jklm.no
Thu Sep 18 08:42:36 PDT 2014
This sholud definitely go in your libc instead.
Cheers,
Tom
On Thu, Sep 18, 2014 at 3:24 PM, Emil Renner Berthing <systemd at esmil.dk> wrote:
> ---
> Makefile.am | 4 +
> configure.ac | 2 +
> src/journal/journal-send.c | 2 +-
> src/shared/log.c | 2 +-
> src/shared/parse-printf-format.c | 273 +++++++++++++++++++++++++++++++++++++++
> src/shared/parse-printf-format.h | 57 ++++++++
> 6 files changed, 338 insertions(+), 2 deletions(-)
> create mode 100644 src/shared/parse-printf-format.c
> create mode 100644 src/shared/parse-printf-format.h
>
> diff --git a/Makefile.am b/Makefile.am
> index 5dc17f8..7fc682f 100644
> --- a/Makefile.am
> +++ b/Makefile.am
> @@ -879,6 +879,10 @@ libsystemd_shared_la_SOURCES = \
> src/shared/switch-root.c \
> src/shared/nss-util.h
>
> +if !HAVE_PRINTF_H
> +libsystemd_shared_la_SOURCES += src/shared/parse-printf-format.c
> +endif
> +
> nodist_libsystemd_shared_la_SOURCES = \
> src/shared/errno-from-name.h \
> src/shared/errno-to-name.h \
> diff --git a/configure.ac b/configure.ac
> index 3db0e24..397521e 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -283,6 +283,8 @@ AM_CONDITIONAL([HAVE_PYTHON_DEVEL], [test "$have_python_devel" = "yes"])
> AC_SEARCH_LIBS([dlsym], [dl], [], [AC_MSG_ERROR([*** Dynamic linking loader library not found])])
> AC_CHECK_HEADERS([sys/capability.h], [], [AC_MSG_ERROR([*** POSIX caps headers not found])])
> AC_CHECK_HEADERS([linux/btrfs.h], [], [])
> +AC_CHECK_HEADERS([printf.h], [], [])
> +AM_CONDITIONAL(HAVE_PRINTF_H, [test "x$ac_cv_header_printf_h" = xyes])
>
> # unconditionally pull-in librt with old glibc versions
> AC_SEARCH_LIBS([clock_gettime], [rt], [], [])
> diff --git a/src/journal/journal-send.c b/src/journal/journal-send.c
> index bb1ef66..9ca4a0b 100644
> --- a/src/journal/journal-send.c
> +++ b/src/journal/journal-send.c
> @@ -25,12 +25,12 @@
> #include <stddef.h>
> #include <unistd.h>
> #include <fcntl.h>
> -#include <printf.h>
>
> #define SD_JOURNAL_SUPPRESS_LOCATION
>
> #include "sd-journal.h"
> #include "util.h"
> +#include "parse-printf-format.h"
> #include "socket-util.h"
>
> #define SNDBUF_SIZE (8*1024*1024)
> diff --git a/src/shared/log.c b/src/shared/log.c
> index 26c604a..8e968f1 100644
> --- a/src/shared/log.c
> +++ b/src/shared/log.c
> @@ -27,11 +27,11 @@
> #include <sys/socket.h>
> #include <sys/un.h>
> #include <stddef.h>
> -#include <printf.h>
>
> #include "log.h"
> #include "util.h"
> #include "missing.h"
> +#include "parse-printf-format.h"
> #include "macro.h"
> #include "socket-util.h"
>
> diff --git a/src/shared/parse-printf-format.c b/src/shared/parse-printf-format.c
> new file mode 100644
> index 0000000..49437e5
> --- /dev/null
> +++ b/src/shared/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 at 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;
> +}
> diff --git a/src/shared/parse-printf-format.h b/src/shared/parse-printf-format.h
> new file mode 100644
> index 0000000..4371177
> --- /dev/null
> +++ b/src/shared/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 at 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"
> +
> +#ifdef 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 */
> --
> 2.1.0
>
> _______________________________________________
> systemd-devel mailing list
> systemd-devel at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/systemd-devel
More information about the systemd-devel
mailing list