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