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
Hal Murray, March 17, 1986 9:41:58 pm PST
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 ] = {
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 =>
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] AND work[m.first] THEN EXIT;
ENDLOOP;
ENDCASE =>
out.PutF["\nCouldn't get members of \"%g\": %g",
[rope[enumName]], [rope[Code[info.type]]] ];
};
EnumProc: TYPE = PROC[name: Rope.ROPE] RETURNS[exit: BOOL ← FALSE];
EnumAllNames:
PROC[pattern: Rope.
ROPE, which: NameClass, work: EnumProc, out:
IO.
STREAM ] = {
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];
};
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] = {
GroupWork: EnumProc = {
info: REF GVNames.GetEntryInfo;
rc: GVNames.NameType;
[rc, info] ← GVNames.GetEntry[name];
SELECT rc
FROM
notFound => NULL;
group => {
group: REF group GVNames.GetEntryInfo = NARROW[info];
SubListWork[name, group.members.current, m];
SubListWork[name, group.owners.current, o];
SubListWork[name, group.friends.current, f];
CheckForNoUpArrow[name];
CheckForBogusArrows[name];
CheckForNoRemark[name, group.remark];
CheckForNoMembers[name, group.members.current];
CheckForOwnerOfSelf[name, group.owners.current]; };
ENDCASE => {
out.PutF["\nCouldn't read entry for \"%g\": %g",
[rope[name]], [rope[Code[rc]]] ];
exit ← TRUE; };
};
SubListWork:
PROC[name: Rope.
ROPE, list: GVNames.RListHandle, which: ListType] = {
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 => {
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; };
ENDCASE => ERROR;
ENDLOOP;
};
CheckForNoUpArrow:
PROC [name: Rope.
ROPE] = {
IF Rope.Find[name, "^."] # -1 THEN RETURN;
IF Rope.Find[name, ".GV"] = -1 THEN RETURN;
IF Rope.Find[name, ".MS"] = -1 THEN RETURN;
IF Rope.Find[name, ".Internet"] = -1 THEN RETURN;
out.PutF["%g has no uparrow. *\n", [rope[name]] ];
};
CheckForBogusArrows:
PROC [name: Rope.
ROPE] = {
IF Rope.Find[name, ""] # -1
THEN
out.PutF["%g has a new uparrow. **\n", [rope[name]] ];
};
CheckForNoRemark:
PROC [name: Rope.
ROPE, remark: Rope.
ROPE] = {
IF Rope.IsEmpty[remark]
THEN
out.PutF["%g has no remark. *\n", [rope[name]] ];
};
CheckForNoMembers:
PROC [name: Rope.
ROPE, members: GVNames.RListHandle] = {
IF members =
NIL
THEN
out.PutF["%g has no members. *\n", [rope[name]] ];
};
CheckForOwnerOfSelf:
PROC [name: Rope.
ROPE, owners: GVNames.RListHandle] = {
IF owners =
NIL
THEN {
out.PutF["%g has no owners. ***\n", [rope[name]] ];
RETURN; };
UNTIL owners =
NIL
DO
SELECT Compare[name, owners.first]
FROM
greater => owners ← owners.rest;
less => EXIT;
equal => {
out.PutF["%g is an owner of itself. *********\n", [rope[name]] ];
EXIT; };
ENDCASE => ERROR;
ENDLOOP;
};
EnumAllNames[pattern, groups, GroupWork, out];
};
PurgeEnum:
PROC [members, groups: Rope.
ROPE, out:
IO.
STREAM] = {
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"];
};
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.