-- CopyOp.mesa
-- edited by Schroeder, February 5, 1981 5:18 PM
-- edited by Andrew Birrell, January 14, 1981 3:40 PM
-- edited by Brotz, November 18, 1981 3:43 PM

DIRECTORY
opD: FROM "OperationsDefs" USING [Expand, FileError, FileErrorReason, Stuff],
Storage USING [CopyString, FreeString],
TimeDefs USING [PackedTime];


CopyOp: MONITOR
IMPORTS opD, Storage
EXPORTS opD =

BEGIN
OPEN opD;


Copy: PUBLIC PROC [source, target: STRING, OverwriteOK: PROC RETURNS [BOOLEAN] ]
RETURNS [bytesCopied: LONG CARDINAL] =
BEGIN
state: {start, bufferEmpty, bufferFull, stop};
resumeExpander, resumeStuffer: CONDITION;
bufferP: POINTER;
bufferC: CARDINAL;
error: FileErrorReason;
eString: STRING;
ec: FileErrorReason;
es: STRING;
stufferProcess: PROCESS RETURNS [FileErrorReason, STRING];
stufferStarted: BOOLEAN ← FALSE;

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

StartStuffer: PROCEDURE [t: TimeDefs.PackedTime]
RETURNS [error: FileErrorReason, s: STRING] =
BEGIN
error ← ok;
s ← NIL;
opD.Stuff[target, Stuffer, OverwriteAllowed, t
! FileError =>
BEGIN
error ← reason;
IF errorString # NIL THEN s ← Storage.CopyString[errorString];
CONTINUE;
END];
IF error = cancel THEN error ← ok;
IF error # ok THEN StopExpander[];
END; -- of StartStuffer --

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

Stuffer: ENTRY PROCEDURE RETURNS [p: POINTER, c: CARDINAL, b: BOOLEAN] =
BEGIN
IF state # stop THEN
BEGIN
state ← bufferEmpty;
NOTIFY resumeExpander;
UNTIL state # bufferEmpty DO WAIT resumeStuffer; ENDLOOP;
p ← bufferP;
c ← bufferC;
END;
b ← state = bufferFull;
END; -- of Stuffer --

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

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

Expander: ENTRY PROCEDURE [p: POINTER, c: CARDINAL] RETURNS [BOOLEAN] =
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;
RETURN[state = bufferEmpty];
END; -- of Expander --

bytesCopied ← 0;
state ← start;
error ← ok;
eString ← NIL;
opD.Expand[source, Expander, GetReady
! FileError =>
BEGIN
error ← reason;
IF errorString # NIL THEN eString ← Storage.CopyString[errorString];
CONTINUE;
END];
error ← SELECT error FROM ok, cancel => ok, ENDCASE => get;
IF stufferStarted THEN
BEGIN
IF error # ok THEN StopStuffer[];
[ec, es] ← JOIN stufferProcess;
IF error = ok THEN
BEGIN
error ← SELECT ec FROM ok, cancel => ok, ENDCASE => put;
IF eString # NIL THEN Storage.FreeString[eString];
eString ← es;
END
ELSE {IF es # NIL THEN Storage.FreeString[es]};
END;
IF error # ok THEN ERROR
FileError[error, eString ! UNWIND => IF eString # NIL THEN Storage.FreeString[eString]];
END; -- of Copy --


END. -- of CopyOp --