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