Don't like ads? PRO users don't see any ads ;-)
Guest

Untitled

By: Shahid4 on Jun 22nd, 2012  |  syntax: D  |  size: 37.54 KB  |  hits: 23  |  expires: Never
download  |  raw  |  embed  |  report abuse  |  print
Text below is selected. Please press Ctrl+C to copy to your clipboard. (⌘+C on Mac)
  1. /*******************************************************************************
  2.  
  3.         Authors: Shahid
  4.  
  5.         Date: 20, December 2011
  6.  
  7.         Version: 0.3
  8.  
  9.         Bugs:    Segfaults on invalid utf8
  10.  
  11.         Notes:   I've now realised that each Type will need it's own truncation code
  12.  
  13.         History: 0.3 [24.03.11] - refactored in big Itoa changes
  14.                  0.21[31.12.11] - Support the Varient type
  15.                  0.21[20.12.11] - better itoa (non-asm)
  16.                  0.2 [25.07.11] - better itoa
  17.                  0.1 [14.07.11] - initial working code
  18.                  0.0 [25.04.11] - initial printf idea
  19.  
  20.         TODO:
  21.                 Support 0 padding for Decimal printing ( sign after padding )
  22.                 Printing arrays
  23.                 Compile time state
  24.                 repeat( string, times )
  25.  
  26.         Examples:
  27.  
  28.         Text I/O ( but only Output at the moment )
  29.  
  30.  *******************************************************************************/
  31. module  shd.io.text;
  32.  
  33. import  shd.exception,
  34.         shd.types,
  35.         shd.unicode;
  36.  
  37. import  shd.aSm.x86.array;
  38.  
  39. import  GlibC = shd.c.glibc: snprintf;
  40.  
  41. import  shd.text.convert;
  42.  
  43.  
  44. public enum Colour
  45. {
  46.         Default=0,
  47.         // The Standard Colour set values
  48.         Red=1, Yellow, Green, Blue, Orange=5, Purple, Black, White, Grey, Brown=10,
  49.         /* ... more to come! */
  50.         Max     // The last colour +1
  51. }
  52.  
  53.  
  54. /// Backend of TextFormater
  55. interface TextPrinter
  56. {
  57.         void  colour( int x );
  58.         void  flush();
  59.         void  print( char_c[] s );
  60.  
  61.         int   width( dchar d ); // what is this?
  62. }
  63.  
  64. /**
  65.  * String Formatter inspired by C# and python
  66.  *
  67.  * Start Date: 14, March 2011
  68.  */
  69. class TextFormatter
  70. {
  71.         private {
  72.                 TextPrinter printer;
  73.         }
  74.  
  75.         this( TextPrinter printer )
  76.         {
  77.                 this.printer = printer;
  78.         }
  79.  
  80.         /**
  81.          * Print text
  82.          */
  83.         final void print(A...)( A args )
  84.         {
  85.                 // REFACTOR
  86.                 foreach( arg; args )
  87.                         format( "{}", arg );
  88.         }
  89.         final void println(A...)( A args ) /// ditto
  90.         {
  91.                 print( args );
  92.                 printer.print(['\n']);
  93.                 printer.flush();
  94.         }
  95.         /**
  96.          * Print formatted text
  97.          */
  98.         final void format(A...)( string formatString, A args )
  99.         {
  100.                 Variant[A.length] a;
  101.  
  102.                 foreach( i, arg; args )
  103.                 {
  104.                         a[i].set( arg );
  105.                 }
  106.                 parse( formatString, a[] );
  107.         }
  108.         final void formatln(A...)( string formatString, A args ) //ditto
  109.         {
  110.                 format( formatString, args );
  111.                 printer.print(['\n']);
  112.                 printer.flush();
  113.         }
  114.  
  115.         /**
  116.          * Select the colour by index code
  117.          *
  118.          * Params: x - an "enum Colour". Though implementions may define more ( hence type int ).
  119.          */
  120.         void colour( int c ) { printer.colour( c ); }
  121.  
  122.         /// Shortcuts for colour()
  123.         void normal()   { printer.colour( Colour.Default ); }
  124.         void red()      { printer.colour( Colour.Red     ); }
  125.         void yellow()   { printer.colour( Colour.Yellow  ); }
  126.         void green()    { printer.colour( Colour.Green   ); }
  127.         void blue()     { printer.colour( Colour.Blue    ); }
  128.         void orange()   { printer.colour( Colour.Orange  ); }
  129.         void purple()   { printer.colour( Colour.Purple  ); }
  130.         void black()    { printer.colour( Colour.Black   ); }
  131.         void white()    { printer.colour( Colour.White   ); }
  132.         void grey()     { printer.colour( Colour.Grey    ); }
  133.         void brown()    { printer.colour( Colour.Brown   ); }
  134.  
  135.         void flush()    { printer.flush();              }
  136.  
  137.         /* *************************************
  138.                         Internals
  139.          ***************************************/
  140.  
  141.         /*
  142.          * Implementation of format()
  143.          *
  144.          * TODO explain { Error msgs }
  145.          *
  146.          */
  147.         private void parse( char_c[] format, ref Variant[] args )
  148.         {
  149.                 char_c* ptr = format.ptr;
  150.                 char_c* end = format.ptr + format.length;
  151.  
  152.                 int      indexAuto;     // Remember the last index used + 1
  153.                 char[64] buffer;        // must be 16 byte aligned ( not anymore )
  154.  
  155.                 //
  156.                 // Begin
  157.                 //
  158.                 while( true )
  159.                 {
  160.                         AlignState  s;                  // Formatting state
  161.                         uint        index = indexAuto;
  162.                         char_c[]    formatMod;          // eg {:derp}
  163.  
  164.                         char_c*  cp = find!char( '{', ptr, end );
  165.  
  166.                         // write out
  167.                         printer.print( ptr[0 .. cp-ptr] );
  168.                         if( cp is end )
  169.                                 break;
  170.  
  171.                         cp++;
  172.  
  173.                         //
  174.                         // Second '{'. ie "{{"
  175.                         //
  176.                         if( *cp == '{' )
  177.                         {
  178.                                 ptr = cp+1;
  179.                                 printer.print( "{" ); // REFACTOR
  180.                                 continue;
  181.                         }
  182.                         //
  183.                         // Colour formatter
  184.                         //
  185.                         if( *cp == 'c' )
  186.                         {
  187.                         try
  188.                         {
  189.                                 uint colourF;
  190.                                 uint colourB = uint.max;
  191.  
  192.                                 // Foreground Colour
  193.                                 parseUInt( colourF, ++cp, end, Parse.Empty );
  194.  
  195.                                 // Background Colour ( Currently not supported )
  196.                                 if( *cp == ',' )
  197.                                 {
  198.                                         parseUInt( colourB, ++cp, end, Parse.Empty );
  199.                                 }
  200.  
  201.                                 //debug D.writefln("DEBUG colour](%d,%d)",colourF,colourB);
  202.  
  203.                                 // Check everything parsed ok
  204.                                 if( *cp != '}' )
  205.                                         throw new ParseException("Open");
  206.  
  207.                                 printer.colour( colourF );
  208.                                 // Optional background colour
  209.                                 //if( colourB != uint.max ) printer.colour2( colourB );
  210.  
  211.                                 ptr = cp+1;
  212.                                 continue;
  213.                         }
  214.                         catch( ParseException e )
  215.                         {
  216.                                 printer.print("{Bad Colour: " ~ e.msg ~ " }");
  217.  
  218.                                 ptr = find!char( '}', cp, end ) + 1;
  219.                                 if( ptr > end )
  220.                                         break;
  221.  
  222.                                 // Continue the loop
  223.                                 continue;
  224.                         }
  225.                         } // end if 'c'
  226.  
  227.                         //
  228.                         // { index , alignment ! truncation : format }
  229.                         //
  230.                         try
  231.                         {
  232.  
  233.                         // Shortcut
  234.                         if( *cp == '}' )
  235.                                 goto ParsingDone;
  236.  
  237.                         // index
  238.                         if( true )
  239.                         {
  240.                                 eatSpace( cp, end );                    // Whitespace
  241.                                 parseUInt( index, cp, end, Parse.Empty );
  242.                                 eatSpace( cp, end );                    // Whitespace
  243.                         }
  244.  
  245.                         // alignment
  246.                         if( *cp == ',' )
  247.                         {
  248.                                 eatSpace( ++cp, end );                  // Whitespace
  249.                                 parseAlign( s.alignment, cp, end, &s );
  250.                                 eatSpace( cp, end );                    // Whitespace
  251.                         }
  252.  
  253.                         // truncation
  254.                         if( *cp == '!' )
  255.                         {
  256.                                 eatSpace( ++cp, end );                  // Whitespace
  257.                                 parseAlign( s.truncation, cp, end );
  258.                                 eatSpace( cp, end );                    // Whitespace
  259.                         }
  260.  
  261.                         // format
  262.                         if( *cp == ':' )
  263.                         {
  264.                                 char_c* oldcp = cp+1;
  265.                                 do {
  266.                                         if( ++cp is end )
  267.                                                 throw new ParseException("Open");
  268.                                 }
  269.                                 while( *cp != '}' );
  270.                                 formatMod = oldcp[0.. cp-oldcp];
  271.                         }
  272.  
  273.                         if( *cp != '}' )
  274.                                 throw new ParseException("Open");
  275.  
  276.                         /++debug {
  277.                                 import std.stdio;
  278.                                 auto atc = ["","<","^",">", "" ];
  279.  
  280.                                 // { index, alignment !truncation :formatMod }
  281.                                 writef("DEBUG] State: \"{%d", index );
  282.                                 if( s.alignment.type  != AlignState.Type.Unset )
  283.                                         writef(",%s%d", atc[s.alignment.type ], s.alignment.disp  );
  284.                                 if( s.truncation.type != AlignState.Type.Unset )
  285.                                         writef("!%s%d", atc[s.truncation.type], s.truncation.disp );
  286.                                 if( formatMod )
  287.                                         writef(":%s",formatMod);
  288.                                 writefln("}\"");
  289.                         } // +/
  290.  
  291.                         /*
  292.                          * format Parsing Done, now work on the argument(s)
  293.                          */
  294.  
  295.         ParsingDone:    // setup for next Iteration
  296.                         ptr       = cp+1;
  297.                         indexAuto = index + 1;
  298.  
  299.                         if( index >= args.length ) {
  300.                                 printer.print("{Not enough args}");
  301.                                 continue;
  302.                         }
  303.  
  304.                         Variant a = args[index];
  305.  
  306.                         itoaControl  ifmt;
  307.  
  308.                         with( Variant.Type )
  309.                         with( itoaControl  )
  310.                         final switch( a.type )
  311.                         {
  312.                                 case Pointer:
  313.                                         ifmt.base = Base.Hex;
  314.                                         goto case uLong;
  315.  
  316.                                 case   Byte:
  317.                                 case  Short:
  318.                                 case    Int:
  319.                                 case   Long:    ifmt.isSigned = true;
  320.                                                 // fall through
  321.                                 case  uByte:
  322.                                 case uShort:
  323.                                 case   uInt:
  324.                                 case  uLong:
  325.                                         parseFormatMod( s, ifmt, formatMod );
  326.                                         s.str = itoa( a.uLong, buffer, ifmt );
  327.  
  328.                                         // Derp
  329.                                         if( ! s.hasAlignment )
  330.                                         {
  331.                                                 printer.print( s.str );
  332.                                         } else {
  333.                                                 // Alignment + Truncation
  334.                                                 // cast away const because s.str is a slice of buffer \\
  335.                                                 itoaAlign( cast(char[]) s.str, buffer, ifmt, s );
  336.  
  337.                                                 printAlign( s );
  338.                                         }
  339.                                         continue; // while(true)
  340.  
  341.                                 case Float:
  342.                                         a.Double = a.Float;
  343.                                         // fall through
  344.                                 case Double:
  345.                                         /* Very basic floating point support */
  346.                                         auto len = GlibC.snprintf( buffer.ptr, buffer.length, "%f", a.Double );
  347.                                         s.str = buffer[0..len];
  348.                                         break;
  349.                                 //case Real:
  350.                                 //      a.Double = cast(double) a.Real; // FIXME
  351.                                 //      goto case Double;
  352.  
  353.                                 case Bool:
  354.                                         s.str = a.Bool ? "true" : "false";
  355.                                         break;
  356.                                 // Already a String
  357.                                 case Char:
  358.                                         s.str = (&a.Char)[0..1];
  359.                                         break;
  360.                                 case String:
  361.                                         s.str = a.String;
  362.                                         break;
  363.                                 case Data:
  364.                                         ifmt.padL =  4;
  365.                                         ifmt.padR = 10;
  366.                                         auto str = itoa( a.Data.length, buffer, ifmt );
  367.                                         str[0  .. 4] = "{…";
  368.                                         str[$-10..$] = " bytes…}";
  369.                                         s.str = str;
  370.                                         break;
  371.                                 // ---
  372.                                 case   ByteA:
  373.                                 case  uByteA:
  374.                                 case  ShortA:
  375.                                 case uShortA:
  376.                                 case    IntA:
  377.                                 case   uIntA:
  378.                                 case   LongA:
  379.                                 case  uLongA:   formatArray( a, s, formatMod );
  380.                                                 continue; // while(true)
  381.                                 // --:
  382.                                 case  FloatA:
  383.                                 case DoubleA:
  384.                                 // --:
  385.                                 case   BoolA:
  386.                                 case PointerA:
  387.  
  388.                                         goto case Data;
  389.                         }
  390.  
  391.                         // If no Additional processing is needed
  392.                         if( ! s.hasAlignment )
  393.                         {
  394.                                 printer.print( s.str );
  395.                                 continue;
  396.                         }
  397.  
  398.                         // First Truncate
  399.                         with( AlignState.Type ) final switch( s.truncation.type )
  400.                         {
  401.                                 case Unset:
  402.                                         // Calc width upto alignMax because
  403.                                         // This can break early if width >= alignment
  404.                                         with ( s.truncation )
  405.                                         {
  406.                                                 disp = s.alignment.disp;
  407.                                                 truncTail( s ); // calcs s.width
  408.                                                 disp = 0;
  409.                                                 // ---
  410.                                                 s.strLeft = s.str;
  411.                                                 s.truncCharCount = 0;
  412.                                         }
  413.                                         break;
  414.                                 case Left:
  415.                                         truncHead( s );
  416.                                         break;
  417.                                 case Centre:
  418.                                         truncBody( s );
  419.                                         break;
  420.                                 case Default:
  421.                                 case Right:
  422.                                         truncTail( s );
  423.                                         break;
  424.                         }
  425.  
  426.                         // Alignment
  427.                         s.calcA( s.width );
  428.  
  429.                         // Print Aligned
  430.                         printAlign( s );
  431.  
  432.                 }
  433.                 catch( ParseException e )
  434.                 {
  435.                         printer.print("{Bad Format: " ~ e.msg ~ " }");
  436.  
  437.                         ptr = find!char( '}', cp, end ) + 1;
  438.                         if( ptr > end )
  439.                                 break;
  440.  
  441.                 }
  442.                 //catch( InvalidArgException e ) {}
  443.                 catch( UTFException e )
  444.                 {
  445.                         printer.print("{Malformed UTF8}");
  446.                 }
  447.                 } // End while {}
  448.  
  449.                 return;
  450.         }
  451.  
  452.         void printAlign( ref AlignState s )
  453.         {
  454.                 //REFACTOR ----
  455.                 writeSpace(    s.padL );
  456.                 printer.print( s.strLeft  );
  457.  
  458.                 foreach( x; 0..s.truncCharCount)
  459.                         printer.print( s.truncChar );
  460.  
  461.                 printer.print( s.strRight  );
  462.                 writeSpace(    s.padR );
  463.                 //REFACTOR ----
  464.         }
  465.  
  466.         /* *************************************
  467.                       Parse Helpers
  468.          ***************************************/
  469.  
  470.         // Padding Assistance function
  471.         //
  472.         // Question:  what if count is negative
  473.         //            Should i trim the string?
  474.         //            and if so from front/mid/end???
  475.         private void writeSpace( int count )
  476.         {
  477.                 // 16 space chars
  478.                 immutable char[16] pad = "                ";
  479.                 //mutable char[16] pad = "123456789abcdefX";
  480.  
  481.                 if( count <= 0 )
  482.                         return;
  483.  
  484.                 while( count > 16 )
  485.                 {
  486.                         printer.print( pad );
  487.                         count -= 16;
  488.                 }
  489.                 printer.print( pad[0..count] );
  490.         }
  491.         private void eatSpace( ref char_c* start, char_c* end )
  492.         {
  493.                 while( start !is end )
  494.                 {
  495.                         if( *start != ' ' )
  496.                                 return;
  497.                         start++;
  498.                 }
  499.                 throw new ParseException("Open");
  500.         }
  501.  
  502.         // Read Align/Trunc spec
  503.         private void parseAlign( out AlignState.Input a, ref char_c* cp, char_c* end, AlignState* s=null )
  504.         {
  505.                 with( AlignState.Type ) switch( *cp )
  506.                 {
  507.                         case '<': a.type = Left;        cp++; break;
  508.                         case '^': a.type = Centre;      cp++; break;
  509.                         case '>': a.type = Right;       cp++; break;
  510.                         default : a.type = Default;
  511.                 }
  512.  
  513.                 if( s !is null && cp !is end && *cp == '0' )
  514.                         s.padCharL = '0';
  515.  
  516.                 uint saget;
  517.                 if( parseUInt( saget, cp, end, Parse.Empty ) )
  518.                         a.type = AlignState.Type.Unset;
  519.                 a.disp = cast(int) saget;
  520.         }
  521.  
  522.  
  523.         /*
  524.          * Format Array and print
  525.          *
  526.          * TODO: if itoaAlign() Overflows
  527.          * TODO: cleanup terrible mess
  528.          */
  529.         private void formatArray( ref Variant v, ref AlignState s, char_c[] formatMod )
  530.         {
  531.                 // I dunno why I called it onion
  532.                 union Onion {
  533.                          long    i;
  534.                         ubyte[8] a;
  535.                 } static assert( Onion.sizeof == 8 );
  536.  
  537.                 int             size;           // size of the Element Type
  538.                 byte            signExtend;     // 0x80 for Signed, else 0
  539.                 char[64]        buffer;         // itoa
  540.                 itoaControl     ctrl;           //  〃
  541.                 Onion           onion;
  542.  
  543.                 immutable bool  compact = formatMod.length &&
  544.                                           formatMod[0] == 'c';
  545.  
  546.                 with( Variant.Type )
  547.                 switch( v.type )
  548.                 {
  549.                         case  ByteA: case  uByteA: size = 1; break;
  550.                         case ShortA: case uShortA: size = 2; break;
  551.                         case   IntA: case   uIntA: size = 4; break;
  552.                         case  LongA: case  uLongA: size = 8; break;
  553.  
  554.                         default: assert( false, "Not Numerical Array Type" );
  555.                 }
  556.  
  557.                 // Setup
  558.                 if( compact )
  559.                 {
  560.                         if( v.ByteA.length == 0 )
  561.                                 return;
  562.  
  563.                         parseFormatMod( s, ctrl, formatMod[1..$] );
  564.  
  565.                         if( s.alignment.type == AlignState.Type.Unset )
  566.                         {
  567.                                 s.alignment.type = AlignState.Type.Default;
  568.                                 s.alignment.disp = size*2;
  569.                                 s.padCharL       = '0';
  570.                         }
  571.  
  572.                         ctrl.prefixLeft= true;
  573.                         ctrl.base      = ctrl.Base.Hex;
  574.                         ctrl.isSigned  = false;
  575.                         signExtend     = 0;
  576.                 } else {
  577.                         if( v.ByteA.length == 0 )
  578.                         {
  579.                                 printer.print("[]");
  580.                                 return;
  581.                         }
  582.                         parseFormatMod( s, ctrl, formatMod );
  583.  
  584.                         buffer[62..64]= ", ";
  585.                         ctrl.padR     = 2;
  586.                         s.alignment.disp += 2;
  587.                         ctrl.isSigned = (v.type & 1);
  588.                         signExtend    = (v.type & 1) << 7; // MSB
  589.  
  590.                         debug { red(); printer.print("["); normal(); }
  591.                         else  {
  592.                         printer.print("[");
  593.                         }
  594.                 }
  595.  
  596.                 // Horrible Hack
  597.                 ubyte_c* cp  =      v.uByteA.ptr;
  598.                 ubyte_c* end = cp + v.uByteA.length * size - size;
  599.  
  600.                 // Derp
  601.                 if( s.hasAlignment )
  602.                 {
  603.                         for( ; cp < end; cp += size )
  604.                         {
  605.                                 auto s2 = s;
  606.  
  607.                                 // Sign Extend
  608.                                 onion.i = signExtend & cp[size-1];
  609.  
  610.                                 onion.a[0..size] = cp[0..size];
  611.                                 s2.str = itoa( onion.i, buffer, ctrl );
  612.  
  613.                                 // cast away const because s.str is a slice of buffer \\
  614.                                 itoaAlign( cast(char[]) s2.str, buffer, ctrl, s2 );
  615.  
  616.                                 printAlign( s2 );
  617.                         }
  618.  
  619.                         if( !compact )
  620.                         {
  621.                                 buffer[63] = ']';
  622.                                 ctrl.padR  = 1;
  623.                                 s.alignment.disp -= 1;
  624.                         }
  625.  
  626.                         onion.i = signExtend & cp[size-1];
  627.                         onion.a[0..size] = cp[0..size];
  628.                         s.str = itoa( onion.i, buffer, ctrl );
  629.                         // cast away const because s.str is a slice of buffer \\
  630.                         itoaAlign( cast(char[]) s.str, buffer, ctrl, s );
  631.  
  632.                         printAlign( s );
  633.                 }
  634.                 else
  635.                 {
  636.                         for( ; cp < end; cp += size )
  637.                         {
  638.                                 // Sign Extend
  639.                                 onion.i = cast(byte) (signExtend & cp[size-1]);
  640.  
  641.                                 onion.a[0..size] = cp[0..size];
  642.                                 printer.print( itoa( onion.i, buffer, ctrl ) );
  643.                         }
  644.  
  645.                         buffer[63] = ']';
  646.                         ctrl.padR  = 1;
  647.  
  648.                         onion.i = cast(byte) (signExtend & cp[size-1]);
  649.                         onion.a[0..size] = cp[0..size];
  650.                         printer.print( itoa( onion.i, buffer, ctrl ) );
  651.                 }
  652.         }
  653. /+      // Templated version
  654.         void formatArrayT( A : E[], E )( A a, State s, char_c[] formatMod )
  655.         {
  656.                 char[64]        buffer;
  657.                 itoaControl     ctrl;
  658.  
  659.                 buffer[62..64] = ", ";
  660.                 ctrl.padR      = 2;
  661.                 ctrl.isSigned  = isSigned!E;
  662.  
  663.                 parseFormatMod( s, ctrl, formatMod );
  664.  
  665.                 printer.print("[");
  666.                 foreach( e; a[0..$-1] )
  667.                 {
  668.                         printer.print( itoa( e, buffer, ctrl ) );
  669.                 }
  670.  
  671.                 buffer[63] = ']';
  672.                 ctrl.padR  =  1;
  673.                 printer.print( itoa( a[$-1], buffer, ctrl ) );
  674.         }
  675. +/
  676.  
  677.         /* *************************************
  678.                         Trunc code
  679.          ***************************************/
  680.  
  681.         // TODO list
  682.         // 1) optimise for ascii
  683.         // 2) deal with control/non-printable Unicode chars
  684.  
  685.         // This is a pain because there is no easy way to do this
  686.         private void truncHead( ref AlignState s )
  687.         {
  688.                 char_c* beg = s.str.ptr;
  689.                 char_c* end = s.str.ptr + s.str.length;
  690.                 char_c* cp  = s.str.ptr + s.str.length-1;
  691.                 auto    columns = s.truncation.disp;
  692.                 dchar   d;
  693.  
  694.                 for( ; beg <= cp;  cp-- )
  695.                 {
  696.                         // If we are in a multibyte
  697.                         if( (*cp & 0xC0) == 0x80 )
  698.                                 continue;
  699.  
  700.                         // Fast lane
  701.                         if( columns > 2 )
  702.                         {
  703.                                            UTF8.toUTF32( d, cp, end );
  704.                                 columns -= printer.width( d );
  705.                                 continue;
  706.                         }
  707.  
  708.                         auto temp1 = UTF8.toUTF32( d, cp, end );
  709.                         auto temp2 = printer.width( d );
  710.                         columns -= temp2;
  711.  
  712.                         if( columns < 1 )
  713.                         {
  714.                                 if( cp is beg && columns == 0 )
  715.                                         break;
  716.  
  717.                                 // Undo the last char
  718.                                      cp += temp1;
  719.                                 columns += temp2;
  720.  
  721.                                 s.strRight      = s.str[ cp - beg .. $ ];
  722.                                 s.width         = s.truncation.disp;
  723.                                 s.truncCharCount= columns;
  724.                                 return;
  725.                         }
  726.                 }
  727.                 s.strRight      = s.str;
  728.                 s.width         = s.truncation.disp - columns;
  729.                 s.truncCharCount=0;
  730.                 return;
  731.         }
  732.  
  733.         private void truncBody( ref AlignState s )
  734.         {
  735.                 //REFACTOR
  736.                 switch( s.truncation.disp )
  737.                 {
  738.                         case 1:
  739.                                 s.strLeft  = "…";
  740.                                 s.strRight = null;
  741.                                 s.width    = 1;
  742.                                 return;
  743.                         case 2:
  744.                         //      s.strRight = null;
  745.                                 return truncTail( s );
  746.                         default:
  747.                                 auto save = s.truncation.disp;
  748.  
  749.                                 s.truncation.disp = save - save/2;
  750.                                 truncHead( s );
  751.                                 s.truncation.disp = save/2; // real
  752.                 }
  753.  
  754.                 char_c* beg = s.str.ptr;
  755.                 char_c* end = s.strRight.ptr; // s.str.ptr + s.str.length;
  756.                 char_c* cp  = s.str.ptr;
  757.                 auto    columns = s.truncation.disp;
  758.                 dchar   d;
  759.  
  760.                 // Similar version of TruncTail
  761.                 while( cp < end )
  762.                 {
  763.                         // Fast lane
  764.                         if( columns > 2 )
  765.                         {
  766.                                      cp += UTF8.toUTF32( d, cp, end );
  767.                                 columns -= printer.width( d );
  768.                                 continue;
  769.                         }
  770.  
  771.                         // attempt one more char
  772.                         auto temp1 = UTF8.toUTF32( d, cp, end );
  773.                         auto temp2 = printer.width( d );
  774.  
  775.                         if( columns-temp2 >= 0 )
  776.                         {
  777.                                      cp += temp1;
  778.                                 columns -= temp2;
  779.                                 continue;
  780.                         }
  781.  
  782.                         // t.disp >= s.width ( don't use truncChar )
  783.                         if( cp+temp1 is end && temp2 <= s.truncCharCount + columns )
  784.                         {
  785.                                     cp += temp1;
  786.                                 columns = 0;
  787.                                 s.truncCharCount = 0;
  788.                         }
  789.                         else if ( columns-temp2 == -1 && s.truncCharCount == 2 )
  790.                         {
  791.                         // rare case when we end up with 3 '…' in the middle
  792.                                       cp += temp1;
  793.                         s.truncCharCount -= temp2;
  794.                         }
  795.  
  796.                         s.strLeft        = s.str[ 0 .. cp - beg ];
  797.                         s.width         += s.truncation.disp;
  798.                         s.truncCharCount+= columns;
  799.                         return;
  800.                 }
  801. //              We c̲a̲n̲ actually get here
  802.                 s.strLeft        = s.str;
  803.                 s.strRight       = null;
  804.                 s.width         += s.truncation.disp - columns - s.truncCharCount;
  805.                 s.truncCharCount =0;
  806.                 return;
  807.         }
  808.  
  809.         private void truncTail( ref AlignState s )
  810.         {
  811.                 char_c* beg = s.str.ptr;
  812.                 char_c* end = s.str.ptr + s.str.length;
  813.                 char_c* cp  = s.str.ptr;
  814.                 auto    columns = s.truncation.disp;
  815.                 dchar   d;
  816.  
  817.                 while( cp < end )
  818.                 {
  819.                         // Fast lane
  820.                         if( columns > 2 )
  821.                         {
  822.                                      cp += UTF8.toUTF32( d, cp, end );
  823.                                 columns -= printer.width( d );
  824.                                 continue;
  825.                         }
  826.  
  827.                         // attempt one more char
  828.                         auto temp1 = UTF8.toUTF32( d, cp, end );
  829.                         auto temp2 = printer.width( d );
  830.                         columns -= temp2;
  831.  
  832.                         if( columns > 0 )
  833.                         {
  834.                                 cp += temp1;
  835.                                 continue;
  836.                         }
  837.                         if( cp+1 is end && columns == 0 )
  838.                                 break;
  839.  
  840.                         s.strLeft       = s.str[ 0 .. cp - beg ];
  841.                         s.width         = s.truncation.disp;
  842.                         s.truncCharCount= columns + temp2;
  843.                         return;
  844.                 }
  845.  
  846.                 s.strLeft       = s.str;
  847.                 s.width         = s.truncation.disp - columns;
  848.                 s.truncCharCount=0;
  849.                 return;
  850.         }
  851. }
  852.  
  853. /*
  854.  * Alignment:
  855.  *
  856.  * + : prefix with '+' or '-'
  857.  * - : prefix with '␠' or '-'
  858.  *   : none = only prefix '-'   ( default )
  859.  *
  860.  * Format:
  861.  *
  862.  * b : Binary                   [ No ]
  863.  * c : UTF8 character           [ TODO ]
  864.  * d : Decimal                  ( default )
  865.  * o : Octal
  866.  * x : Hexadecimal Lowercase
  867.  * X : Hexadecimal Uppercase
  868.  * n : Separate     Thousands comma
  869.  * _ : Separate Ten Thousands underscore
  870.  */
  871. void parseFormatMod( ref AlignState s, ref itoaControl ctrl, char_c[] formatMod ) pure
  872. {
  873.         // TODO: Make this more general
  874.         // instead of '0' being special.
  875.         if ( s.padCharL == '0' )
  876.                 ctrl.prefixLeft = true;
  877.  
  878.         with( itoaControl )
  879.         foreach( c; formatMod )
  880.         switch( c )
  881.         {
  882.                 case '+': ctrl.sign = Sign.Plus;        break;
  883.                 case '-': ctrl.sign = Sign.Minus;       break;
  884.                 //
  885.                 case 'x': ctrl.lower = true;    // fall through
  886.                 case 'X': ctrl.base  = Base.Hex;        break;
  887.                 case 'd': ctrl.base  = Base.Dec;        break;
  888.                 case 'o': ctrl.base  = Base.Oct;        break;
  889.                 //se 'b': ctrl.base = 2 ;               break;
  890.                 //
  891.                 case 'n': ctrl.group  = Group.Thou;
  892.                           ctrl.grpChar= ',';            break;
  893.                 case '_': ctrl.group  = Group.TThou;
  894.                           ctrl.grpChar= '_';            break;
  895.                 default: {
  896.                         throw new ParseException("Malformed formatMod");
  897.                 }
  898.         }
  899. }
  900.  
  901. /* ******************************************************************************
  902.                                 Unit Tests!
  903.  *******************************************************************************/
  904.  
  905. version(unittest)
  906. {
  907.  
  908. unittest
  909. {
  910.         //import shd.c.glibc: LC, setlocale;
  911.         import shd.c.glibc;
  912.         import shd.output;
  913.  
  914.         setlocale( LC.ALL, "en_GB.UTF-8" );
  915.  
  916.         alias Shdout T;
  917.  
  918.         string[] trunc = [
  919.                 "abcdefghijklmnopqrstuvwxyz",
  920.                  "A一一一一一一一一一一一一一一一一一一一A",
  921.                  "一一一一一一一一一一一一一一一一一一一一",
  922.                 ];
  923.                 // extended
  924.         string[] trunc2 = [
  925.                  "一一一一一一一一一一一一一一一一一一一A",
  926.                  "A一一一一一一一一一一一一一一一一一一一",
  927.                  "abcdefghijklmnopqrs一一一一一一一一一一一一一一一一",
  928.                  "abcdefghijklmnopqrs一一一一一一一一一一一一一一一A",
  929.                  "A一一一一一一一一一一一一一一一一一jklmnopqrstuvwxyz",
  930.                   "一一一一一一一一一一一一一一一一一jklmnopqrstuvwxyz",
  931.                 ];
  932.  
  933.         char_c[] truncHeadGlue( char_c[] str, int columns )
  934.         {
  935.                 AlignState s;
  936.  
  937.                 s.str = str;
  938.                 s.truncation.disp = columns;
  939.  
  940.                 T.truncHead( s );
  941.  
  942.                 auto n = s.truncCharCount;
  943.  
  944.                 return "………"[0..(n*3)] ~ s.strRight;
  945.         }
  946.         char_c[] truncBodyGlue( char_c[] str, int columns )
  947.         {
  948.                 AlignState s;
  949.  
  950.                 s.str = str;
  951.                 s.truncation.disp = columns;
  952.  
  953.                 T.truncBody( s );
  954.  
  955.                 auto n = s.truncCharCount;
  956.  
  957.                 return s.strLeft ~ "………"[0..(n*3)] ~ s.strRight;
  958.  
  959.         }
  960.         char_c[] truncTailGlue( char_c[] str, int columns )
  961.         {
  962.                 AlignState s;
  963.  
  964.                 s.str = str;
  965.                 s.truncation.disp = columns;
  966.  
  967.                 T.truncTail( s );
  968.  
  969.                 auto n = s.truncCharCount;
  970.  
  971.                 return s.strLeft ~ "………"[0..(n*3)];
  972.  
  973.                 // TODO check i
  974.         }
  975.  
  976.         // Trunc Head
  977.         {
  978.                 alias truncHeadGlue func;
  979.                 string[] ans = [
  980.                         "…","…","…",
  981.                         //2
  982.                         "…z","…A","……",
  983.                         //3
  984.                         "…yz","……A","…一",
  985.                         //4
  986.                         "…xyz","…一A","……一",
  987.                         //5
  988.                         "…wxyz",
  989.                         "……一A",
  990.                         "…一一",
  991.                         //6
  992.                         "…vwxyz",
  993.                         "…一一A",
  994.                         "……一一",
  995.                         //20,21,22
  996.                         "…hijklmnopqrstuvwxyz",
  997.                         "…一一一一一一一一一A",
  998.                         "……一一一一一一一一一",
  999.                         "…ghijklmnopqrstuvwxyz",
  1000.                         "……一一一一一一一一一A",
  1001.                         "…一一一一一一一一一一",
  1002.                         "…fghijklmnopqrstuvwxyz",
  1003.                         "…一一一一一一一一一一A",
  1004.                         "……一一一一一一一一一一",
  1005.                 ];
  1006.                 int i;
  1007.                 foreach( t; trunc ) { assert( func( t,  1) == ans[i++] ); }
  1008.                 foreach( t; trunc ) { assert( func( t,  2) == ans[i++] ); }
  1009.                 foreach( t; trunc ) { assert( func( t,  3) == ans[i++] ); }
  1010.                 foreach( t; trunc ) { assert( func( t,  4) == ans[i++] ); }
  1011.                 foreach( t; trunc ) { assert( func( t,  5) == ans[i++] ); }
  1012.                 foreach( t; trunc ) { assert( func( t,  6) == ans[i++] ); }
  1013.  
  1014.                 foreach( t; trunc ) { assert( func( t, 20) == ans[i++] ); }
  1015.                 foreach( t; trunc ) { assert( func( t, 21) == ans[i++] ); }
  1016.                 foreach( t; trunc ) { assert( func( t, 22) == ans[i++] ); }
  1017.         }
  1018.         // Trunc Tail
  1019.         {
  1020.                 alias truncTailGlue func;
  1021.                 string[] ans = [
  1022.                         "…","…","…",
  1023.                         //2
  1024.                         "a…","A…","……",
  1025.                         //3
  1026.                         "ab…","A……","一…",
  1027.                         //4
  1028.                         "abc…","A一…","一……",
  1029.                         //5
  1030.                         "abcd…",
  1031.                         "A一……",
  1032.                         "一一…",
  1033.                         //6
  1034.                         "abcde…",
  1035.                         "A一一…",
  1036.                         "一一……",
  1037.                         //20,21,22
  1038.                         "abcdefghijklmnopqrs…",
  1039.                         "A一一一一一一一一一…",
  1040.                         "一一一一一一一一一……",
  1041.                         "abcdefghijklmnopqrst…",
  1042.                         "A一一一一一一一一一……",
  1043.                         "一一一一一一一一一一…",
  1044.                         "abcdefghijklmnopqrstu…",
  1045.                         "A一一一一一一一一一一…",
  1046.                         "一一一一一一一一一一……",
  1047.                 ];
  1048.                 int i;
  1049.                 foreach( t; trunc ) { assert( func( t,  1) == ans[i++] ); }
  1050.                 foreach( t; trunc ) { assert( func( t,  2) == ans[i++] ); }
  1051.                 foreach( t; trunc ) { assert( func( t,  3) == ans[i++] ); }
  1052.                 foreach( t; trunc ) { assert( func( t,  4) == ans[i++] ); }
  1053.                 foreach( t; trunc ) { assert( func( t,  5) == ans[i++] ); }
  1054.                 foreach( t; trunc ) { assert( func( t,  6) == ans[i++] ); }
  1055.  
  1056.                 foreach( t; trunc ) { assert( func( t, 20) == ans[i++] ); }
  1057.                 foreach( t; trunc ) { assert( func( t, 21) == ans[i++] ); }
  1058.                 foreach( t; trunc ) { assert( func( t, 22) == ans[i++] ); }
  1059.         }
  1060.         // Trunc Body
  1061.         {
  1062.                 alias truncBodyGlue func;
  1063.                 string[] ans = [
  1064.                         "…","…","…",
  1065.                         //2
  1066.                         "a…","A…","……",
  1067.                         //3
  1068.                         "a…z","A…A","一…",
  1069.                         //4
  1070.                         "ab…z","A……A","一……",
  1071.                         //5↓
  1072.                         "ab…yz",
  1073.                         "A一…A",
  1074.                         "一…一",
  1075.                         //6 ↓
  1076.                         "abc…yz",
  1077.                         "A一……A",
  1078.                         "一……一",
  1079.                         //20       ↓
  1080.                         "abcdefghij…rstuvwxyz",
  1081.                         "A一一一一……一一一一A",
  1082.                         "一一一一一……一一一一",
  1083.                         //21       ↓
  1084.                         "abcdefghij…qrstuvwxyz",
  1085.                         "A一一一一一…一一一一A",
  1086.                         "一一一一一…一一一一一",
  1087.                         //22        ↓
  1088.                         "abcdefghijk…qrstuvwxyz",
  1089.                         "A一一一一一……一一一一A",
  1090.                         "一一一一一……一一一一一",
  1091.                 ];
  1092.                 int i;
  1093.                 foreach( t; trunc ) { assert( func( t,  1) == ans[i++] ); }
  1094.                 foreach( t; trunc ) { assert( func( t,  2) == ans[i++] ); }
  1095.                 foreach( t; trunc ) { assert( func( t,  3) == ans[i++] ); }
  1096.                 foreach( t; trunc ) { assert( func( t,  4) == ans[i++] ); }
  1097.                 foreach( t; trunc ) { assert( func( t,  5) == ans[i++] ); }
  1098.                 foreach( t; trunc ) { assert( func( t,  6) == ans[i++] ); }
  1099.  
  1100.                 foreach( t; trunc ) { assert( func( t, 20) == ans[i++] ); }
  1101.                 foreach( t; trunc ) { assert( func( t, 21) == ans[i++] ); }
  1102.                 foreach( t; trunc ) { assert( func( t, 22) == ans[i++] ); }
  1103.  
  1104.                 // extended
  1105.                 string[] ans2 = [
  1106.                         //1
  1107.                         "…","…","…","…","…","…",
  1108.                         //2
  1109.                         "……","A…","a…","a…","A…","……",
  1110.                         //3
  1111.                         "……A","A……","a……","a…A","A…z","……z",
  1112.                         //4
  1113.                         "A一…一",
  1114.                         "abc…一",
  1115.                         "abc……A",
  1116.                         "A一…yz",
  1117.                         "一……yz",
  1118.                         //5↓
  1119.                         "一……A",
  1120.                         "A……一",
  1121.                         "ab…一",
  1122.                         "ab……A",
  1123.                         "A……yz",
  1124.                         "一…yz",
  1125.                         //6 ↓
  1126.                         "一一…A", // Good no triple Ellipsis
  1127.                         "A一…一",
  1128.                         "abc…一",
  1129.                         "abc……A",
  1130.                         "A一…yz",
  1131.                         "一……yz",
  1132.                         //20       ↓
  1133.                         "一一一一一…一一一一A",
  1134.                         "A一一一一一…一一一一",
  1135.                         "abcdefghij……一一一一",
  1136.                         "abcdefghij…一一一一A",
  1137.                         "A一一一一……rstuvwxyz",
  1138.                         "一一一一一…rstuvwxyz",
  1139.                         //21       ↓
  1140.                         "一一一一一……一一一一A",
  1141.                         "A一一一一……一一一一一",
  1142.                         "abcdefghij…一一一一一",
  1143.                         "abcdefghij……一一一一A",
  1144.                         "A一一一一……qrstuvwxyz",
  1145.                         "一一一一一…qrstuvwxyz",
  1146.                         //22        ↓
  1147.                         "一一一一一一…一一一一A",
  1148.                         "A一一一一一…一一一一一",
  1149.                         "abcdefghijk…一一一一一",
  1150.                         "abcdefghijk……一一一一A",
  1151.                         "A一一一一一…qrstuvwxyz",
  1152.                         "一一一一一……qrstuvwxyz",
  1153.                 ];
  1154.                 i=0;
  1155.                 foreach( t; trunc ) { assert( func( t,  1) == ans[i++] ); }
  1156.                 foreach( t; trunc ) { assert( func( t,  2) == ans[i++] ); }
  1157.                 foreach( t; trunc ) { assert( func( t,  3) == ans[i++] ); }
  1158.                 foreach( t; trunc ) { assert( func( t,  4) == ans[i++] ); }
  1159.                 foreach( t; trunc ) { assert( func( t,  5) == ans[i++] ); }
  1160.                 foreach( t; trunc ) { assert( func( t,  6) == ans[i++] ); }
  1161.  
  1162.                 foreach( t; trunc ) { assert( func( t, 20) == ans[i++] ); }
  1163.                 foreach( t; trunc ) { assert( func( t, 21) == ans[i++] ); }
  1164.                 foreach( t; trunc ) { assert( func( t, 22) == ans[i++] ); }
  1165.  
  1166.                 /+      // Generate Answers
  1167.                 {
  1168.                         alias truncBodyGlue func;
  1169.  
  1170.                         foreach( i; [1,2,3,4] )
  1171.                         {
  1172.                                 Shdout.format("\n//{}\n",i);
  1173.                                 foreach( t; trunc2 ) {  Shdout.format( "\"{}\",", func( t,  i)); }
  1174.                         }
  1175.                         Shdout.print("\n");
  1176.                         foreach( i; [5,6,20,21,22] )
  1177.                         {
  1178.                                 auto o = ( i > 9 ? 3 : 2 );
  1179.                                 Shdout.format("//{}",i);
  1180.                                 foreach( x; 0..(i/2)-o) Shdout.print(" "); Shdout.print("↓\n");
  1181.                                 foreach( t; trunc2 ) {  Shdout.format( "\"{}\",\n", func( t,  i)); }
  1182.                         }
  1183.                 }// +/
  1184.         }
  1185. }
  1186.  
  1187. unittest
  1188. {
  1189.         import shd.output;
  1190.         Shdout.println(">---Start format / itoa  --<");
  1191.  
  1192.         immutable int n = 123;
  1193.         string[] Question = [
  1194.                         "|{ ,0:+}|", "|{ ,0:-}|", "|{ ,0}|",
  1195.                         "|{ ,1:+}|", "|{ ,1:-}|", "|{ ,1}|",
  1196.                         "|{ ,2:+}|", "|{ ,2:-}|", "|{ ,2}|",
  1197.                         "|{ ,3:+}|", "|{ ,3:-}|", "|{ ,3}|",
  1198.                         "|{ ,4:+}|", "|{ ,4:-}|", "|{ ,4}|",
  1199.                         "|{ ,5:+}|", "|{ ,5:-}|", "|{ ,5}|",
  1200.                         "|{ ,6:+}|", "|{ ,6:-}|", "|{ ,6}|",
  1201.                         "|{ ,7:+}|", "|{ ,7:-}|", "|{ ,7}|",
  1202.                         "|{ ,8:+}|", "|{ ,8:-}|", "|{ ,8}|",
  1203.                         "|{ ,9:+}|", "|{ ,9:-}|", "|{ ,9}|",
  1204.                         "|{,10:+}|", "|{,10:-}|", "|{,10}|",
  1205.                         "|{,11:+}|", "|{,11:-}|", "|{,11}|",
  1206.                         "|{,12:+}|", "|{,12:-}|", "|{,12}|",
  1207.                         "|{,13:+}|", "|{,13:-}|", "|{,13}|",
  1208.                         "|{,14:+}|", "|{,14:-}|", "|{,14}|",
  1209.                         "|{,15:+}|", "|{,15:-}|", "|{,15}|",
  1210.                         "|{,16:+}|", "|{,16:-}|", "|{,16}|",
  1211.  
  1212.                         "|{ ,<0:+}|", "|{ ,<0:-}|", "|{ ,<0}|",
  1213.                         "|{ ,<1:+}|", "|{ ,<1:-}|", "|{ ,<1}|",
  1214.                         "|{ ,<2:+}|", "|{ ,<2:-}|", "|{ ,<2}|",
  1215.                         "|{ ,<3:+}|", "|{ ,<3:-}|", "|{ ,<3}|",
  1216.                         "|{ ,<4:+}|", "|{ ,<4:-}|", "|{ ,<4}|",
  1217.                         "|{ ,<5:+}|", "|{ ,<5:-}|", "|{ ,<5}|",
  1218.                         "|{ ,<6:+}|", "|{ ,<6:-}|", "|{ ,<6}|",
  1219.                         "|{ ,<7:+}|", "|{ ,<7:-}|", "|{ ,<7}|",
  1220.                         "|{ ,<8:+}|", "|{ ,<8:-}|", "|{ ,<8}|",
  1221.                         "|{ ,<9:+}|", "|{ ,<9:-}|", "|{ ,<9}|",
  1222.                         "|{,<10:+}|", "|{,<10:-}|", "|{,<10}|",
  1223.                         "|{,<11:+}|", "|{,<11:-}|", "|{,<11}|",
  1224.                         "|{,<12:+}|", "|{,<12:-}|", "|{,<12}|",
  1225.                         "|{,<13:+}|", "|{,<13:-}|", "|{,<13}|",
  1226.                         "|{,<14:+}|", "|{,<14:-}|", "|{,<14}|",
  1227.                         "|{,<15:+}|", "|{,<15:-}|", "|{,<15}|",
  1228.                         "|{,<16:+}|", "|{,<16:-}|", "|{,<16}|",
  1229.  
  1230.                         "|{ ,^0:+}|", "|{ ,^0:-}|", "|{ ,^0}|",
  1231.                         "|{ ,^1:+}|", "|{ ,^1:-}|", "|{ ,^1}|",
  1232.                         "|{ ,^2:+}|", "|{ ,^2:-}|", "|{ ,^2}|",
  1233.                         "|{ ,^3:+}|", "|{ ,^3:-}|", "|{ ,^3}|",
  1234.                         "|{ ,^4:+}|", "|{ ,^4:-}|", "|{ ,^4}|",
  1235.                         "|{ ,^5:+}|", "|{ ,^5:-}|", "|{ ,^5}|",
  1236.                         "|{ ,^6:+}|", "|{ ,^6:-}|", "|{ ,^6}|",
  1237.                         "|{ ,^7:+}|", "|{ ,^7:-}|", "|{ ,^7}|",
  1238.                         "|{ ,^8:+}|", "|{ ,^8:-}|", "|{ ,^8}|",
  1239.                         "|{ ,^9:+}|", "|{ ,^9:-}|", "|{ ,^9}|",
  1240.                         "|{,^10:+}|", "|{,^10:-}|", "|{,^10}|",
  1241.                         "|{,^11:+}|", "|{,^11:-}|", "|{,^11}|",
  1242.                         "|{,^12:+}|", "|{,^12:-}|", "|{,^12}|",
  1243.                         "|{,^13:+}|", "|{,^13:-}|", "|{,^13}|",
  1244.                         "|{,^14:+}|", "|{,^14:-}|", "|{,^14}|",
  1245.                         "|{,^15:+}|", "|{,^15:-}|", "|{,^15}|",
  1246.                         "|{,^16:+}|", "|{,^16:-}|", "|{,^16}|",
  1247.                         ];
  1248.         string[] Question0 = [
  1249.                         "|{ ,01:+}|", "|{ ,01:-}|", "|{ ,01}|",
  1250.                         "|{ ,02:+}|", "|{ ,02:-}|", "|{ ,02}|",
  1251.                         "|{ ,03:+}|", "|{ ,03:-}|", "|{ ,03}|",
  1252.                         "|{ ,04:+}|", "|{ ,04:-}|", "|{ ,04}|",
  1253.                         "|{ ,05:+}|", "|{ ,05:-}|", "|{ ,05}|",
  1254.                         "|{ ,06:+}|", "|{ ,06:-}|", "|{ ,06}|",
  1255.                         "|{ ,07:+}|", "|{ ,07:-}|", "|{ ,07}|",
  1256.                         "|{ ,08:+}|", "|{ ,08:-}|", "|{ ,08}|",
  1257.                         "|{ ,09:+}|", "|{ ,09:-}|", "|{ ,09}|",
  1258.                         "|{,010:+}|", "|{,010:-}|", "|{,010}|",
  1259.                         "|{,011:+}|", "|{,011:-}|", "|{,011}|",
  1260.                         "|{,012:+}|", "|{,012:-}|", "|{,012}|",
  1261.                         "|{,013:+}|", "|{,013:-}|", "|{,013}|",
  1262.                         "|{,014:+}|", "|{,014:-}|", "|{,014}|",
  1263.                         "|{,015:+}|", "|{,015:-}|", "|{,015}|",
  1264.                         "|{,016:+}|", "|{,016:-}|", "|{,016}|",
  1265.  
  1266.                         "|{ ,<01:+}|", "|{ ,<01:-}|", "|{ ,<01}|",
  1267.                         "|{ ,<02:+}|", "|{ ,<02:-}|", "|{ ,<02}|",
  1268.                         "|{ ,<03:+}|", "|{ ,<03:-}|", "|{ ,<03}|",
  1269.                         "|{ ,<04:+}|", "|{ ,<04:-}|", "|{ ,<04}|",
  1270.                         "|{ ,<05:+}|", "|{ ,<05:-}|", "|{ ,<05}|",
  1271.                         "|{ ,<06:+}|", "|{ ,<06:-}|", "|{ ,<06}|",
  1272.                         "|{ ,<07:+}|", "|{ ,<07:-}|", "|{ ,<07}|",
  1273.                         "|{ ,<08:+}|", "|{ ,<08:-}|", "|{ ,<08}|",
  1274.                         "|{ ,<09:+}|", "|{ ,<09:-}|", "|{ ,<09}|",
  1275.                         "|{,<010:+}|", "|{,<010:-}|", "|{,<010}|",
  1276.                         "|{,<011:+}|", "|{,<011:-}|", "|{,<011}|",
  1277.                         "|{,<012:+}|", "|{,<012:-}|", "|{,<012}|",
  1278.                         "|{,<013:+}|", "|{,<013:-}|", "|{,<013}|",
  1279.                         "|{,<014:+}|", "|{,<014:-}|", "|{,<014}|",
  1280.                         "|{,<015:+}|", "|{,<015:-}|", "|{,<015}|",
  1281.                         "|{,<016:+}|", "|{,<016:-}|", "|{,<016}|",
  1282.  
  1283.                         "|{ ,^01:+}|", "|{ ,^01:-}|", "|{ ,^01}|",
  1284.                         "|{ ,^02:+}|", "|{ ,^02:-}|", "|{ ,^02}|",
  1285.                         "|{ ,^03:+}|", "|{ ,^03:-}|", "|{ ,^03}|",
  1286.                         "|{ ,^04:+}|", "|{ ,^04:-}|", "|{ ,^04}|",
  1287.                         "|{ ,^05:+}|", "|{ ,^05:-}|", "|{ ,^05}|",
  1288.                         "|{ ,^06:+}|", "|{ ,^06:-}|", "|{ ,^06}|",
  1289.                         "|{ ,^07:+}|", "|{ ,^07:-}|", "|{ ,^07}|",
  1290.                         "|{ ,^08:+}|", "|{ ,^08:-}|", "|{ ,^08}|",
  1291.                         "|{ ,^09:+}|", "|{ ,^09:-}|", "|{ ,^09}|",
  1292.                         "|{,^010:+}|", "|{,^010:-}|", "|{,^010}|",
  1293.                         "|{,^011:+}|", "|{,^011:-}|", "|{,^011}|",
  1294.                         "|{,^012:+}|", "|{,^012:-}|", "|{,^012}|",
  1295.                         "|{,^013:+}|", "|{,^013:-}|", "|{,^013}|",
  1296.                         "|{,^014:+}|", "|{,^014:-}|", "|{,^014}|",
  1297.                         "|{,^015:+}|", "|{,^015:-}|", "|{,^015}|",
  1298.                         "|{,^016:+}|", "|{,^016:-}|", "|{,^016}|",
  1299.                         ];
  1300.         string[] Answer = [
  1301.                 // Default
  1302.                 "|+123|",               "| 123|",               "|123|",
  1303.                 "|+123|",               "| 123|",               "|123|",
  1304.                 "|+123|",               "| 123|",               "|123|",
  1305.                 "|+123|",               "| 123|",               "|123|",
  1306.                 "|+123|",               "| 123|",               "| 123|",
  1307.                 "| +123|",              "|  123|",              "|  123|",
  1308.                 "|  +123|",             "|   123|",             "|   123|",
  1309.                 "|   +123|",            "|    123|",            "|    123|",
  1310.                 "|    +123|",           "|     123|",           "|     123|",
  1311.                 "|     +123|",          "|      123|",          "|      123|",
  1312.                 "|      +123|",         "|       123|",         "|       123|",
  1313.                 "|       +123|",        "|        123|",        "|        123|",
  1314.                 "|        +123|",       "|         123|",       "|         123|",
  1315.                 "|         +123|",      "|          123|",      "|          123|",
  1316.                 "|          +123|",     "|           123|",     "|           123|",
  1317.                 "|           +123|",    "|            123|",    "|            123|",
  1318.                 "|            +123|",   "|             123|",   "|             123|",
  1319.                 // Left
  1320.                 "|+123|",               "| 123|",               "|123|",
  1321.                 "|+123|",               "| 123|",               "|123|",
  1322.                 "|+123|",               "| 123|",               "|123|",
  1323.                 "|+123|",               "| 123|",               "|123|",
  1324.                 "|+123|",               "| 123|",               "|123 |",
  1325.                 "|+123 |",              "| 123 |",              "|123  |",
  1326.                 "|+123  |",             "| 123  |",             "|123   |",
  1327.                 "|+123   |",            "| 123   |",            "|123    |",
  1328.                 "|+123    |",           "| 123    |",           "|123     |",
  1329.                 "|+123     |",          "| 123     |",          "|123      |",
  1330.                 "|+123      |",         "| 123      |",         "|123       |",
  1331.                 "|+123       |",        "| 123       |",        "|123        |",
  1332.                 "|+123        |",       "| 123        |",       "|123         |",
  1333.                 "|+123         |",      "| 123         |",      "|123          |",
  1334.                 "|+123          |",     "| 123          |",     "|123           |",
  1335.                 "|+123           |",    "| 123           |",    "|123            |",
  1336.                 "|+123            |",   "| 123            |",   "|123             |",
  1337.                 // Centre
  1338.                 "|+123|",               "| 123|",               "|123|",
  1339.                 "|+123|",               "| 123|",               "|123|",
  1340.                 "|+123|",               "| 123|",               "|123|",
  1341.                 "|+123|",               "| 123|",               "|123|",
  1342.                 "|+123|",               "| 123|",               "|123 |",
  1343.                 "|+123 |",              "| 123 |",              "| 123 |",
  1344.                 "| +123 |",             "|  123 |",             "| 123  |",
  1345.                 "| +123  |",            "|  123  |",            "|  123  |",
  1346.                 "|  +123  |",           "|   123  |",           "|  123   |",
  1347.                 "|  +123   |",          "|   123   |",          "|   123   |",
  1348.                 "|   +123   |",         "|    123   |",         "|   123    |",
  1349.                 "|   +123    |",        "|    123    |",        "|    123    |",
  1350.                 "|    +123    |",       "|     123    |",       "|    123     |",
  1351.                 "|    +123     |",      "|     123     |",      "|     123     |",
  1352.                 "|     +123     |",     "|      123     |",     "|     123      |",
  1353.                 "|     +123      |",    "|      123      |",    "|      123      |",
  1354.                 "|      +123      |",   "|       123      |",   "|      123       |",
  1355.                 ];
  1356.         string[] Answer0 = [
  1357.                 // Default
  1358.                 "|+123|",               "| 123|",               "|123|",
  1359.                 "|+123|",               "| 123|",               "|123|",
  1360.                 "|+123|",               "| 123|",               "|123|",
  1361.                 "|+123|",               "| 123|",               "|0123|",
  1362.                 "|+0123|",              "| 0123|",              "|00123|",
  1363.                 "|+00123|",             "| 00123|",             "|000123|",
  1364.                 "|+000123|",            "| 000123|",            "|0000123|",
  1365.                 "|+0000123|",           "| 0000123|",           "|00000123|",
  1366.                 "|+00000123|",          "| 00000123|",          "|000000123|",
  1367.                 "|+000000123|",         "| 000000123|",         "|0000000123|",
  1368.                 "|+0000000123|",        "| 0000000123|",        "|00000000123|",
  1369.                 "|+00000000123|",       "| 00000000123|",       "|000000000123|",
  1370.                 "|+000000000123|",      "| 000000000123|",      "|0000000000123|",
  1371.                 "|+0000000000123|",     "| 0000000000123|",     "|00000000000123|",
  1372.                 "|+00000000000123|",    "| 00000000000123|",    "|000000000000123|",
  1373.                 "|+000000000000123|",   "| 000000000000123|",   "|0000000000000123|",
  1374.                 // Left
  1375.                 "|+123|",               "| 123|",               "|123|",
  1376.                 "|+123|",               "| 123|",               "|123|",
  1377.                 "|+123|",               "| 123|",               "|123|",
  1378.                 "|+123|",               "| 123|",               "|123 |",
  1379.                 "|+123 |",              "| 123 |",              "|123  |",
  1380.                 "|+123  |",             "| 123  |",             "|123   |",
  1381.                 "|+123   |",            "| 123   |",            "|123    |",
  1382.                 "|+123    |",           "| 123    |",           "|123     |",
  1383.                 "|+123     |",          "| 123     |",          "|123      |",
  1384.                 "|+123      |",         "| 123      |",         "|123       |",
  1385.                 "|+123       |",        "| 123       |",        "|123        |",
  1386.                 "|+123        |",       "| 123        |",       "|123         |",
  1387.                 "|+123         |",      "| 123         |",      "|123          |",
  1388.                 "|+123          |",     "| 123          |",     "|123           |",
  1389.                 "|+123           |",    "| 123           |",    "|123            |",
  1390.                 "|+123            |",   "| 123            |",   "|123             |",
  1391.                 // Centre
  1392.                 "|+123|",               "| 123|",               "|123|",
  1393.                 "|+123|",               "| 123|",               "|123|",
  1394.                 "|+123|",               "| 123|",               "|123|",
  1395.                 "|+123|",               "| 123|",               "|123 |",
  1396.                 "|+123 |",              "| 123 |",              "|0123 |",
  1397.                 "|+0123 |",             "| 0123 |",             "|0123  |",
  1398.                 "|+0123  |",            "| 0123  |",            "|00123  |",
  1399.                 "|+00123  |",           "| 00123  |",           "|00123   |",
  1400.                 "|+00123   |",          "| 00123   |",          "|000123   |",
  1401.                 "|+000123   |",         "| 000123   |",         "|000123    |",
  1402.                 "|+000123    |",        "| 000123    |",        "|0000123    |",
  1403.                 "|+0000123    |",       "| 0000123    |",       "|0000123     |",
  1404.                 "|+0000123     |",      "| 0000123     |",      "|00000123     |",
  1405.                 "|+00000123     |",     "| 00000123     |",     "|00000123      |",
  1406.                 "|+00000123      |",    "| 00000123      |",    "|000000123      |",
  1407.                 "|+000000123      |",   "| 000000123      |",   "|000000123       |",
  1408.                 ];
  1409.  
  1410.                 import std.array;
  1411.  
  1412.                 auto array = appender!(char[])();
  1413.  
  1414.                 class ArrayPrinter : TextPrinter
  1415.                 {
  1416.                         void  colour( int x ) {};
  1417.                         void  flush() {};
  1418.                         int   width( dchar d ) { return 1; }
  1419.  
  1420.                         void  print( char_c[] s ) { array.put( s ); }
  1421.                 }
  1422.  
  1423.                 auto A = new ArrayPrinter();
  1424.                 auto F = new TextFormatter( A );
  1425.  
  1426.                 foreach( i, q;  Question )
  1427.                 {
  1428.                         array.clear;
  1429.                         F.format( q, n );
  1430.                         assert(  Answer[i] == array.data );
  1431.                 }
  1432.                 foreach( i, q; Question0 )
  1433.                 {
  1434.                         array.clear;
  1435.                         F.format( q, n );
  1436.                         assert(  Answer0[i] == array.data );
  1437.                 }
  1438.  
  1439.                 // TODO octal/dec tests
  1440.  
  1441.                 /+      // Generate Answers
  1442.                 {
  1443.                         import shd.output;
  1444.                         Shdout.print("\n\"");
  1445.                         foreach( i, q; Question )
  1446.                         {
  1447.                                 Shdout.format( q, n );
  1448.                                 if( (i+1)%3 )
  1449.                                         Shdout.print("\",\t\"");
  1450.                                 else
  1451.                                         Shdout.print("\",\n\"");
  1452.  
  1453.                         }
  1454.                 }// +/
  1455.         Shdout.println(">--- End  format / itoa  --<");
  1456. }
  1457. }// end version(unittest)