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: BOOLFALSE];
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: REFNIL, msg: Rope.ROPENIL]
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.