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 ] = 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 = 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. >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 This is NOT the same as Rope.Compare: GV's collating sequence forces upper case to lower case [cmd: Handle] RETURNS [result: REF _ NIL, msg: Rope.ROPE _ NIL] Ê…˜Jšœ4™4Jšœ ™ Jšœ,™,Jšœ(™(J˜šÏk ˜ Jšœœ˜Jšœœ ˜Jšœ œ˜'Jšœ œ˜#J˜Jšœ˜Jšœœ ˜Jšœ˜Jšœœ˜Jšœ œ˜%—J˜šœ œ˜Jšœ&œ*˜YJšœÏc œ˜—J˜Jš˜J˜š Ïn œœ œœœ˜>Jš˜Jšœœ˜š œœ œœ ˜'šœœœ˜"Jšœ"˜"Jšœ2˜2—Jšœ˜—Jšœœ ˜Jšœ˜—J˜šŸœœ œœ˜;Jšœ^™^Jš˜Jšœœ˜Jšœœ˜š œœœœ ˜!Jšœœ˜#Jšœœ˜#Jšœ œœ˜Jšœ œœ ˜ Jšœ˜—Jšœ œœ˜Jšœ œœ ˜ Jšœ˜Jšœ˜—J˜šŸœœœœ˜6šœœœ˜Jšœ˜Jšœ˜Jšœ%˜%Jšœ˜Jšœ"˜"Jšœ˜Jšœ;˜;Jšœ˜Jšœ˜Jšœ1˜1Jšœ˜%——J˜Jšœ œ!˜0J˜š Ÿœœœ)œœ˜gJš˜šœœ ˜šœ˜Jšœ˜Jšœ˜Jšœ˜—Jšœœ˜J˜ —J˜8šœœ˜˜Jš˜šœ,œ˜?Jšœ œ˜&Jšœ0œ˜8Jšœœœœ˜$Jšœ˜—Jšœ˜——šœ˜ šœ0˜0Jšœ,˜,——Jšœ˜—J˜JšŸœœœ œœœœ˜CJ˜š Ÿ œœœ)œœ˜[Jš˜Jšœœ˜!šœ˜Jš˜Jšœ3œ˜MJ˜@Jšœ˜—J˜-J˜7Jšœ˜—J˜Jšœ œ˜J˜š Ÿœœœœœ˜JJš˜Jšœ œ˜-JšœJ˜Jšœœ˜,Jšœ5˜5Jšœ4˜4Jšœ5˜5Jšœœ˜—Jšœ œœœ˜=J˜Jšœ˜—J˜š Ÿœœ.œœœ˜]Jš˜šœœž)˜GJš˜Jšœœ˜Jšœ˜Jšœ$˜$šœ˜Jšœ œ˜˜šœœ˜šœ ˜ Jš˜J˜(Jšœ'˜'Jšœ(˜(Jšœ,˜,Jšœ˜—Jšœœ˜——šœ˜ Jš˜šœ0˜0Jšœ!˜!—Jšœœ˜ Jšœ˜——Jšœ˜—šŸ œœ œ/˜PJš˜Jšœ"˜"šœœœ˜šœ˜(Jšœ˜J˜˜Jš˜Jšœ œ ˜˜-J˜šœœ˜J˜/Jšœœ˜—J˜—Jšœ=˜=Jšœ˜Jšœ ˜ Jšœ˜—Jšœœ˜—Jšœ˜—Jšœ˜—šŸœœ œ˜GJš˜šœœœ˜J˜3Jšœ˜ —šœ˜šœ˜%Jšœ˜Jšœœ˜ ˜Jš˜J˜AJšœ˜Jšœ˜—Jšœœ˜—Jšœ˜—Jšœ˜—Jšœ.˜.Jšœ˜—J˜š Ÿ œœœœœ˜=Jš˜Jšœ œ˜$šœ˜Jš˜Jšœœ˜ Jšœ#˜#šœ+œ˜>Jšœœœ˜+J˜ Jšœ˜—Jšœœ œ˜?Jšœ œ˜4Jšœ˜—J˜0J˜+J˜*šœ+œ˜>Jšœ˜Jšœ˜Jšœ˜—Jšœ*˜*J˜Jšœ˜—J˜šœ˜Jš œœ œœ œœ™?Jšœœ˜-J˜—šŸ œœ˜šœ ˜ Jšœ˜Jšœ˜Jšœ5˜5——J˜˜.J˜BJ˜—Jšœ˜—…—ì!¯