ð Syntax10.Scn.FntSyntax10i.Scn.Fntõÿÿÿ@ïðIStampElemsAlloc11 Dec 95JSyntax10b.Scn.Fnt˜)= …( Ù XZA O åL ¤<8sMODULE Sessions; (* ww  *) IMPORT TCP, Oberon, Texts, TextFrames, Viewers, Display, Input; CONST Sec* = 300; TYPE Terminal* = POINTER TO TerminalDesc; Session* = POINTER TO SessionDesc; SessionDesc = RECORD(TCP.ConnectionDesc) terminals: Terminal; alive: BOOLEAN; name*: ARRAY 64 OF CHAR END ; Receiver* = PROCEDURE (t: Terminal; ch: CHAR); Flusher* = PROCEDURE (t: Terminal; changed, terminated: BOOLEAN); TerminalDesc* = RECORD next: Terminal; session: Session; nextTime, timeout*: LONGINT; receive*: Receiver; flush*: Flusher END ; Tester* = PROCEDURE (t: Terminal): BOOLEAN; IdentifyMsg* = RECORD(Display.FrameMsg) session*: Session END ; Sentinel = POINTER TO RECORD(TerminalDesc) END ; Task = POINTER TO RECORD(Oberon.TaskDesc) sessionID: LONGINT END ; VAR w: Texts.Writer; PROCEDURE Distribute*(s: Session; ch: CHAR); VAR t, p: Terminal; BEGIN p := s.terminals; t := p.next; WHILE ~(t IS Sentinel) DO p.next := t.next; t.receive(t, ch); IF t.session = s THEN p.next := t; p := t; t := t.next ELSE t := p.next END END END Distribute; PROCEDURE Flush*(s: Session; changed, terminated: BOOLEAN); VAR t, p: Terminal; now: LONGINT; BEGIN p := s.terminals; t := p.next; now := Oberon.Time(); WHILE ~(t IS Sentinel) DO p.next := t.next; IF changed OR terminated OR (t.nextTime <= now) & (t.timeout >= 0) THEN t.flush(t, changed, terminated); t.nextTime := now + t.timeout END ; IF t.session = s THEN p.next := t; p := t; t := t.next ELSE t := p.next END END END Flush; PROCEDURE TaskHandler; CONST BufSize = 4096; VAR n, i: LONGINT; terminated: BOOLEAN; s: Session; c: TCP.Connection; buf: ARRAY BufSize OF CHAR; BEGIN c := TCP.ThisConnection(Oberon.CurTask(Task).sessionID); IF c # NIL THEN s := c(Session); terminated := ~TCP.Connected(s); n := TCP.Available(s); IF n # 0 THEN IF n > BufSize THEN n := BufSize END ; TCP.ReadBytes(s, buf, 0, n); i := 0; REPEAT Distribute(s, buf[i]); INC(i) UNTIL i = n; s.alive := ~terminated; Flush(s, TRUE, terminated) ELSIF s.alive THEN Flush(s, FALSE, terminated) END ; s.alive := ~terminated ELSE Oberon.Remove(Oberon.CurTask) END END TaskHandler; PROCEDURE New*(hostname: ARRAY OF CHAR; port: INTEGER): Session; VAR res: INTEGER; s: Session; sentinel: Sentinel; task: Task; adr: TCP.IpAdr; BEGIN s := NIL; TCP.HostByName(hostname, adr, res); IF res = TCP.Done THEN NEW(s); TCP.Connect(s, TCP.AnyPort, adr, port, 0, res); IF res = TCP.Done THEN COPY(hostname, s.name); s.alive := TRUE; NEW(sentinel); s.terminals := sentinel; sentinel.session := s; sentinel.next := sentinel; NEW(task); task.handle := TaskHandler; task.time := -1; task.sessionID := s.id; Oberon.Install(task) ELSE s := NIL END END ; RETURN s END New; PROCEDURE Remove*(t: Terminal); VAR p, q: Terminal; BEGIN IF t.session # NIL THEN p := t.session.terminals; q := p.next; WHILE (q # t) & ~(q IS Sentinel) DO p := q; q := q.next END ; IF q = t THEN p.next := t.next END ; t.session := NIL; t.next := NIL END END Remove; PROCEDURE Install*(t: Terminal; s: Session; r: Receiver; f: Flusher; timeout: LONGINT); VAR sentinel: Terminal; BEGIN Remove(t); t.session := s; sentinel := s.terminals; t.next := sentinel.next; sentinel.next := t; t.receive := r; t.flush := f; t.timeout := timeout; t.nextTime := Oberon.Time() + timeout END Install; PROCEDURE ThisSession*(t: Terminal): Session; BEGIN RETURN t.session END ThisSession; PROCEDURE ThisTerminal*(s: Session; test: Tester): Terminal; VAR t: Terminal; BEGIN t := s.terminals.next; WHILE ~(t IS Sentinel) & ~test(t) DO t := t.next END ; IF t IS Sentinel THEN RETURN NIL ELSE RETURN t END END ThisTerminal; PROCEDURE SendChar*(s: Session; ch: CHAR); BEGIN TCP.Write(s, ch) END SendChar; PROCEDURE SendString*(s: Session; str: ARRAY OF CHAR); VAR i: LONGINT; BEGIN i := 0; WHILE str[i] # 0X DO INC(i) END ; TCP.WriteBytes(s, str, 0, i) END SendString; PROCEDURE Send*; VAR text: Texts.Text; beg, end, time: LONGINT; v: Viewers.Viewer; s: Texts.Scanner; identify: IdentifyMsg; BEGIN IF Oberon.Par.vwr.dsc = Oberon.Par.frame THEN v := Oberon.Par.vwr ELSE v := Oberon.FocusViewer END ; identify.session := NIL; v.handle(v, identify); IF identify.session # NIL THEN 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 ; LOOP IF (s.class = Texts.Name) OR (s.class = Texts.String) THEN SendString(identify.session, s.s) ELSIF s.class = Texts.Int THEN SendChar(identify.session, CHR(s.i MOD 256)) ELSE EXIT END ; Texts.Scan(s) END END END Send; PROCEDURE Stop*; END Stop; PROCEDURE Start*; END Start; BEGIN Texts.OpenWriter(w) END Sessions.