-- file: ChollaMail.mesa -- edited by Crowther, January 14, 1982 12:49 PM -- edited by Brotz, March 4, 1983 10:13 AM -- edited by White, August 27, 1982 5:03 PM DIRECTORY Ascii, ccD: FROM "ChollaCmdDefs", csD: FROM "CoreStreamDefs", Core, dsD: FROM "DisplayDefs", Editor, exD: FROM "ExceptionDefs", inD: FROM "InteractorDefs", intCommon, MessageParse, mfD: FROM "MailFormatDefs", opD: FROM "OperationsDefs", Process, RetrieveDefs, Storage, String, vmD: FROM "VirtualMgrDefs"; ChollaMail: MONITOR IMPORTS ccD, csD, dsD, Editor, exD, inD, intC: intCommon, MessageParse, mfD, opD, Process, RetrieveDefs, Storage, String, vmD EXPORTS ccD = BEGIN thereIsSomethingToDo: BOOLEAN _ FALSE; SomethingToDo: CONDITION _ [timeout: Process.SecondsToTicks[30]]; haveNewMail: BOOLEAN _ FALSE; haveUnsentMail: BOOLEAN _ TRUE; chollaRetrieveHandle: RetrieveDefs.Handle; chollaRetrieveRegistry: STRING; chollaRetrieveName: STRING; chollaRetrievePassword: STRING = "cholla"L; chollaUserBlk: Core.DMSUserBlk; chollaDLSimpleName: STRING; formatString: STRING = "1"; -- SendDataMail takes a composed message which must be in the third window. -- to that message it adds (or modifies) the following fields: -- MessageType {BB or Done or Step, or Spec, or Technician} -- Action{ replace = update, insert, delete} -- ThisID {ID of new message for replace or insert. Unused for delete -- LeftID {ID of left guy for insert or delete. Unused for replace -- RightID {ID of right guy for insert or delete. Unused for replace -- To (Cholla^) -- From (for shortcut) -- it then fires off the message, using a shunt to deal with self(mode S). -- ReceiveDataMail takes a virtual message and finds the corresponding fields -- if the from field is self, then it discards (mode S) -- it asks the appropiate type guy for the TOC and index -- if it can't get it, then put it on pending. -- Three cases: -- Update possible - do it -- Update out of date - discard it -- Update impossible - put on pending --Delete takes: toc key index --Insert takes: toc key index cm --Update takes: toc key index cm SendChollaMail: PUBLIC PROCEDURE [action: ccD.Action, type: ccD.ChollaFileType, toc: vmD.TOCHandle, key: CARDINAL, index: vmD.TOCIndex, cm: vmD.ComposedMessagePtr, fieldListPtr: MessageParse.FieldListPtr, dummy: BOOLEAN _ FALSE, nIDs: CARDINAL _ 1] RETURNS [worked: BOOLEAN] = BEGIN indexString: STRING _ [10]; callToc: vmD.TOCHandle _ toc; --we're going to change toc, so GetIDs do replace right. newID: ccD.UniqueID _ ccD.MakeUniqueID[dummy, nIDs]; id: ccD.UniqueID; cmcopy: vmD.ComposedMessagePtr; IF cm = NIL THEN exD.SysBug[]; -- We don't care about neighbor ID's if replace, done, or bboard. -- toc _ NIL makes GetIDxx return badID. IF action = replace OR type = done OR type = bb THEN toc _ NIL ELSE IF toc = NIL THEN exD.SysBug[]; String.AppendDecimal[indexString, index]; MessageParse.ReplaceOrAppendField[cm, fieldListPtr, "From"L, intC.workstationName, TRUE]; MessageParse.ReplaceOrAppendField[cm, fieldListPtr, "To"L, intC.chollaDL, TRUE]; MessageParse.ReplaceOrAppendField[cm, fieldListPtr, "User"L, intC.user.name]; MessageParse.ReplaceOrAppendField[cm, fieldListPtr, "Index"L, indexString]; MessageParse.ReplaceOrAppendField[cm, fieldListPtr, "Type"L, SELECT type FROM done => "Done"L, bb => "BB"L, step => "Step"L, technician => "Technician"L, spec => "Spec"L, data => "Data"L, prototypes => "Prototypes"L, ENDCASE => "None"L]; MessageParse.ReplaceOrAppendField[cm, fieldListPtr, "Action"L, SELECT action FROM insert => "Insert"L, replace => "Update"L, ENDCASE => "Delete"L]; id _ ccD.GetIDUsingIndex[toc, key, index - 1, uniqueID]; ccD.FillIDField[cm, fieldListPtr, "LeftID"L, @id]; id _ ccD.GetIDUsingIndex[toc, key, index + (IF action = insert THEN 0 ELSE 1), uniqueID]; ccD.FillIDField[cm, fieldListPtr, "RightID"L, @id]; IF action = insert THEN ccD.FillIDField[cm, fieldListPtr, "UniqueID"L, @newID]; ccD.FillIDField[cm, fieldListPtr, "ThisID"L, @newID]; MessageParse.ReplaceOrAppendField[cm, fieldListPtr, "Format"L, formatString]; cmcopy _ vmD.AllocateComposedMessageObject[]; vmD.InitComposedMessage[cmcopy, ""L]; vmD.InsertRangeInMessage[0, cmcopy, [0, vmD.GetMessageSize[cm], cm]]; IF (worked _ ReceiveChollaMail[cm, fieldListPtr, FALSE, callToc, key]) THEN AppendToUnsentMailQueue[cmcopy]; -- ReceiveChollaMail removes inserted fields. vmD.FreeVirtualMessageObject[cmcopy]; IF cm = intC.cmTextNbr.message THEN BEGIN MessageParse.DeleteField[cm, fieldListPtr, "UniqueID"L]; MessageParse.DeleteField[cm, fieldListPtr, "ThisID"L]; MessageParse.DeleteField[cm, fieldListPtr, "Format"L]; ccD.ResetTheEditor[FALSE, fieldListPtr^]; END; END; -- of SendChollaMail -- AppendToUnsentMailQueue: PUBLIC PROCEDURE [cm: vmD.ComposedMessagePtr] = BEGIN toc: vmD.TOCHandle; key: CARDINAL; [toc, key] _ ccD.GetTOCForFile["ChollaUnsentMail"L, unsentMail, FALSE]; IF ~opD.ReplaceMailOperation[delete: FALSE, index: vmD.FirstFreeTOCIndex[toc, key], msg: cm, toc: toc, key: key] THEN exD.SysBug[]; haveUnsentMail _ TRUE; vmD.UnlockTOC[toc, key]; CallSomethingToDo[]; END; -- of AppendToUnsentMailQueue -- CallSomethingToDo: PUBLIC ENTRY PROCEDURE = BEGIN thereIsSomethingToDo _ TRUE; NOTIFY SomethingToDo; END; -- of CallSomethingToDo -- WaitForSomethingToDo: ENTRY PROCEDURE = BEGIN UNTIL thereIsSomethingToDo DO WAIT SomethingToDo ENDLOOP; thereIsSomethingToDo _ FALSE; END; -- of WaitForSomethingToDo -- ChollaMailProcess: PUBLIC PROCEDURE = -- This procedure is FORKed early on. Its purpose is to remove messages from the unsent -- mail queue and from the pending mail queue. BEGIN IF intC.workstationName = NIL OR intC.chollaDL = NIL THEN GO TO profileError; FOR i: CARDINAL DECREASING IN [0 .. intC.workstationName.length) DO IF intC.workstationName[i] = '. THEN BEGIN length: CARDINAL = intC.workstationName.length - i - 1; chollaRetrieveRegistry _ Storage.String[length]; FOR j: CARDINAL IN [0 .. length) DO chollaRetrieveRegistry[j] _ intC.workstationName[i + j + 1]; ENDLOOP; chollaRetrieveRegistry.length _ length; chollaRetrieveName _ Storage.String[i]; FOR j: CARDINAL IN [0 .. i) DO chollaRetrieveName[j] _ intC.workstationName[j]; ENDLOOP; chollaRetrieveName.length _ i; EXIT; END; REPEAT FINISHED => GO TO profileError; ENDLOOP; chollaUserBlk _ [name: chollaRetrieveName, registry: chollaRetrieveRegistry, password: chollaRetrievePassword]; FOR i: CARDINAL IN [0 .. intC.chollaDL.length) DO IF intC.chollaDL[i] = '^ THEN BEGIN chollaDLSimpleName _ Storage.String[i]; FOR j: CARDINAL IN [0 .. i) DO chollaDLSimpleName[j] _ intC.chollaDL[j]; ENDLOOP; chollaDLSimpleName.length _ i; EXIT; END; REPEAT FINISHED => GO TO profileError; ENDLOOP; Process.Yield[]; chollaRetrieveHandle _ RetrieveDefs.Create [pollingInterval: 30 --seconds --, reportChanges: TellMeAboutMyMail]; RetrieveDefs.NewUser[chollaRetrieveHandle, intC.workstationName, "Cholla"L]; haveNewMail _ TRUE; UNTIL ccD.shutDownCholla DO IF haveUnsentMail THEN FireOffUnsentMail[]; IF haveNewMail THEN RetrieveChollaMail[]; WaitForSomethingToDo[]; Process.Yield[]; ENDLOOP; RetrieveDefs.Destroy[chollaRetrieveHandle]; Storage.FreeString[chollaRetrieveRegistry]; Storage.FreeString[chollaRetrieveName]; EXITS profileError => BEGIN exD.DisplayExceptionString["No registry found in Workstation or ChollaDL profile entry!"L]; intC.isCholla _ FALSE; END; END; -- of ChollaMailProcess -- FireOffUnsentMail: PROCEDURE = BEGIN cm: vmD.ComposedMessagePtr _ NIL; DO toc: vmD.TOCHandle; key: CARDINAL; [toc, key] _ ccD.GetTOCForFile["ChollaUnsentMail"L, unsentMail, FALSE]; IF toc.indexFF = 1 THEN {haveUnsentMail _ FALSE; vmD.UnlockTOC[toc, key]; EXIT}; IF cm = NIL THEN cm _ vmD.AllocateComposedMessageObject[]; ccD.LoadComposedMessage[toc, key, 1, cm]; vmD.UnlockTOC[toc, key]; Process.Yield[]; Process.Yield[]; Process.Yield[]; SELECT inD.SendOperation[cm, @chollaUserBlk, Editor.FormatMessage,TRUE,FALSE] FROM ok => NULL; commFailure => EXIT; badMessage => BEGIN bogusMessageHeader: STRING = "Subject: Bogus outgoing Cholla mail To: ChollaSupport.pa "L; exD.DisplayExceptionString ["Malformed outgoing message. Please notify ChollaSupport."L]; ccD.InsertString[cm, 0, bogusMessageHeader]; AppendToUnsentMailQueue[cm]; vmD.DeleteRangeInMessage[[0, bogusMessageHeader.length, cm]]; END; ENDCASE => exD.SysBug[]; [toc, key] _ ccD.GetTOCForFile["ChollaUnsentMail"L, unsentMail, FALSE]; IF ~opD.ReplaceMailOperation[delete: TRUE, index: 1, msg: NIL, toc: toc, key: key] THEN exD.SysBug[]; vmD.UnlockTOC[toc, key]; Process.Yield[]; ENDLOOP; IF cm # NIL THEN vmD.FreeVirtualMessageObject[cm]; END; -- of FireOffUnsentMail -- TellMeAboutMyMail: PROCEDURE [mbxState: RetrieveDefs.MBXState] = BEGIN SELECT mbxState FROM badName, badPwd => exD.DisplayExceptionString["Bad workstation name in your profile"L]; notEmpty => {haveNewMail _ TRUE; CallSomethingToDo[]}; ENDCASE; END; -- TellMeAboutMyMail -- -- ///////RECEIVE SIDE/////// MessageDescriptorPtr: TYPE = POINTER TO MessageDescriptor; MessageDescriptor: TYPE= RECORD [cm: vmD.ComposedMessagePtr, fieldListPtr: MessageParse.FieldListPtr, type: ccD.ChollaFileType, action: ccD.Action, index: CARDINAL, oldID, leftID, rightID, thisID: ccD.UniqueID, toc: vmD.TOCHandle, key: CARDINAL, remote, done: BOOLEAN]; RetrieveChollaMail: PROCEDURE = BEGIN completed: CARDINAL _ 0; cm: vmD.ComposedMessagePtr; toc: vmD.TOCHandle; key: CARDINAL; tnp: inD.TOCTextNbrPtr = intC.tocTextNbr; ResetMessages: PROCEDURE = BEGIN fp: vmD.TOCFixedPart; FOR i: vmD.TOCIndex DECREASING IN [1 .. toc.indexFF) DO vmD.GetTOCFixedPart[toc, key, i, @fp]; IF fp.deleted THEN {IF ~opD.ReplaceMailOperation[delete: TRUE, index: i, msg: NIL, toc: toc, key: key] THEN exD.SysBug[]} ELSE {fp.mark _ Ascii.SP; vmD.PutTOCFixedPart[toc, key, i, @fp]}; ENDLOOP; END; -- of ResetMessages -- FindUntriedMessage: PROCEDURE RETURNS [index: vmD.TOCIndex] = BEGIN fp: vmD.TOCFixedPart; FOR i: vmD.TOCIndex IN [1 .. toc.indexFF) DO vmD.GetTOCFixedPart[toc, key, i, @fp]; IF ~fp.deleted AND fp.mark = Ascii.SP THEN RETURN[i]; ENDLOOP; RETURN[0]; END; -- of FindUntriedMessage -- MarkCurrentMessage: PROCEDURE [index: vmD.TOCIndex] = BEGIN fp: vmD.TOCFixedPart; vmD.GetTOCFixedPart[toc, key, index, @fp]; fp.mark _ 'c; vmD.PutTOCFixedPart[toc, key, index, @fp]; END; -- of MarkCurrentMessage -- FindCurrentMessage: PROCEDURE RETURNS [index: vmD.TOCIndex] = BEGIN fp: vmD.TOCFixedPart; index _ 0; FOR i: vmD.TOCIndex IN [1 .. toc.indexFF) DO vmD.GetTOCFixedPart[toc, key, i, @fp]; IF ~fp.deleted AND fp.mark = 'c THEN {IF index = 0 THEN index _ i ELSE RETURN[0]}; ENDLOOP; END; -- of FindCurrentMessage -- [toc, key] _ ccD.GetTOCForFile["Pending"L, pending]; haveNewMail _ FALSE; IF ~opD.AccessNewMailOperation [toc, key, chollaRetrieveHandle, chollaRetrieveRegistry, TRUE] AND toc.indexFF = 1 THEN {vmD.UnlockTOC[toc, key]; RETURN}; IF tnp.toc = toc THEN BEGIN dsD.ClearRectangle[inD.leftMargin, inD.rightMargin, tnp.topY, tnp.bottomY]; inD.TOCTextPainter[tnp, key]; END; ResetMessages[]; cm _ vmD.AllocateComposedMessageObject[]; DO didAMessage: BOOLEAN _ FALSE; didThisMessage: BOOLEAN; fp: vmD.TOCFixedPart; DO index: vmD.TOCIndex _ FindUntriedMessage[]; fieldList: MessageParse.FieldList; Process.Yield[]; Process.Yield[]; IF index = 0 THEN BEGIN ResetMessages[]; IF didAMessage THEN EXIT ELSE {vmD.FreeVirtualMessageObject[cm]; vmD.UnlockTOC[toc, key]; RETURN}; END; ccD.LoadComposedMessage[toc, key, index, cm]; MarkCurrentMessage[index]; vmD.UnlockTOC[toc, key]; fieldList _ MessageParse.MakeFieldList[cm]; didThisMessage _ ReceiveChollaMail[cm, @fieldList, TRUE, NIL, 0]; MessageParse.FreeFieldList[fieldList]; [toc, key] _ ccD.GetTOCForFile["Pending"L, pending]; index _ FindCurrentMessage[]; IF index = 0 THEN {ResetMessages[]; EXIT}; vmD.GetTOCFixedPart[toc, key, index, @fp]; IF didThisMessage THEN fp.deleted _ didAMessage _ TRUE ELSE fp.mark _ 'n; -- something other than 'c (current) or SP (untried). vmD.PutTOCFixedPart[toc, key, index, @fp]; IF tnp.toc = toc THEN inD.RefreshTOCChange[toc, key, index, replace]; ENDLOOP; ENDLOOP; END; -- of RetrieveChollaMail -- ReceiveChollaMail: PROCEDURE [cm: vmD.ComposedMessagePtr, fieldListPtr: MessageParse.FieldListPtr, remote: BOOLEAN, toc: vmD.TOCHandle, key: CARDINAL] RETURNS [done: BOOLEAN] = BEGIN ENABLE MessageParse.ParseFault => GOTO Bad; m: MessageDescriptor; temp: STRING _ [50]; FieldType: TYPE = {date, sender, from, type, action, index, leftId, rightId, to, user, thisId, uniqueId}; fields: ARRAY FieldType OF STRING _ ["Date"L, "Sender"L, "From"L, "Type"L, "Action"L, "Index"L, "LeftID"L, "RightID"L, "To"L, "User"L, "ThisID"L, "UniqueID"L]; legal: BOOLEAN; ReadOne: PROCEDURE [fieldType: FieldType] RETURNS [STRING] = BEGIN fl: MessageParse.FieldList _ MessageParse.LocateField[fieldListPtr^, fields[fieldType]].field; [] _ MessageParse.GetNextWord[vm: cm, start: fl.valueStart, end: fl.valueEnd, s: temp]; RETURN[temp]; END; -- of ReadOne -- IF remote AND String.EquivalentString[ReadOne[from], chollaRetrieveName] THEN RETURN[TRUE]; IF MessageParse.LocateField[fieldListPtr^, fields[type]].count = 0 THEN GO TO Bad; IF String.EquivalentString[ReadOne[type], "FileRequest"L] THEN {ccD.DoAFileRequest[cm, fieldListPtr^]; RETURN[TRUE]}; m.type _ ccD.StringToType[temp]; IF m.type = none THEN GO TO Bad; FOR i: FieldType IN FieldType DO n: CARDINAL _ MessageParse.LocateField[fieldListPtr^, fields[i]].count; IF n > 1 OR (i > sender AND n = 0) THEN GOTO Bad; ENDLOOP; m.toc _ toc; m.key _ key; m.cm _ cm; m.fieldListPtr _ fieldListPtr; m.remote _ remote; m.action _ ccD.StringToAction[ReadOne[action]]; IF m.action = none THEN GO TO Bad; m.index _ String.StringToDecimal[ReadOne[index]]; m.leftID _ ccD.GetIDUsingVM[cm, fieldListPtr, fields[leftId]]; m.rightID _ ccD.GetIDUsingVM[cm, fieldListPtr, fields[rightId]]; m.thisID _ ccD.GetIDUsingVM[cm, fieldListPtr, fields[thisId]]; m.oldID _ ccD.GetIDUsingVM[cm, fieldListPtr, fields[uniqueId]]; FOR i: FieldType IN [date .. user] DO MessageParse.DeleteField[cm, fieldListPtr, fields[i]]; ENDLOOP; legal _ SELECT m.type FROM bb => DoABBMessage[@m], done => DoADoneMessage[@m], step => DoAStepListMessage[@m], technician => FALSE, spec, data => DoSpecOrDataMessage[@m], prototypes => DoAPrototypesMessage[@m], ENDCASE => FALSE; IF ~ legal THEN GO TO Bad; RETURN[m.done]; EXITS Bad => BEGIN originalSize: vmD.CharIndex = vmD.GetMessageSize[cm]; bogusMessageHeader: STRING = " Subject: Bogus incoming Cholla mail To: ChollaSupport.pa "L; done _ remote; IF remote THEN exD.DisplayExceptionString["Bogus mail received!"L]; vmD.StartMessageInsertion[cm, 0]; vmD.InsertStringInMessage[cm, "From: "L]; vmD.InsertStringInMessage[cm, intC.workstationName]; vmD.InsertStringInMessage[cm, bogusMessageHeader]; vmD.StopMessageInsertion[cm]; AppendToUnsentMailQueue[cm]; vmD.DeleteRangeInMessage[[0, vmD.GetMessageSize[cm] - originalSize, cm]]; END; END; -- of ReceiveChollaMail -- DoADoneMessage: PROCEDURE [m: MessageDescriptorPtr] RETURNS [legal: BOOLEAN] = -- Sets m.done to TRUE if this message is no longer to be kept in Pending.mail. BEGIN string: STRING _ [ccD.tocCacheFileStringLength]; statusString: STRING _ [15]; status: ccD.StepStatus; subjectString: STRING _ [opD.maxTOCStringLength - ccD.maxRunNameLength - 2]; stepListDotMail: STRING = "StepList"L; doneDotMail: STRING = "Done"L; toc: vmD.TOCHandle; key: CARDINAL; success: BOOLEAN _ FALSE; changedCurrentDoneFile: BOOLEAN; technician: STRING _ [ccD.maxUserNameLength]; step: vmD.TOCIndex = MessageParse.GetNumberFromField[m.cm, m.fieldListPtr^, "Step"L]; doneTOCIndexFF: vmD.TOCIndex; IF step = 0 OR step # m.index THEN RETURN[FALSE]; MessageParse.GetStringFromField[m.cm, m.fieldListPtr^, "Run"L, string]; IF string.length = 0 THEN RETURN[FALSE]; String.AppendString[string, doneDotMail]; legal _ TRUE; IF m.remote THEN ccD.StartMultiTOCOperation[]; IF m.toc = NIL THEN [toc, key] _ ccD.GetTOCForFile[string, done, FALSE] ELSE {toc _ m.toc; key _ m.key}; IF m.action = delete THEN legal _ success _ FALSE ELSE SELECT TRUE FROM step < toc.indexFF => {m.action _ replace; success _ DoActionOnDoneMessage[m, toc, key]}; step = toc.indexFF => {m.action _ insert; success _ DoActionOnDoneMessage[m, toc, key]}; ENDCASE => m.done _ FALSE; doneTOCIndexFF _ toc.indexFF; IF m.toc = NIL THEN vmD.UnlockTOC[toc, key]; IF ~success OR ccD.IsDummyID[m.oldID] THEN {IF m.remote THEN ccD.FinishMultiTOCOperation[]; RETURN}; -- success means that we changed the done file. Now store new state. changedCurrentDoneFile _ String.EquivalentString[ccD.currentDoneFileName, string]; string.length _ string.length - doneDotMail.length; String.AppendString[string, stepListDotMail]; [toc, key] _ ccD.GetTOCForFile[string, stepList, FALSE]; IF toc = NIL THEN exD.SysBug[]; MessageParse.GetStringFromField[m.cm, m.fieldListPtr^, "Status"L, statusString]; status _ ccD.StringToStatus[statusString]; ccD.SetStepStatus[toc, key, step, status]; string.length _ string.length - stepListDotMail.length; ccD.MakeBBRunEntryString[toc, key, doneTOCIndexFF, string, subjectString]; vmD.UnlockTOC[toc, key]; ccD.UpdateBBRunEntry[run: string, string: subjectString, newID: m.thisID]; IF status = started THEN BEGIN version: CARDINAL = MessageParse.GetNumberFromField[m.cm, m.fieldListPtr^, "Version"L]; MessageParse.GetStringFromField[m.cm, m.fieldListPtr^, "Spec"L, string]; MessageParse.GetStringFromField[m.cm, m.fieldListPtr^, "Started-By"L, technician]; ccD.UpdateTechnician[technician, string, version]; END; IF m.remote AND changedCurrentDoneFile AND ccD.currentStepNumber = step THEN exD.DisplayExceptionString ["The lower window may be out of date. Try invoking ""Step""."L]; IF m.remote THEN ccD.FinishMultiTOCOperation[]; END; -- of DoADoneMessage -- DoABBMessage: PROCEDURE [m: MessageDescriptorPtr] RETURNS [legal: BOOLEAN] = BEGIN toc: vmD.TOCHandle; key: CARDINAL; tocString: STRING _ [opD.maxTOCStringLength]; run: STRING _ [ccD.tocCacheFileStringLength]; stepListDotMail: STRING = "StepList"L; doneDotMail: STRING = "Done"L; success: BOOLEAN; isRunDelete: BOOLEAN; legal _ TRUE; IF ccD.UniqueIDIsInDeletionList[m.oldID] THEN {m.done _ TRUE; RETURN}; IF m.toc = NIL THEN [toc, key] _ ccD.GetTOCForFile["ChollaBulletinBoard"L, bb] ELSE {toc _ m.toc; key _ m.key}; [success, isRunDelete] _ DoActionOnBBMessage[m, toc, key, run]; IF m.toc = NIL THEN vmD.UnlockTOC[toc, key]; -- Note: no multiTOC operation here on remote case since files are locked only one at a time. IF success AND isRunDelete THEN BEGIN -- destroy the run files. String.AppendString[run, stepListDotMail]; [toc, key] _ ccD.GetTOCForFile[run, stepList, FALSE]; FOR index: vmD.TOCIndex DECREASING IN [1 .. toc.indexFF) DO IF ~opD.ReplaceMailOperation[delete: TRUE, index: index, msg: NIL, toc: toc, key: key] THEN exD.SysBug[]; ENDLOOP; vmD.UnlockTOC[toc, key]; run.length _ run.length - stepListDotMail.length; String.AppendString[run, doneDotMail]; [toc, key] _ ccD.GetTOCForFile[run, done, FALSE]; FOR index: vmD.TOCIndex DECREASING IN [1 .. toc.indexFF) DO IF ~opD.ReplaceMailOperation[delete: TRUE, index: index, msg: NIL, toc: toc, key: key] THEN exD.SysBug[]; ENDLOOP; vmD.UnlockTOC[toc, key]; END; IF success AND m.action = delete THEN ccD.InsertInDeletionList[m.oldID]; END; -- of DoABBMessage -- DoSpecOrDataMessage: PROCEDURE [m: MessageDescriptorPtr] RETURNS [legal: BOOLEAN] = BEGIN toc: vmD.TOCHandle; key: CARDINAL; fileName: STRING _ [ccD.tocCacheFileStringLength]; MessageParse.GetStringFromField[m.cm, m.fieldListPtr^, "FileName"L, fileName]; IF fileName.length = 0 THEN RETURN[FALSE]; legal _ TRUE; IF m.toc = NIL THEN [toc, key] _ ccD.GetTOCForFile[fileName, IF m.type = spec THEN spec ELSE data, FALSE] ELSE {toc _ m.toc; key _ m.key}; MessageParse.DeleteField[m.cm, m.fieldListPtr, "FileName"L]; SELECT TRUE FROM ~DoActionOnMessage[m, toc, key] => NULL; m.index = 1 => UpdateCurrentVersion[toc, key]; m.action = replace => MarkIfArchived[toc, key, m]; ENDCASE; IF m.toc = NIL THEN vmD.UnlockTOC[toc, key]; END; -- of DoSpecOrDataMessage -- MarkIfArchived: PROCEDURE [toc: vmD.TOCHandle, key: CARDINAL, m: MessageDescriptorPtr] = BEGIN archivedValue: STRING _ [5]; fp: vmD.TOCFixedPart; MessageParse.GetStringFromField[m.cm, m.fieldListPtr^, "Archived"L, archivedValue]; vmD.GetTOCFixedPart[toc, key, m.index, @fp]; fp.mark _ IF String.EquivalentString[archivedValue, "TRUE"L] THEN 'a ELSE Ascii.SP; vmD.PutTOCFixedPart[toc, key, m.index, @fp]; END; -- of MarkIfArchived -- UpdateCurrentVersion: PROCEDURE [toc: vmD.TOCHandle, key: CARDINAL] = BEGIN fp: vmD.TOCFixedPart; dm: vmD.DisplayMessagePtr; fieldList: MessageParse.FieldList; messageID: vmD.TOCIndex; firstFree: vmD.TOCIndex = vmD.FirstFreeTOCIndex[toc, key]; IF firstFree <= 2 THEN RETURN; FOR i: vmD.TOCIndex IN [1 .. firstFree) DO vmD.GetTOCFixedPart[toc, key, i, @fp]; IF fp.mark = '* THEN {fp.mark _ Ascii.SP; vmD.PutTOCFixedPart[toc, key, i, @fp]}; ENDLOOP; dm _ vmD.AllocateDisplayMessageObject[]; vmD.LoadDisplayMessage[toc, key, 1, dm]; fieldList _ MessageParse.MakeFieldList[dm]; messageID _ MessageParse.GetNumberFromField[dm, fieldList, "CurrentVersion"L]; vmD.FlushDisplayMessage[dm, key]; vmD.FreeVirtualMessageObject[dm]; MessageParse.FreeFieldList[fieldList]; vmD.GetTOCFixedPart[toc, key, messageID + 1, @fp]; fp.mark _ '*; vmD.PutTOCFixedPart[toc, key, messageID + 1, @fp]; END; -- of UpdateCurrentVersion -- DoAStepListMessage: PROCEDURE [m: MessageDescriptorPtr] RETURNS [legal: BOOLEAN] = BEGIN toc: vmD.TOCHandle; key: CARDINAL; string: STRING _ [ccD.tocCacheFileStringLength]; stepListDotMail: STRING = "StepList"L; doneDotMail: STRING = "Done"L; doneTOCIndexFF: vmD.TOCIndex; MessageParse.GetStringFromField[m.cm, m.fieldListPtr^, "Run"L, string]; IF string.length = 0 THEN RETURN[FALSE]; legal _ TRUE; String.AppendString[string, doneDotMail]; [toc, key] _ ccD.GetTOCForFile[string, done, FALSE]; doneTOCIndexFF _ toc.indexFF; vmD.UnlockTOC[toc, key]; string.length _ string.length - doneDotMail.length; IF m.remote THEN ccD.StartMultiTOCOperation[]; IF m.toc = NIL THEN BEGIN String.AppendString[string, stepListDotMail]; [toc, key] _ ccD.GetTOCForFile[string, stepList, FALSE]; string.length _ string.length - stepListDotMail.length; END ELSE {toc _ m.toc; key _ m.key}; IF DoActionOnMessage[m, toc, key] THEN BEGIN subjectString: STRING _ [opD.maxTOCStringLength - 2]; IF toc.indexFF = 1 THEN subjectString _ NIL ELSE ccD.MakeBBRunEntryString[toc, key, doneTOCIndexFF, string, subjectString]; ccD.UpdateBBRunEntry[run: string, string: subjectString, newID: m.thisID]; END; IF m.toc = NIL THEN vmD.UnlockTOC[toc, key]; IF m.remote THEN ccD.FinishMultiTOCOperation[]; END; -- of DoAStepListMessage -- DoAPrototypesMessage: PROCEDURE [m: MessageDescriptorPtr] RETURNS [legal: BOOLEAN] = BEGIN toc: vmD.TOCHandle; key: CARDINAL; string: STRING _ [ccD.tocCacheFileStringLength]; IF m.toc = NIL THEN [toc, key] _ ccD.GetTOCForFile["StepListPrototypes"L, prototypes, FALSE] ELSE {toc _ m.toc; key _ m.key}; [] _ DoActionOnMessage[m, toc, key]; IF m.toc = NIL THEN vmD.UnlockTOC[toc, key]; RETURN[TRUE]; END; -- of DoAPrototypesMessage -- DoActionOnMessage: PROCEDURE [m: MessageDescriptorPtr, toc: vmD.TOCHandle, key: CARDINAL] RETURNS [success: BOOLEAN] = BEGIN thisIDforIndex, uniqueIDforIndex, id: ccD.UniqueID; success _ FALSE; IF toc = NIL THEN exD.SysBug[]; m.done _ TRUE; ccD.GetBothIDsUsingIndex[toc, key, m.index, @thisIDforIndex, @uniqueIDforIndex]; SELECT m.action FROM insert => BEGIN IF m.thisID = thisIDforIndex THEN RETURN; -- duplicate -- id _ ccD.GetIDUsingIndex[toc, key, m.index - 1, uniqueID]; SELECT TRUE FROM ~ccD.NewMatchesOld[@m.leftID, @id] OR ~ccD.NewMatchesOld[@m.rightID, @uniqueIDforIndex] => m.done _ FALSE; m.type = step => success _ DoStepListInsertion[m, toc, key]; ~opD.ReplaceMailOperation [delete: FALSE, index: m.index, msg: m.cm, toc: toc, key: key] => exD.DisplayExceptionString["Can't insert in file"L]; ENDCASE => success _ TRUE; END; delete => SELECT TRUE FROM m.leftID # ccD.GetIDUsingIndex[toc, key, m.index - 1, uniqueID] OR m.rightID # ccD.GetIDUsingIndex[toc, key, m.index + 1, uniqueID] OR m.oldID # uniqueIDforIndex => m.done _ FALSE; ~opD.ReplaceMailOperation [delete: TRUE, index: m.index, msg: NIL, toc: toc, key: key] => exD.DisplayExceptionString["Can't delete in file"L]; ENDCASE => success _ TRUE; replace => BEGIN SELECT TRUE FROM ~ccD.NewMatchesOld[@m.oldID, @uniqueIDforIndex] => m.done _ FALSE; ccD.IsIllegalID[thisIDforIndex] => m.done _ FALSE; ~ccD.NewerUniqueID[@m.thisID, @thisIDforIndex] => NULL; ENDCASE => IF ~(success _ opD.ReplaceMailOperation [delete: TRUE, index: m.index, msg: m.cm, toc: toc, key: key]) THEN exD.DisplayExceptionString["Can't insert in done file"L]; END; ENDCASE => exD.SysBug[]; END; -- of DoActionOnMessage -- DoActionOnDoneMessage: PROCEDURE [m: MessageDescriptorPtr, toc: vmD.TOCHandle, key: CARDINAL] RETURNS [success: BOOLEAN] = BEGIN doneFileThisID: ccD.UniqueID; success _ FALSE; IF toc = NIL THEN exD.SysBug[]; m.done _ TRUE; SELECT m.action FROM insert => BEGIN -- Only occurs on append to done file. success _ opD.ReplaceMailOperation [delete: FALSE, index: m.index, msg: m.cm, toc: toc, key: key]; IF ~success THEN exD.DisplayExceptionString["Can't insert in file"L]; END; replace => BEGIN -- we already have something in the done file. IF ccD.IsDummyID[m.thisID] THEN RETURN; doneFileThisID _ ccD.GetIDUsingIndex[toc, key, m.index, thisID]; IF ccD.IsDummyID[doneFileThisID] OR ccD.NewerUniqueID[@m.thisID, @doneFileThisID] THEN BEGIN success _ opD.ReplaceMailOperation [delete: TRUE, index: m.index, msg: m.cm, toc: toc, key: key]; IF ~success THEN exD.DisplayExceptionString["Can't insert in done file"L]; END; END; ENDCASE => exD.SysBug[]; END; -- of DoActionOnDoneMessage -- DoActionOnBBMessage: PROCEDURE [m: MessageDescriptorPtr, toc: vmD.TOCHandle, key: CARDINAL, run: STRING] RETURNS [success, isRunDelete: BOOLEAN] = BEGIN thisID: ccD.UniqueID; index: vmD.TOCIndex; found: BOOLEAN _ TRUE; success _ FALSE; IF toc = NIL THEN exD.SysBug[]; m.done _ TRUE; isRunDelete _ FALSE; FOR index IN [1 .. toc.indexFF) DO IF m.oldID = ccD.GetIDUsingIndex[toc, key, index, uniqueID] THEN EXIT; REPEAT FINISHED => found _ FALSE; ENDLOOP; SELECT m.action FROM insert => BEGIN IF found THEN RETURN; -- duplicate -- success _ opD.ReplaceMailOperation [delete: FALSE, index: 1, msg: m.cm, toc: toc, key: key]; IF ~success THEN exD.DisplayExceptionString["Can't insert in Bulletin Board!"L]; END; delete => BEGIN dm: vmD.DisplayMessagePtr; fieldList: MessageParse.FieldList; IF ~found THEN {m.done _ FALSE; RETURN[FALSE, FALSE]}; dm _ vmD.AllocateDisplayMessageObject[]; vmD.LoadDisplayMessage[toc, key, index, dm]; fieldList _ MessageParse.MakeFieldList[dm]; isRunDelete _ ccD.ObtainRunName[dm, fieldList, run]; MessageParse.FreeFieldList[fieldList]; vmD.FlushDisplayMessage[dm, key]; vmD.FreeVirtualMessageObject[dm]; success _ opD.ReplaceMailOperation [delete: TRUE, index: index, msg: NIL, toc: toc, key: key]; IF ~success THEN exD.DisplayExceptionString["Can't delete in Bulletin Board!"L]; END; replace => BEGIN IF ~found THEN {m.done _ FALSE; RETURN[FALSE, FALSE]}; thisID _ ccD.GetIDUsingIndex[toc, key, index, thisID]; IF ccD.NewerUniqueID[@m.thisID, @thisID] THEN BEGIN success _ opD.ReplaceMailOperation [delete: TRUE, index: index, msg: m.cm, toc: toc, key: key]; IF ~success THEN exD.DisplayExceptionString["Can't replace in Bulletin Board!"L]; END; END; ENDCASE => exD.SysBug[]; END; -- of DoActionOnBBMessage -- DoStepListInsertion: PROCEDURE [m: MessageDescriptorPtr, toc: vmD.TOCHandle, key: CARDINAL] RETURNS [success: BOOLEAN] = BEGIN IF MessageParse.LocateField[m.fieldListPtr^, "Subject"L].count # 0 THEN BEGIN success _ opD.ReplaceMailOperation [delete: FALSE, index: m.index, msg: m.cm, toc: toc, key: key]; IF ~success THEN exD.DisplayExceptionString["Can't insert in file"L]; END ELSE BEGIN tocIndex: vmD.TOCIndex _ m.index; appending: BOOLEAN = (m.index = toc.indexFF); cmIndex: vmD.CharIndex _ 0; cmSize: vmD.CharIndex; char: CHARACTER; entry: STRING _ [250]; entryIndex: CARDINAL; id: ccD.UniqueID _ m.thisID; runName: STRING _ [ccD.tocCacheFileStringLength]; newcm: vmD.ComposedMessagePtr = vmD.AllocateComposedMessageObject[]; sh: csD.StreamHandle; nmIndex: vmD.CharIndex; AppendMessageToStepList: PROCEDURE = BEGIN fp: vmD.TOCFixedPart; tocString: STRING = [opD.maxTOCStringLength]; fp.deleted _ fp.changed _ fp.bogus _ FALSE; fp.seen _ TRUE; fp.mark _ Ascii.SP; fp.offsetToHeader _ 0; fp.textLength _ vmD.GetMessageSize[newcm]; [fp.firstPage, fp.firstByte] _ csD.MapPositionToPageByte[csD.GetPosition[sh]]; mfD.CreateStamp[@fp, StampPutChar]; nmIndex _ 0; UNTIL nmIndex >= fp.textLength DO [] _ vmD.GetMessageChar[newcm, nmIndex]; csD.WriteBlock[sh, newcm.buffer, newcm.get.floor + (nmIndex - newcm.get.first), newcm.get.free - nmIndex]; nmIndex _ newcm.get.free; ENDLOOP; nmIndex _ 0; mfD.ParseHeaderForTOC[tocString, ParseReadChar]; vmD.ExtendTOC[toc, key, @fp, tocString]; END; -- of AppendMessageToStepList -- ParseReadChar: PROCEDURE RETURNS [char: CHARACTER] = BEGIN char _ vmD.GetMessageChar[newcm, nmIndex]; nmIndex _ nmIndex + 1; END; -- of ParseReadChar -- StampPutChar: PROCEDURE [c: CHARACTER] = {csD.Write[sh, c]}; MessageParse.GetStringFromField[m.cm, m.fieldListPtr^, "Run"L, runName]; MessageParse.DeleteField[m.cm, m.fieldListPtr, "Run"L]; MessageParse.DeleteField[m.cm, m.fieldListPtr, "Format"L]; MessageParse.DeleteField[m.cm, m.fieldListPtr, "ThisID"L]; MessageParse.DeleteField[m.cm, m.fieldListPtr, "UniqueID"L]; cmSize _ vmD.GetMessageSize[m.cm]; IF appending THEN sh _ csD.Open[toc.mailFile, byte, append]; UNTIL cmIndex >= cmSize DO bogus: BOOLEAN _ FALSE; slashCount: CARDINAL _ 0; entryIndex _ 0; UNTIL cmIndex >= cmSize DO char _ vmD.GetMessageChar[m.cm, cmIndex]; cmIndex _ cmIndex + 1; SELECT char FROM Ascii.CR => EXIT; '/ => slashCount _ slashCount + 1; ': => IF slashCount < 2 THEN bogus _ TRUE; ENDCASE; IF entryIndex < entry.maxlength THEN {entry[entryIndex] _ char; entryIndex _ entryIndex + 1}; ENDLOOP; IF entryIndex = 0 OR bogus OR slashCount = 0 OR slashCount MOD 2 = 1 THEN LOOP; entry.length _ entryIndex; MakeMessageFromEntry[entry, newcm, @id, runName]; IF appending THEN AppendMessageToStepList[] ELSE [] _ opD.ReplaceMailOperation [delete: FALSE, index: tocIndex, msg: newcm, toc: toc, key: key]; tocIndex _ tocIndex + 1; id.time _ id.time + 1; ENDLOOP; IF appending THEN BEGIN tnp: inD.TOCTextNbrPtr = intC.tocTextNbr; csD.Close[sh]; IF tnp.toc = toc THEN BEGIN dsD.ClearRectangle[inD.leftMargin, inD.rightMargin, tnp.topY, tnp.bottomY]; inD.TOCTextPainter[tnp, key]; END; END; vmD.FreeVirtualMessageObject[newcm]; success _ TRUE; END; END; -- of DoStepListInsertion -- MakeMessageFromEntry: PROCEDURE [entry: STRING, msg: vmD.ComposedMessagePtr, id: ccD.UniqueIDPtr, runName: STRING] = -- Constructs in "msg" a message suitable for insertion in a Step List file. The status section -- of "entry" will be replaced with NotStarted. N.B. "entry" may be modified. BEGIN fieldList: MessageParse.FieldList; template: STRING = "Subject: xxx Run: xxx UniqueID: xxx ThisID: xxx Format: xxx "L; slashIndex: CARDINAL _ 0; subject: STRING; notStarted: STRING = "Not Started "L; FOR i: CARDINAL IN [0 .. entry.length) DO IF entry[i] = '/ THEN {slashIndex _ i; EXIT}; ENDLOOP; IF slashIndex > 0 THEN BEGIN FOR i: CARDINAL IN [slashIndex .. entry.length) DO entry[i - slashIndex] _ entry[i]; ENDLOOP; entry.length _ entry.length - slashIndex; END; Process.Yield[]; Process.Yield[]; vmD.InitComposedMessage[msg, template]; subject _ Storage.String[notStarted.length + entry.length]; String.AppendString[subject, notStarted]; String.AppendString[subject, entry]; fieldList _ MessageParse.MakeFieldList[msg]; MessageParse.ReplaceOrAppendField[msg, @fieldList, "Subject"L, subject]; MessageParse.ReplaceOrAppendField[msg, @fieldList, "Run"L, runName]; ccD.FillIDField[msg, @fieldList, "UniqueID"L, id]; ccD.FillIDField[msg, @fieldList, "ThisID"L, id]; MessageParse.ReplaceOrAppendField[msg, @fieldList, "Format"L, formatString]; Storage.FreeString[subject]; MessageParse.FreeFieldList[fieldList]; END; -- of MakeMessageFromEntry -- END. -- of ChollaMail --(1792)\f1 3174v4V5108v23V6734v1V35v23V17563v49V68v16V599v7V66v3V