DIRECTORY ACFind USING [ActionProc, Create, Find, Ref], Basics USING [CompareCard], Commander USING [CommandProc, Register], CommandTool USING [ParseToList], Convert USING [IntFromRope, RopeFromInt], FS USING [Error], MBQueue USING [Create, Queue], MessageWindow USING [Append, Blink], Process USING [Detach], Rope USING [Cat, Equal, Fetch, Length, Match, ROPE], SymTab USING [Create, EachPairAction, Fetch, Insert, Pairs, Ref], UserCredentials USING [Get], UserProfile USING [Boolean, ListOfTokens], ViewerTools USING [TiogaContents], WalnutOps USING [AddMsg, CreateMsgSet, GetDisplayProps, GetMsg, MsgSetNames, MsgSetsInfo, MsgsInSetEnumeration, MoveMsg], WalnutSortDB, WalnutWindow USING [QueueCall, SelectMsgSetsFromMSNames], WalnutWindowInternal USING [Report], WalnutRegistry USING [EventProc, MsgProc, ProcSet, Register], WalnutSortMail; WalnutSortMailImpl: CEDAR MONITOR IMPORTS ACFind, Basics, Commander, CommandTool, Convert, FS, MBQueue, MessageWindow, Process, Rope, SymTab, UserCredentials, UserProfile, WalnutOps, WalnutRegistry, WalnutSortDB, WalnutWindow, WalnutWindowInternal EXPORTS WalnutSortMail = BEGIN ROPE: TYPE ~ Rope.ROPE; TriggerSet: TYPE ~ WalnutSortDB.TriggerSet; Trigger: TYPE ~ WalnutSortDB.Trigger; Error: PUBLIC ERROR [info: ROPE] = CODE; EachMsg: WalnutRegistry.MsgProc ~ { IF sortEnabled AND event=firstRead THEN SortMsg[IF UserProfile.Boolean[key: "WalnutSort.SelectOnTOC"] THEN WalnutOps.GetDisplayProps[msg: msgName].TOCentry ELSE WalnutOps.GetMsg[msgName].contents.contents]; }; SortMsg: PUBLIC PROC [msg: ROPE] ~ { EachKeyword: ACFind.ActionProc ~ { trig: REF Trigger _ NARROW[SymTab.Fetch[ts.mapping, keyFound].val]; SELECT Basics.CompareCard[trig.priority, highestPriorityFound] FROM less => RETURN; greater => { highestPriorityFound _ trig.priority; msNamesToActivate _ SymTab.Create[]; }; ENDCASE => NULL; FOR each: LIST OF ROPE _ trig.msNames, each.rest UNTIL each=NIL DO [] _ SymTab.Insert[msNamesToActivate, each.first, NIL]; ENDLOOP; }; EachMSName: SymTab.EachPairAction = { msNamesToActivateList _ CONS[key, msNamesToActivateList]; RETURN[FALSE]; }; highestPriorityFound: CARDINAL _ 0; msNamesToActivate: SymTab.Ref; msNamesToActivateList: LIST OF ROPE _ NIL; finder: ACFind.Ref _ NIL; ts: TriggerSet _ GetTriggerSet[]; finder _ NARROW[ts.clientData]; IF ACFind.Find[finder, msg, EachKeyword] THEN [] _ SymTab.Pairs[msNamesToActivate, EachMSName] ELSE msNamesToActivateList _ UserProfile.ListOfTokens["WalnutSort.DefaultSelections"]; IF msNamesToActivateList # NIL THEN [] _ WalnutWindow.SelectMsgSetsFromMSNames[msNamesToActivateList]; }; ReSort: PUBLIC PROC [pattern, fromMsgSets: LIST OF ROPE, newMsgSet: ROPE, removeFromOld, checkOnlyTitle: BOOLEAN _ FALSE] RETURNS [msgsMoved: LIST OF ROPE _ NIL] ~ { patternRE: ACFind.Ref ~ ACFind.Create[keys: pattern, caseSensitive: FALSE]; msgSets: LIST OF ROPE _ NIL; count: INT _ 0; CMSProc: PROC[] RETURNS[doReset: BOOL] = { doReset _ TRUE; WalnutOps.CreateMsgSet[newMsgSet, WalnutOps.MsgSetsInfo[].version]; FOR mss: LIST OF ROPE _ WalnutOps.MsgSetNames[].mL, mss.rest UNTIL mss=NIL DO FOR each: LIST OF ROPE _ fromMsgSets, each.rest UNTIL each=NIL DO IF Rope.Match[pattern: each.first, object: mss.first, case: FALSE] THEN { msgSets _ CONS[mss.first, msgSets]; EXIT; }; ENDLOOP; ENDLOOP; WalnutWindowInternal.Report["Messages moved:"]; FOR mss: LIST OF ROPE _ msgSets, mss.rest UNTIL mss=NIL DO ms: ROPE ~ mss.first; FOR msgs: LIST OF ROPE _ WalnutOps.MsgsInSetEnumeration[ms].mL, msgs.rest UNTIL msgs=NIL DO FindKeyInText: ACFind.ActionProc = { RETURN [TRUE] }; herald, text: ROPE; herald _ text _ WalnutOps.GetDisplayProps[msgs.first].TOCentry; IF ~checkOnlyTitle THEN { text _ WalnutOps.GetMsg[msgs.first].contents.contents; }; IF ACFind.Find[self: patternRE, text: text, action: FindKeyInText] THEN { count _ count+1; msgsMoved _ CONS[msgs.first, msgsMoved]; WalnutWindowInternal.Report[" ", herald]; IF removeFromOld THEN [] _ WalnutOps.MoveMsg[msgs.first, [ms], [newMsgSet]] ELSE [] _ WalnutOps.AddMsg[msgs.first, [ms], [newMsgSet]]; }; ENDLOOP; ENDLOOP; WalnutWindowInternal.Report[Convert.RopeFromInt[from: count], " messages moved."]; }; [] _ WalnutWindow.QueueCall[CMSProc] }; WatchWalnut: WalnutRegistry.EventProc = { SELECT event FROM stopped => WalnutSortDisable[]; started => IF Rope.Equal[user, UserCredentials.Get[].name, FALSE] THEN WalnutSortEnable[]; ENDCASE => NULL; }; user: ROPE _ NIL; sortEnabled: BOOLEAN _ FALSE; queue: MBQueue.Queue ~ MBQueue.Create[]; procSet: WalnutRegistry.ProcSet ~ [ eventProc: WatchWalnut, msgProc: EachMsg ]; WalnutSortEnable: PUBLIC ENTRY PROC ~ { ENABLE UNWIND => NULL; IF NOT sortEnabled THEN { currentUser: ROPE _ UserCredentials.Get[].name; IF NOT Rope.Equal[currentUser, user, FALSE] THEN { user _ currentUser; WalnutSortDB.DeclareSegment[user]; }; sortEnabled _ TRUE; TRUSTED {Process.Detach[FORK MakeWalnutSortGetTriggerSet[]];} }; }; WalnutSortDisable: PUBLIC ENTRY PROC ~ { ENABLE UNWIND => NULL; WalnutSortDB.Close[]; sortEnabled _ FALSE; }; LoadDefinitions: Commander.CommandProc ~ { tokens: LIST OF ROPE ~ CommandTool.ParseToList[cmd].list; usageMessage: ROPE ~ "Usage: WSLoad .\n"; SELECT TRUE FROM tokens=NIL => msg _ usageMessage; ENDCASE => WalnutSortDB.LoadFromFile[tokens.first ! FS.Error => {msg _ error.explanation; GOTO Complain}; ANY => {msg _ "Failed.\n"; GOTO Complain} ]; EXITS Complain => {RETURN [$Failure, msg]}; }; DumpDefinitions: Commander.CommandProc ~ { tokens: LIST OF ROPE ~ CommandTool.ParseToList[cmd].list; usageMessage: ROPE ~ "Usage: WSDump .\n"; SELECT TRUE FROM tokens=NIL => msg _ usageMessage; ENDCASE => WalnutSortDB.DumpToFile[tokens.first ! FS.Error => {msg _ error.explanation; GOTO Complain}; ANY => {msg _ "Failed.\n"; GOTO Complain} ]; EXITS Complain => {RETURN [$Failure, msg]}; }; MakeFinder: PROC [mapping: SymTab.Ref] RETURNS [finder: ACFind.Ref] ~ { BuildKeyList: SymTab.EachPairAction = { keyList _ CONS[key, keyList]; RETURN[FALSE]; }; keyList: LIST OF ROPE _ NIL; [] _ SymTab.Pairs[x: mapping, action: BuildKeyList]; finder _ ACFind.Create[keyList]; }; GetTriggerSet: PROC RETURNS [ts: TriggerSet] ~ { ts _ WalnutSortDB.GetTriggerSetFromDB[]; IF ts.clientData = NIL THEN ts.clientData _ MakeFinder[ts.mapping]; }; MakeWalnutSortGetTriggerSet: PROC ~ { [] _ GetTriggerSet[ ! Error => { MessageWindow.Append[info, TRUE]; MessageWindow.Blink[]; CONTINUE }]; }; WalnutSort: Commander.CommandProc ~ { tokens: LIST OF ROPE ~ CommandTool.ParseToList[cmd].list; usageMessage: ROPE ~ "Usage: WalnutSort on|off.\n"; SELECT TRUE FROM tokens=NIL OR Rope.Equal[tokens.first, "on", FALSE] => { WalnutSortEnable[]; msg _ "Sort enabled.\n"; }; Rope.Equal[tokens.first, "off", FALSE] => {WalnutSortDisable[]; msg _ "Sort disabled.\n"}; ENDCASE => msg _ usageMessage; }; resortMsg: ROPE ~ "WSResort [-[m][t]] _ IN ."; ResortCmd: Commander.CommandProc ~ { arg: LIST OF ROPE _ CommandTool.ParseToList[cmd].list; pattern: LIST OF ROPE _ NIL; to: ROPE; deleteFromOld, checkOnlyTitle: BOOL _ FALSE; IF arg = NIL THEN GOTO NoPattern; IF Rope.Fetch[arg.first]='- THEN { FOR k: NAT IN [0.. NAT[Rope.Length[arg.first]]) DO SELECT Rope.Fetch[arg.first, k] FROM 'm => deleteFromOld _ TRUE; 't => checkOnlyTitle _ TRUE; ENDCASE; ENDLOOP; arg _ arg.rest; }; IF arg = NIL OR arg.rest = NIL OR NOT Rope.Equal[arg.rest.first, "_"] THEN GOTO NoPattern; to _ arg.first; arg _ arg.rest.rest; IF Rope.Equal[s1: arg.first, s2: "*"] THEN { pattern _ LIST[""]; arg _ arg.rest; } ELSE UNTIL arg=NIL OR Rope.Equal[s1: arg.first, s2: "IN"] DO pattern _ CONS[arg.first, pattern]; arg _ arg.rest; ENDLOOP; IF arg = NIL OR arg.rest = NIL OR NOT Rope.Equal[s1: arg.first, s2: "IN"] THEN GOTO NoPattern; [] _ ReSort[pattern, arg.rest --from--, to, deleteFromOld, checkOnlyTitle]; EXITS NoPattern => RETURN [msg: resortMsg]; }; SetTrigger: Commander.CommandProc ~ { tokens: LIST OF ROPE _ CommandTool.ParseToList[cmd].list; priority: INT _ 10; usageMessage: ROPE ~ "Usage: WSSetTrigger [-priority] keyword messageSetName.\n"; SELECT TRUE FROM tokens=NIL => RETURN [msg: usageMessage]; Rope.Fetch[tokens.first]='- => { priority _ -Convert.IntFromRope[tokens.first]; tokens _ tokens.rest; }; ENDCASE; IF tokens=NIL OR tokens.rest=NIL THEN RETURN [msg: usageMessage]; WalnutSortDB.DeleteTrigger[tokens.rest.first, tokens.first]; WalnutSortDB.AddTrigger[tokens.rest.first, tokens.first, priority]; }; DeleteTrigger: Commander.CommandProc ~ { tokens: LIST OF ROPE ~ CommandTool.ParseToList[cmd].list; usageMessage: ROPE ~ "Usage: WSDeleteTrigger keyword messageSetName.\n"; IF tokens=NIL OR tokens.rest=NIL THEN RETURN [msg: usageMessage]; WalnutSortDB.DeleteTrigger[tokens.rest.first, tokens.first]; }; ListTriggers: Commander.CommandProc ~ { WalnutSortDB.DumpToStream[cmd.out]; }; Init: PROC ~ { OPEN Commander; Register["WalnutSort", WalnutSort, "WalnutSort on|off --Enable/disable auto message set button selection"]; Register["WSDump", DumpDefinitions, "Dump the Walnut Sort Definitions (WSDump filename)."]; Register["WSLoad", LoadDefinitions, "Load the Walnut Sort Definitions (WSLoad filename)."]; Register["WSSetTrigger", SetTrigger, "Create a keyword trigger (WSSetTrigger [-priority] keyword messageSetName)."]; Register["WSDeleteTrigger", DeleteTrigger, "Remove a keyword trigger (WSDeleteTrigger keyword messageSetName)."]; Register["WSListTriggers", ListTriggers, "List current triggers."]; Register["WSResort", ResortCmd, Rope.Cat["Resort Walnut messages into another message set. (", resortMsg, ")."]]; [] _ WalnutRegistry.Register[procSet: procSet, queue: queue]; }; Init[]; END. :WalnutSortMailImpl.mesa Copyright c 1984, 1985, 1986 by Xerox Corporation. All rights reserved. Created December 18, 1984 4:36:42 pm PST by Dave Rumph Dave Rumph, June 23, 1986 5:56:23 pm PDT Eric Nickell, June 25, 1986 3:57:00 pm PDT Willie-Sue, July 12, 1985 11:16:15 am PDT Donahue, May 12, 1986 7:53:15 am PDT PROC[msgName: Rope.ROPE, event: MsgEvent, clientData: REF ANY]; [key: SymTab.Key, val: SymTab.Val] RETURNS [quit: BOOL] Don't catch WalnutSortMail.Error here because retries are attempted below its generation and Walnut has successfully established communication with the server. Something else must be wrong. Let the debugger help figure it out. Here, no trigger was found - look for a default action in User Profile Note that pattern and fromMsgSets are LIST OF ROPE, and pattern may be "". fromMsgSetsRE: ACFind.Ref ~ ACFind.Create[keys: fromMsgSets, caseSensitive: FALSE]; Add to msgSets any names matching the fromMsgSets pattern. FindMsgSetName: ACFind.ActionProc = { [position: INT, keyFound: ROPE] RETURNS [quit: BOOL _ FALSE] pos _ position - Rope.Length[keyFound]; RETURN [TRUE] }; pos: INT _ INT.LAST; IF ACFind.Find[fromMsgSetsRE, mss.first, FindMsgSetName] AND pos=0 THEN msgSets _ CONS[mss.first, msgSets] ; Scan the messages in each message set. [position: INT, keyFound: ROPE] RETURNS [quit: BOOL _ FALSE] [event: WalnutRegistry.Event, clientData: REF ANY] Activates the WalnutSort facility. Disables the WalnutSort facility. [key: SymTab.Key, val: SymTab.Val] RETURNS [quit: BOOL] from _ arg; Dave Rumph, October 15, 1985 12:55:53 pm PDT Smoother interaction with DB when user changes. changes to: WalnutSortEnable (calls WalnutSortDB.DeclareSegment when user changes) Eric Nickell November 21, 1985 9:00:11 am PST changes to: WalnutSort (changed so that WalnutSort command defaults to turning WalnutSort on) Κ˜šœ™Icodešœ Οmœ=™HJšœ6™6J™(J™*K™)K™$—J˜šΟk ˜ Kšœžœ!˜-Jšœžœ˜Jšœ žœ˜(Jšœ žœ˜ Kšœžœ˜)Kšžœžœ ˜Kšœžœ˜Kšœžœ˜$Kšœžœ ˜Kšœžœ$žœ˜4Kšœžœ5˜AKšœžœ˜Kšœ žœ˜*Kšœ žœ˜"Kšœ žœj˜yJšœ ˜ Kšœ žœ'˜9Kšœžœ ˜$Kšœžœ)˜=Jšœ˜—J˜šœžœž˜!Kšžœ2žœš˜ΥJšžœ˜Jšœž˜J˜Jšžœžœžœ˜Jšœ žœ˜+Jšœ žœ˜%Icode2š œžœžœžœžœ˜(šΟnœ˜#Jšžœžœžœžœ™?K•StartOfExpansion [msg: ROPE]š žœ žœžœ žœ3žœ2žœ.˜ΞJ˜—šŸœžœžœžœ˜$šΟb œ˜"Kšœžœ žœ)˜Cšžœ9ž˜CKšœžœ˜šœ ˜ Kšœ%˜%Kšœ$˜$Kšœ˜—Kšžœžœ˜—š žœžœžœžœžœžœž˜BKšœ2žœ˜7Kšžœ˜—Kšœ˜K˜—–; -- [key: SymTab.Key, val: SymTab.Val] RETURNS [quit: BOOL]š  œ˜%KšΠck7™7Kšœžœ˜9Kšžœžœ˜K˜K˜—Kšœžœ˜#K˜Kš œžœžœžœžœ˜*Kšœžœ˜šœ!˜!K™δ—Kšœ žœ˜šžœ'ž˜-K˜0—šž˜K™FKšœQ˜Q—KšžœžœžœC˜fK˜—šŸœžœžœžœžœžœ žœ!žœžœžœ žœžœžœžœ˜₯Kšœ&žœžœžœ™JKšœDžœ˜KKšœLžœ™SKš œ žœžœžœžœ˜Kšœžœ˜šŸœžœžœ žœ˜*Kšœ žœ˜K˜CK™:š žœžœžœžœ(žœžœž˜M–@ -- [position: INT, keyFound: ROPE] RETURNS [quit: BOOL _ FALSE]šΠbnœ™%Kš‘<™