-- file VirtSS.Mesa
-- edited by Schroeder, January 18, 1981 3:08 PM.
-- edited by Brotz, November 30, 1981 10:18 PM
-- edited by Taft, May 8, 1983 4:21 PM

DIRECTORY
Core USING [Close, Delete, Open],
exD: FROM "ExceptionDefs" USING [SysBug],
Process USING [Yield],
Storage USING [Free, Node],
tfD: FROM "TempFileDefs" USING [FreeTempFile],
vmD: FROM "VirtualMgrDefs" USING [bugTrapValue, CleanupTOCOption,
CMOCharMapTable, ComposedMessageObject, ComposedMessagePtr,
DisplayMessageObject, DisplayMessagePtr, DMList, DMListBlk, ExtendTOC,
FindTOCAddress, GetBuffer, LoadTOCOption, MakeBufferEmpty, PageNumber, TOC,
TOCAddressOnPage, TOCFixedPart, TOCFixedPartPtr, TOCHandle, TOCIndex,
TOCPageHeader, TOCPageTable, TOCPageTableSize, TOCType, VirtualMessagePtr,
VirtualObject, VoidCharCache],
VMDefs USING [FileHandle, GetFileLength, PageNumber, Position, Release, SetFileLength];

VirtSS: PROGRAM
IMPORTS Core, exD, Process, Storage, tfD, vmD, VMDefs
EXPORTS vmD =

BEGIN
OPEN vmD;

-- Data Structures and Types.

vMORoot: VirtualMessagePtr ← NIL;


-- This is VirtSS, Start/Stop routines. See also VirtTOC.


TOCOverflow: PUBLIC ERROR = CODE;


CreateTOC: PUBLIC PROCEDURE RETURNS [toc: TOCHandle] =
BEGIN
toc ← Storage.Node[SIZE[TOC]];
toc↑ ← VirtualObject
[file: NIL,
buffer: NIL,
logicalPageNumber: 0,
bufferState: empty,
open: FALSE,
vOVar: TOC
[pageTable: Storage.Node[SIZE[TOCPageTable]],
filePageFF: 0, -- Next avail page in TOC file.
indexFF: 1, -- Next avail message index.
firstChange: 0, -- No changes yet.
mailFile: NIL,
dmList: Storage.Node[SIZE[DMListBlk]],
runArray: NIL,
runs: 0,
maxRuns: 0,
key: 0,
keyHolder: NIL]];
toc.dmList↑ ← DMListBlk[next: NIL, dm: NIL];
END; -- of CreateTOC --


DestroyTOC: PUBLIC PROCEDURE [toc: TOCHandle, key: CARDINAL] =
BEGIN
dml, nextDml: DMList;
IF key = 0 OR key # toc.key THEN exD.SysBug[];
Storage.Free[toc.pageTable];
IF toc.runArray # NIL THEN Storage.Free[toc.runArray];
IF toc.bufferState # empty THEN VMDefs.Release[toc.buffer];
FOR dml ← toc.dmList, nextDml UNTIL dml = NIL DO
nextDml ← dml.next;
IF dml.dm # NIL THEN exD.SysBug[];
Storage.Free[dml];
ENDLOOP;
Storage.Free[toc];
END; -- of DestroyTOC --


LoadTOC: PUBLIC PROCEDURE [toc: TOCHandle, key: CARDINAL, TOCFileName: STRING,
mailFileHandle: VMDefs.FileHandle, option: LoadTOCOption]
RETURNS [firstUnSeen: TOCIndex] =
-- Initializes the TOC virtual structure from the indicated file. If the file doesn’t contain
-- a recognizable TOC then an empty TOC is created in the file. Before calling this
-- procedure, the client is expected to have called CleanupTOC if another TOC
-- previously was in use. firstUnSeen returns the index of the first unseen entry found
-- in the TOC, or 0 if no unseen entries are found. If option=new then an empty TOC
-- is created in the file provided.
-- May raise TOCOverflow if the mail file contains more messages than can be held in
-- the TOC.
BEGIN
pn: PageNumber;
position: VMDefs.Position;
pageHeader: TOCPageHeader;
index: TOCIndex;
fp: TOCFixedPartPtr;
zerothFP: TOCFixedPart ← [FALSE, FALSE, FALSE, FALSE, ’~, 0, 0, bugTrapValue, 0, 0];

