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, Find, Length, ROPE, Substr], 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, newMsgSet: ROPE, removeFromOld, checkOnlyTitle: BOOLEAN _ FALSE] RETURNS [msgsMoved: LIST OF ROPE _ NIL] ~ { CreateFromRope: PROC [pattern: ROPE, ignoreCase: BOOL] RETURNS [ref: ACFind.Ref] ~ { CheckSpecialCases: PROC [in: ROPE] RETURNS [out: ROPE] ~ { RETURN [IF Rope.Equal[in, "*"] THEN "" ELSE in]; }; loc: INT; list: LIST OF ROPE _ NIL; WHILE (loc _ Rope.Find[s1: pattern, s2: "|"]) # -1 DO list _ CONS[CheckSpecialCases[Rope.Substr[base: pattern, len: loc]], list]; pattern _ Rope.Substr[base: pattern, start: loc+1]; ENDLOOP; list _ CONS[CheckSpecialCases[pattern], list]; ref _ ACFind.Create[keys: list, caseSensitive: ~ignoreCase]; }; patternRE: ACFind.Ref ~ CreateFromRope[pattern: pattern, ignoreCase: TRUE]; fromMsgSetsRE: ACFind.Ref ~ CreateFromRope[pattern: fromMsgSets, ignoreCase: TRUE]; msgSets: LIST OF ROPE; count: INT _ 0; CMSProc: PROC RETURNS[doReset: BOOL] = { WalnutOps.CreateMsgSet[newMsgSet, WalnutOps.MsgSetsInfo[].version]; RETURN[TRUE]; }; FOR mss: LIST OF ROPE _ WalnutOps.MsgSetNames[].mL, mss.rest UNTIL mss=NIL DO FindMsgSetName: ACFind.ActionProc = { pos _ position+1 - 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] ; ENDLOOP; [] _ WalnutWindow.QueueCall[CMSProc]; 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."]; }; 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: LoadSortDefs .\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: DumpSortDefs .\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] [ []]."; ResortCmd: Commander.CommandProc ~ { arg: LIST OF ROPE _ CommandTool.ParseToList[cmd].list; pattern, from, 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 THEN GOTO NoPattern; pattern _ arg.first; from _ IF arg.rest#NIL THEN arg.rest.first ELSE "#*"; to _ IF arg.rest#NIL AND arg.rest.rest#NIL THEN arg.rest.rest.first ELSE "Temp"; [] _ ReSort[pattern, from, to, deleteFromOld, checkOnlyTitle]; EXITS NoPattern => RETURN [msg: resortMsg]; }; TriggerCmd: Commander.CommandProc ~ { tokens: LIST OF ROPE _ CommandTool.ParseToList[cmd].list; priority: INT _ 10; usageMessage: ROPE ~ "Usage: Trigger [-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: DeleteTrigger 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", TriggerCmd, "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, January 13, 1986 3:11:46 pm PST Eric Nickell, April 3, 1986 12:25:38 pm PST Willie-Sue, July 12, 1985 11:16:15 am PDT Rick Beach, May 9, 1986 2:56:43 pm 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 Regular Expressions. Add to msgSets any names matching the fromMsgSets pattern. [position: INT, keyFound: ROPE] RETURNS [quit: BOOL _ FALSE] 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] 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) Κ2˜šœ™Icodešœ Οmœ=™HJšœ6™6J™+J™+K™)K™&—J˜šΟk ˜ Kšœžœ!˜-Jšœžœ˜Jšœ žœ˜(Jšœ žœ˜ Kšœžœ˜)Kšžœžœ ˜Kšœžœ˜Kšœžœ˜$Kšœžœ ˜Kšœžœ#žœ ˜;Kšœžœ5˜AKšœžœ˜Kšœ žœ˜*Kšœ žœ˜"Kšœ žœj˜yJšœ ˜ Kšœ žœ'˜9Kšœžœ ˜$Kšœžœ)˜=Jšœ˜—J˜šΠlnœžœž˜!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šœ:™:š  œžœ žœžœžœ˜Tš  œžœžœžœžœ˜:Kšžœžœžœžœ˜0K˜—Kšœžœ˜ Kš œžœžœžœžœ˜–>[s1: ROPE, pos1: INT _ 0, s2: ROPE, case: BOOL _ TRUE]šžœ.ž˜5K–9[base: ROPE, start: INT _ 0, len: INT _ 2147483647]šœžœ@˜KK–9[base: ROPE, start: INT _ 0, len: INT _ 2147483647]˜3Kšžœ˜—Kšœžœ#˜.K˜˜>šž˜Kšœ žœ˜%—K˜—š  œ˜%Jš œžœžœžœžœ˜9Kšœ žœ˜Kšœžœ:˜LK˜šžœžœž˜Kšœžœžœ˜)šœ ˜ K˜.K˜Kšœ˜—Kšžœ˜—Kš žœžœžœ žœžœ˜AKšœ<˜T