-- File: RoutingInfoTool.mesa,  Last Edit: HGM  March 19, 1981  4:45 PM
-- Please don't forget to update the herald....

DIRECTORY
  Storage USING [String, FreeStringNil],
  String USING [AppendString, AppendNumber],
  Time USING [AppendCurrent],

  FormSW USING [
    ClientItemsProcType, ProcType, AllocateItemDescriptor, newLine, CommandItem,
    StringItem],
  MsgSW USING [Post],
  Put USING [Char, CR, Text, Line],
  Tool USING [Create, MakeSWsProc, MakeMsgSW, MakeFormSW, MakeFileSW],
  ToolWindow USING [TransitionProcType],
  Window USING [Handle],

  AddressTranslation USING [
    AppendMyHostNumber, AppendNetworkAddress,
    AppendNetworkNumber, AppendSystemElement,
    StringToNetworkAddress],
  BufferDefs USING [BufferAccessHandle, OisBuffer],
  OISCPTypes USING [RoutingInfoTuple],
  OISCPConstants USING [unknownSocketID, routingInformationSocket],
  OISCP USING [
    OiscpPackageDestroy, OiscpPackageMake,
    GetFreeSendOisBufferFromPool, ReturnFreeOisBuffer,
    GetOisPacketTextLength, SetOisPacketTextLength],
  Router USING [EnumerateRoutingTable, RoutingTableEntry],
  Socket USING [
    Abort, AssignNetworkAddress, Create, Delete, GetPacket, PutPacket,
    SetWaitTime, TimeOut, TransferStatus],
  SocketInternal USING [GetBufferPool, SocketHandle],
  SpecialSystem USING [
    HostNumber, GetProcessorID, NetworkAddress, NetworkNumber, ProcessorID],
  System USING [],

  DriverDefs USING [Network, GetDeviceChain],
  PupRouterDefs USING [RoutingTableEntry, EnumerateRoutingTable, PupGateInfo],
  PupDefs USING [
    PupPackageMake, PupPackageDestroy, GetFreePupBuffer, ReturnFreePupBuffer,
    PupBuffer, PupSocket, PupSocketDestroy, PupSocketMake, SecondsToTocks,
    SetPupContentsBytes, GetPupContentsBytes, AppendPupAddress, AppendErrorPup,
    GetPupAddress, PupNameTrouble],
  PupTypes USING [PupAddress, fillInSocketID, gatewaySoc, allHosts];

