-- Copyright (C) 1983, 1985  by Xerox Corporation. All rights reserved. 
-- VoiceForwardingNoDisk.mesa, WIrish, 24-Apr-87 11:55:52
-- From: PupBootServerNoDisk.mesa, HGM,  9-May-86 16:14:32

DIRECTORY
  Ascii USING [CR],
  CmFile USING [Handle, TableError],
  Event USING [aboutToSwap],
  EventTypes USING [aboutToBoot, aboutToBootPhysicalVolume],
  Heap USING [systemZone],
  Process USING [Yield],
  Put USING [Text],
  String USING [AppendChar, AppendNumber, AppendString],
  StringLookUp USING [noMatch],
  Supervisor USING [
    AddDependency, AgentProcedure, CreateSubsystem, RemoveDependency,
    SubsystemHandle],
  Token USING [FreeTokenString, Item, Octal],

  Buffer USING [ReturnBuffer],
  Indirect USING [Close, NextValue, OpenSection],
  PupDefs USING [
    GetPupAddress,
    PupBuffer, PupNameTrouble, PupPackageMake, PupRouterSendThis,
    PupSocket, PupSocketDestroy, PupSocketKick, PupSocketMake, Tocks, veryLongWait],
  PupRouterDefs USING [NetworkContext],
  PupTypes USING [fillInPupAddress, PupAddress, PupNetID, rpcpSoc];

