-- File: PupHacks.mesa, Last Edit:
-- HGM January 23, 1981 1:06 AM
-- Taft April 18, 1983 1:19 PM
-- This version edited for (ugh) the old Mesa6 PupPackage
-- From OthelloPup.mesa of August 29, 1980 9:21 AM

DIRECTORY
-- Things from the system
IODefs,
Process USING [Detach, Yield],
Runtime USING [GetBcdTime],
String USING [AppendLongDecimal],
Time USING [Append, Unpack],
-- Pup things...
DriverDefs USING [Network],
PupDefs USING [
AppendPupAddress, DataWordsPerPupBuffer, GetFreePupBuffer,
GetPupAddress, GetPupContentsBytes, MoveStringBodyToPupBuffer,
ReturnFreePupBuffer,
PupBuffer, PupNameTrouble, PupPackageDestroy, PupPackageMake,
PupRouterBroadcastThis, PupSocket, PupSocketDestroy, PupSocketMake,
SecondsToTocks, SetPupContentsBytes],
PupRouterDefs USING [

Get
PupRoutingTableEntry, PupRoutingTableEntry,
RoutingCacheEntry, routingCacheHead],
PupTypes USING [
echoSoc, fillInPupAddress, fillInSocketID, maxDataWordsPerGatewayPup,
miscSrvSoc, PupAddress];

PupHacks: PROGRAM
IMPORTS IODefs, Process, Runtime, String, Time, PupDefs, PupRouterDefs =
BEGIN OPEN IODefs;

name: STRING ← [40]; -- Global so ESC will work

AddressToName: PROCEDURE =
BEGIN OPEN PupTypes, PupDefs;
soc: PupSocket;
b: PupBuffer;
a: PupAddress ← [, , [0, 0]];
hit: BOOLEAN ← FALSE;
WriteString["Address to Name for: "L];
ReadID[name ! Rubout => { WriteLine[" XXX"L]; GOTO NoName; }];
IF name.length = 0 THEN BEGIN WriteLine["Address needed."L]; RETURN; END;
GetPupAddress[@a, name !
PupNameTrouble => BEGIN WriteLine[e]; GOTO NoName; END];
soc ← PupSocketMake[fillInSocketID, fillInPupAddress, SecondsToTocks[2]];
THROUGH [0..10) UNTIL hit DO
b ← GetFreePupBuffer[];
b.pupType ← addressLookup;
b.pupID ← [0, 0];
b.dest.socket ← PupTypes.miscSrvSoc;
b.source ← soc.getLocalAddress[];
b.address ← a;
SetPupContentsBytes[b, 2*SIZE[PupAddress]];
PupRouterBroadcastThis[b];
UNTIL hit OR (b ← soc.get[]) = NIL DO
SELECT b.pupType FROM
addressIs =>
BEGIN
hit ← TRUE;
WriteString[" => "L];
PrintBodyAsText[b];
END;
nameError =>
BEGIN
hit ← TRUE;
WriteString[" => ERROR: "L];
PrintBodyAsText[b];
END;
ENDCASE => PrintErrorPup[b];
WriteLine["."L];
ReturnFreePupBuffer[b];
ENDLOOP;
IF ~hit THEN WriteLine["No Response that try."L];
ENDLOOP;
PupSocketDestroy[soc];
EXITS NoName => NULL;
END;

EchoUser: PROCEDURE =
BEGIN OPEN PupDefs, PupTypes;
bytesPerBuffer: CARDINAL;
funny, late, recv, sent, wrong: LONG CARDINAL ← 0;
me, where: PupAddress ← [,,echoSoc];
mySoc: PupSocket;
packetNumber: CARDINAL ← 0;
pleaseStop: BOOLEAN ← FALSE;
routing: PupRouterDefs.PupRoutingTableEntry;
Watch: PROCEDURE = { [] ← ReadChar[]; pleaseStop ← TRUE };

