-- Copyright (C) 1984  by Xerox Corporation. All rights reserved. 
-- TestTimeServer.mesa, HGM,  4-Nov-84  8:57:47

DIRECTORY
  AddressTranslation USING [Error, PrintError, StringToNetworkAddress],
  Format USING [StringProc],
  FormSW USING [
    AllocateItemDescriptor, ClientItemsProcType, CommandItem, ItemHandle, newLine,
    ProcType, StringItem],
  Heap USING [systemZone],
  MsgSW USING [Post],
  Put USING [Char, CR, Date, Decimal, Line, LongDecimal, NetworkAddress, Text],
  Runtime USING [GetBcdTime],
  String USING [AppendString],
  System USING [
    GetClockPulses, GetGreenwichMeanTime, gmtEpoch, GreenwichMeanTime,
    NetworkAddress, nullNetworkAddress, nullSocketNumber,
    Pulses, PulsesToMicroseconds, SocketNumber],
  Time USING [Append, Packed, Unpack],
  Tool USING [
    Create, UnusedLogName, MakeFormSW, MakeFileSW, MakeMsgSW, MakeSWsProc],
  ToolWindow USING [TransitionProcType],
  Unformat USING [Error, NetworkAddress],
  Window USING [Handle],

  Buffer USING [NSBuffer],
  NSConstants USING [timeServerSocket],
  NSTypes USING [wordsPerExchangeHeader],
  PacketExchange USING [
    CreateRequestor, Delete, Error, ExchangeClientType, ExchangeHandle, SendRequest],
  Socket USING [
    AssignNetworkAddress, BroadcastAddressFromSocket, ChannelHandle, Create,
    Delete, GetPacket, GetPacketBytes, GetSendBuffer, GetSource, PutPacket,
    BroadcastPacketToAllConnectedNets, ReturnBuffer, SetDestination, SetPacketWords,
    SetWaitTime, TimeOut],
  TimeServerFormat USING [TSPacket, Version, WireToGMT, WireToLong],
  TimeServerFormatOld USING [request, response, TimeFormat],
  WireFormat USING [WireLongNumber, WireToMesaLongNumber];

