-- ReturnOp.mesa
-- edited by Schroeder, January 18, 1981 3:06 PM
-- edited by Brotz, August 15, 1980 5:48 PM
-- edited by Levin, January 12, 1981 3:09 PM

DIRECTORY
crD: FROM "CoreDefs",
exD: FROM "ExceptionDefs",
gsD: FROM "GlobalStorageDefs",
intCommon: FROM "IntCommon",
mfD: FROM "MailFormatDefs",
opD: FROM "OperationsDefs",
ovD: FROM "OverviewDefs",
vmD: FROM "VirtualMgrDefs";


ReturnOp: PROGRAM
IMPORTS crD, exD, gsD, intC:intCommon, mfD, vmD
EXPORTS opD
SHARES opD =

BEGIN

fileAccessError: ERROR = CODE; -- Had error return trying to read or write a file

ReturnMailFileOperation: PUBLIC PROCEDURE [buffer: POINTER TO opD.BufferRecord]
RETURNS [ovD.ErrorCode] =
BEGIN
-- variables of ReturnMailFileOperation

out: RECORD[ --output buffer
ptr: POINTER TO UNSPECIFIED, --pointer to buffer start
limit: CARDINAL, --size of buffer in bytes (a multiple of 512)
byteCount: CARDINAL, --number of bytes to write out from the buffer
firstPage: crD.PageNumber, --number of the first page in the buffer
string: STRING, --a string overlayed on the buffer area
isGSPage: BOOLEAN --buffer page was gotten from global storage division
] ← [NIL, , 0, , , FALSE];

in: RECORD[ --input buffer
ptr: POINTER TO UNSPECIFIED, --pointer to buffer start
limit: CARDINAL, --size of buffer in bytes (a multiple of 512)
firstPage: crD.PageNumber, --number of the first page in the buffer
firstByte: CARDINAL, --starting point for a copy
string: STRING, --a string overlayed on the buffer area
isGSPage: BOOLEAN --buffer page was gotten from global storage division
] ← [ , , , , , FALSE];

errorCode: ovD.ErrorCode;
tOCEntry: vmD.TOCFixedPart;
tb: vmD.TOCFixedPartPtr = @tOCEntry;
stateOfPreviousEntry: {initialDeletions, deleted, notDeleted};
changingInPlace: BOOLEAN ← TRUE; --becomes FALSE when first deletion found
replacePosition, --next place for ReplaceCharInOutBuffer to put character in output buffer
i, p, b, appendCharCount, firstFree, eofByte, firstByteOfFirstDeletion: CARDINAL;
eofPage, firstPageOfFirstDeletion: crD.PageNumber;
tocValid: BOOLEAN ← TRUE;

-- internal procedures of ReturnMailFileOperation


AdvanceOutBufferPosition: PROCEDURE[page: crD.PageNumber, byte: CARDINAL] =
BEGIN
IF out.ptr = NIL THEN
BEGIN -- need to set up the buffers
buffer.s1 ← (buffer.s1 / 256) * 512; --normalize size to n*512 bytes
buffer.s2 ← (buffer.s2 / 256) * 512; --normalize size to n*512 bytes
IF buffer.s1 > buffer.s2 THEN
{in.ptr ← buffer.b2; in.limit ← buffer.s2; out.ptr ← buffer.b1; out.limit ← buffer.s1}
ELSE {in.ptr ← buffer.b1; in.limit ← buffer.s1; out.ptr ← buffer.b2; out.limit← buffer.s2};
IF out.limit = 0 THEN -- larger buffer didn’t contain a full page
{out.ptr ← gsD.GetMemoryPages[1]; out.limit ← 512; out.isGSPage ← TRUE};
out.string ← LOOPHOLE[out.ptr - 2, STRING];
-- make sure that requested page doesn’t appear to be in buffer
out.firstPage ← page + 1;
END; -- need to set up the buffers
IF out.firstPage # page THEN
BEGIN --desired page not in the output buffer
IF out.byteCount # 0 THEN WriteOutputBuffer[];
IF byte # 0 OR changingInPlace THEN
BEGIN
[errorCode, out.byteCount] ← crD.ReadPages[out.ptr, 512, page, intC.mailFileHandle];
IF errorCode # ovD.ok THEN ERROR fileAccessError;
END;
out.firstPage ← page;
END;
IF changingInPlace THEN replacePosition ← byte ELSE out.byteCount ← byte;
END; -- of AdvanceOutBufferPosition --


ReplaceCharInOutBuffer: PROCEDURE[c: CHARACTER] =
BEGIN
IF replacePosition >= 512 THEN
BEGIN
WriteOutputBuffer[];
out.firstPage ← out.firstPage + 1;
replacePosition ← 0;
[errorCode, out.byteCount] ←
crD.ReadPages[out.ptr, 512, out.firstPage, intC.mailFileHandle];
IF errorCode # ovD.ok THEN ERROR fileAccessError;
END;
out.string[replacePosition] ← c;
replacePosition ← replacePosition + 1;
END; -- of ReplaceCharInOutBuffer --


