-- file: InsertMailMain.Mesa
-- edited by Brotz, December 15, 1980 11:29 AM

DIRECTORY
Ascii,
crD: FROM "CoreDefs",
dsD: FROM "DisplayDefs",
exD: FROM "ExceptionDefs",
inD: FROM "InteractorDefs",
Inline,
InsertMailDefs,
intCommon: FROM "IntCommon",
MailParse,
mfD: FROM "MailFormatDefs",
opD: FROM "OperationsDefs",
ovD: FROM "OverviewDefs",
Storage,
String,
tsD: FROM "TOCSelectionDefs",
vmD: FROM "VirtualMgrDefs";

InsertMailMain: PROGRAM
IMPORTS crD, dsD, exD, inD, Inline, intC: intCommon, mfD, Storage, String,
tsD, vmD
EXPORTS InsertMailDefs
SHARES vmD =

BEGIN
OPEN crD, vmD;

-- Purpose: a runnable bcd that inserts the current composed message into the current mail
-- file. The message will be inserted just before the first message selected in the TOC, or
-- if no messages are selected, at the end of the mail file. A stamp for this message is
-- created. The table of contents file is also edited to contain an entry for this new
-- message. No cleanup actions are performed on the current mail file or table of contents
-- file.


bytesPerStamp: CARDINAL = 24;


InsertVMInMailFile: PUBLIC PROCEDURE
[message: vmD.VirtualMessagePtr, tocIndex: TOCIndex ← 0] =
BEGIN
cmLength: CARDINAL ← GetMessageSize[message];
messageLength: CARDINAL ← cmLength;
insertionLength: CARDINAL ← messageLength + bytesPerStamp;
tocString: STRING ← [opD.maxTOCStringLength];
tocPtr: POINTER TO TOC;
tocPage, mailPage, newMailPage, insertPage, destPage: PageNumber;
mailByte, newMailByte, insertByte: PageByte;
extraPages, extraMailPages: CARDINAL;
tocWord: CARDINAL[0 .. 256];
needNewTocPage: BOOLEAN;
mailFile, tocFile: UFileHandle;
fp, newEntryTOCFp: TOCFixedPart;
hdrHandle: mfD.Handle;

charIndex: ovD.CharIndex ← 0;
NextCharForParseHeader: PROCEDURE RETURNS [char: CHARACTER] =
BEGIN
IF charIndex >= cmLength THEN RETURN[MailParse.endOfInput];
char ← GetMessageChar[message, charIndex];
charIndex ← charIndex + 1;
END; -- of NextCharForParseHeader --

BackupCharForParseHeader: PROCEDURE =
BEGIN
charIndex ← charIndex - 1;
END; -- of BackupCharForParseHeader --

FindTOCInsertionPoint: PROCEDURE [index: TOCIndex, string: STRING]
RETURNS [page: PageNumber, word: CARDINAL[0 .. 256], needNewPage: BOOLEAN] =
BEGIN
erc: ovD.ErrorCode;
roomNeeded: CARDINAL = SIZE[TOCFixedPart] + String.WordsForString[string.length];
buffer: POINTER ← Storage.Pages[1];
tocPageHdr: POINTER TO TOCPageHdr ← LOOPHOLE[buffer];
firstFreeWord: CARDINAL;
IF tocPtr.indexFF = 0 OR index > tocPtr.indexFF THEN exD.SysBug[];
FOR page ← 1, page + 1 UNTIL page >= tocPtr.filePageFF DO
IF tocPtr.pageTableHeader[page] > index THEN EXIT;
ENDLOOP;
page ← page - 1;
-- page is now the page on which index is found.

IF page > 0 AND tocPtr.pageTableHeader[page] = index THEN
BEGIN -- it’s the first entry on page. Maybe it will fit on the previous page.
[erc, ] ← ReadPages[buffer, 512, page - 1, tocFile];
IF erc # ovD.ok THEN exD.SysBug[];
firstFreeWord ← FirstFreeWord[buffer, tocPageHdr.numberOfEntries];
IF roomNeeded + firstFreeWord <= 256 THEN
{Storage.FreePages[buffer]; RETURN[page - 1, firstFreeWord, FALSE]};
END;

