[Top] [Prev] [Next] [Bottom]

1

1 Tcl Fundamentals

This chapter describes the basic syntax rules for the Tcl scripting language. It describes the basic mechanisms used by the Tcl interpreter: substitution and grouping. It touches lightly on the following Tcl commands: puts, format, set, expr, string, while, incr, and proc.
Tcl is a string-based command language. The language has only a few fundamental constructs and relatively little syntax, which makes it easy to learn. The Tcl syntax is meant to be simple. Tcl is designed to be a glue that assembles software building blocks into applications. A simpler glue makes the job easier. In addition, Tcl is interpreted when the application runs. The interpreter makes it easy to build and refine your application in an interactive manner. A great way to learn Tcl is to try out commands interactively. If you are not sure how to run Tcl on your system, see Chapter 2 for instructions for starting Tcl on UNIX, Windows, and Macintosh systems.

This chapter takes you through the basics of the Tcl language syntax. Even if you are an expert programmer, it is worth taking the time to read these few pages to make sure you understand the fundamentals of Tcl. The basic mechanisms are all related to strings and string substitutions, so it is fairly easy to visualize what is going on in the interpreter. The model is a little different than some other programming languages you may already be familiar with, so it is worth making sure you understand the basic concepts.

Tcl Commands

The basic syntax for a Tcl command is:

command arg1 arg2 arg3 ...
The command is either the name of a built-in command or a Tcl procedure. White space (i.e., space or tab) is used to separate the command name and its arguments, and a newline or semicolon is used to terminate a command. The arguments to a command are just strings.

Tcl has syntax for grouping, which allows multiple words in one argument, and substitution, which is used with programming variables and nested command calls. The Tcl interpreter does grouping first, then substitutions, and finally it calls the command. It is up to the command to interpret its arguments. This model is described in detail in this Chapter.

Hello, World!

The "Hello, World!" example.
puts stdout {Hello, World!}
=> Hello, World!

In this example, the command is puts, which takes two arguments: an I/O stream identifier and a string. puts writes the string to the I/O stream along with a trailing newline character. There are two points to emphasize:

Variables

The set command is used to assign a value to a variable. It takes two arguments: the first is the name of the variable and the second is the value. Variable names can be any length, and case is significant. In fact, you can use any character in a variable name.

It is not necessary to declare Tcl variables before you use them.

The interpreter will create the variable when it is first assigned a value. The value of a variable is obtained later with the dollar-sign syntax illustrated in Example 1-2:

Tcl variables.
set var 5
=> 5
set b $var
=> 5

The second set command assigns to variable b the value of variable var. The use of the dollar sign is our first example of substitution. You can imagine that the second set command gets rewritten by substituting the value of var for $var to obtain a new command.

set b 5
The actual implementation is a little different, but not much.

Command Substitution

The second form of substitution is command substitution. A nested command is delimited by square brackets, [ ]. The Tcl interpreter takes everything between the brackets and evaluates it as a command. It rewrites the outer command by replacing the square brackets and everything between them with the result of the nested command. This is similar to the use of backquotes in other shells, except that it has the additional advantage of supporting arbitrary nesting of commands.

Command substitution.
set len [string length foobar]
=> 6

In the example, the nested command is:

string length foobar
This command returns the length of the string foobar. The string command is described in detail starting on page 43. The nested command runs first. Then command substitution causes the outer command to be rewritten as if it were:

set len 6
If there are several cases of command substitution within a single command, the interpreter processes them from left to right. As each right bracket is encountered, the command it delimits is evaluated. This results in a sensible ordering in which nested commands are evaluated first so their result can be used in arguments to the outer command.

Math Expressions

The Tcl interpreter itself does not evaluate math expressions. Instead, the expr command is used to evaluate math expressions. The interpreter treats expr just like any other command, and it leaves the expression parsing up to the expr implementation. The math syntax supported by expr is the same as the C expression syntax. The expr command deals with integer, floating point, and boolean values. Logical operations return either 0 (false) or 1 (true). Integer values are promoted to floating point values as needed. Octal values are indicated by a leading zero (e.g., 033 is 27 decimal). Hexadecimal values are indicated by a leading 0x. Scientific notation for floating point numbers is supported. A summary of the operator precedence is given on page 18.

