ref: babf901b4a508c3ec5d1f89655f10377bbdf9637
dir: /appl/cmd/mash/builtins.b/
implement Mashbuiltin;
#
# "builtins" builtin, defines:
#
# env - print environment or individual elements
# eval - interpret arguments as mash input
# exit - exit toplevel, eval or subshell
# load - load a builtin
# prompt - print or set prompt
# quote - print arguments quoted as input for mash
# run - interpret a file as mash input
# status - report existence of error output
# time - time the execution of a command
# whatis - print variable, function and builtin
#
include "mash.m";
include "mashparse.m";
mashlib: Mashlib;
Cmd, Env, Stab: import mashlib;
sys, bufio: import mashlib;
Iobuf: import bufio;
#
# Interface to catch the use as a command.
#
init(nil: ref Draw->Context, nil: list of string)
{
ssys := load Sys Sys->PATH;
ssys->fprint(ssys->fildes(2), "builtins: cannot run as a command\n");
raise "fail: error";
}
#
# Used by whatis.
#
name(): string
{
return "builtins";
}
#
# Install commands.
#
mashinit(nil: list of string, lib: Mashlib, this: Mashbuiltin, e: ref Env)
{
mashlib = lib;
e.defbuiltin("env", this);
e.defbuiltin("eval", this);
e.defbuiltin("exit", this);
e.defbuiltin("load", this);
e.defbuiltin("prompt", this);
e.defbuiltin("quote", this);
e.defbuiltin("run", this);
e.defbuiltin("status", this);
e.defbuiltin("time", this);
e.defbuiltin("whatis", this);
}
#
# Execute a builtin.
#
mashcmd(e: ref Env, l: list of string)
{
case hd l {
"env" =>
l = tl l;
if (l == nil) {
out := e.outfile();
if (out == nil)
return;
prsymbs(out, e.global, "=");
prsymbs(out, e.local, ":=");
out.close();
} else
e.usage("env");
"eval" =>
eval(e, tl l);
"exit" =>
raise mashlib->EXIT;
"load" =>
l = tl l;
if (len l == 1)
e.doload(hd l);
else
e.usage("load file");
"prompt" =>
l = tl l;
case len l {
0 =>
mashlib->prprompt(0);
1 =>
mashlib->prompt = hd l;
2 =>
mashlib->prompt = hd l;
mashlib->contin = hd tl l;
* =>
e.usage("prompt [string]");
}
"quote" =>
l = tl l;
if (l != nil) {
out := e.outfile();
if (out == nil)
return;
f := 0;
while (l != nil) {
if (f)
out.putc(' ');
else
f = 1;
out.puts(mashlib->quote(hd l));
l = tl l;
}
out.putc('\n');
out.close();
}
"run" =>
if (!run(e, tl l))
e.usage("run [-] [-denx] file [arg ...]");
"status" =>
l = tl l;
if (l != nil)
status(e, l);
else
e.usage("status cmd [arg ...]");
"time" =>
l = tl l;
if (l != nil)
time(e, l);
else
e.usage("time cmd [arg ...]");
"whatis" =>
l = tl l;
if (l != nil) {
out := e.outfile();
if (out == nil)
return;
while (l != nil) {
whatis(e, out, hd l);
l = tl l;
}
out.close();
}
}
}
#
# Print a variable and its value.
#
prone(out: ref Iobuf, eq, s: string, v: list of string)
{
out.puts(s);
out.putc(' ');
out.puts(eq);
if (v != mashlib->empty) {
do {
out.putc(' ');
out.puts(mashlib->quote(hd v));
v = tl v;
} while (v != nil);
}
out.puts(";\n");
}
#
# Print the contents of a symbol table.
#
prsymbs(out: ref Iobuf, t: ref Stab, eq: string)
{
if (t == nil)
return;
for (l := t.all(); l != nil; l = tl l) {
s := hd l;
v := s.value;
if (v != nil)
prone(out, eq, s.name, v);
}
}
#
# Print variables, functions and builtins.
#
whatis(e: ref Env, out: ref Iobuf, s: string)
{
f := 0;
v := e.global.find(s);
if (v != nil) {
if (v.value != nil)
prone(out, "=", s, v.value);
if (v.func != nil) {
out.puts("fn ");
out.puts(s);
out.puts(" { ");
out.puts(v.func.text());
out.puts(" };\n");
}
if (v.builtin != nil) {
out.puts("load ");
out.puts(v.builtin->name());
out.puts("; ");
out.puts(s);
out.puts(";\n");
}
f = 1;
}
if (e.local != nil) {
v = e.local.find(s);
if (v != nil) {
prone(out, ":=", s, v.value);
f = 1;
}
}
if (!f) {
out.puts(s);
out.puts(": not found\n");
}
}
#
# Catenate arguments and interpret as mash input.
#
eval(e: ref Env, l: list of string)
{
s: string;
while (l != nil) {
s = s + " " + hd l;
l = tl l;
}
e = e.copy();
e.flags &= ~mashlib->EInter;
e.sopen(s);
mashlib->parse->parse(e);
}
#
# Interpret file as mash input.
#
run(e: ref Env, l: list of string): int
{
f := 0;
if (l == nil)
return 0;
e = e.copy();
s := hd l;
while (s[0] == '-') {
if (s == "-")
f = 1;
else {
for (i := 1; i < len s; i++) {
case s[i] {
'd' =>
e.flags |= mashlib->EDumping;
'e' =>
e.flags |= mashlib->ERaise;
'n' =>
e.flags |= mashlib->ENoxeq;
'x' =>
e.flags |= mashlib->EEcho;
* =>
return 0;
}
}
}
l = tl l;
if (l == nil)
return 0;
s = hd l;
}
fd := sys->open(s, Sys->OREAD);
if (fd == nil) {
err := mashlib->errstr();
if (mashlib->nonexistent(err) && s[0] != '/' && s[0:2] != "./") {
fd = sys->open(mashlib->LIB + s, Sys->OREAD);
if (fd == nil)
err = mashlib->errstr();
else
s = mashlib->LIB + s;
}
if (fd == nil) {
if (!f)
e.report(s + ": " + err);
return 1;
}
}
e.local = Stab.new();
e.local.assign(mashlib->ARGS, tl l);
e.flags &= ~mashlib->EInter;
e.fopen(fd, s);
mashlib->parse->parse(e);
return 1;
}
#
# Run a command and report true on no error output.
#
status(e: ref Env, l: list of string)
{
in := child(e, l);
if (in == nil)
return;
b := array[256] of byte;
n := sys->read(in, b, len b);
if (n != 0) {
while (n > 0)
n = sys->read(in, b, len b);
if (n < 0)
e.couldnot("read", "pipe");
} else
e.output(Mashlib->TRUE);
}
#
# Status env child.
#
child(e: ref Env, l: list of string): ref Sys->FD
{
e = e.copy();
fds := e.pipe();
if (fds == nil)
return nil;
if (sys->dup(fds[0].fd, 2) < 0) {
e.couldnot("dup", "pipe");
return nil;
}
t := e.stderr;
e.stderr = fds[0];
e.runit(l, nil, nil, 0);
e.stderr = t;
sys->dup(t.fd, 2);
return fds[1];
}
#
# Time the execution of a command.
#
time(e: ref Env, l: list of string)
{
t1 := sys->millisec();
e.runit(l, nil, nil, 1);
t2 := sys->millisec();
sys->fprint(e.stderr, "%.4g\n", real (t2 - t1) / 1000.0);
}