home *** CD-ROM | disk | FTP | other *** search
/ Amiga MA Magazine 1998 #6 / amigamamagazinepolishissue1998.iso / coders / jËzyki_programowania / oberon / system / terminals.mod (.txt) < prev    next >
Oberon Text  |  1977-12-31  |  21KB  |  544 lines

  1. Syntax10.Scn.Fnt
  2. Syntax10i.Scn.Fnt
  3. Syntax10b.Scn.Fnt
  4. MODULE Terminals;    (*Copyright (c) Robert Griesemer and Wolfgang Weck, 1992-95 /gri, ww, mf 30.9.93*)
  5.     IMPORT Texts;
  6.     CONST
  7.     (* geometry *)
  8.         Height*=24; MaxWidth*=132;
  9.     (* attributes *)
  10.         none*=0; bold*=1; underline*=2; blinking*=4; reverse*=8;
  11.     (* notifier id's *)
  12.         update*=1; moveCursor*=2; scroll*=3;
  13.     (* control characters *)
  14.         ENQ=5X; BS=08X; HT=09X; LF=0AX; VT=0BX; FF=0CX; CR=0DX;
  15.         CAN=18X; SUB=1AX; ESC=1BX; DEL=7FX; NSC=91X; BRK=0ACX;
  16.     (* flags *)
  17.         ansi*=0; applic*=1; lineFeed*=2; insert*=3; relative*=4; cursorKeys*=5; autowrap*=6;
  18.     TYPE
  19.         Char*=RECORD ch*: CHAR; attr*: SHORTINT END;
  20.         Line*=POINTER TO RECORD
  21.             len*: INTEGER;
  22.             ch*: ARRAY MaxWidth+1 OF Char
  23.         END;
  24.         Location*=RECORD
  25.             line*, col*: INTEGER
  26.         END;
  27.         Terminal*=POINTER TO TerminalDesc;
  28.         Sender*=PROCEDURE (T: Terminal; ch: CHAR);
  29.         Breaker*=PROCEDURE (T: Terminal);
  30.         Notifier*=PROCEDURE (T: Terminal; op, fromLine, fromCol, toLine, toCol: INTEGER; oldCursor: Location);
  31.         TerminalDesc*=RECORD
  32.         (* text *)
  33.             attr*: SHORTINT;
  34.             width*: INTEGER;
  35.             top, bottom: INTEGER;
  36.             cursor*: Location;
  37.             wrapBefore: BOOLEAN;
  38.             notify*: Notifier;
  39.             line*: ARRAY Height+1 OF Line;
  40.         (* state machine *)
  41.             flags*: SET;
  42.             send: Sender;
  43.             break: Breaker;
  44.             state, parPos, strPos: INTEGER;
  45.             parBuf: ARRAY 32 OF CHAR;
  46.             strBuf: ARRAY 256 OF CHAR;
  47.             tabs: ARRAY MaxWidth+1 OF BOOLEAN;
  48.             answerback*: ARRAY 32 OF CHAR;
  49.             oldAttr: SHORTINT;
  50.             oldCursor: Location;
  51.             oldRelative: BOOLEAN;
  52.             cache*: Texts.Writer;
  53.             text*: Texts.Text;
  54.             pin*: LONGINT
  55.         END;
  56. (* text operations *)
  57.     PROCEDURE MoveLines (t: Terminal; top, bot, dH: INTEGER);
  58.         VAR i, j, d, s, from, to: INTEGER; line: Line; buf: ARRAY Height OF Line;
  59.     BEGIN
  60.         IF (dH # 0) & (top <= bot) THEN
  61.             IF top < 1 THEN top:=1 END;
  62.             IF bot > Height THEN bot:=Height END;
  63.             IF dH < 0 THEN d:=1; from:=top-dH; to:=top; s:=bot ELSE d:=-1; from:=bot-dH; to:=bot; s:=top END;
  64.             i:=to; j:=0;
  65.             REPEAT line:=t.line[i]; line.len:=0; buf[j]:=line; i:=i+d; INC(j) UNTIL i=from;
  66.             s:=s+d;
  67.             WHILE from # s DO t.line[to]:=t.line[from]; to:=to+d; from:=from+d END;
  68.             j:=0; s:=ABS(dH);
  69.             REPEAT t.line[to]:=buf[j]; to:=to+d; INC(j) UNTIL j=s;
  70.             t.notify(t, scroll, top, dH, bot, 0, t.cursor)
  71.         END
  72.     END MoveLines;
  73.     PROCEDURE Erase (t: Terminal; fromLine, fromCol, toLine, toCol: INTEGER); (* [from, to] *)
  74.         VAR line: Line; i, j, len: INTEGER; char: Char;
  75.     BEGIN char.ch:=" "; char.attr:=none; line:=t.line[fromLine]; len:=line.len;
  76.         IF fromLine=toLine THEN
  77.             IF fromCol <= len THEN
  78.                 IF toCol > len THEN line.len:=fromCol-1
  79.                 ELSE j:=fromCol;
  80.                     WHILE j <= toCol DO line.ch[j]:=char; INC(j) END
  81.                 END
  82.             END
  83.         ELSE
  84.             IF fromCol <= len THEN line.len:=fromCol-1 END;
  85.             j:=fromLine+1;
  86.             WHILE j < toLine DO t.line[j].len:=0; INC(j) END;
  87.             line:=t.line[toLine]; len:=line.len;
  88.             IF toCol >= len THEN line.len:=0
  89.             ELSE i:=1;
  90.                 WHILE i <= toCol DO line.ch[i].ch:=" "; INC(i) END
  91.             END
  92.         END;
  93.         t.notify(t, update, fromLine, fromCol, toLine, toCol, t.cursor)
  94.     END Erase;
  95.     PROCEDURE DeleteChars (t: Terminal; n: INTEGER); (* including cursor character *)
  96.         VAR c, j, len: INTEGER; line: Line; cursor: Location;
  97.     BEGIN cursor:=t.cursor; c:=cursor.col; line:=t.line[cursor.line]; j:=c+n; len:=line.len;
  98.         IF j <= len THEN
  99.             REPEAT line.ch[c]:=line.ch[j]; INC(c); INC(j) UNTIL j > len;
  100.             line.len:=c-1;
  101.             t.notify(t, update, cursor.line, cursor.col, cursor.line, len, cursor)
  102.         END
  103.     END DeleteChars;
  104.     PROCEDURE DeleteLines (t: Terminal; n: INTEGER); (* including cursor line *)
  105.         VAR top, bot: INTEGER;
  106.     BEGIN top:=t.cursor.line; bot:=t.bottom;
  107.         IF bot-top+1 < n THEN n:=bot-top+1 END;
  108.         MoveLines(t, t.cursor.line, t.bottom, -n)
  109.     END DeleteLines;
  110.     PROCEDURE InsertChars (t: Terminal; n: INTEGER); (* move including cursor character, cursor not moved *)
  111.         VAR i, j, len: INTEGER; line: Line; char: Char; cursor: Location;
  112.     BEGIN cursor:=t.cursor; line:=t.line[cursor.line]; len:=line.len; j:=len+n;
  113.         IF j >= t.width THEN j:=t.width END;
  114.         i:=j-n;
  115.         IF i < cursor.col THEN
  116.             IF len >= cursor.col THEN line.len:=cursor.col-1 END
  117.         ELSE line.len:=j;
  118.             REPEAT line.ch[j]:=line.ch[i]; DEC(i); DEC(j) UNTIL i < cursor.col;
  119.             char.ch:=" "; char.attr:=none;
  120.             WHILE j >= cursor.col DO line.ch[j]:=char; DEC(j) END
  121.         END;
  122.         t.notify(t, update, cursor.line, cursor.col, cursor.line, line.len, cursor)
  123.     END InsertChars;
  124.     PROCEDURE InsertLines (t: Terminal; n: INTEGER); (* move including cursor line, cursor not moved *)
  125.         VAR top, bot: INTEGER;
  126.     BEGIN top:=t.cursor.line; bot:=t.bottom;
  127.         IF bot-top+1 < n THEN n:=bot-top+1 END;
  128.         MoveLines(t, top, bot, n)
  129.     END InsertLines;
  130.     PROCEDURE Scroll (t: Terminal; up: BOOLEAN); (* scroll one line within margins *)
  131.     BEGIN
  132.         IF up THEN MoveLines(t, t.top, t.bottom, -1) ELSE MoveLines(t, t.top, t.bottom, 1) END
  133.     END Scroll;
  134.     PROCEDURE SetCursor (t: Terminal; line, col: INTEGER; relative: BOOLEAN); (* (1, 1) means upper, left corner *)
  135.         VAR newLoc: Location;
  136.     BEGIN
  137.         IF relative THEN
  138.             IF line < t.top THEN line:=t.top ELSIF line > t.bottom THEN line:=t.bottom END
  139.         ELSE
  140.             IF line < 1 THEN line:=1 ELSIF line > Height THEN line:=Height END
  141.         END;
  142.         IF col < 1 THEN col:=1 ELSIF col > t.width THEN col:=t.width END;
  143.         newLoc.line:=line; newLoc.col:=col;
  144.         t.notify(t, moveCursor, t.cursor.line, t.cursor.col, line, col, newLoc);
  145.         t.cursor:=newLoc; t.wrapBefore:=FALSE
  146.     END SetCursor;
  147.     PROCEDURE SetMargins (t: Terminal; top, bottom: INTEGER); (* [top, bottom] *)
  148.     BEGIN t.top:=top; t.bottom:=bottom
  149.     END SetMargins;
  150.     PROCEDURE SetWidth (t: Terminal; width: INTEGER);
  151.         VAR i: INTEGER;
  152.     BEGIN t.width:=width; i:=1;
  153.         WHILE i <= Height DO
  154.             IF t.line[i].len > width THEN t.line[i].len:=width END;
  155.             INC(i)
  156.         END;
  157.         IF t.cursor.col > width THEN t.cursor.col:=width END;
  158.         t.notify(t, update, 1, 1, Height, t.width, t.cursor)
  159.     END SetWidth;
  160.     PROCEDURE SetAttribute (t: Terminal; attr: SHORTINT);
  161.     BEGIN
  162.         IF attr=none THEN t.attr:=attr ELSE t.attr:=t.attr+attr END
  163.     END SetAttribute;
  164.     PROCEDURE PutChar(t: Terminal; VAR cursor: Location; VAR wrapBefore: BOOLEAN; ch: Char; VAR dH: INTEGER);
  165.         VAR len: INTEGER; line: Line; char: Char;
  166.     BEGIN
  167.         IF wrapBefore THEN
  168.             IF cursor.col=t.width THEN INC(cursor.line); cursor.col:=1;
  169.                 IF cursor.line > t.bottom THEN Scroll(t, TRUE); DEC(cursor.line); DEC(dH) END
  170.             ELSE INC(cursor.col)
  171.             END
  172.         END;
  173.         line:=t.line[cursor.line]; len:=line.len;
  174.         IF (ch.ch=" ") & (ch.attr=none) & (cursor.col=len) THEN DEC(line.len)
  175.         ELSIF (ch.ch # " ") OR (ch.attr # none) OR (cursor.col < len) THEN
  176.             IF len <= cursor.col THEN line.len:=cursor.col; char.ch:=" "; char.attr:=none; INC(len);
  177.                 WHILE len < cursor.col DO line.ch[len]:=char; INC(len) END
  178.             END;
  179.             line.ch[cursor.col]:=ch
  180.         END;
  181.         IF cursor.col=t.width THEN wrapBefore:=autowrap IN t.flags
  182.         ELSE INC(cursor.col); wrapBefore:=FALSE
  183.         END
  184.     END PutChar;
  185.     PROCEDURE WriteString (t: Terminal; s: ARRAY OF CHAR; n: INTEGER); (* writes n characters at curs. pos. *)
  186.         VAR i, dH: INTEGER; cursor, oldCur: Location; char: Char;
  187.     BEGIN cursor:=t.cursor;
  188.         oldCur:=cursor; char.attr:=t.attr; dH:=0; i:=0;
  189.         WHILE i < n DO char.ch:=s[i]; PutChar(t, cursor, t.wrapBefore, char, dH); INC(i) END;
  190.         t.cursor:=cursor;
  191.         IF oldCur.line+dH < 1 THEN t.notify(t, update, 1, oldCur.col, cursor.line, cursor.col, oldCur)
  192.         ELSE t.notify(t, update, oldCur.line+dH, oldCur.col, cursor.line, cursor.col, oldCur)
  193.         END
  194.     END WriteString;
  195.     PROCEDURE EFill (t: Terminal);
  196.         VAR i, j, w: INTEGER; line: Line; char: Char;
  197.     BEGIN i:=1; w:=t.width; char.ch:="E"; char.attr:=none;
  198.         WHILE i <= Height DO j:=1; line:=t.line[i]; line.len:=w;
  199.             WHILE j <= w DO line.ch[j]:=char; INC(j) END;
  200.             INC(i)
  201.         END;
  202.         t.notify(t, update, 1, 1, Height, t.width, t.cursor)
  203.     END EFill;
  204. (* sequence interpretation *)
  205.     PROCEDURE DelLast (T: Terminal);
  206.     BEGIN
  207.         IF T.cache.buf.len > 0 THEN Texts.Append(T.text, T.cache.buf) END;
  208.         IF T.text.len > 0 THEN Texts.Delete(T.text, T.text.len-1, T.text.len) END
  209.     END DelLast;
  210.     PROCEDURE Reset* (T: Terminal);
  211.         VAR i: INTEGER;
  212.     BEGIN
  213.         T.flags:={ansi, autowrap}; T.state:=0; T.strPos:=0; i:=1;
  214.         WHILE i <= MaxWidth DO T.tabs[i]:=i MOD 8=1; INC(i) END;
  215.         T.answerback:="*** Hello World ***";
  216.         T.oldAttr:=none; T.oldCursor.line:=1; T.oldCursor.col:=1; T.oldRelative:=FALSE;
  217.         Erase(T, 1, 1, Height, T.width);
  218.         SetMargins(T, 1, Height);
  219.         SetCursor(T, 1, 1, FALSE)
  220.     END Reset;
  221.     PROCEDURE SendStr (T: Terminal; s: ARRAY OF CHAR);
  222.         VAR i: INTEGER;
  223.     BEGIN i:=0;
  224.         WHILE s[i] # 0X DO T.send(T, s[i]); INC(i) END
  225.     END SendStr;
  226.     PROCEDURE SendInt (T: Terminal; x: INTEGER);
  227.         VAR i: INTEGER; d: ARRAY 3 OF CHAR;
  228.     BEGIN i:=0;
  229.         REPEAT d[i]:=CHR(x MOD 10+ORD("0")); x:=x DIV 10;  INC(i) UNTIL x=0;
  230.         WHILE i > 0 DO DEC(i); T.send(T, d[i]) END
  231.     END SendInt;
  232.     PROCEDURE Update (T: Terminal);
  233.     BEGIN IF T.strPos > 0 THEN WriteString(T, T.strBuf, T.strPos); T.strPos:=0 END
  234.     END Update;
  235.     PROCEDURE Flush* (T: Terminal);
  236.     BEGIN Update(T);
  237.         IF (T.text # NIL) & (T.cache.buf.len > 0) THEN Texts.Append(T.text, T.cache.buf) END
  238.     END Flush;
  239.     PROCEDURE Write (T: Terminal; ch: CHAR);
  240.     BEGIN
  241.         IF T.strPos >= LEN(T.strBuf) THEN Update(T) END;
  242.         T.strBuf[T.strPos]:=ch; INC(T.strPos)
  243.     END Write;
  244.     PROCEDURE ESCSequence (T: Terminal; last: CHAR);
  245.         VAR ch: CHAR;
  246.     BEGIN ch:=T.parBuf[0];
  247.         IF ansi IN T.flags THEN
  248.             IF ("7" <= last) & (last <= "c") THEN
  249.                 CASE last OF
  250.                     | "7": T.oldAttr:=T.attr; T.oldCursor:=T.cursor; T.oldRelative:=relative IN T.flags
  251.                     | "8":
  252.                         IF ch="#" THEN EFill(T)
  253.                         ELSE SetAttribute(T, T.oldAttr);
  254.                             SetCursor(T, T.oldCursor.line, T.oldCursor.col, FALSE);
  255.                             IF T.oldRelative THEN INCL(T.flags, relative)
  256.                             ELSE EXCL(T.flags, relative)
  257.                             END
  258.                         END
  259.                     | "=": INCL(T.flags, applic)
  260.                     | ">": EXCL(T.flags, applic)
  261.                     | "D":
  262.                         IF T.cursor.line=T.bottom THEN Scroll(T, TRUE)
  263.                         ELSE SetCursor(T, T.cursor.line+1, T.cursor.col, FALSE)
  264.                         END
  265.                     | "E":
  266.                         IF T.cursor.line=T.bottom THEN Scroll(T, TRUE); SetCursor(T, T.cursor.line, 1, FALSE)
  267.                         ELSE SetCursor(T, T.cursor.line+1, 1, FALSE)
  268.                         END
  269.                     | "H": T.tabs[T.cursor.col]:=TRUE
  270.                     | "M":
  271.                         IF T.cursor.line=T.top THEN Scroll(T, FALSE)
  272.                         ELSE SetCursor(T, T.cursor.line-1, T.cursor.col, relative IN T.flags)
  273.                         END
  274.                     | "Z": T.send(T, ESC); SendStr(T, "[?1;2c") (* VT100 *)
  275.                     | "c": Reset(T)
  276.                     | "9" .. "<", "?" .. "C", "F", "G", "I" .. "L", "N" .. "Y", "[" .. "b": (* ignore *)
  277.                 END
  278.             END
  279.         ELSE (* VT52 mode *)
  280.             IF ("<" <= last) & (last <= "Z") THEN
  281.                 CASE last OF
  282.                     | "<": INCL(T.flags, ansi)
  283.                     | "=": INCL(T.flags, applic)
  284.                     | ">": EXCL(T.flags, applic)
  285.                     | "A": SetCursor(T, T.cursor.line-1, T.cursor.col, FALSE)
  286.                     | "B": SetCursor(T, T.cursor.line+1, T.cursor.col, FALSE)
  287.                     | "C": SetCursor(T, T.cursor.line, T.cursor.col+1, FALSE)
  288.                     | "D": SetCursor(T, T.cursor.line, T.cursor.col-1, FALSE)
  289.                     | "H": SetCursor(T, 1, 1, FALSE)
  290.                     | "I":
  291.                         IF T.cursor.line=1 THEN Scroll(T, FALSE)
  292.                         ELSE SetCursor(T, T.cursor.line-1, T.cursor.col, FALSE)
  293.                         END
  294.                     | "J": Erase(T, T.cursor.line, T.cursor.col, Height, T.width)
  295.                     | "K": Erase(T, T.cursor.line, T.cursor.col, T.cursor.line, T.width)
  296.                     | "Z": T.send(T, ESC); SendStr(T, "/Z")
  297.                     | "?", "@", "E" .. "G", "L" .. "Y", "[", "\": (* ignore *)
  298.                 END
  299.             END
  300.         END
  301.     END ESCSequence;
  302.     PROCEDURE CSISequence (T: Terminal; last: CHAR);
  303.         VAR ch: CHAR; pos, p1, p2: INTEGER;
  304.         PROCEDURE Next;
  305.         BEGIN ch:=T.parBuf[pos]; INC(pos)
  306.         END Next;
  307.         PROCEDURE Par (zeroVal: INTEGER): INTEGER;
  308.             VAR x: INTEGER;
  309.         BEGIN x:=0;
  310.             IF ("0" <= ch) & (ch <= "9") THEN x:=ORD(ch)-ORD("0"); Next;
  311.                 WHILE ("0" <= ch) & (ch <= "9") & (x <= (MAX(INTEGER)-9) DIV 10) DO
  312.                     x:=10*x+ORD(ch)-ORD("0"); Next
  313.                 END;
  314.                 WHILE ("0" <= ch) & (ch <= "9") DO Next END;
  315.                 IF ch=";" THEN Next END
  316.             ELSIF ch=";" THEN Next
  317.             END;
  318.             IF x=0 THEN x:=zeroVal END;
  319.             RETURN x
  320.         END Par;
  321.     BEGIN T.parBuf[T.parPos]:=0FFX; pos:=0; Next;
  322.         IF ( "@" <= last) & (last <= "y") THEN
  323.             CASE last OF
  324.                 | "@": InsertChars(T, Par(1))
  325.                 | "A": SetCursor(T, T.cursor.line-Par(1), T.cursor.col, relative IN T.flags)
  326.                 | "B": SetCursor(T, T.cursor.line+Par(1), T.cursor.col, relative IN T.flags)
  327.                 | "C": SetCursor(T, T.cursor.line, T.cursor.col+Par(1), relative IN T.flags)
  328.                 | "D": SetCursor(T, T.cursor.line, T.cursor.col-Par(1), relative IN T.flags)
  329.                 | "H", "f":
  330.                     p1:=Par(1); p2:=Par(1);
  331.                     IF relative IN T.flags THEN SetCursor(T, T.top+p1-1, p2, TRUE)
  332.                     ELSE  SetCursor(T, p1, p2, FALSE)
  333.                     END
  334.                 | "J":
  335.                     p1:=Par(0);
  336.                     IF p1=0 THEN Erase(T, T.cursor.line, T.cursor.col, Height, T.width)
  337.                     ELSIF p1=1 THEN Erase(T, 1, 1, T.cursor.line, T.cursor.col)
  338.                     ELSIF p1=2 THEN Erase(T, 1, 1, Height, T.width)
  339.                     END
  340.                 | "K":
  341.                     p1:=Par(0);
  342.                     IF p1=0 THEN Erase(T, T.cursor.line, T.cursor.col, T.cursor.line, T.width)
  343.                     ELSIF p1=1 THEN Erase(T, T.cursor.line, 1, T.cursor.line, T.cursor.col)
  344.                     ELSIF p1=2 THEN Erase(T, T.cursor.line, 1, T.cursor.line, T.width)
  345.                     END
  346.                 | "L": InsertLines(T, Par(1))
  347.                 | "M": DeleteLines(T, Par(1))
  348.                 | "P": DeleteChars(T, Par(1))
  349.                 | "c": IF Par(0)=0 THEN T.send(T, ESC); SendStr(T, "[?1;2c") (* VT100 *) END
  350.                 | "g":
  351.                     REPEAT p1:=Par(0);
  352.                         IF p1=0 THEN T.tabs[T.cursor.col]:=FALSE
  353.                         ELSIF p1=3 THEN p1:=1;
  354.                             WHILE p1 <= MaxWidth DO T.tabs[p1]:=FALSE; INC(p1) END
  355.                         END
  356.                     UNTIL pos > T.parPos
  357.                 | "h":
  358.                     IF ch="?" THEN Next;
  359.                         REPEAT p1:=Par(0);
  360.                             IF p1=1 THEN INCL(T.flags, cursorKeys)
  361.                             ELSIF p1=3 THEN
  362.                                 Erase(T, 1, 1, Height, T.width);
  363.                                 SetWidth(T, MaxWidth);
  364.                                 SetMargins(T, 1, Height);
  365.                                 SetCursor(T, 1, 1, relative IN T.flags)
  366.                             ELSIF p1=6 THEN INCL(T.flags, relative); SetCursor(T, 1, 1, TRUE)
  367.                             ELSIF p1=7 THEN INCL(T.flags, autowrap)
  368.                             END
  369.                         UNTIL pos > T.parPos
  370.                     ELSE
  371.                         REPEAT p1:=Par(0);
  372.                             IF p1=4 THEN INCL(T.flags, insert)
  373.                             ELSIF p1=20 THEN INCL(T.flags, lineFeed)
  374.                             END
  375.                         UNTIL pos > T.parPos
  376.                     END
  377.                 | "l":
  378.                     IF ch="?" THEN Next;
  379.                         REPEAT p1:=Par(0);
  380.                             IF p1=1 THEN EXCL(T.flags, cursorKeys)
  381.                             ELSIF p1=2 THEN EXCL(T.flags, ansi)
  382.                             ELSIF p1=3 THEN
  383.                                 Erase(T, 1, 1, Height, T.width);
  384.                                 SetWidth(T, 80);
  385.                                 SetMargins(T, 1, Height);
  386.                                 SetCursor(T, 1, 1, relative IN T.flags)
  387.                             ELSIF p1=6 THEN EXCL(T.flags, relative); SetCursor(T, 1, 1, FALSE)
  388.                             ELSIF p1=7 THEN EXCL(T.flags, autowrap)
  389.                             END
  390.                         UNTIL pos > T.parPos
  391.                     ELSE
  392.                         REPEAT p1:=Par(0);
  393.                             IF p1=4 THEN EXCL(T.flags, insert)
  394.                             ELSIF p1=20 THEN EXCL(T.flags, lineFeed)
  395.                             END
  396.                         UNTIL pos > T.parPos
  397.                     END
  398.                 | "m":
  399.                     REPEAT p1:=Par(0);
  400.                         IF p1=0 THEN SetAttribute(T, none)
  401.                         ELSIF p1=1 THEN SetAttribute(T, bold)
  402.                         ELSIF p1=4 THEN SetAttribute(T, underline)
  403.                         ELSIF p1=5 THEN SetAttribute(T, blinking)
  404.                         ELSIF p1=7 THEN SetAttribute(T, reverse)
  405.                         END
  406.                     UNTIL pos > T.parPos
  407.                 | "n":
  408.                     IF ch="?" THEN Next;
  409.                         IF Par(0)=15 THEN (* printer status report *) T.send(T, ESC); SendStr(T, "[?13n") END
  410.                     ELSE p1:=Par(0);
  411.                         IF p1=5 THEN (* terminal status report *) T.send(T, ESC); SendStr(T, "[0n")
  412.                         ELSIF p1=6 THEN (* cursor position report *)
  413.                             T.send(T, ESC); T.send(T, "[");
  414.                             IF relative IN T.flags THEN SendInt(T, T.cursor.line-T.top+1)
  415.                             ELSE SendInt(T, T.cursor.line)
  416.                             END;
  417.                             T.send(T, ";"); SendInt(T, T.cursor.col); T.send(T, "R")
  418.                         END
  419.                     END
  420.                 | "r":
  421.                     p1:=Par(0); p2:=Par(0);
  422.                     IF (p1=0) & (p2=0) THEN p1:=1; p2:=Height END;
  423.                     SetMargins(T, p1, p2);
  424.                     SetCursor(T, 1, 1, TRUE)
  425.                 |"y":
  426.                     p1:=Par(0);
  427.                     IF p1=2 THEN p1:=Par(0);
  428.                         IF p1=1 THEN Reset(T)
  429.                         ELSE T.send(T, ESC); SendStr(T, "[0n")
  430.                         END
  431.                     END
  432.                 | "E" .. "G", "I", "N", "O", "Q" .. "b", "d", "e", "i" .. "k", "o" .. "q", "s" .. "x": (* ignore *)
  433.             END
  434.         END
  435.     END CSISequence;
  436.     PROCEDURE Receive* (T: Terminal; ch: CHAR);
  437.         VAR p: INTEGER;
  438.     BEGIN
  439.         ch:=CHR(ORD(ch) MOD 128);
  440.         IF ch < " " THEN (* interpret control characters immediately *)
  441.             CASE ch OF
  442.                 | ENQ: SendStr(T, T.answerback)
  443.                 | BS:
  444.                     Update(T); SetCursor(T, T.cursor.line, T.cursor.col-1, FALSE);
  445.                     IF T.text # NIL THEN DelLast(T) END
  446.                 | HT:
  447.                     Update(T); p:=T.cursor.col+1;
  448.                     WHILE (p <= T.width) & ~T.tabs[p] DO INC(p) END;
  449.                     SetCursor(T, T.cursor.line, p, FALSE);
  450.                     IF T.text # NIL THEN Texts.Write(T.cache, HT) END
  451.                 | LF, VT, FF:
  452.                     Update(T);
  453.                     IF T.cursor.line=T.bottom THEN Scroll(T, TRUE);
  454.                         IF lineFeed IN T.flags THEN SetCursor(T, T.cursor.line, 1, FALSE) END
  455.                     ELSIF lineFeed IN T.flags THEN SetCursor(T, T.cursor.line+1, 1, FALSE)
  456.                     ELSE SetCursor(T, T.cursor.line+1, T.cursor.col, FALSE)
  457.                     END;
  458.                     IF T.text # NIL THEN Texts.Write(T.cache, CR) END
  459.                 | CR: Update(T); SetCursor(T, T.cursor.line, 1, FALSE)
  460.                 | CAN, SUB: (* cancel *) T.state:=0
  461.                 | ESC: T.state:=1
  462.                 | 0X .. 4X, 6X, 7X, 0EX .. 17X, 19X, 1CX .. 1FX: (* ignore *)
  463.             END
  464.         ELSE (* drive state machine *)
  465.             CASE T.state OF
  466.                 | 0: (* normal characters *)
  467.                     IF ch=DEL THEN
  468.                         IF T.cursor.col > 1 THEN Update(T);
  469.                             SetCursor(T, T.cursor.line, T.cursor.col-1, FALSE);
  470.                             DeleteChars(T, 1)
  471.                         END;
  472.                         IF T.text # NIL THEN DelLast(T) END
  473.                     ELSE
  474.                         IF insert IN T.flags THEN Update(T); InsertChars(T, 1) END;
  475.                         Write(T, ch);
  476.                         IF T.text # NIL THEN Texts.Write(T.cache, ch) END
  477.                     END
  478.                 | 1: (* sequence introduction *)
  479.                     IF ch="[" THEN T.state:=3; T.parPos:=0
  480.                     ELSIF ~(ansi IN T.flags) & (ch="Y") THEN T.state:=4; T.parPos:=0
  481.                     ELSIF (" " <= ch) & (ch <= "/") THEN T.state:=2; T.parBuf[0]:=ch; T.parPos:=1
  482.                     ELSIF ("0" <= ch) & (ch <= "~") THEN T.state:=2; T.parPos:=0; Update(T); ESCSequence(T, ch); T.state:=0
  483.                     ELSE (* error *) T.state:=0
  484.                     END
  485.                 | 2: (* ESC sequence *)
  486.                     IF ("0" <= ch) & (ch <= "~") THEN Update(T); ESCSequence(T, ch); T.state:=0
  487.                     ELSIF T.parPos < LEN(T.parBuf) THEN T.parBuf[T.parPos]:=ch; INC(T.parPos)
  488.                     ELSE (* error *) T.state:=0
  489.                     END
  490.                 | 3: (* CSI sequence *)
  491.                     IF ("@" <= ch) & (ch <= "~") THEN Update(T); CSISequence(T, ch); T.state:=0
  492.                     ELSIF T.parPos < LEN(T.parBuf) THEN T.parBuf[T.parPos]:=ch; INC(T.parPos)
  493.                     ELSE (* error *) T.state:=0
  494.                     END
  495.                 | 4: (* VT52 ESC Y sequence *)
  496.                     IF T.parPos=0 THEN T.parBuf[0]:=ch; T.parPos:=1
  497.                     ELSE Update(T); SetCursor(T, ORD(T.parBuf[0])-31, ORD(ch)-31, FALSE); T.state:=0
  498.                     END
  499.             END
  500.         END
  501.     END Receive;
  502.     PROCEDURE Send* (T: Terminal; ch: CHAR);
  503.     BEGIN
  504.         IF T.text # NIL THEN T.pin:=T.text.len END;
  505.         IF ch <= DEL THEN (* normal ASCII *) T.send(T, ch);
  506.             IF (ch=CR) & (lineFeed IN T.flags) THEN T.send(T, LF) END
  507.         ELSIF (ch=BRK) OR (ch=0ADX) THEN T.break(T)
  508.         ELSIF (ch=0AEX) OR (ch=0AFX) THEN SendStr(T, T.answerback)
  509.         ELSIF (0C1X <= ch) & (ch <= 0C4X) THEN (* cursor keys *) T.send(T, ESC);
  510.             IF ansi IN T.flags THEN
  511.                 IF cursorKeys IN T.flags THEN T.send(T, "O") ELSE T.send(T, "[") END
  512.             END;
  513.             T.send(T, CHR(ORD(ch)-128))
  514.         ELSIF ch=80X THEN T.send(T, "A")
  515.         ELSIF ch=81X THEN T.send(T, "O")
  516.         ELSIF ch=82X THEN T.send(T, "U")
  517.         ELSIF ch=83X THEN T.send(T, "a")
  518.         ELSIF ch=84X THEN T.send(T, "o")
  519.         ELSIF ch=85X THEN T.send(T, "u")
  520.         ELSIF ch=9FX THEN T.send(T, " ")
  521.         END
  522.     END Send;
  523.     PROCEDURE SendString* (T: Terminal; VAR s: ARRAY OF CHAR);
  524.         VAR i: INTEGER;
  525.     BEGIN i:=0;
  526.         WHILE s[i] # 0X DO Send(T, s[i]); INC(i) END
  527.     END SendString;
  528.     PROCEDURE SendText* (T: Terminal; text: Texts.Text; beg, end: LONGINT);
  529.         VAR R: Texts.Reader; ch: CHAR;
  530.     BEGIN Texts.OpenReader(R, text, beg);
  531.         WHILE Texts.Pos(R) < end DO Texts.Read(R, ch); Send(T, ch) END
  532.     END SendText;
  533.     PROCEDURE Open* (T: Terminal; text: Texts.Text; send: Sender; break: Breaker; notify: Notifier);
  534.         VAR l: Line; i: INTEGER;
  535.     BEGIN
  536.         T.width:=80; T.top:=1; T.bottom:=Height; T.cursor.line:=1; T.cursor.col:=1;
  537.         T.attr:=none; T.wrapBefore:=FALSE; T.notify:=notify;
  538.         i:=Height;
  539.         REPEAT NEW(l); T.line[i]:=l ; DEC(i) UNTIL i=0;
  540.         Reset(T); T.text:=text; Texts.OpenWriter(T.cache); T.pin:=0;
  541.         T.send:=send; T.break:=break
  542.     END Open;
  543. END Terminals.
  544.