A GUI front end

Normally I'm more a command line person. Still, the ACK has some idosyncracies that may be flattened out by a simple front end. And since Graphical seems to be the word of this era (how else would you like to spoil all these GHz-es and CPU cores?) I set out to create a GUI front end for the ack. To get this accomplished there are some methods:

The first two of these are rather difficult for a NOOP guy like I am. So I opted for the Tcl/Tk approach. Programming with Tcl/Tk is fast since it is an interpreted language. The major drawback of Tcl/Tk however is that it pretends to be a language, but it is no more than a bunch of commands without much interconnections. It is not logical, contains virtually no structure and it seems to be maintained by as many persons as possible. The documentation at www.tcl.tk comes close to the Java documentation. Overdone, over the top and too extensive. And when you look around on the net, the forums seem to be populated by experts and their answers are only relevant to other experts.

Still, Tcl/Tk is the fastest way to get a simple user interface going. It may require lots of trial and error, but learning Qt or GTK requires more study and that's not what I am looking for.



Version 001 : get it going

To the right, you see how the version 001 presents itself on screen. Below is the source code for the first version of the GUI front end. It is called 'gack'. It cannot do much sensible things. It is a mere mockup for the user interface and some command line printing.

The GUI front end as is should

Tcl/Tk can do this. With many a fancy colour. And that's what counts, if you run Ubuntu. Below is the source code gack.tcl:
#! /usr/bin/wish

# GUI for the ACK

set version	001

set name	{}
set fontf20 	{times 18 bold}
set fontf12 	{times 12 bold}

proc init { } {
   
   global modules switches
   set fileID [open modules.list r]
   while { [ gets $fileID line] > 0 } {
      lappend modules $line
   }
   close $fileID
   set fileID [open switches.list r]
   while { [ gets $fileID line] > 0 } {
      lappend switches $line
   }
   close $fileID
}
				       
proc testit   {} { puts stdout "Testit pressed"		}

proc editswit {} { puts stdout "edit switches"		}

proc editmods {} { puts stdout "edit modules"		}

proc compile  {} { puts stdout "compile it"		}

proc edit {file} { puts stdout $file			}


proc run { } {
   global name version fontf20 fontf12 modules
   wm title . "ACK Control version $version"
   
   frame .bott
   frame .topp
   frame .qtext -borderwidth 4
   frame .topq  -borderwidth 5
   frame .center
   frame .center.left  -relief groove -borderwidth 5
   frame .center.right -relief groove -borderwidth 5
   
   # topmost section
   
   label .topp.label -text "gack version $version"	-font $fontf12
   pack .topp.label -pady 5 -fill x
   
   # not so topmost section
   
   label .topq.label -font $fontf12 -text "Source : [lindex $modules 0]"
   label .topq.exe   -font $fontf12 -text "Target : [lindex $modules 0]"
   
   pack .topq.label -side left  -padx 5
   pack .topq.exe   -side right -padx 5
   
   # Center section
   
   listbox .center.left.mods   -listvariable modules  -height 6
   listbox .center.right.swits -listvariable switches -height 6
   
   button .center.left.butt  -text Modules  -borderwidth 3 -bg grey   -command editmods
   button .center.right.butt -text Switches -borderwidth 3 -bg grey50 -command editswit
   
   pack .center.left.butt  .center.left.mods   -side top -padx 5m -pady 2m
   pack .center.right.butt .center.right.swits -side top -padx 5m -pady 2m
   pack .center.left  -side left  -padx 5m
   pack .center.right -side right -padx 5m
   
   # Lower section
   
   text .qtext.log -borderwidth 3 -relief groove
   
   pack .qtext.log -padx 10 -pady 10
   
   # Master control section
   
   button .bott.comp -text Compile -borderwidth 4 -bg pink   \
     -command compile
   button .bott.run  -text Run     -borderwidth 4 -bg green  \
     -command testit
   button .bott.edit -text Edit    -borderwidth 4 -bg yellow \
     -command { edit [lindex $modules [.center.left.mods curselection]] }
   button .bott.stop -text Quit    -borderwidth 4 -bg cyan   -command exit
   
   pack .bott.comp .bott.edit -side left  -padx 5m -pady 2m -fill x
   pack .bott.stop .bott.run  -side right -padx 5m -pady 2m -fill x
   
   # Final packing up
   
   pack .topp .topq .center .bott -side top -fill x
}

init
run
   
