<> DIRECTORY Ascii USING [ControlA, ControlH, ControlQ, ControlR, ControlV, ControlW, ControlX, BS, CR, DEL, ESC, SP], IO USING [Close, SetEcho, GetChar, EraseChar, PutChar, CharsAvail, Backup, STREAM], TTY USING [Handle], TTYIO USING [], Stream USING [Handle], String USING [SubString, StringToNumber, AppendNumber, AppendChar, StringToLongNumber, AppendLongNumber], Format USING [LongSubString, LongSubStringDescriptor, NumberFormat, DecimalFormat, DateFormat, StringProc, Number, LongNumber, Octal, LongOctal, Date], Rope USING [ROPE], Time USING [Packed], ConvertUnsafe USING [ToRope], <> ViewerIO USING [CreateViewerStreams] ; TTYImpl: MONITOR IMPORTS IO, ConvertUnsafe, String, Format, ViewerIO EXPORTS TTY, TTYIO = BEGIN <> Handle: PUBLIC TYPE = CARDINAL; -- the exported type LongSubStringDescriptor: TYPE = Format.LongSubStringDescriptor; LongSubString: TYPE = Format.LongSubString; TTYHandle: TYPE = REF TTYRecord; TTYRecord: TYPE = RECORD [handle: Handle, in, out: IO.STREAM, beginLine: BOOLEAN _ TRUE]; <> LineOverflow: PUBLIC SIGNAL [s: STRING] RETURNS [ns: STRING] = CODE; Rubout: PUBLIC SIGNAL = CODE; InvalidTTYHandle: PUBLIC ERROR = CODE; <> TTYHandles: LIST OF TTYHandle _ NIL; <> currentHandle: CARDINAL _ 0; <> CreateTTYHandleFromStreams: PUBLIC ENTRY PROC [in, out: IO.STREAM] RETURNS[TTY.Handle] = <> CHECKED {currentHandle _ currentHandle + 1; TTYHandles _ CONS[NEW[TTYRecord _ [handle: currentHandle, in: in, out: out]], TTYHandles]; RETURN[currentHandle] }; GetStreamsFromTTYHandle: PUBLIC ENTRY PROC [handle: TTY.Handle] RETURNS[in: IO.STREAM, out: IO.STREAM] = <> CHECKED {x: TTYHandle _ Lookup[handle]; RETURN[x.in, x.out]; }; Create: PUBLIC PROC [name: STRING] RETURNS [h: TTY.Handle] = {in, out: IO.STREAM; [in, out] _ ViewerIO.CreateViewerStreams[name: ConvertUnsafe.ToRope[name], editedStream: FALSE]; RETURN[CreateTTYHandleFromStreams[in, out]]; }; Destroy: PUBLIC PROC [h: TTY.Handle] = {ENABLE InvalidTTYHandle => CONTINUE; x: TTYHandle _ Lookup[h]; IF x # NIL THEN {IO.Close[x.in]; IO.Close[x.out]; x.handle _ 0; }; }; Lookup: ENTRY SAFE PROC [h: TTY.Handle] RETURNS [TTYHandle] = CHECKED INLINE {FOR l: LIST OF TTYHandle _ TTYHandles, l.rest UNTIL l = NIL DO IF l.first.handle = h THEN RETURN[l.first]; REPEAT FINISHED => ERROR InvalidTTYHandle; ENDLOOP }; <> SetEcho: PUBLIC PROC [h: TTY.Handle, new: BOOLEAN] RETURNS [old: BOOLEAN] = {x: TTYHandle _ Lookup[h]; oldH: IO.STREAM _ x.in.SetEcho[IF new THEN x.out ELSE NIL]; RETURN[oldH = x.out]; }; UserAbort: PUBLIC PROCEDURE RETURNS [yes: BOOLEAN] = { <> <> RETURN[FALSE]; }; ResetUserAbort: PUBLIC PROCEDURE = { <> <> }; <> GetChar: PUBLIC PROC [h: TTY.Handle] RETURNS [c: CHARACTER] = {x: TTYHandle _ Lookup[h]; oldH: IO.STREAM _ x.in.SetEcho[NIL]; -- the semantics of TTY.GetChar are never to echo. BEGIN ENABLE UNWIND => [] _ x.in.SetEcho[oldH]; c _ x.in.GetChar[]; [] _ x.in.SetEcho[oldH]; END; }; PutChar: PUBLIC PROC [h: TTY.Handle, c: CHARACTER] = {x: TTYHandle _ Lookup[h]; IF c = Ascii.BS THEN x.out.EraseChar['A] -- compatibility kluge. Note that this does not work correctly if the underlying stream really needs to know what character is being erased, e.g. so as to know its width, or bit pattern, but for most applications, i.e. viewersstreams, this is not the case, and the EraseChar ignores the character. ELSE x.out.PutChar[c]; x.beginLine _ (c = Ascii.CR); }; CharsAvailable: PUBLIC PROC [h: TTY.Handle] RETURNS [number: CARDINAL] = {x: TTYHandle _ Lookup[h]; IF x.in.CharsAvail[] THEN RETURN[1] ELSE RETURN[0]; }; NewLine: PUBLIC PROC [h: TTY.Handle] RETURNS [yes: BOOLEAN] = {x: TTYHandle _ Lookup[h]; RETURN[x.beginLine] }; EraseChar: PROC [h: TTY.Handle, c: CHARACTER] = {x: TTYHandle _ Lookup[h]; x.out.EraseChar[c]; }; BackupChar: PUBLIC PROC [h: TTY.Handle, c: CHARACTER] = {x: TTYHandle _ Lookup[h]; x.in.Backup[c]; }; PutBlanks: PUBLIC PROC [h: TTY.Handle, n: CARDINAL _ 1] = {FOR i: CARDINAL IN [0..n) DO PutChar[h, Ascii.SP]; ENDLOOP; -- go through PutChar to make beginLine check. }; PutString: PUBLIC PROC [h: TTY.Handle, s: STRING] = {FOR i: CARDINAL IN [0..s.length) DO PutChar[h, s[i]]; ENDLOOP; }; PutLongString: PUBLIC PROC [h: TTY.Handle, s: LONG STRING] = {FOR i: CARDINAL IN [0..s.length) DO PutChar[h, s[i]]; ENDLOOP; }; PutSubString: PUBLIC PROC [h: TTY.Handle, ss: String.SubString] = {start: CARDINAL _ ss.offset; end: CARDINAL _ start + ss.length; FOR i: CARDINAL IN [start..end) DO PutChar[h, ss.base[i]]; ENDLOOP; }; PutLongSubString: PUBLIC PROC [h: TTY.Handle, ss: LongSubString] = {start: CARDINAL _ ss.offset; end: CARDINAL _ start + ss.length; FOR i: CARDINAL IN [start..end) DO PutChar[h, ss.base[i]]; ENDLOOP; }; GetEditedString: PUBLIC PROC [ h: TTY.Handle, s: STRING, t: PUBLIC PROC [c: CHARACTER] RETURNS [yes: BOOLEAN], newstring: BOOLEAN] RETURNS[c: CHARACTER] = <> BEGIN OPEN Ascii; InternalGetChar: PROC [h: TTY.Handle] RETURNS [CHARACTER] = INLINE {RETURN[GetChar[h]]; }; sw: TTY.Handle = h; -- was Window.Handle i: CARDINAL; state: {ti, v, li}; echo: BOOLEAN = SetEcho[sw, FALSE]; [] _ SetEcho[sw, echo]; c _ InternalGetChar[sw]; IF newstring THEN IF c = ESC THEN BEGIN PutString[sw, s]; c _ InternalGetChar[sw]; END ELSE s.length _ 0; UNTIL t[c] DO SELECT c FROM DEL => SIGNAL Rubout; ControlA, ControlH => BEGIN IF s.length > 0 THEN BEGIN IF echo THEN PutChar[sw, c]; s.length _ s.length-1; END; END; ControlW, ControlQ => BEGIN -- text to be backed up is of the form <<...
  • ; the and are to be removed.>> state _ ti; FOR i 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 EraseChar[sw, s[i]]; REPEAT Done => s.length _ i+1; FINISHED => s.length _ 0; ENDLOOP; END; ControlR => IF echo THEN BEGIN PutChar[sw, CR]; PutString[sw, s]; END; ControlX => BEGIN IF echo THEN FOR i IN [0..s.length) DO EraseChar[sw, s[i]] ENDLOOP; s.length _ 0; END; ControlV => BEGIN WHILE s.length >= s.maxlength DO s _ SIGNAL LineOverflow[s]; ENDLOOP; s[s.length] _ c _ InternalGetChar[sw]; s.length _ s.length+1; IF echo THEN PutChar[sw, 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[sw, c]; END; c _ InternalGetChar[sw]; ENDLOOP; RETURN[c]; END; -- of GetEditedString IsCR: PROCEDURE [c: CHARACTER] RETURNS [BOOLEAN] = { RETURN[c=Ascii.CR] }; IsAtom: PROCEDURE [c: CHARACTER] RETURNS [BOOLEAN] = { RETURN[IF c = Ascii.SP OR c = Ascii.CR THEN TRUE ELSE FALSE] }; GetID: PUBLIC PROCEDURE [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, Ascii.CR]; }; GetString: PUBLIC PROC [h: TTY.Handle, s: STRING, t: PUBLIC PROC [c: CHARACTER] RETURNS [yes: BOOLEAN]] = {PutChar[h, GetEditedString[h, s, t, TRUE]]; }; <> GetDecimal: PUBLIC PROCEDURE [h: TTY.Handle] RETURNS [n: INTEGER] = BEGIN s: STRING _ [10]; [] _ GetEditedString[h, s, IsAtom, TRUE]; RETURN[String.StringToNumber[s, 10]] END; GetNumber: PUBLIC PROCEDURE [ h: TTY.Handle, default: UNSPECIFIED, radix: CARDINAL] RETURNS [n: UNSPECIFIED] = BEGIN OPEN String; s: STRING _ [10]; IF radix = 10 AND LOOPHOLE[default, INTEGER] < 0 THEN BEGIN default _ -default; s[0] _ '-; s.length _ 1 END; AppendNumber[s,default,radix]; IF radix = 8 THEN AppendChar[s,'B]; [] _ GetEditedString[h, s, IsAtom, TRUE]; RETURN[StringToNumber[s, radix]]; END; GetOctal: PUBLIC PROCEDURE [h: TTY.Handle] RETURNS [n: UNSPECIFIED] = BEGIN s: STRING _ [10]; [] _ GetEditedString[h, s, IsAtom, TRUE]; RETURN[String.StringToNumber[s, 8]] END; GetLongDecimal: PUBLIC PROCEDURE [h: TTY.Handle] RETURNS [n: LONG INTEGER] = BEGIN s: STRING _ [32]; [] _ GetEditedString[h, s, IsAtom, TRUE]; RETURN[String.StringToLongNumber[s, 10]] END; GetLongNumber: PUBLIC PROCEDURE [ h: TTY.Handle, default: LONG UNSPECIFIED, radix: CARDINAL] RETURNS [n: LONG UNSPECIFIED] = BEGIN OPEN String; s: STRING _ [32]; temp: LONG INTEGER _ LOOPHOLE[default]; IF radix = 10 AND temp < 0 THEN BEGIN temp _ -temp; s[0] _ '-; s.length _ 1; default _ LOOPHOLE[temp, LONG UNSPECIFIED]; END; AppendLongNumber[s,default,radix]; IF radix = 8 THEN AppendChar[s,'B]; [] _ GetEditedString[h, s, IsAtom, TRUE]; RETURN[StringToLongNumber[s, radix]]; END; GetLongOctal: PUBLIC PROCEDURE [h: TTY.Handle] RETURNS [n: LONG UNSPECIFIED]= BEGIN s: STRING _ [32]; [] _ GetEditedString[h, s, IsAtom, TRUE]; RETURN[String.StringToLongNumber[s, 8]] END; PutNumber: PUBLIC PROCEDURE [h: TTY.Handle, n: UNSPECIFIED, format: Format.NumberFormat] = BEGIN Send: Format.StringProc = BEGIN PutString[h, s]; END; Format.Number[n, format, Send]; END; PutLongNumber: PUBLIC PROCEDURE [ h: TTY.Handle, n: LONG UNSPECIFIED, format: Format.NumberFormat] = BEGIN Send: Format.StringProc = BEGIN PutString[h, s]; END; Format.LongNumber[n, format, Send]; END; PutOctal: PUBLIC PROCEDURE [h: TTY.Handle, n: UNSPECIFIED] = BEGIN Send: Format.StringProc = BEGIN PutString[h, s]; END; Format.Octal[n, Send]; END; PutLongOctal: PUBLIC PROCEDURE [h: TTY.Handle, n: LONG UNSPECIFIED] = BEGIN Send: Format.StringProc = BEGIN PutString[h, s]; END; Format.LongOctal[n, Send]; END; PutDecimal: PUBLIC PROCEDURE [h: TTY.Handle, n: INTEGER] = {PutNumber[h, n, Format.DecimalFormat]}; PutLongDecimal: PUBLIC PROCEDURE [h: TTY.Handle, n: LONG INTEGER] = {PutLongNumber[h, n, Format.DecimalFormat]}; PutDate: PUBLIC PROC [h: TTY.Handle, gmt: Time.Packed, format: Format.DateFormat _ noSeconds] = {Send: Format.StringProc = BEGIN PutString[h, s]; END; Format.Date[pt: gmt, format: format, proc: Send]}; <> BackingStream: PUBLIC PROC [h: TTY.Handle] RETURNS [stream: Stream.Handle] = {ERROR}; SetBackingSize: PUBLIC PROC [h: TTY.Handle, size: LONG CARDINAL] = {ERROR}; <> END. Change Log 22-Mar-82 9:05:50 Fixed PutChar to check for character = BS and if so, to do an eraseChar instead Made lookup and create entry procedures. November 5, 1982 4:25 pm added UserAbort, ResetUserAbort.