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.