-- file: ChollaCmdStub.mesa
-- Stub version for non-Cholla Laurel
-- edited by Taft, May 9, 1983 1:56 PM

DIRECTORY
AltoFile USING [DiskFull],
ccD: FROM "ChollaCmdDefs",
Editor,
exD: FROM "ExceptionDefs",
intCommon,
opD: FROM "OperationsDefs",
Process,
String,
Storage,
tsD: FROM "TOCSelectionDefs",
vmD: FROM "VirtualMgrDefs",
VMDefs;

ChollaCmdStub: MONITOR
IMPORTS AltoFile, exD, intC: intCommon, opD, Process, String, Storage, tsD, vmD, VMDefs
EXPORTS ccD
SHARES vmD =

BEGIN
OPEN ccD;

-- Create a TOC cache with a single entry (for the Laurel TOC), and define
-- LaurelTOCCacheType to index only that element. GetTOCForFile is fooled into doing
-- the right thing with only minor edits required.
-- (GetTOCForFile undoubtedly could be substantially simplified for the Laurel-only case,
-- but it’s extremely complex and should be touched only by someone who understands it.)

LaurelTOCCacheType: TYPE = TOCCacheType [laurel..laurel];
assertLaurelIsFirst: LaurelTOCCacheType = FIRST[TOCCacheType];

cacheTable: ARRAY LaurelTOCCacheType OF TOCCache ← [[]];


GetTOCForFile: PUBLIC PROCEDURE
[fileName: STRING, cacheType: TOCCacheType, usingA: BOOLEAN ← TRUE]
RETURNS [toc: vmD.TOCHandle, key: CARDINAL] =
BEGIN
worked: BOOLEAN;
IF cacheType#laurel THEN ERROR;
DO
[toc, key, worked] ← GetTOCForFileInner[fileName, cacheType, usingA];
IF worked THEN EXIT;
Process.Yield[]; Process.Yield[];
ENDLOOP;
END; -- of GetTOCForFile --


GetTOCForFileInner: ENTRY PROCEDURE
[fileName: STRING, cacheType: TOCCacheType, usingA: BOOLEAN ← TRUE]
RETURNS [toc: vmD.TOCHandle, key: CARDINAL, worked: BOOLEAN] =
-- Returns a toc corresponding to a mail file with "filename". ".mail" is added if no dot is
-- present in the "filename" parameter. "cacheType" determines which category of mail
-- file is requested; different categories do not interfere with each others replacement
-- strategy. "usingA" TRUE means that the toc is requested for user interaction; FALSE
-- means that the Cholla mail process is requesting this toc. Normally, the toc is returned
-- locked. If anything goes wrong, "toc" retruned is NIL.
-- A new toc requested from the same cacheType with the same "usingA" will cause the
-- previous toc held in that cacheType to be returned. To force a return of both tocs held
-- in the cacheType without getting a toc on a new file, call GetTOCForFile with
-- "filename" = NIL.
-- A note on which tocs are contained in which positions:
-- (Conservation of TOC data structure law.)
-- Motivation: there are several (9) distinct classes of mail file used by Cholla. Each class
-- generally needs two tocs (called a and b) available at all times. One of these tocs (a)
-- is used directly by the user, the other (b) is used by the Cholla mail process.
-- Sometimes a = b, in which case the "other" toc data structure is saved in the spare slot.
-- laurel cache is different because it might match any of the tocs in the other categories!
-- For all toc caches except the laurel toc cache,
-- if tocCache.spare = NIL, then a and b are different tocs.
-- if tocCache.spare # NIL, then a and b are the same, and tocCache.spare holds the
-- "other" toc not currently in use by this tocCache.
-- For the laurel toc cache, b is never used. If a matches any other toc in any other
-- cache, then spare contains the toc displaced from a.
-- In any case, a toc in the spare slot is closed.
BEGIN

MatchOtherCache: PROCEDURE RETURNS [cache: POINTER TO TOCCacheX] =
-- Returns the TOCCacheX that contains the toc we’re looking for.
BEGIN -- for laurel cache diddling only
-- FOR ct: TOCCacheType IN (FIRST[TOCCacheType] .. LAST[TOCCacheType]] DO
-- cache ← @cacheTable[ct].a;
-- IF cache.file # NIL AND String.EquivalentString[fname, cache.file]
-- THEN RETURN;
-- cache ← @cacheTable[ct].b;
-- IF cache.file # NIL AND String.EquivalentString[fname, cache.file]
-- THEN RETURN;
-- ENDLOOP;
RETURN[NIL];
END; -- of MatchOtherCache --

