ref: babf901b4a508c3ec5d1f89655f10377bbdf9637
dir: /appl/lib/tcl_core.b/
implement Tcl_Core;
# these are the outside modules, self explanatory..
include "sys.m";
sys: Sys;
include "draw.m";
draw: Draw;
include "bufio.m";
bufmod : Bufio;
Iobuf : import bufmod;
include "string.m";
str : String;
include "tk.m";
tk: Tk;
include "wmlib.m";
wmlib: Wmlib;
# these are stand alone Tcl libraries, for Tcl pieces that
# are "big" enough to be called their own.
include "tcl.m";
include "tcllib.m";
include "utils.m";
htab: Str_Hashtab;
mhtab : Mod_Hashtab;
shtab : Sym_Hashtab;
stack : Tcl_Stack;
utils : Tcl_Utils;
Hash: import htab;
MHash : import mhtab;
SHash : import shtab;
# global error flag and message. One day, this will be stack based..
errmsg : string;
error, mypid : int;
sproc : adt {
name : string;
args : string;
script : string;
};
TCL_UNKNOWN, TCL_SIMPLE, TCL_ARRAY : con iota;
# Global vars. Simple variables, and associative arrays.
libmods : ref MHash;
proctab := array[100] of sproc;
retfl : int;
symtab : ref SHash;
nvtab : ref Hash;
avtab : array of (ref Hash,string);
tclmod : TclData;
core_commands:=array[] of {
"append" , "array", "break" , "continue" , "catch", "dumpstack",
"exit" , "expr" , "eval" ,
"for" , "foreach" ,
"global" , "if" , "incr" , "info",
"lappend" , "level" , "load" ,
"proc" , "return" , "set" ,
"source" ,"switch" , "time" ,
"unset" , "uplevel", "upvar", "while" , "#"
};
about() : array of string {
return core_commands;
}
init(ctxt: ref Draw->Context, argv: list of string) {
sys = load Sys Sys->PATH;
draw = load Draw Draw->PATH;
bufmod = load Bufio Bufio->PATH;
htab = load Str_Hashtab Str_Hashtab->PATH;
mhtab = load Mod_Hashtab Mod_Hashtab->PATH;
shtab = load Sym_Hashtab Sym_Hashtab->PATH;
stack = load Tcl_Stack Tcl_Stack->PATH;
str = load String String->PATH;
utils = load Tcl_Utils Tcl_Utils->PATH;
tk = load Tk Tk->PATH;
wmlib= load Wmlib Wmlib->PATH;
if (bufmod == nil || htab == nil || stack == nil ||
str == nil || utils == nil || tk == nil ||
wmlib==nil || mhtab == nil || shtab == nil){
sys->print("can't load initial modules %r\n");
exit;
}
# get a new stack frame.
stack->init();
(nvtab,avtab,symtab)=stack->newframe();
libmods=mhtab->alloc(101);
# grab my pid, and set a new group to make me easy to kill.
mypid=sys->pctl(sys->NEWPGRP, nil);
# no default top window.
tclmod.top=nil;
tclmod.context=ctxt;
tclmod.debug=0;
# set up library modules.
args:=array[] of {"do_load","io"};
do_load(args);
args=array[] of {"do_load","string"};
do_load(args);
args=array[] of {"do_load","calc"};
do_load(args);
args=array[] of {"do_load","list"};
do_load(args);
args=array[] of {"do_load","tk"};
do_load(args);
arr:=about();
for(i:=0;i<len arr;i++)
libmods.insert(arr[i],nil);
# cmd line args...
if (argv != nil)
argv = tl argv;
while (argv != nil) {
loadfile(hd argv);
argv = tl argv;
}
}
set_top(win:ref Tk->Toplevel){
tclmod.top=win;
}
clear_error(){
error=0;
errmsg="";
}
notify(num : int,s : string) : string {
error=1;
case num{
1 =>
errmsg=sys->sprint(
"wrong # args: should be \"%s\"",s);
* =>
errmsg= s;
}
return errmsg;
}
grab_lines(new_inp,unfin: string ,lines : chan of string){
error=0;
tclmod.lines=lines;
input,line : string;
if (new_inp==nil)
new_inp = "tcl%";
if (unfin==nil)
unfin = "tcl>";
sys->print("%s ", new_inp);
iob := bufmod->fopen(sys->fildes(0),bufmod->OREAD);
if (iob==nil){
sys->print("cannot open stdin for reading.\n");
return;
}
while((input=iob.gets('\n'))!=nil){
line+=input;
if (!finished(line,0))
sys->print("%s ", unfin);
else{
lines <- = line;
line=nil;
}
}
}
# this is the main function. Its input is a complete (i.e. matching
# brackets etc) tcl script, and its output is a message - if there
# is one.
evalcmd(s: string, termchar: int) : string {
msg : string;
i:=0;
retfl=0;
if (tclmod.debug==2)
sys->print("Entered evalcmd, s=%s, termchar=%c\n",s,termchar);
# strip null statements..
while((i<len s) && (s[i]=='\n' || s[i]==';')) i++;
if (i==len s) return nil;
# parse the script statement by statement
for(;s!=nil;i++){
# wait till we have a complete statement
if (i==len s || ((s[i]==termchar || s[i]==';' || s[i]=='\n')
&& finished(s[0:i],termchar))){
# throw it away if its a comment...
if (s[0]!='#')
argv := parsecmd(s[0:i],termchar,0);
msg = nil;
if (tclmod.debug==2)
for(k:=0;k<len argv;k++)
sys->print("argv[%d]: (%s)\n",k,argv[k]);
# argv is now a completely parsed array of arguments
# for the Tcl command..
# find the module that the command is in and
# execute it.
if (len argv != 0){
mod:=lookup(argv[0]);
if (mod!=nil){
(error,msg)=
mod->exec(ref tclmod,argv);
if (error)
errmsg=msg;
} else {
if (argv[0]!=nil &&
argv[0][0]=='.')
msg=do_tk(argv);
else
msg=exec(argv);
}
}
# was there an error?
if (error) {
if (len argv > 0 && argv[0]!=""){
stat : string;
stat = "In function "+argv[0];
if (len argv >1 && argv[1]!=""){
stat[len stat]=' ';
stat+=argv[1];
}
stat+=".....\n\t";
errmsg=stat+errmsg;
}
msg=errmsg;
}
# we stop parsing if we hit a break, continue, return,
# error, termchar or end of string.
if (msg=="break" || msg=="continue" || error || retfl==1
|| len s <= i || (len s > i && s[i]==termchar))
return msg;
# otherwise eat up the parsed statement and continue
s=s[i+1:];
i=-1;
}
}
return msg;
}
# returns 1 if the line has matching braces, brackets and
# double-quotes and does not end in "\\\n"
finished(s : string, termchar : int) : int {
cb:=0;
dq:=0;
sb:=0;
if (s==nil) return 1;
if (termchar=='}') cb++;
if (termchar==']') sb++;
if (len s > 1 && s[len s -2]=='\\')
return 0;
if (s[0]=='{') cb++;
if (s[0]=='}' && cb>0) cb--;
if (s[0]=='[') sb++;
if (s[0]==']' && sb>0) sb--;
if (s[0]=='"') dq=1-dq;
for(i:=1;i<len s;i++){
if (s[i]=='{' && s[i-1]!='\\') cb++;
if (s[i]=='}' && s[i-1]!='\\' && cb>0) cb--;
if (s[i]=='[' && s[i-1]!='\\') sb++;
if (s[i]==']' && s[i-1]!='\\' && sb>0) sb--;
if (s[i]=='"' && s[i-1]!='\\') dq=1-dq;
}
return (cb==0 && sb==0 && dq==0);
}
# counts the offset till the next matching ']'
strip_to_match(s : string, ptr: int) : int {
j :=0;
nb:=0;
while(j<len s){
if (s[j]=='{')
while (j < len s && s[j]!='}') j++;
if (s[j]=='[') nb++;
if (s[j]==']'){
nb--;
if (nb==-1) return ptr+j;
}
j++;
}
return ptr+j;
}
# returns the type of variable represented by the string s, which is
# a name.
isa(s: string) : (int,int,string) {
found,val : int;
name,al : string;
curlev:=stack->level();
if (tclmod.debug==2)
sys->print("Called isa with %s, current stack level is %d\n",s,curlev);
(found,nil)=nvtab.find(s);
if (found) return (TCL_SIMPLE,curlev,s);
for (i:=0;i<len avtab;i++){
(nil,name)=avtab[i];
if (name==s) return (TCL_ARRAY,curlev,s);
}
if (symtab==nil)
return (TCL_UNKNOWN,curlev,s);
(found,val,al)=symtab.find(s);
if (!found)
return (TCL_UNKNOWN,curlev,s);
(tnv,tav,nil):=stack->examine(val);
if (tclmod.debug==2)
sys->print("have a level %d for %s\n",val,al);
if (tnv!=nil){
(found,nil)=tnv.find(al);
if (found) return (TCL_SIMPLE,val,al);
}
if (tav!=nil){
for (i=0;i<len tav;i++){
(nil,name)=tav[i];
if (name==al) return (TCL_ARRAY,val,al);
}
}
if (tclmod.debug==2)
sys->print("%s not found, creating at stack level %d\n",al,val);
return (TCL_UNKNOWN,val,al);
}
# This function only works if the string is already parsed!
# takes a var_name and returns the hash table for it and the
# name to look up. This is one of two things:
# for simple variables:
# findvar(foo) ---> (nvtab,foo)
# for associative arrays:
# findvar(foo(bar)) -----> (avtab[i],bar)
# where avtab[i].name==foo
# if create is 1, then an associative array is created upon first
# reference.
# returns (nil,error message) if there is a problem.
find_var(s : string,create : int) : (ref Hash,string) {
rest,name,index : string;
retval,tnv : ref Hash;
tav : array of (ref Hash,string);
i,tag,lev: int;
(name,index)=str->splitl(s,"(");
if (index!=nil){
(index,rest)=str->splitl(index[1:],")");
if (rest!=")")
return (nil,"bad variable name");
}
(tag,lev,name) = isa(name);
case tag {
TCL_SIMPLE =>
if (index!=nil)
return (nil,"variable isn't array");
(tnv,nil,nil)=stack->examine(lev);
return (tnv,name);
TCL_ARRAY =>
if (index==nil)
return (nil,"variable is array");
(nil,tav,nil)=stack->examine(lev);
for(i=0;i<len tav;i++){
(retval,rest)=tav[i];
if (rest==name)
return (retval,index);
}
return (nil,"find_var: impossible!!");
# if we get here, the variable needs to be
# created.
TCL_UNKNOWN =>
if (!create)
return (nil,"no such variable");
(tnv,tav,nil)=stack->examine(lev);
if (index==nil)
return (tnv,name);
}
# if we get here, we are creating an associative variable in the
# tav array.
for(i=0;i<len tav;i++){
(retval,rest)=tav[i];
if (rest==nil){
retval=htab->alloc(101);
tav[i]=(retval,name);
return (retval,index);
}
}
return (nil,"associative array table full!");
}
# the main parsing function, a la ousterhouts man pages. Takes a
# string that is meant to be a tcl statement and parses it,
# reevaluating and quoting upto the termchar character. If disable
# is true, then whitespace is not ignored.
parsecmd(s: string, termchar,disable: int) : array of string {
argv:= array[200] of string;
buf,nm,id: string;
argc := 0;
nc := 0;
c :=0;
tab : ref Hash;
if (disable && (termchar=='\n' || termchar==';')) termchar=0;
outer:
for (i := 0; i<len s ;) {
if ((i>0 &&s[i-1]!='\\' &&s[i]==termchar)||(s[0]==termchar))
break;
case int s[i] {
' ' or '\t' or '\n' =>
if (!disable){
if (nc > 0) { # end of a word?
argv[argc++] = buf;
buf = nil;
nc = 0;
}
i++;
}
else
buf[nc++]=s[i++];
'$' =>
if (i>0 && s[i-1]=='\\')
buf[nc++]=s[i++];
else {
(nm,id) = parsename(s[i+1:], termchar);
if (id!=nil)
nm=nm+"("+id+")";
(tab,nm)=find_var(nm,0); #don't create var!
if (len nm > 0 && tab!=nil) {
(found, val) := tab.find(nm);
buf += val;
nc += len val;
#sys->print("Here s[i:] is (%s)\n",s[i:]);
if(nm==id)
while(s[i]!=')') i++;
else
if (s[i+1]=='{')
while(s[i]!='}') i++;
else
i += len nm;
if (nc==0 && (i==len s-1 ||
s[i+1]==' ' ||
s[i+1]=='\t'||
s[i+1]==termchar))
argv[argc++]=buf;
} else {
buf[nc++] = '$';
}
i++;
}
'{' =>
if (i>0 && s[i-1]=='\\')
buf[nc++]=s[i++];
else if (s[i+1]=='}'){
argv[argc++] = nil;
buf = nil;
nc = 0;
i+=2;
} else {
nbra := 1;
for (i++; i < len s; i++) {
if (s[i] == '{')
nbra++;
else if (s[i] == '}') {
nbra--;
if (nbra == 0) {
i++;
continue outer;
}
}
buf[nc++] = s[i];
}
}
'[' =>
if (i>0 && s[i-1]=='\\')
buf[nc++]=s[i++];
else{
a:=evalcmd(s[i+1:],']');
if (error)
return nil;
if (nc>0){
buf+=a;
nc += len a;
} else {
argv[argc++] = a;
buf = nil;
nc = 0;
}
i++;
i=strip_to_match(s[i:],i);
i++;
}
'"' =>
if (i>0 && s[i-1]!='\\' && nc==0){
ans:=parsecmd(s[i+1:],'"',1);
#sys->print("len ans is %d\n",len ans);
if (len ans!=0){
for(;;){
i++;
if(s[i]=='"' &&
s[i-1]!='\\')
break;
}
i++;
argv[argc++] = ans[0];
} else {
argv[argc++] = nil;
i+=2;
}
buf = nil;
nc = 0;
}
else buf[nc++] = s[i++];
* =>
if (s[i]=='\\'){
c=unesc(s[i:]);
if (c!=0){
buf[nc++] = c;
i+=2;
} else {
if (i+1 < len s && !(s[i+1]=='"'
|| s[i+1]=='$' || s[i+1]=='{'
|| s[i+1]=='['))
buf[nc++]=s[i];
i++;
}
c=0;
} else
buf[nc++]=s[i++];
}
}
if (nc > 0) # fix up last word if present
argv[argc++] = buf;
ret := array[argc] of string;
ret[0:] = argv[0:argc];
return ret;
}
# parses a name by Tcl rules, a valid name is either $foo, $foo(bar)
# or ${foo}.
parsename(s: string, termchar: int) : (string,string) {
ret,arr,rest: string;
rets : array of string;
if (len s == 0)
return (nil,nil);
if (s[0]=='{'){
(ret,nil)=str->splitl(s,"}");
#sys->print("returning [%s]\n",ret[1:]);
return (ret[1:],nil);
}
loop: for (i := 0; i < len s && s[i] != termchar; i++) {
case (s[i]) {
'a' to 'z' or 'A' to 'Z' or '0' to '9' or '_' =>
ret[i] = s[i];
* =>
break loop;
'(' =>
arr=ret[0:i];
rest=s[i+1:];
rets=parsecmd(rest,')',0);
# should always be len 1?
if (len rets >1)
sys->print("len rets>1 in parsename!\n");
return (arr,rets[0]);
}
}
return (ret,nil);
}
loadfile(file :string) : string {
iob : ref Iobuf;
msg,input,line : string;
if (file==nil)
return nil;
iob = bufmod->open(file,bufmod->OREAD);
if (iob==nil)
return notify(0,sys->sprint(
"couldn't read file \"%s\":%r",file));
while((input=iob.gets('\n'))!=nil){
line+=input;
if (finished(line,0)){
# put in a return catch here...
line = prepass(line);
msg=evalcmd(line,0);
if (error) return errmsg;
line=nil;
}
}
return msg;
}
#unescapes a string. Can do better.....
unesc(s: string) : int {
c: int;
if (len s == 1) return 0;
case s[1] {
'a'=> c = '\a';
'n'=> c = '\n';
't'=> c = '\t';
'r'=> c = '\r';
'b'=> c = '\b';
'\\'=> c = '\\';
'}' => c = '}';
']' => c=']';
# do hex and octal.
* => c = 0;
}
return c;
}
# prepass a string and replace "\\n[ \t]*" with ' '
prepass(s : string) : string {
for(i := 0; i < len s; i++) {
if(s[i] != '\\')
continue;
j:=i;
if (s[i+1] == '\n') {
s[j]=' ';
i++;
while(i<len s && (s[i]==' ' || s[i]=='\t'))
i++;
if (i==len s)
s = s[0:j];
else
s=s[0:j]+s[i+1:];
i=j;
}
}
return s;
}
exec(argv : array of string) : string {
msg : string;
if (argv[0]=="")
return nil;
case (argv[0]) {
"append" =>
msg= do_append(argv);
"array" =>
msg= do_array(argv);
"break" or "continue" =>
return argv[0];
"catch" =>
msg=do_catch(argv);
"debug" =>
msg=do_debug(argv);
"dumpstack" =>
msg=do_dumpstack(argv);
"exit" =>
do_exit();
"expr" =>
msg = do_expr(argv);
"eval" =>
msg = do_eval(argv);
"for" =>
msg = do_for(argv);
"foreach" =>
msg = do_foreach(argv);
"format" =>
msg = do_string(argv);
"global" =>
msg = do_global(argv);
"if" =>
msg = do_if(argv);
"incr" =>
msg = do_incr(argv);
"info" =>
msg = do_info(argv);
"lappend" =>
msg = do_lappend(argv);
"level" =>
msg=sys->sprint("Current Stack "+
"level is %d",
stack->level());
"load" =>
msg=do_load(argv);
"proc" =>
msg=do_proc(argv);
"return" =>
msg=do_return(argv);
retfl =1;
"set" =>
msg = do_set(argv);
"source" =>
msg = do_source(argv);
"string" =>
msg = do_string(argv);
"switch" =>
msg = do_switch(argv);
"time" =>
msg=do_time(argv);
"unset" =>
msg = do_unset(argv);
"uplevel" =>
msg=do_uplevel(argv);
"upvar" =>
msg=do_upvar(argv);
"while" =>
msg = do_while(argv);
"#" =>
msg=nil;
* =>
msg = uproc(argv);
}
return msg;
}
# from here on is the list of commands, alpahabetised, we hope.
do_append(argv :array of string) : string {
tab : ref Hash;
if (len argv==1 || len argv==2)
return notify(1,
"append varName value ?value ...?");
name := argv[1];
(tab,name)=find_var(name,1);
if (tab==nil)
return notify(0,name);
(found, val) := tab.find(name);
for (i:=2;i<len argv;i++)
val+=argv[i];
tab.insert(name,val);
return val;
}
do_array(argv : array of string) : string {
tab : ref Hash;
name : string;
flag : int;
if (len argv!=3)
return notify(1,"array [names, size] name");
case argv[1] {
"names" =>
flag=1;
"size" =>
flag=0;
* =>
return notify(0,"expexted names or size, got "+argv[1]);
}
(tag,lev,al) := isa(argv[2]);
if (tag!=TCL_ARRAY)
return notify(0,argv[2]+" isn't an array");
(nil,tav,nil):=stack->examine(lev);
for (i:=0;i<len tav;i++){
(tab,name)=tav[i];
if (name==al) break;
}
if (flag==0)
return string tab.lsize;
return tab.dump();
}
do_catch(argv : array of string) : string {
if (len argv==1 || len argv > 3)
return notify(1,"catch command ?varName?");
msg:=evalcmd(argv[1],0);
if (len argv==3 && error){
(tab,name):=find_var(argv[2],1);
if (tab==nil)
return notify(0,name);
tab.insert(name, msg);
}
ret:=string error;
error=0;
return ret;
}
do_debug(argv : array of string) : string {
add : string;
if (len argv!=2)
return notify(1,"debug");
(i,rest):=str->toint(argv[1],10);
if (rest!=nil)
return notify(0,"Expected integer and got "+argv[1]);
tclmod.debug=i;
if (tclmod.debug==0)
add="off";
else
add="on";
return "debugging is now "+add+" at level"+ string i;
}
do_dumpstack(argv : array of string) : string {
if (len argv!=1)
return notify(1,"dumpstack");
stack->dump();
return nil;
}
do_eval(argv : array of string) : string {
eval_str : string;
for(i:=1;i<len argv;i++){
eval_str += argv[i];
eval_str[len eval_str]=' ';
}
return evalcmd(eval_str[0:len eval_str -1],0);
}
do_exit(){
kfd := sys->open("#p/"+string mypid+"/ctl", sys->OWRITE);
if(kfd == nil)
sys->print("error opening pid %d (%r)\n",mypid);
sys->fprint(kfd, "killgrp");
exit;
}
do_expr(argv : array of string) : string {
retval : string;
for (i:=1;i<len argv;i++){
retval+=argv[i];
retval[len retval]=' ';
}
retval=retval[0: len retval -1];
argv=parsecmd(retval,0,0);
cal:=lookup("calc");
(err,ret):= cal->exec(ref tclmod,argv);
if (err) return notify(0,ret);
return ret;
}
do_for(argv : array of string) : string {
if (len argv!=5)
return notify(1,"for start test next command");
test := array[] of {"expr",argv[2]};
evalcmd(argv[1],0);
for(;;){
msg:=do_expr(test);
if (msg=="Error!")
return notify(0,sys->sprint(
"syntax error in expression \"%s\"",
argv[2]));
if (msg=="0")
return nil;
msg=evalcmd(argv[4],0);
if (msg=="break")
return nil;
if (msg=="continue"); #do nothing!
evalcmd(argv[3],0);
if (error)
return errmsg;
}
}
do_foreach(argv: array of string) : string{
tab : ref Hash;
if (len argv!=4)
return notify(1,"foreach varName list command");
name := argv[1];
(tab,name)=find_var(name,1);
if (tab==nil)
return notify(0,name);
arr:=utils->break_it(argv[2]);
for(i:=0;i<len arr;i++){
tab.insert(name,arr[i]);
evalcmd(argv[3],0);
}
return nil;
}
do_global(argv : array of string) : string {
if (len argv==1)
return notify(1,"global varName ?varName ...?");
if (symtab==nil)
return nil;
for (i:=1 ; i < len argv;i++)
symtab.insert(argv[i],argv[i],0);
return nil;
}
do_if(argv : array of string) : string {
if (len argv==1)
return notify(1,"no expression after \"if\" argument");
expr1 := array[] of {"expr",argv[1]};
msg:=do_expr(expr1);
if (msg=="Error!")
return notify(0,sys->sprint(
"syntax error in expression \"%s\"",
argv[1]));
if (len argv==2)
return notify(1,sys->sprint(
"no script following \""+
"%s\" argument",msg));
if (msg=="0"){
if (len argv>3){
if (argv[3]=="else"){
if (len argv==4)
return notify(1,
"no script"+
" following \"else\" argument");
return evalcmd(argv[4],0);
}
if (argv[3]=="elseif"){
argv[3]="if";
return do_if(argv[3:]);
}
}
return nil;
}
return evalcmd(argv[2],0);
}
do_incr(argv :array of string) : string {
num,xtra : int;
rest :string;
tab : ref Hash;
if (len argv==1)
return notify(1,"incr varName ?increment?");
name := argv[1];
(tab,name)=find_var(name,0); #doesn't create!!
if (tab==nil)
return notify(0,name);
(found, val) := tab.find(name);
if (!found)
return notify(0,sys->sprint("can't read \"%s\": "
+"no such variable",name));
(num,rest)=str->toint(val,10);
if (rest!=nil)
return notify(0,sys->sprint(
"expected integer but got \"%s\"",val));
if (len argv == 2){
num+=1;
tab.insert(name,string num);
}
if (len argv == 3) {
val = argv[2];
(xtra,rest)=str->toint(val,10);
if (rest!=nil)
return notify(0,sys->sprint(
"expected integer but got \"%s\""
,val));
num+=xtra;
tab.insert(name, string num);
}
return string num;
}
do_info(argv : array of string) : string {
if (len argv==1)
return notify(1,"info option ?arg arg ...?");
case argv[1] {
"args" =>
return do_info_args(argv,0);
"body" =>
return do_info_args(argv,1);
"commands" =>
return do_info_commands(argv);
"exists" =>
return do_info_exists(argv);
"procs" =>
return do_info_procs(argv);
}
return sys->sprint(
"bad option \"%s\": should be args, body, commands, exists, procs",
argv[1]);
}
do_info_args(argv : array of string,body :int) : string {
name: string;
s : sproc;
if (body)
name="body";
else
name="args";
if (len argv!=3)
return notify(1,"info "+name+" procname");
for(i:=0;i<len proctab;i++){
s=proctab[i];
if (s.name==argv[2])
break;
}
if (i==len proctab)
return notify(0,argv[2]+" isn't a procedure.");
if (body)
return s.script;
return s.args;
}
do_info_commands(argv : array of string) : string {
if (len argv==1 || len argv>3)
return notify(1,"info commands [pattern]");
return libmods.dump();
}
do_info_exists(argv : array of string) : string {
name, index : string;
tab : ref Hash;
if (len argv!=3)
return notify(1,"info exists varName");
(name,index)=parsename(argv[2],0);
(i,nil,nil):=isa(name);
if (i==TCL_UNKNOWN)
return "0";
if (index==nil)
return "1";
(tab,name)=find_var(argv[2],0);
if (tab==nil)
return "0";
(found, val) := tab.find(name);
if (!found)
return "0";
return "1";
}
do_info_procs(argv : array of string) : string {
if (len argv==1 || len argv>3)
return notify(1,"info procs [pattern]");
retval : string;
for(i:=0;i<len proctab;i++){
s:=proctab[i];
if (s.name!=nil){
retval+=s.name;
retval[len retval]=' ';
}
}
return retval;
}
do_lappend(argv : array of string) : string{
tab : ref Hash;
retval :string;
retval=nil;
if (len argv==1 || len argv==2)
return notify(1,
"lappend varName value ?value ...?");
name := argv[1];
(tab,name)=find_var(name,1);
if (tab==nil)
return notify(0,name);
(found, val) := tab.find(name);
for(i:=2;i<len argv;i++){
flag:=0;
if (spaces(argv[i])) flag=1;
if (flag) retval[len retval]='{';
retval += argv[i];
if (flag) retval[len retval]='}';
retval[len retval]=' ';
}
if (retval!=nil)
retval=retval[0:len retval-1];
if (val!=nil)
retval=val+" "+retval;
tab.insert(name,retval);
return retval;
}
spaces(s : string) : int{
if (s==nil) return 1;
for(i:=0;i<len s;i++)
if (s[i]==' ' || s[i]=='\t') return 1;
return 0;
}
do_load(argv : array of string) : string {
# look for a dis library to load up, then
# add to library array.
if (len argv!=2)
return notify(1,"load libname");
fname:="/dis/lib/tcl_"+argv[1]+".dis";
mod:= load TclLib fname;
if (mod==nil)
return notify(0,
sys->sprint("Cannot load %s",fname));
arr:=mod->about();
for(i:=0;i<len arr;i++)
libmods.insert(arr[i],mod);
return nil;
}
do_proc(argv : array of string) : string {
if (len argv != 4)
return notify(1,"proc name args body");
for(i:=0;i<len proctab;i++)
if (proctab[i].name==nil ||
proctab[i].name==argv[1]) break;
if (i==len proctab)
return notify(0,"procedure table full!");
proctab[i].name=argv[1];
proctab[i].args=argv[2];
proctab[i].script=argv[3];
return nil;
}
do_return(argv : array of string) : string {
if (len argv==1)
return nil;
# put in options here.....
return argv[1];
}
do_set(argv : array of string) : string {
tab : ref Hash;
if (len argv == 1 || len argv > 3)
return notify(1,"set varName ?newValue?");
name := argv[1];
(tab,name)=find_var(name,1);
if (tab==nil)
return notify(0,name);
(found, val) := tab.find(name);
if (len argv == 2)
if (!found)
val = notify(0,sys->sprint(
"can't read \"%s\": "
+"no such variable",name));
if (len argv == 3) {
val = argv[2];
tab.insert(name, val);
}
return val;
}
do_source(argv : array of string) : string {
if (len argv !=2)
return notify(1,"source fileName");
return loadfile(argv[1]);
}
do_string(argv : array of string) : string {
stringmod := lookup("string");
if (stringmod==nil)
return notify(0,sys->sprint(
"String Package not loaded (%r)"));
(err,retval):= stringmod->exec(ref tclmod,argv);
if (err) return notify(0,retval);
return retval;
}
do_switch(argv : array of string) : string {
i:=0;
arr : array of string;
if (len argv < 3)
return notify(1,"switch "
+"?switches? string pattern body ... "+
"?default body?\"");
if (len argv == 3)
arr=utils->break_it(argv[2]);
else
arr=argv[2:];
if (len arr % 2 !=0)
return notify(0,
"extra switch pattern with no body");
for (i=0;i<len arr;i+=2)
if (argv[1]==arr[i])
break;
if (i==len arr){
if (arr[i-2]=="default")
return evalcmd(arr[i-1],0);
else return nil;
}
while (i<len arr && arr[i+1]=="-") i+=2;
return evalcmd(arr[i+1],0);
}
do_time(argv : array of string) : string {
rest : string;
end,start,times : int;
if (len argv==1 || len argv>3)
return notify(1,"time command ?count?");
if (len argv==2)
times=1;
else{
(times,rest)=str->toint(argv[2],10);
if (rest!=nil)
return notify(0,sys->sprint(
"expected integer but got \"%s\"",argv[2]));
}
start=sys->millisec();
for(i:=0;i<times;i++)
evalcmd(argv[1],0);
end=sys->millisec();
r:= (real end - real start) / real times;
return sys->sprint("%g milliseconds per iteration", r);
}
do_unset(argv : array of string) : string {
tab : ref Hash;
name: string;
if (len argv == 1)
return notify(1,"unset "+
"varName ?varName ...?");
for(i:=1;i<len argv;i++){
name = argv[i];
(tab,name)=find_var(name,0);
if (tab==nil)
return notify(0,sys->sprint("can't unset \"%s\": no such" +
" variable",name));
tab.delete(name);
}
return nil;
}
do_uplevel(argv : array of string) : string {
level: int;
rest,scr : string;
scr=nil;
exact:=0;
i:=1;
if (len argv==1)
return notify(1,"uplevel ?level? command ?arg ...?");
if (len argv==2)
level=-1;
else {
lev:=argv[1];
if (lev[0]=='#'){
exact=1;
lev=lev[1:];
}
(level,rest)=str->toint(lev,10);
if (rest!=nil){
i=2;
level =-1;
}
}
oldlev:=stack->level();
if (!exact)
level+=oldlev;
(tnv,tav,sym):=stack->examine(level);
if (tnv==nil && tav==nil)
return notify(0,"bad level "+argv[1]);
if (tclmod.debug==2)
sys->print("In uplevel, current level is %d, moving to level %d\n",
oldlev,level);
stack->move(level);
oldav:=avtab;
oldnv:=nvtab;
oldsym:=symtab;
avtab=tav;
nvtab=tnv;
symtab=sym;
for(;i<len argv;i++)
scr=scr+argv[i]+" ";
msg:=evalcmd(scr[0:len scr-1],0);
avtab=oldav;
nvtab=oldnv;
symtab=oldsym;
ok:=stack->move(oldlev);
if (tclmod.debug==2)
sys->print("Leaving uplevel, current level is %d, moving back to"+
" level %d,move was %d\n",
level,oldlev,ok);
return msg;
}
do_upvar(argv : array of string) : string {
level:int;
rest:string;
i:=1;
exact:=0;
if (len argv<3 || len argv>4)
return notify(1,"upvar ?level? ThisVar OtherVar");
if (len argv==3)
level=-1;
else {
lev:=argv[1];
if (lev[0]=='#'){
exact=1;
lev=lev[1:];
}
(level,rest)=str->toint(lev,10);
if (rest!=nil){
i=2;
level =-1;
}
}
if (!exact)
level+=stack->level();
symtab.insert(argv[i],argv[i+1],level);
return nil;
}
do_while(argv : array of string) : string {
if (len argv!=3)
return notify(1,"while test command");
for(;;){
expr1 := array[] of {"expr",argv[1]};
msg:=do_expr(expr1);
if (msg=="Error!")
return notify(0,sys->sprint(
"syntax error in expression \"%s\"",
argv[1]));
if (msg=="0")
return nil;
evalcmd(argv[2],0);
if (error)
return errmsg;
}
}
uproc(argv : array of string) : string {
cmd,add : string;
for(i:=0;i< len proctab;i++)
if (proctab[i].name==argv[0])
break;
if (i==len proctab)
return notify(0,sys->sprint("invalid command name \"%s\"",
argv[0]));
# save tables
# push a newframe
# bind args to arguments
# do cmd
# pop frame
# return msg
# globals are supported, but upvar and uplevel are not!
arg_arr:=utils->break_it(proctab[i].args);
j:=len arg_arr;
if (len argv < j+1 && arg_arr[j-1]!="args"){
j=len argv-1;
return notify(0,sys->sprint(
"no value given for"+
" parameter \"%s\" to \"%s\"",
arg_arr[j],proctab[i].name));
}
if ((len argv > j+1) && arg_arr[j-1]!="args")
return notify(0,"called "+proctab[i].name+
" with too many arguments");
oldavtab:=avtab;
oldnvtab:=nvtab;
oldsymtab:=symtab;
(nvtab,avtab,symtab)=stack->newframe();
for (j=0;j< len arg_arr-1;j++){
cmd="set "+arg_arr[j]+" {"+argv[j+1]+"}";
evalcmd(cmd,0);
}
if (len arg_arr>j && arg_arr[j] != "args") {
cmd="set "+arg_arr[j]+" {"+argv[j+1]+"}";
evalcmd(cmd,0);
}
else {
if (len arg_arr > j) {
if (j+1==len argv)
add="";
else
add=argv[j+1];
cmd="set "+arg_arr[j]+" ";
arglist:="{"+add+" ";
j++;
while(j<len argv-1) {
arglist+=argv[j+1];
arglist[len arglist]=' ';
j++;
}
arglist[len arglist]='}';
cmd+=arglist;
evalcmd(cmd,0);
}
}
msg:=evalcmd(proctab[i].script,0);
stack->pop();
avtab=oldavtab;
nvtab=oldnvtab;
symtab=oldsymtab;
#sys->print("Error is %d, msg is %s\n",error,msg);
return msg;
}
do_tk(argv : array of string) : string {
tkpack:=lookup("button");
(err,retval):= tkpack->exec(ref tclmod,argv);
if (err) return notify(0,retval);
return retval;
}
lookup(s : string) : TclLib {
(found,mod):=libmods.find(s);
if (!found)
return nil;
return mod;
}