diff options
Diffstat (limited to 'appl/cmd/mash/builtins.b')
| -rw-r--r-- | appl/cmd/mash/builtins.b | 347 |
1 files changed, 347 insertions, 0 deletions
diff --git a/appl/cmd/mash/builtins.b b/appl/cmd/mash/builtins.b new file mode 100644 index 00000000..b4374581 --- /dev/null +++ b/appl/cmd/mash/builtins.b @@ -0,0 +1,347 @@ +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); +} |