-- Check if new entry will fit on page.
[erc, ] ← ReadPages[buffer, 512, page, tocFile];
IF erc # ovD.ok THEN exD.SysBug[];
firstFreeWord ← FirstFreeWord[buffer, tocPageHdr.numberOfEntries];
needNewPage ← (firstFreeWord + roomNeeded > 256);
word ← FirstFreeWord[buffer, index - tocPtr.pageTableHeader[page]];
Storage.FreePages[buffer];
END; -- of FindTOCInsertionPoint --

-- WriteCompactionMark: PROCEDURE =
--
BEGIN
--
buffer: POINTER ← Storage.Pages[1];
--
array: POINTER TO PACKED ARRAY [0 .. 0) OF CHARACTER ← LOOPHOLE[buffer];
--
erc: ovD.ErrorCode;
--
IF mailByte # 0 THEN
--
BEGIN
--
[erc, ] ← crD.ReadPages[buffer, 512, mailPage, mailFile];
--
IF erc # ovD.ok THEN exD.SysBug[];
--
END;
--
array[mailByte] ← 003C;
--
erc ← WritePages[buffer, mailByte + 1, mailPage, mailFile];
--
IF erc # ovD.ok THEN exD.SysBug[];
--
Storage.FreePages[buffer];
--
END;

MoveUpInMailFile: PROCEDURE =
BEGIN
bufferSize: CARDINAL = 6;
outBuffer: POINTER ← Storage.Pages[bufferSize];
outArray: POINTER TO PACKED ARRAY [0 .. 0) OF CHARACTER
← LOOPHOLE[outBuffer];
inBuffer: POINTER ← Storage.Pages[bufferSize];
inArray: POINTER TO PACKED ARRAY [0 .. 0) OF CHARACTER
← LOOPHOLE[inBuffer];
inPage, outPage, inPageBase, outPageBase: PageNumber;
outByte, inByte, outBufferBytes: CARDINAL;

ReadInBuffer: PROCEDURE =
BEGIN
erc: ovD.ErrorCode;
inPageBase ← IF inPage < bufferSize THEN insertPage
ELSE MAX[insertPage, inPage - bufferSize];
inByte ← (inPage - inPageBase - 1) * 512 + (IF inByte > 0 THEN inByte ELSE 512);
inPage ← inPageBase;
[erc, ] ← ReadPages[inBuffer, bufferSize * 512, inPageBase, mailFile];
-- always read a full buffer, since reading past eof does no harm.
IF erc # ovD.ok THEN exD.SysBug[];
END; -- of ReadInBuffer --

WriteOutBuffer: PROCEDURE =
BEGIN
erc: ovD.ErrorCode;
erc ← WritePages[outBuffer, outBufferBytes, outPageBase, mailFile];
IF erc # ovD.ok THEN exD.SysBug[];
outPage ← outPageBase;
outPageBase ← IF outPage < bufferSize THEN destPage
ELSE MAX[destPage, outPage - bufferSize];
outByte ← outBufferBytes ← (outPage - outPageBase) * 512;
END; -- of WriteOutBuffer --

CopyBytes: PROCEDURE =
BEGIN
bytesToCopy: CARDINAL ← MIN[inByte, outByte];
inByte ← inByte - bytesToCopy;
outByte ← outByte - bytesToCopy;
FOR i: CARDINAL IN [0 .. bytesToCopy) DO
outArray[outByte + i] ← inArray[inByte + i];
ENDLOOP;
END; -- of CopyBytes --

inPage ← mailPage + 1;
inByte ← (mailByte + 1) MOD 512;
outPage ← newMailPage + 1;
outByte ← (newMailByte + 1) MOD 512;
destPage ← insertPage + (insertByte + insertionLength) / 512;
outPageBase ← IF outPage < bufferSize THEN destPage
ELSE MAX[destPage, outPage - bufferSize];
outByte ← outBufferBytes ← (outPage - outPageBase - 1) * 512
+ (IF outByte > 0 THEN outByte ELSE 512);
ReadInBuffer[];
DO
CopyBytes[];
IF (inByte = 0 AND inPageBase = insertPage)
OR (outByte = 0 AND outPageBase = destPage) THEN
BEGIN
IF destPage = insertPage THEN {outByte ← inByte ← insertByte; CopyBytes[]};
WriteOutBuffer[];
EXIT;
END;
IF inByte = 0 THEN ReadInBuffer[];
IF outByte = 0 THEN WriteOutBuffer[];
ENDLOOP;

