fmtcheck.c revision 181154
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
36/*	$NetBSD: fmtcheck.c,v 1.2 2000/11/01 01:17:20 briggs Exp $	*/
37#include <sys/cdefs.h>
38__FBSDID("$FreeBSD: head/lib/libc/gen/fmtcheck.c 181154 2008-08-02 06:02:42Z das $");
39
40#include <stdio.h>
41#include <string.h>
42#include <ctype.h>
43
44__weak_reference(__fmtcheck, fmtcheck);
45
46enum __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};
75typedef enum __e_fmtcheck_types EFT;
76
77enum 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
94static EFT
95get_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
246static EFT
247get_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
265static EFT
266get_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