private static TclObject ScanNumber(byte[] src, int pos, int type) // Format character from "binary scan" { switch (type) { case 'c': { return(TclInteger.newInstance((sbyte)src[pos])); } case 's': { short value = (short)((src[pos] & 0xff) + ((src[pos + 1] & 0xff) << 8)); return(TclInteger.newInstance((int)value)); } case 'S': { short value = (short)((src[pos + 1] & 0xff) + ((src[pos] & 0xff) << 8)); return(TclInteger.newInstance((int)value)); } case 'i': { int value = (src[pos] & 0xff) + ((src[pos + 1] & 0xff) << 8) + ((src[pos + 2] & 0xff) << 16) + ((src[pos + 3] & 0xff) << 24); return(TclInteger.newInstance(value)); } case 'I': { int value = (src[pos + 3] & 0xff) + ((src[pos + 2] & 0xff) << 8) + ((src[pos + 1] & 0xff) << 16) + ((src[pos] & 0xff) << 24); return(TclInteger.newInstance(value)); } case 'f': { System.IO.MemoryStream ms = new System.IO.MemoryStream(src, pos, 4, false); System.IO.BinaryReader reader = new System.IO.BinaryReader(ms); double fvalue = reader.ReadSingle(); reader.Close(); ms.Close(); return(TclDouble.newInstance(fvalue)); } case 'd': { System.IO.MemoryStream ms = new System.IO.MemoryStream(src, pos, 8, false); System.IO.BinaryReader reader = new System.IO.BinaryReader(ms); double dvalue = reader.ReadDouble(); reader.Close(); ms.Close(); return(TclDouble.newInstance(dvalue)); } } return(null); }
/// <summary> This procedure is invoked to process the "scan" Tcl command. /// See the user documentation for details on what it does. /// /// Each iteration of the cmdProc compares the scanArr's current index to /// the frmtArr's index. If the chars are equal then the indicies are /// incremented. If a '%' is found in the frmtArr, the formatSpecifier /// is parced from the frmtArr, the corresponding value is extracted from /// the scanArr, and that value is set in the Tcl Interp. /// /// If the chars are not equal, or the conversion fails, the boolean /// scanArrDone is set to true, indicating the scanArr is not to be /// parced and no new values are to be set. However the frmtArr is still /// parced because of the priority of error messages. In the C version /// of Tcl, bad format specifiers throw errors before incorrect argument /// input or other scan errors. Thus we need to parce the entire frmtArr /// to verify correct formating. This is dumb and inefficient but it is /// consistent w/ the current C-version of Tcl. /// </summary> public TCL.CompletionCode cmdProc(Interp interp, TclObject[] argv) { if (argv.Length < 3) { throw new TclNumArgsException(interp, 1, argv, "string format ?varName varName ...?"); } ; StrtoulResult strul; // Return value for parcing the scanArr when // extracting integers/longs StrtodResult strd;; // Return value for parcing the scanArr when // extracting doubles char[] scanArr; // Array containing parce info char[] frmtArr; // Array containing info on how to // parse the scanArr int scanIndex; // Index into the scan array int frmtIndex; // Index into the frmt array int tempIndex; // Temporary index holder int argIndex; // Index into the current arg int width; // Stores the user specified result width int base_; // Base of the integer being converted int numUnMatched; // Number of fields actually set. int numMatched; // Number of fields actually matched. int negateScan; // Mult by result, set to -1 if true int i; // Generic variable char ch; // Generic variable bool cont; // Used in loops to indicate when to stop bool scanOK; // Set to false if strtoul/strtod fails bool scanArrDone; // Set to false if strtoul/strtod fails bool widthFlag; // True is width is specified bool discardFlag; // If a "%*" is in the formatString dont // write output to arg scanArr = argv[1].ToString().ToCharArray(); frmtArr = argv[2].ToString().ToCharArray(); width = base_ = numMatched = numUnMatched = 0; scanIndex = frmtIndex = 0; scanOK = true; scanArrDone = false; argIndex = 3; // Skip all (if any) of the white space before getting to a char frmtIndex = skipWhiteSpace(frmtArr, frmtIndex); // Search through the frmtArr. If the next char is a '%' parse the // next chars and determine the type (if any) of the format specifier. // If the scanArr has been fully searched, do nothing but incerment // "numUnMatched". The reason to continue the frmtArr search is for // consistency in output. Previously scan format errors were reported // before arg input mismatch, so this maintains the same level of error // checking. while (frmtIndex < frmtArr.Length) { discardFlag = widthFlag = false; negateScan = 1; cont = true; // Parce the format array and read in the correct value from the // scan array. When the correct value is retrieved, set the // variable (from argv) in the interp. if (frmtArr[frmtIndex] == '%') { frmtIndex++; checkOverFlow(interp, frmtArr, frmtIndex); // Two '%'s in a row, do nothing... if (frmtArr[frmtIndex] == '%') { frmtIndex++; scanIndex++; continue; } // Check for a discard field flag if (frmtArr[frmtIndex] == '*') { discardFlag = true; frmtIndex++; checkOverFlow(interp, frmtArr, frmtIndex); } // Check for a width field and accept the 'h', 'l', 'L' // characters, but do nothing with them. // // Note: The order of the width specifier and the other // chars is unordered, so we need to iterate until all // of the specifiers are identified. while (cont) { cont = false; switch (frmtArr[frmtIndex]) { case 'h': case 'l': case 'L': { // Just ignore these values frmtIndex++; cont = true; break; } default: { if (System.Char.IsDigit(frmtArr[frmtIndex])) { strul = Util.strtoul(new string(frmtArr), frmtIndex, base_); frmtIndex = strul.index; width = (int)strul.value; widthFlag = true; cont = true; } } break; } checkOverFlow(interp, frmtArr, frmtIndex); } // On all conversion specifiers except 'c', move the // scanIndex to the next non-whitespace. ch = frmtArr[frmtIndex]; if ((ch != 'c') && (ch != '[') && !scanArrDone) { scanIndex = skipWhiteSpace(scanArr, scanIndex); } if (scanIndex >= scanArr.Length) { scanArrDone = true; } if ((scanIndex < scanArr.Length) && (ch != 'c') && (ch != '[')) { // Since strtoul dosent take signed numbers, make the // value positive and store the sign. if (scanArr[scanIndex] == '-') { negateScan = -1; scanIndex++; width--; } else if (scanArr[scanIndex] == '+') { scanIndex++; width--; } // The width+scanIndex might be greater than // the scanArr so we need to re-adjust when this // happens. if (widthFlag && (width + scanIndex > scanArr.Length)) { width = scanArr.Length - scanIndex; } } if (scanIndex >= scanArr.Length) { scanArrDone = true; } // Foreach iteration we want strul and strd to be // null since we error check on this case. strul = null; strd = null; switch (ch) { case 'd': case 'o': case 'x': { if (!scanArrDone) { if (ch == 'd') { base_ = 10; } else if (ch == 'o') { base_ = 8; } else { base_ = 16; } // If the widthFlag is set then convert only // "width" characters to an ascii representation, // else read in until the end of the integer. The // scanIndex is moved to the point where we stop // reading in. if (widthFlag) { strul = Util.strtoul(new string(scanArr, 0, width + scanIndex), scanIndex, base_); } else { strul = Util.strtoul(new string(scanArr), scanIndex, base_); } if (strul.errno != 0) { scanOK = false; break; } scanIndex = strul.index; if (!discardFlag) { i = (int)strul.value * negateScan; if (argIndex == argv.Length) { numMatched--; } else { testAndSetVar(interp, argv, argIndex++, TclInteger.newInstance(i)); } } } break; } case 'c': { if (widthFlag) { errorCharFieldWidth(interp); } if (!discardFlag && !scanArrDone) { testAndSetVar(interp, argv, argIndex++, TclInteger.newInstance(scanArr[scanIndex++])); } break; } case 's': { if (!scanArrDone) { // If the widthFlag is set then read only "width" // characters into the string, else read in until // the first whitespace or endArr is found. The // scanIndex is moved to the point where we stop // reading in. tempIndex = scanIndex; if (!widthFlag) { width = scanArr.Length; } for (i = 0; (scanIndex < scanArr.Length) && (i < width); i++) { ch = scanArr[scanIndex]; if ((ch == ' ') || (ch == '\n') || (ch == '\r') || (ch == '\t') || (ch == '\f')) { break; } scanIndex++; } if (!discardFlag) { string str = new string(scanArr, tempIndex, scanIndex - tempIndex); testAndSetVar(interp, argv, argIndex++, TclString.newInstance(str)); } } break; } case 'e': case 'f': case 'g': { if (!scanArrDone) { // If the wisthFlag is set then read only "width" // characters into the string, else read in until // the first whitespace or endArr is found. The // scanIndex is moved to the point where we stop // reading in. if (widthFlag) { strd = Util.strtod(new string(scanArr, 0, width + scanIndex), scanIndex); } else { strd = Util.strtod(new string(scanArr), scanIndex); } if (strd.errno != 0) { scanOK = false; break; } scanIndex = strd.index; if (!discardFlag) { double d = strd.value * negateScan; testAndSetVar(interp, argv, argIndex++, TclDouble.newInstance(d)); } } break; } case '[': { bool charMatchFound = false; bool charNotMatch = false; char[] tempArr; int startIndex; int endIndex; string unmatched = "unmatched [ in format string"; if ((++frmtIndex) >= frmtArr.Length) { throw new TclException(interp, unmatched); } if (frmtArr[frmtIndex] == '^') { charNotMatch = true; frmtIndex += 2; } else { frmtIndex++; } tempIndex = frmtIndex - 1; if (frmtIndex >= frmtArr.Length) { throw new TclException(interp, unmatched); } // Extract the list of chars for matching. while (frmtArr[frmtIndex] != ']') { if ((++frmtIndex) >= frmtArr.Length) { throw new TclException(interp, unmatched); } } tempArr = new string(frmtArr, tempIndex, frmtIndex - tempIndex).ToCharArray(); startIndex = scanIndex; if (charNotMatch) { // Format specifier contained a '^' so interate // until one of the chars in tempArr is found. while (scanOK && !charMatchFound) { if (scanIndex >= scanArr.Length) { scanOK = false; break; } for (i = 0; i < tempArr.Length; i++) { if (tempArr[i] == scanArr[scanIndex]) { charMatchFound = true; break; } } if (widthFlag && ((scanIndex - startIndex) >= width)) { break; } if (!charMatchFound) { scanIndex++; } } } else { // Iterate until the char in the scanArr is not // in the tempArr. charMatchFound = true; while (scanOK && charMatchFound) { if (scanIndex >= scanArr.Length) { scanOK = false; break; } charMatchFound = false; for (i = 0; i < tempArr.Length; i++) { if (tempArr[i] == scanArr[scanIndex]) { charMatchFound = true; break; } } if (widthFlag && (scanIndex - startIndex) >= width) { break; } if (charMatchFound) { scanIndex++; } } } // Indicates nothing was found. endIndex = scanIndex - startIndex; if (endIndex <= 0) { scanOK = false; break; } if (!discardFlag) { string str = new string(scanArr, startIndex, endIndex); testAndSetVar(interp, argv, argIndex++, TclString.newInstance(str)); } break; } default: { errorBadField(interp, ch); } break; } // As long as the scan was successful (scanOK), the format // specifier did not contain a '*' (discardFlag), and // we are not at the end of the scanArr (scanArrDone); // increment the num of vars set in the interp. Otherwise // increment the number of valid format specifiers. if (scanOK && !discardFlag && !scanArrDone) { numMatched++; } else if ((scanArrDone || !scanOK) && !discardFlag) { numUnMatched++; } frmtIndex++; } else if (scanIndex < scanArr.Length && scanArr[scanIndex] == frmtArr[frmtIndex]) { // No '%' was found, but the characters matched scanIndex++; frmtIndex++; } else { // No '%' found and the characters int frmtArr & scanArr // did not match. frmtIndex++; } } // The numMatched is the return value: a count of the num of vars set. // While the numUnMatched is the number of formatSpecifiers that // passed the parsing stage, but did not match anything in the scanArr. if ((numMatched + numUnMatched) != (argv.Length - 3)) { errorDiffVars(interp); } interp.setResult(TclInteger.newInstance(numMatched)); return(TCL.CompletionCode.RETURN); }
public static TclObject Tcl_NewDoubleObj(double value) { return(TclDouble.newInstance(value)); }