tocCache: TOCCachePtr ← @cacheTable[cacheType];
laurelCache: TOCCachePtr = @cacheTable[laurel];
match: POINTER TO TOCCacheX ← @laurelCache.a;
myT: POINTER TO TOCCacheX = IF usingA THEN @tocCache.a ELSE @tocCache.b;
hisT: POINTER TO TOCCacheX = IF usingA THEN @tocCache.b ELSE @tocCache.a;
myTocIsOpen: BOOLEAN ← TRUE;
fname: STRING ← [tocCacheFileStringLength];
laurel: BOOLEAN = (cacheType = laurel);
sameAsLaurelsFile: BOOLEAN;
IF laurel AND ~usingA THEN exD.SysBug[];
worked ← TRUE;
IF fileName # NIL THEN
BEGIN
String.AppendString[fname, fileName];
FOR i: CARDINAL IN [0 .. fname.length) DO
IF fname[i] = ’. THEN EXIT;
REPEAT
FINISHED => String.AppendString[fname, IF laurel THEN ".mail"L ELSE ".chml"L];
ENDLOOP;
END;
sameAsLaurelsFile
← laurelCache.a.file # NIL AND String.EquivalentString[fname, laurelCache.a.file];
IF fileName = NIL OR (laurel AND sameAsLaurelsFile) THEN
BEGIN
keyA, keyB, keySpare: CARDINAL ← 0;
bNotEqualA: BOOLEAN ← tocCache.b.toc # NIL AND tocCache.b.toc # tocCache.a.toc;

UnlockABAndSpare: PROCEDURE =
BEGIN
IF keyA # 0 THEN vmD.UnlockTOC[tocCache.a.toc, keyA];
IF keyB # 0 THEN vmD.UnlockTOC[tocCache.b.toc, keyB];
IF keySpare # 0 THEN vmD.UnlockTOC[tocCache.spare, keySpare];
END; -- of UnlockABAndSpare --

