xref: /freebsd/lib/libc/gen/fmtcheck.c (revision c9dbb1cc52b063bbd9ab078a7afc89a8696da659)
1 /*	$NetBSD: fmtcheck.c,v 1.8 2008/04/28 20:22:59 martin Exp $	*/
2 
3 /*-
4  * Copyright (c) 2000 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code was contributed to The NetBSD Foundation by Allen Briggs.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
19  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
20  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
22  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28  * POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 #include <sys/cdefs.h>
32 __FBSDID("$FreeBSD$");
33 
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
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
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
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 *
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