-- Grapevine: database comparison tool
-- DBCompare.mesa
-- Andrew Birrell 17-Dec-81 14:09:34
DIRECTORY
BodyDefs,
IODefs,
LocateDefs USING[ FindRegServer, FoundServerInfo ],
NameInfoDefs,
ProtocolDefs,
PupDefs,
StringDefs,
TimeDefs;
DBCompare: PROGRAM
IMPORTS IODefs, LocateDefs, NameInfoDefs, ProtocolDefs, PupDefs,
StringDefs, TimeDefs =
BEGIN
OPEN BodyDefs, IODefs, NameInfoDefs, ProtocolDefs, PupDefs;
target: STRING = [64]; -- server under investigation --
targetAddr: PupDefs.PupAddress; -- target's address --
targetStr: Handle ← NIL; -- stream to target server --
serverAddr: PupDefs.PupAddress; -- other server's address --
addrKnown: BOOLEAN ← FALSE; -- validity of serverAddr --
str: Handle ← NIL; -- stream to other server --
-- "Operate" procedure stolen from Maintain and modified to reject
-- the target server --
Operate: PUBLIC PROC[op: ProtocolDefs.RSOperation, name: BodyDefs.RName,
value: BodyDefs.RName ← NIL,
connect: BodyDefs.Connect ← NIL,
remark: BodyDefs.Remark ← NIL,
key: BodyDefs.Password ← [0,0,0,0],
sendRList: PROC[ProtocolDefs.Handle] ← NIL ]
RETURNS[ rc: ProtocolDefs.ReturnCode ] =
BEGIN
OPEN ProtocolDefs;
TryUpdate: PROC[str: ProtocolDefs.Handle] =
BEGIN
SendRSOperation[str, op];
IF op # NoOp THEN SendRName[str, name];
SELECT op FROM
IN [Expand..ReadEntry] =>
SendTimestamp[str, BodyDefs.oldestTime];
IdentifyCaller =>
SendPassword[str:str, pw: key, key: [0,0,0,0]];
IN [AddMember..DeleteFriend], NewName,
IN [IsMemberDirect..IsFriendClosure] =>
{ IF value = NIL THEN ERROR; SendRName[str, value] };
Authenticate, CreateIndividual, ChangePassword =>
SendPassword[str:str, pw: key, key: [0,0,0,0]];
ChangeConnect =>
{ IF connect = NIL THEN ERROR; SendConnect[str, connect] };
ChangeRemark =>
{ IF remark = NIL THEN ERROR; SendRemark[str, remark] };
AddListOfMembers =>
{ IF sendRList = NIL THEN ERROR; sendRList[str] };
ENDCASE => NULL;
SendNow[str];
IF op # NoOp THEN rc ← ReceiveRC[str];
END;
oldBad: BOOLEAN ← FALSE;
Create: PROC =
BEGIN
serverSite: STRING = [21] --377#377#177777|177777--;
PupDefs.AppendPupAddress[serverSite, serverAddr];
WriteString[serverSite];
WriteString[" ... "L];
str ← ProtocolDefs.CreateStream[serverAddr];
END;
Destroy: PROC =
BEGIN
IF str # NIL THEN DestroyStream[str];
str ← NIL;
END;
Accept: PROC[addr: PupDefs.PupAddress]RETURNS[BOOLEAN] =
BEGIN
addr.socket ← RegServerEnquirySocket;
IF addr = targetAddr THEN RETURN[FALSE];
IF str # NIL AND serverAddr # addr THEN Destroy[];
IF str = NIL
THEN BEGIN
serverAddr ← addr;
Create[ ! Failed => GOTO failed];
addrKnown ← TRUE;
END;
RETURN[TRUE];
EXITS failed => RETURN[FALSE]
END;
BEGIN
IF str # NIL
THEN BEGIN
TryUpdate[ str ! Failed => GOTO streamGone ];
EXITS streamGone => Destroy[];
END;
IF str = NIL
THEN BEGIN
IF addrKnown
THEN Create[ ! Failed => GOTO notThere]
ELSE BEGIN
[] ← LocateDefs.FindRegServer["x.GV"L, Accept];
IF str = NIL THEN GOTO notThere;
END;
TryUpdate[str ! Failed => GOTO notThere];
END;
IF rc.code = WrongServer
THEN oldBad ← TRUE
ELSE oldBad ← FALSE;
EXITS notThere => { Destroy[]; oldBad ← TRUE };
END;
IF oldBad
THEN BEGIN -- need to find the correct R-Server --
foundInfo: LocateDefs.FoundServerInfo;
WriteString["Locating registration server ... "L];
foundInfo ← LocateDefs.FindRegServer[name, Accept];
WITH foundInfo SELECT FROM
notFound => rc ← [BadRName, notFound];
allDown => rc ← [AllDown,notFound];
found =>
BEGIN
TryUpdate[ str ! Failed => GOTO down ];
EXITS down =>
{ Destroy[]; rc ← [AllDown,notFound] };
END;
ENDCASE => ERROR;
END;
END;
WriteStamp: PROC[stamp: Timestamp] =
BEGIN
text: STRING = [30];
WriteOctal: PROC[n: CARDINAL] =
BEGIN
buffer: STRING = [6] --177777--;
StringDefs.AppendNumber[buffer, n, 8];
WriteString[buffer];
END;
WriteChar['[];
WriteOctal[stamp.net];
WriteChar['#];
WriteOctal[stamp.host];
WriteChar[',];
TimeDefs.AppendDayTime [text, TimeDefs.UnpackDT [stamp.time]];
WriteString[text];
WriteChar[']];
END;
WriteType: PROC[type: RNameType] =
BEGIN
WriteString[SELECT type FROM
group => "group"L,
individual => "individual"L,
notFound => "not found"L,
dead => "dead"L,
ENDCASE => ERROR];
END;
WriteRC: PROC[rc: ReturnCode] =
BEGIN
WriteString[SELECT rc.code FROM
done => "ok"L,
noChange => "no change"L,
outOfDate => "out of date"L,
NotAllowed => "not allowed"L,
BadOperation => "bad operation"L,
BadProtocol => "bad protocol"L,
BadRName => "bad R-Name: "L,
BadPassword => "bad password"L,
WrongServer => "wrong server"L,
AllDown => "all suitable R-Servers down"L,
ENDCASE => ERROR];
IF rc.code = BadRName THEN WriteType[rc.type];
END;
WriteComponent: PROC[type: RNameType, i: CARDINAL] =
BEGIN
WriteString["component="L];
WriteString[SELECT type FROM
group => SELECT i FROM
0 => "prefix"L,
1 => "remark"L,
2 => "members"L,
3 => "memberStamps"L,
4 => "delMembers"L,
5 => "delMemberStamps",
6 => "owners"L,
7 => "ownerStamps"L,
8 => "delOwners"L,
9 => "delOwnerStamps"L,
10 => "friends"L,
11 => "friendStamps"L,
12 => "delFriends"L,
13 => "delFriendStamps"L,
ENDCASE => ERROR,
individual => SELECT i FROM
0 => "prefix"L,
1 => "password"L,
2 => "connect"L,
3 => "forward"L,
4 => "forwardStamps"L,
5 => "delForward"L,
6 => "delForwardStamps",
7 => "inboxes"L,
8 => "inboxStamps"L,
9 => "delInboxes"L,
10 => "delInboxStamps"L,
ENDCASE => ERROR,
dead => SELECT i FROM
0 => "prefix",
ENDCASE => ERROR,
ENDCASE => ERROR];
WriteString[". "L];
END;
Complaint: PROC[server: STRING, name: RName, rc: ReturnCode] =
BEGIN
WriteChar[CR]; WriteString[name];
WriteString[" ("L]; WriteString[server]; WriteString[") "L];
WriteRC[rc];
END;
Differ: PROC[name: RName, difference: STRING] =
BEGIN
WriteChar[CR];
WriteString[name]; WriteString[": "L];
WriteString[difference]; WriteString[" different. "L];
END;
CompareComponent: PROC[type: RNameType, count: CARDINAL, name: RName] =
BEGIN
differ: BOOLEAN ← FALSE;
bLength: CARDINAL = 64;
buffer1: PACKED ARRAY [0..bLength) OF CHARACTER;
buffer2: PACKED ARRAY [0..bLength) OF CHARACTER;
length1: CARDINAL ← ReceiveCount[targetStr];
length2: CARDINAL ← ReceiveCount[str];
IF length1 # length2
THEN {Differ[name, "component length"];
WriteComponent[type, count]};
WHILE length1 > 0 OR length2 > 0
DO wanted1: CARDINAL = 2*MIN[bLength/2, length1] --bytes--;
wanted2: CARDINAL = 2*MIN[bLength/2, length2];
IF wanted1 > 0 THEN ReceiveBytes[targetStr, @buffer1, wanted1];
IF wanted2 > 0 THEN ReceiveBytes[str, @buffer2, wanted2];
length1 ← length1 - wanted1/2; length2 ← length2 - wanted2/2;
FOR i: CARDINAL IN [0..MIN[wanted1,wanted2]/2)
DO IF buffer1[i] # buffer2[i] THEN differ ← TRUE ENDLOOP;
ENDLOOP;
IF differ AND count # 0
THEN {Differ[name, "component contents"L];
WriteComponent[type, count]};
END;
SkipComponent: PROC[thisStr: Handle] =
BEGIN
bLength: CARDINAL = 64;
buffer1: PACKED ARRAY [0..bLength) OF CHARACTER;
length1: CARDINAL ← ReceiveCount[targetStr];
WHILE length1 > 0
DO wanted1: CARDINAL = 2*MIN[bLength/2, length1] --bytes--;
ReceiveBytes[thisStr, @buffer1, wanted1];
length1 ← length1 - wanted1/2;
ENDLOOP;
END;
Look: PROC[name: RName]RETURNS[done: BOOLEAN] =
BEGIN
ok: BOOLEAN ← TRUE;
stamp1, stamp2: Timestamp;
rc1, rc2: ReturnCode;
[rc1,stamp1] ← Enquire[targetStr, ReadEntry, name, oldestTime];
rc2 ← Operate[op: ReadEntry, name: name];
IF rc2.code = done THEN stamp2 ← ReceiveTimestamp[str];
WriteChar['!];
IF rc1.code # done
THEN { Complaint[target, name, rc1]; ok←FALSE };
IF rc2.code # done
THEN { Complaint["other server"L, name, rc2]; ok←FALSE };
IF rc1.type # rc2.type
THEN { Differ[name, "types"L]; ok←FALSE };
BEGIN
count1: CARDINAL ← IF rc1.code # done THEN 0
ELSE ReceiveCount[targetStr];
count2: CARDINAL ← IF rc2.code # done THEN 0
ELSE ReceiveCount[str];
IF ok AND count1 # count2 THEN ERROR;
IF ok AND stamp1 # stamp2
THEN { Differ[name, "global stamps"L]; ok ← FALSE };
IF ok
THEN FOR c: CARDINAL IN [0..MIN[count1,count2])
DO count1 ← count1-1;
count2 ← count2-1;
CompareComponent[rc1.type, c, name];
ENDLOOP;
THROUGH [1..count1] DO SkipComponent[targetStr] ENDLOOP;
THROUGH [1..count2] DO SkipComponent[str] ENDLOOP;
END;
done ← (rc1.code=WrongServer OR rc2.code=WrongServer)
OR (rc1.code=AllDown OR rc2.code=AllDown);
END;
LookEnum: PROC[enumName: RName,
work: PROC[RName]RETURNS[done: BOOLEAN] ] =
-- argument is "groups.reg", "individuals.reg", or "dead.reg" --
BEGIN
memberInfo: MemberInfo = GetMembers[enumName];
WriteChar[CR];
WriteString[enumName];
IF targetStr = NIL THEN targetStr ← CreateStream[targetAddr];
WITH memberInfo SELECT FROM
group => BEGIN ENABLE UNWIND => Close[members];
WriteChar[CR];
Enumerate[members, work];
Close[members];
END;
allDown => WriteString[": all R-Servers down!"L];
notFound => WriteString[": not found!"L];
ENDCASE => ERROR;
IF targetStr # NIL THEN { DestroyStream[targetStr]; targetStr←NIL };
END;
LookAtRegistry: PROC[regGroup: RName] RETURNS[done: BOOLEAN] =
BEGIN
wanted: RName = [maxRNameLength];
IF targetStr # NIL THEN { DestroyStream[targetStr]; targetStr←NIL };
SELECT NameInfoDefs.IsMemberDirect[regGroup, target] FROM
yes => NULL;
no => RETURN[FALSE]; -- target isn't in this registry --
notGroup => ERROR;
allDown => NULL;
ENDCASE => ERROR;
FOR i: CARDINAL DECREASING IN [0..regGroup.length)
DO regGroup.length ← regGroup.length-1;
IF regGroup[i] = '. THEN EXIT;
ENDLOOP;
WriteString["Registry "L]; WriteLine[regGroup];
StringDefs.AppendString[wanted, "Groups."L];
StringDefs.AppendString[wanted, regGroup];
LookEnum[wanted, Look];
wanted.length ← 0;
StringDefs.AppendString[wanted, "Individuals."L];
StringDefs.AppendString[wanted, regGroup];
LookEnum[wanted, Look];
wanted.length ← 0;
StringDefs.AppendString[wanted, "Dead."L];
StringDefs.AppendString[wanted, regGroup];
LookEnum[wanted, Look];
done ← FALSE;
END;
LookAtAll: PROC =
{ LookEnum["Groups.gv"L, LookAtRegistry ! ProtocolDefs.Failed =>
{ WriteString["ProtocolDefs.Failed"L]; CONTINUE }] };
GetStream: PROC RETURNS[ok: BOOLEAN] =
BEGIN
targetConnect: STRING = [64];
WriteString["Server R-Name: "L];
DO c: CHARACTER = ReadChar[];
SELECT c FROM
SP, CR, ESC => EXIT;
BS => IF target.length > 0
THEN { WriteChar['\]; target.length←target.length-1;
WriteChar[target[target.length]] };
ENDCASE => { WriteChar[c]; StringDefs.AppendChar[target,c] };
ENDLOOP;
SELECT NameInfoDefs.GetConnect[target, targetConnect] FROM
individual => NULL;
ENDCASE => { WriteString[" bad name"L]; RETURN[FALSE] };
GetPupAddress[@targetAddr, targetConnect];
targetAddr.socket ← RegServerEnquirySocket;
WriteChar[CR];
RETURN[TRUE];
END;
ProtocolDefs.Init[];
UNTIL GetStream[] DO target.length ← 0 ENDLOOP;
LookAtAll[];
IF targetStr # NIL THEN DestroyStream[targetStr];
IF str # NIL THEN DestroyStream[str];
END.