xref: /dragonfly/lib/libc/gen/fmtcheck.c (revision 4b1e43f6db5ebe40ea18d46c7f0f7df6b6fd6ae2)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-NetBSD
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  * $NetBSD: fmtcheck.c,v 1.8 2008/04/28 20:22:59 martin Exp $
31  * $FreeBSD: head/lib/libc/gen/fmtcheck.c 326193 2017-11-25 17:12:48Z pfg $
32  */
33 
34 #include <stdio.h>
35 #include <string.h>
36 #include <ctype.h>
37 
38 const char *__fmtcheck(const char *, const char *);
39 
40 enum __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 };
69 typedef enum __e_fmtcheck_types EFT;
70 
71 enum 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 
88 static EFT
get_next_format_from_precision(const char ** pf)89 get_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 
240 static EFT
get_next_format_from_width(const char ** pf)241 get_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 
259 static EFT
get_next_format(const char ** pf,EFT eft)260 get_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 
305 const char *
__fmtcheck(const char * f1,const char * f2)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 
327 __weak_reference(__fmtcheck, fmtcheck);
328