Note: This website is archived. For up-to-date information about D projects and development, please visit wiki.dlang.org.

Changeset 1719

Show
Ignore:
Timestamp:
07/04/10 21:44:42 (14 years ago)
Author:
andrei
Message:

Now all writers are passed by value

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/phobos/std/format.d

    r1649 r1719  
    11// Written in the D programming language. 
    22 
    33/** 
    44 * This module implements the workhorse functionality for string and 
    55 * I/O formatting.  It's comparable to C99's vsprintf(). 
    66 * 
    77 * Macros: 
    88 *  WIKI = Phobos/StdFormat 
    99 * 
    10  * Copyright: Copyright Digital Mars 2000 - 2009. 
    11  * License:   <a href="http://www.boost.org/LICENSE_1_0.txt">Boost License 1.0</a>. 
    12  * Authors:   $(WEB digitalmars.com, Walter Bright) 
     10 * Copyright: Copyright Digital Mars 2000-. 
     11 * License:   $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). 
    1312 * 
    14  *          Copyright Digital Mars 2000 - 2009. 
    15  * Distributed under the Boost Software License, Version 1.0. 
    16  *    (See accompanying file LICENSE_1_0.txt or copy at 
    17  *          http://www.boost.org/LICENSE_1_0.txt) 
     13 * Authors: $(WEB digitalmars.com, Walter Bright), $(WEB 
     14 * erdani.com, Andrei Alexandrescu) 
    1815 */ 
    1916module std.format; 
    2017 
    21 //debug=format;     // uncomment to turn on debugging printf's 
    22  
    23 import std.algorithm, std.array, std.bitmanip;  
    24 import std.stdarg;  // caller will need va_list 
    25 import std.utf; 
     18//debug=format;                // uncomment to turn on debugging printf's 
     19 
    2620import core.stdc.stdio, core.stdc.stdlib, core.stdc.string; 
    27 import std.string; 
    28 import std.ctype; 
    29 import std.conv; 
    30 import std.functional; 
    31 import std.traits; 
    32 import std.typetuple; 
    33 import std.contracts; 
    34 import std.system; 
    35 import std.range; 
     21import std.algorithm, std.array, std.bitmanip, std.contracts, std.conv, 
     22    std.ctype, std.functional, std.range, std.stdarg, std.string, std.system, 
     23    std.traits, std.typetuple, std.utf; 
    3624version(unittest) { 
    3725    import std.stdio, std.typecons; 
    3826} 
    3927 
    4028version (Windows) version (DigitalMars) 
    4129{ 
    4230    version = DigitalMarsC; 
    4331} 
    4432 
    4533version (DigitalMarsC) 
     
    408396    std.format.doFormat(&putc, _arguments, _argptr); 
    409397} 
    410398 
    411399... 
    412400 
    413401int x = 27; 
    414402// prints 'The answer is 27:6' 
    415403myPrint("The answer is %s:", x, 6); 
    416404------------------------ 
    417405 */ 
    418  
    419406void doFormat(void delegate(dchar) putc, TypeInfo[] arguments, va_list argptr) 
    420 {   int j; 
     407{ 
    421408    TypeInfo ti; 
    422409    Mangle m; 
    423410    uint flags; 
    424411    int field_width; 
    425412    int precision; 
    426413 
    427414    enum : uint 
    428415    { 
    429416    FLdash = 1, 
    430417    FLplus = 2, 
     
    446433                    valti.classinfo.name[9..14] == "Const") 
    447434                valti = (cast(TypeInfo_Const)valti).next; 
    448435            else 
    449436                break; 
    450437        } 
    451438        return valti; 
    452439    } 
    453440 
    454441    void formatArg(char fc) 
    455442    { 
    456     bool vbit; 
    457     ulong vnumber; 
    458     char vchar; 
    459     dchar vdchar; 
    460     Object vobject; 
    461     real vreal; 
    462     creal vcreal; 
    463     Mangle m2; 
    464     int signed = 0; 
    465     uint base = 10; 
    466     int uc; 
    467     char[ulong.sizeof * 8] tmpbuf;  // long enough to print long in binary 
    468     const(char)* prefix = ""; 
    469     string s; 
    470  
    471     void putstr(const char[] s) 
    472     { 
    473         //printf("flags = x%x\n", flags); 
    474         int prepad = 0; 
    475         int postpad = 0; 
    476         int padding = field_width - (strlen(prefix) + toUCSindex(s, s.length)); 
    477         if (padding > 0) 
    478         { 
    479         if (flags & FLdash) 
    480             postpad = padding; 
    481         else 
    482             prepad = padding; 
    483         } 
    484  
    485         if (flags & FL0pad) 
    486         { 
    487         while (*prefix) 
    488             putc(*prefix++); 
    489         while (prepad--) 
    490             putc('0'); 
    491         } 
    492         else 
    493         { 
    494         while (prepad--) 
    495             putc(' '); 
    496         while (*prefix) 
    497             putc(*prefix++); 
    498         } 
    499  
    500         foreach (dchar c; s) 
    501         putc(c); 
    502  
    503         while (postpad--) 
    504         putc(' '); 
    505     } 
    506  
    507     void putreal(real v) 
    508     { 
    509         //printf("putreal %Lg\n", vreal); 
    510          
    511         switch (fc) 
    512         { 
    513         case 's': 
    514             fc = 'g'; 
    515             break; 
    516  
    517         case 'f', 'F', 'e', 'E', 'g', 'G', 'a', 'A': 
    518             break; 
    519  
    520         default: 
    521             //printf("fc = '%c'\n", fc); 
    522         Lerror: 
    523             throw new FormatError("floating"); 
    524         } 
    525         version (DigitalMarsC) 
    526         { 
    527             uint sl; 
    528             char[] fbuf = tmpbuf; 
    529             if (!(flags & FLprecision)) 
    530                 precision = 6; 
    531             while (1) 
     443        bool vbit; 
     444        ulong vnumber; 
     445        char vchar; 
     446        dchar vdchar; 
     447        Object vobject; 
     448        real vreal; 
     449        creal vcreal; 
     450        Mangle m2; 
     451        int signed = 0; 
     452        uint base = 10; 
     453        int uc; 
     454        char[ulong.sizeof * 8] tmpbuf; // long enough to print long in binary 
     455        const(char)* prefix = ""; 
     456        string s; 
     457 
     458        void putstr(const char[] s) 
     459        { 
     460            //printf("flags = x%x\n", flags); 
     461            int prepad = 0; 
     462            int postpad = 0; 
     463            int padding = field_width - 
     464                (strlen(prefix) + toUCSindex(s, s.length)); 
     465            if (padding > 0) 
    532466            { 
    533                 sl = fbuf.length; 
    534                 prefix = (*__pfloatfmt)(fc, flags | FLlngdbl, 
    535                         precision, &v, cast(char*)fbuf, &sl, field_width); 
    536             if (sl != -1) 
     467                if (flags & FLdash) 
     468                    postpad = padding; 
     469                else 
     470                    prepad = padding; 
     471            } 
     472 
     473            if (flags & FL0pad) 
     474            { 
     475                while (*prefix) 
     476                    putc(*prefix++); 
     477                while (prepad--) 
     478                    putc('0'); 
     479            } 
     480            else 
     481            { 
     482                while (prepad--) 
     483                    putc(' '); 
     484                while (*prefix) 
     485                    putc(*prefix++); 
     486            } 
     487 
     488            foreach (dchar c; s) 
     489                putc(c); 
     490 
     491            while (postpad--) 
     492                putc(' '); 
     493        } 
     494 
     495        void putreal(real v) 
     496        { 
     497            //printf("putreal %Lg\n", vreal); 
     498 
     499            switch (fc) 
     500            { 
     501                case 's': 
     502                    fc = 'g'; 
     503                    break; 
     504 
     505                case 'f', 'F', 'e', 'E', 'g', 'G', 'a', 'A': 
     506                    break; 
     507 
     508                default: 
     509                    //printf("fc = '%c'\n", fc); 
     510                Lerror: 
     511                    throw new FormatError("floating"); 
     512            } 
     513            version (DigitalMarsC) 
     514            { 
     515                uint sl; 
     516                char[] fbuf = tmpbuf; 
     517                if (!(flags & FLprecision)) 
     518                    precision = 6; 
     519                while (1) 
     520                { 
     521                    sl = fbuf.length; 
     522                    prefix = (*__pfloatfmt)(fc, flags | FLlngdbl, 
     523                            precision, &v, cast(char*)fbuf, &sl, field_width); 
     524                    if (sl != -1) 
     525                        break; 
     526                    sl = fbuf.length * 2; 
     527                    fbuf = (cast(char*)alloca(sl * char.sizeof))[0 .. sl]; 
     528                } 
     529                putstr(fbuf[0 .. sl]); 
     530            } 
     531            else 
     532            { 
     533                int sl; 
     534                char[] fbuf = tmpbuf; 
     535                char[12] format; 
     536                format[0] = '%'; 
     537                int i = 1; 
     538                if (flags & FLdash) 
     539                    format[i++] = '-'; 
     540                if (flags & FLplus) 
     541                    format[i++] = '+'; 
     542                if (flags & FLspace) 
     543                    format[i++] = ' '; 
     544                if (flags & FLhash) 
     545                    format[i++] = '#'; 
     546                if (flags & FL0pad) 
     547                    format[i++] = '0'; 
     548                format[i + 0] = '*'; 
     549                format[i + 1] = '.'; 
     550                format[i + 2] = '*'; 
     551                format[i + 3] = 'L'; 
     552                format[i + 4] = fc; 
     553                format[i + 5] = 0; 
     554                if (!(flags & FLprecision)) 
     555                    precision = -1; 
     556                while (1) 
     557                { 
     558                    sl = fbuf.length; 
     559                    auto n = snprintf(fbuf.ptr, sl, format.ptr, field_width, 
     560                            precision, v); 
     561                    //printf("format = '%s', n = %d\n", cast(char*)format, n); 
     562                    if (n >= 0 && n < sl) 
     563                    {        sl = n; 
     564                        break; 
     565                    } 
     566                    if (n < 0) 
     567                        sl = sl * 2; 
     568                    else 
     569                        sl = n + 1; 
     570                    fbuf = (cast(char*)alloca(sl * char.sizeof))[0 .. sl]; 
     571                } 
     572                putstr(fbuf[0 .. sl]); 
     573            } 
     574            return; 
     575        } 
     576 
     577        static Mangle getMan(TypeInfo ti) 
     578        { 
     579          auto m = cast(Mangle)ti.classinfo.name[9]; 
     580          if (ti.classinfo.name.length == 20 && 
     581              ti.classinfo.name[9..20] == "StaticArray") 
     582                m = cast(Mangle)'G'; 
     583          return m; 
     584        } 
     585 
     586        void putArray(void* p, size_t len, TypeInfo valti) 
     587        { 
     588          //printf("\nputArray(len = %u), tsize = %u\n", len, valti.tsize()); 
     589          putc('['); 
     590          valti = skipCI(valti); 
     591          size_t tsize = valti.tsize(); 
     592          auto argptrSave = argptr; 
     593          auto tiSave = ti; 
     594          auto mSave = m; 
     595          ti = valti; 
     596          //printf("\n%.*s\n", valti.classinfo.name); 
     597          m = getMan(valti); 
     598          while (len--) 
     599          { 
     600            //doFormat(putc, (&valti)[0 .. 1], p); 
     601            argptr = p; 
     602            formatArg('s'); 
     603 
     604            p += tsize; 
     605            if (len > 0) putc(','); 
     606          } 
     607          m = mSave; 
     608          ti = tiSave; 
     609          argptr = argptrSave; 
     610          putc(']'); 
     611        } 
     612 
     613        void putAArray(ubyte[long] vaa, TypeInfo valti, TypeInfo keyti) 
     614        { 
     615            putc('['); 
     616            bool comma=false; 
     617            auto argptrSave = argptr; 
     618            auto tiSave = ti; 
     619            auto mSave = m; 
     620            valti = skipCI(valti); 
     621            keyti = skipCI(keyti); 
     622            foreach(ref fakevalue; vaa) 
     623            { 
     624                if (comma) putc(','); 
     625                comma = true; 
     626                // the key comes before the value 
     627                ubyte* key = &fakevalue - long.sizeof; 
     628 
     629                //doFormat(putc, (&keyti)[0..1], key); 
     630                argptr = key; 
     631                ti = keyti; 
     632                m = getMan(keyti); 
     633                formatArg('s'); 
     634 
     635                putc(':'); 
     636                auto keysize = keyti.tsize; 
     637                keysize = (keysize + 3) & ~3; 
     638                ubyte* value = key + keysize; 
     639                //doFormat(putc, (&valti)[0..1], value); 
     640                argptr = value; 
     641                ti = valti; 
     642                m = getMan(valti); 
     643                formatArg('s'); 
     644            } 
     645            m = mSave; 
     646            ti = tiSave; 
     647            argptr = argptrSave; 
     648            putc(']'); 
     649        } 
     650 
     651        //printf("formatArg(fc = '%c', m = '%c')\n", fc, m); 
     652        switch (m) 
     653        { 
     654            case Mangle.Tbool: 
     655                vbit = va_arg!(bool)(argptr); 
     656                if (fc != 's') 
     657                {   vnumber = vbit; 
     658                    goto Lnumber; 
     659                } 
     660                putstr(vbit ? "true" : "false"); 
     661                return; 
     662 
     663 
     664            case Mangle.Tchar: 
     665                vchar = va_arg!(char)(argptr); 
     666                if (fc != 's') 
     667                {   vnumber = vchar; 
     668                    goto Lnumber; 
     669                } 
     670            L2: 
     671                putstr((&vchar)[0 .. 1]); 
     672                return; 
     673 
     674            case Mangle.Twchar: 
     675                vdchar = va_arg!(wchar)(argptr); 
     676                goto L1; 
     677 
     678            case Mangle.Tdchar: 
     679                vdchar = va_arg!(dchar)(argptr); 
     680            L1: 
     681                if (fc != 's') 
     682                {   vnumber = vdchar; 
     683                    goto Lnumber; 
     684                } 
     685                if (vdchar <= 0x7F) 
     686                {   vchar = cast(char)vdchar; 
     687                    goto L2; 
     688                } 
     689                else 
     690                {   if (!isValidDchar(vdchar)) 
     691                        throw new UtfException("invalid dchar in format", 0); 
     692                    char[4] vbuf; 
     693                    putstr(toUTF8(vbuf, vdchar)); 
     694                } 
     695                return; 
     696 
     697 
     698            case Mangle.Tbyte: 
     699                signed = 1; 
     700                vnumber = va_arg!(byte)(argptr); 
     701                goto Lnumber; 
     702 
     703            case Mangle.Tubyte: 
     704                vnumber = va_arg!(ubyte)(argptr); 
     705                goto Lnumber; 
     706 
     707            case Mangle.Tshort: 
     708                signed = 1; 
     709                vnumber = va_arg!(short)(argptr); 
     710                goto Lnumber; 
     711 
     712            case Mangle.Tushort: 
     713                vnumber = va_arg!(ushort)(argptr); 
     714                goto Lnumber; 
     715 
     716            case Mangle.Tint: 
     717                signed = 1; 
     718                vnumber = va_arg!(int)(argptr); 
     719                goto Lnumber; 
     720 
     721            case Mangle.Tuint: 
     722            Luint: 
     723                vnumber = va_arg!(uint)(argptr); 
     724                goto Lnumber; 
     725 
     726            case Mangle.Tlong: 
     727                signed = 1; 
     728                vnumber = cast(ulong)va_arg!(long)(argptr); 
     729                goto Lnumber; 
     730 
     731            case Mangle.Tulong: 
     732            Lulong: 
     733                vnumber = va_arg!(ulong)(argptr); 
     734                goto Lnumber; 
     735 
     736            case Mangle.Tclass: 
     737                vobject = va_arg!(Object)(argptr); 
     738                if (vobject is null) 
     739                    s = "null"; 
     740                else 
     741                    s = vobject.toString(); 
     742                goto Lputstr; 
     743 
     744            case Mangle.Tpointer: 
     745                vnumber = cast(ulong)va_arg!(void*)(argptr); 
     746                if (fc != 'x')  uc = 1; 
     747                flags |= FL0pad; 
     748                if (!(flags & FLprecision)) 
     749                {   flags |= FLprecision; 
     750                    precision = (void*).sizeof; 
     751                } 
     752                base = 16; 
     753                goto Lnumber; 
     754 
     755 
     756            case Mangle.Tfloat: 
     757            case Mangle.Tifloat: 
     758                if (fc == 'x' || fc == 'X') 
     759                    goto Luint; 
     760                vreal = va_arg!(float)(argptr); 
     761                goto Lreal; 
     762 
     763            case Mangle.Tdouble: 
     764            case Mangle.Tidouble: 
     765                if (fc == 'x' || fc == 'X') 
     766                    goto Lulong; 
     767                vreal = va_arg!(double)(argptr); 
     768                goto Lreal; 
     769 
     770            case Mangle.Treal: 
     771            case Mangle.Tireal: 
     772                vreal = va_arg!(real)(argptr); 
     773                goto Lreal; 
     774 
     775 
     776            case Mangle.Tcfloat: 
     777                vcreal = va_arg!(cfloat)(argptr); 
     778                goto Lcomplex; 
     779 
     780            case Mangle.Tcdouble: 
     781                vcreal = va_arg!(cdouble)(argptr); 
     782                goto Lcomplex; 
     783 
     784            case Mangle.Tcreal: 
     785                vcreal = va_arg!(creal)(argptr); 
     786                goto Lcomplex; 
     787 
     788            case Mangle.Tsarray: 
     789                putArray(argptr, (cast(TypeInfo_StaticArray)ti).len, 
     790                        (cast(TypeInfo_StaticArray)ti).next); 
     791                return; 
     792 
     793            case Mangle.Tarray: 
     794                int mi = 10; 
     795                if (ti.classinfo.name.length == 14 && 
     796                    ti.classinfo.name[9..14] == "Array") 
     797                { // array of non-primitive types 
     798                  TypeInfo tn = (cast(TypeInfo_Array)ti).next; 
     799                  tn = skipCI(tn); 
     800                  switch (cast(Mangle)tn.classinfo.name[9]) 
     801                  { 
     802                    case Mangle.Tchar:  goto LarrayChar; 
     803                    case Mangle.Twchar: goto LarrayWchar; 
     804                    case Mangle.Tdchar: goto LarrayDchar; 
     805                    default: 
     806                        break; 
     807                  } 
     808                  void[] va = va_arg!(void[])(argptr); 
     809                  putArray(va.ptr, va.length, tn); 
     810                  return; 
     811                } 
     812                if (ti.classinfo.name.length == 25 && 
     813                    ti.classinfo.name[9..25] == "AssociativeArray") 
     814                { // associative array 
     815                  ubyte[long] vaa = va_arg!(ubyte[long])(argptr); 
     816                  putAArray(vaa, 
     817                        (cast(TypeInfo_AssociativeArray)ti).next, 
     818                        (cast(TypeInfo_AssociativeArray)ti).key); 
     819                  return; 
     820                } 
     821 
     822                while (1) 
     823                { 
     824                    m2 = cast(Mangle)ti.classinfo.name[mi]; 
     825                    switch (m2) 
     826                    { 
     827                        case Mangle.Tchar: 
     828                        LarrayChar: 
     829                            s = va_arg!(string)(argptr); 
     830                            goto Lputstr; 
     831 
     832                        case Mangle.Twchar: 
     833                        LarrayWchar: 
     834                            wchar[] sw = va_arg!(wchar[])(argptr); 
     835                            s = toUTF8(sw); 
     836                            goto Lputstr; 
     837 
     838                        case Mangle.Tdchar: 
     839                        LarrayDchar: 
     840                            auto sd = va_arg!(dstring)(argptr); 
     841                            s = toUTF8(sd); 
     842                        Lputstr: 
     843                            if (fc != 's') 
     844                                throw new FormatError("string"); 
     845                            if (flags & FLprecision && precision < s.length) 
     846                                s = s[0 .. precision]; 
     847                            putstr(s); 
     848                            break; 
     849 
     850                        case Mangle.Tconst: 
     851                        case Mangle.Timmutable: 
     852                            mi++; 
     853                            continue; 
     854 
     855                        default: 
     856                            TypeInfo ti2 = primitiveTypeInfo(m2); 
     857                            if (!ti2) 
     858                              goto Lerror; 
     859                            void[] va = va_arg!(void[])(argptr); 
     860                            putArray(va.ptr, va.length, ti2); 
     861                    } 
     862                    return; 
     863                } 
     864 
     865            case Mangle.Ttypedef: 
     866                ti = (cast(TypeInfo_Typedef)ti).base; 
     867                m = cast(Mangle)ti.classinfo.name[9]; 
     868                formatArg(fc); 
     869                return; 
     870 
     871            case Mangle.Tenum: 
     872                ti = (cast(TypeInfo_Enum)ti).base; 
     873                m = cast(Mangle)ti.classinfo.name[9]; 
     874                formatArg(fc); 
     875                return; 
     876 
     877            case Mangle.Tstruct: 
     878            {        TypeInfo_Struct tis = cast(TypeInfo_Struct)ti; 
     879                if (tis.xtoString is null) 
     880                    throw new FormatError("Can't convert " ~ tis.toString() 
     881                            ~ " to string: \"string toString()\" not defined"); 
     882                s = tis.xtoString(argptr); 
     883                argptr += (tis.tsize() + 3) & ~3; 
     884                goto Lputstr; 
     885            } 
     886 
     887            default: 
     888                goto Lerror; 
     889        } 
     890 
     891    Lnumber: 
     892        switch (fc) 
     893        { 
     894            case 's': 
     895            case 'd': 
     896                if (signed) 
     897                {   if (cast(long)vnumber < 0) 
     898                    {        prefix = "-"; 
     899                        vnumber = -vnumber; 
     900                    } 
     901                    else if (flags & FLplus) 
     902                        prefix = "+"; 
     903                    else if (flags & FLspace) 
     904                        prefix = " "; 
     905                } 
    537906                break; 
    538             sl = fbuf.length * 2; 
    539             fbuf = (cast(char*)alloca(sl * char.sizeof))[0 .. sl]; 
     907 
     908            case 'b': 
     909                signed = 0; 
     910                base = 2; 
     911                break; 
     912 
     913            case 'o': 
     914                signed = 0; 
     915                base = 8; 
     916                break; 
     917 
     918            case 'X': 
     919                uc = 1; 
     920                if (flags & FLhash && vnumber) 
     921                    prefix = "0X"; 
     922                signed = 0; 
     923                base = 16; 
     924                break; 
     925 
     926            case 'x': 
     927                if (flags & FLhash && vnumber) 
     928                    prefix = "0x"; 
     929                signed = 0; 
     930                base = 16; 
     931                break; 
     932 
     933            default: 
     934                goto Lerror; 
     935        } 
     936 
     937        if (!signed) 
     938        { 
     939            switch (m) 
     940            { 
     941                case Mangle.Tbyte: 
     942                    vnumber &= 0xFF; 
     943                    break; 
     944 
     945                case Mangle.Tshort: 
     946                    vnumber &= 0xFFFF; 
     947                    break; 
     948 
     949                case Mangle.Tint: 
     950                    vnumber &= 0xFFFFFFFF; 
     951                    break; 
     952 
     953                default: 
     954                    break; 
    540955            } 
    541             putstr(fbuf[0 .. sl]); 
    542         } 
    543         else 
    544         { 
    545             int sl; 
    546             char[] fbuf = tmpbuf; 
    547             char[12] format; 
    548             format[0] = '%'; 
    549             int i = 1; 
    550             if (flags & FLdash) 
    551                 format[i++] = '-'; 
    552             if (flags & FLplus) 
    553                 format[i++] = '+'; 
    554             if (flags & FLspace) 
    555                 format[i++] = ' '; 
    556             if (flags & FLhash) 
    557                 format[i++] = '#'; 
    558             if (flags & FL0pad) 
    559                 format[i++] = '0'; 
    560             format[i + 0] = '*'; 
    561             format[i + 1] = '.'; 
    562             format[i + 2] = '*'; 
    563             format[i + 3] = 'L'; 
    564             format[i + 4] = fc; 
    565             format[i + 5] = 0; 
    566             if (!(flags & FLprecision)) 
    567                 precision = -1; 
    568             while (1) 
    569             {   int n; 
    570                  
    571                 sl = fbuf.length; 
    572                 n = snprintf(fbuf.ptr, sl, format.ptr, field_width, precision, v); 
    573                 //printf("format = '%s', n = %d\n", cast(char*)format, n); 
    574                 if (n >= 0 && n < sl) 
    575                 {   sl = n; 
    576                     break; 
    577                 } 
    578                 if (n < 0) 
    579                     sl = sl * 2; 
     956        } 
     957 
     958        if (flags & FLprecision && fc != 'p') 
     959            flags &= ~FL0pad; 
     960 
     961        if (vnumber < base) 
     962        { 
     963            if (vnumber == 0 && precision == 0 && flags & FLprecision && 
     964                !(fc == 'o' && flags & FLhash)) 
     965            { 
     966                putstr(null); 
     967                return; 
     968            } 
     969            if (precision == 0 || !(flags & FLprecision)) 
     970            {        vchar = cast(char)('0' + vnumber); 
     971                if (vnumber < 10) 
     972                    vchar = cast(char)('0' + vnumber); 
    580973                else 
    581                     sl = n + 1
    582                 fbuf = (cast(char*)alloca(sl * char.sizeof))[0 .. sl]
     974                    vchar = cast(char)((uc ? 'A' - 10 : 'a' - 10) + vnumber)
     975                goto L2
    583976            } 
    584             putstr(fbuf[0 .. sl]); 
    585         } 
    586         return; 
    587     } 
    588      
    589     static Mangle getMan(TypeInfo ti) 
    590     { 
    591       auto m = cast(Mangle)ti.classinfo.name[9]; 
    592       if (ti.classinfo.name.length == 20 && 
    593           ti.classinfo.name[9..20] == "StaticArray") 
    594         m = cast(Mangle)'G'; 
    595       return m; 
    596     } 
    597  
    598     void putArray(void* p, size_t len, TypeInfo valti) 
    599     { 
    600       //printf("\nputArray(len = %u), tsize = %u\n", len, valti.tsize()); 
    601       putc('['); 
    602       valti = skipCI(valti); 
    603       size_t tsize = valti.tsize(); 
    604       auto argptrSave = argptr; 
    605       auto tiSave = ti; 
    606       auto mSave = m; 
    607       ti = valti; 
    608       //printf("\n%.*s\n", valti.classinfo.name); 
    609       m = getMan(valti); 
    610       while (len--) 
    611       { 
    612         //doFormat(putc, (&valti)[0 .. 1], p); 
    613         argptr = p; 
    614         formatArg('s'); 
    615  
    616         p += tsize; 
    617         if (len > 0) putc(','); 
    618       } 
    619       m = mSave; 
    620       ti = tiSave; 
    621       argptr = argptrSave; 
    622       putc(']'); 
    623     } 
    624  
    625     void putAArray(ubyte[long] vaa, TypeInfo valti, TypeInfo keyti) 
    626     { 
    627       putc('['); 
    628       bool comma=false; 
    629       auto argptrSave = argptr; 
    630       auto tiSave = ti; 
    631       auto mSave = m; 
    632       valti = skipCI(valti); 
    633       keyti = skipCI(keyti); 
    634       foreach(ref fakevalue; vaa) 
    635       { 
    636         if (comma) putc(','); 
    637         comma = true; 
    638         // the key comes before the value 
    639         ubyte* key = &fakevalue - long.sizeof; 
    640  
    641         //doFormat(putc, (&keyti)[0..1], key); 
    642         argptr = key; 
    643         ti = keyti; 
    644         m = getMan(keyti); 
    645         formatArg('s'); 
    646  
    647         putc(':'); 
    648         auto keysize = keyti.tsize; 
    649         keysize = (keysize + 3) & ~3; 
    650         ubyte* value = key + keysize; 
    651         //doFormat(putc, (&valti)[0..1], value); 
    652         argptr = value; 
    653         ti = valti; 
    654         m = getMan(valti); 
    655         formatArg('s'); 
    656       } 
    657       m = mSave; 
    658       ti = tiSave; 
    659       argptr = argptrSave; 
    660       putc(']'); 
    661     } 
    662  
    663     //printf("formatArg(fc = '%c', m = '%c')\n", fc, m); 
    664     switch (m) 
    665     { 
    666         case Mangle.Tbool: 
    667         vbit = va_arg!(bool)(argptr); 
    668         if (fc != 's') 
    669         {   vnumber = vbit; 
    670             goto Lnumber; 
    671         } 
    672         putstr(vbit ? "true" : "false"); 
    673         return; 
    674  
    675  
    676         case Mangle.Tchar: 
    677         vchar = va_arg!(char)(argptr); 
    678         if (fc != 's') 
    679         {   vnumber = vchar; 
    680             goto Lnumber; 
    681         } 
    682         L2: 
    683         putstr((&vchar)[0 .. 1]); 
    684         return; 
    685  
    686         case Mangle.Twchar: 
    687         vdchar = va_arg!(wchar)(argptr); 
    688         goto L1; 
    689  
    690         case Mangle.Tdchar: 
    691         vdchar = va_arg!(dchar)(argptr); 
    692         L1: 
    693         if (fc != 's') 
    694         {   vnumber = vdchar; 
    695             goto Lnumber; 
    696         } 
    697         if (vdchar <= 0x7F) 
    698         {   vchar = cast(char)vdchar; 
    699             goto L2; 
    700         } 
    701         else 
    702         {   if (!isValidDchar(vdchar)) 
    703             throw new UtfException("invalid dchar in format", 0); 
    704             char[4] vbuf; 
    705             putstr(toUTF8(vbuf, vdchar)); 
    706         } 
    707         return; 
    708  
    709  
    710         case Mangle.Tbyte: 
    711         signed = 1; 
    712         vnumber = va_arg!(byte)(argptr); 
    713         goto Lnumber; 
    714  
    715         case Mangle.Tubyte: 
    716         vnumber = va_arg!(ubyte)(argptr); 
    717         goto Lnumber; 
    718  
    719         case Mangle.Tshort: 
    720         signed = 1; 
    721         vnumber = va_arg!(short)(argptr); 
    722         goto Lnumber; 
    723  
    724         case Mangle.Tushort: 
    725         vnumber = va_arg!(ushort)(argptr); 
    726         goto Lnumber; 
    727  
    728         case Mangle.Tint: 
    729         signed = 1; 
    730         vnumber = va_arg!(int)(argptr); 
    731         goto Lnumber; 
    732  
    733         case Mangle.Tuint: 
    734         Luint: 
    735         vnumber = va_arg!(uint)(argptr); 
    736         goto Lnumber; 
    737  
    738         case Mangle.Tlong: 
    739         signed = 1; 
    740         vnumber = cast(ulong)va_arg!(long)(argptr); 
    741         goto Lnumber; 
    742  
    743         case Mangle.Tulong: 
    744         Lulong: 
    745         vnumber = va_arg!(ulong)(argptr); 
    746         goto Lnumber; 
    747  
    748         case Mangle.Tclass: 
    749         vobject = va_arg!(Object)(argptr); 
    750         if (vobject is null) 
    751             s = "null"; 
    752         else 
    753             s = vobject.toString(); 
    754         goto Lputstr; 
    755  
    756         case Mangle.Tpointer: 
    757         vnumber = cast(ulong)va_arg!(void*)(argptr); 
    758         if (fc != 'x')  uc = 1; 
    759         flags |= FL0pad; 
    760         if (!(flags & FLprecision)) 
    761         {   flags |= FLprecision; 
    762             precision = (void*).sizeof; 
    763         } 
    764         base = 16; 
    765         goto Lnumber; 
    766  
    767  
    768         case Mangle.Tfloat: 
    769         case Mangle.Tifloat: 
    770         if (fc == 'x' || fc == 'X') 
    771             goto Luint; 
    772         vreal = va_arg!(float)(argptr); 
    773         goto Lreal; 
    774  
    775         case Mangle.Tdouble: 
    776         case Mangle.Tidouble: 
    777         if (fc == 'x' || fc == 'X') 
    778             goto Lulong; 
    779         vreal = va_arg!(double)(argptr); 
    780         goto Lreal; 
    781  
    782         case Mangle.Treal: 
    783         case Mangle.Tireal: 
    784         vreal = va_arg!(real)(argptr); 
    785         goto Lreal; 
    786  
    787  
    788         case Mangle.Tcfloat: 
    789         vcreal = va_arg!(cfloat)(argptr); 
    790         goto Lcomplex; 
    791  
    792         case Mangle.Tcdouble: 
    793         vcreal = va_arg!(cdouble)(argptr); 
    794         goto Lcomplex; 
    795  
    796         case Mangle.Tcreal: 
    797         vcreal = va_arg!(creal)(argptr); 
    798         goto Lcomplex; 
    799  
    800         case Mangle.Tsarray: 
    801         putArray(argptr, (cast(TypeInfo_StaticArray)ti).len, (cast(TypeInfo_StaticArray)ti).next); 
    802         return; 
    803  
    804         case Mangle.Tarray: 
    805         int mi = 10; 
    806             if (ti.classinfo.name.length == 14 && 
    807             ti.classinfo.name[9..14] == "Array")  
    808         { // array of non-primitive types 
    809           TypeInfo tn = (cast(TypeInfo_Array)ti).next; 
    810           tn = skipCI(tn); 
    811           switch (cast(Mangle)tn.classinfo.name[9]) 
    812           { 
    813             case Mangle.Tchar:  goto LarrayChar; 
    814             case Mangle.Twchar: goto LarrayWchar; 
    815             case Mangle.Tdchar: goto LarrayDchar; 
    816             default: 
    817             break; 
    818           } 
    819           void[] va = va_arg!(void[])(argptr); 
    820           putArray(va.ptr, va.length, tn); 
    821           return; 
    822         } 
    823         if (ti.classinfo.name.length == 25 && 
    824             ti.classinfo.name[9..25] == "AssociativeArray")  
    825         { // associative array 
    826           ubyte[long] vaa = va_arg!(ubyte[long])(argptr); 
    827           putAArray(vaa, 
    828             (cast(TypeInfo_AssociativeArray)ti).next, 
    829             (cast(TypeInfo_AssociativeArray)ti).key); 
    830           return; 
    831         } 
    832  
    833         while (1) 
    834         { 
    835             m2 = cast(Mangle)ti.classinfo.name[mi]; 
    836             switch (m2) 
    837             { 
    838             case Mangle.Tchar: 
    839             LarrayChar: 
    840                 s = va_arg!(string)(argptr); 
    841                 goto Lputstr; 
    842  
    843             case Mangle.Twchar: 
    844             LarrayWchar: 
    845                 wchar[] sw = va_arg!(wchar[])(argptr); 
    846                 s = toUTF8(sw); 
    847                 goto Lputstr; 
    848  
    849             case Mangle.Tdchar: 
    850             LarrayDchar: 
    851                 auto sd = va_arg!(dstring)(argptr); 
    852                 s = toUTF8(sd); 
    853             Lputstr: 
    854                 if (fc != 's') 
    855                 throw new FormatError("string"); 
    856                 if (flags & FLprecision && precision < s.length) 
    857                 s = s[0 .. precision]; 
    858                 putstr(s); 
    859                 break; 
    860  
    861             case Mangle.Tconst: 
    862             case Mangle.Timmutable: 
    863                 mi++; 
    864                 continue; 
    865  
    866             default: 
    867                 TypeInfo ti2 = primitiveTypeInfo(m2); 
    868                 if (!ti2) 
    869                   goto Lerror; 
    870                 void[] va = va_arg!(void[])(argptr); 
    871                 putArray(va.ptr, va.length, ti2); 
    872             } 
    873             return; 
    874         } 
    875  
    876         case Mangle.Ttypedef: 
    877         ti = (cast(TypeInfo_Typedef)ti).base; 
    878         m = cast(Mangle)ti.classinfo.name[9]; 
    879         formatArg(fc); 
    880         return; 
    881  
    882         case Mangle.Tenum: 
    883         ti = (cast(TypeInfo_Enum)ti).base; 
    884         m = cast(Mangle)ti.classinfo.name[9]; 
    885         formatArg(fc); 
    886         return; 
    887  
    888         case Mangle.Tstruct: 
    889         {   TypeInfo_Struct tis = cast(TypeInfo_Struct)ti; 
    890         if (tis.xtoString is null) 
    891             throw new FormatError("Can't convert " ~ tis.toString() ~ " to string: \"string toString()\" not defined"); 
    892         s = tis.xtoString(argptr); 
    893         argptr += (tis.tsize() + 3) & ~3; 
    894         goto Lputstr; 
    895         } 
    896  
    897         default: 
    898         goto Lerror; 
    899     } 
    900  
    901     Lnumber: 
    902     switch (fc) 
    903     { 
    904         case 's': 
    905         case 'd': 
    906         if (signed) 
    907         {   if (cast(long)vnumber < 0) 
    908             {   prefix = "-"; 
    909             vnumber = -vnumber; 
    910             } 
    911             else if (flags & FLplus) 
    912             prefix = "+"; 
    913             else if (flags & FLspace) 
    914             prefix = " "; 
    915         } 
    916         break; 
    917  
    918         case 'b': 
    919         signed = 0; 
    920         base = 2; 
    921         break; 
    922  
    923         case 'o': 
    924         signed = 0; 
    925         base = 8; 
    926         break; 
    927  
    928         case 'X': 
    929         uc = 1; 
    930         if (flags & FLhash && vnumber) 
    931             prefix = "0X"; 
    932         signed = 0; 
    933         base = 16; 
    934         break; 
    935  
    936         case 'x': 
    937         if (flags & FLhash && vnumber) 
    938             prefix = "0x"; 
    939         signed = 0; 
    940         base = 16; 
    941         break; 
    942  
    943         default: 
    944         goto Lerror; 
    945     } 
    946  
    947     if (!signed) 
    948     { 
    949         switch (m) 
    950         { 
    951         case Mangle.Tbyte: 
    952             vnumber &= 0xFF; 
    953             break; 
    954  
    955         case Mangle.Tshort: 
    956             vnumber &= 0xFFFF; 
    957             break; 
    958  
    959         case Mangle.Tint: 
    960             vnumber &= 0xFFFFFFFF; 
    961             break; 
    962  
    963         default: 
    964             break; 
    965         } 
    966     } 
    967  
    968     if (flags & FLprecision && fc != 'p') 
    969         flags &= ~FL0pad; 
    970  
    971     if (vnumber < base) 
    972     { 
    973         if (vnumber == 0 && precision == 0 && flags & FLprecision && 
    974         !(fc == 'o' && flags & FLhash)) 
    975         { 
    976         putstr(null); 
    977         return; 
    978         } 
    979         if (precision == 0 || !(flags & FLprecision)) 
    980         {   vchar = cast(char)('0' + vnumber); 
    981         if (vnumber < 10) 
    982             vchar = cast(char)('0' + vnumber); 
    983         else 
    984             vchar = cast(char)((uc ? 'A' - 10 : 'a' - 10) + vnumber); 
    985         goto L2; 
    986         } 
    987     } 
    988  
    989     int n = tmpbuf.length; 
    990     char c; 
    991     int hexoffset = uc ? ('A' - ('9' + 1)) : ('a' - ('9' + 1)); 
    992  
    993     while (vnumber) 
    994     { 
    995         c = cast(char)((vnumber % base) + '0'); 
    996         if (c > '9') 
    997         c += hexoffset; 
    998         vnumber /= base; 
    999         tmpbuf[--n] = c; 
    1000     } 
    1001     if (tmpbuf.length - n < precision && precision < tmpbuf.length) 
    1002     { 
    1003         int m = tmpbuf.length - precision; 
    1004         tmpbuf[m .. n] = '0'; 
    1005         n = m; 
    1006     } 
    1007     else if (flags & FLhash && fc == 'o') 
    1008         prefix = "0"; 
    1009     putstr(tmpbuf[n .. tmpbuf.length]); 
    1010     return; 
     977        } 
     978 
     979        int n = tmpbuf.length; 
     980        char c; 
     981        int hexoffset = uc ? ('A' - ('9' + 1)) : ('a' - ('9' + 1)); 
     982 
     983        while (vnumber) 
     984        { 
     985            c = cast(char)((vnumber % base) + '0'); 
     986            if (c > '9') 
     987                c += hexoffset; 
     988            vnumber /= base; 
     989            tmpbuf[--n] = c; 
     990        } 
     991        if (tmpbuf.length - n < precision && precision < tmpbuf.length) 
     992        { 
     993            int m = tmpbuf.length - precision; 
     994            tmpbuf[m .. n] = '0'; 
     995            n = m; 
     996        } 
     997        else if (flags & FLhash && fc == 'o') 
     998            prefix = "0"; 
     999        putstr(tmpbuf[n .. tmpbuf.length]); 
     1000        return; 
    10111001 
    10121002    Lreal: 
    10131003    putreal(vreal); 
    10141004    return; 
    10151005 
    10161006    Lcomplex: 
    10171007    putreal(vcreal.re); 
    10181008    putc('+'); 
    10191009    putreal(vcreal.im); 
    10201010    putc('i'); 
    10211011    return; 
    10221012 
    10231013    Lerror: 
    1024     throw new FormatError("formatArg"); 
    1025     } 
    1026  
    1027  
    1028     for (j = 0; j < arguments.length; ) 
     1014        throw new FormatError("formatArg"); 
     1015    } 
     1016 
     1017    for (int j = 0; j < arguments.length; ) 
    10291018    { 
    10301019        ti = arguments[j++]; 
    10311020        //printf("test1: '%.*s' %d\n", ti.classinfo.name, 
    10321021        //ti.classinfo.name.length); ti.print(); 
    10331022         
    10341023        flags = 0; 
    10351024        precision = 0; 
    10361025        field_width = 0; 
    10371026         
    10381027        ti = skipCI(ti); 
     
    15311520    assert(std.string.format("%8s", "bar") == "     bar"); 
    15321521    assert(std.string.format("%8s", "b\u00e9ll\u00f4") == "   b\u00e9ll\u00f4"); 
    15331522} 
    15341523 
    15351524// Andrei 
    15361525//------------------------------------------------------------------------------ 
    15371526 
    15381527/* 
    15391528 * A compiled version of an individual writef format 
    15401529 * specifier. FormatInfo only focuses on representation, without 
    1541  * assigning any semantics to the fields. */ 
     1530 * assigning any semantics to the fields. 
     1531 */ 
    15421532struct FormatInfo 
    15431533{ 
    1544     /** Special values for width and precision, DYNAMIC width or 
     1534    /** 
     1535     * Special values for width and precision, DYNAMIC width or 
    15451536     * precision means that they were specified with '*' in the format 
    1546      * string. */ 
    1547     enum short DYNAMIC = short.max; 
     1537     * string. 
     1538     */ 
     1539    enum int DYNAMIC = int.max; 
    15481540    /** Special value for precision */ 
    1549     enum short UNSPECIFIED = DYNAMIC - 1; 
     1541    enum int UNSPECIFIED = DYNAMIC - 1; 
    15501542    /** minimum width, default 0.  */ 
    1551     short width = 0; 
     1543    int width = 0; 
    15521544    /** precision. */ 
    1553     short precision = UNSPECIFIED;  
     1545    int precision = UNSPECIFIED; 
    15541546    /** The actual format specifier, 's' by default. */ 
    15551547    char spec = 's'; 
    15561548    /** Index of the argument for positional parameters, from 1 to 
    15571549     * ubyte.max. (0 means not used) */ 
    15581550    ubyte index; 
    15591551    /* Flags: flDash for '-', flZero for '0', flSpace for ' ', flPlus 
    15601552     *  for '+', flHash for '#'. */ 
    15611553    mixin(bitfields!( 
    15621554                bool, "flDash", 1, 
    15631555                bool, "flZero", 1, 
    15641556                bool, "flSpace", 1, 
    15651557                bool, "flPlus", 1, 
    15661558                bool, "flHash", 1, 
    15671559                ubyte, "", 3)); 
    15681560    /* For arrays only: the trailing  */     
    15691561    const(char)[] innerTrailing, trailing; 
    15701562 
    1571 /
    1572  * Given a string format specification fmt, parses a format 
    1573  * specifier. The string is assumed to start with the character 
    1574  * immediately following the '%'. The string is advanced to right 
    1575  * after the end of the format specifier. */ 
    1576     static FormatInfo parse(S)(ref S fmt) 
    1577     { 
    1578         FormatInfo result; 
    1579         if (fmt.empty) return result
    1580         scope(success) result.trailing = to!(const(char)[])(fmt); 
    1581         size_t i = 0; 
    1582         for (;;) 
     1563    /*
     1564     * Given a string format specification fmt, parses a format 
     1565     * specifier. The string is assumed to start with the character 
     1566     * immediately following the '%'. The string is advanced to right 
     1567     * after the end of the format specifier. 
     1568    */ 
     1569    this(S)(ref S fmt) 
     1570    { 
     1571        if (fmt.empty) return
     1572        scope(success) trailing = to!(typeof(trailing))(fmt); 
     1573        for (size_t i = 0; i < fmt.length; ) 
     1574        { 
    15831575            switch (fmt[i]) 
    15841576            { 
    1585             case '(': 
    1586             { 
    1587                 // embedded format specifier 
    1588                 auto j = i + 1; 
    1589                 void check(bool condition) 
     1577                case '(': 
    15901578                { 
    1591                     enforce( 
    1592                         condition, 
    1593                         text("Incorrect format specifier: %", fmt[i .. $])); 
     1579                    // embedded format specifier 
     1580                    auto j = i + 1; 
     1581                    void check(bool condition) 
     1582                    { 
     1583                        enforce( 
     1584                            condition, 
     1585                            text("Incorrect format specifier: %", fmt[i .. $])); 
     1586                    } 
     1587                    for (uint innerParens;; ++j) 
     1588                    { 
     1589                        check(j < fmt.length); 
     1590                        if (fmt[j] != '%') 
     1591                        { 
     1592                            // skip, we're waiting for %( and %) 
     1593                            continue; 
     1594                        } 
     1595                        ++j; 
     1596                        if (fmt[j] == ')') 
     1597                        { 
     1598                            if (innerParens-- == 0) break; 
     1599                        } 
     1600                        else if (fmt[j] == '(') 
     1601                        { 
     1602                            ++innerParens; 
     1603                        } 
     1604                    } 
     1605                    auto innerFmtSpec = fmt[i + 1 .. j - 1]; 
     1606                    this = FormatInfo(innerFmtSpec); 
     1607                    innerTrailing = to!(typeof(innerTrailing))(innerFmtSpec); 
     1608                    // We practically found the format specifier 
     1609                    i = j + 1; 
     1610                    //raw = to!(const(char)[])(fmt[0 .. i]); 
     1611                    fmt = fmt[i .. $]; 
     1612                    return; 
    15941613                } 
    1595                 for (int innerParens;; ++j) 
    1596                 { 
    1597                     check(j < fmt.length); 
    1598                     if (fmt[j] == ')') 
    1599                     { 
    1600                         if (innerParens-- == 0) break; 
    1601                     } 
    1602                     else if (fmt[j] == '(') 
    1603                     { 
    1604                         ++innerParens; 
    1605                     } 
    1606                     else if (fmt[j] == '%') 
    1607                     { 
    1608                         // skip the '%' and the character following 
    1609                         // it, whatever it is 
    1610                         ++j; 
    1611                     } 
    1612                 } 
    1613                 auto innerFmtSpec = fmt[i + 1 .. j]; 
    1614                 result = parse(innerFmtSpec); 
    1615                 result.innerTrailing = to!(typeof(innerTrailing))(innerFmtSpec); 
    1616                 // We practically found the format specifier 
    1617                 i = j + 1; 
    1618                 //result.raw = to!(const(char)[])(fmt[0 .. i]); 
    1619                 fmt = fmt[i .. $]; 
    1620                 return result; 
    1621             } 
    1622             case '-': result.flDash = true; ++i; break; 
    1623             case '+': result.flPlus = true; ++i; break; 
    1624             case '#': result.flHash = true; ++i; break; 
    1625             case '0': result.flZero = true; ++i; break; 
    1626             case ' ': result.flSpace = true; ++i; break; 
    1627             case '*': 
    1628                 if (isdigit(fmt[++i])) 
    1629                 { 
    1630                     // a '*' followed by digits and '$' is a positional format 
    1631                     fmt = fmt[1 .. $]; 
    1632                     result.width = -.parse!(typeof(result.width))(fmt); 
    1633                     i = 0; 
    1634                     if (fmt[i++] != '$') throw new FormatError("$ expected"); 
    1635                 } 
    1636                 else  
    1637                 { 
    1638                     // read result 
    1639                     result.width = DYNAMIC; 
    1640                 } 
    1641                 break; 
    1642             case '1': case '2': case '3': case '4': 
    1643             case '5': case '6': case '7': case '8': case '9': 
    1644                 auto tmp = fmt[i .. $]; 
    1645                 const widthOrArgIndex = .parse!(ushort)(tmp); 
    1646                 assert(tmp.length, 
    1647                         text("Incorrect format specifier %", fmt[i .. $])); 
    1648                 i = tmp.ptr - fmt.ptr; 
    1649                 if (tmp.length && tmp[0] == '$') 
    1650                 { 
    1651                     // index! 
    1652                     result.index = to!(ubyte)(widthOrArgIndex); 
    1653                     ++i; 
    1654                 } 
    1655                 else 
    1656                 { 
    1657                     // width 
    1658                     result.width = cast(short) widthOrArgIndex; 
    1659                 } 
    1660                 break; 
    1661             case '.': 
    1662                 if (fmt[++i] == '*') 
    1663                 { 
     1614                case '-': flDash = true; ++i; break; 
     1615                case '+': flPlus = true; ++i; break; 
     1616                case '#': flHash = true; ++i; break; 
     1617                case '0': flZero = true; ++i; break; 
     1618                case ' ': flSpace = true; ++i; break; 
     1619                case '*': 
    16641620                    if (isdigit(fmt[++i])) 
    16651621                    { 
    1666                         // a '.*' followed by digits and '$' is a 
    1667                         // positional precision 
    1668                         fmt = fmt[i .. $]; 
     1622                        // a '*' followed by digits and '$' is a 
     1623                        // positional format 
     1624                        fmt = fmt[1 .. $]; 
     1625                        width = -.parse!(typeof(width))(fmt); 
    16691626                        i = 0; 
    1670                         result.precision = cast(short) -.parse!(int)(fmt); 
    1671                         if (fmt[i++] != '$') 
    1672                         { 
    1673                             throw new FormatError("$ expected"); 
    1674                         } 
     1627                        enforce(fmt[i++] == '$', new FormatError("$ expected")); 
    16751628                    } 
    16761629                    else 
    16771630                    { 
    16781631                        // read result 
    1679                         result.precision = DYNAMIC; 
     1632                        width = DYNAMIC; 
    16801633                    } 
    1681                 } 
    1682                 else if (fmt[i] == '-') 
    1683                 { 
    1684                     // negative precision, as good as 0 
    1685                     result.precision = 0; 
     1634                    break; 
     1635                case '1': .. case '9': 
    16861636                    auto tmp = fmt[i .. $]; 
    1687                     .parse!(int)(tmp); // skip digits 
     1637                    const widthOrArgIndex = .parse!(uint)(tmp); 
     1638                    assert(tmp.length, 
     1639                            text("Incorrect format specifier %", fmt[i .. $])); 
    16881640                    i = tmp.ptr - fmt.ptr; 
    1689                 } 
    1690                 else if (isdigit(fmt[i])) 
    1691                 { 
    1692                     auto tmp = fmt[i .. $]; 
    1693                     result.precision = .parse!(short)(tmp); 
    1694                     i = tmp.ptr - fmt.ptr; 
    1695                 } 
    1696                 else 
    1697                 { 
    1698                     // "." was specified, but nothing after it 
    1699                     result.precision = 0; 
    1700                 } 
    1701                 break; 
    1702             default: 
    1703                 // this is the format char 
    1704                 result.spec = cast(char)fmt[i++]; 
    1705                 //result.raw = to!(const(char)[])(fmt[0 .. i]); 
    1706                 fmt = fmt[i .. $]; 
    1707                 return result; 
    1708             } // end switch and for 
     1641                    if (tmp.length && tmp[0] == '$') 
     1642                    { 
     1643                        // index! 
     1644                        index = to!(ubyte)(widthOrArgIndex); 
     1645                        ++i; 
     1646                    } 
     1647                    else 
     1648                    { 
     1649                        // width 
     1650                        width = to!int(widthOrArgIndex); 
     1651                    } 
     1652                    break; 
     1653                case '.': 
     1654                    if (fmt[++i] == '*') 
     1655                    { 
     1656                        if (isdigit(fmt[++i])) 
     1657                        { 
     1658                            // a '.*' followed by digits and '$' is a 
     1659                            // positional precision 
     1660                            fmt = fmt[i .. $]; 
     1661                            i = 0; 
     1662                            precision = -.parse!(int)(fmt); 
     1663                            if (fmt[i++] != '$') 
     1664                            { 
     1665                                throw new FormatError("$ expected"); 
     1666                            } 
     1667                        } 
     1668                        else 
     1669                        { 
     1670                            // read result 
     1671                            precision = DYNAMIC; 
     1672                        } 
     1673                    } 
     1674                    else if (fmt[i] == '-') 
     1675                    { 
     1676                        // negative precision, as good as 0 
     1677                        precision = 0; 
     1678                        auto tmp = fmt[i .. $]; 
     1679                        .parse!(int)(tmp); // skip digits 
     1680                        i = tmp.ptr - fmt.ptr; 
     1681                    } 
     1682                    else if (isdigit(fmt[i])) 
     1683                    { 
     1684                        auto tmp = fmt[i .. $]; 
     1685                        precision = .parse!int(tmp); 
     1686                        i = tmp.ptr - fmt.ptr; 
     1687                    } 
     1688                    else 
     1689                    { 
     1690                        // "." was specified, but nothing after it 
     1691                        precision = 0; 
     1692                    } 
     1693                    break; 
     1694                default: 
     1695                    // this is the format char 
     1696                    spec = cast(char)fmt[i++]; 
     1697                    fmt = fmt[i .. $]; 
     1698                    return; 
     1699            } // end switch 
     1700        } // end for 
     1701        enforce(false, text("Incorrect format specifier: ", fmt)); 
    17091702    } 
    17101703} 
    17111704 
    17121705//------------------------------------------------------------------------------ 
    17131706// Writes characters in the format strings up to the first format 
    17141707// specifier and updates the format specifier to remove the written 
    17151708// portion The updated format fmt does not include the '%' 
    1716 private void writeUpToFormatSpec(OutRange, S)(ref OutRange w, ref S fmt) 
     1709private void writeUpToFormatSpec(OutRange, S)(OutRange w, ref S fmt) 
    17171710{ 
    17181711    for (size_t i = 0; i < fmt.length; ++i) 
    17191712    { 
    17201713        if (fmt[i] != '%') continue; 
    17211714        if (fmt[++i] != '%') 
    17221715        { 
    17231716            // spec found, print and bailout 
    17241717            w.put(fmt[0 .. i - 1]); 
    17251718            fmt = fmt[i .. $]; 
    17261719            return; 
     
    17311724        fmt = fmt[i + 1 .. $]; 
    17321725        i = 0; 
    17331726    } 
    17341727    // no format spec found 
    17351728    w.put(fmt); 
    17361729    fmt = null; 
    17371730} 
    17381731 
    17391732unittest 
    17401733{ 
    1741     Appender!(string) w; 
     1734    string s; 
     1735    auto w = appender(&s); 
    17421736    string fmt = "abc%sdef%sghi"; 
    17431737    writeUpToFormatSpec(w, fmt); 
    1744     assert(w.data == "abc" && fmt == "sdef%sghi"); 
     1738    assert(w.data == "abc"); 
     1739    assert(fmt == "sdef%sghi"); 
    17451740    writeUpToFormatSpec(w, fmt); 
    17461741    assert(w.data == "abcsdef" && fmt == "sghi"); 
    17471742    // test with embedded %%s 
    17481743    fmt = "ab%%cd%%ef%sg%%h%sij"; 
    17491744    w.clear; 
    17501745    writeUpToFormatSpec(w, fmt); 
    17511746    assert(w.data == "ab%cd%ef" && fmt == "sg%%h%sij"); 
    17521747    writeUpToFormatSpec(w, fmt); 
    17531748    assert(w.data == "ab%cd%efsg%h" && fmt == "sij"); 
    17541749} 
    17551750 
    17561751/* 
    17571752 * Formats an integral number 'arg' according to 'f' and writes it to 
    17581753 * 'w'. 
    1759  */  
    1760 private void formatImpl(Writer, D)(ref Writer w, D argx, FormatInfo f) 
     1754 */ 
     1755private void formatImpl(Writer, D)(Writer w, D argx, FormatInfo f) 
    17611756if (isIntegral!(D)) 
    17621757{ 
    17631758    Unqual!(D) arg = argx; 
    17641759    if (f.spec == 'r') 
    17651760    { 
    17661761        // raw write, skip all else and write the thing 
    17671762        auto begin = cast(const char*) &arg; 
    17681763        if (std.system.endian == Endian.LittleEndian && f.flPlus 
    17691764            || std.system.endian == Endian.BigEndian && f.flDash) 
    17701765        { 
     
    18441839        forcedPrefix = '0'; 
    18451840    } 
    18461841    // write left pad; write sign; write 0x or 0X; write digits; 
    18471842    //   write right pad 
    18481843    // Writing left pad 
    18491844    int spacesToPrint =  
    18501845        f.width // start with the minimum width 
    18511846        - digits.length  // take away digits to print 
    18521847        - (forcedPrefix != 0) // take away the sign if any 
    18531848        - (base == 16 && f.flHash() && arg ? 2 : 0); // 0x or 0X 
    1854     int delta = f.precision - digits.length; 
     1849    const int delta = f.precision - digits.length; 
    18551850    if (delta > 0) spacesToPrint -= delta; 
    18561851    //writeln(spacesToPrint); 
    18571852    if (spacesToPrint > 0) // need to do some padding 
    18581853    { 
    18591854        if (leftPad == '0') 
    18601855        { 
    18611856            // pad with zeros 
    18621857            f.precision = 
    18631858                cast(typeof(f.precision)) (spacesToPrint + digits.length); 
    18641859                //to!(typeof(f.precision))(spacesToPrint + digits.length); 
     
    20902085        auto s = obj.toString; 
    20912086        w.put(s);                                         
    20922087    } else { 
    20932088        // last resort: just print type name 
    20942089        w.put(D.stringof); 
    20952090    } 
    20962091} 
    20972092 
    20982093unittest 
    20992094{ 
    2100     Appender!(immutable(char)[]) w; 
     2095    string s; 
     2096    auto w = appender(&s); 
    21012097    int[] a = [ 1, 3, 2 ]; 
    2102        formattedWrite(w, "testing %(s, ) embedded", a); 
    2103        assert(w.data == "testing 1, 3, 2 embedded", w.data); 
     2098           formattedWrite(w, "testing %(s, %) embedded", a); 
     2099    assert(w.data == "testing 1, 3, 2 embedded", w.data); 
    21042100    w.clear; 
    2105     formattedWrite(w, "testing (%(s%) %()) embedded", a); 
     2101    formattedWrite(w, "testing (%(s) (%)) embedded", a); 
    21062102    assert(w.data == "testing (1) (3) (2) embedded", w.data); 
    21072103 
    21082104    int[0] empt = []; 
    21092105    w.clear; 
    21102106    formattedWrite(w, "(%s)", empt); 
    21112107    assert(w.data == "()", w.data); 
    21122108} 
    21132109 
    21142110//------------------------------------------------------------------------------ 
    21152111// Fix for issue 1591 
     
    21712167The positional and non-positional styles can be mixed in the same 
    21722168format string. (POSIX leaves this behavior undefined.) The internal 
    21732169counter for non-positional parameters tracks the next parameter after 
    21742170the largest positional parameter already used. 
    21752171 
    21762172Warning: 
    21772173This is the function internally used by writef* but it's still 
    21782174undergoing active development. Do not rely on it. 
    21792175 */ 
    21802176 
    2181 void formattedWrite(Writer, F, A...)(ref Writer w, const(F)[] fmt, A args) 
     2177void formattedWrite(Writer, F, A...)(Writer w, const(F)[] fmt, A args) 
    21822178{ 
    21832179    enum len = args.length; 
    21842180    void function(ref Writer, const(void)*, FormatInfo) funs[len] = void; 
    21852181    const(void)* argsAddresses[len] = void; 
    21862182    foreach (i, arg; args) 
    21872183    { 
    21882184        funs[i] = &formatGeneric!(Writer, typeof(arg)); 
    21892185        argsAddresses[i] = &arg; 
    21902186    } 
    21912187    // Are we already done with formats? Then just dump each parameter in turn 
    21922188    uint currentArg = 0; 
    21932189    for (;;) 
    21942190    { 
    21952191        writeUpToFormatSpec(w, fmt); 
    21962192        if (fmt.length == 0)  
    21972193        { 
    21982194            // New behavior: break if there are too few specifiers AND 
    21992195            // at least one specifier did exist 
    22002196            break; 
    22012197        } 
    2202         auto spec = FormatInfo.parse(fmt); 
     2198        auto spec = FormatInfo(fmt); 
    22032199        if (currentArg == funs.length && !spec.index) 
    22042200        { 
    22052201            // leftover spec? 
    2206             if (fmt.length) 
    2207             { 
    2208                 throw new FormatError( 
    2209                     cast(string) ("Orphan format specifier: %" ~ fmt)); 
    2210             } 
     2202            enforce(fmt.length == 0, new FormatError( 
     2203                    cast(string) ("Orphan format specifier: %" ~ fmt))); 
    22112204            break; 
    22122205        } 
    22132206        if (spec.width == FormatInfo.DYNAMIC) 
    22142207        { 
    22152208            auto width = to!(typeof(spec.width))(getNthInt(currentArg, args)); 
    22162209            if (width < 0) 
    22172210            { 
    22182211                spec.flDash = true; 
    2219                 width = cast(short)(0 - width)
     2212                width = -width
    22202213            } 
    22212214            spec.width = width; 
    22222215            ++currentArg; 
    22232216        } 
    22242217        else if (spec.width < 0) 
    22252218        { 
    22262219            // means: get width as a positional parameter 
    22272220            auto index = cast(uint) -spec.width; 
    22282221            auto width = to!(typeof(spec.width))(getNthInt(index, args)); 
    22292222            if (currentArg < index) currentArg = index; 
    22302223            if (width < 0) 
    22312224            { 
    22322225                spec.flDash = true; 
    2233                 width = cast(short)(0 - width)
     2226                width = -width
    22342227            } 
    22352228            spec.width = width; 
    22362229        } 
    22372230        if (spec.precision == FormatInfo.DYNAMIC) 
    22382231        { 
    22392232            auto precision = to!(typeof(spec.precision))( 
    22402233                getNthInt(currentArg, args)); 
    22412234            if (precision >= 0) spec.precision = precision; 
    22422235            // else negative precision is same as no precision 
    22432236            else spec.precision = FormatInfo.UNSPECIFIED; 
     
    22662259            funs[currentArg](w, argsAddresses[currentArg], spec); 
    22672260            ++currentArg; 
    22682261        } 
    22692262    } 
    22702263} 
    22712264 
    22722265/* ======================== Unit Tests ====================================== */ 
    22732266 
    22742267unittest 
    22752268{ 
    2276     Appender!(string) stream
     2269    auto stream = appender((string*).init)
    22772270    formattedWrite(stream, "%s", 1.1); 
    22782271    assert(stream.data == "1.1", stream.data); 
    22792272} 
    22802273 
    22812274unittest 
    22822275{ 
    2283     Appender!(string) stream
     2276    auto stream = appender((string*).init)
    22842277    formattedWrite(stream, "%u", 42); 
    22852278    assert(stream.data == "42", stream.data); 
    22862279} 
    22872280 
    22882281unittest 
    22892282{ 
    22902283    // testing raw writes 
    2291     Appender!(string) w; 
     2284    string s; 
     2285    auto w = appender(&s); 
    22922286    uint a = 0x02030405; 
    22932287    formattedWrite(w, "%+r", a); 
    22942288    assert(w.data.length == 4 && w.data[0] == 2 && w.data[1] == 3 
    22952289        && w.data[2] == 4 && w.data[3] == 5); 
    22962290    w.clear; 
    22972291    formattedWrite(w, "%-r", a); 
    22982292    assert(w.data.length == 4 && w.data[0] == 5 && w.data[1] == 4 
    22992293        && w.data[2] == 3 && w.data[3] == 2); 
    23002294} 
    23012295 
    23022296unittest 
    23032297{ 
    23042298    // testing positional parameters 
    2305     Appender!(string) w
     2299    auto w = appender!string()
    23062300    formattedWrite(w, 
    23072301            "Numbers %2$s and %1$s are reversed and %1$s%2$s repeated", 
    23082302            42, 0); 
    23092303    assert(w.data == "Numbers 0 and 42 are reversed and 420 repeated", 
    23102304            w.data); 
    23112305    w.clear; 
    23122306    formattedWrite(w, "asd%s", 23); 
    23132307    assert(w.data == "asd23", w.data); 
    23142308    w.clear; 
    23152309    formattedWrite(w, "%s%s", 23, 45); 
    23162310    assert(w.data == "2345", w.data); 
    23172311} 
    23182312 
    23192313unittest 
    23202314{ 
    23212315    debug(format) printf("std.format.format.unittest\n"); 
    2322      
    2323     Appender!(string) stream
     2316 
     2317    auto stream = appender!string()
    23242318    //goto here; 
    23252319     
    23262320    formattedWrite(stream, 
    23272321            "hello world! %s %s ", true, 57, 1_000_000_000, 'x', " foo"); 
    23282322    assert(stream.data == "hello world! true 57 ", 
    23292323        stream.data); 
    23302324     
    23312325    stream.clear; 
    23322326    formattedWrite(stream, "%g %A %s", 1.67, -1.28, float.nan); 
    23332327  //std.c.stdio.fwrite(stream.data.ptr, stream.data.length, 1, stderr); 
     
    27442738                                      writeln("Expected: >", exp, "<"); 
    27452739                                      writeln("Actual:   >", stream.data, 
    27462740                                              "<"); 
    27472741                                      assert(false); 
    27482742                                  } 
    27492743                              }+/ 
    27502744} 
    27512745 
    27522746unittest 
    27532747{ 
    2754    immutable(char[5])[int] aa = ([3:"hello", 4:"betty"]); 
    2755    if (false) writeln(aa.keys); 
    2756    assert(aa[3] == "hello"); 
    2757    assert(aa[4] == "betty"); 
    2758    if (false) 
    2759    
    2760        writeln(aa.values[0]); 
    2761        writeln(aa.values[1]); 
    2762        writefln("%s", typeid(typeof(aa.values))); 
    2763        writefln("%s", aa[3]); 
    2764        writefln("%s", aa[4]); 
    2765        writefln("%s", aa.values); 
    2766        //writefln("%s", aa); 
    2767        wstring a = "abcd"; 
    2768        writefln(a); 
    2769        dstring b = "abcd"; 
    2770        writefln(b); 
    2771    
    2772  
    2773    Appender!(string) stream
    2774    alias TypeTuple!(byte, ubyte, short, ushort, int, uint, long, ulong, 
    2775        float, double, real, 
    2776        ifloat, idouble, ireal, cfloat, cdouble, creal) AllNumerics; 
    2777    foreach (T; AllNumerics) 
    2778    
    2779        static if (is(T : ireal)) 
    2780            T value = 1i; 
    2781        else static if (is(T : creal)) 
    2782            T value = 1 + 1i; 
    2783        else 
    2784            T value = 1; 
    2785        stream.clear; formattedWrite(stream, "%s", value); 
    2786        static if (is(T : creal)) 
    2787            assert(stream.data == "1+1i"); 
    2788        else 
    2789            assert(stream.data == "1"); 
    2790        // test typedefs too 
    2791        typedef T Wyda; 
    2792        Wyda another = 1; 
    2793        stream.clear; formattedWrite(stream, "%s", another); 
    2794        assert(stream.data == "1"); 
    2795    
    2796     
    2797    //auto r = std.string.format("%s", aa.values); 
    2798    stream.clear; formattedWrite(stream, "%s", aa); 
    2799    assert(stream.data == "[3:[h,e,l,l,o],4:[b,e,t,t,y]]", stream.data); 
     2748    immutable(char[5])[int] aa = ([3:"hello", 4:"betty"]); 
     2749    if (false) writeln(aa.keys); 
     2750    assert(aa[3] == "hello"); 
     2751    assert(aa[4] == "betty"); 
     2752    // if (false) 
     2753    //
     2754    //     writeln(aa.values[0]); 
     2755    //     writeln(aa.values[1]); 
     2756    //     writefln("%s", typeid(typeof(aa.values))); 
     2757    //     writefln("%s", aa[3]); 
     2758    //     writefln("%s", aa[4]); 
     2759    //     writefln("%s", aa.values); 
     2760    //     //writefln("%s", aa); 
     2761    //     wstring a = "abcd"; 
     2762    //     writefln(a); 
     2763    //     dstring b = "abcd"; 
     2764    //     writefln(b); 
     2765    //
     2766 
     2767    auto stream = appender!string()
     2768    alias TypeTuple!(byte, ubyte, short, ushort, int, uint, long, ulong, 
     2769            float, double, real, 
     2770            ifloat, idouble, ireal, cfloat, cdouble, creal) AllNumerics; 
     2771    foreach (T; AllNumerics) 
     2772   
     2773        static if (is(T : ireal)) 
     2774            T value = 1i; 
     2775        else static if (is(T : creal)) 
     2776            T value = 1 + 1i; 
     2777        else 
     2778            T value = 1; 
     2779        stream.clear; formattedWrite(stream, "%s", value); 
     2780        static if (is(T : creal)) 
     2781            assert(stream.data == "1+1i"); 
     2782        else 
     2783            assert(stream.data == "1"); 
     2784        // test typedefs too 
     2785        typedef T Wyda; 
     2786        Wyda another = 1; 
     2787        stream.clear; formattedWrite(stream, "%s", another); 
     2788        assert(stream.data == "1"); 
     2789   
     2790 
     2791    //auto r = std.string.format("%s", aa.values); 
     2792    stream.clear; formattedWrite(stream, "%s", aa); 
     2793    assert(stream.data == "[3:[h,e,l,l,o],4:[b,e,t,t,y]]", stream.data); 
    28002794//    r = std.string.format("%s", aa); 
    28012795//   assert(r == "[3:[h,e,l,l,o],4:[b,e,t,t,y]]"); 
    28022796} 
    28032797 
    28042798//------------------------------------------------------------------------------ 
    28052799void formattedRead(R, S...)(ref R r, const(char)[] fmt, S args) 
    28062800{ 
    28072801    static if (!S.length) 
    28082802    { 
    2809         r = parseToFormatSpec(r, fmt); 
     2803        parseToFormatSpec(r, fmt); 
    28102804        enforce(fmt.length == 0); 
    28112805    } 
    28122806    else 
    28132807    { 
    28142808        FormatInfo spec; 
    28152809        // The loop below accounts for '*' == fields meant to be read 
    28162810        // and skipped 
    28172811        for (;;) 
    28182812        { 
    2819             r = parseToFormatSpec(r, fmt); 
    2820             spec = FormatInfo.parse(fmt); 
     2813            parseToFormatSpec(r, fmt); 
     2814            spec = FormatInfo(fmt); 
    28212815            if (spec.width != spec.DYNAMIC) break; 
    28222816            // must skip this field 
    28232817            skipData(r, spec); 
    28242818        } 
    28252819        alias typeof(*args[0]) A; 
    28262820        //@@@BUG 2725 
    28272821        //static if (is(A X == Tuple!(T), T)) 
    28282822        static if (is(A.Types[0])) 
    28292823        { 
    28302824            //@@@BUG 
     
    28352829            //             args[1 .. $]); 
    28362830            // else 
    28372831            //     return formattedRead(r, fmt, args[1 .. $]); 
    28382832            // assume it's a tuple 
    28392833            foreach (i, T; A.Types) 
    28402834            { 
    28412835                //writeln("Parsing ", r, " with format ", fmt); 
    28422836                args[0].field[i] = unformat!(T)(r, spec); 
    28432837                for (;;) 
    28442838                { 
    2845                     r = parseToFormatSpec(r, fmt); 
    2846                     spec = FormatInfo.parse(fmt); 
     2839                    parseToFormatSpec(r, fmt); 
     2840                    spec = FormatInfo(fmt); 
    28472841                    if (spec.width != spec.DYNAMIC) break; 
    28482842                    // must skip this guy 
    28492843                    skipData(r, spec); 
    28502844                } 
    28512845            } 
    28522846            return formattedRead(r, fmt, args[1 .. $]); 
    28532847        } 
    28542848        else 
    28552849        { 
    28562850            *args[0] = unformat!(A)(r, spec); 
     
    28702864        break; 
    28712865    default: 
    28722866        assert(false, text("Not understood: ", spec.spec)); 
    28732867    } 
    28742868} 
    28752869 
    28762870//------------------------------------------------------------------------------ 
    28772871T unformat(T, Range)(ref Range input, FormatInfo spec) 
    28782872    if (isArray!T && !isSomeString!T) 
    28792873{ 
    2880     Appender!(T) app
     2874    auto app = appender!T()
    28812875    for (;;) 
    28822876    { 
    28832877        auto e = parse!(ElementType!(T))(input); 
    28842878        app.put(e); 
    28852879        if (!std.string.startsWith(input, spec.innerTrailing)) break; // done 
    28862880        input = input[spec.innerTrailing.length .. $]; 
    28872881        if (input.empty) break; // the trailing is terminator, not 
    28882882                                // separator 
    28892883    } 
    28902884    return app.data; 
    28912885} 
    28922886 
    28932887//------------------------------------------------------------------------------ 
    2894 T unformat(T, Range)(ref Range input, FormatInfo spec) if (isSomeString!T) 
    2895 
    2896     Appender!(T) app; 
     2888T unformat(T, Range)(ref Range input, FormatInfo spec) 
     2889if (isInputRange!Range && isSomeString!T) 
     2890
     2891    auto app = appender!T(); 
    28972892    if (spec.trailing.empty) 
    28982893    { 
    28992894        for (; !input.empty; input.popFront()) 
    29002895        { 
    29012896            app.put(input.front); 
    29022897        } 
    29032898    } 
    29042899    else 
    29052900    { 
    2906         for (; !input.empty && !input.startsWith(spec.trailing.front)
     2901        for (; !input.empty && input.front != spec.trailing.front
    29072902             input.popFront()) 
    29082903        { 
    29092904            app.put(input.front); 
    29102905        } 
    29112906    } 
    2912     //input = input[spec.innerTrailing.length .. $]
    2913     return app.data
     2907    auto result = app.data
     2908    return result
    29142909} 
    29152910 
    29162911unittest 
    29172912{ 
    29182913    string s1, s2; 
    29192914    char[] line = "hello, world".dup; 
    29202915    formattedRead(line, "%s", &s1); 
    29212916    assert(s1 == "hello, world", s1); 
    29222917 
    29232918    line = "hello, world;yah".dup; 
     
    29272922} 
    29282923 
    29292924private template acceptedSpecs(T) 
    29302925{ 
    29312926    static if (isIntegral!T) enum acceptedSpecs = "sdu";// + "coxX" (todo) 
    29322927    else static if (isFloatingPoint!T) enum acceptedSpecs = "seEfgG"; 
    29332928    else enum acceptedSpecs = ""; 
    29342929} 
    29352930 
    29362931//------------------------------------------------------------------------------ 
    2937 T unformat(T, Range)(ref Range input, FormatInfo spec) if (isIntegral!T) 
     2932T unformat(T, Range)(ref Range input, FormatInfo spec) 
     2933if (isIntegral!T && isInputRange!Range) 
    29382934{ 
    29392935    enforce(std.algorithm.find("cdosuxX", spec.spec).length, 
    29402936            text("Wrong integral type specifier: `", spec.spec, "'")); 
    29412937    if (std.algorithm.find("dsu", spec.spec).length) 
    29422938    { 
    29432939        return parse!T(input); 
    29442940    } 
    29452941    assert(0, "Parsing spec '"~spec.spec~"' not implemented."); 
    29462942} 
    29472943 
    29482944//------------------------------------------------------------------------------ 
    2949 T unformat(T, Range)(ref Range input, FormatInfo spec) if (isFloatingPoint!T) 
     2945T unformat(T, Range)(ref Range input, FormatInfo spec) 
     2946if (isFloatingPoint!T) 
    29502947{ 
    29512948    if (spec.spec == 'r') 
    29522949    { 
    29532950        // raw read 
    2954         enforce(input.length >= T.sizeof); 
     2951        //enforce(input.length >= T.sizeof); 
    29552952        enforce(isSomeString!Range || ElementType!(Range).sizeof == 1); 
    29562953        union X 
    29572954        { 
    29582955            ubyte[T.sizeof] raw; 
    29592956            T typed; 
    29602957        } 
    29612958        X x; 
    29622959        foreach (i; 0 .. T.sizeof) 
    29632960        { 
    29642961            static if (isSomeString!Range) 
    29652962            { 
    29662963                x.raw[i] = input[0]; 
    29672964                input = input[1 .. $]; 
    29682965            } 
    29692966            else 
    29702967            { 
    2971                 x.raw[i] = input.front; 
     2968                // TODO: recheck this 
     2969                x.raw[i] = cast(ubyte) input.front; 
    29722970                input.popFront(); 
    29732971            } 
    29742972        } 
    29752973        return x.typed; 
    29762974    } 
    29772975    enforce(std.algorithm.find(acceptedSpecs!T, spec.spec).length, 
    29782976            text("Format specifier `", spec.spec, 
    29792977                    "' not accepted for floating point types")); 
    29802978    return parse!T(input); 
    2981 } 
    2982  
    2983 //------------------------------------------------------------------------------ 
    2984 T unformat(T, Range)(ref Range input, FormatInfo spec) if (is(T == typedef)) 
    2985 { 
    2986     static if (is(T Original == typedef)) 
    2987     { 
    2988         return cast(T) unformat!Original(input, spec); 
    2989     } 
    2990     else 
    2991     { 
    2992         static assert(false); 
    2993     } 
    2994 } 
    2995  
    2996 unittest 
    2997 { 
    2998     typedef int Int; 
    2999     string s = "123"; 
    3000     Int x; 
    3001     formattedRead(s, "%s", &x); 
    3002     assert(x == 123); 
    30032979} 
    30042980 
    30052981unittest 
    30062982{ 
    30072983    union A 
    30082984    { 
    30092985        char[float.sizeof] untyped; 
    30102986        float typed; 
    30112987    }; 
    30122988    A a; 
    30132989    a.typed = 5.5; 
    30142990    char[] input = a.untyped[]; 
    30152991    float witness; 
    30162992    formattedRead(input, "%r", &witness); 
    30172993    assert(witness == a.typed); 
    30182994} 
    30192995 
    30202996//------------------------------------------------------------------------------ 
    3021 private R parseToFormatSpec(R)(R r, ref const(char)[] fmt) 
     2997private void parseToFormatSpec(R)(ref R r, ref const(char)[] fmt) 
    30222998{ 
    30232999    while (fmt.length) 
    30243000    { 
    30253001        if (fmt[0] == '%') 
    30263002        { 
    30273003            if (fmt.length > 1 && fmt[1] == '%') 
    30283004            { 
    30293005                assert(!r.empty); 
    30303006                // Require a '%' 
    30313007                if (r.front != '%') break; 
     
    30353011            else 
    30363012            { 
    30373013                fmt.popFront; 
    30383014                break; 
    30393015            } 
    30403016        } 
    30413017        else 
    30423018        { 
    30433019            if (fmt.front == ' ') 
    30443020            { 
    3045                 r = std.algorithm.find!(not!isspace)(r); 
     3021                while (!r.empty && isspace(r.front)) r.popFront(); 
     3022                //r = std.algorithm.find!(not!isspace)(r); 
    30463023            } 
    30473024            else 
    30483025            { 
    3049                 enforce( 
    3050                     !r.empty, 
    3051                     text("parseToFormatSpec: Cannot find character `", 
    3052                             fmt.front, "' in the input string.")); 
     3026                enforce(!r.empty, 
     3027                        text("parseToFormatSpec: Cannot find character `", 
     3028                                fmt.front, "' in the input string.")); 
    30533029                //static assert(0); 
    30543030                if (r.front != fmt.front) break; 
    30553031                r.popFront; 
    30563032            } 
    30573033            fmt.popFront; 
    30583034        } 
    30593035    } 
    3060     return r; 
    30613036} 
    30623037 
    30633038unittest 
    30643039{ 
    30653040    char[] line = "1 2".dup; 
    30663041    string format = "%s %s"; 
    30673042    int a, b; 
    30683043    formattedRead(line, format, &a, &b); 
    30693044    assert(a == 1 && b == 2); 
    30703045 
     3046    line = "1 2 3".dup; 
     3047    format = "%d "; 
     3048    formattedRead(line, format, &a); 
     3049    assert(a == 1); 
     3050 
    30713051    Tuple!(int, float) t; 
    30723052    line = "1 2.125".dup; 
    30733053    formattedRead(line, format, &t); 
    30743054    assert(t.field[0] == 1 && t.field[1] == 2.125); 
    30753055 
    30763056    line = "1 7643 2.125".dup; 
    30773057    formattedRead(line, "%s %*u %s", &t); 
    30783058    assert(t.field[0] == 1 && t.field[1] == 2.125); 
    30793059}