-- Copyright (C) 1983  by Xerox Corporation. All rights reserved. 
-- RemoteTTY.mesa, HGM, 21-Dec-83  3:29:52

DIRECTORY
  Ascii USING [CR, BEL],
  Process USING [Detach, EnableAborts, MsecToTicks, SetTimeout],
  PupStream USING [RejectThisRequest],
  Stream USING [
    defaultObject, Delete, DeleteProcedure, GetBlock, GetProcedure, 
    Handle, Object, PutByte, PutChar, PutProcedure, PutString,
    SendAttention, SendAttentionProcedure, SetSSTProcedure,
    WaitAttentionProcedure, WaitForAttention],
  String USING [AppendString],
  TelnetStream USING [
    AppendHostName, ConnectProc, FilterProc,  Listen],
  TTY USING [Handle],
  TTYConstants USING [aborted, blinkDisplay];

RemoteTTY: MONITOR
  IMPORTS Process, PupStream, Stream, String, TelnetStream
  EXPORTS TTY =
  BEGIN
  
  first: BOOLEAN ← FALSE;
  local: Stream.Object ← Stream.defaultObject;
  remote: Stream.Handle ← NIL;
  waitForInput: CONDITION;
  
  CreateTTYInstance: PUBLIC ENTRY PROCEDURE [
    name: LONG STRING, backingStream: Stream.Handle, tty: TTY.Handle] 
    RETURNS [ttyImpl, backing: Stream.Handle] = 
    BEGIN ENABLE UNWIND => NULL;
    IF ~first THEN
      BEGIN
      FOR i: CARDINAL IN [0..2) UNTIL remote # NIL DO WAIT waitForInput; ENDLOOP;
      RETURN[@local, NIL];  -- might be phoney
      END;
    UNTIL remote # NIL DO WAIT waitForInput; ENDLOOP;
    RETURN[@local, NIL];
    END;
  
  Delete: PUBLIC ENTRY Stream.DeleteProcedure =
    BEGIN ENABLE UNWIND => NULL;
    IF first THEN
      BEGIN
      IF remote # NIL THEN Stream.Delete[remote];
      remote ← NIL;
      END;
    first ← TRUE;
    END;
  
  PutBlock: ENTRY Stream.PutProcedure =
    BEGIN ENABLE UNWIND => NULL;
    IF remote = NIL THEN RETURN;
    FOR i: CARDINAL IN [block.startIndex..block.stopIndexPlusOne) DO
      Stream.PutByte[remote, block.blockPointer[i] ! ABORTED => EXIT];
      ENDLOOP;
    END;
    
  GetBlock: Stream.GetProcedure =
    BEGIN
    IF remote = NIL THEN WaitForConnection[];  -- phoney connection
    [bytesTransferred, why, sst] ← Stream.GetBlock[remote, block];
    END;
  
  WaitForConnection: ENTRY PROCEDURE =
    BEGIN ENABLE UNWIND => NULL;
    UNTIL remote # NIL DO WAIT waitForInput; ENDLOOP;
    END;
  
  SetSST: Stream.SetSSTProcedure =
    BEGIN
    IF sst = TTYConstants.blinkDisplay THEN Stream.PutChar[@local, Ascii.BEL];
    END;
  
  SendAttention: ENTRY Stream.SendAttentionProcedure =
    BEGIN ENABLE UNWIND => NULL;
    IF remote # NIL THEN Stream.SendAttention[remote, byte];
    END;
  
  WaitForAttention: ENTRY Stream.WaitAttentionProcedure =
    BEGIN ENABLE UNWIND => NULL;
    IF remote = NIL THEN RETURN[TTYConstants.aborted];
    RETURN[Stream.WaitForAttention[remote]];
    END;
  
  Connect: ENTRY TelnetStream.ConnectProc = 
    BEGIN ENABLE UNWIND => NULL;
    IF remote = NIL THEN
      BEGIN
      remote ← sH;
      BROADCAST waitForInput;
      END;
    IF remote # sH THEN
      BEGIN  -- Maybe tell him what's going on....
      him: STRING = [100];
      Stream.PutString[sH, "Sorry, talking to "L ! ABORTED => CONTINUE];
      TelnetStream.AppendHostName[sH, him ! ABORTED => CONTINUE];
      Stream.PutString[sH, him ! ABORTED => CONTINUE];
      Stream.PutChar[sH, Ascii.CR ! ABORTED => CONTINUE];
      Stream.Delete[sH];
      END;
    END;
  
  Filter: ENTRY TelnetStream.FilterProc =
    BEGIN ENABLE UNWIND => NULL;
    IF remote # NIL THEN
      BEGIN
      temp: STRING = [100];
      String.AppendString[temp, "Sorry, talking to "L];
      TelnetStream.AppendHostName[remote, temp ! ABORTED => CONTINUE];
      ERROR PupStream.RejectThisRequest[temp];
      END;
    END;

  -- Initialization
  local.put ← PutBlock;
  local.get ← GetBlock;
  local.setSST ← SetSST;
  local.sendAttention ← SendAttention;
  local.waitAttention ← WaitForAttention;
  local.delete ← Delete;
  Process.EnableAborts[@waitForInput];
  Process.SetTimeout[@waitForInput, Process.MsecToTicks[10000]];
  Process.Detach[FORK TelnetStream.Listen[Connect, Filter]];  -- Don't quite hang on 981
  END...