0001: /*
0002: * Util.java --
0003: *
0004: * This class provides useful Tcl utility methods.
0005: *
0006: * Copyright (c) 1997 Cornell University.
0007: * Copyright (c) 1997-1999 by Sun Microsystems, Inc.
0008: *
0009: * See the file "license.terms" for information on usage and redistribution
0010: * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
0011: *
0012: * RCS: @(#) $Id: Util.java,v 1.26 2006/06/13 06:52:47 mdejong Exp $
0013: */
0014:
0015: package tcl.lang;
0016:
0017: import sunlabs.brazil.util.regexp.Regexp;
0018:
0019: import java.io.*;
0020: import java.util.*;
0021:
0022: public class Util {
0023:
0024: static final int TCL_DONT_USE_BRACES = 1;
0025: static final int USE_BRACES = 2;
0026: static final int BRACES_UNMATCHED = 4;
0027:
0028: // Some error messages.
0029:
0030: static final String intTooBigCode = "ARITH IOVERFLOW {integer value too large to represent}";
0031: static final String fpTooBigCode = "ARITH OVERFLOW {floating-point value too large to represent}";
0032:
0033: // This table below is used to convert from ASCII digits to a
0034: // numerical equivalent. It maps from '0' through 'z' to integers
0035: // (100 for non-digit characters).
0036:
0037: static char cvtIn[] = { 0, 1, 2, 3, 4, 5, 6, 7,
0038: 8,
0039: 9, // '0' - '9'
0040: 100, 100, 100, 100, 100,
0041: 100,
0042: 100, // punctuation
0043: 10, 11, 12, 13, 14, 15, 16, 17,
0044: 18,
0045: 19, // 'A' - 'Z'
0046: 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34,
0047: 35, 100, 100, 100, 100, 100,
0048: 100, // punctuation
0049: 10, 11, 12, 13, 14, 15, 16, 17, 18,
0050: 19, // 'a' - 'z'
0051: 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34,
0052: 35 };
0053:
0054: // Largest possible base 10 exponent. Any
0055: // exponent larger than this will already
0056: // produce underflow or overflow, so there's
0057: // no need to worry about additional digits.
0058:
0059: static final int maxExponent = 511;
0060:
0061: // Table giving binary powers of 10. Entry
0062: // is 10^2^i. Used to convert decimal
0063: // exponents into floating-point numbers.
0064:
0065: static final double powersOf10[] = { 10., 100., 1.0e4, 1.0e8,
0066: 1.0e16, 1.0e32, 1.0e64, 1.0e128, 1.0e256 };
0067:
0068: // Default precision for converting floating-point values to strings.
0069:
0070: static final int DEFAULT_PRECISION = 12;
0071:
0072: // The following variable determine the precision used when converting
0073: // floating-point values to strings. This information is linked to all
0074: // of the tcl_precision variables in all interpreters inside a JVM via
0075: // PrecTraceProc.
0076: //
0077: // Note: since multiple threads may change precision concurrently, race
0078: // conditions may occur.
0079: //
0080: // It should be modified only by the PrecTraceProc class.
0081:
0082: static int precision = DEFAULT_PRECISION;
0083:
0084: /*
0085: *----------------------------------------------------------------------
0086: *
0087: * Util --
0088: * Dummy constructor to keep Java from automatically creating a
0089: * default public constructor for the Util class.
0090: *
0091: * Side effects:
0092: * None.
0093: *
0094: *----------------------------------------------------------------------
0095: */
0096:
0097: private Util() {
0098: // Do nothing. This should never be called.
0099: }
0100:
0101: /*
0102: *----------------------------------------------------------------------
0103: *
0104: * strtoul --
0105: *
0106: * Implements functionality of the strtoul() function used in the
0107: * C Tcl library. This method will parse digits from what
0108: * should be a 32-bit (signed) integer and report the index
0109: * of the character immediately following the digits.
0110: *
0111: * E.g.: "0x7fffffff" -> 2147483647
0112: * "0x80000000" -> -2147483648
0113: * "0x100000000" -> errno = TCL.INTEGER_RANGE
0114: * "-0xFF" -> -255
0115: *
0116: * This method behaves like the strtoul() function in NativeTcl.
0117: * This method will return a signed 64-bit Java long type in
0118: * the strtoulResult argument. This value is used as a signed
0119: * integer in the expr module. A leading signed character like
0120: * '+' or '-' is supported. Leading spaces are skipped.
0121: *
0122: * Results:
0123: * The strtoulResult argument will be populated with the parsed
0124: * value and the index of the character following the digits.
0125: * If an error is detected, then the strtoulResult errno value
0126: * will be set accordingly.
0127: *
0128: * Side effects:
0129: * None.
0130: *
0131: *----------------------------------------------------------------------
0132: */
0133:
0134: static void strtoul(String s, // String of ASCII digits, possibly preceded by
0135: // white space. For bases greater than 10, either
0136: // lower- or upper-case digits may be used.
0137: int start, // The index of s where the number starts.
0138: int base, // Base for conversion. Must be less than 37. If 0,
0139: // then the base is chosen from the leading characters
0140: // of string: "0x" means hex, "0" means octal,
0141: // anything else means decimal.
0142: StrtoulResult strtoulResult) // Location to store results in
0143: {
0144: long result = 0;
0145: int digit;
0146: boolean anyDigits = false;
0147: boolean negative = false;
0148: int len = s.length();
0149: int i = start;
0150: char c = '\0';
0151:
0152: // Skip any leading blanks.
0153:
0154: while (i < len
0155: && (((c = s.charAt(i)) == ' ') || Character
0156: .isWhitespace(c))) {
0157: i++;
0158: }
0159: if (i >= len) {
0160: strtoulResult.update(0, 0, TCL.INVALID_INTEGER);
0161: return;
0162: }
0163:
0164: if (c == '-') {
0165: negative = true;
0166: }
0167: if (c == '-' || c == '+') {
0168: i++;
0169: if (i >= len) {
0170: strtoulResult.update(0, 0, TCL.INVALID_INTEGER);
0171: return;
0172: }
0173: c = s.charAt(i);
0174: }
0175:
0176: // If no base was provided, pick one from the leading characters
0177: // of the string.
0178:
0179: if (base == 0) {
0180: if (c == '0') {
0181: if (i < len - 1) {
0182: i++;
0183: c = s.charAt(i);
0184: if (c == 'x' || c == 'X') {
0185: i++;
0186: base = 16;
0187: }
0188: }
0189: if (base == 0) {
0190: // Must set anyDigits here, otherwise "0" produces a
0191: // "no digits" error.
0192:
0193: anyDigits = true;
0194: base = 8;
0195: }
0196: } else {
0197: base = 10;
0198: }
0199: } else if (base == 16) {
0200: if (i < len - 2) {
0201: // Skip a leading "0x" from hex numbers.
0202:
0203: if ((c == '0') && (s.charAt(i + 1) == 'x')) {
0204: i += 2;
0205: }
0206: }
0207: }
0208:
0209: long max = (((long) ((long) 1 << 32)) / ((long) base));
0210: boolean overflowed = false;
0211:
0212: for (;; i += 1) {
0213: if (i >= len) {
0214: break;
0215: }
0216: digit = s.charAt(i) - '0';
0217: if (digit < 0 || digit > ('z' - '0')) {
0218: break;
0219: }
0220: digit = cvtIn[digit];
0221: if (digit >= base) {
0222: break;
0223: }
0224:
0225: if (result > max) {
0226: overflowed = true;
0227: }
0228:
0229: result = result * base + digit;
0230: anyDigits = true;
0231: }
0232:
0233: // See if there were any digits at all.
0234:
0235: if (!anyDigits) {
0236: strtoulResult.update(0, 0, TCL.INVALID_INTEGER);
0237: } else if (overflowed) {
0238: strtoulResult.update(0, i, TCL.INTEGER_RANGE);
0239: } else {
0240: strtoulResult.update((negative ? -result : result), i, 0);
0241: }
0242: }
0243:
0244: /*
0245: *----------------------------------------------------------------------
0246: *
0247: * getInt --
0248: *
0249: * Converts an ASCII string to an integer.
0250: *
0251: * Results:
0252: * The integer value of the string.
0253: *
0254: * Side effects:
0255: * None.
0256: *
0257: *----------------------------------------------------------------------
0258: */
0259:
0260: static int getInt(Interp interp, // The current interpreter. Can be null.
0261: String s) // The string to convert from. Must be in valid
0262: // Tcl integer format.
0263: throws TclException // If the string is not a valid Tcl integer.
0264: {
0265: int len = s.length();
0266: int i = 0;
0267: char c;
0268:
0269: StrtoulResult res;
0270: if (interp == null) {
0271: res = new StrtoulResult();
0272: } else {
0273: res = interp.strtoulResult;
0274: }
0275: Util.strtoul(s, i, 0, res);
0276:
0277: if (res.errno < 0) {
0278: if (res.errno == TCL.INTEGER_RANGE) {
0279: if (interp != null) {
0280: interp.setErrorCode(TclString
0281: .newInstance(intTooBigCode));
0282: }
0283: throw new TclException(interp,
0284: "integer value too large to represent");
0285: } else {
0286: throw new TclException(interp,
0287: "expected integer but got \"" + s + "\""
0288: + checkBadOctal(interp, s));
0289: }
0290: } else if (res.index < len) {
0291: for (i = res.index; i < len; i++) {
0292: if (((c = s.charAt(i)) != ' ')
0293: && !Character.isWhitespace(c)) {
0294: throw new TclException(interp,
0295: "expected integer but got \"" + s + "\""
0296: + checkBadOctal(interp, s));
0297: }
0298: }
0299: }
0300:
0301: return (int) res.value;
0302: }
0303:
0304: /*
0305: *----------------------------------------------------------------------
0306: *
0307: * TclGetIntForIndex -> Util.getIntForIndex
0308: *
0309: * This procedure returns an integer corresponding to the list index
0310: * held in a Tcl object. The Tcl object's value is expected to be
0311: * either an integer or a string of the form "end([+-]integer)?".
0312: *
0313: * Results:
0314: * The return value is the index that is found from the string. If
0315: * the Tcl object referenced by tobj has the value "end", the
0316: * value stored is endValue. If tobj's value is not of the form
0317: * "end([+-]integer)?" and it
0318: * can not be converted to an integer, an exception is raised.
0319: *
0320: * Side effects:
0321: * The object referenced by tobj might be converted to an
0322: * integer object.
0323: *
0324: *----------------------------------------------------------------------
0325: */
0326: static final int getIntForIndex(Interp interp, // Interp object, can be null
0327: TclObject tobj, int endValue) throws TclException {
0328: int length, offset;
0329:
0330: if (tobj.isIntType()) {
0331: return TclInteger.get(interp, tobj);
0332: }
0333:
0334: String bytes = tobj.toString();
0335: length = bytes.length();
0336:
0337: if ((length == 0)
0338: || !"end".regionMatches(0, bytes, 0, (length > 3) ? 3
0339: : length)) {
0340: try {
0341: offset = TclInteger.get(null, tobj);
0342: } catch (TclException e) {
0343: throw new TclException(interp, "bad index \"" + bytes
0344: + "\": must be integer or end?-integer?"
0345: + checkBadOctal(interp, bytes));
0346: }
0347: return offset;
0348: }
0349:
0350: if (length <= 3) {
0351: return endValue;
0352: } else if ((length > 4) && (bytes.charAt(3) == '-')) {
0353: // This is our limited string expression evaluator
0354: // Pass everything after "end-" to then reverse for offset.
0355:
0356: try {
0357: offset = Util.getInt(interp, bytes.substring(4));
0358: offset = -offset;
0359: return endValue + offset;
0360: } catch (TclException ex) {
0361: // Fall through to bad index error
0362: }
0363: }
0364:
0365: throw new TclException(interp, "bad index \"" + bytes
0366: + "\": must be integer or end?-integer?"
0367: + checkBadOctal(interp, bytes.substring(3)));
0368: }
0369:
0370: /*
0371: *----------------------------------------------------------------------
0372: *
0373: * TclCheckBadOctal -> Util.checkBadOctal
0374: *
0375: * This procedure checks for a bad octal value and returns a
0376: * meaningful error that should be appended to the interp's result.
0377: *
0378: * Results:
0379: * Returns error message if it was a bad octal.
0380: *
0381: * Side effects:
0382: * None.
0383: *
0384: *----------------------------------------------------------------------
0385: */
0386:
0387: static final String checkBadOctal(Interp interp, // Interpreter to use for error reporting.
0388: // If NULL, then no error message is returned.
0389: String value) {
0390: int p = 0;
0391: final int len = value.length();
0392:
0393: // A frequent mistake is invalid octal values due to an unwanted
0394: // leading zero. Try to generate a meaningful error message.
0395:
0396: while (p < len && Character.isWhitespace(value.charAt(p))) {
0397: p++;
0398: }
0399: if ((p < len)
0400: && (value.charAt(p) == '+' || value.charAt(p) == '-')) {
0401: p++;
0402: }
0403: if ((p < len) && (value.charAt(p) == '0')) {
0404: while ((p < len) && Character.isDigit(value.charAt(p))) { // INTL: digit.
0405: p++;
0406: }
0407: while ((p < len) && Character.isWhitespace(value.charAt(p))) { // INTL: ISO space.
0408: p++;
0409: }
0410: if (p >= len) {
0411: // Reached end of string
0412: if (interp != null) {
0413: return " (looks like invalid octal number)";
0414: }
0415: }
0416: }
0417: return "";
0418: }
0419:
0420: /*
0421: *----------------------------------------------------------------------
0422: *
0423: * strtod --
0424: *
0425: * Converts the leading decimal digits of a string into double
0426: * and report the index of the character immediately following the
0427: * digits.
0428: *
0429: * Results:
0430: * Converts the leading decimal digits of a string into double
0431: * and report the index of the character immediately following the
0432: * digits.
0433: *
0434: * Side effects:
0435: * None.
0436: *
0437: *----------------------------------------------------------------------
0438: */
0439:
0440: static void strtod(String s, // String of ASCII digits, possibly preceded by
0441: // white space. For bases greater than 10, either lower- or
0442: // upper-case digits may be used.
0443: final int start, // The index of the string to start on.
0444: int len, // The string length, or -1
0445: StrtodResult strtodResult) // place to store results
0446: {
0447: int decPt = -1; // Number of mantissa digits BEFORE decimal
0448: // point.
0449: int si;
0450: int i = (start < 0 ? 0 : start);
0451: boolean negative = false;
0452: char c = '\0';
0453: String sub;
0454:
0455: if (len < 0) {
0456: len = s.length();
0457: }
0458:
0459: // Skip any leading blanks.
0460:
0461: while (i < len
0462: && (((c = s.charAt(i)) == ' ') || Character
0463: .isWhitespace(c))) {
0464: i++;
0465: }
0466: if (i >= len) {
0467: strtodResult.update(0, 0, TCL.INVALID_DOUBLE);
0468: return;
0469: }
0470:
0471: // Return special value for the string "NaN"
0472:
0473: if (c == 'N' || c == 'n') {
0474: sub = (i == 0 ? s : s.substring(i));
0475: if (sub.toLowerCase().startsWith("nan")) {
0476: strtodResult.update(Double.NaN, i + 3, 0);
0477: return;
0478: }
0479: }
0480:
0481: if (c == '-') {
0482: negative = true;
0483: }
0484: if (c == '-' || c == '+') {
0485: i++;
0486: if (i >= len) {
0487: strtodResult.update(0, 0, TCL.INVALID_DOUBLE);
0488: return;
0489: }
0490: c = s.charAt(i);
0491: }
0492:
0493: // The strings "Inf", "-Inf", "Infinity", and "-Infinity"
0494: // map to special double values.
0495:
0496: if (c == 'I') {
0497: int infLen = 0;
0498:
0499: sub = (i == 0 ? s : s.substring(i));
0500:
0501: if (sub.startsWith("Infinity")) {
0502: infLen = "Infinity".length();
0503: } else if (sub.startsWith("Inf")) {
0504: infLen = "Inf".length();
0505: }
0506:
0507: if (infLen > 0) {
0508: strtodResult.update(
0509: (negative ? Double.NEGATIVE_INFINITY
0510: : Double.POSITIVE_INFINITY),
0511: i + infLen, 0);
0512: return;
0513: }
0514: }
0515:
0516: // Index of first digit now known
0517:
0518: si = i;
0519:
0520: // Count the number of digits in the mantissa (including the decimal
0521: // point), and also locate the decimal point.
0522:
0523: boolean maybeZero = true;
0524:
0525: for (int mantSize = 0;; mantSize += 1) {
0526: if ((c >= '0' && c <= '9') || Character.isDigit(c)) {
0527: // This is a digit
0528: } else {
0529: if ((c != '.') || (decPt >= 0)) {
0530: break;
0531: }
0532: decPt = mantSize;
0533: }
0534: if (c != '0' && c != '.') {
0535: maybeZero = false; // non zero digit found...
0536: }
0537: i++;
0538: if (i >= len) {
0539: break;
0540: } else {
0541: c = s.charAt(i);
0542: }
0543: }
0544:
0545: // Skim off the exponent.
0546:
0547: if (i < len) {
0548: if (si != i) { // same c value as when above for loop was entered
0549: c = s.charAt(i);
0550: }
0551: if ((c == 'E') || (c == 'e')) {
0552: i++;
0553: if (i < len) {
0554: c = s.charAt(i);
0555: if (c == '-') {
0556: i++;
0557: } else if (c == '+') {
0558: i++;
0559: }
0560:
0561: boolean notdigit = false;
0562: if (i < len) {
0563: c = s.charAt(i);
0564: if ((c >= '0' && c <= '9')
0565: || Character.isDigit(c)) {
0566: // This is a digit
0567: } else {
0568: notdigit = true;
0569: }
0570: }
0571:
0572: if (i >= len || notdigit) {
0573: // A number like 1E+ or 1eq2 is not a double
0574: // with an exponent part. In this case, return
0575: // the number up to the 'E' or 'e'.
0576: if (c == '-' || c == '+') {
0577: i--;
0578: }
0579: i--;
0580: } else {
0581: for (; i < len; i++) {
0582: c = s.charAt(i);
0583: if ((c >= '0' && c <= '9')
0584: || Character.isDigit(c)) {
0585: // This is a digit
0586: } else {
0587: break;
0588: }
0589: }
0590: }
0591: }
0592: }
0593: }
0594:
0595: // Avoid pointless substring or NumberFormatException
0596: // for an empty string or for a string like "abc"
0597: // that has no digits.
0598:
0599: if (si == i) {
0600: strtodResult.update(0, 0, TCL.INVALID_DOUBLE);
0601: return;
0602: }
0603:
0604: s = s.substring(si, i);
0605: double result = 0;
0606:
0607: try {
0608: result = Double.valueOf(s).doubleValue();
0609: } catch (NumberFormatException e) {
0610: strtodResult.update(0, 0, TCL.INVALID_DOUBLE);
0611: return;
0612: }
0613:
0614: if ((result == Double.NEGATIVE_INFINITY)
0615: || (result == Double.POSITIVE_INFINITY)
0616: || (result == 0.0 && !maybeZero)) {
0617: strtodResult.update(result, i, TCL.DOUBLE_RANGE);
0618: return;
0619: }
0620:
0621: if (result == Double.NaN) {
0622: strtodResult.update(0, 0, TCL.INVALID_DOUBLE);
0623: return;
0624: }
0625:
0626: strtodResult.update((negative ? -result : result), i, 0);
0627: return;
0628: }
0629:
0630: /*
0631: *----------------------------------------------------------------------
0632: *
0633: * getDouble --
0634: *
0635: * Converts an ASCII string to a double.
0636: *
0637: * Results:
0638: * The double value of the string.
0639: *
0640: * Side effects:
0641: * None.
0642: *
0643: *----------------------------------------------------------------------
0644: */
0645:
0646: static double getDouble(Interp interp, // The current interpreter, can be null.
0647: String s) // The string to convert from. Must be in valid
0648: // Tcl double format.
0649: throws TclException // If the string is not a valid Tcl double.
0650: {
0651: int len = s.length();
0652: int i = 0;
0653: char c;
0654:
0655: StrtodResult res;
0656: if (interp == null) {
0657: res = new StrtodResult();
0658: } else {
0659: res = interp.strtodResult;
0660: }
0661: Util.strtod(s, i, len, res);
0662:
0663: if (res.errno != 0) {
0664: if (res.errno == TCL.DOUBLE_RANGE) {
0665: if (interp != null) {
0666: interp.setErrorCode(TclString
0667: .newInstance(fpTooBigCode));
0668: }
0669: throw new TclException(interp,
0670: "floating-point value too large to represent");
0671: } else {
0672: throw new TclException(interp,
0673: "expected floating-point number but got \"" + s
0674: + "\"");
0675: }
0676: } else if (res.index < len) {
0677: for (i = res.index; i < len; i++) {
0678: if ((c = s.charAt(i)) != ' '
0679: && !Character.isWhitespace(c)) {
0680: throw new TclException(interp,
0681: "expected floating-point number but got \""
0682: + s + "\"");
0683: }
0684: }
0685: }
0686:
0687: return res.value;
0688: }
0689:
0690: /*
0691: *----------------------------------------------------------------------
0692: *
0693: * Tcl_ConcatObj -> concat
0694: *
0695: * Concatenate the strings from a set of objects into a single string
0696: * object with spaces between the original strings.
0697: *
0698: * Results:
0699: * The return value is a new string object containing a concatenation
0700: * of the strings in objv. Its ref count is zero.
0701: *
0702: * Side effects:
0703: * None.
0704: *
0705: *----------------------------------------------------------------------
0706: */
0707:
0708: static TclObject concat(int from, // The starting index.
0709: int to, // The ending index (inclusive).
0710: TclObject[] objv) // Array of objects to concatenate.
0711: throws TclException {
0712: int allocSize, elemLength, i, j;
0713: String element;
0714: StringBuffer concatStr;
0715: TclObject obj, tlist;
0716: boolean allList;
0717:
0718: if (from > objv.length) {
0719: return TclString.newInstance("");
0720: }
0721: if (to <= objv.length) {
0722: to = objv.length - 1;
0723: }
0724:
0725: // Check first to see if all the items are of list type. If so,
0726: // we will concat them together as lists, and return a list object.
0727: // This is only valid when the lists have no current string
0728: // representation, since we don't know what the original type was.
0729: // An original string rep may have lost some whitespace info when
0730: // converted which could be important.
0731:
0732: allList = true;
0733: for (i = from; i <= to; i++) {
0734: obj = objv[i];
0735: if (obj.hasNoStringRep() && obj.isListType()) {
0736: // A pure list element
0737: } else {
0738: allList = false;
0739: break;
0740: }
0741: }
0742: if (allList) {
0743: tlist = TclList.newInstance();
0744: for (i = from; i <= to; i++) {
0745: // Tcl_ListObjAppendList could be used here, but this saves
0746: // us a bit of type checking (since we've already done it)
0747: // Use of MAX_VALUE tells us to always put the new stuff on
0748: // the end. It will be set right in Tcl_ListObjReplace.
0749:
0750: obj = objv[i];
0751: TclObject[] elements = TclList.getElements(null, obj);
0752: TclList.replace(null, tlist, Integer.MAX_VALUE, 0,
0753: elements, 0, elements.length - 1);
0754: }
0755: return tlist;
0756: }
0757:
0758: allocSize = 0;
0759: for (i = from; i <= to; i++) {
0760: obj = objv[i];
0761: element = obj.toString();
0762: elemLength = element.length();
0763: if ((element != null) && (elemLength > 0)) {
0764: allocSize += (elemLength + 1);
0765: }
0766: }
0767: if (allocSize == 0) {
0768: allocSize = 1;
0769: }
0770:
0771: // Allocate storage for the concatenated result.
0772:
0773: concatStr = new StringBuffer(allocSize);
0774:
0775: // Now concatenate the elements. Clip white space off the front and back
0776: // to generate a neater result, and ignore any empty elements.
0777:
0778: for (i = from; i <= to; i++) {
0779: obj = objv[i];
0780: element = obj.toString();
0781: element = TrimLeft(element, " ");
0782: elemLength = element.length();
0783:
0784: // Trim trailing white space. But, be careful not to trim
0785: // a space character if it is preceded by a backslash: in
0786: // this case it could be significant.
0787:
0788: for (j = elemLength - 1; j >= 0; j--) {
0789: char c = element.charAt(j);
0790: if (c == ' ' || Character.isWhitespace(c)) {
0791: // A whitespace char
0792: if (j > 0 && element.charAt(j - 1) == '\\') {
0793: // Don't trim backslash space
0794: break;
0795: }
0796: } else {
0797: // Not a whitespace char
0798: break;
0799: }
0800: }
0801: if (j != (elemLength - 1)) {
0802: element = element.substring(0, j + 1);
0803: }
0804: if (element.length() == 0) {
0805: /* Don't leave extra space in the buffer */
0806: if (i == to && (concatStr.length() > 0)) {
0807: concatStr.setLength(concatStr.length() - 1);
0808: }
0809: continue;
0810: }
0811: concatStr.append(element);
0812: if (i < to) {
0813: concatStr.append(' ');
0814: }
0815: }
0816:
0817: return TclString.newInstance(concatStr);
0818: }
0819:
0820: /*
0821: *----------------------------------------------------------------------
0822: *
0823: * stringMatch --
0824: *
0825: * See if a particular string matches a particular pattern. The
0826: * matching operation permits the following special characters in
0827: * the pattern: *?\[] (see the manual entry for details on what
0828: * these mean).
0829: *
0830: * Results:
0831: * True if the string matches with the pattern.
0832: *
0833: * Side effects:
0834: * None.
0835: *
0836: *----------------------------------------------------------------------
0837: */
0838:
0839: public static final boolean stringMatch(String str, //String to compare pattern against.
0840: String pat) //Pattern which may contain special characters.
0841: {
0842: char[] strArr = str.toCharArray();
0843: char[] patArr = pat.toCharArray();
0844: int strLen = str.length(); // Cache the len of str.
0845: int patLen = pat.length(); // Cache the len of pat.
0846: int pIndex = 0; // Current index into patArr.
0847: int sIndex = 0; // Current index into patArr.
0848: char strch; // Stores current char in string.
0849: char ch1; // Stores char after '[' in pat.
0850: char ch2; // Stores look ahead 2 char in pat.
0851: boolean incrIndex = false; // If true it will incr both p/sIndex.
0852:
0853: while (true) {
0854:
0855: if (incrIndex == true) {
0856: pIndex++;
0857: sIndex++;
0858: incrIndex = false;
0859: }
0860:
0861: // See if we're at the end of both the pattern and the string.
0862: // If so, we succeeded. If we're at the end of the pattern
0863: // but not at the end of the string, we failed.
0864:
0865: if (pIndex == patLen) {
0866: return sIndex == strLen;
0867: }
0868: if ((sIndex == strLen) && (patArr[pIndex] != '*')) {
0869: return false;
0870: }
0871:
0872: // Check for a "*" as the next pattern character. It matches
0873: // any substring. We handle this by calling ourselves
0874: // recursively for each postfix of string, until either we
0875: // match or we reach the end of the string.
0876:
0877: if (patArr[pIndex] == '*') {
0878: pIndex++;
0879: if (pIndex == patLen) {
0880: return true;
0881: }
0882: while (true) {
0883: if (stringMatch(str.substring(sIndex), pat
0884: .substring(pIndex))) {
0885: return true;
0886: }
0887: if (sIndex == strLen) {
0888: return false;
0889: }
0890: sIndex++;
0891: }
0892: }
0893:
0894: // Check for a "?" as the next pattern character. It matches
0895: // any single character.
0896:
0897: if (patArr[pIndex] == '?') {
0898: incrIndex = true;
0899: continue;
0900: }
0901:
0902: // Check for a "[" as the next pattern character. It is followed
0903: // by a list of characters that are acceptable, or by a range
0904: // (two characters separated by "-").
0905:
0906: if (patArr[pIndex] == '[') {
0907: pIndex++;
0908: while (true) {
0909: if ((pIndex == patLen) || (patArr[pIndex] == ']')) {
0910: return false;
0911: }
0912: if (sIndex == strLen) {
0913: return false;
0914: }
0915: ch1 = patArr[pIndex];
0916: strch = strArr[sIndex];
0917: if (((pIndex + 1) != patLen)
0918: && (patArr[pIndex + 1] == '-')) {
0919: if ((pIndex += 2) == patLen) {
0920: return false;
0921: }
0922: ch2 = patArr[pIndex];
0923: if (((ch1 <= strch) && (ch2 >= strch))
0924: || ((ch1 >= strch) && (ch2 <= strch))) {
0925: break;
0926: }
0927: } else if (ch1 == strch) {
0928: break;
0929: }
0930: pIndex++;
0931: }
0932:
0933: for (pIndex++; ((pIndex != patLen) && (patArr[pIndex] != ']')); pIndex++) {
0934: }
0935: if (pIndex == patLen) {
0936: pIndex--;
0937: }
0938: incrIndex = true;
0939: continue;
0940: }
0941:
0942: // If the next pattern character is '\', just strip off the '\'
0943: // so we do exact matching on the character that follows.
0944:
0945: if (patArr[pIndex] == '\\') {
0946: pIndex++;
0947: if (pIndex == patLen) {
0948: return false;
0949: }
0950: }
0951:
0952: // There's no special character. Just make sure that the next
0953: // characters of each string match.
0954:
0955: if ((sIndex == strLen)
0956: || (patArr[pIndex] != strArr[sIndex])) {
0957: return false;
0958: }
0959: incrIndex = true;
0960: }
0961: }
0962:
0963: /*
0964: *----------------------------------------------------------------------
0965: *
0966: * Tcl_UtfToTitle -> toTitle --
0967: *
0968: * Changes the first character of a string to title case or
0969: * uppercase and the rest of the string to lowercase.
0970: *
0971: * Results:
0972: * Returns the generated string.
0973: *
0974: * Side effects:
0975: * None.
0976: *
0977: *----------------------------------------------------------------------
0978: */
0979:
0980: static String toTitle(String str) // String to convert in place.
0981: {
0982: // Capitalize the first character and then lowercase the rest of the
0983: // characters until we get to the end of string.
0984:
0985: int length = str.length();
0986: if (length == 0) {
0987: return "";
0988: }
0989: StringBuffer buf = new StringBuffer(length);
0990: buf.append(Character.toTitleCase(str.charAt(0)));
0991: buf.append(str.substring(1).toLowerCase());
0992: return buf.toString();
0993: }
0994:
0995: /*
0996: *-----------------------------------------------------------------------------
0997: *
0998: * regExpMatch --
0999: *
1000: * See if a string matches a regular expression.
1001: *
1002: * Results:
1003: * Returns a boolean whose value depends on whether a match was made.
1004: *
1005: * Side effects:
1006: * None.
1007: *
1008: *-----------------------------------------------------------------------------
1009: */
1010:
1011: static final boolean regExpMatch(Interp interp, // Current interpreter.
1012: String string, // The string to match.
1013: TclObject pattern) // The regular expression.
1014: throws TclException {
1015: Regexp r = TclRegexp.compile(interp, pattern, false);
1016: return r.match(string, (String[]) null);
1017: }
1018:
1019: /*
1020: *-----------------------------------------------------------------------------
1021: *
1022: * appendElement --
1023: *
1024: * Append a string to the string buffer. If the string buffer is not
1025: * empty, append a space before appending "s".
1026: *
1027: * Results:
1028: * None.
1029: *
1030: * Side effects:
1031: * The value of "sbuf" is changesd.
1032: *
1033: *-----------------------------------------------------------------------------
1034: */
1035:
1036: static final void appendElement(Interp interp, // Current interpreter.
1037: StringBuffer sbuf, // The buffer to append to.
1038: String s) // The string to append.
1039: throws TclException {
1040: if (sbuf.length() > 0) {
1041: sbuf.append(' ');
1042: }
1043:
1044: int flags = scanElement(interp, s);
1045: convertElement(s, flags, sbuf);
1046: }
1047:
1048: /*
1049: *----------------------------------------------------------------------
1050: *
1051: * findElement --
1052: *
1053: * Given a String that contains a Tcl list, locate the first (or next)
1054: * element in the list.
1055: *
1056: * Results:
1057: * This method returns true and populates the FindElemResult if an
1058: * element was found. If no element was found, false will be returned.
1059: * The FindElemResult contains the index of the first and last characters
1060: * of the element and the string value of the element.
1061: *
1062: * Side effects:
1063: * None.
1064: *
1065: *----------------------------------------------------------------------
1066: */
1067:
1068: static final boolean findElement(Interp interp, // Current interpreter. If non-null, is used
1069: // to store error messages.
1070: String s, // The string to locate an element.
1071: int i, // The index inside s to start locating an
1072: // element.
1073: int len, // The length of the string.
1074: FindElemResult fer) // The result object to populate.
1075: throws TclException {
1076: int openBraces = 0;
1077: boolean inQuotes = false;
1078: char c = '\0';
1079: int elemStart, elemEnd;
1080: int size = 0;
1081:
1082: while (i < len
1083: && (((c = s.charAt(i)) == ' ') || Character
1084: .isWhitespace(c))) {
1085: i++;
1086: }
1087: if (i >= len) {
1088: return false;
1089: }
1090: if (c == '{') {
1091: openBraces = 1;
1092: i++;
1093: } else if (c == '"') {
1094: inQuotes = true;
1095: i++;
1096: }
1097:
1098: // An element typically consist of a range of characters
1099: // that are a substring of the string s, so s.substring()
1100: // can be used in most cases. If an element requires
1101: // backslashes then use a StringBuffer.
1102:
1103: StringBuffer sbuf = null;
1104: int simpleStart, simpleEnd = -1;
1105: String elem;
1106:
1107: elemStart = i;
1108: simpleStart = i;
1109:
1110: while (true) {
1111: if (i >= len) {
1112: elemEnd = i;
1113: size = (elemEnd - elemStart);
1114: if (openBraces != 0) {
1115: throw new TclException(interp,
1116: "unmatched open brace in list");
1117: } else if (inQuotes) {
1118: throw new TclException(interp,
1119: "unmatched open quote in list");
1120: }
1121: if (sbuf == null) {
1122: elem = s.substring(elemStart, elemEnd);
1123: } else {
1124: sbuf.append(s.substring(simpleStart, elemEnd));
1125: elem = sbuf.toString();
1126: }
1127: fer.update(elemStart, elemEnd, elem, size);
1128: return true;
1129: }
1130:
1131: c = s.charAt(i);
1132: switch (c) {
1133: // Open brace: don't treat specially unless the element is
1134: // in braces. In this case, keep a nesting count.
1135:
1136: case '{':
1137: if (openBraces != 0) {
1138: openBraces++;
1139: }
1140: i++;
1141: break;
1142:
1143: // Close brace: if element is in braces, keep nesting
1144: // count and quit when the last close brace is seen.
1145:
1146: case '}':
1147: if (openBraces == 1) {
1148: elemEnd = i;
1149: size = (elemEnd - elemStart);
1150: if (i == len - 1
1151: || Character.isWhitespace(s.charAt(i + 1))) {
1152: if (sbuf == null) {
1153: elem = s.substring(elemStart, elemEnd);
1154: } else {
1155: sbuf.append(s.substring(simpleStart,
1156: elemEnd));
1157: elem = sbuf.toString();
1158: }
1159: fer.update(elemStart, elemEnd + 1, elem, size);
1160: return true;
1161: } else {
1162: int errEnd;
1163: for (errEnd = i + 1; errEnd < len; errEnd++) {
1164: if (Character
1165: .isWhitespace(s.charAt(errEnd))) {
1166: break;
1167: }
1168: }
1169: throw new TclException(interp,
1170: "list element in braces followed by \""
1171: + s.substring(i + 1, errEnd)
1172: + "\" instead of space");
1173: }
1174: } else if (openBraces != 0) {
1175: openBraces--;
1176: }
1177: i++;
1178: break;
1179:
1180: // Backslash: skip over everything up to the end of the
1181: // backslash sequence.
1182:
1183: case '\\':
1184: BackSlashResult bs = Interp.backslash(s, i, len);
1185: if (openBraces > 0) {
1186: // Backslashes are ignored in brace-quoted stuff
1187:
1188: } else {
1189: if (sbuf == null) {
1190: sbuf = new StringBuffer();
1191: }
1192: sbuf.append(s.substring(simpleStart, i));
1193: sbuf.append(bs.c);
1194: simpleStart = bs.nextIndex;
1195: }
1196: i = bs.nextIndex;
1197:
1198: break;
1199:
1200: // Space: ignore if element is in braces or quotes; otherwise
1201: // terminate element.
1202:
1203: case ' ':
1204: case '\f':
1205: case '\n':
1206: case '\r':
1207: case '\t':
1208: if ((openBraces == 0) && !inQuotes) {
1209: elemEnd = i;
1210: size = (elemEnd - elemStart);
1211: if (sbuf == null) {
1212: elem = s.substring(elemStart, elemEnd);
1213: } else {
1214: sbuf.append(s.substring(simpleStart, elemEnd));
1215: elem = sbuf.toString();
1216: }
1217: fer.update(elemStart, elemEnd, elem, size);
1218: return true;
1219: } else {
1220: i++;
1221: }
1222: break;
1223:
1224: // Double-quote: if element is in quotes then terminate it.
1225:
1226: case '"':
1227: if (inQuotes) {
1228: elemEnd = i;
1229: size = (elemEnd - elemStart);
1230: if (i == len - 1
1231: || Character.isWhitespace(s.charAt(i + 1))) {
1232: if (sbuf == null) {
1233: elem = s.substring(elemStart, elemEnd);
1234: } else {
1235: sbuf.append(s.substring(simpleStart,
1236: elemEnd));
1237: elem = sbuf.toString();
1238: }
1239: fer.update(elemStart, elemEnd + 1, elem, size);
1240: return true;
1241: } else {
1242: int errEnd;
1243: for (errEnd = i + 1; errEnd < len; errEnd++) {
1244: if (Character
1245: .isWhitespace(s.charAt(errEnd))) {
1246: break;
1247: }
1248: }
1249: throw new TclException(interp,
1250: "list element in quotes followed by \""
1251: + s.substring(i + 1, errEnd)
1252: + "\" instead of space");
1253: }
1254: } else {
1255: i++;
1256: }
1257: break;
1258:
1259: default:
1260: i++;
1261: }
1262: }
1263: }
1264:
1265: /*
1266: *----------------------------------------------------------------------
1267: *
1268: * Tcl_ScanElement -> scanElement
1269: *
1270: * This procedure is a companion procedure to convertElement.
1271: * It scans a string to see what needs to be done to it (e.g.
1272: * add backslashes or enclosing braces) to make the string into
1273: * a valid Tcl list element.
1274: *
1275: * Results:
1276: * The flags needed by Tcl_ConvertElement when doing the actual
1277: * conversion.
1278: *
1279: * Side effects:
1280: * None.
1281: *
1282: *----------------------------------------------------------------------
1283: */
1284:
1285: static int scanElement(Interp interp, // The current interpreter.
1286: String string) // The String to scan.
1287: throws TclException {
1288: int flags, nestingLevel;
1289: char c;
1290: int len;
1291: int i;
1292:
1293: // This procedure and Tcl_ConvertElement together do two things:
1294: //
1295: // 1. They produce a proper list, one that will yield back the
1296: // argument strings when evaluated or when disassembled with
1297: // Tcl_SplitList. This is the most important thing.
1298: //
1299: // 2. They try to produce legible output, which means minimizing the
1300: // use of backslashes (using braces instead). However, there are
1301: // some situations where backslashes must be used (e.g. an element
1302: // like "{abc": the leading brace will have to be backslashed. For
1303: // each element, one of three things must be done:
1304: //
1305: // (a) Use the element as-is (it doesn't contain anything special
1306: // characters). This is the most desirable option.
1307: //
1308: // (b) Enclose the element in braces, but leave the contents alone.
1309: // This happens if the element contains embedded space, or if it
1310: // contains characters with special interpretation ($, [, ;, or \),
1311: // or if it starts with a brace or double-quote, or if there are
1312: // no characters in the element.
1313: //
1314: // (c) Don't enclose the element in braces, but add backslashes to
1315: // prevent special interpretation of special characters. This is a
1316: // last resort used when the argument would normally fall under case
1317: // (b) but contains unmatched braces. It also occurs if the last
1318: // character of the argument is a backslash or if the element contains
1319: // a backslash followed by newline.
1320: //
1321: // The procedure figures out how many bytes will be needed to store
1322: // the result (actually, it overestimates). It also collects
1323: // information about the element in the form of a flags word.
1324:
1325: final boolean debug = false;
1326:
1327: nestingLevel = 0;
1328: flags = 0;
1329:
1330: i = 0;
1331: len = string.length();
1332: if (len == 0) {
1333: string = String.valueOf('\0');
1334:
1335: // FIXME : pizza compiler workaround
1336: // We really should be able to use the "\0" form but there
1337: // is a nasty bug in the pizza compiler shipped with kaffe
1338: // that causes "\0" to be read as the empty string.
1339:
1340: //string = "\0";
1341: }
1342:
1343: if (debug) {
1344: System.out.println("scanElement string is \"" + string
1345: + "\"");
1346: }
1347:
1348: c = string.charAt(i);
1349: if ((c == '{') || (c == '"') || (c == '\0')) {
1350: flags |= USE_BRACES;
1351: }
1352: for (; i < len; i++) {
1353: if (debug) {
1354: System.out.println("getting char at index " + i);
1355: System.out
1356: .println("char is '" + string.charAt(i) + "'");
1357: }
1358:
1359: c = string.charAt(i);
1360: switch (c) {
1361: case '{':
1362: nestingLevel++;
1363: break;
1364: case '}':
1365: nestingLevel--;
1366: if (nestingLevel < 0) {
1367: flags |= TCL_DONT_USE_BRACES | BRACES_UNMATCHED;
1368: }
1369: break;
1370: case '[':
1371: case '$':
1372: case ';':
1373: case ' ':
1374: case '\f':
1375: case '\n':
1376: case '\r':
1377: case '\t':
1378: case 0x0b:
1379:
1380: // 0x0b is the character '\v' -- this escape sequence is
1381: // not available in Java, so we hard-code it. We need to
1382: // support \v to provide compatibility with native Tcl.
1383:
1384: flags |= USE_BRACES;
1385: break;
1386: case '\\':
1387: if ((i >= len - 1) || (string.charAt(i + 1) == '\n')) {
1388: flags = TCL_DONT_USE_BRACES | BRACES_UNMATCHED;
1389: } else {
1390: BackSlashResult bs = Interp.backslash(string, i,
1391: len);
1392:
1393: // Subtract 1 because the for loop will automatically
1394: // add one on the next iteration.
1395:
1396: i = (bs.nextIndex - 1);
1397: flags |= USE_BRACES;
1398: }
1399: break;
1400: }
1401: }
1402: if (nestingLevel != 0) {
1403: flags = TCL_DONT_USE_BRACES | BRACES_UNMATCHED;
1404: }
1405:
1406: return flags;
1407: }
1408:
1409: /*
1410: *----------------------------------------------------------------------
1411: *
1412: * Tcl_ConvertElement -> convertElement
1413: *
1414: * This is a companion procedure to scanElement. Given the
1415: * information produced by scanElement, this procedure converts
1416: * a string to a list element equal to that string.
1417: *
1418: * Results:
1419: * Conterts a string so to a new string so that Tcl List information
1420: * is not lost.
1421: *
1422: * Side effects:
1423: * None.
1424: *
1425: *----------------------------------------------------------------------
1426: */
1427:
1428: static void convertElement(String s, // Source information for list element.
1429: int flags, // Flags produced by scanElement
1430: StringBuffer sbuf) // Buffer to write element to
1431: {
1432: int i = 0;
1433: char c;
1434: int len = s.length();
1435:
1436: // See the comment block at the beginning of the ScanElement
1437: // code for details of how this works.
1438:
1439: if ((s == null) || (s.length() == 0) || (s.charAt(0) == '\0')) {
1440: sbuf.append("{}");
1441: return;
1442: }
1443:
1444: if (((flags & USE_BRACES) != 0)
1445: && ((flags & TCL_DONT_USE_BRACES) == 0)) {
1446: sbuf.append('{');
1447: sbuf.append(s);
1448: sbuf.append('}');
1449: } else {
1450: c = s.charAt(0);
1451: if (c == '{') {
1452: // Can't have a leading brace unless the whole element is
1453: // enclosed in braces. Add a backslash before the brace.
1454: // Furthermore, this may destroy the balance between open
1455: // and close braces, so set BRACES_UNMATCHED.
1456:
1457: sbuf.append('\\');
1458: sbuf.append('{');
1459: i++;
1460: flags |= BRACES_UNMATCHED;
1461: }
1462:
1463: for (; i < len; i++) {
1464: c = s.charAt(i);
1465: switch (c) {
1466: case ']':
1467: case '[':
1468: case '$':
1469: case ';':
1470: case ' ':
1471: case '\\':
1472: case '"':
1473: sbuf.append('\\');
1474: break;
1475:
1476: case '{':
1477: case '}':
1478: // It may not seem necessary to backslash braces, but
1479: // it is. The reason for this is that the resulting
1480: // list element may actually be an element of a sub-list
1481: // enclosed in braces (e.g. if Tcl_DStringStartSublist
1482: // has been invoked), so there may be a brace mismatch
1483: // if the braces aren't backslashed.
1484:
1485: if ((flags & BRACES_UNMATCHED) != 0) {
1486: sbuf.append('\\');
1487: }
1488: break;
1489:
1490: case '\f':
1491: sbuf.append('\\');
1492: sbuf.append('f');
1493: continue;
1494:
1495: case '\n':
1496: sbuf.append('\\');
1497: sbuf.append('n');
1498: continue;
1499:
1500: case '\r':
1501: sbuf.append('\\');
1502: sbuf.append('r');
1503: continue;
1504:
1505: case '\t':
1506: sbuf.append('\\');
1507: sbuf.append('t');
1508: continue;
1509: case 0x0b:
1510: // 0x0b is the character '\v' -- this escape sequence is
1511: // not available in Java, so we hard-code it. We need to
1512: // support \v to provide compatibility with native Tcl.
1513:
1514: sbuf.append('\\');
1515: sbuf.append('v');
1516: continue;
1517: }
1518:
1519: sbuf.append(c);
1520: }
1521: }
1522:
1523: return;
1524: }
1525:
1526: /*
1527: *----------------------------------------------------------------------
1528: *
1529: * TrimLeft --
1530: *
1531: * Trim characters in "pattern" off the left of a string
1532: * If pattern isn't supplied, whitespace is trimmed
1533: *
1534: * Results:
1535: * |>None.<|
1536: *
1537: * Side effects:
1538: * |>None.<|
1539: *
1540: *----------------------------------------------------------------------
1541: */
1542:
1543: static String TrimLeft(String str, String pattern) {
1544: int i, j;
1545: char c, p;
1546: char[] strArray = str.toCharArray();
1547: char[] patternArray = pattern.toCharArray();
1548: final int strLen = strArray.length;
1549: final int patLen = patternArray.length;
1550: boolean done;
1551:
1552: for (i = 0; i < strLen; i++) {
1553: c = str.charAt(i);
1554: done = true;
1555: for (j = 0; j < patLen; j++) {
1556: p = pattern.charAt(j);
1557: if (c == p || (p == ' ' && Character.isWhitespace(c))) {
1558: done = false;
1559: break;
1560: }
1561: }
1562: if (done) {
1563: break;
1564: }
1565: }
1566: return str.substring(i, strLen);
1567: }
1568:
1569: /*
1570: *----------------------------------------------------------------------
1571: *
1572: * TrimLeft --
1573: *
1574: * |>description<|
1575: *
1576: * Results:
1577: * |>None.<|
1578: *
1579: * Side effects:
1580: * |>None.<|
1581: *
1582: *----------------------------------------------------------------------
1583: */
1584:
1585: static String TrimLeft(String str) {
1586: return TrimLeft(str, " \n\t\r");
1587: }
1588:
1589: /*
1590: *----------------------------------------------------------------------
1591: *
1592: * TrimRight --
1593: *
1594: * Trim characters in "pattern" off the right of a string
1595: * If pattern isn't supplied, whitespace is trimmed
1596: *
1597: * Results:
1598: * |>None.<|
1599: *
1600: * Side effects:
1601: * |>None.<|
1602: *
1603: *----------------------------------------------------------------------
1604: */
1605:
1606: static String TrimRight(String str, String pattern) {
1607: char[] strArray = str.toCharArray();
1608: char[] patternArray = pattern.toCharArray();
1609: final int patLen = patternArray.length;
1610: char c, p;
1611: int j;
1612: boolean done;
1613: int last = strArray.length - 1;
1614:
1615: // Remove trailing characters...
1616:
1617: while (last >= 0) {
1618: c = strArray[last];
1619: done = true;
1620: for (j = 0; j < patLen; j++) {
1621: p = patternArray[j];
1622: if (c == p || (p == ' ' && Character.isWhitespace(c))) {
1623: done = false;
1624: break;
1625: }
1626: }
1627: if (done) {
1628: break;
1629: }
1630: last--;
1631: }
1632: return str.substring(0, last + 1);
1633: }
1634:
1635: static String TrimRight(String str) {
1636: return TrimRight(str, " \n\t\r");
1637: }
1638:
1639: /*
1640: *----------------------------------------------------------------------
1641: *
1642: * getBoolean --
1643: *
1644: * Given a string, return a boolean value corresponding
1645: * to the string.
1646: *
1647: * Results:
1648: *
1649: *
1650: * Side effects:
1651: * None.
1652: *
1653: *----------------------------------------------------------------------
1654: */
1655:
1656: static boolean getBoolean(Interp interp, // The current interpreter.
1657: String string) // The string representation of the boolean.
1658: throws TclException // For malformed boolean values.
1659: {
1660: String s = string.toLowerCase();
1661:
1662: // The length of 's' needs to be > 1 if it begins with 'o',
1663: // in order to compare between "on" and "off".
1664:
1665: int slen = s.length();
1666:
1667: if (slen > 0) {
1668: char c = s.charAt(0);
1669: switch (c) {
1670: case '0':
1671: if (slen == 1) {
1672: return false;
1673: }
1674: break;
1675: case '1':
1676: if (slen == 1) {
1677: return true;
1678: }
1679: break;
1680: case 'f':
1681: if ("false".startsWith(s)) {
1682: return false;
1683: }
1684: break;
1685: case 'o':
1686: if (slen > 1 && "on".startsWith(s)) {
1687: return true;
1688: }
1689: if (slen > 1 && "off".startsWith(s)) {
1690: return false;
1691: }
1692: break;
1693: case 'n':
1694: if ("no".startsWith(s)) {
1695: return false;
1696: }
1697: break;
1698: case 't':
1699: if ("true".startsWith(s)) {
1700: return true;
1701: }
1702: break;
1703: case 'y':
1704: if ("yes".startsWith(s)) {
1705: return true;
1706: }
1707: break;
1708: }
1709: }
1710:
1711: throw new TclException(interp,
1712: "expected boolean value but got \"" + string + "\"");
1713: }
1714:
1715: /*
1716: *-----------------------------------------------------------------------------
1717: *
1718: * getActualPlatform --
1719: *
1720: * This static procedure returns the integer code for the actuall platform
1721: * on which Jacl is running.
1722: *
1723: * Results:
1724: * Returns and integer.
1725: *
1726: * Side effects:
1727: * None.
1728: *
1729: *-----------------------------------------------------------------------------
1730: */
1731:
1732: final static int getActualPlatform() {
1733: if (Util.isWindows()) {
1734: return JACL.PLATFORM_WINDOWS;
1735: }
1736: if (Util.isMac()) {
1737: return JACL.PLATFORM_MAC;
1738: }
1739: return JACL.PLATFORM_UNIX;
1740: }
1741:
1742: /*
1743: *----------------------------------------------------------------------
1744: *
1745: * isUnix --
1746: *
1747: * Returns true if running on a Unix platform.
1748: *
1749: * Results:
1750: * Returns a boolean.
1751: *
1752: * Side effects:
1753: * None.
1754: *
1755: *----------------------------------------------------------------------
1756: */
1757:
1758: final static boolean isUnix() {
1759: if (isMac() || isWindows()) {
1760: return false;
1761: }
1762: return true;
1763: }
1764:
1765: /*
1766: *----------------------------------------------------------------------
1767: *
1768: * isMac --
1769: *
1770: * Returns true if running on a Mac platform. Note that
1771: * this method returns false for Mac OSX.
1772: *
1773: * Results:
1774: * Returns a boolean.
1775: *
1776: * Side effects:
1777: * None.
1778: *
1779: *----------------------------------------------------------------------
1780: */
1781:
1782: final static boolean isMac() {
1783: String os = System.getProperty("os.name").toLowerCase();
1784: if (os.startsWith("mac") && !os.endsWith("x")) {
1785: return true;
1786: }
1787: return false;
1788: }
1789:
1790: /*
1791: *----------------------------------------------------------------------
1792: *
1793: * isWindows --
1794: *
1795: * Returns true if running on a Windows platform.
1796: *
1797: * Results:
1798: * Returns a boolean.
1799: *
1800: * Side effects:
1801: * None.
1802: *
1803: *----------------------------------------------------------------------
1804: */
1805:
1806: final static boolean isWindows() {
1807: String os = System.getProperty("os.name");
1808: if (os.toLowerCase().startsWith("win")) {
1809: return true;
1810: }
1811: return false;
1812: }
1813:
1814: /*
1815: *----------------------------------------------------------------------
1816: *
1817: * isJacl --
1818: *
1819: * Returns true if running in Jacl. This method is used
1820: * by conditional logic in the tcljava module.
1821: *
1822: * Results:
1823: * Returns a boolean.
1824: *
1825: * Side effects:
1826: * None.
1827: *
1828: *----------------------------------------------------------------------
1829: */
1830:
1831: static boolean isJacl() {
1832: return true;
1833: }
1834:
1835: /*
1836: *----------------------------------------------------------------------
1837: *
1838: * looksLikeInt --
1839: *
1840: * Returns true when isJacl() is true and this string looks
1841: * like an integer.
1842: *
1843: * Results:
1844: * Returns a boolean.
1845: *
1846: * Side effects:
1847: * None.
1848: *
1849: *----------------------------------------------------------------------
1850: */
1851:
1852: static boolean looksLikeInt(String s) {
1853: return Expression.looksLikeInt(s, s.length(), 0, true);
1854: }
1855:
1856: /*
1857: *----------------------------------------------------------------------
1858: *
1859: * setupPrecisionTrace --
1860: *
1861: * Sets up the variable trace of the tcl_precision variable.
1862: *
1863: * Results:
1864: * None.
1865: *
1866: * Side effects:
1867: * A variable trace is set up for the tcl_precision global
1868: * variable.
1869: *
1870: *----------------------------------------------------------------------
1871: */
1872:
1873: static void setupPrecisionTrace(Interp interp) // Current interpreter.
1874: {
1875: try {
1876: interp.traceVar("tcl_precision", new PrecTraceProc(),
1877: TCL.GLOBAL_ONLY | TCL.TRACE_WRITES
1878: | TCL.TRACE_READS | TCL.TRACE_UNSETS);
1879: } catch (TclException e) {
1880: throw new TclRuntimeError("unexpected TclException: " + e);
1881: }
1882: }
1883:
1884: /*
1885: *----------------------------------------------------------------------
1886: *
1887: * printDouble --
1888: *
1889: * Returns the string form of a double number. The exact formatting
1890: * of the string depends on the tcl_precision variable.
1891: *
1892: * Results:
1893: * Returns the string form of double number.
1894: *
1895: * Side effects:
1896: * None.
1897: *
1898: *----------------------------------------------------------------------
1899: */
1900:
1901: static String printDouble(double number) // The number to format into a string.
1902: {
1903: String s = FormatCmd.toString(number, precision, 10);
1904: int length = s.length();
1905: for (int i = 0; i < length; i++) {
1906: if ((s.charAt(i) == '.') || Character.isLetter(s.charAt(i))) {
1907: return s;
1908: }
1909: }
1910: return s + ".0";
1911: }
1912:
1913: /*
1914: *----------------------------------------------------------------------
1915: *
1916: * tryGetSystemProperty --
1917: *
1918: * Tries to get a system property. If it fails because of security
1919: * exceptions, then return the default value.
1920: *
1921: * Results:
1922: * The value of the system property. If it fails because of security
1923: * exceptions, then return the default value.
1924: *
1925: * Side effects:
1926: * None.
1927: *
1928: *----------------------------------------------------------------------
1929: */
1930:
1931: static String tryGetSystemProperty(String propName, // Name of the property
1932: String defautlValue) // Default value.
1933: {
1934: try {
1935: return System.getProperty(propName);
1936: } catch (SecurityException e) {
1937: return defautlValue;
1938: }
1939: }
1940:
1941: } // end Util
1942:
1943: /*
1944: *----------------------------------------------------------------------
1945: *
1946: * PrecTraceProc.java --
1947: *
1948: * The PrecTraceProc class is used to implement variable traces for
1949: * the tcl_precision variable to control precision used when
1950: * converting floating-point values to strings.
1951: *
1952: *----------------------------------------------------------------------
1953: */
1954:
1955: final class PrecTraceProc implements VarTrace {
1956:
1957: // Maximal precision supported by Tcl.
1958:
1959: static final int TCL_MAX_PREC = 17;
1960:
1961: /*
1962: *----------------------------------------------------------------------
1963: *
1964: * traceProc --
1965: *
1966: * This function gets called when the tcl_precision variable is
1967: * accessed in the given interpreter.
1968: *
1969: * Results:
1970: * None.
1971: *
1972: * Side effects:
1973: * If the new value doesn't make sense then this procedure undoes
1974: * the effect of the variable modification. Otherwise it modifies
1975: * Util.precision that's used by Util.printDouble().
1976: *
1977: *----------------------------------------------------------------------
1978: */
1979:
1980: public void traceProc(Interp interp, // Interpreter containing variable.
1981: String name1, // Name of variable.
1982: String name2, // Second part of variable name.
1983: int flags) // Information about what happened.
1984: throws TclException // If the action is a TCL.TRACES_WRITE and
1985: // the new value doesn't make sense.
1986: {
1987: // If the variable is unset, then recreate the trace and restore
1988: // the default value of the format string.
1989:
1990: if ((flags & TCL.TRACE_UNSETS) != 0) {
1991: if (((flags & TCL.TRACE_DESTROYED) != 0)
1992: && ((flags & TCL.INTERP_DESTROYED) == 0)) {
1993: interp.traceVar(name1, name2, new PrecTraceProc(),
1994: TCL.GLOBAL_ONLY | TCL.TRACE_WRITES
1995: | TCL.TRACE_READS | TCL.TRACE_UNSETS);
1996: Util.precision = Util.DEFAULT_PRECISION;
1997: }
1998: return;
1999: }
2000:
2001: // When the variable is read, reset its value from our shared
2002: // value. This is needed in case the variable was modified in
2003: // some other interpreter so that this interpreter's value is
2004: // out of date.
2005:
2006: if ((flags & TCL.TRACE_READS) != 0) {
2007: interp.setVar(name1, name2, Util.precision, flags
2008: & TCL.GLOBAL_ONLY);
2009: return;
2010: }
2011:
2012: // The variable is being written. Check the new value and disallow
2013: // it if it isn't reasonable.
2014: //
2015: // (ToDo) Disallow it if this is a safe interpreter (we don't want
2016: // safe interpreters messing up the precision of other
2017: // interpreters).
2018:
2019: TclObject tobj = null;
2020: try {
2021: tobj = interp.getVar(name1, name2,
2022: (flags & TCL.GLOBAL_ONLY));
2023: } catch (TclException e) {
2024: // Do nothing when var does not exist.
2025: }
2026:
2027: String value;
2028:
2029: if (tobj != null) {
2030: value = tobj.toString();
2031: } else {
2032: value = "";
2033: }
2034:
2035: StrtoulResult r = interp.strtoulResult;
2036: Util.strtoul(value, 0, 10, r);
2037:
2038: if ((r.value <= 0) || (r.value > TCL_MAX_PREC)
2039: || (r.value > 100) || (r.index == 0)
2040: || (r.index != value.length())) {
2041: interp
2042: .setVar(name1, name2, Util.precision,
2043: TCL.GLOBAL_ONLY);
2044: throw new TclException(interp,
2045: "improper value for precision");
2046: }
2047:
2048: Util.precision = (int) r.value;
2049: }
2050:
2051: } // end PrecTraceProc
|