-- File: Slosher.mesa,  Last Edit: HGM  December 20, 1980  4:09 PM

DIRECTORY
  Inline USING [LowHalf, LongDivMod],
  Process USING [Yield],
  Storage USING [Node],
  System USING [GetClockPulses],
  File USING [Capability],
  George USING [
    CountFreeDiskPages, CreateInputStream, CreateOutputStream, Destroy, DiskFull,
    GetCreateDate, GetLength, GetWords, Handle, PutByte, PutWords, SetCreateDate,
    SetIndex],
  Slosh USING [RecvStatus, Why];

Slosher: MONITOR IMPORTS Inline, Process, Storage, System, George EXPORTS Slosh =
  BEGIN OPEN Slosh;

  RejectThisTrash: PUBLIC ERROR [text: STRING] = CODE;

  first: Proc ← NIL;
  Proc: TYPE = POINTER TO ProcObject;
  ProcObject: TYPE = RECORD [
    next: Proc, proc: PROCEDURE [Why, STRING, File.Capability]];

  AddProcs: PUBLIC ENTRY PROCEDURE [
    proc: PROCEDURE [Why, STRING, File.Capability]] =
    BEGIN
    temp: Proc ← Storage.Node[SIZE[ProcObject]];
    temp↑ ← [first, proc];
    first ← temp;
    END;

  Check: PUBLIC PROCEDURE [fn: STRING, temp: File.Capability] =
    BEGIN RunTheList[check, fn, temp]; END;

  Release: PUBLIC PROCEDURE [fn: STRING, file: File.Capability] =
    BEGIN RunTheList[release, fn, file]; END;

  Arrived: PUBLIC PROCEDURE [fn: STRING, file: File.Capability] =
    BEGIN RunTheList[arrived, fn, file]; END;

  Failed: PUBLIC PROCEDURE [fn: STRING, file: File.Capability] =
    BEGIN RunTheList[failed, fn, file]; END;

  RunTheList: PROCEDURE [why: Why, fn: STRING, file: File.Capability] =
    BEGIN
    proc: Proc;
    FOR proc ← first, proc.next UNTIL proc = NIL DO
      proc.proc[why, fn, file]; ENDLOOP;
    END;

  RetransmissionInterval: PUBLIC PROCEDURE RETURNS [seconds: CARDINAL] =
    BEGIN
    mumble: LONG CARDINAL = System.GetClockPulses[]; -- Alto Pulses are short
    RETURN[Inline.LowHalf[mumble] MOD 5*60]; -- 5 min

    END;

  CopyFile: PUBLIC PROCEDURE [to, from: File.Capability]
    RETURNS [result: RecvStatus] =
    BEGIN
    n: CARDINAL ← 0;
    pagesLeft: CARDINAL;
    sourceLength, destLength: LONG CARDINAL;
    sourcePages, destPages: CARDINAL;
    sourceBytes, destBytes: CARDINAL;
    source, dest: George.Handle;
    buffer: PACKED ARRAY [0..512) OF [0..377B];
    source ← George.CreateInputStream[from];
    dest ← George.CreateOutputStream[to];
    George.SetCreateDate[dest, George.GetCreateDate[source]];
    BEGIN
    pagesLeft ← George.CountFreeDiskPages[];
    destLength ← George.GetLength[dest];
    George.SetIndex[dest, 0];
    sourceLength ← George.GetLength[source];
    George.SetIndex[source, 0];
    [sourcePages, sourceBytes] ← Inline.LongDivMod[sourceLength, 512];
    [destPages, destBytes] ← Inline.LongDivMod[destLength, 512];
    IF (destPages + pagesLeft) < (sourcePages + 10) THEN GOTO WontFit;
    THROUGH [0..sourcePages) DO
      [] ← George.GetWords[source, @buffer, 256];
      Process.Yield[];
      George.PutWords[dest, @buffer, 256 ! George.DiskFull[] => GOTO FileFull];
      Process.Yield[];
      ENDLOOP;
    [] ← George.GetWords[source, @buffer, 256];
    George.PutWords[
      dest, @buffer, sourceBytes/2 ! George.DiskFull[] => GOTO FileFull];
    IF (sourceBytes MOD 2) # 0 THEN George.PutByte[dest, buffer[sourceBytes - 1]];
    result ← statusStoreOk;
    EXITS
      WontFit => result ← statusDiskFull;
      FileFull => result ← statusFileClobbered;
    END;
    George.Destroy[source];
    George.Destroy[dest];
    END;

  END.