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
40enum __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};
69typedef enum __e_fmtcheck_types EFT;
70
71enum 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
88static EFT
89get_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
240static EFT
241get_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
259static EFT
260get_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
305const 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