The implementation of expr takes all its arguments, concatenates them into a single string, and then parses the string as a math expression. After expr computes the answer, the answer is formatted into a string and returned:

Simple arithmetic.
expr 7.2 / 4
=> 1.8

You can include variable references and nested commands in math expressions. The following example uses expr to add 7 to the length of the string foobar. As a result of the innermost command substitution, the expr command sees 6 + 7, and len gets the value 13:

Nested commands.
set len [expr [string length foobar] + 7]
=> 13

The expression evaluator supports a number of built-in math functions. For a complete listing, see page 19. The following example computes the value of pi:

Built-in math functions.
set pi [expr 2*asin(1.0)]
=> 3.1415926535897931

Backslash Substitution

The final type of substitution done by the Tcl interpreter is backslash substitution. This is used to quote characters that have special meaning to the interpreter. For example, you can specify a literal dollar sign, brace, or bracket by quoting it with a backslash. As a rule, however, if you find yourself using lots of backslashes, there is probably a simpler way to achieve the effect you are striving for. In particular, the list command described on page 55 will do quoting for you automatically. In Example 1-7 backslash is used to get a literal $:

Quoting special characters with backslash.
set dollar \$foo
=> $foo
set x $dollar
=> $foo

Only a single round of interpretation is done.

The second set command in the example illustrates an important property of Tcl. The value of dollar does not affect the substitution done in the assignment to x. In other words, the Tcl parser does not care about the value of a variable when it does the substitution. After the example, the value of x and dollar is the string $foo. In general, you do not have to worry about the value of variables until you use eval, which is described in Chapter 10.

You can also use backslash sequences to specify characters with their hexadecimal or octal value:

set escape \0x1b
set escape \033
The value of variable escape is the ASCII ESC character, which has character code 27. The table on page 18 summarizes backslash substitutions.

A common use of backslashes is to continue long commands on multiple lines. This is necessary because a newline terminates a command unless an argument is being grouped as described in the next section. A backslash as the last character in a line is converted into a space. In addition, all the white space at the beginning of the next line is replaced by this substitution. The backslash in the next example is required; otherwise the expr command gets terminated by the newline after the plus sign.

Continuing long lines with backslashes.
set totalLength [expr [string length $one] + \
		[string length $two]]

Grouping with Braces and Double Quotes

Double quotes and curly braces are used to group words together into one argument. The difference between double quotes and curly braces is that quotes allow substitutions to occur in the group, while curly braces prevent substitutions. This rule applies to command, variable, and backslash substitutions.

Grouping with double quotes vs. braces.
set s Hello
=> Hello
puts stdout "The length of $s is [string length $s]."
=> The length of Hello is 5.
puts stdout {The length of $s is [string length $s].}
=> The length of $s is [string length $s].

In the second command of Example 1-9, the Tcl interpreter does variable and command substitution on the second argument to puts. In the third command, substitutions are prevented so the string is printed as-is.

In practice, grouping with curly braces is used when substitutions on the argument must be delayed until a later time (or never done at all). Examples include loops, conditional statements, and procedure declarations. Double quotes are useful in simple cases like the puts command previously shown.

Another common use of quotes is with the format command. This is similar to the C printf function. The first argument to format is a format specifier that often includes special characters like newlines, tabs, and spaces. The easiest way to specify these characters is with backslash sequences (e.g., \n for newline and \t for tab). The backslashes must be substituted before the format command is called, so you need to use quotes to group the format specifier.

puts [format "Item: %s\t%5.3f" $name $value]
Here format is used to align a name and a value with a tab. The %s and %5.3f indicate how the remaining arguments to format are to be formatted. Note that the trailing \n usually found in a C printf call is not needed because puts provides one for us. For more information about the format command, see page 46.

Square Brackets Do Not Group

The square bracket syntax used for command substitution does not provide grouping. Instead, a nested command is considered part of the current group. In the command below the double quotes group the last argument, and the nested command is just part of that group.

puts stdout "The length of $s is [string length $s]."
In the next example the last argument is a nested command. There is no need to explicitly group the nested command because the Tcl parser treats the whole nested command as part of the group.

puts stdout [string length $s]
In general, you can place a bracketed command or variable reference anywhere. The following computes a command name:

[findCommand $x] arg arg

Grouping before Substitution

