xref: /freebsd/lib/libc/gen/fmtcheck.c (revision ba3c1f5972d7b90feb6e6da47905ff2757e0fe57)
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 __FBSDID("$FreeBSD$");
35 
36 #include <stdio.h>
37 #include <string.h>
38 #include <ctype.h>
39 
40 __weak_reference(__fmtcheck, fmtcheck);
41 const char * __fmtcheck(const char *, const char *);
42 
43 enum __e_fmtcheck_types {
44 	FMTCHECK_START,
45 	FMTCHECK_SHORT,
46 	FMTCHECK_INT,
47 	FMTCHECK_WINTT,
48 	FMTCHECK_LONG,
49 	FMTCHECK_QUAD,
50 	FMTCHECK_INTMAXT,
51 	FMTCHECK_PTRDIFFT,
52 	FMTCHECK_SIZET,
53 	FMTCHECK_CHARPOINTER,
54 	FMTCHECK_SHORTPOINTER,
55 	FMTCHECK_INTPOINTER,
56 	FMTCHECK_LONGPOINTER,
57 	FMTCHECK_QUADPOINTER,
58 	FMTCHECK_INTMAXTPOINTER,
59 	FMTCHECK_PTRDIFFTPOINTER,
60 	FMTCHECK_SIZETPOINTER,
61 #ifndef NO_FLOATING_POINT
62 	FMTCHECK_DOUBLE,
63 	FMTCHECK_LONGDOUBLE,
64 #endif
65 	FMTCHECK_STRING,
66 	FMTCHECK_WSTRING,
67 	FMTCHECK_WIDTH,
68 	FMTCHECK_PRECISION,
69 	FMTCHECK_DONE,
70 	FMTCHECK_UNKNOWN
71 };
72 typedef enum __e_fmtcheck_types EFT;
73 
74 enum e_modifier {
75 	MOD_NONE,
76 	MOD_CHAR,
77 	MOD_SHORT,
78 	MOD_LONG,
79 	MOD_QUAD,
80 	MOD_INTMAXT,
81 	MOD_LONGDOUBLE,
82 	MOD_PTRDIFFT,
83 	MOD_SIZET,
84 };
85 
86 #define RETURN(pf,f,r) do { \
87 			*(pf) = (f); \
88 			return r; \
89 		       } /*NOTREACHED*/ /*CONSTCOND*/ while (0)
90 
91 static EFT
92 get_next_format_from_precision(const char **pf)
93 {
94 	enum e_modifier	modifier;
95 	const char	*f;
96 
97 	f = *pf;
98 	switch (*f) {
99 	case 'h':
100 		f++;
101 		if (!*f) RETURN(pf,f,FMTCHECK_UNKNOWN);
102 		if (*f == 'h') {
103 			f++;
104 			modifier = MOD_CHAR;
105 		} else {
106 			modifier = MOD_SHORT;
107 		}
108 		break;
109 	case 'j':
110 		f++;
111 		modifier = MOD_INTMAXT;
112 		break;
113 	case 'l':
114 		f++;
115 		if (!*f) RETURN(pf,f,FMTCHECK_UNKNOWN);
116 		if (*f == 'l') {
117 			f++;
118 			modifier = MOD_QUAD;
119 		} else {
120 			modifier = MOD_LONG;
121 		}
122 		break;
123 	case 'q':
124 		f++;
125 		modifier = MOD_QUAD;
126 		break;
127 	case 't':
128 		f++;
129 		modifier = MOD_PTRDIFFT;
130 		break;
131 	case 'z':
132 		f++;
133 		modifier = MOD_SIZET;
134 		break;
135 	case 'L':
136 		f++;
137 		modifier = MOD_LONGDOUBLE;
138 		break;
139 	default:
140 		modifier = MOD_NONE;
141 		break;
142 	}
143 	if (!*f) RETURN(pf,f,FMTCHECK_UNKNOWN);
144 	if (strchr("diouxX", *f)) {
145 		switch (modifier) {
146 		case MOD_LONG:
147 			RETURN(pf,f,FMTCHECK_LONG);
148 		case MOD_QUAD:
149 			RETURN(pf,f,FMTCHECK_QUAD);
150 		case MOD_INTMAXT:
151 			RETURN(pf,f,FMTCHECK_INTMAXT);
152 		case MOD_PTRDIFFT:
153 			RETURN(pf,f,FMTCHECK_PTRDIFFT);
154 		case MOD_SIZET:
155 			RETURN(pf,f,FMTCHECK_SIZET);
156 		case MOD_CHAR:
157 		case MOD_SHORT:
158 		case MOD_NONE:
159 			RETURN(pf,f,FMTCHECK_INT);
160 		default:
161 			RETURN(pf,f,FMTCHECK_UNKNOWN);
162 		}
163 	}
164 	if (*f == 'n') {
165 		switch (modifier) {
166 		case MOD_CHAR:
167 			RETURN(pf,f,FMTCHECK_CHARPOINTER);
168 		case MOD_SHORT:
169 			RETURN(pf,f,FMTCHECK_SHORTPOINTER);
170 		case MOD_LONG:
171 			RETURN(pf,f,FMTCHECK_LONGPOINTER);
172 		case MOD_QUAD:
173 			RETURN(pf,f,FMTCHECK_QUADPOINTER);
174 		case MOD_INTMAXT:
175 			RETURN(pf,f,FMTCHECK_INTMAXTPOINTER);
176 		case MOD_PTRDIFFT:
177 			RETURN(pf,f,FMTCHECK_PTRDIFFTPOINTER);
178 		case MOD_SIZET:
179 			RETURN(pf,f,FMTCHECK_SIZETPOINTER);
180 		case MOD_NONE:
181 			RETURN(pf,f,FMTCHECK_INTPOINTER);
182 		default:
183 			RETURN(pf,f,FMTCHECK_UNKNOWN);
184 		}
185 	}
186 	if (strchr("DOU", *f)) {
187 		if (modifier != MOD_NONE)
188 			RETURN(pf,f,FMTCHECK_UNKNOWN);
189 		RETURN(pf,f,FMTCHECK_LONG);
190 	}
191 #ifndef NO_FLOATING_POINT
192 	if (strchr("aAeEfFgG", *f)) {
193 		switch (modifier) {
194 		case MOD_LONGDOUBLE:
195 			RETURN(pf,f,FMTCHECK_LONGDOUBLE);
196 		case MOD_LONG:
197 		case MOD_NONE:
198 			RETURN(pf,f,FMTCHECK_DOUBLE);
199 		default:
200 			RETURN(pf,f,FMTCHECK_UNKNOWN);
201 		}
202 	}
203 #endif
204 	if (*f == 'c') {
205 		switch (modifier) {
206 		case MOD_LONG:
207 			RETURN(pf,f,FMTCHECK_WINTT);
208 		case MOD_NONE:
209 			RETURN(pf,f,FMTCHECK_INT);
210 		default:
211 			RETURN(pf,f,FMTCHECK_UNKNOWN);
212 		}
213 	}
214 	if (*f == 'C') {
215 		if (modifier != MOD_NONE)
216 			RETURN(pf,f,FMTCHECK_UNKNOWN);
217 		RETURN(pf,f,FMTCHECK_WINTT);
218 	}
219 	if (*f == 's') {
220 		switch (modifier) {
221 		case MOD_LONG:
222 			RETURN(pf,f,FMTCHECK_WSTRING);
223 		case MOD_NONE:
224 			RETURN(pf,f,FMTCHECK_STRING);
225 		default:
226 			RETURN(pf,f,FMTCHECK_UNKNOWN);
227 		}
228 	}
229 	if (*f == 'S') {
230 		if (modifier != MOD_NONE)
231 			RETURN(pf,f,FMTCHECK_UNKNOWN);
232 		RETURN(pf,f,FMTCHECK_WSTRING);
233 	}
234 	if (*f == 'p') {
235 		if (modifier != MOD_NONE)
236 			RETURN(pf,f,FMTCHECK_UNKNOWN);
237 		RETURN(pf,f,FMTCHECK_LONG);
238 	}
239 	RETURN(pf,f,FMTCHECK_UNKNOWN);
240 	/*NOTREACHED*/
241 }
242 
243 static EFT
244 get_next_format_from_width(const char **pf)
245 {
246 	const char	*f;
247 
248 	f = *pf;
249 	if (*f == '.') {
250 		f++;
251 		if (*f == '*') {
252 			RETURN(pf,f,FMTCHECK_PRECISION);
253 		}
254 		/* eat any precision (empty is allowed) */
255 		while (isdigit(*f)) f++;
256 		if (!*f) RETURN(pf,f,FMTCHECK_UNKNOWN);
257 	}
258 	RETURN(pf,f,get_next_format_from_precision(pf));
259 	/*NOTREACHED*/
260 }
261 
262 static EFT
263 get_next_format(const char **pf, EFT eft)
264 {
265 	int		infmt;
266 	const char	*f;
267 
268 	if (eft == FMTCHECK_WIDTH) {
269 		(*pf)++;
270 		return get_next_format_from_width(pf);
271 	} else if (eft == FMTCHECK_PRECISION) {
272 		(*pf)++;
273 		return get_next_format_from_precision(pf);
274 	}
275 
276 	f = *pf;
277 	infmt = 0;
278 	while (!infmt) {
279 		f = strchr(f, '%');
280 		if (f == NULL)
281 			RETURN(pf,f,FMTCHECK_DONE);
282 		f++;
283 		if (!*f)
284 			RETURN(pf,f,FMTCHECK_UNKNOWN);
285 		if (*f != '%')
286 			infmt = 1;
287 		else
288 			f++;
289 	}
290 
291 	/* Eat any of the flags */
292 	while (*f && (strchr("#'0- +", *f)))
293 		f++;
294 
295 	if (*f == '*') {
296 		RETURN(pf,f,FMTCHECK_WIDTH);
297 	}
298 	/* eat any width */
299 	while (isdigit(*f)) f++;
300 	if (!*f) {
301 		RETURN(pf,f,FMTCHECK_UNKNOWN);
302 	}
303 
304 	RETURN(pf,f,get_next_format_from_width(pf));
305 	/*NOTREACHED*/
306 }
307 
308 const char *
309 __fmtcheck(const char *f1, const char *f2)
310 {
311 	const char	*f1p, *f2p;
312 	EFT		f1t, f2t;
313 
314 	if (!f1) return f2;
315 
316 	f1p = f1;
317 	f1t = FMTCHECK_START;
318 	f2p = f2;
319 	f2t = FMTCHECK_START;
320 	while ((f1t = get_next_format(&f1p, f1t)) != FMTCHECK_DONE) {
321 		if (f1t == FMTCHECK_UNKNOWN)
322 			return f2;
323 		f2t = get_next_format(&f2p, f2t);
324 		if (f1t != f2t)
325 			return f2;
326 	}
327 	return f1;
328 }
329