-- MailFileOp.mesa -- Edited by Schroeder, March 3, 1981 10:05 AM. -- Edited by Brotz, March 4, 1983 9:52 AM. -- Edited by Levin, February 24, 1981 4:49 PM. DIRECTORY Core USING [Close, Delete, Open], csD: FROM "CoreStreamDefs" USING [Close, Destroy, GetLength, GetPosition, MapPageByteToPosition, MapPositionToPageByte, Open, Position, Read, SetPosition, StreamCopy, StreamHandle, Write], Inline USING [LowHalf], MailParseDefs USING [endOfInput], mfD: FROM "MailFormatDefs" USING [CreateStamp, ParseHeaderForTOC, ParseStamp], opD: FROM "OperationsDefs" USING [MailFileProblem, maxTOCStringLength], Process USING [Yield], String USING [AppendString], vmD: FROM "VirtualMgrDefs" USING [CleanupTOC, ExtendTOC, FirstChangedTOCIndex, FirstFreeTOCIndex, GetTOCFixedPart, LoadTOC, PageNumber, SetTOCValidity, PutTOCFixedPart, TOCFixedPart, TOCFixedPartPtr, TOCHandle, TOCIndex], VMDefs USING [Error, FileHandle, GetFileLength, PageNumber, Position, SetFileLength]; MailFileOp: PROGRAM IMPORTS Core, csD, Inline, mfD, Process, String, vmD, VMDefs EXPORTS opD = BEGIN MailFileError: PUBLIC ERROR [reason: opD.MailFileProblem] = CODE; GetMailFileOperation: PUBLIC PROCEDURE [toc: vmD.TOCHandle, key: CARDINAL, mailFile: STRING] RETURNS [firstUnseen: vmD.TOCIndex] = BEGIN -- variables for GetMailFileOperation mailFileHandle: VMDefs.FileHandle _ NIL; mailStream: csD.StreamHandle _ NIL; tOCEntry1, tOCEntry2: vmD.TOCFixedPart; -- Buffers for TOC entries tb1: vmD.TOCFixedPartPtr = @tOCEntry1; tb2: vmD.TOCFixedPartPtr = @tOCEntry2; TOCFileName: STRING; lastTOCIndex: vmD.TOCIndex; TOCString: STRING _ [opD.maxTOCStringLength]; fName: STRING _ [75]; i: CARDINAL; code: opD.MailFileProblem _ ok; eofPosition, currentPosition, nextCharLimit: csD.Position; parseStampLimit: CARDINAL = 24; -- internal procedures for GetMailFileOperation Cleanup: PROCEDURE = BEGIN IF mailStream # NIL THEN {csD.Destroy[mailStream]; mailStream _ NIL}; END; -- of Cleanup -- ErrorCleanup: PROCEDURE = BEGIN Cleanup[]; (IF eofPosition # 0 THEN Core.Close ELSE Core.Delete)[toc.mailFile]; vmD.CleanupTOC[toc, key, delete]; -- a nop if no toc exists yet END; -- of ErrorCleanup -- PositionNextChar: PROCEDURE [page: VMDefs.PageNumber, byte, limit: CARDINAL] = --sets buffer management variables so NextChar will produce char at specified position BEGIN currentPosition _ csD.MapPageByteToPosition[page, byte]; IF currentPosition <= eofPosition THEN csD.SetPosition[mailStream, currentPosition]; nextCharLimit _ currentPosition + limit; END; -- of PositionNextChar -- NextChar: PROCEDURE RETURNS [lastChar: CHARACTER] = BEGIN lastChar _ IF currentPosition >= nextCharLimit THEN MailParseDefs.endOfInput ELSE csD.Read[mailStream]; currentPosition _ currentPosition + 1; END; -- of NextChar -- -- code of GetMailFileOperation BEGIN -- block for EXITS fName.length _ 0; String.AppendString[fName, mailFile]; mailFile _ fName; FOR i IN [0 .. fName.length) DO IF fName[i] = '. THEN EXIT; REPEAT FINISHED => String.AppendString[fName, ".mail"L]; ENDLOOP; mailFileHandle _ Core.Open[mailFile, update]; Process.Yield[]; Process.Yield[]; TOCString.length _ 0; String.AppendString[TOCString, mailFile]; String.AppendString[TOCString, "-dmsTOC"L]; TOCFileName _ TOCString; mailStream _ csD.Open[mailFileHandle, byte, read]; eofPosition _ csD.GetLength[mailStream]; IF eofPosition = 0 THEN BEGIN firstUnseen _ vmD.LoadTOC[toc, key, TOCFileName, mailFileHandle, new]; GOTO simpleReturn; END; firstUnseen _ vmD.LoadTOC[toc, key, TOCFileName, mailFileHandle, old ! UNWIND => ErrorCleanup[]]; lastTOCIndex _ vmD.FirstFreeTOCIndex[toc, key] - 1; IF lastTOCIndex = 0 THEN PositionNextChar[0, 0, parseStampLimit] -- no toc entries yet ELSE BEGIN -- TOC has some entries already vmD.GetTOCFixedPart[toc, key, lastTOCIndex, tb1]; PositionNextChar[tb1.firstPage, tb1.firstByte, tb1.offsetToHeader]; IF ~mfD.ParseStamp[NextChar, tb2 ! UNWIND => ErrorCleanup[]] OR tb2.textLength # tb1.textLength OR tb2.offsetToHeader # tb1.offsetToHeader OR vmD.FirstChangedTOCIndex[toc] <= lastTOCIndex THEN BEGIN -- last TOC entry is not reasonable vmD.CleanupTOC[toc, key, delete]; firstUnseen _ vmD.LoadTOC[toc, key, TOCFileName, mailFileHandle, new ! UNWIND => ErrorCleanup[]]; PositionNextChar[0, 0, parseStampLimit]; END -- last TOC entry is not reasonable ELSE -- TOC is reasonable PositionNextChar [tb1.firstPage, tb1.firstByte + tb1.offsetToHeader + tb1.textLength, parseStampLimit]; END; -- TOC has some entries already WHILE currentPosition < eofPosition DO Process.Yield[]; Process.Yield[]; [tb1.firstPage, tb1.firstByte] _ csD.MapPositionToPageByte[currentPosition]; tb1.changed _ FALSE; IF ~mfD.ParseStamp[NextChar, tb1 ! UNWIND => ErrorCleanup[]] THEN {code _ notAMailFile; ErrorCleanup[]; GO TO simpleReturn}; IF ~tb1.deleted THEN BEGIN PositionNextChar[tb1.firstPage, tb1.firstByte + tb1.offsetToHeader, tb1.textLength]; mfD.ParseHeaderForTOC[TOCString, NextChar ! UNWIND => Cleanup[]]; IF firstUnseen = 0 AND ~tb1.seen THEN firstUnseen _ vmD.FirstFreeTOCIndex[toc, key]; vmD.ExtendTOC[toc, key, tb1, TOCString ! UNWIND => Cleanup[]]; END; PositionNextChar[tb1.firstPage, tb1.firstByte+ tb1.offsetToHeader+ tb1.textLength, parseStampLimit]; ENDLOOP; IF currentPosition > eofPosition THEN BEGIN -- last stamp pointed beyond the end of the file, so contract last stamp lastMessageStart: csD.Position _ csD.MapPageByteToPosition[tb1.firstPage, tb1.firstByte]; WriteStampChar: PROCEDURE [c: CHARACTER] = {csD.Write[mailStream, c]}; tb1.textLength _ Inline.LowHalf[eofPosition - lastMessageStart - tb1.offsetToHeader]; vmD.PutTOCFixedPart[toc, key, lastTOCIndex, tb1]; csD.Destroy[mailStream]; mailStream _ csD.Open[mailFileHandle, byte, write]; csD.SetPosition[mailStream, lastMessageStart]; mfD.CreateStamp[tb1, WriteStampChar]; code _ lastStampTooLong; END; EXITS simpleReturn => NULL; END; -- of EXITS block Cleanup[]; Process.Yield[]; Process.Yield[]; IF code # ok THEN ERROR MailFileError[code]; END; -- of GetMailFileOperation -- ReturnMailFileOperation: PUBLIC PROCEDURE [toc: vmD.TOCHandle, key: CARDINAL] = BEGIN TOCEntry: vmD.TOCFixedPart; fp: vmD.TOCFixedPartPtr = @TOCEntry; stateOfPreviousEntry: {initialDeletions, deleted, notDeleted}; changingInPlace: BOOLEAN _ TRUE; --becomes FALSE when first deletion found i, b, appendCharCount, firstFree: CARDINAL; positionOfFirstDeletion, eof: VMDefs.Position; tocValid: BOOLEAN _ TRUE; in, out: csD.StreamHandle _ NIL; -- internal procedures of ReturnMailFileOperation AppendCharToOutBuffer: PROCEDURE [c: CHARACTER] = BEGIN csD.Write[out, c]; appendCharCount _ appendCharCount + 1; END; -- of AppendCharToOutBuffer -- CopyTo: PROCEDURE [page: VMDefs.PageNumber, byte: CARDINAL] = -- Copies bytes between the current in position and page, byte to the current out position. BEGIN csD.StreamCopy[in, out, csD.MapPageByteToPosition[page, byte] - csD.GetPosition[in]]; END; -- of CopyTo -- Cleanup: PROCEDURE = BEGIN IF in = NIL THEN {csD.Destroy[in]; in _ NIL}; IF out = NIL THEN {csD.Destroy[out]; out _ NIL}; END; -- of Destroy -- -- Code for ReturnMailFileOperation BEGIN -- block for EXITS and signals ENABLE VMDefs.Error => GOTO errorReturn; IF (firstFree _ vmD.FirstFreeTOCIndex[toc, key]) = 1 THEN {Core.Delete[toc.mailFile]; vmD.CleanupTOC[toc, key, delete]; RETURN}; out _ csD.Open[toc.mailFile, byte, write]; in _ csD.Open[toc.mailFile, byte, read]; FOR i IN [vmD.FirstChangedTOCIndex[toc] .. firstFree) DO vmD.GetTOCFixedPart[toc, key, i, fp]; IF fp.deleted THEN {toc.firstChange _ i; EXIT}; IF fp.changed THEN BEGIN csD.SetPosition[out, csD.MapPageByteToPosition[fp.firstPage, fp.firstByte]]; mfD.CreateStamp[fp, AppendCharToOutBuffer]; vmD.PutTOCFixedPart[toc, key, i, fp, FALSE]; END; REPEAT FINISHED => BEGIN -- no deleted entries were found, so we are done csD.Close[out]; csD.Destroy[in]; Core.Close[toc.mailFile]; toc.firstChange _ 0; vmD.CleanupTOC[toc, key, resetChanges]; RETURN; END; ENDLOOP; -- a deleted entry exists -- remember page and byte of last character before first deleted message positionOfFirstDeletion _ [fp.firstPage, fp.firstByte]; stateOfPreviousEntry _ initialDeletions; changingInPlace _ FALSE; eof _ VMDefs.GetFileLength[toc.mailFile]; -- 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[toc, key, i, fp]; --get next entry from TOC = firstFree => BEGIN -- calculate distance, in pages and bytes, from last toc message to EOF b _ fp.firstByte + fp.offsetToHeader + fp.textLength; fp.firstPage _ fp.firstPage + (b / 512); fp.firstByte _ b MOD 512; -- now fp.firstPage and fp.firstByte show end of mailfile as described by the toc IF fp.firstByte = eof.byte AND fp.firstPage = eof.page THEN -- no more to copy BEGIN IF stateOfPreviousEntry = notDeleted THEN CopyTo[fp.firstPage, fp.firstByte]; EXIT; END ELSE BEGIN -- more to copy because of a partial TOC --makeup entry describing message starting after last TOCed message fp.deleted _ FALSE; fp.changed _ FALSE; END; END; ENDCASE => -- > firstFree -- BEGIN --makeup an entry describing deleted message starting at old EOF for mailfile fp.firstPage _ eof.page; fp.firstByte _ eof.byte; fp.deleted _ TRUE; END; IF fp.deleted THEN --this entry is deleted BEGIN IF stateOfPreviousEntry = notDeleted THEN {CopyTo[fp.firstPage, fp.firstByte]; stateOfPreviousEntry _ deleted} END ELSE BEGIN --this entry is not deleted IF fp.changed THEN --this entry has been changed BEGIN SELECT stateOfPreviousEntry FROM = notDeleted => CopyTo[fp.firstPage, fp.firstByte]; = deleted => NULL; ENDCASE -- = initialDeletions -- => BEGIN csD.SetPosition[out, csD.MapPageByteToPosition [positionOfFirstDeletion.page, positionOfFirstDeletion.byte]]; vmD.SetTOCValidity[toc, FALSE]; tocValid _ FALSE; END; appendCharCount _ 0; mfD.CreateStamp[fp, AppendCharToOutBuffer]; -- record the copy starting point csD.SetPosition [in, csD.MapPageByteToPosition[fp.firstPage, fp.firstByte + appendCharCount]]; 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 csD.SetPosition[out, csD.MapPageByteToPosition [positionOfFirstDeletion.page, positionOfFirstDeletion.byte]]; vmD.SetTOCValidity[toc, FALSE]; tocValid _ FALSE; END; -- record the copy starting point csD.SetPosition[in, csD.MapPageByteToPosition[fp.firstPage, fp.firstByte]]; END; END; --this entry has not been changed stateOfPreviousEntry _ notDeleted; END; --this entry is not deleted ENDLOOP; IF stateOfPreviousEntry = initialDeletions THEN eof _ positionOfFirstDeletion --new EOF is start of first deletion -- ELSE [eof.page, eof.byte] _ csD.MapPositionToPageByte[csD.GetPosition[out]]; -- new EOF is end of last copy -- IF eof # [0, 0] THEN -- something is left BEGIN csD.Destroy[in]; csD.Close[out]; VMDefs.SetFileLength[toc.mailFile, eof]; Core.Close[toc.mailFile]; IF NOT tocValid THEN vmD.SetTOCValidity[toc, TRUE]; vmD.CleanupTOC[toc, key, resetChanges]; END ELSE -- whole file was deleted {Cleanup[]; Core.Delete[toc.mailFile]; vmD.CleanupTOC[toc, key, delete]}; EXITS errorReturn => {Cleanup[]; Core.Close[toc.mailFile]; vmD.CleanupTOC[toc, key, dontResetChanges]}; END; -- of EXITS block END; -- of ReturnMailFileOperation -- END. -- of MailFileOp --z20461(529)\f1