5.7   Tcl: Shell Interpretation

The shell has a Tcl interpreter interface as well as the C interpreter interface. This section illustrates some uses of the shell Tcl interpreter. If you are not familiar with Tcl, we suggest you skip this section and return to it after you have gotten acquainted with Tcl. (For an outline of Tcl, see B. Tcl.) In the interim, you can do a great deal of development work with the shell C interpreter alone.

To toggle between the Tcl interpreter and the C interpreter in the shell, type the single character ?. The shell prompt changes to remind you of the interpreter state: the prompt -> indicates the C interpreter is listening, and the prompt tcl> indicates the Tcl interpreter is listening.1 For example, in the following interaction we use the C interpreter to define a variable in the symbol table, then switch into the Tcl interpreter to define a similar Tcl variable in the shell itself, and finally switch back to the C interpreter:

-> hello="hi there" 
new symbol "hello" added to symbol table. 
hello = 0x3616e8: value = 3544824 = 0x3616f8 = hello + 0x10 
-> ? 
tcl> set hello {hi there} 
hi there 
tcl> ? 
-> 

If you start windsh from the Windows command line, you can also use the option -Tclmode (or -T) to start with the Tcl interpreter rather than the C interpreter.

Using the shell's Tcl interface allows you to extend the shell with your own procedures, and also provides a set of control structures which you can use interactively. The Tcl interpreter also acts as a host shell, giving you access to UNIX command-line utilities on your development host.

For example, you can call stty from the Tcl interpreter to change the special characters in use--in the following, to specify the quit character as CTRL+B and verify the new setting (the quit character is normally CTRL+X; when you type it in the shell, it reboots the target, restarts the target server, and resets all attached tools):

tcl> stty quit \002 
tcl> stty 
speed 9600 baud; line = 0; 
quit = ^B; eof = ^A; status = <undef>; min = 1; time = 0; 
-icanon -echo

5.7.1   Tcl: Controlling the Target

In the Tcl interpreter, you can create custom commands, or use Tcl control structures for repetitive tasks, while using the building blocks that allow the C interpreter and the WindSh commands to control the target remotely. These building blocks as a whole are called the wtxtcl procedures.

For example, wtxMemRead returns the contents of a block of target memory (given its starting address and length). That command in turn uses a special memory-block datatype designed to permit memory transfers without unnecessary Tcl data conversions. The following example uses wtxMemRead, together with the memory-block routine memBlockWriteFile, to write a Tcl procedure that dumps target memory to a host file. Because almost all the work is done on the host, this procedure works whether or not the target run-time environment contains I/O libraries or any networked access to the host file system.

# tgtMemDump - copy target memory to host file 
# 
# SYNOPSIS: 
#  tgtMemDump hostfile start nbytes
proc tgtMemDump {fname start nbytes} { set memHandle [wtxMemRead $start $nbytes] memBlockWriteFile $memHandle $fname }

For reference information on the wtxtcl routines available in the Tornado shell, see the Tornado API Guide (or the Tornado API entry in the Tornado Online Manuals).

All of the commands defined for the C interpreter (5.2.3 Invoking Built-In Shell Routines) are also available, with a double-underscore prefix, from the Tcl level; for example, to call i( )from the Tcl interpreter, run the Tcl procedure __i. However, in many cases, it is more convenient to call a wtxtcl routine instead, because the WindSh commands are designed to operate in the C-interpreter context. For example, you can call the dynamic linker using ld from the Tcl interpreter, but the argument that names the object module may not seem intuitive: it is the address of a string stored on the target. It is more convenient to call the underlying wtxtcl command. In the case of the dynamic linker, the underlying wtxtcl command is wtxObjModuleLoad, which takes an ordinary Tcl string as its argument, as described in Tornado API Guide: WTX Tcl API.

Tcl: Calling Target Routines

The shParse utility allows you to embed calls to the C interpreter in Tcl expressions; the most frequent application is to call a single target routine, with the arguments specified (and perhaps capture the result). For example, the following sends a logging message to your VxWorks target console:

tcl> shParse {logMsg("Greetings from Tcl!\n")}  
32

You can also use shParse to call WindSh commands more conveniently from the Tcl interpreter, rather than using their wtxtcl building blocks. For example, the following is a convenient way to spawn a task from Tcl, using the C-interpreter command sp( ), if you do not remember the underlying wtxtcl command:

tcl> shParse {sp appTaskBegin} 
task spawned: id = 25e388, name = u1 
0

Tcl: Passing Values to Target Routines

Because shParse accepts a single, ordinary Tcl string as its argument, you can pass values from the Tcl interpreter to C subroutine calls simply by using Tcl facilities to concatenate the appropriate values into a C expression.

For example, a more realistic way of calling logMsg( )from the Tcl interpreter would be to pass as its argument the value of a Tcl variable rather than a literal string. The following example evaluates a Tcl variable tclLog and inserts its value (with a newline appended) as the logMsg( ) argument:

tcl> shParse "logMsg(\"$tclLog\\n\")" 
32

5.7.2   Tcl: Calling Under C Control

To dip quickly into Tcl and return immediately to the C interpreter, you can type a single line of Tcl prefixed with the ? character (rather than using ? by itself to toggle into Tcl mode). For example:

-> ?set test wonder; puts "This is a $test." 
This is a wonder. 
-> 

Notice that the -> prompt indicates we are still in the C interpreter, even though we just executed a line of Tcl.


*

CAUTION: You may not embed Tcl evaluation inside a C expression; the ? prefix works only as the first nonblank character on a line, and passes the entire line following it to the Tcl interpreter.

For example, you may occasionally want to use Tcl control structures to supplement the facilities of the C interpreter. Suppose you have an application under development that involves several collaborating tasks; in an interactive development session, you may need to restart the whole group of tasks repeatedly. You can define a Tcl variable with a list of all the task entry points, as follows:

-> ? set appTasks {appFrobStart appGetStart appPutStart } 
appFrobStart appGetStart appPutStart 

Then whenever you need to restart the whole list of tasks, you can use something like the following:

-> ? foreach it $appTasks {shParse "sp($it)"} 
task spawned: id = 25e388, name = u0 
task spawned: id = 259368, name = u1 
task spawned: id = 254348, name = u2 
task spawned: id = 24f328, name = u3

5.7.3   Tcl: Tornado Shell lnitialization

When you execute an instance of the Tornado shell, it begins by looking for a file called .wind/windsh.tcl in two places: first under wind, and then in the directory specified by the HOME environment variable (if that environment variable is defined). In each of these directories, if the file exists, the shell reads and executes its contents as Tcl expressions before beginning to interact. You can use this file to automate any initialization steps you perform repeatedly.

You can also specify a Tcl expression to execute initially on the windsh command line, with the option -e tclExpr. For example, you can test an initialization file before saving it as .wind/windsh.tcl using this option, as follows:

% windsh phobos -e "source ./tcltest" &

Example 5-4:  Shell Initialization File

This file causes I/O for target routines called in WindSh to be directed to the target's standard I/O rather than to WindSh. It changes the default C++ strategy to automatic for this shell, sets a path for locating load modules, and causes modules not to be copied to the target server.

# Redirect Task I/O to WindSh  
shConfig SH_GET_TASK_IO off  
# Set C++ strategy  
shConfig LD_CALL_XTORS on  
# Set Load Path  
shConfig LD_PATH "/folk/jmichel/project/app;/folk/jmichel/project/test"  
# Let the Target Server directly access the module  
shConfig LD_SEND_MODULES off 

1:  The examples in this book assume you are using the default shell prompts, but you can change the C interpreter prompt to whatever string you like using shellPromptSet( ).