-- AppendOp.mesa
-- Edited by Schroeder, Wednesday Nov. 5, 1980 2:32 pm PST.
-- Edited by Brotz, November 30, 1981 10:41 AM
-- Edited by Taft, May 17, 1983 11:52 AM

DIRECTORY
AltoFile USING [DiskFull],
csD: FROM "CoreStreamDefs" USING [Close, GetLength, OpenFromName, Position, Read,
Reset, SetPosition, StreamHandle, Write, WriteBlock],
DiskIODefs USING [DiskError],
exD: FROM "ExceptionDefs" USING [moveTargetExists],
inD: FROM "InteractorDefs" USING [AskUserToConfirm],
mfD: FROM "MailFormatDefs" USING [CreateStamp, ParseStamp],
opD: FROM "OperationsDefs" USING [NoMoveReason],
tsD: FROM "TOCSelectionDefs" USING [FirstSelectedEntry, NextSelectedEntry],
vmD: FROM "VirtualMgrDefs" USING [GetTOCFixedPart, PageNumber, TOCFixedPart,
TOCFixedPartPtr, TOCHandle, TOCIndex],
VMDefs USING [Deactivate, Error, Page, pageByteSize, PageNumber, ReadPage];

AppendOp: PROGRAM
IMPORTS AltoFile, csD, DiskIODefs, inD, mfD, tsD, vmD, VMDefs
EXPORTS opD =

BEGIN


NoMessagesMoved: PUBLIC ERROR [reason: opD.NoMoveReason] = CODE;
-- May be raised by AppendMailToFileOperation if no messages are actually moved.


AppendMailToFileOperation: PUBLIC PROCEDURE
[toc: vmD.TOCHandle, key: CARDINAL, appendFileName: STRING] =
-- Opens the appendFile named, appends all undeleted selected messages to that file with
-- stamps, closes append file. Skips any messages in the range that are deleted.
-- May raise NoMessagesMoved.
BEGIN
outputSH: csD.StreamHandle ← csD.OpenFromName[appendFileName, byte, append];
tOCEntry: vmD.TOCFixedPart;
tb: vmD.TOCFixedPartPtr = @tOCEntry;
b, putCount, bytesToCopy, bytesOnThisPage, inBufferStart: CARDINAL;
messageIndex: vmD.TOCIndex;
inBuffer: VMDefs.Page;
inBufferPageNumber: VMDefs.PageNumber;
failureState: opD.NoMoveReason ← noUndeleted;

-- internal procedures of AppendMailToFileOperation

AGetChar: PROCEDURE RETURNS [CHARACTER] =
BEGIN
RETURN[csD.Read[outputSH]];
END; -- of AGetChar --

APutChar: PROCEDURE [c: CHARACTER] =
BEGIN
putCount ← putCount + 1;
csD.Write[outputSH, c];
END; -- of AGetChar --

-- The main work is done in a nested procedure so that UNWIND catch phrases
-- will get control when signals are caught and unwound by the outer ENABLE.

AppendInner: PROCEDURE =
BEGIN

FOR messageIndex ← tsD.FirstSelectedEntry[toc, key],
tsD.NextSelectedEntry[toc, key, messageIndex] UNTIL messageIndex = 0
DO -- loop for each message
vmD.GetTOCFixedPart[toc, key, messageIndex, tb];
IF ~tb.deleted THEN
BEGIN -- copy this message --
IF tb.changed THEN
BEGIN
putCount ← 0;
mfD.CreateStamp[tb, APutChar];
b ← tb.firstByte + putCount;
tb.firstPage ← tb.firstPage + b / 512;
tb.firstByte ← b MOD 512;
tb.offsetToHeader ← tb.offsetToHeader - putCount;
END;
inBufferPageNumber ← tb.firstPage;
inBufferStart ← tb.firstByte;
bytesToCopy ← tb.offsetToHeader + tb.textLength;
UNTIL bytesToCopy = 0 DO
inBuffer ← VMDefs.ReadPage[[toc.mailFile, inBufferPageNumber], 2];
bytesOnThisPage ← MIN[bytesToCopy, VMDefs.pageByteSize - inBufferStart];
csD.WriteBlock[outputSH, inBuffer, inBufferStart, bytesOnThisPage
! UNWIND => VMDefs.Deactivate[inBuffer]];
inBufferStart ← 0;
inBufferPageNumber ← inBufferPageNumber + 1;
bytesToCopy ← bytesToCopy - bytesOnThisPage;
VMDefs.Deactivate[inBuffer];
ENDLOOP;
failureState ← ok;
END; -- copy this message --
ENDLOOP; -- loop for each message
csD.Close[outputSH];
END; -- AppendInner

-- code for AppendMailToFileOperation

BEGIN -- for EXITS --
ENABLE {
VMDefs.Error =>
BEGIN
failureState ← IF reason = resources THEN diskFull ELSE diskError;
GOTO bailOut;
END;
AltoFile.DiskFull => {failureState ← diskFull; GOTO bailOut};
DiskIODefs.DiskError => {failureState ← diskError; GOTO bailOut};
};

position: csD.Position = csD.GetLength[outputSH];
IF position # 0 THEN
BEGIN -- see if target file looks like a mail file.
stampOk: BOOLEAN;
csD.SetPosition[outputSH, 0];
stampOk ← mfD.ParseStamp[AGetChar, tb];
csD.Reset[outputSH];
IF ~stampOk THEN
{IF ~inD.AskUserToConfirm[exD.moveTargetExists] THEN GOTO bailOut};
END;
AppendInner[]; -- the real work of the procedure
EXITS
bailOut => {csD.Reset[outputSH]; csD.Close[outputSH]};
END; -- of EXITS block

IF failureState # ok THEN ERROR NoMessagesMoved[failureState];
END; -- of AppendMailToFileOperation --


END. -- of AppendOp --