HuntForOldDLs.mesa
Copyright © 1986 by Xerox Corporation. All rights reserved.
Hal Murray, March 15, 1986 5:08:06 pm PST
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: BOOLFALSE;
allRegistries: ROPENIL;
defaultRegistries: ROPE = "??";
stop: BOOLFALSE;
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: BOOLFALSE;
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: BOOLFALSE;
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: BOOLFALSE;
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: BOOLFALSE;
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: BOOLFALSE;
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: BOOLFALSE] = {
notFound: BOOLFALSE;
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: ROPENIL; -- 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[];
}.