The Tcl parser makes a single pass through a command as it makes grouping decisions and performs string substitutions. Grouping decisions are made before substitutions are performed, which is an important property of Tcl. This means that the values being substituted do not affect grouping because the grouping decisions have already been made.

The following example demonstrates how nested command substitution affects grouping. A nested command is treated as an unbroken sequence of characters, regardless of its internal structure. It is included with the surrounding group of characters when collecting arguments for the main command.

Embedded command and variable substitution.
set x 7; set y 9
puts stdout $x+$y=[expr $x + $y]
=> 7+9=16

In the example the second argument to puts is:

$x+$y=[expr $x + $y]
The white space inside the nested command is ignored for the purposes of grouping the argument. By the time Tcl encounters the left bracket, it has already done some variable substitutions to obtain:

7+9=
When the left bracket is encountered, the interpreter calls itself recursively to evaluate the nested command. Again, the $x and $y are substituted before calling expr. Finally, the result of expr is substituted for everything from the left bracket to the right bracket. The puts command gets the following as its second argument:

7+9=16
Grouping before substitution.

The point of this example is that the grouping decision about puts's second argument is made before the command substitution is done. Even if the result of the nested command contained spaces or other special characters, they would be ignored for the purposes of grouping the arguments to the outer command. Grouping and variable substitution interact the same as grouping and command substitution. Spaces or special characters in variable values do not affect grouping decisions because these decisions are made before the variable values are substituted.

If you want the output to look nicer in the example, with spaces around the + and =, then you can use double quotes to explicitly group the argument to puts:

puts stdout "$x + $y = [expr $x + $y]"
The double quotes are used for grouping in this case to allow the variable and command substitution on the argument to puts. Note that it is never necessary to explicitly group a nested command with double quotes if it makes up the whole argument. The following is a redundant use of double quotes:

puts stdout "[expr $x + $y]"

Procedures

Tcl uses the proc command to define procedures. Once defined, a Tcl procedure is used just like any of the built-in Tcl commands. The basic syntax to define a procedure is:

proc name arglist body
The first argument is the name of the procedure being defined. The second argument is a list of parameters to the procedure. The third argument is a command body that is one or more Tcl commands.

The procedure name is case sensitive, and in fact it can contain any characters. Procedure names and variable names do not conflict with each other. As a convention, this book begins procedure names with uppercase letters and it begins variable names with lowercase letters. Good programming style is important as your Tcl scripts get larger. Tcl coding style is discussed in Chapter 12.

Defining a procedure.
proc Diag {a b} {
	set c [expr sqrt($a * $a + $b * $b)]
	return $c
}
puts "The diagonal of a 3, 4 right triangle is [Diag 3 4]"
=> The diagonal of a 3, 4 right triangle is 5.0

The Diag procedure defined in the example computes the length of the diagonal side of a right triangle given the lengths of the other two sides. The sqrt function is one of many math functions supported by the expr command. The variable c is local to the procedure; it is only defined during execution of Diag. Variable scope is discussed further in Chapter 7. It is not really necessary to use the variable c in this example. The procedure could also be written as:

proc Diag {a b} {
	return [expr sqrt($a * $a + $b * $b)]
}
The return command is used to return the result of the prodecure. The return command is optional in this example because the Tcl interpreter returns the value of the last command in the body as the value of the procedure. So, the procedure could be reduced to:

proc Diag {a b} {
	expr sqrt($a * $a + $b * $b)
}
Note the stylized use of curly braces in the example. The curly brace at the end of the first line starts the third argument to proc, which is the command body. In this case, the Tcl interpreter sees the opening left brace, causing it to ignore newline characters and scan the text until a matching right brace is found. Double quotes have the same property. They group characters, including newlines, until another double quote is found. The result of the grouping is that the third argument to proc is a sequence of commands. When they are evaluated later, the embedded newlines will terminate each command. The other crucial effect of the curly braces around the procedure body is to delay any substitutions in the body until the time the procedure is called. For example, the variables a, b, and c are not defined until the procedure is called, so we do not want to do variable substitution at the time Diag is defined.

The proc command supports additional features such as having variable numbers of arguments and default values for arguments. These are described in detail in Chapter 7.

A Factorial Example

To reinforce what we have learned so far, here is a longer example that uses a while loop to compute the factorial function:

A while loop to compute factorial.
proc Factorial {x} {
	set i 1; set product 1
	while {$i <= $x} {
		set product [expr $product * $i]
		incr i
	}
	return $product
}
Factorial 10
=> 3628800