Storage.FreePages[outBuffer];
Storage.FreePages[inBuffer];
END; -- of MoveUpInMailFile --

InsertStampAndMessage: PROCEDURE =
BEGIN
buffer: POINTER ← Storage.Pages[1];
array: POINTER TO PACKED ARRAY [0 .. 0) OF CHARACTER ← LOOPHOLE[buffer];
outPage: PageNumber ← insertPage;
outIndex: CARDINAL ← insertByte;
erc: ovD.ErrorCode;
stampIndex: CARDINAL ← 0;
stamp: STRING ← [bytesPerStamp];

PutCharsInStamp: PROCEDURE [char: CHARACTER] =
BEGIN
stamp[stampIndex] ← char;
stampIndex ← stampIndex + 1;
END; -- of PutCharsInStamp --

CopyBytesOfStamp: PROCEDURE =
BEGIN
erc: ovD.ErrorCode;
bytesToCopy: CARDINAL;
stampIndex ← 0;
UNTIL stampIndex = bytesPerStamp DO
bytesToCopy ← MIN[512 - outIndex, bytesPerStamp - stampIndex];
FOR i: CARDINAL IN [0 .. bytesToCopy) DO
array[outIndex + i] ← stamp[stampIndex + i];
ENDLOOP;
stampIndex ← stampIndex + bytesToCopy;
outIndex ← (outIndex + bytesToCopy) MOD 512;
erc ← WritePages[buffer, IF outPage = newMailPage THEN newMailByte + 1
ELSE 512, outPage, mailFile];
IF erc # ovD.ok THEN exD.SysBug[];
IF stampIndex = bytesPerStamp AND outIndex > 0 THEN EXIT;
outPage ← outPage + 1;
IF outPage = destPage THEN
BEGIN
[erc, ] ← ReadPages[buffer, 512, outPage, mailFile];
IF erc # ovD.ok THEN exD.SysBug[];
END;
ENDLOOP;
END; -- of CopyBytesOfStamp --

CopyBytesOfMessage: PROCEDURE =
BEGIN
erc: ovD.ErrorCode;
messageIndex: ovD.CharIndex ← 0;
bytesToCopy: CARDINAL;
get: POINTER TO CharCache ← @message.get;
char: CHARACTER;
UNTIL messageIndex = messageLength DO
bytesToCopy ← MIN[512 - outIndex, messageLength - messageIndex];
FOR i: CARDINAL IN [messageIndex .. messageIndex + bytesToCopy) DO
char ← Inline.BITAND[IF i IN [get.first .. get.free)
THEN get.string[i + get.floor - get.first]
ELSE GetMessageChar[message, i],
ovD.CharMask];
array[outIndex + i - messageIndex] ← char;
ENDLOOP;
messageIndex ← messageIndex + bytesToCopy;
outIndex ← (outIndex + bytesToCopy) MOD 512;
erc ← WritePages[buffer, IF outPage = newMailPage THEN newMailByte + 1
ELSE 512, outPage, mailFile];
IF erc # ovD.ok THEN exD.SysBug[];
IF messageIndex = messageLength THEN EXIT;
outPage ← outPage + 1;
IF outPage = destPage THEN
BEGIN
[erc, ] ← ReadPages[buffer, 512, outPage, mailFile];
IF erc # ovD.ok THEN exD.SysBug[];
END;
ENDLOOP;
END; -- of CopyBytesOfMessage --

newEntryTOCFp ← TOCFixedPart
[changed: TRUE,
deleted: FALSE,
seen: FALSE,
bogus: FALSE,
mark: ’ ,
firstPage: insertPage,
firstByte: insertByte,
bugTrap: bugTrapValue,
offsetToHeader: bytesPerStamp,
textLength: messageLength];
mfD.CreateStamp[@newEntryTOCFp, PutCharsInStamp];
stamp.length ← bytesPerStamp;

[erc, ] ← ReadPages[buffer, 512, insertPage, mailFile];
IF erc # ovD.ok THEN exD.SysBug[];
CopyBytesOfStamp[];
CopyBytesOfMessage[];
Storage.FreePages[buffer];
END; -- of InsertStampAndMessage --

