summaryrefslogtreecommitdiff
path: root/appl/cmd/ssh/shcons.b
diff options
context:
space:
mode:
Diffstat (limited to 'appl/cmd/ssh/shcons.b')
-rw-r--r--appl/cmd/ssh/shcons.b296
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;
+ }
+}