ğÊSyntax10.Scn.Fnt Syntax10i.Scn.Fntõÿÿÿ°ğIStampElemsAlloc11 Dec 95Syntax10b.Scn.Fnt  ~ ./ ! zØcşŒ5ÚÍ!cî,o>MODULE Shell; (* ww  *) IMPORT TCP, Sessions, Oberon, MenuViewers, Viewers, TextFrames, Texts, Display, Fonts; CONST Left = 2; Middle = 1; Right = 0; Normal = 0; EscRead = 1; EscBraRead = 2; EscCtrRead = 3; UndRead = 4; UndBackRead = 5; UndWritten = 6; CrRead = 7; Terminated = -1; Menu = "System.Close System.Copy System.Grow Shell.Clear Shell.Stop Edit.Search Edit.Replace Edit.Store "; TYPE Terminal* = POINTER TO TerminalDesc; TerminalDesc* = RECORD(Sessions.TerminalDesc) text*: Texts.Text; lastInp*: LONGINT; state*: INTEGER; w*: Texts.Writer END ; Frame* = POINTER TO FrameDesc; FrameDesc* = RECORD(TextFrames.FrameDesc) terminal*: Terminal END ; ShellTerminal = POINTER TO RECORD(TerminalDesc) END ; VAR defOpts*: SET; defParc*: TextFrames.Parc; normFont*, boldFont*: Fonts.Font; w: Texts.Writer; PROCEDURE Install* (t: Terminal; s: Sessions.Session; text: Texts.Text; r: Sessions.Receiver; f: Sessions.Flusher; timeout: LONGINT); BEGIN t.text := text; t.lastInp := text.len; t.state := Normal; Texts.OpenWriter(t.w); Texts.SetFont(t.w, normFont); Sessions.Install(t, s, r, f, timeout) END Install; PROCEDURE OpenFrame*(f: Frame; h: Display.Handler; t: Terminal; pos: LONGINT); BEGIN f.terminal := t; TextFrames.Open(f, t.text, pos); f.handle := h END OpenFrame; PROCEDURE SetCaret(f: Frame); VAR r: Texts.Reader; i, j: INTEGER; last, pos: LONGINT; ch: CHAR; msg: Oberon.ControlMsg; list: ARRAY 256 OF LONGINT; BEGIN IF f.hasCar & (f.carloc.pos < f.text.len) THEN last := TextFrames.Pos(f, f.X + f.W, f.Y); TextFrames.RemoveCaret(f); IF last < f.text.len THEN list[0] := f.org; i := 1; j := 1; Texts.OpenReader(r, f.text, f.org); Texts.Read(r, ch); WHILE ~r.eot DO IF ch = 0DX THEN list[i] := Texts.Pos(r); IF list[i] < last THEN INC(j) END ; i := (i + 1) MOD 256 END ; Texts.Read(r, ch) END ; IF i < j THEN pos := list[(256 + i - j) MOD 256] ELSE pos := list[(i - j) MOD 256] END ; Oberon.RemoveMarks(f.X, f.Y, f.W, f.H); msg.id := Oberon.neutralize; TextFrames.Handle(f, msg); TextFrames.Show(f, pos) END ; TextFrames.SetCaret(f, f.text.len) END END SetCaret; PROCEDURE ConsumeText(term: Terminal; text: Texts.Text; beg, end: LONGINT); VAR ch: CHAR; s: Sessions.Session; r: Texts.Reader; msg: Texts.CopyMsg; BEGIN s := Sessions.ThisSession(term); Texts.OpenReader(r, text, beg); WHILE beg < end DO Texts.Read(r, ch); INC(beg); IF r.elem # NIL THEN msg.e := NIL; r.elem.handle(r.elem, msg); Texts.WriteElem(w, msg.e); Texts.Insert(term.text, term.text.len, w.buf) ELSIF ch = "€" THEN Sessions.SendChar(s, "A"); Sessions.SendChar(s, "e") ELSIF ch = "" THEN Sessions.SendChar(s, "O"); Sessions.SendChar(s, "e") ELSIF ch = "‚" THEN Sessions.SendChar(s, "U"); Sessions.SendChar(s, "e") ELSIF ch = "ƒ" THEN Sessions.SendChar(s, "a"); Sessions.SendChar(s, "e") ELSIF (ch = "†") OR (ch = "‹") OR (ch = "”") THEN Sessions.SendChar(s, "a") ELSIF ch = "„" THEN Sessions.SendChar(s, "o"); Sessions.SendChar(s, "e") ELSIF (ch = "‰") OR (ch = "") THEN Sessions.SendChar(s, "o") ELSIF ch = "…" THEN Sessions.SendChar(s, "u"); Sessions.SendChar(s, "e") ELSIF (ch = "Š") OR (ch = "") THEN Sessions.SendChar(s, "u") ELSIF ch = "“" THEN Sessions.SendChar(s, "c") ELSIF ch = "•" THEN Sessions.SendChar(s, "n") ELSIF (ch = "‡") OR (ch = "Œ") OR (ch = "") OR (ch = "‘") THEN Sessions.SendChar(s, "e") ELSIF (ch = "ˆ") OR (ch = "") OR (ch = "’") THEN Sessions.SendChar(s, "i") ELSIF ch = 0ABX THEN Sessions.SendChar(s, "s") ELSIF ch = 9FX THEN Sessions.SendChar(s, " ") ELSIF ch = "›" THEN Sessions.SendChar(s, "-") ELSE Sessions.SendChar(s, ch) END END END ConsumeText; PROCEDURE Edit*(f: Frame; x, y: INTEGER; keys: SET); VAR term: Terminal; t: Texts.Text; r: Texts.Reader; cpm: Oberon.CopyOverMsg; ipm: Oberon.InputMsg; keySum: SET; beg, end, time: LONGINT; ch: CHAR; BEGIN keySum := keys; term := f.terminal; IF f.X + TextFrames.barW <= x THEN IF Left IN keys THEN Oberon.PassFocus(MenuViewers.Ancestor); TextFrames.TrackCaret(f, x, y, keySum); IF keySum = {Left, Middle} THEN Oberon.GetSelection(t, beg, end, time); IF time >= 0 THEN ConsumeText(term, t, beg, end); term.lastInp := term.text.len END ; SetCaret(f) ELSIF keySum = {Left, Right} THEN Oberon.GetSelection(t, beg, end, time); Texts.OpenReader(r, f.text, f.carloc.pos); Texts.Read(r, ch); IF ~r.eot & (time > 0) THEN Texts.ChangeLooks(t, beg, end, {0..2}, r.fnt, r.col, r.voff) END ; TextFrames.RemoveCaret(f) END ELSIF Right IN keys THEN TextFrames.TrackSelection(f, x, y, keySum); IF keySum = {Left, Right} THEN Oberon.PassFocus(MenuViewers.Ancestor); Oberon.GetSelection(t, beg, end, time); Texts.Delete(f.text, beg, end); IF term.lastInp >= beg THEN DEC(term.lastInp, end - beg) END ELSIF keySum = {Middle, Right} THEN Oberon.GetSelection(t, beg, end, time); cpm.text := t; cpm.beg := beg; cpm.end := end; Oberon.FocusViewer.handle(Oberon.FocusViewer, cpm) END ELSE ipm.id := Oberon.track; ipm.X := x; ipm.Y := y; ipm.keys := keys; TextFrames.Handle(f, ipm) END ELSE ipm.id := Oberon.track; ipm.X := x; ipm.Y := y; ipm.keys := keys; TextFrames.Handle(f, ipm) END END Edit; PROCEDURE ConsumeKey(term: Terminal; ch: CHAR); VAR s: Sessions.Session; BEGIN s := Sessions.ThisSession(term); IF ch = "‘" THEN Sessions.SendChar(s, 1BX) ELSIF ch = "€" THEN Sessions.SendChar(s, 81X) ELSIF ch = "" THEN Sessions.SendChar(s, 8FX) ELSIF ch = "‚" THEN Sessions.SendChar(s, 95X) ELSIF ch = "ƒ" THEN Sessions.SendChar(s, 01X) ELSIF ch = "„" THEN Sessions.SendChar(s, 0FX) ELSIF ch = "…" THEN Sessions.SendChar(s, 15X) ELSIF ch = 0ABX THEN Sessions.SendChar(s, 13X) ELSIF ch = 93X THEN Sessions.SendChar(s, 3X) ELSE Sessions.SendChar(s, ch) END END ConsumeKey; PROCEDURE Scroll*(f: Frame; lastInp: LONGINT); VAR last, pos, prev: LONGINT; cnt: INTEGER; text: Texts.Text; ch: CHAR; r: Texts.Reader; BEGIN text := f.text; last := TextFrames.Pos(f, MAX(INTEGER), f.Y); REPEAT Texts.OpenReader(r, text, last); cnt := 1; REPEAT Texts.Read(r, ch); IF ch = 0DX THEN INC(cnt) END UNTIL r.eot; pos := f.org; Texts.OpenReader(r, text, pos); REPEAT Texts.Read(r, ch); IF ch = 0DX THEN DEC(cnt); pos := Texts.Pos(r) END UNTIL (cnt = 0) OR r.eot OR (Texts.Pos(r) = lastInp); prev := f.org; IF pos # prev THEN TextFrames.Show(f, pos) END ; last := TextFrames.Pos(f, MAX(INTEGER), f.Y) UNTIL (last >= text.len) OR (Texts.Pos(r) = lastInp) OR (prev = f.org) END Scroll; PROCEDURE Handler*(f: Display.Frame; VAR m: Display.FrameMsg); VAR nF: Frame; term: Terminal; text: Texts.Text; carSet: BOOLEAN; last: LONGINT; ctlm: Oberon.ControlMsg; BEGIN WITH f: Frame DO term := f.terminal; IF m IS Sessions.IdentifyMsg THEN m(Sessions.IdentifyMsg).session := Sessions.ThisSession(term) ELSIF m IS Oberon.CopyOverMsg THEN WITH m: Oberon.CopyOverMsg DO IF f.hasCar THEN ConsumeText(term, m.text, m.beg, m.end); term.lastInp := term.text.len; SetCaret(f) END END ELSIF m IS Oberon.InputMsg THEN WITH m: Oberon.InputMsg DO IF m.id = Oberon.consume THEN IF f.hasCar THEN ConsumeKey(term, m.ch); term.lastInp := term.text.len; SetCaret(f) END ELSIF m.id = Oberon.track THEN Edit(f, m.X, m.Y, m.keys) END END ELSIF m IS Oberon.CopyMsg THEN WITH m: Oberon.CopyMsg DO IF (m.F # NIL) & (m.F IS Frame) THEN nF := m.F(Frame) ELSE NEW(nF); m.F := nF END ; OpenFrame(nF, f.handle, term, f.org) END ELSIF m IS TextFrames.UpdateMsg THEN WITH m: TextFrames.UpdateMsg DO text := m.text; IF text = term.text THEN carSet := f.hasCar; ctlm.id := Oberon.neutralize; TextFrames.Handle(f, ctlm); TextFrames.Handle(f, m); IF (f.H > 0) & (m.id = TextFrames.insert) THEN last := TextFrames.Pos(f, MAX(INTEGER), f.Y); IF (last < text.len) & (last >= m.beg) THEN Scroll(f, f.terminal.lastInp) END END ; IF carSet THEN TextFrames.SetCaret(f, TextFrames.Pos(f, f.X + f.W, f.Y)) END END END ELSE TextFrames.Handle(f, m) END ; IF term.state = Terminated THEN f.handle := TextFrames.Handle END END END Handler; PROCEDURE NewFrame*(terminal: Terminal): Frame; VAR f: Frame; BEGIN NEW(f); OpenFrame(f, Handler, terminal, 0); RETURN f END NewFrame; PROCEDURE OpenScanner(VAR s: Texts.Scanner); VAR text: Texts.Text; beg, end, time: LONGINT; BEGIN Texts.OpenScanner(s, Oberon.Par.text, Oberon.Par.pos); Texts.Scan(s); IF (s.class = Texts.Char) & (s.line = 0) & (s.c = "^") THEN Oberon.GetSelection(text, beg, end, time); IF time > 0 THEN Texts.OpenScanner(s, text, beg); Texts.Scan(s) END END END OpenScanner; PROCEDURE SetParc*; VAR msg: Texts.CopyMsg; text: Texts.Text; r: Texts.Reader; beg, end, time: LONGINT; ch: CHAR; BEGIN Oberon.GetSelection(text, beg, end, time); IF time >= 0 THEN Texts.OpenReader(r, text, beg); Texts.Read(r, ch); IF (r.elem # NIL) & (r.elem IS TextFrames.Parc) THEN msg.e := NIL; r.elem.handle(r.elem, msg); defParc := msg.e(TextFrames.Parc); END END END SetParc; PROCEDURE SetFont*; VAR s: Texts.Scanner; name: ARRAY 32 OF CHAR; BEGIN OpenScanner(s); IF s.class = Texts.Name THEN COPY(s.s, name); Texts.Scan(s); IF (s.class = Texts.Char) & (s.c = "/") & (s.nextCh = "b") THEN boldFont := Fonts.This(name) ELSE normFont := Fonts.This(name) END END END SetFont; PROCEDURE Clear*; VAR v: Viewers.Viewer; f: Display.Frame; text: Texts.Text; term: Terminal; msg: Texts.CopyMsg; BEGIN f := NIL; IF Oberon.Par.vwr.dsc = Oberon.Par.frame THEN f := Oberon.Par.frame.next ELSE v := Oberon.MarkedViewer(); IF v.dsc # NIL THEN f := v.dsc.next END END ; IF (f # NIL) & (f IS Frame) THEN term := f(Frame).terminal(Terminal); text := term.text; Texts.Delete(text, 0, text.len); msg.e := NIL; defParc.handle(defParc, msg); Texts.WriteElem(w, msg.e); Texts.Append(text, w.buf); term.lastInp := text.len; Texts.OpenWriter(term.w); Texts.SetFont(term.w, normFont) END END Clear; PROCEDURE IsShellTerm(t: Sessions.Terminal): BOOLEAN; BEGIN RETURN t IS ShellTerminal END IsShellTerm; PROCEDURE Receive*(t: Sessions.Terminal; ch: CHAR); VAR l: LONGINT; text: Texts.Text; BEGIN WITH t: Terminal DO IF t.w.buf.len = 0 THEN Texts.SetFont(t.w, normFont) END ; IF (ch = 08X) OR (ch = 7FX) THEN IF t.state = UndRead THEN t.state := UndBackRead ELSE text := t.text; Texts.Append(text, t.w.buf); l := text.len; IF l > 0 THEN Texts.Delete(text, l-1, l) END ; IF t.state = UndWritten THEN t.state := UndBackRead END END ELSE IF t.state = UndRead THEN Texts.Write(t.w, "_"); t.state := Normal END ; CASE t.state OF Normal, UndBackRead, UndWritten, CrRead: IF ch = 1BX THEN t.state := EscRead ELSIF ch = "_" THEN t.state := UndRead ELSE IF (ch >= " ") & (ch < 7FX) OR (ch = 09X) THEN IF t.state = UndBackRead THEN Texts.SetFont(t.w, boldFont); Texts.Write(t.w, ch); Texts.SetFont(t.w, normFont) ELSE Texts.Write(t.w, ch) END ; t.state := Normal ELSIF ch = 0DX THEN Texts.WriteLn(t.w); t.state := CrRead ELSIF ch = 0AX THEN IF t.state # CrRead THEN Texts.WriteLn(t.w) END ; t.state := Normal ELSIF ch = 0CX THEN Texts.WriteLn(t.w); t.state := Normal ELSIF ch # 07X THEN Texts.Write(t.w, "<"); Texts.WriteInt(t.w, ORD(ch), 0); Texts.Write(t.w, ">"); t.state := Normal ELSE t.state := Normal END END | EscRead: IF ch = "[" THEN t.state := EscBraRead ELSIF ("0" <= ch) & (ch <= "~") THEN t.state := Normal ELSE t.state := EscCtrRead END | EscBraRead: IF ("@" <= ch) & (ch <= "~") THEN t.state := Normal END | EscCtrRead: IF ("0" <= ch) & (ch <= "~") THEN t.state := Normal END END END END END Receive; PROCEDURE Flush*(t: Sessions.Terminal; changed, terminated: BOOLEAN); BEGIN WITH t: Terminal DO IF t.state = UndRead THEN Texts.Write(t.w, "_"); t.state := UndWritten END ; Texts.Append(t.text, t.w.buf); IF terminated THEN t.state := Terminated END END END Flush; PROCEDURE GetPar(VAR s: Texts.Scanner; VAR hostname, user: ARRAY OF CHAR); BEGIN COPY("", user); IF ((s.class = Texts.Name) OR (s.class = Texts.String)) & (s.line = 0) THEN COPY(s.s, hostname); IF s.nextCh = "\" THEN Texts.Read(s, s.nextCh); Texts.Scan(s); IF ((s.class = Texts.Name) OR (s.class = Texts.String)) & (s.line = 0) THEN COPY(s.s, user) END END ; Texts.Scan(s) ELSE COPY("localhost", hostname) END END GetPar; PROCEDURE RLogin(hostname, user: ARRAY OF CHAR; VAR s: Sessions.Session); VAR i: LONGINT; ch: CHAR; BEGIN s := Sessions.New(hostname, 23); IF s # NIL THEN LOOP TCP.Read(s, ch); IF ch = 0FFX THEN TCP.Read(s, ch); IF ch = 0FDX THEN TCP.Read(s, ch); IF ch = 18X THEN TCP.Write(s, 0FFX); TCP.Write(s, 0FCX); TCP.Write(s, 18X); EXIT END ELSIF ch > 0FAX THEN TCP.Read(s, ch) END END END ; IF user # "" THEN LOOP IF (ch = 0AX) OR (ch = 0DX) THEN TCP.Read(s, ch); WHILE ch = 0X DO TCP.Read(s, ch) END ; IF ch = "l" THEN TCP.Read(s, ch); IF ch = "o" THEN TCP.Read(s, ch); IF ch = "g" THEN TCP.Read(s, ch); IF ch = "i" THEN TCP.Read(s, ch); IF ch = "n" THEN TCP.Read(s, ch); IF ch = ":" THEN TCP.Read(s, ch); i := 0; ch := user[i]; WHILE ch # 0X DO TCP.Write(s, ch); INC(i); ch := user[i] END ; TCP.Write(s, 0DX); EXIT END END END END END END ELSE TCP.Read(s, ch) END END END END END RLogin; PROCEDURE Open*; VAR x, y: INTEGER; v: Viewers.Viewer; t: Sessions.Terminal; term: ShellTerminal; s: Texts.Scanner; text: Texts.Text; copy: Texts.CopyMsg; identify: Sessions.IdentifyMsg; host, user: ARRAY 32 OF CHAR; BEGIN OpenScanner(s); identify.session := NIL; IF (s.line = 0) & (s.class = Texts.Char) & (s.c = "*") THEN v := Oberon.MarkedViewer(); v.handle(v, identify) ELSE GetPar(s, host, user); RLogin(host, user, identify.session) END ; IF identify.session # NIL THEN t := Sessions.ThisTerminal(identify.session, IsShellTerm); IF t # NIL THEN term := t(ShellTerminal) ELSE NEW(term); text := TextFrames.Text(""); copy.e := NIL; defParc.handle(defParc, copy); Texts.WriteElem(w, copy.e); Texts.Append(text, w.buf); Install(term, identify.session, text, Receive, Flush, -1) END ; Oberon.AllocateUserViewer(Oberon.Mouse.X, x, y); v := MenuViewers.New( TextFrames.NewMenu(identify.session.name, Menu), NewFrame(term), TextFrames.menuH, x, y ) END END Open; PROCEDURE Stop*; VAR v: Viewers.Viewer; f: Display.Frame; term: Terminal; s: Sessions.Session; BEGIN f := NIL; IF Oberon.Par.vwr.dsc = Oberon.Par.frame THEN f := Oberon.Par.frame.next ELSE v := Oberon.MarkedViewer(); IF v.dsc # NIL THEN f := v.dsc.next END END ; IF (f # NIL) & (f IS Frame) THEN term := f(Frame).terminal; s := Sessions.ThisSession(term); IF s # NIL THEN Sessions.Remove(term); term.state := Terminated; END ELSE Texts.WriteString(w, "Shell.Stop failed (bad receiver)"); Texts.WriteLn(w);Texts.Append(Oberon.Log, w.buf) END END Stop; PROCEDURE InitDefParc; VAR msg: Texts.CopyMsg; i: INTEGER; tab, tabInc: LONGINT; BEGIN msg.e := NIL; TextFrames.defParc.handle(TextFrames.defParc, msg); defParc := msg.e(TextFrames.Parc); i := 0; tabInc := 167 * TextFrames.mm DIV 10; tab := 0; WHILE i < 32 DO INC(tab, tabInc); defParc.tab[i] := tab; INC(i) END ; defParc.nofTabs := i END InitDefParc; BEGIN normFont := Fonts.This("Courier10.Scn.Fnt"); boldFont := Fonts.This("Syntax10b.Scn.Fnt"); Texts.OpenWriter(w); InitDefParc END Shell.