--SrvrTn.mesa
--Server telnet with old style stream hooks
--Copies output to display, takes keyboard if anything is there
--LStewart September 6, 1978 7:12 PM

DIRECTORY
IODefs: FROM "IODefs",
ProcessDefs: FROM "ProcessDefs" USING [Detach,SetTimeout,MsecToTicks],
PupStream: FROM "PupStream",
SrvrTnDefs: FROM "SrvrTnDefs",
Stream: FROM "Stream",
StreamDefs: FROM "StreamDefs";

SrvrTn: MONITOR
IMPORTS IODefs, PupStream, Stream, ProcessDefs
EXPORTS SrvrTnDefs
SHARES StreamDefs =

BEGIN OPEN IODefs, Stream, StreamDefs;

listener: PupStream.PupListener ← NIL;
socNum: PupStream.PupSocketID ← [0,1]; --telnet socket
herald: STRING ← "Mesa Server Telnet";
myStream: Stream.Handle;
closed: BOOLEAN ← TRUE;
echoLF: BOOLEAN ← TRUE;
chars: BOOLEAN ← FALSE;
open,sender: CONDITION;
timeLastSent: INTEGER;
clock: POINTER TO INTEGER = LOOPHOLE[430B];
keyStream, displayStream: StreamHandle;

myStreamObject: StreamObject ←
[
reset: Reset,
get: Get,
putback: PutBack,
put: Put,
endof: Endof,
destroy: Destroy,
body: Other[]
];

Reset: PROCEDURE[os: StreamHandle] =
BEGIN
NULL;
END;

Get: PROCEDURE[os: StreamHandle] RETURNS [UNSPECIFIED] =
BEGIN
eatByte: BOOLEAN;
c: UNSPECIFIED;
BEGIN
ENABLE BEGIN
SSTChange =>
BEGIN
IF sst=5 THEN
--WARNING! This is dangerous!!
BEGIN
SetSST[myStream,6];
SendNow[myStream];
END;
IF sst IN [2..4] THEN eatByte ← TRUE;
RESUME;
END;
PupStream.StreamClosing =>
BEGIN
closed ← TRUE;
WaitForOpen[];
RETRY
;
END
;
Stream.TimeOut => RESUME;
END;
IF keyStream.endof[keyStream] THEN
BEGIN
WaitForOpen[];
eatByte ← TRUE;
WHILE eatByte DO
eatByte ← FALSE;
c ← GetByte[myStream];
ENDLOOP;
RETURN[c];
END
ELSE RETURN [keyStream.get[keyStream]];
END;
END;

PutBack: PROCEDURE[os: StreamHandle, thing: UNSPECIFIED] =
BEGIN
NULL;
END;

Put: PROCEDURE[os: StreamHandle, thing: UNSPECIFIED] =
BEGIN ENABLE PupStream.StreamClosing =>
BEGIN
closed ← TRUE;
WaitForOpen[];
RETRY
;
END
;
c: CHARACTER;
displayStream.put[displayStream,thing];
WaitForOpen[];
PutByte[myStream,thing];
chars ← TRUE;
c ← thing;
IF c=CR AND echoLF THEN PutByte[myStream,LOOPHOLE[LF]];
IF c=CR THEN MySendNow[];
END;

Endof
: PROCEDURE[os: StreamHandle] RETURNS [BOOLEAN] =
BEGIN
RETURN[closed];
END;

Destroy: ENTRY PROCEDURE[os: StreamHandle] =
BEGIN
myStream.delete[myStream];
END;

MySendNow: ENTRY PROCEDURE =
BEGIN
Sendit[];
END;

Sendit: INTERNAL PROCEDURE =
BEGIN
SendNow[myStream];
chars ← FALSE;
timeLastSent ← clock↑;
END;

SendOccasionally: ENTRY PROCEDURE =
BEGIN
WHILE NOT closed DO
WAIT sender;
IF chars AND (clock↑-timeLastSent)>50 THEN Sendit[];
ENDLOOP;
END;

WaitForOpen: ENTRY PROCEDURE =
BEGIN
WHILE closed DO WAIT open ENDLOOP;
END;

ServerStart: ENTRY PROCEDURE [bs: Handle, remote: PupStream.PupAddress] =
BEGIN
i: CARDINAL;
eatByte: BOOLEAN ← FALSE;
-- Mainline of server
IF closed THEN
BEGIN ENABLE PupStream.StreamClosing => GOTO Closeit;
FOR i IN [0..herald.length)
DO
PutByte[bs,LOOPHOLE[herald.text[i]]];
ENDLOOP;
PutByte[bs,LOOPHOLE[CR]];
IF echoLF THEN PutByte[bs,LOOPHOLE[LF]];
SendNow[bs];
myStream ← bs;
closed ← FALSE;
ProcessDefs.Detach[FORK SendOccasionally];
BROADCAST open;
END
;
EXITS
Closeit =>
BEGIN
bs.delete[bs];
closed ← TRUE;
END;
END;

--
Mainline code
WriteLine["Starting Server Telnet"];
listener ← PupStream.CreatePupByteStreamListener [socNum, ServerStart, PupStream.SecondsToTocks[3]];
ProcessDefs.SetTimeout[@open,ProcessDefs.MsecToTicks[1000]];
ProcessDefs.SetTimeout[@sender,ProcessDefs.MsecToTicks[1000]];
keyStream ← IODefs.GetInputStream[];
displayStream ← IODefs.GetOutputStream[];
IODefs.SetInputStream[@myStreamObject];
IODefs.SetOutputStream[@myStreamObject];
WaitForOpen[];
END.