<> <> <> <> <> <> <> <> DIRECTORY Ascii USING [ BS, ControlA, ControlR, ControlQ, ControlV, ControlW, ControlX, CR, DEL, ESC, FF, SP, TAB], BitBlt USING [AlignedBBTable, BBptr, BBTableSpace, BITBLT], Environment USING [BitAddress], Format USING [ Date, Decimal, LongDecimal, LongNumber, LongOctal, LongSubStringItem, Number, Octal, SubString], Inline USING [BITAND], Process USING [Detach, SetPriority], Runtime USING [GetTableBase], SpecialSpace USING [ MakeGlobalFrameResident, --MakeGlobalFrameSwappable,-- MakeProcedureResident], String USING [ AppendChar, AppendLongNumber, AppendNumber, StringToLongNumber, StringToNumber, SubString], TerminalMultiplex USING [InputController, RegisterInputController, SelectTerminal], Time USING [Packed], TTY USING [DateFormat, Handle, LongSubString, NumberFormat], UserTerminal USING [ Coordinate, cursor, GetBitBltTable, keyboard, mouse, screenHeight, screenWidth, SetMousePosition, SetState, SetBackground, WaitForScanLine]; MockingbirdTTY: MONITOR LOCKS m USING m: POINTER TO MONITORLOCK IMPORTS BitBlt, Format, Inline, Process, Runtime, SpecialSpace, String, TerminalMultiplex, UserTerminal EXPORTS TTY = BEGIN OPEN Ascii, BitBlt; screenLock: MONITORLOCK; keyboardLock: MONITORLOCK; shuttingDown: BOOLEAN _ FALSE; shutDown: CONDITION _ [timeout: 0]; startingUp: BOOLEAN _ FALSE; startUp: CONDITION _ [timeout: 0]; noTrack: CARDINAL _ 0; Initialize: PROCEDURE = { IF ~TerminalMultiplex.SelectTerminal[alternate] THEN ERROR; TerminalMultiplex.RegisterInputController[InputController]; InputController[enable]}; InputController: TerminalMultiplex.InputController = { SELECT action FROM enable => {IF useCount = 0 THEN TurnOnTerminal[]; StartTerminal[]}; disable => TurnOffTerminal[]; ENDCASE}; TurnOnTerminal: PROCEDURE = { TurnOnInternal: ENTRY PROC [m: POINTER TO MONITORLOCK] = { IF ~font.newStyle OR font.indexed OR font.min NOT IN [0C..177C] OR font.max+1 NOT IN [0C..177C] THEN ERROR; WHILE shuttingDown DO WAIT shutDown ENDLOOP; SpecialSpace.MakeProcedureResident[ProcessKeyboard]; SpecialSpace.MakeGlobalFrameResident[MockingbirdTTY]; CDT _ FALSE; echo _ TRUE; in _ out _ 0; [] _ UserTerminal.SetState[on]; [] _ UserTerminal.SetBackground[white]; ClearScreen[]; -- to force init of bbPtr (since may have changed) Process.Detach[FORK ProcessKeyboard]}; TurnOnInternal[@keyboardLock]}; StartTerminal: PROCEDURE = { StartInternal: ENTRY PROC [m: POINTER TO MONITORLOCK] = { startingUp _ TRUE; BROADCAST startUp}; StartInternal[@keyboardLock]}; TurnOffTerminal: PROCEDURE = { TurnOffInternal: ENTRY PROC [m: POINTER TO MONITORLOCK] = { shuttingDown _ TRUE; WHILE shuttingDown DO WAIT shutDown ENDLOOP}; <> <> <<[] _ UserTerminal.SetState[disconnected]};>> TurnOffInternal[@keyboardLock]}; EnableCursorTracking: PUBLIC PROCEDURE = { EnableInternal: ENTRY PROC [m: POINTER TO MONITORLOCK] = { IF (noTrack _ noTrack - 1) = 0 THEN doBlink _ TRUE}; EnableInternal[@keyboardLock]}; DisableCursorTracking: PUBLIC PROCEDURE = { DisableInternal: ENTRY PROC [m: POINTER TO MONITORLOCK] = { IF (noTrack _ noTrack + 1) ~= 0 THEN doBlink _ FALSE}; DisableInternal[@keyboardLock]}; <<~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~>> <> <<~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~>> font: LONG POINTER TO MACHINE DEPENDENT RECORD [ newStyle(0:0..0): BOOLEAN, indexed(0:1..1): BOOLEAN, fixed(0:2..2): BOOLEAN, kerned(0:3..3): BOOLEAN, pad(0:4..15): [0..7777B], min(1): CHARACTER, -- limits of chars in font max(2): CHARACTER, -- limits of chars in font maxwidth(3): CARDINAL, length(4): CARDINAL, ascent(5): CARDINAL, descent(6): CARDINAL, xoffset(7): CARDINAL, raster(8): CARDINAL, chars(9:0..63): SELECT OVERLAID * FROM hasBoundingBox => [ boundingBox(9:0..63): RECORD [FontBBox, FontBBoy, FontBBdx, FontBBDy: INTEGER], BBBitmap(13): ARRAY [0..0) OF WORD], noBoundingBox => [bitmap(9): ARRAY [0..0) OF WORD], ENDCASE] = GetFont[]; bitmap: LONG POINTER = IF font.kerned THEN @font.BBBitmap ELSE @font.bitmap; xInSegment: LONG POINTER TO ARRAY CHARACTER [0C..0C) OF CARDINAL = bitmap + font.raster*FontHeight[] - (font.min-0C); height: INTEGER[0..LAST[INTEGER]] = FontHeight[]; CharWidth: PROC [char: CHARACTER] RETURNS [[0..LAST[INTEGER]]] = INLINE BEGIN IF char NOT IN [font.min..font.max] THEN char _ font.max+1; RETURN[xInSegment[char+1] - xInSegment[char]] END; FontHeight: PROC RETURNS [[0..LAST[INTEGER]]] = INLINE {RETURN[font.ascent+font.descent]}; <<~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~>> <> <<~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~>> downUp: TYPE = {down, up}; keycount: CARDINAL = 80; -- must be 0 mod 16 Keyarray: TYPE = MACHINE DEPENDENT RECORD [SELECT OVERLAID * FROM b => [bits: PACKED ARRAY [0..keycount) OF downUp], wds => [wds: ARRAY [0..keycount/16) OF WORD], ENDCASE]; KeyItem: TYPE = RECORD [ Letter: BOOLEAN, ShiftCode: CHARACTER [0C..177C], NormalCode: CHARACTER [0C..377C]]; <> ctrl: CARDINAL = 52; leftShift: CARDINAL = 57; shiftLock: CARDINAL = 72; rightShift: CARDINAL = 76; spare3: CARDINAL = 77; KeyTable: ARRAY [16..keycount) OF KeyItem = [ <> <> [FALSE, 45C, 65C], -- %,5 [FALSE, 44C, 64C], -- $,4 [FALSE, 176C, 66C], -- ~,6 [TRUE, 105C, 145C], -- E [FALSE, 46C, 67C], -- &,7 [TRUE, 104C, 144C], -- D [TRUE, 125C, 165C], -- U [TRUE, 126C, 166C], -- V [FALSE, 51C, 60C], -- ),0 [TRUE, 113C, 153C], -- K [FALSE, 30C, 55C], -- `,- [TRUE, 120C, 160C], -- P [FALSE, 77C, 57C], -- ?,/ [FALSE, 174C, 134C], -- |,\ [FALSE, 12C, 12C], -- LF [FALSE, 10C, 10C], -- BS <> [FALSE, 43C, 63C], -- #,3 [FALSE, 100C, 62C], -- @,2 [TRUE, 127C, 167C], -- W [TRUE, 121C, 161C], -- Q [TRUE, 123C, 163C], -- S [TRUE, 101C, 141C], -- A [FALSE, 50C, 71C], -- (,9 [TRUE, 111C, 151C], -- I [TRUE, 130C, 170C], -- X [TRUE, 117C, 157C], -- O [TRUE, 114C, 154C], -- L [FALSE, 74C, 54C], -- <,, [FALSE, 42C, 47C], -- ",' [FALSE, 175C, 135C], --},] [FALSE, 0C, 0C], -- SPARE2 [FALSE, 0C, 0C], -- SPARE1 <> [FALSE, 41C, 61C], -- !,1 [FALSE, 33C, 33C], -- ESCAPE [FALSE, 11C, 11C], -- TAB [TRUE, 106C, 146C], -- F [FALSE, 0C, 0C], -- CONTROL [TRUE, 103C, 143C], -- C [TRUE, 112C, 152C], -- J [TRUE, 102C, 142C], -- B [TRUE, 132C, 172C], -- Z [FALSE, 0C, 0C], -- SHIFT [FALSE, 76C, 56C], -- >,. [FALSE, 72C, 73C], -- :,; [FALSE, 15C, 15C], -- CR [FALSE, 136C, 137C], -- ^,_ [FALSE, 177C, 177C], -- DEL [FALSE, 0C, 0C], -- NOT USED (FL3) <> [TRUE, 122C, 162C], -- R [TRUE, 124C, 164C], -- T [TRUE, 107C, 147C], -- G [TRUE, 131C, 171C], -- Y [TRUE, 110C, 150C], -- H [FALSE, 52C, 70C], -- *,8 [TRUE, 116C, 156C], -- N [TRUE, 115C, 155C], -- M [FALSE, 0C, 0C], -- LOCK [FALSE, 40C, 40C], -- SPACE [FALSE, 173C, 133C], -- {,[ [FALSE, 53C, 75C], -- +,= [FALSE, 0C, 0C], -- Shift [FALSE, 0C, 0C], -- Spare3 [FALSE, 0C, 0C], -- not user (FR4) [FALSE, 0C, 0C]]; -- not user (FR5) <<~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~>> <> <<~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~>> useCount: CARDINAL _ 0; Create: PUBLIC PROC [STRING] RETURNS [TTY.Handle] = BEGIN useCount _ useCount + 1; RETURN[LOOPHOLE[100000B]] END; Destroy: PUBLIC PROC [TTY.Handle] = BEGIN useCount _ useCount - 1; END; <<~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~>> <> <<~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~>> CDT: BOOLEAN; charactersAvailable: CONDITION; echo: BOOLEAN; <> CtrlChar: PROC [c: CHARACTER] RETURNS [CHARACTER] = INLINE {RETURN[LOOPHOLE[Inline.BITAND[c, 37B]]]}; <> ProcessKeyboard: PROC = BEGIN old, new: Keyarray; kp: LONG POINTER TO Keyarray = LOOPHOLE[UserTerminal.keyboard]; blinkCount: CARDINAL _ 33; GoAway: ENTRY PROC [m: POINTER TO MONITORLOCK] RETURNS [BOOLEAN] = INLINE { IF shuttingDown THEN {shuttingDown _ FALSE; BROADCAST shutDown; RETURN[TRUE]} ELSE RETURN[FALSE]}; WaitStart: ENTRY PROC [m: POINTER TO MONITORLOCK] = INLINE { WHILE ~startingUp DO WAIT startUp; ENDLOOP}; Process.SetPriority[6]; new _ kp^; WHILE TRUE DO Brdcst: ENTRY PROC [m: POINTER TO MONITORLOCK] = INLINE {BROADCAST charactersAvailable}; charsSeen: BOOLEAN _ FALSE; IF GoAway[@keyboardLock] THEN WaitStart[@keyboardLock]; startingUp _ FALSE; old _ new; UserTerminal.WaitForScanLine[0]; new _ kp^; TrackCursor[@keyboardLock]; IF (blinkCount_blinkCount-1) = 0 THEN {BlinkCursor[]; blinkCount _ 34}; FOR i: CARDINAL IN [1..keycount/16) DO IF old.wds[i]#new.wds[i] THEN FOR j: CARDINAL IN [i*16..(i+1)*16) DO char: CHARACTER; entry: KeyItem; IF new.bits[j]=up OR old.bits[j]=down THEN LOOP; IF (char _ (entry_KeyTable[j]).NormalCode)#0C THEN BEGIN SELECT TRUE FROM new.bits[ctrl]=down => IF char=177C THEN {CDT _ TRUE; LOOP} ELSE char _ CtrlChar[char]; new.bits[leftShift]=down, new.bits[rightShift]=down => char _ entry.ShiftCode; new.bits[shiftLock]=down AND entry.Letter => char _ entry.ShiftCode; ENDCASE; StuffBuffer[char, @keyboardLock]; charsSeen _ TRUE END; ENDLOOP ENDLOOP; IF charsSeen THEN Brdcst[@keyboardLock]; ENDLOOP END; SetEcho: PUBLIC PROC [h: TTY.Handle, new: BOOLEAN] RETURNS [old: BOOLEAN] = BEGIN SetEntry: ENTRY PROC [m: POINTER TO MONITORLOCK] = INLINE {old _ echo; echo _ new}; SetEntry[@keyboardLock] END; TrackCursor: ENTRY PROC [m: POINTER TO MONITORLOCK] = INLINE BEGIN mouse: UserTerminal.Coordinate _ UserTerminal.mouse^; IF noTrack > 0 THEN RETURN; mouse.x _ MIN[MAX[0, mouse.x], UserTerminal.screenWidth]; mouse.y _ MIN[MAX[0, mouse.y], UserTerminal.screenHeight]; UserTerminal.cursor^ _ mouse; UserTerminal.SetMousePosition[mouse] END; <<~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~>> <> <<~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~>> in, out: CARDINAL; buffer: PACKED ARRAY [0..50) OF CHARACTER; CharsAvailable: PUBLIC PROC [h: TTY.Handle] RETURNS [CARDINAL] = BEGIN P: ENTRY PROC [m: POINTER TO MONITORLOCK] RETURNS [CARDINAL] = INLINE {RETURN[IF in>=out THEN in-out ELSE in+LENGTH[buffer]-out]}; RETURN[P[@keyboardLock]] END; GetChar: PUBLIC PROC [h: TTY.Handle] RETURNS [c: CHARACTER] = BEGIN P: ENTRY PROC [m: POINTER TO MONITORLOCK] = INLINE BEGIN WHILE in=out DO WAIT charactersAvailable ENDLOOP; c _ buffer[out]; IF (out_out+1) = LENGTH[buffer] THEN out _ 0; END; P[@keyboardLock] END; PutBackChar: PUBLIC PROC [h: TTY.Handle, c: CHARACTER] = BEGIN P: ENTRY PROC [m: POINTER TO MONITORLOCK] = INLINE BEGIN newout: CARDINAL = IF out=0 THEN LENGTH[buffer]-1 ELSE out-1; IF newout#in THEN {buffer[out _ newout] _ c; BROADCAST charactersAvailable}; END; P[@keyboardLock] END; ResetUserAbort: PUBLIC PROC = BEGIN CDResetEntry: ENTRY PROC [m: POINTER TO MONITORLOCK] = INLINE {CDT _ FALSE}; CDResetEntry[@keyboardLock] END; StuffBuffer: ENTRY PROC [c: CHARACTER, m: POINTER TO MONITORLOCK] = INLINE BEGIN newin: CARDINAL; IF (newin_in+1) = LENGTH[buffer] THEN newin _ 0; IF newin#out THEN {buffer[in] _ c; in _ newin}; END; <> UserAbort: PUBLIC PROC RETURNS [BOOLEAN] = {RETURN[CDT]}; <<~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~>> <> <<~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~>> BBTable: BBTableSpace; bbPtr: BBptr = AlignedBBTable[@BBTable]; charPos, line, nCharPos, nLines: CARDINAL; firstLine, thisLine: Environment.BitAddress; bitsPerTextLine: CARDINAL; -- = screenWidth*font.height doBlink: BOOLEAN _ FALSE; <> GetBitAddress: PROC [p: LONG POINTER, o: CARDINAL] RETURNS [Environment.BitAddress] = {RETURN[[p+o/16, 0, o MOD 16]]}; PutBlanks: PUBLIC PROC [h: TTY.Handle, n: CARDINAL] = {THROUGH [0..n) DO PutChar[h, ' ] ENDLOOP}; <> BlinkCursor: PUBLIC PROC = BEGIN BlinkCursorEntry: ENTRY PROC [m: POINTER TO MONITORLOCK] = BEGIN blinker: CARDINAL _ 60000B; IF doBlink THEN BEGIN bbPtr.src _ [@blinker, 0, 0]; bbPtr.srcDesc _ [gray[[0, 0, 0, 0]]]; bbPtr.flags _ [gray: TRUE, dstFunc: xor]; BITBLT[bbPtr]; bbPtr.srcDesc _ [srcBpl[font.raster*16]]; bbPtr.flags _ []; END; END; IF doBlink THEN BlinkCursorEntry[@screenLock]; END; NewLine: PUBLIC PROC [TTY.Handle] RETURNS [BOOLEAN] = {RETURN[charPos=0]}; PutChar: PUBLIC PROC [h: TTY.Handle, c: CHARACTER] = BEGIN PutCharEntry: ENTRY PROC [m: POINTER TO MONITORLOCK] = INLINE {doBlink _ FALSE; ClearThisChar[]; DisplayChar[c]; doBlink _ TRUE}; <> END; PutLongString: PUBLIC PROC [h: TTY.Handle, s: LONG STRING] = {IF s#NIL THEN FOR i: CARDINAL IN [0..s.length) DO PutChar[h, s[i]] ENDLOOP}; PutString: PUBLIC PROC [h: TTY.Handle, s: STRING] = {PutLongString[h, s]}; <> Backup: INTERNAL PROC = BEGIN t: CARDINAL = bbPtr.dst.bit+16-font.maxwidth; IF charPos=0 THEN RETURN; charPos _ charPos - 1; bbPtr.dst.word _ bbPtr.dst.word + t/16 - 1; bbPtr.dst.bit _ t MOD 16; END; ClearScreen: INTERNAL PROC = BEGIN zero: CARDINAL _ 0; bbPtr^ _ UserTerminal.GetBitBltTable[]; bitsPerTextLine _ bbPtr.dstBpl*height; firstLine _ thisLine _ GetBitAddress[ bbPtr.dst.word, bbPtr.dst.bit+8+8*bbPtr.dstBpl]; charPos _ 0; line _ 1; nCharPos _ (bbPtr.width-16)/font.maxwidth; nLines _ (bbPtr.height-16)/height; bbPtr.src _ [@zero, 0, 0]; bbPtr.srcDesc _ [gray[[0, 0, 0, 0]]]; bbPtr.flags _ [gray: TRUE]; BITBLT[bbPtr]; <> bbPtr.dst _ firstLine; <> <> bbPtr.srcDesc _ [srcBpl[font.raster*16]]; bbPtr.height _ height; bbPtr.width _ font.maxwidth; bbPtr.flags _ []; END; ClearThisChar: INTERNAL PROC = BEGIN zero: CARDINAL _ 0; bbPtr.src _ [@zero, 0, 0]; bbPtr.srcDesc _ [gray[[0, 0, 0, 0]]]; bbPtr.flags _ [gray: TRUE]; BITBLT[bbPtr]; bbPtr.srcDesc _ [srcBpl[font.raster*16]]; bbPtr.flags _ []; END; DisplayChar: INTERNAL PROC [c: CHARACTER] = BEGIN SELECT c FROM IN (SP..'~] => BEGIN IF c NOT IN [font.min..font.max] THEN c _ font.max+1; bbPtr.src _ GetBitAddress[bitmap, xInSegment[c]]; BitBlt.BITBLT[bbPtr]; END; SP => NULL; CR => {Newline[]; RETURN}; BS => {Backup[]; ClearThisChar[]; RETURN}; TAB => {UNTIL (charPos MOD 8)=0 DO DisplayChar[SP] ENDLOOP; RETURN}; FF => {ClearScreen[]; RETURN}; IN [0C..SP) => {DisplayChar['^]; DisplayChar[c+('A-1C)]; RETURN}; ENDCASE => RETURN; IF (charPos_charPos+1)>=nCharPos THEN Newline[] ELSE bbPtr.dst _ GetBitAddress[bbPtr.dst.word, bbPtr.dst.bit+font.maxwidth]; END; Newline: INTERNAL PROC = BEGIN IF line> <> <<~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~>> BackingStream: PUBLIC PROC [TTY.Handle] RETURNS [never: UNSPECIFIED] = {ERROR}; GetDecimal: PUBLIC PROC [h: TTY.Handle] RETURNS [INTEGER] = BEGIN s: STRING _ [10]; [] _ GetEditedString[h, s, IsAtom, TRUE]; RETURN[String.StringToNumber[s, 10]] END; GetID: PUBLIC PROC [h: TTY.Handle, s: STRING] = {[] _ GetEditedString[h, s, IsAtom, TRUE]}; GetLine: PUBLIC PROC [h: TTY.Handle, s: STRING] = {[] _ GetEditedString[h, s, IsCR, TRUE]; PutChar[h, CR]}; GetLongDecimal: PUBLIC PROC [h: TTY.Handle] RETURNS [n: LONG INTEGER] = BEGIN s: STRING _ [32]; [] _ GetEditedString[h, s, IsAtom, TRUE]; RETURN[String.StringToLongNumber[s, 10]] END; GetLongNumber: PUBLIC PROC [ h: TTY.Handle, default: LONG UNSPECIFIED, radix: CARDINAL] RETURNS [n: LONG UNSPECIFIED] = BEGIN s: STRING _ [32]; IF radix = 10 AND LOOPHOLE[default, LONG INTEGER] < 0 THEN {s[0] _ '-; s.length _ 1; default _ -default}; String.AppendLongNumber[s, default, radix]; IF radix = 8 THEN String.AppendChar[s, 'B]; [] _ GetEditedString[h, s, IsAtom, TRUE]; RETURN[String.StringToLongNumber[s, radix]]; END; GetLongOctal: PUBLIC PROC [h: TTY.Handle] RETURNS [n: LONG UNSPECIFIED] = BEGIN s: STRING _ [32]; [] _ GetEditedString[h, s, IsAtom, TRUE]; RETURN[String.StringToLongNumber[s, 8]] END; GetNumber: PUBLIC PROC [h: TTY.Handle, default: UNSPECIFIED, radix: CARDINAL] RETURNS [n: UNSPECIFIED] = BEGIN s: STRING _ [10]; IF radix = 10 AND LOOPHOLE[default, INTEGER] < 0 THEN {default _ -default; s[0] _ '-; s.length _ 1}; String.AppendNumber[s, default, radix]; IF radix = 8 THEN String.AppendChar[s, 'B]; [] _ GetEditedString[h, s, IsAtom, TRUE]; RETURN[String.StringToNumber[s, radix]]; END; GetOctal: PUBLIC PROC [h: TTY.Handle] RETURNS [n: UNSPECIFIED] = BEGIN s: STRING _ [10]; [] _ GetEditedString[h, s, IsAtom, TRUE]; RETURN[String.StringToNumber[s, 8]] END; GetString: PUBLIC PROC [ h: TTY.Handle, s: STRING, t: PROC [c: CHARACTER] RETURNS [yes: BOOLEAN]] = {PutChar[h, GetEditedString[h, s, t, TRUE]]}; LineOverflow: PUBLIC SIGNAL [s: STRING] RETURNS [ns: STRING] = CODE; Rubout: PUBLIC SIGNAL = CODE; GetEditedString: PUBLIC PROC [ h: TTY.Handle, s: STRING, t: PROC [c: CHARACTER] RETURNS [yes: BOOLEAN], newstring: BOOLEAN] RETURNS [c: CHARACTER] = BEGIN c _ GetChar[h]; IF newstring THEN IF c = ESC THEN {PutString[h, s]; c _ GetChar[h]} ELSE s.length _ 0; UNTIL t[c] DO SELECT c FROM DEL => SIGNAL Rubout; ControlA, BS => -- backspace IF s.length > 0 THEN {IF echo THEN PutChar[h, BS]; s.length _ s.length - 1}; ControlW, ControlQ => -- backword BEGIN <, the and are to>> <> state: {ti, v, li} _ ti; FOR i: CARDINAL DECREASING IN [0..s.length) DO SELECT s[i] FROM IN ['A..'Z], IN ['a..'z], IN ['0..'9] => IF state = ti THEN state _ v; ENDCASE => IF state = v THEN state _ li; IF state = li THEN GO TO Done; IF echo THEN PutChar[h, BS]; REPEAT Done => s.length _ i + 1; FINISHED => s.length _ 0; ENDLOOP; END; ControlX => -- back everything BEGIN IF echo THEN FOR i: CARDINAL IN [0..s.length) DO PutChar[h, BS] ENDLOOP; s.length _ 0; END; ControlR => -- refresh-- IF echo THEN {PutChar[h, CR]; PutString[h, s]}; ControlV => -- dont parse next char BEGIN WHILE s.length >= s.maxlength DO s _ SIGNAL LineOverflow[s] ENDLOOP; s[s.length] _ c _ GetChar[h]; s.length _ s.length + 1; IF echo THEN PutChar[h, c] END; ENDCASE => BEGIN WHILE s.length >= s.maxlength DO s _ SIGNAL LineOverflow[s] ENDLOOP; s[s.length] _ c; s.length _ s.length + 1; IF echo THEN PutChar[h, c]; END; c _ GetChar[h]; ENDLOOP; END; IsAtom: PROC [c: CHARACTER] RETURNS [BOOLEAN] = {RETURN[c = SP OR c = CR]}; IsCR: PROC [c: CHARACTER] RETURNS [BOOLEAN] = {RETURN[c = CR]}; <> OutString: PROC [s: STRING] = {PutLongString[LOOPHOLE[0], s]}; PutDate: PUBLIC PROC [h: TTY.Handle, gmt: Time.Packed, format: TTY.DateFormat] = {Format.Date[gmt, format, OutString]}; PutDecimal: PUBLIC PROC [h: TTY.Handle, n: INTEGER] = {Format.Decimal[n, OutString]}; PutLongDecimal: PUBLIC PROC [h: TTY.Handle, n: LONG INTEGER] = {Format.LongDecimal[n, OutString]}; PutLongNumber: PUBLIC PROC [h: TTY.Handle, n: LONG UNSPECIFIED, format: TTY.NumberFormat] = {Format.LongNumber[n, format, OutString]}; PutLongOctal: PUBLIC PROC [h: TTY.Handle, n: LONG UNSPECIFIED] = {Format.LongOctal[n, OutString]}; PutNumber: PUBLIC PROC [h: TTY.Handle, n: UNSPECIFIED, format: TTY.NumberFormat] = {Format.Number[n, format, OutString]}; PutOctal: PUBLIC PROC [h: TTY.Handle, n: UNSPECIFIED] = {Format.Octal[n, OutString]}; PutLongSubString: PUBLIC PROC [h: TTY.Handle, ss: TTY.LongSubString] = {Format.LongSubStringItem[ss, OutString]}; PutSubString: PUBLIC PROC [h: TTY.Handle, ss: String.SubString] = {Format.SubString[ss, OutString]}; Initialize[]; END.