-- Copyright (C) 1981, 1984, 1985  by Xerox Corporation. All rights reserved. 
-- OthelloPup.mesa, HGM, 10-Mar-85  5:51:08

-- File: OthelloPup.mesa,  Last Edit: HGM  May 22, 1979  10:35 PM
-- File: OthelloPup.mesa,  Last Edit: Fay/BLyon August 21, 1980  3:42 PM
-- File: OthelloPup.mesa,  Last Edit: BLyon August 29, 1980  9:21 AM
-- File: OthelloPup.mesa,  Last Edit: ?? 25-Nov-81 13:22:47
-- File: OthelloPup.mesa,  Last Edit: Fay 17-Dec-81 14:22:18

DIRECTORY
  Process USING [Detach, Pause, Yield],
  Buffer USING [GetBuffer, ReturnBuffer],
  Driver USING [Network],
  OthelloDefs,
  PupDefs USING [
    AppendPupAddress, DataWordsPerPupBuffer, GetPupAddress,
    GetPupContentsBytes, PupBuffer, PupNameTrouble,
    PupPackageDestroy, PupPackageMake, PupSocket, PupSocketDestroy,
    PupSocketMake, SecondsToTocks, SetPupContentsBytes, veryShortWait],
  PupRouterDefs USING [GetRoutingTable, RoutingTableEntry, RoutingTableObject],
  PupPktOps USING [pupBuffers],
  PupTypes USING [echoSoc, fillInSocketID, maxDataWordsPerGatewayPup, PupAddress];

