/* * FormatCmd.java * * Copyright (c) 1997 Sun Microsystems, Inc. * * See the file "license.terms" for information on usage and * redistribution of this file, and for a DISCLAIMER OF ALL * WARRANTIES. * * Included in SQLite3 port to C# for use in testharness only; 2008 Noah B Hart * * RCS @(#) $Id: FormatCmd.java,v 1.7 2003/02/01 00:56:29 mdejong Exp $ * */ using System; using System.Text; namespace tcl.lang { /// This class implements the built-in "format" command in Tcl. class FormatCmd : Command { private const int LEFT_JUSTIFY = 1; private const int SHOW_SIGN = 2; private const int SPACE_OR_SIGN = 4; private const int PAD_W_ZERO = 8; private const int ALT_OUTPUT = 16; private const int SIGNED_VALUE = 32; private const int RADIX = 1; // Integer types. %d, %x, %o private const int FLOAT = 2; // Floating point. %f private const int EXP = 3; // Exponentional. %e and %E private const int GENERIC = 4; // Floating or exponential, // depending on exponent. %g /// This procedure is invoked to process the "format" Tcl command. /// See the user documentation for details on what it does. /// /// The first argument to the cmdProc is the formatString. The cmdProc /// simply copies all the chars into the sbuf until a '%' is found. At /// this point the cmdProc parces the formatString and determines the /// format parameters. The parcing of the formatString can be broken into /// six possible phases: /// /// Phase 0 - Simply Print: If the next char is % /// Phase 1 - XPG3 Position Specifier: If the format [1-n]$ is used /// Phase 2 - A Set of Flags: One or more of the following + - /// [space] 0 # /// Phase 3 - A Minimun Field Width Either [integer] or * /// Phase 4 - A Precision If the format .[integer] or .* /// Phase 5 - A Length Modifier If h is present /// Phase 6 - A Conversion Character If one of the following is used /// d u i o x X c s f E g G /// /// Any phase can skip ahead one or more phases, but are not allowed /// to move back to previous phases. Once the parameters are determined /// the cmdProc calls one of three private methods that returns a fully /// formatted string. This loop occurs for ever '%' in the formatString. /// public TCL.CompletionCode cmdProc( Interp interp, TclObject[] argv ) { StringBuilder sbuf; // Stores the return value of the parsed // format string StrtoulResult stoul; // A return object to the strtoul call char[] format; // The format argument is converted to a char // array and manipulated as such int phase; // Stores the current phase of the parsing int width; // Minimum field width int precision; // Field precision from field specifier int fmtFlags; // Used to store the format flags ( #,+,etc) int argIndex; // Index of argument to substitute next. int fmtIndex; // Used to locate end of the format fields. int endIndex; // Used to locate end of numerical fields. int intValue; // Generic storage variable long lngValue; // Store the TclInteger.get() result double dblValue; // Store the TclDouble.get() result bool noPercent; // Special case for speed: indicates there's // no field specifier, just a string to copy. bool xpgSet; // Indicates that xpg has been used for the // particular format of the main while loop bool gotXpg; // True means that an XPG3 %n$-style // specifier has been seen. bool gotSequential; // True means that a regular sequential // (non-XPG3) conversion specifier has // been seen. bool useShort; // Value to be printed is short // (half word). bool precisionSet; // Used for f, e, and E conversions bool cont; // Used for phase 3 if ( argv.Length < 2 ) { throw new TclNumArgsException( interp, 1, argv, "formatString ?arg arg ...?" ); } argIndex = 2; fmtIndex = 0; gotXpg = gotSequential = false; format = argv[1].ToString().ToCharArray(); sbuf = new StringBuilder(); // So, what happens here is to scan the format string one % group // at a time, making many individual appends to the StringBuffer. while ( fmtIndex < format.Length ) { fmtFlags = phase = width = 0; noPercent = true; xpgSet = precisionSet = useShort = false; precision = -1; // Append all characters to sbuf that are not used for the // format specifier. if ( format[fmtIndex] != '%' ) { int i; for ( i = fmtIndex; ( i < format.Length ); i++ ) { if ( format[i] == '%' ) { noPercent = false; break; } } sbuf.Append( new string( format, fmtIndex, i - fmtIndex ) ); fmtIndex = i; if ( noPercent ) { break; } } // If true, then a % has been indicated but we are at the end // of the format string. Call function to throw exception. if ( fmtIndex + 1 >= format.Length ) { errorEndMiddle( interp ); } // Phase 0: // Check for %%. If true then simply write a single '%' // to the list. checkOverFlow( interp, format, fmtIndex + 1 ); if ( format[fmtIndex + 1] == '%' ) { sbuf.Append( "%" ); fmtIndex += 2; // Re-enter the loop continue; } fmtIndex++; checkOverFlow( interp, format, fmtIndex ); if ( System.Char.IsDigit( format[fmtIndex] ) ) { // Parce the format array looking for the end of // the number. stoul = strtoul( format, fmtIndex ); intValue = (int)stoul.value; endIndex = stoul.index; if ( format[endIndex] == '$' ) { if ( intValue == 0 ) { errorBadIndex( interp, true ); } // Phase 1: // Check for an XPG3-style %n$ specification. // Note: there must not be a mixture of XPG3 // specs and non-XPG3 specs in the same format string. if ( gotSequential ) { errorMixedXPG( interp ); } gotXpg = true; xpgSet = true; phase = 2; fmtIndex = endIndex + 1; argIndex = intValue + 1; if ( ( argIndex < 2 ) || ( argIndex >= argv.Length ) ) { errorBadIndex( interp, gotXpg ); } } else { // Phase 3: // Format jumped straight to phase 3; Setting // width field. Again, verify that all format // specifiers are sequential. if ( gotXpg ) { errorMixedXPG( interp ); } gotSequential = true; if ( format[fmtIndex] != '0' ) { fmtIndex = endIndex; width = intValue; phase = 4; } } } else { if ( gotXpg ) { errorMixedXPG( interp ); } gotSequential = true; } // Phase 2: // Setting the Format Flags. At this point the phase value // can be either zero or three. Anything greater is an // incorrect format. if ( phase < 3 ) { checkOverFlow( interp, format, fmtIndex ); char ch = format[fmtIndex]; cont = true; while ( cont ) { switch ( ch ) { case '-': { fmtFlags |= LEFT_JUSTIFY; break; } case '#': { fmtFlags |= ALT_OUTPUT; break; } case '0': { fmtFlags |= PAD_W_ZERO; break; } case ' ': { fmtFlags |= SPACE_OR_SIGN; break; } case '+': { fmtFlags |= SHOW_SIGN; break; } default: { cont = false; } break; } if ( cont ) { fmtIndex++; checkOverFlow( interp, format, fmtIndex ); ch = format[fmtIndex]; } } phase = 3; } // Phase 3: // Setting width field. Partially redundant code from the // Phase 1 if/else statement, but this is made to run fast. checkOverFlow( interp, format, fmtIndex ); if ( System.Char.IsDigit( format[fmtIndex] ) ) { stoul = strtoul( format, fmtIndex ); width = (int)stoul.value; fmtIndex = stoul.index; } else if ( format[fmtIndex] == '*' ) { if ( argv.Length > argIndex ) { width = TclInteger.get( interp, argv[argIndex] ); if ( width < 0 ) { width = -width; fmtFlags |= LEFT_JUSTIFY; } argIndex++; fmtIndex++; } } // Phase 4: // Setting the precision field. checkOverFlow( interp, format, fmtIndex ); if ( format[fmtIndex] == '.' ) { fmtIndex++; checkOverFlow( interp, format, fmtIndex ); if ( System.Char.IsDigit( format[fmtIndex] ) ) { precisionSet = true; stoul = strtoul( format, fmtIndex ); precision = (int)stoul.value; fmtIndex = stoul.index; } else if ( format[fmtIndex] == '*' ) { if ( argv.Length > argIndex ) { precisionSet = true; precision = TclInteger.get( interp, argv[argIndex] ); argIndex++; fmtIndex++; checkOverFlow( interp, format, fmtIndex ); } } else { // Format field had a '.' without an integer or '*' // preceeding it (eg %2.d or %2.-5d) errorBadField( interp, format[fmtIndex] ); } } // Phase 5: // Setting the length modifier. if ( format[fmtIndex] == 'h' ) { fmtIndex++; checkOverFlow( interp, format, fmtIndex ); useShort = true; } else if ( format[fmtIndex] == 'l' ) { fmtIndex++; checkOverFlow( interp, format, fmtIndex ); // 'l' is ignored, but should still be processed. } if ( ( argIndex < 2 ) || ( argIndex >= argv.Length ) ) { errorBadIndex( interp, gotXpg ); } // Phase 6: // Setting conversion field. // At this point, variables are initialized as follows: // // width The specified field width. This is always // non-negative. Zero is the default. // precision The specified precision. The default // is -1. // argIndex The argument index from the argv array // for the appropriate arg. // fmtFlags The format flags are set via bitwise // operations. Below are the bits // and their meanings. // ALT_OUTPUT set if a '#' is present. // SHOW_SIGN set if a '+' is present. // SPACE_OR_SIGN set if a ' ' is present. // LEFT_JUSTIFY set if a '-' is present or if the // field width was negative. // PAD_W_ZERO set if a '0' is present string strValue = ""; char index = format[fmtIndex]; switch ( index ) { case 'u': case 'd': case 'o': case 'x': case 'X': case 'i': { if ( index == 'u' ) { // Since Java does not provide unsigned ints we need to // make our own. If the value is negative we need to // clear out all of the leading bits from the 33rd bit // and on. The result is a long value equal to that // of an unsigned int. lngValue = (long)TclInteger.get( interp, argv[argIndex] ); if ( lngValue < 0 ) { lngValue = ( lngValue << 32 ); lngValue = ( SupportClass.URShift( lngValue, 32 ) ); } } else { fmtFlags |= SIGNED_VALUE; lngValue = (long)TclInteger.get( interp, argv[argIndex] ); } // If the useShort option has been selected, we need // to clear all but the first 16 bits. if ( useShort ) { lngValue = ( lngValue << 48 ); lngValue = ( lngValue >> 48 ); } if ( index == 'o' ) { sbuf.Append( cvtLngToStr( lngValue, width, precision, fmtFlags, 8, "01234567".ToCharArray(), "0" ) ); } else if ( index == 'x' ) { sbuf.Append( cvtLngToStr( lngValue, width, precision, fmtFlags, 16, "0123456789abcdef".ToCharArray(), "0x" ) ); } else if ( index == 'X' ) { sbuf.Append( cvtLngToStr( lngValue, width, precision, fmtFlags, 16, "0123456789ABCDEF".ToCharArray(), "0X" ) ); } else { sbuf.Append( cvtLngToStr( lngValue, width, precision, fmtFlags, 10, "0123456789".ToCharArray(), "" ) ); } break; } case 'c': { intValue = 0; char[] arr = new char[] { (char)TclInteger.get( interp, argv[argIndex] ) }; strValue = new string( arr ); sbuf.Append( cvtStrToStr( strValue, width, precision, fmtFlags ) ); break; } case 's': { strValue = argv[argIndex].ToString(); sbuf.Append( cvtStrToStr( strValue, width, precision, fmtFlags ) ); break; } case 'f': { dblValue = TclDouble.get( interp, argv[argIndex] ); sbuf.Append( cvtDblToStr( dblValue, width, precision, fmtFlags, 10, "0123456789".ToCharArray(), "", FLOAT ) ); break; } case 'e': { dblValue = TclDouble.get( interp, argv[argIndex] ); sbuf.Append( cvtDblToStr( dblValue, width, precision, fmtFlags, 10, "e".ToCharArray(), "", EXP ) ); break; } case 'E': { dblValue = TclDouble.get( interp, argv[argIndex] ); sbuf.Append( cvtDblToStr( dblValue, width, precision, fmtFlags, 10, "E".ToCharArray(), "", EXP ) ); break; } case 'g': { dblValue = TclDouble.get( interp, argv[argIndex] ); sbuf.Append( cvtDblToStr( dblValue, width, precision, fmtFlags, 10, "e".ToCharArray(), "", GENERIC ) ); break; } case 'G': { dblValue = TclDouble.get( interp, argv[argIndex] ); sbuf.Append( cvtDblToStr( dblValue, width, precision, fmtFlags, 10, "E".ToCharArray(), "", GENERIC ) ); break; } default: { errorBadField( interp, format[fmtIndex] ); } break; } fmtIndex++; argIndex++; } interp.setResult( sbuf.ToString() ); return TCL.CompletionCode.RETURN; } /// This procedure is invoked in "phase 6" od the Format cmdProc. It /// converts the lngValue to a string with a specified format determined by /// the other input variables. /// /// - Is the value of the argument input /// /// - The minimum width of the string. /// /// - The minimum width if the integer. If the string len /// is less than precision, leading 0 are appended. /// /// - Specifies various formatting to the string /// representation (-, +, space, 0, #) /// /// - The base of the integer (8, 10, 16) /// /// - The char set to use for the conversion to ascii OR /// The char used for sci notation. /// /// - If not empty, str to append on the beginnig of the /// resulting string (eg 0 or 0x or 0X ). /// /// String representation of the long. /// private string cvtLngToStr( long lngValue, int width, int precision, int flags, int base_, char[] charSet, string altPrefix ) { StringBuilder sbuf = new StringBuilder( 100 ); StringBuilder tmpBuf = new StringBuilder( 0 ).Append( "" ); int i; int length; int nspace; int prefixSize = 0; char prefix = (char)( 0 ); // For the format %#x, the value zero is printed "0" not "0x0". // I think this is stupid. if ( lngValue == 0 ) { flags = ( flags | ALT_OUTPUT ); } if ( ( flags & SIGNED_VALUE ) != 0 ) { if ( lngValue < 0 ) { if ( altPrefix.Length > 0 ) { lngValue = ( lngValue << 32 ); lngValue = ( SupportClass.URShift( lngValue, 32 ) ); } else { lngValue = -lngValue; prefix = '-'; prefixSize = 1; } } else if ( ( flags & SHOW_SIGN ) != 0 ) { prefix = '+'; prefixSize = 1; } else if ( ( flags & SPACE_OR_SIGN ) != 0 ) { prefix = ' '; prefixSize = 1; } } if ( ( ( PAD_W_ZERO & flags ) != 0 ) && ( precision < width - prefixSize ) ) { precision = width - prefixSize; } // Convert to ascii do { sbuf.Insert( 0, charSet[(int)( lngValue % base_ )] ); lngValue = lngValue / base_; } while ( lngValue > 0 ); length = sbuf.Length; for ( i = ( precision - length ); i > 0; i-- ) { sbuf.Insert( 0, '0' ); } if ( prefix != 0 ) { sbuf.Insert( 0, prefix ); } if ( ( flags & ALT_OUTPUT ) != 0 ) { if ( ( altPrefix.Length > 0 ) && ( sbuf[0] != altPrefix[0] ) ) { sbuf.Insert( 0, altPrefix ); } } // The text of the conversion is pointed to by "bufpt" and is // "length" characters long. The field width is "width". Do // the output. nspace = width - sbuf.Length; if ( nspace > 0 ) { tmpBuf = new StringBuilder( nspace ); for ( i = 0; i < nspace; i++ ) { tmpBuf.Append( " " ); } } if ( ( LEFT_JUSTIFY & flags ) != 0 ) { // left justified return sbuf.ToString() + tmpBuf.ToString(); } else { // right justified return tmpBuf.ToString() + sbuf.ToString(); } } internal static string toString( double dblValue, int precision, int base_ ) { return cvtDblToStr( dblValue, 0, precision, 0, base_, "e".ToCharArray(), null, GENERIC ); } /// This procedure is invoked in "phase 6" od the Format cmdProc. It /// converts the lngValue to a string with a specified format determined /// by the other input variables. /// /// - Is the value of the argument input /// /// - The minimum width of the string. /// /// - The minimum width if the integer. If the string len /// is less than precision, leading 0 are appended. /// /// - Specifies various formatting to the string /// representation (-, +, space, 0, #) /// /// - The base of the integer (8, 10, 16) /// /// - The char set to use for the conversion to ascii OR /// The char used for sci notation. /// /// - If not empty, str to append on the beginnig of the /// resulting string (eg 0 or 0x or 0X ). /// /// - Either FLOAT, EXP, or GENERIC depending on the /// format specifier. /// /// String representation of the long. /// private static string cvtDblToStr( double dblValue, int width, int precision, int flags, int base_, char[] charSet, string altPrefix, int xtype ) { if ( base_ == 10 ) return dblValue.ToString(); StringBuilder sbuf = new StringBuilder( 100 ); int i; int exp; int length; int count; int digit; int prefixSize = 0; char prefix = (char)( 0 ); double rounder; bool flag_exp = false; // Flag for exponential representation bool flag_rtz = true; // Flag for "remove trailing zeros" bool flag_dp = true; // Flag for remove "decimal point" if ( System.Double.IsNaN( dblValue ) ) { return "NaN"; } if ( dblValue == System.Double.NegativeInfinity ) { return "-Inf"; } if ( dblValue == System.Double.PositiveInfinity ) { return "Inf"; } // If precision < 0 (eg -1) then the precision defaults if ( precision < 0 ) { precision = 6; } if ( dblValue < 0.0 ) { dblValue = -dblValue; prefix = '-'; prefixSize = 1; } // ATK I do not know how C# can note negative 0 // else if (dblValue == 0.0 && (dblValue).Equals((- 0.0))) // { // // Handle -0.0 // // // // 15.19.1 "Numerical Comparison Operators <, <=, >, and >= " // // of the Java Language Spec says: // // "Positive zero and negative zero are considered // // equal. Therefore, -0.0<0.0 is false, for example, but // // -0.0<=0.0 is true." // // // // The Double.equal man page says: // // "If d1 represents +0.0 while d2 represents -0.0, or // // vice versa, the equal test has the value false, even // // though +0.0==-0.0 has the value true. This allows // // hashtables to operate properly. // // dblValue = - dblValue; // prefix = '-'; // prefixSize = 1; // } else if ( ( flags & SHOW_SIGN ) != 0 ) { prefix = '+'; prefixSize = 1; } else if ( ( flags & SPACE_OR_SIGN ) != 0 ) { prefix = ' '; prefixSize = 1; } // For GENERIC xtypes the precision includes the ones digit // so just decrement to get the correct precision. if ( xtype == GENERIC && precision > 0 ) { precision--; } // Rounding works like BSD when the constant 0.4999 is used. Wierd! //for (i = precision, rounder = 0.4999; i > 0; i--, rounder *= 0.1) //; string ss = "0." + new String( '0', precision ) + "4999"; rounder = Convert.ToDouble( ss ); if ( xtype == FLOAT ) { dblValue += rounder; } // Normalize dblValue to within 10.0 > dblValue >= 1.0 exp = 0; if ( dblValue > 0.0 ) { int k = 0; while ( ( dblValue >= 1e8 ) && ( k++ < 100 ) ) { dblValue *= 1e-8; exp += 8; } while ( ( dblValue >= 10.0 ) && ( k++ < 100 ) ) { dblValue *= 0.1; exp++; } while ( ( dblValue < 1e-8 ) && ( k++ < 100 ) ) { dblValue *= 1e8; exp -= 8; } while ( ( dblValue < 1.0 ) && ( k++ < 100 ) ) { dblValue *= 10.0; exp--; } if ( k >= 100 ) { return "NaN"; } } // If the field type is GENERIC, then convert to either EXP // or FLOAT, as appropriate. flag_exp = xtype == EXP; if ( xtype != FLOAT ) { //dblValue += rounder; if ( dblValue >= 10.0 ) { dblValue *= 0.1; exp++; } } if ( xtype == GENERIC ) { flag_rtz = !( ( flags & ALT_OUTPUT ) != 0 ); if ( ( exp < -4 ) || ( exp > precision ) ) { xtype = EXP; } else { precision = ( precision - exp ); xtype = FLOAT; } } else { flag_rtz = false; } // The "exp+precision" test causes output to be of type EXP if // the precision is too large to fit in buf[]. count = 0; if ( xtype == FLOAT ) { flag_dp = ( ( precision > 0 ) || ( ( flags & ALT_OUTPUT ) != 0 ) ); if ( prefixSize > 0 ) { // Sign sbuf.Append( prefix ); } if ( exp < 0 ) { // Digits before "." sbuf.Append( '0' ); } for ( ; exp >= 0; exp-- ) { if ( count++ >= 16 ) { sbuf.Append( '0' ); } else { digit = (int)dblValue; dblValue = ( dblValue - digit ) * 10.0; sbuf.Append( digit ); } } if ( flag_dp ) { sbuf.Append( '.' ); } for ( exp++; ( exp < 0 ) && ( precision > 0 ); precision--, exp++ ) { sbuf.Append( '0' ); } while ( ( precision-- ) > 0 ) { if ( count++ >= 16 ) { sbuf.Append( '0' ); } else { digit = (int)dblValue; dblValue = ( dblValue - digit ) * 10.0; sbuf.Append( digit ); } } if ( flag_rtz && flag_dp ) { // Remove trailing zeros and "." int len, index; len = index = 0; for ( len = ( sbuf.Length - 1 ), index = 0; ( len >= 0 ) && ( sbuf[len] == '0' ); len--, index++ ) ; if ( ( len >= 0 ) && ( sbuf[len] == '.' ) ) { index++; } if ( index > 0 ) { sbuf = new StringBuilder( sbuf.ToString().Substring( 0, ( sbuf.Length - index ) - ( 0 ) ) ); } } } else { // EXP or GENERIC flag_dp = ( ( precision > 0 ) || ( ( flags & ALT_OUTPUT ) != 0 ) ); if ( prefixSize > 0 ) { sbuf.Append( prefix ); } digit = (int)dblValue; dblValue = ( dblValue - digit ) * 10.0; sbuf.Append( digit ); if ( flag_dp ) { sbuf.Append( '.' ); } while ( precision-- > 0 ) { if ( count++ >= 16 ) { sbuf.Append( '0' ); } else { digit = (int)dblValue; dblValue = ( dblValue - digit ) * 10.0; sbuf.Append( digit ); } } if ( flag_rtz && flag_dp ) { // Remove trailing zeros and "." for ( i = 0, length = ( sbuf.Length - 1 ); ( length >= 0 ) && ( sbuf[length] == '0' ); length--, i++ ) ; if ( ( length >= 0 ) && ( sbuf[length] == '.' ) ) { i++; } if ( i > 0 ) { sbuf = new StringBuilder( sbuf.ToString().Substring( 0, ( sbuf.Length - i ) - ( 0 ) ) ); } } if ( ( exp != 0 ) || flag_exp ) { sbuf.Append( charSet[0] ); if ( exp < 0 ) { sbuf.Append( '-' ); exp = -exp; } else { sbuf.Append( '+' ); } if ( exp >= 100 ) { sbuf.Append( ( exp / 100 ) ); exp %= 100; } sbuf.Append( exp / 10 ); sbuf.Append( exp % 10 ); } } // The converted number is in sbuf. Output it. // Note that the number is in the usual order, not reversed as with // integer conversions. length = sbuf.Length; // Special case: Add leading zeros if the PAD_W_ZERO flag is // set and we are not left justified if ( ( ( PAD_W_ZERO & flags ) != 0 ) && ( ( LEFT_JUSTIFY & flags ) == 0 ) ) { int nPad = width - length; i = prefixSize; while ( ( nPad-- ) > 0 ) { sbuf.Insert( prefixSize, '0' ); } length = width; } // Count the number of spaces remaining and creat a StringBuffer // (tmpBuf) with the correct number of spaces. int nspace = width - length; StringBuilder tmpBuf = new StringBuilder( 0 ).Append( "" ); if ( nspace > 0 ) { tmpBuf = new StringBuilder( nspace ); for ( i = 0; i < nspace; i++ ) { tmpBuf.Append( " " ); } } if ( ( LEFT_JUSTIFY & flags ) != 0 ) { // left justified return sbuf.ToString() + tmpBuf.ToString(); } else { // right justified return tmpBuf.ToString() + sbuf.ToString(); } } /// This procedure is invoked in "phase 6" od the Format cmdProc. It /// converts the strValue to a string with a specified format determined /// by the other input variables. /// /// - Is the String w/o formatting. /// /// - The minimum width of the string. /// /// - The minimum width if the integer. If the string /// len is less than precision, leading 0 are /// appended. /// /// - Specifies various formatting to the string /// representation (-, +, space, 0, #) /// /// String representation of the integer. /// private static string cvtStrToStr( string strValue, int width, int precision, int flags ) { string left = ""; string right = ""; if ( precision < 0 ) { precision = 0; } if ( ( precision != 0 ) && ( precision < strValue.Length ) ) { strValue = strValue.Substring( 0, ( precision ) - ( 0 ) ); } if ( width > strValue.Length ) { StringBuilder sbuf = new StringBuilder(); int index = ( width - strValue.Length ); for ( int i = 0; i < index; i++ ) { if ( ( flags & PAD_W_ZERO ) != 0 ) { sbuf.Append( '0' ); } else { sbuf.Append( ' ' ); } } if ( ( LEFT_JUSTIFY & flags ) != 0 ) { right = sbuf.ToString(); } else { left = sbuf.ToString(); } } return ( left + strValue + right ); } /// Search through the array while the current char is a digit. When end /// of array occurs or the char is not a digit, stop the loop, convert the /// sub-array into a long. At this point return a StrtoulResult object /// that contains the new long value and the current pointer to the array. /// /// /// - the array that contains a string representation of an int. /// /// - the arr index where the numeric value begins. /// /// StrtoResult containing the value and new index/ /// private StrtoulResult strtoul( char[] arr, int endIndex ) { int orgIndex; orgIndex = endIndex; for ( ; endIndex < arr.Length; endIndex++ ) { if ( !System.Char.IsDigit( arr[endIndex] ) ) { break; } } return ( new StrtoulResult( System.Int64.Parse( new string( arr, orgIndex, endIndex - orgIndex ) ), endIndex, 0 ) ); } /* * * Error routines: * */ /// Called whenever the fmtIndex in the cmdProc is changed. It verifies /// the the array index is still within the bounds of the array. If no /// throw error. /// /// - The TclInterp which called the cmdProc method . /// /// - The array to be checked. /// /// - The new value for the array index. /// private static void checkOverFlow( Interp interp, char[] arr, int index ) { if ( ( index >= arr.Length ) || ( index < 0 ) ) { throw new TclException( interp, "\"%n$\" argument index out of range" ); } } /// Called whenever Sequential format specifiers are interlaced with /// XPG format specifiers in one call to cmdProc. /// /// /// - The TclInterp which called the cmdProc method . /// private static void errorMixedXPG( Interp interp ) { throw new TclException( interp, "cannot mix \"%\" and \"%n$\" conversion specifiers" ); } /// Called whenever the argIndex access outside the argv array. If the /// type is an XPG then the error message is different. /// /// /// - The TclInterp which called the cmdProc method . /// /// - Boolean the determines if the current format is of a /// XPG type or Sequential /// private static void errorBadIndex( Interp interp, bool gotXpg ) { if ( gotXpg ) { throw new TclException( interp, "\"%n$\" argument index out of range" ); } else { throw new TclException( interp, "not enough arguments for all format specifiers" ); } } /// Called whenever the current char in the format array is erroneous /// /// /// - The TclInterp which called the cmdProc method . /// /// - The erroneous character /// private static void errorBadField( Interp interp, char fieldSpecifier ) { throw new TclException( interp, "bad field specifier \"" + fieldSpecifier + "\"" ); } /// Called whenever the a '%' is found but then the format string ends. /// /// /// - The TclInterp which called the cmdProc method . /// private static void errorEndMiddle( Interp interp ) { throw new TclException( interp, "format string ended in middle of field specifier" ); } } }