FlushTOC: PROCEDURE =
BEGIN
erc: ovD.ErrorCode;
mtPtr: MemoryTableEntryPtr;
FOR mtPtr ← tocPtr.memoryHeader, mtPtr.next UNTIL mtPtr = NIL DO
IF mtPtr.state = dirty THEN
BEGIN
erc ← WritePages[mtPtr.address, 512, mtPtr.filePageNumber, tocFile];
IF erc # ovD.ok THEN exD.SysBug[];
END;
mtPtr.state ← unused;
ENDLOOP;
END; -- of FlushTOC --

ShiftTOCTail: PROCEDURE =
BEGIN
bufferSize: CARDINAL = 6;
buffer: POINTER ← Storage.Pages[bufferSize];
page: PageNumber ← tocPtr.filePageFF;
nPages: CARDINAL;
erc: ovD.ErrorCode;
UNTIL page = tocPage + 1 DO
nPages ← MIN[bufferSize, page - tocPage - 1];
page ← page - nPages;
[erc, ] ← ReadPages[buffer, nPages * 512, page, tocFile];
IF erc # ovD.ok THEN exD.SysBug[];
erc ← WritePages[buffer, nPages * 512, page + 1, tocFile];
IF erc # ovD.ok THEN exD.SysBug[];
ENDLOOP;
FOR p: PageNumber DECREASING IN (tocPage + 1 .. tocPtr.filePageFF] DO
tocPtr.pageTableHeader[p] ← tocPtr.pageTableHeader[p - 1];
ENDLOOP;
tocPtr.filePageFF ← tocPtr.filePageFF + 1;
Storage.FreePages[buffer];
END; -- of ShiftTOCTail --

