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 "file.h"
32#ifndef lint
33FILE_RCSID("@(#)$File: fmtcheck.c,v 1.6 2022/09/24 20:30:13 christos Exp $")
34#endif /* lint */
35
36#include <stdio.h>
37#include <string.h>
38#include <ctype.h>
39
40enum __e_fmtcheck_types {
41	FMTCHECK_START,
42	FMTCHECK_SHORT,
43	FMTCHECK_INT,
44	FMTCHECK_LONG,
45	FMTCHECK_QUAD,
46	FMTCHECK_SHORTPOINTER,
47	FMTCHECK_INTPOINTER,
48	FMTCHECK_LONGPOINTER,
49	FMTCHECK_QUADPOINTER,
50	FMTCHECK_DOUBLE,
51	FMTCHECK_LONGDOUBLE,
52	FMTCHECK_STRING,
53	FMTCHECK_WIDTH,
54	FMTCHECK_PRECISION,
55	FMTCHECK_DONE,
56	FMTCHECK_UNKNOWN
57};
58typedef enum __e_fmtcheck_types EFT;
59
60#define RETURN(pf,f,r) do { \
61			*(pf) = (f); \
62			return r; \
63		       } /*NOTREACHED*/ /*CONSTCOND*/ while (0)
64
65static EFT
66get_next_format_from_precision(const char **pf)
67{
68	int		sh, lg, quad, longdouble;
69	const char	*f;
70
71	sh = lg = quad = longdouble = 0;
72
73	f = *pf;
74	switch (*f) {
75	case 'h':
76		f++;
77		sh = 1;
78		break;
79	case 'l':
80		f++;
81		if (!*f) RETURN(pf,f,FMTCHECK_UNKNOWN);
82		if (*f == 'l') {
83			f++;
84			quad = 1;
85		} else {
86			lg = 1;
87		}
88		break;
89	case 'q':
90		f++;
91		quad = 1;
92		break;
93	case 'L':
94		f++;
95		longdouble = 1;
96		break;
97#ifdef WIN32
98	case 'I':
99		f++;
100		if (!*f) RETURN(pf,f,FMTCHECK_UNKNOWN);
101		if (*f == '3' && f[1] == '2') {
102			f += 2;
103		} else if (*f == '6' && f[1] == '4') {
104			f += 2;
105			quad = 1;
106		}
107#ifdef _WIN64
108		else {
109			quad = 1;
110		}
111#endif
112		break;
113#endif
114	default:
115		break;
116	}
117	if (!*f) RETURN(pf,f,FMTCHECK_UNKNOWN);
118	if (strchr("diouxX", *f)) {
119		if (longdouble)
120			RETURN(pf,f,FMTCHECK_UNKNOWN);
121		if (lg)
122			RETURN(pf,f,FMTCHECK_LONG);
123		if (quad)
124			RETURN(pf,f,FMTCHECK_QUAD);
125		RETURN(pf,f,FMTCHECK_INT);
126	}
127	if (*f == 'n') {
128		if (longdouble)
129			RETURN(pf,f,FMTCHECK_UNKNOWN);
130		if (sh)
131			RETURN(pf,f,FMTCHECK_SHORTPOINTER);
132		if (lg)
133			RETURN(pf,f,FMTCHECK_LONGPOINTER);
134		if (quad)
135			RETURN(pf,f,FMTCHECK_QUADPOINTER);
136		RETURN(pf,f,FMTCHECK_INTPOINTER);
137	}
138	if (strchr("DOU", *f)) {
139		if (sh + lg + quad + longdouble)
140			RETURN(pf,f,FMTCHECK_UNKNOWN);
141		RETURN(pf,f,FMTCHECK_LONG);
142	}
143	if (strchr("eEfg", *f)) {
144		if (longdouble)
145			RETURN(pf,f,FMTCHECK_LONGDOUBLE);
146		if (sh + lg + quad)
147			RETURN(pf,f,FMTCHECK_UNKNOWN);
148		RETURN(pf,f,FMTCHECK_DOUBLE);
149	}
150	if (*f == 'c') {
151		if (sh + lg + quad + longdouble)
152			RETURN(pf,f,FMTCHECK_UNKNOWN);
153		RETURN(pf,f,FMTCHECK_INT);
154	}
155	if (*f == 's') {
156		if (sh + lg + quad + longdouble)
157			RETURN(pf,f,FMTCHECK_UNKNOWN);
158		RETURN(pf,f,FMTCHECK_STRING);
159	}
160	if (*f == 'p') {
161		if (sh + lg + quad + longdouble)
162			RETURN(pf,f,FMTCHECK_UNKNOWN);
163		RETURN(pf,f,FMTCHECK_LONG);
164	}
165	RETURN(pf,f,FMTCHECK_UNKNOWN);
166	/*NOTREACHED*/
167}
168
169static EFT
170get_next_format_from_width(const char **pf)
171{
172	const char	*f;
173
174	f = *pf;
175	if (*f == '.') {
176		f++;
177		if (*f == '*') {
178			RETURN(pf,f,FMTCHECK_PRECISION);
179		}
180		/* eat any precision (empty is allowed) */
181		while (isdigit((unsigned char)*f)) f++;
182		if (!*f) RETURN(pf,f,FMTCHECK_UNKNOWN);
183	}
184	RETURN(pf,f,get_next_format_from_precision(pf));
185	/*NOTREACHED*/
186}
187
188static EFT
189get_next_format(const char **pf, EFT eft)
190{
191	int		infmt;
192	const char	*f;
193
194	if (eft == FMTCHECK_WIDTH) {
195		(*pf)++;
196		return get_next_format_from_width(pf);
197	} else if (eft == FMTCHECK_PRECISION) {
198		(*pf)++;
199		return get_next_format_from_precision(pf);
200	}
201
202	f = *pf;
203	infmt = 0;
204	while (!infmt) {
205		f = strchr(f, '%');
206		if (f == NULL)
207			RETURN(pf,f,FMTCHECK_DONE);
208		f++;
209		if (!*f)
210			RETURN(pf,f,FMTCHECK_UNKNOWN);
211		if (*f != '%')
212			infmt = 1;
213		else
214			f++;
215	}
216
217	/* Eat any of the flags */
218	while (*f && (strchr("#0- +", *f)))
219		f++;
220
221	if (*f == '*') {
222		RETURN(pf,f,FMTCHECK_WIDTH);
223	}
224	/* eat any width */
225	while (isdigit((unsigned char)*f)) f++;
226	if (!*f) {
227		RETURN(pf,f,FMTCHECK_UNKNOWN);
228	}
229
230	RETURN(pf,f,get_next_format_from_width(pf));
231	/*NOTREACHED*/
232}
233
234const char *
235fmtcheck(const char *f1, const char *f2)
236{
237	const char	*f1p, *f2p;
238	EFT		f1t, f2t;
239
240	if (!f1) return f2;
241
242	f1p = f1;
243	f1t = FMTCHECK_START;
244	f2p = f2;
245	f2t = FMTCHECK_START;
246	while ((f1t = get_next_format(&f1p, f1t)) != FMTCHECK_DONE) {
247		if (f1t == FMTCHECK_UNKNOWN)
248			return f2;
249		f2t = get_next_format(&f2p, f2t);
250		if (f1t != f2t)
251			return f2;
252	}
253	return f1;
254}
255