-- XConnectionMgrImpl.mesa
-- Created by Jeff Weinstein on  3-May-87  0:49:16

DIRECTORY
  ArpaRouter,
  XCRoutines,
  Environment,
  Heap,
  Inline,
  Process,
  TcpStream,
  XConnection,
  XConnectionMgr,
  XEventQ,
  XQueue;
  
XConnectionMgrImpl:PROGRAM IMPORTS Heap, Inline, Process, TcpStream, XConnection, XCRoutines, XEventQ EXPORTS XConnectionMgr =
  BEGIN
  
  xConnClientPrefix:TYPE = MACHINE DEPENDENT RECORD [
                                               byteOrder(0:0..7):[0..256),
					       pad(0:8..15):[0..256),
					       majorVersion(1):CARDINAL,
					       minorVersion(2):CARDINAL,
					       nbytesAuthProto(3):CARDINAL,
					       nbytesAuthString(4):CARDINAL,
					       pad2(5):CARDINAL];
  xReq:TYPE = MACHINE DEPENDENT RECORD [
                                  reqType:[0..256),
				  data:[0..256),
				  length:CARDINAL
				  ];
  
  xTcpPortNumber:PUBLIC ArpaRouter.Port = LOOPHOLE[6000];
  
  GrabFlag:BOOLEAN ← FALSE;
  GrabClient:XConnection.Client;
  Error:LONG POINTER TO UNSPECIFIED = LOOPHOLE[LONG[-1]];
  
  zone:UNCOUNTED ZONE ← Heap.Create[initial:16];
  
  whichByteIsFirst:CHARACTER = 'l;
  
  clientArray:ARRAY [0..XConnection.MaxClients) OF LONG POINTER;
  
  ReadRequestFromClient:PUBLIC PROCEDURE[who:LONG CARDINAL, status:LONG POINTER TO INTEGER, oldbuf:LONG POINTER TO UNSPECIFIED] RETURNS [request:LONG POINTER TO UNSPECIFIED] =
    BEGIN
      Heap.FreeNode[zone,oldbuf];
      [request, status↑] ← XConnection.DeQueueRequest[who!
                             XConnection.QueueEmpty =>
			       BEGIN
			       request ← NIL;
			       status↑ ← 0;
			       CONTINUE;
			       END;
			     XConnection.ConnectionNotOpen =>
			       BEGIN
			       request ← NIL;
			       status↑ ← -1;
			       CONTINUE;
			       END;
			       ];
    END;
    
  WriteToClient:PUBLIC PROCEDURE[who:LONG CARDINAL, remaining:INTEGER, buf:LONG POINTER TO UNSPECIFIED] RETURNS [status:INTEGER] = 
    BEGIN
      status ← remaining;
      XConnection.SendToClient[who, [buf, 0, remaining]!
        XConnection.ConnectionNotOpen =>
	  BEGIN
	  status ← -1;
	  CONTINUE;
	  END;
	  ];
    END;
    
  OnlyListenToOneClient:PUBLIC PROCEDURE[who:LONG CARDINAL] = 
    BEGIN
    IF GrabFlag = FALSE THEN
      BEGIN
      GrabFlag ← TRUE;
      GrabClient ← who;
      END;
    END;
    
  ListenToAllClients:PUBLIC PROCEDURE =
    BEGIN
    GrabFlag ← FALSE;
    END;
    
  CloseDownConnection:PUBLIC PROCEDURE[who:LONG CARDINAL] =
    BEGIN
    XConnection.ClosingConnection[who];
    XConnection.DestroyConnection[who];
    END;
    
  ReallyMarkConnectionClosed: PUBLIC PROCEDURE[who:LONG CARDINAL] =
    BEGIN