WriteString["Echo to: "L];
ReadID[name ! Rubout => { WriteLine[" XXX"L]; GOTO NoName; }];
IF name.length = 0 THEN BEGIN WriteLine["Name needed"L]; RETURN; END;
GetPupAddress[@where, name
! PupNameTrouble =>
{ WriteString[" ***** "L]; WriteLine[e]; GOTO NoName }];
mySoc ← PupSocketMake[fillInSocketID, where, SecondsToTocks[2]];
me ← mySoc.getLocalAddress[];
WriteString[". ["L];
PrintPupAddress[me];
WriteString["] => ["L];
routing ← PupRouterDefs.
GetPupRoutingTableEntry[where.net];
IF routing.hop#0 THEN
BEGIN -- UGH
WriteNumber[routing.network.netNumber,[8,FALSE,TRUE,0]];
WriteChar[’#];
WriteNumber[routing.route,[8,FALSE,TRUE,0]];
WriteChar[’#];
WriteString["] => ["L];
END;
PrintPupAddress[where];
WriteLine["]..."L];
Process.Detach[FORK Watch[]];
bytesPerBuffer ←
2*MIN[DataWordsPerPupBuffer[], maxDataWordsPerGatewayPup];
UNTIL pleaseStop DO
FOR len: CARDINAL IN [0..bytesPerBuffer] UNTIL pleaseStop DO
b: PupBuffer ← GetFreePupBuffer[];
b.pupID.a ← b.pupID.b ← (packetNumber←packetNumber+1);
b.pupType ← echoMe;
SetPupContentsBytes[b,len];
FOR i: CARDINAL IN [0..len) DO b.pupBytes[i] ← i; ENDLOOP;
mySoc.put[b];
sent ← sent+1;
Process.Yield[]; -- be sure we don’t hog machine
UNTIL (b←mySoc.get[])=NIL DO
SELECT TRUE FROM
(b.pupType#iAmEcho) =>
{ funny ← funny+1; PrintErrorPup[b] };
((b.pupID.a#packetNumber)
OR (b.pupID.b#packetNumber)
OR (len#GetPupContentsBytes[b])) =>
{ WriteChar[’#]; late ← late+1 };
ENDCASE =>
BEGIN
FOR i: CARDINAL IN [0..len) DO
IF b.pupBytes[i]#(i MOD 400B) THEN
{ wrong ← wrong+1; WriteChar[’~]; GOTO Wrong };
ENDLOOP;
WriteChar[’!];
recv ← recv+1;
EXIT;
END;
ReturnFreePupBuffer[b];
REPEAT Wrong => NULL;
ENDLOOP;
IF b#NIL THEN ReturnFreePupBuffer[b] ELSE WriteChar[’?];
ENDLOOP;
WriteLine[""L];
ENDLOOP;
PupSocketDestroy[mySoc];
WriteString["Out: "L];
WriteLongNumber[sent];
WriteString[", In: "L];
WriteLongNumber[recv];
WriteString[" ("L];
WriteLongNumber[(recv*100)/sent];
WriteLine["%)"L];
IF late#0 THEN
BEGIN
WriteString["Late: "L];
WriteLongNumber[late];
WriteString[" ("L];
WriteLongNumber[(late*100)/sent];
WriteLine["%)"L];
END;
IF funny#0 THEN { WriteLongNumber[funny]; WriteLine[" funny"L] };
IF wrong#0 THEN { WriteLongNumber[wrong]; WriteLine[" wrong data"L] };
EXITS NoName => NULL;
END;

NameToAddress: PROCEDURE =
BEGIN OPEN PupTypes, PupDefs;
soc: PupSocket;
b: PupBuffer;
hit: BOOLEAN ← FALSE;
WriteString["Name to Address for: "L];
ReadID[name ! Rubout => { WriteLine[" XXX"L]; GOTO NoName; }];
IF name.length = 0 THEN BEGIN WriteLine["Name needed"L]; RETURN; END;
soc ← PupSocketMake[fillInSocketID, fillInPupAddress, SecondsToTocks[2]];
THROUGH [0..10) UNTIL hit DO
b ← GetFreePupBuffer[];
b.pupType ← nameLookup;
b.pupID ← [0, 0];
b.dest.socket ← PupTypes.miscSrvSoc;
b.source ← soc.getLocalAddress[];
MoveStringBodyToPupBuffer[b, name];
PupRouterBroadcastThis[b];
UNTIL hit OR (b ← soc.get[]) = NIL DO
SELECT b.pupType FROM
nameIs =>
BEGIN
n: CARDINAL ← GetPupContentsBytes[b]/(2*SIZE[PupAddress]);
addresses: POINTER TO ARRAY [0..0) OF PupAddress ←
LOOPHOLE[@b.pupBody];
hit ← TRUE;
WriteString[" => "L];
FOR i: CARDINAL IN [0..n) DO
IF i # 0 THEN WriteString[", "L];
PrintPupAddress[addresses[i]];
ENDLOOP;
END;
nameError =>
BEGIN
hit ← TRUE;
WriteString[" => ERROR: "L];
PrintBodyAsText[b];
END;
ENDCASE => PrintErrorPup[b];
WriteLine[""L];
ReturnFreePupBuffer[b];
ENDLOOP;
IF ~hit THEN WriteLine["No Response that try."L];
ENDLOOP;
PupSocketDestroy[soc];
EXITS NoName => NULL;
END;

RoutingTable: PROCEDURE =
BEGIN
k: CARDINAL;

WriteLine["Local PupRouting Table:"L];
WriteLine["| Net Via Hops | Net Via Hops | Net Via Hops |"L];
WriteLine["|-------------------|-------------------|-------------------|"L];
k ← 0;
FOR r: POINTER TO PupRouterDefs.RoutingCacheEntry ←
PupRouterDefs.routingCacheHead, r.next UNTIL r=NIL DO
network: DriverDefs.Network = r.entry.network;
IF network=NIL THEN LOOP;
IF k=0 THEN WriteChar[’|];
O4[r.net]; O4[network.netNumber];
WriteChar[’#];
O3Z[IF r.entry.hop#0 THEN r.entry.route ELSE network.hostNumber];
WriteChar[’#];
O4[r.entry.hop];
WriteString[" |"L];
IF (k←k+1)=3 THEN { WriteLine[""L]; k←0 };
ENDLOOP;
IF k#0 THEN WriteLine[""L];
END;

PrintErrorPup: PROCEDURE [b: PupDefs.PupBuffer] =
BEGIN
source: PupTypes.PupAddress ← b.source;
WriteLine[""L];
IF b.pupType=error THEN
BEGIN
len: CARDINAL = PupDefs.GetPupContentsBytes[b];
WriteString["[Error Pup, code="L];
WriteOctal[b.errorCode];
WriteString[", from: "L];
PrintPupAddress[source];
WriteString["] "L];
FOR i: CARDINAL IN [0..len-2*(10+1+1)) DO
WriteChar[b.errorText[i]]; ENDLOOP;
END
ELSE
BEGIN
WriteString[" ***** "L];
WriteString["Funny PupType = "L];
WriteOctal[b.pupType];
WriteString[" ***** "L];
END;
WriteLine[""L];
END;

PrintPupAddress: PROCEDURE [a: PupTypes.PupAddress] =
BEGIN
buffer: STRING ← [50];
PupDefs.AppendPupAddress[buffer, a];
WriteString[buffer];
END;

PrintBodyAsText: PROCEDURE [b: PupDefs.PupBuffer] =
BEGIN
FOR i: CARDINAL IN [0..PupDefs.GetPupContentsBytes[b]) DO
WriteChar[b.pupChars[i]]; ENDLOOP;
END;

CommandLoop: PROCEDURE =
BEGIN
PupDefs.PupPackageMake[];
PrintHeaderLine[];
DO
c: CHARACTER;
WriteChar[’>];
c ← ReadChar[];
SELECT c FROM
’A, ’a => AddressToName[];
’E, ’e => EchoUser[];
’N, ’n => NameToAddress[];
’R, ’r => RoutingTable[];
’Q, ’q => BEGIN WriteLine["Quit."L]; EXIT; END;
’H, ’h, ’? => WriteLine ["
Commands are: A(ddress to Name), E(cho), N(ame to Address), or R(outing).
H(elp) or ? prints this message. Q(uit) gets you out of here."L];
ENDCASE => WriteLine ["? XXX"L];
ENDLOOP;
PupDefs.PupPackageDestroy[];
END;

PrintHeaderLine: PROCEDURE =
BEGIN
version: STRING = [20];
me: PupTypes.PupAddress ← [,,[0,0]];
WriteString[" PupHacks of "L];
Time.Append[version, Time.Unpack[Runtime.GetBcdTime[]]];
WriteString[version];
WriteString[" running on "L];
PupDefs.GetPupAddress[@me, "ME"L];
PrintPupAddress[me];
WriteLine["."];
END;

WriteLongNumber: PROCEDURE [n: LONG CARDINAL] =
BEGIN
temp: STRING = [20];
String.AppendLongDecimal[temp,n];
WriteString[temp];
END;

-- prints 4 chars, octal, no trailing B
O4: PROCEDURE [n: CARDINAL] =
BEGIN
WriteNumber[n,[8,FALSE,TRUE,4]];
END;

-- prints 3 chars, octal, leading zeros
O3Z: PROCEDURE [n: CARDINAL] =
BEGIN
WriteNumber[n,[8,TRUE,TRUE,3]];
END;

-- initialization
CommandLoop[];
END.