1 1.1 mrg /** 2 1.1 mrg * Check the arguments to `printf` and `scanf` against the `format` string. 3 1.1 mrg * 4 1.1 mrg * Copyright: Copyright (C) 1999-2022 by The D Language Foundation, All Rights Reserved 5 1.1 mrg * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) 6 1.1 mrg * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 7 1.1 mrg * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/chkformat.d, _chkformat.d) 8 1.1 mrg * Documentation: https://dlang.org/phobos/dmd_chkformat.html 9 1.1 mrg * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/chkformat.d 10 1.1 mrg */ 11 1.1 mrg module dmd.chkformat; 12 1.1 mrg 13 1.1 mrg //import core.stdc.stdio : printf, scanf; 14 1.1 mrg import core.stdc.ctype : isdigit; 15 1.1 mrg 16 1.1 mrg import dmd.astenums; 17 1.1 mrg import dmd.cond; 18 1.1 mrg import dmd.errors; 19 1.1 mrg import dmd.expression; 20 1.1 mrg import dmd.globals; 21 1.1 mrg import dmd.identifier; 22 1.1 mrg import dmd.mtype; 23 1.1 mrg import dmd.target; 24 1.1 mrg 25 1.1 mrg 26 1.1 mrg /****************************************** 27 1.1 mrg * Check that arguments to a printf format string are compatible 28 1.1 mrg * with that string. Issue errors for incompatibilities. 29 1.1 mrg * 30 1.1 mrg * Follows the C99 specification for printf. 31 1.1 mrg * 32 1.1 mrg * Takes a generous, rather than strict, view of compatiblity. 33 1.1 mrg * For example, an unsigned value can be formatted with a signed specifier. 34 1.1 mrg * 35 1.1 mrg * Diagnosed incompatibilities are: 36 1.1 mrg * 37 1.1 mrg * 1. incompatible sizes which will cause argument misalignment 38 1.1 mrg * 2. deferencing arguments that are not pointers 39 1.1 mrg * 3. insufficient number of arguments 40 1.1 mrg * 4. struct arguments 41 1.1 mrg * 5. array and slice arguments 42 1.1 mrg * 6. non-pointer arguments to `s` specifier 43 1.1 mrg * 7. non-standard formats 44 1.1 mrg * 8. undefined behavior per C99 45 1.1 mrg * 46 1.1 mrg * Per the C Standard, extra arguments are ignored. 47 1.1 mrg * 48 1.1 mrg * No attempt is made to fix the arguments or the format string. 49 1.1 mrg * 50 1.1 mrg * Params: 51 1.1 mrg * loc = location for error messages 52 1.1 mrg * format = format string 53 1.1 mrg * args = arguments to match with format string 54 1.1 mrg * isVa_list = if a "v" function (format check only) 55 1.1 mrg * 56 1.1 mrg * Returns: 57 1.1 mrg * `true` if errors occurred 58 1.1 mrg * References: 59 1.1 mrg * C99 7.19.6.1 60 1.1 mrg * https://www.cplusplus.com/reference/cstdio/printf/ 61 1.1 mrg */ 62 1.1 mrg bool checkPrintfFormat(ref const Loc loc, scope const char[] format, scope Expression[] args, bool isVa_list) 63 1.1 mrg { 64 1.1 mrg //printf("checkPrintFormat('%.*s')\n", cast(int)format.length, format.ptr); 65 1.1 mrg size_t n, gnu_m_count; // index in args / number of Format.GNU_m 66 1.1 mrg for (size_t i = 0; i < format.length;) 67 1.1 mrg { 68 1.1 mrg if (format[i] != '%') 69 1.1 mrg { 70 1.1 mrg ++i; 71 1.1 mrg continue; 72 1.1 mrg } 73 1.1 mrg bool widthStar; 74 1.1 mrg bool precisionStar; 75 1.1 mrg size_t j = i; 76 1.1 mrg const fmt = parsePrintfFormatSpecifier(format, j, widthStar, precisionStar); 77 1.1 mrg const slice = format[i .. j]; 78 1.1 mrg i = j; 79 1.1 mrg 80 1.1 mrg if (fmt == Format.percent) 81 1.1 mrg continue; // "%%", no arguments 82 1.1 mrg 83 1.1 mrg if (isVa_list) 84 1.1 mrg { 85 1.1 mrg // format check only 86 1.1 mrg if (fmt == Format.error) 87 1.1 mrg deprecation(loc, "format specifier `\"%.*s\"` is invalid", cast(int)slice.length, slice.ptr); 88 1.1 mrg continue; 89 1.1 mrg } 90 1.1 mrg 91 1.1 mrg if (fmt == Format.GNU_m) 92 1.1 mrg ++gnu_m_count; 93 1.1 mrg 94 1.1 mrg Expression getNextArg(ref bool skip) 95 1.1 mrg { 96 1.1 mrg if (n == args.length) 97 1.1 mrg { 98 1.1 mrg if (args.length < (n + 1) - gnu_m_count) 99 1.1 mrg deprecation(loc, "more format specifiers than %d arguments", cast(int)n); 100 1.1 mrg else 101 1.1 mrg skip = true; 102 1.1 mrg return null; 103 1.1 mrg } 104 1.1 mrg return args[n++]; 105 1.1 mrg } 106 1.1 mrg 107 1.1 mrg void errorMsg(const char* prefix, Expression arg, const char* texpect, Type tactual) 108 1.1 mrg { 109 1.1 mrg deprecation(arg.loc, "%sargument `%s` for format specification `\"%.*s\"` must be `%s`, not `%s`", 110 1.1 mrg prefix ? prefix : "", arg.toChars(), cast(int)slice.length, slice.ptr, texpect, tactual.toChars()); 111 1.1 mrg } 112 1.1 mrg 113 1.1 mrg if (widthStar) 114 1.1 mrg { 115 1.1 mrg bool skip; 116 1.1 mrg auto e = getNextArg(skip); 117 1.1 mrg if (skip) 118 1.1 mrg continue; 119 1.1 mrg if (!e) 120 1.1 mrg return true; 121 1.1 mrg auto t = e.type.toBasetype(); 122 1.1 mrg if (t.ty != Tint32 && t.ty != Tuns32) 123 1.1 mrg errorMsg("width ", e, "int", t); 124 1.1 mrg } 125 1.1 mrg 126 1.1 mrg if (precisionStar) 127 1.1 mrg { 128 1.1 mrg bool skip; 129 1.1 mrg auto e = getNextArg(skip); 130 1.1 mrg if (skip) 131 1.1 mrg continue; 132 1.1 mrg if (!e) 133 1.1 mrg return true; 134 1.1 mrg auto t = e.type.toBasetype(); 135 1.1 mrg if (t.ty != Tint32 && t.ty != Tuns32) 136 1.1 mrg errorMsg("precision ", e, "int", t); 137 1.1 mrg } 138 1.1 mrg 139 1.1 mrg bool skip; 140 1.1 mrg auto e = getNextArg(skip); 141 1.1 mrg if (skip) 142 1.1 mrg continue; 143 1.1 mrg if (!e) 144 1.1 mrg return true; 145 1.1 mrg auto t = e.type.toBasetype(); 146 1.1 mrg auto tnext = t.nextOf(); 147 1.1 mrg const c_longsize = target.c.longsize; 148 1.1 mrg const ptrsize = target.ptrsize; 149 1.1 mrg 150 1.1 mrg // Types which are promoted to int are allowed. 151 1.1 mrg // Spec: C99 6.5.2.2.7 152 1.1 mrg final switch (fmt) 153 1.1 mrg { 154 1.1 mrg case Format.u: // unsigned int 155 1.1 mrg case Format.d: // int 156 1.1 mrg if (t.ty != Tint32 && t.ty != Tuns32) 157 1.1 mrg errorMsg(null, e, fmt == Format.u ? "uint" : "int", t); 158 1.1 mrg break; 159 1.1 mrg 160 1.1 mrg case Format.hhu: // unsigned char 161 1.1 mrg case Format.hhd: // signed char 162 1.1 mrg if (t.ty != Tint32 && t.ty != Tuns32 && t.ty != Tint8 && t.ty != Tuns8) 163 1.1 mrg errorMsg(null, e, fmt == Format.hhu ? "ubyte" : "byte", t); 164 1.1 mrg break; 165 1.1 mrg 166 1.1 mrg case Format.hu: // unsigned short int 167 1.1 mrg case Format.hd: // short int 168 1.1 mrg if (t.ty != Tint32 && t.ty != Tuns32 && t.ty != Tint16 && t.ty != Tuns16) 169 1.1 mrg errorMsg(null, e, fmt == Format.hu ? "ushort" : "short", t); 170 1.1 mrg break; 171 1.1 mrg 172 1.1 mrg case Format.lu: // unsigned long int 173 1.1 mrg case Format.ld: // long int 174 1.1 mrg if (!(t.isintegral() && t.size() == c_longsize)) 175 1.1 mrg { 176 1.1 mrg if (fmt == Format.lu) 177 1.1 mrg errorMsg(null, e, (c_longsize == 4 ? "uint" : "ulong"), t); 178 1.1 mrg else 179 1.1 mrg errorMsg(null, e, (c_longsize == 4 ? "int" : "long"), t); 180 1.1 mrg } 181 1.1 mrg break; 182 1.1 mrg 183 1.1 mrg case Format.llu: // unsigned long long int 184 1.1 mrg case Format.lld: // long long int 185 1.1 mrg if (t.ty != Tint64 && t.ty != Tuns64) 186 1.1 mrg errorMsg(null, e, fmt == Format.llu ? "ulong" : "long", t); 187 1.1 mrg break; 188 1.1 mrg 189 1.1 mrg case Format.ju: // uintmax_t 190 1.1 mrg case Format.jd: // intmax_t 191 1.1 mrg if (t.ty != Tint64 && t.ty != Tuns64) 192 1.1 mrg { 193 1.1 mrg if (fmt == Format.ju) 194 1.1 mrg errorMsg(null, e, "core.stdc.stdint.uintmax_t", t); 195 1.1 mrg else 196 1.1 mrg errorMsg(null, e, "core.stdc.stdint.intmax_t", t); 197 1.1 mrg } 198 1.1 mrg break; 199 1.1 mrg 200 1.1 mrg case Format.zd: // size_t 201 1.1 mrg if (!(t.isintegral() && t.size() == ptrsize)) 202 1.1 mrg errorMsg(null, e, "size_t", t); 203 1.1 mrg break; 204 1.1 mrg 205 1.1 mrg case Format.td: // ptrdiff_t 206 1.1 mrg if (!(t.isintegral() && t.size() == ptrsize)) 207 1.1 mrg errorMsg(null, e, "ptrdiff_t", t); 208 1.1 mrg break; 209 1.1 mrg 210 1.1 mrg case Format.GNU_a: // Format.GNU_a is only for scanf 211 1.1 mrg case Format.lg: 212 1.1 mrg case Format.g: // double 213 1.1 mrg if (t.ty != Tfloat64 && t.ty != Timaginary64) 214 1.1 mrg errorMsg(null, e, "double", t); 215 1.1 mrg break; 216 1.1 mrg 217 1.1 mrg case Format.Lg: // long double 218 1.1 mrg if (t.ty != Tfloat80 && t.ty != Timaginary80) 219 1.1 mrg errorMsg(null, e, "real", t); 220 1.1 mrg break; 221 1.1 mrg 222 1.1 mrg case Format.p: // pointer 223 1.1 mrg if (t.ty != Tpointer && t.ty != Tnull && t.ty != Tclass && t.ty != Tdelegate && t.ty != Taarray) 224 1.1 mrg errorMsg(null, e, "void*", t); 225 1.1 mrg break; 226 1.1 mrg 227 1.1 mrg case Format.n: // pointer to int 228 1.1 mrg if (!(t.ty == Tpointer && tnext.ty == Tint32)) 229 1.1 mrg errorMsg(null, e, "int*", t); 230 1.1 mrg break; 231 1.1 mrg 232 1.1 mrg case Format.ln: // pointer to long int 233 1.1 mrg if (!(t.ty == Tpointer && tnext.isintegral() && tnext.size() == c_longsize)) 234 1.1 mrg errorMsg(null, e, (c_longsize == 4 ? "int*" : "long*"), t); 235 1.1 mrg break; 236 1.1 mrg 237 1.1 mrg case Format.lln: // pointer to long long int 238 1.1 mrg if (!(t.ty == Tpointer && tnext.ty == Tint64)) 239 1.1 mrg errorMsg(null, e, "long*", t); 240 1.1 mrg break; 241 1.1 mrg 242 1.1 mrg case Format.hn: // pointer to short 243 1.1 mrg if (!(t.ty == Tpointer && tnext.ty == Tint16)) 244 1.1 mrg errorMsg(null, e, "short*", t); 245 1.1 mrg break; 246 1.1 mrg 247 1.1 mrg case Format.hhn: // pointer to signed char 248 1.1 mrg if (!(t.ty == Tpointer && tnext.ty == Tint16)) 249 1.1 mrg errorMsg(null, e, "byte*", t); 250 1.1 mrg break; 251 1.1 mrg 252 1.1 mrg case Format.jn: // pointer to intmax_t 253 1.1 mrg if (!(t.ty == Tpointer && tnext.ty == Tint64)) 254 1.1 mrg errorMsg(null, e, "core.stdc.stdint.intmax_t*", t); 255 1.1 mrg break; 256 1.1 mrg 257 1.1 mrg case Format.zn: // pointer to size_t 258 1.1 mrg if (!(t.ty == Tpointer && tnext.isintegral() && tnext.isunsigned() && tnext.size() == ptrsize)) 259 1.1 mrg errorMsg(null, e, "size_t*", t); 260 1.1 mrg break; 261 1.1 mrg 262 1.1 mrg case Format.tn: // pointer to ptrdiff_t 263 1.1 mrg if (!(t.ty == Tpointer && tnext.isintegral() && !tnext.isunsigned() && tnext.size() == ptrsize)) 264 1.1 mrg errorMsg(null, e, "ptrdiff_t*", t); 265 1.1 mrg break; 266 1.1 mrg 267 1.1 mrg case Format.c: // char 268 1.1 mrg if (t.ty != Tint32 && t.ty != Tuns32) 269 1.1 mrg errorMsg(null, e, "char", t); 270 1.1 mrg break; 271 1.1 mrg 272 1.1 mrg case Format.lc: // wint_t 273 1.1 mrg if (t.ty != Tint32 && t.ty != Tuns32) 274 1.1 mrg errorMsg(null, e, "wchar_t", t); 275 1.1 mrg break; 276 1.1 mrg 277 1.1 mrg case Format.s: // pointer to char string 278 1.1 mrg if (!(t.ty == Tpointer && (tnext.ty == Tchar || tnext.ty == Tint8 || tnext.ty == Tuns8))) 279 1.1 mrg errorMsg(null, e, "char*", t); 280 1.1 mrg break; 281 1.1 mrg 282 1.1 mrg case Format.ls: // pointer to wchar_t string 283 1.1 mrg if (!(t.ty == Tpointer && tnext.ty.isSomeChar && tnext.size() == target.c.wchar_tsize)) 284 1.1 mrg errorMsg(null, e, "wchar_t*", t); 285 1.1 mrg break; 286 1.1 mrg 287 1.1 mrg case Format.error: 288 1.1 mrg deprecation(loc, "format specifier `\"%.*s\"` is invalid", cast(int)slice.length, slice.ptr); 289 1.1 mrg break; 290 1.1 mrg 291 1.1 mrg case Format.GNU_m: 292 1.1 mrg break; // not assert(0) because it may go through it if there are extra arguments 293 1.1 mrg 294 1.1 mrg case Format.percent: 295 1.1 mrg assert(0); 296 1.1 mrg } 297 1.1 mrg } 298 1.1 mrg return false; 299 1.1 mrg } 300 1.1 mrg 301 1.1 mrg /****************************************** 302 1.1 mrg * Check that arguments to a scanf format string are compatible 303 1.1 mrg * with that string. Issue errors for incompatibilities. 304 1.1 mrg * 305 1.1 mrg * Follows the C99 specification for scanf. 306 1.1 mrg * 307 1.1 mrg * Takes a generous, rather than strict, view of compatiblity. 308 1.1 mrg * For example, an unsigned value can be formatted with a signed specifier. 309 1.1 mrg * 310 1.1 mrg * Diagnosed incompatibilities are: 311 1.1 mrg * 312 1.1 mrg * 1. incompatible sizes which will cause argument misalignment 313 1.1 mrg * 2. deferencing arguments that are not pointers 314 1.1 mrg * 3. insufficient number of arguments 315 1.1 mrg * 4. struct arguments 316 1.1 mrg * 5. array and slice arguments 317 1.1 mrg * 6. non-standard formats 318 1.1 mrg * 7. undefined behavior per C99 319 1.1 mrg * 320 1.1 mrg * Per the C Standard, extra arguments are ignored. 321 1.1 mrg * 322 1.1 mrg * No attempt is made to fix the arguments or the format string. 323 1.1 mrg * 324 1.1 mrg * Params: 325 1.1 mrg * loc = location for error messages 326 1.1 mrg * format = format string 327 1.1 mrg * args = arguments to match with format string 328 1.1 mrg * isVa_list = if a "v" function (format check only) 329 1.1 mrg * 330 1.1 mrg * Returns: 331 1.1 mrg * `true` if errors occurred 332 1.1 mrg * References: 333 1.1 mrg * C99 7.19.6.2 334 1.1 mrg * https://www.cplusplus.com/reference/cstdio/scanf/ 335 1.1 mrg */ 336 1.1 mrg bool checkScanfFormat(ref const Loc loc, scope const char[] format, scope Expression[] args, bool isVa_list) 337 1.1 mrg { 338 1.1 mrg size_t n = 0; 339 1.1 mrg for (size_t i = 0; i < format.length;) 340 1.1 mrg { 341 1.1 mrg if (format[i] != '%') 342 1.1 mrg { 343 1.1 mrg ++i; 344 1.1 mrg continue; 345 1.1 mrg } 346 1.1 mrg bool asterisk; 347 1.1 mrg size_t j = i; 348 1.1 mrg const fmt = parseScanfFormatSpecifier(format, j, asterisk); 349 1.1 mrg const slice = format[i .. j]; 350 1.1 mrg i = j; 351 1.1 mrg 352 1.1 mrg if (fmt == Format.percent || asterisk) 353 1.1 mrg continue; // "%%", "%*": no arguments 354 1.1 mrg 355 1.1 mrg if (isVa_list) 356 1.1 mrg { 357 1.1 mrg // format check only 358 1.1 mrg if (fmt == Format.error) 359 1.1 mrg deprecation(loc, "format specifier `\"%.*s\"` is invalid", cast(int)slice.length, slice.ptr); 360 1.1 mrg continue; 361 1.1 mrg } 362 1.1 mrg 363 1.1 mrg Expression getNextArg() 364 1.1 mrg { 365 1.1 mrg if (n == args.length) 366 1.1 mrg { 367 1.1 mrg if (!asterisk) 368 1.1 mrg deprecation(loc, "more format specifiers than %d arguments", cast(int)n); 369 1.1 mrg return null; 370 1.1 mrg } 371 1.1 mrg return args[n++]; 372 1.1 mrg } 373 1.1 mrg 374 1.1 mrg void errorMsg(const char* prefix, Expression arg, const char* texpect, Type tactual) 375 1.1 mrg { 376 1.1 mrg deprecation(arg.loc, "%sargument `%s` for format specification `\"%.*s\"` must be `%s`, not `%s`", 377 1.1 mrg prefix ? prefix : "", arg.toChars(), cast(int)slice.length, slice.ptr, texpect, tactual.toChars()); 378 1.1 mrg } 379 1.1 mrg 380 1.1 mrg auto e = getNextArg(); 381 1.1 mrg if (!e) 382 1.1 mrg return true; 383 1.1 mrg 384 1.1 mrg auto t = e.type.toBasetype(); 385 1.1 mrg auto tnext = t.nextOf(); 386 1.1 mrg const c_longsize = target.c.longsize; 387 1.1 mrg const ptrsize = target.ptrsize; 388 1.1 mrg 389 1.1 mrg final switch (fmt) 390 1.1 mrg { 391 1.1 mrg case Format.n: 392 1.1 mrg case Format.d: // pointer to int 393 1.1 mrg if (!(t.ty == Tpointer && tnext.ty == Tint32)) 394 1.1 mrg errorMsg(null, e, "int*", t); 395 1.1 mrg break; 396 1.1 mrg 397 1.1 mrg case Format.hhn: 398 1.1 mrg case Format.hhd: // pointer to signed char 399 1.1 mrg if (!(t.ty == Tpointer && tnext.ty == Tint16)) 400 1.1 mrg errorMsg(null, e, "byte*", t); 401 1.1 mrg break; 402 1.1 mrg 403 1.1 mrg case Format.hn: 404 1.1 mrg case Format.hd: // pointer to short 405 1.1 mrg if (!(t.ty == Tpointer && tnext.ty == Tint16)) 406 1.1 mrg errorMsg(null, e, "short*", t); 407 1.1 mrg break; 408 1.1 mrg 409 1.1 mrg case Format.ln: 410 1.1 mrg case Format.ld: // pointer to long int 411 1.1 mrg if (!(t.ty == Tpointer && tnext.isintegral() && !tnext.isunsigned() && tnext.size() == c_longsize)) 412 1.1 mrg errorMsg(null, e, (c_longsize == 4 ? "int*" : "long*"), t); 413 1.1 mrg break; 414 1.1 mrg 415 1.1 mrg case Format.lln: 416 1.1 mrg case Format.lld: // pointer to long long int 417 1.1 mrg if (!(t.ty == Tpointer && tnext.ty == Tint64)) 418 1.1 mrg errorMsg(null, e, "long*", t); 419 1.1 mrg break; 420 1.1 mrg 421 1.1 mrg case Format.jn: 422 1.1 mrg case Format.jd: // pointer to intmax_t 423 1.1 mrg if (!(t.ty == Tpointer && tnext.ty == Tint64)) 424 1.1 mrg errorMsg(null, e, "core.stdc.stdint.intmax_t*", t); 425 1.1 mrg break; 426 1.1 mrg 427 1.1 mrg case Format.zn: 428 1.1 mrg case Format.zd: // pointer to size_t 429 1.1 mrg if (!(t.ty == Tpointer && tnext.isintegral() && tnext.isunsigned() && tnext.size() == ptrsize)) 430 1.1 mrg errorMsg(null, e, "size_t*", t); 431 1.1 mrg break; 432 1.1 mrg 433 1.1 mrg case Format.tn: 434 1.1 mrg case Format.td: // pointer to ptrdiff_t 435 1.1 mrg if (!(t.ty == Tpointer && tnext.isintegral() && !tnext.isunsigned() && tnext.size() == ptrsize)) 436 1.1 mrg errorMsg(null, e, "ptrdiff_t*", t); 437 1.1 mrg break; 438 1.1 mrg 439 1.1 mrg case Format.u: // pointer to unsigned int 440 1.1 mrg if (!(t.ty == Tpointer && tnext.ty == Tuns32)) 441 1.1 mrg errorMsg(null, e, "uint*", t); 442 1.1 mrg break; 443 1.1 mrg 444 1.1 mrg case Format.hhu: // pointer to unsigned char 445 1.1 mrg if (!(t.ty == Tpointer && tnext.ty == Tuns8)) 446 1.1 mrg errorMsg(null, e, "ubyte*", t); 447 1.1 mrg break; 448 1.1 mrg 449 1.1 mrg case Format.hu: // pointer to unsigned short int 450 1.1 mrg if (!(t.ty == Tpointer && tnext.ty == Tuns16)) 451 1.1 mrg errorMsg(null, e, "ushort*", t); 452 1.1 mrg break; 453 1.1 mrg 454 1.1 mrg case Format.lu: // pointer to unsigned long int 455 1.1 mrg if (!(t.ty == Tpointer && tnext.isintegral() && tnext.isunsigned() && tnext.size() == c_longsize)) 456 1.1 mrg errorMsg(null, e, (c_longsize == 4 ? "uint*" : "ulong*"), t); 457 1.1 mrg break; 458 1.1 mrg 459 1.1 mrg case Format.llu: // pointer to unsigned long long int 460 1.1 mrg if (!(t.ty == Tpointer && tnext.ty == Tuns64)) 461 1.1 mrg errorMsg(null, e, "ulong*", t); 462 1.1 mrg break; 463 1.1 mrg 464 1.1 mrg case Format.ju: // pointer to uintmax_t 465 1.1 mrg if (!(t.ty == Tpointer && tnext.ty == Tuns64)) 466 1.1 mrg errorMsg(null, e, "core.stdc.stdint.uintmax_t*", t); 467 1.1 mrg break; 468 1.1 mrg 469 1.1 mrg case Format.g: // pointer to float 470 1.1 mrg if (!(t.ty == Tpointer && tnext.ty == Tfloat32)) 471 1.1 mrg errorMsg(null, e, "float*", t); 472 1.1 mrg break; 473 1.1 mrg 474 1.1 mrg case Format.lg: // pointer to double 475 1.1 mrg if (!(t.ty == Tpointer && tnext.ty == Tfloat64)) 476 1.1 mrg errorMsg(null, e, "double*", t); 477 1.1 mrg break; 478 1.1 mrg 479 1.1 mrg case Format.Lg: // pointer to long double 480 1.1 mrg if (!(t.ty == Tpointer && tnext.ty == Tfloat80)) 481 1.1 mrg errorMsg(null, e, "real*", t); 482 1.1 mrg break; 483 1.1 mrg 484 1.1 mrg case Format.GNU_a: 485 1.1 mrg case Format.GNU_m: 486 1.1 mrg case Format.c: 487 1.1 mrg case Format.s: // pointer to char string 488 1.1 mrg if (!(t.ty == Tpointer && (tnext.ty == Tchar || tnext.ty == Tint8 || tnext.ty == Tuns8))) 489 1.1 mrg errorMsg(null, e, "char*", t); 490 1.1 mrg break; 491 1.1 mrg 492 1.1 mrg case Format.lc: 493 1.1 mrg case Format.ls: // pointer to wchar_t string 494 1.1 mrg if (!(t.ty == Tpointer && tnext.ty.isSomeChar && tnext.size() == target.c.wchar_tsize)) 495 1.1 mrg errorMsg(null, e, "wchar_t*", t); 496 1.1 mrg break; 497 1.1 mrg 498 1.1 mrg case Format.p: // double pointer 499 1.1 mrg if (!(t.ty == Tpointer && tnext.ty == Tpointer)) 500 1.1 mrg errorMsg(null, e, "void**", t); 501 1.1 mrg break; 502 1.1 mrg 503 1.1 mrg case Format.error: 504 1.1 mrg deprecation(loc, "format specifier `\"%.*s\"` is invalid", cast(int)slice.length, slice.ptr); 505 1.1 mrg break; 506 1.1 mrg 507 1.1 mrg case Format.percent: 508 1.1 mrg assert(0); 509 1.1 mrg } 510 1.1 mrg } 511 1.1 mrg return false; 512 1.1 mrg } 513 1.1 mrg 514 1.1 mrg private: 515 1.1 mrg 516 1.1 mrg /************************************** 517 1.1 mrg * Parse the *format specifier* which is of the form: 518 1.1 mrg * 519 1.1 mrg * `%[*][width][length]specifier` 520 1.1 mrg * 521 1.1 mrg * Params: 522 1.1 mrg * format = format string 523 1.1 mrg * idx = index of `%` of start of format specifier, 524 1.1 mrg * which gets updated to index past the end of it, 525 1.1 mrg * even if `Format.error` is returned 526 1.1 mrg * asterisk = set if there is a `*` sub-specifier 527 1.1 mrg * Returns: 528 1.1 mrg * Format 529 1.1 mrg */ 530 1.1 mrg Format parseScanfFormatSpecifier(scope const char[] format, ref size_t idx, 531 1.1 mrg out bool asterisk) nothrow pure @safe 532 1.1 mrg { 533 1.1 mrg auto i = idx; 534 1.1 mrg assert(format[i] == '%'); 535 1.1 mrg const length = format.length; 536 1.1 mrg 537 1.1 mrg Format error() 538 1.1 mrg { 539 1.1 mrg idx = i; 540 1.1 mrg return Format.error; 541 1.1 mrg } 542 1.1 mrg 543 1.1 mrg ++i; 544 1.1 mrg if (i == length) 545 1.1 mrg return error(); 546 1.1 mrg 547 1.1 mrg if (format[i] == '%') 548 1.1 mrg { 549 1.1 mrg idx = i + 1; 550 1.1 mrg return Format.percent; 551 1.1 mrg } 552 1.1 mrg 553 1.1 mrg // * sub-specifier 554 1.1 mrg if (format[i] == '*') 555 1.1 mrg { 556 1.1 mrg ++i; 557 1.1 mrg if (i == length) 558 1.1 mrg return error(); 559 1.1 mrg asterisk = true; 560 1.1 mrg } 561 1.1 mrg 562 1.1 mrg // fieldWidth 563 1.1 mrg while (isdigit(format[i])) 564 1.1 mrg { 565 1.1 mrg i++; 566 1.1 mrg if (i == length) 567 1.1 mrg return error(); 568 1.1 mrg } 569 1.1 mrg 570 1.1 mrg /* Read the scanset 571 1.1 mrg * A scanset can be anything, so we just check that it is paired 572 1.1 mrg */ 573 1.1 mrg if (format[i] == '[') 574 1.1 mrg { 575 1.1 mrg while (i < length) 576 1.1 mrg { 577 1.1 mrg if (format[i] == ']') 578 1.1 mrg break; 579 1.1 mrg ++i; 580 1.1 mrg } 581 1.1 mrg 582 1.1 mrg // no `]` found 583 1.1 mrg if (i == length) 584 1.1 mrg return error(); 585 1.1 mrg 586 1.1 mrg ++i; 587 1.1 mrg // no specifier after `]` 588 1.1 mrg // it could be mixed with the one above, but then idx won't have the right index 589 1.1 mrg if (i == length) 590 1.1 mrg return error(); 591 1.1 mrg } 592 1.1 mrg 593 1.1 mrg /* Read the specifier 594 1.1 mrg */ 595 1.1 mrg char genSpec; 596 1.1 mrg Format specifier = parseGenericFormatSpecifier(format, i, genSpec); 597 1.1 mrg if (specifier == Format.error) 598 1.1 mrg return error(); 599 1.1 mrg 600 1.1 mrg idx = i; 601 1.1 mrg return specifier; // success 602 1.1 mrg } 603 1.1 mrg 604 1.1 mrg /************************************** 605 1.1 mrg * Parse the *format specifier* which is of the form: 606 1.1 mrg * 607 1.1 mrg * `%[flags][field width][.precision][length modifier]specifier` 608 1.1 mrg * 609 1.1 mrg * Params: 610 1.1 mrg * format = format string 611 1.1 mrg * idx = index of `%` of start of format specifier, 612 1.1 mrg * which gets updated to index past the end of it, 613 1.1 mrg * even if `Format.error` is returned 614 1.1 mrg * widthStar = set if * for width 615 1.1 mrg * precisionStar = set if * for precision 616 1.1 mrg * Returns: 617 1.1 mrg * Format 618 1.1 mrg */ 619 1.1 mrg Format parsePrintfFormatSpecifier(scope const char[] format, ref size_t idx, 620 1.1 mrg out bool widthStar, out bool precisionStar) nothrow pure @safe 621 1.1 mrg { 622 1.1 mrg auto i = idx; 623 1.1 mrg assert(format[i] == '%'); 624 1.1 mrg const length = format.length; 625 1.1 mrg bool hash; 626 1.1 mrg bool zero; 627 1.1 mrg bool flags; 628 1.1 mrg bool width; 629 1.1 mrg bool precision; 630 1.1 mrg 631 1.1 mrg Format error() 632 1.1 mrg { 633 1.1 mrg idx = i; 634 1.1 mrg return Format.error; 635 1.1 mrg } 636 1.1 mrg 637 1.1 mrg ++i; 638 1.1 mrg if (i == length) 639 1.1 mrg return error(); 640 1.1 mrg 641 1.1 mrg if (format[i] == '%') 642 1.1 mrg { 643 1.1 mrg idx = i + 1; 644 1.1 mrg return Format.percent; 645 1.1 mrg } 646 1.1 mrg 647 1.1 mrg /* Read the `flags` 648 1.1 mrg */ 649 1.1 mrg while (1) 650 1.1 mrg { 651 1.1 mrg const c = format[i]; 652 1.1 mrg if (c == '-' || 653 1.1 mrg c == '+' || 654 1.1 mrg c == ' ') 655 1.1 mrg { 656 1.1 mrg flags = true; 657 1.1 mrg } 658 1.1 mrg else if (c == '#') 659 1.1 mrg { 660 1.1 mrg hash = true; 661 1.1 mrg } 662 1.1 mrg else if (c == '0') 663 1.1 mrg { 664 1.1 mrg zero = true; 665 1.1 mrg } 666 1.1 mrg else 667 1.1 mrg break; 668 1.1 mrg ++i; 669 1.1 mrg if (i == length) 670 1.1 mrg return error(); 671 1.1 mrg } 672 1.1 mrg 673 1.1 mrg /* Read the `field width` 674 1.1 mrg */ 675 1.1 mrg { 676 1.1 mrg const c = format[i]; 677 1.1 mrg if (c == '*') 678 1.1 mrg { 679 1.1 mrg width = true; 680 1.1 mrg widthStar = true; 681 1.1 mrg ++i; 682 1.1 mrg if (i == length) 683 1.1 mrg return error(); 684 1.1 mrg } 685 1.1 mrg else if ('1' <= c && c <= '9') 686 1.1 mrg { 687 1.1 mrg width = true; 688 1.1 mrg ++i; 689 1.1 mrg if (i == length) 690 1.1 mrg return error(); 691 1.1 mrg while ('0' <= format[i] && format[i] <= '9') 692 1.1 mrg { 693 1.1 mrg ++i; 694 1.1 mrg if (i == length) 695 1.1 mrg return error(); 696 1.1 mrg } 697 1.1 mrg } 698 1.1 mrg } 699 1.1 mrg 700 1.1 mrg /* Read the `precision` 701 1.1 mrg */ 702 1.1 mrg if (format[i] == '.') 703 1.1 mrg { 704 1.1 mrg precision = true; 705 1.1 mrg ++i; 706 1.1 mrg if (i == length) 707 1.1 mrg return error(); 708 1.1 mrg const c = format[i]; 709 1.1 mrg if (c == '*') 710 1.1 mrg { 711 1.1 mrg precisionStar = true; 712 1.1 mrg ++i; 713 1.1 mrg if (i == length) 714 1.1 mrg return error(); 715 1.1 mrg } 716 1.1 mrg else if ('0' <= c && c <= '9') 717 1.1 mrg { 718 1.1 mrg ++i; 719 1.1 mrg if (i == length) 720 1.1 mrg return error(); 721 1.1 mrg while ('0' <= format[i] && format[i] <= '9') 722 1.1 mrg { 723 1.1 mrg ++i; 724 1.1 mrg if (i == length) 725 1.1 mrg return error(); 726 1.1 mrg } 727 1.1 mrg } 728 1.1 mrg } 729 1.1 mrg 730 1.1 mrg /* Read the specifier 731 1.1 mrg */ 732 1.1 mrg char genSpec; 733 1.1 mrg Format specifier = parseGenericFormatSpecifier(format, i, genSpec); 734 1.1 mrg if (specifier == Format.error) 735 1.1 mrg return error(); 736 1.1 mrg 737 1.1 mrg switch (genSpec) 738 1.1 mrg { 739 1.1 mrg case 'c': 740 1.1 mrg case 's': 741 1.1 mrg if (hash || zero) 742 1.1 mrg return error(); 743 1.1 mrg break; 744 1.1 mrg 745 1.1 mrg case 'd': 746 1.1 mrg case 'i': 747 1.1 mrg if (hash) 748 1.1 mrg return error(); 749 1.1 mrg break; 750 1.1 mrg 751 1.1 mrg case 'n': 752 1.1 mrg if (hash || zero || precision || width || flags) 753 1.1 mrg return error(); 754 1.1 mrg break; 755 1.1 mrg 756 1.1 mrg default: 757 1.1 mrg break; 758 1.1 mrg } 759 1.1 mrg 760 1.1 mrg idx = i; 761 1.1 mrg return specifier; // success 762 1.1 mrg } 763 1.1 mrg 764 1.1 mrg /* Different kinds of formatting specifications, variations we don't 765 1.1 mrg care about are merged. (Like we don't care about the difference between 766 1.1 mrg f, e, g, a, etc.) 767 1.1 mrg 768 1.1 mrg For `scanf`, every format is a pointer. 769 1.1 mrg */ 770 1.1 mrg enum Format 771 1.1 mrg { 772 1.1 mrg d, // int 773 1.1 mrg hhd, // signed char 774 1.1 mrg hd, // short int 775 1.1 mrg ld, // long int 776 1.1 mrg lld, // long long int 777 1.1 mrg jd, // intmax_t 778 1.1 mrg zd, // size_t 779 1.1 mrg td, // ptrdiff_t 780 1.1 mrg u, // unsigned int 781 1.1 mrg hhu, // unsigned char 782 1.1 mrg hu, // unsigned short int 783 1.1 mrg lu, // unsigned long int 784 1.1 mrg llu, // unsigned long long int 785 1.1 mrg ju, // uintmax_t 786 1.1 mrg g, // float (scanf) / double (printf) 787 1.1 mrg lg, // double (scanf) 788 1.1 mrg Lg, // long double (both) 789 1.1 mrg s, // char string (both) 790 1.1 mrg ls, // wchar_t string (both) 791 1.1 mrg c, // char (printf) 792 1.1 mrg lc, // wint_t (printf) 793 1.1 mrg p, // pointer 794 1.1 mrg n, // pointer to int 795 1.1 mrg hhn, // pointer to signed char 796 1.1 mrg hn, // pointer to short 797 1.1 mrg ln, // pointer to long int 798 1.1 mrg lln, // pointer to long long int 799 1.1 mrg jn, // pointer to intmax_t 800 1.1 mrg zn, // pointer to size_t 801 1.1 mrg tn, // pointer to ptrdiff_t 802 1.1 mrg GNU_a, // GNU ext. : address to a string with no maximum size (scanf) 803 1.1 mrg GNU_m, // GNU ext. : string corresponding to the error code in errno (printf) / length modifier (scanf) 804 1.1 mrg percent, // %% (i.e. no argument) 805 1.1 mrg error, // invalid format specification 806 1.1 mrg } 807 1.1 mrg 808 1.1 mrg /************************************** 809 1.1 mrg * Parse the *length specifier* and the *specifier* of the following form: 810 1.1 mrg * `[length]specifier` 811 1.1 mrg * 812 1.1 mrg * Params: 813 1.1 mrg * format = format string 814 1.1 mrg * idx = index of of start of format specifier, 815 1.1 mrg * which gets updated to index past the end of it, 816 1.1 mrg * even if `Format.error` is returned 817 1.1 mrg * genSpecifier = Generic specifier. For instance, it will be set to `d` if the 818 1.1 mrg * format is `hdd`. 819 1.1 mrg * Returns: 820 1.1 mrg * Format 821 1.1 mrg */ 822 1.1 mrg Format parseGenericFormatSpecifier(scope const char[] format, 823 1.1 mrg ref size_t idx, out char genSpecifier, bool useGNUExts = 824 1.1 mrg findCondition(global.versionids, Identifier.idPool("CRuntime_Glibc"))) nothrow pure @trusted 825 1.1 mrg { 826 1.1 mrg const length = format.length; 827 1.1 mrg 828 1.1 mrg /* Read the `length modifier` 829 1.1 mrg */ 830 1.1 mrg const lm = format[idx]; 831 1.1 mrg bool lm1; // if jztL 832 1.1 mrg bool lm2; // if `hh` or `ll` 833 1.1 mrg if (lm == 'j' || 834 1.1 mrg lm == 'z' || 835 1.1 mrg lm == 't' || 836 1.1 mrg lm == 'L') 837 1.1 mrg { 838 1.1 mrg ++idx; 839 1.1 mrg if (idx == length) 840 1.1 mrg return Format.error; 841 1.1 mrg lm1 = true; 842 1.1 mrg } 843 1.1 mrg else if (lm == 'h' || lm == 'l') 844 1.1 mrg { 845 1.1 mrg ++idx; 846 1.1 mrg if (idx == length) 847 1.1 mrg return Format.error; 848 1.1 mrg lm2 = lm == format[idx]; 849 1.1 mrg if (lm2) 850 1.1 mrg { 851 1.1 mrg ++idx; 852 1.1 mrg if (idx == length) 853 1.1 mrg return Format.error; 854 1.1 mrg } 855 1.1 mrg } 856 1.1 mrg 857 1.1 mrg /* Read the `specifier` 858 1.1 mrg */ 859 1.1 mrg Format specifier; 860 1.1 mrg const sc = format[idx]; 861 1.1 mrg genSpecifier = sc; 862 1.1 mrg switch (sc) 863 1.1 mrg { 864 1.1 mrg case 'd': 865 1.1 mrg case 'i': 866 1.1 mrg if (lm == 'L') 867 1.1 mrg specifier = Format.error; 868 1.1 mrg else 869 1.1 mrg specifier = lm == 'h' && lm2 ? Format.hhd : 870 1.1 mrg lm == 'h' ? Format.hd : 871 1.1 mrg lm == 'l' && lm2 ? Format.lld : 872 1.1 mrg lm == 'l' ? Format.ld : 873 1.1 mrg lm == 'j' ? Format.jd : 874 1.1 mrg lm == 'z' ? Format.zd : 875 1.1 mrg lm == 't' ? Format.td : 876 1.1 mrg Format.d; 877 1.1 mrg break; 878 1.1 mrg 879 1.1 mrg case 'u': 880 1.1 mrg case 'o': 881 1.1 mrg case 'x': 882 1.1 mrg case 'X': 883 1.1 mrg if (lm == 'L') 884 1.1 mrg specifier = Format.error; 885 1.1 mrg else 886 1.1 mrg specifier = lm == 'h' && lm2 ? Format.hhu : 887 1.1 mrg lm == 'h' ? Format.hu : 888 1.1 mrg lm == 'l' && lm2 ? Format.llu : 889 1.1 mrg lm == 'l' ? Format.lu : 890 1.1 mrg lm == 'j' ? Format.ju : 891 1.1 mrg lm == 'z' ? Format.zd : 892 1.1 mrg lm == 't' ? Format.td : 893 1.1 mrg Format.u; 894 1.1 mrg break; 895 1.1 mrg 896 1.1 mrg case 'a': 897 1.1 mrg if (useGNUExts) 898 1.1 mrg { 899 1.1 mrg // https://www.gnu.org/software/libc/manual/html_node/Dynamic-String-Input.html 900 1.1 mrg specifier = Format.GNU_a; 901 1.1 mrg break; 902 1.1 mrg } 903 1.1 mrg goto case; 904 1.1 mrg 905 1.1 mrg case 'f': 906 1.1 mrg case 'F': 907 1.1 mrg case 'e': 908 1.1 mrg case 'E': 909 1.1 mrg case 'g': 910 1.1 mrg case 'G': 911 1.1 mrg case 'A': 912 1.1 mrg if (lm == 'L') 913 1.1 mrg specifier = Format.Lg; 914 1.1 mrg else if (lm1 || lm2 || lm == 'h') 915 1.1 mrg specifier = Format.error; 916 1.1 mrg else 917 1.1 mrg specifier = lm == 'l' ? Format.lg : Format.g; 918 1.1 mrg break; 919 1.1 mrg 920 1.1 mrg case 'c': 921 1.1 mrg if (lm1 || lm2 || lm == 'h') 922 1.1 mrg specifier = Format.error; 923 1.1 mrg else 924 1.1 mrg specifier = lm == 'l' ? Format.lc : Format.c; 925 1.1 mrg break; 926 1.1 mrg 927 1.1 mrg case 's': 928 1.1 mrg if (lm1 || lm2 || lm == 'h') 929 1.1 mrg specifier = Format.error; 930 1.1 mrg else 931 1.1 mrg specifier = lm == 'l' ? Format.ls : Format.s; 932 1.1 mrg break; 933 1.1 mrg 934 1.1 mrg case 'p': 935 1.1 mrg if (lm1 || lm2 || lm == 'h' || lm == 'l') 936 1.1 mrg specifier = Format.error; 937 1.1 mrg else 938 1.1 mrg specifier = Format.p; 939 1.1 mrg break; 940 1.1 mrg 941 1.1 mrg case 'n': 942 1.1 mrg if (lm == 'L') 943 1.1 mrg specifier = Format.error; 944 1.1 mrg else 945 1.1 mrg specifier = lm == 'l' && lm2 ? Format.lln : 946 1.1 mrg lm == 'l' ? Format.ln : 947 1.1 mrg lm == 'h' && lm2 ? Format.hhn : 948 1.1 mrg lm == 'h' ? Format.hn : 949 1.1 mrg lm == 'j' ? Format.jn : 950 1.1 mrg lm == 'z' ? Format.zn : 951 1.1 mrg lm == 't' ? Format.tn : 952 1.1 mrg Format.n; 953 1.1 mrg break; 954 1.1 mrg 955 1.1 mrg case 'm': 956 1.1 mrg if (useGNUExts) 957 1.1 mrg { 958 1.1 mrg // https://www.gnu.org/software/libc/manual/html_node/Other-Output-Conversions.html 959 1.1 mrg specifier = Format.GNU_m; 960 1.1 mrg break; 961 1.1 mrg } 962 1.1 mrg goto default; 963 1.1 mrg 964 1.1 mrg default: 965 1.1 mrg specifier = Format.error; 966 1.1 mrg break; 967 1.1 mrg } 968 1.1 mrg 969 1.1 mrg ++idx; 970 1.1 mrg return specifier; // success 971 1.1 mrg } 972 1.1 mrg 973 1.1 mrg unittest 974 1.1 mrg { 975 1.1 mrg /* parseGenericFormatSpecifier 976 1.1 mrg */ 977 1.1 mrg 978 1.1 mrg char genSpecifier; 979 1.1 mrg size_t idx; 980 1.1 mrg 981 1.1 mrg assert(parseGenericFormatSpecifier("hhd", idx, genSpecifier) == Format.hhd); 982 1.1 mrg assert(genSpecifier == 'd'); 983 1.1 mrg 984 1.1 mrg idx = 0; 985 1.1 mrg assert(parseGenericFormatSpecifier("hn", idx, genSpecifier) == Format.hn); 986 1.1 mrg assert(genSpecifier == 'n'); 987 1.1 mrg 988 1.1 mrg idx = 0; 989 1.1 mrg assert(parseGenericFormatSpecifier("ji", idx, genSpecifier) == Format.jd); 990 1.1 mrg assert(genSpecifier == 'i'); 991 1.1 mrg 992 1.1 mrg idx = 0; 993 1.1 mrg assert(parseGenericFormatSpecifier("lu", idx, genSpecifier) == Format.lu); 994 1.1 mrg assert(genSpecifier == 'u'); 995 1.1 mrg 996 1.1 mrg idx = 0; 997 1.1 mrg assert(parseGenericFormatSpecifier("k", idx, genSpecifier) == Format.error); 998 1.1 mrg 999 1.1 mrg /* parsePrintfFormatSpecifier 1000 1.1 mrg */ 1001 1.1 mrg 1002 1.1 mrg bool widthStar; 1003 1.1 mrg bool precisionStar; 1004 1.1 mrg 1005 1.1 mrg // one for each Format 1006 1.1 mrg idx = 0; 1007 1.1 mrg assert(parsePrintfFormatSpecifier("%d", idx, widthStar, precisionStar) == Format.d); 1008 1.1 mrg assert(idx == 2); 1009 1.1 mrg assert(!widthStar && !precisionStar); 1010 1.1 mrg 1011 1.1 mrg idx = 0; 1012 1.1 mrg assert(parsePrintfFormatSpecifier("%ld", idx, widthStar, precisionStar) == Format.ld); 1013 1.1 mrg assert(idx == 3); 1014 1.1 mrg 1015 1.1 mrg idx = 0; 1016 1.1 mrg assert(parsePrintfFormatSpecifier("%lld", idx, widthStar, precisionStar) == Format.lld); 1017 1.1 mrg assert(idx == 4); 1018 1.1 mrg 1019 1.1 mrg idx = 0; 1020 1.1 mrg assert(parsePrintfFormatSpecifier("%jd", idx, widthStar, precisionStar) == Format.jd); 1021 1.1 mrg assert(idx == 3); 1022 1.1 mrg 1023 1.1 mrg idx = 0; 1024 1.1 mrg assert(parsePrintfFormatSpecifier("%zd", idx, widthStar, precisionStar) == Format.zd); 1025 1.1 mrg assert(idx == 3); 1026 1.1 mrg 1027 1.1 mrg idx = 0; 1028 1.1 mrg assert(parsePrintfFormatSpecifier("%td", idx, widthStar, precisionStar) == Format.td); 1029 1.1 mrg assert(idx == 3); 1030 1.1 mrg 1031 1.1 mrg idx = 0; 1032 1.1 mrg assert(parsePrintfFormatSpecifier("%g", idx, widthStar, precisionStar) == Format.g); 1033 1.1 mrg assert(idx == 2); 1034 1.1 mrg 1035 1.1 mrg idx = 0; 1036 1.1 mrg assert(parsePrintfFormatSpecifier("%Lg", idx, widthStar, precisionStar) == Format.Lg); 1037 1.1 mrg assert(idx == 3); 1038 1.1 mrg 1039 1.1 mrg idx = 0; 1040 1.1 mrg assert(parsePrintfFormatSpecifier("%p", idx, widthStar, precisionStar) == Format.p); 1041 1.1 mrg assert(idx == 2); 1042 1.1 mrg 1043 1.1 mrg idx = 0; 1044 1.1 mrg assert(parsePrintfFormatSpecifier("%n", idx, widthStar, precisionStar) == Format.n); 1045 1.1 mrg assert(idx == 2); 1046 1.1 mrg 1047 1.1 mrg idx = 0; 1048 1.1 mrg assert(parsePrintfFormatSpecifier("%ln", idx, widthStar, precisionStar) == Format.ln); 1049 1.1 mrg assert(idx == 3); 1050 1.1 mrg 1051 1.1 mrg idx = 0; 1052 1.1 mrg assert(parsePrintfFormatSpecifier("%lln", idx, widthStar, precisionStar) == Format.lln); 1053 1.1 mrg assert(idx == 4); 1054 1.1 mrg 1055 1.1 mrg idx = 0; 1056 1.1 mrg assert(parsePrintfFormatSpecifier("%hn", idx, widthStar, precisionStar) == Format.hn); 1057 1.1 mrg assert(idx == 3); 1058 1.1 mrg 1059 1.1 mrg idx = 0; 1060 1.1 mrg assert(parsePrintfFormatSpecifier("%hhn", idx, widthStar, precisionStar) == Format.hhn); 1061 1.1 mrg assert(idx == 4); 1062 1.1 mrg 1063 1.1 mrg idx = 0; 1064 1.1 mrg assert(parsePrintfFormatSpecifier("%jn", idx, widthStar, precisionStar) == Format.jn); 1065 1.1 mrg assert(idx == 3); 1066 1.1 mrg 1067 1.1 mrg idx = 0; 1068 1.1 mrg assert(parsePrintfFormatSpecifier("%zn", idx, widthStar, precisionStar) == Format.zn); 1069 1.1 mrg assert(idx == 3); 1070 1.1 mrg 1071 1.1 mrg idx = 0; 1072 1.1 mrg assert(parsePrintfFormatSpecifier("%tn", idx, widthStar, precisionStar) == Format.tn); 1073 1.1 mrg assert(idx == 3); 1074 1.1 mrg 1075 1.1 mrg idx = 0; 1076 1.1 mrg assert(parsePrintfFormatSpecifier("%c", idx, widthStar, precisionStar) == Format.c); 1077 1.1 mrg assert(idx == 2); 1078 1.1 mrg 1079 1.1 mrg idx = 0; 1080 1.1 mrg assert(parsePrintfFormatSpecifier("%lc", idx, widthStar, precisionStar) == Format.lc); 1081 1.1 mrg assert(idx == 3); 1082 1.1 mrg 1083 1.1 mrg idx = 0; 1084 1.1 mrg assert(parsePrintfFormatSpecifier("%s", idx, widthStar, precisionStar) == Format.s); 1085 1.1 mrg assert(idx == 2); 1086 1.1 mrg 1087 1.1 mrg idx = 0; 1088 1.1 mrg assert(parsePrintfFormatSpecifier("%ls", idx, widthStar, precisionStar) == Format.ls); 1089 1.1 mrg assert(idx == 3); 1090 1.1 mrg 1091 1.1 mrg idx = 0; 1092 1.1 mrg assert(parsePrintfFormatSpecifier("%%", idx, widthStar, precisionStar) == Format.percent); 1093 1.1 mrg assert(idx == 2); 1094 1.1 mrg 1095 1.1 mrg // Synonyms 1096 1.1 mrg idx = 0; 1097 1.1 mrg assert(parsePrintfFormatSpecifier("%i", idx, widthStar, precisionStar) == Format.d); 1098 1.1 mrg assert(idx == 2); 1099 1.1 mrg 1100 1.1 mrg idx = 0; 1101 1.1 mrg assert(parsePrintfFormatSpecifier("%u", idx, widthStar, precisionStar) == Format.u); 1102 1.1 mrg assert(idx == 2); 1103 1.1 mrg 1104 1.1 mrg idx = 0; 1105 1.1 mrg assert(parsePrintfFormatSpecifier("%o", idx, widthStar, precisionStar) == Format.u); 1106 1.1 mrg assert(idx == 2); 1107 1.1 mrg 1108 1.1 mrg idx = 0; 1109 1.1 mrg assert(parsePrintfFormatSpecifier("%x", idx, widthStar, precisionStar) == Format.u); 1110 1.1 mrg assert(idx == 2); 1111 1.1 mrg 1112 1.1 mrg idx = 0; 1113 1.1 mrg assert(parsePrintfFormatSpecifier("%X", idx, widthStar, precisionStar) == Format.u); 1114 1.1 mrg assert(idx == 2); 1115 1.1 mrg 1116 1.1 mrg idx = 0; 1117 1.1 mrg assert(parsePrintfFormatSpecifier("%f", idx, widthStar, precisionStar) == Format.g); 1118 1.1 mrg assert(idx == 2); 1119 1.1 mrg 1120 1.1 mrg idx = 0; 1121 1.1 mrg assert(parsePrintfFormatSpecifier("%F", idx, widthStar, precisionStar) == Format.g); 1122 1.1 mrg assert(idx == 2); 1123 1.1 mrg 1124 1.1 mrg idx = 0; 1125 1.1 mrg assert(parsePrintfFormatSpecifier("%G", idx, widthStar, precisionStar) == Format.g); 1126 1.1 mrg assert(idx == 2); 1127 1.1 mrg 1128 1.1 mrg idx = 0; 1129 1.1 mrg Format g = parsePrintfFormatSpecifier("%a", idx, widthStar, precisionStar); 1130 1.1 mrg assert(g == Format.g || g == Format.GNU_a); 1131 1.1 mrg assert(idx == 2); 1132 1.1 mrg 1133 1.1 mrg idx = 0; 1134 1.1 mrg assert(parsePrintfFormatSpecifier("%A", idx, widthStar, precisionStar) == Format.g); 1135 1.1 mrg assert(idx == 2); 1136 1.1 mrg 1137 1.1 mrg idx = 0; 1138 1.1 mrg assert(parsePrintfFormatSpecifier("%lg", idx, widthStar, precisionStar) == Format.lg); 1139 1.1 mrg assert(idx == 3); 1140 1.1 mrg 1141 1.1 mrg // width, precision 1142 1.1 mrg idx = 0; 1143 1.1 mrg assert(parsePrintfFormatSpecifier("%*d", idx, widthStar, precisionStar) == Format.d); 1144 1.1 mrg assert(idx == 3); 1145 1.1 mrg assert(widthStar && !precisionStar); 1146 1.1 mrg 1147 1.1 mrg idx = 0; 1148 1.1 mrg assert(parsePrintfFormatSpecifier("%.*d", idx, widthStar, precisionStar) == Format.d); 1149 1.1 mrg assert(idx == 4); 1150 1.1 mrg assert(!widthStar && precisionStar); 1151 1.1 mrg 1152 1.1 mrg idx = 0; 1153 1.1 mrg assert(parsePrintfFormatSpecifier("%*.*d", idx, widthStar, precisionStar) == Format.d); 1154 1.1 mrg assert(idx == 5); 1155 1.1 mrg assert(widthStar && precisionStar); 1156 1.1 mrg 1157 1.1 mrg // Too short formats 1158 1.1 mrg { 1159 1.1 mrg foreach (s; ["%", "%-", "%+", "% ", "%#", "%0", "%*", "%1", "%19", "%.", "%.*", "%.1", "%.12", 1160 1.1 mrg "%j", "%z", "%t", "%l", "%h", "%ll", "%hh"]) 1161 1.1 mrg { 1162 1.1 mrg idx = 0; 1163 1.1 mrg assert(parsePrintfFormatSpecifier(s, idx, widthStar, precisionStar) == Format.error); 1164 1.1 mrg assert(idx == s.length); 1165 1.1 mrg } 1166 1.1 mrg } 1167 1.1 mrg 1168 1.1 mrg // Undefined format combinations 1169 1.1 mrg { 1170 1.1 mrg foreach (s; ["%#d", "%llg", "%jg", "%zg", "%tg", "%hg", "%hhg", 1171 1.1 mrg "%#c", "%0c", "%jc", "%zc", "%tc", "%Lc", "%hc", "%hhc", "%llc", 1172 1.1 mrg "%#s", "%0s", "%js", "%zs", "%ts", "%Ls", "%hs", "%hhs", "%lls", 1173 1.1 mrg "%jp", "%zp", "%tp", "%Lp", "%hp", "%lp", "%hhp", "%llp", 1174 1.1 mrg "%-n", "%+n", "% n", "%#n", "%0n", "%*n", "%1n", "%19n", "%.n", "%.*n", "%.1n", "%.12n", "%Ln", "%K"]) 1175 1.1 mrg { 1176 1.1 mrg idx = 0; 1177 1.1 mrg assert(parsePrintfFormatSpecifier(s, idx, widthStar, precisionStar) == Format.error); 1178 1.1 mrg assert(idx == s.length); 1179 1.1 mrg } 1180 1.1 mrg } 1181 1.1 mrg 1182 1.1 mrg /* parseScanfFormatSpecifier 1183 1.1 mrg */ 1184 1.1 mrg 1185 1.1 mrg bool asterisk; 1186 1.1 mrg 1187 1.1 mrg // one for each Format 1188 1.1 mrg idx = 0; 1189 1.1 mrg assert(parseScanfFormatSpecifier("%d", idx, asterisk) == Format.d); 1190 1.1 mrg assert(idx == 2); 1191 1.1 mrg assert(!asterisk); 1192 1.1 mrg 1193 1.1 mrg idx = 0; 1194 1.1 mrg assert(parseScanfFormatSpecifier("%hhd", idx, asterisk) == Format.hhd); 1195 1.1 mrg assert(idx == 4); 1196 1.1 mrg 1197 1.1 mrg idx = 0; 1198 1.1 mrg assert(parseScanfFormatSpecifier("%hd", idx, asterisk) == Format.hd); 1199 1.1 mrg assert(idx == 3); 1200 1.1 mrg 1201 1.1 mrg idx = 0; 1202 1.1 mrg assert(parseScanfFormatSpecifier("%ld", idx, asterisk) == Format.ld); 1203 1.1 mrg assert(idx == 3); 1204 1.1 mrg 1205 1.1 mrg idx = 0; 1206 1.1 mrg assert(parseScanfFormatSpecifier("%lld", idx, asterisk) == Format.lld); 1207 1.1 mrg assert(idx == 4); 1208 1.1 mrg 1209 1.1 mrg idx = 0; 1210 1.1 mrg assert(parseScanfFormatSpecifier("%jd", idx, asterisk) == Format.jd); 1211 1.1 mrg assert(idx == 3); 1212 1.1 mrg 1213 1.1 mrg idx = 0; 1214 1.1 mrg assert(parseScanfFormatSpecifier("%zd", idx, asterisk) == Format.zd); 1215 1.1 mrg assert(idx == 3); 1216 1.1 mrg 1217 1.1 mrg idx = 0; 1218 1.1 mrg assert(parseScanfFormatSpecifier("%td", idx, asterisk,) == Format.td); 1219 1.1 mrg assert(idx == 3); 1220 1.1 mrg 1221 1.1 mrg idx = 0; 1222 1.1 mrg assert(parseScanfFormatSpecifier("%u", idx, asterisk) == Format.u); 1223 1.1 mrg assert(idx == 2); 1224 1.1 mrg 1225 1.1 mrg idx = 0; 1226 1.1 mrg assert(parseScanfFormatSpecifier("%hhu", idx, asterisk,) == Format.hhu); 1227 1.1 mrg assert(idx == 4); 1228 1.1 mrg 1229 1.1 mrg idx = 0; 1230 1.1 mrg assert(parseScanfFormatSpecifier("%hu", idx, asterisk) == Format.hu); 1231 1.1 mrg assert(idx == 3); 1232 1.1 mrg 1233 1.1 mrg idx = 0; 1234 1.1 mrg assert(parseScanfFormatSpecifier("%lu", idx, asterisk) == Format.lu); 1235 1.1 mrg assert(idx == 3); 1236 1.1 mrg 1237 1.1 mrg idx = 0; 1238 1.1 mrg assert(parseScanfFormatSpecifier("%llu", idx, asterisk) == Format.llu); 1239 1.1 mrg assert(idx == 4); 1240 1.1 mrg 1241 1.1 mrg idx = 0; 1242 1.1 mrg assert(parseScanfFormatSpecifier("%ju", idx, asterisk) == Format.ju); 1243 1.1 mrg assert(idx == 3); 1244 1.1 mrg 1245 1.1 mrg idx = 0; 1246 1.1 mrg assert(parseScanfFormatSpecifier("%g", idx, asterisk) == Format.g); 1247 1.1 mrg assert(idx == 2); 1248 1.1 mrg 1249 1.1 mrg idx = 0; 1250 1.1 mrg assert(parseScanfFormatSpecifier("%lg", idx, asterisk) == Format.lg); 1251 1.1 mrg assert(idx == 3); 1252 1.1 mrg 1253 1.1 mrg idx = 0; 1254 1.1 mrg assert(parseScanfFormatSpecifier("%Lg", idx, asterisk) == Format.Lg); 1255 1.1 mrg assert(idx == 3); 1256 1.1 mrg 1257 1.1 mrg idx = 0; 1258 1.1 mrg assert(parseScanfFormatSpecifier("%p", idx, asterisk) == Format.p); 1259 1.1 mrg assert(idx == 2); 1260 1.1 mrg 1261 1.1 mrg idx = 0; 1262 1.1 mrg assert(parseScanfFormatSpecifier("%s", idx, asterisk) == Format.s); 1263 1.1 mrg assert(idx == 2); 1264 1.1 mrg 1265 1.1 mrg idx = 0; 1266 1.1 mrg assert(parseScanfFormatSpecifier("%ls", idx, asterisk,) == Format.ls); 1267 1.1 mrg assert(idx == 3); 1268 1.1 mrg 1269 1.1 mrg idx = 0; 1270 1.1 mrg assert(parseScanfFormatSpecifier("%%", idx, asterisk) == Format.percent); 1271 1.1 mrg assert(idx == 2); 1272 1.1 mrg 1273 1.1 mrg // Synonyms 1274 1.1 mrg idx = 0; 1275 1.1 mrg assert(parseScanfFormatSpecifier("%i", idx, asterisk) == Format.d); 1276 1.1 mrg assert(idx == 2); 1277 1.1 mrg 1278 1.1 mrg idx = 0; 1279 1.1 mrg assert(parseScanfFormatSpecifier("%n", idx, asterisk) == Format.n); 1280 1.1 mrg assert(idx == 2); 1281 1.1 mrg 1282 1.1 mrg idx = 0; 1283 1.1 mrg assert(parseScanfFormatSpecifier("%o", idx, asterisk) == Format.u); 1284 1.1 mrg assert(idx == 2); 1285 1.1 mrg 1286 1.1 mrg idx = 0; 1287 1.1 mrg assert(parseScanfFormatSpecifier("%x", idx, asterisk) == Format.u); 1288 1.1 mrg assert(idx == 2); 1289 1.1 mrg 1290 1.1 mrg idx = 0; 1291 1.1 mrg assert(parseScanfFormatSpecifier("%f", idx, asterisk) == Format.g); 1292 1.1 mrg assert(idx == 2); 1293 1.1 mrg 1294 1.1 mrg idx = 0; 1295 1.1 mrg assert(parseScanfFormatSpecifier("%e", idx, asterisk) == Format.g); 1296 1.1 mrg assert(idx == 2); 1297 1.1 mrg 1298 1.1 mrg idx = 0; 1299 1.1 mrg g = parseScanfFormatSpecifier("%a", idx, asterisk); 1300 1.1 mrg assert(g == Format.g || g == Format.GNU_a); 1301 1.1 mrg assert(idx == 2); 1302 1.1 mrg 1303 1.1 mrg idx = 0; 1304 1.1 mrg assert(parseScanfFormatSpecifier("%c", idx, asterisk) == Format.c); 1305 1.1 mrg assert(idx == 2); 1306 1.1 mrg 1307 1.1 mrg // asterisk 1308 1.1 mrg idx = 0; 1309 1.1 mrg assert(parseScanfFormatSpecifier("%*d", idx, asterisk) == Format.d); 1310 1.1 mrg assert(idx == 3); 1311 1.1 mrg assert(asterisk); 1312 1.1 mrg 1313 1.1 mrg idx = 0; 1314 1.1 mrg assert(parseScanfFormatSpecifier("%9ld", idx, asterisk) == Format.ld); 1315 1.1 mrg assert(idx == 4); 1316 1.1 mrg assert(!asterisk); 1317 1.1 mrg 1318 1.1 mrg idx = 0; 1319 1.1 mrg assert(parseScanfFormatSpecifier("%*25984hhd", idx, asterisk) == Format.hhd); 1320 1.1 mrg assert(idx == 10); 1321 1.1 mrg assert(asterisk); 1322 1.1 mrg 1323 1.1 mrg // scansets 1324 1.1 mrg idx = 0; 1325 1.1 mrg assert(parseScanfFormatSpecifier("%[a-zA-Z]s", idx, asterisk) == Format.s); 1326 1.1 mrg assert(idx == 10); 1327 1.1 mrg assert(!asterisk); 1328 1.1 mrg 1329 1.1 mrg idx = 0; 1330 1.1 mrg assert(parseScanfFormatSpecifier("%*25[a-z]hhd", idx, asterisk) == Format.hhd); 1331 1.1 mrg assert(idx == 12); 1332 1.1 mrg assert(asterisk); 1333 1.1 mrg 1334 1.1 mrg // Too short formats 1335 1.1 mrg foreach (s; ["%", "% ", "%#", "%0", "%*", "%1", "%19", 1336 1.1 mrg "%j", "%z", "%t", "%l", "%h", "%ll", "%hh", "%K"]) 1337 1.1 mrg { 1338 1.1 mrg idx = 0; 1339 1.1 mrg assert(parseScanfFormatSpecifier(s, idx, asterisk) == Format.error); 1340 1.1 mrg assert(idx == s.length); 1341 1.1 mrg } 1342 1.1 mrg 1343 1.1 mrg 1344 1.1 mrg // Undefined format combinations 1345 1.1 mrg foreach (s; ["%Ld", "%llg", "%jg", "%zg", "%tg", "%hg", "%hhg", 1346 1.1 mrg "%jc", "%zc", "%tc", "%Lc", "%hc", "%hhc", "%llc", 1347 1.1 mrg "%jp", "%zp", "%tp", "%Lp", "%hp", "%lp", "%hhp", "%llp", 1348 1.1 mrg "%-", "%+", "%#", "%0", "%.", "%Ln"]) 1349 1.1 mrg { 1350 1.1 mrg idx = 0; 1351 1.1 mrg assert(parseScanfFormatSpecifier(s, idx, asterisk) == Format.error); 1352 1.1 mrg assert(idx == s.length); 1353 1.1 mrg 1354 1.1 mrg } 1355 1.1 mrg 1356 1.1 mrg // Invalid scansets 1357 1.1 mrg foreach (s; ["%[]", "%[s", "%[0-9lld", "%[", "%[a-z]"]) 1358 1.1 mrg { 1359 1.1 mrg idx = 0; 1360 1.1 mrg assert(parseScanfFormatSpecifier(s, idx, asterisk) == Format.error); 1361 1.1 mrg assert(idx == s.length); 1362 1.1 mrg } 1363 1.1 mrg 1364 1.1 mrg } 1365