// EFTPFile.bcpl
// June 17, 1976  10:57 PM  bk, jfs
// last modified on June 3, 1979  11:47 AM by Taft
// Copyright Xerox Corporation 1979

get "PupEFTP.decl"  // that will also get pup-related stuff

external
[
//incoming procedures
InitEFTPPackage; OpenEFTPSoc; CloseEFTPSoc
ReceiveEFTPBlock; SendEFTPBlock; SendEFTPEnd; GetEFTPAbort
Endofs; ReadBlock; WriteBlock; Puts; FilePos
Ws; Wo; Wns; Wl; Zero

// incoming statics, for common use, space allocated elsewhere
EFTPLclPort; EFTPFrnPort; EFTPSocket; dsp

//outgoing procedures
EFTPSendFile; EFTPReceiveFile
]

static
[
// for use in this program, allocated in this module
bytecount; totalbytecount
packetcount; blockcount
]

// These routines will be called from the FrontEnd, and may be
// called more than once in the life of a program

//-----------------------------------------------------------------
let EFTPSendFile(diskstream, foreignport) = valof
//-----------------------------------------------------------------
[
OpenEFTPSoc(EFTPSocket, 0, foreignport)
packetcount = 0
totalbytecount = 0

let maxBufferBytes = 2*256*6   //enough to buffer 6 disk pages
let tempvec = vec 256*6      //in words
let bytesLeftInBuffer = 0
let nextAddr = tempvec

let result = nil

// A loop, to read pages and send off blocks
   [Sendloop
   //See if we need more input
   if bytesLeftInBuffer eq 0 then 
      [
      //See if  time to quit
      if  Endofs(diskstream) then break
      bytesLeftInBuffer =
      FillFromDisk(diskstream, tempvec, maxBufferBytes)
      totalbytecount = totalbytecount + bytesLeftInBuffer
      nextAddr = tempvec   //reset address pointer
      ]
   //data remains to be sent, at nextAddr
   packetcount = packetcount + 1
   test bytesLeftInBuffer ls 512
   ifso bytecount = bytesLeftInBuffer
   ifnot bytecount = 512

   [dummyLoop
   result = 
      SendEFTPBlock(EFTPSocket, nextAddr, bytecount, longBlockTimeout)
   if result eq EFTPTimeout & 
         EFTPSocket>>EFTPSoc.TransferNotStarted then
         [
         Wl("No answer yet, will keep trying...")
         loop
         ]
   break
   ]dummyLoop repeat

   //Something happened...
   if result ge 0 then 
      [
      bytesLeftInBuffer = bytesLeftInBuffer - bytecount
      nextAddr = nextAddr + bytecount/2
      loop   //it went properly
      ]

   //If we get here, had a real failure....
   switchon result into
      [resultCase
      case EFTPTimeout:
         [
         Ws("EFTPSend timed out while sending....")
         endcase
         ]
      case EFTPAbortSent:
         [
         Ws("EFTPAbort had to be sent....")
         endcase
         ]
      case EFTPAbortReceived:
         [
         Ws("EFTPAbort received while sending....")
         let temppbi = GetEFTPAbort(EFTPSocket)
         PrintAbortPBI(temppbi)
         endcase
         ]
      default:
         [
         Ws("Unknown error from EFTPSend....")
         ]
      ]resultCase
   CloseEFTPSoc(EFTPSocket)
   resultis false   //bail out
   ]Sendloop repeat   //keep going, til you break out

// All of the data has been sent; now do an end sequence
result = SendEFTPEnd(EFTPSocket, longBlockTimeout)
CloseEFTPSoc(EFTPSocket)
test result
   ifso [ Wns(dsp, totalbytecount, 0, 10); Ws(" bytes sent.  ") ]
   ifnot Ws("SendEnd sequence failed.....")
resultis result
]


