wasCSharpSQLite – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 /*
2 * ScanCmd.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: ScanCmd.java,v 1.2 1999/05/09 01:22:09 dejong Exp $
13 *
14 */
15 using System;
16 namespace tcl.lang
17 {
18  
19 /// <summary> This class implements the built-in "scan" command in Tcl.
20 ///
21 /// </summary>
22  
23 class ScanCmd : Command
24 {
25 /// <summary> This procedure is invoked to process the "scan" Tcl command.
26 /// See the user documentation for details on what it does.
27 ///
28 /// Each iteration of the cmdProc compares the scanArr's current index to
29 /// the frmtArr's index. If the chars are equal then the indicies are
30 /// incremented. If a '%' is found in the frmtArr, the formatSpecifier
31 /// is parced from the frmtArr, the corresponding value is extracted from
32 /// the scanArr, and that value is set in the Tcl Interp.
33 ///
34 /// If the chars are not equal, or the conversion fails, the boolean
35 /// scanArrDone is set to true, indicating the scanArr is not to be
36 /// parced and no new values are to be set. However the frmtArr is still
37 /// parced because of the priority of error messages. In the C version
38 /// of Tcl, bad format specifiers throw errors before incorrect argument
39 /// input or other scan errors. Thus we need to parce the entire frmtArr
40 /// to verify correct formating. This is dumb and inefficient but it is
41 /// consistent w/ the current C-version of Tcl.
42 /// </summary>
43  
44 public TCL.CompletionCode cmdProc( Interp interp, TclObject[] argv )
45 {
46  
47 if ( argv.Length < 3 )
48 {
49 throw new TclNumArgsException( interp, 1, argv, "string format ?varName varName ...?" );
50 }
51 ;
52  
53 StrtoulResult strul; // Return value for parcing the scanArr when
54 // extracting integers/longs
55 StrtodResult strd;
56 ; // Return value for parcing the scanArr when
57 // extracting doubles
58 char[] scanArr; // Array containing parce info
59 char[] frmtArr; // Array containing info on how to
60 // parse the scanArr
61 int scanIndex; // Index into the scan array
62 int frmtIndex; // Index into the frmt array
63 int tempIndex; // Temporary index holder
64 int argIndex; // Index into the current arg
65 int width; // Stores the user specified result width
66 int base_; // Base of the integer being converted
67 int numUnMatched; // Number of fields actually set.
68 int numMatched; // Number of fields actually matched.
69 int negateScan; // Mult by result, set to -1 if true
70 int i; // Generic variable
71 char ch; // Generic variable
72 bool cont; // Used in loops to indicate when to stop
73 bool scanOK; // Set to false if strtoul/strtod fails
74 bool scanArrDone; // Set to false if strtoul/strtod fails
75 bool widthFlag; // True is width is specified
76 bool discardFlag; // If a "%*" is in the formatString dont
77 // write output to arg
78  
79  
80 scanArr = argv[1].ToString().ToCharArray();
81  
82 frmtArr = argv[2].ToString().ToCharArray();
83 width = base_ = numMatched = numUnMatched = 0;
84 scanIndex = frmtIndex = 0;
85 scanOK = true;
86 scanArrDone = false;
87 argIndex = 3;
88  
89 // Skip all (if any) of the white space before getting to a char
90  
91 frmtIndex = skipWhiteSpace( frmtArr, frmtIndex );
92  
93 // Search through the frmtArr. If the next char is a '%' parse the
94 // next chars and determine the type (if any) of the format specifier.
95 // If the scanArr has been fully searched, do nothing but incerment
96 // "numUnMatched". The reason to continue the frmtArr search is for
97 // consistency in output. Previously scan format errors were reported
98 // before arg input mismatch, so this maintains the same level of error
99 // checking.
100  
101 while ( frmtIndex < frmtArr.Length )
102 {
103 discardFlag = widthFlag = false;
104 negateScan = 1;
105 cont = true;
106  
107 // Parce the format array and read in the correct value from the
108 // scan array. When the correct value is retrieved, set the
109 // variable (from argv) in the interp.
110  
111 if ( frmtArr[frmtIndex] == '%' )
112 {
113  
114 frmtIndex++;
115 checkOverFlow( interp, frmtArr, frmtIndex );
116  
117 // Two '%'s in a row, do nothing...
118  
119 if ( frmtArr[frmtIndex] == '%' )
120 {
121 frmtIndex++;
122 scanIndex++;
123 continue;
124 }
125  
126 // Check for a discard field flag
127  
128 if ( frmtArr[frmtIndex] == '*' )
129 {
130 discardFlag = true;
131 frmtIndex++;
132 checkOverFlow( interp, frmtArr, frmtIndex );
133 }
134  
135 // Check for a width field and accept the 'h', 'l', 'L'
136 // characters, but do nothing with them.
137 //
138 // Note: The order of the width specifier and the other
139 // chars is unordered, so we need to iterate until all
140 // of the specifiers are identified.
141  
142 while ( cont )
143 {
144 cont = false;
145  
146 switch ( frmtArr[frmtIndex] )
147 {
148  
149 case 'h':
150 case 'l':
151 case 'L':
152 {
153 // Just ignore these values
154  
155 frmtIndex++;
156 cont = true;
157 break;
158 }
159  
160 default:
161 {
162 if ( System.Char.IsDigit( frmtArr[frmtIndex] ) )
163 {
164 strul = Util.strtoul( new string( frmtArr ), frmtIndex, base_ );
165 frmtIndex = strul.index;
166 width = (int)strul.value;
167 widthFlag = true;
168 cont = true;
169 }
170 }
171 break;
172  
173 }
174 checkOverFlow( interp, frmtArr, frmtIndex );
175 }
176  
177 // On all conversion specifiers except 'c', move the
178 // scanIndex to the next non-whitespace.
179  
180 ch = frmtArr[frmtIndex];
181 if ( ( ch != 'c' ) && ( ch != '[' ) && !scanArrDone )
182 {
183 scanIndex = skipWhiteSpace( scanArr, scanIndex );
184 }
185 if ( scanIndex >= scanArr.Length )
186 {
187 scanArrDone = true;
188 }
189  
190 if ( ( scanIndex < scanArr.Length ) && ( ch != 'c' ) && ( ch != '[' ) )
191 {
192 // Since strtoul dosent take signed numbers, make the
193 // value positive and store the sign.
194  
195 if ( scanArr[scanIndex] == '-' )
196 {
197 negateScan = -1;
198 scanIndex++;
199 width--;
200 }
201 else if ( scanArr[scanIndex] == '+' )
202 {
203 scanIndex++;
204 width--;
205 }
206  
207 // The width+scanIndex might be greater than
208 // the scanArr so we need to re-adjust when this
209 // happens.
210  
211 if ( widthFlag && ( width + scanIndex > scanArr.Length ) )
212 {
213 width = scanArr.Length - scanIndex;
214 }
215 }
216  
217 if ( scanIndex >= scanArr.Length )
218 {
219 scanArrDone = true;
220 }
221  
222 // Foreach iteration we want strul and strd to be
223 // null since we error check on this case.
224  
225 strul = null;
226 strd = null;
227  
228 switch ( ch )
229 {
230  
231 case 'd':
232 case 'o':
233 case 'x':
234 {
235  
236 if ( !scanArrDone )
237 {
238  
239 if ( ch == 'd' )
240 {
241 base_ = 10;
242 }
243 else if ( ch == 'o' )
244 {
245 base_ = 8;
246 }
247 else
248 {
249 base_ = 16;
250 }
251  
252 // If the widthFlag is set then convert only
253 // "width" characters to an ascii representation,
254 // else read in until the end of the integer. The
255 // scanIndex is moved to the point where we stop
256 // reading in.
257  
258 if ( widthFlag )
259 {
260 strul = Util.strtoul( new string( scanArr, 0, width + scanIndex ), scanIndex, base_ );
261 }
262 else
263 {
264 strul = Util.strtoul( new string( scanArr ), scanIndex, base_ );
265 }
266 if ( strul.errno != 0 )
267 {
268 scanOK = false;
269 break;
270 }
271 scanIndex = strul.index;
272  
273 if ( !discardFlag )
274 {
275 i = (int)strul.value * negateScan;
276 if ( argIndex == argv.Length )
277 numMatched--;
278 else
279 testAndSetVar( interp, argv, argIndex++, TclInteger.newInstance( i ) );
280 }
281 }
282 break;
283 }
284  
285 case 'c':
286 {
287 if ( widthFlag )
288 {
289 errorCharFieldWidth( interp );
290 }
291 if ( !discardFlag && !scanArrDone )
292 {
293 testAndSetVar( interp, argv, argIndex++, TclInteger.newInstance( scanArr[scanIndex++] ) );
294 }
295 break;
296 }
297  
298 case 's':
299 {
300 if ( !scanArrDone )
301 {
302 // If the widthFlag is set then read only "width"
303 // characters into the string, else read in until
304 // the first whitespace or endArr is found. The
305 // scanIndex is moved to the point where we stop
306 // reading in.
307  
308 tempIndex = scanIndex;
309 if ( !widthFlag )
310 {
311 width = scanArr.Length;
312 }
313 for ( i = 0; ( scanIndex < scanArr.Length ) && ( i < width ); i++ )
314 {
315 ch = scanArr[scanIndex];
316 if ( ( ch == ' ' ) || ( ch == '\n' ) || ( ch == '\r' ) || ( ch == '\t' ) || ( ch == '\f' ) )
317 {
318 break;
319 }
320 scanIndex++;
321 }
322  
323 if ( !discardFlag )
324 {
325 string str = new string( scanArr, tempIndex, scanIndex - tempIndex );
326 testAndSetVar( interp, argv, argIndex++, TclString.newInstance( str ) );
327 }
328 }
329 break;
330 }
331  
332 case 'e':
333 case 'f':
334 case 'g':
335 {
336 if ( !scanArrDone )
337 {
338 // If the wisthFlag is set then read only "width"
339 // characters into the string, else read in until
340 // the first whitespace or endArr is found. The
341 // scanIndex is moved to the point where we stop
342 // reading in.
343  
344 if ( widthFlag )
345 {
346 strd = Util.strtod( new string( scanArr, 0, width + scanIndex ), scanIndex );
347 }
348 else
349 {
350 strd = Util.strtod( new string( scanArr ), scanIndex );
351 }
352 if ( strd.errno != 0 )
353 {
354 scanOK = false;
355 break;
356 }
357 scanIndex = strd.index;
358  
359 if ( !discardFlag )
360 {
361 double d = strd.value * negateScan;
362 testAndSetVar( interp, argv, argIndex++, TclDouble.newInstance( d ) );
363 }
364 }
365 break;
366 }
367  
368 case '[':
369 {
370 bool charMatchFound = false;
371 bool charNotMatch = false;
372 char[] tempArr;
373 int startIndex;
374 int endIndex;
375 string unmatched = "unmatched [ in format string";
376  
377 if ( ( ++frmtIndex ) >= frmtArr.Length )
378 {
379 throw new TclException( interp, unmatched );
380 }
381  
382 if ( frmtArr[frmtIndex] == '^' )
383 {
384 charNotMatch = true;
385 frmtIndex += 2;
386 }
387 else
388 {
389 frmtIndex++;
390 }
391 tempIndex = frmtIndex - 1;
392  
393 if ( frmtIndex >= frmtArr.Length )
394 {
395 throw new TclException( interp, unmatched );
396 }
397  
398 // Extract the list of chars for matching.
399  
400 while ( frmtArr[frmtIndex] != ']' )
401 {
402 if ( ( ++frmtIndex ) >= frmtArr.Length )
403 {
404 throw new TclException( interp, unmatched );
405 }
406 }
407 tempArr = new string( frmtArr, tempIndex, frmtIndex - tempIndex ).ToCharArray();
408  
409 startIndex = scanIndex;
410 if ( charNotMatch )
411 {
412 // Format specifier contained a '^' so interate
413 // until one of the chars in tempArr is found.
414  
415 while ( scanOK && !charMatchFound )
416 {
417 if ( scanIndex >= scanArr.Length )
418 {
419 scanOK = false;
420 break;
421 }
422 for ( i = 0; i < tempArr.Length; i++ )
423 {
424 if ( tempArr[i] == scanArr[scanIndex] )
425 {
426 charMatchFound = true;
427 break;
428 }
429 }
430 if ( widthFlag && ( ( scanIndex - startIndex ) >= width ) )
431 {
432 break;
433 }
434 if ( !charMatchFound )
435 {
436 scanIndex++;
437 }
438 }
439 }
440 else
441 {
442 // Iterate until the char in the scanArr is not
443 // in the tempArr.
444  
445 charMatchFound = true;
446 while ( scanOK && charMatchFound )
447 {
448 if ( scanIndex >= scanArr.Length )
449 {
450 scanOK = false;
451 break;
452 }
453 charMatchFound = false;
454 for ( i = 0; i < tempArr.Length; i++ )
455 {
456 if ( tempArr[i] == scanArr[scanIndex] )
457 {
458 charMatchFound = true;
459 break;
460 }
461 }
462 if ( widthFlag && ( scanIndex - startIndex ) >= width )
463 {
464 break;
465 }
466 if ( charMatchFound )
467 {
468 scanIndex++;
469 }
470 }
471 }
472  
473 // Indicates nothing was found.
474  
475 endIndex = scanIndex - startIndex;
476 if ( endIndex <= 0 )
477 {
478 scanOK = false;
479 break;
480 }
481  
482 if ( !discardFlag )
483 {
484 string str = new string( scanArr, startIndex, endIndex );
485 testAndSetVar( interp, argv, argIndex++, TclString.newInstance( str ) );
486 }
487 break;
488 }
489  
490 default:
491 {
492 errorBadField( interp, ch );
493 }
494 break;
495  
496 }
497  
498 // As long as the scan was successful (scanOK), the format
499 // specifier did not contain a '*' (discardFlag), and
500 // we are not at the end of the scanArr (scanArrDone);
501 // increment the num of vars set in the interp. Otherwise
502 // increment the number of valid format specifiers.
503  
504 if ( scanOK && !discardFlag && !scanArrDone )
505 {
506 numMatched++;
507 }
508 else if ( ( scanArrDone || !scanOK ) && !discardFlag )
509 {
510 numUnMatched++;
511 }
512 frmtIndex++;
513 }
514 else if ( scanIndex < scanArr.Length && scanArr[scanIndex] == frmtArr[frmtIndex] )
515 {
516 // No '%' was found, but the characters matched
517  
518 scanIndex++;
519 frmtIndex++;
520 }
521 else
522 {
523 // No '%' found and the characters int frmtArr & scanArr
524 // did not match.
525  
526 frmtIndex++;
527 }
528 }
529  
530 // The numMatched is the return value: a count of the num of vars set.
531 // While the numUnMatched is the number of formatSpecifiers that
532 // passed the parsing stage, but did not match anything in the scanArr.
533  
534 if ( ( numMatched + numUnMatched ) != ( argv.Length - 3 ) )
535 {
536 errorDiffVars( interp );
537 }
538 interp.setResult( TclInteger.newInstance( numMatched ) );
539 return TCL.CompletionCode.RETURN;
540 }
541  
542  
543 /// <summary> Given an array and an index into it, move the index forward
544 /// until a non-whitespace char is found.
545 ///
546 /// </summary>
547 /// <param name="arr"> - the array to search
548 /// </param>
549 /// <param name="index">- where to begin the search
550 /// </param>
551 /// <returns> The index value where the whitespace ends.
552 /// </returns>
553  
554 private int skipWhiteSpace( char[] arr, int index )
555 {
556 bool cont;
557 do
558 {
559 if ( index >= arr.Length )
560 {
561 return index;
562 }
563 cont = false;
564 switch ( arr[index] )
565 {
566  
567 case '\t':
568 case '\n':
569 case '\r':
570 case '\f':
571 case ' ':
572 {
573 cont = true;
574 index++;
575 }
576 break;
577 }
578 }
579 while ( cont );
580  
581 return index;
582 }
583  
584  
585 /// <summary> Called whenever the cmdProc wants to set an interp value.
586 /// This method <ol>
587 /// <li> verifies that there exisits a varName from the argv array,
588 /// <li> that the variable either dosent exisit or is of type scalar
589 /// <li> set the variable in interp if (1) and (2) are OK
590 /// </ol>
591 ///
592 /// </summary>
593 /// <param name="interp"> - the Tcl interpreter
594 /// </param>
595 /// <param name="argv"> - the argument array
596 /// </param>
597 /// <param name="argIndex">- the current index into the argv array
598 /// </param>
599 /// <param name="tobj"> - the TclObject that the varName equals
600 ///
601 /// </param>
602  
603 private static void testAndSetVar( Interp interp, TclObject[] argv, int argIndex, TclObject tobj )
604 {
605 if ( argIndex < argv.Length )
606 {
607 try
608 {
609  
610 interp.setVar( argv[argIndex].ToString(), tobj, 0 );
611 }
612 catch ( TclException e )
613 {
614  
615 throw new TclException( interp, "couldn't set variable \"" + argv[argIndex].ToString() + "\"" );
616 }
617 }
618 else
619 {
620 errorDiffVars( interp );
621 }
622 }
623  
624  
625 /// <summary> Called whenever the frmtIndex in the cmdProc is changed. It verifies
626 /// the the array index is still within the bounds of the array. If no
627 /// throw error.
628 /// </summary>
629 /// <param name="interp"> - The TclInterp which called the cmdProc method .
630 /// </param>
631 /// <param name="arr"> - The array to be checked.
632 /// </param>
633 /// <param name="index"> - The new value for the array index.
634 /// </param>
635  
636 private static void checkOverFlow( Interp interp, char[] arr, int index )
637 {
638 if ( ( index >= arr.Length ) || ( index < 0 ) )
639 {
640 throw new TclException( interp, "\"%n$\" argument index out of range" );
641 }
642 }
643  
644  
645 /// <summary> Called whenever the number of varName args do not match the number
646 /// of found and valid formatSpecifiers (matched and unmatched).
647 ///
648 /// </summary>
649 /// <param name="interp"> - The TclInterp which called the cmdProc method .
650 /// </param>
651  
652 private static void errorDiffVars( Interp interp )
653 {
654  
655 throw new TclException( interp, "different numbers of variable names and field specifiers" );
656 }
657  
658  
659 /// <summary> Called whenever the current char in the frmtArr is erroneous
660 ///
661 /// </summary>
662 /// <param name="interp"> - The TclInterp which called the cmdProc method .
663 /// </param>
664 /// <param name="fieldSpecifier"> - The erroneous character
665 /// </param>
666  
667 private static void errorBadField( Interp interp, char fieldSpecifier )
668 {
669 throw new TclException( interp, "bad scan conversion character \"" + fieldSpecifier + "\"" );
670 }
671  
672  
673 /// <summary> Called whenever the a width field is used in a char ('c') format
674 /// specifier
675 ///
676 /// </summary>
677 /// <param name="interp"> - The TclInterp which called the cmdProc method .
678 /// </param>
679  
680 private static void errorCharFieldWidth( Interp interp )
681 {
682 throw new TclException( interp, "field width may not be specified in %c conversion" );
683 }
684 }
685 }