AppendCharToOutBuffer: PROCEDURE[c: CHARACTER] =
BEGIN
IF out.byteCount >= out.limit THEN
BEGIN
WriteOutputBuffer[];
out.firstPage ← out.firstPage + out.limit / 512;
out.byteCount ← 0;
END;
out.string[out.byteCount] ← c;
out.byteCount ← out.byteCount + 1;
appendCharCount ← appendCharCount + 1;
END; -- of AppendCharToOutBuffer --


WriteOutputBuffer: PROCEDURE =
BEGIN
IF (errorCode ← crD.WritePages[out.ptr, out.byteCount, out.firstPage, intC.mailFileHandle])
# ovD.ok THEN ERROR fileAccessError;
END; -- of WriteOutputBuffer --

SetUpInBufferAndIndicateCompactionUnderway: PROCEDURE =
BEGIN
--set up the input buffer
IF in.limit = 0 THEN -- smaller buffer didn’t contain a full page
{in.ptr ← gsD.GetMemoryPages[1]; in.limit ← 512; in.isGSPage ← TRUE};
in.string ← LOOPHOLE[in.ptr - 2, STRING];
-- put compaction mark in toc
vmD.SetTOCValidity[FALSE];
tocValid ← FALSE;
END; --of SetUpInBufferAndIndicateCompactionUnderway--

CopyTo: PROCEDURE [page: crD.PageNumber, byte: CARDINAL] =
BEGIN
pagesLeft, index, firstCopyCount, byteCount: CARDINAL;
--get page and byte of last byte to copy
IF tocValid THEN exD.SysBug[]; -- make sure in buffer set up--
IF byte = 0 THEN {page ← page -1; byte ← 511} ELSE byte ← byte - 1;
pagesLeft ← page - in.firstPage + 1;
UNTIL pagesLeft = 0 DO
--calculate number of bytes to read this time
byteCount ← IF pagesLeft < in.limit / 512 THEN pagesLeft * 512 ELSE in.limit;
--do the read
[errorCode, ] ← crD.ReadPages[in.ptr, byteCount, in.firstPage, intC.mailFileHandle];
IF errorCode # ovD.ok THEN ERROR fileAccessError;
--calculate pages left to read and first page of in buffer for next iteration
in.firstPage ← in.firstPage + byteCount / 512;
pagesLeft ← page - in.firstPage + 1;
--set byteCount to be the number of bytes to copy for this iteration
IF pagesLeft = 0 THEN byteCount ← byteCount - (511 - byte);
byteCount ← byteCount - in.firstByte;
--copy to the output buffer
firstCopyCount ← MIN[byteCount, out.limit - out.byteCount];
FOR index IN [0 .. firstCopyCount) DO
out.string[out.byteCount + index] ← in.string[in.firstByte + index];
ENDLOOP;
IF (out.byteCount ← out.byteCount + firstCopyCount) >= out.limit THEN
BEGIN
WriteOutputBuffer[];
out.firstPage ← out.firstPage + out.limit / 512;
out.byteCount ← byteCount - firstCopyCount;
in.firstByte ← in.firstByte + firstCopyCount;
FOR index IN [0 .. out.byteCount) DO
out.string[index] ← in.string[in.firstByte + index];
ENDLOOP;
END;
in.firstByte ← 0;
ENDLOOP;
END; -- of CopyTo --

-- Code for ReturnMailFileOperation

BEGIN -- block for EXITS and signals
ENABLE fileAccessError => GOTO errorReturn;

IF (firstFree ← vmD.GetFirstFreeTOCIndex[]) = 1 THEN
BEGIN
vmD.CleanupTOC[delete];
errorCode ← crD.DeleteFile[intC.mailFileHandle];
GOTO normalReturn;
END;

FOR i IN [vmD.GetFirstChangedTOCIndex[] .. firstFree) DO
vmD.GetTOCFixedPart[i,tb]; --first argument should be of type TOCIndex
IF tb.deleted THEN EXIT;
IF tb.changed THEN
BEGIN
AdvanceOutBufferPosition[tb.firstPage, tb.firstByte];
mfD.CreateStamp[tb, ReplaceCharInOutBuffer];
END;
REPEAT
FINISHED =>
BEGIN -- no deleted entries were found, so we are done
IF out.byteCount # 0 THEN WriteOutputBuffer[];
errorCode ← crD.CloseFile[intC.mailFileHandle];
vmD.CleanupTOC[resetChanges];
GOTO normalReturn;
END;
ENDLOOP;

