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