-- file: ChollaCmd.mesa -- edited by Barth, August 20, 1981 11:45 AM -- edited by Brotz, October 22, 1982 11:45 AM -- edited by Crowther, December 22, 1981 10:23 AM -- edited by Taft, May 9, 1983 1:56 PM DIRECTORY AltoFile USING [DiskFull], ccD: FROM "ChollaCmdDefs", dsD: FROM "DisplayDefs", Editor, exD: FROM "ExceptionDefs", inD: FROM "InteractorDefs", intCommon, lmD: FROM "LaurelMenuDefs", MailParseDefs, NameInfoDefs, opD: FROM "OperationsDefs", Process, RetrieveDefs, String, Storage, tsD: FROM "TOCSelectionDefs", vmD: FROM "VirtualMgrDefs", VMDefs; ChollaCmd: MONITOR IMPORTS AltoFile, ccD, dsD, exD, inD, intC: intCommon, lmD, NameInfoDefs, opD, Process, RetrieveDefs, String, Storage, tsD, vmD, VMDefs EXPORTS ccD SHARES vmD = BEGIN OPEN ccD; activeStepSpecName: PUBLIC STRING _ [tocCacheFileStringLength]; -- last one displayed by step or spec cacheTable: PUBLIC ARRAY TOCCacheType OF TOCCache _ [[], [], [], [], [], [], [], [], [], [], []]; stepListRunName: PUBLIC STRING _ [maxRunNameLength]; stepListLowerHp: PUBLIC inD.HousePtr; iOwnThisStep: PUBLIC BOOLEAN _ FALSE; currentStepNumber: PUBLIC vmD.TOCIndex _ 0; displayedStep: PUBLIC vmD.TOCIndex; currentDoneFileName: PUBLIC STRING _ [tocCacheFileStringLength]; shutDownCholla: PUBLIC BOOLEAN; mailProcess: PROCESS; AuthorizationState: TYPE = {unknown, no, yes}; authorizationTable: ARRAY AuthorizationClass OF AuthorizationState; StartChollaMailProcess: PUBLIC PROCEDURE = BEGIN shutDownCholla _ FALSE; mailProcess _ FORK ChollaMailProcess[]; END; -- of StartChollaMailProcess -- FinishChollaMailProcess: PUBLIC PROCEDURE = BEGIN shutDownCholla _ TRUE; CallSomethingToDo[]; JOIN mailProcess; END; -- of StartChollaMailProcess -- BBoardCommand: PUBLIC inD.CommandProcedure = BEGIN OPEN inD; tnp: TOCTextNbrPtr = intC.tocTextNbr; dm: MessageTextNbrPtr = intC.dmTextNbr; IF ~intC.isCholla THEN {exD.DisplayExceptionString["BBoard not permitted if profile is in error"L]; RETURN}; IF ~IsAuthorized[onlooker, hp] THEN RETURN; intC.tocTextNbr.displayFormatted _ FALSE; intC.deliverCommandVisible _ FALSE; CleanupDisplayMessage[]; dsD.ClearRectangle[leftMargin, rightMargin, tnp.topY, tnp.bottomY]; [ , ] _ GetTOCForFile[NIL, laurel]; tnp.toc _ NIL; tnp.haveToc _ FALSE; InstallBB[]; hp.command _ inD.NullCommand; END; -- of BulletinBoardCommand -- RestoreLaurel: PUBLIC PROCEDURE = BEGIN tnp: inD.TOCTextNbrPtr = intC.tocTextNbr; CleanupDisplayMessage[]; dsD.ClearRectangle[inD.leftMargin, inD.rightMargin, tnp.topY, tnp.bottomY]; lmD.ChangeEditorMenu[singleLine]; intC.bboardCommandHouse.command _ BBoardCommand; intC.userBracketsHouse.nextHouse _ intC.newMailCommandHouse; inD.ChangeCommandMenu [cnp: intC.mailCommandNbr, region: intC.mailCommandRegion, linesToKeep: 2]; intC.tocCommandNbr.houses _ intC.displayCommandHouse; inD.ChangeCommandMenu [cnp: intC.tocCommandNbr, region: intC.TOCCommandRegion, linesToKeep: 0]; intC.cmCommandNbr.houses _ intC.newFormCommandHouse; inD.ChangeCommandMenu [cnp: intC.cmCommandNbr, region: intC.CMCommandRegion, linesToKeep: 0]; intC.deliverCommandVisible _ TRUE; tnp.displayFormatted _ TRUE; tnp.haveToc _ FALSE; tnp.toc _ NIL; IF intC.mailFileCommandHouse.text.length > 0 THEN BEGIN inD.IndicateCommandBusy[intC.mailFileCommandHouse]; inD.GetMailFileCommand[intC.mailFileCommandHouse, TRUE]; inD.IndicateCommandFinished[intC.mailFileCommandHouse]; END; END; -- of RestoreLaurel -- CleanUpTOCCaches: PUBLIC PROCEDURE = -- Returns all tocs in all toc caches except for the ones held in the laurel toc cache. BEGIN FOR ct: TOCCacheType IN (FIRST[TOCCacheType] .. LAST[TOCCacheType]] DO [ , ] _ GetTOCForFile[NIL, ct]; ENDLOOP; END; -- of CleanUpTOCCaches -- GetTOCForFile: PUBLIC PROCEDURE [fileName: STRING, cacheType: TOCCacheType, usingA: BOOLEAN _ TRUE] RETURNS [toc: vmD.TOCHandle, key: CARDINAL] = BEGIN worked: BOOLEAN; 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 -- LoadComposedMessage: PUBLIC PROCEDURE [toc: vmD.TOCHandle, key, index: CARDINAL, cm: vmD.ComposedMessagePtr] = BEGIN dm: vmD.DisplayMessagePtr _ vmD.AllocateDisplayMessageObject[]; vmD.LoadDisplayMessage[toc, key, index, dm]; vmD.InitComposedMessage[cm, ""L]; vmD.InsertRangeInMessage[0, cm, [0, vmD.GetMessageSize[dm], dm]]; vmD.FlushDisplayMessage[dm, key]; vmD.FreeVirtualMessageObject[dm]; END; -- of LoadComposedMessage -- z20461(635) CleanupDisplayMessage: PUBLIC PROCEDURE = BEGIN key: CARDINAL; mnp: inD.MessageTextNbrPtr = intC.dmTextNbr; dm: vmD.DisplayMessagePtr = vmD.DisplayMessage[mnp.message]; toc: vmD.TOCHandle = dm.toc; IF ~mnp.haveMessage THEN RETURN; key _ vmD.WaitForLock[toc]; dsD.ClearRectangle[inD.leftMargin, inD.rightMargin, mnp.topY, mnp.bottomY]; vmD.FlushDisplayMessage[dm, key]; vmD.UnlockTOC[toc, key]; mnp.haveMessage _ FALSE; END; -- of CleanupDisplayMessage -- IsAuthorized: PUBLIC PROCEDURE [class: AuthorizationClass, hp: inD.HousePtr, displayNotAuthMessage: BOOLEAN _ TRUE] RETURNS [ok: BOOLEAN] = BEGIN IF authorizationTable[class] = unknown THEN SELECT RetrieveDefs.MailboxState[intC.retrieveHandle] FROM badName, badPwd => FOR c: AuthorizationClass IN AuthorizationClass DO authorizationTable[c] _ no; ENDLOOP; cantAuth => NULL; ENDCASE => BEGIN name: STRING _ [MailParseDefs.maxRecipientLength]; String.AppendString[name, intC.user.name ! String.StringBoundsFault => CONTINUE]; String.AppendChar[name, '. ! String.StringBoundsFault => CONTINUE]; String.AppendString[name, intC.user.registry ! String.StringBoundsFault => CONTINUE]; UNTIL authorizationTable[class] # unknown DO classToCheck: AuthorizationClass _ class; IF class = onlooker AND authorizationTable[technician] = unknown THEN classToCheck _ technician; SELECT NameInfoDefs.IsMemberClosure [SELECT classToCheck FROM kahuna => "Kahunas^.cholla"L, maven => "Mavens^.cholla"L, technician => "Technicians^.cholla"L, ENDCASE => "Onlookers^.cholla"L, name] FROM no => FOR c: AuthorizationClass IN [FIRST[AuthorizationClass] .. classToCheck] DO authorizationTable[c] _ no; ENDLOOP; yes => FOR c: AuthorizationClass IN [classToCheck .. LAST[AuthorizationClass]] DO authorizationTable[c] _ yes; ENDLOOP; ENDCASE => EXIT; ENDLOOP; END; ok _ authorizationTable[class] # no; IF ~ok THEN BEGIN IF displayNotAuthMessage THEN exD.DisplayExceptionString["You are not authorized to use this command!"L]; inD.IndicateCommandFinished[hp]; END; END; -- of IsAuthorized -- ResetAuthorization: PUBLIC PROCEDURE = BEGIN FOR class: AuthorizationClass IN AuthorizationClass DO authorizationTable[class] _ unknown; ENDLOOP; END; -- of ResetAuthorization -- StringToStatus: PUBLIC PROCEDURE [s: STRING] RETURNS [StepStatus] = BEGIN RETURN[SELECT TRUE FROM String.EquivalentString[s, "Finished"L] => finished, String.EquivalentString[s, "NotStarted"L] => notStarted, String.EquivalentString[s, "Started"L] => started, String.EquivalentString[s, "InProcess"L] => inProcess, String.EquivalentString[s, "Processed"L] => processDone, String.EquivalentString[s, "Rejected"L] => rejected, ENDCASE => none]; END; -- of StringToStatus -- ResetAuthorization[]; END. -- of ChollaCmd --z20461