<> <> DIRECTORY Commander USING [CommandProc, Register], IO USING [int, Put, PutChar, PutRope, rope, STREAM], Rope USING [Cat, Concat, Equal, Fetch, Find, Index, ROPE, Size, SkipOver, SkipTo, Substr], RopeIO USING [FromFile], TiogaFileOps USING [CreateRoot, InsertAsLastChild, Ref, SetContents, SetFormat, SetStyle, Store], UserCredentials USING [GetUserCredentials]; WalnutToPeanut: CEDAR PROGRAM IMPORTS Commander, IO, Rope, RopeIO, TiogaFileOps, UserCredentials = BEGIN ROPE: TYPE = Rope.ROPE; Node: TYPE = TiogaFileOps.Ref; File: TYPE = REF FileRep; FileRep: TYPE = RECORD[ link: File, -- for fileList name: ROPE, -- category name ("Active") filename: ROPE, -- file name ("Active.mail") root: Node, -- root node last: Node -- last top-level node ]; Message: TYPE = REF MessageRep; MessageRep: TYPE = RECORD[ sender: ROPE, date: ROPE, categories: ROPE, contents: ROPE ]; simpleUserName, userRName: ROPE _ NIL; InitUserName: PROC = { uN: ROPE = UserCredentials.GetUserCredentials[].name; pos: INT = Rope.Find[uN, "."]; IF pos<0 THEN { simpleUserName _ uN; userRName _ Rope.Concat[uN, ".PA"] } ELSE { simpleUserName _ Rope.Substr[uN, 0, pos]; userRName _ uN }; }; fileList: File _ NIL; CreateFile: PROC[name: ROPE] RETURNS[File] = { file: File = NEW[FileRep]; file.name _ name; file.filename _ Rope.Concat[name, ".mail"]; file.root _ TiogaFileOps.CreateRoot[]; TiogaFileOps.SetStyle[file.root, "Mail"]; file.last _ TiogaFileOps.InsertAsLastChild[file.root]; TiogaFileOps.SetContents[file.last, file.filename]; file.link _ fileList; fileList _ file; RETURN[file]; }; GetFile: PROC[name: ROPE] RETURNS[File] = { FOR file: File _ fileList, file.link UNTIL file=NIL DO IF Rope.Equal[file.name, name, FALSE] THEN RETURN[file]; REPEAT FINISHED => RETURN[CreateFile[name]]; ENDLOOP; }; <> <<>> ParseFailed: ERROR = CODE; ParseMessage: PROC[m: ROPE] RETURNS[Message] = { message: Message = NEW[MessageRep]; index: INT _ 0; Require: PROC[x: ROPE] = { start: INT = index; IF Rope.Find[m, x, start]=start THEN index _ start+Rope.Size[x] ELSE ERROR ParseFailed; }; SkipTo: PROC[x: ROPE] = { start: INT = index; index _ Rope.Find[m, x, start]+Rope.Size[x]; }; ReadTo: PROC[x: ROPE] RETURNS[ROPE] = { start: INT = index; end: INT = Rope.Find[m, x, start]; index _ end+Rope.Size[x]; RETURN[Rope.Substr[m, start, end-start]]; }; ReadN: PROC[n: INT] RETURNS[ROPE] = { start: INT = index; index _ start+n; RETURN[Rope.Substr[m, start, index-start]]; }; Require["*start*\n"]; SkipTo["\n"]; Require["gvMsgID: "]; message.sender _ ReadTo[" $"]; SkipTo["@"]; message.date _ ReadN[9]; SkipTo["\n"]; Require["Categories: "]; message.categories _ ReadTo["\n"]; message.contents _ Rope.Substr[m, index]; RETURN[message]; }; DeleteLeadingSPs: PROC[m: ROPE] RETURNS[ROPE] = { start: INT _ 0; len: INT _ Rope.Size[m]; WHILE start maxLen THEN s _ Rope.Concat[Rope.Substr[s, 0, maxLen-3], "..."]; RETURN[s]; }; GetFieldContents: PROC[m, name: ROPE] RETURNS[contents: ROPE] = { start: INT _ Rope.Find[m, name]; IF start<0 THEN contents _ "?" ELSE { end: INT; start _ start + Rope.Size[name]; end _ Rope.Find[m, "\n", start]; IF end <= start THEN contents _ "?" ELSE contents _ Rope.Substr[m, start, end-start]; }; }; MakeHeader: PROC[message: Message] RETURNS[header: ROPE] = { date, name, subject: ROPE _ NIL; date _ message.date; IF Rope.Equal[message.sender, userRName, FALSE] OR Rope.Equal[message.sender, simpleUserName, FALSE] THEN name _ Rope.Concat["To: ", GetFieldContents[message.contents, "\nTo: "]] ELSE name _ message.sender; subject _ GetFieldContents[message.contents, "\nSubject: "]; header _ Rope.Cat["\t", date, "\t", Truncate[name, 16], "\t", Truncate[subject, 44]]; RETURN[header]; }; AppendMessage: PROC[file: File, message: Message] = { head: Node = TiogaFileOps.InsertAsLastChild[file.root, file.last]; body: Node = TiogaFileOps.InsertAsLastChild[head]; TiogaFileOps.SetContents[head, MakeHeader[message]]; TiogaFileOps.SetFormat[head, "header"]; TiogaFileOps.SetContents[body, DeleteTrailingCRs[message.contents]]; file.last _ head; }; StoreFile: PROC[file: File] = { TiogaFileOps.Store[file.root, file.filename]; file.root _ NIL; file.last _ NIL; }; EnumerateNames: PROC[rope: ROPE, action: PROC[ROPE]] = { length: INT = Rope.Size[rope]; index: INT _ 0; DO start: INT = Rope.SkipOver[rope, index, " "]; IF start CONTINUE]; IF log=NIL THEN { s.PutRope["unable to open file!\n"]; RETURN }; length _ Rope.Size[log]; s.Put[IO.int[length], IO.rope[" bytes\n"]]; start _ Rope.Find[log, "*start*\n"]; IF start<0 THEN { s.PutRope["No messages found!\n"]; RETURN }; s.PutRope["Reading...\n"]; WHILE start GOTO Abort]; count _ count+1; IF count MOD 10=0 THEN { s.PutChar[IF count MOD 100=0 THEN '! ELSE '~]; }; start _ end; ENDLOOP; s.Put[IO.rope["... "], IO.int[count], IO.rope[" messages\n"]]; UNTIL fileList=NIL DO file: File = fileList; fileList _ file.link; s.PutRope["Writing "]; s.PutRope[file.filename]; s.PutRope["..."]; StoreFile[file]; s.PutRope["ok\n"]; ENDLOOP; EXITS Abort => { s.PutRope["... failed!\n"]; s.PutRope["Do an Expunge with Walnut, then try running WalnutToPeanut again.\n"]; fileList _ NIL; }; }; WalnutToPeanutCommand: Commander.CommandProc = { ReadLog["Walnut.DBLog", cmd.out]; }; InitUserName[]; Commander.Register["WalnutToPeanut", WalnutToPeanutCommand, "Create Peanut mail files from a freshly-expunged Walnut log"]; END.