commands evaluation academic performance

67
Commands evaluation Each Tcl command call is a sentence of the form : command arg1 arg2 arg3 ... The Tcl evaluator take each word of this sentence and evaluate it. After evaluation of each word, the first word (command) is considered to be a function name and this function is executed with as arguments the following words. To evaluate a word, the interpretor has to do the following substitutions in the word string : If the word is surrounded by " ", this word may contain spaces, but substitution is still applicable inside the quotations. Inside the quotation, there may be spaces and carriage returns. If a word is surrounded by { }, this word is unaffected (substitution is thus not applicable on this word). Inside the braces, there may be spaces and carriage returns. Moreover, the { } braces may be nested. If a part of the word is surrounded by [ ], this part is considered as a command sentence : the text within the brackets is evaluated as a Tcl command and replaced with the result. where substitution is applicable, every string beginning with $ is replaced with the variable represented by this string. This string is ended by a space, a '-' or a ','. Examples 1. set a "World !" In the evaluation of the 3 words 'set', 'a' and '"World !"', no substitution has to be done, only the " " are removed. The command 'set' is then executed with as parameters 'a' and 'World !'. This command tell Tcl to define a new variable 'a' (if not already defined) and to set its value to 'World !'. 2. set b "Hello $a" Set the variable 'b' to 'Hello World !'. Here, the variable substitution has occurred inside the second

Upload: anandintel

Post on 24-Dec-2015

214 views

Category:

Documents


0 download

DESCRIPTION

Commands Evaluation

TRANSCRIPT

Commands evaluation

Each Tcl command call is a sentence of the form : command arg1 arg2 arg3 ...

The Tcl evaluator take each word of this sentence and evaluate it. After evaluation of each word, the first word (command) is considered to be a function name and this function is executed with as arguments the following words.

To evaluate a word, the interpretor has to do the following substitutions in the word string :

If the word is surrounded by " ", this word may contain spaces, but substitution is still applicable inside the quotations. Inside the quotation, there may be spaces and carriage returns.

If a word is surrounded by { }, this word is unaffected (substitution is thus not applicable on this word). Inside the braces, there may be spaces and carriage returns. Moreover, the { } braces may be nested.

If a part of the word is surrounded by [ ], this part is considered as a command sentence : the text within the brackets is evaluated as a Tcl command and replaced with the result.

where substitution is applicable, every string beginning with $ is replaced with the variable represented by this string. This string is ended by a space, a '-' or a ','.

Examples

1. set a "World !" 

In the evaluation of the 3 words 'set', 'a' and '"World !"', no substitution has to be done, only the " " are removed. The command 'set' is then executed with as parameters 'a' and 'World !'. This command tell Tcl to define a new variable 'a' (if not already defined) and to set its value to 'World !'.

2. set b "Hello $a" 

Set the variable 'b' to 'Hello World !'. Here, the variable substitution has occurred inside the second parameter where the variable 'a' is replaced by its value.

3. set c [string range $b 0 3] 

Set the variable c to 'Hell', which is the 4 first letters of 'Hello World !'. In this case, The part between [ ] has been executed as a command

If you want to break a command sentence in lines you can only do it inside the { } brace or in the " " quotation or you can break the line with a '\' at the end of any break line.

Example

if {$c == "Hell"} { puts "Oh god !"} else { puts "Peace !"}

This test the value of the variable c. If it is the string 'Hell' it prints 'Oh god !' on screen, otherwise, it prints 'Peace !'. In this sentence, Tcl see 5 words :

'if' is the first : nothing to be evaluated. '$c == "Hell"' is the second : because of the surrounding curly braces, there is no

further evaluation on this word.

'puts "Oh god !"' : for the same reason, no further evaluation

'else' : nothing to do.

'puts "Peace !"' : no further evaluation.

The first word, 'if' is seen as the command and this command is executed with as parameters the 4 following words. That is later that the condition '$c == "Hell"' is evaluated, during the execution of the if command.

Notice where we placed the line breaks (inside the { }).

Strings and Lists

Under Tcl, the value of each variable is stored as a string. Even if you want to save a number in a variable, this number is transformed into a string.

As a special type of string, the list deserve a special attention in data representation in Tcl. The list is nothing more than a string with, as elements separator, the space. A list may contains sublists.

Example

% set list {12 {78 5} 45 "Im a not a number"}12 {78 5} 45 "Im a not a number"% set sublist1 [lindex $list 1]78 5% set sublist2 [lindex $list 3]Im a not a number% lindex $sublist2 2not

Mathematics expression

Whereas all variables are of type string, the mathematical operations internally uses float and integer number representation to produce their results. The command that calculate mathematical expression is 'expr'.

Example

% set result [expr (4+6)/4]2% set result [expr (4.0+6)/4]

2.5

In the first calculation, the interpretor has used the integer number representation. In the second, it has used the float number representation.

How to display something ?

To display a string, you can use the command 'puts'

Example

% set variable 255% puts "The number $variable"The number 255% puts [format "The number %d is equal to 0x%02X" \ $variable $variable]The number 255 is equal to 0xFF

As it can be seen in the previous example, the command format is very similar to the C command 'printf'.

Control flow

The following commands are similar to the C equivalent. Only 'foreach' has no C equivalent (have a look at the example to see what it do).

if {...condition...} {...body...} while {...condition...} {body}

for {... init ...} {...condition...} {...increment...} {...body...}

foreach varnames {...list...} {...body...}

the '...condition...' is evaluated in the same way that it should be with command 'expr'.

Examples

1. while2.3. % while {$i<4} {4. > puts "$i*$i is [expr $i*$i]"5. > incr i6. > }7. 0*0 is 08. 1*1 is 19. 2*2 is 410. 3*3 is 9

11. for

12.13. % for {set i 0} {$i<4} {incr i} {14. > puts "$i*$i is [expr $i*$i]"

15. > }16. 0*0 is 017. 1*1 is 118. 2*2 is 419. 3*3 is 9

20. foreach