IF tocCache.a.toc # NIL THEN
BEGIN
keyA ← vmD.LockTOC[tocCache.a.toc];
IF keyA = 0 THEN {UnlockABAndSpare[]; RETURN[NIL, 0, FALSE]};
END;
IF bNotEqualA THEN
BEGIN
keyB ← vmD.LockTOC[tocCache.b.toc];
IF keyB = 0 THEN {UnlockABAndSpare[]; RETURN[NIL, 0, FALSE]};
END;
IF tocCache.spare # NIL THEN
BEGIN
keySpare ← vmD.LockTOC[tocCache.spare];
IF keySpare = 0 THEN {UnlockABAndSpare[]; RETURN[NIL, 0, FALSE]};
END;
SELECT TRUE FROM
laurelCache.spare = NIL => NULL; -- laurelCache.a.toc # other toc cache’s toc.
laurel => -- some other cache holds laurelCache.a.toc.
BEGIN
IF keyA # 0 THEN {vmD.UnlockTOC[tocCache.a.toc, keyA]; keyA ← 0};
tocCache.a.toc ← NIL;
END;
ENDCASE =>
BEGIN -- not laurel’s toc cache. Check if this duplicates laurel’s toc cache
IF String.EquivalentString[tocCache.a.file, laurelCache.a.file] THEN
BEGIN
IF keyA # 0 THEN {vmD.UnlockTOC[tocCache.a.toc, keyA]; keyA ← 0};
tocCache.a.toc ← laurelCache.spare;
IF tocCache.a.toc # NIL THEN
{keyA ← vmD.LockTOC[tocCache.a.toc]; IF keyA = 0 THEN exD.SysBug[]};
laurelCache.spare ← NIL;
END;
IF String.EquivalentString[tocCache.b.file, laurelCache.a.file] THEN
BEGIN
IF keyB # 0 THEN {vmD.UnlockTOC[tocCache.b.toc, keyB]; keyB ← 0};
tocCache.b.toc ← laurelCache.spare;
IF tocCache.b.toc # NIL THEN
{keyB ← vmD.LockTOC[tocCache.b.toc]; IF keyB = 0 THEN exD.SysBug[]};
laurelCache.spare ← NIL;
END;
-- Note cleverness if a=b=laurel.a : result is laurel.spare = NIL, b = NIL, a = old spare,
-- original 3-way shared toc remains in laurel.a.
END;
IF tocCache.spare # NIL THEN ReturnAndDestroyTOC[tocCache.spare, keySpare];
IF tocCache.a.toc # NIL THEN ReturnAndDestroyTOC[tocCache.a.toc, keyA];
IF tocCache.b.toc # NIL AND tocCache.b.toc # tocCache.a.toc THEN
ReturnAndDestroyTOC[tocCache.b.toc, keyB];
IF myT.file # NIL THEN Storage.FreeString[myT.file];
IF hisT.file # NIL THEN Storage.FreeString[hisT.file];
tocCache↑ ← [];
IF fileName = NIL THEN RETURN[NIL, 0, TRUE];
END;
IF myT.toc = NIL THEN
BEGIN
myT.toc ← vmD.CreateTOC[];
myT.file ← Storage.String[tocCacheFileStringLength];
myTocIsOpen ← FALSE;
END;
toc ← myT.toc;
key ← vmD.LockTOC[toc];
IF key = 0 THEN RETURN[NIL, 0, FALSE];
IF myTocIsOpen THEN
SELECT TRUE FROM
String.EquivalentString[myT.file, fname] => RETURN;
toc = hisT.toc
OR (laurel AND laurelCache.spare # NIL)
OR (~laurel AND toc = laurelCache.a.toc) =>
BEGIN -- toc is a duplicate of some other toc. Don’t return it, get a toc from a spare.
cacheForSpare: TOCCachePtr ← IF toc = hisT.toc THEN tocCache ELSE laurelCache;
myT.toc ← cacheForSpare.spare;
cacheForSpare.spare ← NIL;
vmD.UnlockTOC[toc, key];
toc ← myT.toc;
key ← vmD.LockTOC[toc];
IF key = 0 THEN exD.SysBug[];
END;
myT.file.length = 0 => exD.SysBug[];
ENDCASE => opD.ReturnMailFileOperation[toc, key];
myT.file.length ← 0;
IF toc.open THEN exD.SysBug[];
SELECT TRUE FROM
hisT.toc # NIL AND String.EquivalentString[fname, hisT.file] =>
BEGIN
vmD.UnlockTOC[toc, key];
tocCache.spare ← toc;
toc ← myT.toc ← hisT.toc;
key ← vmD.LockTOC[toc];
END;
(laurel AND (match ← MatchOtherCache[]) # NIL) OR (~laurel AND sameAsLaurelsFile)
=>
BEGIN
vmD.UnlockTOC[toc, key];
laurelCache.spare ← toc;
toc ← myT.toc ← match.toc;
key ← vmD.LockTOC[toc];
END;
~CallGetOp[toc, key, fname] =>
BEGIN
vmD.DestroyTOC[toc, key];
myT.toc ← NIL;
Storage.FreeString[myT.file];
myT.file ← NIL;
RETURN[NIL, 0, TRUE];
END;
ENDCASE;
String.AppendString[myT.file, fname]; --must be here!
IF key = 0 THEN RETURN[NIL, 0, FALSE];
END; -- of GetTOCForFileInner --


ReturnAndDestroyTOC: PROCEDURE [toc: vmD.TOCHandle, key: CARDINAL] =
BEGIN
IF toc = NIL THEN RETURN;
IF toc.open THEN opD.ReturnMailFileOperation[toc, key];
vmD.DestroyTOC[toc, key];
END; -- of ReturnAndDestroyTOC --


CallGetOp: PROCEDURE [toc: vmD.TOCHandle, key: CARDINAL, fileName: STRING]
RETURNS [successful: BOOLEAN] =
-- Encapsulates GetMailFileOperation call and Error catches. Returns LAST[CARDINAL]
-- in "firstUnSeenTOCIndex" if the operation failes.
BEGIN
firstUnSeenTOCIndex: vmD.TOCIndex ← LAST[CARDINAL];
i: vmD.TOCIndex;
tsD.ResetTOCSelection[toc, key];
firstUnSeenTOCIndex ← opD.GetMailFileOperation
[toc, key, fileName
! VMDefs.CantOpen =>
BEGIN
SELECT reason FROM
illegalFileName => exD.DisplayException[exD.illegalMailFileName];
alreadyExists => exD.DisplayException[exD.fileInUse];
accessDenied =>
exD.DisplayExceptionString["User credentials insufficient to access this file."L];
io => exD.DisplayExceptionString["Cannot connect to remote server."L];
ENDCASE => REJECT;
CONTINUE;
END;
VMDefs.Error =>
BEGIN
IF reason = resources THEN exD.DisplayException[exD.diskFullSomeNotIndexed];
CONTINUE;
END;
AltoFile.DiskFull =>
BEGIN
exD.DisplayException[exD.diskFullSomeNotIndexed];
CONTINUE;
END;
opD.MailFileError =>
BEGIN
SELECT reason FROM
notAMailFile => exD.DisplayException[exD.formatErrorCantGet];
lastStampTooLong =>
{firstUnSeenTOCIndex ← 0; exD.DisplayException[exD.mayBeTruncated]};
ENDCASE;
CONTINUE;
END;
vmD.TOCOverflow =>
BEGIN
exD.DisplayException[exD.tocOverflowSomeNotIndexed];
firstUnSeenTOCIndex ← toc.indexFF - 1;
CONTINUE;
END];
IF firstUnSeenTOCIndex = LAST[CARDINAL] THEN RETURN[FALSE];
IF firstUnSeenTOCIndex # 0 THEN -- some unseen entry exists
tsD.SetTOCSelection[toc, key, firstUnSeenTOCIndex]
ELSE IF (i ← vmD.FirstFreeTOCIndex[toc, key]) > 1 THEN
tsD.SetTOCSelection[toc, key, i - 1];
RETURN[TRUE];
END; -- of CallGetOp --


ResetAuthorization: PUBLIC PROCEDURE = {};


END. -- of ChollaCmd --