diff options
| author | Charles.Forsyth <devnull@localhost> | 2006-12-22 17:07:39 +0000 |
|---|---|---|
| committer | Charles.Forsyth <devnull@localhost> | 2006-12-22 17:07:39 +0000 |
| commit | 37da2899f40661e3e9631e497da8dc59b971cbd0 (patch) | |
| tree | cbc6d4680e347d906f5fa7fca73214418741df72 /appl/tiny | |
| parent | 54bc8ff236ac10b3eaa928fd6bcfc0cdb2ba46ae (diff) | |
20060303a
Diffstat (limited to 'appl/tiny')
| -rw-r--r-- | appl/tiny/mkfile | 18 | ||||
| -rw-r--r-- | appl/tiny/rm.b | 23 | ||||
| -rw-r--r-- | appl/tiny/sh.b | 628 |
3 files changed, 669 insertions, 0 deletions
diff --git a/appl/tiny/mkfile b/appl/tiny/mkfile new file mode 100644 index 00000000..8ffc3d60 --- /dev/null +++ b/appl/tiny/mkfile @@ -0,0 +1,18 @@ +<../../mkconfig + +TARG=\ + rm.dis\ + sh.dis\ + +MODULES=\ + +SYSMODULES=\ + sys.m\ + draw.m\ + env.m\ + filepat.m\ + bufio.m\ + +DISBIN=$ROOT/dis/tiny + +<$ROOT/mkfiles/mkdis diff --git a/appl/tiny/rm.b b/appl/tiny/rm.b new file mode 100644 index 00000000..81179869 --- /dev/null +++ b/appl/tiny/rm.b @@ -0,0 +1,23 @@ +implement Rm; + +include "sys.m"; + sys: Sys; +include "draw.m"; + +Rm: module +{ + init: fn(ctxt: ref Draw->Context, argv: list of string); +}; + +init(nil: ref Draw->Context, argv: list of string) +{ + sys = load Sys Sys->PATH; + stderr := sys->fildes(2); + + argv = tl argv; + while(argv != nil) { + if(sys->remove(hd argv) < 0) + sys->fprint(stderr, "rm: %s: %r\n", hd argv); + argv = tl argv; + } +} diff --git a/appl/tiny/sh.b b/appl/tiny/sh.b new file mode 100644 index 00000000..1e755a67 --- /dev/null +++ b/appl/tiny/sh.b @@ -0,0 +1,628 @@ +implement Sh; + +include "sys.m"; + sys: Sys; + FD: import Sys; + +include "draw.m"; + Context: import Draw; + +include "filepat.m"; + filepat: Filepat; + nofilepat := 0; # true if load Filepat has failed. + +include "bufio.m"; + bufio: Bufio; + Iobuf: import bufio; + +include "env.m"; + env: Env; + +stdin: ref FD; +stderr: ref FD; +waitfd: ref FD; + +Quoted: con '\uFFF0'; +stringQuoted: con "\uFFF0"; + +Sh: module +{ + init: fn(ctxt: ref Context, argv: list of string); +}; + +Command: adt +{ + args: list of string; + inf, outf: string; + append: int; +}; + +Async, Seq: con iota; + +Pipeline: adt +{ + cmds: list of ref Command; + term: int; +}; + +usage() +{ + sys->fprint(stderr, "Usage: sh [-n] [-c cmd] [file]\n"); +} + +init(ctxt: ref Context, argv: list of string) +{ + n: int; + arg: list of string; + buf := array[1024] of byte; + + sys = load Sys Sys->PATH; + env = load Env Env->PATH; + + stderr = sys->fildes(2); + + waitfd = sys->open("#p/"+string sys->pctl(0, nil)+"/wait", sys->OREAD); + if(waitfd == nil){ + sys->fprint(stderr, "sh: open wait: %r\n"); + return; + } + + eflag := nflag := lflag := 0; + cmd: string; + if(argv != nil) + argv = tl argv; + for(; argv != nil && len hd argv && (hd argv)[0]=='-'; argv = tl argv) + case hd argv { + "-e" => + eflag = 1; + "-n" => + nflag = 1; + "-l" => + lflag = 1; + "-c" => + argv = tl argv; + if(len argv != 1){ + usage(); + return; + } + cmd = hd argv; + * => + usage(); + return; + } + + if (lflag) + startup(ctxt); + + if(eflag == 0) + sys->pctl(sys->FORKENV, nil); + if(nflag == 0) + sys->pctl(sys->FORKNS, nil); + if(cmd != nil){ + arg = tokenize(cmd+"\n"); + if(arg != nil) + runit(ctxt, parseit(arg)); + return; + } + if(argv != nil){ + script(ctxt, hd argv); + return; + } + + stdin = sys->fildes(0); + + prompt := sysname() + "$ "; + for(;;){ + sys->print("%s", prompt); + n = sys->read(stdin, buf, len buf); + if(n <= 0) + break; + arg = tokenize(string buf[0:n]); + if(arg != nil) + runit(ctxt, parseit(arg)); + } +} + +rev(arg: list of string): list of string +{ + ret: list of string; + + while(arg != nil){ + ret = hd arg :: ret; + arg = tl arg; + } + return ret; +} + +waitfor(pid: int) +{ + if(pid <= 0) + return; + buf := array[sys->WAITLEN] of byte; + status := ""; + for(;;){ + n := sys->read(waitfd, buf, len buf); + if(n < 0){ + sys->fprint(stderr, "sh: read wait: %r\n"); + return; + } + status = string buf[0:n]; + if(status[len status-1] != ':') + sys->fprint(stderr, "%s\n", status); + who := int status; + if(who != 0){ + if(who == pid) + return; + } + } +} + +mkprog(ctxt: ref Context, arg: list of string, infd, outfd: ref Sys->FD, waitpid: chan of int) +{ + fds := list of {0, 1, 2}; + if(infd != nil) + fds = infd.fd :: fds; + if(outfd != nil) + fds = outfd.fd :: fds; + pid := sys->pctl(sys->NEWFD, fds); + console := sys->fildes(2); + + if(infd != nil){ + sys->dup(infd.fd, 0); + infd = nil; + } + if(outfd != nil){ + sys->dup(outfd.fd, 1); + outfd = nil; + } + + waitpid <-= pid; + + if(pid < 0 || arg == nil) + return; + + { + exec(ctxt, arg, console); + }exception{ + "fail:*" => + #sys->fprint(console, "%s:%s\n", hd arg, e.name[5:]); + exit; + "write on closed pipe" => + #sys->fprint(console, "%s: %s\n", hd arg, e.name); + exit; + } +} + +exec(ctxt: ref Context, args: list of string, console: ref Sys->FD) +{ + if (args == nil) + return; + cmd := hd args; + file := cmd; + + if(len file<4 || file[len file-4:]!=".dis") + file += ".dis"; + + c := load Sh file; + if(c == nil) { + err := sys->sprint("%r"); + if(err != "permission denied" && err != "access permission denied" && file[0]!='/' && file[0:2]!="./"){ + c = load Sh "/dis/"+file; + if(c == nil) + err = sys->sprint("%r"); + } + if(c == nil){ + sys->fprint(console, "%s: %s\n", cmd, err); + return; + } + } + + c->init(ctxt, args); +} + +script(ctxt: ref Context, src: string) +{ + bufio = load Bufio Bufio->PATH; + if(bufio == nil){ + sys->fprint(stderr, "sh: load bufio: %r\n"); + return; + } + + f := bufio->open(src, Bufio->OREAD); + if(f == nil){ + sys->fprint(stderr, "sh: open %s: %r\n", src); + return; + } + for(;;){ + s := f.gets('\n'); + if(s == nil) + break; + arg := tokenize(s); + if(arg != nil) + runit(ctxt, parseit(arg)); + } +} + +sysname(): string +{ + fd := sys->open("#c/sysname", sys->OREAD); + if(fd == nil) + return "anon"; + buf := array[128] of byte; + n := sys->read(fd, buf, len buf); + if(n < 0) + return "anon"; + return string buf[0:n]; +} + +# Lexer. + +tokenize(s: string): list of string +{ + tok: list of string; + token := ""; + instring := 0; + +loop: + for(i:=0; i<len s; i++) { + if(instring) { + if(s[i] != '\'') + token = addchar(token, s[i]); + else if(i == len s-1 || s[i+1] != '\'') { + if(i == len s-1 || s[i+1] == ' ' || s[i+1] == '\t' || s[i+1] == '\n'){ + tok = token :: tok; + token = ""; + } + instring = 0; + } else { + token[len token] = '\''; + i++; + } + continue; + } + case s[i] { + ' ' or '\t' or '\n' or '#' or + '\'' or '|' or '&' or ';' or + '>' or '<' or '\r' => + if(token != "" && s[i]!='\''){ + tok = token :: tok; + token = ""; + } + case s[i] { + '#' => + break loop; + '\'' => + instring = 1; + '>' => + ss := ""; + ss[0] = s[i]; + if(i<len s-1 && s[i+1]==s[i]) + ss[1] = s[i++]; + tok = ss :: tok; + '|' or '&' or ';' or '<' => + ss := ""; + ss[0] = s[i]; + tok = ss :: tok; + } + * => + token[len token] = s[i]; + } + } + if(instring){ + sys->fprint(stderr, "sh: unmatched quote\n"); + return nil; + } + return rev(tok); +} + +ismeta(char: int): int +{ + case char { + '*' or '[' or '?' or + '#' or '\'' or '|' or + '&' or ';' or '>' or + '<' => + return 1; + } + return 0; +} + +addchar(token: string, char: int): string +{ + if(ismeta(char) && (len token==0 || token[0]!=Quoted)) + token = stringQuoted + token; + token[len token] = char; + return token; +} + +# Parser. + +getcommand(words: list of string): (ref Command, list of string) +{ + args: list of string; + word: string; + si, so: string; + append := 0; + +gather: + do { + word = hd words; + + case word { + ">" or ">>" => + if(so != nil) + return (nil, nil); + + words = tl words; + + if(words == nil) + return (nil, nil); + + so = hd words; + if(len so>0 && so[0]==Quoted) + so = so[1:]; + if(word == ">>") + append = 1; + "<" => + if(si != nil) + return (nil, nil); + + words = tl words; + + if(words == nil) + return (nil, nil); + + si = hd words; + if(len si>0 && si[0]==Quoted) + si = si[1:]; + "|" or ";" or "&" => + break gather; + * => + files := doexpand(word); + while(files != nil){ + args = hd files :: args; + files = tl files; + } + } + + words = tl words; + } while (words != nil); + + return (ref Command(rev(args), si, so, append), words); +} + +doexpand(file: string): list of string +{ + if(file == nil) + return file :: nil; + if(len file>0 && file[0]==Quoted) + return file[1:] :: nil; + if (nofilepat) + return file :: nil; + for(i:=0; i<len file; i++) + if(file[i]=='*' || file[i]=='[' || file[i]=='?'){ + if(filepat == nil) { + if ((filepat = load Filepat Filepat->PATH) == nil) { + sys->fprint(stderr, "sh: warning: cannot load %s: %r\n", + Filepat->PATH); + nofilepat = 1; + return file :: nil; + } + } + files := filepat->expand(file); + if(files != nil) + return files; + break; + } + return file :: nil; +} + +revc(arg: list of ref Command): list of ref Command +{ + ret: list of ref Command; + while(arg != nil) { + ret = hd arg :: ret; + arg = tl arg; + } + return ret; +} + +getpipe(words: list of string): (ref Pipeline, list of string) +{ + cmds: list of ref Command; + cur: ref Command; + word: string; + + term := Seq; +gather: + while(words != nil) { + word = hd words; + + if(word == "|") + return (nil, nil); + + (cur, words) = getcommand(words); + + if(cur == nil) + return (nil, nil); + + cmds = cur :: cmds; + + if(words == nil) + break gather; + + word = hd words; + words = tl words; + + case word { + ";" => + break gather; + "&" => + term = Async; + break gather; + "|" => + continue gather; + } + return (nil, nil); + } + + if(word == "|") + return (nil, nil); + + return (ref Pipeline(revc(cmds), term), words); +} + +revp(arg: list of ref Pipeline): list of ref Pipeline +{ + ret: list of ref Pipeline; + + while(arg != nil) { + ret = hd arg :: ret; + arg = tl arg; + } + return ret; +} + +parseit(words: list of string): list of ref Pipeline +{ + ret: list of ref Pipeline; + cur: ref Pipeline; + + while(words != nil) { + (cur, words) = getpipe(words); + if(cur == nil){ + sys->fprint(stderr, "sh: syntax error\n"); + return nil; + } + ret = cur :: ret; + } + return revp(ret); +} + +# Runner. + +runpipeline(ctx: ref Context, pipeline: ref Pipeline) +{ + if(pipeline.term == Async) + sys->pctl(sys->NEWPGRP, nil); + pid := startpipeline(ctx, pipeline); + if(pid < 0) + return; + if(pipeline.term == Seq) + waitfor(pid); +} + +startpipeline(ctx: ref Context, pipeline: ref Pipeline): int +{ + pid := 0; + cmds := pipeline.cmds; + first := 1; + inpipe, outpipe: ref Sys->FD; + while(cmds != nil) { + last := tl cmds == nil; + cmd := hd cmds; + + infd: ref Sys->FD; + if(!first) + infd = inpipe; + else if(cmd.inf != nil){ + infd = sys->open(cmd.inf, Sys->OREAD); + if(infd == nil){ + sys->fprint(stderr, "sh: can't open %s: %r\n", cmd.inf); + return -1; + } + } + + outfd: ref Sys->FD; + if(!last){ + fds := array[2] of ref Sys->FD; + if(sys->pipe(fds) < 0){ + sys->fprint(stderr, "sh: can't make pipe: %r\n"); + return -1; + } + outpipe = fds[0]; + outfd = fds[1]; + fds = nil; + }else if(cmd.outf != nil){ + if(cmd.append){ + outfd = sys->open(cmd.outf, Sys->OWRITE); + if(outfd != nil) + sys->seek(outfd, big 0, Sys->SEEKEND); + } + if(outfd == nil) + outfd = sys->create(cmd.outf, Sys->OWRITE, 8r666); + if(outfd == nil){ + sys->fprint(stderr, "sh: can't open %s: %r\n", cmd.outf); + return -1; + } + } + + rpid := chan of int; + spawn mkprog(ctx, cmd.args, infd, outfd, rpid); + pid = <-rpid; + infd = nil; + outfd = nil; + + inpipe = outpipe; + outpipe = nil; + + first = 0; + cmds = tl cmds; + } + return pid; +} + +runit(ctx: ref Context, pipes: list of ref Pipeline) +{ + while(pipes != nil) { + pipeline := hd pipes; + pipes = tl pipes; + if(pipeline.term == Seq) + runpipeline(ctx, pipeline); + else + spawn runpipeline(ctx, pipeline); + } +} + +strchr(s: string, c: int): int +{ + ln := len s; + for (i := 0; i < ln; i++) + if (s[i] == c) + return i; + return -1; +} + +# PROFILE: con "/lib/profile"; +PROFILE: con "/lib/infernoinit"; + +startup(ctxt: ref Context) +{ + if (env == nil) + return; + # if (env->getenv("home") != nil) + # return; + home := gethome(); + env->setenv("home", home); + escript(ctxt, PROFILE); + escript(ctxt, home + PROFILE); +} + +escript(ctxt: ref Context, file: string) +{ + fd := sys->open(file, Sys->OREAD); + if (fd != nil) + script(ctxt, file); +} + +gethome(): string +{ + fd := sys->open("/dev/user", sys->OREAD); + if(fd == nil) + return "/"; + buf := array[128] of byte; + n := sys->read(fd, buf, len buf); + if(n < 0) + return "/"; + return "/usr/" + string buf[0:n]; +} |
