xref: /inferno-os/appl/lib/tcl_core.b (revision 37da2899f40661e3e9631e497da8dc59b971cbd0)
1*37da2899SCharles.Forsythimplement Tcl_Core;
2*37da2899SCharles.Forsyth
3*37da2899SCharles.Forsyth# these are the outside modules, self explanatory..
4*37da2899SCharles.Forsythinclude "sys.m";
5*37da2899SCharles.Forsyth	sys: Sys;
6*37da2899SCharles.Forsythinclude "draw.m";
7*37da2899SCharles.Forsyth	draw: Draw;
8*37da2899SCharles.Forsyth
9*37da2899SCharles.Forsythinclude "bufio.m";
10*37da2899SCharles.Forsyth	bufmod : Bufio;
11*37da2899SCharles.ForsythIobuf : import bufmod;
12*37da2899SCharles.Forsyth
13*37da2899SCharles.Forsythinclude "string.m";
14*37da2899SCharles.Forsyth	str : String;
15*37da2899SCharles.Forsyth
16*37da2899SCharles.Forsythinclude "tk.m";
17*37da2899SCharles.Forsyth	tk: Tk;
18*37da2899SCharles.Forsyth
19*37da2899SCharles.Forsythinclude	"wmlib.m";
20*37da2899SCharles.Forsyth	wmlib: Wmlib;
21*37da2899SCharles.Forsyth
22*37da2899SCharles.Forsyth# these are stand alone Tcl libraries, for Tcl pieces that
23*37da2899SCharles.Forsyth# are "big" enough to be called their own.
24*37da2899SCharles.Forsyth
25*37da2899SCharles.Forsythinclude "tcl.m";
26*37da2899SCharles.Forsyth
27*37da2899SCharles.Forsythinclude "tcllib.m";
28*37da2899SCharles.Forsyth
29*37da2899SCharles.Forsythinclude "utils.m";
30*37da2899SCharles.Forsyth	htab: Str_Hashtab;
31*37da2899SCharles.Forsyth	mhtab : Mod_Hashtab;
32*37da2899SCharles.Forsyth	shtab : Sym_Hashtab;
33*37da2899SCharles.Forsyth	stack : Tcl_Stack;
34*37da2899SCharles.Forsyth	utils : Tcl_Utils;
35*37da2899SCharles.Forsyth
36*37da2899SCharles.ForsythHash: import htab;
37*37da2899SCharles.ForsythMHash : import mhtab;
38*37da2899SCharles.ForsythSHash : import shtab;
39*37da2899SCharles.Forsyth
40*37da2899SCharles.Forsyth
41*37da2899SCharles.Forsyth
42*37da2899SCharles.Forsyth
43*37da2899SCharles.Forsyth# global error flag and message. One day, this will be stack based..
44*37da2899SCharles.Forsytherrmsg : string;
45*37da2899SCharles.Forsytherror, mypid : int;
46*37da2899SCharles.Forsyth
47*37da2899SCharles.Forsythsproc : adt {
48*37da2899SCharles.Forsyth	name : string;
49*37da2899SCharles.Forsyth	args : string;
50*37da2899SCharles.Forsyth	script : string;
51*37da2899SCharles.Forsyth};
52*37da2899SCharles.Forsyth
53*37da2899SCharles.ForsythTCL_UNKNOWN, TCL_SIMPLE, TCL_ARRAY : con iota;
54*37da2899SCharles.Forsyth
55*37da2899SCharles.Forsyth# Global vars. Simple variables, and associative arrays.
56*37da2899SCharles.Forsythlibmods : ref MHash;
57*37da2899SCharles.Forsythproctab := array[100] of sproc;
58*37da2899SCharles.Forsythretfl : int;
59*37da2899SCharles.Forsythsymtab : ref SHash;
60*37da2899SCharles.Forsythnvtab : ref Hash;
61*37da2899SCharles.Forsythavtab : array of (ref Hash,string);
62*37da2899SCharles.Forsythtclmod : TclData;
63*37da2899SCharles.Forsyth
64*37da2899SCharles.Forsythcore_commands:=array[] of {
65*37da2899SCharles.Forsyth	"append" , "array", "break" , "continue" , "catch", "dumpstack",
66*37da2899SCharles.Forsyth	"exit" , "expr" , "eval" ,
67*37da2899SCharles.Forsyth	"for" , "foreach" ,
68*37da2899SCharles.Forsyth	"global" , "if" , "incr" , "info",
69*37da2899SCharles.Forsyth	"lappend" , "level" , "load" ,
70*37da2899SCharles.Forsyth	"proc" , "return" , "set" ,
71*37da2899SCharles.Forsyth	"source" ,"switch" , "time" ,
72*37da2899SCharles.Forsyth	"unset" , "uplevel", "upvar", "while" , "#"
73*37da2899SCharles.Forsyth};
74*37da2899SCharles.Forsyth
75*37da2899SCharles.Forsyth
76*37da2899SCharles.Forsythabout() : array of string {
77*37da2899SCharles.Forsyth	return core_commands;
78*37da2899SCharles.Forsyth}
79*37da2899SCharles.Forsyth
80*37da2899SCharles.Forsythinit(ctxt: ref Draw->Context, argv: list of string) {
81*37da2899SCharles.Forsyth	sys = load Sys Sys->PATH;
82*37da2899SCharles.Forsyth	draw = load Draw Draw->PATH;
83*37da2899SCharles.Forsyth	bufmod = load Bufio Bufio->PATH;
84*37da2899SCharles.Forsyth	htab = load Str_Hashtab Str_Hashtab->PATH;
85*37da2899SCharles.Forsyth	mhtab = load Mod_Hashtab Mod_Hashtab->PATH;
86*37da2899SCharles.Forsyth	shtab = load Sym_Hashtab Sym_Hashtab->PATH;
87*37da2899SCharles.Forsyth	stack = load Tcl_Stack Tcl_Stack->PATH;
88*37da2899SCharles.Forsyth	str = load String String->PATH;
89*37da2899SCharles.Forsyth	utils = load Tcl_Utils Tcl_Utils->PATH;
90*37da2899SCharles.Forsyth	tk = load Tk Tk->PATH;
91*37da2899SCharles.Forsyth	wmlib= load Wmlib Wmlib->PATH;
92*37da2899SCharles.Forsyth	if (bufmod == nil || htab == nil || stack == nil ||
93*37da2899SCharles.Forsyth		str == nil || utils == nil || tk == nil ||
94*37da2899SCharles.Forsyth		wmlib==nil || mhtab == nil || shtab == nil){
95*37da2899SCharles.Forsyth		sys->print("can't load initial modules %r\n");
96*37da2899SCharles.Forsyth		exit;
97*37da2899SCharles.Forsyth	}
98*37da2899SCharles.Forsyth
99*37da2899SCharles.Forsyth	# get a new stack frame.
100*37da2899SCharles.Forsyth	stack->init();
101*37da2899SCharles.Forsyth	(nvtab,avtab,symtab)=stack->newframe();
102*37da2899SCharles.Forsyth
103*37da2899SCharles.Forsyth	libmods=mhtab->alloc(101);
104*37da2899SCharles.Forsyth
105*37da2899SCharles.Forsyth	# grab my pid, and set a new group to make me easy to kill.
106*37da2899SCharles.Forsyth	mypid=sys->pctl(sys->NEWPGRP, nil);
107*37da2899SCharles.Forsyth
108*37da2899SCharles.Forsyth	# no default top window.
109*37da2899SCharles.Forsyth	tclmod.top=nil;
110*37da2899SCharles.Forsyth	tclmod.context=ctxt;
111*37da2899SCharles.Forsyth	tclmod.debug=0;
112*37da2899SCharles.Forsyth
113*37da2899SCharles.Forsyth	# set up library modules.
114*37da2899SCharles.Forsyth	args:=array[] of {"do_load","io"};
115*37da2899SCharles.Forsyth	do_load(args);
116*37da2899SCharles.Forsyth	args=array[] of {"do_load","string"};
117*37da2899SCharles.Forsyth	do_load(args);
118*37da2899SCharles.Forsyth	args=array[] of {"do_load","calc"};
119*37da2899SCharles.Forsyth	do_load(args);
120*37da2899SCharles.Forsyth	args=array[] of {"do_load","list"};
121*37da2899SCharles.Forsyth	do_load(args);
122*37da2899SCharles.Forsyth	args=array[] of {"do_load","tk"};
123*37da2899SCharles.Forsyth	do_load(args);
124*37da2899SCharles.Forsyth	arr:=about();
125*37da2899SCharles.Forsyth	for(i:=0;i<len arr;i++)
126*37da2899SCharles.Forsyth		libmods.insert(arr[i],nil);
127*37da2899SCharles.Forsyth
128*37da2899SCharles.Forsyth	# cmd line args...
129*37da2899SCharles.Forsyth	if (argv != nil)
130*37da2899SCharles.Forsyth		argv = tl argv;
131*37da2899SCharles.Forsyth	while (argv != nil) {
132*37da2899SCharles.Forsyth		loadfile(hd argv);
133*37da2899SCharles.Forsyth		argv = tl argv;
134*37da2899SCharles.Forsyth	}
135*37da2899SCharles.Forsyth
136*37da2899SCharles.Forsyth}
137*37da2899SCharles.Forsyth
138*37da2899SCharles.Forsythset_top(win:ref Tk->Toplevel){
139*37da2899SCharles.Forsyth	tclmod.top=win;
140*37da2899SCharles.Forsyth}
141*37da2899SCharles.Forsyth
142*37da2899SCharles.Forsythclear_error(){
143*37da2899SCharles.Forsyth	error=0;
144*37da2899SCharles.Forsyth	errmsg="";
145*37da2899SCharles.Forsyth}
146*37da2899SCharles.Forsyth
147*37da2899SCharles.Forsythnotify(num : int,s : string) : string {
148*37da2899SCharles.Forsyth	error=1;
149*37da2899SCharles.Forsyth	case num{
150*37da2899SCharles.Forsyth		1 =>
151*37da2899SCharles.Forsyth			errmsg=sys->sprint(
152*37da2899SCharles.Forsyth			"wrong # args: should be \"%s\"",s);
153*37da2899SCharles.Forsyth		* =>
154*37da2899SCharles.Forsyth			errmsg= s;
155*37da2899SCharles.Forsyth	}
156*37da2899SCharles.Forsyth	return errmsg;
157*37da2899SCharles.Forsyth}
158*37da2899SCharles.Forsyth
159*37da2899SCharles.Forsythgrab_lines(new_inp,unfin: string ,lines : chan of string){
160*37da2899SCharles.Forsyth	error=0;
161*37da2899SCharles.Forsyth	tclmod.lines=lines;
162*37da2899SCharles.Forsyth	input,line : string;
163*37da2899SCharles.Forsyth	if (new_inp==nil)
164*37da2899SCharles.Forsyth		new_inp = "tcl%";
165*37da2899SCharles.Forsyth	if (unfin==nil)
166*37da2899SCharles.Forsyth		unfin = "tcl>";
167*37da2899SCharles.Forsyth	sys->print("%s ", new_inp);
168*37da2899SCharles.Forsyth	iob := bufmod->fopen(sys->fildes(0),bufmod->OREAD);
169*37da2899SCharles.Forsyth	if (iob==nil){
170*37da2899SCharles.Forsyth		sys->print("cannot open stdin for reading.\n");
171*37da2899SCharles.Forsyth		return;
172*37da2899SCharles.Forsyth	}
173*37da2899SCharles.Forsyth	while((input=iob.gets('\n'))!=nil){
174*37da2899SCharles.Forsyth		line+=input;
175*37da2899SCharles.Forsyth		if (!finished(line,0))
176*37da2899SCharles.Forsyth			sys->print("%s ", unfin);
177*37da2899SCharles.Forsyth		else{
178*37da2899SCharles.Forsyth			lines <- = line;
179*37da2899SCharles.Forsyth			line=nil;
180*37da2899SCharles.Forsyth		}
181*37da2899SCharles.Forsyth	}
182*37da2899SCharles.Forsyth}
183*37da2899SCharles.Forsyth
184*37da2899SCharles.Forsyth# this is the main function. Its input is a complete (i.e. matching
185*37da2899SCharles.Forsyth# brackets etc) tcl script, and its output is a message - if there
186*37da2899SCharles.Forsyth# is one.
187*37da2899SCharles.Forsythevalcmd(s: string, termchar: int) : string {
188*37da2899SCharles.Forsyth	msg : string;
189*37da2899SCharles.Forsyth	i:=0;
190*37da2899SCharles.Forsyth	retfl=0;
191*37da2899SCharles.Forsyth	if (tclmod.debug==2)
192*37da2899SCharles.Forsyth		sys->print("Entered evalcmd, s=%s, termchar=%c\n",s,termchar);
193*37da2899SCharles.Forsyth	# strip null statements..
194*37da2899SCharles.Forsyth	while((i<len s) && (s[i]=='\n' || s[i]==';')) i++;
195*37da2899SCharles.Forsyth	if (i==len s) return nil;
196*37da2899SCharles.Forsyth
197*37da2899SCharles.Forsyth	# parse the script statement by statement
198*37da2899SCharles.Forsyth	for(;s!=nil;i++){
199*37da2899SCharles.Forsyth		# wait till we have a complete statement
200*37da2899SCharles.Forsyth		if (i==len s || ((s[i]==termchar || s[i]==';' || s[i]=='\n')
201*37da2899SCharles.Forsyth			&& finished(s[0:i],termchar))){
202*37da2899SCharles.Forsyth			# throw it away if its a comment...
203*37da2899SCharles.Forsyth			if (s[0]!='#')
204*37da2899SCharles.Forsyth				argv := parsecmd(s[0:i],termchar,0);
205*37da2899SCharles.Forsyth			msg = nil;
206*37da2899SCharles.Forsyth			if (tclmod.debug==2)
207*37da2899SCharles.Forsyth				for(k:=0;k<len argv;k++)
208*37da2899SCharles.Forsyth				sys->print("argv[%d]: (%s)\n",k,argv[k]);
209*37da2899SCharles.Forsyth
210*37da2899SCharles.Forsyth			# argv is now a completely parsed array of arguments
211*37da2899SCharles.Forsyth			# for the Tcl command..
212*37da2899SCharles.Forsyth
213*37da2899SCharles.Forsyth			# find the module that the command is in and
214*37da2899SCharles.Forsyth			# 	execute it.
215*37da2899SCharles.Forsyth			if (len argv != 0){
216*37da2899SCharles.Forsyth				mod:=lookup(argv[0]);
217*37da2899SCharles.Forsyth				if (mod!=nil){
218*37da2899SCharles.Forsyth					(error,msg)=
219*37da2899SCharles.Forsyth					   mod->exec(ref tclmod,argv);
220*37da2899SCharles.Forsyth					if (error)
221*37da2899SCharles.Forsyth						errmsg=msg;
222*37da2899SCharles.Forsyth				} else {
223*37da2899SCharles.Forsyth					if (argv[0]!=nil &&
224*37da2899SCharles.Forsyth						argv[0][0]=='.')
225*37da2899SCharles.Forsyth						msg=do_tk(argv);
226*37da2899SCharles.Forsyth					else
227*37da2899SCharles.Forsyth						msg=exec(argv);
228*37da2899SCharles.Forsyth				}
229*37da2899SCharles.Forsyth			}
230*37da2899SCharles.Forsyth
231*37da2899SCharles.Forsyth			# was there an error?
232*37da2899SCharles.Forsyth			if (error) {
233*37da2899SCharles.Forsyth				if (len argv > 0 && argv[0]!=""){
234*37da2899SCharles.Forsyth					stat : string;
235*37da2899SCharles.Forsyth					stat = "In function "+argv[0];
236*37da2899SCharles.Forsyth					if (len argv >1 && argv[1]!=""){
237*37da2899SCharles.Forsyth						stat[len stat]=' ';
238*37da2899SCharles.Forsyth						stat+=argv[1];
239*37da2899SCharles.Forsyth					}
240*37da2899SCharles.Forsyth					stat+=".....\n\t";
241*37da2899SCharles.Forsyth					errmsg=stat+errmsg;
242*37da2899SCharles.Forsyth				}
243*37da2899SCharles.Forsyth				msg=errmsg;
244*37da2899SCharles.Forsyth			}
245*37da2899SCharles.Forsyth
246*37da2899SCharles.Forsyth			# we stop parsing if we hit a break, continue, return,
247*37da2899SCharles.Forsyth			# error, termchar or end of string.
248*37da2899SCharles.Forsyth			if (msg=="break" || msg=="continue" || error || retfl==1
249*37da2899SCharles.Forsyth				|| len s <= i || (len s > i && s[i]==termchar))
250*37da2899SCharles.Forsyth				return msg;
251*37da2899SCharles.Forsyth
252*37da2899SCharles.Forsyth			# otherwise eat up the parsed statement and continue
253*37da2899SCharles.Forsyth			s=s[i+1:];
254*37da2899SCharles.Forsyth			i=-1;
255*37da2899SCharles.Forsyth		}
256*37da2899SCharles.Forsyth	}
257*37da2899SCharles.Forsyth	return msg;
258*37da2899SCharles.Forsyth}
259*37da2899SCharles.Forsyth
260*37da2899SCharles.Forsyth
261*37da2899SCharles.Forsyth# returns 1 if the line has matching braces, brackets and
262*37da2899SCharles.Forsyth# double-quotes and does not end in "\\\n"
263*37da2899SCharles.Forsythfinished(s : string, termchar : int) : int {
264*37da2899SCharles.Forsyth	cb:=0;
265*37da2899SCharles.Forsyth	dq:=0;
266*37da2899SCharles.Forsyth	sb:=0;
267*37da2899SCharles.Forsyth	if (s==nil) return 1;
268*37da2899SCharles.Forsyth	if (termchar=='}') cb++;
269*37da2899SCharles.Forsyth	if (termchar==']') sb++;
270*37da2899SCharles.Forsyth	if (len s > 1 && s[len s -2]=='\\')
271*37da2899SCharles.Forsyth		return 0;
272*37da2899SCharles.Forsyth	if (s[0]=='{') cb++;
273*37da2899SCharles.Forsyth	if (s[0]=='}' && cb>0) cb--;
274*37da2899SCharles.Forsyth	if (s[0]=='[') sb++;
275*37da2899SCharles.Forsyth	if (s[0]==']' && sb>0) sb--;
276*37da2899SCharles.Forsyth	if (s[0]=='"') dq=1-dq;
277*37da2899SCharles.Forsyth	for(i:=1;i<len s;i++){
278*37da2899SCharles.Forsyth		if (s[i]=='{' && s[i-1]!='\\') cb++;
279*37da2899SCharles.Forsyth		if (s[i]=='}' && s[i-1]!='\\' && cb>0) cb--;
280*37da2899SCharles.Forsyth		if (s[i]=='[' && s[i-1]!='\\') sb++;
281*37da2899SCharles.Forsyth		if (s[i]==']' && s[i-1]!='\\' && sb>0) sb--;
282*37da2899SCharles.Forsyth		if (s[i]=='"' && s[i-1]!='\\') dq=1-dq;
283*37da2899SCharles.Forsyth	}
284*37da2899SCharles.Forsyth	return (cb==0 && sb==0 && dq==0);
285*37da2899SCharles.Forsyth}
286*37da2899SCharles.Forsyth
287*37da2899SCharles.Forsyth# counts the offset till the next matching ']'
288*37da2899SCharles.Forsythstrip_to_match(s : string, ptr: int) : int {
289*37da2899SCharles.Forsyth	j :=0;
290*37da2899SCharles.Forsyth	nb:=0;
291*37da2899SCharles.Forsyth	while(j<len s){
292*37da2899SCharles.Forsyth		if (s[j]=='{')
293*37da2899SCharles.Forsyth			while (j < len s && s[j]!='}') j++;
294*37da2899SCharles.Forsyth		if (s[j]=='[') nb++;
295*37da2899SCharles.Forsyth		if (s[j]==']'){
296*37da2899SCharles.Forsyth			nb--;
297*37da2899SCharles.Forsyth			if (nb==-1) return ptr+j;
298*37da2899SCharles.Forsyth		}
299*37da2899SCharles.Forsyth		j++;
300*37da2899SCharles.Forsyth	}
301*37da2899SCharles.Forsyth	return ptr+j;
302*37da2899SCharles.Forsyth}
303*37da2899SCharles.Forsyth
304*37da2899SCharles.Forsyth# returns the type of variable represented by the string s, which is
305*37da2899SCharles.Forsyth# a name.
306*37da2899SCharles.Forsythisa(s: string) : (int,int,string) {
307*37da2899SCharles.Forsyth	found,val : int;
308*37da2899SCharles.Forsyth	name,al : string;
309*37da2899SCharles.Forsyth	curlev:=stack->level();
310*37da2899SCharles.Forsyth	if (tclmod.debug==2)
311*37da2899SCharles.Forsyth		sys->print("Called isa with %s, current stack level is %d\n",s,curlev);
312*37da2899SCharles.Forsyth	(found,nil)=nvtab.find(s);
313*37da2899SCharles.Forsyth	if (found) return (TCL_SIMPLE,curlev,s);
314*37da2899SCharles.Forsyth	for (i:=0;i<len avtab;i++){
315*37da2899SCharles.Forsyth		(nil,name)=avtab[i];
316*37da2899SCharles.Forsyth		if (name==s) return (TCL_ARRAY,curlev,s);
317*37da2899SCharles.Forsyth	}
318*37da2899SCharles.Forsyth	if (symtab==nil)
319*37da2899SCharles.Forsyth		return (TCL_UNKNOWN,curlev,s);
320*37da2899SCharles.Forsyth	(found,val,al)=symtab.find(s);
321*37da2899SCharles.Forsyth	if (!found)
322*37da2899SCharles.Forsyth		return (TCL_UNKNOWN,curlev,s);
323*37da2899SCharles.Forsyth	(tnv,tav,nil):=stack->examine(val);
324*37da2899SCharles.Forsyth	if (tclmod.debug==2)
325*37da2899SCharles.Forsyth		sys->print("have a level %d for %s\n",val,al);
326*37da2899SCharles.Forsyth	if (tnv!=nil){
327*37da2899SCharles.Forsyth		(found,nil)=tnv.find(al);
328*37da2899SCharles.Forsyth		if (found) return (TCL_SIMPLE,val,al);
329*37da2899SCharles.Forsyth	}
330*37da2899SCharles.Forsyth	if (tav!=nil){
331*37da2899SCharles.Forsyth		for (i=0;i<len tav;i++){
332*37da2899SCharles.Forsyth			(nil,name)=tav[i];
333*37da2899SCharles.Forsyth			if (name==al) return (TCL_ARRAY,val,al);
334*37da2899SCharles.Forsyth		}
335*37da2899SCharles.Forsyth	}
336*37da2899SCharles.Forsyth	if (tclmod.debug==2)
337*37da2899SCharles.Forsyth		sys->print("%s not found, creating at stack level %d\n",al,val);
338*37da2899SCharles.Forsyth	return (TCL_UNKNOWN,val,al);
339*37da2899SCharles.Forsyth}
340*37da2899SCharles.Forsyth
341*37da2899SCharles.Forsyth# This function only works if the string is already parsed!
342*37da2899SCharles.Forsyth# takes a var_name and returns the hash table for it and the
343*37da2899SCharles.Forsyth# name to look up. This is one of two things:
344*37da2899SCharles.Forsyth# for simple variables:
345*37da2899SCharles.Forsyth# findvar(foo) ---> (nvtab,foo)
346*37da2899SCharles.Forsyth# for associative arrays:
347*37da2899SCharles.Forsyth# findvar(foo(bar)) -----> (avtab[i],bar)
348*37da2899SCharles.Forsyth# where avtab[i].name==foo
349*37da2899SCharles.Forsyth# if create is 1, then an associative array is created upon first
350*37da2899SCharles.Forsyth# reference.
351*37da2899SCharles.Forsyth# returns (nil,error message) if there is a problem.
352*37da2899SCharles.Forsyth
353*37da2899SCharles.Forsythfind_var(s : string,create : int) : (ref Hash,string) {
354*37da2899SCharles.Forsyth	rest,name,index : string;
355*37da2899SCharles.Forsyth	retval,tnv : ref Hash;
356*37da2899SCharles.Forsyth	tav : array of (ref Hash,string);
357*37da2899SCharles.Forsyth	i,tag,lev: int;
358*37da2899SCharles.Forsyth	(name,index)=str->splitl(s,"(");
359*37da2899SCharles.Forsyth	if (index!=nil){
360*37da2899SCharles.Forsyth		(index,rest)=str->splitl(index[1:],")");
361*37da2899SCharles.Forsyth		if (rest!=")")
362*37da2899SCharles.Forsyth			return (nil,"bad variable name");
363*37da2899SCharles.Forsyth	}
364*37da2899SCharles.Forsyth	(tag,lev,name) = isa(name);
365*37da2899SCharles.Forsyth	case tag {
366*37da2899SCharles.Forsyth		TCL_SIMPLE =>
367*37da2899SCharles.Forsyth			if (index!=nil)
368*37da2899SCharles.Forsyth				return (nil,"variable isn't array");
369*37da2899SCharles.Forsyth			(tnv,nil,nil)=stack->examine(lev);
370*37da2899SCharles.Forsyth			return (tnv,name);
371*37da2899SCharles.Forsyth		TCL_ARRAY =>
372*37da2899SCharles.Forsyth			if (index==nil)
373*37da2899SCharles.Forsyth				return (nil,"variable is array");
374*37da2899SCharles.Forsyth			(nil,tav,nil)=stack->examine(lev);
375*37da2899SCharles.Forsyth			for(i=0;i<len tav;i++){
376*37da2899SCharles.Forsyth				(retval,rest)=tav[i];
377*37da2899SCharles.Forsyth				if (rest==name)
378*37da2899SCharles.Forsyth					return (retval,index);
379*37da2899SCharles.Forsyth			}
380*37da2899SCharles.Forsyth			return (nil,"find_var: impossible!!");
381*37da2899SCharles.Forsyth		# if we get here, the variable needs to be
382*37da2899SCharles.Forsyth		# created.
383*37da2899SCharles.Forsyth		TCL_UNKNOWN =>
384*37da2899SCharles.Forsyth			if (!create)
385*37da2899SCharles.Forsyth				return (nil,"no such variable");
386*37da2899SCharles.Forsyth			(tnv,tav,nil)=stack->examine(lev);
387*37da2899SCharles.Forsyth			if (index==nil)
388*37da2899SCharles.Forsyth				return (tnv,name);
389*37da2899SCharles.Forsyth
390*37da2899SCharles.Forsyth	}
391*37da2899SCharles.Forsyth	# if we get here, we are creating an associative variable in the
392*37da2899SCharles.Forsyth	# tav array.
393*37da2899SCharles.Forsyth	for(i=0;i<len tav;i++){
394*37da2899SCharles.Forsyth		(retval,rest)=tav[i];
395*37da2899SCharles.Forsyth		if (rest==nil){
396*37da2899SCharles.Forsyth			retval=htab->alloc(101);
397*37da2899SCharles.Forsyth			tav[i]=(retval,name);
398*37da2899SCharles.Forsyth			return (retval,index);
399*37da2899SCharles.Forsyth		}
400*37da2899SCharles.Forsyth	}
401*37da2899SCharles.Forsyth	return (nil,"associative array table full!");
402*37da2899SCharles.Forsyth}
403*37da2899SCharles.Forsyth
404*37da2899SCharles.Forsyth# the main parsing function, a la ousterhouts man pages. Takes a
405*37da2899SCharles.Forsyth# string that is meant to be a tcl statement and parses it,
406*37da2899SCharles.Forsyth# reevaluating and quoting upto the termchar character. If disable
407*37da2899SCharles.Forsyth# is true, then whitespace is not ignored.
408*37da2899SCharles.Forsythparsecmd(s: string, termchar,disable: int) : array of string {
409*37da2899SCharles.Forsyth	argv:= array[200] of string;
410*37da2899SCharles.Forsyth	buf,nm,id: string;
411*37da2899SCharles.Forsyth	argc := 0;
412*37da2899SCharles.Forsyth	nc := 0;
413*37da2899SCharles.Forsyth	c :=0;
414*37da2899SCharles.Forsyth	tab : ref Hash;
415*37da2899SCharles.Forsyth
416*37da2899SCharles.Forsyth	if (disable && (termchar=='\n' || termchar==';')) termchar=0;
417*37da2899SCharles.Forsyth   outer:
418*37da2899SCharles.Forsyth	for (i := 0; i<len s ;) {
419*37da2899SCharles.Forsyth		if ((i>0 &&s[i-1]!='\\' &&s[i]==termchar)||(s[0]==termchar))
420*37da2899SCharles.Forsyth			break;
421*37da2899SCharles.Forsyth		case int s[i] {
422*37da2899SCharles.Forsyth		' ' or '\t' or '\n' =>
423*37da2899SCharles.Forsyth			if (!disable){
424*37da2899SCharles.Forsyth				if (nc > 0) {	# end of a word?
425*37da2899SCharles.Forsyth					argv[argc++] = buf;
426*37da2899SCharles.Forsyth					buf = nil;
427*37da2899SCharles.Forsyth					nc = 0;
428*37da2899SCharles.Forsyth				}
429*37da2899SCharles.Forsyth				i++;
430*37da2899SCharles.Forsyth			}
431*37da2899SCharles.Forsyth			else
432*37da2899SCharles.Forsyth				buf[nc++]=s[i++];
433*37da2899SCharles.Forsyth		'$' =>
434*37da2899SCharles.Forsyth			if (i>0 && s[i-1]=='\\')
435*37da2899SCharles.Forsyth				buf[nc++]=s[i++];
436*37da2899SCharles.Forsyth			else {
437*37da2899SCharles.Forsyth				(nm,id) = parsename(s[i+1:], termchar);
438*37da2899SCharles.Forsyth				if (id!=nil)
439*37da2899SCharles.Forsyth					nm=nm+"("+id+")";
440*37da2899SCharles.Forsyth				(tab,nm)=find_var(nm,0); #don't create var!
441*37da2899SCharles.Forsyth				if (len nm > 0 && tab!=nil) {
442*37da2899SCharles.Forsyth					(found, val) := tab.find(nm);
443*37da2899SCharles.Forsyth					buf += val;
444*37da2899SCharles.Forsyth					nc += len val;
445*37da2899SCharles.Forsyth					#sys->print("Here s[i:] is (%s)\n",s[i:]);
446*37da2899SCharles.Forsyth					if(nm==id)
447*37da2899SCharles.Forsyth						while(s[i]!=')') i++;
448*37da2899SCharles.Forsyth					else
449*37da2899SCharles.Forsyth						if (s[i+1]=='{')
450*37da2899SCharles.Forsyth							while(s[i]!='}') i++;
451*37da2899SCharles.Forsyth						else
452*37da2899SCharles.Forsyth							i += len nm;
453*37da2899SCharles.Forsyth					if (nc==0 && (i==len s-1 ||
454*37da2899SCharles.Forsyth							s[i+1]==' ' ||
455*37da2899SCharles.Forsyth							s[i+1]=='\t'||
456*37da2899SCharles.Forsyth							s[i+1]==termchar))
457*37da2899SCharles.Forsyth						argv[argc++]=buf;
458*37da2899SCharles.Forsyth				} else {
459*37da2899SCharles.Forsyth					buf[nc++] = '$';
460*37da2899SCharles.Forsyth				}
461*37da2899SCharles.Forsyth				i++;
462*37da2899SCharles.Forsyth			}
463*37da2899SCharles.Forsyth		'{' =>
464*37da2899SCharles.Forsyth			if (i>0 && s[i-1]=='\\')
465*37da2899SCharles.Forsyth				buf[nc++]=s[i++];
466*37da2899SCharles.Forsyth			else if (s[i+1]=='}'){
467*37da2899SCharles.Forsyth				argv[argc++] = nil;
468*37da2899SCharles.Forsyth				buf = nil;
469*37da2899SCharles.Forsyth				nc = 0;
470*37da2899SCharles.Forsyth				i+=2;
471*37da2899SCharles.Forsyth			} else {
472*37da2899SCharles.Forsyth				nbra := 1;
473*37da2899SCharles.Forsyth				for (i++; i < len s; i++) {
474*37da2899SCharles.Forsyth					if (s[i] == '{')
475*37da2899SCharles.Forsyth						nbra++;
476*37da2899SCharles.Forsyth					else if (s[i] == '}') {
477*37da2899SCharles.Forsyth						nbra--;
478*37da2899SCharles.Forsyth						if (nbra == 0) {
479*37da2899SCharles.Forsyth							i++;
480*37da2899SCharles.Forsyth							continue outer;
481*37da2899SCharles.Forsyth						}
482*37da2899SCharles.Forsyth					}
483*37da2899SCharles.Forsyth					buf[nc++] = s[i];
484*37da2899SCharles.Forsyth				}
485*37da2899SCharles.Forsyth			}
486*37da2899SCharles.Forsyth		'[' =>
487*37da2899SCharles.Forsyth			if (i>0 && s[i-1]=='\\')
488*37da2899SCharles.Forsyth				buf[nc++]=s[i++];
489*37da2899SCharles.Forsyth			else{
490*37da2899SCharles.Forsyth				a:=evalcmd(s[i+1:],']');
491*37da2899SCharles.Forsyth				if (error)
492*37da2899SCharles.Forsyth					return nil;
493*37da2899SCharles.Forsyth				if (nc>0){
494*37da2899SCharles.Forsyth					buf+=a;
495*37da2899SCharles.Forsyth					nc += len a;
496*37da2899SCharles.Forsyth				} else {
497*37da2899SCharles.Forsyth					argv[argc++] = a;
498*37da2899SCharles.Forsyth					buf = nil;
499*37da2899SCharles.Forsyth					nc = 0;
500*37da2899SCharles.Forsyth				}
501*37da2899SCharles.Forsyth				i++;
502*37da2899SCharles.Forsyth				i=strip_to_match(s[i:],i);
503*37da2899SCharles.Forsyth				i++;
504*37da2899SCharles.Forsyth			}
505*37da2899SCharles.Forsyth		'"' =>
506*37da2899SCharles.Forsyth			if (i>0 && s[i-1]!='\\' && nc==0){
507*37da2899SCharles.Forsyth				ans:=parsecmd(s[i+1:],'"',1);
508*37da2899SCharles.Forsyth				#sys->print("len ans is %d\n",len ans);
509*37da2899SCharles.Forsyth				if (len ans!=0){
510*37da2899SCharles.Forsyth					for(;;){
511*37da2899SCharles.Forsyth						i++;
512*37da2899SCharles.Forsyth						if(s[i]=='"' &&
513*37da2899SCharles.Forsyth							s[i-1]!='\\')
514*37da2899SCharles.Forsyth						break;
515*37da2899SCharles.Forsyth					}
516*37da2899SCharles.Forsyth					i++;
517*37da2899SCharles.Forsyth					argv[argc++] = ans[0];
518*37da2899SCharles.Forsyth				} else {
519*37da2899SCharles.Forsyth					argv[argc++] = nil;
520*37da2899SCharles.Forsyth					i+=2;
521*37da2899SCharles.Forsyth				}
522*37da2899SCharles.Forsyth				buf = nil;
523*37da2899SCharles.Forsyth				nc = 0;
524*37da2899SCharles.Forsyth			}
525*37da2899SCharles.Forsyth			else buf[nc++] = s[i++];
526*37da2899SCharles.Forsyth		* =>
527*37da2899SCharles.Forsyth			if (s[i]=='\\'){
528*37da2899SCharles.Forsyth				c=unesc(s[i:]);
529*37da2899SCharles.Forsyth				if (c!=0){
530*37da2899SCharles.Forsyth					buf[nc++] = c;
531*37da2899SCharles.Forsyth					i+=2;
532*37da2899SCharles.Forsyth				} else {
533*37da2899SCharles.Forsyth					if (i+1 < len s && !(s[i+1]=='"'
534*37da2899SCharles.Forsyth						|| s[i+1]=='$' || s[i+1]=='{'
535*37da2899SCharles.Forsyth						|| s[i+1]=='['))
536*37da2899SCharles.Forsyth						buf[nc++]=s[i];
537*37da2899SCharles.Forsyth					i++;
538*37da2899SCharles.Forsyth				}
539*37da2899SCharles.Forsyth				c=0;
540*37da2899SCharles.Forsyth			} else
541*37da2899SCharles.Forsyth				buf[nc++]=s[i++];
542*37da2899SCharles.Forsyth		}
543*37da2899SCharles.Forsyth	}
544*37da2899SCharles.Forsyth	if (nc > 0)	# fix up last word if present
545*37da2899SCharles.Forsyth		argv[argc++] = buf;
546*37da2899SCharles.Forsyth	ret := array[argc] of string;
547*37da2899SCharles.Forsyth	ret[0:] = argv[0:argc];
548*37da2899SCharles.Forsyth	return ret;
549*37da2899SCharles.Forsyth}
550*37da2899SCharles.Forsyth
551*37da2899SCharles.Forsyth# parses a name by Tcl rules, a valid name is either $foo, $foo(bar)
552*37da2899SCharles.Forsyth# or ${foo}.
553*37da2899SCharles.Forsythparsename(s: string, termchar: int) : (string,string) {
554*37da2899SCharles.Forsyth	ret,arr,rest: string;
555*37da2899SCharles.Forsyth	rets : array of string;
556*37da2899SCharles.Forsyth	if (len s == 0)
557*37da2899SCharles.Forsyth		return (nil,nil);
558*37da2899SCharles.Forsyth	if (s[0]=='{'){
559*37da2899SCharles.Forsyth		(ret,nil)=str->splitl(s,"}");
560*37da2899SCharles.Forsyth		#sys->print("returning [%s]\n",ret[1:]);
561*37da2899SCharles.Forsyth		return (ret[1:],nil);
562*37da2899SCharles.Forsyth	}
563*37da2899SCharles.Forsyth	loop: for (i := 0; i < len s && s[i] != termchar; i++) {
564*37da2899SCharles.Forsyth		case (s[i]) {
565*37da2899SCharles.Forsyth		'a' to 'z' or 'A' to 'Z' or '0' to '9' or '_' =>
566*37da2899SCharles.Forsyth			ret[i] = s[i];
567*37da2899SCharles.Forsyth		* =>
568*37da2899SCharles.Forsyth			break loop;
569*37da2899SCharles.Forsyth		'(' =>
570*37da2899SCharles.Forsyth			arr=ret[0:i];
571*37da2899SCharles.Forsyth			rest=s[i+1:];
572*37da2899SCharles.Forsyth			rets=parsecmd(rest,')',0);
573*37da2899SCharles.Forsyth			# should always be len 1?
574*37da2899SCharles.Forsyth			if (len rets >1)
575*37da2899SCharles.Forsyth				sys->print("len rets>1 in parsename!\n");
576*37da2899SCharles.Forsyth			return (arr,rets[0]);
577*37da2899SCharles.Forsyth		}
578*37da2899SCharles.Forsyth	}
579*37da2899SCharles.Forsyth	return (ret,nil);
580*37da2899SCharles.Forsyth}
581*37da2899SCharles.Forsyth
582*37da2899SCharles.Forsythloadfile(file :string) : string {
583*37da2899SCharles.Forsyth	iob : ref Iobuf;
584*37da2899SCharles.Forsyth	msg,input,line : string;
585*37da2899SCharles.Forsyth	if (file==nil)
586*37da2899SCharles.Forsyth		return nil;
587*37da2899SCharles.Forsyth	iob = bufmod->open(file,bufmod->OREAD);
588*37da2899SCharles.Forsyth	if (iob==nil)
589*37da2899SCharles.Forsyth		return notify(0,sys->sprint(
590*37da2899SCharles.Forsyth			"couldn't read file \"%s\":%r",file));
591*37da2899SCharles.Forsyth	while((input=iob.gets('\n'))!=nil){
592*37da2899SCharles.Forsyth		line+=input;
593*37da2899SCharles.Forsyth		if (finished(line,0)){
594*37da2899SCharles.Forsyth			# put in a return catch here...
595*37da2899SCharles.Forsyth			line = prepass(line);
596*37da2899SCharles.Forsyth			msg=evalcmd(line,0);
597*37da2899SCharles.Forsyth			if (error) return errmsg;
598*37da2899SCharles.Forsyth			line=nil;
599*37da2899SCharles.Forsyth		}
600*37da2899SCharles.Forsyth	}
601*37da2899SCharles.Forsyth	return msg;
602*37da2899SCharles.Forsyth}
603*37da2899SCharles.Forsyth
604*37da2899SCharles.Forsyth
605*37da2899SCharles.Forsyth#unescapes a string. Can do better.....
606*37da2899SCharles.Forsythunesc(s: string) : int {
607*37da2899SCharles.Forsyth	c: int;
608*37da2899SCharles.Forsyth	if (len s == 1) return 0;
609*37da2899SCharles.Forsyth	case s[1] {
610*37da2899SCharles.Forsyth		'a'=>   c = '\a';
611*37da2899SCharles.Forsyth		'n'=>	c = '\n';
612*37da2899SCharles.Forsyth		't'=>	c = '\t';
613*37da2899SCharles.Forsyth		'r'=>	c = '\r';
614*37da2899SCharles.Forsyth		'b'=>	c = '\b';
615*37da2899SCharles.Forsyth		'\\'=>	c = '\\';
616*37da2899SCharles.Forsyth		'}' =>  c = '}';
617*37da2899SCharles.Forsyth		']' =>  c=']';
618*37da2899SCharles.Forsyth		# do hex and octal.
619*37da2899SCharles.Forsyth		* =>	c = 0;
620*37da2899SCharles.Forsyth	}
621*37da2899SCharles.Forsyth	return c;
622*37da2899SCharles.Forsyth}
623*37da2899SCharles.Forsyth
624*37da2899SCharles.Forsyth# prepass a string and replace "\\n[ \t]*" with ' '
625*37da2899SCharles.Forsythprepass(s : string) : string {
626*37da2899SCharles.Forsyth	for(i := 0; i < len s; i++) {
627*37da2899SCharles.Forsyth		if(s[i] != '\\')
628*37da2899SCharles.Forsyth			continue;
629*37da2899SCharles.Forsyth		j:=i;
630*37da2899SCharles.Forsyth		if (s[i+1] == '\n') {
631*37da2899SCharles.Forsyth			s[j]=' ';
632*37da2899SCharles.Forsyth			i++;
633*37da2899SCharles.Forsyth			while(i<len s && (s[i]==' ' || s[i]=='\t'))
634*37da2899SCharles.Forsyth				i++;
635*37da2899SCharles.Forsyth			if (i==len s)
636*37da2899SCharles.Forsyth				s = s[0:j];
637*37da2899SCharles.Forsyth			else
638*37da2899SCharles.Forsyth				s=s[0:j]+s[i+1:];
639*37da2899SCharles.Forsyth		i=j;
640*37da2899SCharles.Forsyth		}
641*37da2899SCharles.Forsyth	}
642*37da2899SCharles.Forsyth	return s;
643*37da2899SCharles.Forsyth}
644*37da2899SCharles.Forsyth
645*37da2899SCharles.Forsythexec(argv : array of string) : string {
646*37da2899SCharles.Forsyth	msg : string;
647*37da2899SCharles.Forsyth	if (argv[0]=="")
648*37da2899SCharles.Forsyth		return nil;
649*37da2899SCharles.Forsyth	case (argv[0]) {
650*37da2899SCharles.Forsyth		"append" =>
651*37da2899SCharles.Forsyth			msg= do_append(argv);
652*37da2899SCharles.Forsyth		"array" =>
653*37da2899SCharles.Forsyth			msg= do_array(argv);
654*37da2899SCharles.Forsyth		"break" or "continue" =>
655*37da2899SCharles.Forsyth			return argv[0];
656*37da2899SCharles.Forsyth		"catch" =>
657*37da2899SCharles.Forsyth			msg=do_catch(argv);
658*37da2899SCharles.Forsyth		"debug" =>
659*37da2899SCharles.Forsyth			msg=do_debug(argv);
660*37da2899SCharles.Forsyth		"dumpstack" =>
661*37da2899SCharles.Forsyth			msg=do_dumpstack(argv);
662*37da2899SCharles.Forsyth		"exit" =>
663*37da2899SCharles.Forsyth			do_exit();
664*37da2899SCharles.Forsyth		"expr" =>
665*37da2899SCharles.Forsyth			msg = do_expr(argv);
666*37da2899SCharles.Forsyth		"eval" =>
667*37da2899SCharles.Forsyth			msg = do_eval(argv);
668*37da2899SCharles.Forsyth		"for" =>
669*37da2899SCharles.Forsyth			msg = do_for(argv);
670*37da2899SCharles.Forsyth		"foreach" =>
671*37da2899SCharles.Forsyth			msg = do_foreach(argv);
672*37da2899SCharles.Forsyth		"format" =>
673*37da2899SCharles.Forsyth			msg = do_string(argv);
674*37da2899SCharles.Forsyth		"global" =>
675*37da2899SCharles.Forsyth			msg = do_global(argv);
676*37da2899SCharles.Forsyth		"if" =>
677*37da2899SCharles.Forsyth			msg = do_if(argv);
678*37da2899SCharles.Forsyth		"incr" =>
679*37da2899SCharles.Forsyth			msg = do_incr(argv);
680*37da2899SCharles.Forsyth		"info" =>
681*37da2899SCharles.Forsyth			msg = do_info(argv);
682*37da2899SCharles.Forsyth		"lappend" =>
683*37da2899SCharles.Forsyth			msg = do_lappend(argv);
684*37da2899SCharles.Forsyth		"level" =>
685*37da2899SCharles.Forsyth			msg=sys->sprint("Current Stack "+
686*37da2899SCharles.Forsyth			    "level is %d",
687*37da2899SCharles.Forsyth				stack->level());
688*37da2899SCharles.Forsyth		"load" =>
689*37da2899SCharles.Forsyth			msg=do_load(argv);
690*37da2899SCharles.Forsyth		"proc" =>
691*37da2899SCharles.Forsyth			msg=do_proc(argv);
692*37da2899SCharles.Forsyth		"return" =>
693*37da2899SCharles.Forsyth			msg=do_return(argv);
694*37da2899SCharles.Forsyth			retfl =1;
695*37da2899SCharles.Forsyth		"set" =>
696*37da2899SCharles.Forsyth			msg = do_set(argv);
697*37da2899SCharles.Forsyth		"source" =>
698*37da2899SCharles.Forsyth			msg = do_source(argv);
699*37da2899SCharles.Forsyth		"string" =>
700*37da2899SCharles.Forsyth			msg = do_string(argv);
701*37da2899SCharles.Forsyth		"switch" =>
702*37da2899SCharles.Forsyth			msg = do_switch(argv);
703*37da2899SCharles.Forsyth		"time" =>
704*37da2899SCharles.Forsyth			msg=do_time(argv);
705*37da2899SCharles.Forsyth		"unset" =>
706*37da2899SCharles.Forsyth			msg = do_unset(argv);
707*37da2899SCharles.Forsyth		"uplevel" =>
708*37da2899SCharles.Forsyth			msg=do_uplevel(argv);
709*37da2899SCharles.Forsyth		"upvar" =>
710*37da2899SCharles.Forsyth			msg=do_upvar(argv);
711*37da2899SCharles.Forsyth		"while" =>
712*37da2899SCharles.Forsyth			msg = do_while(argv);
713*37da2899SCharles.Forsyth		"#" =>
714*37da2899SCharles.Forsyth			msg=nil;
715*37da2899SCharles.Forsyth		* =>
716*37da2899SCharles.Forsyth			msg = uproc(argv);
717*37da2899SCharles.Forsyth	}
718*37da2899SCharles.Forsyth	return msg;
719*37da2899SCharles.Forsyth}
720*37da2899SCharles.Forsyth
721*37da2899SCharles.Forsyth# from here on is the list of commands, alpahabetised, we hope.
722*37da2899SCharles.Forsyth
723*37da2899SCharles.Forsythdo_append(argv :array of string) : string {
724*37da2899SCharles.Forsyth	tab : ref Hash;
725*37da2899SCharles.Forsyth	if (len argv==1 || len argv==2)
726*37da2899SCharles.Forsyth		 return notify(1,
727*37da2899SCharles.Forsyth			"append varName value ?value ...?");
728*37da2899SCharles.Forsyth	name := argv[1];
729*37da2899SCharles.Forsyth	(tab,name)=find_var(name,1);
730*37da2899SCharles.Forsyth	if (tab==nil)
731*37da2899SCharles.Forsyth		return notify(0,name);
732*37da2899SCharles.Forsyth	(found, val) := tab.find(name);
733*37da2899SCharles.Forsyth	for (i:=2;i<len argv;i++)
734*37da2899SCharles.Forsyth		val+=argv[i];
735*37da2899SCharles.Forsyth	tab.insert(name,val);
736*37da2899SCharles.Forsyth	return val;
737*37da2899SCharles.Forsyth}
738*37da2899SCharles.Forsyth
739*37da2899SCharles.Forsythdo_array(argv : array of string) : string {
740*37da2899SCharles.Forsyth	tab : ref Hash;
741*37da2899SCharles.Forsyth	name : string;
742*37da2899SCharles.Forsyth	flag : int;
743*37da2899SCharles.Forsyth	if (len argv!=3)
744*37da2899SCharles.Forsyth		return notify(1,"array [names, size] name");
745*37da2899SCharles.Forsyth	case argv[1] {
746*37da2899SCharles.Forsyth		"names" =>
747*37da2899SCharles.Forsyth			flag=1;
748*37da2899SCharles.Forsyth		"size" =>
749*37da2899SCharles.Forsyth			flag=0;
750*37da2899SCharles.Forsyth		* =>
751*37da2899SCharles.Forsyth			return notify(0,"expexted names or size, got "+argv[1]);
752*37da2899SCharles.Forsyth
753*37da2899SCharles.Forsyth	}
754*37da2899SCharles.Forsyth	(tag,lev,al) := isa(argv[2]);
755*37da2899SCharles.Forsyth	if (tag!=TCL_ARRAY)
756*37da2899SCharles.Forsyth		return notify(0,argv[2]+" isn't an array");
757*37da2899SCharles.Forsyth	(nil,tav,nil):=stack->examine(lev);
758*37da2899SCharles.Forsyth	for (i:=0;i<len tav;i++){
759*37da2899SCharles.Forsyth		(tab,name)=tav[i];
760*37da2899SCharles.Forsyth		if (name==al) break;
761*37da2899SCharles.Forsyth	}
762*37da2899SCharles.Forsyth	if (flag==0)
763*37da2899SCharles.Forsyth		return string tab.lsize;
764*37da2899SCharles.Forsyth	return tab.dump();
765*37da2899SCharles.Forsyth}
766*37da2899SCharles.Forsyth
767*37da2899SCharles.Forsythdo_catch(argv : array of string) : string {
768*37da2899SCharles.Forsyth	if (len argv==1 || len argv > 3)
769*37da2899SCharles.Forsyth		return notify(1,"catch command ?varName?");
770*37da2899SCharles.Forsyth	msg:=evalcmd(argv[1],0);
771*37da2899SCharles.Forsyth	if (len argv==3 && error){
772*37da2899SCharles.Forsyth		(tab,name):=find_var(argv[2],1);
773*37da2899SCharles.Forsyth		if (tab==nil)
774*37da2899SCharles.Forsyth			return notify(0,name);
775*37da2899SCharles.Forsyth		tab.insert(name, msg);
776*37da2899SCharles.Forsyth	}
777*37da2899SCharles.Forsyth	ret:=string error;
778*37da2899SCharles.Forsyth	error=0;
779*37da2899SCharles.Forsyth	return ret;
780*37da2899SCharles.Forsyth}
781*37da2899SCharles.Forsyth
782*37da2899SCharles.Forsythdo_debug(argv : array of string) : string {
783*37da2899SCharles.Forsyth	add : string;
784*37da2899SCharles.Forsyth	if (len argv!=2)
785*37da2899SCharles.Forsyth		return notify(1,"debug");
786*37da2899SCharles.Forsyth	(i,rest):=str->toint(argv[1],10);
787*37da2899SCharles.Forsyth	if (rest!=nil)
788*37da2899SCharles.Forsyth		return notify(0,"Expected integer and got "+argv[1]);
789*37da2899SCharles.Forsyth	tclmod.debug=i;
790*37da2899SCharles.Forsyth	if (tclmod.debug==0)
791*37da2899SCharles.Forsyth		add="off";
792*37da2899SCharles.Forsyth	else
793*37da2899SCharles.Forsyth		add="on";
794*37da2899SCharles.Forsyth	return "debugging is now "+add+" at level"+ string i;
795*37da2899SCharles.Forsyth}
796*37da2899SCharles.Forsyth
797*37da2899SCharles.Forsythdo_dumpstack(argv : array of string) : string {
798*37da2899SCharles.Forsyth	if (len argv!=1)
799*37da2899SCharles.Forsyth		return notify(1,"dumpstack");
800*37da2899SCharles.Forsyth	stack->dump();
801*37da2899SCharles.Forsyth	return nil;
802*37da2899SCharles.Forsyth}
803*37da2899SCharles.Forsyth
804*37da2899SCharles.Forsythdo_eval(argv : array of string) : string {
805*37da2899SCharles.Forsyth	eval_str : string;
806*37da2899SCharles.Forsyth	for(i:=1;i<len argv;i++){
807*37da2899SCharles.Forsyth		eval_str += argv[i];
808*37da2899SCharles.Forsyth		eval_str[len eval_str]=' ';
809*37da2899SCharles.Forsyth	}
810*37da2899SCharles.Forsyth	return evalcmd(eval_str[0:len eval_str -1],0);
811*37da2899SCharles.Forsyth}
812*37da2899SCharles.Forsyth
813*37da2899SCharles.Forsythdo_exit(){
814*37da2899SCharles.Forsyth	kfd := sys->open("#p/"+string mypid+"/ctl", sys->OWRITE);
815*37da2899SCharles.Forsyth	if(kfd == nil)
816*37da2899SCharles.Forsyth		sys->print("error opening pid %d (%r)\n",mypid);
817*37da2899SCharles.Forsyth	sys->fprint(kfd, "killgrp");
818*37da2899SCharles.Forsyth	exit;
819*37da2899SCharles.Forsyth}
820*37da2899SCharles.Forsyth
821*37da2899SCharles.Forsyth
822*37da2899SCharles.Forsyth
823*37da2899SCharles.Forsythdo_expr(argv : array of string) : string {
824*37da2899SCharles.Forsyth	retval : string;
825*37da2899SCharles.Forsyth	for (i:=1;i<len argv;i++){
826*37da2899SCharles.Forsyth		retval+=argv[i];
827*37da2899SCharles.Forsyth		retval[len retval]=' ';
828*37da2899SCharles.Forsyth	}
829*37da2899SCharles.Forsyth	retval=retval[0: len retval -1];
830*37da2899SCharles.Forsyth	argv=parsecmd(retval,0,0);
831*37da2899SCharles.Forsyth	cal:=lookup("calc");
832*37da2899SCharles.Forsyth	(err,ret):= cal->exec(ref tclmod,argv);
833*37da2899SCharles.Forsyth	if (err) return notify(0,ret);
834*37da2899SCharles.Forsyth	return ret;
835*37da2899SCharles.Forsyth}
836*37da2899SCharles.Forsyth
837*37da2899SCharles.Forsyth
838*37da2899SCharles.Forsythdo_for(argv : array of string) : string {
839*37da2899SCharles.Forsyth	if (len argv!=5)
840*37da2899SCharles.Forsyth		return notify(1,"for start test next command");
841*37da2899SCharles.Forsyth	test := array[] of {"expr",argv[2]};
842*37da2899SCharles.Forsyth	evalcmd(argv[1],0);
843*37da2899SCharles.Forsyth	for(;;){
844*37da2899SCharles.Forsyth		msg:=do_expr(test);
845*37da2899SCharles.Forsyth		if (msg=="Error!")
846*37da2899SCharles.Forsyth		return notify(0,sys->sprint(
847*37da2899SCharles.Forsyth			"syntax error in expression \"%s\"",
848*37da2899SCharles.Forsyth					argv[2]));
849*37da2899SCharles.Forsyth		if (msg=="0")
850*37da2899SCharles.Forsyth			return nil;
851*37da2899SCharles.Forsyth		msg=evalcmd(argv[4],0);
852*37da2899SCharles.Forsyth		if (msg=="break")
853*37da2899SCharles.Forsyth			return nil;
854*37da2899SCharles.Forsyth		if (msg=="continue"); #do nothing!
855*37da2899SCharles.Forsyth		evalcmd(argv[3],0);
856*37da2899SCharles.Forsyth		if (error)
857*37da2899SCharles.Forsyth			return errmsg;
858*37da2899SCharles.Forsyth	}
859*37da2899SCharles.Forsyth}
860*37da2899SCharles.Forsyth
861*37da2899SCharles.Forsyth
862*37da2899SCharles.Forsyth
863*37da2899SCharles.Forsythdo_foreach(argv: array of string) : string{
864*37da2899SCharles.Forsyth	tab : ref Hash;
865*37da2899SCharles.Forsyth	if (len argv!=4)
866*37da2899SCharles.Forsyth		return notify(1,"foreach varName list command");
867*37da2899SCharles.Forsyth	name := argv[1];
868*37da2899SCharles.Forsyth	(tab,name)=find_var(name,1);
869*37da2899SCharles.Forsyth	if (tab==nil)
870*37da2899SCharles.Forsyth		return notify(0,name);
871*37da2899SCharles.Forsyth	arr:=utils->break_it(argv[2]);
872*37da2899SCharles.Forsyth	for(i:=0;i<len arr;i++){
873*37da2899SCharles.Forsyth		tab.insert(name,arr[i]);
874*37da2899SCharles.Forsyth		evalcmd(argv[3],0);
875*37da2899SCharles.Forsyth	}
876*37da2899SCharles.Forsyth	return nil;
877*37da2899SCharles.Forsyth}
878*37da2899SCharles.Forsyth
879*37da2899SCharles.Forsyth
880*37da2899SCharles.Forsyth
881*37da2899SCharles.Forsythdo_global(argv : array of string) : string {
882*37da2899SCharles.Forsyth	if (len argv==1)
883*37da2899SCharles.Forsyth		return notify(1,"global varName ?varName ...?");
884*37da2899SCharles.Forsyth	if (symtab==nil)
885*37da2899SCharles.Forsyth		return nil;
886*37da2899SCharles.Forsyth	for (i:=1 ; i < len argv;i++)
887*37da2899SCharles.Forsyth		symtab.insert(argv[i],argv[i],0);
888*37da2899SCharles.Forsyth	return nil;
889*37da2899SCharles.Forsyth}
890*37da2899SCharles.Forsyth
891*37da2899SCharles.Forsyth
892*37da2899SCharles.Forsyth
893*37da2899SCharles.Forsythdo_if(argv : array of string) : string {
894*37da2899SCharles.Forsyth	if (len argv==1)
895*37da2899SCharles.Forsyth		return notify(1,"no expression after \"if\" argument");
896*37da2899SCharles.Forsyth	expr1 := array[] of {"expr",argv[1]};
897*37da2899SCharles.Forsyth	msg:=do_expr(expr1);
898*37da2899SCharles.Forsyth	if (msg=="Error!")
899*37da2899SCharles.Forsyth		return notify(0,sys->sprint(
900*37da2899SCharles.Forsyth			"syntax error in expression \"%s\"",
901*37da2899SCharles.Forsyth					argv[1]));
902*37da2899SCharles.Forsyth	if (len argv==2)
903*37da2899SCharles.Forsyth		return notify(1,sys->sprint(
904*37da2899SCharles.Forsyth			"no script following \""+
905*37da2899SCharles.Forsyth					"%s\" argument",msg));
906*37da2899SCharles.Forsyth	if (msg=="0"){
907*37da2899SCharles.Forsyth		if (len argv>3){
908*37da2899SCharles.Forsyth			if (argv[3]=="else"){
909*37da2899SCharles.Forsyth				if (len argv==4)
910*37da2899SCharles.Forsyth					return notify(1,
911*37da2899SCharles.Forsyth					"no script"+
912*37da2899SCharles.Forsyth				" following \"else\" argument");
913*37da2899SCharles.Forsyth				return evalcmd(argv[4],0);
914*37da2899SCharles.Forsyth			}
915*37da2899SCharles.Forsyth			if (argv[3]=="elseif"){
916*37da2899SCharles.Forsyth				argv[3]="if";
917*37da2899SCharles.Forsyth				return do_if(argv[3:]);
918*37da2899SCharles.Forsyth			}
919*37da2899SCharles.Forsyth		}
920*37da2899SCharles.Forsyth		return nil;
921*37da2899SCharles.Forsyth	}
922*37da2899SCharles.Forsyth	return evalcmd(argv[2],0);
923*37da2899SCharles.Forsyth}
924*37da2899SCharles.Forsyth
925*37da2899SCharles.Forsythdo_incr(argv :array of string) : string {
926*37da2899SCharles.Forsyth	num,xtra : int;
927*37da2899SCharles.Forsyth	rest :string;
928*37da2899SCharles.Forsyth	tab : ref Hash;
929*37da2899SCharles.Forsyth	if (len argv==1)
930*37da2899SCharles.Forsyth		return notify(1,"incr varName ?increment?");
931*37da2899SCharles.Forsyth	name := argv[1];
932*37da2899SCharles.Forsyth	(tab,name)=find_var(name,0); #doesn't create!!
933*37da2899SCharles.Forsyth	if (tab==nil)
934*37da2899SCharles.Forsyth		return notify(0,name);
935*37da2899SCharles.Forsyth	(found, val) := tab.find(name);
936*37da2899SCharles.Forsyth	if (!found)
937*37da2899SCharles.Forsyth		return notify(0,sys->sprint("can't read \"%s\": "
938*37da2899SCharles.Forsyth			+"no such variable",name));
939*37da2899SCharles.Forsyth	(num,rest)=str->toint(val,10);
940*37da2899SCharles.Forsyth	if (rest!=nil)
941*37da2899SCharles.Forsyth		return notify(0,sys->sprint(
942*37da2899SCharles.Forsyth			"expected integer but got \"%s\"",val));
943*37da2899SCharles.Forsyth	if (len argv == 2){
944*37da2899SCharles.Forsyth		num+=1;
945*37da2899SCharles.Forsyth		tab.insert(name,string num);
946*37da2899SCharles.Forsyth	}
947*37da2899SCharles.Forsyth	if (len argv == 3) {
948*37da2899SCharles.Forsyth		val = argv[2];
949*37da2899SCharles.Forsyth		(xtra,rest)=str->toint(val,10);
950*37da2899SCharles.Forsyth		if (rest!=nil)
951*37da2899SCharles.Forsyth			return notify(0,sys->sprint(
952*37da2899SCharles.Forsyth				"expected integer but got \"%s\""
953*37da2899SCharles.Forsyth							,val));
954*37da2899SCharles.Forsyth		num+=xtra;
955*37da2899SCharles.Forsyth		tab.insert(name, string num);
956*37da2899SCharles.Forsyth	}
957*37da2899SCharles.Forsyth	return string num;
958*37da2899SCharles.Forsyth}
959*37da2899SCharles.Forsyth
960*37da2899SCharles.Forsythdo_info(argv : array of string) : string {
961*37da2899SCharles.Forsyth	if (len argv==1)
962*37da2899SCharles.Forsyth		return notify(1,"info option ?arg arg ...?");
963*37da2899SCharles.Forsyth	case argv[1] {
964*37da2899SCharles.Forsyth		"args" =>
965*37da2899SCharles.Forsyth			return do_info_args(argv,0);
966*37da2899SCharles.Forsyth		"body" =>
967*37da2899SCharles.Forsyth			return do_info_args(argv,1);
968*37da2899SCharles.Forsyth		"commands" =>
969*37da2899SCharles.Forsyth			return do_info_commands(argv);
970*37da2899SCharles.Forsyth		"exists" =>
971*37da2899SCharles.Forsyth			return do_info_exists(argv);
972*37da2899SCharles.Forsyth		"procs" =>
973*37da2899SCharles.Forsyth			return do_info_procs(argv);
974*37da2899SCharles.Forsyth
975*37da2899SCharles.Forsyth	}
976*37da2899SCharles.Forsyth	return sys->sprint(
977*37da2899SCharles.Forsyth	"bad option \"%s\": should be args, body, commands, exists, procs",
978*37da2899SCharles.Forsyth			argv[1]);
979*37da2899SCharles.Forsyth}
980*37da2899SCharles.Forsyth
981*37da2899SCharles.Forsythdo_info_args(argv : array of string,body :int) : string {
982*37da2899SCharles.Forsyth	name: string;
983*37da2899SCharles.Forsyth	s : sproc;
984*37da2899SCharles.Forsyth	if (body)
985*37da2899SCharles.Forsyth		name="body";
986*37da2899SCharles.Forsyth	else
987*37da2899SCharles.Forsyth		name="args";
988*37da2899SCharles.Forsyth	if (len argv!=3)
989*37da2899SCharles.Forsyth		return notify(1,"info "+name+" procname");
990*37da2899SCharles.Forsyth	for(i:=0;i<len proctab;i++){
991*37da2899SCharles.Forsyth		s=proctab[i];
992*37da2899SCharles.Forsyth		if (s.name==argv[2])
993*37da2899SCharles.Forsyth			break;
994*37da2899SCharles.Forsyth	}
995*37da2899SCharles.Forsyth	if (i==len proctab)
996*37da2899SCharles.Forsyth		return notify(0,argv[2]+" isn't a procedure.");
997*37da2899SCharles.Forsyth	if (body)
998*37da2899SCharles.Forsyth		return s.script;
999*37da2899SCharles.Forsyth	return s.args;
1000*37da2899SCharles.Forsyth}
1001*37da2899SCharles.Forsyth
1002*37da2899SCharles.Forsythdo_info_commands(argv : array of string) : string {
1003*37da2899SCharles.Forsyth	if (len argv==1 || len argv>3)
1004*37da2899SCharles.Forsyth		return notify(1,"info commands [pattern]");
1005*37da2899SCharles.Forsyth	return libmods.dump();
1006*37da2899SCharles.Forsyth}
1007*37da2899SCharles.Forsyth
1008*37da2899SCharles.Forsythdo_info_exists(argv : array of string) : string {
1009*37da2899SCharles.Forsyth	name, index : string;
1010*37da2899SCharles.Forsyth	tab : ref Hash;
1011*37da2899SCharles.Forsyth	if (len argv!=3)
1012*37da2899SCharles.Forsyth		return notify(1,"info exists varName");
1013*37da2899SCharles.Forsyth	(name,index)=parsename(argv[2],0);
1014*37da2899SCharles.Forsyth	(i,nil,nil):=isa(name);
1015*37da2899SCharles.Forsyth	if (i==TCL_UNKNOWN)
1016*37da2899SCharles.Forsyth		return "0";
1017*37da2899SCharles.Forsyth	if (index==nil)
1018*37da2899SCharles.Forsyth		return "1";
1019*37da2899SCharles.Forsyth	(tab,name)=find_var(argv[2],0);
1020*37da2899SCharles.Forsyth	if (tab==nil)
1021*37da2899SCharles.Forsyth		return "0";
1022*37da2899SCharles.Forsyth	(found, val) := tab.find(name);
1023*37da2899SCharles.Forsyth	if (!found)
1024*37da2899SCharles.Forsyth		return "0";
1025*37da2899SCharles.Forsyth	return "1";
1026*37da2899SCharles.Forsyth
1027*37da2899SCharles.Forsyth}
1028*37da2899SCharles.Forsyth
1029*37da2899SCharles.Forsythdo_info_procs(argv : array of string) : string {
1030*37da2899SCharles.Forsyth	if (len argv==1 || len argv>3)
1031*37da2899SCharles.Forsyth		return notify(1,"info procs [pattern]");
1032*37da2899SCharles.Forsyth	retval : string;
1033*37da2899SCharles.Forsyth	for(i:=0;i<len proctab;i++){
1034*37da2899SCharles.Forsyth		s:=proctab[i];
1035*37da2899SCharles.Forsyth		if (s.name!=nil){
1036*37da2899SCharles.Forsyth			retval+=s.name;
1037*37da2899SCharles.Forsyth			retval[len retval]=' ';
1038*37da2899SCharles.Forsyth		}
1039*37da2899SCharles.Forsyth	}
1040*37da2899SCharles.Forsyth	return retval;
1041*37da2899SCharles.Forsyth}
1042*37da2899SCharles.Forsyth
1043*37da2899SCharles.Forsythdo_lappend(argv : array of string) : string{
1044*37da2899SCharles.Forsyth	tab : ref Hash;
1045*37da2899SCharles.Forsyth	retval :string;
1046*37da2899SCharles.Forsyth	retval=nil;
1047*37da2899SCharles.Forsyth	if (len argv==1 || len argv==2)
1048*37da2899SCharles.Forsyth		return notify(1,
1049*37da2899SCharles.Forsyth			"lappend varName value ?value ...?");
1050*37da2899SCharles.Forsyth	name := argv[1];
1051*37da2899SCharles.Forsyth	(tab,name)=find_var(name,1);
1052*37da2899SCharles.Forsyth	if (tab==nil)
1053*37da2899SCharles.Forsyth		return notify(0,name);
1054*37da2899SCharles.Forsyth	(found, val) := tab.find(name);
1055*37da2899SCharles.Forsyth	for(i:=2;i<len argv;i++){
1056*37da2899SCharles.Forsyth		flag:=0;
1057*37da2899SCharles.Forsyth		if (spaces(argv[i])) flag=1;
1058*37da2899SCharles.Forsyth		if (flag) retval[len retval]='{';
1059*37da2899SCharles.Forsyth		retval += argv[i];
1060*37da2899SCharles.Forsyth		if (flag) retval[len retval]='}';
1061*37da2899SCharles.Forsyth		retval[len retval]=' ';
1062*37da2899SCharles.Forsyth	}
1063*37da2899SCharles.Forsyth	if (retval!=nil)
1064*37da2899SCharles.Forsyth		retval=retval[0:len retval-1];
1065*37da2899SCharles.Forsyth	if (val!=nil)
1066*37da2899SCharles.Forsyth		retval=val+" "+retval;
1067*37da2899SCharles.Forsyth	tab.insert(name,retval);
1068*37da2899SCharles.Forsyth	return retval;
1069*37da2899SCharles.Forsyth}
1070*37da2899SCharles.Forsyth
1071*37da2899SCharles.Forsythspaces(s : string) : int{
1072*37da2899SCharles.Forsyth	if (s==nil) return 1;
1073*37da2899SCharles.Forsyth	for(i:=0;i<len s;i++)
1074*37da2899SCharles.Forsyth		if (s[i]==' ' || s[i]=='\t') return 1;
1075*37da2899SCharles.Forsyth	return 0;
1076*37da2899SCharles.Forsyth}
1077*37da2899SCharles.Forsyth
1078*37da2899SCharles.Forsythdo_load(argv : array of string) : string {
1079*37da2899SCharles.Forsyth	# look for a dis library to load up, then
1080*37da2899SCharles.Forsyth	# add to library array.
1081*37da2899SCharles.Forsyth	if (len argv!=2)
1082*37da2899SCharles.Forsyth		return notify(1,"load libname");
1083*37da2899SCharles.Forsyth	fname:="/dis/lib/tcl_"+argv[1]+".dis";
1084*37da2899SCharles.Forsyth	mod:= load TclLib fname;
1085*37da2899SCharles.Forsyth	if (mod==nil)
1086*37da2899SCharles.Forsyth		return notify(0,
1087*37da2899SCharles.Forsyth			sys->sprint("Cannot load %s",fname));
1088*37da2899SCharles.Forsyth	arr:=mod->about();
1089*37da2899SCharles.Forsyth	for(i:=0;i<len arr;i++)
1090*37da2899SCharles.Forsyth		libmods.insert(arr[i],mod);
1091*37da2899SCharles.Forsyth	return nil;
1092*37da2899SCharles.Forsyth}
1093*37da2899SCharles.Forsyth
1094*37da2899SCharles.Forsyth
1095*37da2899SCharles.Forsythdo_proc(argv : array of string) : string {
1096*37da2899SCharles.Forsyth	if (len argv != 4)
1097*37da2899SCharles.Forsyth		return notify(1,"proc name args body");
1098*37da2899SCharles.Forsyth	for(i:=0;i<len proctab;i++)
1099*37da2899SCharles.Forsyth		if (proctab[i].name==nil ||
1100*37da2899SCharles.Forsyth			proctab[i].name==argv[1]) break;
1101*37da2899SCharles.Forsyth	if (i==len proctab)
1102*37da2899SCharles.Forsyth		return notify(0,"procedure table full!");
1103*37da2899SCharles.Forsyth	proctab[i].name=argv[1];
1104*37da2899SCharles.Forsyth	proctab[i].args=argv[2];
1105*37da2899SCharles.Forsyth	proctab[i].script=argv[3];
1106*37da2899SCharles.Forsyth	return nil;
1107*37da2899SCharles.Forsyth}
1108*37da2899SCharles.Forsyth
1109*37da2899SCharles.Forsythdo_return(argv : array of string) : string {
1110*37da2899SCharles.Forsyth	if (len argv==1)
1111*37da2899SCharles.Forsyth		return nil;
1112*37da2899SCharles.Forsyth	# put in options here.....
1113*37da2899SCharles.Forsyth	return argv[1];
1114*37da2899SCharles.Forsyth}
1115*37da2899SCharles.Forsyth
1116*37da2899SCharles.Forsythdo_set(argv : array of string) : string {
1117*37da2899SCharles.Forsyth	tab : ref Hash;
1118*37da2899SCharles.Forsyth	if (len argv == 1 || len argv > 3)
1119*37da2899SCharles.Forsyth		return notify(1,"set varName ?newValue?");
1120*37da2899SCharles.Forsyth	name := argv[1];
1121*37da2899SCharles.Forsyth	(tab,name)=find_var(name,1);
1122*37da2899SCharles.Forsyth	if (tab==nil)
1123*37da2899SCharles.Forsyth		return notify(0,name);
1124*37da2899SCharles.Forsyth	(found, val) := tab.find(name);
1125*37da2899SCharles.Forsyth	if (len argv == 2)
1126*37da2899SCharles.Forsyth		if (!found)
1127*37da2899SCharles.Forsyth			val = notify(0,sys->sprint(
1128*37da2899SCharles.Forsyth				"can't read \"%s\": "
1129*37da2899SCharles.Forsyth				+"no such variable",name));
1130*37da2899SCharles.Forsyth	if (len argv == 3) {
1131*37da2899SCharles.Forsyth		val = argv[2];
1132*37da2899SCharles.Forsyth		tab.insert(name, val);
1133*37da2899SCharles.Forsyth	}
1134*37da2899SCharles.Forsyth	return val;
1135*37da2899SCharles.Forsyth}
1136*37da2899SCharles.Forsyth
1137*37da2899SCharles.Forsythdo_source(argv : array of string) : string {
1138*37da2899SCharles.Forsyth	if (len argv !=2)
1139*37da2899SCharles.Forsyth		return notify(1,"source fileName");
1140*37da2899SCharles.Forsyth	return loadfile(argv[1]);
1141*37da2899SCharles.Forsyth}
1142*37da2899SCharles.Forsyth
1143*37da2899SCharles.Forsythdo_string(argv : array of string) : string {
1144*37da2899SCharles.Forsyth	stringmod := lookup("string");
1145*37da2899SCharles.Forsyth	if (stringmod==nil)
1146*37da2899SCharles.Forsyth		return notify(0,sys->sprint(
1147*37da2899SCharles.Forsyth		"String Package not loaded (%r)"));
1148*37da2899SCharles.Forsyth	(err,retval):= stringmod->exec(ref tclmod,argv);
1149*37da2899SCharles.Forsyth	if (err) return notify(0,retval);
1150*37da2899SCharles.Forsyth	return retval;
1151*37da2899SCharles.Forsyth}
1152*37da2899SCharles.Forsyth
1153*37da2899SCharles.Forsythdo_switch(argv : array of string) : string {
1154*37da2899SCharles.Forsyth	i:=0;
1155*37da2899SCharles.Forsyth	arr : array of string;
1156*37da2899SCharles.Forsyth	if (len argv < 3)
1157*37da2899SCharles.Forsyth		return notify(1,"switch "
1158*37da2899SCharles.Forsyth			+"?switches? string pattern body ... "+
1159*37da2899SCharles.Forsyth			"?default body?\"");
1160*37da2899SCharles.Forsyth	if (len argv == 3)
1161*37da2899SCharles.Forsyth		arr=utils->break_it(argv[2]);
1162*37da2899SCharles.Forsyth	else
1163*37da2899SCharles.Forsyth		arr=argv[2:];
1164*37da2899SCharles.Forsyth	if (len arr % 2 !=0)
1165*37da2899SCharles.Forsyth		return notify(0,
1166*37da2899SCharles.Forsyth			"extra switch pattern with no body");
1167*37da2899SCharles.Forsyth	for (i=0;i<len arr;i+=2)
1168*37da2899SCharles.Forsyth		if (argv[1]==arr[i])
1169*37da2899SCharles.Forsyth			break;
1170*37da2899SCharles.Forsyth	if (i==len arr){
1171*37da2899SCharles.Forsyth		if (arr[i-2]=="default")
1172*37da2899SCharles.Forsyth			return evalcmd(arr[i-1],0);
1173*37da2899SCharles.Forsyth		else return nil;
1174*37da2899SCharles.Forsyth	}
1175*37da2899SCharles.Forsyth	while (i<len arr && arr[i+1]=="-") i+=2;
1176*37da2899SCharles.Forsyth	return evalcmd(arr[i+1],0);
1177*37da2899SCharles.Forsyth}
1178*37da2899SCharles.Forsyth
1179*37da2899SCharles.Forsythdo_time(argv : array of string) : string {
1180*37da2899SCharles.Forsyth	rest : string;
1181*37da2899SCharles.Forsyth	end,start,times : int;
1182*37da2899SCharles.Forsyth	if (len argv==1 || len argv>3)
1183*37da2899SCharles.Forsyth		return notify(1,"time command ?count?");
1184*37da2899SCharles.Forsyth	if (len argv==2)
1185*37da2899SCharles.Forsyth		times=1;
1186*37da2899SCharles.Forsyth	else{
1187*37da2899SCharles.Forsyth		(times,rest)=str->toint(argv[2],10);
1188*37da2899SCharles.Forsyth		if (rest!=nil)
1189*37da2899SCharles.Forsyth			return notify(0,sys->sprint(
1190*37da2899SCharles.Forsyth				"expected integer but got \"%s\"",argv[2]));
1191*37da2899SCharles.Forsyth	}
1192*37da2899SCharles.Forsyth	start=sys->millisec();
1193*37da2899SCharles.Forsyth	for(i:=0;i<times;i++)
1194*37da2899SCharles.Forsyth		evalcmd(argv[1],0);
1195*37da2899SCharles.Forsyth	end=sys->millisec();
1196*37da2899SCharles.Forsyth	r:= (real end - real start) / real times;
1197*37da2899SCharles.Forsyth	return sys->sprint("%g milliseconds per iteration", r);
1198*37da2899SCharles.Forsyth}
1199*37da2899SCharles.Forsyth
1200*37da2899SCharles.Forsythdo_unset(argv : array of string) : string {
1201*37da2899SCharles.Forsyth	tab : ref Hash;
1202*37da2899SCharles.Forsyth	name: string;
1203*37da2899SCharles.Forsyth	if (len argv == 1)
1204*37da2899SCharles.Forsyth		return notify(1,"unset "+
1205*37da2899SCharles.Forsyth			"varName ?varName ...?");
1206*37da2899SCharles.Forsyth	for(i:=1;i<len argv;i++){
1207*37da2899SCharles.Forsyth		name = argv[i];
1208*37da2899SCharles.Forsyth		(tab,name)=find_var(name,0);
1209*37da2899SCharles.Forsyth		if (tab==nil)
1210*37da2899SCharles.Forsyth			return notify(0,sys->sprint("can't unset \"%s\": no such" +
1211*37da2899SCharles.Forsyth					" variable",name));
1212*37da2899SCharles.Forsyth		tab.delete(name);
1213*37da2899SCharles.Forsyth
1214*37da2899SCharles.Forsyth	}
1215*37da2899SCharles.Forsyth	return nil;
1216*37da2899SCharles.Forsyth}
1217*37da2899SCharles.Forsyth
1218*37da2899SCharles.Forsythdo_uplevel(argv : array of string) : string {
1219*37da2899SCharles.Forsyth	level: int;
1220*37da2899SCharles.Forsyth	rest,scr : string;
1221*37da2899SCharles.Forsyth	scr=nil;
1222*37da2899SCharles.Forsyth	exact:=0;
1223*37da2899SCharles.Forsyth	i:=1;
1224*37da2899SCharles.Forsyth	if (len argv==1)
1225*37da2899SCharles.Forsyth		return notify(1,"uplevel ?level? command ?arg ...?");
1226*37da2899SCharles.Forsyth	if (len argv==2)
1227*37da2899SCharles.Forsyth		level=-1;
1228*37da2899SCharles.Forsyth	else {
1229*37da2899SCharles.Forsyth		lev:=argv[1];
1230*37da2899SCharles.Forsyth		if (lev[0]=='#'){
1231*37da2899SCharles.Forsyth			exact=1;
1232*37da2899SCharles.Forsyth			lev=lev[1:];
1233*37da2899SCharles.Forsyth		}
1234*37da2899SCharles.Forsyth		(level,rest)=str->toint(lev,10);
1235*37da2899SCharles.Forsyth		if (rest!=nil){
1236*37da2899SCharles.Forsyth			i=2;
1237*37da2899SCharles.Forsyth			level =-1;
1238*37da2899SCharles.Forsyth		}
1239*37da2899SCharles.Forsyth	}
1240*37da2899SCharles.Forsyth	oldlev:=stack->level();
1241*37da2899SCharles.Forsyth	if (!exact)
1242*37da2899SCharles.Forsyth		level+=oldlev;
1243*37da2899SCharles.Forsyth	(tnv,tav,sym):=stack->examine(level);
1244*37da2899SCharles.Forsyth	if (tnv==nil && tav==nil)
1245*37da2899SCharles.Forsyth		return notify(0,"bad level "+argv[1]);
1246*37da2899SCharles.Forsyth	if (tclmod.debug==2)
1247*37da2899SCharles.Forsyth		sys->print("In uplevel, current level is %d, moving to level %d\n",
1248*37da2899SCharles.Forsyth				oldlev,level);
1249*37da2899SCharles.Forsyth	stack->move(level);
1250*37da2899SCharles.Forsyth	oldav:=avtab;
1251*37da2899SCharles.Forsyth	oldnv:=nvtab;
1252*37da2899SCharles.Forsyth	oldsym:=symtab;
1253*37da2899SCharles.Forsyth	avtab=tav;
1254*37da2899SCharles.Forsyth	nvtab=tnv;
1255*37da2899SCharles.Forsyth	symtab=sym;
1256*37da2899SCharles.Forsyth	for(;i<len argv;i++)
1257*37da2899SCharles.Forsyth		scr=scr+argv[i]+" ";
1258*37da2899SCharles.Forsyth	msg:=evalcmd(scr[0:len scr-1],0);
1259*37da2899SCharles.Forsyth	avtab=oldav;
1260*37da2899SCharles.Forsyth	nvtab=oldnv;
1261*37da2899SCharles.Forsyth	symtab=oldsym;
1262*37da2899SCharles.Forsyth	ok:=stack->move(oldlev);
1263*37da2899SCharles.Forsyth	if (tclmod.debug==2)
1264*37da2899SCharles.Forsyth		sys->print("Leaving uplevel, current level is %d, moving back to"+
1265*37da2899SCharles.Forsyth				" level %d,move was %d\n",
1266*37da2899SCharles.Forsyth				level,oldlev,ok);
1267*37da2899SCharles.Forsyth	return msg;
1268*37da2899SCharles.Forsyth}
1269*37da2899SCharles.Forsyth
1270*37da2899SCharles.Forsythdo_upvar(argv : array of string) : string {
1271*37da2899SCharles.Forsyth	level:int;
1272*37da2899SCharles.Forsyth	rest:string;
1273*37da2899SCharles.Forsyth	i:=1;
1274*37da2899SCharles.Forsyth	exact:=0;
1275*37da2899SCharles.Forsyth	if (len argv<3 || len argv>4)
1276*37da2899SCharles.Forsyth		return notify(1,"upvar ?level? ThisVar OtherVar");
1277*37da2899SCharles.Forsyth	if (len argv==3)
1278*37da2899SCharles.Forsyth		level=-1;
1279*37da2899SCharles.Forsyth	else {
1280*37da2899SCharles.Forsyth		lev:=argv[1];
1281*37da2899SCharles.Forsyth		if (lev[0]=='#'){
1282*37da2899SCharles.Forsyth			exact=1;
1283*37da2899SCharles.Forsyth			lev=lev[1:];
1284*37da2899SCharles.Forsyth		}
1285*37da2899SCharles.Forsyth		(level,rest)=str->toint(lev,10);
1286*37da2899SCharles.Forsyth		if (rest!=nil){
1287*37da2899SCharles.Forsyth			i=2;
1288*37da2899SCharles.Forsyth			level =-1;
1289*37da2899SCharles.Forsyth		}
1290*37da2899SCharles.Forsyth	}
1291*37da2899SCharles.Forsyth	if (!exact)
1292*37da2899SCharles.Forsyth		level+=stack->level();
1293*37da2899SCharles.Forsyth	symtab.insert(argv[i],argv[i+1],level);
1294*37da2899SCharles.Forsyth	return nil;
1295*37da2899SCharles.Forsyth}
1296*37da2899SCharles.Forsyth
1297*37da2899SCharles.Forsythdo_while(argv : array of string) : string {
1298*37da2899SCharles.Forsyth	if (len argv!=3)
1299*37da2899SCharles.Forsyth		return notify(1,"while test command");
1300*37da2899SCharles.Forsyth	for(;;){
1301*37da2899SCharles.Forsyth		expr1 := array[] of {"expr",argv[1]};
1302*37da2899SCharles.Forsyth		msg:=do_expr(expr1);
1303*37da2899SCharles.Forsyth		if (msg=="Error!")
1304*37da2899SCharles.Forsyth			return notify(0,sys->sprint(
1305*37da2899SCharles.Forsyth			"syntax error in expression \"%s\"",
1306*37da2899SCharles.Forsyth					argv[1]));
1307*37da2899SCharles.Forsyth		if (msg=="0")
1308*37da2899SCharles.Forsyth			return nil;
1309*37da2899SCharles.Forsyth		evalcmd(argv[2],0);
1310*37da2899SCharles.Forsyth		if (error)
1311*37da2899SCharles.Forsyth			return errmsg;
1312*37da2899SCharles.Forsyth	}
1313*37da2899SCharles.Forsyth}
1314*37da2899SCharles.Forsyth
1315*37da2899SCharles.Forsythuproc(argv : array of string) : string {
1316*37da2899SCharles.Forsyth	cmd,add : string;
1317*37da2899SCharles.Forsyth	for(i:=0;i< len proctab;i++)
1318*37da2899SCharles.Forsyth		if (proctab[i].name==argv[0])
1319*37da2899SCharles.Forsyth			break;
1320*37da2899SCharles.Forsyth	if (i==len proctab)
1321*37da2899SCharles.Forsyth		return notify(0,sys->sprint("invalid command name \"%s\"",
1322*37da2899SCharles.Forsyth				argv[0]));
1323*37da2899SCharles.Forsyth	# save tables
1324*37da2899SCharles.Forsyth	# push a newframe
1325*37da2899SCharles.Forsyth	# bind args to arguments
1326*37da2899SCharles.Forsyth	# do cmd
1327*37da2899SCharles.Forsyth	# pop frame
1328*37da2899SCharles.Forsyth	# return msg
1329*37da2899SCharles.Forsyth
1330*37da2899SCharles.Forsyth	# globals are supported, but upvar and uplevel are not!
1331*37da2899SCharles.Forsyth
1332*37da2899SCharles.Forsyth	arg_arr:=utils->break_it(proctab[i].args);
1333*37da2899SCharles.Forsyth	j:=len arg_arr;
1334*37da2899SCharles.Forsyth	if (len argv < j+1 && arg_arr[j-1]!="args"){
1335*37da2899SCharles.Forsyth		j=len argv-1;
1336*37da2899SCharles.Forsyth		return notify(0,sys->sprint(
1337*37da2899SCharles.Forsyth			"no value given for"+
1338*37da2899SCharles.Forsyth			" parameter \"%s\" to \"%s\"",
1339*37da2899SCharles.Forsyth			arg_arr[j],proctab[i].name));
1340*37da2899SCharles.Forsyth	}
1341*37da2899SCharles.Forsyth	if ((len argv > j+1) && arg_arr[j-1]!="args")
1342*37da2899SCharles.Forsyth		return notify(0,"called "+proctab[i].name+
1343*37da2899SCharles.Forsyth					" with too many arguments");
1344*37da2899SCharles.Forsyth	oldavtab:=avtab;
1345*37da2899SCharles.Forsyth	oldnvtab:=nvtab;
1346*37da2899SCharles.Forsyth	oldsymtab:=symtab;
1347*37da2899SCharles.Forsyth	(nvtab,avtab,symtab)=stack->newframe();
1348*37da2899SCharles.Forsyth	for (j=0;j< len arg_arr-1;j++){
1349*37da2899SCharles.Forsyth		cmd="set "+arg_arr[j]+" {"+argv[j+1]+"}";
1350*37da2899SCharles.Forsyth		evalcmd(cmd,0);
1351*37da2899SCharles.Forsyth	}
1352*37da2899SCharles.Forsyth	if (len arg_arr>j && arg_arr[j] != "args") {
1353*37da2899SCharles.Forsyth		cmd="set "+arg_arr[j]+" {"+argv[j+1]+"}";
1354*37da2899SCharles.Forsyth		evalcmd(cmd,0);
1355*37da2899SCharles.Forsyth	}
1356*37da2899SCharles.Forsyth	else {
1357*37da2899SCharles.Forsyth		if (len arg_arr > j) {
1358*37da2899SCharles.Forsyth			if (j+1==len argv)
1359*37da2899SCharles.Forsyth				add="";
1360*37da2899SCharles.Forsyth			else
1361*37da2899SCharles.Forsyth				add=argv[j+1];
1362*37da2899SCharles.Forsyth			cmd="set "+arg_arr[j]+" ";
1363*37da2899SCharles.Forsyth			arglist:="{"+add+" ";
1364*37da2899SCharles.Forsyth			j++;
1365*37da2899SCharles.Forsyth			while(j<len argv-1) {
1366*37da2899SCharles.Forsyth				arglist+=argv[j+1];
1367*37da2899SCharles.Forsyth				arglist[len arglist]=' ';
1368*37da2899SCharles.Forsyth				j++;
1369*37da2899SCharles.Forsyth			}
1370*37da2899SCharles.Forsyth			arglist[len arglist]='}';
1371*37da2899SCharles.Forsyth			cmd+=arglist;
1372*37da2899SCharles.Forsyth			evalcmd(cmd,0);
1373*37da2899SCharles.Forsyth		}
1374*37da2899SCharles.Forsyth	}
1375*37da2899SCharles.Forsyth	msg:=evalcmd(proctab[i].script,0);
1376*37da2899SCharles.Forsyth	stack->pop();
1377*37da2899SCharles.Forsyth	avtab=oldavtab;
1378*37da2899SCharles.Forsyth	nvtab=oldnvtab;
1379*37da2899SCharles.Forsyth	symtab=oldsymtab;
1380*37da2899SCharles.Forsyth	#sys->print("Error is %d, msg is %s\n",error,msg);
1381*37da2899SCharles.Forsyth	return msg;
1382*37da2899SCharles.Forsyth}
1383*37da2899SCharles.Forsyth
1384*37da2899SCharles.Forsythdo_tk(argv : array of string) : string {
1385*37da2899SCharles.Forsyth	tkpack:=lookup("button");
1386*37da2899SCharles.Forsyth	(err,retval):= tkpack->exec(ref tclmod,argv);
1387*37da2899SCharles.Forsyth	if (err) return notify(0,retval);
1388*37da2899SCharles.Forsyth	return retval;
1389*37da2899SCharles.Forsyth}
1390*37da2899SCharles.Forsyth
1391*37da2899SCharles.Forsyth
1392*37da2899SCharles.Forsythlookup(s : string) : TclLib {
1393*37da2899SCharles.Forsyth	(found,mod):=libmods.find(s);
1394*37da2899SCharles.Forsyth	if (!found)
1395*37da2899SCharles.Forsyth		return nil;
1396*37da2899SCharles.Forsyth	return mod;
1397*37da2899SCharles.Forsyth}
1398