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