-- Transport mechanism: Maintain: high-level maintenance of groups -- [Indigo]<Grapevine>Maintain>MaintainGroups.mesa -- Andrew Birrell 13-Jan-82 16:19:46 -- Philip Karlton 15-May-81 17:17:48 DIRECTORY Ascii USING[ CR, SP ], BodyDefs USING[ maxRemarkLength, maxRNameLength, Remark, RName ], GlassDefs USING[ Handle ], MaintainPrivate USING[ BadName, CheckMailName, CopyName, Del, Failed, Handle, Operate, ReadWord ], NameInfoDefs USING[ Close, Enumerate, GetMembers, IsMemberDirect, MemberInfo, Membership ], ProtocolDefs USING[ Handle, SendCount, SendRName ], Segments USING[ FileNameProblem ], Streams USING[ Destroy, Ended, GetChar, NewStream, Read, Handle, SetIndex ], String USING[ AppendString, EquivalentString ]; MaintainGroups: PROGRAM IMPORTS MaintainPrivate, NameInfoDefs, ProtocolDefs, Segments, Streams, String EXPORTS MaintainPrivate = BEGIN OPEN MaintainPrivate; VerifyGroup: PUBLIC PROCEDURE[handle: MaintainPrivate.Handle] = BEGIN OPEN handle; ReadWord[glass, ": "L, group]; CopyName[from: group, to: dName]; glass.WriteString[" ... "L]; glass.SendNow[]; [] ← VerifySingleGroup[glass, group ! HaveIDoneThis => RESUME[FALSE]]; END; AddListOfMembers: PUBLIC PROCEDURE[handle: MaintainPrivate.Handle] = BEGIN OPEN handle; file: STRING = [40]; ReadWord[glass, " from file: "L, file]; ReadWord[glass, " to group: "L, group]; CopyName[from: group, to: dName]; AddList[handle: handle, file: file, name: group]; END; RemoveAllMemberships: PUBLIC PROCEDURE[handle: MaintainPrivate.Handle] = BEGIN OPEN handle; registry: STRING = [64]; ReadWord[glass, " in registry: "L, registry]; ReadWord[glass, " for R-Name: "L, dName]; AllGroups[handle, registry, dName, [delete[]] ]; END; TypeAllGroups: PUBLIC PROCEDURE[handle: MaintainPrivate.Handle] = BEGIN OPEN handle; registry: STRING = [64]; ReadWord[glass, " in registry: "L, registry]; ReadWord[glass, " containing R-Name: "L, dName]; AllGroups[handle, registry, dName, [type[]] ]; END; ModifyAllOccurrences: PUBLIC PROCEDURE[handle: MaintainPrivate.Handle] = BEGIN OPEN handle; registry: STRING = [64]; ReadWord[glass, " of R-Name: "L, name]; MaintainPrivate.CheckMailName[name]; ReadWord[glass, " in groups in registry: "L, registry]; ReadWord[glass, " to be R-Name: ", dName]; MaintainPrivate.CheckMailName[dName]; IF String.EquivalentString[name, dName] THEN glass.WriteString[" ... silly!"L] ELSE AllGroups[handle, registry, name, [replace[dName]] ]; END; VerifyAllGroups: PUBLIC PROCEDURE[handle: MaintainPrivate.Handle] = BEGIN OPEN handle; registry: STRING = [64]; ReadWord[glass, " in registry "L, registry]; EnumerateGroups[glass, registry, VerifySingleGroup ! HaveIDoneThis => RESUME[TRUE] ]; END; CompareRNames: PROC[n1, n2: BodyDefs.RName] RETURNS [{less, equal, greater}] = BEGIN length: CARDINAL = MIN [n1.length, n2.length]; i: CARDINAL; FOR i IN [0 .. length) DO BEGIN ch1: CHARACTER = IF n1[i] IN CHARACTER['A .. 'Z] THEN (n1[i] - 'A) + 'a ELSE n1[i]; ch2: CHARACTER = IF n2[i] IN CHARACTER['A .. 'Z] THEN (n2[i] - 'A) + 'a ELSE n2[i]; IF ch1 < ch2 THEN RETURN [less]; IF ch1 > ch2 THEN RETURN [greater]; END ENDLOOP; IF n1.length < n2.length THEN RETURN [less] ELSE IF n1.length = n2.length THEN RETURN [equal] ELSE RETURN [greater]; END; EnumerateGroups: PROC[glass: GlassDefs.Handle, registry: STRING, work: PROC[glass: GlassDefs.Handle, group: BodyDefs.RName]RETURNS[done:BOOLEAN] ] = BEGIN OPEN glass; groupName: BodyDefs.RName = [BodyDefs.maxRNameLength]; groupInfo: NameInfoDefs.MemberInfo; String.AppendString[groupName, "Groups."L]; String.AppendString[groupName, registry]; WriteString[" ... finding groups ... "L]; glass.SendNow[]; groupInfo ← NameInfoDefs.GetMembers[groupName]; WITH groupInfo SELECT FROM notFound => BEGIN WriteChar['"]; WriteString[registry]; WriteString[""" is not a registry"L]; ERROR MaintainPrivate.Failed[]; END; allDown => BEGIN WriteString["all servers for that registry are down"L]; ERROR MaintainPrivate.Failed[]; END; group => BEGIN Enumeratee: PROC[member: BodyDefs.RName]RETURNS[done:BOOLEAN] = { done ← work[glass, member] }; WriteString["enumerating them ... "L]; glass.SendNow[]; NameInfoDefs.Enumerate[members, Enumeratee ! UNWIND => NameInfoDefs.Close[members] ]; NameInfoDefs.Close[members]; END; ENDCASE => ERROR; END; AllGroups: PROC[handle: MaintainPrivate.Handle, registry: STRING, name: BodyDefs.RName, action: RECORD[ var: SELECT type:* FROM type, delete => NULL, replace => [new: BodyDefs.RName], ENDCASE] ] = BEGIN CheckForMember: PROC[glass: GlassDefs.Handle, group: BodyDefs.RName] RETURNS[done: BOOLEAN] = BEGIN OPEN glass; info: NameInfoDefs.Membership = NameInfoDefs.IsMemberDirect[group, name]; done ← FALSE; IF DelTyped[] THEN ERROR MaintainPrivate.Del[]; SELECT info FROM allDown => { WriteString["all down"L]; ERROR MaintainPrivate.Failed }; notGroup => NULL --strange--; no => NULL; yes => BEGIN WriteString[group]; WITH action SELECT FROM replace => BEGIN WriteString[": adding"L]; [] ← MaintainPrivate.Operate[handle: handle, op: AddMember, name: group, value: new]; END; type, delete => NULL; ENDCASE => ERROR; WITH action SELECT FROM type => NULL; delete, replace => BEGIN WriteString[": removing"L]; [] ← MaintainPrivate.Operate[handle: handle, op: DeleteMember, name: group, value: name]; END; ENDCASE => ERROR; WriteString["; "L]; SendNow[]; END; ENDCASE => ERROR; END; EnumerateGroups[handle.glass, registry, CheckForMember]; END; HaveIDoneThis: SIGNAL[new: BodyDefs.RName] RETURNS[BOOLEAN] = CODE; VerifySingleGroup: PROC[glass: GlassDefs.Handle, group: BodyDefs.RName] RETURNS[done: BOOLEAN] = BEGIN OPEN glass; info: NameInfoDefs.MemberInfo = NameInfoDefs.GetMembers[group]; done ← FALSE; WITH info SELECT FROM notFound => BEGIN WriteString["group """L]; WriteString[group]; WriteString[""" doesn't exist ... "L]; END; allDown => BEGIN WriteString["all servers for """L]; WriteString[group]; WriteString[""" are down"L]; done ← TRUE; ERROR MaintainPrivate.Failed[]; END; individual => BEGIN WriteString[group]; WriteString[" is an individual! ... "L]; END; group => BEGIN CheckMember: PROC[member: BodyDefs.RName]RETURNS[done: BOOLEAN] = BEGIN done ← FALSE; IF DelTyped[] THEN ERROR MaintainPrivate.Del[]; MaintainPrivate.CheckMailName[member ! MaintainPrivate.BadName => BEGIN WriteChar[Ascii.CR]; WriteString["Bad name """L]; WriteString[member]; WriteString[""" in group "L]; WriteString[group]; WriteString[" ... "L]; GOTO bad END ]; FOR index: CARDINAL DECREASING IN [0..member.length) DO IF member[index] = '↑ THEN BEGIN IF NOT (SIGNAL HaveIDoneThis[member]) THEN done ← VerifySingleGroup[glass, member]; EXIT END; ENDLOOP; EXITS bad => NULL; END; NameInfoDefs.Enumerate[members, CheckMember ! HaveIDoneThis => IF String.EquivalentString[new, group] THEN RESUME[TRUE]; UNWIND => NameInfoDefs.Close[members] ]; NameInfoDefs.Close[members]; END; ENDCASE => ERROR; END; AddList: PROC[handle: MaintainPrivate.Handle, file: STRING, name: BodyDefs.RName] = BEGIN stream: Streams.Handle = Streams.NewStream[ name: file, access: Streams.Read ! Segments.FileNameProblem[] => GOTO badFile ]; ParseFile[handle, stream, name ! UNWIND => Streams.Destroy[stream] ]; Streams.Destroy[stream]; EXITS badFile => { handle.glass.WriteString[" ... """L]; handle.glass.WriteString[file]; handle.glass.WriteString[""" doesn't exist"L]; ERROR MaintainPrivate.Failed[] } END; ParseFile: PROC[handle: MaintainPrivate.Handle, stream: Streams.Handle, name: BodyDefs.RName] = BEGIN OPEN handle.glass; count: CARDINAL ← 0; memberLength: CARDINAL ← 0; ch: CHARACTER; Get: PROC RETURNS[CHARACTER] = INLINE { RETURN[ ch ← IF Streams.Ended[stream] THEN '; ELSE Streams.GetChar[stream] ] }; orderOK: BOOLEAN ← TRUE; badFound: BOOLEAN ← FALSE; remark: BodyDefs.Remark = [BodyDefs.maxRemarkLength]; remarkExists: BOOLEAN ← FALSE; colonPos: CARDINAL ← 0; FOR index: CARDINAL IN [0..LAST[CARDINAL]) DO IF Streams.Ended[stream] THEN EXIT; IF Get[] = ': THEN { remarkExists ← TRUE; colonPos ← index; EXIT }; ENDLOOP; -- parse members and read remark -- BEGIN prevMember: BodyDefs.RName = [BodyDefs.maxRNameLength]; FirstWork: PROC[member: BodyDefs.RName] = BEGIN IF orderOK AND NOT badFound AND CompareRNames[prevMember,member] # less THEN BEGIN WriteString["name """L]; WriteString[member]; WriteString[""" isn't in alphabetical order"L]; orderOK ← FALSE; END ELSE BEGIN prevMember.length ← 0; String.AppendString[prevMember, member] END; memberLength ← memberLength + SIZE[StringBody[member.length]]; IF orderOK THEN MaintainPrivate.CheckMailName[member ! MaintainPrivate.BadName => BEGIN IF NOT badFound THEN { badFound ← TRUE; WriteString["bad name(s): "L]; } ELSE WriteString[", "L]; WriteString[member]; CONTINUE END ]; END; WriteString[" ... checking members ... "L]; SingleParseOfFile[stream, name, remarkExists, colonPos, remark, FirstWork]; END; IF orderOK AND NOT badFound AND remarkExists THEN BEGIN WriteString["setting remark"L]; [] ← MaintainPrivate.Operate[handle: handle, op: ChangeRemark, name: name, remark: remark]; END; -- add members -- IF orderOK AND NOT badFound THEN BEGIN AddListWork: PROC[str: ProtocolDefs.Handle] = BEGIN SecondWork: PROC[member: BodyDefs.RName] = BEGIN ProtocolDefs.SendRName[str, member]; count ← count + 1; END; ProtocolDefs.SendCount[str, memberLength]; count ← 0; SingleParseOfFile[stream, name, remarkExists, colonPos, remark, SecondWork]; END; WriteString["adding members"L]; [] ← MaintainPrivate.Operate[handle: handle, op: AddListOfMembers, name: name, sendRList: AddListWork]; END ELSE ERROR MaintainPrivate.Failed[]; WriteString[". "L]; WriteDecimal[count]; WriteString[" members"L]; END; SingleParseOfFile: PROC[stream: Streams.Handle, name: BodyDefs.RName, remarkExists: BOOLEAN, colonPos: CARDINAL, remark: BodyDefs.Remark, memberWork: PROC[BodyDefs.RName] ] = BEGIN ch: CHARACTER; Get: PROC RETURNS[CHARACTER] = INLINE { RETURN[ ch ← IF Streams.Ended[stream] THEN '; ELSE Streams.GetChar[stream] ] }; Streams.SetIndex[stream, 0]; IF remarkExists THEN BEGIN remark.length ← 0; FOR index: CARDINAL IN [0..colonPos) DO IF remark.length < remark.maxlength THEN { remark[index] ← Get[]; remark.length ← remark.length+1 } ELSE [] ← Get[]; ENDLOOP; [] ← Get[] -- skip the colon --; END; [] ← Get[]; DO member: BodyDefs.RName = [BodyDefs.maxRNameLength]; DO SELECT ch FROM Ascii.SP, Ascii.CR => NULL; ENDCASE => EXIT; [] ← Get[]; ENDLOOP; IF ch = '; THEN EXIT; DO SELECT ch FROM ';, ',, Ascii.SP, Ascii.CR => EXIT; ENDCASE => NULL; IF member.length < member.maxlength THEN { member[member.length] ← ch; member.length←member.length+1 }; [] ← Get[]; ENDLOOP; IF member.length > 0 THEN BEGIN memberWork[member]; member.length ← 0; END; IF ch = ', THEN [] ← Get[]; ENDLOOP; END; END.