implement Tkcmd; include "sys.m"; sys: Sys; stderr: ref Sys->FD; include "draw.m"; draw: Draw; Display, Image, Point: import draw; include "tk.m"; tk: Tk; include "wmlib.m"; wmlib: Wmlib; include "bufio.m"; include "arg.m"; Tkcmd : module { init: fn(ctxt: ref Draw->Context, argv: list of string); }; usage() { sys->print("usage: tkcmd [-iu] [toplevelarg]\n"); sys->raise("fail:usage"); } badmodule(m: string) { sys->fprint(stderr, "tkcmd: cannot load %s: %r\n", m); sys->raise("fail:bad module"); } init(ctxt: ref Draw->Context, argv: list of string) { sys = load Sys Sys->PATH; stderr = sys->fildes(2); draw = load Draw Draw->PATH; tk = load Tk Tk->PATH; if (tk == nil) badmodule(Tk->PATH); wmlib= load Wmlib Wmlib->PATH; if (wmlib==nil) badmodule(Wmlib->PATH); arg := load Arg Arg->PATH; if (arg == nil) badmodule(Arg->PATH); arg->init(argv); update := 1; interactive := isconsole(sys->fildes(0)); while ((opt := arg->opt()) != 0) { case opt { 'i' => interactive = 1; 'u' => update = 0; * => usage(); } } argv = arg->argv(); arg = nil; tkarg := ""; if (argv != nil) { if (tl argv != nil) usage(); tkarg = hd argv; } wmlib->init(); sys->pctl(Sys->NEWPGRP, nil); shellit(ctxt, tkarg, interactive, update); } isconsole(fd: ref Sys->FD): int { (ok1, d1) := sys->fstat(fd); (ok2, d2) := sys->stat("/dev/cons"); if (ok1 < 0 || ok2 < 0) return 0; return d1.dtype == d2.dtype && d1.qid.path == d2.qid.path; } shellit(ctxt: ref Draw->Context, arg: string, interactive, update: int) { (Wwsh, winctl) := wmlib->titlebar(ctxt.screen, arg, "Tk",Wmlib->Appl); tk->cmd(Wwsh, "update"); ps1 := ""; ps2 := "tk> "; if (!interactive) ps1 = ps2 = ""; lines := chan of string; sync := chan of int; spawn grab_lines(ps1, ps2, lines, sync); output := chan of string; tk->namechan(Wwsh, output, "stdout"); pid := <-sync; loop: for(;;) alt { line := <-lines => if (line == nil) break loop; line = line[0:len line - 1]; result := tk->cmd(Wwsh, line); if (result != nil) sys->print("%s\n", result); if (update) tk->cmd(Wwsh, "update"); sys->print("%s", ps1); menu := <-winctl => sys->print("titlectl '%s'\n", menu); wmlib->titlectl(Wwsh, menu); s := <-output => sys->print("%s\n", s); sys->print("%s", ps1); } } grab_lines(new_inp, unfin: string, lines: chan of string, sync: chan of int) { sync <-= sys->pctl(0, nil); bufmod := load Bufio Bufio->PATH; Iobuf: import bufmod; if (bufmod == nil) { lines <-= nil; return; } sys->print("%s", new_inp); iob := bufmod->fopen(sys->fildes(0),bufmod->OREAD); if (iob==nil){ sys->fprint(stderr, "tkcmd: cannot open stdin for reading.\n"); lines <-= nil; return; } line := ""; while((input := iob.gets('\n')) != nil) { line+=input; if (!finished(line,0)) sys->print("%s", unfin); else{ lines <-= line; line=nil; } } lines <-= nil; } # returns 1 if the line has matching braces, brackets and # double-quotes and does not end in "\\\n" finished(s : string, termchar : int) : int { cb:=0; dq:=0; sb:=0; if (s==nil) return 1; if (termchar=='}') cb++; if (termchar==']') sb++; if (len s > 1 && s[len s -2]=='\\') return 0; if (s[0]=='{') cb++; if (s[0]=='}' && cb>0) cb--; if (s[0]=='[') sb++; if (s[0]==']' && sb>0) sb--; if (s[0]=='"') dq=1-dq; for(i:=1;i0) cb--; if (s[i]=='[' && s[i-1]!='\\') sb++; if (s[i]==']' && s[i-1]!='\\' && sb>0) sb--; if (s[i]=='"' && s[i-1]!='\\') dq=1-dq; } return (cb==0 && sb==0 && dq==0); }