internal static ParseResult parseBraces(Interp interp, string str, int index, int length) { char[] arr = str.ToCharArray(); int level = 1; for (int i = index; i < length;) { if (Parser.charType(arr[i]) == Parser.TYPE_NORMAL) { i++; } else if (arr[i] == '}') { level--; if (level == 0) { str = new string( arr, index, i - index ); return(new ParseResult(str, i + 1)); } i++; } else if (arr[i] == '{') { level++; i++; } else if (arr[i] == '\\') { BackSlashResult bs = Parser.backslash(arr, i); i = bs.nextIndex; } else { i++; } } //if you run off the end of the string you went too far throw new TclException(interp, "missing close-brace"); }
internal static FindElemResult findElement(Interp interp, string s, int i, int len) { int openBraces = 0; bool inQuotes = false; for ( ; i < len && System.Char.IsWhiteSpace(s[i]); i++) { ; } if (i >= len) { return(null); } char c = s[i]; if (c == '{') { openBraces = 1; i++; } else if (c == '"') { inQuotes = true; i++; } System.Text.StringBuilder sbuf = new System.Text.StringBuilder(); while (true) { if (i >= len) { if (openBraces != 0) { throw new TclException(interp, "unmatched open brace in list"); } else if (inQuotes) { throw new TclException(interp, "unmatched open quote in list"); } return(new FindElemResult(i, sbuf.ToString())); } c = s[i]; switch (c) { // Open brace: don't treat specially unless the element is // in braces. In this case, keep a nesting count. case '{': if (openBraces != 0) { openBraces++; } sbuf.Append(c); i++; break; // Close brace: if element is in braces, keep nesting // count and quit when the last close brace is seen. case '}': if (openBraces == 1) { if (i == len - 1 || System.Char.IsWhiteSpace(s[i + 1])) { return(new FindElemResult(i + 1, sbuf.ToString())); } else { int errEnd; for (errEnd = i + 1; errEnd < len; errEnd++) { if (System.Char.IsWhiteSpace(s[errEnd])) { break; } } throw new TclException(interp, "list element in braces followed by \"" + s.Substring(i + 1, (errEnd) - (i + 1)) + "\" instead of space"); } } else if (openBraces != 0) { openBraces--; } sbuf.Append(c); i++; break; // Backslash: skip over everything up to the end of the // backslash sequence. case '\\': BackSlashResult bs = Interp.backslash(s, i, len); if (openBraces > 0) { // Quotes are ignored in brace-quoted stuff sbuf.Append(s.Substring(i, (bs.nextIndex) - (i))); } else { sbuf.Append(bs.c); } i = bs.nextIndex; break; // Space: ignore if element is in braces or quotes; otherwise // terminate element. case ' ': case '\f': case '\n': case '\r': case '\t': if ((openBraces == 0) && !inQuotes) { return(new FindElemResult(i + 1, sbuf.ToString())); } else { sbuf.Append(c); i++; } break; // Double-quote: if element is in quotes then terminate it. case '"': if (inQuotes) { if (i == len - 1 || System.Char.IsWhiteSpace(s[i + 1])) { return(new FindElemResult(i + 1, sbuf.ToString())); } else { int errEnd; for (errEnd = i + 1; errEnd < len; errEnd++) { if (System.Char.IsWhiteSpace(s[errEnd])) { break; } } throw new TclException(interp, "list element in quotes followed by \"" + s.Substring(i + 1, (errEnd) - (i + 1)) + "\" instead of space"); } } else { sbuf.Append(c); i++; } break; default: sbuf.Append(c); i++; break; } } }
/// <summary> This procedure is invoked to process the "subst" Tcl command. /// See the user documentation for details on what it does. /// /// </summary> /// <param name="interp">the current interpreter. /// </param> /// <param name="argv">command arguments. /// </param> /// <exception cref=""> TclException if wrong # of args or invalid argument(s). /// </exception> public TCL.CompletionCode cmdProc(Interp interp, TclObject[] argv) { int currentObjIndex, len, i; int objc = argv.Length - 1; bool doBackslashes = true; bool doCmds = true; bool doVars = true; System.Text.StringBuilder result = new System.Text.StringBuilder(); string s; char c; for (currentObjIndex = 1; currentObjIndex < objc; currentObjIndex++) { if (!argv[currentObjIndex].ToString().StartsWith("-")) { break; } int opt = TclIndex.get(interp, argv[currentObjIndex], validCmds, "switch", 0); switch (opt) { case OPT_NOBACKSLASHES: doBackslashes = false; break; case OPT_NOCOMMANDS: doCmds = false; break; case OPT_NOVARS: doVars = false; break; default: throw new TclException(interp, "SubstCmd.cmdProc: bad option " + opt + " index to cmds"); } } if (currentObjIndex != objc) { throw new TclNumArgsException(interp, currentObjIndex, argv, "?-nobackslashes? ?-nocommands? ?-novariables? string"); } /* * Scan through the string one character at a time, performing * command, variable, and backslash substitutions. */ s = argv[currentObjIndex].ToString(); len = s.Length; i = 0; while (i < len) { c = s[i]; if ((c == '[') && doCmds) { ParseResult res; try { interp.evalFlags = Parser.TCL_BRACKET_TERM; interp.eval(s.Substring(i + 1, (len) - (i + 1))); TclObject interp_result = interp.getResult(); interp_result.preserve(); res = new ParseResult(interp_result, i + interp.termOffset); } catch (TclException e) { i = e.errIndex + 1; throw; } i = res.nextIndex + 2; result.Append(res.value.ToString()); res.release(); } else if (c == '\r') { /* * (ToDo) may not be portable on Mac */ i++; } else if ((c == '$') && doVars) { ParseResult vres = Parser.parseVar(interp, s.Substring(i, (len) - (i))); i += vres.nextIndex; result.Append(vres.value.ToString()); vres.release(); } else if ((c == '\\') && doBackslashes) { BackSlashResult bs = tcl.lang.Interp.backslash(s, i, len); i = bs.nextIndex; if (bs.isWordSep) { break; } else { result.Append(bs.c); } } else { result.Append(c); i++; } } interp.setResult(result.ToString()); return(TCL.CompletionCode.RETURN); }
internal static int scanElement(Interp interp, string inString) { int flags, nestingLevel; char c; int len; int i; // This procedure and Tcl_ConvertElement together do two things: // // 1. They produce a proper list, one that will yield back the // argument strings when evaluated or when disassembled with // Tcl_SplitList. This is the most important thing. // // 2. They try to produce legible output, which means minimizing the // use of backslashes (using braces instead). However, there are // some situations where backslashes must be used (e.g. an element // like "{abc": the leading brace will have to be backslashed. For // each element, one of three things must be done: // // (a) Use the element as-is (it doesn't contain anything special // characters). This is the most desirable option. // // (b) Enclose the element in braces, but leave the contents alone. // This happens if the element contains embedded space, or if it // contains characters with special interpretation ($, [, ;, or \), // or if it starts with a brace or double-quote, or if there are // no characters in the element. // // (c) Don't enclose the element in braces, but add backslashes to // prevent special interpretation of special characters. This is a // last resort used when the argument would normally fall under case // (b) but contains unmatched braces. It also occurs if the last // character of the argument is a backslash or if the element contains // a backslash followed by newline. // // The procedure figures out how many bytes will be needed to store // the result (actually, it overestimates). It also collects // information about the element in the form of a flags word. nestingLevel = 0; flags = 0; i = 0; len = (inString != null ? inString.Length : 0); if (len == 0) { inString = '\x0000'.ToString(); // FIXME : pizza compiler workaround // We really should be able to use the "\0" form but there // is a nasty bug in the pizza compiler shipped with kaffe // that causes "\0" to be read as the empty string. //string = "\0"; } System.Diagnostics.Debug.WriteLine("scanElement string is \"" + inString + "\""); c = inString[i]; if ((c == '{') || (c == '"') || (c == '\x0000')) { flags |= USE_BRACES; } for ( ; i < len; i++) { System.Diagnostics.Debug.WriteLine("getting char at index " + i); System.Diagnostics.Debug.WriteLine("char is '" + inString[i] + "'"); c = inString[i]; switch (c) { case '{': nestingLevel++; break; case '}': nestingLevel--; if (nestingLevel < 0) { flags |= TCL_DONT_USE_BRACES | BRACES_UNMATCHED; } break; case '[': case '$': case ';': case ' ': case '\f': case '\n': case '\r': case '\t': case (char)(0x0b): flags |= USE_BRACES; break; case '\\': if ((i >= len - 1) || (inString[i + 1] == '\n')) { flags = TCL_DONT_USE_BRACES | BRACES_UNMATCHED; } else { BackSlashResult bs = Interp.backslash(inString, i, len); // Subtract 1 because the for loop will automatically // add one on the next iteration. i = (bs.nextIndex - 1); flags |= USE_BRACES; } break; } } if (nestingLevel != 0) { flags = TCL_DONT_USE_BRACES | BRACES_UNMATCHED; } return(flags); }