Home | History | Annotate | Line # | Download | only in dmd
      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