DIRECTORY FS USING [Error, ErrorFromStream], IO, Rope, WalnutDB -- using lots -- , WalnutDefs USING [Error, VersionMismatch, MsgSet], WalnutLog -- using lots -- , WalnutMiscLog USING [walnutItemFixedLength, CreateReadArchiveLog], WalnutOps USING [], WalnutOpsInternal USING [CarefullyApply, CheckInProgress, CheckReport, LongRunningApply, ParseLog], WalnutOpsMonitorImpl, WalnutStream USING [Open], WalnutRoot USING [CommitAndContinue]; WalnutOpsArchiveFileImpl: CEDAR MONITOR LOCKS walnutOpsMonitorImpl IMPORTS FS, IO, Rope, walnutOpsMonitorImpl: WalnutOpsMonitorImpl, WalnutDB, WalnutDefs, WalnutLog, WalnutMiscLog, WalnutOpsInternal, WalnutStream, WalnutRoot EXPORTS WalnutOps SHARES WalnutOpsMonitorImpl = BEGIN OPEN WalnutOpsInternal; ROPE: TYPE = Rope.ROPE; STREAM: TYPE = IO.STREAM; ReadArchiveFile: PUBLIC ENTRY PROC[file: ROPE, msgSet: WalnutDefs.MsgSet _ [NIL, -1]] RETURNS[numNew: INT] = { ENABLE UNWIND => NULL; ok: BOOL _ FALSE; fStream: STREAM; reason: ROPE; Raf: PROC = { at: INT; IF msgSet.name # NIL THEN [] _ WalnutDB.VerifyMsgSet[msgSet]; at _ WalnutLog.StartReadArchiveFile[file, msgSet.name].at; WalnutDB.SetReadArchivePos[at]; }; Raf2: PROC = { [] _ WalnutLog.EndReadArchiveFile[]; WalnutDB.SetReadArchivePos[0]; }; Caf: PROC[inProgress: BOOL] = { fromPos: INT _ 0; IF ~inProgress THEN { at: INT; ok _ WalnutLog.PrepareToCopyTempLog[ which: readArchive, pagesAlreadyCopied: 0, reportProc: WalnutOpsInternal.CheckReport]; IF ~ok THEN RETURN; at _ WalnutLog.StartCopyReadArchive[].at; WalnutDB.SetCopyReadArchivePos[at]; WalnutDB.SetOpInProgressPos[at]; WalnutRoot.CommitAndContinue[]; } ELSE { -- calculate fromPos logLen: INT _ WalnutLog.LogLength[]; startedCopyAt, startCopyPos: INT; startCopyPos _ WalnutDB.GetCopyReadArchivePos[]; IF WalnutLog.SetPosition[startCopyPos] # 0 THEN ERROR WalnutDefs.Error[$log, $BadLog, IO.PutFR["no entry at %g", IO.int[startCopyPos]]]; [] _ WalnutLog.NextEntry[]; -- skip the copy entry startedCopyAt _ WalnutLog.NextAt[]; fromPos _ logLen - startedCopyAt; }; CheckReport[ IO.PutFR["\nCopying the ReadArchiveTempLog, starting at bytePos %g\n", IO.int[fromPos]]]; WalnutLog.CopyTempLog[ readArchive, WalnutDB.GetCopyReadArchivePos[], fromPos, CheckReport]; CheckReport["\n"]; WalnutDB.SetParseLogInProgress[TRUE]; WalnutDB.SetParseLogPos[WalnutDB.GetOpInProgressPos[]]; WalnutDB.SetOpInProgressPos[-1]; }; WalnutOpsInternal.CheckInProgress[]; fStream _ WalnutStream.Open[name: file, readOnly: TRUE ! FS.Error => { CheckReport[error.explanation]; fStream _ NIL; CONTINUE} ].strm; IF fStream = NIL THEN RETURN[-1]; WalnutOpsInternal.CarefullyApply[proc: Raf, didUpdate: TRUE]; BEGIN ENABLE BEGIN FS.Error => { reason _ error.explanation; GOTO exit }; IO.Error => { reason _ FS.ErrorFromStream[stream].explanation; IF reason = NIL THEN reason _ "IO Error creating readArchiveLog"; GOTO exit }; END; [ok, reason] _ WalnutMiscLog.CreateReadArchiveLog[fStream, msgSet.name, CheckReport]; EXITS exit => ok _ FALSE; END; fStream.Close[ ! IO.Error, FS.Error => CONTINUE]; IF ~ok THEN { CheckReport[IO.PutFR[" Archive Read of %g failed", IO.rope[file]]]; IF reason # NIL THEN CheckReport[" Error reported as: ", reason]; RETURN[-1]; } ELSE Raf2[]; WalnutOpsInternal.LongRunningApply[Caf]; IF ~ok THEN { CheckReport[IO.PutFR[" Out of space trying to copy readArchiveLog for file %g", IO.rope[file]]]; RETURN[-1]; }; CheckReport["\nAdding messages to database\n"]; numNew _ WalnutOpsInternal.ParseLog[TRUE]; }; WriteArchiveFile: PUBLIC ENTRY PROC[ file: ROPE, msgSetList: LIST OF WalnutDefs.MsgSet, append: BOOL] RETURNS[ok: BOOL]= { ENABLE UNWIND => NULL; wStream: STREAM; someMsgWasTooBig: BOOL _ FALSE; walnutItemForm: ROPE = "@%05d 00525 %05d\n"; -- 20 chars, tioga formatting startHeaderForm: ROPE = "*start*\n%05d %05d US \n"; thisMsgSet, exp: ROPE; startHeaderFixedLen: INT = 24; first: BOOL _ TRUE; WriteProc: PROC[msg, TOCentry: ROPE, hasBeenRead: BOOL, startOfSubject: INT] = { textStart, textLen, formatLen, prefixLen: INT; length, walnutItemLen: INT _ 0; walnutItem: ROPE; [textStart, textLen, formatLen, , ] _ WalnutDB.GetMsgText[msg]; walnutItem _ Rope.Cat[msg, "\n", thisMsgSet, "\n"]; walnutItemLen _ WalnutMiscLog.walnutItemFixedLength + walnutItem.Length[] + formatLen; IF formatLen # 0 THEN walnutItemLen _ walnutItemLen + 1; -- for extra CR after formatting prefixLen _ startHeaderFixedLen + walnutItemLen; length _ prefixLen + textLen + 1; -- extra CR after text IF length > 99999 THEN { CheckReport[IO.PutFR["\nLength of msg %g is too big (%g bytes) - skipping", IO.rope[msg], IO.int[length]] ]; someMsgWasTooBig _ TRUE; RETURN }; wStream.PutRope[ IO.PutFR[startHeaderForm, IO.int[length], IO.int[prefixLen]] ]; wStream.PutRope[ IO.PutFR[walnutItemForm, IO.int[walnutItemLen-2], IO.int[formatLen] ]]; wStream.PutRope[walnutItem]; IF formatLen # 0 THEN { WalnutLog.CopyBytesToArchive[wStream, textStart+textLen, formatLen]; wStream.PutChar['\n]; }; wStream.PutChar['@]; WalnutLog.CopyBytesToArchive[wStream, textStart, textLen]; wStream.PutChar['\n]; }; ok _ FALSE; WalnutOpsInternal.CheckInProgress[]; BEGIN ENABLE BEGIN WalnutDefs.Error => { IF wStream # NIL THEN wStream.Close[ ! IO.Error, FS.Error => CONTINUE]; REJECT; }; WalnutDefs.VersionMismatch => { IF wStream # NIL THEN wStream.Close[ ! IO.Error, FS.Error => CONTINUE]; REJECT; }; IO.Error => { CheckReport[exp _ FS.ErrorFromStream[stream].explanation]; GOTO err }; FS.Error => { CheckReport[exp _ error.explanation]; GOTO err }; END; BEGIN wStream _ WalnutStream.Open[ name: file, useOldIfFound: append, exclusive: TRUE ! FS.Error => { CheckReport[error.explanation]; GOTO none }].strm; EXITS none => wStream _ NIL; END; IF wStream = NIL THEN { CheckReport[IO.PutFR["\nCould not open %g", IO.rope[file]]]; RETURN }; IF append THEN wStream.SetIndex[wStream.GetLength[]] ELSE { wStream.SetIndex[0]; wStream.SetLength[0]; }; CheckReport["\n Archiving: "]; FOR mL: LIST OF WalnutDefs.MsgSet _ msgSetList, mL.rest UNTIL mL=NIL DO thisMsgSet _ mL.first.name; IF ~WalnutDB.VerifyMsgSet[mL.first] THEN { CheckReport["\n MsgSet ", thisMsgSet, " doesn't exist - continuing"]; LOOP; }; IF first THEN first _ FALSE ELSE CheckReport[", "]; CheckReport[thisMsgSet]; [] _ WalnutDB.EnumerateMsgsInSet[name: thisMsgSet, proc: WriteProc]; ENDLOOP; EXITS err => { wStream.Close[]; ERROR WalnutDefs.Error[$log, $ErrorDuringWriteArchive, exp]; }; END; wStream.Close[]; ok _ TRUE; CheckReport["\n Finished writing archive file\n"]; }; END. BWalnutOpsArchiveFileImpl.mesa Copyright c 1985 by Xerox Corporation. All rights reserved. Willie-Sue, April 2, 1986 6:00:16 pm PST Donahue, August 5, 1985 2:47:44 pm PDT Implementation of Reading and Writing Archive files Types Procedures These procedures are some of the primitive atomic actions out of which Walnut is built. More complex operations will be found in WalnutClientOps (someday). Producing and reading archive files Reading an archive file is a long operation; it is resilient, so that it will complete when restarted. The file is first copied to the current log (if the archive file exists) and then the tail of the log is "replayed", which may involve creating new messages and shuffling them around in the database. Write a "readArchiveFile" log entry; if the archiveFile exists, parses it and writes the appropriate entries (new msgs and moves if msgSet is not "Active" (NIL defaults to categories specified in the file). Then replays the log. If the file couldn't be read, numNew = -1; Write an archive file that contains the messages from the given message sets. No log entry is written and no updates are made to the database (we just hold the monitor to guarantee that no changes to the message sets occur). -- the -2 below are because the bytecount within the prefix item does not include the surrounding @'s Κί– "cedar" style˜šΟnΟb™Jšœ Οmœ1™