<> <> <> <> <> <> <> <> 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]; < -- 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] = <> < use BasicTime.Now>> 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.