Most of this all is rather straight forward. Create buttons, lists and populate frames. It's the surroundings however that make things difficult. There are no real rules when to use a '$' prefix for variables. That's hard to work with for a Wirthina programmer like I am.
Also it is hard to predict when (and where to use attributes like '-fill x'. The '.topq' frame refused to fill correctly, until the '-fill x' was added to the last 'pack' line, although that frame is packed vertically.


gack version 002: that's more like it

To the right you see gack version 002. The most important differences with gack001 were:

Not all of the changes were easy. Tcl is not a real language. At least, not a language like we, wirthians, are accustomed to. The internet is of little help when you get stuck, for the reasons mentioned before.

My best companions here are still (how old fashioned!) two paper books: Here is an example of the output generated by clicking the
  1. 'Edit'
  2. 'Compile'
  3. 'Run'
  4. 'Modules'
  5. 'Switches'
  6. 'Edit' (once more, but now with another selection)
buttons:
jan@Beryllium:~/develop/ack/GUI$ gack.tcl
exec kate Plov030.mod
ack Plov030.mod Sio.mod NumConv.mod -w -t -v -gdb -o Plov030
exec Plov030
edit switches
edit modules
exec kate NumConv.mod
   


gack version 002 : the source

Below is the source code of gack version 002. It is split up in sections with some comments, when required.

#! /usr/bin/wish

# GUI for the ACK

set version	002
   
This is how a variable is declared and filled. In fact, 'set' is equivalent to the 'LET' keyword of early versions of BASIC.
set fname {}
set fontf20 	{times 18 bold}
set fontf12 	{times 12 bold}
   
The curly braces are a bit tricky to use. Sometimes you need a {} pair, sometimes a pair of double quotes. Sometimes a pair of []. In these cases, the variable 'fontf20' holds the (ASCII) value "times 18 bold" which specifies a letter type (font face and size).
proc init { } {
   
   global modules switches fname

   set fileID [open modules.list r]
   while { [ gets $fileID line] > 0 } {
      lappend modules $line
   }
   close $fileID

   set fileID [open switches.list r]
   while { [ gets $fileID line] > 0 } {
      lappend switches $line
   }
   close $fileID

   set name [lindex $modules 0]
   set nr [string last . $name]
   incr nr -1
   set fname [string range $name 0 $nr]
}
   
Init does just what the name says. It takes no arguments (hence the first empty '{ }' braces). Next it needs a 'gobal' line to tell the procedure which variables are in scope. The fact that a variable is declarec globally does not mean it is in scope...
Next a file is opened and the modules that are required for this project are read in. And something similar for the compiler switches. Next the name of the target executable is derived from the source file name. Tcl does have an 'incr' function but the matching 'decr' does not exist.
proc testit { } { 
   
   global fname
   set exname "exec "
   append exname $fname
   puts stdout $exname
}
   
This is the command that is executed when the 'Run' button is clicked
proc editswit {} {
   
   puts stdout "edit switches"
}

proc editmods {} {
   
   puts stdout "edit modules"
}

proc compile { } {

   global fname modules switches
   set runname "ack "
   foreach i $modules {
      append runname $i
      append runname " "
   }
   foreach i $switches {
      append runname $i
      append runname " "
   }
   append runname "-o "
   append runname $fname
   puts stdout $runname
}
   
Same for the buttons 'Modules', 'Switches' and 'Compile'. This kind of Tcl is rather fun to do.
proc edit {file} {

   set nr [.center.left.mods curselection]
   set editname "exec kate "
   if {$nr >= 0} {
      if {$nr <= 9} {
	 append editname $file
      }
   }
   puts stdout $editname
}
   
The two nested if's were an experiment to prevent an action when no module is selected. For some rason I could not find out what the value of 'nr' is when nothing is selected. I did some experiments. When the top field was selected, 'nr' was zero (as was to be expected). Yet, when nothing was selected, the 'nr' variable was '', i.e. plain old NOTHING, an empty set.
proc run { } {
   
   global name version fontf20 fontf12 modules fname
   wm title . "ACK Control version $version"
   
The quoted text is first processed by filling in the value of the variable 'version' before passing the string to the 'wm' command. If a word has a dollar sign up front, it is treated as a variable that needs to be inserted right there. Since the variable contains text, the text is inserted and afterwards, the full text is processed. So, if a variable is '18' it is not the number eightteen, but just the two ASCII digits '1' and '8'. Which makes up for the following line of Tcl code:
if {"0xa" = "10"} { puts stdout "0xa = 10" }
The puts command is executed. Hexadecimal 'A' indeed is decimal '10' so Tcl sees this as true...
   frame .bott
   frame .topp
   frame .qtext -borderwidth 4
   frame .topq  -borderwidth 5
   frame .center
   frame .center.left  -relief groove -borderwidth 5
   frame .center.right -relief groove -borderwidth 5
   
A frame is a container to hold widgets (graphical elements). By carefully creating and filling frames, you can make the user interface appear nice on screen.
   # topmost section
   
   label .topp.label -text "gack version $version"	-font $fontf12
   pack .topp.label -pady 5 -fill x
   
   # not so topmost section
   
   label .topq.label -font $fontf12 -text "Source : [lindex $modules 0]"
   label .topq.exe   -font $fontf12 -text "Target : $fname"
   
   pack .topq.label -side left  -padx 5
   pack .topq.exe   -side right -padx 5
   
with 'pack' you put widgets in a frame (or subframe).
   # Center section
   
   listbox .center.left.mods   -listvariable modules  -height 6 -relief groove -borderwidth 3
   .center.left.mods selection set 0
   listbox .center.right.swits -listvariable switches -height 6
   
   button .center.left.butt  -text Modules  -borderwidth 3 -bg grey   -command editmods
   button .center.right.butt -text Switches -borderwidth 3 -bg grey50 -command editswit
   
Some buttons and listboxes are created.
   pack .center.left.butt  .center.left.mods   -side top -padx 5m -pady 2m
   pack .center.right.butt .center.right.swits -side top -padx 5m -pady 2m
   pack .center.left  -side left  -padx 5m
   pack .center.right -side right -padx 5m
   
   # Lower section
   
   text .qtext.log -borderwidth 3 -relief groove
   
   pack .qtext.log -padx 10 -pady 10
   
   # Master control section
   
   button .bott.comp -text Compile -borderwidth 4 -bg pink   \
     -command compile
   button .bott.run  -text Run     -borderwidth 4 -bg green  \
     -command testit
   button .bott.edit -text Edit    -borderwidth 4 -bg yellow \
     -command { edit [lindex $modules [.center.left.mods curselection]] }
   
This last line of source was like HELL to come up with. After all, it -was- in the documentations. Yet so little attention was paid to it that it was very hard to extract.
   button .bott.stop -text Quit    -borderwidth 4 -bg cyan   -command exit
   
   pack .bott.comp .bott.edit -side left  -padx 5m -pady 2m -fill x
   pack .bott.stop .bott.run  -side right -padx 5m -pady 2m -fill x
   
   # Final packing up
   
   pack .topp .topq .center .bott .qtext -side top -fill x
}
   
from here on is the main routine....
init
run
   
Initially I could not use the 'exec' command to run external programs. So I looked up some previous Tcl/Tk scripts and everything looked normal. Then I saw that the 'exec' procedure was used more than once... So now I changed the source such that the 'proc edit {file}' has been taken out altogether. Instead, I changed the line in the 'run' procedure to look like:
   button .bott.edit -text Edit    -borderwidth 4 -bg yellow \
     -command { exec kate [lindex $modules [.center.left.mods curselection]] }
   
and this runs fine! Kate is one of the few editors that has syntax highlighting for Modula-2 (and all other languages of any significance).

Now see if I can get the Compile button to work and catch the compiler messages in the logging window. Then, if that works, the Modules and Switches lists need to be made such that old modules can be deleted and new ones added. And perhaps scroll bars in both lists. And in the logging screen.


Fixing the intricate problems

Tcl is nitpickers programming dream. Either you do it 100.000% the Tcl wy, or you don't. 99/99% is not enough. This section describes how very annoying Tcl can be, if you are familiar with a real programming language.

The compile section

The compile section would just NOT run. Error on error. As long as the command arguments to the ACK compiler were in a Tcl variable, the thing would produce endless error messages. Here's the source of the procedure:
proc compile { } {

   global fname modules switches
   set runname " "
   foreach i $modules {
      append runname $i
      append runname " "
   }
   foreach i $switches {
      append runname $i
      append runname " "
   }
   append runname "-o "
   append runname $fname
   exec ack $runname
}
   
When you print $runname, you get exactly what you would have types in at a command prompt:
Plov030.mod Sio.mod NumConv.mod -w -t -o Plov030
and still the Tcl program complains that it cannot find the source file. The error here is, that the 'exec' does not try to execute a line like
ack Plov030.mod Sio.mod NumConv.mod -w -t -o Plov030
but rather the line
ack "Plov030.mod Sio.mod NumConv.mod -w -t -o Plov030"
The full command tail is handed over as one and only one argument.... The error was fixed by adding a new keyword to the exec line:
eval exec ack $runname
does the trick. The 'eval' function (or property or method or whatever) splits up the different sections of the 'runname' variable. At these moments, I am glad my main programming language is Modula-2. Modula-2 is predictable. It requires but little trial and error. Tcl is based on trial and error.

Below is the output of the ls command after gack.tcl compiled:
jan@Beryllium:~/develop/ack/GUI$ ls -l Plov030*
-rwxr-xr-x 1 jan users  59164 2010-09-29 18:29 Plov030*
-rw-r--r-- 1 jan users  47361 2010-09-29 18:29 Plov030.k
-rw-r--r-- 1 jan users  40106 2010-09-29 18:29 Plov030.m
-rw-r--r-- 1 jan users  40489 2010-09-28 02:00 Plov030.mod
-rw-r--r-- 1 jan users  60053 2010-09-29 18:29 Plov030.o
-rw-r--r-- 1 jan users  67574 2010-09-29 18:29 Plov030.out
-rw-r--r-- 1 jan users 106568 2010-09-29 18:29 Plov030.s
   
See the time stamp? It's today 29 september 2010 and the time is 18:30. YES, the gack works!

Test run of the executable

I stripped out the full testit procedure and changed the button definition into
button .bott.run  -text Run     -borderwidth 4 -bg green -command { exec $fname }
and when I ran it, nothing happened. No Tcl error but also no Plov030 error... I blame it on the log file not yet working. Time to fix that first.

It took some time and some testing (and a major rewrite) but this is gack version 4 running:




OK, the version number is listed as '5' but in effect it is version '4' with a few small clean ups. All buttons and functions work:

Of course the program is open source software and it is published under the rules and guide lines of the GPL version 2. You use it with the same liability towards me, as when you participate in traffic every day. Blah blah blah.

I still have some wishes for future versions:


gack version 004 : the source

Below is the source code of the version 004 (which is labeled 005 for stupidity reasons) .he source code is broken up in parts. Use the download section to get to the full file.

#! /usr/bin/wish

# GUI for the ACK

set version	005

set fname 	{}
set args	{}
set fontf20 	{monospace 20 bold}
set fontf12 	{monospace 12 bold}
set fontf10	{monospace 10 bold}


proc init { } {
   
   global modules switches fname

   set fileID [open modules.list r]
   while { [ gets $fileID line] > 0 } {
      lappend modules $line
   }
   close $fileID

   set fileID [open switches.list r]
   while { [ gets $fileID line] > 0 } {
      lappend switches $line
   }
   close $fileID

   set name [lindex $modules 0]
   set nr [string last . $name]
   incr nr -1
   set fname [string range $name 0 $nr]
}
   
Init does just what it says: it reads the files that were created during the previous session.
proc save {} {
   global switches modules
   
   set fileID [open modules.list w]
   foreach i $modules {
      puts $fileID $i
   }
   close $fileID
   
   set fileID [open switches.list w]
   foreach i $switches {
      puts $fileID $i
   }
   close $fileID
   exit
}
   
This is the complement of the Init section: it saves the state of the compiling session. The 'exit' returns to the operating system and closes the GUI.
proc unixcomm { command } {	# Execute an (operating system) command
   global input log unicom
   if [catch {open "| $command |& cat"} input] {
      $log insert end $input\n
   } else {
      fileevent $input readable Log
      $log insert end $command\n
   }
   set unicom {}
}
   
I borrowed this code from a code example in the Welch and Jones book (page 377). It works. Parts of it I understand. But the fileevent and Log 6thingies are obscure to me.
proc elless { } {		# Carry out an 'ls -l' command
   global input log
   set runname "ls -l"
   if [catch {open "| $runname |& cat"} input] {
      $log insert end $input\n
   } else {
      fileevent $input readable Log
      $log insert end $runname\n
   }
}


proc testit { } {		# Run the freshly created executable
   global log fname input args
   set command "./"
   append command $fname
   if { [string length $args] > 0 } {		;# check for arguments
      append command " "
      append command $args
   }
   if [catch {open "| $command |& cat"} input] {
      $log insert end $input\n
   } else {
      fileevent $input readable Log
      $log insert end $command\n
   }
}


proc Log { } {			# Copied from 'Practical Tcl/Tk programming'
   global input log
   if [eof $input] {
      catch { close $input }
   } else {
      gets $input line
      $log insert end $line\n
      $log see end
   }
}
   
I haven't got a clue what it does. Let's say it is the appendix of gack programming...
proc compile { } {
   global fname modules switches log input
   set runname "ack "				;# Compose start of command
   foreach i $modules {				;# Process external Modules
      append runname $i				;#   Add Module name
      append runname " "			;#   Add some whitespace
   }
   foreach i $switches {			;# Process compiler switches
      append runname $i
      append runname " "
   }
   append runname "-o "					;# Prepare to declare
   append runname $fname				;#   name of executable
   if [catch {open "| $runname |& cat"} input] {	;# Catch the screen output from
      $log insert end $input\n				;#   the command
   } else {						;# or
      fileevent $input readable Log
      $log insert end $runname\n			;#   echo the command itself
   }
}
   
What follows is the main routine of the gack.
proc run { } {
   
   global name version fontf20 fontf12 fontf10 modules fname log args
   wm title . "ACK Control version $version"
   
   frame .row1						;# GACK version
   frame .row2  -borderwidth 5				;# Summary
   frame .row3						;# Lists section
   frame .row3.left  -relief groove -borderwidth 5	;#   Modules to compile
   frame .row3.mid   -relief raised -borderwidth 5	;#   Add/Delete elements
   frame .row3.right -relief groove -borderwidth 5	;#   Compiler switches
   frame .row4						;# Button row
   frame .row5						;# Unix command
   frame .row6 -borderwidth 4				;# Logging screen
   
   # ROW 1 section	: GACK version
   
   label .row1.label -text "gack version $version"	-font $fontf20
   pack .row1.label -pady 5 -fill x
   
   # ROW 2 section	: Summary
   
   label .row2.src  -font $fontf12 -text "Source : [lindex $modules 0]"
   label .row2.exe  -font $fontf12 -text "Target : $fname"
   label .row2.args -font $fontf12 -text "Arguments : $args"

   pack .row2.src -side left  -padx 5
   pack .row2.exe -side right -padx 5
   
   # ROW 3 section	: Listboxes
   
   listbox .row3.left.mods   -listvariable modules  -height 6 -relief groove -borderwidth 3 \
     -yscrollcommand { .row3.left.scroll set }
   scrollbar .row3.left.scroll -command { .row3.left.mods yview }
   .row3.left.mods selection set 0

   listbox .row3.right.swits -listvariable switches -height 6 -relief groove -borderwidth 3 \
     -yscrollcommand { .row3.right.scroll set }
   scrollbar .row3.right.scroll -command { .row3.left.swits yview }
   
   label .row3.mid.label -text "List control" -font $fontf12 
   button .row3.mid.del -text Delete  -borderwidth 2 -bg blue4 -fg yellow  \
     -font $fontf10 -command {
       set nr [.row3.left.mods curselection]		;# nr := index in list
       if { $nr > 0 } {					;# if 'nr' valid index
	 set modules [lreplace $modules $nr $nr]	;# delete entry and update list
       }
       set nr [.row3.right.swits curselection]		;# same as above
       if { $nr >= 0 } {
	 set switches [lreplace $switches $nr $nr]
       }
   }

   button .row3.mid.add -text Add     -borderwidth 2 -bg gold              \
     -font $fontf10 -command {
      set nr [.row3.left.mods curselection]
      if { $nr >= 0 } {
	 incr nr
	 set modules [linsert $modules $nr $unicom]
      }
      set nr [.row3.right.swits curselection]
      if { $nr >= 0 } {
	 incr nr
	 set switches [linsert $switches $nr $unicom]
      }
   }

   pack .row3.left.mods .row3.left.scroll    -side left  -padx 1  -pady 2m -fill y
   pack .row3.mid.label .row3.mid.add  .row3.mid.del         -side top   -padx 5m -pady 1m
   pack .row3.right.scroll .row3.right.swits -side right -padx 1  -pady 2m -fill y
   pack .row3.left .row3.mid .row3.right   -side left  -padx 5m -fill both

   # ROW 4 section	: Master control
   
   button .row4.comp -text Compile -borderwidth 2 -bg pink   -command compile -font $fontf12
   button .row4.ls   -text "ls -l" -borderwidth 2 -bg orange -command elless  -font $fontf12
   button .row4.run  -text Run     -borderwidth 2 -bg green  -command testit  -font $fontf12
   button .row4.stop -text Quit    -borderwidth 2 -bg cyan   -command save    -font $fontf12
   button .row4.args -text "Set args" -borderwidth 2 -bg OliveDrab -fg white  -font $fontf12 \
     -command {
      set args $unicom
      $log insert end "Arguments : $args\n"
      set unicom ""
   }

   button .row4.edit -text Edit    -borderwidth 2 -bg yellow -font $fontf12 \
     -command { exec kate [lindex $modules [.row3.left.mods curselection]] }

   pack .row4.comp .row4.edit 			 -side left  -padx 3m -pady 2m -fill x
   pack .row4.stop .row4.run .row4.ls .row4.args -side right -padx 3m -pady 2m -fill x
   
   # ROW 5 section	: Unix command

   label  .row5.label -text "Text entry :" -font $fontf12
   button .row5.clr   -text Clear            -font $fontf12   -bg DarkSalmon -borderwidth 3 \
     -command {set unicom "" }
   button .row5.butt  -text "Execute"        -font $fontf12   -bg coral -borderwidth 3 \
     -command { unixcomm $unicom }
   entry  .row5.entry -width 40 -relief sunken -borderwidth 3 -textvariable unicom
   
   bind .row5.entry <Return> { unixcomm $unicom }
   
   pack .row5.label .row5.entry -side left -pady 2m -padx 2m
   pack .row5.clr .row5.butt -side right -fill x -padx 2m -pady 2m

   # ROW 6 section	: LOG screen

   set log [ text .row6.llog -width 80 -height 15 -borderwidth 4 -relief groove \
     -yscrollcommand { .row6.scroll set } -setgrid true ]
   scrollbar .row6.scroll -command { .row6.llog yview }
   
   pack .row6.llog   -side left  -fill both -expand true -padx 2 -pady 2m
   pack .row6.scroll -side right -fill y                 -padx 2 -pady 2m
   
   # Final packing up
   
   pack .row1 .row2 .row3 .row4 .row5 .row6 -side top -fill x
}

init
run
   


Additions in version 005

The main differences for version 005 are:

  1. When no 'list' files are present, the gack will ask for the name of the main file and prime the switches list with a '-w'
  2. An 'askfile' procedure was added to help opion 'I'
  3. To make sure the right executable name was derived, a 'vwait' command was added
So, here are the new procedures. You can get a full gack.tcl file in the download section.
proc askfile { } {

   global mainfile fontf10 fontf12 modules
   toplevel .rx
   wm title .rx "Ask for a module name"
	    
   label .rx.conf  -text "Please enter the name of the main source " -font $fontf12
   entry .rx.entry -width 40 -relief sunken -borderwidth 3 -textvariable mainfile -font $fontf12
   button .rx.butt -text Accept -borderwidth 2 -bg pink    -font $fontf12 \
     -command {
      set modules [list $mainfile]
      destroy .rx
   } 

   pack .rx.conf .rx.entry .rx.butt -side left -padx 2m -pady 5m
}
   
askfile opens a new popup screen. In it, it asks for the name of the main file (for example 'Plov030.mod'). When it is done (by pressing 'Accept') the variable is handed over to the main routine and the gack starts out. askfile is started from the init section:
proc init { } {
   
   global modules switches fname mainfile

   if  {[file exists modules.list] == 1} {	;# if file exists,
      set fileID [open modules.list r]		;# open it and read it
      while { [ gets $fileID line] > 0 } {
	 lappend modules $line
      }
      close $fileID
   } else {					;# otherwise, 
      askfile					;# ask for a filename
      vwait modules				;# and wait for the askfile window to close
   }

   if {[file exists switches.list] == 1} {
      set fileID [open switches.list r]
      while { [ gets $fileID line] > 0 } {
	 lappend switches $line
      }
      close $fileID
   } else {
      lappend switches "-w"
   }

   set name [lindex $modules 0]
   set nr [string last . $name]
   incr nr -1
   set fname [string range $name 0 $nr]
}
   
For the rest, versions 4 and 5 are comparable.


Page created on 27 September 2010 and