-- File: GCEcho.mesa, HGM August 16, 1978 3:32 AM
-- Last Edit: Ly December 16, 1978 2:59 PM

DIRECTORY
GCDefs: FROM "GCDefs" USING [
GCInterface, PrintErrorPup, PrintPupAddress, WaitUntilKeyHit],
IODefs: FROM "IODefs" USING [
CR, ReadID, Rubout, WriteChar, WriteLine, WriteString],
ProcessDefs: FROM "<Mesa>ProcessDefs" USING [Detach, Yield, Abort, Aborted],
PupDefs: FROM "PupDefs" USING [
AppendPupAddress, DataWordsPerBuffer, GetFreePupBuffer,
GetPupAddress, GetPupContentsBytes, PupAddress,
PupBuffer, PupNameTrouble, PupSocket,
PupSocketDestroy, PupSocketMake, ReturnFreePupBuffer,
SecondsToTocks, SetPupContentsBytes, veryLongWait],
PupTypes: FROM "PupTypes" USING [
fillInPupAddress, fillInSocketID, echoSoc];

GCEcho: PROGRAM [
gc: GCDefs.GCInterface]
IMPORTS GCDefs, IODefs, ProcessDefs, PupDefs
EXPORTS GCDefs =
BEGIN OPEN
gc, IODefs, PupDefs, PupTypes, GCDefs;

where: PupAddress ← [myNet,myHost,echoSoc];

GCEchoer: PUBLIC PROCEDURE =
BEGIN
SetEchoHost[];
stopFlag ← FALSE;
ProcessDefs.Detach[FORK GCEchoit[]];
WaitUntilKeyHit[];
WriteChar[CR];
END;

SetEchoHost: PROCEDURE =
BEGIN
name: STRING ← [50];
DO
AppendPupAddress[name,@where];
WriteString["Echo to: "L];
ReadID[name ! Rubout => BEGIN WriteLine["xxx"L]; GOTO Quit; END];
GetPupAddress[@where,name !
PupNameTrouble => BEGIN WriteLine[e]; GOTO Quit; END];
EXIT;
REPEAT
Quit => NULL;
ENDLOOP;
IF where.net=0 THEN where.net←myNet;
WriteString["="L];
PrintPupAddress[@where];
WriteLine["."L];
END;

GCEchoit: PROCEDURE =
BEGIN
l: CARDINAL;
b: PupBuffer;
r: CARDINAL ← 0;
cycle: CARDINAL;
packetNumber: CARDINAL ← 0;
mySoc: PupSocket ← PupSocketMake[fillInSocketID,where,SecondsToTocks[2]];
l ← 1; -- Cyclic length
UNTIL stopFlag DO
cycle ← 1;
UNTIL stopFlag DO
b ← GetFreePupBuffer[];
l ← cycle;
b.pupID.a←b.pupID.b ← (packetNumber←packetNumber+1);
b.pupType ← echoMe;
SetPupContentsBytes[b,l];
mySoc.put[b];
UNTIL (b←mySoc
.get[])=NIL DO
SELECT TRUE FROM
(b.pupType=error) =>
BEGIN
PrintErrorPup[b];
END;
((b.pupType#iAmEcho)
OR (b.pupID.a#packetNumber)
OR (b.pupID.b#packetNumber)
OR (l#GetPupContentsBytes[b])) => WriteChar[’#];
ENDCASE =>
BEGIN
WriteChar[’!];
EXIT;
END;
ReturnFree
PupBuffer[b];
ENDLOOP;
IF b#NIL THEN ReturnFree
PupBuffer[b]
ELSE
BEGIN
WriteChar[’?];
LOOP; -- Try same packet again, thats why the funny control structure
END;
cycle ← cycle+1;
IF cycle>DataWordsPerBuffer[]*2 THEN EXIT;
ENDLOOP;
WriteChar[CR];
ENDLOOP;
PupSocketDestroy[mySoc];
END;

-- Echo Server

echoFork: PROCESS;
echoSocket: PupSocket ← NIL;
echoStop, echoServing: BOOLEAN ← FALSE;


ToggleEchoServer: PUBLIC PROCEDURE =
BEGIN
WriteString["Echo Server "];
IF ~echoServing THEN
BEGIN
echoStop ← FALSE;
echoSocket ← PupSocketMake[
echoSoc,
fillInPupAddress,
veryLongWait];
echoFork ← FORK GCEchoServer[];
ProcessDefs.Yield[]; -- let it get started to avoid Abort BUG
echoServing ← TRUE;
END
ELSE
BEGIN
echoStop ← TRUE;
ProcessDefs.Abort[echoFork];
JOIN echoFork[];
PupSocketDestroy[echoSocket];
echoSocket ← NIL;
echoServing ← FALSE;
END;
WriteLine[IF echoServing THEN "On" ELSE "Off"];
END;

GCEchoServer
: PROCEDURE =
BEGIN
b: PupBuffer;
UNTIL echoStop DO
IF (b←echoSocket.get[ ! ProcessDefs.Aborted => EXIT])#NIL THEN
BEGIN
IF b.pupType#echoMe THEN
BEGIN
WriteChar[’↑];
ReturnFreePupBuffer[b];
LOOP;
END;
WriteChar[’$];
echoSocket.setRemoteAddress[b.source];
b.pupType ← iAmEcho;
echoSocket.put[b];
END;
ENDLOOP;
END;

-- initialization
END.