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