|
|
1.1 ! root 1: /* ! 2: * tclExpr.c -- ! 3: * ! 4: * This file contains the code to evaluate expressions for ! 5: * Tcl. ! 6: * ! 7: * Copyright 1987 Regents of the University of California ! 8: * Permission to use, copy, modify, and distribute this ! 9: * software and its documentation for any purpose and without ! 10: * fee is hereby granted, provided that the above copyright ! 11: * notice appear in all copies. The University of California ! 12: * makes no representations about the suitability of this ! 13: * software for any purpose. It is provided "as is" without ! 14: * express or implied warranty. ! 15: */ ! 16: ! 17: #ifndef lint ! 18: static char rcsid[] = "$Header: /sprite/src/lib/tcl/RCS/tclExpr.c,v 1.13 90/03/22 15:24:59 ouster Exp $ SPRITE (Berkeley)"; ! 19: #pragma ref rcsid ! 20: #endif not lint ! 21: ! 22: #define _POSIX_SOURCE ! 23: ! 24: #include <stdio.h> ! 25: #include <ctype.h> ! 26: #include "tcl.h" ! 27: #include "tclInt.h" ! 28: ! 29: /* ! 30: * The data structure below describes the state of parsing an expression. ! 31: * It's passed among the routines in this module. ! 32: */ ! 33: ! 34: typedef struct { ! 35: Tcl_Interp *interp; /* Intepreter to use for command execution ! 36: * and variable lookup. */ ! 37: char *originalExpr; /* The entire expression, as originally ! 38: * passed to Tcl_Expr. */ ! 39: char *expr; /* Position to the next character to be ! 40: * scanned from the expression string. */ ! 41: int token; /* Type of the last token to be parsed from ! 42: * expr. See below for definitions. ! 43: * Corresponds to the characters just ! 44: * before expr. */ ! 45: int number; /* If token is NUMBER, gives value of ! 46: * the number. */ ! 47: } ExprInfo; ! 48: ! 49: /* ! 50: * The token types are defined below. In addition, there is a table ! 51: * associating a precedence with each operator. The order of types ! 52: * is important. Consult the code before changing it. ! 53: */ ! 54: ! 55: #define NUMBER 0 ! 56: #define OPEN_PAREN 1 ! 57: #define CLOSE_PAREN 2 ! 58: #define END 3 ! 59: #define UNKNOWN 4 ! 60: ! 61: /* ! 62: * Binary operators: ! 63: */ ! 64: ! 65: #define MULT 8 ! 66: #define DIVIDE 9 ! 67: #define MOD 10 ! 68: #define PLUS 11 ! 69: #define MINUS 12 ! 70: #define LEFT_SHIFT 13 ! 71: #define RIGHT_SHIFT 14 ! 72: #define LESS 15 ! 73: #define GREATER 16 ! 74: #define LEQ 17 ! 75: #define GEQ 18 ! 76: #define EQUAL 19 ! 77: #define NEQ 20 ! 78: #define BIT_AND 21 ! 79: #define BIT_XOR 22 ! 80: #define BIT_OR 23 ! 81: #define AND 24 ! 82: #define OR 25 ! 83: #define QUESTY 26 ! 84: #define COLON 27 ! 85: ! 86: /* ! 87: * Unary operators: ! 88: */ ! 89: ! 90: #define UNARY_MINUS 28 ! 91: #define NOT 29 ! 92: #define BIT_NOT 30 ! 93: ! 94: /* ! 95: * Precedence table. The values for non-operator token types are ignored. ! 96: */ ! 97: ! 98: int precTable[] = { ! 99: 0, 0, 0, 0, 0, 0, 0, 0, ! 100: 11, 11, 11, /* MULT, DIVIDE, MOD */ ! 101: 10, 10, /* PLUS, MINUS */ ! 102: 9, 9, /* LEFT_SHIFT, RIGHT_SHIFT */ ! 103: 8, 8, 8, 8, /* LESS, GREATER, LEQ, GEQ */ ! 104: 7, 7, /* EQUAL, NEQ */ ! 105: 6, /* BIT_AND */ ! 106: 5, /* BIT_XOR */ ! 107: 4, /* BIT_OR */ ! 108: 3, /* AND */ ! 109: 2, /* OR */ ! 110: 1, 1, /* QUESTY, COLON */ ! 111: 12, 12, 12 /* UNARY_MINUS, NOT, BIT_NOT */ ! 112: }; ! 113: ! 114: /* ! 115: *---------------------------------------------------------------------- ! 116: * ! 117: * ExprGetNum -- ! 118: * ! 119: * Parse off a number from a string. ! 120: * ! 121: * Results: ! 122: * The return value is the integer value corresponding to the ! 123: * leading digits of string. If termPtr isn't NULL, *termPtr ! 124: * is filled in with the address of the character after the ! 125: * last one that is part of the number. ! 126: * ! 127: * Side effects: ! 128: * None. ! 129: * ! 130: *---------------------------------------------------------------------- ! 131: */ ! 132: ! 133: int ! 134: ExprGetNum(string, termPtr) ! 135: register char *string; /* ASCII representation of number. ! 136: * If leading digit is "0" then read ! 137: * in base 8; if "0x", then read in ! 138: * base 16. */ ! 139: register char **termPtr; /* If non-NULL, fill in with address ! 140: * of terminating character. */ ! 141: { ! 142: int result, sign; ! 143: register char c; ! 144: ! 145: c = *string; ! 146: result = 0; ! 147: if (c == '-') { ! 148: sign = -1; ! 149: string++; c = *string; ! 150: } else { ! 151: sign = 1; ! 152: } ! 153: if (c == '0') { ! 154: string++; c = *string; ! 155: if (c == 'x') { ! 156: while (1) { ! 157: string++; c = *string; ! 158: if ((c >= '0') && (c <= '9')) { ! 159: result = (result << 4) + (c - '0'); ! 160: } else if ((c >= 'a') && (c <= 'f')) { ! 161: result = (result << 4) + 10 + (c - 'a'); ! 162: } else if ((c >= 'A') && (c <= 'F')) { ! 163: result = (result << 4) + 10 + (c - 'A'); ! 164: } else { ! 165: break; ! 166: } ! 167: } ! 168: } else { ! 169: while ((c >= '0') && (c <= '7')) { ! 170: result = (result << 3) + (c - '0'); ! 171: string++; c = *string; ! 172: } ! 173: } ! 174: } else { ! 175: while ((c >= '0') && (c <= '9')) { ! 176: result = (result*10) + (c - '0'); ! 177: string++; c = *string; ! 178: } ! 179: } ! 180: if (termPtr != NULL) { ! 181: *termPtr = string; ! 182: } ! 183: return result*sign; ! 184: } ! 185: ! 186: /* ! 187: *---------------------------------------------------------------------- ! 188: * ! 189: * ExprLex -- ! 190: * ! 191: * Lexical analyzer for expression parser. ! 192: * ! 193: * Results: ! 194: * TCL_OK is returned unless an error occurred while doing lexical ! 195: * analysis or executing an embedded command. In that case a ! 196: * standard Tcl error is returned, using interp->result to hold ! 197: * an error message. In the event of a successful return, the token ! 198: * and (possibly) number fields in infoPtr are updated to refer to ! 199: * the next symbol in the expression string, and the expr field is ! 200: * advanced. ! 201: * ! 202: * Side effects: ! 203: * None. ! 204: * ! 205: *---------------------------------------------------------------------- ! 206: */ ! 207: ! 208: int ! 209: ExprLex(interp, infoPtr) ! 210: Tcl_Interp *interp; /* Interpreter to use for error ! 211: * reporting. */ ! 212: register ExprInfo *infoPtr; /* Describes the state of the parse. */ ! 213: { ! 214: register char *p, c; ! 215: char *var, *term; ! 216: int result; ! 217: ! 218: /* ! 219: * The next token is either: ! 220: * (a) a variable name (indicated by a $ sign plus a variable ! 221: * name in the standard Tcl fashion); lookup the value ! 222: * of the variable and return its numeric equivalent as a ! 223: * number. ! 224: * (b) an embedded command (anything between '[' and ']'). ! 225: * Execute the command and convert its result to a number. ! 226: * (c) a series of decimal digits. Convert it to a number. ! 227: * (d) space: skip it. ! 228: * (d) an operator. See what kind it is. ! 229: */ ! 230: ! 231: p = infoPtr->expr; ! 232: c = *p; ! 233: while (isspace(c)) { ! 234: p++; c = *p; ! 235: } ! 236: infoPtr->expr = p+1; ! 237: switch (c) { ! 238: case '0': ! 239: case '1': ! 240: case '2': ! 241: case '3': ! 242: case '4': ! 243: case '5': ! 244: case '6': ! 245: case '7': ! 246: case '8': ! 247: case '9': ! 248: infoPtr->token = NUMBER; ! 249: infoPtr->number = ExprGetNum(p, &infoPtr->expr); ! 250: return TCL_OK; ! 251: ! 252: case '$': ! 253: infoPtr->token = NUMBER; ! 254: var = Tcl_ParseVar(infoPtr->interp, p, &infoPtr->expr); ! 255: if (var == NULL) { ! 256: return TCL_ERROR; ! 257: } ! 258: if (((Interp *) infoPtr->interp)->noEval) { ! 259: infoPtr->number = 0; ! 260: return TCL_OK; ! 261: } ! 262: infoPtr->number = ExprGetNum(var, &term); ! 263: if ((term == var) || (*term != 0)) { ! 264: c = *infoPtr->expr; ! 265: *infoPtr->expr = 0; ! 266: Tcl_Return(interp, (char *) NULL, TCL_STATIC); ! 267: sprintf(interp->result, ! 268: "variable \"%.50s\" contained non-numeric value \"%.50s\"", ! 269: p, var); ! 270: *infoPtr->expr = c; ! 271: return TCL_ERROR; ! 272: } ! 273: return TCL_OK; ! 274: ! 275: case '[': ! 276: infoPtr->token = NUMBER; ! 277: result = Tcl_Eval(infoPtr->interp, p+1, TCL_BRACKET_TERM, ! 278: &infoPtr->expr); ! 279: if (result != TCL_OK) { ! 280: return result; ! 281: } ! 282: infoPtr->expr++; ! 283: if (((Interp *) infoPtr->interp)->noEval) { ! 284: infoPtr->number = 0; ! 285: Tcl_Return(interp, (char *) NULL, TCL_STATIC); ! 286: return TCL_OK; ! 287: } ! 288: infoPtr->number = ExprGetNum(interp->result, &term); ! 289: if ((term == interp->result) || (*term != 0)) { ! 290: char string[200]; ! 291: infoPtr->expr[-1]; ! 292: infoPtr->expr[-1] = 0; ! 293: sprintf(string, "command \"%.50s\" returned non-numeric result \"%.50s\"", ! 294: p+1, interp->result); ! 295: infoPtr->expr[-1] = c; ! 296: Tcl_Return(interp, string, TCL_VOLATILE); ! 297: return TCL_ERROR; ! 298: } ! 299: Tcl_Return(interp, (char *) NULL, TCL_STATIC); ! 300: return TCL_OK; ! 301: ! 302: case '(': ! 303: infoPtr->token = OPEN_PAREN; ! 304: return TCL_OK; ! 305: ! 306: case ')': ! 307: infoPtr->token = CLOSE_PAREN; ! 308: return TCL_OK; ! 309: ! 310: case '*': ! 311: infoPtr->token = MULT; ! 312: return TCL_OK; ! 313: ! 314: case '/': ! 315: infoPtr->token = DIVIDE; ! 316: return TCL_OK; ! 317: ! 318: case '%': ! 319: infoPtr->token = MOD; ! 320: return TCL_OK; ! 321: ! 322: case '+': ! 323: infoPtr->token = PLUS; ! 324: return TCL_OK; ! 325: ! 326: case '-': ! 327: infoPtr->token = MINUS; ! 328: return TCL_OK; ! 329: ! 330: case '?': ! 331: infoPtr->token = QUESTY; ! 332: return TCL_OK; ! 333: ! 334: case ':': ! 335: infoPtr->token = COLON; ! 336: return TCL_OK; ! 337: ! 338: case '<': ! 339: switch (p[1]) { ! 340: case '<': ! 341: infoPtr->expr = p+2; ! 342: infoPtr->token = LEFT_SHIFT; ! 343: break; ! 344: case '=': ! 345: infoPtr->expr = p+2; ! 346: infoPtr->token = LEQ; ! 347: break; ! 348: default: ! 349: infoPtr->token = LESS; ! 350: break; ! 351: } ! 352: return TCL_OK; ! 353: ! 354: case '>': ! 355: switch (p[1]) { ! 356: case '>': ! 357: infoPtr->expr = p+2; ! 358: infoPtr->token = RIGHT_SHIFT; ! 359: break; ! 360: case '=': ! 361: infoPtr->expr = p+2; ! 362: infoPtr->token = GEQ; ! 363: break; ! 364: default: ! 365: infoPtr->token = GREATER; ! 366: break; ! 367: } ! 368: return TCL_OK; ! 369: ! 370: case '=': ! 371: if (p[1] == '=') { ! 372: infoPtr->expr = p+2; ! 373: infoPtr->token = EQUAL; ! 374: } else { ! 375: infoPtr->token = UNKNOWN; ! 376: } ! 377: return TCL_OK; ! 378: ! 379: case '!': ! 380: if (p[1] == '=') { ! 381: infoPtr->expr = p+2; ! 382: infoPtr->token = NEQ; ! 383: } else { ! 384: infoPtr->token = NOT; ! 385: } ! 386: return TCL_OK; ! 387: ! 388: case '&': ! 389: if (p[1] == '&') { ! 390: infoPtr->expr = p+2; ! 391: infoPtr->token = AND; ! 392: } else { ! 393: infoPtr->token = BIT_AND; ! 394: } ! 395: return TCL_OK; ! 396: ! 397: case '^': ! 398: infoPtr->token = BIT_XOR; ! 399: return TCL_OK; ! 400: ! 401: case '|': ! 402: if (p[1] == '|') { ! 403: infoPtr->expr = p+2; ! 404: infoPtr->token = OR; ! 405: } else { ! 406: infoPtr->token = BIT_OR; ! 407: } ! 408: return TCL_OK; ! 409: ! 410: case '~': ! 411: infoPtr->token = BIT_NOT; ! 412: return TCL_OK; ! 413: ! 414: case 0: ! 415: infoPtr->token = END; ! 416: infoPtr->expr = p; ! 417: return TCL_OK; ! 418: ! 419: default: ! 420: infoPtr->expr = p+1; ! 421: infoPtr->token = UNKNOWN; ! 422: return TCL_OK; ! 423: } ! 424: } ! 425: ! 426: /* ! 427: *---------------------------------------------------------------------- ! 428: * ! 429: * ExprGetValue -- ! 430: * ! 431: * Parse a "value" from the remainder of the expression in infoPtr. ! 432: * ! 433: * Results: ! 434: * Normally TCL_OK is returned. The value of the parsed number is ! 435: * returned in infoPtr->number. If an error occurred, then ! 436: * interp->result contains an error message and TCL_ERROR is returned. ! 437: * ! 438: * Side effects: ! 439: * Information gets parsed from the remaining expression, and the ! 440: * expr and token fields in infoPtr get updated. Information is ! 441: * parsed until either the end of the expression is reached (null ! 442: * character or close paren), an error occurs, or a binary operator ! 443: * is encountered with precedence <= prec. In any of these cases, ! 444: * infoPtr->token will be left pointing to the token AFTER the ! 445: * expression. ! 446: * ! 447: *---------------------------------------------------------------------- ! 448: */ ! 449: ! 450: int ! 451: ExprGetValue(interp, infoPtr, prec) ! 452: Tcl_Interp *interp; /* Interpreter to use for error ! 453: * reporting. */ ! 454: register ExprInfo *infoPtr; /* Describes the state of the parse ! 455: * just before the value (i.e. ExprLex ! 456: * will be called to get first token ! 457: * of value). */ ! 458: int prec; /* Treat any un-parenthesized operator ! 459: * with precedence <= this as the end ! 460: * of the expression. */ ! 461: { ! 462: Interp *iPtr = (Interp *) interp; ! 463: int result, operator, operand; ! 464: int gotOp; /* Non-zero means already lexed the ! 465: * operator (while picking up value ! 466: * for unary operator). Don't lex ! 467: * again. */ ! 468: ! 469: /* ! 470: * There are two phases to this procedure. First, pick off an initial ! 471: * value. Then, parse (binary operator, value) pairs until done. ! 472: */ ! 473: ! 474: gotOp = 0; ! 475: result = ExprLex(interp, infoPtr); ! 476: if (result != TCL_OK) { ! 477: return result; ! 478: } ! 479: if (infoPtr->token == OPEN_PAREN) { ! 480: ! 481: /* ! 482: * Parenthesized sub-expression. ! 483: */ ! 484: ! 485: result = ExprGetValue(interp, infoPtr, -1); ! 486: if (result != TCL_OK) { ! 487: return result; ! 488: } ! 489: if (infoPtr->token != CLOSE_PAREN) { ! 490: Tcl_Return(interp, (char *) NULL, TCL_STATIC); ! 491: sprintf(interp->result, ! 492: "unmatched parentheses in expression \"%.50s\"", ! 493: infoPtr->originalExpr); ! 494: return TCL_ERROR; ! 495: } ! 496: } else { ! 497: if (infoPtr->token == MINUS) { ! 498: infoPtr->token = UNARY_MINUS; ! 499: } ! 500: if (infoPtr->token >= UNARY_MINUS) { ! 501: ! 502: /* ! 503: * Process unary operators. ! 504: */ ! 505: ! 506: operator = infoPtr->token; ! 507: result = ExprGetValue(interp, infoPtr, precTable[infoPtr->token]); ! 508: if (result != TCL_OK) { ! 509: return result; ! 510: } ! 511: switch (operator) { ! 512: case UNARY_MINUS: ! 513: infoPtr->number = -infoPtr->number; ! 514: break; ! 515: case NOT: ! 516: infoPtr->number = !infoPtr->number; ! 517: break; ! 518: case BIT_NOT: ! 519: infoPtr->number = ~infoPtr->number; ! 520: break; ! 521: } ! 522: gotOp = 1; ! 523: } else if (infoPtr->token != NUMBER) { ! 524: goto syntaxError; ! 525: } ! 526: } ! 527: ! 528: /* ! 529: * Got the first operand. Now fetch (operator, operand) pairs. ! 530: */ ! 531: ! 532: if (!gotOp) { ! 533: result = ExprLex(interp, infoPtr); ! 534: if (result != TCL_OK) { ! 535: return result; ! 536: } ! 537: } ! 538: while (1) { ! 539: operand = infoPtr->number; ! 540: operator = infoPtr->token; ! 541: if ((operator < MULT) || (operator >= UNARY_MINUS)) { ! 542: if ((operator == END) || (operator == CLOSE_PAREN)) { ! 543: return TCL_OK; ! 544: } else { ! 545: goto syntaxError; ! 546: } ! 547: } ! 548: if (precTable[operator] <= prec) { ! 549: return TCL_OK; ! 550: } ! 551: ! 552: /* ! 553: * If we're doing an AND or OR and the first operand already ! 554: * determines the result, don't execute anything in the ! 555: * second operand: just parse. Same style for ?: pairs. ! 556: */ ! 557: ! 558: if (((operator == AND) && !operand) ! 559: || ((operator == OR) && operand)) { ! 560: iPtr->noEval++; ! 561: result = ExprGetValue(interp, infoPtr, precTable[operator]); ! 562: iPtr->noEval--; ! 563: } else if (operator == QUESTY) { ! 564: if (operand != 0) { ! 565: result = ExprGetValue(interp, infoPtr, precTable[operator]); ! 566: operand = infoPtr->number; ! 567: if (result != TCL_OK) ! 568: return result; ! 569: if (infoPtr->token != COLON) ! 570: goto syntaxError; ! 571: iPtr->noEval++; ! 572: result = ExprGetValue(interp, infoPtr, precTable[operator]); ! 573: iPtr->noEval--; ! 574: } else { ! 575: iPtr->noEval++; ! 576: result = ExprGetValue(interp, infoPtr, precTable[operator]); ! 577: iPtr->noEval--; ! 578: if (result != TCL_OK) ! 579: return result; ! 580: if (infoPtr->token != COLON) ! 581: goto syntaxError; ! 582: result = ExprGetValue(interp, infoPtr, precTable[operator]); ! 583: operand = infoPtr->number; ! 584: } ! 585: infoPtr->number = operand; ! 586: } else { ! 587: result = ExprGetValue(interp, infoPtr, precTable[operator]); ! 588: } ! 589: if (result != TCL_OK) { ! 590: return result; ! 591: } ! 592: if ((infoPtr->token < MULT) && (infoPtr->token != NUMBER) ! 593: && (infoPtr->token != END) ! 594: && (infoPtr->token != CLOSE_PAREN)) { ! 595: goto syntaxError; ! 596: } ! 597: switch (operator) { ! 598: case MULT: ! 599: infoPtr->number = operand * infoPtr->number; ! 600: break; ! 601: case DIVIDE: ! 602: if (infoPtr->number == 0) { ! 603: Tcl_Return(interp, "divide by zero", TCL_STATIC); ! 604: return TCL_ERROR; ! 605: } ! 606: infoPtr->number = operand / infoPtr->number; ! 607: break; ! 608: case MOD: ! 609: if (infoPtr->number == 0) { ! 610: Tcl_Return(interp, "divide by zero", TCL_STATIC); ! 611: return TCL_ERROR; ! 612: } ! 613: infoPtr->number = operand % infoPtr->number; ! 614: break; ! 615: case PLUS: ! 616: infoPtr->number = operand + infoPtr->number; ! 617: break; ! 618: case MINUS: ! 619: infoPtr->number = operand - infoPtr->number; ! 620: break; ! 621: case LEFT_SHIFT: ! 622: infoPtr->number = operand << infoPtr->number; ! 623: break; ! 624: case RIGHT_SHIFT: ! 625: infoPtr->number = operand >> infoPtr->number; ! 626: break; ! 627: case LESS: ! 628: infoPtr->number = operand < infoPtr->number; ! 629: break; ! 630: case GREATER: ! 631: infoPtr->number = operand > infoPtr->number; ! 632: break; ! 633: case LEQ: ! 634: infoPtr->number = operand <= infoPtr->number; ! 635: break; ! 636: case GEQ: ! 637: infoPtr->number = operand >= infoPtr->number; ! 638: break; ! 639: case EQUAL: ! 640: infoPtr->number = operand == infoPtr->number; ! 641: break; ! 642: case NEQ: ! 643: infoPtr->number = operand != infoPtr->number; ! 644: break; ! 645: case BIT_AND: ! 646: infoPtr->number = operand & infoPtr->number; ! 647: break; ! 648: case BIT_XOR: ! 649: infoPtr->number = operand ^ infoPtr->number; ! 650: break; ! 651: case BIT_OR: ! 652: infoPtr->number = operand | infoPtr->number; ! 653: break; ! 654: case AND: ! 655: infoPtr->number = operand && infoPtr->number; ! 656: break; ! 657: case OR: ! 658: infoPtr->number = operand || infoPtr->number; ! 659: break; ! 660: } ! 661: } ! 662: ! 663: syntaxError: ! 664: Tcl_Return(interp, (char *) NULL, TCL_STATIC); ! 665: sprintf(interp->result, "syntax error in expression \"%.50s\"", ! 666: infoPtr->originalExpr); ! 667: return TCL_ERROR; ! 668: } ! 669: ! 670: /* ! 671: *---------------------------------------------------------------------- ! 672: * ! 673: * Tcl_Expr -- ! 674: * ! 675: * Parse and evaluate an expression. ! 676: * ! 677: * Results: ! 678: * The return value is TCL_OK if the expression was correctly parsed; ! 679: * if there was a syntax error or some other error during parsing, ! 680: * then another Tcl return value is returned and Tcl_Result points ! 681: * to an error message. If all went well, *valuePtr is filled in ! 682: * with the result corresponding to the expression string. ! 683: * ! 684: * Side effects: ! 685: * None. ! 686: * ! 687: *---------------------------------------------------------------------- ! 688: */ ! 689: ! 690: int ! 691: Tcl_Expr(interp, string, valuePtr) ! 692: Tcl_Interp *interp; /* Intepreter to use for variables etc. */ ! 693: char *string; /* Expression to evaluate. */ ! 694: int *valuePtr; /* Where to store result of evaluation. */ ! 695: { ! 696: ExprInfo info; ! 697: int result; ! 698: ! 699: info.interp = interp; ! 700: info.originalExpr = string; ! 701: info.expr = string; ! 702: result = ExprGetValue(interp, &info, -1); ! 703: if (result != TCL_OK) { ! 704: return result; ! 705: } ! 706: if (info.token != END) { ! 707: Tcl_Return(interp, (char *) NULL, TCL_STATIC); ! 708: sprintf(interp->result, "syntax error in expression \"%.50s\"", string); ! 709: return TCL_ERROR; ! 710: } ! 711: *valuePtr = info.number; ! 712: return TCL_OK; ! 713: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.