Index: tcl053.js
==================================================================
--- tcl053.js
+++ tcl053.js
@@ -9,27 +9,30 @@
  * Richard Suchenwirth 2007, 2013: cleanup, additions
  * vim: syntax=javascript autoindent softtabwidth=4
  */
 // 'use strict'; // breaks some tests, like expr 0376, for loop
 var _step = 0; // set to 1 for debugging
-var fs = require('fs');
-var puts = console.log;
+if(process.env["DEBUG"] == 1) _step = 1;
+var fs    = require('fs');
+var puts  = console.log; // saves a lot of typing... ;^)
 
 function TclInterp () {
     this.patchlevel = "0.5.3";
     this.callframe  = [{}];
     this.level      = 0;
     this.levelcall  = [];
     this.commands   = {};
     this.procs      = [];
     this.script     = "";
+    this.getsing    = 0;
     this.OK  = 0;
     this.RET = 1;
     this.BRK = 2;
     this.CNT = 3;
     this.getVar = function(name) {
-        var nm = name.toString();
+        var nm  = name.toString();
+	// no arrays supported yet, but a read-only exception for ::env()
         if (nm.match("^::env[(]")) nm = nm.substr(2);
         if (nm.match("^env[(]")) {
             var key = nm.substr(4,nm.length-5);
             var val = process.env[key];
         } else if (nm.match("^::")) {
@@ -39,23 +42,24 @@
         }
         if (val == null) throw 'can\'t read "'+name+'": no such variable';
         return val;
     }
     this.setVar = function(name, val) {
-        var nm = name.toString();
-        if (nm.match("^::")) {
-            this.callframe[0][nm.substr(2)] = val;
-        } else {this.callframe[this.level][name] = val;}
-        return val;
+      var nm  = name.toString();
+      if (val != null && val.toString().match(/\\/))
+	val = eval("'"+val.toString()+"'");
+      if (nm.match("^::")) {
+	this.callframe[0][nm.substr(2)] = val;
+      } else {this.callframe[this.level][name] = val;}
+      return val;
     }
     this.setVar("argc",  process.argv.length-2);
     this.setVar("argv0", process.argv[1]);
     this.setVar("argv",  process.argv.slice(2));
     this.setVar("errorInfo", "");
 
     this.incrLevel = function() {
-      //puts("going down from ("+this.level+"): "+this.levelcall);
       this.callframe[++this.level] = {};
       return this.level;
     }
     this.decrLevel = function() {
       this.callframe[this.level] = null;
@@ -104,10 +108,11 @@
 	if(msg.length >= 125) msg += "...";
 	var msg = e+'\n        while executing\n"'+msg+'"';
 	for(var i = this.level; i > 0; i--)
 	  msg += '\n        invoked from within\n"'+this.levelcall[i]+'"'
 	this.setVar("::errorInfo", msg);
+	if(_step) puts("e: "+e);
 	throw e;
       }
     }
     this.eval2 = function(code) {
       this.code  = this.OK;
@@ -170,10 +175,15 @@
       }
       if (args.length > 0) result = this.call(args);
       return this.objectify(result);
     }
     //---------------------------------- Commands in alphabetical order
+    /*this.registerCommand("after", function (interp, args) {
+        this.arity(args, 3);
+	var code = args[2].toString();
+	setTimeout(args[1], function(code) {interp.eval(code)});
+	});*/
     this.registerCommand("append", function (interp, args) {
         this.arity(args, 2, Infinity);
         var vname = args[1].toString();
 	try {var str = interp.getVar(vname);} catch(e) {var str = "";}
 	for (var i = 2; i < args.length; i++) str += args[i].toString();
@@ -204,12 +214,14 @@
         interp.code = interp.CNT;
         return;
       });
     this.registerSubCommand("clock", "format", function (interp, args) {
         var now = new Date();
-        now.setTime(args[1]);
-        return now.toString();
+        now.setTime(args[1]*1000);
+        var ts = now.toString().split(" ");
+	var tz = ts[6].toString().replace("(","").replace(")","");
+	return ts[0]+" "+ts[1]+" "+ts[2]+" "+ts[4]+" "+tz+" "+ts[3];
       });
     this.registerSubCommand("clock", "milliseconds", function (interp, args) {
 	var t = new Date();
 	return t.valueOf();
       });