21.22. % set observations \23. {Bruxelles 15 22 London 12 19 Paris 18 27}24. Bruxelles 15 22 London 12 19 Paris 18 2725. % foreach {town Tmin Tmax} $observations {26. > set Tavg [expr ($Tmin+$Tmax)/2.0]27. > puts "$town $Tavg"28. > }29. Bruxelles 18.530. London 15.531. Paris 22.5

Array

Arrays are always unidimensional but the index is a string. If you use a separator in the index string (such as ',', '-'), you can get the same effect than with a multidimensional array in other languages.

Example

% set observations \ {Bruxelles 15 22 London 12 19 Paris 18 27}Bruxelles 15 22 London 12 19 Paris 18 27% foreach {town Tmin Tmax} $observations {set obs($town-min) $Tminset obs($town-max) $Tmax}% parray obsobs(Bruxelles-max) = 22obs(Bruxelles-min) = 15obs(London-max) = 19obs(London-min) = 12obs(Paris-max) = 27obs(Paris-min) = 18

Procedures

Procedures are the equivalent of the C functions.

Example

% proc sum2 {a b} {> return [expr $a+$b]> }

if a procedure does not contain any 'return' statement, the default return value is the return value of the last evaluated function in this procedure. So the following script is perfectly equivalent :

% proc sum2 {a b} {> expr $a + $b> }

To call the 'sum2' function, we do the following :

% sum2 12 517

The special argument name 'args' contains a list with the rest of the arguments

Example

% proc sum {args} {> set result 0> foreach n $args {> set result [expr $result+$n]> }> return $result> }% sum 12 9 6 431

it is also possible to specify default parameters. So, if you don't specify the last parameters, the default values will be substituted.

Example

% proc count {start end {step 1}} {> for {set i $start} {$i<=$end} {incr i $step} {> puts $i> }> }% count 1 3123% count 1 5 2135

If you want to use global variables in a function, you have to declare it as global.

Example

% set global_counter 3% proc incr_counter {} {

> global global_counter> incr global_counter> }% incr_counter4% set global_counter4

You can also declare a table as global.

Example

% set counter(value) 3% set counter(active) 1% proc incr_counter {} {> global counter> if {$counter(active)} {> incr counter(value)> }> }% incr_counter4% set counter(active) 00% incr_counter4

Eval

The 'eval' command

concatenate all its arguments in one string splits this string using spaces as separators

evaluate the command sentence formed by all the substrings

In the following example, we used the function 'sum' that we have already defined.

Example

% proc average {args} {> return [expr [eval sum $args] / [llength $args]] > } % average 45.0 65.0 78.0 55.060.75

If you had omitted the 'eval' command in the previous example, the 'sum' procedure would have returned an error because 'sum' should be called with only one string argument (in the previous example, this argument would have been '45.0 65.0 78.0 55.0') while 'sum' is expecting numerical arguments.

uplevel, upvar

With the 'upvar' command, you can access a variable which belongs to a higher level of the procedure call stack.

Example

% proc decr {n steps} {> upvar $n upa> set upa [expr $upa - $steps]> }% set nb 1212% decr nb 39% puts $nb9

In the previous example, the parameter 'n' gets the value 'nb' (the string 'nb' !) if we type 'decr nb 3'. The command 'upvar $n upa' means that the variable 'upa' becomes a synonym to the variable 'nb' (coming from a higher level of the stack).

With the 'uplevel' command, you can evaluate something on higher level in the stack.

Example

% proc do {todo condition} {> set ok 1> while {$ok} {> uplevel $todo> if {[uplevel "expr $condition"]==0} {set ok 0}> }> }% set i 00% do {puts $iincr i} {$i<4}0123

Inside the procedure 'do', the evaluation of the script 'todo' and the conditional 'condition' has to made on a higher level of stack (in the same way that if they were evaluated from out of 'do').

error and catch

If you insert an 'error' command in your code, this command will stop the execution of your script and return the error message that follow the 'error' command. With the command 'catch', you can also intercept a error to avoid that your script stops on an error.

If 'catch' return 0, it means that no error occurred while evaluating the script send as parameter of catch. If 'catch' return 1, it means that an error occurred.

Example

% proc div {a b} {> if {$b==0} {> error "divided by zero"> } else {> return [expr $a/$b]> }> }% div 8 32% div 8 0divide by zero% catch {div 8 3}0% catch {div 8 0}1% catch {set result [div 8 3]}0% catch {set result [div 8 0]}1

The last call is completely equivalent to

catch {div 8 3} result

Introduction

Emacs is a powerful text editor. Especially suited for programming, it uses intensively the keyboard shortcuts. This page is a short summary of the most used shortcuts in Emacs. If you are configuring your Linux, you will appreciate that Emacs also works in text mode.

File access

Ctrl-x Ctrl-f

Open file

Ctrl-x Ctrl-s

Save

Buffer control

Ctrl-x k

Kill buffer (close file)

Ctrl-x b

Switch to an other buffer

Ctrl-x 2

Split the window vertically to see an extra buffer

Ctrl-x 3

Split the window horizontally to see an extra buffer

Ctrl-x 0

Hide the current window

Ctrl-x 1

Show only the current window

Ctrl-x o

Jump to an other window

Text editing

Ctrl-Space

Marks beginning of a block

Alt-w

Marks end of block and copy

Ctrl-w

Marks end of block and cut

Ctrl-y

Paste

Ctrl-a

Go to the beginning of the line

Ctrl-e

Go to the end of the line

Ctrl-k

Kill end of line (after the cursor)

General

Ctrl-g

Cancel the command you are typing

Ctrl-x Ctrl-c

Quit emacs

Ctrl-_

Undo

TAB

Automatic indentation (source code) while editing

TAB

Automatic completion while selecting a file or while typing a command in the mini-buffer

Alt-x

Type a command in the mini-buffer

Alt-x c-mode

Change major mode to c-mode

Alt-x global-font-lock-mode

Switch the 'global font lock mode' (highlights syntax)

Search and replace

Ctrl-s

Incremental forward search (press Ctrl-s again to search further)

Ctrl-r

Incremental backward search (press Ctrl-r again to search further)

Alt-%

Search and replace