-- a deleted entry exists
-- remember page and byte of last character before first deleted message
firstPageOfFirstDeletion ← tb.firstPage;
firstByteOfFirstDeletion ← tb.firstByte;
stateOfPreviousEntry ← initialDeletions;
changingInPlace ← FALSE;
[errorCode, eofPage, eofByte] ← crD.UFileLength[intC.mailFileHandle];
IF errorCode # ovD.ok THEN GOTO errorReturn;

-- process remaining messages in the toc; remember that message i is deleted
FOR i IN (i .. firstFree + 1] DO
SELECT i FROM
< firstFree => vmD.GetTOCFixedPart[i, tb]; --get next entry from TOC
= firstFree =>
BEGIN -- calculate distance, in pages and bytes, from last toc message to EOF
b ← tb.firstByte + tb.offsetToHeader + tb.textLength;
tb.firstPage ← tb.firstPage + (b / 512);
tb.firstByte ← b MOD 512;
-- now tb.firstPage and tb.firstByte show end of mailfile as described by the toc
IF tb.firstByte = eofByte AND tb.firstPage = eofPage THEN -- no more to copy
BEGIN
IF stateOfPreviousEntry = notDeleted THEN CopyTo[tb.firstPage, tb.firstByte];
EXIT;
END
ELSE BEGIN -- more to copy because of a partial TOC
--makeup entry describing message starting after last TOCed message
tb.deleted ← FALSE;
tb.changed ← FALSE;
END;
END;
ENDCASE => -- > firstFree --
BEGIN
--makeup an entry describing deleted message starting at old EOF for mailfile
tb.firstPage ← eofPage;
tb.firstByte ← eofByte;
tb.deleted ← TRUE;
END;
IF tb.deleted THEN --this entry is deleted
BEGIN
IF stateOfPreviousEntry = notDeleted THEN
{CopyTo[tb.firstPage, tb.firstByte]; stateOfPreviousEntry ← deleted}
END
ELSE BEGIN --this entry is not deleted
IF tb.changed THEN --this entry has been changed
BEGIN
SELECT stateOfPreviousEntry FROM
= notDeleted => CopyTo[tb.firstPage, tb.firstByte];
= deleted => NULL;
ENDCASE -- = initialDeletions -- =>
BEGIN
AdvanceOutBufferPosition[firstPageOfFirstDeletion, firstByteOfFirstDeletion];
SetUpInBufferAndIndicateCompactionUnderway[];
END;
appendCharCount ← 0;
mfD.CreateStamp[tb, AppendCharToOutBuffer];
-- record the copy starting point
b ← tb.firstByte + appendCharCount;
in.firstPage ← tb.firstPage + (b / 512);
in.firstByte ← b MOD 512;
END --this entry has been changed
ELSE BEGIN --this entry has not been changed
IF stateOfPreviousEntry # notDeleted THEN
BEGIN -- = initialDeletions OR = deleted --
IF stateOfPreviousEntry = initialDeletions THEN
BEGIN
AdvanceOutBufferPosition[firstPageOfFirstDeletion, firstByteOfFirstDeletion];
SetUpInBufferAndIndicateCompactionUnderway[];
END;
-- record the copy starting point
in.firstPage ← tb.firstPage;
in.firstByte ← tb.firstByte;
END;
END; --this entry has not been changed
stateOfPreviousEntry ← notDeleted;
END; --this entry is not deleted
ENDLOOP;

IF stateOfPreviousEntry = initialDeletions THEN --new EOF is start of first deletion
{p ← firstPageOfFirstDeletion; b ← firstByteOfFirstDeletion}
ELSE --new EOF is end of last copy
{p ← out.firstPage + out.byteCount / 512; b ← out.byteCount MOD 512};
IF p # 0 OR b # 0 THEN -- something is left
BEGIN
IF out.byteCount # 0 THEN WriteOutputBuffer[];
errorCode ← crD.UFileTruncate[p, b, intC.mailFileHandle];
IF errorCode # ovD.ok THEN GOTO errorReturn;
errorCode ← crD.CloseFile[intC.mailFileHandle];
IF NOT tocValid THEN vmD.SetTOCValidity[TRUE];
vmD.CleanupTOC[resetChanges];
END
ELSE -- whole file was deleted
{errorCode ← crD.DeleteFile[intC.mailFileHandle]; vmD.CleanupTOC[delete]};

EXITS
normalReturn => NULL;
errorReturn =>
{[] ← crD.CloseFile[intC.mailFileHandle]; vmD.CleanupTOC[dontResetChanges]};
END; -- of EXITS block

IF out.isGSPage THEN gsD.ReturnMemoryPages[1, out.ptr];
IF in.isGSPage THEN gsD.ReturnMemoryPages[1, in.ptr];
intC.mailFileHandle ← NIL;
RETURN[errorCode];
END; -- of ReturnMailFileOperation --


END. -- of ReturnOp --