TestTimeServer: PROGRAM
  IMPORTS
    AddressTranslation, FormSW, Heap, MsgSW, Put,
    Runtime, String, System, Time, Tool, Unformat,
    PacketExchange, Socket,
    TimeServerFormat, WireFormat =
  BEGIN

  z: UNCOUNTED ZONE = Heap.systemZone;

  -- global variable declarations
  msg, log, form: Window.Handle ← NIL;
  remoteAddress: LONG STRING ← NIL;
  localAddr: System.NetworkAddress;
  
  version: WORD = TimeServerFormat.Version;

  Init: PROCEDURE =
    BEGIN
    herald: STRING = [100];
    String.AppendString[herald, "TestTimeServer of  "L];
    Time.Append[herald, Time.Unpack[Runtime.GetBcdTime[]]];
    [] ← Tool.Create[
      name: herald, makeSWsProc: MakeSWs, clientTransition: Transition];
    END;

  MakeSWs: Tool.MakeSWsProc =
    BEGIN
    logFileName: STRING = [40];
    msg ← Tool.MakeMsgSW[window: window, lines: 1];
    form ← Tool.MakeFormSW[window: window, formProc: MakeItemArray];
    Tool.UnusedLogName[logFileName, "TestTimeServer.log$"L];
    log ← Tool.MakeFileSW[window: window, name: logFileName];
    END;

  MakeItemArray: FormSW.ClientItemsProcType =
    BEGIN
    nItems: CARDINAL = 8;
    i: INTEGER ← -1;
    items ← FormSW.AllocateItemDescriptor[nItems];
    items[i ← i + 1] ← FormSW.CommandItem[
      tag: "Statistics"L, place: FormSW.newLine, proc: Statistics];
    items[i ← i + 1] ← FormSW.StringItem[
      tag: "RemoteAddress"L, string: @remoteAddress, inHeap: TRUE];
    items[i ← i + 1] ← FormSW.CommandItem[
      tag: "PEXLocalBroadcast"L, place: FormSW.newLine, proc: PEXLocalBroadcast];
    items[i ← i + 1] ← FormSW.CommandItem[tag: "PEXAskRemote"L, proc: PEXAskRemote];
    items[i ← i + 1] ← FormSW.CommandItem[
      tag: "LocalBroadcast"L, place: FormSW.newLine, proc: LocalBroadcast];
    items[i ← i + 1] ← FormSW.CommandItem[tag: "AskRemote"L, proc: AskRemote];
    items[i ← i + 1] ← FormSW.CommandItem[
      tag: "OldLocalBroadcast"L, place: FormSW.newLine, proc: OldLocalBroadcast];
    items[i ← i + 1] ← FormSW.CommandItem[tag: "OldAskRemote"L, proc: OldAskRemote];
    IF (i + 1) # nItems THEN ERROR;
    RETURN[items, TRUE];
    END;

  Transition: ToolWindow.TransitionProcType =
    BEGIN
    SELECT TRUE FROM
      old = inactive =>
        BEGIN
        localAddr ← Socket.AssignNetworkAddress[];
        remoteAddress ← z.NEW[StringBody[40]];
        String.AppendString[remoteAddress, "0#*#"L];
        END;
      new = inactive =>
        BEGIN z.FREE[@remoteAddress]; END;
      ENDCASE => NULL;
    END;

  Statistics: FormSW.ProcType =
    BEGIN
    wordsInRequest: CARDINAL = SIZE[statisticRequest TimeServerFormat.TSPacket];
    wordsInAnswer: CARDINAL = SIZE[statisticResponse TimeServerFormat.TSPacket];
    request: LONG POINTER TO  statisticRequest TimeServerFormat.TSPacket;
    answer: LONG POINTER TO statisticResponse TimeServerFormat.TSPacket;
    him: System.NetworkAddress;
    soc: Socket.ChannelHandle;
    b: Buffer.NSBuffer;
    errFlag, hit: BOOLEAN ← FALSE;

    MsgSW.Post[msg, ""L];
    him ← GetAddress[remoteAddress, NSConstants.timeServerSocket !
      Trouble =>
        BEGIN
	Put.Line[msg, reason];
        errFlag ← TRUE;
        CONTINUE;
        END];
    IF errFlag THEN RETURN;
    MsgSW.Post[msg, "Here we go...."L];
    Put.CR[log];
    Put.Line[log, "Statistics.."L];
    soc ← Socket.Create[local: localAddr, receive: 10];
    Socket.SetWaitTime[soc, 5000];  -- milli-seconds
      
    FOR i: CARDINAL IN [0..10) UNTIL hit DO
      b ← Socket.GetSendBuffer[soc];
      Socket.SetDestination[b, him];
      b.ns.packetType ← packetExchange;
      b.ns.exchangeID ← [1, 1];
      b.ns.exchangeType ← timeService;
      request ← LOOPHOLE[@b.ns.exchangeBody];
      request↑ ← [version, statisticRequest[]];
      Socket.SetPacketWords[b, NSTypes.wordsPerExchangeHeader + wordsInRequest];
      Socket.PutPacket[soc, b];
      DO
        b ← Socket.GetPacket[
          soc ! Socket.TimeOut => BEGIN IF ~hit THEN Put.Char[log, '?]; EXIT; END];
	answer ← LOOPHOLE[@b.ns.exchangeBody];
        SELECT TRUE FROM
	  b.ns.packetType # packetExchange
          OR Socket.GetPacketBytes[b] # 2*(NSTypes.wordsPerExchangeHeader + wordsInAnswer)
          OR (b.ns.exchangeType # timeService)
	  OR answer.version # version
	  OR answer.type # statisticResponse =>
            BEGIN Put.Char[log, '#]; END;
          ENDCASE =>
            BEGIN  -- the response we were looking for
            hit ← TRUE;
            PrintStatistics[answer, Socket.GetSource[b]];
            END;
        Socket.ReturnBuffer[b];
        ENDLOOP;
      ENDLOOP;
    Put.CR[log];
    Socket.Delete[soc];
    MsgSW.Post[msg, "OK"L];
	        END;
     
  PEXLocalBroadcast: FormSW.ProcType =
    BEGIN
    handle: PacketExchange.ExchangeHandle;
    him: System.NetworkAddress;
    bytesInRequest: CARDINAL = 2*SIZE[timeRequest TimeServerFormat.TSPacket];
    bytesInAnswer: CARDINAL = 2*SIZE[timeResponse TimeServerFormat.TSPacket];
    request: timeRequest TimeServerFormat.TSPacket ← [version, timeRequest[]];
    answer: timeResponse TimeServerFormat.TSPacket;
    bytes: CARDINAL;
    response: PacketExchange.ExchangeClientType;
    MsgSW.Post[msg, "Here we go...."L];
    Put.CR[log];
    Put.Line[log, "PEX Local Broadcast.."L];
    handle ← PacketExchange.CreateRequestor[10000, 2000];
    him ← Socket.BroadcastAddressFromSocket[
      NSConstants.timeServerSocket];
    BEGIN
    start, stop: System.Pulses;
    start ← System.GetClockPulses[];
    [bytes, response] ← PacketExchange.SendRequest[
      handle,
      him,
      [LOOPHOLE[LONG[@request]], 0, bytesInRequest],
      [LOOPHOLE[LONG[@answer]], 0, bytesInAnswer],
      timeService !
        PacketExchange.Error =>
	  BEGIN
	  SELECT why FROM
	    timeout => Put.Line[log, "Timeout."L];
	    ENDCASE => Put.Line[log, "Strange error type."L];
	  GOTO Rejected;
	  END ];
    SELECT TRUE FROM
      bytes # bytesInAnswer => Put.Line[log, "Wrong size response."L];
      response # timeService => Put.Line[log, "Wrong type response."L];
      answer.version # version => Put.Line[log, "Wrong version."L];
      ENDCASE =>
        BEGIN
        local: System.GreenwichMeanTime ← System.GetGreenwichMeanTime[];
        delay: LONG CARDINAL;
        stop ← System.GetClockPulses[];
        delay ← System.PulsesToMicroseconds[[stop-start]]/1000;
	PrintAnswer[@answer, System.nullNetworkAddress, local, delay];
	END;
    EXITS Rejected => NULL;
    END;
    PacketExchange.Delete[handle];
    MsgSW.Post[msg, "OK"L];
    END;
     
  PEXAskRemote: FormSW.ProcType =
    BEGIN
    errFlag: BOOLEAN ← FALSE;
    handle: PacketExchange.ExchangeHandle;
    him: System.NetworkAddress;
    bytesInRequest: CARDINAL = 2*SIZE[timeRequest TimeServerFormat.TSPacket];
    bytesInAnswer: CARDINAL = 2*SIZE[timeResponse TimeServerFormat.TSPacket];
    request: timeRequest TimeServerFormat.TSPacket ← [version, timeRequest[]];
    answer: timeResponse TimeServerFormat.TSPacket;
    bytes: CARDINAL;
    response: PacketExchange.ExchangeClientType;
    him ← GetAddress[remoteAddress, NSConstants.timeServerSocket !
      Trouble =>
        BEGIN
        Put.Text[msg, "AddressTranslation troubles: "L];
	Put.Line[msg, reason];
        errFlag ← TRUE;
        CONTINUE;
        END];
    IF errFlag THEN RETURN;
    MsgSW.Post[msg, "Here we go...."L];
    Put.CR[log];
    Put.Line[log, "PEX Ask Remote.."L];
    handle ← PacketExchange.CreateRequestor[10000, 2000];
    BEGIN
    start, stop: System.Pulses;
    start ← System.GetClockPulses[];
    [bytes, response] ← PacketExchange.SendRequest[
      handle,
      him,
      [LOOPHOLE[LONG[@request]], 0, bytesInRequest],
      [LOOPHOLE[LONG[@answer]], 0, bytesInAnswer],
      timeService !
        PacketExchange.Error =>
	  BEGIN
	  SELECT why FROM
	    timeout => Put.Line[log, "Timeout."L];
	    ENDCASE => Put.Line[log, "Strange error type."L];
	  GOTO Rejected;
	  END ];
    SELECT TRUE FROM
      bytes # bytesInAnswer => Put.Line[log, "Wrong size response."L];
      response # timeService => Put.Line[log, "Wrong type response."L];
      answer.version # version => Put.Line[log, "Wrong version."L];
      ENDCASE =>
        BEGIN
        local: System.GreenwichMeanTime ← System.GetGreenwichMeanTime[];
        delay: LONG CARDINAL;
        stop ← System.GetClockPulses[];
        delay ← System.PulsesToMicroseconds[[stop-start]]/1000;
	PrintAnswer[@answer, System.nullNetworkAddress];
	END;
    EXITS Rejected => NULL;
    END;
    PacketExchange.Delete[handle];
    MsgSW.Post[msg, "OK"L];
    END;
  
  LocalBroadcast: FormSW.ProcType =
    BEGIN
    wordsInRequest: CARDINAL = SIZE[timeRequest TimeServerFormat.TSPacket];
    wordsInAnswer: CARDINAL = SIZE[timeResponse TimeServerFormat.TSPacket];
    request: LONG POINTER TO timeRequest TimeServerFormat.TSPacket;
    answer: LONG POINTER TO timeResponse TimeServerFormat.TSPacket;
    soc: Socket.ChannelHandle;
    b: Buffer.NSBuffer;
    him: System.NetworkAddress;
    hit: BOOLEAN ← FALSE;

    MsgSW.Post[msg, "Here we go...."L];
    Put.CR[log];
    Put.Line[log, "Local Broadcast.."L];
    soc ← Socket.Create[local: localAddr, receive: 10];
    Socket.SetWaitTime[soc, 5000];  -- milli-seconds
    him ← Socket.BroadcastAddressFromSocket[
      NSConstants.timeServerSocket];

    FOR i: CARDINAL IN [0..10) UNTIL hit DO
      start, stop: System.Pulses;
      b ← Socket.GetSendBuffer[soc];
      Socket.SetDestination[b, him];
      b.ns.packetType ← packetExchange;
      b.ns.exchangeID ← [1, 1];
      b.ns.exchangeType ← timeService;
      request ← LOOPHOLE[@b.ns.exchangeBody];
      request↑ ← [version, timeRequest[]];
      Socket.SetPacketWords[b, NSTypes.wordsPerExchangeHeader + wordsInRequest];
      start ← System.GetClockPulses[];
      Socket.BroadcastPacketToAllConnectedNets[soc, b];
      DO
        b ← Socket.GetPacket[
          soc ! Socket.TimeOut => BEGIN IF ~hit THEN Put.Char[log, '?]; EXIT; END];
	answer ← LOOPHOLE[@b.ns.exchangeBody];
        SELECT TRUE FROM
	  b.ns.packetType # packetExchange
          OR Socket.GetPacketBytes[b] # 2*(NSTypes.wordsPerExchangeHeader + wordsInAnswer)
          OR (b.ns.exchangeType # timeService)
	  OR answer.version # version
	  OR answer.type # timeResponse =>
            BEGIN Put.Char[log, '#]; END;
          ENDCASE =>
            BEGIN  -- the response we were looking for
            local: System.GreenwichMeanTime ← System.GetGreenwichMeanTime[];
	    delay: LONG CARDINAL;
	    stop ← System.GetClockPulses[];
	    delay ← System.PulsesToMicroseconds[[stop-start]]/1000;
            PrintAnswer[answer, Socket.GetSource[b], local, delay];
            hit ← TRUE;
            END;
        Socket.ReturnBuffer[b];
        ENDLOOP;
      ENDLOOP;
    Put.CR[log];
    Socket.Delete[soc];
    MsgSW.Post[msg, "OK"L];
    END;

  AskRemote: FormSW.ProcType =
    BEGIN
    wordsInRequest: CARDINAL = SIZE[timeRequest TimeServerFormat.TSPacket];
    wordsInAnswer: CARDINAL = SIZE[timeResponse TimeServerFormat.TSPacket];
    request: LONG POINTER TO  timeRequest TimeServerFormat.TSPacket;
    answer: LONG POINTER TO timeResponse TimeServerFormat.TSPacket;
    him: System.NetworkAddress;
    soc: Socket.ChannelHandle;
    b: Buffer.NSBuffer;
    errFlag, hit: BOOLEAN ← FALSE;

    MsgSW.Post[msg, ""L];
    him ← GetAddress[remoteAddress, NSConstants.timeServerSocket !
      Trouble =>
        BEGIN
        Put.Text[msg, "AddressTranslation troubles: "L];
	Put.Line[msg, reason];
        errFlag ← TRUE;
        CONTINUE;
        END];
    IF errFlag THEN RETURN;
    MsgSW.Post[msg, "Here we go...."L];
    Put.CR[log];
    Put.Line[log, "Ask Remote.."L];
    soc ← Socket.Create[local: localAddr, receive: 10];
    Socket.SetWaitTime[soc, 5000];  -- milli-seconds

    FOR i: CARDINAL IN [0..10) UNTIL hit DO
      start, stop: System.Pulses;
      b ← Socket.GetSendBuffer[soc];
      Socket.SetDestination[b, him];
      b.ns.packetType ← packetExchange;
      b.ns.exchangeID ← [1, 1];
      b.ns.exchangeType ← timeService;
      request ← LOOPHOLE[@b.ns.exchangeBody];
      request↑ ← [version, timeRequest[]];
      Socket.SetPacketWords[b, NSTypes.wordsPerExchangeHeader + wordsInRequest];
      start ← System.GetClockPulses[];
      Socket.PutPacket[soc, b];
      DO
        b ← Socket.GetPacket[
          soc ! Socket.TimeOut => BEGIN IF ~hit THEN Put.Char[log, '?]; EXIT; END];
	answer ← LOOPHOLE[@b.ns.exchangeBody];
        SELECT TRUE FROM
	  b.ns.packetType # packetExchange
          OR Socket.GetPacketBytes[b] # 2*(NSTypes.wordsPerExchangeHeader + wordsInAnswer)
          OR (b.ns.exchangeType # timeService)
	  OR answer.version # version
	  OR answer.type # timeResponse =>
            BEGIN Put.Char[log, '#]; END;
          ENDCASE =>
            BEGIN  -- the response we were looking for
            local: System.GreenwichMeanTime ← System.GetGreenwichMeanTime[];
	    delay: LONG CARDINAL;
	    stop ← System.GetClockPulses[];
	    delay ← System.PulsesToMicroseconds[[stop-start]]/1000;
            PrintAnswer[answer, Socket.GetSource[b], local, delay];
            hit ← TRUE;
            END;
        Socket.ReturnBuffer[b];
        ENDLOOP;
      ENDLOOP;
    Put.CR[log];
    Socket.Delete[soc];
    MsgSW.Post[msg, "OK"L];
    END;

  OldLocalBroadcast: FormSW.ProcType =
    BEGIN
    soc: Socket.ChannelHandle;
    b: Buffer.NSBuffer;
    him: System.NetworkAddress;
    hit: BOOLEAN ← FALSE;

    MsgSW.Post[msg, "Here we go...."L];
    Put.CR[log];
    Put.Line[log, "Old Local Broadcast.."L];
    soc ← Socket.Create[local: localAddr, receive: 10];
    Socket.SetWaitTime[soc, 5000];  -- milli-seconds
    him ← Socket.BroadcastAddressFromSocket[
      NSConstants.timeServerSocket];

    FOR i: CARDINAL IN [0..10) UNTIL hit DO
      b ← Socket.GetSendBuffer[soc];
      Socket.SetDestination[b, him];
      b.ns.packetType ← echo;
      b.ns.nsWords[0] ← b.ns.nsWords[1] ← i;
      b.ns.nsWords[2] ← TimeServerFormatOld.request;
      Socket.SetPacketWords[b, 3];
      Socket.BroadcastPacketToAllConnectedNets[soc, b];
      DO
        b ← Socket.GetPacket[
          soc ! Socket.TimeOut => BEGIN IF ~hit THEN Put.Char[log, '?]; EXIT; END];
        SELECT TRUE FROM
          Socket.GetPacketBytes[b] # 2*(3 + SIZE[TimeServerFormatOld.TimeFormat])
            OR (b.ns.nsWords[2] # TimeServerFormatOld.response) =>
            BEGIN Put.Char[log, '#]; END;
          ENDCASE =>
            BEGIN  -- the response we were looking for
            PrintOldAnswer[LOOPHOLE[@b.ns.nsWords[3]], Socket.GetSource[b]];
            hit ← TRUE;
            END;
        Socket.ReturnBuffer[b];
        ENDLOOP;
      ENDLOOP;
    Put.CR[log];
    Socket.Delete[soc];
    MsgSW.Post[msg, "OK"L];
    END;

  OldAskRemote: FormSW.ProcType =
    BEGIN
    him: System.NetworkAddress;
    soc: Socket.ChannelHandle;
    b: Buffer.NSBuffer;
    errFlag, hit: BOOLEAN ← FALSE;

    MsgSW.Post[msg, ""L];
    him ← GetAddress[remoteAddress, NSConstants.timeServerSocket !
      Trouble =>
        BEGIN
        Put.Text[msg, "AddressTranslation troubles: "L];
	Put.Line[msg, reason];
        errFlag ← TRUE;
        CONTINUE;
        END];
    IF errFlag THEN RETURN;
    MsgSW.Post[msg, "Here we go...."L];
    Put.CR[log];
    Put.Line[log, "Old Ask Remote.."L];
    soc ← Socket.Create[local: localAddr, receive: 10];
    Socket.SetWaitTime[soc, 5000];  -- milli-seconds

    FOR i: CARDINAL IN [0..10) UNTIL hit DO
      b ← Socket.GetSendBuffer[soc];
      Socket.SetDestination[b, him];
      b.ns.packetType ← echo;
      b.ns.nsWords[0] ← b.ns.nsWords[1] ← i;
      b.ns.nsWords[2] ← TimeServerFormatOld.request;
      Socket.SetPacketWords[b, 3];
      Socket.PutPacket[soc, b];
      DO
        b ← Socket.GetPacket[
          soc ! Socket.TimeOut => BEGIN IF ~hit THEN Put.Char[log, '?]; EXIT; END];
        SELECT TRUE FROM
          Socket.GetPacketBytes[b] # 2*(3 + SIZE[TimeServerFormatOld.TimeFormat])
            OR (b.ns.nsWords[2] # TimeServerFormatOld.response) =>
            BEGIN Put.Char[log, '#]; END;
          ENDCASE =>
            BEGIN  -- the response we were looking for
            hit ← TRUE;
            PrintOldAnswer[LOOPHOLE[@b.ns.nsWords[3]], Socket.GetSource[b]];
            END;
        Socket.ReturnBuffer[b];
        ENDLOOP;
      ENDLOOP;
    Put.CR[log];
    Socket.Delete[soc];
    MsgSW.Post[msg, "OK"L];
    END;

  PrintStatistics: PROCEDURE [
    stats: LONG POINTER TO statisticResponse TimeServerFormat.TSPacket,
    who: System.NetworkAddress] =
    BEGIN
    when: Time.Packed ← LOOPHOLE[TimeServerFormat.WireToGMT[stats.timeSet]];
    IF who # System.nullNetworkAddress THEN
      BEGIN
      Put.Text[log, "Response from: "L];
      Put.NetworkAddress[log, who];
      Put.CR[log];
      END;
    Put.Text[log, "Requests: "L];
    Put.LongDecimal[log, TimeServerFormat.WireToLong[stats.numberRequests]];
    Put.Text[log, ", Active: "L];
    Put.Text[log, IF stats.active THEN "TRUE"L ELSE "FALSE"L];
    Put.Text[log, ", Resetting: "L];
    Put.Text[log, IF stats.resetting THEN "TRUE"L ELSE "FALSE"L];
    Put.CR[log];
    Put.Text[log, "Time Last Set: "L];
    Put.Date[log, when, full];
    Put.Text[log, ", Last change: "L];
    Put.LongDecimal[log, TimeServerFormat.WireToLong[stats.lastChange]];
    Put.CR[log];
    IF stats.source # System.nullNetworkAddress THEN
      BEGIN
      Put.Text[log, "Reset From: "L];
      Put.NetworkAddress[log, stats.source];
      Put.CR[log];
      END;
    END;
      
  PrintAnswer: PROCEDURE [
    wtf: LONG POINTER TO timeResponse TimeServerFormat.TSPacket,
    who: System.NetworkAddress,
    local: System.GreenwichMeanTime ← System.gmtEpoch,
    flight: LONG CARDINAL ← 0] =
    BEGIN
    remote: Time.Packed ← LOOPHOLE[TimeServerFormat.WireToGMT[wtf.time]];
    IF who # System.nullNetworkAddress THEN
      BEGIN
      Put.Text[log, "Response from: "L];
      Put.NetworkAddress[log, who];
      Put.CR[log];
      END;
    Put.Text[log, "Time: "L];
    Put.Date[log, remote, full];
    Put.Text[log, ", Zone: "L];
    Put.Text[
      log, SELECT wtf.zoneS FROM west => "W"L, east => "E"L, ENDCASE => ERROR];
    Put.Decimal[log, wtf.zoneH];
    Put.Char[log, ':];
    Put.Decimal[log, wtf.zoneM];
    Put.Text[log, ", DST: "L];
    Put.Decimal[log, wtf.beginDST];
    Put.Text[log, ", "L];
    Put.Decimal[log, wtf.endDST];
    Put.CR[log];
    IF local # System.gmtEpoch THEN
      BEGIN
      delta: LONG INTEGER = remote - local;
      Put.Text[log, "His clock is "L];
      Put.LongDecimal[log, delta];
      Put.Line[log, " seconds faster than ours."L];
      END;
    IF wtf.errorAccurate THEN
      BEGIN
      Put.Text[log, "Error is less than "L];
      Put.LongDecimal[log, TimeServerFormat.WireToLong[wtf.absoluteError]];
      Put.Line[log, " ms."L];
      END;
    IF flight # 0 THEN
      BEGIN
      Put.Text[log, "The flight time was "L];
      Put.LongDecimal[log, flight];
      Put.Line[log, " ms."L];
      END;
    END;
      
  PrintOldAnswer: PROCEDURE [
    wtf: LONG POINTER TO TimeServerFormatOld.TimeFormat,
    who: System.NetworkAddress] =
    BEGIN
    now: Time.Packed ← LOOPHOLE[WireFormat.WireToMesaLongNumber[wtf.time]];
    IF who # System.nullNetworkAddress THEN
      BEGIN
      Put.Text[log, "Response from: "L];
      Put.NetworkAddress[log, who];
      Put.CR[log];
      END;
    Put.Text[log, "Time: "L];
    Put.Date[log, now, full];
    Put.Text[log, ", Zone: "L];
    Put.Text[
      log, SELECT wtf.zoneS FROM west => "W"L, east => "E"L, ENDCASE => ERROR];
    Put.Decimal[log, wtf.zoneH];
    Put.Char[log, ':];
    Put.Decimal[log, wtf.zoneM];
    Put.Text[log, ", DST: "L];
    Put.Decimal[log, wtf.beginDST];
    Put.Text[log, ", "L];
    Put.Decimal[log, wtf.endDST];
    Put.CR[log];
    END;


  Trouble: ERROR [reason: LONG STRING] = CODE;
  GetAddress: PROCEDURE [host: LONG STRING, socket: System.SocketNumber]
    RETURNS [addr: System.NetworkAddress] =
    BEGIN
    localFailed: BOOLEAN ← FALSE;
    IF host = NIL THEN ERROR Trouble["NIL => Address Fault"L];
    addr ← Unformat.NetworkAddress[host, octal !
      Unformat.Error => BEGIN localFailed ← TRUE; CONTINUE; END ];
    IF localFailed THEN
      BEGIN
      addr ← AddressTranslation.StringToNetworkAddress[host !
        AddressTranslation.Error =>
	  BEGIN
	  temp: STRING = [200];
	  proc: Format.StringProc = {String.AppendString[temp, s]};
	  AddressTranslation.PrintError[errorRecord, proc];
	  ERROR Trouble[temp];
	  END].addr;
        addr.socket ← socket;  -- CH returns trash in socket
      END;
    IF addr.socket = System.nullSocketNumber THEN addr.socket ← socket;
    END;

  Init[];
  END...