edited by Teitelman ileMarch 29, 1983 5:04 pm tt
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],
UserExec USING [ExecHandle, GetExecHandle, UserAbort, ResetUserAbort],
ViewerIO USING [CreateViewerStreams]
;
TTYImpl: MONITOR
IMPORTS IO, ConvertUnsafe, String, Format, ViewerIO
EXPORTS TTY, TTYIO
= BEGIN
Types
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: BOOLEANTRUE];
Signals
LineOverflow: PUBLIC SIGNAL [s: STRING] RETURNS [ns: STRING] = CODE;
Rubout: PUBLIC SIGNAL = CODE;
InvalidTTYHandle: PUBLIC ERROR = CODE;
Variables
TTYHandles: LIST OF TTYHandle ← NIL;
maintains correspondence between TTYHandle and IO
currentHandle: CARDINAL ← 0;
Starting and stopping
CreateTTYHandleFromStreams: PUBLIC ENTRY PROC [in, out: IO.STREAM] RETURNS[TTY.Handle] =
transducer from IO to TTY.
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] =
transducer from TTY to IO.
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
};
Utilities
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] = {
execHandle: UserExec.ExecHandle = UserExec.GetExecHandle[];
IF UserExec.UserAbort[execHandle] THEN {UserExec.ResetUserAbort[execHandle]; RETURN[TRUE]} ELSE
RETURN[FALSE]; 
};
ResetUserAbort: PUBLIC PROCEDURE = {
execHandle: UserExec.ExecHandle = UserExec.GetExecHandle[];
UserExec.ResetUserAbort[execHandle];
};
Read & write procedures
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] =
mose of code simply copied from TTYSWsb
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
...<li><v><ti>; the <v> and <ti> 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]];
};
Numerical i/o
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 INTEGERLOOPHOLE[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]};
not implemented
BackingStream: PUBLIC PROC [h: TTY.Handle] RETURNS [stream: Stream.Handle] = {ERROR};
SetBackingSize: PUBLIC PROC [h: TTY.Handle, size: LONG CARDINAL] = {ERROR};
UserAbort and ResetUserAbort default to the definitions exported by original TTY when running in Tajo, and are exported by viewersiostreamimpl for viewers.
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.