BEGIN -- for EXITS

DoSomeYields: PROCEDURE =
{THROUGH [1 .. 3] DO Process.Yield[]; ENDLOOP};

IF toc.open OR key = 0 OR key # toc.key THEN exD.SysBug[];
toc.file ← Core.Open[TOCFileName, update];

DoSomeYields[];

toc.open ← TRUE;
--now a call to CleanupTOC[dontResetChanges or delete] will work.

position ← VMDefs.GetFileLength[toc.file];
pn ← firstUnSeen ← toc.indexFF ← toc.firstChange ← 0;
toc.filePageFF ← position.page;
--now a call to CleanupTOC[resetChanges] will work.

toc.mailFile ← mailFileHandle;

IF option = new OR position.page ~IN (0 .. TOCPageTableSize] OR position.byte # 0
THEN GOTO truncateTOC;

FOR pn IN [0 .. position.page) DO
DoSomeYields[];
GetBuffer[toc, pn, FALSE];
pageHeader ← LOOPHOLE[toc.buffer];
IF pageHeader.numberOfEntries = 0 OR pageHeader.garbageDetector # TOCType
THEN {MakeBufferEmpty[toc]; GOTO truncateTOC};
toc.pageTable[pn].firstTOCIndex ← toc.indexFF; -- 1st entry on page.
toc.indexFF ← toc.indexFF + pageHeader.numberOfEntries;
FOR index IN [toc.pageTable[pn] .. toc.indexFF)
WHILE toc.firstChange = 0 OR firstUnSeen = 0 DO
fp ← TOCAddressOnPage[toc, index, toc.buffer, pn];
IF fp.changed AND toc.firstChange = 0 THEN toc.firstChange ← index;
IF ~fp.seen AND firstUnSeen = 0 THEN firstUnSeen ← index;
ENDLOOP;
ENDLOOP;

EXITS
truncateTOC =>
BEGIN
IF position.page # pn THEN VMDefs.SetFileLength[toc.file, [pn, 0]];
toc.filePageFF ← pn;
IF pn = 0 THEN ExtendTOC[toc, key, @zerothFP, "dummy"L];
END;
END; -- for EXITS
END; -- of LoadTOC --


CleanupTOC: PUBLIC PROCEDURE
[toc: TOCHandle, key: CARDINAL, option: CleanupTOCOption] =
BEGIN
pageHeader: TOCPageHeader;
lastPage: PageNumber;

IF key = 0 OR key # toc.key THEN exD.SysBug[];
IF toc.open THEN
BEGIN
toc.open ← FALSE;
IF option = delete THEN GOTO deleteTOC;
IF option = resetChanges AND toc.firstChange # 0 THEN
BEGIN
[] ← FindTOCAddress[toc, toc.firstChange];
lastPage ← toc.logicalPageNumber;
pageHeader ← LOOPHOLE[toc.buffer];
pageHeader.numberOfEntries ← toc.firstChange - toc.pageTable[lastPage];
toc.bufferState ← dirty;
IF pageHeader.numberOfEntries = 0 THEN lastPage ← lastPage - 1;
END
ELSE lastPage ← toc.filePageFF - 1;
MakeBufferEmpty[toc];
IF option = resetChanges AND lastPage + 1 < toc.filePageFF
THEN VMDefs.SetFileLength[toc.file, [lastPage + 1, 0]];
Core.Close[toc.file];

EXITS
deleteTOC =>
BEGIN
IF toc.bufferState # empty THEN VMDefs.Release[toc.buffer];
toc.bufferState ← empty;
Core.Delete[toc.file];
END;
END; -- of IF toc.open.
toc.mailFile ← NIL;
END; -- of CleanupTOC --


