chiark / gitweb /
Prep v227: Clean up various *-util.[hc] files
[elogind.git] / src / basic / parse-printf-format.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2014 Emil Renner Berthing <systemd@esmil.dk>
7
8   With parts from the musl C library
9   Copyright 2005-2014 Rich Felker, et al.
10
11   systemd is free software; you can redistribute it and/or modify it
12   under the terms of the GNU Lesser General Public License as published by
13   the Free Software Foundation; either version 2.1 of the License, or
14   (at your option) any later version.
15
16   systemd is distributed in the hope that it will be useful, but
17   WITHOUT ANY WARRANTY; without even the implied warranty of
18   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19   Lesser General Public License for more details.
20
21   You should have received a copy of the GNU Lesser General Public License
22   along with systemd; If not, see <http://www.gnu.org/licenses/>.
23 ***/
24
25 #include <stddef.h>
26 #include <string.h>
27
28 #include "parse-printf-format.h"
29
30 #ifndef HAVE_PRINTF_H
31
32 static const char *consume_nonarg(const char *fmt)
33 {
34         do {
35                 if (*fmt == '\0')
36                         return fmt;
37         } while (*fmt++ != '%');
38         return fmt;
39 }
40
41 static const char *consume_num(const char *fmt)
42 {
43         for (;*fmt >= '0' && *fmt <= '9'; fmt++)
44                 /* do nothing */;
45         return fmt;
46 }
47
48 static const char *consume_argn(const char *fmt, size_t *arg)
49 {
50         const char *p = fmt;
51         size_t val = 0;
52
53         if (*p < '1' || *p > '9')
54                 return fmt;
55         do {
56                 val = 10*val + (*p++ - '0');
57         } while (*p >= '0' && *p <= '9');
58
59         if (*p != '$')
60                 return fmt;
61         *arg = val;
62         return p+1;
63 }
64
65 static const char *consume_flags(const char *fmt)
66 {
67         while (1) {
68                 switch (*fmt) {
69                 case '#':
70                 case '0':
71                 case '-':
72                 case ' ':
73                 case '+':
74                 case '\'':
75                 case 'I':
76                         fmt++;
77                         continue;
78                 }
79                 return fmt;
80         }
81 }
82
83 enum state {
84         BARE,
85         LPRE,
86         LLPRE,
87         HPRE,
88         HHPRE,
89         BIGLPRE,
90         ZTPRE,
91         JPRE,
92         STOP
93 };
94
95 enum type {
96         NONE,
97         PTR,
98         INT,
99         UINT,
100         ULLONG,
101         LONG,
102         ULONG,
103         SHORT,
104         USHORT,
105         CHAR,
106         UCHAR,
107         LLONG,
108         SIZET,
109         IMAX,
110         UMAX,
111         PDIFF,
112         UIPTR,
113         DBL,
114         LDBL,
115         MAXTYPE
116 };
117
118 static const short pa_types[MAXTYPE] = {
119         [NONE]   = PA_INT,
120         [PTR]    = PA_POINTER,
121         [INT]    = PA_INT,
122         [UINT]   = PA_INT,
123         [ULLONG] = PA_INT | PA_FLAG_LONG_LONG,
124         [LONG]   = PA_INT | PA_FLAG_LONG,
125         [ULONG]  = PA_INT | PA_FLAG_LONG,
126         [SHORT]  = PA_INT | PA_FLAG_SHORT,
127         [USHORT] = PA_INT | PA_FLAG_SHORT,
128         [CHAR]   = PA_CHAR,
129         [UCHAR]  = PA_CHAR,
130         [LLONG]  = PA_INT | PA_FLAG_LONG_LONG,
131         [SIZET]  = PA_INT | PA_FLAG_LONG,
132         [IMAX]   = PA_INT | PA_FLAG_LONG_LONG,
133         [UMAX]   = PA_INT | PA_FLAG_LONG_LONG,
134         [PDIFF]  = PA_INT | PA_FLAG_LONG_LONG,
135         [UIPTR]  = PA_INT | PA_FLAG_LONG,
136         [DBL]    = PA_DOUBLE,
137         [LDBL]   = PA_DOUBLE | PA_FLAG_LONG_DOUBLE
138 };
139
140 #define S(x) [(x)-'A']
141 #define E(x) (STOP + (x))
142
143 static const unsigned char states[]['z'-'A'+1] = {
144         { /* 0: bare types */
145                 S('d') = E(INT), S('i') = E(INT),
146                 S('o') = E(UINT),S('u') = E(UINT),S('x') = E(UINT), S('X') = E(UINT),
147                 S('e') = E(DBL), S('f') = E(DBL), S('g') = E(DBL),  S('a') = E(DBL),
148                 S('E') = E(DBL), S('F') = E(DBL), S('G') = E(DBL),  S('A') = E(DBL),
149                 S('c') = E(CHAR),S('C') = E(INT),
150                 S('s') = E(PTR), S('S') = E(PTR), S('p') = E(UIPTR),S('n') = E(PTR),
151                 S('m') = E(NONE),
152                 S('l') = LPRE,   S('h') = HPRE, S('L') = BIGLPRE,
153                 S('z') = ZTPRE,  S('j') = JPRE, S('t') = ZTPRE
154         }, { /* 1: l-prefixed */
155                 S('d') = E(LONG), S('i') = E(LONG),
156                 S('o') = E(ULONG),S('u') = E(ULONG),S('x') = E(ULONG),S('X') = E(ULONG),
157                 S('e') = E(DBL),  S('f') = E(DBL),  S('g') = E(DBL),  S('a') = E(DBL),
158                 S('E') = E(DBL),  S('F') = E(DBL),  S('G') = E(DBL),  S('A') = E(DBL),
159                 S('c') = E(INT),  S('s') = E(PTR),  S('n') = E(PTR),
160                 S('l') = LLPRE
161         }, { /* 2: ll-prefixed */
162                 S('d') = E(LLONG), S('i') = E(LLONG),
163                 S('o') = E(ULLONG),S('u') = E(ULLONG),
164                 S('x') = E(ULLONG),S('X') = E(ULLONG),
165                 S('n') = E(PTR)
166         }, { /* 3: h-prefixed */
167                 S('d') = E(SHORT), S('i') = E(SHORT),
168                 S('o') = E(USHORT),S('u') = E(USHORT),
169                 S('x') = E(USHORT),S('X') = E(USHORT),
170                 S('n') = E(PTR),
171                 S('h') = HHPRE
172         }, { /* 4: hh-prefixed */
173                 S('d') = E(CHAR), S('i') = E(CHAR),
174                 S('o') = E(UCHAR),S('u') = E(UCHAR),
175                 S('x') = E(UCHAR),S('X') = E(UCHAR),
176                 S('n') = E(PTR)
177         }, { /* 5: L-prefixed */
178                 S('e') = E(LDBL),S('f') = E(LDBL),S('g') = E(LDBL), S('a') = E(LDBL),
179                 S('E') = E(LDBL),S('F') = E(LDBL),S('G') = E(LDBL), S('A') = E(LDBL),
180                 S('n') = E(PTR)
181         }, { /* 6: z- or t-prefixed (assumed to be same size) */
182                 S('d') = E(PDIFF),S('i') = E(PDIFF),
183                 S('o') = E(SIZET),S('u') = E(SIZET),
184                 S('x') = E(SIZET),S('X') = E(SIZET),
185                 S('n') = E(PTR)
186         }, { /* 7: j-prefixed */
187                 S('d') = E(IMAX), S('i') = E(IMAX),
188                 S('o') = E(UMAX), S('u') = E(UMAX),
189                 S('x') = E(UMAX), S('X') = E(UMAX),
190                 S('n') = E(PTR)
191         }
192 };
193
194 size_t parse_printf_format(const char *fmt, size_t n, int *types)
195 {
196         size_t i = 0;
197         size_t last = 0;
198
199         memset(types, 0, n);
200
201         while (1) {
202                 size_t arg;
203                 unsigned int state;
204
205                 fmt = consume_nonarg(fmt);
206                 if (*fmt == '\0')
207                         break;
208                 if (*fmt == '%') {
209                         fmt++;
210                         continue;
211                 }
212                 arg = 0;
213                 fmt = consume_argn(fmt, &arg);
214                 /* flags */
215                 fmt = consume_flags(fmt);
216                 /* width */
217                 if (*fmt == '*') {
218                         size_t warg = 0;
219                         fmt = consume_argn(fmt+1, &warg);
220                         if (warg == 0)
221                                 warg = ++i;
222                         if (warg > last)
223                                 last = warg;
224                         if (warg <= n && types[warg-1] == NONE)
225                                 types[warg-1] = INT;
226                 } else
227                         fmt = consume_num(fmt);
228                 /* precision */
229                 if (*fmt == '.') {
230                         fmt++;
231                         if (*fmt == '*') {
232                                 size_t parg = 0;
233                                 fmt = consume_argn(fmt+1, &parg);
234                                 if (parg == 0)
235                                         parg = ++i;
236                                 if (parg > last)
237                                         last = parg;
238                                 if (parg <= n && types[parg-1] == NONE)
239                                         types[parg-1] = INT;
240                         } else {
241                                 if (*fmt == '-')
242                                         fmt++;
243                                 fmt = consume_num(fmt);
244                         }
245                 }
246                 /* length modifier and conversion specifier */
247                 state = BARE;
248                 do {
249                         unsigned char c = *fmt++;
250
251                         if (c < 'A' || c > 'z')
252                                 continue;
253                         state = states[state]S(c);
254                         if (state == 0)
255                                 continue;
256                 } while (state < STOP);
257
258                 if (state == E(NONE))
259                         continue;
260
261                 if (arg == 0)
262                         arg = ++i;
263                 if (arg > last)
264                         last = arg;
265                 if (arg <= n)
266                         types[arg-1] = state - STOP;
267         }
268
269         if (last > n)
270                 last = n;
271         for (i = 0; i < last; i++)
272                 types[i] = pa_types[types[i]];
273
274         return last;
275 }
276
277 #endif // HAVE_PRINTF_H