<> <> <> <<>> DIRECTORY Buttons USING [Button, ButtonProc, Create], Containers USING [ChildXBound, ChildYBound, Container, Create], GVBasics USING [oldestTime], GVNames USING [CheckStamp, GetList, GetMembers, GetRemark, ListType, MemberInfo, RemarkInfo, RListHandle], GVSend USING [AddRecipient, AddToItem, CheckValidity, Create, Handle, Send, StartSend, StartText], IO USING [card, Flush, PutF, PutFR, PutRope, STREAM, rope, time], Labels USING [Create, Label, Set], Menus USING [CreateEntry, CreateMenu, InsertMenuEntry, Menu, MenuProc], Process USING [Pause, SecondsToTicks], Rope USING [Cat, Equal, Length, ROPE, SkipOver, SkipTo, Substr], Rules USING [Create, Rule], UserCredentials USING [Get], ViewerIO USING [CreateViewerStreams], ViewerOps USING [CreateViewer, PaintViewer, SetMenu, SetOpenHeight], ViewerClasses USING [Viewer], ViewerTools USING [MakeNewTextViewer, GetContents, SetContents, SetSelection]; HuntForOldDLs: CEDAR MONITOR IMPORTS Buttons, Containers, GVNames, GVSend, IO, Labels, Menus, Process, Rope, Rules, UserCredentials, ViewerIO, ViewerOps, ViewerTools = { ROPE: TYPE = Rope.ROPE; entryHeight: CARDINAL = 15; entryVSpace: CARDINAL = 10; entryHSpace: CARDINAL = 10; heightSoFar: CARDINAL _ 0; container: Containers.Container; registries: ViewerClasses.Viewer; totalGroups: Labels.Label; currentName: Labels.Label; exceptions: ViewerClasses.Viewer; inStr, outStr: IO.STREAM; BuildOuter: PROC = { myMenu: Menus.Menu _ Menus.CreateMenu[]; container _ Containers.Create[ info: [name: "Hunt for Old DLs", scrollable: FALSE] ]; Menus.InsertMenuEntry[menu: myMenu, entry: Menus.CreateEntry[name: "DoIt", proc: DoIt] ]; Menus.InsertMenuEntry[menu: myMenu, entry: Menus.CreateEntry[name: "Stop", proc: StopIt] ]; ViewerOps.SetMenu[viewer: container, menu: myMenu]; BuildUserInput[]; BuildUserFeedback[]; BuildExceptions[]; ViewerOps.SetOpenHeight[viewer: container, clientHeight: heightSoFar]; ViewerOps.PaintViewer[viewer: container, hint: all]; }; BuildUserInput: PROC = { Bl4: Buttons.Button; heightSoFar _ heightSoFar+entryVSpace/2; Bl4 _ Buttons.Create[info: [name: "Registries:", parent: container, border: FALSE, wy: heightSoFar], proc: ForceSelToRegistries]; registries _ ViewerTools.MakeNewTextViewer[info: [parent: container, wx: Bl4.wx+Bl4.ww+entryHSpace, wy: heightSoFar, ww: 0, wh: 2*entryHeight, data: defaultRegistries, scrollable: TRUE, border: FALSE], paint: FALSE]; Containers.ChildXBound[container, registries]; heightSoFar _ heightSoFar+entryVSpace+registries.wh; }; ForceSelToRegistries: Buttons.ButtonProc = { IF mouseButton#red THEN ViewerTools.SetContents[registries, defaultRegistries] ELSE ViewerTools.SetSelection[registries]; }; BuildUserFeedback: PROC = { label: Labels.Label; rule: Rules.Rule; rule _ Rules.Create[info: [parent: container, wx: 0, wy: heightSoFar, ww: 0, wh: 1]]; Containers.ChildXBound[container, rule]; heightSoFar _ heightSoFar+entryVSpace; label _ Labels.Create[ info: [name: "DL's:", parent: container, wx: 0, wy: heightSoFar, border: FALSE]]; totalGroups _ Labels.Create[ info: [ name: "0000", parent: container, wx: label.wx+label.ww+entryHSpace, wy: heightSoFar, border: TRUE]]; Labels.Set[totalGroups, "0"]; label _ Labels.Create[info: [name: "Current Name:", parent: container, wx: totalGroups.wx+totalGroups.ww+5*entryHSpace, wy: heightSoFar, border: FALSE]]; currentName _ Labels.Create[ info: [ name: "", parent: container, wx: label.wx+label.ww+entryHSpace, wy: heightSoFar, border: FALSE]]; Labels.Set[currentName, "none"]; Containers.ChildXBound[container, currentName]; heightSoFar _ heightSoFar+entryVSpace+label.wh; }; BuildExceptions: PROC = { r2: Rules.Rule; r2 _ Rules.Create[info: [parent: container, wx: 0, wy: heightSoFar, ww: 0, wh: 1]]; Containers.ChildXBound[container, r2]; heightSoFar _ heightSoFar+entryVSpace; exceptions _ ViewerOps.CreateViewer[flavor: $Typescript, info: [name: "", parent: container, wx: 0, wy: heightSoFar, wh: 5*entryHeight, border: FALSE]]; heightSoFar _ heightSoFar+entryVSpace+exceptions.wh; Containers.ChildXBound[container, exceptions]; Containers.ChildYBound[container, exceptions]; [in: inStr, out: outStr] _ ViewerIO.CreateViewerStreams[name: "", viewer: exceptions, backingFile: "HuntForOldDLs.log"]; }; <<**************** start of operational code ****************>> <<>> mapperStopped: ERROR = CODE; mappingInProgress: BOOL _ FALSE; allRegistries: ROPE _ NIL; defaultRegistries: ROPE = "??"; stop: BOOL _ FALSE; DoIt: Menus.MenuProc = { IF ~ReserveMapper[] THEN RETURN; Map[ ! mapperStopped => {outStr.PutF["\nStopped at %t.\n\n", IO.time[]]; CONTINUE} ]; ReleaseMapper[]; }; -- DoIt-- StopIt: Menus.MenuProc = {SetStop[]}; ReserveMapper: ENTRY PROC RETURNS [BOOL] = { IF mappingInProgress THEN RETURN [FALSE] ELSE {mappingInProgress _ TRUE; RETURN[TRUE]}; }; ReleaseMapper: ENTRY PROC = {mappingInProgress _ FALSE; stop _ FALSE}; SetStop: ENTRY PROC = {stop _ TRUE}; StopSet: ENTRY PROC RETURNS [BOOL] = {RETURN [stop]}; Stopped: PROC RETURNS [BOOL] = { IF StopSet[] THEN ERROR mapperStopped ELSE RETURN[FALSE]}; ReportAllDown: PROC [dot: BOOL, name: ROPE] = { IF dot THEN outStr.PutRope["."] ELSE outStr.PutF["\nAll down: %g. ", [rope[name]]]; }; ForWordsInRopeDo: PROC [r: ROPE, work: PROC[ROPE] RETURNS[done: BOOL]] RETURNS [stopped: BOOL] = { parsePosition: CARDINAL _ Rope.SkipOver[r, 0, " "]; -- skip initial blanks UNTIL parsePosition = Rope.Length[r] DO --parse registry input-- start: CARDINAL = parsePosition; parsePosition _ Rope.SkipTo[r, parsePosition, " "]; IF work[Rope.Substr[r, start, parsePosition-start]] THEN RETURN [stopped: TRUE]; parsePosition _ Rope.SkipOver[r, parsePosition, " "]; ENDLOOP; RETURN [stopped: FALSE] }; -- ForWordsInRopeDo -- CheckRegistryValidity: PROC [reg: ROPE] RETURNS [done: BOOL] = { printDot: BOOL _ FALSE; UNTIL Stopped[] DO SELECT GVNames.CheckStamp[Rope.Cat[reg, ".gv"]] FROM group => NULL; individual, notFound => { outStr.PutF["%g%g\n\n", [rope[reg]], [rope[" is not a valid registry."]]]; ERROR mapperStopped }; allDown => {ReportAllDown[printDot, reg]; printDot_TRUE; LOOP}; ENDCASE => ERROR; EXIT; ENDLOOP; RETURN [done: FALSE] }; --CheckRegistryValidity-- GroupNotFound: ERROR = CODE; GetRList: PROC [n: ROPE, t: GVNames.ListType] RETURNS [l: ROPE] = { printDot: BOOL _ FALSE; UNTIL Stopped[] DO WITH GVNames.GetList[n, GVBasics.oldestTime, t] SELECT FROM g: GVNames.MemberInfo[group] => { AddNameToRope: PROC [m: ROPE] RETURNS [done: BOOL] = { IF l # NIL THEN l _ Rope.Cat[l, ", "]; l _ Rope.Cat[l, m]; done _ FALSE }; l _ NIL; EnumerateRList[g.members, AddNameToRope]; }; a: GVNames.MemberInfo[allDown] => {ReportAllDown[printDot, n]; printDot_TRUE; LOOP}; ENDCASE => ERROR GroupNotFound; EXIT; ENDLOOP; }; --GetRList-- GetMembers: PROC [g: ROPE] RETURNS [members: GVNames.RListHandle] = { printDot: BOOL _ FALSE; UNTIL Stopped[] DO WITH GVNames.GetMembers[g] SELECT FROM g: GVNames.MemberInfo[group] => members _ g.members; a: GVNames.MemberInfo[allDown] => { ReportAllDown[printDot, g]; printDot _ TRUE; LOOP; }; ENDCASE => ERROR GroupNotFound; EXIT; ENDLOOP; }; --GetMembers-- GetMemberCount: PROC [g: ROPE] RETURNS [count: CARDINAL] = { printDot: BOOL _ FALSE; UNTIL Stopped[] DO WITH GVNames.GetMembers[g] SELECT FROM g: GVNames.MemberInfo[group] => count _ g.count; a: GVNames.MemberInfo[allDown] => { ReportAllDown[printDot, g]; printDot _ TRUE; LOOP; }; ENDCASE => ERROR GroupNotFound; EXIT; ENDLOOP; }; --GetMembers-- GetRemark: PROC [g: ROPE] RETURNS [r: ROPE] = { printDot: BOOL _ FALSE; UNTIL Stopped[] DO info: GVNames.RemarkInfo; [info, r] _ GVNames.GetRemark[g]; SELECT info FROM group => IF Rope.Length[r] = 0 THEN r _ NIL; allDown => {ReportAllDown[printDot, g]; printDot_TRUE; LOOP}; ENDCASE => ERROR GroupNotFound; EXIT; ENDLOOP; }; --GetRemark-- CardToRope: PROC [c: CARDINAL] RETURNS [ROPE] = {RETURN [IO.PutFR[v1: IO.card[c]]]}; EnumerateRList: PROC [r: GVNames.RListHandle, p: PROC [ROPE] RETURNS [BOOL] ] = { FOR r _ r, r.rest UNTIL r=NIL OR p[r.first] DO ENDLOOP; }; senderPwd: ROPE = UserCredentials.Get[].password; sender: ROPE = UserCredentials.Get[].name; SendTheMessage: PROC [group: ROPE, registry: ROPE, remark: ROPE, count: INT] = { recipient: ROPE = Rope.Cat["Owners-", group]; msg: ROPE; handle: GVSend.Handle; msg _ Rope.Cat[msg, "Date: ", IO.PutFR["%G", IO.time[]], "\n"]; msg _ Rope.Cat[msg, "From: ", sender, "\n"]; msg _ Rope.Cat[msg, "Subject: Is ", group, " really needed any more?\n"]; msg _ Rope.Cat[msg, "To: ", recipient, "\n"]; msg _ Rope.Cat[msg, "Reply-to: Owners-", registry, ".GV\n"]; msg _ Rope.Cat[msg, "\n"]; msg _ Rope.Cat[msg, "Name: " , group, "\n"]; msg _ Rope.Cat[msg, "Remark: " , remark, "\n"]; msg _ Rope.Cat[msg, "Members: ", IO.PutFR["%G", IO.card[count]], "\n"]; msg _ Rope.Cat[msg, "\n"]; msg _ Rope.Cat[msg, "It's time for a big Grapevine cleanup. Is this DL really used these days? If it can be deleted, just Answer this message and it will go away.\n"]; msg _ Rope.Cat[msg, "\n"]; msg _ Rope.Cat[msg, "If not, please consider moving this DL to the NS mail system or splitting it into a GV/NS list. Consult your local support group if you need information or assistance.\n"]; msg _ Rope.Cat[msg, "\n"]; msg _ Rope.Cat[msg, "Thanks for your help. Sorry for the clutter.\n"]; msg _ Rope.Cat[msg, "\n"]; msg _ Rope.Cat[msg, "(This is a recording. Don't bother to reply if you want to keep the DL.)\n"]; handle _ GVSend.Create[]; [] _ GVSend.StartSend[ handle: handle, senderPwd: senderPwd, sender: sender, returnTo: NIL, validate: TRUE ]; GVSend.AddRecipient[handle, recipient]; IF GVSend.CheckValidity[handle, NIL] # 1 THEN ERROR; GVSend.StartText[handle]; GVSend.AddToItem[handle, msg]; GVSend.Send[handle]; }; Map: PROC = { currentRegistry: ROPE; EnumerateRegistry: PROC [registry: ROPE] RETURNS [BOOL] = { rG: ROPE = Rope.Cat["groups.", registry]; regRLH: GVNames.RListHandle; Labels.Set[currentName, rG]; regRLH _ GetMembers[rG ! GroupNotFound => {outStr.PutF["\n%g not found?", [rope[rG]]]; ERROR mapperStopped}]; currentRegistry _ registry; EnumerateRList[regRLH, GroupWork]; RETURN[FALSE] }; --EnumerateRegsitry-- GroupWork: PROC [group: ROPE] RETURNS [done: BOOL _ FALSE] = { notFound: BOOL _ FALSE; remark: Rope.ROPE; count: CARDINAL; Labels.Set[currentName, group]; Labels.Set[totalGroups, CardToRope[totalGrp _ totalGrp + 1]]; count _ GetMemberCount[group ! GroupNotFound => {notFound _ TRUE; CONTINUE}]; remark _ GetRemark[group ! GroupNotFound => {notFound _ TRUE; CONTINUE}]; IF notFound THEN { IO.PutF[outStr, "%G isn't a group??? ***\n", IO.rope[group]]; RETURN; }; IO.PutF[outStr, "%4G: %G ...", IO.card[totalGrp], IO.rope[group]]; IF skipper # NIL AND ~Rope.Equal[skipper, group, FALSE] THEN { IO.PutF[outStr, " skipped.\n"]; RETURN; }; skipper _ NIL; SendTheMessage[group, currentRegistry, remark, count]; IO.PutF[outStr, " ok.\n"]; IF FALSE THEN Process.Pause[Process.SecondsToTicks[15]]; }; -- GroupWork-- inputRegs: ROPE = ViewerTools.GetContents[registries]; totalGrp: CARDINAL _ 0; skipper: ROPE _ NIL; -- Hack for restarting after a crash outStr.PutF["\nRunning at %t.\n", IO.time[]]; Labels.Set[totalGroups, "0"]; [] _ ForWordsInRopeDo[inputRegs, CheckRegistryValidity]; [] _ ForWordsInRopeDo[inputRegs, EnumerateRegistry]; outStr.PutF["\nDone at %t.\n\n", IO.time[]]; IO.Flush[outStr]; }; --Map-- BuildOuter[]; }.