DIRECTORY AlpineFS USING [StreamOptions, StreamOpen], AlpineWalnutCmds USING [CopyForExpunge], BasicTime USING [GMT, FromPupTime, Now, ToPupTime, Update], DB USING [Attribute, BoolType, Entity, EntitySet, Error, GMT, IntType, Relation, Relship, RelshipSet, RopeType, TimeType, Transaction, CloseTransaction, CreateRelship, DeclareAttribute, DeclareRelation, DomainSubset, EraseSegment, GetF, GetName, GetP, MarkTransaction, NextEntity, NextRelship, Null, OpenTransaction, RelationSubset, ReleaseEntitySet, ReleaseRelshipSet, SetF, TransactionOf, B2V, I2V, T2V, V2B, V2I, V2S, V2T], GVBasics USING [Timestamp], GVRetrieve USING [Handle], FS USING [Error, defaultStreamOptions, StreamOptions, StreamOpen], IO, Rope, UserCredentials USING [Get], ViewerTools USING [TiogaContentsRec], WalnutDB USING [mPrefixPos, MsgDomain, InitializeDBVars], WalnutDBLog USING [ walnutLogInfo], WalnutLog USING [MsgRec, RName, TiogaContents], WalnutLogExtras, WalnutSendOps USING [userRName, RFC822Date, TiogaTextFromStrm], WalnutStream USING [ ExpungeFromStream, FindStartOfEntry, FlushAndSetCreateDate, GVLogEntry, MakeLogEntry, MsgRecFromStream, RopeFromStream, UpdateFromStream], WalnutWindow USING [excessBytesInLogFile, logIsAlpineFile, readOnlyAccess, walnut, walnutLogName, walnutNullTrans, Report, ReportRope, SetWalnutUpdatesPending]; WalnutLogImpl: CEDAR MONITOR IMPORTS AlpineFS, AlpineWalnutCmds, BasicTime, FS, IO, Rope, UserCredentials, DB, WalnutDB, WalnutDBLog, WalnutSendOps, WalnutStream, WalnutWindow EXPORTS WalnutLog, WalnutLogExtras, WalnutDBLog = BEGIN OPEN DB, WalnutLog, WalnutStream, WalnutWindow; ROPE: TYPE = Rope.ROPE; SchemaVersionTime: PUBLIC DB.GMT_ [TimeParse["July 25, 1983 2:12 pm"]]; currentSegment: PUBLIC ATOM_ $Walnut; -- default onlyOneTempLog: PUBLIC BOOL_ TRUE; walnutTempLogName: PUBLIC ROPE; msgIDRope: PUBLIC ROPE_ "gvMsgID"; categoriesRope: PUBLIC ROPE_ "Categories"; doneRope: ROPE_ " ...done"; bufferSize: CARDINAL = 512; copyBuffer: PUBLIC REF TEXT _ NEW[TEXT[bufferSize]]; -- one to share logStream: IO.STREAM_ NIL; lastMsgReadFromLog: INT; -- pos in log after last msg entered in database lastMsgWrittenToLog: INT; -- pos in log at end of last msg retrieved skipRope: ROPE = "\nUnexpected EOF, skipping ahead."; currentLogName: ROPE; -- set by call to InitializeLog alpineStreamOptions: AlpineFS.StreamOptions; localStreamOptions: FS.StreamOptions; SchemaMismatch: PUBLIC SIGNAL[schemaVersion: DB.GMT] = CODE; walnutLogInfo: PUBLIC Relation; -- used to keep current info about the log file wExpectedLength: PUBLIC Attribute; -- int (how long the log file is) wExpectedDBPos: PUBLIC Attribute; -- int (how much of the log has been parsed into Msgs) wStartExpungePos: PUBLIC Attribute; -- int (where in log file the last/current expunge started) wCopyInProgress: PUBLIC Attribute; -- bool (TRUE while Copying onto tail of log file) wSchemaVersion: PUBLIC Attribute; -- time (for keeping track of changes in walnut's schema) wLogFileName: PUBLIC Attribute; -- string (where to find the log file) walnutInfoRelship: PUBLIC Relship; -- need fetch & check only once GVMessageToLog: PUBLIC ENTRY PROC[ gvH: GVRetrieve.Handle, timeStamp: GVBasics.Timestamp, gvSender: RName] RETURNS[ok: BOOL] = { ENABLE UNWIND => NULL; prefix: ROPE_ Rope.Cat[msgIDRope, ": ", gvSender, " $ ", RopeFromTimestamp[timeStamp], "\nCategories: Active\n"]; [lastMsgWrittenToLog, ok]_ GVLogEntry[logStream, gvH, prefix]; }; msgSetEntry: ROPE = "Domain: MsgSet\nname: %g\n\n"; msgEntry: ROPE ="Relation: mCategory\nof: %g\nis: %g\n\n"; LogCreateMsgSet: PUBLIC ENTRY PROC[msName: ROPE] = { ENABLE UNWIND => NULL; []_ MakeLogEntry[logStream, insertion, IO.PutFR[msgSetEntry, IO.rope[msName]]] }; LogDestroyMsgSet: PUBLIC ENTRY PROC[msName: ROPE] = { ENABLE UNWIND => NULL; []_ MakeLogEntry[logStream, deletion, IO.PutFR[msgSetEntry, IO.rope[msName]]]; }; LogAddMsg: PUBLIC ENTRY PROC[mName: ROPE, msName: ROPE] = { ENABLE UNWIND => NULL; []_ MakeLogEntry [logStream, insertion, IO.PutFR[msgEntry, IO.rope[mName], IO.rope[msName]]]; }; LogRemoveMsg: PUBLIC ENTRY PROC[mName: ROPE, msName: ROPE] = { ENABLE UNWIND => NULL; []_ MakeLogEntry [logStream, deletion, IO.PutFR[msgEntry, IO.rope[mName], IO.rope[msName]]]; }; LogMsgHasBeenRead: PUBLIC ENTRY PROC[mName: ROPE] = { ENABLE UNWIND => NULL; []_ MakeLogEntry[logStream, hasbeenread, NIL, mName.Concat["\n"]] }; AddMessageToLog: PUBLIC ENTRY PROC [entryText, prefix: ROPE] = { ENABLE UNWIND => NULL; lastMsgWrittenToLog_ MakeLogEntry[logStream, message, entryText, prefix, FALSE].endStrmPos }; InitializeLogVars: INTERNAL PROC = BEGIN OPEN WalnutDBLog; walnutLogInfo_ DeclareRelation["walnutLogInfo", currentSegment, OldOnly]; IF walnutLogInfo#NIL THEN DO -- check schema version numbers { rs: RelshipSet; curVersion: BasicTime.GMT; wSchemaVersion_ DeclareAttribute[walnutLogInfo, "wSchemaVersion", TimeType]; rs_ RelationSubset[walnutLogInfo]; walnutInfoRelship_ NextRelship[rs]; IF NextRelship[rs] # NIL THEN ERROR; IF walnutInfoRelship = NIL THEN {ReleaseRelshipSet[rs]; EXIT}; ReleaseRelshipSet[rs]; curVersion_ V2T[GetF[walnutInfoRelship, wSchemaVersion]]; IF SchemaVersionTime.time#curVersion THEN SIGNAL SchemaMismatch[[curVersion]]; EXIT; }; ENDLOOP ELSE { walnutLogInfo_ DeclareRelation["walnutLogInfo", currentSegment]; wSchemaVersion_ DeclareAttribute[walnutLogInfo, "wSchemaVersion", TimeType]; }; wExpectedLength_ DeclareAttribute[walnutLogInfo, "wExpectedLength", IntType]; wExpectedDBPos_ DeclareAttribute[walnutLogInfo, "wExpectedDBPos", IntType]; wStartExpungePos_ DeclareAttribute[walnutLogInfo, "wStartExpungePos", IntType]; wCopyInProgress_ DeclareAttribute[walnutLogInfo, "wCopyInProgress", BoolType]; wLogFileName_ DeclareAttribute[walnutLogInfo, "wLogFileName", RopeType]; BEGIN rs: RelshipSet_ RelationSubset[walnutLogInfo]; walnutInfoRelship_ NextRelship[rs]; IF NextRelship[rs] # NIL THEN ERROR; IF walnutInfoRelship = NIL THEN walnutInfoRelship_ CreateRelship[walnutLogInfo]; ReleaseRelshipSet[rs]; END; IF ~WalnutWindow.readOnlyAccess THEN []_ SetF[walnutInfoRelship, wSchemaVersion, T2V[SchemaVersionTime]]; END; GetCurrentLogFile: PUBLIC ENTRY PROC RETURNS[ROPE] = { ENABLE UNWIND => NULL; RETURN[V2S[GetF[walnutInfoRelship, wLogFileName]]] }; GetExpectedLogLength: PUBLIC ENTRY PROC RETURNS[INT] = { ENABLE UNWIND => NULL; RETURN[V2I[GetF[walnutInfoRelship, wExpectedLength]]] }; SetExpectedLogLength: PUBLIC ENTRY PROC[len: INT] = { ENABLE UNWIND => NULL; SetF[walnutInfoRelship, wExpectedLength, I2V[len]]; SetWalnutUpdatesPending[TRUE] }; GetExpectedDBLogPos: PUBLIC ENTRY PROC RETURNS[INT] = { ENABLE UNWIND => NULL; RETURN[V2I[GetF[walnutInfoRelship, wExpectedDBPos]]] }; SetExpectedDBLogPos: PUBLIC ENTRY PROC[len: INT] = { ENABLE UNWIND => NULL; SetF[walnutInfoRelship, wExpectedDBPos, I2V[len]]; SetWalnutUpdatesPending[TRUE] }; GetStartExpungePos: PUBLIC ENTRY PROC RETURNS[INT] = { ENABLE UNWIND => NULL; RETURN[V2I[GetF[walnutInfoRelship, wStartExpungePos]]] }; SetStartExpungePos: PUBLIC ENTRY PROC[len: INT] = { ENABLE UNWIND => NULL; SetF[walnutInfoRelship, wStartExpungePos, I2V[len]]; SetWalnutUpdatesPending[TRUE] }; GetCopyInProgress: PUBLIC ENTRY PROC RETURNS [BOOL] = { ENABLE UNWIND => NULL; RETURN[V2B[GetF[walnutInfoRelship, wCopyInProgress]]] }; SetCopyInProgress: PUBLIC ENTRY PROC[doingCopy: BOOL] = { ENABLE UNWIND => NULL; SetF[walnutInfoRelship, wCopyInProgress, B2V[doingCopy]]; SetWalnutUpdatesPending[TRUE]; }; LogLength: PUBLIC ENTRY PROC[doFlush: BOOL] RETURNS[curLength: INT] = { ENABLE UNWIND => NULL; IF logStream = NIL THEN RETURN[-1]; IF doFlush THEN FlushAndSetCreateDate[logStream]; RETURN[logStream.GetLength[]]; }; ExpungeMsgs: PUBLIC ENTRY PROC [tempLog: IO.STREAM, doUpdates, tailRewrite: BOOL] RETURNS[ok: BOOL] = BEGIN phase: INTEGER_ 1; BEGIN ENABLE UNWIND => { IF phase = 1 THEN Report[" Expunge NOT done"] ELSE IF phase = 2 THEN Report[IO.PutFR[" Copy of %g NOT done; will be done after restart", IO.rope[walnutTempLogName]]]; IF tempLog#NIL THEN tempLog.Close[]; }; startExpungePos: INT; IF tempLog # NIL THEN { tempLog.Close[]; tempLog_ NIL}; [startExpungePos, ok, tempLog]_ ExpungeFromStream[logStream, doUpdates, tailRewrite]; IF ~ok OR ~doUpdates THEN { IF tempLog#NIL THEN tempLog.Close[]; RETURN}; SetF[walnutInfoRelship, wCopyInProgress, B2V[TRUE]]; SetF[walnutInfoRelship, wStartExpungePos, I2V[startExpungePos]]; MarkTransaction[TransactionOf[currentSegment]]; phase_ 2; CopyTempLogToLog[tempLog, startExpungePos]; IF startExpungePos = 0 THEN FinishFullExpunge[]; END; END; InitializeLog: PUBLIC ENTRY PROC[fileName: ROPE] RETURNS [curLength: INT] = BEGIN ENABLE UNWIND => NULL; IF logStream # NIL THEN RETURN[logStream.GetLength[]]; -- already open currentLogName_ fileName; RETURN[DoInitializeLog[]]; END; CloseLogStream: PUBLIC ENTRY PROC = { ENABLE UNWIND => NULL; IF logStream#NIL THEN logStream.Close[]; logStream_ NIL }; AbortLogTransaction: PUBLIC ENTRY PROC = { ENABLE UNWIND => NULL; log: IO.STREAM_ logStream; logStream_ NIL; IF log#NIL THEN log.Close[abort: TRUE]; }; FinishExpunge: PUBLIC ENTRY PROC = BEGIN ENABLE UNWIND => NULL; startPos: INT_ V2I[GetF[walnutInfoRelship, wStartExpungePos]]; tempLog: IO.STREAM_ FS.StreamOpen[walnutTempLogName]; CopyTempLogToLog[tempLog, startPos]; IF startPos = 0 THEN FinishFullExpunge[]; END; FinishFullExpunge: INTERNAL PROC = BEGIN InternalCloseWalnutTransaction[]; DB.EraseSegment[segment: currentSegment]; InternalOpenWalnutTransaction[currentSegment, walnutNullTrans, TRUE]; -- no trans, noLog []_ InternalUpdateFromLog[0, FALSE]; SetF[walnutInfoRelship, wStartExpungePos, I2V[logStream.GetLength[]]]; InternalCloseWalnutTransaction[]; InternalOpenWalnutTransaction[currentSegment, NIL, FALSE] -- with transaction END; ResetLogToExpectedLength: PUBLIC ENTRY PROC = BEGIN ENABLE UNWIND => NULL; endPos: INT_ V2I[GetF[walnutInfoRelship, wExpectedLength]]; logStream.SetIndex[endPos]; logStream.SetLength[endPos]; FlushAndSetCreateDate[logStream]; END; OpenWalnutTransaction: PUBLIC ENTRY PROC[segment: ATOM, trans: Transaction, noLog: BOOL] = BEGIN ENABLE UNWIND => NULL; InternalOpenWalnutTransaction[segment, trans, noLog]; END; InternalOpenWalnutTransaction: INTERNAL PROC[segment: ATOM, trans: Transaction, noLog: BOOL] = BEGIN transOpen: BOOL_ FALSE; IF walnut#NIL THEN ReportRope[" Opening Walnut transaction ..."]; OpenTransaction[ segment: segment, userName: WalnutSendOps.userRName, password: UserCredentials.Get[].password, useTrans: trans, noLog: noLog ! Error => IF code=TransactionAlreadyOpen THEN {transOpen_ TRUE; CONTINUE}]; IF transOpen THEN { CloseTransaction[TransactionOf[segment]]; OpenTransaction[ segment: segment, userName: WalnutSendOps.userRName, password: UserCredentials.Get[].password, useTrans: trans, noLog: noLog]; }; currentSegment_ segment; InitializeLogVars[]; WalnutDB.InitializeDBVars[]; lastMsgReadFromLog_ V2I[GetF[walnutInfoRelship, wExpectedDBPos]]; Report[doneRope]; END; CloseWalnutTransaction: PUBLIC ENTRY PROC = BEGIN ENABLE UNWIND => NULL; InternalCloseWalnutTransaction[]; END; InternalCloseWalnutTransaction: INTERNAL PROC = BEGIN len: INT; ReportRope[" Closing Walnut transaction ..."]; IF logStream#NIL AND ~readOnlyAccess THEN { len_ logStream.GetLength[]; FlushAndSetCreateDate[logStream]; IF lastMsgReadFromLog >= lastMsgWrittenToLog THEN lastMsgReadFromLog_ len; IF TransactionOf[currentSegment]#NIL THEN { SetF[walnutInfoRelship, wExpectedLength, I2V[len]]; SetF[walnutInfoRelship, wExpectedDBPos, I2V[lastMsgReadFromLog]]; }; }; IF TransactionOf[currentSegment]#NIL THEN CloseTransaction[TransactionOf[currentSegment]]; SetWalnutUpdatesPending[FALSE]; Report[doneRope]; END; MarkWalnutTransaction: PUBLIC ENTRY PROC = BEGIN ENABLE UNWIND => NULL; MarkTrans[doReport: TRUE]; END; MarkTrans: INTERNAL PROC[doReport: BOOL] = BEGIN len: INT; IF readOnlyAccess THEN RETURN; IF logStream # NIL THEN { len_ logStream.GetLength[]; IF lastMsgReadFromLog >= lastMsgWrittenToLog THEN lastMsgReadFromLog_ len; SetF[walnutInfoRelship, wExpectedLength, I2V[len]]; SetF[walnutInfoRelship, wExpectedDBPos, I2V[lastMsgReadFromLog]]; }; IF doReport THEN ReportRope["\nSaving Walnut updates ..."]; MarkTransaction[TransactionOf[currentSegment]]; SetWalnutUpdatesPending[FALSE]; IF doReport THEN Report[doneRope]; END; QuietlyMarkTransaction: PUBLIC ENTRY PROC = BEGIN ENABLE UNWIND => NULL; MarkTrans[doReport: FALSE]; END; TiogaTextFromLog: PUBLIC ENTRY PROC[startPos, length: INT] RETURNS[contents: TiogaContents] = BEGIN ENABLE UNWIND => NULL; contents_ WalnutSendOps.TiogaTextFromStrm[logStream, startPos-1, length+1]; IF contents # NIL THEN { IF contents.formatting=NIL THEN contents.contents_ Rope.Substr[contents.contents, 1]; RETURN }; contents_ NEW[ViewerTools.TiogaContentsRec]; contents.contents_ IO.PutFR["[message body lost!!! (%g,%g)]\n", IO.int[startPos], IO.int[length]]; END; RopeFromLog: PUBLIC ENTRY PROC[startPos, length: INT] RETURNS[ROPE] = BEGIN ENABLE UNWIND => NULL; RETURN[RopeFromStream[logStream, startPos, length]]; END; UpdateFromLog: PUBLIC ENTRY PROC[startPos: INT] RETURNS[success: BOOL] = BEGIN ENABLE UNWIND => NULL; RETURN[InternalUpdateFromLog[startPos, TRUE]]; END; InternalUpdateFromLog: INTERNAL PROC[startPos: INT, doReport: BOOL] RETURNS[success: BOOL] = BEGIN logWasntOpen: BOOL_ logStream = NIL; IF logWasntOpen THEN []_ DoInitializeLog[]; IF success_ UpdateFromStream[logStream, startPos] THEN { IF startPos = 0 AND doReport THEN Report[" WalnutScavenge done"]; lastMsgReadFromLog_ lastMsgWrittenToLog_ logStream.GetLength[]; SetF[walnutInfoRelship, wExpectedLength, I2V[lastMsgWrittenToLog]]; SetF[walnutInfoRelship, wExpectedDBPos, I2V[lastMsgReadFromLog]]} ELSE Report["\nThere were problems reading the log file."]; IF logWasntOpen THEN {logStream.Close[]; logStream_ NIL}; END; NextMsgRecFromLog: PUBLIC ENTRY PROC[startOfNextMessage: INT] RETURNS[endPos: INT, anymore: BOOL, msgRec: MsgRec] = BEGIN ENABLE UNWIND => NULL; startPos, entryLength, prefixLength: INT; entryChar: CHAR; logStream.SetIndex[startOfNextMessage]; [startPos, prefixLength, entryLength, entryChar]_ FindStartOfEntry[logStream, TRUE]; IF entryLength <= 0 THEN RETURN[lastMsgReadFromLog_ logStream.GetIndex[], FALSE, NIL]; SELECT entryChar FROM '?, ' => -- msg entry { msgRec_ MsgRecFromStream[logStream, prefixLength, entryLength-prefixLength]; IF entryChar # '? THEN msgRec.hasBeenRead_ TRUE; lastMsgReadFromLog_ startPos+entryLength; }; ENDCASE => {lastMsgReadFromLog_ startPos + entryLength}; -- after this entry RETURN[lastMsgReadFromLog, TRUE, msgRec]; END; ConstructMsgID: PUBLIC PROC[mr: MsgRec] RETURNS[ROPE] = BEGIN ts: GVBasics.Timestamp_ [net: 3, host: 14, time: 0]; IF mr.gvSender = NIL THEN { IF mr.inMsgSender # NIL THEN mr.gvSender_ mr.inMsgSender ELSE IF mr.from = NIL THEN mr.gvSender_ "UnknownSender" ELSE mr.gvSender_ mr.from}; IF mr.date = NIL THEN mr.date_ WalnutSendOps.RFC822Date[]; -- current time mr.dateCode_ TimeParse[mr.date]; ts.time_ BasicTime.ToPupTime[mr.dateCode]; mr.gvID_ Rope.Cat[mr.gvSender, " $ ", RopeFromTimestamp[ts] ]; RETURN[mr.gvID]; END; ParseMsgID: PUBLIC PROC[mr: MsgRec] = BEGIN local: ROPE_ mr.gvID; date: ROPE; pos: INT; mr.gvSender_ local.Substr[0, pos_ local.Find[" $ "]]; IF mr.date.Length[] = 0 THEN mr.date_ WalnutSendOps.RFC822Date[]; -- current time date_ local.Substr[local.Find["@", pos]+1]; mr.dateCode_ TimeParse[date]; END; TimeParse: PROC[date: ROPE] RETURNS[dateCode: BasicTime.GMT] = BEGIN ris: IO.STREAM _ IO.RIS[date]; dateCode_ IO.GetTime[ris ! IO.Error => GOTO badTime]; EXITS badTime => dateCode_ BasicTime.Now[]; END; RopeFromTimestamp: PUBLIC PROC[ts: GVBasics.Timestamp] RETURNS [ROPE] = BEGIN tr: ROPE_ WalnutSendOps.RFC822Date[BasicTime.FromPupTime[ts.time]]; RETURN[IO.PutFR["%g#%g@%g", IO.int[ts.net], IO.int[ts.host], IO.rope[tr]]]; END; GenerateNewMsgID: PUBLIC PROC[mr: MsgRec] = BEGIN lc: LONG CARDINAL_ BasicTime.ToPupTime[BasicTime.Update[mr.dateCode, 11]]; ts: GVBasics.Timestamp_ [net: 3, host: 14, time: lc]; mr.dateCode_ BasicTime.FromPupTime[lc]; mr.gvID_ Rope.Cat[mr.gvSender, " $ ", RopeFromTimestamp[ts]]; END; DoInitializeLog: INTERNAL PROC RETURNS [curLength: INT] = BEGIN IF WalnutWindow.logIsAlpineFile THEN BEGIN IF WalnutWindow.readOnlyAccess THEN logStream_ AlpineFS.StreamOpen[currentLogName, $read, alpineStreamOptions] ELSE logStream_ AlpineFS.StreamOpen[currentLogName, $write, alpineStreamOptions ! FS.Error => IF error.group = user AND error.code = $unknownFile THEN GOTO newFileNeeded ELSE REJECT]; EXITS newFileNeeded => logStream_ AlpineFS.StreamOpen[currentLogName, $create, alpineStreamOptions]; END ELSE BEGIN IF WalnutWindow.readOnlyAccess THEN logStream_ FS.StreamOpen[currentLogName, $read, localStreamOptions] ELSE logStream_ FS.StreamOpen[currentLogName, $write, localStreamOptions ! FS.Error => IF error.group = user AND error.code = $unknownFile THEN GOTO newFileNeeded ELSE REJECT]; EXITS newFileNeeded => logStream_ FS.StreamOpen[currentLogName, $create, localStreamOptions]; END; RETURN[lastMsgWrittenToLog_ logStream.GetLength[]] END; CopyTempLogToLog: INTERNAL PROC[tempLog: IO.STREAM, logStreamStartPos: INT] = BEGIN bytes: NAT; Report[IO.PutFR["Copying %g to %g, starting at position %g", IO.rope[walnutTempLogName], IO.rope[currentLogName], IO.int[logStreamStartPos]]]; tempLog.SetIndex[0]; IF logIsAlpineFile AND logStreamStartPos=0 THEN { logStream.Close[]; logStream_ NIL; tempLog.Close[]; AlpineWalnutCmds.CopyForExpunge[ walnutLogName, walnutTempLogName, excessBytesInLogFile]; lastMsgReadFromLog_ DoInitializeLog[]; } ELSE { logStream.SetIndex[logStreamStartPos]; DO IF (bytes_ tempLog.GetBlock[copyBuffer]) = 0 THEN EXIT; logStream.PutBlock[copyBuffer]; ENDLOOP; lastMsgReadFromLog_ lastMsgWrittenToLog_ logStream.GetIndex[]; logStream.SetLength[lastMsgReadFromLog]; -- set the log length to current length FlushAndSetCreateDate[logStream]; tempLog.Close[]; }; SetF[walnutInfoRelship, wCopyInProgress, B2V[FALSE]]; SetF[walnutInfoRelship, wStartExpungePos, I2V[lastMsgReadFromLog]]; SetF[walnutInfoRelship, wExpectedLength, I2V[lastMsgReadFromLog]]; SetF[walnutInfoRelship, wExpectedDBPos, I2V[lastMsgReadFromLog]]; MarkTransaction[TransactionOf[currentSegment]]; END; CheckLogPointers: PROC RETURNS[ans: ROPE] = BEGIN es: EntitySet; msg: Entity; firstLine: ROPE; num, numBad: INT_ 0; BEGIN ENABLE UNWIND => {IF es#NIL THEN ReleaseEntitySet[es ! UNWIND => CONTINUE]}; es_ DomainSubset[WalnutDB.MsgDomain]; Report["\nChecking log pointers for all msgs"]; UNTIL DB.Null[msg_ NextEntity[es]] DO logPrefixPos: INT_ V2I[GetP[msg, WalnutDB.mPrefixPos]]; logStream.SetIndex[logPrefixPos]; firstLine_ logStream.GetLineRope[]; IF (num_ num + 1) MOD 10 = 0 THEN ReportRope[IF num MOD 100 = 0 THEN "!" ELSE "~"]; IF Rope.Equal[firstLine, "*start*"] THEN LOOP; numBad_ numBad + 1; Report[IO.PutFR["\nMsg: %g is listed at %g", IO.rope[GetName[msg]], IO.int[logPrefixPos]]]; ENDLOOP; ReleaseEntitySet[es]; IF numBad # 0 THEN Report[IO.PutFR["There are %g bad messages in the database", IO.int[numBad]]]; END; RETURN[IO.PutFR["There are %g messages in the database", IO.int[num]]]; END; alpineStreamOptions_ [tiogaRead: FALSE, truncatePagesOnClose: FALSE]; localStreamOptions_ [ tiogaRead: FALSE, commitAndReopenTransOnFlush: TRUE, truncatePagesOnClose: FALSE, finishTransOnClose: TRUE, closeFSOpenFileOnClose: TRUE]; END. fFile: WalnutLogImpl.mesa Copyright c 1984 by Xerox Corporation. All rights reserved. Contents: types and procedures for writing on the log file Created by: Willie-Sue on July 16, 1982 Last edited by: Willie-Sue Hoo-Hah on August 28, 1984 12:27:25 pm PDT Last Edited by: Donahue, August 25, 1983 2:56 pm ******************************************************** ******************************************************** schemaVersion is time found in database ******************************************************** see WalnutDB for parallel operation on Walnut's database GVMessageToLog more or less corresponds to DeclareMsg reads message items from Grapevine & makes a log entry for this message writes an entry for creating a msgSet writes an entry for destroying a msgSet writes an entry for adding a msg to a msgSet writes an entry for removing a msg from a msgSet writes an entry for reading a msg (displaying for the first time) * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * like GVMessageToLog, but is used by OldMailReader makes a message LogEntryType on log (used by old mail reader) * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * should only be one relship in this relation should only be one relship in this relation Retrieves name for current log file from database Retrieves last stored log length from database (used for recovery from crash) sets the log length in the database Retrieves last stored DB log pos from database (used for recovery from crash) sets the last stored DB log position Retrieves the time at which new mail was last retrieved set the starting position of the next expunge returns TRUE if a copy onto tail of log file is in progress set the copy in progress bit in the DB * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * utility routines & ones that read the log returns length of log file * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Expunge writes on or changes the current log Dumping is driven from the log file & preserves the bits that came from Grapevine Dumps all the Msgs in the database to a tempLog IF doUpdates it sets all msgs' body pointers to reference the NEW log and copies tempLog to logStream * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * initializes fileName as the current log stream called if wCopyInProgress is TRUE during startup must know when the state of Walnut's segment is changing might have been aborted funny case of aborting during initialization, log not open all Tioga texts must begin with a carriage return, which we get from the preceding header used by ArchiveMsgSet ******************************************************** operations that read the log rebuild database Read *start* from log ignore all log entries except those with entryChar = '? or SP any other entries have been processed uses gv address of Cabernet as default if date is Unintelligible => use BasicTime.Now mr.gvID_ Rope.Cat[mr.gvSender, " $ ", ~~GVBasics.RopeFromTimestamp[ts]]; parses gvSender out of mr.gvID, compute dateCode used to create a new id for a msg (probably from an OldMailReader file) ******************************************************** copy tempLog to logStream must reopen the log stream * * * * * * * * * * for debugging, called from exec with NOTHING ELSE happening ******************************************************** Ê™˜šœ™Jšœ Ïmœ1™<—šœ ™ Jšœ0™0J˜—Jšœ'™'šœ™Jšœ5™5—Jšœ0™0J˜šÏk ˜ Jšœ žœ˜+Jšœžœ˜(Jšœ žœžœ'˜;šžœžœ1žœ˜YJ˜,J˜CJ˜MJ˜XJ˜8—Jšœ žœ ˜Jšœ žœ ˜Jšžœžœ:˜BJšžœ˜J˜Jšœžœ˜Jšœ žœ˜%Jšœ žœ+˜9Jšœ žœ˜#Jšœ žœ ˜/Jšœ˜Jšœžœ,˜?šœ žœ>˜PJ˜+J˜"—šœ žœ@˜RJ˜J˜-J˜——šœžœž˜šž˜Jšœ'žœžœ˜EJšžœ'˜)J˜—Jšžœ*˜1J˜—Jšžœžœžœ(˜5J˜Jšžœžœžœ˜J˜Jšœžœžœžœ'˜GJ˜Jšœžœžœ Ïc ˜1Jšœžœžœžœ˜"Jšœž œ˜J˜Jšœ žœžœ ˜"Jšœžœžœ˜*Jšœ žœ ˜J˜Jšœ žœ˜Jš œ žœžœžœžœžœŸ˜EJ˜Jšœ8™8Jšœ žœžœžœ˜J˜JšœžœŸ0˜IJšœžœŸ*˜EJšœ žœ'˜5JšœžœŸ˜6J˜,Jšœžœ˜%J˜Jšœ8™8Jšœ'™'Jš œžœžœžœžœžœ˜J˜J˜——Jšœ žœ"˜3Jšœ žœ,˜:J˜Jš  œžœžœžœ žœ˜2šœ%™%šœžœžœžœ˜Jšœ'žœžœ˜N—J˜J˜—Jš  œžœžœžœ žœ˜3šœ'™'šœžœžœžœ˜Jšœ&žœžœ˜N—J˜J˜—Jš   œžœžœžœžœ žœ˜9šœ,™,šœžœžœžœ˜˜Jšœžœžœžœ˜L—J˜J˜——Jš   œžœžœžœžœ žœ˜<šœ0™0šœžœžœžœ˜˜Jšœžœžœžœ˜K—J˜J˜——Jš  œžœžœžœžœ˜3šœA™Ašœžœžœžœ˜Jšœ)žœ˜AJ˜J˜——JšœG™GJšœ1™1J˜Jš  œžœžœžœžœ˜>šœ=™=šœžœžœžœ˜˜Jšœ4žœ ˜E—J˜J˜——JšœG™GJ˜š œžœžœ˜"šžœžœ ˜J˜Iš žœžœžœžœŸ˜=˜Jšœžœ˜˜LJ˜"J˜#Jšœ+™+Jšžœžœžœžœ˜$Jšžœžœžœžœ˜>J˜—J˜9Jšžœ#žœžœ˜NJšžœ˜—J˜Jšž˜—šžœ˜˜BJ˜LJ˜J˜J˜MJ˜KJ˜OJ˜NJ˜HJ˜——šž˜J˜.˜#Jšœ+™+—Jšžœžœžœžœ˜$Jšžœžœžœ1˜PJ˜—šžœ˜šžœž˜$J˜DJ˜———Jšžœ˜J˜—š  œžœžœžœžœžœ˜4Jšœ1™1šœžœžœžœ˜Jšžœ,˜2J˜J˜——š  œžœžœžœžœžœ˜6JšœM™Mšœžœžœžœ˜Jšžœ/˜5J˜J˜——š  œžœžœžœžœ˜3Jšœ#™#šœžœžœžœ˜J˜3Jšœžœ˜J˜J˜——š  œžœžœžœžœžœ˜5JšœM™Mšœžœžœžœ˜Jšžœ.˜4—J˜J˜—š  œžœžœžœžœ˜2Jšœ$™$šœžœžœžœ˜J˜2Jšœžœ˜J˜J˜——š  œžœžœžœžœžœ˜4Jšœ7™7šœžœžœžœ˜Jšžœ0˜6—J˜J˜—š  œžœžœžœžœ˜1Jšœ-™-šœžœžœžœ˜J˜4Jšœžœ˜J˜J˜——š  œžœžœžœžœžœ˜5Jšœ;™;šœžœžœžœ˜Jšžœ/˜5—J˜J˜—š  œžœžœžœ žœ˜7Jšœ&™&šœžœžœžœ˜J˜9Jšœžœ˜J˜J˜——JšœG™GJšœ)™)J˜Jš  œžœžœžœ žœžœ žœ˜Ešœ™šœžœžœžœ˜Jšžœ žœžœžœ˜#Jšžœ žœ"˜1Jšžœ˜—J˜J˜—JšœG™GJšœ,™,J˜š  œžœžœžœ žœžœžœ˜QJšžœžœ˜—JšœQ™QJšœ/™/JšœI™Išœ™šž˜Jšœžœ˜šžœžœžœ˜šœžœ žœ˜/Jšžœžœ ž˜šœžœ:˜CJšžœ˜—Jšžœ žœžœ˜$—J˜Jšœžœ˜Jšžœ žœžœžœ˜7J˜Ušžœžœ ž˜Jš œžœ žœžœžœ˜/J˜—J˜Jšœ-žœ˜4J˜@J˜/J˜ J˜+Jšžœžœ˜0—Jšžœ˜—Jšžœ˜J˜—JšœG™GJš  œžœžœžœ žœžœ žœ˜Kšœ.™.šžœžœžœžœ˜Jš žœ žœžœžœŸ˜GJ˜Jšžœ˜—Jšžœ˜J˜—š œžœžœžœ˜#šœžœžœžœ˜Jšžœ žœžœ˜(Jšœ ž˜—˜J˜——š œžœžœžœ˜(šœžœžœžœ˜Jšœžœžœ ˜Jšœ žœ˜Jšžœžœžœžœ˜'—˜J˜——Jš  œžœžœžœ˜"šœ0™0šžœžœžœžœ˜Jšœ žœ1˜>Jšœ žœžœžœ˜5J˜$Jšžœžœ˜)—Jšžœ˜—J˜š œžœžœ˜"šž˜Jšœ!˜!Jšžœ'˜)Jšœ?žœŸ˜XJšœžœ˜$J˜FJšœ!˜!Jšœ.žœžœŸ˜M—Jšžœ˜—J˜š œžœžœžœ˜-šžœžœžœžœ˜Jšœžœ0˜;J˜J˜J˜!—Jšžœ˜J˜—Jšœ8™8J˜š œ˜Jš žœžœžœ žœžœ˜Cšžœžœžœžœ˜Jšœ5˜5—Jšžœ˜—J˜š œ˜Jšžœžœ žœžœ˜?šž˜Jšœ žœžœ˜Jšžœžœžœ/˜AJ˜J˜J˜"J˜)J˜˜Jšžœžœ žœžœ˜AJ˜—šžœ ž˜˜+˜J˜J˜"J˜)J˜J˜——J˜˜J˜J˜J˜J˜AJ˜———Jšžœ˜J˜—š œžœžœžœ˜+šžœžœžœžœ˜Jšœ!˜!—Jšžœ˜—J˜š œžœžœ˜/šž˜Jšœžœ˜ J˜.šžœ žœžœž˜)˜J˜!Jšžœ+žœ˜J——šœ™šžœžœž˜)˜5J˜A—J˜—J˜J˜——šœ:™:Jšžœžœžœ1˜ZJ˜Jšœžœ˜J˜—Jšžœ˜J˜—š œžœžœžœ˜*šžœžœžœžœ˜Jšœžœ˜—Jšžœ˜—J˜š  œžœžœ žœ˜*šž˜Jšœžœ˜ Jšžœžœžœ˜šžœ žœž˜˜Jšžœ+žœ˜JJ˜3J˜A—J˜—Jšžœ žœ+˜;J˜/Jšœžœ˜Jšžœ žœ˜"—Jšžœ˜—J˜š œžœžœžœ˜+šžœžœžœžœ˜Jšœžœ˜—Jšžœ˜—J˜š  œžœžœžœžœ˜:Jšžœ˜"šžœžœžœžœ˜J˜KJšœY™YJ˜šžœ žœž˜šœžœžœžœ6˜WJšžœ˜ ——Jšœ žœ˜,˜Jšžœ+žœžœ˜O——Jšžœ˜J˜—Jš  œžœžœžœžœžœžœ˜Ešœ™šžœžœžœžœ˜Jšžœ.˜4—Jšžœ˜J˜—Jšœ8™8Jšœ™J˜š  œžœžœžœ žœžœ žœ˜HJšœ™šžœžœžœžœ˜Jšžœ!žœ˜.—šžœ˜J˜——š œžœžœ žœ žœžœ žœ˜^šž˜Jšœžœžœ˜$Jšžœžœ˜+J˜šžœ0ž˜6šœžœžœ žœ ˜CJ˜?J˜CJ˜A—Jšžœ7˜;J˜Jšžœžœ žœ˜9——Jšžœ˜J˜—š  œžœžœžœžœ˜=Jšžœ žœ žœ˜5šžœžœžœžœ˜Jšœ%žœ˜)Jšœ žœ˜˜J˜'———šœ™JšœNžœ˜TJš žœžœžœ+žœžœ˜VJ˜—Jšœ=™=šœ%™%šžœ žœ˜šœ Ÿ ˜˜NJšžœžœžœ˜0J˜)—J˜——Jšžœ2Ÿ˜LJšžœžœ ˜)Jšžœ˜J˜—Jš  œžœžœ žœžœ˜7Jšœ&™&šœ.™.šž˜J˜4šžœžœž˜šœžœžœžœ˜:šžœžœ žœžœ˜7Jšžœ˜———Jšžœ žœžœ&Ÿ˜JJ˜ J˜*J˜——JšœH™H˜J˜>Jšžœ ˜Jšžœ˜J˜—Jš  œžœžœ˜%šœ0™0šž˜Jšœžœ ˜Jšœžœ˜ Jšœžœ˜ J˜5Jšžœžœ&Ÿ˜Q˜+J˜——Jšžœ˜J˜—š   œžœžœžœžœ˜>šž˜Jš œžœžœžœžœ˜Jšœ žœžœ žœ ˜5—šž˜J˜%—Jšžœ˜J˜—š  œžœžœžœžœ˜Gšž˜šœžœ;˜CJš žœžœžœžœžœ ˜K——Jšžœ˜J˜—Jš œžœžœ˜+šœG™Gšž˜Jšœžœžœ9˜JJ˜5J˜'J˜=—Jšžœ˜J˜—Jšœ8™8J˜š  œžœžœžœ žœ˜9šž˜Jšžœž˜$šž˜šžœž˜#˜ J˜?—šž˜šœMžœ ˜Xšžœžœžœžœ˜KJšžœžœ˜ ———šž˜˜J˜M———Jšž˜—šžœžœ˜ šžœž˜#Jšœ žœ6˜C—šž˜šœ žœ9žœ ˜Qšžœžœžœžœ˜KJšžœžœ˜ ———šž˜Jšœžœ9˜W—Jšžœ˜—Jšžœ,˜2—Jšžœ˜J˜—Jš  œžœžœ žœžœžœ˜Mšœ™Jšž˜Jšœžœ˜ J˜šœžœ3˜Jšœ*Ÿ'˜QJ˜!J˜J˜J˜——Jšœ-žœ˜5J˜CJ˜BJ˜AJ˜/Jšžœ˜J˜—Jšœ™Jšœ;™;J˜š œžœžœžœ˜+šž˜J˜J˜ Jšœ žœ˜Jšœ žœ˜šžœžœžœ˜Jš œžœžœžœžœžœ˜;—J˜%J˜/šžœžœž˜%Jšœžœ&˜7J˜!J˜#šžœžœž˜!Jš œ žœžœ žœžœ˜1—Jšžœ"žœžœ˜.J˜Jšœžœ$žœžœ˜[—Jšžœ˜J˜šžœ ž˜Jšœžœ4žœ˜N—Jšžœ˜Jšžœžœ0žœ ˜G—Jšžœ˜J˜—Jšœ8™8J˜Jšœ!žœžœ˜EJ˜˜šœ žœ˜Jšœžœ˜"Jšœžœ˜Jšœžœ˜Jšœžœ˜J˜——Jšžœ˜J˜J˜—…—KØr×