274 lines
6.0 KiB
C
274 lines
6.0 KiB
C
/*-*- 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;
|
|
}
|