-- CopyOp.mesa
-- edited by Schroeder, February 5, 1981 5:18 PM
-- edited by Andrew Birrell, January 14, 1981 3:40 PM
-- edited by Brotz, March 9, 1981 4:25 PM

DIRECTORY
opD: FROM "OperationsDefs",
ovD: FROM "OverviewDefs",
TimeDefs;


CopyOp: MONITOR
IMPORTS opD
EXPORTS opD =

PUBLIC BEGIN

Copy: PROCEDURE [source, target: STRING,
OverwriteOK: PROC RETURNS [BOOLEAN] ]
RETURNS [error: ovD.ErrorCode, getOrPut: opD.GetOrPutType,
errorString: STRING, bytesCopied: LONG CARDINAL] =
BEGIN
state: {start, bufferEmpty, bufferFull, stop};
resumeExpander, resumeStuffer: CONDITION;
bufferP: POINTER;
bufferC: CARDINAL;
ec: ovD.ErrorCode; es: STRING;
stufferProcess: PROCESS RETURNS [ovD.ErrorCode, STRING];
stufferStarted: BOOLEAN ← FALSE;

GetReady: PROCEDURE
[bytes: LONG CARDINAL, created: TimeDefs.PackedTime] =
BEGIN
stufferProcess ← FORK StartStuffer[created];
stufferStarted ← TRUE;
END; --GetReady--

StartStuffer: PROCEDURE [t: TimeDefs.PackedTime]
RETURNS [e: ovD.ErrorCode, s: STRING] =
BEGIN
[e, s] ← opD.Stuff[target, Stuffer, OverwriteAllowed, t];
IF e=ovD.cancelCode THEN e ← ovD.ok;
IF e#ovD.ok THEN StopExpander[];
END; --StartStuffer--

OverwriteAllowed: PROCEDURE RETURNS [allowed: BOOLEAN] =
BEGIN
allowed ← OverwriteOK[];
IF NOT allowed THEN StopExpander[];
END; --OverwriteAllowed--

Stuffer: ENTRY PROCEDURE
RETURNS [p: POINTER, c: CARDINAL, e: ovD.ErrorCode] =
BEGIN
IF state#stop THEN BEGIN
state ← bufferEmpty; NOTIFY resumeExpander;
UNTIL state#bufferEmpty DO WAIT resumeStuffer; ENDLOOP;
p ← bufferP; c ← bufferC;
END;
e ← IF state=bufferFull THEN ovD.ok ELSE ovD.cancelCode;
END; --Stuffer--

StopExpander: ENTRY PROCEDURE =
{state ← stop; NOTIFY resumeExpander};

StopStuffer: ENTRY PROCEDURE =
{state ← stop; NOTIFY resumeStuffer};

Expander: ENTRY PROCEDURE [p: POINTER, c: CARDINAL]
RETURNS [e: ovD.ErrorCode] =
BEGIN
UNTIL state#start DO WAIT resumeExpander; ENDLOOP;
IF state#stop THEN
BEGIN
bufferP ← p; bufferC ← c;
state ← bufferFull; NOTIFY resumeStuffer;
bytesCopied ← bytesCopied + c;
END;
IF c#0 THEN UNTIL state#bufferFull DO WAIT resumeExpander; ENDLOOP;
e ← IF state=bufferEmpty THEN ovD.ok ELSE ovD.cancelCode;
END; --Expander--

bytesCopied ← 0;
state ← start;
getOrPut ← get;
[error, errorString] ← opD.Expand[source, Expander, GetReady];
IF error=ovD.cancelCode THEN error ← ovD.ok;
IF stufferStarted THEN
BEGIN
IF error#ovD.ok THEN StopStuffer[];
[ec, es] ← JOIN stufferProcess;
IF error=ovD.ok
THEN {error ← ec; errorString ← es; getOrPut ← put}
ELSE {IF es#NIL THEN opD.FreeErrorString[es]};
END;

END; --Copy--

END. --CopyOp--