@@ -358,30 +370,71 @@
     this.registerCommand("expr", function (interp, args) {
 	var expression = args.slice(1).join(" ");
 	return interp.expr(interp, expression);
       });
     this.expr = function (interp, expression) { // also used in for, if, while
+      var mx;
       try {
-	var mx = expression.match(/(\[.*\])/g);
+	mx = expression.match(/(\[.*\])/g);
 	for (var i in mx)
 	  puts("have to deal with "+mx[i].toString());
       } catch(e) {puts(i+". exception: "+e);}
       mx = expression.match(/(\$[A-Za-z0-9_:]+)/g);
-      for (i in mx) {
+      for (var i in mx) {
 	var val = interp.getVar(mx[i].slice(1)).toString();
 	if(isNaN(val) || !isFinite(val)) val = '"'+val+'"';
 	eval("var "+mx[i]+' = '+val);
       }
       var res = eval(expression);
       if(res == false) res = 0; else if(res == true) res = 1;
       return res;
     };
+    this.registerSubCommand("file", "atime", function (interp, args) {
+        this.arity(args, 2);
+	var stat = fs.statSync(args[1].toString());
+	return stat.atime.getTime()/1000;
+      })
     this.registerSubCommand("file", "dirname", function (interp, args) {
         this.arity(args, 2);
-	var path = args[1].toString().split("/");
-	path.pop();
-	return path.join("/");
+	return interp.dirname(args[1].toString());
+     });
+    this.dirname = function(p) { // also used in [glob]
+      //require("path"); //not working :(
+      //return path.dirname(p.toString());
+      if(p == ".") p = process.cwd();
+      p = p.split("/"); 
+      p.pop();
+      if(p == "") return("/");
+      return p.join("/");
+    };
+    this.registerSubCommand("file", "exists", function (interp, args) {
+        this.arity(args, 2);
+	var file = args[1].toString();
+	try {var fd = fs.openSync(file,"r");} catch(e) {return 0;}
+	fs.closeSync(fd);
+	return 1;
+     });
+    this.registerSubCommand("file", "extension", function (interp, args) {
+        this.arity(args, 2);
+	var fn  = args[1].toString();
+	var res = fn.split(".").pop();
+	res = (res == fn)? "" : "."+res;
+	return res;
+     });
+    this.registerSubCommand("file", "mtime", function (interp, args) {
+        this.arity(args, 2);
+	var stat = fs.statSync(args[1].toString());
+	return stat.mtime.getTime()/1000;
+      })
+    this.registerSubCommand("file", "size", function (interp, args) {
+        this.arity(args, 2);
+	var stat = fs.statSync(args[1].toString());
+	return stat.size;
+      })
+    this.registerSubCommand("file", "tail", function (interp, args) {
+        this.arity(args, 2);
+	return args[1].toString().split("/").pop();
       });
     this.registerCommand("for", function (interp, args) {
         this.arity(args, 5);
         interp.eval(args[1].toString());
         if(interp.code != interp.OK) return;
@@ -432,34 +485,58 @@
 	  return x.toString(16);
 	} else if(fmt=="%X") {
 	  var x = new Number(val);
 	  return x.toString(16).toUpperCase();
 	}
-	else throw "unknown format";
-	
-      });
-    /*
-    this.registerCommand("gets", function (interp, args) {
-        this.arity(args, 2, 3);
-	this.tmp = null;
-	rl.on('line', itp.gets);
-	while(true) {
-	  if(this.tmp != null) break;
-	};
-	rl.on('line', this.getsevalputs);
-        var reply = this.tmp;
-        // = prompt(args[1],"");
-        //process.stdin.resume();
-        //process.stdin.on('data', function(str) {
-        //reply = str;
-        //    });
-        if(args[2] != null) {
-	  interp.setVar(args[2],interp.objectify(reply));
-	  return reply.length;
-        } else return reply;
-      });
-    */
+	else throw "unknown format";	
+      });
+   this.registerCommand("gets0", function (interp, args) {
+	this.arity(args, 2, 3);
+	interp.getsing = 1;
+	//interp.buf = "";
+	//while(interp.buf == "") {
+	//interp.timeout = setTimeout(function(){}, 10000);
+	//  if(interp.getsing==0) break;
+	//}
+	return; // result will be in interp.buf when done
+     });
+   this.gets = function(char) {
+     try {
+       if(char.match(/foo[\r\n]/)) {
+	 this.getsing = 0;
+	 puts("received: "+this.buf);
+       } else {
+	 puts("<"+char+">"+this.getsing);
+	 this.buf += char;
+       }
+     } catch(e) {puts(e)};
+   }
+   this.registerCommand("glob", function (interp, args) {
+	this.arity(args, 2, Infinity);
+	args.shift();
+	var res    = [];
+	var prefix = "";
+	var dir    = ".";
+	for (var arg in args) {
+	  var path    = args[arg].toString();
+	  if(path.match("[/]")) {
+	     var dir    = interp.dirname(path);
+	     var prefix = dir+"/";
+	  }
+	  var pattern = path.split("/").pop().replace(/[*]/g,".*");
+	  var files   = fs.readdirSync(dir);
+	  for (var i in files) {
+	    if(files[i].match("^[.]")) continue;
+	    if(files[i] == pattern) {res.push(files[i]);}
+	    if(files[i].match("^"+pattern+"$")) {
+	      var file = (dir == ".")? files[i] : dir+"/"+files[i];
+	      res.push(file.replace(/\/\//,"/"));
+	    }
+	  }
+	}
+	return res;
+      });
     this.registerCommand("if", function (interp, args) {
         this.arity(args, 3, Infinity);
         var cond = args[1].toString();
         var test = interp.objectify(interp.expr(interp, cond));
         if (test.toBoolean()) return interp.eval(args[2].toString());
@@ -537,11 +614,11 @@
     this.registerSubCommand("info", "script", function (interp, args) {
         return interp.script;
     });
     this.registerSubCommand("info", "vars", function (interp, args) {
 	var res = [];
-	for(i in interp.callframe[interp.level]) {
+	for(var i in interp.callframe[interp.level]) {
 	  try {
 	    if(interp.getVar(i) != null) {res.push(i);}
 	  } catch(e) {};
 	}
 	return res;
@@ -599,13 +676,13 @@
         this.arity(args, 2);
         return args[1].toList().length;
       });
     this.registerCommand("lrange", function (interp, args) {
         this.arity(args, 4);
-        var list    = interp.objectify(args[1]);
+        var list  = interp.objectify(args[1]);
         var start = list.listIndex(args[2]);
-        var end     = list.listIndex(args[3])+1;
+        var end   = list.listIndex(args[3])+1;
         try {
 	  return list.content.slice(start, end);
         } catch (e) {return [];}
       });
     this.registerCommand("lreverse", function (interp, args) {
@@ -693,15 +770,16 @@
     });
     this.registerCommand("source", function (interp, args) {
         this.arity(args, 2);
         interp.script = args[1].toString();
         try {
-	  var data = fs.readFileSync(interp.script).toString();
+	  var data = fs.readFileSync(interp.script,{encoding: 'utf8'}).toString();
         } catch(e) {
+	  puts("e: "+e);
 	  throw 'couldn\' read file "'+interp.script
 	    +'": no such file or directory';}
-	var res    = interp.eval(data);
+	var res       = interp.eval(data);
 	interp.script = "";
 	return res;
       });
     this.registerCommand("split", function (interp, args) {
         this.arity(args, 2, 3);
@@ -886,11 +964,11 @@
 Tcl.isOctal    = new RegExp("^[+\\-]?0[0-7]*$");
 Tcl.isHexSeq   = new RegExp("[0-9a-fA-F]*");
 Tcl.isOctalSeq = new RegExp("[0-7]*");
 Tcl.isList     = new RegExp("[\\{\\} ]");
 Tcl.isNested   = new RegExp("^\\{.*\\}$");
-Tcl.getVar     = new RegExp("^[a-zA-Z0-9_]+", "g");
+Tcl.getVar     = new RegExp("^[:a-zA-Z0-9_]+", "g");
 
 Tcl.Proc = function (interp, args) {
    var priv = this.privdata;
    interp.incrLevel();
    var arglist = priv[0].toList();
@@ -922,11 +1000,11 @@
            break;
        }
        interp.setVar(name, interp.objectify(args[i]));
    }
    if (name == "args" && i+1 < arglist.length)
-       throw "'args' should be the last argument";
+     throw "'args' should be the last argument";
    try {
        var res = interp.eval(body);
        interp.code = interp.OK;
        interp.decrLevel();
        return res;
@@ -942,12 +1020,11 @@
   args[0]  = main;
   var ens  = this.ensemble;
   if (ens == null) {
     throw "Not an ensemble command: "+main;
   } else if (ens[sub] == null) {
-    
-    var matches   = 0, lastmatch = "";
+    var matches = 0, lastmatch = "";
     for (var i in ens) { // maybe unambiguous prefix?
       if (i.match("^"+sub) != null) {
 	matches  += 1;
 	lastmatch = i;
       } 
@@ -1107,13 +1184,13 @@
   this.func     = func;
   this.privdata = privdata;
   this.ensemble = arguments[2];
   
   this.call = function(interp, args) {
-    var r = (this.func)(interp, args);
-    r = interp.objectify(r);
-    return r;
+    var res = (this.func)(interp, args);
+    res = interp.objectify(res);
+    return res;
   }
   this.arity = function (args, min, max) {
     if(max == undefined) max = min;
     if (args.length < min || args.length > max) {
       throw min + ".."+max + " words expected, got "+args.length;
@@ -1139,30 +1216,30 @@
   this.cur         = this.text.charAt(0);
   this.getText     = function () {
     return this.text.substring(this.start,this.end+1);
   }
   this.parseString = function () {
-    var newword = (this.type==this.SEP ||
+    var newword = (this.type == this.SEP ||
 		   this.type == this.EOL || this.type == this.STR);
     if (newword && this.cur == "{") return this.parseBrace();
     else if (newword && this.cur == '"') {
       this.insidequote = true;
       this.feedchar();
     }
     this.start = this.index;
     while (true) {
       if (this.len == 0) {
-	this.end = this.index-1;
+	this.end  = this.index-1;
 	this.type = this.ESC;
 	return this.OK;
       }
       /*if (this.cur == "\\") { // works not :(
 	if (this.len >= 2) this.feedSequence();
 	}
 	else*/ if ("$[ \t\n\r;".indexOf(this.cur)>=0) {
 	if ("$[".indexOf(this.cur)>=0 || !this.insidequote) {
-	  this.end = this.index-1;
+	  this.end  = this.index-1;
 	  this.type = this.ESC;
 	  return this.OK;
 	}
       }
       else if (this.cur == '"' && this.insidequote) {
@@ -1175,11 +1252,11 @@
       this.feedchar();
     }
     return this.OK;
   }
   this.parseList = function () {
-    var level = 0;
+    var level  = 0;
     this.start = this.index;
     while (true) {
       if (this.len == 0) {
 	this.end  = this.index;
 	this.type = this.EOL;
@@ -1301,12 +1378,11 @@
 	this.parseComment();
 	continue;
       }
       return this.parseString();
     }
-    puts("unreachable?");
-    return this.OK; // unreached
+    //return this.OK; // unreached
   }
   this.feedSequence = function () {
     //return;
     if (this.cur != "\\") throw "Invalid escape sequence";
     var cur = this.steal(1);
@@ -1362,14 +1438,14 @@
   this.feedcharstart = function () {
     this.feedchar();
     this.start = this.index;
   }
   this.setPos = function (index) {
-    var d = index-this.index;
+    var d      = index-this.index;
     this.index = index;
-    this.len -= d;
-    this.cur = this.text.charAt(this.index);
+    this.len  -= d;
+    this.cur   = this.text.charAt(this.index);
   }
   this.feedchar = function () {
     this.index++;
     this.len--;
     if (this.len < 0)
@@ -1382,24 +1458,21 @@
 var res;
 process.argv.slice(2).forEach(function(cmd,index,array) {
        itp.eval(cmd);
      });
 var readline = require('readline');
-var rl = readline.createInterface(process.stdin, process.stdout);
+var rl       = readline.createInterface(process.stdin, process.stdout);
 rl.setPrompt('% ');
 rl.prompt();
-itp.getsevalputs = function(line) {
+itp.gets = function(line) {
+  if (itp.getsing == 0) {
     try {
       res = itp.eval(line.trim());
     } catch(e) {res = e;}
-    if(res != null && res.toString() != "" && res.toString().length) 
-      puts(res.toString());
-    rl.prompt();
+    if (itp.getsing == 0) {
+      if(res != null && res.toString().length) 
+	puts(res.toString());
+      rl.prompt();
+    }
+  } else {itp.buf = line; itp.getsing = 0; rl.prompt();}
 };
-itp.gets = function(line) {
-  puts("received "+line); 
-  this.tmp = line;
-}
-rl.on('line', itp.getsevalputs
-).on('close',function() {
-    process.exit(0);
-  });
+rl.on('line', itp.gets).on('close',function() {process.exit(0);});

Index: test_tcljs.tcl
==================================================================
--- test_tcljs.tcl
+++ test_tcljs.tcl
@@ -6,18 +6,19 @@
 set vars [info vars] ;# for later cleanup
 set version 0.5.3
 set total   0
 set passed  0
 set fail    0
-puts "------------------------ [info script] patchlevel: [info patchlevel]"
+puts "----- [info script] of [clock format [file mtime [info script]]], patchlevel: [info patchlevel]"
 
 proc e.g. {cmd -> expected} {
+    #puts $cmd
     incr ::total
     incr ::fail ;# to also count exceptions
     set res [uplevel 1 $cmd]
     if ![string equal $res $expected] {
-    #if {$res != $expected} {} #should work, but wouldn't
+	#if {$res != $expected} {}
 	puts "**** $cmd -> $res, expected: $expected"
     } else {incr ::passed; incr ::fail -1}
 }
 
 # e.g. {exec echo hello} -> hello ;# needs blocking exec
@@ -26,19 +27,20 @@
 e.g. {set x foo}        -> foo
 e.g. {append x bar}     -> foobar
 
 proc sum args {expr [join $args +]}
 e.g. {sum 1 2 3} -> 6
-#native sum2 {function (interp, args) {return eval(args.join("+"));}}
-#e.g. {sum2 2 3 4} -> 9
 
-e.g. {catch foo msg} -> 1
-e.g. {set msg} -> {invalid command name "foo"}
+e.g. {catch foo msg}    -> 1
+e.g. {set msg}          -> {invalid command name "foo"}
 e.g. {catch {expr 7*6}} -> 0
 e.g. {catch {expr 7*6} msg; set msg} -> 42
 
+e.g. {clock format 0}   -> {Thu Jan 01 01:00:00 CET 1970}
+
 e.g. {concat {a b} {c d}} -> {a b c d}
+e.g. {concat $::version}  -> $version
 
 e.g. {set d [dict create a 1 b 2 c 3]} -> {a 1 b 2 c 3}
 e.g. {dict exists $d c} -> 1
 e.g. {dict exists $d x} -> 0
 e.g. {dict get $d b}    -> 2
@@ -49,20 +51,22 @@
 e.g. {dict unset d x}   -> {a 1 c 3}
 e.g. {dict unset d nix} -> {a 1 c 3}
 e.g. {dict set dx a 1}  -> {a 1} ;# create new dict if not exists
 
 e.g. {set home [file dirname [pwd]]; list} -> {}
-e.g. {string equal [set env(HOME)] $home}   -> 1
+e.g. {string equal [set env(HOME)] $home}  -> 1
+# e.g. {string equal $::env(HOME) $home}  -> 1
 e.g. {string equal [set ::env(HOME)] $home} -> 1
+e.g. {file dirname /foo/bar/grill}          -> /foo/bar
+e.g. {file tail    /foo/bar/grill}          -> grill
 
 e.g. {expr 6*7}         -> 42
 e.g. {expr {6 * 7 + 1}} -> 43
 e.g. {set x 43}         -> 43
 e.g. {expr {$x-1}}      -> 42
 e.g. {expr $x-1}        -> 42
 if ![info exists auto_path] { ;#these tests are not for a real tclsh
-    e.g. {clock format 0} -> {Thu Jan 01 1970 01:00:00 GMT+0100 (CET)}
     e.g. {set i [expr 1/0]} -> Infinity
     e.g. {expr $i==$i+42}   -> 1
     e.g. {set n [expr sqrt(-1)]} -> NaN
     e.g. {expr $n == $n} -> 0
     e.g. {expr $n==$n}   -> 0
@@ -92,10 +96,16 @@
 e.g. {expr {$x != $y}} -> 1
 e.g. {expr 43 % 5}     -> 3 
 e.g. {set x -44; expr {-$x}} -> 44
 e.g. {expr 1<<3} -> 8
 
+e.g. {file dirname foo/bar/grill}  -> foo/bar
+e.g. {file dirname /foo/bar/grill} -> /foo/bar
+e.g. {file extension foo.txt}      -> .txt
+e.g. {file extension Makefile}     -> ""
+e.g. {file tail foo/bar/grill}     -> grill
+
 set forres ""
 e.g. {for {set i 0} {$i < 5} {incr i} {append forres $i}; set forres} -> 01234
 e.g. {foreach i {a b c d e} {append foreachres $i}; set foreachres}   -> abcde
 
 e.g. {format %x 255} -> ff
@@ -104,18 +114,18 @@
 e.g. {set x 41}  -> 41
 e.g. {incr x}    -> 42
 e.g. {incr x 2}  -> 44
 e.g. {incr x -3} -> 41
 
-e.g. {info args e.g.} -> {cmd -> expected}
+e.g. {info args e.g.}        -> {cmd -> expected}
 e.g. {unset -nocomplain foo} -> {}
-e.g. {info exists foo} -> 0
-e.g. {set foo 42}      -> 42
-e.g. {info exists foo} -> 1
-e.g. {info level}      -> 0 ;# e.g. runs the command one level up
+e.g. {info exists foo}       -> 0
+e.g. {set foo 42}            -> 42
+e.g. {info exists foo}       -> 1
+e.g. {info level}            -> 0 ;# e.g. runs the command one level up
 e.g. {proc f x {set y 0; info vars}} -> ""
-e.g. {f 41}            -> {x y}
+e.g. {f 41}                          -> {x y}
 set tmp [f 40]; e.g. {lappend tmp z} -> {x y z}
 e.g. {info args f}      -> x
 e.g. {info body f}      -> {set y 0; info vars}
 e.g. {info bod f}       -> {set y 0; info vars}
 
@@ -140,10 +150,11 @@
 
 e.g. {proc f args {expr [join $args +]}} -> ""
 e.g. {f 1}     -> 1
 e.g. {f 1 2}   -> 3
 e.g. {f 1 2 3} -> 6
+e.g. {proc f {arg b} {expr $arg*$b}; f 6 7} -> 42 ;# should work with 'args'
 
 e.g. {regexp {X[ABC]Y} XAY}    -> 1
 e.g. {regexp {X[ABC]Y} XDY}    -> 0
 e.g. {regsub {[A-C]+} uBAAD x} -> uxD 
 
@@ -152,30 +163,32 @@
 e.g. {split "a b  c d "}     -> {a b {} c d {}}
 e.g. {split usr/local/bin /} -> {usr local bin}
 e.g. {split /usr/local/bin /} -> {{} usr local bin}
 e.g. {split abc ""}          -> {a b c}
 
-e.g. {string compare a b}     -> -1
-e.g. {string compare b a}     -> 1
-e.g. {string compare b b}     -> 0
-e.g. {string equal foo foo}   -> 1
-e.g. {string equal foo bar}   -> 0
-e.g. {string index abcde 2}   -> c
-e.g. {string length ""}       -> 0
-e.g. {string length foo}      -> 3
-e.g. {string range hello 1 3} -> ell
-e.g. {string tolower Tcl}     -> tcl
-e.g. {string toupper Tcl}     -> TCL
-e.g. {string trim " foo "}    -> foo
-
-e.g. {set x a.\x62.c} -> a.b.c ;# severe malfunction, breaks test suite operation :(
+e.g. {string compare a b}       -> -1
+e.g. {string compare b a}       -> 1
+e.g. {string compare b b}       -> 0
+e.g. {string equal foo foo}     -> 1
+e.g. {string equal foo bar}     -> 0
+e.g. {string index abcde 2}     -> c
+e.g. {string length ""}         -> 0
+e.g. {string length foo}        -> 3
+e.g. {string range hello 1 3}   -> ell
+e.g. {string range hello 1 end} -> ello
+e.g. {string tolower Tcl}       -> tcl
+e.g. {string toupper Tcl}       -> TCL
+e.g. {string trim " foo "}      -> foo
+
+e.g. {set x a.\x62.c} -> a.b.c
+e.g. {set e \u20ac} -> "€" ;# breaks in node v0.6.19, works in v0.10.22
+
 
 puts "total $total tests, passed $passed, failed $fail"
 #----------- clean up variables used in tests
 foreach var [info vars] {
     set pos [lsearch $vars $var] ;# expr can't substitute commands yet
-    set neq [string compare $var vars]
     if {$var != "vars" && $pos < 0} {unset $var}
 }
-unset vars var pos neq
+unset vars var pos
 puts "vars now: [info vars]"
 puts "[llength [info commands]] commands implemented"