1 /* $NetBSD: fmtcheck.c,v 1.16 2017/12/13 06:43:45 rin 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.16 2017/12/13 06:43:45 rin 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_WINTT, 51 FMTCHECK_LONG, 52 FMTCHECK_QUAD, 53 FMTCHECK_INTMAXT, 54 FMTCHECK_PTRDIFFT, 55 FMTCHECK_SIZET, 56 FMTCHECK_POINTER, 57 FMTCHECK_CHARPOINTER, 58 FMTCHECK_SHORTPOINTER, 59 FMTCHECK_INTPOINTER, 60 FMTCHECK_LONGPOINTER, 61 FMTCHECK_QUADPOINTER, 62 FMTCHECK_INTMAXTPOINTER, 63 FMTCHECK_PTRDIFFTPOINTER, 64 FMTCHECK_SIZETPOINTER, 65 FMTCHECK_DOUBLE, 66 FMTCHECK_LONGDOUBLE, 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 #ifdef WIN32 142 case 'I': 143 f++; 144 if (!*f) RETURN(pf,f,FMTCHECK_UNKNOWN); 145 if (*f == '3' && f[1] == '2') { 146 f += 2; 147 modifier = MOD_NONE; 148 } else if (*f == '6' && f[1] == '4') { 149 f += 2; 150 modifier = MOD_QUAD; 151 } 152 else { 153 #ifdef _WIN64 154 modifier = MOD_QUAD; 155 #else 156 modifier = MOD_NONE; 157 #endif 158 } 159 break; 160 #endif 161 default: 162 modifier = MOD_NONE; 163 break; 164 } 165 if (!*f) RETURN(pf,f,FMTCHECK_UNKNOWN); 166 if (strchr("diouxX", *f)) { 167 switch (modifier) { 168 case MOD_LONG: 169 RETURN(pf,f,FMTCHECK_LONG); 170 case MOD_QUAD: 171 RETURN(pf,f,FMTCHECK_QUAD); 172 case MOD_INTMAXT: 173 RETURN(pf,f,FMTCHECK_INTMAXT); 174 case MOD_PTRDIFFT: 175 RETURN(pf,f,FMTCHECK_PTRDIFFT); 176 case MOD_SIZET: 177 RETURN(pf,f,FMTCHECK_SIZET); 178 case MOD_CHAR: 179 case MOD_SHORT: 180 case MOD_NONE: 181 RETURN(pf,f,FMTCHECK_INT); 182 default: 183 RETURN(pf,f,FMTCHECK_UNKNOWN); 184 } 185 } 186 if (*f == 'n') { 187 switch (modifier) { 188 case MOD_CHAR: 189 RETURN(pf,f,FMTCHECK_CHARPOINTER); 190 case MOD_SHORT: 191 RETURN(pf,f,FMTCHECK_SHORTPOINTER); 192 case MOD_LONG: 193 RETURN(pf,f,FMTCHECK_LONGPOINTER); 194 case MOD_QUAD: 195 RETURN(pf,f,FMTCHECK_QUADPOINTER); 196 case MOD_INTMAXT: 197 RETURN(pf,f,FMTCHECK_INTMAXTPOINTER); 198 case MOD_PTRDIFFT: 199 RETURN(pf,f,FMTCHECK_PTRDIFFTPOINTER); 200 case MOD_SIZET: 201 RETURN(pf,f,FMTCHECK_SIZETPOINTER); 202 case MOD_NONE: 203 RETURN(pf,f,FMTCHECK_INTPOINTER); 204 default: 205 RETURN(pf,f,FMTCHECK_UNKNOWN); 206 } 207 } 208 if (strchr("DOU", *f)) { 209 if (modifier != MOD_NONE) 210 RETURN(pf,f,FMTCHECK_UNKNOWN); 211 RETURN(pf,f,FMTCHECK_LONG); 212 } 213 if (strchr("aAeEfFgG", *f)) { 214 switch (modifier) { 215 case MOD_LONGDOUBLE: 216 RETURN(pf,f,FMTCHECK_LONGDOUBLE); 217 case MOD_LONG: 218 case MOD_NONE: 219 RETURN(pf,f,FMTCHECK_DOUBLE); 220 default: 221 RETURN(pf,f,FMTCHECK_UNKNOWN); 222 } 223 } 224 if (*f == 'c') { 225 switch (modifier) { 226 case MOD_LONG: 227 RETURN(pf,f,FMTCHECK_WINTT); 228 case MOD_NONE: 229 RETURN(pf,f,FMTCHECK_INT); 230 default: 231 RETURN(pf,f,FMTCHECK_UNKNOWN); 232 } 233 } 234 if (*f == 'C') { 235 if (modifier != MOD_NONE) 236 RETURN(pf,f,FMTCHECK_UNKNOWN); 237 RETURN(pf,f,FMTCHECK_WINTT); 238 } 239 if (*f == 's') { 240 switch (modifier) { 241 case MOD_LONG: 242 RETURN(pf,f,FMTCHECK_WSTRING); 243 case MOD_NONE: 244 RETURN(pf,f,FMTCHECK_STRING); 245 default: 246 RETURN(pf,f,FMTCHECK_UNKNOWN); 247 } 248 } 249 if (*f == 'S') { 250 if (modifier != MOD_NONE) 251 RETURN(pf,f,FMTCHECK_UNKNOWN); 252 RETURN(pf,f,FMTCHECK_WSTRING); 253 } 254 if (*f == 'p') { 255 if (modifier != MOD_NONE) 256 RETURN(pf,f,FMTCHECK_UNKNOWN); 257 RETURN(pf,f,FMTCHECK_POINTER); 258 } 259 RETURN(pf,f,FMTCHECK_UNKNOWN); 260 /*NOTREACHED*/ 261 } 262 263 static EFT 264 get_next_format_from_width(const char **pf) 265 { 266 const char *f; 267 268 f = *pf; 269 if (*f == '.') { 270 f++; 271 if (*f == '*') { 272 RETURN(pf,f,FMTCHECK_PRECISION); 273 } 274 /* eat any precision (empty is allowed) */ 275 while (isdigit((unsigned char)*f)) f++; 276 if (!*f) RETURN(pf,f,FMTCHECK_UNKNOWN); 277 } 278 RETURN(pf,f,get_next_format_from_precision(pf)); 279 /*NOTREACHED*/ 280 } 281 282 static EFT 283 get_next_format(const char **pf, EFT eft) 284 { 285 int infmt; 286 const char *f; 287 288 if (eft == FMTCHECK_WIDTH) { 289 (*pf)++; 290 return get_next_format_from_width(pf); 291 } else if (eft == FMTCHECK_PRECISION) { 292 (*pf)++; 293 return get_next_format_from_precision(pf); 294 } 295 296 f = *pf; 297 infmt = 0; 298 while (!infmt) { 299 f = strchr(f, '%'); 300 if (f == NULL) 301 RETURN(pf,f,FMTCHECK_DONE); 302 f++; 303 if (!*f) 304 RETURN(pf,f,FMTCHECK_UNKNOWN); 305 if (*f != '%') 306 infmt = 1; 307 else 308 f++; 309 } 310 311 /* Eat any of the flags */ 312 while (*f && (strchr("#'0- +", *f))) 313 f++; 314 315 if (*f == '*') { 316 RETURN(pf,f,FMTCHECK_WIDTH); 317 } 318 /* eat any width */ 319 while (isdigit((unsigned char)*f)) f++; 320 if (!*f) { 321 RETURN(pf,f,FMTCHECK_UNKNOWN); 322 } 323 324 RETURN(pf,f,get_next_format_from_width(pf)); 325 /*NOTREACHED*/ 326 } 327 328 const char * 329 fmtcheck(const char *f1, const char *f2) 330 { 331 const char *f1p, *f2p; 332 EFT f1t, f2t; 333 334 if (!f1) return f2; 335 336 f1p = f1; 337 f1t = FMTCHECK_START; 338 f2p = f2; 339 f2t = FMTCHECK_START; 340 while ((f1t = get_next_format(&f1p, f1t)) != FMTCHECK_DONE) { 341 if (f1t == FMTCHECK_UNKNOWN) 342 return f2; 343 f2t = get_next_format(&f2p, f2t); 344 if (f1t != f2t) 345 return f2; 346 } 347 return f1; 348 } 349