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