xref: /inferno-os/appl/cmd/mash/builtins.b (revision 37da2899f40661e3e9631e497da8dc59b971cbd0)
1implement Mashbuiltin;
2
3#
4#	"builtins" builtin, defines:
5#
6#	env	- print environment or individual elements
7#	eval	- interpret arguments as mash input
8#	exit	- exit toplevel, eval or subshell
9#	load	- load a builtin
10#	prompt	- print or set prompt
11#	quote	- print arguments quoted as input for mash
12#	run	- interpret a file as mash input
13#	status	- report existence of error output
14#	time	- time the execution of a command
15#	whatis	- print variable, function and builtin
16#
17
18include	"mash.m";
19include	"mashparse.m";
20
21mashlib:	Mashlib;
22
23Cmd, Env, Stab:	import mashlib;
24sys, bufio:	import mashlib;
25
26Iobuf:	import bufio;
27
28#
29#	Interface to catch the use as a command.
30#
31init(nil: ref Draw->Context, nil: list of string)
32{
33	ssys := load Sys Sys->PATH;
34	ssys->fprint(ssys->fildes(2), "builtins: cannot run as a command\n");
35	raise "fail: error";
36}
37
38#
39#	Used by whatis.
40#
41name(): string
42{
43	return "builtins";
44}
45
46#
47#	Install commands.
48#
49mashinit(nil: list of string, lib: Mashlib, this: Mashbuiltin, e: ref Env)
50{
51	mashlib = lib;
52	e.defbuiltin("env", this);
53	e.defbuiltin("eval", this);
54	e.defbuiltin("exit", this);
55	e.defbuiltin("load", this);
56	e.defbuiltin("prompt", this);
57	e.defbuiltin("quote", this);
58	e.defbuiltin("run", this);
59	e.defbuiltin("status", this);
60	e.defbuiltin("time", this);
61	e.defbuiltin("whatis", this);
62}
63
64#
65#	Execute a builtin.
66#
67mashcmd(e: ref Env, l: list of string)
68{
69	case hd l {
70	"env" =>
71		l = tl l;
72		if (l == nil) {
73			out := e.outfile();
74			if (out == nil)
75				return;
76			prsymbs(out, e.global, "=");
77			prsymbs(out, e.local, ":=");
78			out.close();
79		} else
80			e.usage("env");
81	"eval" =>
82		eval(e, tl l);
83	"exit" =>
84		raise mashlib->EXIT;
85	"load" =>
86		l = tl l;
87		if (len l == 1)
88			e.doload(hd l);
89		else
90			e.usage("load file");
91	"prompt" =>
92		l = tl l;
93		case len l {
94		0 =>
95			mashlib->prprompt(0);
96		1 =>
97			mashlib->prompt = hd l;
98		2 =>
99			mashlib->prompt = hd l;
100			mashlib->contin = hd tl l;
101		* =>
102			e.usage("prompt [string]");
103		}
104	"quote" =>
105		l = tl l;
106		if (l != nil) {
107			out := e.outfile();
108			if (out == nil)
109				return;
110			f := 0;
111			while (l != nil) {
112				if (f)
113					out.putc(' ');
114				else
115					f = 1;
116				out.puts(mashlib->quote(hd l));
117				l = tl l;
118			}
119			out.putc('\n');
120			out.close();
121		}
122	"run" =>
123		if (!run(e, tl l))
124			e.usage("run [-] [-denx] file [arg ...]");
125	"status" =>
126		l = tl l;
127		if (l != nil)
128			status(e, l);
129		else
130			e.usage("status cmd [arg ...]");
131	"time" =>
132		l = tl l;
133		if (l != nil)
134			time(e, l);
135		else
136			e.usage("time cmd [arg ...]");
137	"whatis" =>
138		l = tl l;
139		if (l != nil) {
140			out := e.outfile();
141			if (out == nil)
142				return;
143			while (l != nil) {
144				whatis(e, out, hd l);
145				l = tl l;
146			}
147			out.close();
148		}
149	}
150}
151
152#
153#	Print a variable and its value.
154#
155prone(out: ref Iobuf, eq, s: string, v: list of string)
156{
157	out.puts(s);
158	out.putc(' ');
159	out.puts(eq);
160	if (v != mashlib->empty) {
161		do {
162			out.putc(' ');
163			out.puts(mashlib->quote(hd v));
164			v = tl v;
165		} while (v != nil);
166	}
167	out.puts(";\n");
168}
169
170#
171#	Print the contents of a symbol table.
172#
173prsymbs(out: ref Iobuf, t: ref Stab, eq: string)
174{
175	if (t == nil)
176		return;
177	for (l := t.all(); l != nil; l = tl l) {
178		s := hd l;
179		v := s.value;
180		if (v != nil)
181			prone(out, eq, s.name, v);
182	}
183}
184
185#
186#	Print variables, functions and builtins.
187#
188whatis(e: ref Env, out: ref Iobuf, s: string)
189{
190	f := 0;
191	v := e.global.find(s);
192	if (v != nil) {
193		if (v.value != nil)
194			prone(out, "=", s, v.value);
195		if (v.func != nil) {
196			out.puts("fn ");
197			out.puts(s);
198			out.puts(" { ");
199			out.puts(v.func.text());
200			out.puts(" };\n");
201		}
202		if (v.builtin != nil) {
203			out.puts("load ");
204			out.puts(v.builtin->name());
205			out.puts("; ");
206			out.puts(s);
207			out.puts(";\n");
208		}
209		f = 1;
210	}
211	if (e.local != nil) {
212		v = e.local.find(s);
213		if (v != nil) {
214			prone(out, ":=", s, v.value);
215			f = 1;
216		}
217	}
218	if (!f) {
219		out.puts(s);
220		out.puts(": not found\n");
221	}
222}
223
224#
225#	Catenate arguments and interpret as mash input.
226#
227eval(e: ref Env, l: list of string)
228{
229	s: string;
230	while (l != nil) {
231		s = s + " " + hd l;
232		l = tl l;
233	}
234	e = e.copy();
235	e.flags &= ~mashlib->EInter;
236	e.sopen(s);
237	mashlib->parse->parse(e);
238}
239
240#
241#	Interpret file as mash input.
242#
243run(e: ref Env, l: list of string): int
244{
245	f := 0;
246	if (l == nil)
247		return 0;
248	e = e.copy();
249	s := hd l;
250	while (s[0] == '-') {
251		if (s == "-")
252			f = 1;
253		else {
254			for (i := 1; i < len s; i++) {
255				case s[i] {
256				'd' =>
257					e.flags |= mashlib->EDumping;
258				'e' =>
259					e.flags |= mashlib->ERaise;
260				'n' =>
261					e.flags |= mashlib->ENoxeq;
262				'x' =>
263					e.flags |= mashlib->EEcho;
264				* =>
265					return 0;
266				}
267			}
268		}
269		l = tl l;
270		if (l == nil)
271			return 0;
272		s = hd l;
273	}
274	fd := sys->open(s, Sys->OREAD);
275	if (fd == nil) {
276		err := mashlib->errstr();
277		if (mashlib->nonexistent(err) && s[0] != '/' && s[0:2] != "./") {
278			fd = sys->open(mashlib->LIB + s, Sys->OREAD);
279			if (fd == nil)
280				err = mashlib->errstr();
281			else
282				s = mashlib->LIB + s;
283		}
284		if (fd == nil) {
285			if (!f)
286				e.report(s + ": " + err);
287			return 1;
288		}
289	}
290	e.local = Stab.new();
291	e.local.assign(mashlib->ARGS, tl l);
292	e.flags &= ~mashlib->EInter;
293	e.fopen(fd, s);
294	mashlib->parse->parse(e);
295	return 1;
296}
297
298#
299#	Run a command and report true on no error output.
300#
301status(e: ref Env, l: list of string)
302{
303	in := child(e, l);
304	if (in == nil)
305		return;
306	b := array[256] of byte;
307	n := sys->read(in, b, len b);
308	if (n != 0) {
309		while (n > 0)
310			n = sys->read(in, b, len b);
311		if (n < 0)
312			e.couldnot("read", "pipe");
313	} else
314		e.output(Mashlib->TRUE);
315}
316
317#
318#	Status env child.
319#
320child(e: ref Env, l: list of string): ref Sys->FD
321{
322	e = e.copy();
323	fds := e.pipe();
324	if (fds == nil)
325		return nil;
326	if (sys->dup(fds[0].fd, 2) < 0) {
327		e.couldnot("dup", "pipe");
328		return nil;
329	}
330	t := e.stderr;
331	e.stderr = fds[0];
332	e.runit(l, nil, nil, 0);
333	e.stderr = t;
334	sys->dup(t.fd, 2);
335	return fds[1];
336}
337
338#
339#	Time the execution of a command.
340#
341time(e: ref Env, l: list of string)
342{
343	t1 := sys->millisec();
344	e.runit(l, nil, nil, 1);
345	t2 := sys->millisec();
346	sys->fprint(e.stderr, "%.4g\n", real (t2 - t1) / 1000.0);
347}
348