-- File: WalnutUpdateImpl.mesa -- Contents: -- reads the logStream, starting at startPos, & makes the appropriate database updates -- Created by: Willie-Sue on April 27, 1983 -- Last edited by: -- Willie-Sue on May 31, 1983 3:55 pm DIRECTORY DB, FileIO, IO, Rope, RopeIO, Runtime, WalnutDB, WalnutDBLog, WalnutLog, WalnutRetrieve, WalnutStream, WalnutWindow; WalnutUpdateImpl: CEDAR PROGRAM IMPORTS DB, FileIO, IO, Rope, RopeIO, WalnutDB, WalnutDBLog, WalnutLog, WalnutRetrieve, WalnutStream, WalnutWindow EXPORTS WalnutStream = BEGIN OPEN DB, WalnutDB, WalnutDBLog, WalnutLog, WalnutStream, WalnutWindow; skipRope: ROPE_ "Unexpected EOF; trying for next entry"; -- ******************************************************** UpdateFromStream: PUBLIC PROC [strm: IO.STREAM, startPos: INT] RETURNS[success: BOOL] = BEGIN doReport: BOOL_ (startPos = 0); -- report if scavenging from beginning BadFormat: PROC[s: ROPE] = { IF doReport THEN Report[IO.PutFR["Bad log file format at %g: %g", IO.int[strm.GetIndex[]], IO.rope[s]]] }; BEGIN ENABLE IO.EndOfStream => {BadFormat["Unexpected EOF"]; GOTO GiveUp}; entryLength, prefixLength, beginMsgPos: INT; msg: Msg; msgSet: MsgSet; entryChar: CHAR; DomainUpdate: PROC RETURNS[BOOL] = BEGIN msName, tag: ROPE; [tag, msName]_ TagAndValue[strm, 2]; IF NOT tag.Equal["name"] THEN {BadFormat["expected name"]; RETURN[FALSE]}; ReportRope[Rope.FromChar[entryChar]]; SELECT entryChar FROM '- => {msgSet_ DeclareMsgSet[msName, OldOnly].msgSet; IF msgSet = NIL THEN RETURN[TRUE]; []_ DestroyMsgSet[msgSet] }; '+ => []_ DeclareMsgSet[msName]; ENDCASE => ERROR; RETURN[TRUE]; END; RelationUpdate: PROC RETURNS[BOOL] = BEGIN mName, categoryName, tag: ROPE; [tag, mName]_ TagAndValue[strm, 2]; IF NOT tag.Equal["of"] THEN {BadFormat["of?"]; RETURN[FALSE]}; msg_ DeclareMsg[mName, OldOnly].msg; IF msg=NIL THEN {BadFormat[Rope.Cat[mName, " doesn\'t exist!"]]; RETURN[FALSE]}; [tag, categoryName]_ TagAndValue[strm, 2]; IF NOT tag.Equal["is"] THEN {BadFormat["is?"]; RETURN[FALSE]}; ReportRope[Rope.FromChar[entryChar]]; SELECT entryChar FROM '- => { msgSet_ DeclareMsgSet[categoryName, OldOnly].msgSet; -- check if msgSet exists IF msgSet # NIL THEN []_ RemoveMsgFromMsgSet[msg, msgSet] }; '+ => -- create msgSet if it doesn't exist []_ AddMsgToMsgSet[msg, DeclareMsgSet[categoryName].msgSet] ENDCASE => ERROR; RETURN[TRUE]; END; ProcessHasBeenRead: PROC = BEGIN len: INT; mName: ROPE_ RopeIO.GetRope[strm, prefixLength-minPrefixLength ! IO.EndOfStream => { Report["\nUnexpected EOF, ignoring HasBeenRead entry"]; GOTO eof}]; IF mName.Fetch[len_ mName.Length[]-1] = '\n THEN mName_ mName.Substr[0, len]; msg_ DeclareMsg[mName, OldOnly].msg; IF msg = NIL THEN RETURN; -- ignore SetMsgHasBeenRead[msg]; EXITS eof => RETURN; END; strm.SetIndex[startPos]; DO -- Read *start* from log [beginMsgPos, prefixLength, entryLength, entryChar]_ FindStartOfEntry[strm, doReport]; IF entryLength = -1 THEN RETURN[TRUE]; IF (entryLength=0) OR (entryLength=prefixLength) AND NOT (entryChar = '_) THEN RETURN[TRUE]; -- Do delete, create, or message update to database SELECT entryChar FROM '_ => ProcessHasBeenRead[]; -- mark message as read '+, '- => -- add or remove relship BEGIN foo, domainOrRelation: ROPE; ok: BOOL; strm.SetIndex[beginMsgPos+prefixLength]; -- ignore prefix info [domainOrRelation, foo] _ TagAndValue[strm, 2]; IF domainOrRelation.Equal["Domain"] THEN { IF (ok_ foo.Equal["MsgSet"]) THEN ok_ DomainUpdate[] -- Add or delete MsgSet ELSE BadFormat[IO.PutFR["%g is not a valid domain", IO.rope[foo]]]; } ELSE IF domainOrRelation.Equal["Relation"] THEN { IF (ok_ foo.Equal["mCategory"]) THEN ok_ RelationUpdate[] --AddTo or RemoveFrom ELSE BadFormat["expected mCategory"] } ELSE -- domainOrRelation not "Domain" or "Relation" {BadFormat["not domain or relation"]; ok_ FALSE}; IF ~ok THEN strm.SetIndex[beginMsgPos+entryLength] -- next entry ELSE IF strm.GetChar[]#'\n THEN BadFormat["Missing CR"];-- Skip over the trailing CR END; ENDCASE => -- regular message entry BEGIN msgRec: MsgRec_ WalnutStream.MsgRecFromStream[strm, prefixLength, entryLength-prefixLength]; IF entryChar # '? THEN msgRec.hasBeenRead_ TRUE; []_ MsgRecToMsg[msgRec]; ReportRope["."]; strm.SetIndex[beginMsgPos+entryLength]; END; ENDLOOP; EXITS GiveUp => RETURN[TRUE]; -- try to press on with what we have END; END; ExpungeFromStream: PUBLIC PROC[strm, tempLog: IO.STREAM, doUpdates, tailRewrite: BOOL] RETURNS[startExpungePos: INT, ok: BOOL] = -- Dumping is driven from the log file & preserves the bits that came from Grapevine -- Dumps all the Msgs in the database to a tempLog, setting all -- their body pointers to reference the NEW log, then copies tempLog to logStream BEGIN OPEN IO; newLen: INT; BEGIN ENABLE IO.EndOfStream => GOTO badLogFile; msg: Msg; toBeDestroyedList: LIST OF Msg; count: INT_ 0; expungeFilePos, startPos, prefixLength, entryLength: INT; entryChar: CHAR; numBad: INTEGER_ 0; DoCount: PROC = {IF (count_ count + 1) MOD 10 = 0 THEN ReportRope[IF count MOD 100 = 0 THEN "!" ELSE "~"] }; WriteBadLogEntry: PROC[startPos: INT] RETURNS[nextStartPos: INT] = { errorStream: STREAM_ FileIO.Open["WalnutExpunge.errlog", write]; line: ROPE; IF (numBad_ numBad+1) = 1 THEN errorStream.SetIndex[0] ELSE errorStream.SetIndex[errorStream.GetLength[]]; strm.SetIndex[startPos]; errorStream.PutRope[ IO.PutFR["\n Bad entry from pos %g of log file, written at %g\n", int[startPos], time[]]]; errorStream.PutRope[strm.GetSequence[]]; -- *start* line errorStream.PutChar[strm.GetChar[]]; -- CR DO nextStartPos_ strm.GetIndex[]; line_ strm.GetSequence[]; IF line.Equal["*start*"] THEN {nextStartPos_ strm.GetIndex[]-8; EXIT}; errorStream.PutRope[line]; errorStream.PutChar[strm.GetChar[]]; ENDLOOP; errorStream.SetLength[errorStream.GetIndex[]]; errorStream.Close[]; }; CopyEntryIfNecessary: PROC = { thisTag, thisValue: ROPE_ NIL; thisMsg: Msg; doCopy: BOOL_ TRUE; prefixPos: INT; IF entryChar = '_ THEN -- hasbeenread entry { -- strm.SetIndex[startPos+minPrefixLength]; where strm is pos'd thisValue_ RopeIO.GetRope[strm, prefixLength-minPrefixLength-1]; []_ strm.GetChar[ ! IO.EndOfStream => CONTINUE] } ELSE { strm.SetIndex[startPos+prefixLength]; [thisTag, thisValue]_ TagAndValue[strm, 2]; IF thisTag.Equal["Relation"] AND thisValue.Equal["mCategory"] THEN { [thisTag, thisValue]_ TagAndValue[strm, 2]; IF ~thisTag.Equal["of"] THEN thisValue_ NIL; }; }; IF thisValue # NIL THEN { thisMsg_ DeclareMsg[thisValue, OldOnly].msg; IF ~Null[thisMsg] THEN IF (prefixPos_ V2I[GetP[thisMsg, mPrefixPos]]) >= startExpungePos THEN doCopy_ FALSE; }; IF doCopy THEN { numToDo: INT; strm.SetIndex[startPos]; FOR numToDo_ entryLength, numToDo-512 UNTIL numToDo<512 DO []_ tempLog.GetBlock[copyBuffer]; strm.PutBlock[copyBuffer]; ENDLOOP; IF numToDo # 0 THEN { copyBuffer.length_ numToDo; []_ tempLog.GetBlock[copyBuffer]; strm.PutBlock[copyBuffer]; copyBuffer.length_ 512; }; }; }; tempLog.SetIndex[0]; startExpungePos_ IF tailRewrite THEN V2I[GetF[walnutInfoRelship, wStartExpungePos]] ELSE 0; strm.SetIndex[startExpungePos]; DO -- loop for dumping messages & making other log entries msgID: ROPE_ NIL; fullText: ROPE_ NIL; headersPos, curPos: INT; badEntry: BOOL_ FALSE; [startPos, prefixLength, entryLength, entryChar]_ FindStartOfEntry[strm, TRUE]; IF entryLength = 0 THEN EXIT; IF (entryChar # ' ) AND (entryChar # '?) THEN -- perhaps copy other entry to tempLog { IF startExpungePos # 0 THEN CopyEntryIfNecessary[] ELSE strm.SetIndex[startPos+entryLength]; LOOP }; headersPos_ startPos+prefixLength; UNTIL (curPos_ strm.GetIndex[]) = headersPos DO -- look for a msgID: field tag, value: ROPE; [tag, value]_ TagAndValue[strm: strm, inc: 2]; IF Rope.Equal[tag, msgIDRope, FALSE] THEN msgID_ value; IF curPos > headersPos THEN -- bad entry we don't understand { nextStartPos: INT_ WriteBadLogEntry[startPos]; cMsg: ROPE_ " Confirm to push on, deny to stop the Expunge"; ReportRope[PutFR["\nBad message entry at log pos %g;", int[startPos]]]; Report[" entry written on WalnutExpunge.errlog"]; Report[cMsg]; IF (ok_ UserConfirmed[]) THEN {strm.SetIndex[nextStartPos]; badEntry_ TRUE; EXIT}; RETURN[startExpungePos, FALSE]}; ENDLOOP; IF badEntry THEN LOOP; IF msgID = NIL THEN BEGIN -- have to read fullText to fashion msgID mr: MsgRec_ NEW[MessageRecObject]; []_ WalnutRetrieve.ParseMsgIntoFields[mr, strm, entryLength-prefixLength ! IO.EndOfStream => {Report[skipRope]; strm.SetIndex[startPos+entryLength]; LOOP}]; msgID_ ConstructMsgID[mr]; strm.SetIndex[startPos+prefixLength]; -- back up stream END; msg_ DeclareMsg[msgID, OldOnly].msg; IF (msg=NIL) OR Null[msg] THEN {strm.SetIndex[startPos+entryLength]; LOOP} ELSE BEGIN curPrefixPos: INT; thisMsgSet: MsgSet; nameOfCatsList: ROPE_ NIL; -- if is only in deletedMsgSet, don't put on tempLog FOR mL: LIST OF DB.Value_ GetPList[msg, mCategoryIs], mL.rest UNTIL mL=NIL DO IF ~Eq[thisMsgSet_ V2E[mL.first], deletedMsgSet] THEN nameOfCatsList_ Rope.Cat[nameOfCatsList, " ", DB.GetName[thisMsgSet]]; ENDLOOP; IF nameOfCatsList.Length[] = 0 THEN { toBeDestroyedList_ CONS[msg, toBeDestroyedList]; GOTO doneWithOne}; msgID_ Rope.Cat[msgID, "\nCategories:", nameOfCatsList]; IF fullText = NIL THEN fullText_ RopeIO.GetRope[strm, entryLength-prefixLength ! IO.EndOfStream => {Report[skipRope]; GOTO doneWithOne}]; DoCount[]; expungeFilePos_ MakeLogEntry[ tempLog, IF V2B[GetP[msg, mHasBeenReadIs]] THEN message ELSE newMessage, fullText, Rope.Cat[msgIDRope, ": ", msgID, "\n"]]; -- set header & body pos pointers to posn in NEW log if doing updates: IF doUpdates THEN { thisPrefixPos: INT_ startExpungePos + expungeFilePos - entryLength; curPrefixPos_ V2I[GetP[msg, mPrefixPos]]; IF curPrefixPos # thisPrefixPos THEN { []_ SetP[msg, mHeadersPos, I2V[thisPrefixPos+prefixLength]]; []_ SetP[msg, mPrefixPos, I2V[thisPrefixPos]]; }; }; EXITS doneWithOne => strm.SetIndex[startPos+entryLength]; END; -- put message on log file ENDLOOP; -- loop for dumping messages Report[PutFR["\nDumped %g messages from Log File... ", int[count]]]; tempLog.SetLength[newLen_ tempLog.GetIndex[]]; tempLog.Flush[]; -- make sure tempLog is in good shape Report[PutFR["Old log length was %g bytes", int[strm.GetLength[]]]]; IF tailRewrite THEN { Report[PutFR ["Previous end of log was %g bytes", int[strm.GetLength[]-startExpungePos]]]; Report[PutFR["New end of log file is %g bytes (%g messages)", int[newLen], int[count]]]; Report[PutFR["New log length is %g bytes", int[startExpungePos+newLen]]]; } ELSE { Report[PutFR["Old log length was %g bytes", int[strm.GetLength[]]]]; Report[PutFR["New log length is %g bytes (%g messages)", int[newLen], int[count]]] }; IF doUpdates THEN { ReportRope[" Destroying Msgs in Deleted MsgSet ..."]; -- may see a msg more than once in the log file, hence must check if already destroyed count_ 0; FOR mL: LIST OF Msg_ toBeDestroyedList, mL.rest UNTIL mL = NIL DO IF ~Null[mL.first] THEN { DestroyEntity[mL.first]; -- destroy the message DoCount[]; }; ENDLOOP; Report[PutFR["\n%g Msgs destroyed",int[count]]]; BEGIN -- fix up num in deleted (should be 0 but who knows) num: INT_ V2I[GetP[deletedMsgSet, msNumInSetIs]] - count; []_ SetP[deletedMsgSet, msNumInSetIs, I2V[num]]; END; }; -- end doUpdates RETURN[startExpungePos, TRUE]; EXITS badLogFile => { tempLog.SetLength[tempLog.GetIndex[]]; tempLog.Close[]; RETURN[startExpungePos, FALSE] }; END; END; END. Ęn˜JšmĪc,œWœ?œ&œĪk œžœ žœrĪnœž œžœžœžœžœfžœžœžœžœKžœ,<Ÿœžœžœžœžœ žœžœ žœžœ žœ'œŸ œžœžœžœ žœ žœ(žœžœžœžœžœ.žœ4žœ-žœŸ œžœžœžœžœžœ-žœžœžœžœžœ˜™ Jš˛œ,žœ žœQžœ žœžœ`žœžœžœžœžœŸœžœžœžœžœžœ.žœžœžœžœžœ1žœžœžœ2žœžœ7žœžœžœžœžœ3žœ žœHœ žœ žœžœ9&œHžœžœžœžœžœŸœžœžœžœ žœ6žœMžœ žœ*žœGžœžœžœžœ œžœžœžœ žœœ_žœžœžœžœžœžœžœžœžœžœžœ5œžœ žœ"œœžœ žœžœ4œAžœ"žœ žœžœœ žœ žœ#žœ#žœžœ$žœ žœžœœ žœ  œœžœ/œ4žœ œžœžœ( œ žœžœœžœ žœœžœ{žœžœžœwžœžœžœ žœžœ$œžœžœŸœžœžœžœžœžœžœžœžœįœžœžœžœ žœžœžœžœžœ/žœžœžœ<žœžœ žœŸœžœžœžœžœžœžœ žœžœŸœžœ žœžœžœžœ7žœžœžœžœhžœ†œ'žžœFžœžœ#žœMžœSŸœžœž œžœžœžœžœžœœ@œ`žœžœ žœ]žœžœžœ<žœžœ žœžœ žœžœ6žœžœžœ@žœžœ žœžœžœ*žœ#žœ žœ`žœ žœ žœížœ žœ0žœ*žœ8œ žœžœžœžœžœžœžœOžœžœžœžœžœžœžœ&œžœžœžœ%žœ+žœ(žœœžœ<žœžœžœžœžœ!œžœ,žœ3ž œ žœ!žœ)žœžœ žœžœžœžœ žœžœžœ žœžœžœ*œžœhžœHžœNœžœ.žœžœžœ žœ'žœžœžœžœ0žœžœ5œžœžœžœžœ,žœžœžœžœ/žœ4žœžœžœžœžœžœPžœ žœžœBžœ,žœXžœ žœ žœLGœžœ žœžœcžœžœtœžœ<žœœžœœ&œJžœ žœšžœ­žœ žœ:Xœžœžœžœ!žœžœžœžœžœ œžœ6žœ4œžœfžœœžœžœžœažœžœžœžœžœ˜×W—…—1t7č