Syntax10.Scn.FntuSyntax10i.Scn.Fnt  e2ESyntax10b.Scn.Fntv0 .0 T4 )MODULE Telnet; (* ip/mh 17.3.94 / 5.7.1994*) (* Linux - Patch by RLI, 7 Oct 96 << *) IMPORT SYSTEM, Display, Fonts, Texts, TextFrames, Viewers, MenuViewers, Oberon, Terminals, TerminalFrames, TCP; CONST left = 2; middle = 1; right = 0; BufSize = 1500; MaxTerminals = 32; TYPE Task = POINTER TO TaskDesc; Terminal = POINTER TO TerminalDesc; TerminalDesc = RECORD (Terminals.TerminalDesc) com: TCP.Connection ; localecho: BOOLEAN; task: Task; END ; TextFrame = POINTER TO TextFrameDesc; TextFrameDesc = RECORD (TextFrames.FrameDesc) T: Terminal; END ; TaskDesc = RECORD (Oberon.TaskDesc) tid: INTEGER; (*terminal id*) conid: LONGINT; (*connection id*) END ; VAR buf: ARRAY BufSize OF CHAR; W: Texts.Writer; Term: ARRAY MaxTerminals OF LONGINT (*Terminal*); PROCEDURE FreeId(): INTEGER; VAR i: INTEGER; BEGIN i := 0; WHILE (i < MaxTerminals) & (Term[i] # 0) DO INC(i) END ; RETURN i; END FreeId; PROCEDURE Send0 (T: Terminals.Terminal; ch: CHAR); BEGIN WITH T: Terminal DO IF TCP.Connected(T.com) THEN TCP.Write(T.com, ch); IF T.localecho THEN Terminals.Receive(T, ch); IF ch = 0DX THEN TCP.Write(T.com, 0AX); Terminals.Receive(T, 0AX) END ; Terminals.Flush(T) END END END END Send0; PROCEDURE Break0 (T: Terminals.Terminal); END Break0; PROCEDURE WriteString (T: Terminals.Terminal; str: ARRAY OF CHAR); VAR i: INTEGER; BEGIN i := 0; WHILE str[i] # 0X DO Terminals.Receive(T, str[i]); INC(i); END ; END WriteString; PROCEDURE WriteLn (T: Terminals.Terminal); BEGIN Terminals.Receive(T, 0DX); Terminals.Receive(T, 0AX); END WriteLn; PROCEDURE Receiver; VAR i, res: INTEGER; l: LONGINT; C: TCP.Connection; T: Terminal; BEGIN WITH Oberon.CurTask: Task DO C := TCP.ThisConnection(Oberon.CurTask.conid); IF C = NIL THEN Term[Oberon.CurTask.tid] := 0; Oberon.Remove(Oberon.CurTask); (*Log.Str("Telnet: connection closed by finalization"); Log.Ln;*) RETURN END ; T := SYSTEM.VAL(Terminal, Term[Oberon.CurTask.tid]); l := TCP.Available(C); IF l >= BufSize THEN l := BufSize-1 END ; IF l > 0 THEN TCP.ReadBytes(C, buf, 0, l); buf[l] := 0X; i := 0; WHILE i < l DO IF buf[i] = 0FFX THEN (* IAC *) INC(i); IF buf[i] = 0FFX THEN Terminals.Receive(T, 0FFX); INC(i); ELSIF buf[i] = 0FDX THEN (* DO *) INC(i); IF buf[i] = 018X THEN (* IAC DO TERMINAL-TYPE *) INC(i); (* IAC WILL Terminal-TYPE *) TCP.Write(C, 0FFX); TCP.Write(C, 0FBX); TCP.Write(C, 018X); ELSE TCP.Write(C, 0FFX); TCP.Write(C, 0FCX); TCP.Write(C, buf[i]); (* RLI, 7 Oct 96; << reject unknown stuff Fix *) INC(i); END ; ELSIF buf[i] = 0FAX THEN (* IAC SB *) INC(i); IF buf[i] = 018X THEN(* IAC SB TERMINAL-TYPE *) INC(i); IF buf[i] = 01X THEN (* IAC SB TERMINAL-TYPE SEND *) INC(i); (* IAC SB TERMINAL-TYPE IS DEC-VT100 IAC SE *) TCP.Write(C, 0FFX); TCP.Write(C, 0FAX); TCP.Write(C, 018X); TCP.Write(C, 0); TCP.Write(C, "V"); TCP.Write(C, "T"); TCP.Write(C, "1"); TCP.Write(C, "0"); TCP.Write(C, "0"); TCP.Write(C, 0FFX); TCP.Write(C, 0F0X); ELSE INC(i); END ; ELSE INC(i); END ; ELSIF buf[i] > 0FAX THEN INC(i); INC(i); (* ignore command & options *) ELSE INC(i); (* ignore command *) END ; ELSE Terminals.Receive(T, buf[i]); INC(i); END ; END ; Terminals.Flush(T); END ; Oberon.CurTask.time := Oberon.Time() + 20 END END Receiver; PROCEDURE Open*; (** ^ | ( hostname | IP address ) [ port-number ] **) VAR S: Texts.Scanner; task: Task; F: TerminalFrames.Frame; V: MenuViewers.Viewer; x, y, res: INTEGER; servaddr: TCP.IpAdr; port: INTEGER; name: ARRAY 64 OF CHAR; text: Texts.Text; beg, end, time: LONGINT; T: Terminal; tid: INTEGER; BEGIN Texts.OpenScanner(S, Oberon.Par.text, Oberon.Par.pos); Texts.Scan(S); IF (S.class = Texts.Char) & (S.c = "^") THEN Oberon.GetSelection(text, beg, end, time); IF time > 0 THEN Texts.OpenScanner(S, text, beg); Texts.Scan(S) END END ; IF (S.class = Texts.Name) OR (S.class = Texts.String) THEN COPY(S.s, name); IF ("0" <= name[0]) & (name[0] <= "9") THEN TCP.HostByNumber(name, servaddr, res) ELSE TCP.HostByName(name, servaddr, res) END ; IF res = TCP.Done THEN Texts.Scan(S); IF (S.class = Texts.Int) & (S.line = 0) THEN port := SHORT(S.i) ELSE port := 23 END ; tid := FreeId(); IF tid < MaxTerminals THEN NEW(task); task.safe := FALSE; task.handle := Receiver; task.tid := tid; NEW(T); Terminals.Open(T, TextFrames.Text(""), Send0, Break0, TerminalFrames.NotifyDisplay); T.localecho := port # 23; T.task := task; NEW(F); TerminalFrames.Open(F, TerminalFrames.Handle, T, Fonts.This("Courier10.Scn.Fnt")); Oberon.AllocateUserViewer(Oberon.Mouse.X, x, y); NEW(T.com); TCP.Connect(T.com, TCP.AnyPort, servaddr, port, 5000, res); IF res = TCP.Done THEN task.conid := T.com.id; V := MenuViewers.New( TextFrames.NewMenu(name, "System.Close System.Grow Telnet.OpenLog Telnet.Reset Telnet.Close"), F, TextFrames.menuH, x, y); Oberon.Install(task); Term[tid] := SYSTEM.VAL(LONGINT, T); ELSIF res = TCP.Timeout THEN Texts.WriteString(W,"timeout connecting to "); Texts.WriteString(W, name); Texts.WriteLn(W); Texts.Append(Oberon.Log,W.buf); ELSE Texts.WriteString(W,"TCP.Open failed"); Texts.WriteLn(W); Texts.Append(Oberon.Log,W.buf); END ; END ELSE Texts.WriteString(W, name); Texts.WriteString(W," not found"); Texts.WriteLn(W); Texts.Append(Oberon.Log,W.buf); END ; END ; END Open; PROCEDURE Close*; VAR F: TerminalFrames.Frame; res: INTEGER; BEGIN IF Oberon.Par.vwr.dsc.next IS TerminalFrames.Frame THEN F := Oberon.Par.vwr.dsc.next(TerminalFrames.Frame); IF F.text IS Terminal THEN TCP.Disconnect(F.text(Terminal).com); WriteString(F.text, "Connection closed"); WriteLn(F.text); Terminals.Flush(F.text); Oberon.Remove(F.text(Terminal).task); Term[F.text(Terminal).task.tid] := 0; END END END Close; PROCEDURE Reset*; VAR F: TerminalFrames.Frame; BEGIN IF Oberon.Par.vwr.dsc.next IS TerminalFrames.Frame THEN F := Oberon.Par.vwr.dsc.next(TerminalFrames.Frame); Terminals.Reset(F.text); END ; END Reset; PROCEDURE Send*; (** ^ | { string | name | number } **) VAR S: Texts.Scanner; V: Viewers.Viewer; terminal: Terminals.Terminal; text: Texts.Text; beg, end, time: LONGINT; BEGIN V := Oberon.FocusViewer; IF (V # NIL) & (V IS MenuViewers.Viewer) & (V.dsc.next IS TerminalFrames.Frame) THEN terminal := V.dsc.next(TerminalFrames.Frame).text; Texts.OpenScanner(S, Oberon.Par.text, Oberon.Par.pos); Texts.Scan(S); IF (S.class = Texts.Char) & (S.c = "^") THEN Oberon.GetSelection(text, beg, end, time); IF time > 0 THEN Texts.OpenScanner(S, text, beg); Texts.Scan(S) END END ; WHILE S.class IN {Texts.String, Texts.Name, Texts.Int} DO IF S.class IN {Texts.String, Texts.Name} THEN Terminals.SendString(terminal, S.s) ELSE Terminals.Send(terminal, CHR(S.i MOD 256)); END ; Texts.Scan(S); END END END Send; (* log view *) PROCEDURE SetCaret (F: TextFrames.Frame); VAR pos: LONGINT; BEGIN pos := TextFrames.Pos(F, F.X + F.W, F.Y); IF ~F.hasCar OR (F.carloc.pos # pos) THEN TextFrames.SetCaret(F, pos) END END SetCaret; PROCEDURE SendKey (T: Terminal; ch: CHAR); BEGIN IF ch = "" THEN Terminals.Send(T, 1BX) ELSIF ch = "" THEN Terminals.Send(T, 81X) ELSIF ch = "" THEN Terminals.Send(T, 8FX) ELSIF ch = "" THEN Terminals.Send(T, 95X) ELSIF ch = "" THEN Terminals.Send(T, 01X) ELSIF ch = "" THEN Terminals.Send(T, 0FX) ELSIF ch = "" THEN Terminals.Send(T, 15X) ELSE Terminals.Send(T, ch) END END SendKey; PROCEDURE Handle (F: Display.Frame; VAR msg: Display.FrameMsg); VAR text: Texts.Text; beg, end, time: LONGINT; hasCar: BOOLEAN; r: Texts.Reader; pos, oldpos, last: LONGINT; ch: CHAR; BEGIN WITH F: TextFrame DO IF msg IS Oberon.InputMsg THEN WITH msg: Oberon.InputMsg DO IF (msg.id = Oberon.consume) & F.hasCar THEN SendKey(F.T,msg.ch) ELSIF (msg.id = Oberon.track) & (F.X + F.barW <= msg.X) & (left IN msg.keys) THEN Oberon.PassFocus(MenuViewers.Ancestor); TextFrames.TrackCaret(F, msg.X, msg.Y, msg.keys); IF msg.keys * {middle, right} = {middle} THEN Oberon.GetSelection(text, beg, end, time); IF time >= 0 THEN Terminals.SendText(F.T, text, beg, end) END END ELSE TextFrames.Handle(F, msg) END END ELSIF (msg IS Oberon.CopyOverMsg) & F.hasCar THEN WITH msg: Oberon.CopyOverMsg DO Terminals.SendText(F.T, msg.text, msg.beg, msg.end) END ELSIF msg IS TextFrames.UpdateMsg THEN WITH msg: TextFrames.UpdateMsg DO IF msg.text = F.text THEN hasCar := F.hasCar; TextFrames.Handle(F, msg); IF (msg.id = TextFrames.insert) & (msg.end = F.text.len) THEN last := TextFrames.Pos(F, MAX(INTEGER), F.Y); IF last < F.text.len-1 THEN Oberon.RemoveMarks(F.X, F.Y, F.W, F.H); TextFrames.RemoveSelection(F); TextFrames.RemoveCaret(F); pos := last; REPEAT oldpos := pos; IF last + 2 < msg.beg THEN pos := msg.beg; TextFrames.Show(F, pos) ELSE Texts.OpenReader(r, F.text, F.org); REPEAT Texts.Read(r, ch) UNTIL r.eot OR (ch = 0DX); pos := Texts.Pos(r); TextFrames.Show(F, pos) END ; last := TextFrames.Pos(F, MAX(INTEGER), F.Y) UNTIL (last >= F.text.len-1) OR (oldpos = pos) END END ; IF hasCar THEN SetCaret(F) END END END ELSE TextFrames.Handle(F, msg) END END END Handle; PROCEDURE OpenLog*; VAR x, y: INTEGER; V: MenuViewers.Viewer; F: TerminalFrames.Frame; old: TextFrames.Frame; new: TextFrame; CM: Oberon.CopyMsg; BEGIN IF Oberon.Par.vwr.dsc.next IS TerminalFrames.Frame THEN F := Oberon.Par.vwr.dsc.next(TerminalFrames.Frame); IF F.text IS Terminal THEN Oberon.AllocateUserViewer(Oberon.Mouse.X, x, y); NEW(new); old := TextFrames.NewText(F.text.text, 0); CM.F := new; old.handle(old, CM); (*SYSTEM.MOVE(SYSTEM.VAL(LONGINT,old),SYSTEM.VAL(LONGINT,new),SIZE(TextFrames.FrameDesc));*) new.T := F.text(Terminal); V := MenuViewers.New( TextFrames.NewMenu("Log", "System.Close System.Copy System.Grow Telnet.Clear Edit.Store "), new, TextFrames.menuH, x, y); V.dsc.next.handle := Handle; END ; END ; END OpenLog; PROCEDURE Clear*; VAR F: Display.Frame; BEGIN F := Oberon.Par.vwr.dsc.next; WITH F: TextFrame DO Texts.Delete(F.T.text, 0, F.T.text.len) END END Clear; BEGIN Texts.OpenWriter(W); END Telnet. Telnet.Open "lillian.inf.ethz.ch"