OthelloPup: PROGRAM
  IMPORTS Process,Buffer,  PupDefs, PupRouterDefs, PupPktOps, OthelloDefs =
  BEGIN OPEN OthelloDefs;

  PrintErrorPup:  PROCEDURE [b: PupDefs.PupBuffer] =
    BEGIN
    source: PupTypes.PupAddress ← b.pup.source;
    NewLine[];
    IF b.pup.pupType = error THEN
      BEGIN
      len: CARDINAL = PupDefs.GetPupContentsBytes[b];
      WriteString["[Error Pup, code="L];
      WriteOctal[LOOPHOLE[b.pup.errorCode]];
      WriteString[", from: "L];
      PrintPupAddress[@source];
      WriteString["] "L];
      FOR i: CARDINAL IN [0..len - 2*(10 + 1 + 1)) DO
        WriteChar[b.pup.errorText[i]] ENDLOOP;
      END
    ELSE
      BEGIN
      WriteString[" ***** "L];
      WriteString["Funny PupType = "L];
      WriteOctal[LOOPHOLE[b.pup.pupType]];
      WriteString[" ***** "L];
      END;
    NewLine[];
    END;
      
  EchoUser: PROCEDURE =
    BEGIN OPEN PupDefs, PupTypes;
    bytesPerBuffer: CARDINAL;
    funny, late: LONG CARDINAL ← 0;
    recv, sent: LONG CARDINAL ← 0;
    wrong: LONG CARDINAL ← 0;
    me, where: PupAddress ← [, , echoSoc];
    mySoc: PupSocket;
    packetNumber: CARDINAL ← 0;
    routing: PupRouterDefs.RoutingTableEntry;
    pleaseStop: BOOLEAN ← FALSE;
    Watch: PROCEDURE = 
      BEGIN
      [] ← ReadChar[ ! ABORTED => CONTINUE];
      pleaseStop ← TRUE;
      END;
    GetName["Echo to: "L, @echoName];
    GetPupAddress[
      @where, echoName ! PupNameTrouble => AbortingCommand[e]];
    routing ← @PupRouterDefs.GetRoutingTable[][where.net];
    IF routing=NIL OR routing.network = NIL THEN AbortingCommand["Can't reach that network"L];
    mySoc ← PupSocketMake[fillInSocketID, where, SecondsToTocks[5]];
    me ← mySoc.getLocalAddress[];
    PrintPupAddress[@me];
    WriteString[" => "L];
    IF routing.hop # 0 THEN {
      WriteOctal[routing.network.pupNetNumber]; WriteChar['#];
      WriteOctal[routing.route]; WriteChar['#]; WriteString[" => "L]};
    PrintPupAddress[@where];
    NewLine[];
    Process.Detach[FORK Watch[]];
    bytesPerBuffer ← 2*MIN[DataWordsPerPupBuffer[], maxDataWordsPerGatewayPup];
    UNTIL pleaseStop DO
      FOR len: CARDINAL IN [0..bytesPerBuffer] UNTIL pleaseStop DO
        b: PupBuffer;
	b ← Buffer.GetBuffer[pup, PupPktOps.pupBuffers, send, fullBuffer];
        b.pup.pupID.a ← b.pup.pupID.b ← (packetNumber ← packetNumber + 1);
        b.pup.pupType ← echoMe;
        SetPupContentsBytes[b, len];
        FOR i: CARDINAL IN [0..len) DO b.pup.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.pup.pupType # iAmEcho) => {funny ← funny + 1; PrintErrorPup[b]};
            ((b.pup.pupID.a # packetNumber) OR (b.pup.pupID.b # packetNumber)
              OR (len # GetPupContentsBytes[b])) => {
              WriteChar['#]; late ← late + 1};
            ENDCASE => {
              FOR i: CARDINAL IN [0..len) DO
                IF b.pup.pupBytes[i] # (i MOD 400B) THEN {
                  wrong ← wrong + 1; WriteChar['~]; GOTO Wrong};
                ENDLOOP;
              WriteChar['!];
              recv ← recv + 1;
              EXIT};
          Buffer.ReturnBuffer[b];
          REPEAT Wrong => NULL;
          ENDLOOP;
        IF b # NIL THEN Buffer.ReturnBuffer[b] ELSE WriteChar['?];
        ENDLOOP;
      NewLine[];
      ENDLOOP;
    PupSocketDestroy[mySoc];
    WriteLine["."L];
    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
      BEGIN
      WriteLongNumber[funny];
      WriteLine[" funny"L];
      END;
    IF wrong # 0 THEN
      BEGIN
      WriteLongNumber[wrong];
      WriteLine[" wrong data"L];
      END;
    END;

  PupSender: PROCEDURE =
    BEGIN OPEN PupDefs, PupTypes;
    bytesPerBuffer: CARDINAL;
    me, where: PupAddress ← [, , echoSoc];
    mySoc: PupSocket;
    sent, echo, full, other: LONG CARDINAL ← 0;
    routing: PupRouterDefs.RoutingTableEntry;
    pleaseStop: BOOLEAN ← FALSE;
    Watch: PROCEDURE = 
      BEGIN
      [] ← ReadChar[ ! ABORTED => CONTINUE];
      pleaseStop ← TRUE;
      END;
    GetName["Send to: "L, @echoName];
    GetPupAddress[
      @where, echoName ! PupNameTrouble => AbortingCommand[e]];
    routing ← @PupRouterDefs.GetRoutingTable[][where.net];
    IF routing=NIL OR routing.network = NIL THEN AbortingCommand["Can't reach that network"L];
    mySoc ← PupSocketMake[fillInSocketID, where, PupDefs.veryShortWait];
    me ← mySoc.getLocalAddress[];
    PrintPupAddress[@me];
    WriteString[" => "L];
    IF routing.hop # 0 THEN
      BEGIN
      WriteOctal[routing.network.pupNetNumber]; WriteChar['#];
      WriteOctal[routing.route]; WriteChar['#]; WriteString[" => "L];
      END;
    PrintPupAddress[@where];
    NewLine[];
    Process.Detach[FORK Watch[]];
    bytesPerBuffer ← 100;
    UNTIL pleaseStop DO
      b: PupBuffer;
      FOR i: CARDINAL IN [0..3) DO
        b ← Buffer.GetBuffer[pup, PupPktOps.pupBuffers, send, fullBuffer];
        b.pup.pupID.a ← b.pup.pupID.b ← i;
        b.pup.pupType ← echoMe;
        FOR i: CARDINAL IN [0..bytesPerBuffer) DO b.pup.pupBytes[i] ← i; ENDLOOP;
        SetPupContentsBytes[b, bytesPerBuffer];
        mySoc.put[b];
        sent ← sent + 1;
        IF (sent MOD 100) = 0 THEN BEGIN WriteChar['s]; Process.Pause[1]; END;
        ENDLOOP;
      UNTIL (b ← mySoc.get[]) = NIL DO
        IF b.pup.pupType = iAmEcho THEN
	  BEGIN
	  echo ← echo + 1;
	  IF (echo MOD 100) = 0 THEN WriteChar['r];
	  END
        ELSE
          BEGIN
          IF b.pup.pupType = error AND b.pup.errorCode # resourceLimitsPupErrorCode THEN
	    BEGIN
	    other ← other + 1;
	    PrintErrorPup[b];
	    END
          ELSE full ← full + 1;
          END;
        Buffer.ReturnBuffer[b];
        b ← NIL;
        ENDLOOP;
      Process.Yield[];  -- be sure we don't hog machine
      ENDLOOP;
    NewLine[];
    WriteString["Sent: "L];
    WriteLongNumber[sent];
    WriteString[", Echoed: "L];
    WriteLongNumber[echo];
    IF full # 0 THEN
      BEGIN
      WriteString[", BufferFull: "L];
      WriteLongNumber[full];
      END;
    IF other # 0 THEN
      BEGIN
      WriteString[", Other: "L];
      WriteLongNumber[other];
      END;
    WriteLine["."L];
    PupSocketDestroy[mySoc];
    END;

  PrintLocalPupRoutingTable: PROCEDURE =
    BEGIN
    pupRt: LONG DESCRIPTOR FOR ARRAY OF PupRouterDefs.RoutingTableObject;
    k: CARDINAL ← 0;
    WriteLine["|  Net   Via Hops |  Net   Via Hops |  Net   Via Hops |  Net   Via Hops |"L];
    WriteLine["|-----------------|-----------------|-----------------|-----------------|"L];
    pupRt ← PupRouterDefs.GetRoutingTable[];
    FOR i: CARDINAL IN [0..LENGTH[pupRt]) DO
      D3: PROC [n: CARDINAL] = INLINE {WriteFixedWidthNumber[n, 3, 10]};
      O4: PROC [n: CARDINAL] = INLINE {WriteFixedWidthNumber[n, 4, 8]};
      O3Z: PROC [n: CARDINAL] = {
        n ← n MOD 1000B;
	THROUGH [0..3) DO WriteChar['0 + (n/100B) MOD 10B]; n ← n*10B ENDLOOP};
      r: PupRouterDefs.RoutingTableEntry = @pupRt[i];
      network: Driver.Network = r.network;
      OthelloDefs.CheckUserAbort[! ABORTED => EXIT];
      IF network = NIL THEN LOOP;
      IF k = 0 THEN WriteChar['|];
      O4[i]; O4[network.pupNetNumber];
      WriteChar['#];
      O3Z[IF r.hop # 0 THEN r.route ELSE network.pupHostNumber];
      WriteChar['#];
      D3[r.hop]; WriteString[" |"L];
      IF (k ← k + 1) = 4 THEN BEGIN NewLine[]; k ← 0; Process.Yield[]; END;
      ENDLOOP;
    IF k # 0 THEN NewLine[];
    END;

  PrintPupAddress: PROC [a: POINTER TO PupTypes.PupAddress] = {
    buffer: STRING ← [50];
    PupDefs.AppendPupAddress[buffer, a↑]; WriteString[buffer]};

  echoName: LONG STRING ← NIL;
  Commands: PROC [index: CARDINAL] =
    BEGIN
    SELECT index FROM
      0 => {
        MyNameIs[myNameIs: "Pup Echo User"L, myHelpIs: "Pup echo user"L];
	PupDefs.PupPackageMake[];
	EchoUser[! UNWIND => PupDefs.PupPackageDestroy[]]};
      1 => {
        MyNameIs[myNameIs: "Pup Sender"L, myHelpIs: "Send Pups rapidly"L];
	PupDefs.PupPackageMake[];
	PupSender[! UNWIND => PupDefs.PupPackageDestroy[]]};
      2 => {
        MyNameIs[
	  myNameIs: "Pup Routing Tables"L,
	  myHelpIs: "Show pup network routing tables"L];
	PupDefs.PupPackageMake[];
	PrintLocalPupRoutingTable[! UNWIND => PupDefs.PupPackageDestroy[]]};
      ENDCASE => IndexTooLarge;
    PupDefs.PupPackageDestroy[];
    END;
   
  commandProcessor: CommandProcessor ← [Commands];
  RegisterCommandProc[@commandProcessor];
  END.....