-- File: SloshRecv.mesa,  Last Edit: HGM  February 4, 1981  12:49 PM

DIRECTORY
  Put USING [Line],
  String USING [AppendChar, AppendString, AppendDecimal],
  Storage USING [Node, Free],
  Time USING [AppendCurrent],
  Window USING [Handle],

  File USING [Capability, nullCapability],
  George USING [
    CountFreeDiskPages, CreateOutputStream, CreateNewFile, DeleteFileFromDisk,
    Destroy, DiskFull, Handle, NameToCapability, PutByte, PutWords],
  Lock USING [LockDiskAndWait, UnlockDisk],
  Slosh USING [
    Arrived, Check, CopyFile, Failed, RecvStatus, RejectThisTrash, Release],
  EFTPDefs USING [
    EFTPOpenForReceiving, EFTPAbortReceiving, EFTPGetBlock, EFTPFinishReceiving,
    EFTPEndReceiving, EFTPTimeOut, EFTPTroubleReceiving, EFTPSetRecvTimeout],
  PupTypes USING [PupAddress];

SloshRecv: MONITOR
  IMPORTS Put, String, Storage, Time, George, Lock, Slosh, EFTPDefs EXPORTS Slosh
  =
  BEGIN

  verbose: BOOLEAN = TRUE;

  -- Since disk space seems tight, this routine checks to see if the file already exists.  If not, it writes it into the new file directly.  If it does exist, the file is buffered into a buffer file to be sure we have it all before we clobber the old version.

  RecvFile: PUBLIC ENTRY PROCEDURE [
    who: Window.Handle, fileName, scratch: STRING, file: File.Capability,
    me: PupTypes.PupAddress, ask: PROCEDURE] RETURNS [status: Slosh.RecvStatus] =
    BEGIN OPEN EFTPDefs;
    FixThingsUp: PROCEDURE [e: STRING, why: Slosh.RecvStatus] =
      BEGIN
      message ← e;
      EFTPAbortReceiving[message];
      IF temp # NIL THEN George.Destroy[temp];
      IF new THEN George.DeleteFileFromDisk[file];
      -- delete the file that is half created
      status ← why;
      END;
    copy: File.Capability;
    new: BOOLEAN = file = File.nullCapability;
    pagesLeft: INTEGER;
    temp: George.Handle;
    buffer: POINTER TO PACKED ARRAY [0..512) OF [0..377B];
    pages, bytes: CARDINAL ← 0;
    message: STRING ← NIL;
    trouble: STRING = [100];
    Lock.LockDiskAndWait[fileName, write];
    EFTPSetRecvTimeout[1000];
    -- open the file before asking so booter won't get impatient
    IF new THEN
      BEGIN
      -- 500 looks big, but if we don't make it big enough to start with, the server times out while we are extending the file if we are on the same Ethernet.  Large boot files (Othello) are almost 500 pages.  This means that Pilot boot servers must have plenty of spare disk space.  The parameter is ignored on Altos.
      file ← George.CreateNewFile[fileName, 500];
      temp ← George.CreateOutputStream[file]
      END
    ELSE
      BEGIN
      copy ← George.NameToCapability[scratch, 100];
      temp ← George.CreateOutputStream[copy];
      END;
    buffer ← Storage.Node[1*256];
    BEGIN
    ENABLE
      BEGIN
      EFTPTroubleReceiving =>
	BEGIN String.AppendString[trouble, s]; GOTO EFTPTrouble; END;
      EFTPTimeOut => GOTO EFTPTimeout;
      END;
    n: CARDINAL ← 0;
    [] ← EFTPOpenForReceiving[
      me !
      EFTPTimeOut =>
	BEGIN -- We expect to get here once since we haven't asked yet
	IF (n ← n + 1) > 3 THEN GOTO OpenFailed;
	ask[];
	RESUME
	;
	END];
    EFTPSetRecvTimeout[60000];
    -- This is a silly place to check, but it simplifies recovery
    IF George.CountFreeDiskPages[] < 100 THEN GOTO NotMuchRoom;
    DO
      n ← EFTPGetBlock[buffer, 2*256 ! EFTPEndReceiving => EXIT];
      George.PutWords[temp, buffer, n/2 ! George.DiskFull[] => GOTO TempFull];
      -- This will screwup if any but than the last block has an odd length.
      IF (n MOD 2) # 0 THEN George.PutByte[temp, buffer[n - 1]];
      bytes ← bytes + n;
      IF bytes >= 512 THEN BEGIN bytes ← bytes - 512; pages ← pages + 1; END;
      ENDLOOP;
    EFTPFinishReceiving[];
    George.Destroy[temp]; -- this sets the correct length
    temp ← NIL;
    Slosh.Check[
      fileName, IF new THEN file ELSE copy !
      Slosh.RejectThisTrash =>
	BEGIN String.AppendString[trouble, text]; GOTO Rejected; END];
    IF new THEN BEGIN status ← statusStoreOk; Slosh.Arrived[fileName, file]; END
    ELSE
      BEGIN
      Slosh.Release[fileName, file];
      status ← Slosh.CopyFile[to: file, from: copy];
      IF status = statusStoreOk THEN Slosh.Arrived[fileName, file]
      ELSE Slosh.Failed[fileName, file];
      END;
    EXITS
      OpenFailed => FixThingsUp["EFTP Open Failed"L, statusEFTPFailed];
      NotMuchRoom => FixThingsUp["Not much room on the disk"L, statusDiskFull];
      EFTPTrouble => FixThingsUp["EFTP Trouble"L, LOOPHOLE[EFTPTroubleReceiving]];
      EFTPTimeout => FixThingsUp["EFTP Timeout"L, LOOPHOLE[EFTPTroubleReceiving]];
      TempFull =>
	FixThingsUp[
	  "Disk Full while buffering into buffer file"L, statusDiskFull];
      Rejected => FixThingsUp["Rejected locally"L, statusContentsRejected];
    END;
    Storage.Free[buffer];
    Lock.UnlockDisk[fileName];
    IF ~new THEN BEGIN George.DeleteFileFromDisk[copy]; END;
    IF verbose THEN
      BEGIN
      text: STRING = [150];
      Time.AppendCurrent[text];
      IF message = NIL THEN
	BEGIN
	String.AppendString[text, "  Got "L];
	String.AppendString[text, fileName];
	String.AppendString[text, " ok.  Length="L];
	String.AppendDecimal[text, pages];
	String.AppendChar[text, '.];
	String.AppendDecimal[text, bytes];
	END
      ELSE
	BEGIN
	String.AppendString[text, "  Recv aborted because: "L];
	String.AppendString[text, message];
	IF trouble.length # 0 THEN
	  BEGIN
	  String.AppendString[text, "("L];
	  String.AppendString[text, trouble];
	  String.AppendString[text, ") "L];
	  END;
	String.AppendString[text, " after page "L];
	String.AppendDecimal[text, pages];
	END;
      String.AppendString[text, ", pages left="L];
      pagesLeft ← George.CountFreeDiskPages[];
      String.AppendDecimal[text, pagesLeft];
      String.AppendChar[text, '.];
      LogString[who, text];
      END;
    END;

  LogString: PROCEDURE [msg: Window.Handle, text: STRING] =
    BEGIN IF msg # NIL THEN Put.Line[msg, text]; Put.Line[NIL, text]; END;

  END.