-- Copyright (C) 1983  by Xerox Corporation. All rights reserved. 
-- BootServerInfoClient.mesa, HGM, 23-Sep-83  3:41:54

DIRECTORY
  Courier USING [
    Call, Create, Delete, Error, ErrorCode, Free, Handle,
    NoSuchProcedureNumber, nullParameters, Parameters,
    RemoteErrorSignalled, VersionMismatch],
  ExpeditedCourier USING [DeserializeFromBlock, DirectedBroadcastCall, ResponseProc],
  Heap USING [systemZone],
  NetworkStream USING [],
  System USING [NetworkAddress],
  BootServerInfo USING [
    BootFileNumber, Counters, currentVersion, DescribeBootFileNumber,
    DescribeBootServerInfo, DescribeCounters, DescribeNetworkAddress,
    endEnumeration, Info, Procedures, programNumber, startEnumeration];

BootServerInfoClient: PROGRAM
  IMPORTS Courier, ExpeditedCourier, Heap, BootServerInfo
  EXPORTS BootServerInfo =
  BEGIN

  ProcessCourierError: PROCEDURE [errorCode: Courier.ErrorCode] =
    BEGIN
    finkyDebugger: BOOLEAN ← TRUE;
    strings: ARRAY Courier.ErrorCode OF STRING = [
      --transmission medium errors
      transmissionMediumHardwareProblem: "Transmission medium hardware problem"L,
      transmissionMediumUnavailable: "Transmission medium not available"L,
      transmissionMediumNotReady: "Transmission medium not ready"L,
      noAnswerOrBusy: "No answer or busy"L,
      --communication errors
      noRouteToSystemElement: "No route to that site"L,
      transportTimeout: "Transport timeout"L,
      remoteSystemElementNotResponding: "Remote site not responding"L,
      noCourierAtRemoteSite: "No Courier at remote site"L,
      tooManyConnections: "Too many connections"L,
      --protocol errors
      invalidMessage: "Invalid message"L,
      --procedure errors
      noSuchProcedureNumber: "No such procedure number"L,
      returnTimedOut: "Return Timed Out"L,
      callerAborted: "Caller Aborted"L,
      unknownErrorInRemoteProcedure: "Unknown Error in remote procedure"L,
      --data stream errors
      streamNotYours: "Stream not yours"L,
      truncatedTransfer: "Truncated transfer"L,
      --parameter errors
      parameterInconsistency: "Parameter inconsistency"L,
      invalidArguments: "Invalid arguments"L,
      --program errors
      noSuchProgramNumber: "No such program number"L,
      protocolMismatch: "Protocol Mismatch"L,
      duplicateProgramExport: "Duplicate program Export"L,
      noSuchProgramExport: "No such program Export"L,
      invalidHandle: "Invalid Handle"L,
      noError: "No Error"L];
    IF finkyDebugger THEN ERROR Error[strings[errorCode]];
    END;

  CourierCallProc: TYPE = PROCEDURE [callHandle: Courier.Handle];

  -- CONSTANTS

  z: UNCOUNTED ZONE = Heap.systemZone;

  -- ERRORS

  Error: PUBLIC ERROR [reason: LONG STRING] = CODE;

  -- PROCEDURES

  FindServers: PUBLIC PROCEDURE [
    systemElement: System.NetworkAddress,
    proc: PROCEDURE [System.NetworkAddress] ] =
    BEGIN
    MyResponseProc: ExpeditedCourier.ResponseProc =
      BEGIN
      him: System.NetworkAddress;
      okay: BOOLEAN ← ExpeditedCourier.DeserializeFromBlock[
        [@him, BootServerInfo.DescribeNetworkAddress], z, serializedResponse];
      IF NOT okay THEN RETURN[TRUE];
      proc[him];
      RETURN[TRUE];
      END;
    ExpeditedCourier.DirectedBroadcastCall[
        programNumber: BootServerInfo.programNumber,
        versionNumber: BootServerInfo.currentVersion,
        procedureNumber: BootServerInfo.Procedures.whoAreYou.ORD,
        address: systemElement,
        action: reliablyFindAllServers,
	eachResponse: MyResponseProc,
        arguments: Courier.nullParameters,
        responseBufferCount: 20];
    END;

  GetCounters: PUBLIC PROCEDURE [systemElement: System.NetworkAddress]
    RETURNS [counters: BootServerInfo.Counters] =
    BEGIN
    in: Courier.Parameters ← Courier.nullParameters;
    out: Courier.Parameters ← [@counters, BootServerInfo.DescribeCounters];
    CallProc: CourierCallProc =
      BEGIN
      [] ← Courier.Call[
        callHandle, BootServerInfo.Procedures.getCounters.ORD, in, out];
      END;
    CallTrappingErrors[systemElement, CallProc];
    Courier.Free[out, z];
    END;

  EnumerateBootTable: PUBLIC PROCEDURE [
    systemElement: System.NetworkAddress,
    proc: PROCEDURE [LONG POINTER TO BootServerInfo.Info]] =
    BEGIN
    info: BootServerInfo.Info;
    in: Courier.Parameters ← [@info.bfn, BootServerInfo.DescribeBootFileNumber];
    out: Courier.Parameters ← [@info, BootServerInfo.DescribeBootServerInfo];
    CallProc: CourierCallProc =
      BEGIN
      [] ← Courier.Call[
        callHandle, BootServerInfo.Procedures.enumerateBootTable.ORD, in, out];
      END;
    info.bfn ← BootServerInfo.startEnumeration;
    DO
      CallTrappingErrors[systemElement, CallProc];
      IF info.bfn # BootServerInfo.endEnumeration THEN proc[@info !
        UNWIND => Courier.Free[out, z] ];
      Courier.Free[out, z];
      IF info.bfn = BootServerInfo.endEnumeration THEN EXIT;
      ENDLOOP;
    END;

  CallTrappingErrors: PROCEDURE [
    systemElement: System.NetworkAddress, callProc: CourierCallProc] =
    BEGIN
    callHandle: Courier.Handle ← NIL;
    BEGIN
    ENABLE UNWIND => IF callHandle # NIL THEN Courier.Delete[callHandle];
    BEGIN
    ENABLE
      BEGIN
      Courier.Error => ProcessCourierError[errorCode];
      Courier.NoSuchProcedureNumber => ERROR Error["No such procedure number"L];
      Courier.VersionMismatch => ERROR Error["Version Mismatch"L];
      Courier.RemoteErrorSignalled =>
        BEGIN
        SELECT errorNumber FROM
          ENDCASE => ERROR;
        END;
      END;
    callHandle ← Courier.Create[
      systemElement, BootServerInfo.programNumber, BootServerInfo.currentVersion,
      Heap.systemZone, transactional];
    callProc[callHandle];
    END;  -- ENABLE
    END;  -- ENABLE
    Courier.Delete[callHandle];
    END;


  END.