-- file: ChollaMailUtils.mesa -- edited by Crowther, January 14, 1982 12:49 PM -- edited by Brotz, October 12, 1982 1:13 PM DIRECTORY Ascii, ccD: FROM "ChollaCmdDefs", Core, csD: FROM "CoreStreamDefs", dsD: FROM "DisplayDefs", Editor, exD: FROM "ExceptionDefs", inD: FROM "InteractorDefs", intCommon, MailParse, MessageParse, NameInfoDefs, opD: FROM "OperationsDefs", Process, PupDefs, Storage, String, TimeDefs, tsD: FROM "TOCSelectionDefs", vmD: FROM "VirtualMgrDefs", VMDefs; ChollaMailUtils: MONITOR IMPORTS ccD, Core, csD, dsD, Editor, exD, inD, intC: intCommon, MessageParse, NameInfoDefs, opD, Process, PupDefs, Storage, String, TimeDefs, tsD, vmD, VMDefs EXPORTS ccD = BEGIN -- Monitor to protect multiTOCOperation. first: ccD.UniqueID = [1, 0]; last: ccD.UniqueID = [1, 1]; bad: ccD.UniqueID = [1, 2]; noID: ccD.UniqueID = [1, 3]; illegalID: ccD.UniqueID = [1, 4]; dummyMachine: CARDINAL = 0; machineString: STRING _ [10]; machineNumber: CARDINAL; formatString: STRING = "1"; multiTOCOperation: BOOLEAN _ FALSE; multiTOCCV: CONDITION _ [timeout: Process.SecondsToTicks[1]]; bbRunHintTable: POINTER TO ARRAY [0 .. 0) OF STRING _ NIL; bbRunHintTableLength: CARDINAL _ 0; StartMultiTOCOperation: PUBLIC ENTRY PROCEDURE = -- Used by Cholla operations that need to lock more than one TOC simultaneously. BEGIN WHILE multiTOCOperation DO WAIT multiTOCCV ENDLOOP; multiTOCOperation _ TRUE; END; -- of StartMultiTOCOperation -- FinishMultiTOCOperation: PUBLIC ENTRY PROCEDURE = BEGIN IF ~multiTOCOperation THEN exD.SysBug[]; multiTOCOperation _ FALSE; NOTIFY multiTOCCV; END; -- of FinishMultiTOCOperation -- GetIDUsingIndex: PUBLIC PROCEDURE [toc: vmD.TOCHandle, key: CARDINAL, index: CARDINAL, idType: ccD.IDType] RETURNS [u: ccD.UniqueID] = BEGIN SELECT TRUE FROM (toc = NIL) => RETURN[bad]; (index = 0) => RETURN[first]; (index >= toc.indexFF) => RETURN[last]; ENDCASE => BEGIN dm: vmD.DisplayMessagePtr _ vmD.AllocateDisplayMessageObject[]; fieldList: MessageParse.FieldList; vmD.LoadDisplayMessage[toc, key, index, dm]; fieldList _ MessageParse.MakeFieldList[dm]; u _ GetIDUsingVM [dm, @fieldList, IF idType = thisID THEN "ThisID"L ELSE "UniqueID"L]; vmD.FlushDisplayMessage[dm, key]; vmD.FreeVirtualMessageObject[dm]; MessageParse.FreeFieldList[fieldList]; END; END; -- of GetIDUsingIndex -- GetBothIDsUsingIndex: PUBLIC PROCEDURE [toc: vmD.TOCHandle, key: CARDINAL, index: CARDINAL, thisID, uniqueID: ccD.UniqueIDPtr] = BEGIN IF toc = NIL THEN {thisID^ _ uniqueID^ _ bad; RETURN}; SELECT TRUE FROM (toc = NIL) => thisID^ _ uniqueID^ _ bad; (index = 0) => thisID^ _ uniqueID^ _ first; (index >= toc.indexFF) => thisID^ _ uniqueID^ _ last; ENDCASE => BEGIN dm: vmD.DisplayMessagePtr _ vmD.AllocateDisplayMessageObject[]; fieldList: MessageParse.FieldList; vmD.LoadDisplayMessage[toc, key, index, dm]; fieldList _ MessageParse.MakeFieldList[dm]; thisID^ _ GetIDUsingVM[dm, @fieldList, "ThisID"L]; uniqueID^ _ GetIDUsingVM[dm, @fieldList, "UniqueID"L]; vmD.FlushDisplayMessage[dm, key]; vmD.FreeVirtualMessageObject[dm]; MessageParse.FreeFieldList[fieldList]; END; END; -- of GetBothIDsUsingIndex -- GetIDUsingVM: PUBLIC PROCEDURE [vm: vmD.VirtualMessagePtr, fieldListPtr: MessageParse.FieldListPtr, name: STRING] RETURNS [u: ccD.UniqueID] = BEGIN idFieldList: MessageParse.FieldList; count: CARDINAL; [idFieldList, count] _ MessageParse.LocateField[fieldListPtr^, name]; IF count > 0 THEN u _ ExtractID[vm, idFieldList ! MessageParse.ParseFault, String.InvalidNumber => {u _ bad; CONTINUE}] ELSE WITH cm: vm SELECT FROM CM => BEGIN u _ MakeUniqueID[FALSE, 1]; FillIDField[vmD.ComposedMessage[vm], fieldListPtr, name, @u]; END; ENDCASE => exD.SysBug[]; END; -- of GetIDUsingVM -- ExtractID: PUBLIC PROCEDURE [vm: vmD.VirtualMessagePtr, fieldList: MessageParse.FieldList] RETURNS [u: ccD.UniqueID]= BEGIN temp: STRING _ [20]; x: vmD.CharIndex _ MessageParse.GetNextWord [vm: vm, start: fieldList.valueStart, end: fieldList.valueEnd, s: temp]; IF temp.length = 0 THEN RETURN[bad]; u.machine _ String.StringToDecimal[temp]; [] _ MessageParse.GetNextWord[vm: vm, start: x + 2, end: fieldList.valueEnd, s: temp]; u.time _ String.StringToLongNumber[temp, 10]; END; -- of ExtractID -- FillIDField: PUBLIC PROCEDURE [cm: vmD.ComposedMessagePtr, fieldListPtr: MessageParse.FieldListPtr, s: STRING, u: ccD.UniqueIDPtr] = BEGIN idString: STRING _ [ccD.maxUniqueIDLength]; UniqueIDToString[u^, idString]; MessageParse.ReplaceOrAppendField[cm, fieldListPtr, s, idString]; END; -- of FillIDField -- UniqueIDToString: PUBLIC PROCEDURE [id: ccD.UniqueID, s: STRING] = BEGIN s.length _ 0; String.AppendDecimal[s, id.machine]; String.AppendString[s, " - "L]; String.AppendLongDecimal[s, id.time]; END; -- of UniqueIDToString -- oldTime: LONG CARDINAL _ 0; GetTime: PROCEDURE [nIDs: CARDINAL] RETURNS [time: LONG CARDINAL] = BEGIN time _ TimeDefs.CurrentDayTime[] - ccD.timeOffset; IF time <= oldTime THEN time _ oldTime + 1; oldTime _ time + nIDs - 1; END; -- of GetTime -- MakeUniqueID: PUBLIC PROCEDURE [dummy: BOOLEAN, nIDs: CARDINAL] RETURNS [ccD.UniqueID] = {RETURN[[IF dummy THEN dummyMachine ELSE machineNumber, GetTime[nIDs]]]}; NewerUniqueID: PUBLIC PROCEDURE [a, b: ccD.UniqueIDPtr] RETURNS [BOOLEAN] = {RETURN[a.time > b.time]}; NewMatchesOld: PUBLIC PROCEDURE [new, old: ccD.UniqueIDPtr] RETURNS [BOOLEAN] = {RETURN[old^ = new^ OR old.machine = dummyMachine]}; IsIllegalID: PUBLIC PROCEDURE [id: ccD.UniqueID] RETURNS [BOOLEAN] = {RETURN[id = illegalID]}; IsDummyID: PUBLIC PROCEDURE [id: ccD.UniqueID] RETURNS [BOOLEAN] = {RETURN[id.machine = dummyMachine]}; MakeBBRunEntryString: PUBLIC PROCEDURE [toc: vmD.TOCHandle, key: CARDINAL, doneFF: vmD.TOCIndex, run, string: STRING] = -- Creates a string containing details of steps to be displayed in a run BBoard entry. BEGIN stepName: STRING _ [50]; specName: STRING _ [ccD.maxSpecNameLength]; dataName: STRING _ [0]; parsedStep: ccD.ParsedStep _ [specName, 0, dataName, 0, stepName, notStarted, 0, 0]; tocString: STRING _ [opD.maxTOCStringLength]; status: STRING _ [20]; string.length _ 0; String.AppendString[string, run]; String.AppendString[string, " ("L]; String.AppendDecimal[string, doneFF - 1]; String.AppendChar[string, '/]; String.AppendDecimal[string, toc.indexFF - 1]; String.AppendChar[string, ')]; FOR i: vmD.TOCIndex IN [1 .. MIN[toc.indexFF - 1, doneFF]] DO vmD.GetTOCString[toc, key, i, tocString]; ccD.ParseStep[tocString, @parsedStep]; IF (parsedStep.status # rejected AND parsedStep.status # finished) OR i + 1 >= doneFF THEN BEGIN ENABLE String.StringBoundsFault => EXIT; String.AppendChar[string, Editor.nextCode]; ccD.StatusToString[parsedStep.status, status]; String.AppendString[string, status]; IF string[string.length - 1] # Ascii.SP THEN String.AppendChar[string, Ascii.SP]; String.AppendChar[string, '/]; String.AppendString[string, stepName]; String.AppendString[string, "/ "L]; String.AppendString[string, specName]; END; ENDLOOP; END; -- of MakeBBRunEntryString -- UpdateBBRunEntry: PUBLIC PROCEDURE [run, string: STRING, newID: ccD.UniqueID] = -- Looks for BB entry for "run". If the tocString is non-NIL, then a new BB entry is -- constructed, and the old BB entry is replaced. If the old BB entry was not found, a -- new one is created. If string = NIL, then any existing BB entry for this run is -- deleted. BEGIN toc: vmD.TOCHandle; key: CARDINAL; index: vmD.TOCIndex; bbRunName: STRING _ [ccD.maxRunNameLength]; fieldList: MessageParse.FieldList; dm: vmD.DisplayMessagePtr; cm: vmD.ComposedMessagePtr _ vmD.AllocateComposedMessageObject[]; found: BOOLEAN; [toc, key] _ ccD.GetTOCForFile["ChollaBulletinBoard"L, bb]; [found, index, dm, fieldList] _ FindBBRunEntry[toc, key, run]; IF found = (dm = NIL) THEN exD.SysBug[]; IF found THEN BEGIN vmD.InitComposedMessage[cm, ""L]; vmD.InsertRangeInMessage[0, cm, [0, vmD.GetMessageSize[dm], dm]]; vmD.FlushDisplayMessage[dm, key]; vmD.FreeVirtualMessageObject[dm]; END ELSE BEGIN vmD.InitComposedMessage[cm, "Subject: xxx BBEntryType: Run UniqueID: xxx ThisID: xxx "L]; fieldList _ MessageParse.MakeFieldList[cm]; FillIDField[cm, @fieldList, "UniqueID"L, @newID]; END; FillIDField[cm, @fieldList, "ThisID"L, @newID]; IF string # NIL THEN BEGIN -- construct new BB entry subject field. MessageParse.ReplaceOrAppendField[cm, @fieldList, "Subject"L, string]; IF ~opD.ReplaceMailOperation[delete: found, index: index, msg: cm, toc: toc, key: key] THEN exD.SysBug[]; END ELSE IF found AND ~opD.ReplaceMailOperation[delete: TRUE, index: index, msg: NIL, toc: toc, key: key] THEN exD.SysBug[]; vmD.UnlockTOC[toc, key]; MessageParse.FreeFieldList[fieldList]; vmD.FreeVirtualMessageObject[cm]; END; -- of UpdateBBRunEntry -- FindBBRunEntry: PROCEDURE [toc: vmD.TOCHandle, key: CARDINAL, run: STRING] RETURNS [found: BOOLEAN, index: vmD.TOCIndex, dm: vmD.DisplayMessagePtr, fieldList: MessageParse.FieldList] = -- Returns index of the run'th entry in the BBoard. Returns toc.indexFF if not found. -- If message is found, returns an allocated dm with fieldList for that message. BEGIN bbRunName: STRING _ [ccD.maxRunNameLength]; SearchForRunHint: PROCEDURE [lookForSort: BOOLEAN] RETURNS [found: BOOLEAN] = BEGIN sortFound: BOOLEAN _ FALSE; FOR i: CARDINAL IN [1 .. MIN[bbRunHintTableLength, toc.indexFF]) DO IF String.EquivalentString[bbRunHintTable[i], run] THEN {index _ i; RETURN[TRUE]}; IF lookForSort AND ~sortFound AND bbRunHintTable[i] # NIL AND String.CompareStrings[run, bbRunHintTable[i], TRUE] < 1 THEN {sortFound _ TRUE; index _ i}; ENDLOOP; found _ FALSE; IF ~sortFound THEN index _ toc.indexFF; END; -- of SearchForRunHint -- IF run = NIL OR run.length = 0 THEN exD.SysBug[]; dm _ vmD.AllocateDisplayMessageObject[]; IF bbRunHintTable # NIL AND SearchForRunHint[FALSE] THEN BEGIN -- verify that index is truly the one containing run. vmD.LoadDisplayMessage[toc, key, index, dm]; fieldList _ MessageParse.MakeFieldList[dm]; found _ ccD.ObtainRunName[dm, fieldList, bbRunName] AND String.EquivalentString[bbRunName, run]; IF found THEN RETURN; vmD.FlushDisplayMessage[dm, key]; MessageParse.FreeFieldList[fieldList]; END; IF bbRunHintTable # NIL THEN BEGIN -- Free the old table -- FOR i: CARDINAL IN [1 .. bbRunHintTableLength) DO IF bbRunHintTable[i] # NIL THEN Storage.FreeString[bbRunHintTable[i]]; ENDLOOP; Storage.Free[bbRunHintTable]; END; -- Make a new table -- bbRunHintTableLength _ toc.indexFF; bbRunHintTable _ Storage.Node[SIZE[STRING] * bbRunHintTableLength]; FOR i: vmD.TOCIndex IN [1 .. bbRunHintTableLength) DO vmD.LoadDisplayMessage[toc, key, i, dm]; fieldList _ MessageParse.MakeFieldList[dm]; bbRunHintTable[i] _ IF ~ccD.ObtainRunName[dm, fieldList, bbRunName] THEN NIL ELSE Storage.CopyString[bbRunName]; vmD.FlushDisplayMessage[dm, key]; MessageParse.FreeFieldList[fieldList]; ENDLOOP; IF (found _ SearchForRunHint[TRUE]) THEN {vmD.LoadDisplayMessage[toc, key, index, dm]; fieldList _MessageParse.MakeFieldList[dm]} ELSE {vmD.FreeVirtualMessageObject[dm]; fieldList _ NIL; dm _ NIL}; END; -- of FindBBRunEntry -- DoAFileRequest: PUBLIC PROCEDURE [request: vmD.VirtualMessagePtr, fieldList: MessageParse.FieldList] = BEGIN temp: STRING _ [80]; requestor: STRING _ [MailParse.maxRecipientLength]; toc: vmD.TOCHandle; key: CARDINAL; response: vmD.ComposedMessagePtr _ vmD.AllocateComposedMessageObject[]; senderFL, fromFL: MessageParse.FieldList; count: CARDINAL; filename: STRING _ [ccD.tocCacheFileStringLength]; action: STRING _ [20]; cacheType: ccD.TOCCacheType _ laurel; useA: BOOLEAN; inReplyTo: STRING = " In-Reply-To: Your message of "L; tableOfContentsOf: STRING = "Table Of Contents of "L; CopyValueIntoResponse: PROCEDURE [fl: MessageParse.FieldList] = BEGIN vmD.InsertRangeInMessage[vmD.GetMessageSize[response], response, vmD.MessageRange[fl.valueStart, fl.valueEnd, request]]; END; -- of CopyValueIntoResponse -- CopyValueIntoString: PROCEDURE [fl: MessageParse.FieldList, s: STRING] = BEGIN FOR index: vmD.CharIndex IN [fl.valueStart .. fl.valueEnd) UNTIL s.length >= s.maxlength DO char: CHARACTER = vmD.GetMessageChar[request, index]; IF ~dsD.GetCharProperty[char, white] THEN {s[s.length] _ char; s.length _ s.length + 1}; ENDLOOP; END; -- of CopyValueIntoString -- Authorized: PROCEDURE [name: STRING, class: {read, write}] RETURNS [ok: BOOLEAN] = BEGIN ok _ NameInfoDefs.IsMemberClosure [IF class = read THEN "Onlookers^.cholla"L ELSE "Kahunas^.cholla"L, name] # no; IF ~ok THEN BEGIN vmD.StartMessageInsertion[response, vmD.GetMessageSize[response]]; vmD.InsertStringInMessage[response, "According to information available at this time, you are not authorized to "L]; vmD.InsertStringInMessage[response, IF class = read THEN "read"L ELSE "modify"L]; vmD.InsertStringInMessage[response, " Cholla data. "L]; vmD.StopMessageInsertion[response]; END; END; -- of Authorized -- MessageParse.GetStringFromField[request, fieldList, "FileName"L, filename]; MessageParse.GetStringFromField[request, fieldList, "Action"L, action]; vmD.InitComposedMessage[response, "From: ChollaInfo.cholla ("L]; ccD.InsertString[response, vmD.GetMessageSize[response], intC.workstationName]; ccD.InsertString[response, vmD.GetMessageSize[response], ") Subject: Your Cholla File Request To: "L]; [senderFL, count] _ MessageParse.LocateField[fieldList, "Sender"L]; [fromFL, count] _ MessageParse.LocateField[fieldList, "From"L]; IF count > 0 AND senderFL.valueStart < fromFL.valueStart THEN CopyValueIntoString[senderFL, requestor] ELSE CopyValueIntoString[fromFL, requestor]; ccD.InsertString[response, vmD.GetMessageSize[response], requestor]; ccD.InsertString[response, vmD.GetMessageSize[response], inReplyTo]; CopyValueIntoResponse[MessageParse.LocateField[fieldList, "Date"L].field]; ccD.InsertString[response, vmD.GetMessageSize[response], " "L]; [cacheType, useA] _ MapFilenameToCacheType[filename]; IF cacheType = laurel THEN BEGIN ccD.InsertString[response, vmD.GetMessageSize[response], filename]; ccD.InsertString[response, vmD.GetMessageSize[response], " is an invalid file name. "L]; END ELSE BEGIN String.AppendString[filename, ".chml"L]; -- GetNextWord stops before the dot. SELECT TRUE FROM String.EquivalentString[action, "SendTOC"L] => BEGIN IF ~Authorized[requestor, read] THEN GO TO Interloper; ccD.InsertString[response, vmD.GetMessageSize[response], tableOfContentsOf]; ccD.InsertString[response, vmD.GetMessageSize[response], filename]; ccD.InsertString[response, vmD.GetMessageSize[response], ": "L]; [toc, key] _ ccD.GetTOCForFile[filename, cacheType, useA]; AppendTOCtoCM[toc, key, response]; vmD.UnlockTOC[toc, key]; END; String.EquivalentString[action, "SendFile"L] => BEGIN IF ~Authorized[requestor, read] THEN GO TO Interloper; ccD.InsertString[response, vmD.GetMessageSize[response], "Contents of "L]; ccD.InsertString[response, vmD.GetMessageSize[response], filename]; [toc, key] _ ccD.GetTOCForFile[filename, cacheType, useA]; AppendMailFileToMessage[toc, key, response]; vmD.UnlockTOC[toc, key]; END; String.EquivalentString[action, "DeleteFile"L] => BEGIN IF ~Authorized[requestor, write] THEN GO TO Interloper; [] _ ReplaceMailFile[filename, cacheType, useA, FALSE, request]; ccD.InsertString[response, vmD.GetMessageSize[response], "Mail file "L]; ccD.InsertString[response, vmD.GetMessageSize[response], filename]; ccD.InsertString[response, vmD.GetMessageSize[response], " has been deleted. "L]; END; String.EquivalentString[action, "ReplaceFile"L] => BEGIN worked: BOOLEAN; IF ~Authorized[requestor, write] THEN GO TO Interloper; worked _ ReplaceMailFile[filename, cacheType, useA, TRUE, request]; ccD.InsertString[response, vmD.GetMessageSize[response], "Mail file "L]; ccD.InsertString[response, vmD.GetMessageSize[response], filename]; ccD.InsertString[response, vmD.GetMessageSize[response], IF worked THEN " has been replaced. "L ELSE " could not be replaced. There is probably a format error in the file as sent. The file has been deleted. "L]; END; ENDCASE => BEGIN ccD.InsertString[response, vmD.GetMessageSize[response], "Unrecognized file request. "L]; IF action.length = 0 THEN ccD.InsertString[response, vmD.GetMessageSize[response], "No Action: field found in your request. "L] ELSE BEGIN ccD.InsertString[response, vmD.GetMessageSize[response], action]; ccD.InsertString[response, vmD.GetMessageSize[response], " is not a valid action. "L]; END; END; EXITS Interloper => NULL; END; ccD.AppendToUnsentMailQueue[response]; vmD.FreeVirtualMessageObject[response]; END; -- of DoAFileRequest -- MapFilenameToCacheType: PROCEDURE [name: STRING] RETURNS [cacheType: ccD.TOCCacheType, useA: BOOLEAN] = -- Parses name to determine from which toc cache this file should come. If cacheType -- returned is laurel, then the name is not a cholla file. BEGIN SELECT TRUE FROM String.EquivalentString[name, "ChollaBulletinBoard"L] => RETURN[bb, TRUE]; EquivalentTail[name, "StepList"L] => RETURN[stepList, FALSE]; EquivalentTail[name, "Done"L] => RETURN[done, FALSE]; EquivalentTail[name, "Spec"L] => RETURN[spec, FALSE]; EquivalentTail[name, "Data"L] => RETURN[data, FALSE]; String.EquivalentString[name, "Pending"L] => RETURN[pending, TRUE]; String.EquivalentString[name, "ChollaUnsentMail"L] => RETURN[unsentMail, FALSE]; String.EquivalentString[name, "Technician"L] => RETURN[technician, TRUE]; String.EquivalentString[name, "StepListPrototypes"L] => RETURN[prototypes, TRUE]; ENDCASE => RETURN[laurel, FALSE]; END; -- of MapFilenameToCacheType -- EquivalentTail: PROCEDURE [string: STRING, tail: STRING] RETURNS [BOOLEAN] = BEGIN stringSSD, tailSSD: String.SubStringDescriptor; IF tail.length > string.length THEN RETURN[FALSE]; stringSSD _ [base: string, offset: string.length - tail.length, length: tail.length]; tailSSD _ [base: tail, offset: 0, length: tail.length]; RETURN[String.EquivalentSubString[@stringSSD, @tailSSD]]; END; -- of EquivalentTail -- AppendTOCtoCM: PROCEDURE [toc: vmD.TOCHandle, key: CARDINAL, cm: vmD.ComposedMessagePtr] = BEGIN i, count: CARDINAL; tocString: STRING _ [opD.maxTOCStringLength]; vmD.StartMessageInsertion[cm, vmD.GetMessageSize[cm]]; FOR index: vmD.TOCIndex IN [1 .. toc.indexFF) DO vmD.GetTOCString[toc, key, index, tocString]; count _ 0; FOR i IN [0 .. tocString.length) DO IF tocString[i] = opD.substringSeparator THEN count _ count + 1; IF count = 2 THEN {i _ i + 1; EXIT}; ENDLOOP; vmD.InsertSubstringInMessage[cm, tocString, i, tocString.length - i]; IF tocString.length > i THEN vmD.InsertMessageChar[cm, Ascii.CR]; ENDLOOP; vmD.StopMessageInsertion[cm]; END; -- of AppendTOCtoCM -- AppendMailFileToMessage: PROCEDURE [toc: vmD.TOCHandle, key: CARDINAL, message: vmD.ComposedMessagePtr] = BEGIN messageStart: vmD.CharIndex _ vmD.GetMessageSize[message]; dm: vmD.DisplayMessagePtr _ vmD.AllocateDisplayMessageObject[]; fileLength: VMDefs.Position = VMDefs.GetFileLength[toc.mailFile]; position: VMDefs.Position _ [0, 0]; bytesRemain: BOOLEAN _ TRUE; part: CARDINAL _ 1; partString: STRING _ [6]; IF fileLength = [0, 0] THEN BEGIN ccD.InsertString[message, messageStart, ": File is empty. "L]; RETURN; END; DO ccD.InsertString[message, vmD.GetMessageSize[message], ": "L]; vmD.LoadDisplayMessage[toc, key, 1, dm]; dm.firstPage _ position.page; dm.firstByte _ 0; IF fileLength.page > position.page + 120 THEN {position.page _ position.page + 120; dm.textLength _ 120 * 512} ELSE BEGIN dm.textLength _ (fileLength.page - position.page) * 512 + fileLength.byte; bytesRemain _ FALSE; END; vmD.InsertRangeInMessage[vmD.GetMessageSize[message], message, [0, dm.textLength, dm]]; vmD.FlushDisplayMessage[dm, key]; IF ~bytesRemain THEN RETURN; ccD.AppendToUnsentMailQueue[message]; vmD.DeleteRangeInMessage[[messageStart, vmD.GetMessageSize[message], message]]; ccD.InsertString[message, messageStart, ", Part "L]; part _ part + 1; partString.length _ 0; String.AppendDecimal[partString, part]; ccD.InsertString[message, vmD.GetMessageSize[message], partString]; ENDLOOP; END; -- of AppendMailFileToMessage -- ReplaceMailFile: PROCEDURE [filename: STRING, cacheType: ccD.TOCCacheType, useA: BOOLEAN, replace: BOOLEAN, vm: vmD.VirtualMessagePtr] RETURNS [worked: BOOLEAN] = BEGIN dm: inD.MessageTextNbrPtr = intC.dmTextNbr; tnp: inD.TOCTextNbrPtr = intC.tocTextNbr; displayMessage: vmD.DisplayMessagePtr = vmD.DisplayMessage[dm.message]; toc: vmD.TOCHandle; key: CARDINAL; [toc, key] _ ccD.GetTOCForFile[filename, cacheType, useA]; FOR dmList: vmD.DMList _ toc.dmList, dmList.next UNTIL dmList = NIL DO dispMess: vmD.DisplayMessagePtr = dmList.dm; IF dispMess # NIL THEN BEGIN vmD.FlushDisplayMessage[dispMess, key]; IF dm.haveMessage AND dispMess = vmD.DisplayMessage[dm.message] THEN BEGIN dm.haveMessage _ FALSE; dsD.ClearRectangle[inD.leftMargin, inD.rightMargin, dm.topY, dm.bottomY]; exD.DisplayExceptionString["The displayed message has been deleted."L]; END ELSE exD.SysBug[]; -- someone else is holding onto a deleted display message. END; ENDLOOP; tsD.ResetTOCSelection[toc, key]; VMDefs.SetFileLength[toc.mailFile, [0, 0]]; IF replace THEN BEGIN found: BOOLEAN; at: vmD.CharIndex; vmSize: vmD.CharIndex = vmD.GetMessageSize[vm]; [found, at, , ] _ Editor.FindOperation["'*start'*"L, 0, vmSize, vm]; IF found THEN BEGIN sh: csD.StreamHandle = csD.Open[toc.mailFile, byte, write]; UNTIL at >= vmSize DO [] _ vmD.GetMessageChar[vm, at]; -- MAGIC: set up msg.get. csD.WriteBlock[sh, vm.buffer, vm.get.floor + (at - vm.get.first), vm.get.free - at]; at _ vm.get.free; ENDLOOP; csD.Close[sh]; END; END; Core.Close[toc.mailFile]; vmD.CleanupTOC[toc, key, delete]; worked _ TRUE; [] _ opD.GetMailFileOperation[toc, key, filename ! opD.MailFileError => {worked _ FALSE; CONTINUE}]; IF ~worked THEN BEGIN mailFile: VMDefs.FileHandle _ Core.Open[filename, update]; Core.Delete[mailFile]; [] _ opD.GetMailFileOperation[toc, key, filename]; END; IF toc = tnp.toc THEN BEGIN dsD.ClearRectangle[inD.leftMargin, inD.rightMargin, tnp.topY, tnp.bottomY]; inD.DisplayTOCTail[tnp, key, tnp.lines, 1, 1]; inD.UpdateTOCThumbLine[tnp, key]; END; vmD.UnlockTOC[toc, key]; END; -- of ReplaceMailFile -- StringToType: PUBLIC PROCEDURE [s: STRING] RETURNS [ccD.ChollaFileType] = BEGIN RETURN[SELECT TRUE FROM String.EquivalentString[s, "Done"L] => done, String.EquivalentString[s, "BB"L] => bb, String.EquivalentString[s, "Step"L] => step, String.EquivalentString[s, "Technician"L] => technician, String.EquivalentString[s, "Spec"L] => spec, String.EquivalentString[s, "Data"L] => data, String.EquivalentString[s, "Prototypes"L] => prototypes, ENDCASE => none]; END; -- of StringToType -- StringToAction: PUBLIC PROCEDURE [s: STRING] RETURNS [ccD.Action] = BEGIN RETURN[SELECT TRUE FROM String.EquivalentString[s, "Insert"L] => insert, String.EquivalentString[s, "Delete"L]=> delete, String.EquivalentString[s, "Update"L]=> replace, ENDCASE=> none]; END; -- of StringToAction -- deletionList: POINTER TO ccD.DeletionList _ NIL; InsertInDeletionList: PUBLIC PROCEDURE [uniqueID: ccD.UniqueID] = BEGIN deletionListSH: csD.StreamHandle; IF deletionList = NIL THEN OpenDeletionList[]; deletionList.item[(deletionList.first + deletionList.nItems) MOD ccD.maxItemsInDeletionList] _ uniqueID; IF deletionList.nItems = ccD.maxItemsInDeletionList THEN deletionList.first _ (deletionList.first + 1) MOD ccD.maxItemsInDeletionList ELSE deletionList.nItems _ deletionList.nItems + 1; deletionListSH _ csD.OpenFromName ["ChollaDeletionList.DontDeleteThisFile"L, word, overwrite]; csD.WriteBlock[deletionListSH, deletionList, 0, SIZE[ccD.DeletionList]]; csD.Close[deletionListSH]; END; -- of InsertInDeletionList -- UniqueIDIsInDeletionList: PUBLIC PROCEDURE [uniqueID: ccD.UniqueID] RETURNS [BOOLEAN] = BEGIN IF deletionList = NIL THEN OpenDeletionList[]; FOR index: CARDINAL IN [0 .. deletionList.nItems) DO IF deletionList.item[(deletionList.first + index) MOD ccD.maxItemsInDeletionList] = uniqueID THEN RETURN[TRUE]; ENDLOOP; RETURN[FALSE]; END; -- of UniqueIDIsInDeletionList -- OpenDeletionList: PROCEDURE = BEGIN deletionListSH: csD.StreamHandle _ csD.OpenFromName["ChollaDeletionList.DontDeleteThisFile"L, word, write]; deletionList _ Storage.Node[SIZE[ccD.DeletionList]]; deletionList.first _ deletionList.nItems _ 0; [] _ csD.ReadBlock[deletionListSH, deletionList, 0, SIZE[ccD.DeletionList]]; csD.Close[deletionListSH]; END; -- of OpenDeletionList -- Machine: PROCEDURE = BEGIN address: PupDefs.PupAddress; PupDefs.GetPupAddress[@address, "ME"L]; machineNumber _ 256 * address.net + address.host; machineString.length _ 0; String.AppendDecimal[machineString, machineNumber]; END; -- of Machine -- Machine[]; END. -- of ChollaMailUtils --(1792)\11953v31V34v21V1192v1V267v29V140v43V555v5V221v30V464v6V1023v2V501v2V87v2V26v2V133v1V142v2V184v2V2760v21V80v6V662v10V146v10V