Merge branch 'vendor/BZIP'
[dragonfly.git] / lib / libc / gen / fmtcheck.c
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  * $NetBSD: fmtcheck.c,v 1.2 2000/11/01 01:17:20 briggs Exp $
36  * $FreeBSD: src/lib/libc/gen/fmtcheck.c,v 1.9 2008/08/02 06:02:42 das Exp $
37  */
38
39 #include <stdio.h>
40 #include <string.h>
41 #include <ctype.h>
42
43 const char *__fmtcheck(const char *, const char *);
44
45 enum __e_fmtcheck_types {
46         FMTCHECK_START,
47         FMTCHECK_SHORT,
48         FMTCHECK_INT,
49         FMTCHECK_WINTT,
50         FMTCHECK_LONG,
51         FMTCHECK_QUAD,
52         FMTCHECK_INTMAXT,
53         FMTCHECK_PTRDIFFT,
54         FMTCHECK_SIZET,
55         FMTCHECK_CHARPOINTER,
56         FMTCHECK_SHORTPOINTER,
57         FMTCHECK_INTPOINTER,
58         FMTCHECK_LONGPOINTER,
59         FMTCHECK_QUADPOINTER,
60         FMTCHECK_INTMAXTPOINTER,
61         FMTCHECK_PTRDIFFTPOINTER,
62         FMTCHECK_SIZETPOINTER,
63 #ifndef NO_FLOATING_POINT
64         FMTCHECK_DOUBLE,
65         FMTCHECK_LONGDOUBLE,
66 #endif
67         FMTCHECK_STRING,
68         FMTCHECK_WSTRING,
69         FMTCHECK_WIDTH,
70         FMTCHECK_PRECISION,
71         FMTCHECK_DONE,
72         FMTCHECK_UNKNOWN
73 };
74 typedef enum __e_fmtcheck_types EFT;
75
76 enum e_modifier {
77         MOD_NONE,
78         MOD_CHAR,
79         MOD_SHORT,
80         MOD_LONG,
81         MOD_QUAD,
82         MOD_INTMAXT,
83         MOD_LONGDOUBLE,
84         MOD_PTRDIFFT,
85         MOD_SIZET,
86 };
87
88 #define RETURN(pf,f,r) do { \
89                         *(pf) = (f); \
90                         return r; \
91                        } /*NOTREACHED*/ /*CONSTCOND*/ while (0)
92
93 static EFT
94 get_next_format_from_precision(const char **pf)
95 {
96         enum e_modifier modifier;
97         const char      *f;
98
99         f = *pf;
100         switch (*f) {
101         case 'h':
102                 f++;
103                 if (!*f) RETURN(pf,f,FMTCHECK_UNKNOWN);
104                 if (*f == 'h') {
105                         f++;
106                         modifier = MOD_CHAR;
107                 } else {
108                         modifier = MOD_SHORT;
109                 }
110                 break;
111         case 'j':
112                 f++;
113                 modifier = MOD_INTMAXT;
114                 break;
115         case 'l':
116                 f++;
117                 if (!*f) RETURN(pf,f,FMTCHECK_UNKNOWN);
118                 if (*f == 'l') {
119                         f++;
120                         modifier = MOD_QUAD;
121                 } else {
122                         modifier = MOD_LONG;
123                 }
124                 break;
125         case 'q':
126                 f++;
127                 modifier = MOD_QUAD;
128                 break;
129         case 't':
130                 f++;
131                 modifier = MOD_PTRDIFFT;
132                 break;
133         case 'z':
134                 f++;
135                 modifier = MOD_SIZET;
136                 break;
137         case 'L':
138                 f++;
139                 modifier = MOD_LONGDOUBLE;
140                 break;
141         default:
142                 modifier = MOD_NONE;
143                 break;
144         }
145         if (!*f) RETURN(pf,f,FMTCHECK_UNKNOWN);
146         if (strchr("diouxX", *f)) {
147                 switch (modifier) {
148                 case MOD_LONG:
149                         RETURN(pf,f,FMTCHECK_LONG);
150                 case MOD_QUAD:
151                         RETURN(pf,f,FMTCHECK_QUAD);
152                 case MOD_INTMAXT:
153                         RETURN(pf,f,FMTCHECK_INTMAXT);
154                 case MOD_PTRDIFFT:
155                         RETURN(pf,f,FMTCHECK_PTRDIFFT);
156                 case MOD_SIZET:
157                         RETURN(pf,f,FMTCHECK_SIZET);
158                 case MOD_CHAR:
159                 case MOD_SHORT:
160                 case MOD_NONE:
161                         RETURN(pf,f,FMTCHECK_INT);
162                 default:
163                         RETURN(pf,f,FMTCHECK_UNKNOWN);
164                 }
165         }
166         if (*f == 'n') {
167                 switch (modifier) {
168                 case MOD_CHAR:
169                         RETURN(pf,f,FMTCHECK_CHARPOINTER);
170                 case MOD_SHORT:
171                         RETURN(pf,f,FMTCHECK_SHORTPOINTER);
172                 case MOD_LONG:
173                         RETURN(pf,f,FMTCHECK_LONGPOINTER);
174                 case MOD_QUAD:
175                         RETURN(pf,f,FMTCHECK_QUADPOINTER);
176                 case MOD_INTMAXT:
177                         RETURN(pf,f,FMTCHECK_INTMAXTPOINTER);
178                 case MOD_PTRDIFFT:
179                         RETURN(pf,f,FMTCHECK_PTRDIFFTPOINTER);
180                 case MOD_SIZET:
181                         RETURN(pf,f,FMTCHECK_SIZETPOINTER);
182                 case MOD_NONE:
183                         RETURN(pf,f,FMTCHECK_INTPOINTER);
184                 default:
185                         RETURN(pf,f,FMTCHECK_UNKNOWN);
186                 }
187         }
188         if (strchr("DOU", *f)) {
189                 if (modifier != MOD_NONE)
190                         RETURN(pf,f,FMTCHECK_UNKNOWN);
191                 RETURN(pf,f,FMTCHECK_LONG);
192         }
193 #ifndef NO_FLOATING_POINT
194         if (strchr("aAeEfFgG", *f)) {
195                 switch (modifier) {
196                 case MOD_LONGDOUBLE:
197                         RETURN(pf,f,FMTCHECK_LONGDOUBLE);
198                 case MOD_LONG:
199                 case MOD_NONE:
200                         RETURN(pf,f,FMTCHECK_DOUBLE);
201                 default:
202                         RETURN(pf,f,FMTCHECK_UNKNOWN);
203                 }
204         }
205 #endif
206         if (*f == 'c') {
207                 switch (modifier) {
208                 case MOD_LONG:
209                         RETURN(pf,f,FMTCHECK_WINTT);
210                 case MOD_NONE:
211                         RETURN(pf,f,FMTCHECK_INT);
212                 default:
213                         RETURN(pf,f,FMTCHECK_UNKNOWN);
214                 }
215         }
216         if (*f == 'C') {
217                 if (modifier != MOD_NONE)
218                         RETURN(pf,f,FMTCHECK_UNKNOWN);
219                 RETURN(pf,f,FMTCHECK_WINTT);
220         }
221         if (*f == 's') {
222                 switch (modifier) {
223                 case MOD_LONG:
224                         RETURN(pf,f,FMTCHECK_WSTRING);
225                 case MOD_NONE:
226                         RETURN(pf,f,FMTCHECK_STRING);
227                 default:
228                         RETURN(pf,f,FMTCHECK_UNKNOWN);
229                 }
230         }
231         if (*f == 'S') {
232                 if (modifier != MOD_NONE)
233                         RETURN(pf,f,FMTCHECK_UNKNOWN);
234                 RETURN(pf,f,FMTCHECK_WSTRING);
235         }
236         if (*f == 'p') {
237                 if (modifier != MOD_NONE)
238                         RETURN(pf,f,FMTCHECK_UNKNOWN);
239                 RETURN(pf,f,FMTCHECK_LONG);
240         }
241         RETURN(pf,f,FMTCHECK_UNKNOWN);
242         /*NOTREACHED*/
243 }
244
245 static EFT
246 get_next_format_from_width(const char **pf)
247 {
248         const char      *f;
249
250         f = *pf;
251         if (*f == '.') {
252                 f++;
253                 if (*f == '*') {
254                         RETURN(pf,f,FMTCHECK_PRECISION);
255                 }
256                 /* eat any precision (empty is allowed) */
257                 while (isdigit(*f)) f++;
258                 if (!*f) RETURN(pf,f,FMTCHECK_UNKNOWN);
259         }
260         RETURN(pf,f,get_next_format_from_precision(pf));
261         /*NOTREACHED*/
262 }
263
264 static EFT
265 get_next_format(const char **pf, EFT eft)
266 {
267         int             infmt;
268         const char      *f;
269
270         if (eft == FMTCHECK_WIDTH) {
271                 (*pf)++;
272                 return get_next_format_from_width(pf);
273         } else if (eft == FMTCHECK_PRECISION) {
274                 (*pf)++;
275                 return get_next_format_from_precision(pf);
276         }
277
278         f = *pf;
279         infmt = 0;
280         while (!infmt) {
281                 f = strchr(f, '%');
282                 if (f == NULL)
283                         RETURN(pf,f,FMTCHECK_DONE);
284                 f++;
285                 if (!*f)
286                         RETURN(pf,f,FMTCHECK_UNKNOWN);
287                 if (*f != '%')
288                         infmt = 1;
289                 else
290                         f++;
291         }
292
293         /* Eat any of the flags */
294         while (*f && (strchr("#'0- +", *f)))
295                 f++;
296
297         if (*f == '*') {
298                 RETURN(pf,f,FMTCHECK_WIDTH);
299         }
300         /* eat any width */
301         while (isdigit(*f)) f++;
302         if (!*f) {
303                 RETURN(pf,f,FMTCHECK_UNKNOWN);
304         }
305
306         RETURN(pf,f,get_next_format_from_width(pf));
307         /*NOTREACHED*/
308 }
309
310 const char *
311 __fmtcheck(const char *f1, const char *f2)
312 {
313         const char      *f1p, *f2p;
314         EFT             f1t, f2t;
315
316         if (!f1) return f2;
317         
318         f1p = f1;
319         f1t = FMTCHECK_START;
320         f2p = f2;
321         f2t = FMTCHECK_START;
322         while ((f1t = get_next_format(&f1p, f1t)) != FMTCHECK_DONE) {
323                 if (f1t == FMTCHECK_UNKNOWN)
324                         return f2;
325                 f2t = get_next_format(&f2p, f2t);
326                 if (f1t != f2t)
327                         return f2;
328         }
329         return f1;
330 }
331
332 __weak_reference(__fmtcheck, fmtcheck);