ðÕSyntax10.Scn.FntSyntax10i.Scn.FntSyntax10b.Scn.Fnt       – /V. * d ³ r_Ú¨œi 9º{\A˜/h(6a#|\& µ& µ&Çj ñ Ò “ÚÏ–SMODULE Terminals; (* gri/ww 5 Jun 92 *) IMPORT Texts; CONST (* geometry *) Height* = 24; MaxWidth* = 132; (* attributes *) none* = 0; bold* = 1; underline* = 2; blinking* = 4; reverse* = 8; (* notifier id's *) update* = 1; moveCursor* = 2; scroll* = 3; (* control characters *) ENQ = 5X; BS = 08X; HT = 09X; LF = 0AX; VT = 0BX; FF = 0CX; CR = 0DX; CAN = 18X; SUB = 1AX; ESC = 1BX; DEL = 7FX; NSC = 91X; BRK = 0ACX; (* flags *) ansi* = 0; applic* = 1; lineFeed* = 2; insert* = 3; relative* = 4; cursorKeys* = 5; autowrap* = 6; TYPE Char* = RECORD ch*: CHAR; attr*: SHORTINT END ; Line* = POINTER TO RECORD len*: INTEGER; ch*: ARRAY MaxWidth + 1 OF Char END ; Location* = RECORD line*, col*: INTEGER END ; Terminal* = POINTER TO TerminalDesc; Sender* = PROCEDURE (T: Terminal; ch: CHAR); Breaker* = PROCEDURE (T: Terminal); Notifier* = PROCEDURE (T: Terminal; op, fromLine, fromCol, toLine, toCol: INTEGER; oldCursor: Location); TerminalDesc* = RECORD (* text *) attr*: SHORTINT; width*: INTEGER; top, bottom: INTEGER; cursor*: Location; wrapBefore: BOOLEAN; notify*: Notifier; line*: ARRAY Height + 1 OF Line; (* state machine *) flags*: SET; send: Sender; break: Breaker; state, parPos, strPos: INTEGER; parBuf: ARRAY 32 OF CHAR; strBuf: ARRAY 256 OF CHAR; tabs: ARRAY MaxWidth+1 OF BOOLEAN; answerback*: ARRAY 32 OF CHAR; oldAttr: SHORTINT; oldCursor: Location; oldRelative: BOOLEAN; cache*: Texts.Writer; text*: Texts.Text; pin*: LONGINT END ; (* text operations *) PROCEDURE MoveLines (t: Terminal; top, bot, dH: INTEGER); VAR i, j, d, s, from, to: INTEGER; line: Line; buf: ARRAY Height OF Line; BEGIN IF (dH # 0) & (top <= bot) THEN IF top < 1 THEN top := 1 END ; IF bot > Height THEN bot := Height END ; IF dH < 0 THEN d := 1; from := top - dH; to := top; s := bot ELSE d := -1; from := bot - dH; to := bot; s := top END ; i := to; j := 0; REPEAT line := t.line[i]; line.len := 0; buf[j] := line; i := i + d; INC(j) UNTIL i = from; s := s + d; WHILE from # s DO t.line[to] := t.line[from]; to := to + d; from := from + d END ; j := 0; s := ABS(dH); REPEAT t.line[to] := buf[j]; to := to + d; INC(j) UNTIL j = s; t.notify(t, scroll, top, dH, bot, 0, t.cursor) END END MoveLines; PROCEDURE Erase (t: Terminal; fromLine, fromCol, toLine, toCol: INTEGER); (* [from, to] *) VAR line: Line; i, j, len: INTEGER; char: Char; BEGIN char.ch := " "; char.attr := none; line := t.line[fromLine]; len := line.len; IF fromLine = toLine THEN IF fromCol <= len THEN IF toCol > len THEN line.len := fromCol - 1 ELSE j := fromCol; WHILE j <= toCol DO line.ch[j] := char; INC(j) END END END ELSE IF fromCol <= len THEN line.len := fromCol - 1 END ; j := fromLine + 1; WHILE j < toLine DO t.line[j].len := 0; INC(j) END ; line := t.line[toLine]; len := line.len; IF toCol >= len THEN line.len := 0 ELSE i := 1; WHILE i <= toCol DO line.ch[i].ch := " "; INC(i) END END END ; t.notify(t, update, fromLine, fromCol, toLine, toCol, t.cursor) END Erase; PROCEDURE DeleteChars (t: Terminal; n: INTEGER); (* including cursor character *) VAR c, j, len: INTEGER; line: Line; cursor: Location; BEGIN cursor := t.cursor; c := cursor.col; line := t.line[cursor.line]; j := c + n; len := line.len; IF j <= len THEN REPEAT line.ch[c] := line.ch[j]; INC(c); INC(j) UNTIL j > len; line.len := c - 1; t.notify(t, update, cursor.line, cursor.col, cursor.line, len, cursor) END END DeleteChars; PROCEDURE DeleteLines (t: Terminal; n: INTEGER); (* including cursor line *) VAR top, bot: INTEGER; BEGIN top := t.cursor.line; bot := t.bottom; IF bot - top + 1 < n THEN n := bot - top + 1 END ; MoveLines(t, t.cursor.line, t.bottom, -n) END DeleteLines; PROCEDURE InsertChars (t: Terminal; n: INTEGER); (* move including cursor character, cursor not moved *) VAR i, j, len: INTEGER; line: Line; char: Char; cursor: Location; BEGIN cursor := t.cursor; line := t.line[cursor.line]; len := line.len; j := len + n; IF j >= t.width THEN j := t.width END ; i := j - n; IF i < cursor.col THEN IF len >= cursor.col THEN line.len := cursor.col - 1 END ELSE line.len := j; REPEAT line.ch[j] := line.ch[i]; DEC(i); DEC(j) UNTIL i < cursor.col; char.ch := " "; char.attr := none; WHILE j >= cursor.col DO line.ch[j] := char; DEC(j) END END ; t.notify(t, update, cursor.line, cursor.col, cursor.line, line.len, cursor) END InsertChars; PROCEDURE InsertLines (t: Terminal; n: INTEGER); (* move including cursor line, cursor not moved *) VAR top, bot: INTEGER; BEGIN top := t.cursor.line; bot := t.bottom; IF bot - top + 1 < n THEN n := bot - top + 1 END ; MoveLines(t, top, bot, n) END InsertLines; PROCEDURE Scroll (t: Terminal; up: BOOLEAN); (* scroll one line within margins *) BEGIN IF up THEN MoveLines(t, t.top, t.bottom, -1) ELSE MoveLines(t, t.top, t.bottom, 1) END END Scroll; PROCEDURE SetCursor (t: Terminal; line, col: INTEGER; relative: BOOLEAN); (* (1, 1) means upper, left corner *) VAR newLoc: Location; BEGIN IF relative THEN IF line < t.top THEN line := t.top ELSIF line > t.bottom THEN line := t.bottom END ELSE IF line < 1 THEN line := 1 ELSIF line > Height THEN line := Height END END ; IF col < 1 THEN col := 1 ELSIF col > t.width THEN col := t.width END ; newLoc.line := line; newLoc.col := col; t.notify(t, moveCursor, t.cursor.line, t.cursor.col, line, col, newLoc); t.cursor := newLoc; t.wrapBefore := FALSE END SetCursor; PROCEDURE SetMargins (t: Terminal; top, bottom: INTEGER); (* [top, bottom] *) BEGIN t.top := top; t.bottom := bottom END SetMargins; PROCEDURE SetWidth (t: Terminal; width: INTEGER); VAR i: INTEGER; BEGIN t.width := width; i := 1; WHILE i <= Height DO IF t.line[i].len > width THEN t.line[i].len := width END ; INC(i) END ; IF t.cursor.col > width THEN t.cursor.col := width END ; t.notify(t, update, 1, 1, Height, t.width, t.cursor) END SetWidth; PROCEDURE SetAttribute (t: Terminal; attr: SHORTINT); BEGIN IF attr = none THEN t.attr := attr ELSE t.attr := t.attr + attr END END SetAttribute; PROCEDURE PutChar(t: Terminal; VAR cursor: Location; VAR wrapBefore: BOOLEAN; ch: Char; VAR dH: INTEGER); VAR len: INTEGER; line: Line; char: Char; BEGIN IF wrapBefore THEN IF cursor.col = t.width THEN INC(cursor.line); cursor.col := 1; IF cursor.line > t.bottom THEN Scroll(t, TRUE); DEC(cursor.line); DEC(dH) END ELSE INC(cursor.col) END END ; line := t.line[cursor.line]; len := line.len; IF (ch.ch = " ") & (ch.attr = none) & (cursor.col = len) THEN DEC(line.len) ELSIF (ch.ch # " ") OR (ch.attr # none) OR (cursor.col < len) THEN IF len <= cursor.col THEN line.len := cursor.col; char.ch := " "; char.attr := none; INC(len); WHILE len < cursor.col DO line.ch[len] := char; INC(len) END END ; line.ch[cursor.col] := ch END ; IF cursor.col = t.width THEN wrapBefore := autowrap IN t.flags ELSE INC(cursor.col); wrapBefore := FALSE END END PutChar; PROCEDURE WriteString (t: Terminal; s: ARRAY OF CHAR; n: INTEGER); (* writes n characters at curs. pos. *) VAR i, dH: INTEGER; cursor, oldCur: Location; char: Char; BEGIN cursor := t.cursor; oldCur := cursor; char.attr := t.attr; dH := 0; i := 0; WHILE i < n DO char.ch := s[i]; PutChar(t, cursor, t.wrapBefore, char, dH); INC(i) END ; t.cursor := cursor; IF oldCur.line + dH < 1 THEN t.notify(t, update, 1, oldCur.col, cursor.line, cursor.col, oldCur) ELSE t.notify(t, update, oldCur.line + dH, oldCur.col, cursor.line, cursor.col, oldCur) END END WriteString; PROCEDURE EFill (t: Terminal); VAR i, j, w: INTEGER; line: Line; char: Char; BEGIN i := 1; w := t.width; char.ch := "E"; char.attr := none; WHILE i <= Height DO j := 1; line := t.line[i]; line.len := w; WHILE j <= w DO line.ch[j] := char; INC(j) END ; INC(i) END ; t.notify(t, update, 1, 1, Height, t.width, t.cursor) END EFill; (* sequence interpretation *) PROCEDURE DelLast (T: Terminal); BEGIN IF T.cache.buf.len > 0 THEN Texts.Append(T.text, T.cache.buf) END ; IF T.text.len > 0 THEN Texts.Delete(T.text, T.text.len-1, T.text.len) END END DelLast; PROCEDURE Reset* (T: Terminal); VAR i: INTEGER; BEGIN T.flags := {ansi, autowrap}; T.state := 0; T.strPos := 0; i := 1; WHILE i <= MaxWidth DO T.tabs[i] := i MOD 8 = 1; INC(i) END ; T.answerback := "*** Hello World ***"; T.oldAttr := none; T.oldCursor.line := 1; T.oldCursor.col := 1; T.oldRelative := FALSE; Erase(T, 1, 1, Height, T.width); SetMargins(T, 1, Height); SetCursor(T, 1, 1, FALSE) END Reset; PROCEDURE SendStr (T: Terminal; s: ARRAY OF CHAR); VAR i: INTEGER; BEGIN i := 0; WHILE s[i] # 0X DO T.send(T, s[i]); INC(i) END END SendStr; PROCEDURE SendInt (T: Terminal; x: INTEGER); VAR i: INTEGER; d: ARRAY 3 OF CHAR; BEGIN i := 0; REPEAT d[i] := CHR(x MOD 10 + ORD("0")); x := x DIV 10; INC(i) UNTIL x = 0; WHILE i > 0 DO DEC(i); T.send(T, d[i]) END END SendInt; PROCEDURE Update (T: Terminal); BEGIN IF T.strPos > 0 THEN WriteString(T, T.strBuf, T.strPos); T.strPos := 0 END END Update; PROCEDURE Flush* (T: Terminal); BEGIN Update(T); IF (T.text # NIL) & (T.cache.buf.len > 0) THEN Texts.Append(T.text, T.cache.buf) END END Flush; PROCEDURE Write (T: Terminal; ch: CHAR); BEGIN IF T.strPos >= LEN(T.strBuf) THEN Update(T) END ; T.strBuf[T.strPos] := ch; INC(T.strPos) END Write; PROCEDURE ESCSequence (T: Terminal; last: CHAR); VAR ch: CHAR; BEGIN ch := T.parBuf[0]; IF ansi IN T.flags THEN IF ("7" <= last) & (last <= "c") THEN CASE last OF | "7": T.oldAttr := T.attr; T.oldCursor := T.cursor; T.oldRelative := relative IN T.flags | "8": IF ch = "#" THEN EFill(T) ELSE SetAttribute(T, T.oldAttr); SetCursor(T, T.oldCursor.line, T.oldCursor.col, FALSE); IF T.oldRelative THEN INCL(T.flags, relative) ELSE EXCL(T.flags, relative) END END | "=": INCL(T.flags, applic) | ">": EXCL(T.flags, applic) | "D": IF T.cursor.line = T.bottom THEN Scroll(T, TRUE) ELSE SetCursor(T, T.cursor.line+1, T.cursor.col, FALSE) END | "E": IF T.cursor.line = T.bottom THEN Scroll(T, TRUE); SetCursor(T, T.cursor.line, 1, FALSE) ELSE SetCursor(T, T.cursor.line+1, 1, FALSE) END | "H": T.tabs[T.cursor.col] := TRUE | "M": IF T.cursor.line = T.top THEN Scroll(T, FALSE) ELSE SetCursor(T, T.cursor.line-1, T.cursor.col, relative IN T.flags) END | "Z": T.send(T, ESC); SendStr(T, "[?1;2c") (* VT100 *) | "c": Reset(T) | "9" .. "<", "?" .. "C", "F", "G", "I" .. "L", "N" .. "Y", "[" .. "b": (* ignore *) END END ELSE (* VT52 mode *) IF ("<" <= last) & (last <= "Z") THEN CASE last OF | "<": INCL(T.flags, ansi) | "=": INCL(T.flags, applic) | ">": EXCL(T.flags, applic) | "A": SetCursor(T, T.cursor.line-1, T.cursor.col, FALSE) | "B": SetCursor(T, T.cursor.line+1, T.cursor.col, FALSE) | "C": SetCursor(T, T.cursor.line, T.cursor.col+1, FALSE) | "D": SetCursor(T, T.cursor.line, T.cursor.col-1, FALSE) | "H": SetCursor(T, 1, 1, FALSE) | "I": IF T.cursor.line = 1 THEN Scroll(T, FALSE) ELSE SetCursor(T, T.cursor.line-1, T.cursor.col, FALSE) END | "J": Erase(T, T.cursor.line, T.cursor.col, Height, T.width) | "K": Erase(T, T.cursor.line, T.cursor.col, T.cursor.line, T.width) | "Z": T.send(T, ESC); SendStr(T, "/Z") | "?", "@", "E" .. "G", "L" .. "Y", "[", "\": (* ignore *) END END END END ESCSequence; PROCEDURE CSISequence (T: Terminal; last: CHAR); VAR ch: CHAR; pos, p1, p2: INTEGER; PROCEDURE Next; BEGIN ch := T.parBuf[pos]; INC(pos) END Next; PROCEDURE Par (zeroVal: INTEGER): INTEGER; VAR x: INTEGER; BEGIN x := 0; IF ("0" <= ch) & (ch <= "9") THEN x := ORD(ch) - ORD("0"); Next; WHILE ("0" <= ch) & (ch <= "9") & (x <= (MAX(INTEGER) - 9) DIV 10) DO x := 10*x + ORD(ch) - ORD("0"); Next END ; WHILE ("0" <= ch) & (ch <= "9") DO Next END ; IF ch = ";" THEN Next END ELSIF ch = ";" THEN Next END ; IF x = 0 THEN x := zeroVal END ; RETURN x END Par; BEGIN T.parBuf[T.parPos] := 0FFX; pos := 0; Next; IF ( "@" <= last) & (last <= "y") THEN CASE last OF | "@": InsertChars(T, Par(1)) | "A": SetCursor(T, T.cursor.line - Par(1), T.cursor.col, relative IN T.flags) | "B": SetCursor(T, T.cursor.line + Par(1), T.cursor.col, relative IN T.flags) | "C": SetCursor(T, T.cursor.line, T.cursor.col + Par(1), relative IN T.flags) | "D": SetCursor(T, T.cursor.line, T.cursor.col - Par(1), relative IN T.flags) | "H", "f": p1 := Par(1); p2 := Par(1); IF relative IN T.flags THEN SetCursor(T, T.top + p1 - 1, p2, TRUE) ELSE SetCursor(T, p1, p2, FALSE) END | "J": p1 := Par(0); IF p1 = 0 THEN Erase(T, T.cursor.line, T.cursor.col, Height, T.width) ELSIF p1 = 1 THEN Erase(T, 1, 1, T.cursor.line, T.cursor.col) ELSIF p1 = 2 THEN Erase(T, 1, 1, Height, T.width) END | "K": p1 := Par(0); IF p1 = 0 THEN Erase(T, T.cursor.line, T.cursor.col, T.cursor.line, T.width) ELSIF p1 = 1 THEN Erase(T, T.cursor.line, 1, T.cursor.line, T.cursor.col) ELSIF p1 = 2 THEN Erase(T, T.cursor.line, 1, T.cursor.line, T.width) END | "L": InsertLines(T, Par(1)) | "M": DeleteLines(T, Par(1)) | "P": DeleteChars(T, Par(1)) | "c": IF Par(0) = 0 THEN T.send(T, ESC); SendStr(T, "[?1;2c") (* VT100 *) END | "g": REPEAT p1 := Par(0); IF p1 = 0 THEN T.tabs[T.cursor.col] := FALSE ELSIF p1 = 3 THEN p1 := 1; WHILE p1 <= MaxWidth DO T.tabs[p1] := FALSE; INC(p1) END END UNTIL pos > T.parPos | "h": IF ch = "?" THEN Next; REPEAT p1 := Par(0); IF p1 = 1 THEN INCL(T.flags, cursorKeys) ELSIF p1 = 3 THEN Erase(T, 1, 1, Height, T.width); SetWidth(T, MaxWidth); SetMargins(T, 1, Height); SetCursor(T, 1, 1, relative IN T.flags) ELSIF p1 = 6 THEN INCL(T.flags, relative); SetCursor(T, 1, 1, TRUE) ELSIF p1 = 7 THEN INCL(T.flags, autowrap) END UNTIL pos > T.parPos ELSE REPEAT p1 := Par(0); IF p1 = 4 THEN INCL(T.flags, insert) ELSIF p1 = 20 THEN INCL(T.flags, lineFeed) END UNTIL pos > T.parPos END | "l": IF ch = "?" THEN Next; REPEAT p1 := Par(0); IF p1 = 1 THEN EXCL(T.flags, cursorKeys) ELSIF p1 = 2 THEN EXCL(T.flags, ansi) ELSIF p1 = 3 THEN Erase(T, 1, 1, Height, T.width); SetWidth(T, 80); SetMargins(T, 1, Height); SetCursor(T, 1, 1, relative IN T.flags) ELSIF p1 = 6 THEN EXCL(T.flags, relative); SetCursor(T, 1, 1, FALSE) ELSIF p1 = 7 THEN EXCL(T.flags, autowrap) END UNTIL pos > T.parPos ELSE REPEAT p1 := Par(0); IF p1 = 4 THEN EXCL(T.flags, insert) ELSIF p1 = 20 THEN EXCL(T.flags, lineFeed) END UNTIL pos > T.parPos END | "m": REPEAT p1 := Par(0); IF p1 = 0 THEN SetAttribute(T, none) ELSIF p1 = 1 THEN SetAttribute(T, bold) ELSIF p1 = 4 THEN SetAttribute(T, underline) ELSIF p1 = 5 THEN SetAttribute(T, blinking) ELSIF p1 = 7 THEN SetAttribute(T, reverse) END UNTIL pos > T.parPos | "n": IF ch = "?" THEN Next; IF Par(0) = 15 THEN (* printer status report *) T.send(T, ESC); SendStr(T, "[?13n") END ELSE p1 := Par(0); IF p1 = 5 THEN (* terminal status report *) T.send(T, ESC); SendStr(T, "[0n") ELSIF p1 = 6 THEN (* cursor position report *) T.send(T, ESC); T.send(T, "["); IF relative IN T.flags THEN SendInt(T, T.cursor.line - T.top + 1) ELSE SendInt(T, T.cursor.line) END ; T.send(T, ";"); SendInt(T, T.cursor.col); T.send(T, "R") END END | "r": p1 := Par(0); p2 := Par(0); IF (p1 = 0) & (p2 = 0) THEN p1 := 1; p2 := Height END ; SetMargins(T, p1, p2); SetCursor(T, 1, 1, TRUE) |"y": p1 := Par(0); IF p1 = 2 THEN p1 := Par(0); IF p1 = 1 THEN Reset(T) ELSE T.send(T, ESC); SendStr(T, "[0n") END END | "E" .. "G", "I", "N", "O", "Q" .. "b", "d", "e", "i" .. "k", "o" .. "q", "s" .. "x": (* ignore *) END END END CSISequence; PROCEDURE Receive* (T: Terminal; ch: CHAR); VAR p: INTEGER; BEGIN ch := CHR(ORD(ch) MOD 128); IF ch < " " THEN (* interpret control characters immediately *) CASE ch OF | ENQ: SendStr(T, T.answerback) | BS: Update(T); SetCursor(T, T.cursor.line, T.cursor.col-1, FALSE); IF T.text # NIL THEN DelLast(T) END | HT: Update(T); p := T.cursor.col + 1; WHILE (p <= T.width) & ~T.tabs[p] DO INC(p) END ; SetCursor(T, T.cursor.line, p, FALSE); IF T.text # NIL THEN Texts.Write(T.cache, HT) END | LF, VT, FF: Update(T); IF T.cursor.line = T.bottom THEN Scroll(T, TRUE); IF lineFeed IN T.flags THEN SetCursor(T, T.cursor.line, 1, FALSE) END ELSIF lineFeed IN T.flags THEN SetCursor(T, T.cursor.line+1, 1, FALSE) ELSE SetCursor(T, T.cursor.line+1, T.cursor.col, FALSE) END ; IF T.text # NIL THEN Texts.Write(T.cache, CR) END | CR: Update(T); SetCursor(T, T.cursor.line, 1, FALSE) | CAN, SUB: (* cancel *) T.state := 0 | ESC: T.state := 1 | 0X .. 4X, 6X, 7X, 0EX .. 17X, 19X, 1CX .. 1FX: (* ignore *) END ELSE (* drive state machine *) CASE T.state OF | 0: (* normal characters *) IF ch = DEL THEN IF T.cursor.col > 1 THEN Update(T); SetCursor(T, T.cursor.line, T.cursor.col-1, FALSE); DeleteChars(T, 1) END ; IF T.text # NIL THEN DelLast(T) END ELSE IF insert IN T.flags THEN Update(T); InsertChars(T, 1) END ; Write(T, ch); IF T.text # NIL THEN Texts.Write(T.cache, ch) END END | 1: (* sequence introduction *) IF ch = "[" THEN T.state := 3; T.parPos := 0; ELSIF ~(ansi IN T.flags) & (ch = "Y") THEN T.state := 4; T.parPos := 0 ELSIF (" " <= ch) & (ch <= "/") THEN T.state := 2; T.parBuf[0] := ch; T.parPos := 1 ELSIF ("0" <= ch) & (ch <= "~") THEN T.state := 2; T.parPos := 0; Update(T); ESCSequence(T, ch); T.state := 0 ELSE (* error *) T.state := 0 END | 2: (* ESC sequence *) IF ("0" <= ch) & (ch <= "~") THEN Update(T); ESCSequence(T, ch); T.state := 0 ELSIF T.parPos < LEN(T.parBuf) THEN T.parBuf[T.parPos] := ch; INC(T.parPos) ELSE (* error *) T.state := 0 END | 3: (* CSI sequence *) IF ("@" <= ch) & (ch <= "~") THEN Update(T); CSISequence(T, ch); T.state := 0 ELSIF T.parPos < LEN(T.parBuf) THEN T.parBuf[T.parPos] := ch; INC(T.parPos) ELSE (* error *) T.state := 0 END | 4: (* VT52 ESC Y sequence *) IF T.parPos = 0 THEN T.parBuf[0] := ch; T.parPos := 1 ELSE Update(T); SetCursor(T, ORD(T.parBuf[0])-31, ORD(ch)-31, FALSE); T.state := 0 END END END END Receive; PROCEDURE Send* (T: Terminal; ch: CHAR); BEGIN IF T.text # NIL THEN T.pin := T.text.len END ; IF ch <= DEL THEN (* normal ASCII *) T.send(T, ch); IF (ch = CR) & (lineFeed IN T.flags) THEN T.send(T, LF) END ELSIF (ch = BRK) OR (ch = 0ADX) THEN T.break(T) ELSIF (ch = 0AEX) OR (ch = 0AFX) THEN SendStr(T, T.answerback) ELSIF (0C1X <= ch) & (ch <= 0C4X) THEN (* cursor keys *) T.send(T, ESC); IF ansi IN T.flags THEN IF cursorKeys IN T.flags THEN T.send(T, "O") ELSE T.send(T, "[") END END ; T.send(T, CHR(ORD(ch) - 128)) ELSIF ch = "€" THEN T.send(T, "A"); T.send(T, "e") ELSIF ch = "" THEN T.send(T, "O"); T.send(T, "e") ELSIF ch = "‚" THEN T.send(T, "U"); T.send(T, "e") ELSIF ch = "ƒ" THEN T.send(T, "a"); T.send(T, "e") ELSIF (ch = "†") OR (ch = "‹") OR (ch = "”") THEN T.send(T, "a") ELSIF ch = "„" THEN T.send(T, "o"); T.send(T, "e") ELSIF (ch = "‰") OR (ch = "Ž") THEN T.send(T, "o") ELSIF ch = "…" THEN T.send(T, "u"); T.send(T, "e") ELSIF (ch = "Š") OR (ch = "") THEN T.send(T, "u") ELSIF ch = "“" THEN T.send(T, "c") ELSIF ch = "•" THEN T.send(T, "n") ELSIF (ch = "‡") OR (ch = "Œ") OR (ch = "") OR (ch = "‘") THEN T.send(T, "e") ELSIF (ch = "ˆ") OR (ch = "") OR (ch = "’") THEN T.send(T, "i") ELSIF ch = 9FX THEN T.send(T, " ") ELSIF ch = "›" THEN T.send(T, "-") END END Send; PROCEDURE SendString* (T: Terminal; VAR s: ARRAY OF CHAR); VAR i: INTEGER; BEGIN i := 0; WHILE s[i] # 0X DO Send(T, s[i]); INC(i) END END SendString; PROCEDURE SendText* (T: Terminal; text: Texts.Text; beg, end: LONGINT); VAR R: Texts.Reader; ch: CHAR; BEGIN Texts.OpenReader(R, text, beg); WHILE Texts.Pos(R) < end DO Texts.Read(R, ch); Send(T, ch) END END SendText; PROCEDURE Open* (T: Terminal; text: Texts.Text; send: Sender; break: Breaker; notify: Notifier); VAR l: Line; i: INTEGER; BEGIN T.width := 80; T.top := 1; T.bottom := Height; T.cursor.line := 1; T.cursor.col := 1; T.attr := none; T.wrapBefore := FALSE; T.notify := notify; i := Height; REPEAT NEW(l); T.line[i] := l ; DEC(i) UNTIL i = 0; Reset(T); T.text := text; Texts.OpenWriter(T.cache); T.pin := 0; T.send := send; T.break := break END Open; END Terminals.