Multiple files search

Simple searchAlt-x grep

Perform a 'grep' search

Alt-x grep-find

Perform a search using 'find' and 'grep'

Tags (for programmers)find . -name "*.[ch]" | etags -

Make a TAGS index file for every file whose name ends with '.c' or '.h', starting from the current directory (this is a shell command !)

Alt-x visit-tags-table

Load a TAGS file

Alt-.

Find a tag

tags-search

Search through all files listed in the tags table

tags-query-replace

Search and replace through all files listed in the tags table

Alt-,

continue last tag search

Files comparison

Alt-x ediff

Compares two files

Spell check

Alt-x ispell-buffer

Spell check for the current buffer

Help

Ctrl-h ?

General help functions

Ctrl-h f

Describe the command you type

Ctrl-h a

apropos ... help based on a keyword

Ctrl-h m

Help on the current major mode

Return to introduction

Configure email for Linux on your home PC

1. Are you concerned ?

The aim of this short report is to help Linux users to configure their email system in a multi-user environment. Let's say that you have a PC in your house, that you access Internet with a modem and that you want to provide a email address to everyone in your family : this help is for you ! Maybe it is not so difficult to configure sendmail ... for a Linux freaks, but for the others, a starting point will certainly be appreciated. I think that Internet connection and email configuration is the basic functionality that a PC must satisfy.

This introduction provide the typical 'case' of somebody connecting to Internet with a modem (not permanently connected) through an Internet provider. This typical user have only access to one mailbox (the case of multiple mailbox is simpler) which he wishes to split in multiple mailboxes on his local computer. Since the typical user could frequently change of Internet provider and since he likes to keep only one email address, he use aliases which are only a 'redirection' from a email address to an other. Each local user have a different alias name on the alias server and that is based on this alias name that the local computer can redistribute the mail coming from a unique mailbox to different local mailboxes.

It's also very interesting to be able to write email's off-line. Once online, the mail of all the users are sent and the the mail for everyone is fetched. In one pass ... Think on your telephone bill.

2. Who does it works ? Follow the mail ...

2.1. The typical example

Suppose that you have only two users on your local PC, let's say Paul Dupont and Luc Jackson. The former has the login 'paul' and the last has the the login 'luc'. Through your local Internet provider, you have access to a POP3 mailbox. Your address for this mailbox is '[email protected]'. Luc and Paul have subscribed an alias address, respectively [email protected] and [email protected]. These aliases forward any received mail to your mailbox '[email protected]'.

On the local PC, Luc and Paul have respectively the local addresses '[email protected]' and '[email protected]' where 'linuxpc' is the name you have chosen for your computer. Luc and Paul wish to read their mail with Netscape, through a local POP3 server. As they send a mail to an Internet address, they wish to be able to send this mail off-line. They also wish that the address that the recipient will see doesn't contain the name of your local computer (linuxpc) but well the name of the alias server domain (advalvas.be). So, if luc send a mail to '[email protected]', 'somebody' will think that the mail come from '[email protected]'.

This situation is illustrated in the following figure.

3. Sendmail configuration

3.1 /etc/sendmail.cf

To build the sendmail configuration file, it is highly suggested to use the m4 tool. In the directory /usr/lib/sendmail-cf/cf, create a file .mc. For example, my file /usr/lib/sendmail-cf/cf/linuxpc.mc contains the following :

divert(-1) Begin of a comment text

# This is the macro config file used to generate the /etc/sendmail.cf

# file. If you modify this file you will have to regenerate the

# /etc/sendmail.cf by running this macro config through the m4

# preprocessor:

#

# m4 linuxpc.mc > linuxpc.cf

#

divert(0) End of a comment text