VoiceForwardingNoDisk: MONITOR
  IMPORTS
    CmFile, Event, Heap, Indirect, Process, Put,
    String, Supervisor, Token,
    Buffer, PupDefs
  EXPORTS =
  BEGIN

  broom: Supervisor.SubsystemHandle = Supervisor.CreateSubsystem[Broom];
  z: UNCOUNTED ZONE = Heap.systemZone;

  remote: Chain ← NIL;
  pleaseStop: BOOLEAN ← FALSE;
  
  Chain: TYPE = LONG POINTER TO ChainSlot;
  ChainSlot: TYPE = RECORD [
    next: Chain,
    source: PupTypes.PupNetID,
    dest: PupTypes.PupAddress,
    target: LONG STRING];


  VFRunning: PUBLIC BOOLEAN ← FALSE;
  useCount: CARDINAL ← 0;

  VoiceForwardingOn: PUBLIC ENTRY PROCEDURE =
    BEGIN
    IF (useCount ← useCount + 1) = 1 THEN
      BEGIN
      Supervisor.AddDependency[client: broom, implementor: Event.aboutToSwap];
      VFRunning ← TRUE;
      Starter[];
      END;
    END;

  Starter: PROCEDURE =
    BEGIN
    ScanParameterFile[];
    StartListener[];
    END;

  VoiceForwardingOff: PUBLIC ENTRY PROCEDURE =
    BEGIN
    IF useCount # 0 AND (useCount ← useCount - 1) = 0 THEN
      BEGIN
      VFRunning ← FALSE;
      Stopper[];
      Supervisor.RemoveDependency[client: broom, implementor: Event.aboutToSwap];
      END;
    END;

  Stopper: INTERNAL PROCEDURE =
    BEGIN
    StopListener[];
    ForgetParameters[];
    END;

  VoiceForwarding: PUBLIC PROCEDURE [b: PupDefs.PupBuffer] =
    BEGIN
    ForwardThisOne[b];
    END;

  ForwardThisOne: PROCEDURE [b: PupDefs.PupBuffer] =
    BEGIN
    context: PupRouterDefs.NetworkContext = b.context;
    IF b.pup.source.net = 0 THEN b.pup.source.net ← [context.pupNetNumber];
    FOR finger: Chain ← remote, finger.next UNTIL finger = NIL DO
      IF context.pupNetNumber = finger.source THEN
        BEGIN
	IF finger.dest = PupTypes.fillInPupAddress THEN EXIT;
	b.pup.dest.net ← finger.dest.net;
	b.pup.dest.host ← finger.dest.host;
	PupDefs.PupRouterSendThis[b];
	RETURN;
	END;
      ENDLOOP;
    Buffer.ReturnBuffer[b];
    END;

  voice60Soc: PupDefs.PupSocket ← NIL;
  rpcpSoc: PupDefs.PupSocket ← NIL;
  voice60Fork: PROCESS;
  rpcpFork: PROCESS;
  
  StartListener: PUBLIC PROCEDURE =
    BEGIN
    pleaseStop ← FALSE;
    [] ← PupDefs.PupPackageMake[];
    voice60Soc ← PupDefs.PupSocketMake[
      [0, 60B], PupTypes.fillInPupAddress, PupDefs.veryLongWait];
    voice60Fork ← FORK Voice[];
    rpcpSoc ← PupDefs.PupSocketMake[
      PupTypes.rpcpSoc, PupTypes.fillInPupAddress, PupDefs.veryLongWait];
    rpcpFork ← FORK Rpcp[];
    END;

  StopListener: PUBLIC PROCEDURE =
    BEGIN
    pleaseStop ← TRUE;
    PupDefs.PupSocketKick[voice60Soc];
    JOIN voice60Fork[];
    PupDefs.PupSocketDestroy[voice60Soc];
    PupDefs.PupSocketKick[rpcpSoc];
    JOIN rpcpFork[];
    PupDefs.PupSocketDestroy[rpcpSoc];
    END;

  Voice: PROCEDURE =
    BEGIN
    b: PupDefs.PupBuffer;
    UNTIL pleaseStop DO
      IF (b ← voice60Soc.get[]) # NIL THEN VoiceForwarding[b];
      Process.Yield[];  -- avoid hogging machine
      ENDLOOP;
    END;

  Rpcp: PROCEDURE =
    BEGIN
    b: PupDefs.PupBuffer;
    UNTIL pleaseStop DO
      IF (b ← rpcpSoc.get[]) # NIL THEN VoiceForwarding[b];
      Process.Yield[];  -- avoid hogging machine
      ENDLOOP;
    END;

  ScanParameterFile: PROCEDURE =
    BEGIN
    cmFile: CmFile.Handle;
    Option: TYPE = {remote};
    NextValue: PROCEDURE [
      h: CmFile.Handle, table: LONG DESCRIPTOR FOR ARRAY Option OF LONG STRING]
      RETURNS [Option] = LOOPHOLE[Indirect.NextValue];
    optionTable: ARRAY Option OF LONG STRING ← [remote: "Remote"L];
    cmFile ← Indirect.OpenSection["VoiceForwarding"L];
    IF cmFile = NIL THEN RETURN;
    DO
      option: Option;
      option ← NextValue[
        cmFile, DESCRIPTOR[optionTable] !
        CmFile.TableError =>
          BEGIN
	  IF name[0] # '; THEN Message["Unrecognized parameter: ", name];
	  RETRY;
	  END];
      SELECT option FROM
        LOOPHOLE[StringLookUp.noMatch] => EXIT;
        remote =>
	  BEGIN
	  source: PupTypes.PupNetID = [Token.Octal[cmFile]];
	  temp: LONG STRING ← Token.Item[cmFile, FALSE];
	  new: Chain ← z.NEW[ChainSlot];
	  new↑ ← [NIL, source, PupTypes.fillInPupAddress, z.NEW[StringBody[temp.length]]];
	  String.AppendString[new.target, temp];
	  [] ← Token.FreeTokenString[temp];
	  PupDefs.GetPupAddress[@new.dest, new.target ! PupDefs.PupNameTrouble => CONTINUE];
	  IF remote = NIL THEN remote ← new
	  ELSE
	    BEGIN
	    FOR finger: Chain ← remote, finger.next DO
	      IF finger.next = NIL THEN
	        BEGIN
		finger.next ← new;
		EXIT;
		END;
	      ENDLOOP;
	    END;
          MessageNet["Forwarding Voice requests from net "L, new.source, " to "L, new.target];
	  END;
        ENDCASE => ERROR;
      ENDLOOP;
    Indirect.Close[cmFile];
    END;

  ForgetParameters: PROCEDURE =
    BEGIN
    UNTIL remote = NIL DO
      temp: Chain ← remote;
      remote ← remote.next;
      z.FREE[@temp.target];
      z.FREE[@temp];
      ENDLOOP;
    END;

  MessageNet: PROCEDURE [one: LONG STRING, net: CARDINAL, three, four: LONG STRING] =
    BEGIN
    two: STRING = [20];
    String.AppendNumber[two, net, 8];
    Message[one, two, three, four];
    END;

  Message: PROCEDURE [one, two, three, four: LONG STRING ← NIL] =
    BEGIN
    text: STRING = [200];
    String.AppendString[text, one];
    IF two # NIL THEN String.AppendString[text, two];
    IF three # NIL THEN String.AppendString[text, three];
    IF four # NIL THEN String.AppendString[text, four];
    LogString[text];
    END;

  LogString: PROCEDURE [text: LONG STRING] =
    BEGIN
    String.AppendChar[text, '.];
    String.AppendChar[text, Ascii.CR];
    Put.Text[NIL, text];
    END;

  Broom: ENTRY Supervisor.AgentProcedure =
    BEGIN
    SELECT event FROM
      EventTypes.aboutToBoot, EventTypes.aboutToBootPhysicalVolume =>
        IF VFRunning THEN Stopper[];
      ENDCASE => NULL;
    END;

  -- initialization
  VoiceForwardingOn[];
  END.