-- 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 --z20461(635)