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);
39const char * __fmtcheck(const char *, const char *);
40
41enum __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};
70typedef enum __e_fmtcheck_types EFT;
71
72enum 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
89static EFT
90get_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
241static EFT
242get_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
260static EFT
261get_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
306const 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