--   XConnection.DestroyConnection[who];
    END;
    
  WaitForSomething:PUBLIC PROCEDURE[pClientsReady:LONG POINTER TO ARRAY[0..0) OF XConnectionMgr.ClientPtr, nready:LONG POINTER TO INTEGER, pNewClients:LONG POINTER TO ARRAY[0..0) OF XConnectionMgr.ClientPtr, nnew:LONG POINTER TO INTEGER] =
    BEGIN
    Looping:BOOLEAN ← TRUE;
    readyClients:ARRAY [0..XConnection.MaxClients) OF BOOLEAN;
    nready↑ ← 0;
    nnew↑ ← 0;
    WHILE Looping DO
      XConnection.WaitForEventOrClient[];
      [clients:readyClients] ← XConnection.PendingRequests[];
      FOR i:INTEGER IN [0..XConnection.MaxClients) DO
	IF readyClients[i] THEN
	  BEGIN
	  Looping ← FALSE;
	  pClientsReady[nready↑] ← clientArray[i];
	  nready↑ ← nready↑ + 1;
	  END;
	ENDLOOP;
      IF XEventQ.EventCount[] # 0 THEN
	Looping ← FALSE;
      CreateNewConnections[pNewClients,nnew];
      IF nnew↑ > 0 THEN
        Looping ← FALSE;
      ENDLOOP;
    END;
    
  CreateNewConnections:PROCEDURE [pNewClients:LONG POINTER TO ARRAY[0..0) OF XConnectionMgr.ClientPtr, nnew:LONG POINTER TO INTEGER] =
    BEGIN
      connection:TcpStream.Handle;
      client:XConnectionMgr.ClientPtr;
      swapped:BOOLEAN;
      p:PROCESS;
      DO
        connection ← XConnection.DeQueueConnection[!XConnection.QueueEmpty => EXIT;];
	swapped ← CheckSwapped[connection];
	client ← NextAvailableClientID[];
	clientArray[client.index] ← client;
	XConnection.CreateConnection[client.index, connection];
	p ← FORK ClientListener[client.index, connection, swapped];
	pNewClients[nnew↑] ← client;
	IF swapped THEN
	  client.swapped ← 1
	ELSE
	  client.swapped ← 0;
	nnew↑ ← nnew↑ + 1;
      ENDLOOP;
    END;
  
  CheckSwapped:PROCEDURE[h:TcpStream.Handle] RETURNS [swapped:BOOLEAN] =
    BEGIN
    dummy:Environment.Byte;
    prefix:xConnClientPrefix;
    n:CARDINAL;
    block:Environment.Block;
    block ← [LOOPHOLE[LONG[@prefix]], 0, 12];
    WHILE block.startIndex # block.stopIndexPlusOne DO
      [byteCount:n] ← h.get[block];
      block.startIndex ← block.startIndex + n;
      ENDLOOP;
    IF prefix.byteOrder # LOOPHOLE[whichByteIsFirst] THEN
      swapped ← TRUE
    ELSE
      swapped ← FALSE;
    IF NOT swapped THEN
      n ← Inline.BITSHIFT[Inline.LowByte[prefix.nbytesAuthString],8] + Inline.HighByte[prefix.nbytesAuthString]
    ELSE
      n ← prefix.nbytesAuthString;
    IF NOT swapped THEN
      n ← n + Inline.BITSHIFT[Inline.LowByte[prefix.nbytesAuthProto],8] + Inline.HighByte[prefix.nbytesAuthProto]
    ELSE
      n ← n + prefix.nbytesAuthProto;

    FOR i:CARDINAL IN [0..n) DO
      [] ← h.get[[LOOPHOLE[LONG[@dummy]],0,1]];
      ENDLOOP;
    END;
    
  NextAvailableClientID:PROCEDURE RETURNS[client:XConnectionMgr.ClientPtr] =
    BEGIN
    client ← XCRoutines.NextAvailableClient[];
    END;
    
  ClientListener:PROCEDURE[client:LONG CARDINAL, h:TcpStream.Handle, swapped:BOOLEAN] =
    BEGIN
    req:xReq;
    len:CARDINAL;
    data:XQueue.Data;
    n:CARDINAL;
    status:TcpStream.CompletionCode;
    Process.Detach[Process.GetCurrent[]];
    DO
      block:Environment.Block;
      block ← [LOOPHOLE[LONG[@req],LONG POINTER TO UNSPECIFIED],0,SIZE[xReq]*2];
      WHILE block.startIndex # block.stopIndexPlusOne DO
        [n,status] ← h.get[block!
	                     TcpStream.Suspended => GOTO closing;
			     TcpStream.Closed => GOTO closing;
			     ];
	IF status = closing THEN
	  GOTO closing;
	block.startIndex ← block.startIndex + n;
	ENDLOOP;
--      IF swapped THEN
--        len ← Inline.BITSHIFT[Inline.LowByte[req.length],8] + Inline.HighByte[req.length]
--      ELSE
        len ← req.length * 2;
--     len ← ( len * 2 ) - ( SIZE[xReq] );
      data ← Heap.MakeNode[zone,len];
      Inline.LongCOPY[from:@req,nwords:SIZE[xReq],to:data];
      block ← [data,SIZE[xReq]*2,len*2];
      WHILE block.startIndex # block.stopIndexPlusOne DO
        [n,status] ← h.get[block!
	                     TcpStream.Suspended => GOTO closing;
			     TcpStream.Closed => GOTO closing;
			     ];
	IF status = closing THEN
	  GOTO closing;
	block.startIndex ← block.startIndex + n;
	ENDLOOP;
      XConnection.EnQueueRequest[client,data,len*2];
    ENDLOOP;
    EXITS
      closing =>
        BEGIN 
          XConnection.ClosingConnection[client]; 
	END;
    END;
  
  CreateWellKnownSockets:PUBLIC PROCEDURE =
    BEGIN
    p:PROCESS;
    p ← FORK ConnectionListener[];
    END;
        
  ConnectionListener:PROCEDURE =
    BEGIN
    h:TcpStream.Handle;
    DO
      h ← TcpStream.Listen[localPort:xTcpPortNumber, listenTimeout:LAST[LONG CARDINAL]];
      XConnection.EnQueueConnection[h];
      ENDLOOP;
    END;
    
  END...