//---------------------------------------------------------------
and FillFromDisk(diskstream, addr, maxbytes) = valof
//---------------------------------------------------------------
[
//Returns the number of bytes read into the buffer
// Watch out, Readblock returns a word count
let bytecount =
 ReadBlock(diskstream, addr, maxbytes rshift 1) lshift 1 
if bytecount eq 0 then resultis 0 // hit eof, quit early
if bytecount < maxbytes then
   [
   // got the last page; we have to do some hacking here
   let filepos = vec 1
   FilePos(diskstream, filepos)
   //see if the files total byte count is odd
   if ((filepos!1) & 1) eq 1 then bytecount=bytecount-1
   ]
resultis bytecount
]

//---------------------------------------------------------------
and DumpToDisk(diskstream, addr, bytecount) = valof
//---------------------------------------------------------------
[
// Watch out for an odd bytecount
WriteBlock(diskstream, addr, bytecount rshift 1)
// May have failed to write one byte
if (bytecount & 1) eq 1 then Puts(diskstream, addr>>Byte↑bytecount)
]


//---------------------------------------------------------------
and EFTPReceiveFile(diskstream, foreignport) = valof
//---------------------------------------------------------------

[
//use EFTPLclPort, allocated elsewhere
Zero(EFTPLclPort, lenPort)
EFTPLclPort>>Port.socket↑2 = socketEFTPReceive

OpenEFTPSoc(EFTPSocket, EFTPLclPort, foreignport)
packetcount = 0
totalbytecount = 0

let maxBufferBytes = 2*256*6   //6 disk pages
let tempvec = vec 256*6   //in words
let bytesLeftInBuffer = 0
let nextAddr = tempvec

// a loop to get blocks, and write to the disk
   [Receiveloop
   //Make sure there is enough free space
   if maxBufferBytes-bytesLeftInBuffer ls 512 then
      [
      totalbytecount = totalbytecount + bytesLeftInBuffer
      DumpToDisk(diskstream, tempvec, bytesLeftInBuffer)
      nextAddr = tempvec
      bytesLeftInBuffer = 0
      ]
   let bytecount=
      ReceiveEFTPBlock(EFTPSocket, nextAddr, longBlockTimeout)
   if bytecount eq 0 then 
      [
      //Received an End....
      if bytesLeftInBuffer gr 0 then
         DumpToDisk(diskstream, tempvec, bytesLeftInBuffer)
      totalbytecount = totalbytecount + bytesLeftInBuffer
      break  // end has been properly received
      ]
   if bytecount gr 0 then
      [
      // odd bytecounts are not legal, except in the last packet
      bytesLeftInBuffer = bytesLeftInBuffer + bytecount
      nextAddr = nextAddr + bytecount rshift 1
      packetcount = packetcount + 1
      loop
      ]

   //Some form of error return, figure it out here and punt
   switchon bytecount into
      [ReceiveCaseBlock
      case EFTPTimeout:
         [
         if EFTPSocket>>EFTPSoc.TransferNotStarted then 
            [
            Wl("Nothing received yet, will keep listening...")
            loop
            ]
         Ws("Receiver timed out....")
         endcase
         ]
      case EFTPAbortSent:
         [
         Ws("Abort had to be sent from receiver....")
         endcase
         ]
      case EFTPAbortReceived:
         [
         Ws("EFTPAbort received while receiving....")
         let temppbi = GetEFTPAbort(EFTPSocket)
         PrintAbortPBI(temppbi)
         endcase
         ]
      case EFTPResetReceived:
         [
         Ws("Asked to reset....[Not implemented here]")
         endcase
         ]
      ]ReceiveCaseBlock
   CloseEFTPSoc(EFTPSocket)
   resultis false
   ]Receiveloop repeat

//End has been received, time to clean up and leave
CloseEFTPSoc(EFTPSocket)
Wns(dsp, totalbytecount, 0, 10); Ws(" bytes received. ")
resultis true
]

//---------------------------------------------------------------
and PrintAbortPBI(pbi) be
//---------------------------------------------------------------
[
Puts(dsp, $*N); Wo(pbi>>PBI.pup.words↑1); Ws(" - Abort: ")
for i = 3 to (3 + (pbi>>PBI.pup.length-(pupOvBytes+2)) - 1) do
   Puts(dsp, pbi>>PBI.pup.bytes↑i)
]