The semicolon is used on the first line to remind you that it is a command terminator just like the newline character.

The while loop is used to multiply all the numbers from one up to the value of x. The first argument to while is a boolean expression, and its second argument is a command body to execute. The while command evaluates the boolean expression, and then executes the body if the expression is true (non-zero). The while command continues to test the expression and evaluate the command body until the expression is false (zero). Other control structures are described in Chapter 6.

The same math expression evaluator used by the expr command is used by while to evaluate the boolean expression. There is no need to explicitly use the expr command in the first argument to while, even if you have a much more complex expression.

The loop body and the procedure body are grouped with curly braces in the same way. The opening curly brace has to be on the same line as proc and while. If you like to put opening curly braces on the line after a while or if statement, you have to escape the newline with a backslash:

	while {$i < $x} \
	{
		set product ...
	}
Always group expressions and command bodies with curly braces.

Curly braces around the boolean expression are crucial because they delay variable substitution until the while command implementation tests the expression. The following example is an infinite loop:

set i 1; while $i<=10 {incr i}
The loop will run indefinitely. The reason is that the Tcl interpreter will substitute for $i before while is called, so while gets a constant expression 1<=10 that will always be true. You can avoid these kinds of errors by adopting a consistent coding style that groups expressions with curly braces:

set i 1; while {$i<=10} {incr i}
The incr command is used to increment the value of the loop variable i. This is a handy command that saves us from the longer command:

set i [expr $i + 1]
The incr command can take an additional argument, a positive or negative integer by which to change the value of the variable. Using this form it is possible to eliminate the loop variable i and just modify the parameter x. The loop body can be written like this:

while {$x > 1} {
	set product [expr $product * $x]
	incr x -1
}

More about Variables

The set command will return the value of a variable if it is only passed a single argument. It treats that argument as a variable name and returns the current value of the variable. The dollar-sign syntax used to get the value of a variable is really just an easy way to use the set command.

Using set to return a variable value.
set var {the value of var}
=> the value of var
set name var
=> var
set name
=> var
set $name
=> the value of var

This is a somewhat tricky example. In the last command, $name gets substituted with var. Then the set command returns the value of var, which is the value of var. Nested set commands provide another way to achieve a level of indirection. The last set command above can be written as follows:

set [set name]
=> the value of var

Using a variable to store the name of another variable may seem overly complex. However, there are some times when it is very useful. There is even a special command, upvar, that makes this sort of trick easier. The upvar command is described in detail on page 79 in Chapter 7.

Funny Variable Names

The Tcl interpreter makes some assumptions about variable names that make it easy to embed variable references into other strings. By default, it assumes that variable names only contain letters, digits, and the underscore. The construct $foo.o represents a concatenation of the value of foo and the literal ".o".

If the variable reference is not delimited by punctuation or white space, then you can use curly braces to explicitly delimit the variable name (e.g., ${x}). You can also use this to reference variables with funny characters in their name, although you probably do not want variables named like that. If you find yourself using funny variable names, or computing the names of variables, then you may want to use the upvar command.

Embedded variable references.
set foo filename
set object $foo.o
=> filename.o
set a AAA
set b abc${a}def
=> abcAAAdef
set .o yuk!
set x ${.o}y
=> yuk!y

The unset Command

You can delete a variable with the unset command:

unset varName varName2 ...
Any number of variable names can be passed to the unset command. However, unset will raise an error if a variable is not already defined.

Using info to Find Out about Variables

The existence of a variable can be tested with the info exists command. For example, because incr requires that a variable exist, you might have to test for the existence of the variable first.

Using info to determine if a variable exists.
if {![info exists foobar]} {
	set foobar 0
} else {
	incr foobar
}
In Chapter 7, page 80, there is an example that implements a new version of incr, which handles this case.

More about Math Expressions

This section describes a few fine points about math in Tcl scripts. In Tcl 7.6 and earlier versions math is not that efficient because of conversions between strings and numbers. The expr command must convert its arguments from strings to numbers. It then does all its computations with double precision floating point values. The result is formatted into a string that has, by default, six significant digits. This can be changed by setting the tcl_precision variable to the number of significant digits desired. Seventeen digits of precision are enough to ensure that no information is lost when converting back and forth between a string and an IEEE double precision number:

Controlling precision with tcl_precision.
expr 1 / 3
=> 0
expr 1 / 3.0
=> 0.333333
set tcl_precision 17
=> 17
expr 1 / 3.0
# The trailing 1 is the IEEE rounding digit
=> 0.33333333333333331

In Tcl 8.0 and later versions the overhead of conversions is eliminated in most cases by the built-in compiler. The use of tcl_precision is also eliminated so values are always printed with full precision. Even so, Tcl was not designed to support math intensive applications. You may want to implement math-intensive code in a compiled language and register the function as a Tcl command as described in Chapter 41.

There is support for string comparisons by expr, so you can test string values in if statements. You must use quotes so that expr knows to do string comparisons:

if {$answer == "yes"} { ... }
However, the string compare command described in Chapter 4 is more reliable because expr may do conversions on strings that look like numbers. This area has improved in Tcl 8.0. The issues with string operations and expr are discussed on page 45.

Expressions can include variable and command substitutions and still be grouped with curly braces. This is because an argument to expr is subject to two rounds of substitution: one by the Tcl interpreter, and a second by expr itself. Ordinarily this is not a problem because math values do not contain the characters that are special to the Tcl interpreter. The second round of substitutions is needed to support commands like while and if that use the expression evaluator internally. You may see uses of expr that group the expression into one argument, which was necessary in early versions of Tcl:

set y [expr {$x + $y}]

Comments

Tcl uses the pound character, #, for comments. Unlike many languages, the # must occur at the beginning of a command. A # that occurs elsewhere is not treated specially. An easy trick to append a comment to the end of a command is to precede the # with a semicolon to terminate the previous command:

# Here are some parameters
set rate 7.0        ;# The interest rate
set months 60      ;# The loan term
One subtle effect to watch out for is that a backslash effectively continues a comment line onto the next line of the script. In addition, a semicolon inside a comment is not significant. Only a newline terminates comments:

# Here is the start of a Tcl comment \
and some more of it; still in the comment
The behavior of a backslash in comments is pretty obscure, but it can be exploited as shown in Example 2-3 on page 25.

Substitution and Grouping Summary

The following rules summarize the fundamental mechanisms of grouping and substitution that are performed by the Tcl interpreter before it invokes a command:

Fine Points

if {$x > 1}{puts "x = $x"}
set silly a"b
set x xvalue
set y "foo {$x} bar"
=> foo {xvalue} bar

set x [cmd1][cmd2]
set x "This is line one.
This is line two.
This is line three."
set x $

Reference