InsertTOCEntry: PROCEDURE =
BEGIN
erc: ovD.ErrorCode;
roomNeeded: CARDINAL = SIZE[TOCFixedPart] + String.WordsForString[tocString.length];
tocPageBuffer: POINTER ← Storage.Pages[1];
tocPageArray: POINTER TO ARRAY [0 .. 0) OF WORD ← LOOPHOLE[tocPageBuffer];
tocPageHeader: POINTER TO TOCPageHdr ← LOOPHOLE[tocPageBuffer];
newEntryTOCFpArray: POINTER TO ARRAY [0 .. 0) OF WORD
← LOOPHOLE[@newEntryTOCFp];
newEntryTOCStringArray: POINTER TO ARRAY [0 .. 0) OF WORD
← LOOPHOLE[tocString];
nEntries, firstFreeWord, wordsToCopy: CARDINAL;
firstEntry: TOCIndex ← tocPtr.pageTableHeader[tocPage];
[erc, ] ← ReadPages[tocPageBuffer, 512, tocPage, tocFile];
IF erc # ovD.ok THEN exD.SysBug[];
nEntries ← tocPageHeader.numberOfEntries;
firstFreeWord ← FirstFreeWord[tocPageBuffer, nEntries];
wordsToCopy ← firstFreeWord - tocWord;
IF needNewTocPage THEN
BEGIN
nextPageBuffer: POINTER ← Storage.Pages[1];
nextPageArray: POINTER TO ARRAY [0 .. 0) OF WORD ← LOOPHOLE[nextPageBuffer];
nextPageHeader: POINTER TO TOCPageHdr ← LOOPHOLE[nextPageBuffer];
nextPageHeader.garbageDetector ← tOCType;
IF tocWord + roomNeeded <= 256 THEN
BEGIN -- new entry fits on tocPage.
FOR i: CARDINAL IN [0 .. wordsToCopy) DO
nextPageArray[SIZE[TOCPageHdr] + i] ← tocPageArray[tocWord + i];
ENDLOOP;
FOR i: CARDINAL IN [SIZE[TOCPageHdr] + wordsToCopy .. 256) DO
nextPageArray[i] ← 0;
ENDLOOP;
FOR i: CARDINAL IN [0 .. SIZE[TOCFixedPart]) DO
tocPageArray[tocWord + i] ← newEntryTOCFpArray[i];
ENDLOOP;
FOR i: CARDINAL IN [0 .. roomNeeded - SIZE[TOCFixedPart]) DO
tocPageArray[tocWord + SIZE[TOCFixedPart] + i] ← newEntryTOCStringArray[i];
ENDLOOP;
FOR i: CARDINAL IN [tocWord + roomNeeded .. 256) DO
tocPageArray[i] ← 0;
ENDLOOP;
tocPtr.pageTableHeader[tocPage + 1] ← [tocIndex]; -- will be incremented later.
tocPageHeader.numberOfEntries ← tocIndex - firstEntry + 1;
nextPageHeader.numberOfEntries ← nEntries - (tocIndex - firstEntry);
END
ELSE BEGIN -- new entry must go on next page.
FOR i: CARDINAL IN [0 .. SIZE[TOCFixedPart]) DO
nextPageArray[SIZE[TOCPageHdr] + i] ← newEntryTOCFpArray[i];
ENDLOOP;
FOR i: CARDINAL IN [0 .. roomNeeded - SIZE[TOCFixedPart]) DO
nextPageArray[SIZE[TOCPageHdr] + SIZE[TOCFixedPart] + i]
← newEntryTOCStringArray[i];
ENDLOOP;
FOR i: CARDINAL IN [0 .. wordsToCopy) DO
nextPageArray[SIZE[TOCPageHdr] + roomNeeded + i] ← tocPageArray[tocWord +i];
ENDLOOP;
FOR i: CARDINAL IN [SIZE[TOCPageHdr] + roomNeeded + wordsToCopy .. 256) DO
nextPageArray[i] ← 0;
ENDLOOP;
FOR i: CARDINAL IN [tocWord .. 256) DO
tocPageArray[i] ← 0;
ENDLOOP;
tocPtr.pageTableHeader[tocPage + 1] ← [tocIndex - 1]; -- will be incremented later.
tocPageHeader.numberOfEntries ← tocIndex - firstEntry;
nextPageHeader.numberOfEntries ← nEntries - (tocIndex - firstEntry) + 1;
END;
erc ← WritePages[nextPageBuffer, 512, tocPage + 1, tocFile];
IF erc # ovD.ok THEN exD.SysBug[];
Storage.FreePages[nextPageBuffer];
END
ELSE BEGIN -- insert all on tocPage
FOR i: CARDINAL DECREASING IN [0 .. wordsToCopy) DO
tocPageArray[tocWord + roomNeeded + i] ← tocPageArray[tocWord + i];
ENDLOOP;
FOR i: CARDINAL IN [firstFreeWord + roomNeeded .. 256) DO
tocPageArray[i] ← 0;
ENDLOOP;
FOR i: CARDINAL IN [0 .. SIZE[TOCFixedPart]) DO
tocPageArray[tocWord + i] ← newEntryTOCFpArray[i];
ENDLOOP;
FOR i: CARDINAL IN [0 .. roomNeeded - SIZE[TOCFixedPart]) DO
tocPageArray[tocWord + SIZE[TOCFixedPart] + i] ← newEntryTOCStringArray[i];
ENDLOOP;
tocPageHeader.numberOfEntries ← nEntries + 1;
END;
erc ← WritePages[tocPageBuffer, 512, tocPage, tocFile];
IF erc # ovD.ok THEN exD.SysBug[];
FOR p: PageNumber IN (tocPage .. tocPtr.filePageFF) DO
tocPtr.pageTableHeader[p] ← [tocPtr.pageTableHeader[p] + 1];
ENDLOOP;
tocPtr.indexFF ← tocPtr.indexFF + 1;
tocPtr.firstChange ← MIN[tocPtr.firstChange, tocIndex];
FOR t: TOCIndex IN (tocIndex .. tocPtr.indexFF) DO
GetTOCFixedPart[t, @fp];
fp.firstPage ← fp.firstPage + (fp.firstByte + insertionLength) / 512;
fp.firstByte ← (fp.firstByte + insertionLength) MOD 512;
PutTOCFixedPart[t, @fp];
ENDLOOP;
Storage.FreePages[tocPageBuffer];
END; -- of InsertTOCEntry --

-- main code for InsertCMInMailFile.

tocPtr ← GetTOCPtr[];
IF ~intC.cmTextNbr.haveMessage OR ~intC.haveMailFile OR ~tocPtr.open THEN
{exD.DisplayExceptionString["No message or mail file"L]; RETURN};

mailFile ← tocPtr.mailFile;
tocFile ← tocPtr.file;