SetTOCValidity: PUBLIC PROCEDURE [toc: TOCHandle, valid: BOOLEAN] =
BEGIN
pageHeader: TOCPageHeader;
GetBuffer[toc, 0, FALSE];
pageHeader ← LOOPHOLE[toc.buffer];
pageHeader.garbageDetector ← TOCType + (IF valid THEN 0 ELSE 1);
toc.bufferState ← dirty;
MakeBufferEmpty[toc];
END; -- of SetTOCValidity --


AllocateDisplayMessageObject: PUBLIC PROCEDURE RETURNS [dm: DisplayMessagePtr] =
BEGIN
dm ← Storage.Node[SIZE[DisplayMessageObject]];
dm↑ ← VirtualObject
[file: NIL, buffer: NIL, logicalPageNumber: 0, bufferState: empty, open: FALSE,
vOVar: VMO[next: vMORoot, textLength: 0, get: , formatStart: 0, formatEnd: 0,
vMOVar: DM[firstPage: 0, firstByte: 0, toc: NIL, index: 0]]];
vMORoot ← dm; -- Splice new one into the list.
END; -- of AllocateDisplayMessageObject --


FreeVirtualMessageObject: PUBLIC PROCEDURE [vm: VirtualMessagePtr] =
-- DM or CM.
BEGIN
p, back: VirtualMessagePtr;
IF (p ← vMORoot) = vm THEN vMORoot ← vm.next -- Was at head.
ELSE BEGIN
UNTIL p = NIL DO
back ← p;
p ← p.next;
IF p = vm THEN EXIT;
REPEAT
FINISHED => exD.SysBug[];
ENDLOOP;
back.next ← vm.next; --Splice it out.
END;
IF vm.bufferState # empty THEN VMDefs.Release[vm.buffer];
WITH msg: vm SELECT FROM
DM => IF msg.open THEN exD.SysBug[];
CM =>
{Storage.Free[msg.charMap]; IF msg.file # NIL THEN tfD.FreeTempFile[msg.file]};
ENDCASE => exD.SysBug[];
Storage.Free[vm]
END; -- of FreeVirtualMessageObject --


CleanupCMs: PUBLIC PROCEDURE =
-- Truncates all backing files.
BEGIN
vm: VirtualMessagePtr;
FOR vm ← vMORoot, vm.next UNTIL vm = NIL DO
WITH msg: vm SELECT FROM
DM => NULL;
CM => BEGIN
IF msg.bufferState # empty THEN VMDefs.Release[msg.buffer];
msg.bufferState ← empty;
IF msg.file # NIL THEN tfD.FreeTempFile[msg.file];
msg.open ← FALSE;
END;
ENDCASE => exD.SysBug[];
ENDLOOP;
END; -- of CleanupCMs.


InvalidateCaches: PUBLIC PROCEDURE =
BEGIN
FOR vm: VirtualMessagePtr ← vMORoot, vm.next UNTIL vm = NIL DO
vmD.VoidCharCache[@vm.get];
vmD.MakeBufferEmpty[vm];
ENDLOOP;
END; -- InvalidateCaches


AllocateComposedMessageObject: PUBLIC
PROCEDURE RETURNS [cm: ComposedMessagePtr] =
BEGIN
cm ← Storage.Node[SIZE[ComposedMessageObject]];
cm↑ ← VirtualObject
[file: NIL, buffer: NIL, logicalPageNumber: 0, bufferState: empty, open: FALSE,
vOVar: VMO[next: vMORoot, textLength: , get: , formatStart: 0, formatEnd: 0,
vMOVar: CM[inserting: FALSE, insertionStart: , insertionStop: ,
charMap: Storage.Node[SIZE[CMOCharMapTable]], filePageFF: 0]]];
vMORoot ← cm; -- Splice new one into the list.
END; -- of AllocateComposedMessageObject --


END. -- of VirtSS.