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