hdrHandle ← mfD.InitializeParseHeader[NextCharForParseHeader, BackupCharForParseHeader];
mfD.ParseHeaderForTOC[tocString, hdrHandle];
mfD.FinalizeParseHeader[hdrHandle];

tocString↑ ← StringBody[length: tocString.length, maxlength: tocString.length, text: ];

-- Get TOC file insertion point.
FlushTOC[];
IF tocIndex = 0 THEN
tocIndex ← IF tsD.TOCSelectionEmpty[] THEN 1 ELSE tsD.LastSelectedEntry[] + 1;
[tocPage, tocWord, needNewTocPage] ← FindTOCInsertionPoint[tocIndex, tocString];

-- Compute extra space required for mail file and TOC file.
[ , mailPage, mailByte] ← UFileLength[mailFile];
extraMailPages ← (mailByte + insertionLength + 1) / 512;
extraPages ← extraMailPages + (IF needNewTocPage THEN 1 ELSE 0);
IF (tocPtr.filePageFF > tOCPageTableSize AND needNewTocPage)
OR mailFile.UFileObjectType = alto AND extraPages > CountAltoFreePages[] THEN
{exD.DisplayExceptionString["Insertion will not fit on disk on in TOC"L]; RETURN};

-- Get mail file insertion point.
IF tocIndex = tocPtr.indexFF THEN {insertPage ← mailPage; insertByte ← mailByte}
ELSE {GetTOCFixedPart[tocIndex, @fp]; insertByte ← fp.firstByte; insertPage ← fp.firstPage};

inD.IndicateCommandBusy[intC.mailFileCommandHouse];

newMailPage ← mailPage + (mailByte + insertionLength) / 512;
newMailByte ← (mailByte + insertionLength) MOD 512;

-- Write compaction mark at end of mail file.
-- WriteCompactionMark[];

vmD.SetTOCValidity[FALSE];

-- Shift mail file tail down by message + stamp length.
MoveUpInMailFile[];

-- Insert message and stamp.
InsertStampAndMessage[];
IF UFileTruncate[newMailPage, newMailByte, mailFile] # ovD.ok THEN exD.SysBug[];

-- Flush Virtual TOC buffers.
FlushTOC[];

-- Rewrite TOC entries in tail and shift TOC file tail down if necessary.
IF needNewTocPage THEN ShiftTOCTail[];

-- Insert new TOC entry.
InsertTOCEntry[];

vmD.SetTOCValidity[TRUE];


-- Redisplay TOC and DM.
tsD.SetTOCSelection[tocIndex];
dsD.ClearRectangle[inD.leftMargin, inD.rightMargin,
intC.tocTextNbr.topY, intC.tocTextNbr.bottomY];
inD.DisplayTOCTail[intC.tocTextNbr, intC.tocTextNbr.lines,
intC.tocTextNbr.lines.linePair.index, 1];
inD.UpdateTOCThumbLine[];
inD.IndicateCommandFinished[intC.mailFileCommandHouse];
intC.dmTextNbr.haveMessage ← FALSE;
intC.composedMessageEdited ← FALSE;
inD.IndicateCommandBusy[intC.displayCommandHouse];
inD.DisplayMessageCommand[intC.displayCommandHouse, TRUE];
inD.IndicateCommandFinished[intC.displayCommandHouse];

-- Done.
END; -- of InsertCMInMailFile --


FirstFreeWord: PROCEDURE [buffer: POINTER, entries: CARDINAL]
RETURNS [word: CARDINAL[0 .. 256]] =
BEGIN
p: TOCFixedPartPtr ← LOOPHOLE[buffer + SIZE[TOCPageHdr]];
THROUGH [1 .. entries] DO
p ← WordsInTOCEntry[p] + p;
ENDLOOP;
RETURN[p - buffer];
END; -- of FirstFreeWord --


WordsInTOCEntry: PROCEDURE [hPtr: HardTOCAddress] RETURNS [CARDINAL] =
-- Copied from VirtTOC.mesa.
BEGIN
fWords: CARDINAL = SIZE[TOCFixedPart];
s: STRING = LOOPHOLE[hPtr + fWords, STRING];
RETURN[fWords + String.WordsForString[s.maxlength]];
END; -- of WordsInTOCEntry --


END. -- of InsertMailMain --