Backslash sequences.
\a Bell. (0x7)
\b Backspace. (0x8)
\f Form feed. (0xc)
\n Newline. (0xa)
\r Carriage return. (0xd)
\t Tab (0x9)
\v Vertical tab. (0xb)
\<newline> Replace the newline and the leading white space on the next line with a space.
\\ Backslash. (`\')
\ooo Octal specification of character code. 1, 2, or 3 digits.
\xhh Hexadecimal specification of character code. 1 or 2 digits.
\c Replaced with literal c if c is not one of the cases listed above. In particular, \$, \", \{, \}, \], and \[ are used to obtain these characters.
Backslash Sequences

Arithmetic Operators
Arithmetic operators from highest to lowest precedence.
- ~ ! Unary minus, bitwise NOT, logical NOT.
* / % Multiply, divide, remainder.
+ - Add, subtract.
<< >> Left shift, right shift.
< > <= >= Comparison: less, greater, less or equal, greater or equal.
== != Equal, not equal.
& Bitwise AND.
^ Bitwise XOR.
| Bitwise OR.
&& Logical AND.
|| Logical OR.
x?y:z If x then y else z.

Built-in Math Functions
Built-in math functions.
acos(x) Arc-cosine of x.
asin(x) Arc-sine of x.
atan(x) Arc-tangent of x.
atan2(y,x) Rectangular (x,y) to polar (r,th). atan2 gives th.
ceil(x) Least integral value greater than or equal to x.
cos(x) Cosine of x.
cosh(x) Hyperbolic cosine of x.
exp(x) Exponential, ex.
floor(x) Greatest integral value less than or equal to x.
fmod(x,y) Floating point remainder of x/y.
hypot(x,y) Returns sqrt(x*x + y*y). r part of polar coordinates.
log(x) Natural log of x.
log10(x) Log base 10 of x.
pow(x,y) x to the y power, xy.
sin(x) Sine of x.
sinh(x) Hyperbolic sine of x.
sqrt(x) Square root of x.
tan(x) Tangent of x.
tanh(x) Hyperbolic tangent of x.
abs(x) Absolute value of x.
double(x) Promote x to floating point.
int(x) Truncate x to an integer.
round(x) Round x to an integer.
rand() Return a random floating point value between 0.0 and 1.0.
srand(x) Set the seed for the random number generator to the integer x.

Core Tcl Commands

The pages given in Table 1-4 are the primary reference for the command.
Built-in Tcl commands.
Command Pg.

Description
after 178

Schedule a Tcl command for later execution.
append 45

Append arguments to a variable's value. No spaces added.
array 85

Query array state and search through elements.
binary 49

Convert between strings and binary data.
break 71

Premature loop exit.
catch 71

Trap errors.
cd 106

Change working directory.
clock 145

Get the time and format date strings.
close 106

Close an open I/O stream.
concat 55

Concatenate arguments with spaces between. Splices lists.
console 26

Control the console used to enter commands interactively.
continue 71

Continue with next loop iteration.
error 73

Raise an error.
eof 101

Check for end of file.
eval 113

Concatenate arguments and evaluate them as a command.
exec 91

Fork and execute a UNIX program.
exit 108

Terminate the process.
expr 6

Evaluate a math expression.
fblocked 182

Poll an I/O channel to see if data is ready.
fconfigure 181

Set and query I/O channel properties.
fcopy 197

Copy from one I/O channel to another.
file 94

Query the file system.
fileevent 179

Register callback for event-driven I/O.
flush 101

Flush output from an I/O stream's internal buffers.
for 70

Loop construct similar to C for statement.
foreach 67

Loop construct over a list, or lists, of values.
format 46

Format a string similar to C sprintf.
gets 104

Read a line of input from an I/O stream.
glob 106

Expand a pattern to matching file names.
global 78

Declare global variables.
history 155

Command-line history control.
if 64

Conditional command. Allows else and elseif clauses.
incr 11

Increment a variable by an integer amount.
info 148

Query the state of the Tcl interpreter.
interp 204

Create additional Tcl interpreters.
join 59

Concatenate list elements with a given separator string.
lappend 55

Add elements to the end of a list.
lindex 57

Fetch an element of a list.
linsert 57

Insert elements into a list.
list 55

Create a list out of the arguments.
llength 57

Return the number of elements in a list.
load 523

Load shared libraries that define Tcl commands.
lrange 57

Return a range of list elements.
lreplace 57

Replace elements of a list.
lsearch 58

Search for an element of a list that matches a pattern.
lsort 58

Sort a list.
namespace 172

Create and manipulate namespaces.
open 101

Open a file or process pipeline for I/O.
package 138

Provide or require code packages.
pid 108

Return the process ID.
proc 75

Define a Tcl procedure.
puts 104

Output a string to an I/O stream.
pwd 106

Return the current working directory.
read 105

Read blocks of characters from an I/O stream.
regexp 124

Regular expression matching.
regsub 127

Substitutions based on regular expressions.
rename 76

Change the name of a Tcl command.
return 74

Return a value from a procedure.
scan 48

Parses a string according to a format specification.
seek 106

Set the seek offset of an I/O stream.
set 4

Assign a value to a variable.
socket 186

Open a TCP/IP network connection.
source 24

Evaluate the Tcl commands in a file.
split 59

Chop a string up into list elements.
string 43

Operate on strings.
subst 120

Substitutions without command evaluation.
switch 65

Multi-way branch.
tell 106

Return the current seek offset of an I/O stream.
time 162

Measure the execution time of a command.
trace 153

Monitor variable assignments.
unknown 141

Unknown command handler.
unset 12

Delete variables.
uplevel 118

Execute a command in a different scope.
upvar 79

Reference a variable in a different scope.
variable 167

Declare namespace variables.
vwait 180

Wait for a variable to be modified.
while 67

A loop construct.



[Top] [Prev] [Next] [Bottom]

welch@acm.org
Copyright © 1997, Brent Welch. All rights reserved.
This will be published by Prentice Hall as the 2nd Edition of
Practical Programming in Tcl and Tk