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]
;
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: BOOLEAN ← TRUE];
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
};
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 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]};
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.
November 5, 1982 4:25 pm added UserAbort, ResetUserAbort.