wasCSharpSQLite – Blame information for rev
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | /* |
2 | * FormatCmd.java |
||
3 | * |
||
4 | * Copyright (c) 1997 Sun Microsystems, Inc. |
||
5 | * |
||
6 | * See the file "license.terms" for information on usage and |
||
7 | * redistribution of this file, and for a DISCLAIMER OF ALL |
||
8 | * WARRANTIES. |
||
9 | * |
||
10 | * Included in SQLite3 port to C# for use in testharness only; 2008 Noah B Hart |
||
11 | * |
||
12 | * RCS @(#) $Id: FormatCmd.java,v 1.7 2003/02/01 00:56:29 mdejong Exp $ |
||
13 | * |
||
14 | */ |
||
15 | using System; |
||
16 | using System.Text; |
||
17 | namespace tcl.lang |
||
18 | { |
||
19 | |||
20 | /// <summary> This class implements the built-in "format" command in Tcl.</summary> |
||
21 | |||
22 | class FormatCmd : Command |
||
23 | { |
||
24 | |||
25 | private const int LEFT_JUSTIFY = 1; |
||
26 | private const int SHOW_SIGN = 2; |
||
27 | private const int SPACE_OR_SIGN = 4; |
||
28 | private const int PAD_W_ZERO = 8; |
||
29 | private const int ALT_OUTPUT = 16; |
||
30 | private const int SIGNED_VALUE = 32; |
||
31 | private const int RADIX = 1; // Integer types. %d, %x, %o |
||
32 | private const int FLOAT = 2; // Floating point. %f |
||
33 | private const int EXP = 3; // Exponentional. %e and %E |
||
34 | private const int GENERIC = 4; // Floating or exponential, |
||
35 | // depending on exponent. %g |
||
36 | |||
37 | /// <summary> This procedure is invoked to process the "format" Tcl command. |
||
38 | /// See the user documentation for details on what it does. |
||
39 | /// |
||
40 | /// The first argument to the cmdProc is the formatString. The cmdProc |
||
41 | /// simply copies all the chars into the sbuf until a '%' is found. At |
||
42 | /// this point the cmdProc parces the formatString and determines the |
||
43 | /// format parameters. The parcing of the formatString can be broken into |
||
44 | /// six possible phases: |
||
45 | /// |
||
46 | /// Phase 0 - Simply Print: If the next char is % |
||
47 | /// Phase 1 - XPG3 Position Specifier: If the format [1-n]$ is used |
||
48 | /// Phase 2 - A Set of Flags: One or more of the following + - |
||
49 | /// [space] 0 # |
||
50 | /// Phase 3 - A Minimun Field Width Either [integer] or * |
||
51 | /// Phase 4 - A Precision If the format .[integer] or .* |
||
52 | /// Phase 5 - A Length Modifier If h is present |
||
53 | /// Phase 6 - A Conversion Character If one of the following is used |
||
54 | /// d u i o x X c s f E g G |
||
55 | /// |
||
56 | /// Any phase can skip ahead one or more phases, but are not allowed |
||
57 | /// to move back to previous phases. Once the parameters are determined |
||
58 | /// the cmdProc calls one of three private methods that returns a fully |
||
59 | /// formatted string. This loop occurs for ever '%' in the formatString. |
||
60 | /// </summary> |
||
61 | |||
62 | public TCL.CompletionCode cmdProc( Interp interp, TclObject[] argv ) |
||
63 | { |
||
64 | |||
65 | |||
66 | StringBuilder sbuf; // Stores the return value of the parsed |
||
67 | // format string |
||
68 | StrtoulResult stoul; // A return object to the strtoul call |
||
69 | char[] format; // The format argument is converted to a char |
||
70 | // array and manipulated as such |
||
71 | int phase; // Stores the current phase of the parsing |
||
72 | int width; // Minimum field width |
||
73 | int precision; // Field precision from field specifier |
||
74 | int fmtFlags; // Used to store the format flags ( #,+,etc) |
||
75 | int argIndex; // Index of argument to substitute next. |
||
76 | int fmtIndex; // Used to locate end of the format fields. |
||
77 | int endIndex; // Used to locate end of numerical fields. |
||
78 | int intValue; // Generic storage variable |
||
79 | long lngValue; // Store the TclInteger.get() result |
||
80 | double dblValue; // Store the TclDouble.get() result |
||
81 | bool noPercent; // Special case for speed: indicates there's |
||
82 | // no field specifier, just a string to copy. |
||
83 | bool xpgSet; // Indicates that xpg has been used for the |
||
84 | // particular format of the main while loop |
||
85 | bool gotXpg; // True means that an XPG3 %n$-style |
||
86 | // specifier has been seen. |
||
87 | bool gotSequential; // True means that a regular sequential |
||
88 | // (non-XPG3) conversion specifier has |
||
89 | // been seen. |
||
90 | bool useShort; // Value to be printed is short |
||
91 | // (half word). |
||
92 | bool precisionSet; // Used for f, e, and E conversions |
||
93 | bool cont; // Used for phase 3 |
||
94 | |||
95 | if ( argv.Length < 2 ) |
||
96 | { |
||
97 | throw new TclNumArgsException( interp, 1, argv, "formatString ?arg arg ...?" ); |
||
98 | } |
||
99 | |||
100 | argIndex = 2; |
||
101 | fmtIndex = 0; |
||
102 | gotXpg = gotSequential = false; |
||
103 | |||
104 | format = argv[1].ToString().ToCharArray(); |
||
105 | sbuf = new StringBuilder(); |
||
106 | |||
107 | // So, what happens here is to scan the format string one % group |
||
108 | // at a time, making many individual appends to the StringBuffer. |
||
109 | |||
110 | while ( fmtIndex < format.Length ) |
||
111 | { |
||
112 | fmtFlags = phase = width = 0; |
||
113 | noPercent = true; |
||
114 | xpgSet = precisionSet = useShort = false; |
||
115 | precision = -1; |
||
116 | |||
117 | // Append all characters to sbuf that are not used for the |
||
118 | // format specifier. |
||
119 | |||
120 | |||
121 | if ( format[fmtIndex] != '%' ) |
||
122 | { |
||
123 | int i; |
||
124 | for ( i = fmtIndex; ( i < format.Length ); i++ ) |
||
125 | { |
||
126 | if ( format[i] == '%' ) |
||
127 | { |
||
128 | noPercent = false; |
||
129 | break; |
||
130 | } |
||
131 | } |
||
132 | sbuf.Append( new string( format, fmtIndex, i - fmtIndex ) ); |
||
133 | fmtIndex = i; |
||
134 | if ( noPercent ) |
||
135 | { |
||
136 | break; |
||
137 | } |
||
138 | } |
||
139 | |||
140 | // If true, then a % has been indicated but we are at the end |
||
141 | // of the format string. Call function to throw exception. |
||
142 | |||
143 | if ( fmtIndex + 1 >= format.Length ) |
||
144 | { |
||
145 | errorEndMiddle( interp ); |
||
146 | } |
||
147 | |||
148 | // Phase 0: |
||
149 | // Check for %%. If true then simply write a single '%' |
||
150 | // to the list. |
||
151 | |||
152 | checkOverFlow( interp, format, fmtIndex + 1 ); |
||
153 | if ( format[fmtIndex + 1] == '%' ) |
||
154 | { |
||
155 | sbuf.Append( "%" ); |
||
156 | fmtIndex += 2; |
||
157 | // Re-enter the loop |
||
158 | |||
159 | continue; |
||
160 | } |
||
161 | |||
162 | fmtIndex++; |
||
163 | checkOverFlow( interp, format, fmtIndex ); |
||
164 | if ( System.Char.IsDigit( format[fmtIndex] ) ) |
||
165 | { |
||
166 | // Parce the format array looking for the end of |
||
167 | // the number. |
||
168 | |||
169 | stoul = strtoul( format, fmtIndex ); |
||
170 | intValue = (int)stoul.value; |
||
171 | endIndex = stoul.index; |
||
172 | |||
173 | if ( format[endIndex] == '$' ) |
||
174 | { |
||
175 | if ( intValue == 0 ) |
||
176 | { |
||
177 | errorBadIndex( interp, true ); |
||
178 | } |
||
179 | |||
180 | // Phase 1: |
||
181 | // Check for an XPG3-style %n$ specification. |
||
182 | // Note: there must not be a mixture of XPG3 |
||
183 | // specs and non-XPG3 specs in the same format string. |
||
184 | |||
185 | if ( gotSequential ) |
||
186 | { |
||
187 | errorMixedXPG( interp ); |
||
188 | } |
||
189 | gotXpg = true; |
||
190 | xpgSet = true; |
||
191 | phase = 2; |
||
192 | fmtIndex = endIndex + 1; |
||
193 | argIndex = intValue + 1; |
||
194 | if ( ( argIndex < 2 ) || ( argIndex >= argv.Length ) ) |
||
195 | { |
||
196 | errorBadIndex( interp, gotXpg ); |
||
197 | } |
||
198 | } |
||
199 | else |
||
200 | { |
||
201 | // Phase 3: |
||
202 | // Format jumped straight to phase 3; Setting |
||
203 | // width field. Again, verify that all format |
||
204 | // specifiers are sequential. |
||
205 | |||
206 | if ( gotXpg ) |
||
207 | { |
||
208 | errorMixedXPG( interp ); |
||
209 | } |
||
210 | gotSequential = true; |
||
211 | if ( format[fmtIndex] != '0' ) |
||
212 | { |
||
213 | fmtIndex = endIndex; |
||
214 | width = intValue; |
||
215 | phase = 4; |
||
216 | } |
||
217 | } |
||
218 | } |
||
219 | else |
||
220 | { |
||
221 | if ( gotXpg ) |
||
222 | { |
||
223 | errorMixedXPG( interp ); |
||
224 | } |
||
225 | gotSequential = true; |
||
226 | } |
||
227 | |||
228 | // Phase 2: |
||
229 | // Setting the Format Flags. At this point the phase value |
||
230 | // can be either zero or three. Anything greater is an |
||
231 | // incorrect format. |
||
232 | |||
233 | if ( phase < 3 ) |
||
234 | { |
||
235 | checkOverFlow( interp, format, fmtIndex ); |
||
236 | char ch = format[fmtIndex]; |
||
237 | cont = true; |
||
238 | while ( cont ) |
||
239 | { |
||
240 | switch ( ch ) |
||
241 | { |
||
242 | |||
243 | case '-': |
||
244 | { |
||
245 | fmtFlags |= LEFT_JUSTIFY; |
||
246 | break; |
||
247 | } |
||
248 | |||
249 | case '#': |
||
250 | { |
||
251 | fmtFlags |= ALT_OUTPUT; |
||
252 | break; |
||
253 | } |
||
254 | |||
255 | case '0': |
||
256 | { |
||
257 | fmtFlags |= PAD_W_ZERO; |
||
258 | break; |
||
259 | } |
||
260 | |||
261 | case ' ': |
||
262 | { |
||
263 | fmtFlags |= SPACE_OR_SIGN; |
||
264 | break; |
||
265 | } |
||
266 | |||
267 | case '+': |
||
268 | { |
||
269 | fmtFlags |= SHOW_SIGN; |
||
270 | break; |
||
271 | } |
||
272 | |||
273 | default: |
||
274 | { |
||
275 | cont = false; |
||
276 | } |
||
277 | break; |
||
278 | |||
279 | } |
||
280 | if ( cont ) |
||
281 | { |
||
282 | fmtIndex++; |
||
283 | checkOverFlow( interp, format, fmtIndex ); |
||
284 | ch = format[fmtIndex]; |
||
285 | } |
||
286 | } |
||
287 | phase = 3; |
||
288 | } |
||
289 | |||
290 | // Phase 3: |
||
291 | // Setting width field. Partially redundant code from the |
||
292 | // Phase 1 if/else statement, but this is made to run fast. |
||
293 | |||
294 | checkOverFlow( interp, format, fmtIndex ); |
||
295 | if ( System.Char.IsDigit( format[fmtIndex] ) ) |
||
296 | { |
||
297 | stoul = strtoul( format, fmtIndex ); |
||
298 | width = (int)stoul.value; |
||
299 | fmtIndex = stoul.index; |
||
300 | } |
||
301 | else if ( format[fmtIndex] == '*' ) |
||
302 | { |
||
303 | if ( argv.Length > argIndex ) |
||
304 | { |
||
305 | width = TclInteger.get( interp, argv[argIndex] ); |
||
306 | if ( width < 0 ) |
||
307 | { |
||
308 | width = -width; |
||
309 | fmtFlags |= LEFT_JUSTIFY; |
||
310 | } |
||
311 | argIndex++; |
||
312 | fmtIndex++; |
||
313 | } |
||
314 | } |
||
315 | |||
316 | // Phase 4: |
||
317 | // Setting the precision field. |
||
318 | |||
319 | checkOverFlow( interp, format, fmtIndex ); |
||
320 | if ( format[fmtIndex] == '.' ) |
||
321 | { |
||
322 | fmtIndex++; |
||
323 | checkOverFlow( interp, format, fmtIndex ); |
||
324 | if ( System.Char.IsDigit( format[fmtIndex] ) ) |
||
325 | { |
||
326 | precisionSet = true; |
||
327 | |||
328 | stoul = strtoul( format, fmtIndex ); |
||
329 | precision = (int)stoul.value; |
||
330 | fmtIndex = stoul.index; |
||
331 | } |
||
332 | else if ( format[fmtIndex] == '*' ) |
||
333 | { |
||
334 | if ( argv.Length > argIndex ) |
||
335 | { |
||
336 | precisionSet = true; |
||
337 | precision = TclInteger.get( interp, argv[argIndex] ); |
||
338 | argIndex++; |
||
339 | fmtIndex++; |
||
340 | checkOverFlow( interp, format, fmtIndex ); |
||
341 | } |
||
342 | } |
||
343 | else |
||
344 | { |
||
345 | // Format field had a '.' without an integer or '*' |
||
346 | // preceeding it (eg %2.d or %2.-5d) |
||
347 | |||
348 | errorBadField( interp, format[fmtIndex] ); |
||
349 | } |
||
350 | } |
||
351 | |||
352 | // Phase 5: |
||
353 | // Setting the length modifier. |
||
354 | |||
355 | if ( format[fmtIndex] == 'h' ) |
||
356 | { |
||
357 | fmtIndex++; |
||
358 | checkOverFlow( interp, format, fmtIndex ); |
||
359 | useShort = true; |
||
360 | } |
||
361 | else if ( format[fmtIndex] == 'l' ) |
||
362 | { |
||
363 | fmtIndex++; |
||
364 | checkOverFlow( interp, format, fmtIndex ); |
||
365 | |||
366 | // 'l' is ignored, but should still be processed. |
||
367 | } |
||
368 | |||
369 | if ( ( argIndex < 2 ) || ( argIndex >= argv.Length ) ) |
||
370 | { |
||
371 | errorBadIndex( interp, gotXpg ); |
||
372 | } |
||
373 | |||
374 | // Phase 6: |
||
375 | // Setting conversion field. |
||
376 | // At this point, variables are initialized as follows: |
||
377 | // |
||
378 | // width The specified field width. This is always |
||
379 | // non-negative. Zero is the default. |
||
380 | // precision The specified precision. The default |
||
381 | // is -1. |
||
382 | // argIndex The argument index from the argv array |
||
383 | // for the appropriate arg. |
||
384 | // fmtFlags The format flags are set via bitwise |
||
385 | // operations. Below are the bits |
||
386 | // and their meanings. |
||
387 | |||
388 | // ALT_OUTPUT set if a '#' is present. |
||
389 | // SHOW_SIGN set if a '+' is present. |
||
390 | // SPACE_OR_SIGN set if a ' ' is present. |
||
391 | // LEFT_JUSTIFY set if a '-' is present or if the |
||
392 | // field width was negative. |
||
393 | // PAD_W_ZERO set if a '0' is present |
||
394 | |||
395 | string strValue = ""; |
||
396 | char index = format[fmtIndex]; |
||
397 | |||
398 | switch ( index ) |
||
399 | { |
||
400 | |||
401 | case 'u': |
||
402 | case 'd': |
||
403 | case 'o': |
||
404 | case 'x': |
||
405 | case 'X': |
||
406 | case 'i': |
||
407 | { |
||
408 | if ( index == 'u' ) |
||
409 | { |
||
410 | // Since Java does not provide unsigned ints we need to |
||
411 | // make our own. If the value is negative we need to |
||
412 | // clear out all of the leading bits from the 33rd bit |
||
413 | // and on. The result is a long value equal to that |
||
414 | // of an unsigned int. |
||
415 | |||
416 | lngValue = (long)TclInteger.get( interp, argv[argIndex] ); |
||
417 | if ( lngValue < 0 ) |
||
418 | { |
||
419 | lngValue = ( lngValue << 32 ); |
||
420 | lngValue = ( SupportClass.URShift( lngValue, 32 ) ); |
||
421 | } |
||
422 | } |
||
423 | else |
||
424 | { |
||
425 | fmtFlags |= SIGNED_VALUE; |
||
426 | lngValue = (long)TclInteger.get( interp, argv[argIndex] ); |
||
427 | } |
||
428 | |||
429 | // If the useShort option has been selected, we need |
||
430 | // to clear all but the first 16 bits. |
||
431 | |||
432 | if ( useShort ) |
||
433 | { |
||
434 | lngValue = ( lngValue << 48 ); |
||
435 | lngValue = ( lngValue >> 48 ); |
||
436 | } |
||
437 | |||
438 | if ( index == 'o' ) |
||
439 | { |
||
440 | sbuf.Append( cvtLngToStr( lngValue, width, precision, fmtFlags, 8, "01234567".ToCharArray(), "0" ) ); |
||
441 | } |
||
442 | else if ( index == 'x' ) |
||
443 | { |
||
444 | sbuf.Append( cvtLngToStr( lngValue, width, precision, fmtFlags, 16, "0123456789abcdef".ToCharArray(), "0x" ) ); |
||
445 | } |
||
446 | else if ( index == 'X' ) |
||
447 | { |
||
448 | sbuf.Append( cvtLngToStr( lngValue, width, precision, fmtFlags, 16, "0123456789ABCDEF".ToCharArray(), "0X" ) ); |
||
449 | } |
||
450 | else |
||
451 | { |
||
452 | sbuf.Append( cvtLngToStr( lngValue, width, precision, fmtFlags, 10, "0123456789".ToCharArray(), "" ) ); |
||
453 | } |
||
454 | break; |
||
455 | } |
||
456 | |||
457 | case 'c': |
||
458 | { |
||
459 | intValue = 0; |
||
460 | char[] arr = new char[] { (char)TclInteger.get( interp, argv[argIndex] ) }; |
||
461 | strValue = new string( arr ); |
||
462 | sbuf.Append( cvtStrToStr( strValue, width, precision, fmtFlags ) ); |
||
463 | break; |
||
464 | } |
||
465 | |||
466 | case 's': |
||
467 | { |
||
468 | |||
469 | strValue = argv[argIndex].ToString(); |
||
470 | sbuf.Append( cvtStrToStr( strValue, width, precision, fmtFlags ) ); |
||
471 | break; |
||
472 | } |
||
473 | |||
474 | case 'f': |
||
475 | { |
||
476 | dblValue = TclDouble.get( interp, argv[argIndex] ); |
||
477 | sbuf.Append( cvtDblToStr( dblValue, width, precision, fmtFlags, 10, "0123456789".ToCharArray(), "", FLOAT ) ); |
||
478 | break; |
||
479 | } |
||
480 | |||
481 | case 'e': |
||
482 | { |
||
483 | dblValue = TclDouble.get( interp, argv[argIndex] ); |
||
484 | sbuf.Append( cvtDblToStr( dblValue, width, precision, fmtFlags, 10, "e".ToCharArray(), "", EXP ) ); |
||
485 | break; |
||
486 | } |
||
487 | |||
488 | case 'E': |
||
489 | { |
||
490 | dblValue = TclDouble.get( interp, argv[argIndex] ); |
||
491 | sbuf.Append( cvtDblToStr( dblValue, width, precision, fmtFlags, 10, "E".ToCharArray(), "", EXP ) ); |
||
492 | break; |
||
493 | } |
||
494 | |||
495 | case 'g': |
||
496 | { |
||
497 | dblValue = TclDouble.get( interp, argv[argIndex] ); |
||
498 | sbuf.Append( cvtDblToStr( dblValue, width, precision, fmtFlags, 10, "e".ToCharArray(), "", GENERIC ) ); |
||
499 | break; |
||
500 | } |
||
501 | |||
502 | case 'G': |
||
503 | { |
||
504 | dblValue = TclDouble.get( interp, argv[argIndex] ); |
||
505 | sbuf.Append( cvtDblToStr( dblValue, width, precision, fmtFlags, 10, "E".ToCharArray(), "", GENERIC ) ); |
||
506 | break; |
||
507 | } |
||
508 | |||
509 | default: |
||
510 | { |
||
511 | errorBadField( interp, format[fmtIndex] ); |
||
512 | } |
||
513 | break; |
||
514 | |||
515 | } |
||
516 | fmtIndex++; |
||
517 | argIndex++; |
||
518 | } |
||
519 | interp.setResult( sbuf.ToString() ); |
||
520 | return TCL.CompletionCode.RETURN; |
||
521 | } |
||
522 | |||
523 | |||
524 | /// <summary> This procedure is invoked in "phase 6" od the Format cmdProc. It |
||
525 | /// converts the lngValue to a string with a specified format determined by |
||
526 | /// the other input variables. |
||
527 | /// </summary> |
||
528 | /// <param name="lngValue"> - Is the value of the argument input |
||
529 | /// </param> |
||
530 | /// <param name="width"> - The minimum width of the string. |
||
531 | /// </param> |
||
532 | /// <param name="precision">- The minimum width if the integer. If the string len |
||
533 | /// is less than precision, leading 0 are appended. |
||
534 | /// </param> |
||
535 | /// <param name="flags"> - Specifies various formatting to the string |
||
536 | /// representation (-, +, space, 0, #) |
||
537 | /// </param> |
||
538 | /// <param name="base"> - The base of the integer (8, 10, 16) |
||
539 | /// </param> |
||
540 | /// <param name="charSet"> - The char set to use for the conversion to ascii OR |
||
541 | /// The char used for sci notation. |
||
542 | /// </param> |
||
543 | /// <param name="altPrefix">- If not empty, str to append on the beginnig of the |
||
544 | /// resulting string (eg 0 or 0x or 0X ). |
||
545 | /// </param> |
||
546 | /// <returns> String representation of the long. |
||
547 | /// </returns> |
||
548 | |||
549 | private string cvtLngToStr( long lngValue, int width, int precision, int flags, int base_, char[] charSet, string altPrefix ) |
||
550 | { |
||
551 | StringBuilder sbuf = new StringBuilder( 100 ); |
||
552 | StringBuilder tmpBuf = new StringBuilder( 0 ).Append( "" ); |
||
553 | |||
554 | int i; |
||
555 | int length; |
||
556 | int nspace; |
||
557 | int prefixSize = 0; |
||
558 | char prefix = (char)( 0 ); |
||
559 | |||
560 | // For the format %#x, the value zero is printed "0" not "0x0". |
||
561 | // I think this is stupid. |
||
562 | |||
563 | if ( lngValue == 0 ) |
||
564 | { |
||
565 | flags = ( flags | ALT_OUTPUT ); |
||
566 | } |
||
567 | |||
568 | |||
569 | if ( ( flags & SIGNED_VALUE ) != 0 ) |
||
570 | { |
||
571 | if ( lngValue < 0 ) |
||
572 | { |
||
573 | if ( altPrefix.Length > 0 ) |
||
574 | { |
||
575 | lngValue = ( lngValue << 32 ); |
||
576 | lngValue = ( SupportClass.URShift( lngValue, 32 ) ); |
||
577 | } |
||
578 | else |
||
579 | { |
||
580 | lngValue = -lngValue; |
||
581 | prefix = '-'; |
||
582 | prefixSize = 1; |
||
583 | } |
||
584 | } |
||
585 | else if ( ( flags & SHOW_SIGN ) != 0 ) |
||
586 | { |
||
587 | prefix = '+'; |
||
588 | prefixSize = 1; |
||
589 | } |
||
590 | else if ( ( flags & SPACE_OR_SIGN ) != 0 ) |
||
591 | { |
||
592 | prefix = ' '; |
||
593 | prefixSize = 1; |
||
594 | } |
||
595 | } |
||
596 | |||
597 | if ( ( ( PAD_W_ZERO & flags ) != 0 ) && ( precision < width - prefixSize ) ) |
||
598 | { |
||
599 | precision = width - prefixSize; |
||
600 | } |
||
601 | |||
602 | // Convert to ascii |
||
603 | |||
604 | do |
||
605 | { |
||
606 | sbuf.Insert( 0, charSet[(int)( lngValue % base_ )] ); |
||
607 | lngValue = lngValue / base_; |
||
608 | } |
||
609 | while ( lngValue > 0 ); |
||
610 | |||
611 | length = sbuf.Length; |
||
612 | for ( i = ( precision - length ); i > 0; i-- ) |
||
613 | { |
||
614 | sbuf.Insert( 0, '0' ); |
||
615 | } |
||
616 | if ( prefix != 0 ) |
||
617 | { |
||
618 | sbuf.Insert( 0, prefix ); |
||
619 | } |
||
620 | if ( ( flags & ALT_OUTPUT ) != 0 ) |
||
621 | { |
||
622 | if ( ( altPrefix.Length > 0 ) && ( sbuf[0] != altPrefix[0] ) ) |
||
623 | { |
||
624 | sbuf.Insert( 0, altPrefix ); |
||
625 | } |
||
626 | } |
||
627 | |||
628 | // The text of the conversion is pointed to by "bufpt" and is |
||
629 | // "length" characters long. The field width is "width". Do |
||
630 | // the output. |
||
631 | |||
632 | nspace = width - sbuf.Length; |
||
633 | if ( nspace > 0 ) |
||
634 | { |
||
635 | tmpBuf = new StringBuilder( nspace ); |
||
636 | for ( i = 0; i < nspace; i++ ) |
||
637 | { |
||
638 | tmpBuf.Append( " " ); |
||
639 | } |
||
640 | } |
||
641 | |||
642 | if ( ( LEFT_JUSTIFY & flags ) != 0 ) |
||
643 | { |
||
644 | // left justified |
||
645 | |||
646 | return sbuf.ToString() + tmpBuf.ToString(); |
||
647 | } |
||
648 | else |
||
649 | { |
||
650 | // right justified |
||
651 | |||
652 | return tmpBuf.ToString() + sbuf.ToString(); |
||
653 | } |
||
654 | } |
||
655 | |||
656 | internal static string toString( double dblValue, int precision, int base_ ) |
||
657 | { |
||
658 | return cvtDblToStr( dblValue, 0, precision, 0, base_, "e".ToCharArray(), null, GENERIC ); |
||
659 | } |
||
660 | |||
661 | /// <summary> This procedure is invoked in "phase 6" od the Format cmdProc. It |
||
662 | /// converts the lngValue to a string with a specified format determined |
||
663 | /// by the other input variables. |
||
664 | /// </summary> |
||
665 | /// <param name="dblValue"> - Is the value of the argument input |
||
666 | /// </param> |
||
667 | /// <param name="width"> - The minimum width of the string. |
||
668 | /// </param> |
||
669 | /// <param name="precision">- The minimum width if the integer. If the string len |
||
670 | /// is less than precision, leading 0 are appended. |
||
671 | /// </param> |
||
672 | /// <param name="flags"> - Specifies various formatting to the string |
||
673 | /// representation (-, +, space, 0, #) |
||
674 | /// </param> |
||
675 | /// <param name="base"> - The base of the integer (8, 10, 16) |
||
676 | /// </param> |
||
677 | /// <param name="charSet"> - The char set to use for the conversion to ascii OR |
||
678 | /// The char used for sci notation. |
||
679 | /// </param> |
||
680 | /// <param name="altPrefix">- If not empty, str to append on the beginnig of the |
||
681 | /// resulting string (eg 0 or 0x or 0X ). |
||
682 | /// </param> |
||
683 | /// <param name="xtype"> - Either FLOAT, EXP, or GENERIC depending on the |
||
684 | /// format specifier. |
||
685 | /// </param> |
||
686 | /// <returns> String representation of the long. |
||
687 | /// </returns> |
||
688 | |||
689 | private static string cvtDblToStr( double dblValue, int width, int precision, int flags, int base_, char[] charSet, string altPrefix, int xtype ) |
||
690 | { |
||
691 | if ( base_ == 10 ) |
||
692 | return dblValue.ToString(); |
||
693 | StringBuilder sbuf = new StringBuilder( 100 ); |
||
694 | int i; |
||
695 | int exp; |
||
696 | int length; |
||
697 | int count; |
||
698 | int digit; |
||
699 | int prefixSize = 0; |
||
700 | char prefix = (char)( 0 ); |
||
701 | double rounder; |
||
702 | bool flag_exp = false; // Flag for exponential representation |
||
703 | bool flag_rtz = true; // Flag for "remove trailing zeros" |
||
704 | bool flag_dp = true; // Flag for remove "decimal point" |
||
705 | |||
706 | if ( System.Double.IsNaN( dblValue ) ) |
||
707 | { |
||
708 | return "NaN"; |
||
709 | } |
||
710 | if ( dblValue == System.Double.NegativeInfinity ) |
||
711 | { |
||
712 | return "-Inf"; |
||
713 | } |
||
714 | if ( dblValue == System.Double.PositiveInfinity ) |
||
715 | { |
||
716 | return "Inf"; |
||
717 | } |
||
718 | |||
719 | // If precision < 0 (eg -1) then the precision defaults |
||
720 | |||
721 | if ( precision < 0 ) |
||
722 | { |
||
723 | precision = 6; |
||
724 | } |
||
725 | |||
726 | if ( dblValue < 0.0 ) |
||
727 | { |
||
728 | dblValue = -dblValue; |
||
729 | prefix = '-'; |
||
730 | prefixSize = 1; |
||
731 | } |
||
732 | // ATK I do not know how C# can note negative 0 |
||
733 | // else if (dblValue == 0.0 && (dblValue).Equals((- 0.0))) |
||
734 | // { |
||
735 | // // Handle -0.0 |
||
736 | // // |
||
737 | // // 15.19.1 "Numerical Comparison Operators <, <=, >, and >= " |
||
738 | // // of the Java Language Spec says: |
||
739 | // // "Positive zero and negative zero are considered |
||
740 | // // equal. Therefore, -0.0<0.0 is false, for example, but |
||
741 | // // -0.0<=0.0 is true." |
||
742 | // // |
||
743 | // // The Double.equal man page says: |
||
744 | // // "If d1 represents +0.0 while d2 represents -0.0, or |
||
745 | // // vice versa, the equal test has the value false, even |
||
746 | // // though +0.0==-0.0 has the value true. This allows |
||
747 | // // hashtables to operate properly. |
||
748 | // |
||
749 | // dblValue = - dblValue; |
||
750 | // prefix = '-'; |
||
751 | // prefixSize = 1; |
||
752 | // } |
||
753 | else if ( ( flags & SHOW_SIGN ) != 0 ) |
||
754 | { |
||
755 | prefix = '+'; |
||
756 | prefixSize = 1; |
||
757 | } |
||
758 | else if ( ( flags & SPACE_OR_SIGN ) != 0 ) |
||
759 | { |
||
760 | prefix = ' '; |
||
761 | prefixSize = 1; |
||
762 | } |
||
763 | |||
764 | // For GENERIC xtypes the precision includes the ones digit |
||
765 | // so just decrement to get the correct precision. |
||
766 | |||
767 | if ( xtype == GENERIC && precision > 0 ) |
||
768 | { |
||
769 | precision--; |
||
770 | } |
||
771 | |||
772 | // Rounding works like BSD when the constant 0.4999 is used. Wierd! |
||
773 | //for (i = precision, rounder = 0.4999; i > 0; i--, rounder *= 0.1) |
||
774 | //; |
||
775 | string ss = "0." + new String( '0', precision ) + "4999"; |
||
776 | rounder = Convert.ToDouble( ss ); |
||
777 | if ( xtype == FLOAT ) |
||
778 | { |
||
779 | dblValue += rounder; |
||
780 | } |
||
781 | |||
782 | // Normalize dblValue to within 10.0 > dblValue >= 1.0 |
||
783 | |||
784 | exp = 0; |
||
785 | if ( dblValue > 0.0 ) |
||
786 | { |
||
787 | int k = 0; |
||
788 | while ( ( dblValue >= 1e8 ) && ( k++ < 100 ) ) |
||
789 | { |
||
790 | dblValue *= 1e-8; |
||
791 | exp += 8; |
||
792 | } |
||
793 | while ( ( dblValue >= 10.0 ) && ( k++ < 100 ) ) |
||
794 | { |
||
795 | dblValue *= 0.1; |
||
796 | exp++; |
||
797 | } |
||
798 | while ( ( dblValue < 1e-8 ) && ( k++ < 100 ) ) |
||
799 | { |
||
800 | dblValue *= 1e8; |
||
801 | exp -= 8; |
||
802 | } |
||
803 | while ( ( dblValue < 1.0 ) && ( k++ < 100 ) ) |
||
804 | { |
||
805 | dblValue *= 10.0; |
||
806 | exp--; |
||
807 | } |
||
808 | if ( k >= 100 ) |
||
809 | { |
||
810 | return "NaN"; |
||
811 | } |
||
812 | } |
||
813 | |||
814 | // If the field type is GENERIC, then convert to either EXP |
||
815 | // or FLOAT, as appropriate. |
||
816 | |||
817 | flag_exp = xtype == EXP; |
||
818 | if ( xtype != FLOAT ) |
||
819 | { |
||
820 | //dblValue += rounder; |
||
821 | if ( dblValue >= 10.0 ) |
||
822 | { |
||
823 | dblValue *= 0.1; |
||
824 | exp++; |
||
825 | } |
||
826 | } |
||
827 | if ( xtype == GENERIC ) |
||
828 | { |
||
829 | flag_rtz = !( ( flags & ALT_OUTPUT ) != 0 ); |
||
830 | if ( ( exp < -4 ) || ( exp > precision ) ) |
||
831 | { |
||
832 | xtype = EXP; |
||
833 | } |
||
834 | else |
||
835 | { |
||
836 | precision = ( precision - exp ); |
||
837 | xtype = FLOAT; |
||
838 | } |
||
839 | } |
||
840 | else |
||
841 | { |
||
842 | flag_rtz = false; |
||
843 | } |
||
844 | |||
845 | // The "exp+precision" test causes output to be of type EXP if |
||
846 | // the precision is too large to fit in buf[]. |
||
847 | |||
848 | count = 0; |
||
849 | if ( xtype == FLOAT ) |
||
850 | { |
||
851 | flag_dp = ( ( precision > 0 ) || ( ( flags & ALT_OUTPUT ) != 0 ) ); |
||
852 | if ( prefixSize > 0 ) |
||
853 | { |
||
854 | // Sign |
||
855 | |||
856 | sbuf.Append( prefix ); |
||
857 | } |
||
858 | if ( exp < 0 ) |
||
859 | { |
||
860 | // Digits before "." |
||
861 | |||
862 | sbuf.Append( '0' ); |
||
863 | } |
||
864 | for ( ; exp >= 0; exp-- ) |
||
865 | { |
||
866 | if ( count++ >= 16 ) |
||
867 | { |
||
868 | sbuf.Append( '0' ); |
||
869 | } |
||
870 | else |
||
871 | { |
||
872 | |||
873 | digit = (int)dblValue; |
||
874 | dblValue = ( dblValue - digit ) * 10.0; |
||
875 | sbuf.Append( digit ); |
||
876 | } |
||
877 | } |
||
878 | if ( flag_dp ) |
||
879 | { |
||
880 | sbuf.Append( '.' ); |
||
881 | } |
||
882 | for ( exp++; ( exp < 0 ) && ( precision > 0 ); precision--, exp++ ) |
||
883 | { |
||
884 | sbuf.Append( '0' ); |
||
885 | } |
||
886 | while ( ( precision-- ) > 0 ) |
||
887 | { |
||
888 | if ( count++ >= 16 ) |
||
889 | { |
||
890 | sbuf.Append( '0' ); |
||
891 | } |
||
892 | else |
||
893 | { |
||
894 | |||
895 | digit = (int)dblValue; |
||
896 | dblValue = ( dblValue - digit ) * 10.0; |
||
897 | sbuf.Append( digit ); |
||
898 | } |
||
899 | } |
||
900 | |||
901 | if ( flag_rtz && flag_dp ) |
||
902 | { |
||
903 | // Remove trailing zeros and "." |
||
904 | |||
905 | int len, index; |
||
906 | len = index = 0; |
||
907 | for ( len = ( sbuf.Length - 1 ), index = 0; ( len >= 0 ) && ( sbuf[len] == '0' ); len--, index++ ) |
||
908 | ; |
||
909 | |||
910 | if ( ( len >= 0 ) && ( sbuf[len] == '.' ) ) |
||
911 | { |
||
912 | index++; |
||
913 | } |
||
914 | |||
915 | if ( index > 0 ) |
||
916 | { |
||
917 | sbuf = new StringBuilder( sbuf.ToString().Substring( 0, ( sbuf.Length - index ) - ( 0 ) ) ); |
||
918 | } |
||
919 | } |
||
920 | } |
||
921 | else |
||
922 | { |
||
923 | // EXP or GENERIC |
||
924 | |||
925 | flag_dp = ( ( precision > 0 ) || ( ( flags & ALT_OUTPUT ) != 0 ) ); |
||
926 | |||
927 | if ( prefixSize > 0 ) |
||
928 | { |
||
929 | sbuf.Append( prefix ); |
||
930 | } |
||
931 | |||
932 | digit = (int)dblValue; |
||
933 | dblValue = ( dblValue - digit ) * 10.0; |
||
934 | sbuf.Append( digit ); |
||
935 | if ( flag_dp ) |
||
936 | { |
||
937 | sbuf.Append( '.' ); |
||
938 | } |
||
939 | while ( precision-- > 0 ) |
||
940 | { |
||
941 | if ( count++ >= 16 ) |
||
942 | { |
||
943 | sbuf.Append( '0' ); |
||
944 | } |
||
945 | else |
||
946 | { |
||
947 | |||
948 | digit = (int)dblValue; |
||
949 | dblValue = ( dblValue - digit ) * 10.0; |
||
950 | sbuf.Append( digit ); |
||
951 | } |
||
952 | } |
||
953 | |||
954 | if ( flag_rtz && flag_dp ) |
||
955 | { |
||
956 | // Remove trailing zeros and "." |
||
957 | |||
958 | for ( i = 0, length = ( sbuf.Length - 1 ); ( length >= 0 ) && ( sbuf[length] == '0' ); length--, i++ ) |
||
959 | ; |
||
960 | |||
961 | if ( ( length >= 0 ) && ( sbuf[length] == '.' ) ) |
||
962 | { |
||
963 | i++; |
||
964 | } |
||
965 | |||
966 | if ( i > 0 ) |
||
967 | { |
||
968 | sbuf = new StringBuilder( sbuf.ToString().Substring( 0, ( sbuf.Length - i ) - ( 0 ) ) ); |
||
969 | } |
||
970 | } |
||
971 | if ( ( exp != 0 ) || flag_exp ) |
||
972 | { |
||
973 | sbuf.Append( charSet[0] ); |
||
974 | if ( exp < 0 ) |
||
975 | { |
||
976 | sbuf.Append( '-' ); |
||
977 | exp = -exp; |
||
978 | } |
||
979 | else |
||
980 | { |
||
981 | sbuf.Append( '+' ); |
||
982 | } |
||
983 | if ( exp >= 100 ) |
||
984 | { |
||
985 | sbuf.Append( ( exp / 100 ) ); |
||
986 | exp %= 100; |
||
987 | } |
||
988 | sbuf.Append( exp / 10 ); |
||
989 | sbuf.Append( exp % 10 ); |
||
990 | } |
||
991 | } |
||
992 | |||
993 | // The converted number is in sbuf. Output it. |
||
994 | // Note that the number is in the usual order, not reversed as with |
||
995 | // integer conversions. |
||
996 | |||
997 | length = sbuf.Length; |
||
998 | |||
999 | // Special case: Add leading zeros if the PAD_W_ZERO flag is |
||
1000 | // set and we are not left justified |
||
1001 | |||
1002 | if ( ( ( PAD_W_ZERO & flags ) != 0 ) && ( ( LEFT_JUSTIFY & flags ) == 0 ) ) |
||
1003 | { |
||
1004 | int nPad = width - length; |
||
1005 | i = prefixSize; |
||
1006 | while ( ( nPad-- ) > 0 ) |
||
1007 | { |
||
1008 | sbuf.Insert( prefixSize, '0' ); |
||
1009 | } |
||
1010 | length = width; |
||
1011 | } |
||
1012 | |||
1013 | // Count the number of spaces remaining and creat a StringBuffer |
||
1014 | // (tmpBuf) with the correct number of spaces. |
||
1015 | |||
1016 | int nspace = width - length; |
||
1017 | StringBuilder tmpBuf = new StringBuilder( 0 ).Append( "" ); |
||
1018 | if ( nspace > 0 ) |
||
1019 | { |
||
1020 | tmpBuf = new StringBuilder( nspace ); |
||
1021 | for ( i = 0; i < nspace; i++ ) |
||
1022 | { |
||
1023 | tmpBuf.Append( " " ); |
||
1024 | } |
||
1025 | } |
||
1026 | |||
1027 | if ( ( LEFT_JUSTIFY & flags ) != 0 ) |
||
1028 | { |
||
1029 | // left justified |
||
1030 | |||
1031 | return sbuf.ToString() + tmpBuf.ToString(); |
||
1032 | } |
||
1033 | else |
||
1034 | { |
||
1035 | // right justified |
||
1036 | |||
1037 | return tmpBuf.ToString() + sbuf.ToString(); |
||
1038 | } |
||
1039 | } |
||
1040 | |||
1041 | /// <summary> This procedure is invoked in "phase 6" od the Format cmdProc. It |
||
1042 | /// converts the strValue to a string with a specified format determined |
||
1043 | /// by the other input variables. |
||
1044 | /// </summary> |
||
1045 | /// <param name="strValue"> - Is the String w/o formatting. |
||
1046 | /// </param> |
||
1047 | /// <param name="width"> - The minimum width of the string. |
||
1048 | /// </param> |
||
1049 | /// <param name="precision">- The minimum width if the integer. If the string |
||
1050 | /// len is less than precision, leading 0 are |
||
1051 | /// appended. |
||
1052 | /// </param> |
||
1053 | /// <param name="flags"> - Specifies various formatting to the string |
||
1054 | /// representation (-, +, space, 0, #) |
||
1055 | /// </param> |
||
1056 | /// <returns> String representation of the integer. |
||
1057 | /// </returns> |
||
1058 | |||
1059 | private static string cvtStrToStr( string strValue, int width, int precision, int flags ) |
||
1060 | { |
||
1061 | string left = ""; |
||
1062 | string right = ""; |
||
1063 | |||
1064 | if ( precision < 0 ) |
||
1065 | { |
||
1066 | precision = 0; |
||
1067 | } |
||
1068 | |||
1069 | if ( ( precision != 0 ) && ( precision < strValue.Length ) ) |
||
1070 | { |
||
1071 | strValue = strValue.Substring( 0, ( precision ) - ( 0 ) ); |
||
1072 | } |
||
1073 | |||
1074 | if ( width > strValue.Length ) |
||
1075 | { |
||
1076 | StringBuilder sbuf = new StringBuilder(); |
||
1077 | int index = ( width - strValue.Length ); |
||
1078 | for ( int i = 0; i < index; i++ ) |
||
1079 | { |
||
1080 | if ( ( flags & PAD_W_ZERO ) != 0 ) |
||
1081 | { |
||
1082 | sbuf.Append( '0' ); |
||
1083 | } |
||
1084 | else |
||
1085 | { |
||
1086 | sbuf.Append( ' ' ); |
||
1087 | } |
||
1088 | } |
||
1089 | if ( ( LEFT_JUSTIFY & flags ) != 0 ) |
||
1090 | { |
||
1091 | right = sbuf.ToString(); |
||
1092 | } |
||
1093 | else |
||
1094 | { |
||
1095 | left = sbuf.ToString(); |
||
1096 | } |
||
1097 | } |
||
1098 | |||
1099 | return ( left + strValue + right ); |
||
1100 | } |
||
1101 | |||
1102 | |||
1103 | /// <summary> Search through the array while the current char is a digit. When end |
||
1104 | /// of array occurs or the char is not a digit, stop the loop, convert the |
||
1105 | /// sub-array into a long. At this point return a StrtoulResult object |
||
1106 | /// that contains the new long value and the current pointer to the array. |
||
1107 | /// |
||
1108 | /// </summary> |
||
1109 | /// <param name="arr">- the array that contains a string representation of an int. |
||
1110 | /// </param> |
||
1111 | /// <param name="endIndex">- the arr index where the numeric value begins. |
||
1112 | /// </param> |
||
1113 | /// <returns> StrtoResult containing the value and new index/ |
||
1114 | /// </returns> |
||
1115 | |||
1116 | private StrtoulResult strtoul( char[] arr, int endIndex ) |
||
1117 | { |
||
1118 | int orgIndex; |
||
1119 | |||
1120 | orgIndex = endIndex; |
||
1121 | for ( ; endIndex < arr.Length; endIndex++ ) |
||
1122 | { |
||
1123 | if ( !System.Char.IsDigit( arr[endIndex] ) ) |
||
1124 | { |
||
1125 | break; |
||
1126 | } |
||
1127 | } |
||
1128 | return ( new StrtoulResult( System.Int64.Parse( new string( arr, orgIndex, endIndex - orgIndex ) ), endIndex, 0 ) ); |
||
1129 | } |
||
1130 | |||
1131 | |||
1132 | /* |
||
1133 | * |
||
1134 | * Error routines: |
||
1135 | * |
||
1136 | */ |
||
1137 | |||
1138 | |||
1139 | /// <summary> Called whenever the fmtIndex in the cmdProc is changed. It verifies |
||
1140 | /// the the array index is still within the bounds of the array. If no |
||
1141 | /// throw error. |
||
1142 | /// </summary> |
||
1143 | /// <param name="interp"> - The TclInterp which called the cmdProc method . |
||
1144 | /// </param> |
||
1145 | /// <param name="arr"> - The array to be checked. |
||
1146 | /// </param> |
||
1147 | /// <param name="index"> - The new value for the array index. |
||
1148 | /// </param> |
||
1149 | |||
1150 | private static void checkOverFlow( Interp interp, char[] arr, int index ) |
||
1151 | { |
||
1152 | if ( ( index >= arr.Length ) || ( index < 0 ) ) |
||
1153 | { |
||
1154 | throw new TclException( interp, "\"%n$\" argument index out of range" ); |
||
1155 | } |
||
1156 | } |
||
1157 | |||
1158 | |||
1159 | /// <summary> Called whenever Sequential format specifiers are interlaced with |
||
1160 | /// XPG format specifiers in one call to cmdProc. |
||
1161 | /// |
||
1162 | /// </summary> |
||
1163 | /// <param name="interp"> - The TclInterp which called the cmdProc method . |
||
1164 | /// </param> |
||
1165 | |||
1166 | private static void errorMixedXPG( Interp interp ) |
||
1167 | { |
||
1168 | throw new TclException( interp, "cannot mix \"%\" and \"%n$\" conversion specifiers" ); |
||
1169 | } |
||
1170 | |||
1171 | |||
1172 | /// <summary> Called whenever the argIndex access outside the argv array. If the |
||
1173 | /// type is an XPG then the error message is different. |
||
1174 | /// |
||
1175 | /// </summary> |
||
1176 | /// <param name="interp"> - The TclInterp which called the cmdProc method . |
||
1177 | /// </param> |
||
1178 | /// <param name="gotXpg"> - Boolean the determines if the current format is of a |
||
1179 | /// XPG type or Sequential |
||
1180 | /// </param> |
||
1181 | |||
1182 | private static void errorBadIndex( Interp interp, bool gotXpg ) |
||
1183 | { |
||
1184 | if ( gotXpg ) |
||
1185 | { |
||
1186 | throw new TclException( interp, "\"%n$\" argument index out of range" ); |
||
1187 | } |
||
1188 | else |
||
1189 | { |
||
1190 | throw new TclException( interp, "not enough arguments for all format specifiers" ); |
||
1191 | } |
||
1192 | } |
||
1193 | |||
1194 | |||
1195 | /// <summary> Called whenever the current char in the format array is erroneous |
||
1196 | /// |
||
1197 | /// </summary> |
||
1198 | /// <param name="interp"> - The TclInterp which called the cmdProc method . |
||
1199 | /// </param> |
||
1200 | /// <param name="fieldSpecifier"> - The erroneous character |
||
1201 | /// </param> |
||
1202 | |||
1203 | private static void errorBadField( Interp interp, char fieldSpecifier ) |
||
1204 | { |
||
1205 | throw new TclException( interp, "bad field specifier \"" + fieldSpecifier + "\"" ); |
||
1206 | } |
||
1207 | |||
1208 | |||
1209 | /// <summary> Called whenever the a '%' is found but then the format string ends. |
||
1210 | /// |
||
1211 | /// </summary> |
||
1212 | /// <param name="interp"> - The TclInterp which called the cmdProc method . |
||
1213 | /// </param> |
||
1214 | |||
1215 | private static void errorEndMiddle( Interp interp ) |
||
1216 | { |
||
1217 | throw new TclException( interp, "format string ended in middle of field specifier" ); |
||
1218 | } |
||
1219 | } |
||
1220 | } |