Grapevine: program to purge dead members from groups
DBPurge.mesa
Andrew Birrell October 20, 1983 9:43 am
DIRECTORY
Ascii USING[ Lower ],
Basics USING[ Comparison ],
Commander,
GVBasics USING[ MakeKey, Password ],
GVNames,
IO,
Rope,
UserCredentials USING[ Get ];
DBPurge: CEDAR PROGRAM
IMPORTS Ascii, Commander, GVBasics, GVNames, IO, Rope, UserCredentials
SHARES GVNames--GetEntry-- =
BEGIN
BreakName: PROC[name: Rope.ROPE] RETURNS[sn, reg: Rope.ROPE] =
BEGIN
FOR i: INT DECREASING IN [0..name.Length[])
DO IF name.Fetch[i] = '.
THEN RETURN[
sn: name.Substr[start: 0, len: i],
reg: name.Substr[start: i+1, len: name.Length[]-(i+1)]
];
ENDLOOP;
RETURN[ sn: NIL, reg: name ]
END;
Compare: PROC[a, b: Rope.ROPE] RETURNS[Basics.Comparison] =
This is NOT the same as Rope.Compare: GV's collating sequence forces upper case to lower case
BEGIN
aL: INT = a.Length[];
bL: INT = b.Length[];
FOR i: INT IN [0..MIN[aL, bL])
DO ac: CHAR = Ascii.Lower[a.Fetch[i]];
bc: CHAR = Ascii.Lower[b.Fetch[i]];
IF ac < bc THEN RETURN[less];
IF ac > bc THEN RETURN[greater];
ENDLOOP;
IF aL < bL THEN RETURN[less];
IF aL > bL THEN RETURN[greater];
RETURN[equal]
END;
Code: PROC[type: GVNames.Outcome] RETURNS[Rope.ROPE] =
{ RETURN[ SELECT type FROM
noChange => "no change",
group => "that's a group",
individual => "that's an individual",
notFound => "name not found",
protocolError => "protocol error",
wrongServer => "wrong server",
allDown => "all suitable servers are down or inaccessible",
badPwd => "incorrect password",
outOfDate => "out of date",
notAllowed => "you're not authorized to do that",
ENDCASE => "unknown return code" ] };
NameClass: TYPE = { individuals, groups, dead };
EnumInRegistry: PROC[pattern, registry: Rope.ROPE, which: NameClass, work: EnumProc, out: IO.STREAM ] =
BEGIN
enumName: Rope.ROPE = Rope.Cat[
SELECT which FROM
individuals => "Individuals.",
groups => "Groups.",
dead => "Dead.",
ENDCASE => ERROR,
registry];
info: GVNames.MemberInfo = GVNames.GetMembers[enumName];
WITH info SELECT FROM
i: group GVNames.MemberInfo =>
BEGIN
FOR m: GVNames.RListHandle ← i.members, m.rest UNTIL m = NIL
DO sn: Rope.ROPE = BreakName[m.first].sn;
IF Rope.Match[pattern: pattern, object: sn, case: FALSE]
THEN { IF work[m.first] THEN EXIT };
ENDLOOP;
END;
ENDCASE =>
out.PutF["\nCouldn't get members of \"%g\": %g",
[rope[enumName]], [rope[Code[info.type]]] ];
END;
EnumProc: TYPE = PROC[name: Rope.ROPE] RETURNS[exit: BOOLFALSE];
EnumAllNames: PROC[pattern: Rope.ROPE, which: NameClass, work: EnumProc, out: IO.STREAM ] =
BEGIN
patternSN, patternReg: Rope.ROPE;
RegWork: EnumProc =
BEGIN
out.PutF["\n\nEnumerating names in %g . . .\n", [rope[name]] ];
EnumInRegistry[patternSN, BreakName[name].sn, which, work, out];
END;
[patternSN, patternReg] ← BreakName[pattern];
out.PutRope["\n\nFinding the registries . . ."];
EnumInRegistry[patternReg, "GV", groups, RegWork, out];
END;
ListType: TYPE = { m, o, f };
Remove: PROC[entry, element: Rope.ROPE, which: ListType, out: IO.STREAM] =
BEGIN
user: Rope.ROPE = UserCredentials.Get[].name;
pwd: GVBasics.Password = GVBasics.MakeKey[UserCredentials.Get[].password];
outcome: GVNames.Outcome = SELECT which FROM
m => GVNames.RemoveMember[user, pwd, entry, element],
o => GVNames.RemoveOwner[user, pwd, entry, element],
f => GVNames.RemoveFriend[user, pwd, entry, element],
ENDCASE => ERROR;
out.PutRope[IF outcome = group THEN "ok" ELSE Code[outcome]];
out.PutChar['\n];
END;
RemoveAll: PROC[deadList: GVNames.RListHandle, pattern: Rope.ROPE, out: IO.STREAM] =
BEGIN
GroupWork: EnumProc = TRUSTED -- The silly compiler can't handle "info"
BEGIN
info: REF GVNames.GetEntryInfo;
rc: GVNames.NameType;
[rc, info] ← GVNames.GetEntry[name];
SELECT rc FROM
notFound => NULL;
group =>
WITH info^ SELECT FROM
i: group GVNames.GetEntryInfo =>
BEGIN
SubListWork[name, i.members.current, m];
SubListWork[name, i.owners.current, o];
SubListWork[name, i.friends.current, f];
END;
ENDCASE => ERROR;
ENDCASE =>
BEGIN
out.PutF["\nCouldn't read entry for \"%g\": %g",
[rope[name]], [rope[Code[rc]]] ];
exit ← TRUE;
END;
END;
SubListWork: PROC[name: Rope.ROPE, list: GVNames.RListHandle, which: ListType] =
BEGIN
d: GVNames.RListHandle ← deadList;
UNTIL d = NIL OR list = NIL
DO SELECT Compare[d.first, list.first] FROM
greater => list ← list.rest;
less => d ← d.rest;
equal =>
BEGIN
victim: Rope.ROPE = d.first;
out.PutF["%g is a%g of %g: removing . . . ",
[rope[victim]],
[rope[SELECT which FROM
m => " member", o => "n owner", f => " friend",
ENDCASE => ERROR]],
[rope[name]] ];
Remove[entry: name, element: victim, which: which, out: out];
list ← list.rest; d ← d.rest;
END;
ENDCASE => ERROR;
ENDLOOP;
END;
out.PutRope["\n\nNames to be removed:\n"];
FOR d: GVNames.RListHandle ← deadList, d.rest UNTIL d = NIL
DO out.PutRope[d.first]; out.PutChar[' ]; ENDLOOP;
EnumAllNames[pattern, groups, GroupWork, out];
END;
PurgeEnum: PROC[members, groups: Rope.ROPE, out: IO.STREAM] =
BEGIN
deadList: GVNames.RListHandle ← NIL;
DeadWork: EnumProc =
BEGIN
prev: GVNames.RListHandle ← NIL;
out.PutRope[name]; out.PutChar[' ];
FOR d: GVNames.RListHandle ← deadList, d.rest UNTIL d = NIL
DO IF Compare[name, d.first] = less THEN EXIT;
prev ← d;
ENDLOOP;
IF prev = NIL
THEN deadList ← CONS[first: name, rest: deadList]
ELSE prev.rest ← CONS[first: name, rest: prev.rest];
END;
out.PutRope["Finding all \"dead\" names . . ."];
EnumAllNames[members, dead, DeadWork, out];
RemoveAll[deadList, groups, out];
out.PutRope["\n\nFinished\n"];
END;
DoIt: Commander.CommandProc--[cmd: Handle]-- =
{ PurgeEnum[members: "*.*", groups: "*.*", out: cmd.out] };
Commander.Register[key: "DBPurge", proc: DoIt,
doc: "Purges dead members from groups in the Grapevine database"];
END.