Grapevine: program to purge dead members from groups
DBPurge.mesa
Hal Murray September 2, 1984 6:17:15 am PDT
Andrew Birrell October 20, 1983 9:43 am
DIRECTORY
Ascii USING [Lower],
Basics USING [Comparison],
Commander USING[CommandProc, Register],
GVBasics USING [MakeKey, Password],
GVNames,
IO,
Process USING [Detach],
Rope,
UserCredentials USING [Get],
ViewerIO USING [CreateViewerStreams];
DBPurge:
CEDAR
PROGRAM
IMPORTS Ascii, Commander, GVBasics, GVNames, IO, Process, Rope, UserCredentials, ViewerIO
SHARES GVNames--GetEntry-- =
BEGIN
BreakName:
PROC[name: Rope.
ROPE]
RETURNS[sn, reg: Rope.
ROPE] =
BEGIN
length: INT = name.Length[];
FOR i:
INT
DECREASING
IN [0..length)
DO
IF name.Fetch[i] = '.
THEN
RETURN[
sn: name.Substr[start: 0, len: i],
reg: name.Substr[start: i+1, len: 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: BOOL ← FALSE];
EnumAllNames:
PROC[pattern: Rope.
ROPE, which: NameClass, work: EnumProc, out:
IO.
STREAM ] =
BEGIN
patternSN, patternReg: Rope.ROPE;
RegWork: EnumProc =
BEGIN
out.PutF["\n\n%t Enumerating names in %g . . .\n", IO.time[], [rope[name]] ];
EnumInRegistry[patternSN, BreakName[name].sn, which, work, out];
END;
[patternSN, patternReg] ← BreakName[pattern];
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;
RemoveDeadInGroups:
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];
CheckForOwnerOfSelf[name, i.owners.current];
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;
CheckForOwnerOfSelf:
PROC[name: Rope.
ROPE, list: GVNames.RListHandle] =
BEGIN
IF list =
NIL
THEN {
out.PutF["%g has no owners. ***\n", [rope[name]] ];
RETURN; };
UNTIL list =
NIL DO
SELECT Compare[name, list.first]
FROM
greater => list ← list.rest;
less => EXIT;
equal =>
BEGIN
out.PutF["%g is an owner of itself. *********\n", [rope[name]] ];
EXIT;
END;
ENDCASE => ERROR;
ENDLOOP;
END;
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];
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;
RemoveDeadInGroups[deadList, groups, out];
out.PutRope["\n\nFinished\n"];
END;
DoIt: Commander.CommandProc =
[cmd: Handle] RETURNS [result: REF ← NIL, msg: Rope.ROPE ← NIL]
TRUSTED{ Process.Detach[FORK ReallyDoit[]] };
ReallyDoit:
PROC = {
PurgeEnum[
members: "*.*",
groups: "*.*",
out: ViewerIO.CreateViewerStreams["DBPurge"].out]; };
Commander.Register[key: "DBPurge", proc: DoIt,
doc: "Purges dead members from groups in the Grapevine database"];
END.