RoutingInfoTool: PROGRAM
  IMPORTS
    Storage, String, Time, FormSW, MsgSW, Put, Tool,
    AddressTranslation, OISCP, Router, Socket, SocketInternal, SpecialSystem,
    DriverDefs, PupRouterDefs, PupDefs
  EXPORTS Socket, System
  SHARES BufferDefs =
  BEGIN OPEN PupDefs, PupTypes;

  msg, form, log: Window.Handle ← NIL;

  me, pupTarget, oisTarget: STRING ← NIL;

  Init: PROCEDURE =
    BEGIN
    [] ← Tool.Create[
      name: "Routing Info of March 19, 1981"L, makeSWsProc: MakeSWs,
      clientTransition: ClientTransition];
    END;

  DeviceChain: FormSW.ProcType =
    BEGIN
    network: DriverDefs.Network;
    WriteCR[];
    WriteCurrentDateAndTime[];
    WriteLine["  Device Chain:"L];
    WriteLine["  ix net hst speed bfr dn  type"L];
    FOR network ← DriverDefs.GetDeviceChain[], network.next UNTIL network = NIL DO
      O4[network.index];
      O4[network.netNumber.b];
      O4[network.hostNumber];
      D6[network.speed];
      D4[network.buffers];
      WriteString[IF network.alive THEN "   "L ELSE "  *"L];
      WriteString["  "L];
      SELECT network.device FROM
	ethernetOne => WriteLine["Ethernet - 3mb"L];
	ethernet => WriteLine["Ethernet - 10mb"L];
	sla => WriteLine["SLA"L];
	packetradio => WriteLine["Packet Radio"L];
	phonenet => WriteLine["Phone Net"L];
	ENDCASE => WriteLine["???"L];
      ENDLOOP;
    END;

  LocalPup: FormSW.ProcType =
    BEGIN
    PrintOne: PROCEDURE [rte: PupRouterDefs.RoutingTableEntry] =
      BEGIN
      network: DriverDefs.Network ← rte.network;
      IF network = NIL THEN RETURN;
      nets ← nets + 1;
      IF k = 0 THEN WriteChar['|];
      O4[rte.net];
      O4[network.netNumber.b];
      WriteChar['#];
      IF rte.hop # 0 THEN O3Z[rte.route] ELSE O3Z[network.hostNumber];
      WriteChar['#];
      O4[rte.hop];
      WriteString["  |"L];
      IF (k ← k + 1) = 3 THEN BEGIN WriteCR[]; k ← 0; END;
      END;
    k, nets: CARDINAL ← 0;
    WriteCR[];
    WriteCurrentDateAndTime[];
    WriteLine["  Local Pup Routing Table."L];
    WriteLine["|  Net   Via   Hops |  Net   Via   Hops |  Net   Via   Hops |"L];
    WriteLine["|-------------------|-------------------|-------------------|"L];
    PupRouterDefs.EnumerateRoutingTable[PrintOne];
    IF k # 0 THEN WriteCR[];
    IF nets > 1 THEN
      BEGIN
      WriteString["There are "L];
      WriteDecimal[nets - 1];
      WriteLine[" active networks."L];
      END;
    END;

  RemotePup: FormSW.ProcType =
    BEGIN
    where: PupAddress;
    FindPath: PROCEDURE RETURNS [BOOLEAN] =
      BEGIN
      WriteString[pupTarget];
      WriteChar['=];
      where ← [[0], [0], PupTypes.gatewaySoc];
      GetPupAddress[
        @where, pupTarget !
        PupNameTrouble =>
  	BEGIN MsgSW.Post[msg, e]; WriteLine[e]; GOTO Trouble; END];
      PrintPupAddress[where];
      WriteLine["."L];
      RETURN[TRUE];
      EXITS Trouble => RETURN[FALSE];
      END;
    soc: PupSocket;
    b: PupBuffer;
    nets: CARDINAL ← 0;
    WriteCR[];
    WriteCurrentDateAndTime[];
    WriteString["  Routing Table from "L];
    IF ~FindPath[] THEN RETURN;
    soc ← PupSocketMake[PupTypes.fillInSocketID, where, SecondsToTocks[2]];
    THROUGH [0..10) UNTIL nets # 0 DO
      b ← GetFreePupBuffer[];
      b.pupType ← gatewayRequest;
      SetPupContentsBytes[b, 0];
      b.pupID ← [0, 0];
      soc.put[b];
      UNTIL (b ← soc.get[]) = NIL DO -- 2 sec wait
	IF where # b.source THEN
	  BEGIN
	  WriteString["Reply from: "L];
	  PrintPupAddress[b.source];
	  WriteLine["."L];
	  END;
	SELECT b.pupType FROM
	  gatewayInfo =>
	    BEGIN
	    length: CARDINAL = GetPupContentsBytes[b];
	    info: LONG POINTER TO PupRouterDefs.PupGateInfo ←
	      LOOPHOLE[@b.pupWords];
	    k, n: CARDINAL;
	    n ← length/(2*SIZE[PupRouterDefs.PupGateInfo]);
	    IF (length MOD (2*SIZE[PupRouterDefs.PupGateInfo])) # 0 THEN
	      WriteLine["***** Funny Length *****"L];
	    WriteLine[
	      "|  Net   Via   Hops |  Net   Via   Hops |  Net   Via   Hops |"L];
	    WriteLine[
	      "|-------------------|-------------------|-------------------|"L];
	    k ← 0;
	    FOR i: CARDINAL IN [0..n) DO
	      nets ← nets + 1;
	      IF k = 0 THEN WriteChar['|];
	      O4[info.net];
	      O4[info.viaNet];
	      WriteChar['#];
	      O3Z[info.viaHost];
	      WriteChar['#];
	      O4[info.hop];
	      WriteString["  |"L];
	      IF (k ← k + 1) = 3 THEN BEGIN WriteCR[]; k ← 0; END;
	      info ← info + SIZE[PupRouterDefs.PupGateInfo];
	      ENDLOOP;
	    IF k # 0 THEN WriteCR[];
	    END;
	  ENDCASE => PrintErrorPup[b];
	ReturnFreePupBuffer[b];
	ENDLOOP;
      IF nets = 0 THEN MsgSW.Post[msg, "No Response that try."L];
      ENDLOOP;
    IF where.host # PupTypes.allHosts THEN
      BEGIN
      WriteString["There are "L];
      WriteDecimal[nets];
      WriteLine[" active networks."L];
      END;
    PupSocketDestroy[soc];
    END;

  LocalOis: FormSW.ProcType =
    BEGIN
    PrintOne: PROCEDURE [rte: Router.RoutingTableEntry] =
      BEGIN
      network: DriverDefs.Network ← rte.network;
      IF network = NIL THEN duds ← duds + 1
      ELSE nets ← nets + 1;
      WriteNetNumber[rte.destNetwork];
      WriteChar[' ];
      IF rte.delay # 1 THEN WriteNetAndHost[network.netNumber, rte.route]
      ELSE WriteNetAndHost[network.netNumber, me];
      O4[rte.delay];
      O4[rte.timeUnits];
      IF rte.network=NIL THEN WriteChar['~] ELSE WriteChar[' ];
      WriteCR[];
      END;
    nets, duds: CARDINAL ← 0;
    me: SpecialSystem.ProcessorID = SpecialSystem.GetProcessorID[];
    WriteCR[];
    WriteCurrentDateAndTime[];
    WriteLine["  Local OISCP Routing Table."L];
    WriteLine["  Net             Via           Dly Tim"L];
    Router.EnumerateRoutingTable[PrintOne];
    IF nets > 1 THEN
      BEGIN
      WriteString["There are "L];
      WriteDecimal[nets - 1];
      WriteLine[" active networks."L];
      END;
    IF duds > 0 THEN
      BEGIN
      WriteString["There are "L];
      WriteDecimal[duds];
      WriteLine[" dead slots."L];
      END;
    END;

  -- EXPORTED TYPE(S)
  NetworkAddress: PUBLIC TYPE = SpecialSystem.NetworkAddress;
  ChannelHandle: PUBLIC TYPE = SocketInternal.SocketHandle;
  RemoteOis: FormSW.ProcType =
    BEGIN
    nets: CARDINAL ← 0;
    PrintOne: PROCEDURE [rte: LONG POINTER TO OISCPTypes.RoutingInfoTuple] =
      BEGIN
      nets ← nets + 1;
      WriteNetNumber[rte.objectNetID];
      O4[rte.interrouterDelay];
      WriteCR[];
      END;
    remoteAddr, localAddr: SpecialSystem.NetworkAddress;
    cH: ChannelHandle;
    myBufferAccessHandle: BufferDefs.BufferAccessHandle;
    b: BufferDefs.OisBuffer;
    status: Socket.TransferStatus;
    errFlag, hit: BOOLEAN ← FALSE;

    WriteCR[];
    WriteCurrentDateAndTime[];
    WriteLine["  Remote OIS Routing Table."L];
    MsgSW.Post[msg, ""L];
    remoteAddr ← AddressTranslation.StringToNetworkAddress[oisTarget !
      ANY =>
	BEGIN
	MsgSW.Post[msg, "RemoteAddress is incorrectly specified; try again."L];
	errFlag ← TRUE;
	CONTINUE;
	END];
    IF errFlag THEN RETURN;
    IF remoteAddr.socket=OISCPConstants.unknownSocketID THEN
      remoteAddr.socket ← OISCPConstants.routingInformationSocket;
    localAddr ← Socket.AssignNetworkAddress[];
    cH ← Socket.Create[localAddr];
    myBufferAccessHandle ← SocketInternal.GetBufferPool[cH];
    Socket.SetWaitTime[cH, 1500]; -- milli-seconds

    FOR i: CARDINAL IN [0..10) UNTIL hit DO
      b ← OISCP.GetFreeSendOisBufferFromPool[myBufferAccessHandle];
      OISCP.SetOisPacketTextLength[b, 2*1];
      b.ois.transCntlAndPktTp.packetType ← routingInformation;
      b.ois.destination ← remoteAddr;
      b.ois.routingType ← routingInfoRequest;
      Socket.PutPacket[cH, b];
      DO
	b ← Socket.GetPacket[ cH ! Socket.TimeOut => EXIT];
	status ← LOOPHOLE[b.status];
	SELECT TRUE FROM
	  (status # goodCompletion) =>
	    BEGIN -- some kind of error occurred
	    --PrintSocketEchoError[b, status];
	    Put.Char[log, 'e];
	    END;
          (b.ois.routingType # routingInfoResponse) =>
	    BEGIN
	    Put.Char[log, '#];
	    END;
	  ENDCASE =>
	    BEGIN -- the response we were looking for
	    length, entrys: CARDINAL;
	    length ← OISCP.GetOisPacketTextLength[b];
	    entrys ← length/(2*SIZE[OISCPTypes.RoutingInfoTuple]);
            IF remoteAddr#b.ois.source THEN
              BEGIN
              temp: STRING = [50];
              Put.Text[log, "Response from: "L];
              AddressTranslation.AppendNetworkAddress[temp, b.ois.source];
              Put.Line[log, temp];
              END;
            IF ~hit THEN Put.Line[log,"  Net Dly"L];
            hit ← TRUE;
	    FOR n: CARDINAL IN [0..entrys) DO
	      PrintOne[@b.ois.routingTuple[n]];
              ENDLOOP;
	    END;
	  OISCP.ReturnFreeOisBuffer[b];
	ENDLOOP;
      IF nets = 0 THEN MsgSW.Post[msg, "No Response that try."L];
      ENDLOOP;
    Put.CR[log];
    Socket.Abort[cH];
    Socket.Delete[cH];
    MsgSW.Post[msg, ""L];
    END;

  -- IO things

  WriteNetNumber: PROCEDURE [net: SpecialSystem.NetworkNumber] =
    BEGIN
    temp: STRING = [50];
    AddressTranslation.AppendNetworkNumber[temp, net];
    temp.length ← temp.length - 1;  -- flush #
    THROUGH [temp.length..5) DO WriteChar[' ]; ENDLOOP;
    WriteString[temp];
    END;

  WriteNetAndHost: PROCEDURE [
      net: SpecialSystem.NetworkNumber, host: SpecialSystem.HostNumber] =
    BEGIN
    temp: STRING = [50];
    AddressTranslation.AppendSystemElement[
      temp, SpecialSystem.NetworkAddress[net,host,[0]]];
    THROUGH [temp.length..25) DO WriteChar[' ]; ENDLOOP;
    WriteString[temp];
    END;

  WriteChar: PROCEDURE [c: CHARACTER] = BEGIN Put.Char[log, c]; END;

  WriteCR: PROCEDURE = BEGIN Put.CR[log]; END;

  WriteString: PROCEDURE [s: STRING] = BEGIN Put.Text[log, s]; END;

  WriteLine: PROCEDURE [s: STRING] = BEGIN Put.Line[log, s]; END;

  WriteDecimal: PROCEDURE [n: CARDINAL] = INLINE BEGIN WriteNumber[n, 10, 0]; END;

  O, WriteOctal: PROCEDURE [n: CARDINAL] = INLINE BEGIN WriteNumber[n, 8, 0]; END;

  WriteNumber: PROCEDURE [n, radix, width: CARDINAL] = INLINE
    BEGIN
    temp: STRING = [25];
    String.AppendNumber[temp, n, radix];
    THROUGH [temp.length..width) DO WriteChar[' ]; ENDLOOP;
    WriteString[temp];
    END;

  O3Z: PROCEDURE [n: CARDINAL] =
    BEGIN
    temp: STRING = [25];
    String.AppendNumber[temp, n, 8];
    THROUGH [temp.length..3) DO WriteChar['0]; ENDLOOP;
    WriteString[temp];
    END;

  O4: PROCEDURE [n: CARDINAL] = BEGIN WriteNumber[n, 8, 4]; END;

  D4: PROCEDURE [n: CARDINAL] = BEGIN WriteNumber[n, 10, 4]; END;

  D6: PROCEDURE [n: CARDINAL] = BEGIN WriteNumber[n, 10, 6]; END;

  WriteCurrentDateAndTime: PROCEDURE =
    BEGIN time: STRING = [20]; Time.AppendCurrent[time]; WriteString[time]; END;

  PrintPupAddress: PROCEDURE [a: PupAddress] =
    BEGIN temp: STRING = [40]; AppendPupAddress[temp, a]; WriteString[temp]; END;

  PrintErrorPup: PUBLIC PROCEDURE [b: PupDefs.PupBuffer] =
    BEGIN
    text: STRING = [100];
    AppendErrorPup[text, b];
    MsgSW.Post[msg, text];
    END;

  MakeSWs: Tool.MakeSWsProc =
    BEGIN
    msg ← Tool.MakeMsgSW[window: window, lines: 5];
    form ← Tool.MakeFormSW[window: window, formProc: MakeForm];
    log ← Tool.MakeFileSW[window: window, name: "RoutingInfo.log$"L];
    END;

  MakeForm: FormSW.ClientItemsProcType =
    BEGIN
    nParams: CARDINAL = 8;
    items ← FormSW.AllocateItemDescriptor[nParams];
    items[0] ← FormSW.CommandItem[
      tag: "LocalPup"L, proc: LocalPup, place: FormSW.newLine];
    items[1] ← FormSW.CommandItem[tag: "RemotePup"L, proc: RemotePup];
    items[2] ← FormSW.StringItem[tag: "PupTarget"L, string: @pupTarget];
    items[3] ← FormSW.CommandItem[
      tag: "LocalOis"L, proc: LocalOis, place: FormSW.newLine];
    items[4] ← FormSW.CommandItem[tag: "RemoteOis"L, proc: RemoteOis];
    items[5] ← FormSW.StringItem[tag: "OisTarget"L, string: @oisTarget];
    items[6] ← FormSW.CommandItem[
      tag: "DeviceChain"L, proc: DeviceChain, place: FormSW.newLine];
    items[7] ← FormSW.StringItem[tag: "MyProcessorID"L, string: @me];
    RETURN[items, TRUE];
    END;

  ClientTransition: ToolWindow.TransitionProcType =
    BEGIN
    SELECT TRUE FROM
      old = inactive =>
	BEGIN
	pupTarget ← Storage.String[40];
	String.AppendString[pupTarget, "ME"L];
	oisTarget ← Storage.String[40];
	String.AppendString[oisTarget, "0#*#1"L];
	me ← Storage.String[40];
	AddressTranslation.AppendMyHostNumber[me];
	me.length ← me.length - 1;  -- flush stupid #
	PupDefs.PupPackageMake[];
	OISCP.OiscpPackageMake[];
	END;
      new = inactive =>
	BEGIN
	OISCP.OiscpPackageDestroy[];
	PupDefs.PupPackageDestroy[];
	pupTarget ← Storage.FreeStringNil[pupTarget];
	oisTarget ← Storage.FreeStringNil[oisTarget];
	me ← Storage.FreeStringNil[me];
	END;
      ENDCASE;
    END;

  Init[];
  END.