diff options
| author | Charles.Forsyth <devnull@localhost> | 2009-03-30 08:58:09 +0000 |
|---|---|---|
| committer | Charles.Forsyth <devnull@localhost> | 2009-03-30 08:58:09 +0000 |
| commit | 9e85ae9b75acdbe245243784d8d5b1b4de1b24f9 (patch) | |
| tree | 5dfaf8c8881bbf352dab7338cc9fba000c24eb03 /appl/cmd/ssh/shcons.b | |
| parent | 77bf0d6355c02a5d5199dd37033066727cad375b (diff) | |
x20090330-0957
Diffstat (limited to 'appl/cmd/ssh/shcons.b')
| -rw-r--r-- | appl/cmd/ssh/shcons.b | 296 |
1 files changed, 296 insertions, 0 deletions
diff --git a/appl/cmd/ssh/shcons.b b/appl/cmd/ssh/shcons.b new file mode 100644 index 00000000..65fdaf66 --- /dev/null +++ b/appl/cmd/ssh/shcons.b @@ -0,0 +1,296 @@ +implement Cons; + +# possibly useful bits from wm/sh + +include "sys.m"; + sys: Sys; + FileIO: import sys; + +include "draw.m"; + +include "sh.m"; + +include "string.m"; + str: String; + +include "arg.m"; + +Cons: module +{ + init: fn(ctxt: ref Draw->Context, args: list of string); +}; + +BSW: con 23; # ^w bacspace word +BSL: con 21; # ^u backspace line +EOT: con 4; # ^d end of file +ESC: con 27; # hold mode + +Rdreq: adt +{ + off: int; + nbytes: int; + fid: int; + rc: chan of (array of byte, string); +}; + +rdreq: list of Rdreq; +rawon := 0; +rawinput := ""; +partialread: array of byte; + +events: list of string; +evrdreq: list of Rdreq; + +init(ctxt: ref Context, args: list of string) +{ + sys = load Sys Sys->PATH; + str = load String String->PATH; + arg := load Arg Arg->PATH; + arg->init(args); + arg->setusage("aux/cons [-ilxvn] [-c command] [file [args...]]"); + + sys->pctl(Sys->FORKFD | Sys->FORKNS | Sys->NEWPGRP | Sys->FORKENV, nil); + + shargs: list of string; + while ((opt := arg->opt()) != 0) { + case opt { + 'c' => + shargs = arg->earg() :: "-c" :: shargs; + 'i' or 'l' or 'x' or 'v' or 'n' => + shargs = sys->sprint("-%c", opt) :: shargs; + * => + arg->usage(); + } + } + args = arg->args(); + for (; shargs != nil; shargs = tl shargs) + args = hd shargs :: args; + + ioc := chan of (int, ref FileIO, ref FileIO, string); + spawn newsh(ctxt, ioc, args); + + (nil, file, filectl, consfile) := <-ioc; + if(file == nil || filectl == nil || shctl == nil) { + sys->fprint(sys->fildes(2), "cons: shell /dev/cons creation failed\n"); + return; + } + + for(;;) alt { + c := <-keys => # TO DO: input arriving from remote ...; echo, edit ... + char := c[1]; + if(char == '\\') + char = c[2]; + if(rawon){ + rawinput[len rawinput] = char; + sendinput(t); + break; + } + case char { + * => + cmd(t, ".ft.t insert insert "+c); + '\r' => + ; # TO DO + '\n' or + EOT => + cmd(t, ".ft.t insert insert "+c); + sendinput(t); + '\b' => + cmd(t, ".ft.t tkTextDelIns -c"); + 'u'& 8r37 => + cmd(t, ".ft.t tkTextDelIns -l"); + 'w'& 8r37 => + cmd(t, ".ft.t tkTextDelIns -w"); + } + + rdrpc := <-filectl.read => + if(rdrpc.rc != nil) + rdrpc.rc <-= (nil, "permission denied"); + + (nil, data, nil, wc) := <-filectl.write => + if(wc == nil) { + # consctl closed - revert to cooked mode + rawon = 0; + continue; + } + (nc, cmdlst) := sys->tokenize(string data, " \n"); + if(nc == 1) { + case hd cmdlst { + "rawon" => + rawon = 1; + rawinput = ""; + # discard previous input + advance := string (len tk->cmd(t, ".ft.t get outpoint end") +1); + cmd(t, ".ft.t mark set outpoint outpoint+" + advance + "chars"); + partialread = nil; + "rawoff" => + rawon = 0; + partialread = nil; + "holdon" or "holdoff" => + ; + * => + wc <-= (0, "unknown control message"); + continue; + } + wc <-= (len data, nil); + continue; + } + wc <-= (0, "unknown control message"); + + rdrpc := <-file.read => + if(rdrpc.rc == nil) { + (ok, nil) := sys->stat(consfile); + if (ok < 0) + return; + continue; + } + append(rdrpc); + sendinput(t); + + (nil, data, nil, wc) := <-file.write => + if(wc == nil) { + (ok, nil) := sys->stat(consfile); + if (ok < 0) + return; + continue; + } + # TO DO: data from cons; edit (eg, add \r) and forward to remote + wc <-= (len data, nil); + data = nil; + } +} + +RPCread: type (int, int, int, chan of (array of byte, string)); + +append(r: RPCread) +{ + t := r :: nil; + while(rdreq != nil) { + t = hd rdreq :: t; + rdreq = tl rdreq; + } + rdreq = t; +} + +sendinput(t: ref Tk->Toplevel) +{ + input: string; + if(rawon) + input = rawinput; + else + input = tk->cmd(t, ".ft.t get outpoint end"); + if(rdreq == nil || (input == nil && len partialread == 0)) + return; + r := hd rdreq; + (chars, bytes, partial) := triminput(r.nbytes, input, partialread); + if(bytes == nil) + return; # no terminator yet + rdreq = tl rdreq; + + alt { + r.rc <-= (bytes, nil) => + # check that it really was sent + alt { + r.rc <-= (nil, nil) => + ; + * => + return; + } + * => + return; # requester has disappeared; ignore his request and try another + } + if(rawon) + rawinput = rawinput[chars:]; + else + cmd(t, ".ft.t mark set outpoint outpoint+" + string chars + "chars"); + partialread = partial; +} + +# read at most nr bytes from the input string, returning the number of characters +# consumed, the bytes to be read, and any remaining bytes from a partially +# read multibyte UTF character. +triminput(nr: int, input: string, partial: array of byte): (int, array of byte, array of byte) +{ + if(nr <= len partial) + return (0, partial[0:nr], partial[nr:]); + if(holding) + return (0, nil, partial); + + # keep the array bounds within sensible limits + if(nr > len input*Sys->UTFmax) + nr = len input*Sys->UTFmax; + buf := array[nr+Sys->UTFmax] of byte; + t := len partial; + buf[0:] = partial; + + hold := !rawon; + i := 0; + while(i < len input){ + c := input[i++]; + # special case for ^D - don't read the actual ^D character + if(!rawon && c == EOT){ + hold = 0; + break; + } + + t += sys->char2byte(c, buf, t); + if(c == '\n' && !rawon){ + hold = 0; + break; + } + if(t >= nr) + break; + } + if(hold){ + for(j := i; j < len input; j++){ + c := input[j]; + if(c == '\n' || c == EOT) + break; + } + if(j == len input) + return (0, nil, partial); + # strip ^D when next read would read it, otherwise + # we'll give premature EOF. + if(i == j && input[i] == EOT) + i++; + } + partial = nil; + if(t > nr){ + partial = buf[nr:t]; + t = nr; + } + return (i, buf[0:t], partial); +} + +newsh(ctxt: ref Context, ioc: chan of (int, ref FileIO, ref FileIO, string, ref FileIO), args: list of string) +{ + pid := sys->pctl(sys->NEWFD, nil); + + sh := load Command "/dis/sh.dis"; + if(sh == nil) { + ioc <-= (0, nil, nil, nil); + return; + } + + tty := "cons."+string pid; + + sys->bind("#s","/chan",Sys->MBEFORE); + fio := sys->file2chan("/chan", tty); + fioctl := sys->file2chan("/chan", tty + "ctl"); + ioc <-= (pid, fio, fioctl, "/chan/"+tty); + if(fio == nil || fioctl == nil) + return; + + sys->bind("/chan/"+tty, "/dev/cons", sys->MREPL); + sys->bind("/chan/"+tty+"ctl", "/dev/consctl", sys->MREPL); + + fd0 := sys->open("/dev/cons", Sys->OREAD|Sys->ORCLOSE); + fd1 := sys->open("/dev/cons", Sys->OWRITE); + fd2 := sys->open("/dev/cons", Sys->OWRITE); + + { + sh->init(ctxt, "sh" :: "-n" :: args); + }exception{ + "fail:*" => + exit; + } +} |
