-- file: ReplaceMailOp.mesa -- edited by Brotz, March 4, 1983 9:55 AM -- edited by Crowther, October 6, 1981 4:15 PM DIRECTORY Ascii USING [SP], csD: FROM "CoreStreamDefs" USING [Close, MapPageByteToPosition, MapPositionToPageByte, Open, Position, SetPosition, StreamCopy, StreamHandle, Write, WriteBlock], DiskKDDefs USING [CountFreeDiskPages], dsD: FROM "DisplayDefs" USING [ClearRectangle], Editor USING [RefreshSoThatFirstCharStartsLine], exD: FROM "ExceptionDefs" USING [DisplayExceptionString, SysBug], inD: FROM "InteractorDefs" USING [leftMargin, MessageTextNbrPtr, RefreshTOCChange, rightMargin, TOCChangeAction, TOCTextNbrPtr], Inline USING [COPY, LowHalf], intCommon USING [dmTextNbr, tocTextNbr], MailParseDefs USING [endOfInput], mfD: FROM "MailFormatDefs" USING [CreateStamp, ParseHeaderForTOC], opD: FROM "OperationsDefs" USING [maxTOCStringLength], Process USING [Yield], String USING [WordsForString], vmD: FROM "VirtualMgrDefs" USING [bugTrapValue, CharIndex, CheckpointDisplayMessage, DisplayMessage, DisplayMessagePtr, DMList, FlushDisplayMessage, GetMessageChar, GetMessageSize, GetTOCFixedPart, HardTOCAddress, LoadDisplayMessage, MakeBufferEmpty, PageNumber, PutTOCFixedPart, SearchTOCTable, SetTOCValidity, TOCFixedPart, TOCFixedPartPtr, TOCHandle, TOCIndex, TOCPageHeader, TOCPageHeaderBlk, TOCPageTableSize, TOCType, VirtualMessagePtr], VMDefs USING [Deactivate, FileHandle, FSAlto, GetFileLength, GetFileSystem, MarkStart, Page, PageNumber, Position, ReadPage, Release, RemapPage, SetFileLength, StartFile, StartReading, UsePage, WaitFile]; ReplaceMailOp: PROGRAM IMPORTS csD, DiskKDDefs, dsD, Editor, exD, inD, Inline, intC: intCommon, mfD, Process, String, vmD, VMDefs EXPORTS opD = BEGIN OPEN vmD; bytesPerStamp: CARDINAL = 24; ReplaceMailOperation: PUBLIC PROCEDURE [delete: BOOLEAN, index: TOCIndex, msg: VirtualMessagePtr, toc: TOCHandle, key: CARDINAL] RETURNS [worked: BOOLEAN] = BEGIN messageLength: CARDINAL = IF msg = NIL THEN 0 ELSE GetMessageSize[msg]; insertionLength: CARDINAL = IF msg = NIL THEN 0 ELSE messageLength + bytesPerStamp; deletionLength: CARDINAL _ 0; tocString: STRING _ [opD.maxTOCStringLength]; eof: VMDefs.Position; extraPages: CARDINAL; mailFile: VMDefs.FileHandle = toc.mailFile; fp, newTOCFixedPart: TOCFixedPart; mailFileInsertAt, mailFileMoveFrom, mailFileMoveTo, mailFileEnd, newMailFileEnd: csD.Position; newEndPage: PageNumber; newEndByte: CARDINAL; dm: inD.MessageTextNbrPtr = intC.dmTextNbr; displayMessage: DisplayMessagePtr = DisplayMessage[dm.message]; tnp: inD.TOCTextNbrPtr _ intC.tocTextNbr; action: inD.TOCChangeAction; charIndex: CharIndex _ 0; NextCharForParseHeader: PROCEDURE RETURNS [char: CHARACTER] = BEGIN IF charIndex >= messageLength THEN RETURN[MailParseDefs.endOfInput]; char _ GetMessageChar[msg, charIndex]; charIndex _ charIndex + 1; END; -- of NextCharForParseHeader -- MoveUpInMailFile: PROCEDURE = -- Copy bytes of mailFile to higher addresses, -- from [mailFileMoveFrom .. mailFileEnd) to [mailFileMoveTo .. newMailFileEnd) BEGIN out, in: VMDefs.Page; inPage, outPage, fromPage: PageNumber; outByte, inByte, fromByte: CARDINAL; CopyBytes: PROCEDURE = BEGIN bytesToCopy: CARDINAL _ MIN[inByte, outByte]; IF inPage = fromPage THEN bytesToCopy _ MIN[bytesToCopy, inByte - fromByte]; inByte _ inByte - bytesToCopy; outByte _ outByte - bytesToCopy; FOR i: CARDINAL DECREASING IN [0 .. bytesToCopy) DO out.chars[outByte + i] _ in.chars[inByte + i]; ENDLOOP; END; -- of CopyBytes -- [inPage, inByte] _ csD.MapPositionToPageByte[mailFileEnd]; [outPage, outByte] _ csD.MapPositionToPageByte[newMailFileEnd]; [fromPage, fromByte] _ csD.MapPositionToPageByte[mailFileMoveFrom]; IF outByte = 0 THEN {outByte _ 512; outPage _ outPage - 1}; IF inByte = 0 THEN {inByte _ 512; inPage _ inPage - 1}; in _ VMDefs.ReadPage[[mailFile, inPage]]; out _ VMDefs.UsePage[[mailFile, outPage]]; DO Process.Yield[]; CopyBytes[]; IF inPage = fromPage AND inByte = fromByte THEN {VMDefs.MarkStart[out]; EXIT}; IF inByte = 0 THEN BEGIN VMDefs.Release[in]; inPage _ inPage - 1; inByte _ 512; in _ VMDefs.ReadPage[[mailFile, inPage]]; END; IF outByte = 0 THEN BEGIN VMDefs.MarkStart[out]; VMDefs.Release[out]; outPage _ outPage - 1; outByte _ 512; out _ VMDefs.UsePage[[mailFile, outPage]]; END; ENDLOOP; VMDefs.Release[out]; VMDefs.Release[in]; END; -- of MoveUpInMailFile -- MoveDownInMailFile: PROCEDURE = -- Copy bytes of mailFile to lower addresses, -- from [mailFileMoveFrom .. mailFileEnd) to [mailFileMoveTo .. newMailFileEnd) BEGIN out: csD.StreamHandle _ csD.Open[mailFile, byte, write]; in: csD.StreamHandle _ csD.Open[mailFile, byte, read]; csD.SetPosition[in, mailFileMoveFrom]; csD.SetPosition[out, mailFileMoveTo]; csD.StreamCopy[in, out, mailFileEnd - mailFileMoveFrom]; csD.Close[out]; csD.Close[in]; END; -- of MoveDownInMailFile -- InsertStampAndMessage: PROCEDURE = BEGIN out: csD.StreamHandle _ csD.Open[mailFile, byte, write]; messageIndex: CharIndex _ 0; PutCharInStamp: PROCEDURE [char: CHARACTER] = {csD.Write[out, char]}; csD.SetPosition[out, mailFileInsertAt]; mfD.CreateStamp[@newTOCFixedPart, PutCharInStamp]; UNTIL messageIndex >= messageLength DO [] _ GetMessageChar[msg, messageIndex]; -- MAGIC: set up msg.get. csD.WriteBlock[out, msg.buffer, msg.get.floor + (messageIndex - msg.get.first), msg.get.free - messageIndex]; messageIndex _ msg.get.free; ENDLOOP; csD.Close[out]; END; -- of InsertStampAndMessage -- -- main code for ReplaceMail. IF ~toc.open OR index ~IN [1 .. toc.indexFF] OR (delete AND index = toc.indexFF) OR (~delete AND msg = NIL) OR (msg.vmoType = DM AND DisplayMessage[msg].toc = toc) THEN exD.SysBug[]; action _ SELECT TRUE FROM (delete AND msg = NIL) => delete, delete => replace, ENDCASE => insert; -- Get mail file insertion point. eof _ VMDefs.GetFileLength[mailFile]; mailFileEnd _ csD.MapPageByteToPosition[eof.page, eof.byte]; IF index = toc.indexFF THEN BEGIN mailFileMoveFrom _ mailFileMoveTo _ mailFileInsertAt _ mailFileEnd; newMailFileEnd _ mailFileEnd + insertionLength; END ELSE BEGIN GetTOCFixedPart[toc, key, index, @fp]; mailFileInsertAt _ fp.firstPage * LONG[512] + fp.firstByte; IF delete THEN deletionLength _ fp.textLength + fp.offsetToHeader; mailFileMoveFrom _ mailFileInsertAt + deletionLength; mailFileMoveTo _ mailFileInsertAt + insertionLength; newMailFileEnd _ mailFileEnd + (mailFileMoveTo - mailFileMoveFrom); END; -- Check to be sure an insertion will fit on disk and in TOC. extraPages _ 1 + (IF insertionLength > deletionLength THEN (insertionLength - deletionLength + 511) / 512 ELSE 0); IF action # delete AND (toc.filePageFF + 1 >= TOCPageTableSize OR (VMDefs.GetFileSystem[mailFile] = VMDefs.FSAlto AND extraPages > DiskKDDefs.CountFreeDiskPages[])) THEN RETURN[FALSE]; -- Flush buffers on the mail file held by all displayed messages. FOR dmList: vmD.DMList _ toc.dmList, dmList.next UNTIL dmList = NIL DO IF dmList.dm # NIL THEN vmD.CheckpointDisplayMessage[dmList.dm, key]; ENDLOOP; SetTOCValidity[toc, FALSE]; -- Move in mail file. [newEndPage, newEndByte] _ csD.MapPositionToPageByte[newMailFileEnd]; SELECT mailFileMoveTo FROM > mailFileMoveFrom => BEGIN VMDefs.SetFileLength[mailFile, [newEndPage, newEndByte]]; IF mailFileMoveFrom < mailFileEnd THEN MoveUpInMailFile[]; END; < mailFileMoveFrom => BEGIN IF mailFileMoveFrom < mailFileEnd THEN MoveDownInMailFile[]; VMDefs.SetFileLength[mailFile, [newEndPage, newEndByte]]; END; ENDCASE => VMDefs.SetFileLength[mailFile, [newEndPage, newEndByte]]; -- Set up pieces of new TOC entry. IF msg # NIL THEN BEGIN mfD.ParseHeaderForTOC[tocString, NextCharForParseHeader]; tocString^ _ StringBody[length: tocString.length, maxlength: tocString.length, text: ]; newTOCFixedPart _ TOCFixedPart [changed: FALSE, deleted: FALSE, seen: TRUE, bogus: FALSE, mark: Ascii.SP, firstPage: Inline.LowHalf[mailFileInsertAt / 512], firstByte: Inline.LowHalf[mailFileInsertAt] MOD 512, bugTrap: bugTrapValue, offsetToHeader: bytesPerStamp, textLength: messageLength]; END; -- Insert in mail file. IF msg # NIL THEN InsertStampAndMessage[]; -- Fix TOC. ResetTOCAddresses[toc, key, index, insertionLength, deletionLength, action]; FixUpTOC[toc, index, action, @newTOCFixedPart, tocString]; SetTOCValidity[toc, TRUE]; -- Fix up selection and redisplay TOC. inD.RefreshTOCChange[toc, key, index, action]; -- fix up all dm's FOR dmList: vmD.DMList _ toc.dmList, dmList.next UNTIL dmList = NIL DO dispMess: DisplayMessagePtr = dmList.dm; dmIndex: TOCIndex; Reload: PROCEDURE [i: TOCIndex] = {FlushDisplayMessage[dispMess, key]; LoadDisplayMessage[toc, key, i, dispMess]}; IF dispMess # NIL THEN BEGIN Process.Yield[]; SELECT dmIndex _ dispMess.index FROM < index => NULL; = index => SELECT action FROM delete => BEGIN FlushDisplayMessage[dispMess, key]; IF dm.haveMessage AND dispMess = displayMessage THEN BEGIN dm.haveMessage _ FALSE; dsD.ClearRectangle[inD.leftMargin, inD.rightMargin, dm.topY, dm.bottomY]; exD.DisplayExceptionString["The displayed message has been deleted."L]; END ELSE exD.SysBug[]; -- someone else is holding onto a deleted display message. END; insert => Reload[dmIndex + 1]; replace => BEGIN IF insertionLength # deletionLength THEN Reload[dmIndex]; IF dm.haveMessage AND dispMess = displayMessage THEN BEGIN dsD.ClearRectangle[inD.leftMargin, inD.rightMargin, dm.topY, dm.bottomY]; Editor.RefreshSoThatFirstCharStartsLine [firstChar: 0, firstLine: dm.lines, mnp: dm]; END; END; ENDCASE => ERROR; ENDCASE => BEGIN -- > index -- SELECT action FROM delete => Reload[dmIndex - 1]; insert => Reload[dmIndex + 1]; replace => IF insertionLength # deletionLength THEN Reload[dmIndex]; ENDCASE => ERROR; END; END; ENDLOOP; RETURN[TRUE]; END; -- of ReplaceMail -- FixUpTOC: PROCEDURE [toc: TOCHandle, index: TOCIndex, action: inD.TOCChangeAction, fp: TOCFixedPartPtr, tocString: STRING] = BEGIN isFirstEntry: BOOLEAN; spaceRemaining, sizeOfNewEntry, sizeOfOldEntry: CARDINAL; hardTOCAddress: HardTOCAddress; [isFirstEntry, spaceRemaining, hardTOCAddress] _ GetTOCPage[toc, index]; sizeOfNewEntry _ IF action = delete THEN 0 ELSE String.WordsForString[tocString.length] + SIZE[TOCFixedPart]; sizeOfOldEntry _ IF action = insert THEN 0 ELSE WordsInTOCEntry[hardTOCAddress]; DO Process.Yield[]; SELECT sizeOfNewEntry FROM = 0 => {RemoveTOCEntry[toc, hardTOCAddress, spaceRemaining]; EXIT}; <= sizeOfOldEntry => BEGIN -- cannot happen if inserting only. IF sizeOfNewEntry < sizeOfOldEntry THEN MoveDownInTOCPage[toc.buffer, hardTOCAddress + sizeOfOldEntry, sizeOfOldEntry - sizeOfNewEntry, spaceRemaining]; IF action # delete THEN WriteEntryToTOC[toc, hardTOCAddress, fp, tocString]; EXIT; END; <= sizeOfOldEntry + spaceRemaining => BEGIN -- cannot occur on delete. MoveUpInTOCPage[toc.buffer, hardTOCAddress + sizeOfOldEntry, sizeOfNewEntry - sizeOfOldEntry, spaceRemaining]; WriteEntryToTOC[toc, hardTOCAddress, fp, tocString]; IF action = insert THEN IncrementTOCTable[toc, toc.logicalPageNumber]; EXIT; END; ENDCASE; IF action # insert THEN BEGIN RemoveTOCEntry[toc, hardTOCAddress, spaceRemaining]; -- Cannot result in an empty page! Otherwise, one of above select arms would be used. IF toc.bufferState = empty THEN exD.SysBug[]; spaceRemaining _ spaceRemaining - sizeOfOldEntry; END; action _ insert; sizeOfOldEntry _ 0; IF isFirstEntry THEN BEGIN [ , spaceRemaining, hardTOCAddress] _ GetTOCPage[toc, index - 1]; hardTOCAddress _ hardTOCAddress + WordsInTOCEntry[hardTOCAddress]; IF sizeOfNewEntry <= spaceRemaining THEN BEGIN -- put toc entry on previous page. WriteEntryToTOC[toc, hardTOCAddress, fp, tocString]; IncrementTOCTable[toc, toc.logicalPageNumber]; EXIT; END END; [isFirstEntry, hardTOCAddress, spaceRemaining] _ SplitTOCPage[toc, index, hardTOCAddress, spaceRemaining]; ENDLOOP; END; -- of FixUpTOC -- RemoveTOCEntry: PROCEDURE [toc: TOCHandle, hardTOCAddress: HardTOCAddress, spaceRemaining: CARDINAL] = -- Squeezes out the toc entry at hardTOCAddress, updates page in toc file and toc page -- table. If removing this toc entry results in a toc page with no entries, then the toc -- file is shifted down to squeeze out the entire page. BEGIN entryLength: CARDINAL; pn: PageNumber = toc.logicalPageNumber; tph: TOCPageHeader _ LOOPHOLE[toc.buffer]; IF toc.bufferState = empty THEN exD.SysBug[]; tph.numberOfEntries _ tph.numberOfEntries - 1; toc.indexFF _ toc.indexFF - 1; IF tph.numberOfEntries = 0 THEN BEGIN MakeBufferEmpty[toc]; toc.filePageFF _ toc.filePageFF - 1; FOR p: PageNumber IN [pn .. toc.filePageFF) DO Process.Yield[]; toc.buffer _ VMDefs.ReadPage[[toc.file, p + 1], 2]; VMDefs.RemapPage[toc.buffer, [toc.file, p]]; VMDefs.MarkStart[toc.buffer]; VMDefs.Release[toc.buffer]; toc.pageTable[p] _ [toc.pageTable[p + 1] - 1]; ENDLOOP; VMDefs.SetFileLength[toc.file, [toc.filePageFF, 0]]; RETURN; END; entryLength _ WordsInTOCEntry[hardTOCAddress]; MoveDownInTOCPage [toc.buffer, hardTOCAddress + entryLength, entryLength, spaceRemaining]; toc.bufferState _ dirty; FOR p: PageNumber IN (pn .. toc.filePageFF) DO toc.pageTable[p] _ [toc.pageTable[p] - 1]; ENDLOOP; END; -- of RemoveTOCEntry -- WriteEntryToTOC: PROCEDURE [toc: TOCHandle, hardTOCAddress: HardTOCAddress, fp: TOCFixedPartPtr, s: STRING] = BEGIN Inline.COPY[from: fp, nwords: SIZE[TOCFixedPart], to: hardTOCAddress]; Inline.COPY[from: s, nwords: String.WordsForString[s.length], to: hardTOCAddress + SIZE[TOCFixedPart]]; toc.bufferState _ dirty; END; -- of WriteEntryToTOC -- ResetTOCAddresses: PROCEDURE [toc: TOCHandle, key: CARDINAL, index: TOCIndex, insertionLength, deletionLength: CARDINAL, action: inD.TOCChangeAction] = BEGIN fp: TOCFixedPart; startIndex: TOCIndex; changedIndex: TOCIndex _ 0; pos: LONG CARDINAL; IF insertionLength = deletionLength THEN RETURN; startIndex _ IF action = insert THEN index ELSE index + 1; FOR i: TOCIndex IN [startIndex .. toc.indexFF) DO Process.Yield[]; GetTOCFixedPart[toc, key, i, @fp]; pos _ fp.firstPage * LONG[512] + fp.firstByte + insertionLength - deletionLength; fp.firstPage _ Inline.LowHalf[pos / 512]; fp.firstByte _ Inline.LowHalf[pos] MOD 512; PutTOCFixedPart[toc, key, i, @fp]; IF fp.changed AND changedIndex = 0 THEN changedIndex _ i; ENDLOOP; IF toc.firstChange >= index THEN toc.firstChange _ SELECT TRUE FROM changedIndex = 0 => 0, -- only changed item was just deleted. action = replace => changedIndex, -- indices don't change. action = delete => changedIndex - 1, -- indices will be decremented. ENDCASE => changedIndex + 1; -- insert, indices will be incremented. END; -- of ResetTOCAddresses -- WordsInTOCEntry: PROCEDURE [hPtr: HardTOCAddress] RETURNS [CARDINAL] = INLINE BEGIN fWords: CARDINAL = SIZE[TOCFixedPart]; RETURN[fWords + String.WordsForString[LOOPHOLE[hPtr + fWords, STRING].maxlength]]; END; -- of WordsInTOCEntry -- ReadTOCPage: PROCEDURE [toc: TOCHandle, pn: PageNumber] = BEGIN IF toc.bufferState # empty AND toc.logicalPageNumber = pn THEN RETURN; MakeBufferEmpty[toc]; toc.buffer _ VMDefs.ReadPage[[toc.file, pn]]; toc.logicalPageNumber _ pn; toc.bufferState _ clean; END; -- of ReadTOCPage -- GetTOCPage: PROCEDURE [toc: TOCHandle, index: TOCIndex] RETURNS [isFirstEntry: BOOLEAN, spaceRemaining: CARDINAL, hardTOCAddress: HardTOCAddress] = -- Returns buffer with the page from the table of contents file that contains index. Special -- case for index = indexFF: always returns last good page of the TOC file. BEGIN hta: HardTOCAddress; nEntries, entryOnPage: CARDINAL; tph: TOCPageHeader; pn: PageNumber _ SearchTOCTable[toc, index - (IF index # toc.indexFF THEN 0 ELSE 1)]; ReadTOCPage[toc, pn]; tph _ LOOPHOLE[toc.buffer]; hardTOCAddress _ hta _ tph + SIZE[TOCPageHeaderBlk]; nEntries _ tph.numberOfEntries; entryOnPage _ index - toc.pageTable[pn]; FOR i: CARDINAL IN [0 .. nEntries) DO hta _ hta + WordsInTOCEntry[hta]; IF i + 1 = entryOnPage THEN hardTOCAddress _ hta; ENDLOOP; isFirstEntry _ toc.pageTable[pn] = index; spaceRemaining _ 256 - (hta - tph); END; -- of GetTOCPage -- SplitTOCPage: PROCEDURE [toc: TOCHandle, index: TOCIndex, hardTOCAddress: HardTOCAddress, spaceRemaining: CARDINAL] RETURNS [isFirstEntry: BOOLEAN, newHardTOCAddress: HardTOCAddress, newSpaceRemaining: CARDINAL] = -- toc.buffer contains a TOC page. SplitTOCPage causes all entries at index and beyond to -- be moved to a new page. If index is the index of the first entry not on this page, -- then a blank TOC page (containing only the TOCPageHeader) is created. toc.buffer -- will contain the new page upon return. This procedure will cause the TOC file to -- increase in length by one page. All TOC data structures will be updated to reflect the -- entries in each page after the split is performed. BEGIN nEntries, entryOnPage, delta, hardTOCAddressOffset: CARDINAL; pn, startPn, basePn: PageNumber; chunk: CARDINAL = 6; page: VMDefs.Page; tph: TOCPageHeader _ LOOPHOLE[toc.buffer]; IF toc.bufferState = empty THEN exD.SysBug[]; nEntries _ tph.numberOfEntries; startPn _ toc.logicalPageNumber; hardTOCAddressOffset _ hardTOCAddress - toc.buffer; entryOnPage _ index - toc.pageTable[startPn]; VMDefs.SetFileLength[toc.file, [toc.filePageFF + 1, 0]]; pn _ toc.filePageFF; toc.filePageFF _ toc.filePageFF + 1; MakeBufferEmpty[toc]; VMDefs.StartFile[toc.file]; -- to not lose previous write to startPn when it is Remap'ed. VMDefs.WaitFile[toc.file]; UNTIL pn = startPn DO -- must be FALSE first time. basePn _ IF pn < chunk THEN startPn ELSE MAX[startPn, pn - chunk]; VMDefs.StartReading[[toc.file, basePn], pn - basePn - 1]; UNTIL pn = basePn DO Process.Yield[]; pn _ pn - 1; page _ VMDefs.ReadPage[[toc.file, pn]]; VMDefs.RemapPage[page, [toc.file, pn + 1]]; VMDefs.MarkStart[page]; VMDefs.Deactivate[page]; toc.pageTable[pn + 1] _ toc.pageTable[pn]; ENDLOOP; ENDLOOP; IF entryOnPage # nEntries THEN BEGIN -- must move some entries from page to new page. page _ VMDefs.ReadPage[[toc.file, startPn], 1]; tph _ LOOPHOLE[page]; tph.numberOfEntries _ entryOnPage; VMDefs.MarkStart[page]; VMDefs.Release[page]; END; ReadTOCPage[toc, startPn + 1]; tph _ LOOPHOLE[toc.buffer]; tph.garbageDetector _ TOCType; newHardTOCAddress _ tph + SIZE[TOCPageHeaderBlk]; isFirstEntry _ TRUE; tph.numberOfEntries _ nEntries _ nEntries - entryOnPage; delta _ hardTOCAddressOffset - SIZE[TOCPageHeaderBlk]; IF nEntries > 0 THEN MoveDownInTOCPage [toc.buffer, toc.buffer + hardTOCAddressOffset, delta, spaceRemaining]; toc.bufferState _ dirty; toc.pageTable[startPn + 1] _ [toc.pageTable[startPn] + entryOnPage]; newSpaceRemaining _ spaceRemaining + delta; END; -- of SplitTOCPage -- MoveDownInTOCPage: PROC [base, start: POINTER, delta, spaceRemaining: CARDINAL] = BEGIN Inline.COPY[from: start, nwords: 256 - spaceRemaining - (start - base), to: start - delta]; END; -- of MoveDownInTOCPage -- MoveUpInTOCPage: PROC [base, start: POINTER, delta, spaceRemaining: CARDINAL] = BEGIN from: POINTER TO ARRAY OF WORD _ start; to: POINTER TO ARRAY OF WORD _ start + delta; nWords: CARDINAL _ 256 - spaceRemaining - (start - base); FOR i: CARDINAL DECREASING IN [0 .. nWords) DO to[i] _ from[i]; ENDLOOP; END; -- of MoveUpInTOCPage -- IncrementTOCTable: PROCEDURE [toc: TOCHandle, pn: PageNumber] = -- Rewrites page to the toc file to indicate that it contains an additional entry. Increments -- the first index for each toc page after page in the toc page table. BEGIN tph: TOCPageHeader; ReadTOCPage[toc, pn]; tph _ LOOPHOLE[toc.buffer]; tph.numberOfEntries _ tph.numberOfEntries + 1; toc.bufferState _ dirty; toc.indexFF _ toc.indexFF + 1; FOR p: PageNumber IN (pn .. toc.filePageFF) DO toc.pageTable[p] _ [toc.pageTable[p] + 1]; ENDLOOP; END; -- of IncrementTOCTable -- END. -- of ReplaceMailOp --z20461(529)\f1