xref: /inferno-os/appl/cmd/tkcmd.b (revision 37da2899f40661e3e9631e497da8dc59b971cbd0)
1implement Tkcmd;
2
3include "sys.m";
4	sys: Sys;
5	stderr: ref Sys->FD;
6include "draw.m";
7	draw: Draw;
8	Display, Image, Point: import draw;
9include "tk.m";
10	tk: Tk;
11include "tkclient.m";
12	tkclient: Tkclient;
13include "bufio.m";
14include "arg.m";
15
16Tkcmd : module {
17	init:	fn(ctxt: ref Draw->Context, argv: list of string);
18};
19
20usage()
21{
22	sys->print("usage: tkcmd [-iu] [toplevelarg]\n");
23	raise "fail:usage";
24}
25
26badmodule(m: string)
27{
28		sys->fprint(stderr, "tkcmd: cannot load %s: %r\n", m);
29		raise "fail:bad module";
30}
31
32init(ctxt: ref Draw->Context, argv: list of string)
33{
34	sys  = load Sys  Sys->PATH;
35	stderr = sys->fildes(2);
36	draw = load Draw Draw->PATH;
37	tk = load Tk   Tk->PATH;
38	if (tk == nil)
39		badmodule(Tk->PATH);
40	tkclient = load Tkclient Tkclient->PATH;
41	if (tkclient==nil)
42		badmodule(Tkclient->PATH);
43	arg := load Arg Arg->PATH;
44	if (arg == nil)
45		badmodule(Arg->PATH);
46
47	arg->init(argv);
48	update := 1;
49	interactive := isconsole(sys->fildes(0));
50	while ((opt := arg->opt()) != 0) {
51		case opt {
52		'i' =>
53			interactive = 1;
54		'u' =>
55			update = 0;
56		* =>
57			usage();
58		}
59	}
60	argv = arg->argv();
61	arg = nil;
62	tkarg := "";
63	if (argv != nil) {
64		if (tl argv != nil)
65			usage();
66		tkarg = hd argv;
67	}
68
69	sys->pctl(Sys->NEWPGRP, nil);
70	tkclient->init();
71	shellit(ctxt, tkarg, interactive, update);
72}
73
74isconsole(fd: ref Sys->FD): int
75{
76	(ok1, d1) := sys->fstat(fd);
77	(ok2, d2) := sys->stat("/dev/cons");
78	if (ok1 < 0 || ok2 < 0)
79		return 0;
80	return d1.dtype == d2.dtype && d1.qid.path == d2.qid.path;
81}
82
83shellit(ctxt: ref Draw->Context, arg: string, interactive, update: int)
84{
85	(Wwsh, winctl) := tkclient->toplevel(ctxt, arg, "Tk", Tkclient->Appl);
86	tkclient->onscreen(Wwsh, nil);
87	tkclient->startinput(Wwsh, "ptr" :: "kbd" :: nil);
88	wm := Wwsh.ctxt;
89	if(update)
90		tk->cmd(Wwsh, "update");
91	ps1 := "";
92	ps2 := "";
93	if (!interactive)
94		ps1 = ps2 = "";
95
96	lines := chan of string;
97	sync := chan of int;
98	spawn grab_lines(ps1, ps2, lines, sync);
99	output := chan of string;
100	tk->namechan(Wwsh, output, "stdout");
101	pid := <-sync;
102Loop:
103	for(;;) alt {
104	c := <-wm.kbd =>
105		tk->keyboard(Wwsh, c);
106	m := <-wm.ptr =>
107		tk->pointer(Wwsh, *m);
108	c := <-wm.ctl or
109	c = <-Wwsh.wreq =>
110		tkclient->wmctl(Wwsh, c);
111	line := <-lines =>
112		if (line == nil)
113			break Loop;
114		if (line[0] == '#')
115			break;
116		line = line[0:len line - 1];
117		result := tk->cmd(Wwsh, line);
118		if (result != nil)
119			sys->print("#%s\n", result);
120		if (update)
121			tk->cmd(Wwsh, "update");
122		sys->print("%s", ps1);
123	menu := <-winctl =>
124		tkclient->wmctl(Wwsh, menu);
125	s := <-output =>
126		sys->print("#<stdout>%s\n", s);
127		sys->print("%s", ps1);
128	}
129}
130
131grab_lines(new_inp, unfin: string, lines: chan of string, sync: chan of int)
132{
133	sync <-= sys->pctl(0, nil);
134	{
135		bufmod := load Bufio Bufio->PATH;
136		Iobuf:	import bufmod;
137		if (bufmod == nil) {
138			lines <-= nil;
139			return;
140		}
141		sys->print("%s", new_inp);
142		iob := bufmod->fopen(sys->fildes(0),bufmod->OREAD);
143		if (iob==nil){
144			sys->fprint(stderr, "tkcmd: cannot open stdin for reading.\n");
145			lines <-= nil;
146			return;
147		}
148		line := "";
149		while((input := iob.gets('\n')) != nil) {
150			line+=input;
151			if (!finished(line,0))
152				sys->print("%s", unfin);
153			else{
154				lines <-= line;
155				line=nil;
156			}
157		}
158		lines <-= nil;
159	}exception e{
160	"*" =>
161		sys->fprint(stderr, "tkcmd: fail: %s\n", e);
162		lines <-= nil;
163	}
164}
165
166# returns 1 if the line has matching braces, brackets and
167# double-quotes and does not end in "\\\n"
168finished(s : string, termchar : int) : int {
169	cb:=0;
170	dq:=0;
171	sb:=0;
172	if (s==nil) return 1;
173	if (termchar=='}') cb++;
174	if (termchar==']') sb++;
175	if (len s > 1 && s[len s -2]=='\\')
176		return 0;
177	if (s[0]=='{') cb++;
178	if (s[0]=='}' && cb>0) cb--;
179	if (s[0]=='[') sb++;
180	if (s[0]==']' && sb>0) sb--;
181	if (s[0]=='"') dq=1-dq;
182	for(i:=1;i<len s;i++){
183		if (s[i]=='{' && s[i-1]!='\\') cb++;
184		if (s[i]=='}' && s[i-1]!='\\' && cb>0) cb--;
185		if (s[i]=='[' && s[i-1]!='\\') sb++;
186		if (s[i]==']' && s[i-1]!='\\' && sb>0) sb--;
187		if (s[i]=='"' && s[i-1]!='\\') dq=1-dq;
188	}
189	return (cb==0 && sb==0 && dq==0);
190}
191