include(`../m4/cf.m4') Include a standard macro.

OSTYPE(`linux')dnl Include an OS-specific macro.

undefine(`UUCP_RELAY') The host will not accept UUCP addressed email.

undefine(`BITNET_RELAY') The host will not accept BITNET addressed email.

define(`confAUTO_REBUILD') Automatically rebuild alias file if needed.

define(`confTO_CONNECT', `1m') Maximum time to connect ???

define(`PROCMAIL_MAILER_PATH',`/usr/bin/procmail')

define(`confTO_QUEUEWARN', `48h') The time you can send email off-line before sendmail send you a warning mail is set to 48h

dnl define(`SMART_HOST',`relay.provider.com')

Define a SMTP host where to send the outgoing mails. Because of the word dnl, this line is seen as a comment. In this case, sendmail will communicate directly with the destination host. If you prefer to use the SMTP server of your provider, please

uncomment.

dnl define(SMTP_MAILER_FLAGS, e) Define the SMTP mail to be 'expensive' which means that sendmail try to communicate with the SMTP server only on demand. Because of the worddnl, this line is seen as a comment. If you prefer to use the SMTP server of your provider, please uncomment.

FEATURE(use_cw_file)

Read the file /etc/sendmail.cw file to get alternate names for this host.

FEATURE(local_procmail)Use procmail as the local mailer.

FEATURE(`genericstable',`hash -o /etc/mail/genericstable.db')

This feature will cause certain addresses originating locally or from a 'generic' domain to be looked up in a map and turned into another form, which can change both the domain name and the user name.

FEATURE(masquerade_envelope) The masquerade is use to mask the originator of a message by replacing him email address.masquerade_envelope instructs sendmail to masquerade the envelope sender and recipient as well as those in the headers.

GENERICS_DOMAIN(`linuxpc') Define a 'generic' domain.

GENERICS_DOMAIN(`linuxpc.localdomain') Define a 'generic' domain.

GENERICS_DOMAIN(`localhost') Define a 'generic' domain.

GENERICS_DOMAIN(`localhost.localdomain') Define a 'generic' domain.

MASQUERADE_AS(`advalvas.be') This causes mail being sent to

be labeled as coming from the indicated host, here advalvas.be

MASQUERADE_DOMAIN(`linuxpc.localdomain') Normally the only addresses that are masqueraded are those that come from this host. The MASQUERADE_DOMAIN feature extends the masquerade to the specified host.

MAILER(procmail)dnl

MAILER(smtp)dnl

More information about the m4 configuration file at http://www.sendmail.org/m4/index.html

3.2 /etc/mail/genericstable

For each 'generic host' (such as defined in /usr/lib/sendmail-cf/cf/linuxpc.mc), the user name (and optionally the domain name) of the sender is changed. The table contained in /etc/mail/genericstabledescribe which changes has to happen. For example,

[email protected] Paul.dupontpaul@localhost Paul.dupontpaul [email protected] Luc.Jacksonluc@localhost Luc.Jacksonluc Luc.Jackson

In order to create the database file /etc/mail/genericstable.db, one should type the following : makemap hash /etc/mail/genericstable.db < /etc/mail/genericstable

3.3 /etc/aliases

Optionally, you may change the alias file. In this way, you could, for example, send a mail to 'paul.dupont' locally (without domain name) and this mail would be directly delivered to 'paul'. So, add the following line on the end of /etc/aliases

Paul.Dupont: paulLuc.Jackson: luc

After doing this, you have to rebuild the alias database. Typenewaliases

3.4 /etc/sendmail.cw

This is the file containing names of hosts for which we receive email. In our example,

linuxpclocalhostlocalhost.localdomainlinuxpc.localdomain

3.5 Restart sendmail

As user root, type killall -HUP sendmail

3.6 Check the permission of sendmail

If you want that every user is able to send the mail queue, you have to set the user ID on execution. chmod a+s /usr/sbin/sendmail

4. Configuration of the POP3 server

4.1 Install the server, if needed

The POP2 and POP3 server daemon are name ipop2d and ipop3d, respectively. In the Redhat distribution, they are included in the package imap. You can check if this package is installed : rpm -q imap

If not, look for a file named imap*.rpm on the installation CDROM and install it with rpm -ivh imap*.rpm

4.2 /etc/inetd.conf

the inetd daemon is responsible for listening on the TCP/IP port and to start the appropriate daemon (dependent on the port number) when a connection is established. For example, the pop-3 service use the port 110. Every time one try to connect to the pop-3 service (on port 110), inetd will start ipop3d. In /etc/inetd.conf, be sure that the following lines are presents and uncommented :

pop-2 stream tcp nowait root /usr/sbin/tcpd ipop2dpop-3 stream tcp nowait root /usr/sbin/tcpd ipop3d

4.3 /etc/hosts.allow and /etc/hosts.deny

The files /etc/hosts.allow and /etc/hosts.deny describe which hosts are allowed to use the local INET services. If you want that only the local host be able to access this service, set the following :

In /etc/hosts.allow,ALL: LOCAL

In /etc/hosts.deny,ALL: ALL

4.4 /etc/services

You have to make sure that the services pop-2 and pop-3 are defined. Check in /etc/services that the following lines are presents :

pop-2 109/tcp postofficepop-3 110/tcp

It make the association between a service name (i.e. pop-3) and a service number (i.e. 110).

4.5 restart inetd

To restart inetd, type killall -HUP inetd

5. Configuration of fetchmail

5.1 Create .fetchmailrc

fetchmail fetches the mail on a POP3 server and redistribute the mail locally via sendmail. Each user (who must be able to fetch the mail for everybody) must have a configuration file .fetchmailrc in his home directory. In our example, it looks like this :

poll pophost.provider.com with protocol POP3 aka advalvas.be no dns : user dfzt12 fetchall with password kdwMlp45 to Paul.Dupont=paul Luc.Jackson=luc

The 'aka' is only used for multi-drop. In this example, it declares a DNS alias for 'provider.com'. If you look at the fetchmail man page, you will read :

When fetchmail, while processing a multi-drop mailbox, grovels through message headers looking for names of the mailserver, pre-declaring common ones can save it from having to do DNS lookups.

The option 'no dns' prevents the multi-drop system from looking for domain name aliases (for each recipient). If this option wouldn't be specified, it should be possible that a mail cannot be fetched because one of the recipient has an invalid domain name.

The option 'fetchall' ensures that all messages are fetched even the message that you have already read.

5.2 Check the permission of .fetchmailrc

fetchmail won't work if the configuration file hasn't the appropriate permission. chmod 0710 .fetchmailrc

6. Using sendmail and fetchmail

To fetch the mail and to redistribute it, a user can type fetchmail

To send the mail queue, type /usr/sbin/sendmail -q

To check the mail queue, type /usr/sbin/sendmail -bp 

Rapid Gui Development using Tcl/Tk

Course Text Part One: Tcl

(some text drawn from Brent Welch’s book, "Practical Programming in Tcl/Tk")

Aidan Low

Student Information Processing Board

MIT

January 1999

 

This class is sponsored by SIPB, the MIT Student Information Processing Board, a volunteer student group that provides computer- related services to the MIT community. This class is one of those services. For more information, see our web page, www.mit.edu, or drop by the SIPB office, W20-557. 

Class Homepage:

http://www.mit.edu/iap/tk

 

Class outline:

Tcl

Background

Variables – Set, Unset

Comments

Basic Input/Output

Procedures

Upvar

Arithmatic Operations

String Operations

List Operations

Control Flow

Global variables

Array Operations

More Input/Output

File Operations

System Operations

 

Background

Tcl is an intepreted high-level programming language designed to be easily understandable and easily customizable. It was developed by John Ousterhout when he wanted to give his students an editor that they could alter and extend themselves. One of tcl’s nicest features is that it’s very easy to write and very easy to read. Tcl code is simple and straightforward, for the most part, and as such is easy to modify.

Tk is an associated graphical toolkit which provides the same power to users in the graphical domain. Now complex GUIs can be written in a few dozen lines of code, rather than the pages it requires in more complicated arenas.

  

An Example: A Zsig randomizer

By the end of the night, this (highly inefficient) zsig randomizer should make sense to all of you.

#!/mit/tcl/bin/tclsh

proc getsigs {fname} {

set f [open $fname r]

set thelist {}

set curr {}

set temp [gets $f]

while {![eof $f]} {

if {$temp=="---"} {

set thelist [lappend thelist $curr]

set curr {}

} else {

set curr [lappend curr $temp]

}

set temp [gets $f]

}

set thelist [concat $thelist [list $curr]]

return $thelist

}

proc cleanNum {num} {

## this procedure strips off leading zeros

if {[string index $num 0]!="0"} {

return $num

} else {

return [cleanNum [string range $num 1 end]]

}

}

 

proc random {range} {

if {[file exists "/afs/athena.mit.edu/user/a/i/aidan/.zrand.seed"]} {

set f [open "/afs/athena.mit.edu/user/a/i/aidan/.zrand.seed" r]

set _ran [gets $f]

close $f

} else {

set _ran [pid]

}

set _ran [expr ($_ran * 9301 + 49297) % 233280]

set f [open "/afs/athena.mit.edu/user/a/i/aidan/.zrand.seed" w]

puts $f $_ran

close $f

return [expr int($range * ($_ran / double(233280)))]

}

 

proc pickone {list} {

set index [random [llength $list]]

puts stdout "index is $index"

set bit [lindex $list $index]

set result {}

foreach el $bit {

if {$result=={}} {

set result $el

} else {

set result "[set result]\n[set el]"

}

}

return $result

}

 

proc driver {} {

global argv argc

if {$argc==0} {

puts stdout "Format is z <name> \[other stuff\]"

return

}

set siglist [getsigs ~/.zephyr.list.short]

set sig [pickone $siglist]

puts stdout $sig

puts stdout "Type your message now. End with control-D or a dot on a line by itself."

if {[llength $argv]>4} {

return

# This doesn't work, and I’m too lazy to figure out why

set response [exec zwrite $argv]

puts stdout [lreplace $response 0 14]

} elseif {[llength $argv]==4} {

set response [exec zwrite [lindex $argv 0] [lindex $argv 1] \

[lindex $argv 2] [lindex $argv 3] -s $sig]

puts stdout [lreplace $response 0 14]

} elseif {[llength $argv]==2} {

set response [exec zwrite [lindex $argv 0] [lindex $argv 1] -s $sig]

puts stdout [lreplace $response 0 14]

} else {

set possible_error [catch {set response [exec zwrite [lindex $argv 0] -s $sig]}]

if {$possible_error==1} {

puts stdout "Error.... user not logged in?"

}

puts stdout [lreplace $response 0 14]

}

}

driver

 

Outline

This course will cover Tcl and Tk at a basic level, and looking at features of the language one by one along with examples of the code at work. In the end, we’ll see that a basic word processing program can be written very simply using Tcl and Tk. We’ll close by briefly looking at a number of advanced topics which are beyond the scope of this class, but that you can learn about on your own if you like.

In the first class, we’ll concentrate on Tcl, and in the second, we’ll look at how Tk can extend Tcl to allow the creation of graphical interfaces very simply.

 

Writing Conventions

Text in Courier font

set a [expr $b * 12]

represents code exactly as typed into the interpreter.

Text in italics represents an abstract thing to be filled in. This text

set f [open filename r]

means that the command takes a filename in that space. (i.e.

set f [open "foo.txt" r]

Text wrapped in ? ? means that it is optional.

puts ?–nonewline? textstring

Finally, … means that there are multiple things there.

foo arg … arg

 

 

Starting up (on Athena)

Tcl Interpreter

This will bring up the tcl interpreter in the window you run it in.

add tcl

tclsh

 

Tk Interpreter

This will bring up the Tcl interpreter in the window you run it in, but this tcl interpreter can run tk commands as well. A Tk window will also be created.

add tcl

wish

 

To run a file

To execute a file of tcl or tk code, there are two ways to do it. One is to make the file executable:

chmod 700 filename

and to put

#!/mit/tcl/bin/tclsh

or #!/mit/tcl/bin/wish

at the start of the file.

 

The other way is to run

source filename

in the interpreter once started.

 

Zephyr instance

I encourage all of you to work together as you’re learning Tcl and Tk, and a zephyr instance is a good way to do that. I’ll be subbed to this whenever I can, and you can ask your questions about Tcl and Tk on the instance where everyone can see them and benefit from the answers.

To subscribe for this login session only:

zctl sub message tcltk \*

To unsubscribe for this login session only:

zctl unsub message tcltk \*

To subscribe forever:

zctl add message tcltk \*

To unsubscribe forever:

zctl delete message tcltk \*

To send a message

zwrite –i tcltk

To read the logs of the instance

add zlog

cd /mit/zlog

more tcltk (or emacs tcltk, or whatever)

 

TclVariables – Set, Unset

The core of the tcl language is variables. Unlike other languages, every variable in tcl is a string. The string "123" is the number one hundred twenty-three, but it is also the character string made of the characters ‘1’,’2’ and ‘3’. It depends on the context in which the variable appears.

Assigning a variable a value is done with the set command

set a 3 <- This sets the variable a to be the string "3"

set b "Hello"

set c Hello

To get the value of a variable, the set command is used with just the name of the variable.

set a 3

set a <- This is equal to 3

One variable can be set to the value of another. A command can be run on its own line, but to nest commands, we use the [ ] braces.

set a 3

set d [set a] <- This sets d to be equal to the value of a

Note that [set a] is the value of the variable, and once done, the data has nothing to do with the variable. If the variable a changes later, d will not change.

The $ symbol is a shorthand which gets the value of a variable.

set e $a

A variable can be unset, removing it from the scope of the program, with the unset command.

unset e

set f $e <- this will generate an error, since e is no longer defined

To find out if a variable is defined, you can use info exists, which returns 1 if the variable is defined, and 0 if not.

set a 1

set b [info exists a] <- b is now 1

set c [info exists gobbledygook] <- c is now 0

These commands can be arbitrarily nested, either in a string

set g "this $a is $b weird $c"

or even in the name of a variable

set h 12 <- variable "h" is set to value "12"

set i$h 14 <- variable "i12" is set to the value "14"

 

Basic Input/Output

Input and output in tcl is pretty simple. Puts and gets are the output and input commands.

set a [gets stdin] <- this reads in a value from the console and stores it in a

puts stdout $a <- this command echos back the value to the console

puts stdout "Hello world"

puts stdout "This is value a: $a"

set b "User says [gets stdin]

The default location for puts is stdout, but gets has no default location. Go figure.

puts "Hello world"

By default, puts puts a carriage return at the end of the string it prints, but you can disable this with the nonewline flag.

puts –nonewline "These are on "

puts "the same line."

 

 

Procedures

You can define procedures in tcl and call them.

proc foo {} {

puts "This is procedure foo"

}

A procedure can take arguments.

proc bar {x y z} {

puts "$x $y $z"

}

A procedure can return a single value.

proc baz {x y} {

return [expr $x + $y]

}

set a [baz 2 3]

Procedure arguments can have default values

proc foo2 {{x 12} {y 3}} {

return [expr $x + $y]

}

set b [foo2] <- b is assigned 12 + 3 = 15

set c [foo2 7] <- c is assigned 7 + 3 = 10

set d [foo2 1 8]<- d is assigned 1 + 8 = 9

Note that the arguments with default values must be at the end of the list of arguments, and no arguments with default values may precede arguments without default values.

Procedures can even take any number of parameters, if the last parameter is named "args". Here, calling this procedure with any number of arguments will pass in the arguments as a list (more on lists later) bound to the identifier args.

proc bar2 {args} {

puts "Arguments are $args"

}

 

 

An Example

This somewhat contrived example takes in values from the console and echos them back.

proc foo {a b c} {

puts stdout "First input was $a"

puts stdout "Second input was $b"

puts stdout "Third input was $c"

}

puts stdout "Input 1:"

set in1 [gets stdin]

puts stdout "Input 2:"

set in2 [gets stdin]

puts stdout "Input 3:"

set in3 [gets stdin]

foo $in1 $in2 $in3

 

Upvar

In the arguments we have seen before, arguments are passed "by value". This means that in the code

proc foo {a b c} {

puts "$a $b $c"

}

set f 11

set g 22

set h 33

foo $f $g $h

The interpreter first computes the value of variable f (11), the value of variable g (22), and the value of variable h (33), and then send them (11,22,33) to the procedure foo. Foo knows nothing about where those values came from, and from foo’s perspective, the call might well have been

foo 11 22 33

The other method of passing arguments is called "call by name". In this calling convention, a function can access and modify variables from the caller. The upvar command works to allow you to do this.

Upvar takes in the name of a variable from the caller and the name of a variable to "bind" that variable to. After the upvar command, any reference to the second variable actually refers to the named variable in the caller. To pass a variable by name, you send in the name of the variable, rather than its value. For example:

proc foo {name1 name2 name 3} {

upvar $name1 a

upvar $name2 b

upvar $name3 c

puts "$a $b $c"

}

set f 11

set g 22

set h 33

foo "f" "g" "h"

Now, the caller calls foo with the names of its three variables (f, g, and h). Foo uses upvar to say that after the 3 upvar commands, any reference to "a" really means a reference to the variable "f" as defined in the scope of the caller. Now, in the puts command when the value of a is printed out, the value of f in the caller is really the thing that gets printed.

Upvar can also allow you to modify variables in the caller.

proc foo {name} {

upvar $name a

set a [expr $a + 1]

}

set f 11

puts $f <- f is 11 here

foo "f"

puts $f <- f is 12 here

 

Comments

Comments are text within a program that is ignored when running the program, but used to leave reminders and notes for other people who read the source code later.

Comments in Tcl are specified by the # character at the beginning of the line. This comments all the text to the end of the line. Note that you can only use # where the Tcl interpreter expects a Tcl expression, so you are somewhat restricted in where you can use it. In general, comments should be on their own lines to be safe.

# this procedure does something

proc foo {} {

puts "Hello world"

# is this correct?

puts "Hello werld"

}

 

Arithmatic Operations

The simplest arithmatic operations are simply incrementing and decrementing an integer. This is done with the incr and decr commands, which take in the name of the variable to increment. Note that this is a call by name command, so you send in the variable name, not the value of the variable.

(i.e. incr a, not incr $a)

set a 3

incr a <- a is now 4

decr a <- a is now 3 again

Arithmatic operations in Tcl are executed with the expr command

set a [expr 2 + 3]

set b [expr (2 + 3) * 4]

A number of more complex operations can be done with expr as well:

set c [expr 7 << 3] <- c is assigned 7 left shifted by 3, i.e. 7 * 23 = 56

set d [expr 7 >> 3] <- d is assigned 7 right shifted by 3, i.e. 7 / 23

Note that the "type" of the input determines the operation in some cases

set e [expr 7 / 3] <- e is assigned 7 integer-divided by 3, or 2

set f [expr 7.0 / 3.0] <- f is assigned 7 float-divided by 3, or 2.3333

Other math operations have a slightly different notation

set g [expr pow(3,2)] <- g is assigned 3 raised to the second power

set h [expr acos(.8)] <- h is assigned the arc-cosine of .8

Tcl supports many math operations:

acos(x)

asin(x)

atan(x)

atan2(y,x) Rectangular (x,y) to polar (r,th), atan2 gives th

ceil(x)

cos(x)

cosh(x)

exp(x) ex

floor(x)

fmod(x,y) Floating point remainder of x/y

hypot(x,y) Returns sqrt(x*x + y*y) (r part of polar expresion)

log(x) Natural log

log10(x) Log base 10

pow(x,y) xy

sin(x)

sinh(x)

sqrt(x)

tan(x)

tanh(x)

abs(x)

double(x) Turns x into floating point number

int(x) Turns x into an integer (truncates)

round(x) Turns x into an integer (rounds off)

rand() Return a random floating point value between 0.0 and 1.0

srand(x) Seed the random number generator

 

Another example

This procedures takes in inputs and adds them up until they sum to more than 100.

#running total takes in the name of the total counter

#and the value to add to it

proc runningtotal {totalname new} {

upvar $totalname current

set current [expr $current + $new]

}

set total 0

while {$total < 100} {

puts stdout "Total is now $total."

puts stdout "Input new value:"

set inputvalue [gets stdin]

runningtotal "total" $inputvalue

}

puts stdout "Done."

 

String Operations

Since all Tcl values are stores as strings, it makes sense to have a number of string operations that can be done. Just as expr preceded arithmatic expressions, string precedes these commands.

set a [string length "Hello world"] <- a gets the string length of the string, which is 11

set b [string index "Hello world" 3] <- b gets the 3rd character in Hello World

Note that strings are 0-indexed, so the 0th character is the first one in the string.

Other string operations:

string compare str1 str2 Returns 0 if equal, -1 if str1 sorts before str2

lexographically, 1 if str2 sorts before str1

string first sub str Returns the index of the first character of the first

occurrence of substring sub appearing in str

string last sub str Returns the index of the first character of the last

occurrence of substring sub appearing in str

string match pattern str Returns 1 if str matches the pattern in pattern.

Uses glob-style matching. (* and ? wildcards)

string range str I j Returns the range of characters from index i to index j.

string tolower str

string toupper str

string trim str ?chars? Trims the characters in the string chars from the front and

end of str. chars defaults to whitespace if it is not present.

string trimleft str ?chars?

string trimright str ?chars?

string wordend str ix Return the index in str of the character after the word

containing the character at ix.

string wordstart str ix Return the index in str of the first character in the word

containing the character at ix.

 

Append

Another useful string command is append. This appends one or more arguments to a variable. Note that the first argument is the NAME of the variable, not [set variable] or $variable.

set a "abc"

set b " some letters"

append a " is " $b "that I like"

puts $a <- this will print "abc is some lettersthat I like"

Note: append is efficient, much more so than doing something like

set x "abc "

set x "$x some letters that I like"

Format

Format is like the C printf function.

set a [format "%d %f %s" 10 4.3 "hello"] <- a is assigned "10 4.3 hello"

 

Scan

Scan is like the C scanf function.

scan "10 4.3 hello" "%d %f %s" x y z <- assigns x to be 10, assigns y to be 4.3, assigns z to be hello

 

 

A gratuitous string example

This example reads in a string and then prints out some information about it

proc stringparse {str} {

puts stdout "String's length is [string length $str]"

puts stdout "String's first a is [string first "a" $str]"

puts stdout "String in uppercase is [string toupper $str]"

append str " tacked on for good measure"

puts stdout "String is now $str"

}

puts stdout "Input string:"

set str [gets stdin]

stringparse $str

set str [gets stdin]

stringparse $str

set str [gets stdin]

stringparse $str

 

List Operations

The most common data structure in Tcl is a list.

Lists can be created in a number of ways.

set a [list "a" "b" c"]

<- a is assigned to be a list with three elements, "a", "b", and "c"

Curly braces {} can be used as shorthand for the list command.

set a {"a" "b" "c"}

<- a is assigned to be the same list as earlier

Lists can be nested:

set a [list "a" [list "b" "c"] "d"]

set a {"a" {"b" "c"} "d"}

A number of list operators are very useful.

lindex list i <- returns the ith element of the list

llength list <- returns the number of elements in the list

lappend listName arg … arg <- appends some number of arguments to a list

(note use of list name, not the list itself, as the first arg)

lrange list i j <- return the sublist of the list found between

indexes i and j

linsert list i arg … arg <- insert some number of arguments into a list before the

ith element. Returns the new list.

lreplace list i j arg … arg <- replace the elements i thru j of the list with the args.

returns the new list.

lsearch ?mode? list value <- returns the first index of an element in the list that

matches the value. mode can be –exact (must match

exactly), -glob (uses * and ? wildcards) or –regexp

(match using regular expression syntax). Returns –1 if

not found. Defaults to -glob.

concat list … list <- concatenates multiple lists together into one big list.

lsort ?switches? list <- Sort the list as per the switches passed in. –ascii,

-integer, -real, -dictionary, -increasing, -decreasing

Returns a new list.

join list joinString <- join the elements of a list into a single string, each

element separated by joinString

split string splitChars <- split a string into a list by dividing the string up with

all the characters in splitChars as the delimiters.

 

Control Flow

Control flow in Tcl works like in many other languages.

 

if, while, for

proc foo {a b} {

if {$a > $b} {

return $a

} else {

return $b

}

}

proc bar {a b} {

while {$a>0} {

set a [expr $a – 1]

puts [expr $a + $b]

}

}

proc baz {a b} {

for {set i 0} {$i < $a} {incr i} {

puts "$a $b"

}

}

foreach

One construct that’s cool is foreach. Foreach is like for, except that a loop is run once for each element in a list rather than for each value between two boundaries. This example would print out all the colors in the list.

foreach el {"red" "blue" "green" "yellow"} {

puts $el

}

Foreach can have more than one list variable, in that case the list would be taken in pairs. This example would print out the colors two at a time.

foreach {elX elY} {"red" "blue" "green" "yellow"} {

puts "$elX is a better color than $elY"

}

 

continue, break

The continue and break commands allow control to jump out from the inside of a loop. Break breaks out of the loop and runs the next command, and continue jumps back to the start of the loop.

In some sense, break stops the loop totally and continue skips to the next run through the loop.

switch

Switch allows control to jump to a number of expressions depending on what a value is.

switch -- $name {

"Aidan" {puts "This guy is a dork"}

"Rorschach" {puts "This dude is a badass"}

"Leon" {puts "This guy is surprisingly smart"}

default {puts "I don’t know this person"}

}

The default is to do exact matching, but flags can be put after switch to change this.

switch –exact $name

or switch –glob $name

or switch –regexp $name

Put -- before $name if there’s any chance that $name might begin with a -, or else $name will be interpreted as a flag. It’s best to put -- before $name anyway.

If a - is written as the expression for a match, then control "falls through" to the next non-"-" expression.

switch -- $name {

"Aidan" –

"Atilla" –

"Godilla" {puts "wimp"}

"Jesse the Mind" –

"Steve Austin" –

"Hulk Hogan" {puts "tough guy"}

}

 

catch

Tcl supports error handling. If you use the catch command, you can try execute a block of Tcl expressions and trap any errors that result.

catch command ?resultVar?

catch returns true (1) if the command failed, or false (0) otherwise. The resultVar, if present, is given the return value of the command, or an error message if an error occurred.

if {[catch {foo 1 2 3} result]} {

puts stderr $result

} else {

# do other stuff

}

catch can catch return, break, and continue commands as well. See the documentation for information on this.

 

error

The error command is the counterpart to catch. It signals an error that will trigger an enclosing catch or halt the program with an error message if there is no enclosing catch.

error message

 

return

The return command returns from a procedure, returning a value.

return string

 

 

 

Another Silly Example

proc makelist {} {

set months {}

foreach el {"January" "February" "March" "April" "May" "June" "July"} {

lappend months $el

}

return $months

}

set data {}

foreach el [makelist] {

puts $el

set data [concat $data $el]

if {[llength $data] > 3} {

break

}

}

puts "done"

 

Global variables

So far we’ve looked only at local variables, variables in the same procedure where they’re used. Variables can also be global, and thus accessible anywhere in the program. Global variables are somewhat dangerous to use, but they’re really quite useful, especially in small programs that are easily understood.

To declare a variable global, you simply use the global command

proc setGlobals {val1 val2 val3} {

global a b c

set a $val1

set b $val2

set c $val3

}

proc printGlobals {} {

global a b c

puts "$a $b $c"

}

If you declare a variable outside of any procedures, it is implicitly global.

set a 12

proc foo {} {

global a

puts $a

}

 

 

Array Operations

Another construct Tcl provides is an array indexed by string values. This is essentially a hash table, and allows efficient association of keys and values.

To create an array, simply set a variable along with an index.

set myArray("redKey") "redValue" <- this will create an array called myArray if it

doesn’t exist already, and add the value

"redValue" associated with the key "redKey"

To get the value out again, just use set like we did before.

set theValue [set myArray("redKey")]

or

set theValue $myArray("redKey")

You can test whether a key is present in an array by using info exists, just like for regular variables.

if {[info exists myArray("redKey")]} {

puts $myArray("redKey")

}

The array command allows a number of operations on arrays as well:

array exists arr <- returns 1 is arr is the name of an array variable

array get arr ?pattern? <- turns an array into a list of alternating keys and values

pattern selects the entries to put into the list, if present

(beats the hell out of me how the pattern works, though)

array names arr ?pattern? <- returns a list of the names of the keys in the array

applies the pattern to filter, if present

array set arr list <- create arr from the data in list, which is in the same form

as returned from get

array size arr <- return the number of keys in the array arr

You can iterate through an array as well. Look at the documentation for information on startsearch, nextelement, anymore, and donesearch.

Note that passing arrays between procedures requires call by name, using upvar.

 

More Input/Output

File input/output is reasonably straightforward.

open filename ?access? ?permissions?

access can be:

r Open for reading, file must exist

r+ Open for reading and writing, file must exist

w Open for writing, replace if exists, create if does not

w+ Open for reading and writing, replace or create as needed

a Open for writing, data is appended to file

a+ Open for reading and writing, data is appended

Permissions are the standard numbers used in chmod, but you need a leading 0 to get an octal number.

set fileId [open /tmp/foo w 0600]

Once open, a file can be written to

puts $fileId "Hello world"

or read from

set fileId2 [open /tmp/foo r]

set in [gets $fileId2]

To close an open file (you must close a file before the Tcl program ends to store your changes), simply use close on the fileID

close $fileId

You can also pass –nonewline to puts, if you don’t want to put an end-of-line after the string.

puts –nonewline $fileId "Hello world"

 

 

 

File Operations

Tcl provides a number of operators on files that can be quite useful.

file atime name <- returns file access time as a decimal string

file attributes name ?option? ?value? … <- query or set file attributes

file copy ?-force? source destination <- copy files or directories

file delete ?-force? name

file dirname name <- return parent directory of file name

file executable name <- return 1 if executable, else 0

file extension name <- return the extension (.txt) of the file

file isdirectory name <- returns 1 or 0

file isfile name <- return 1 if name is not a directory, symlink, or device, else 0

file join path path … <- join pathname components into a single pathname

file lstat name var <- place attributes of the link name into var

file mkdir name

file nativename name <- return the platform-native version of name

file owned name <- return 1 if current user owns the file name, else 0

file pathtype name <- relative, absolute, or driverelative

file readable name <- return 1 if readable, else 0

file readlink name <- return the contents of symlink name

file rename ?-force? old new

file rootname name <- return all but the extension in name (i.e. strip off .txt or whatever)

file size name <- return file size of name in bytes

file split name <- split name into its pathname components

file stat name var <- place attributes of name into array var

file tail name <- return the last pathname component of name

file type name <- return type identifier, which is either file, directory, characterSpecial, blockSpecial, fifo, link, or socket

file writeable name <- return 1 if writeable, else 0

 

 

System Operations

Tcl provides the functionality to execute a number of system calls directly.

 

exec

The exec program allows you to execute programs from your Tcl script. The standard output of the program will be returned.

set d [exec date]

If the program writes to the standard error channel or exits with a non-zero exit code, you’ll need to use catch to get the information you want.

catch {exec program arg arg} result

Exec has a lot of weird specifics about how it works, so look at the documentation for specifics.

http://www.scriptics.com/man/tcl8.0/TclCmd/exec.htm

 

exit

The exit command terminates your Tcl program. An integer argument to exit sets the exit status of the process

exit

exit –1

 

pid

The pid command returns the process ID of the current process.

set thisID [pid]

 

environment variables

You can get access to environment variables with the global variable env. This loop prints out all the environment variables and their values.

global env

foreach el [array names env] {

puts "$el - $env($el)"}

 

Links of interest:

The Tcl Platform Company

http://www.scriptics.com/

Brent Welch’s book (excellent)

http://www.beedub.com/book/

Tcl/Tk Man pages

http://www.scriptics.com/man/tcl8.0/contents.htm

Tcl/Tk Plugin for Netscape, Internet Explorer

http://www.scriptics.com:8123/plugin/