DIRECTORY Commander USING [CommandProc, Handle, Register], CommandTool USING [NextArgument], FS USING [StreamOpen], IO, RuntimeError USING [ UNCAUGHT ], Rope USING [Concat, Equal, Fetch, Length, ROPE, Substr], ViewerTools USING [ TiogaContents ], WalnutKernelDefs USING [ LogEntry, LogEntryObject ], WalnutOps USING [EnumerateMsgSets, EnumeratorForMsgs, EnumerateMsgs, GetHandleForRootfile, GetMsg, GetHasBeenRead, MsgSet, NextMsg, WalnutOpsHandle ], WalnutStream USING [ WriteEntry, WriteMsgBody ] ; WalnutCompactImpl: CEDAR PROGRAM IMPORTS Commander, FS, IO, Rope, RuntimeError, WalnutOps, CommandTool, WalnutStream ~ { OPEN IO; ROPE: TYPE = Rope.ROPE; Handle: TYPE ~ WalnutOps.WalnutOpsHandle; LogEntry: TYPE ~ WalnutKernelDefs.LogEntry; LogEntryObject: TYPE ~ WalnutKernelDefs.LogEntryObject; rootFileName: ROPE; newLogFileName: ROPE; doReportID: BOOL _ TRUE; WalnutCompactCommand: Commander.CommandProc = { ENABLE RuntimeError.UNCAUGHT => GOTO Failed; rootFileName _ CommandTool.NextArgument[cmd]; newLogFileName _ CommandTool.NextArgument[cmd]; [result, msg] _ DoCompact[cmd]; EXITS Failed => RETURN[$Failed, "*** Uncaught signal ***"] }; DoCompact: PROC[cmd: Commander.Handle] RETURNS[result: REF ANY _ NIL, msg: ROPE _ NIL]~ { logStream: IO.STREAM _ FS.StreamOpen[newLogFileName, $create]; handle: WalnutOps.WalnutOpsHandle _ WalnutOps.GetHandleForRootfile[rootFileName]; numRead, numWritten: INT_0; IF handle=NIL THEN RETURN[$Failed, "No such Walnut database"]; []_WalnutStream.WriteEntry[logStream, NEW[LogFileInfo LogEntryObject _ [LogFileInfo[key: "Swinehart's mail database", internalFileID: 100001, logSeqNo: 1]]]]; { msgSetEntry: REF CreateMsgSet LogEntryObject _ NEW[CreateMsgSet LogEntryObject _ [CreateMsgSet[NIL]]]; WriteMsgSets: PROC[msgSet: WalnutOps.MsgSet] RETURNS [continue: BOOL_TRUE] ~ { msgSetEntry.msgSet _ msgSet.name; []_WalnutStream.WriteEntry[logStream, msgSetEntry]; }; [] _ WalnutOps.EnumerateMsgSets[handle, TRUE, WriteMsgSets]; }; { createEntry: REF CreateMsg LogEntryObject _ NEW[CreateMsg LogEntryObject _ [CreateMsg[show: FALSE]]]; addEntry: REF AddMsg LogEntryObject _ NEW[AddMsg LogEntryObject _ [AddMsg[]]]; moveEntry: REF MoveMsg LogEntryObject _ NEW[MoveMsg LogEntryObject _ [MoveMsg[]]]; beenReadEntry: REF HasBeenRead LogEntryObject _ NEW[HasBeenRead LogEntryObject _ [HasBeenRead[]]]; enum: WalnutOps.EnumeratorForMsgs _ WalnutOps.EnumerateMsgs[handle]; DO msgID: ROPE; msList: LIST OF ROPE; headers: REF TEXT; contents: ViewerTools.TiogaContents; last: INT; char0: CHAR; isActive: BOOL _ FALSE; -- Will be TRUE if message is in Active message set [msgID, msList, headers] _ WalnutOps.NextMsg[enum]; IF msgID=NIL THEN EXIT; numRead _ numRead+1; IF msList#NIL AND msList.rest=NIL AND msList.first.Equal["deleted", FALSE] THEN LOOP; contents _ WalnutOps.GetMsg[handle, msgID].contents; createEntry.msg _ msgID; last _ contents.contents.Length[] - 1; char0 _ IF last>=0 THEN contents.contents.Fetch[0] ELSE '~; IF ( contents.formatting.Length[] # 0 ) AND ( char0 = '\r OR char0 = '\l ) THEN { IF contents.contents.Fetch[last] = '\000 THEN -- NUL for padding { contents.contents _ Rope.Substr[contents.contents, 1, last-1]; contents.formatting _ Rope.Concat["\000", contents.formatting] } ELSE IF char0 = '\r OR char0 = '\l THEN contents.contents _ Rope.Substr[contents.contents, 1]; }; createEntry.textLen _ contents.contents.Length[]; createEntry.formatLen _ contents.formatting.Length[]; []_WalnutStream.WriteEntry[logStream, createEntry]; []_WalnutStream.WriteMsgBody[logStream, contents]; IF WalnutOps.GetHasBeenRead[handle, msgID] THEN { beenReadEntry.msg _ msgID; []_WalnutStream.WriteEntry[logStream, beenReadEntry]; }; FOR mL: LIST OF ROPE _ msList, mL.rest WHILE mL#NIL DO SELECT TRUE FROM mL.first.Equal["active", FALSE] => { isActive _ TRUE; LOOP; }; mL.first.Equal["deleted", FALSE] => LOOP; -- should not happen, but harmless mL.rest=NIL AND ~isActive => { moveEntry.from _ "Active"; moveEntry.to _ mL.first; moveEntry.msg _ msgID; []_WalnutStream.WriteEntry[logStream, moveEntry]; }; ENDCASE => { addEntry.to _ mL.first; addEntry.msg _ msgID; []_WalnutStream.WriteEntry[logStream, addEntry]; }; ENDLOOP; numWritten _ numWritten+1; IF doReportID THEN cmd.out.PutF["%g\n", rope[msgID]] ELSE IF numRead MOD 10=0 THEN cmd.out.PutRope["! "] ELSE cmd.out.PutRope["."]; ENDLOOP; }; logStream.Close[]; cmd.err.PutF["%g messages read, %g written.\n", int[numRead], int[numWritten]]; RETURN[,"Compact log created on /walnut/Walnut.LogC"]; }; Commander.Register["WalnutCompact", WalnutCompactCommand, "Create most compact Walnut log possible from current, consistent database."]; }. ~WalnutCompactImpl.mesa Copyright Σ 1990 by Xerox Corporation. All rights reserved. Swinehart, November 21, 1990 5:07 pm PST Notes: Writes a new Walnut log file as Walnut.LogC, from an open, allegedly-consistent WallTapestry database. The new log contains only message set creation entries for current message sets, and bodies of messages that are still in the database and not deleted. The user is expected to follow a procedure that has yet to be designed to reassign logs and scavenge a new index set into existence. [cmd: Commander.Handle] RETURNS [result: REF ANY _ NIL, msg: ROPE _ NIL] Flush the stream oncet in a while. Κ– "cedar" style•NewlineDelimiter ˜code™Kšœ<™KšœQ˜QKšœžœ˜Kšžœžœžœžœ%˜>šœ&žœ˜FKšœW˜W—˜šœ žœ˜.Kšžœ-žœ˜7—š ’ œžœžœ žœžœ˜NK˜!K˜3K˜—Kšœ(žœ˜˜>—K˜—šž˜šžœ žœ ž˜"Kšœ6˜6——K˜—Kšœ1˜1Kšœ5˜5K˜3K˜2šžœ)žœ˜1K˜K˜5K˜—šžœžœžœžœžœžœžœžœžœž˜GKšœžœžœžœ˜>Kšœžœžœ£"˜Lšœžœžœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ1˜1Kšœ˜—šžœ˜ Kšœ˜K˜Kšœ0˜0Kšœ˜—Kšžœ˜—K˜Kšžœ žœ"˜4Kšžœžœ žœžœ˜3Kšžœ˜Kšžœ˜—K˜Kš‘œ"‘™$—Kšœ˜K˜OKšžœ0˜6K˜K˜K˜—Kšœˆ˜ˆ—K˜Kšœ˜—…—$ͺ