-- FTPPupComHot.mesa, Edited by: HGM July 31, 1980  5:50 PM  

-- Copyright  Xerox Corporation 1979, 1980

DIRECTORY
  FTPDefs,
  FTPPrivateDefs,
  FTPPupComDefs USING [PupConnection, AbortBecauseStreamClosing],
  PupStream USING [CloseReason, StreamClosing],
  Stream USING [
    Block, CompletionCode, defaultInputOptions, GetBlock, GetByte, InputOptions,
    PutBlock, PutByte, SendNow, SetInputOptions, SetSST, SSTChange, TimeOut];

FTPPupComHot: PROGRAM
  IMPORTS PupStream, Stream, FTPPrivateDefs, FTPPupComDefs
  EXPORTS FTPPupComDefs
  SHARES FTPDefs =
  BEGIN OPEN FTPDefs, FTPPrivateDefs;


  -- **********************!  Constants  !***********************

  ftpsystem: POINTER TO FTPSystem = LocateFtpSystemObject[];



  SendBytes: PUBLIC PROCEDURE [
    communicationSystem: CommunicationSystem, connection: Connection,
    bytePointer: BytePointer] =
    BEGIN
    -- catchphrase
    ENABLE
      BEGIN
      PupStream.StreamClosing =>
        --FTPPupComDefs.-- MyAbortBecauseStreamClosing[why, text];
      Stream.TimeOut => Abort[connectionTimedOut];
      END;
    -- local constants
    pupConnection: FTPPupComDefs.PupConnection = LOOPHOLE[connection];
    startIndex: CARDINAL = IF bytePointer.offset THEN 1 ELSE 0;
    -- local variables
    block: Stream.Block = [
      blockPointer: LONG[bytePointer.address], startIndex: startIndex,
      stopIndexPlusOne: startIndex + bytePointer.count];
    -- abort if output discontinuity
    IF pupConnection.outputDiscontinuity THEN
      Abort[outputDiscontinuityUnexpected];
    -- send bytes
    Stream.PutBlock[pupConnection.streamHandle, block, FALSE];
    -- advance caller's byte pointer
    AdvanceBytePointer[bytePointer, bytePointer.count];
    END;

  ReceiveBytes: PUBLIC PROCEDURE [
    communicationSystem: CommunicationSystem, connection: Connection,
    bytePointer: BytePointer, settleForLess: BOOLEAN] =
    BEGIN
    -- catchphrase
    ENABLE
      BEGIN
      PupStream.StreamClosing =>
        --FTPPupComDefs.-- MyAbortBecauseStreamClosing[why, text];
      Stream.TimeOut => Abort[connectionTimedOut];
      END;
    -- local constants
    pupConnection: FTPPupComDefs.PupConnection = LOOPHOLE[connection];
    startIndex: CARDINAL = IF bytePointer.offset THEN 1 ELSE 0;
    -- local variables
    inputOptions: Stream.InputOptions ← Stream.defaultInputOptions;
    block: Stream.Block = [
      blockPointer: LONG[bytePointer.address], startIndex: startIndex,
      stopIndexPlusOne: startIndex + bytePointer.count];
    bytesTransferred: CARDINAL ← 0;
    completionCode: Stream.CompletionCode ← normal;
    -- return if no operation
    IF bytePointer.count = 0 THEN RETURN;
    -- abort if input discontinuity
    IF pupConnection.inputDiscontinuity
      OR pupConnection.inputDiscontinuityConsumed THEN
      Abort[inputDiscontinuityUnexpected];
    -- adjust input options if necessary
    IF settleForLess # pupConnection.terminateOnEndPhysicalRecord THEN
      BEGIN
      -- select and record new input option
      inputOptions.terminateOnEndRecord ←
        pupConnection.terminateOnEndPhysicalRecord ← settleForLess;
      -- instate new input option
      Stream.SetInputOptions[pupConnection.streamHandle, inputOptions];
      END;
    -- receive bytes
    [bytesTransferred, completionCode, pupConnection.mark] ← Stream.GetBlock[
      pupConnection.streamHandle, block];
    -- note input discontinuity if any
    pupConnection.inputDiscontinuity ← completionCode = sstChange;
    -- advance caller's byte pointer
    AdvanceBytePointer[bytePointer, bytesTransferred];
    END;

  SendByte: PUBLIC PROCEDURE [
    communicationSystem: CommunicationSystem, connection: Connection,
    byte: Byte] =
    BEGIN
    -- catchphrase
    ENABLE
      BEGIN
      PupStream.StreamClosing =>
        --FTPPupComDefs.-- MyAbortBecauseStreamClosing[why, text];
      Stream.TimeOut => Abort[connectionTimedOut];
      END;
    -- local constants
    pupConnection: FTPPupComDefs.PupConnection = LOOPHOLE[connection];
    -- send mark
    IF pupConnection.outputDiscontinuity THEN
      BEGIN
      pupConnection.outputDiscontinuity ← FALSE;
      Stream.SetSST[pupConnection.streamHandle, byte];
      END
      -- send byte

    ELSE Stream.PutByte[pupConnection.streamHandle, byte];
    END;

  ReceiveByte: PUBLIC PROCEDURE [
    communicationSystem: CommunicationSystem, connection: Connection,
    settleForNone: BOOLEAN] RETURNS [byte: Byte, settledForNone: BOOLEAN] =
    BEGIN
    -- catchphrase
    ENABLE
      BEGIN
      PupStream.StreamClosing =>
        --FTPPupComDefs.-- MyAbortBecauseStreamClosing[why, text];
      Stream.TimeOut => Abort[connectionTimedOut];
      END;
    -- local constants
    pupConnection: FTPPupComDefs.PupConnection = LOOPHOLE[connection];
    -- settle for none or abort if input discontinuity
    IF pupConnection.inputDiscontinuity THEN
      IF settleForNone THEN RETURN[byte, TRUE]
      ELSE Abort[inputDiscontinuityUnexpected];
    -- assume won't have to settle for none
    settledForNone ← FALSE;
    -- return mark
    IF pupConnection.inputDiscontinuityConsumed THEN
      BEGIN
      pupConnection.inputDiscontinuityConsumed ← FALSE;
      byte ← pupConnection.mark;
      END
      -- receive byte

    ELSE
      byte ← Stream.GetByte[
        pupConnection.streamHandle !
        Stream.SSTChange =>
          IF settleForNone THEN
            BEGIN
            pupConnection.inputDiscontinuity ← TRUE;
            pupConnection.mark ← sst;
            settledForNone ← TRUE;
            CONTINUE;
            END
          ELSE Abort[inputDiscontinuityUnexpected]];
    END;

  ProduceDiscontinuity: PUBLIC PROCEDURE [
    communicationSystem: CommunicationSystem, connection: Connection] =
    BEGIN
    -- Note:  Extra calls to this procedure without intervening data
    --   are treated as no operations. 
    -- local constants
    pupConnection: FTPPupComDefs.PupConnection = LOOPHOLE[connection];
    -- produce output discontinuity
    pupConnection.outputDiscontinuity ← TRUE;
    END;

  ConsumeDiscontinuity: PUBLIC PROCEDURE [
    communicationSystem: CommunicationSystem, connection: Connection] =
    BEGIN
    -- Note:  Extra calls to this procedure without intervening data
    --   are treated as no operations. 
    -- local constants
    pupConnection: FTPPupComDefs.PupConnection = LOOPHOLE[connection];
    -- consume input discontinuity
    IF pupConnection.inputDiscontinuity THEN
      BEGIN
      pupConnection.inputDiscontinuity ← FALSE;
      pupConnection.inputDiscontinuityConsumed ← TRUE;
      END;
    END;

  ForceOutput: PUBLIC PROCEDURE [
    communicationSystem: CommunicationSystem, connection: Connection] =
    BEGIN
    -- catchphrase
    ENABLE
      BEGIN
      PupStream.StreamClosing =>
        --FTPPupComDefs.-- MyAbortBecauseStreamClosing[why, text];
      Stream.TimeOut => Abort[connectionTimedOut];
      END;
    -- local constants
    pupConnection: FTPPupComDefs.PupConnection = LOOPHOLE[connection];
    -- force output
    Stream.SendNow[pupConnection.streamHandle];
    END;



  MyAbortBecauseStreamClosing: PROC [
    closeReason: PupStream.CloseReason, message: LONG STRING] = {
    string: STRING ← [40];
    IF message # NIL THEN {
      string.length ← MIN[string.maxlength, message.length];
      FOR i: CARDINAL IN [0..string.length) DO string[i] ← message[i] ENDLOOP}
    ELSE string ← NIL;
    FTPPupComDefs.AbortBecauseStreamClosing[closeReason, string]};

  -- **********************!  Main Program  !***********************

  -- no operation

  END. -- of FTPPupComHot