<> <> <> 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] = <> 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\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.