-- 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 [
    GetPupRoutingTableEntry, 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.