home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / misc / volume25 / tcl / part25 < prev    next >
Encoding:
Text File  |  1991-11-15  |  35.5 KB  |  1,197 lines

  1. Newsgroups: comp.sources.misc
  2. From: karl@sugar.neosoft.com (Karl Lehenbauer)
  3. Subject:  v25i093:  tcl - tool command language, version 6.1, Part25/33
  4. Message-ID: <1991Nov15.225510.21628@sparky.imd.sterling.com>
  5. X-Md4-Signature: 8f149172acc91f7fc91b79a979401d2f
  6. Date: Fri, 15 Nov 1991 22:55:10 GMT
  7. Approved: kent@sparky.imd.sterling.com
  8.  
  9. Submitted-by: karl@sugar.neosoft.com (Karl Lehenbauer)
  10. Posting-number: Volume 25, Issue 93
  11. Archive-name: tcl/part25
  12. Environment: UNIX
  13.  
  14. #! /bin/sh
  15. # This is a shell archive.  Remove anything before this line, then unpack
  16. # it by saving it into a file and typing "sh file".  To overwrite existing
  17. # files, type "sh file -c".  You can also feed this as standard input via
  18. # unshar, or by typing "sh <file", e.g..  If this archive is complete, you
  19. # will see the following message at the end:
  20. #        "End of archive 25 (of 33)."
  21. # Contents:  tcl6.1/tclVar.c.1
  22. # Wrapped by karl@one on Tue Nov 12 19:44:29 1991
  23. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  24. if test -f 'tcl6.1/tclVar.c.1' -a "${1}" != "-c" ; then 
  25.   echo shar: Will not clobber existing file \"'tcl6.1/tclVar.c.1'\"
  26. else
  27. echo shar: Extracting \"'tcl6.1/tclVar.c.1'\" \(32992 characters\)
  28. sed "s/^X//" >'tcl6.1/tclVar.c.1' <<'END_OF_FILE'
  29. X/* 
  30. X * tclVar.c --
  31. X *
  32. X *    This file contains routines that implement Tcl variables
  33. X *    (both scalars and arrays).
  34. X *
  35. X *    The implementation of arrays is modelled after an initial
  36. X *    implementation by Karl Lehenbauer, Mark Diekhans and
  37. X *    Peter da Silva.
  38. X *
  39. X * Copyright 1987-1991 Regents of the University of California
  40. X * Permission to use, copy, modify, and distribute this
  41. X * software and its documentation for any purpose and without
  42. X * fee is hereby granted, provided that the above copyright
  43. X * notice appear in all copies.  The University of California
  44. X * makes no representations about the suitability of this
  45. X * software for any purpose.  It is provided "as is" without
  46. X * express or implied warranty.
  47. X */
  48. X
  49. X#ifndef lint
  50. Xstatic char rcsid[] = "$Header: /user6/ouster/tcl/RCS/tclVar.c,v 1.25 91/10/31 16:41:46 ouster Exp $ SPRITE (Berkeley)";
  51. X#endif
  52. X
  53. X#include "tclInt.h"
  54. X
  55. X/*
  56. X * The strings below are used to indicate what went wrong when a
  57. X * variable access is denied.
  58. X */
  59. X
  60. Xstatic char *noSuchVar =    "no such variable";
  61. Xstatic char *isArray =        "variable is array";
  62. Xstatic char *needArray =    "variable isn't array";
  63. Xstatic char *noSuchElement =    "no such element in array";
  64. Xstatic char *traceActive =    "trace is active on variable";
  65. X
  66. X/*
  67. X * Forward references to procedures defined later in this file:
  68. X */
  69. X
  70. Xstatic  char *        CallTraces _ANSI_ARGS_((Interp *iPtr, Var *arrayPtr,
  71. X                Tcl_HashEntry *hPtr, char *name1, char *name2,
  72. X                int flags));
  73. Xstatic void        DeleteSearches _ANSI_ARGS_((Var *arrayVarPtr));
  74. Xstatic void        DeleteArray _ANSI_ARGS_((Interp *iPtr, char *arrayName,
  75. X                Var *varPtr, int flags));
  76. Xstatic Var *        NewVar _ANSI_ARGS_((int space));
  77. Xstatic ArraySearch *    ParseSearchId _ANSI_ARGS_((Tcl_Interp *interp,
  78. X                Var *varPtr, char *varName, char *string));
  79. Xstatic void        VarErrMsg _ANSI_ARGS_((Tcl_Interp *interp,
  80. X                char *name1, char *name2, char *operation,
  81. X                char *reason));
  82. X
  83. X/*
  84. X *----------------------------------------------------------------------
  85. X *
  86. X * Tcl_GetVar --
  87. X *
  88. X *    Return the value of a Tcl variable.
  89. X *
  90. X * Results:
  91. X *    The return value points to the current value of varName.  If
  92. X *    the variable is not defined or can't be read because of a clash
  93. X *    in array usage then a NULL pointer is returned and an error
  94. X *    message is left in interp->result if the TCL_LEAVE_ERR_MSG
  95. X *    flag is set.  Note:  the return value is only valid up until
  96. X *    the next call to Tcl_SetVar or Tcl_SetVar2;  if you depend on
  97. X *    the value lasting longer than that, then make yourself a private
  98. X *    copy.
  99. X *
  100. X * Side effects:
  101. X *    None.
  102. X *
  103. X *----------------------------------------------------------------------
  104. X */
  105. X
  106. Xchar *
  107. XTcl_GetVar(interp, varName, flags)
  108. X    Tcl_Interp *interp;        /* Command interpreter in which varName is
  109. X                 * to be looked up. */
  110. X    char *varName;        /* Name of a variable in interp. */
  111. X    int flags;            /* OR-ed combination of TCL_GLOBAL_ONLY
  112. X                 * or TCL_LEAVE_ERR_MSG bits. */
  113. X{
  114. X    register char *p;
  115. X
  116. X    /*
  117. X     * If varName refers to an array (it ends with a parenthesized
  118. X     * element name), then handle it specially.
  119. X     */
  120. X
  121. X    for (p = varName; *p != '\0'; p++) {
  122. X    if (*p == '(') {
  123. X        char *result;
  124. X        char *open = p;
  125. X
  126. X        do {
  127. X        p++;
  128. X        } while (*p != '\0');
  129. X        p--;
  130. X        if (*p != ')') {
  131. X        goto scalar;
  132. X        }
  133. X        *open = '\0';
  134. X        *p = '\0';
  135. X        result = Tcl_GetVar2(interp, varName, open+1, flags);
  136. X        *open = '(';
  137. X        *p = ')';
  138. X        return result;
  139. X    }
  140. X    }
  141. X
  142. X    scalar:
  143. X    return Tcl_GetVar2(interp, varName, (char *) NULL, flags);
  144. X}
  145. X
  146. X/*
  147. X *----------------------------------------------------------------------
  148. X *
  149. X * Tcl_GetVar2 --
  150. X *
  151. X *    Return the value of a Tcl variable, given a two-part name
  152. X *    consisting of array name and element within array.
  153. X *
  154. X * Results:
  155. X *    The return value points to the current value of the variable
  156. X *    given by name1 and name2.  If the specified variable doesn't
  157. X *    exist, or if there is a clash in array usage, then NULL is
  158. X *    returned and a message will be left in interp->result if the
  159. X *    TCL_LEAVE_ERR_MSG flag is set.  Note:  the return value is
  160. X *    only valid up until the next call to Tcl_SetVar or Tcl_SetVar2;
  161. X *    if you depend on the value lasting longer than that, then make
  162. X *    yourself a private copy.
  163. X *
  164. X * Side effects:
  165. X *    None.
  166. X *
  167. X *----------------------------------------------------------------------
  168. X */
  169. X
  170. Xchar *
  171. XTcl_GetVar2(interp, name1, name2, flags)
  172. X    Tcl_Interp *interp;        /* Command interpreter in which variable is
  173. X                 * to be looked up. */
  174. X    char *name1;        /* Name of array (if name2 is NULL) or
  175. X                 * name of variable. */
  176. X    char *name2;        /* If non-null, gives name of element in
  177. X                 * array. */
  178. X    int flags;            /* OR-ed combination of TCL_GLOBAL_ONLY
  179. X                 * or TCL_LEAVE_ERR_MSG bits. */
  180. X{
  181. X    Tcl_HashEntry *hPtr;
  182. X    Var *varPtr;
  183. X    Interp *iPtr = (Interp *) interp;
  184. X    Var *arrayPtr = NULL;
  185. X
  186. X    /*
  187. X     * Lookup the first name.
  188. X     */
  189. X
  190. X    if ((flags & TCL_GLOBAL_ONLY) || (iPtr->varFramePtr == NULL)) {
  191. X    hPtr = Tcl_FindHashEntry(&iPtr->globalTable, name1);
  192. X    } else {
  193. X    hPtr = Tcl_FindHashEntry(&iPtr->varFramePtr->varTable, name1);
  194. X    }
  195. X    if (hPtr == NULL) {
  196. X    if (flags & TCL_LEAVE_ERR_MSG) {
  197. X        VarErrMsg(interp, name1, name2, "read", noSuchVar);
  198. X    }
  199. X    return NULL;
  200. X    }
  201. X    varPtr = (Var *) Tcl_GetHashValue(hPtr);
  202. X    if (varPtr->flags & VAR_UPVAR) {
  203. X    hPtr = varPtr->value.upvarPtr;
  204. X    varPtr = (Var *) Tcl_GetHashValue(hPtr);
  205. X    }
  206. X
  207. X    /*
  208. X     * If this is an array reference, then remember the traces on the array
  209. X     * and lookup the element within the array.
  210. X     */
  211. X
  212. X    if (name2 != NULL) {
  213. X    if (varPtr->flags & VAR_UNDEFINED) {
  214. X        if (flags & TCL_LEAVE_ERR_MSG) {
  215. X        VarErrMsg(interp, name1, name2, "read", noSuchVar);
  216. X        }
  217. X        return NULL;
  218. X    } else if (!(varPtr->flags & VAR_ARRAY)) {
  219. X        if (flags & TCL_LEAVE_ERR_MSG) {
  220. X        VarErrMsg(interp, name1, name2, "read", needArray);
  221. X        }
  222. X        return NULL;
  223. X    }
  224. X    arrayPtr = varPtr;
  225. X    hPtr = Tcl_FindHashEntry(varPtr->value.tablePtr, name2);
  226. X    if (hPtr == NULL) {
  227. X        if (flags & TCL_LEAVE_ERR_MSG) {
  228. X        VarErrMsg(interp, name1, name2, "read", noSuchElement);
  229. X        }
  230. X        return NULL;
  231. X    }
  232. X    varPtr = (Var *) Tcl_GetHashValue(hPtr);
  233. X    }
  234. X
  235. X    /*
  236. X     * Invoke any traces that have been set for the variable.
  237. X     */
  238. X
  239. X    if ((varPtr->tracePtr != NULL)
  240. X        || ((arrayPtr != NULL) && (arrayPtr->tracePtr != NULL))) {
  241. X    char *msg;
  242. X
  243. X    msg = CallTraces(iPtr, arrayPtr, hPtr, name1, name2,
  244. X        (flags & TCL_GLOBAL_ONLY) | TCL_TRACE_READS);
  245. X    if (msg != NULL) {
  246. X        VarErrMsg(interp, name1, name2, "read", msg);
  247. X        return NULL;
  248. X    }
  249. X
  250. X    /*
  251. X     * Watch out!  The variable could have gotten re-allocated to
  252. X     * a larger size.  Fortunately the hash table entry will still
  253. X     * be around.
  254. X     */
  255. X
  256. X    varPtr = (Var *) Tcl_GetHashValue(hPtr);
  257. X    }
  258. X    if (varPtr->flags & (VAR_UNDEFINED|VAR_UPVAR|VAR_ARRAY)) {
  259. X    if (flags & TCL_LEAVE_ERR_MSG) {
  260. X        VarErrMsg(interp, name1, name2, "read", noSuchVar);
  261. X    }
  262. X    return NULL;
  263. X    }
  264. X    return varPtr->value.string;
  265. X}
  266. X
  267. X/*
  268. X *----------------------------------------------------------------------
  269. X *
  270. X * Tcl_SetVar --
  271. X *
  272. X *    Change the value of a variable.
  273. X *
  274. X * Results:
  275. X *    Returns a pointer to the malloc'ed string holding the new
  276. X *    value of the variable.  The caller should not modify this
  277. X *    string.  If the write operation was disallowed then NULL
  278. X *    is returned;  if the TCL_LEAVE_ERR_MSG flag is set, then
  279. X *    an explanatory message will be left in interp->result.
  280. X *
  281. X * Side effects:
  282. X *    If varName is defined as a local or global variable in interp,
  283. X *    its value is changed to newValue.  If varName isn't currently
  284. X *    defined, then a new global variable by that name is created.
  285. X *
  286. X *----------------------------------------------------------------------
  287. X */
  288. X
  289. Xchar *
  290. XTcl_SetVar(interp, varName, newValue, flags)
  291. X    Tcl_Interp *interp;        /* Command interpreter in which varName is
  292. X                 * to be looked up. */
  293. X    char *varName;        /* Name of a variable in interp. */
  294. X    char *newValue;        /* New value for varName. */
  295. X    int flags;            /* Various flags that tell how to set value:
  296. X                 * any of TCL_GLOBAL_ONLY, TCL_APPEND_VALUE,
  297. X                 * TCL_LIST_ELEMENT, TCL_NO_SPACE, or
  298. X                 * TCL_LEAVE_ERR_MSG. */
  299. X{
  300. X    register char *p;
  301. X
  302. X    /*
  303. X     * If varName refers to an array (it ends with a parenthesized
  304. X     * element name), then handle it specially.
  305. X     */
  306. X
  307. X    for (p = varName; *p != '\0'; p++) {
  308. X    if (*p == '(') {
  309. X        char *result;
  310. X        char *open = p;
  311. X
  312. X        do {
  313. X        p++;
  314. X        } while (*p != '\0');
  315. X        p--;
  316. X        if (*p != ')') {
  317. X        goto scalar;
  318. X        }
  319. X        *open = '\0';
  320. X        *p = '\0';
  321. X        result = Tcl_SetVar2(interp, varName, open+1, newValue, flags);
  322. X        *open = '(';
  323. X        *p = ')';
  324. X        return result;
  325. X    }
  326. X    }
  327. X
  328. X    scalar:
  329. X    return Tcl_SetVar2(interp, varName, (char *) NULL, newValue, flags);
  330. X}
  331. X
  332. X/*
  333. X *----------------------------------------------------------------------
  334. X *
  335. X * Tcl_SetVar2 --
  336. X *
  337. X *    Given a two-part variable name, which may refer either to a
  338. X *    scalar variable or an element of an array, change the value
  339. X *    of the variable.  If the named scalar or array or element
  340. X *    doesn't exist then create one.
  341. X *
  342. X * Results:
  343. X *    Returns a pointer to the malloc'ed string holding the new
  344. X *    value of the variable.  The caller should not modify this
  345. X *    string.  If the write operation was disallowed because an
  346. X *    array was expected but not found (or vice versa), then NULL
  347. X *    is returned;  if the TCL_LEAVE_ERR_MSG flag is set, then
  348. X *    an explanatory message will be left in interp->result.
  349. X *
  350. X * Side effects:
  351. X *    The value of the given variable is set.  If either the array
  352. X *    or the entry didn't exist then a new one is created.
  353. X *
  354. X *----------------------------------------------------------------------
  355. X */
  356. X
  357. Xchar *
  358. XTcl_SetVar2(interp, name1, name2, newValue, flags)
  359. X    Tcl_Interp *interp;        /* Command interpreter in which variable is
  360. X                 * to be looked up. */
  361. X    char *name1;        /* If name2 is NULL, this is name of scalar
  362. X                 * variable.  Otherwise it is name of array. */
  363. X    char *name2;        /* Name of an element within array, or NULL. */
  364. X    char *newValue;        /* New value for variable. */
  365. X    int flags;            /* Various flags that tell how to set value:
  366. X                 * any of TCL_GLOBAL_ONLY, TCL_APPEND_VALUE,
  367. X                 * TCL_LIST_ELEMENT, and TCL_NO_SPACE, or
  368. X                 * TCL_LEAVE_ERR_MSG . */
  369. X{
  370. X    Tcl_HashEntry *hPtr;
  371. X    register Var *varPtr = NULL;
  372. X                /* Initial value only used to stop compiler
  373. X                 * from complaining; not really needed. */
  374. X    register Interp *iPtr = (Interp *) interp;
  375. X    int length, new, listFlags;
  376. X    Var *arrayPtr = NULL;
  377. X
  378. X    /*
  379. X     * Lookup the first name.
  380. X     */
  381. X
  382. X    if ((flags & TCL_GLOBAL_ONLY) || (iPtr->varFramePtr == NULL)) {
  383. X    hPtr = Tcl_CreateHashEntry(&iPtr->globalTable, name1, &new);
  384. X    } else {
  385. X    hPtr = Tcl_CreateHashEntry(&iPtr->varFramePtr->varTable,
  386. X        name1, &new);
  387. X    }
  388. X    if (!new) {
  389. X    varPtr = (Var *) Tcl_GetHashValue(hPtr);
  390. X    if (varPtr->flags & VAR_UPVAR) {
  391. X        hPtr = varPtr->value.upvarPtr;
  392. X        varPtr = (Var *) Tcl_GetHashValue(hPtr);
  393. X    }
  394. X    }
  395. X
  396. X    /*
  397. X     * If this is an array reference, then create a new array (if
  398. X     * needed), remember any traces on the array, and lookup the
  399. X     * element within the array.
  400. X     */
  401. X
  402. X    if (name2 != NULL) {
  403. X    if (new) {
  404. X        varPtr = NewVar(0);
  405. X        Tcl_SetHashValue(hPtr, varPtr);
  406. X        varPtr->flags = VAR_ARRAY;
  407. X        varPtr->value.tablePtr = (Tcl_HashTable *)
  408. X            ckalloc(sizeof(Tcl_HashTable));
  409. X        Tcl_InitHashTable(varPtr->value.tablePtr, TCL_STRING_KEYS);
  410. X    } else {
  411. X        if (varPtr->flags & VAR_UNDEFINED) {
  412. X        varPtr->flags = VAR_ARRAY;
  413. X        varPtr->value.tablePtr = (Tcl_HashTable *)
  414. X            ckalloc(sizeof(Tcl_HashTable));
  415. X        Tcl_InitHashTable(varPtr->value.tablePtr, TCL_STRING_KEYS);
  416. X        } else if (!(varPtr->flags & VAR_ARRAY)) {
  417. X        if (flags & TCL_LEAVE_ERR_MSG) {
  418. X            VarErrMsg(interp, name1, name2, "set", needArray);
  419. X        }
  420. X        return NULL;
  421. X        }
  422. X        arrayPtr = varPtr;
  423. X    }
  424. X    hPtr = Tcl_CreateHashEntry(varPtr->value.tablePtr, name2, &new);
  425. X    }
  426. X
  427. X    /*
  428. X     * Compute how many bytes will be needed for newValue (leave space
  429. X     * for a separating space between list elements).
  430. X     */
  431. X
  432. X    if (flags & TCL_LIST_ELEMENT) {
  433. X    length = Tcl_ScanElement(newValue, &listFlags) + 1;
  434. X    } else {
  435. X    length = strlen(newValue);
  436. X    }
  437. X
  438. X    /*
  439. X     * If the variable doesn't exist then create a new one.  If it
  440. X     * does exist then clear its current value unless this is an
  441. X     * append operation.
  442. X     */
  443. X
  444. X    if (new) {
  445. X    varPtr = NewVar(length);
  446. X    Tcl_SetHashValue(hPtr, varPtr);
  447. X    if ((arrayPtr != NULL) && (arrayPtr->searchPtr != NULL)) {
  448. X        DeleteSearches(arrayPtr);
  449. X    }
  450. X    } else {
  451. X    varPtr = (Var *) Tcl_GetHashValue(hPtr);
  452. X    if (varPtr->flags & VAR_ARRAY) {
  453. X        if (flags & TCL_LEAVE_ERR_MSG) {
  454. X        VarErrMsg(interp, name1, name2, "set", isArray);
  455. X        }
  456. X        return NULL;
  457. X    }
  458. X    if (!(flags & TCL_APPEND_VALUE)) {
  459. X        varPtr->valueLength = 0;
  460. X    }
  461. X    }
  462. X
  463. X    /*
  464. X     * Make sure there's enough space to hold the variable's
  465. X     * new value.  If not, enlarge the variable's space.
  466. X     */
  467. X
  468. X    if ((length + varPtr->valueLength) >= varPtr->valueSpace) {
  469. X    Var *newVarPtr;
  470. X    int newSize;
  471. X
  472. X    newSize = 2*varPtr->valueSpace;
  473. X    if (newSize <= (length + varPtr->valueLength)) {
  474. X        newSize += length;
  475. X    }
  476. X    newVarPtr = NewVar(newSize);
  477. X    newVarPtr->valueLength = varPtr->valueLength;
  478. X    newVarPtr->upvarUses = varPtr->upvarUses;
  479. X    newVarPtr->tracePtr = varPtr->tracePtr;
  480. X    strcpy(newVarPtr->value.string, varPtr->value.string);
  481. X    Tcl_SetHashValue(hPtr, newVarPtr);
  482. X    ckfree((char *) varPtr);
  483. X    varPtr = newVarPtr;
  484. X    }
  485. X
  486. X    /*
  487. X     * Append the new value to the variable, either as a list
  488. X     * element or as a string.
  489. X     */
  490. X
  491. X    if (flags & TCL_LIST_ELEMENT) {
  492. X    if ((varPtr->valueLength > 0) && !(flags & TCL_NO_SPACE)) {
  493. X        varPtr->value.string[varPtr->valueLength] = ' ';
  494. X        varPtr->valueLength++;
  495. X    }
  496. X    varPtr->valueLength += Tcl_ConvertElement(newValue,
  497. X        varPtr->value.string + varPtr->valueLength, listFlags);
  498. X    varPtr->value.string[varPtr->valueLength] = 0;
  499. X    } else {
  500. X    strcpy(varPtr->value.string + varPtr->valueLength, newValue);
  501. X    varPtr->valueLength += length;
  502. X    }
  503. X    varPtr->flags &= ~VAR_UNDEFINED;
  504. X
  505. X    /*
  506. X     * Invoke any write traces for the variable.
  507. X     */
  508. X
  509. X    if ((varPtr->tracePtr != NULL)
  510. X        || ((arrayPtr != NULL) && (arrayPtr->tracePtr != NULL))) {
  511. X    char *msg;
  512. X
  513. X    msg = CallTraces(iPtr, arrayPtr, hPtr, name1, name2,
  514. X        (flags & TCL_GLOBAL_ONLY) | TCL_TRACE_WRITES);
  515. X    if (msg != NULL) {
  516. X        VarErrMsg(interp, name1, name2, "set", msg);
  517. X        return NULL;
  518. X    }
  519. X
  520. X    /*
  521. X     * Watch out!  The variable could have gotten re-allocated to
  522. X     * a larger size.  Fortunately the hash table entry will still
  523. X     * be around.
  524. X     */
  525. X
  526. X    varPtr = (Var *) Tcl_GetHashValue(hPtr);
  527. X    }
  528. X    return varPtr->value.string;
  529. X}
  530. X
  531. X/*
  532. X *----------------------------------------------------------------------
  533. X *
  534. X * Tcl_UnsetVar --
  535. X *
  536. X *    Delete a variable, so that it may not be accessed anymore.
  537. X *
  538. X * Results:
  539. X *    Returns 0 if the variable was successfully deleted, -1
  540. X *    if the variable can't be unset.  In the event of an error,
  541. X *    if the TCL_LEAVE_ERR_MSG flag is set then an error message
  542. X *    is left in interp->result.
  543. X *
  544. X * Side effects:
  545. X *    If varName is defined as a local or global variable in interp,
  546. X *    it is deleted.
  547. X *
  548. X *----------------------------------------------------------------------
  549. X */
  550. X
  551. Xint
  552. XTcl_UnsetVar(interp, varName, flags)
  553. X    Tcl_Interp *interp;        /* Command interpreter in which varName is
  554. X                 * to be looked up. */
  555. X    char *varName;        /* Name of a variable in interp.  May be
  556. X                 * either a scalar name or an array name
  557. X                 * or an element in an array. */
  558. X    int flags;            /* OR-ed combination of any of
  559. X                 * TCL_GLOBAL_ONLY or TCL_LEAVE_ERR_MSG. */
  560. X{
  561. X    register char *p;
  562. X    int result;
  563. X
  564. X    /*
  565. X     * Figure out whether this is an array reference, then call
  566. X     * Tcl_UnsetVar2 to do all the real work.
  567. X     */
  568. X
  569. X    for (p = varName; *p != '\0'; p++) {
  570. X    if (*p == '(') {
  571. X        char *open = p;
  572. X
  573. X        do {
  574. X        p++;
  575. X        } while (*p != '\0');
  576. X        p--;
  577. X        if (*p != ')') {
  578. X        goto scalar;
  579. X        }
  580. X        *open = '\0';
  581. X        *p = '\0';
  582. X        result = Tcl_UnsetVar2(interp, varName, open+1, flags);
  583. X        *open = '(';
  584. X        *p = ')';
  585. X        return result;
  586. X    }
  587. X    }
  588. X
  589. X    scalar:
  590. X    return Tcl_UnsetVar2(interp, varName, (char *) NULL, flags);
  591. X}
  592. X
  593. X/*
  594. X *----------------------------------------------------------------------
  595. X *
  596. X * Tcl_UnsetVar2 --
  597. X *
  598. X *    Delete a variable, given a 2-part name.
  599. X *
  600. X * Results:
  601. X *    Returns 0 if the variable was successfully deleted, -1
  602. X *    if the variable can't be unset.  In the event of an error,
  603. X *    if the TCL_LEAVE_ERR_MSG flag is set then an error message
  604. X *    is left in interp->result.
  605. X *
  606. X * Side effects:
  607. X *    If name1 and name2 indicate a local or global variable in interp,
  608. X *    it is deleted.  If name1 is an array name and name2 is NULL, then
  609. X *    the whole array is deleted.
  610. X *
  611. X *----------------------------------------------------------------------
  612. X */
  613. X
  614. Xint
  615. XTcl_UnsetVar2(interp, name1, name2, flags)
  616. X    Tcl_Interp *interp;        /* Command interpreter in which varName is
  617. X                 * to be looked up. */
  618. X    char *name1;        /* Name of variable or array. */
  619. X    char *name2;        /* Name of element within array or NULL. */
  620. X    int flags;            /* OR-ed combination of any of
  621. X                 * TCL_GLOBAL_ONLY or TCL_LEAVE_ERR_MSG. */
  622. X{
  623. X    Tcl_HashEntry *hPtr, dummyEntry;
  624. X    Var *varPtr, dummyVar;
  625. X    Interp *iPtr = (Interp *) interp;
  626. X    Var *arrayPtr = NULL;
  627. X
  628. X    if ((flags & TCL_GLOBAL_ONLY) || (iPtr->varFramePtr == NULL)) {
  629. X    hPtr = Tcl_FindHashEntry(&iPtr->globalTable, name1);
  630. X    } else {
  631. X    hPtr = Tcl_FindHashEntry(&iPtr->varFramePtr->varTable, name1);
  632. X    }
  633. X    if (hPtr == NULL) {
  634. X    if (flags & TCL_LEAVE_ERR_MSG) {
  635. X        VarErrMsg(interp, name1, name2, "unset", noSuchVar);
  636. X    }
  637. X    return -1;
  638. X    }
  639. X    varPtr = (Var *) Tcl_GetHashValue(hPtr);
  640. X
  641. X    /*
  642. X     * For global variables referenced in procedures, leave the procedure's
  643. X     * reference variable in place, but unset the global variable.  Can't
  644. X     * decrement the actual variable's use count, since we didn't delete
  645. X     * the reference variable.
  646. X     */
  647. X
  648. X    if (varPtr->flags & VAR_UPVAR) {
  649. X    hPtr = varPtr->value.upvarPtr;
  650. X    varPtr = (Var *) Tcl_GetHashValue(hPtr);
  651. X    }
  652. X
  653. X    /*
  654. X     * If the variable being deleted is an element of an array, then
  655. X     * remember trace procedures on the overall array and find the
  656. X     * element to delete.
  657. X     */
  658. X
  659. X    if (name2 != NULL) {
  660. X    if (!(varPtr->flags & VAR_ARRAY)) {
  661. X        if (flags & TCL_LEAVE_ERR_MSG) {
  662. X        VarErrMsg(interp, name1, name2, "unset", needArray);
  663. X        }
  664. X        return -1;
  665. X    }
  666. X    if (varPtr->searchPtr != NULL) {
  667. X        DeleteSearches(varPtr);
  668. X    }
  669. X    arrayPtr = varPtr;
  670. X    hPtr = Tcl_FindHashEntry(varPtr->value.tablePtr, name2);
  671. X    if (hPtr == NULL) {
  672. X        if (flags & TCL_LEAVE_ERR_MSG) {
  673. X        VarErrMsg(interp, name1, name2, "unset", noSuchElement);
  674. X        }
  675. X        return -1;
  676. X    }
  677. X    varPtr = (Var *) Tcl_GetHashValue(hPtr);
  678. X    }
  679. X
  680. X    /*
  681. X     * If there is a trace active on this variable or if the variable
  682. X     * is already being deleted then don't delete the variable:  it
  683. X     * isn't safe, since there are procedures higher up on the stack
  684. X     * that will use pointers to the variable.  Also don't delete an
  685. X     * array if there are traces active on any of its elements.
  686. X     */
  687. X
  688. X    if (varPtr->flags &
  689. X        (VAR_TRACE_ACTIVE|VAR_ELEMENT_ACTIVE)) {
  690. X    if (flags & TCL_LEAVE_ERR_MSG) {
  691. X        VarErrMsg(interp, name1, name2, "unset", traceActive);
  692. X    }
  693. X    return -1;
  694. X    }
  695. X
  696. X    /*
  697. X     * The code below is tricky, because of the possibility that
  698. X     * a trace procedure might try to access a variable being
  699. X     * deleted.  To handle this situation gracefully, copy the
  700. X     * contents of the variable and its hash table entry to
  701. X     * dummy variables, then clean up the actual variable so that
  702. X     * it's been completely deleted before the traces are called.
  703. X     * Then call the traces, and finally clean up the variable's
  704. X     * storage using the dummy copies.
  705. X     */
  706. X
  707. X    dummyVar = *varPtr;
  708. X    Tcl_SetHashValue(&dummyEntry, &dummyVar);
  709. X    if (varPtr->upvarUses == 0) {
  710. X    Tcl_DeleteHashEntry(hPtr);
  711. X    ckfree((char *) varPtr);
  712. X    } else {
  713. X    varPtr->flags = VAR_UNDEFINED;
  714. X    varPtr->tracePtr = NULL;
  715. X    }
  716. X
  717. X    /*
  718. X     * Call trace procedures for the variable being deleted and delete
  719. X     * its traces.
  720. X     */
  721. X
  722. X    if ((dummyVar.tracePtr != NULL)
  723. X        || ((arrayPtr != NULL) && (arrayPtr->tracePtr != NULL))) {
  724. X    (void) CallTraces(iPtr, arrayPtr, &dummyEntry, name1, name2,
  725. X        (flags & TCL_GLOBAL_ONLY) | TCL_TRACE_UNSETS);
  726. X    while (dummyVar.tracePtr != NULL) {
  727. X        VarTrace *tracePtr = dummyVar.tracePtr;
  728. X        dummyVar.tracePtr = tracePtr->nextPtr;
  729. X        ckfree((char *) tracePtr);
  730. X    }
  731. X    }
  732. X
  733. X    /*
  734. X     * If the variable is an array, delete all of its elements.  This
  735. X     * must be done after calling the traces on the array, above (that's
  736. X     * the way traces are defined).
  737. X     */
  738. X
  739. X    if (dummyVar.flags & VAR_ARRAY) {
  740. X    DeleteArray(iPtr, name1, &dummyVar,
  741. X        (flags & TCL_GLOBAL_ONLY) | TCL_TRACE_UNSETS);
  742. X    }
  743. X    if (dummyVar.flags & VAR_UNDEFINED) {
  744. X    if (flags & TCL_LEAVE_ERR_MSG) {
  745. X        VarErrMsg(interp, name1, name2, "set", 
  746. X            (name2 != NULL) ? noSuchVar : noSuchElement);
  747. X    }
  748. X    return -1;
  749. X    }
  750. X    return 0;
  751. X}
  752. X
  753. X/*
  754. X *----------------------------------------------------------------------
  755. X *
  756. X * Tcl_TraceVar --
  757. X *
  758. X *    Arrange for reads and/or writes to a variable to cause a
  759. X *    procedure to be invoked, which can monitor the operations
  760. X *    and/or change their actions.
  761. X *
  762. X * Results:
  763. X *    A standard Tcl return value.
  764. X *
  765. X * Side effects:
  766. X *    A trace is set up on the variable given by varName, such that
  767. X *    future references to the variable will be intermediated by
  768. X *    proc.  See the manual entry for complete details on the calling
  769. X *    sequence for proc.
  770. X *
  771. X *----------------------------------------------------------------------
  772. X */
  773. X
  774. Xint
  775. XTcl_TraceVar(interp, varName, flags, proc, clientData)
  776. X    Tcl_Interp *interp;        /* Interpreter in which variable is
  777. X                 * to be traced. */
  778. X    char *varName;        /* Name of variable;  may end with "(index)"
  779. X                 * to signify an array reference. */
  780. X    int flags;            /* OR-ed collection of bits, including any
  781. X                 * of TCL_TRACE_READS, TCL_TRACE_WRITES,
  782. X                 * TCL_TRACE_UNSETS, and TCL_GLOBAL_ONLY. */
  783. X    Tcl_VarTraceProc *proc;    /* Procedure to call when specified ops are
  784. X                 * invoked upon varName. */
  785. X    ClientData clientData;    /* Arbitrary argument to pass to proc. */
  786. X{
  787. X    register char *p;
  788. X
  789. X    /*
  790. X     * If varName refers to an array (it ends with a parenthesized
  791. X     * element name), then handle it specially.
  792. X     */
  793. X
  794. X    for (p = varName; *p != '\0'; p++) {
  795. X    if (*p == '(') {
  796. X        int result;
  797. X        char *open = p;
  798. X
  799. X        do {
  800. X        p++;
  801. X        } while (*p != '\0');
  802. X        p--;
  803. X        if (*p != ')') {
  804. X        goto scalar;
  805. X        }
  806. X        *open = '\0';
  807. X        *p = '\0';
  808. X        result = Tcl_TraceVar2(interp, varName, open+1, flags,
  809. X            proc, clientData);
  810. X        *open = '(';
  811. X        *p = ')';
  812. X        return result;
  813. X    }
  814. X    }
  815. X
  816. X    scalar:
  817. X    return Tcl_TraceVar2(interp, varName, (char *) NULL, flags,
  818. X        proc, clientData);
  819. X}
  820. X
  821. X/*
  822. X *----------------------------------------------------------------------
  823. X *
  824. X * Tcl_TraceVar2 --
  825. X *
  826. X *    Arrange for reads and/or writes to a variable to cause a
  827. X *    procedure to be invoked, which can monitor the operations
  828. X *    and/or change their actions.
  829. X *
  830. X * Results:
  831. X *    A standard Tcl return value.
  832. X *
  833. X * Side effects:
  834. X *    A trace is set up on the variable given by name1 and name2, such
  835. X *    that future references to the variable will be intermediated by
  836. X *    proc.  See the manual entry for complete details on the calling
  837. X *    sequence for proc.
  838. X *
  839. X *----------------------------------------------------------------------
  840. X */
  841. X
  842. Xint
  843. XTcl_TraceVar2(interp, name1, name2, flags, proc, clientData)
  844. X    Tcl_Interp *interp;        /* Interpreter in which variable is
  845. X                 * to be traced. */
  846. X    char *name1;        /* Name of scalar variable or array. */
  847. X    char *name2;        /* Name of element within array;  NULL means
  848. X                 * trace applies to scalar variable or array
  849. X                 * as-a-whole. */
  850. X    int flags;            /* OR-ed collection of bits, including any
  851. X                 * of TCL_TRACE_READS, TCL_TRACE_WRITES,
  852. X                 * TCL_TRACE_UNSETS, and TCL_GLOBAL_ONLY. */
  853. X    Tcl_VarTraceProc *proc;    /* Procedure to call when specified ops are
  854. X                 * invoked upon varName. */
  855. X    ClientData clientData;    /* Arbitrary argument to pass to proc. */
  856. X{
  857. X    Tcl_HashEntry *hPtr;
  858. X    Var *varPtr = NULL;        /* Initial value only used to stop compiler
  859. X                 * from complaining; not really needed. */
  860. X    Interp *iPtr = (Interp *) interp;
  861. X    register VarTrace *tracePtr;
  862. X    int new;
  863. X
  864. X    /*
  865. X     * Locate the variable, making a new (undefined) one if necessary.
  866. X     */
  867. X
  868. X    if ((flags & TCL_GLOBAL_ONLY) || (iPtr->varFramePtr == NULL)) {
  869. X    hPtr = Tcl_CreateHashEntry(&iPtr->globalTable, name1, &new);
  870. X    } else {
  871. X    hPtr = Tcl_CreateHashEntry(&iPtr->varFramePtr->varTable, name1, &new);
  872. X    }
  873. X    if (!new) {
  874. X    varPtr = (Var *) Tcl_GetHashValue(hPtr);
  875. X    if (varPtr->flags & VAR_UPVAR) {
  876. X        hPtr = varPtr->value.upvarPtr;
  877. X        varPtr = (Var *) Tcl_GetHashValue(hPtr);
  878. X    }
  879. X    }
  880. X
  881. X    /*
  882. X     * If the trace is to be on an array element, make sure that the
  883. X     * variable is an array variable.  If the variable doesn't exist
  884. X     * then define it as an empty array.  Then find the specific
  885. X     * array element.
  886. X     */
  887. X
  888. X    if (name2 != NULL) {
  889. X    if (new) {
  890. X        varPtr = NewVar(0);
  891. X        Tcl_SetHashValue(hPtr, varPtr);
  892. X        varPtr->flags = VAR_ARRAY;
  893. X        varPtr->value.tablePtr = (Tcl_HashTable *)
  894. X            ckalloc(sizeof(Tcl_HashTable));
  895. X        Tcl_InitHashTable(varPtr->value.tablePtr, TCL_STRING_KEYS);
  896. X    } else {
  897. X        if (varPtr->flags & VAR_UNDEFINED) {
  898. X        varPtr->flags = VAR_ARRAY;
  899. X        varPtr->value.tablePtr = (Tcl_HashTable *)
  900. X            ckalloc(sizeof(Tcl_HashTable));
  901. X        Tcl_InitHashTable(varPtr->value.tablePtr, TCL_STRING_KEYS);
  902. X        } else if (!(varPtr->flags & VAR_ARRAY)) {
  903. X        iPtr->result = needArray;
  904. X        return TCL_ERROR;
  905. X        }
  906. X    }
  907. X    hPtr = Tcl_CreateHashEntry(varPtr->value.tablePtr, name2, &new);
  908. X    }
  909. X
  910. X    if (new) {
  911. X    if ((name2 != NULL) && (varPtr->searchPtr != NULL)) {
  912. X        DeleteSearches(varPtr);
  913. X    }
  914. X    varPtr = NewVar(0);
  915. X    varPtr->flags = VAR_UNDEFINED;
  916. X    Tcl_SetHashValue(hPtr, varPtr);
  917. X    } else {
  918. X    varPtr = (Var *) Tcl_GetHashValue(hPtr);
  919. X    }
  920. X
  921. X    /*
  922. X     * Set up trace information.
  923. X     */
  924. X
  925. X    tracePtr = (VarTrace *) ckalloc(sizeof(VarTrace));
  926. X    tracePtr->traceProc = proc;
  927. X    tracePtr->clientData = clientData;
  928. X    tracePtr->flags = flags &
  929. X        (TCL_TRACE_READS|TCL_TRACE_WRITES|TCL_TRACE_UNSETS);
  930. X    tracePtr->nextPtr = varPtr->tracePtr;
  931. X    varPtr->tracePtr = tracePtr;
  932. X    return TCL_OK;
  933. X}
  934. X
  935. X/*
  936. X *----------------------------------------------------------------------
  937. X *
  938. X * Tcl_UntraceVar --
  939. X *
  940. X *    Remove a previously-created trace for a variable.
  941. X *
  942. X * Results:
  943. X *    None.
  944. X *
  945. X * Side effects:
  946. X *    If there exists a trace for the variable given by varName
  947. X *    with the given flags, proc, and clientData, then that trace
  948. X *    is removed.
  949. X *
  950. X *----------------------------------------------------------------------
  951. X */
  952. X
  953. Xvoid
  954. XTcl_UntraceVar(interp, varName, flags, proc, clientData)
  955. X    Tcl_Interp *interp;        /* Interpreter containing traced variable. */
  956. X    char *varName;        /* Name of variable;  may end with "(index)"
  957. X                 * to signify an array reference. */
  958. X    int flags;            /* OR-ed collection of bits describing
  959. X                 * current trace, including any of
  960. X                 * TCL_TRACE_READS, TCL_TRACE_WRITES,
  961. X                 * TCL_TRACE_UNSETS, and TCL_GLOBAL_ONLY. */
  962. X    Tcl_VarTraceProc *proc;    /* Procedure assocated with trace. */
  963. X    ClientData clientData;    /* Arbitrary argument to pass to proc. */
  964. X{
  965. X    register char *p;
  966. X
  967. X    /*
  968. X     * If varName refers to an array (it ends with a parenthesized
  969. X     * element name), then handle it specially.
  970. X     */
  971. X
  972. X    for (p = varName; *p != '\0'; p++) {
  973. X    if (*p == '(') {
  974. X        char *open = p;
  975. X
  976. X        do {
  977. X        p++;
  978. X        } while (*p != '\0');
  979. X        p--;
  980. X        if (*p != ')') {
  981. X        goto scalar;
  982. X        }
  983. X        *open = '\0';
  984. X        *p = '\0';
  985. X        Tcl_UntraceVar2(interp, varName, open+1, flags, proc, clientData);
  986. X        *open = '(';
  987. X        *p = ')';
  988. X        return;
  989. X    }
  990. X    }
  991. X
  992. X    scalar:
  993. X    Tcl_UntraceVar2(interp, varName, (char *) NULL, flags, proc, clientData);
  994. X}
  995. X
  996. X/*
  997. X *----------------------------------------------------------------------
  998. X *
  999. X * Tcl_UntraceVar2 --
  1000. X *
  1001. X *    Remove a previously-created trace for a variable.
  1002. X *
  1003. X * Results:
  1004. X *    None.
  1005. X *
  1006. X * Side effects:
  1007. X *    If there exists a trace for the variable given by name1
  1008. X *    and name2 with the given flags, proc, and clientData, then
  1009. X *    that trace is removed.
  1010. X *
  1011. X *----------------------------------------------------------------------
  1012. X */
  1013. X
  1014. Xvoid
  1015. XTcl_UntraceVar2(interp, name1, name2, flags, proc, clientData)
  1016. X    Tcl_Interp *interp;        /* Interpreter containing traced variable. */
  1017. X    char *name1;        /* Name of variable or array. */
  1018. X    char *name2;        /* Name of element within array;  NULL means
  1019. X                 * trace applies to scalar variable or array
  1020. X                 * as-a-whole. */
  1021. X    int flags;            /* OR-ed collection of bits describing
  1022. X                 * current trace, including any of
  1023. X                 * TCL_TRACE_READS, TCL_TRACE_WRITES,
  1024. X                 * TCL_TRACE_UNSETS, and TCL_GLOBAL_ONLY. */
  1025. X    Tcl_VarTraceProc *proc;    /* Procedure assocated with trace. */
  1026. X    ClientData clientData;    /* Arbitrary argument to pass to proc. */
  1027. X{
  1028. X    register VarTrace *tracePtr;
  1029. X    VarTrace *prevPtr;
  1030. X    Var *varPtr;
  1031. X    Interp *iPtr = (Interp *) interp;
  1032. X    Tcl_HashEntry *hPtr;
  1033. X    ActiveVarTrace *activePtr;
  1034. X
  1035. X    /*
  1036. X     * First, lookup the variable.
  1037. X     */
  1038. X
  1039. X    if ((flags & TCL_GLOBAL_ONLY) || (iPtr->varFramePtr == NULL)) {
  1040. X    hPtr = Tcl_FindHashEntry(&iPtr->globalTable, name1);
  1041. X    } else {
  1042. X    hPtr = Tcl_FindHashEntry(&iPtr->varFramePtr->varTable, name1);
  1043. X    }
  1044. X    if (hPtr == NULL) {
  1045. X    return;
  1046. X    }
  1047. X    varPtr = (Var *) Tcl_GetHashValue(hPtr);
  1048. X    if (varPtr->flags & VAR_UPVAR) {
  1049. X    hPtr = varPtr->value.upvarPtr;
  1050. X    varPtr = (Var *) Tcl_GetHashValue(hPtr);
  1051. X    }
  1052. X    if (name2 != NULL) {
  1053. X    if (!(varPtr->flags & VAR_ARRAY)) {
  1054. X        return;
  1055. X    }
  1056. X    hPtr = Tcl_FindHashEntry(varPtr->value.tablePtr, name2);
  1057. X    if (hPtr == NULL) {
  1058. X        return;
  1059. X    }
  1060. X    varPtr = (Var *) Tcl_GetHashValue(hPtr);
  1061. X    }
  1062. X
  1063. X    flags &= (TCL_TRACE_READS | TCL_TRACE_WRITES | TCL_TRACE_UNSETS);
  1064. X    for (tracePtr = varPtr->tracePtr, prevPtr = NULL; ;
  1065. X        prevPtr = tracePtr, tracePtr = tracePtr->nextPtr) {
  1066. X    if (tracePtr == NULL) {
  1067. X        return;
  1068. X    }
  1069. X    if ((tracePtr->traceProc == proc) && (tracePtr->flags == flags)
  1070. X        && (tracePtr->clientData == clientData)) {
  1071. X        break;
  1072. X    }
  1073. X    }
  1074. X
  1075. X    /*
  1076. X     * The code below makes it possible to delete traces while traces
  1077. X     * are active:  it makes sure that the deleted trace won't be
  1078. X     * processed by CallTraces.
  1079. X     */
  1080. X
  1081. X    for (activePtr = iPtr->activeTracePtr; activePtr != NULL;
  1082. X        activePtr = activePtr->nextPtr) {
  1083. X    if (activePtr->nextTracePtr == tracePtr) {
  1084. X        activePtr->nextTracePtr = tracePtr->nextPtr;
  1085. X    }
  1086. X    }
  1087. X    if (prevPtr == NULL) {
  1088. X    varPtr->tracePtr = tracePtr->nextPtr;
  1089. X    } else {
  1090. X    prevPtr->nextPtr = tracePtr->nextPtr;
  1091. X    }
  1092. X    ckfree((char *) tracePtr);
  1093. X}
  1094. X
  1095. X/*
  1096. X *----------------------------------------------------------------------
  1097. X *
  1098. X * Tcl_VarTraceInfo --
  1099. X *
  1100. X *    Return the clientData value associated with a trace on a
  1101. X *    variable.  This procedure can also be used to step through
  1102. X *    all of the traces on a particular variable that have the
  1103. X *    same trace procedure.
  1104. X *
  1105. X * Results:
  1106. X *    The return value is the clientData value associated with
  1107. X *    a trace on the given variable.  Information will only be
  1108. X *    returned for a trace with proc as trace procedure.  If
  1109. X *    the clientData argument is NULL then the first such trace is
  1110. X *    returned;  otherwise, the next relevant one after the one
  1111. X *    given by clientData will be returned.  If the variable
  1112. X *    doesn't exist, or if there are no (more) traces for it,
  1113. X *    then NULL is returned.
  1114. X *
  1115. X * Side effects:
  1116. X *    None.
  1117. X *
  1118. X *----------------------------------------------------------------------
  1119. X */
  1120. X
  1121. XClientData
  1122. XTcl_VarTraceInfo(interp, varName, flags, proc, prevClientData)
  1123. X    Tcl_Interp *interp;        /* Interpreter containing variable. */
  1124. X    char *varName;        /* Name of variable;  may end with "(index)"
  1125. X                 * to signify an array reference. */
  1126. X    int flags;            /* 0 or TCL_GLOBAL_ONLY. */
  1127. X    Tcl_VarTraceProc *proc;    /* Procedure assocated with trace. */
  1128. X    ClientData prevClientData;    /* If non-NULL, gives last value returned
  1129. X                 * by this procedure, so this call will
  1130. X                 * return the next trace after that one.
  1131. X                 * If NULL, this call will return the
  1132. X                 * first trace. */
  1133. X{
  1134. X    register char *p;
  1135. X
  1136. X    /*
  1137. X     * If varName refers to an array (it ends with a parenthesized
  1138. X     * element name), then handle it specially.
  1139. X     */
  1140. X
  1141. X    for (p = varName; *p != '\0'; p++) {
  1142. X    if (*p == '(') {
  1143. X        ClientData result;
  1144. X        char *open = p;
  1145. X
  1146. X        do {
  1147. X        p++;
  1148. X        } while (*p != '\0');
  1149. X        p--;
  1150. X        if (*p != ')') {
  1151. X        goto scalar;
  1152. X        }
  1153. X        *open = '\0';
  1154. X        *p = '\0';
  1155. X        result = Tcl_VarTraceInfo2(interp, varName, open+1, flags, proc,
  1156. X        prevClientData);
  1157. X        *open = '(';
  1158. X        *p = ')';
  1159. X        return result;
  1160. X    }
  1161. X    }
  1162. X
  1163. X    scalar:
  1164. X    return Tcl_VarTraceInfo2(interp, varName, (char *) NULL, flags, proc,
  1165. X        prevClientData);
  1166. X}
  1167. END_OF_FILE
  1168. if test 32992 -ne `wc -c <'tcl6.1/tclVar.c.1'`; then
  1169.     echo shar: \"'tcl6.1/tclVar.c.1'\" unpacked with wrong size!
  1170. fi
  1171. # end of 'tcl6.1/tclVar.c.1'
  1172. fi
  1173. echo shar: End of archive 25 \(of 33\).
  1174. cp /dev/null ark25isdone
  1175. MISSING=""
  1176. for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 ; do
  1177.     if test ! -f ark${I}isdone ; then
  1178.     MISSING="${MISSING} ${I}"
  1179.     fi
  1180. done
  1181. if test "${MISSING}" = "" ; then
  1182.     echo You have unpacked all 33 archives.
  1183.     rm -f ark[1-9]isdone ark[1-9][0-9]isdone
  1184. else
  1185.     echo You still need to unpack the following archives:
  1186.     echo "        " ${MISSING}
  1187. fi
  1188. ##  End of shell archive.
  1189. exit 0
  1190.  
  1191. exit 0 # Just in case...
  1192. -- 
  1193. Kent Landfield                   INTERNET: kent@sparky.IMD.Sterling.COM
  1194. Sterling Software, IMD           UUCP:     uunet!sparky!kent
  1195. Phone:    (402) 291-8300         FAX:      (402) 291-4362
  1196. Please send comp.sources.misc-related mail to kent@uunet.uu.net.
  1197.