-- AppendOp.mesa
-- Edited by Schroeder, Wednesday Nov. 5, 1980 2:32 pm PST.
-- Edited by Brotz, September 1, 1980 10:03 AM

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

AppendOp: PROGRAM
IMPORTS crD, gsD, inD, intC:intCommon, mfD, tsD, vmD
EXPORTS opD
SHARES opD =

BEGIN

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

AppendMailToFileOperation: PUBLIC PROCEDURE [appendFileName: crD.UFilename]
RETURNS [ovD.ErrorCode] =
BEGIN
-- variables of the procedure
appendFileHandle: crD.UFileHandle;
errorCode, errorCode2: ovD.ErrorCode;
tOCEntry: vmD.TOCFixedPart;
tb: vmD.TOCFixedPartPtr = @tOCEntry;
b, count, bytesToCopy, index: CARDINAL;
messageIndex: vmD.TOCIndex;
aMessageWasCopied: BOOLEAN ← FALSE;

outBufferPage: gsD.MemoryPagePtr;
outBufferString: STRING; -- turns the BufferPage into a string
outBufferSize: CARDINAL; -- size in bytes, always a multiple of 512
nextOutSlot: CARDINAL;
pageInOutBuffer: crD.PageNumber;

inBufferPage: gsD.MemoryPagePtr;
inBufferString: STRING; -- turns the BufferPage into a string
nextInSlot: CARDINAL;
pageInInBuffer: crD.PageNumber;

eofPage: crD.PageNumber;
eofByte: CARDINAL;

-- internal procedures of AppendMailToFileOperation

WriteOutputBuffer: PROCEDURE =
BEGIN
IF (errorCode ← crD.WritePages[outBufferPage, nextOutSlot, pageInOutBuffer,
appendFileHandle]) # ovD.ok
THEN ERROR fileAccessError;
pageInOutBuffer ← pageInOutBuffer + outBufferSize/512;
nextOutSlot ← 0;
END; -- of WriteOutputBuffer --


ReadInputBuffer: PROCEDURE =
BEGIN
[errorCode, ] ← crD.ReadPages[inBufferPage, 512, pageInInBuffer, intC.mailFileHandle];
IF errorCode # ovD.ok THEN ERROR fileAccessError;
END; -- of ReadInputBuffer --


APutChar: PROCEDURE [c: CHARACTER] =
BEGIN
IF nextOutSlot >= outBufferSize THEN WriteOutputBuffer[];
outBufferString[nextOutSlot] ← c;
nextOutSlot ← nextOutSlot +1;
count ← count +1;
END; -- of APutChar --


AGetChar: PROCEDURE RETURNS [CHARACTER] =
BEGIN --assumes that page boundary won’t be crossed
nextOutSlot ← nextOutSlot + 1;
RETURN[outBufferString[nextOutSlot - 1]];
END; -- of AGetChar --

-- code for AppendMailToFileOperation

[errorCode, appendFileHandle] ← crD.OpenFile[intC.user, appendFileName, update];
IF errorCode # ovD.ok THEN RETURN[errorCode];

outBufferSize ← IF tsD.LastSelectedEntry[] < tsD.FirstSelectedEntry[] + 20
THEN 1024 ELSE 2048;
outBufferPage ← gsD.GetMemoryPages[outBufferSize / 512];
outBufferString ← LOOPHOLE[outBufferPage - 2, STRING];

inBufferPage ← gsD.GetMemoryPages[1];
inBufferString ← LOOPHOLE[inBufferPage - 2, STRING];

BEGIN -- block for exits
ENABLE fileAccessError =>
BEGIN
IF errorCode = ovD.diskFull OR errorCode = ovD.fileTooBig THEN
[] ← crD.UFileTruncate[eofPage, eofByte, appendFileHandle];
GOTO cleanupAndReturn;
END;
[errorCode, eofPage, eofByte] ← crD.UFileLength[appendFileHandle];
IF errorCode # ovD.ok THEN GOTO cleanupAndReturn;
IF eofPage # 0 OR eofByte # 0 THEN
BEGIN --see if target file looks like a mail file
pageInOutBuffer ← nextOutSlot ← 0;
[errorCode, ] ← crD.ReadPages[outBufferPage, 512, pageInOutBuffer, appendFileHandle];
IF errorCode # ovD.ok THEN GOTO cleanupAndReturn;
IF mfD.ParseStamp[AGetChar, tb] # ovD.ok THEN
{IF ~inD.AskUserToConfirm[exD.moveTargetExists] THEN GOTO cleanupAndReturn};
END;
pageInOutBuffer ← eofPage;
nextOutSlot ← eofByte;
IF pageInOutBuffer > 0 AND nextOutSlot > 0 THEN
BEGIN
[errorCode, ] ← crD.ReadPages[outBufferPage, 512, pageInOutBuffer, appendFileHandle];
IF errorCode # ovD.ok THEN GOTO cleanupAndReturn;
END;
pageInInBuffer ← 77777B;

FOR messageIndex ← tsD.FirstSelectedEntry[], tsD.NextSelectedEntry[messageIndex]
UNTIL messageIndex = 0 DO -- loop for each message
vmD.GetTOCFixedPart[messageIndex,tb];
IF ~tb.deleted THEN
BEGIN
IF tb.changed THEN
BEGIN
count ← 0;
mfD.CreateStamp[tb, APutChar];
b ← tb.firstByte + count;
tb.firstPage ← tb.firstPage + b/512;
tb.firstByte ← b MOD 512;
tb.offsetToHeader ← tb.offsetToHeader - count;
END;
IF pageInInBuffer # tb.firstPage THEN
{pageInInBuffer ← tb.firstPage; ReadInputBuffer[]};
nextInSlot ← tb.firstByte;
bytesToCopy ← tb.offsetToHeader + tb.textLength;
UNTIL bytesToCopy = 0 DO
IF nextInSlot > 511 THEN --need a new page in the input buffer
{pageInInBuffer ← pageInInBuffer +1; ReadInputBuffer[]; nextInSlot ← 0};
DO -- copy input buffer
count ← MIN[outBufferSize - nextOutSlot, 512 - nextInSlot, bytesToCopy];
FOR index IN [0 .. count) DO
outBufferString[nextOutSlot + index] ← inBufferString[nextInSlot + index];
ENDLOOP;
nextInSlot ← nextInSlot + count;
bytesToCopy ← bytesToCopy - count;
IF (nextOutSlot ← nextOutSlot + count) >= outBufferSize THEN
WriteOutputBuffer[];
IF nextInSlot > 511 OR bytesToCopy = 0 THEN EXIT;
ENDLOOP; -- copy input buffer
ENDLOOP; -- UNTIL bytesToCopy = 0
aMessageWasCopied ← TRUE;
END; -- of ~tb.deleted THEN block
ENDLOOP; -- loop for each message

IF aMessageWasCopied AND nextOutSlot # 0 THEN WriteOutputBuffer[];
GOTO cleanupAndReturn;

EXITS
cleanupAndReturn =>
BEGIN
errorCode2 ← crD.CloseFile[appendFileHandle];
IF errorCode = ovD.ok THEN errorCode ← errorCode2; --return the first error
gsD.ReturnMemoryPages[outBufferSize / 512, outBufferPage];
gsD.ReturnMemoryPages[1, inBufferPage];
RETURN[IF (~aMessageWasCopied AND errorCode = ovD.ok)
THEN ovD.noMessagesMoved ELSE errorCode];
END;
END; -- of EXITS block
END; -- of AppendMailToFileOperation --


END.
-- of AppendOp --