1 /* $NetBSD: fmtcheck.c,v 1.8 2008/04/28 20:22:59 martin Exp $ */
2
3 /*-
4 * SPDX-License-Identifier: BSD-2-Clause
5 *
6 * Copyright (c) 2000 The NetBSD Foundation, Inc.
7 * All rights reserved.
8 *
9 * This code was contributed to The NetBSD Foundation by Allen Briggs.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
31 */
32
33 #include <sys/cdefs.h>
34 #include <stdio.h>
35 #include <string.h>
36 #include <ctype.h>
37
38 __weak_reference(__fmtcheck, fmtcheck);
39 const char * __fmtcheck(const char *, const char *);
40
41 enum __e_fmtcheck_types {
42 FMTCHECK_START,
43 FMTCHECK_SHORT,
44 FMTCHECK_INT,
45 FMTCHECK_WINTT,
46 FMTCHECK_LONG,
47 FMTCHECK_QUAD,
48 FMTCHECK_INTMAXT,
49 FMTCHECK_PTRDIFFT,
50 FMTCHECK_SIZET,
51 FMTCHECK_CHARPOINTER,
52 FMTCHECK_SHORTPOINTER,
53 FMTCHECK_INTPOINTER,
54 FMTCHECK_LONGPOINTER,
55 FMTCHECK_QUADPOINTER,
56 FMTCHECK_INTMAXTPOINTER,
57 FMTCHECK_PTRDIFFTPOINTER,
58 FMTCHECK_SIZETPOINTER,
59 #ifndef NO_FLOATING_POINT
60 FMTCHECK_DOUBLE,
61 FMTCHECK_LONGDOUBLE,
62 #endif
63 FMTCHECK_STRING,
64 FMTCHECK_WSTRING,
65 FMTCHECK_WIDTH,
66 FMTCHECK_PRECISION,
67 FMTCHECK_DONE,
68 FMTCHECK_UNKNOWN
69 };
70 typedef enum __e_fmtcheck_types EFT;
71
72 enum e_modifier {
73 MOD_NONE,
74 MOD_CHAR,
75 MOD_SHORT,
76 MOD_LONG,
77 MOD_QUAD,
78 MOD_INTMAXT,
79 MOD_LONGDOUBLE,
80 MOD_PTRDIFFT,
81 MOD_SIZET,
82 };
83
84 #define RETURN(pf,f,r) do { \
85 *(pf) = (f); \
86 return r; \
87 } /*NOTREACHED*/ /*CONSTCOND*/ while (0)
88
89 static EFT
get_next_format_from_precision(const char ** pf)90 get_next_format_from_precision(const char **pf)
91 {
92 enum e_modifier modifier;
93 const char *f;
94
95 f = *pf;
96 switch (*f) {
97 case 'h':
98 f++;
99 if (!*f) RETURN(pf,f,FMTCHECK_UNKNOWN);
100 if (*f == 'h') {
101 f++;
102 modifier = MOD_CHAR;
103 } else {
104 modifier = MOD_SHORT;
105 }
106 break;
107 case 'j':
108 f++;
109 modifier = MOD_INTMAXT;
110 break;
111 case 'l':
112 f++;
113 if (!*f) RETURN(pf,f,FMTCHECK_UNKNOWN);
114 if (*f == 'l') {
115 f++;
116 modifier = MOD_QUAD;
117 } else {
118 modifier = MOD_LONG;
119 }
120 break;
121 case 'q':
122 f++;
123 modifier = MOD_QUAD;
124 break;
125 case 't':
126 f++;
127 modifier = MOD_PTRDIFFT;
128 break;
129 case 'z':
130 f++;
131 modifier = MOD_SIZET;
132 break;
133 case 'L':
134 f++;
135 modifier = MOD_LONGDOUBLE;
136 break;
137 default:
138 modifier = MOD_NONE;
139 break;
140 }
141 if (!*f) RETURN(pf,f,FMTCHECK_UNKNOWN);
142 if (strchr("diouxX", *f)) {
143 switch (modifier) {
144 case MOD_LONG:
145 RETURN(pf,f,FMTCHECK_LONG);
146 case MOD_QUAD:
147 RETURN(pf,f,FMTCHECK_QUAD);
148 case MOD_INTMAXT:
149 RETURN(pf,f,FMTCHECK_INTMAXT);
150 case MOD_PTRDIFFT:
151 RETURN(pf,f,FMTCHECK_PTRDIFFT);
152 case MOD_SIZET:
153 RETURN(pf,f,FMTCHECK_SIZET);
154 case MOD_CHAR:
155 case MOD_SHORT:
156 case MOD_NONE:
157 RETURN(pf,f,FMTCHECK_INT);
158 default:
159 RETURN(pf,f,FMTCHECK_UNKNOWN);
160 }
161 }
162 if (*f == 'n') {
163 switch (modifier) {
164 case MOD_CHAR:
165 RETURN(pf,f,FMTCHECK_CHARPOINTER);
166 case MOD_SHORT:
167 RETURN(pf,f,FMTCHECK_SHORTPOINTER);
168 case MOD_LONG:
169 RETURN(pf,f,FMTCHECK_LONGPOINTER);
170 case MOD_QUAD:
171 RETURN(pf,f,FMTCHECK_QUADPOINTER);
172 case MOD_INTMAXT:
173 RETURN(pf,f,FMTCHECK_INTMAXTPOINTER);
174 case MOD_PTRDIFFT:
175 RETURN(pf,f,FMTCHECK_PTRDIFFTPOINTER);
176 case MOD_SIZET:
177 RETURN(pf,f,FMTCHECK_SIZETPOINTER);
178 case MOD_NONE:
179 RETURN(pf,f,FMTCHECK_INTPOINTER);
180 default:
181 RETURN(pf,f,FMTCHECK_UNKNOWN);
182 }
183 }
184 if (strchr("DOU", *f)) {
185 if (modifier != MOD_NONE)
186 RETURN(pf,f,FMTCHECK_UNKNOWN);
187 RETURN(pf,f,FMTCHECK_LONG);
188 }
189 #ifndef NO_FLOATING_POINT
190 if (strchr("aAeEfFgG", *f)) {
191 switch (modifier) {
192 case MOD_LONGDOUBLE:
193 RETURN(pf,f,FMTCHECK_LONGDOUBLE);
194 case MOD_LONG:
195 case MOD_NONE:
196 RETURN(pf,f,FMTCHECK_DOUBLE);
197 default:
198 RETURN(pf,f,FMTCHECK_UNKNOWN);
199 }
200 }
201 #endif
202 if (*f == 'c') {
203 switch (modifier) {
204 case MOD_LONG:
205 RETURN(pf,f,FMTCHECK_WINTT);
206 case MOD_NONE:
207 RETURN(pf,f,FMTCHECK_INT);
208 default:
209 RETURN(pf,f,FMTCHECK_UNKNOWN);
210 }
211 }
212 if (*f == 'C') {
213 if (modifier != MOD_NONE)
214 RETURN(pf,f,FMTCHECK_UNKNOWN);
215 RETURN(pf,f,FMTCHECK_WINTT);
216 }
217 if (*f == 's') {
218 switch (modifier) {
219 case MOD_LONG:
220 RETURN(pf,f,FMTCHECK_WSTRING);
221 case MOD_NONE:
222 RETURN(pf,f,FMTCHECK_STRING);
223 default:
224 RETURN(pf,f,FMTCHECK_UNKNOWN);
225 }
226 }
227 if (*f == 'S') {
228 if (modifier != MOD_NONE)
229 RETURN(pf,f,FMTCHECK_UNKNOWN);
230 RETURN(pf,f,FMTCHECK_WSTRING);
231 }
232 if (*f == 'p') {
233 if (modifier != MOD_NONE)
234 RETURN(pf,f,FMTCHECK_UNKNOWN);
235 RETURN(pf,f,FMTCHECK_LONG);
236 }
237 RETURN(pf,f,FMTCHECK_UNKNOWN);
238 /*NOTREACHED*/
239 }
240
241 static EFT
get_next_format_from_width(const char ** pf)242 get_next_format_from_width(const char **pf)
243 {
244 const char *f;
245
246 f = *pf;
247 if (*f == '.') {
248 f++;
249 if (*f == '*') {
250 RETURN(pf,f,FMTCHECK_PRECISION);
251 }
252 /* eat any precision (empty is allowed) */
253 while (isdigit(*f)) f++;
254 if (!*f) RETURN(pf,f,FMTCHECK_UNKNOWN);
255 }
256 RETURN(pf,f,get_next_format_from_precision(pf));
257 /*NOTREACHED*/
258 }
259
260 static EFT
get_next_format(const char ** pf,EFT eft)261 get_next_format(const char **pf, EFT eft)
262 {
263 int infmt;
264 const char *f;
265
266 if (eft == FMTCHECK_WIDTH) {
267 (*pf)++;
268 return get_next_format_from_width(pf);
269 } else if (eft == FMTCHECK_PRECISION) {
270 (*pf)++;
271 return get_next_format_from_precision(pf);
272 }
273
274 f = *pf;
275 infmt = 0;
276 while (!infmt) {
277 f = strchr(f, '%');
278 if (f == NULL)
279 RETURN(pf,f,FMTCHECK_DONE);
280 f++;
281 if (!*f)
282 RETURN(pf,f,FMTCHECK_UNKNOWN);
283 if (*f != '%')
284 infmt = 1;
285 else
286 f++;
287 }
288
289 /* Eat any of the flags */
290 while (*f && (strchr("#'0- +", *f)))
291 f++;
292
293 if (*f == '*') {
294 RETURN(pf,f,FMTCHECK_WIDTH);
295 }
296 /* eat any width */
297 while (isdigit(*f)) f++;
298 if (!*f) {
299 RETURN(pf,f,FMTCHECK_UNKNOWN);
300 }
301
302 RETURN(pf,f,get_next_format_from_width(pf));
303 /*NOTREACHED*/
304 }
305
306 const char *
__fmtcheck(const char * f1,const char * f2)307 __fmtcheck(const char *f1, const char *f2)
308 {
309 const char *f1p, *f2p;
310 EFT f1t, f2t;
311
312 if (!f1) return f2;
313
314 f1p = f1;
315 f1t = FMTCHECK_START;
316 f2p = f2;
317 f2t = FMTCHECK_START;
318 while ((f1t = get_next_format(&f1p, f1t)) != FMTCHECK_DONE) {
319 if (f1t == FMTCHECK_UNKNOWN)
320 return f2;
321 f2t = get_next_format(&f2p, f2t);
322 if (f1t != f2t)
323 return f2;
324 }
325 return f1;
326 }
327