DIRECTORY
BasicTime USING[ FromPupTime ],
GVBasics,
Commander USING[ CommandProc, Register ],
GVLocate	USING[ FindRegServer, FoundServerInfo ],
GVNames,
GVProtocol,
IO,
Process USING[ Detach ],
PupDefs,
Rope USING[ Cat, Find, ROPE, Substr ],
ViewerIO	USING[ CreateViewerStreams ];

DBCompare: MONITOR
IMPORTS BasicTime, GVLocate, GVNames, GVProtocol, IO, Process, PupDefs, Rope, Commander, ViewerIO =

BEGIN

OPEN GVBasics, IO, GVNames, GVProtocol, PupDefs;

targetRope: Rope.ROPE; -- 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 --

ReadElsewhere: SAFE PROC[out: IO.STREAM, name: GVBasics.RName ]
RETURNS[ rc: GVProtocol.ReturnCode ] = CHECKED
BEGIN
OPEN GVProtocol;
TryUpdate: SAFE PROC[str: GVProtocol.Handle] = CHECKED
BEGIN
SendRSOperation[str, ReadEntry];
SendRName[str, name];
SendTimestamp[str, GVBasics.oldestTime];
SendNow[str];
rc _ ReceiveRC[str];
END;
oldBad: BOOLEAN _ FALSE;
Create: SAFE PROC = CHECKED
BEGIN
out.PutF["%b#%b#", [cardinal[serverAddr.net]], [cardinal[serverAddr.host]] ];
str _ GVProtocol.CreateStream[serverAddr, RSEnquiry];
END;
Destroy: SAFE PROC = CHECKED
BEGIN
IF str # NIL THEN str.Close[];
str _ NIL;
END;
Accept: SAFE PROC[addr: PupDefs.PupAddress]RETURNS[BOOLEAN] = CHECKED
BEGIN
IF addr.net = targetAddr.net AND addr.host = targetAddr.host 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
[] _ GVLocate.FindRegServer["x.GV", 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: GVLocate.FoundServerInfo = GVLocate.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[out: IO.STREAM, stamp: Timestamp] =
BEGIN
out.PutF["[%b#%b,%t]",
[cardinal[stamp.net]], [cardinal[stamp.host]], [time[BasicTime.FromPupTime[stamp.time]]] ];
END;

WriteType: PROC[out: IO.STREAM, type: RNameType] =
BEGIN
out.PutRope[SELECT type FROM
group => "group",
individual => "individual",
notFound => "not found",
dead => "dead",
ENDCASE => ERROR];
END;

WriteRC: PROC[out: IO.STREAM, rc: ReturnCode] =
BEGIN
out.PutRope[SELECT rc.code FROM
done => "ok",
noChange => "no change",
outOfDate => "out of date",
NotAllowed => "not allowed",
BadOperation => "bad operation",
BadProtocol => "bad protocol",
BadRName => "bad R-Name: ",
BadPassword => "bad password",
WrongServer => "wrong server",
AllDown => "all suitable R-Servers down",
ENDCASE => ERROR];
IF rc.code = BadRName THEN WriteType[out, rc.type];
END;

WriteComponent: PROC[out: IO.STREAM, type: RNameType, i: CARDINAL] =
BEGIN
out.PutF["component=%g. ", [rope[
SELECT type FROM
group => SELECT i FROM
0 => "prefix",
1 => "remark",
2 => "members",
3 => "memberStamps",
4 => "delMembers",
5 => "delMemberStamps",
6 => "owners",
7 => "ownerStamps",
8 => "delOwners",
9 => "delOwnerStamps",
10 => "friends",
11 => "friendStamps",
12 => "delFriends",
13 => "delFriendStamps",
ENDCASE => ERROR,
individual => SELECT i FROM
0 => "prefix",
1 => "password",
2 => "connect",
3 => "forward",
4 => "forwardStamps",
5 => "delForward",
6 => "delForwardStamps",
7 => "inboxes",
8 => "inboxStamps",
9 => "delInboxes",
10 => "delInboxStamps",
ENDCASE => ERROR,
dead => SELECT i FROM
0 => "prefix",
ENDCASE => ERROR,
ENDCASE => ERROR]] ];
END;

ComponentType: TYPE = {rnames, stamps, other};

GetComponentType: PROC [type: RNameType, i: CARDINAL] RETURNS [ComponentType] =
BEGIN
SELECT type FROM
group => SELECT i FROM
2, 4, 6, 8, 10, 12 => RETURN [rnames];
3, 5, 7, 9, 11, 13 => RETURN [stamps]; 
ENDCASE;
individual => SELECT i FROM
3, 5, 7, 9 => RETURN [rnames];
4, 6, 8, 10 => RETURN [stamps]; 
ENDCASE;
ENDCASE;
RETURN[other];
END;

Complaint: PROC[out: IO.STREAM, server: Rope.ROPE, name: RName, rc: ReturnCode] =
BEGIN
out.PutF["\n%g (%g) ", [rope[name]], [rope[server]] ];
WriteRC[out, rc];
END;

Differ: PROC[out: IO.STREAM, name: RName, difference: Rope.ROPE] =
BEGIN
out.PutF["\n%g: %g different.  ", [rope[name]], [rope[difference]] ];
END;

ReceiveString: PROC [from: Handle, to: STRING] =
BEGIN
length: CARDINAL = ReceiveCount[from];
maxlength: CARDINAL = ReceiveCount[from];
ReceiveBytes[from, [base: LOOPHOLE[LONG[@to.text]], count: ((length+1)/2)*2]];
to.length _ length;
END;

CompareComponent: PROC[out: IO.STREAM, 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;
string1: STRING = [64];
string2: STRING = [64];
length1: CARDINAL _ ReceiveCount[targetStr];
length2: CARDINAL _ ReceiveCount[str];
stamp1: Timestamp;
stamp2: Timestamp;
IF length1 # length2
THEN BEGIN
Differ[out, name, "component length"];
WriteComponent[out, type, count];
END;
SELECT GetComponentType[type, count] FROM
rnames => WHILE length1 > 0 OR length2 > 0 DO
string1.length _ 0;
string2.length _ 0;
IF length1 > 0 THEN BEGIN
ReceiveString[targetStr, string1];
length1 _ length1 - (string1.length + 1) / 2 - 2;
END;
IF length2 > 0 THEN BEGIN
ReceiveString[str, string2];
length2 _ length2 - (string2.length + 1) / 2 - 2;
END;
IF string1.length # string2.length
THEN differ _ TRUE
ELSE FOR i: CARDINAL IN [0..string1.length)
DO IF string1[i] # string2[i] THEN differ _ TRUE; ENDLOOP;
ENDLOOP;
stamps => WHILE length1 > 0 OR length2 > 0 DO
IF length1 > 0 THEN BEGIN
stamp1 _ ReceiveTimestamp[targetStr];
length1 _ length1 - SIZE[Timestamp];
END;
IF length2 > 0 THEN BEGIN
stamp2 _ ReceiveTimestamp[str];
length2 _ length2 - SIZE[Timestamp];
END;
IF stamp1 # stamp2 THEN differ _ TRUE; 
ENDLOOP;
ENDCASE => 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, [base: LOOPHOLE[LONG[@buffer1]], count: wanted1]];
IF wanted2 > 0 THEN ReceiveBytes[str, [base: LOOPHOLE[LONG[@buffer2]], count: wanted2]];
length1 _ length1 - wanted1/2;
length2 _ length2 - wanted2/2;
IF count # 0 THEN FOR i: CARDINAL IN [0..MIN[wanted1,wanted2]) DO
IF buffer1[i] # buffer2[i] THEN differ _ TRUE;
ENDLOOP;
ENDLOOP;
IF differ AND count # 0
THEN {Differ[out, name, "component contents"];
WriteComponent[out, 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, [base: LOOPHOLE[LONG[@buffer1]], count: wanted1]];
length1 _ length1 - wanted1/2;
ENDLOOP;
END;

Look: PROC[out: IO.STREAM, name: GVBasics.RName]RETURNS[done: BOOLEAN] =
BEGIN
ok: BOOLEAN _ TRUE;
stamp1, stamp2: Timestamp;
rc1, rc2: ReturnCode;
[rc1,stamp1] _ Enquire[targetStr, ReadEntry, name, oldestTime];
rc2 _ ReadElsewhere[out: out, name: name];
IF rc2.code = done THEN stamp2 _ ReceiveTimestamp[str];
out.PutChar['!];
IF rc1.code # done
THEN { Complaint[out, "target server", name, rc1]; ok_FALSE };
IF rc2.code # done
THEN { Complaint[out, "other server", name, rc2]; ok_FALSE };
IF rc1.type # rc2.type
THEN { Differ[out, name, "types"]; 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[out, name, "global stamps"];
IF ok THEN FOR c: CARDINAL IN [0..MIN[count1,count2])
DO count1 _ count1-1;
count2 _ count2-1;
CompareComponent[out, 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[out: IO.STREAM, enumName: GVBasics.RName,
work: PROC[IO.STREAM, GVBasics.RName]RETURNS[done: BOOLEAN] ] =
BEGIN
out.PutChar['\n];
out.Put[[rope[enumName]]];
BEGIN
memberInfo: GVNames.MemberInfo = GVNames.GetMembers[enumName];
IF targetStr = NIL THEN targetStr _ CreateStream[targetAddr, RSEnquiry];
WITH memberInfo SELECT FROM
group => BEGIN
out.PutChar['\n];
FOR m: LIST OF GVBasics.RName _ members, m.rest UNTIL m = NIL
DO IF work[out, m.first] THEN EXIT ENDLOOP;
END;
allDown => out.PutRope[": all R-Servers down!"];
notFound => out.PutRope[": not found!"];
ENDCASE => ERROR;
IF targetStr # NIL THEN { targetStr.Close[]; targetStr_NIL };
END;
END;

LookAtRegistry: PROC[out: IO.STREAM, regGroup: GVBasics.RName]RETURNS[done: BOOLEAN] =
BEGIN
dotPos: INT = regGroup.Find["."];
reg: Rope.ROPE = regGroup.Substr[0,dotPos];
IF targetStr # NIL THEN { targetStr.Close[]; targetStr_NIL };
SELECT GVNames.IsMemberDirect[regGroup, targetRope] FROM
yes => NULL;
no => RETURN[FALSE]; -- target isn't in this registry --
notGroup => ERROR;
allDown => NULL;
ENDCASE => ERROR;
out.PutF["\n\n%t Registry %g\n", IO.time[], [rope[reg]]];
LookEnum[out, Rope.Cat["Groups.", reg], Look];
LookEnum[out, Rope.Cat["Individuals.", reg], Look];
LookEnum[out, Rope.Cat["Dead.", reg], Look];
done _ FALSE;
END;

LookAtAll: ENTRY PROC =
BEGIN
out: IO.STREAM = ViewerIO.CreateViewerStreams["GV database comparison"].out;
out.PutF["%t Comparing database on %g with rest of world.\n", IO.time[], [rope[targetRope]]];
LookEnum[out, "Groups.gv", LookAtRegistry ! GVProtocol.Failed =>
CHECKED{ out.PutRope["GVProtocol.Failed"]; CONTINUE }];
out.PutF["\n\n%t Finished!\n", IO.time[]];
END;

GetStream: PROC[out, in: IO.STREAM] RETURNS[ok: BOOLEAN] =
BEGIN
info: GVNames.ConnectInfo;
targetConnect: GVBasics.Connect;
targetRope _ in.GetTokenRope[].token;
IF targetRope.Find["."] >= 0
THEN [info, targetConnect] _ GVNames.GetConnect[targetRope]
ELSE { info _ individual; targetConnect _ targetRope };
SELECT info FROM
individual => NULL;
ENDCASE => { out.PutRope[" bad name"]; RETURN[FALSE] };
targetAddr _ GetPupAddress[[0,0], targetConnect];
RETURN[TRUE];
END;


DoIt: ENTRY Commander.CommandProc = TRUSTED
BEGIN ENABLE UNWIND => NULL;
IF GetStream[cmd.out, IO.RIS[cmd.commandLine]]
THEN Process.Detach[FORK LookAtAll[]];
IF targetStr # NIL THEN targetStr.Close[];
IF str # NIL THEN str.Close[];
END;

Commander.Register["DBCompare", DoIt, "Compare GV database on given server"];


END.


�����DBCompare.mesa, Grapevine: database comparison tool
Hal Murray May 1, 1985 8:05:57 pm PDT
HGM  May 31, 1984 3:19:54 am PDT 
Andrew Birrell  February 8, 1984 4:42 pm
Mike Schroeder  March 19, 1983 2:12 pm
argument is "groups.reg", "individuals.reg", or "dead.reg" --
Ê>��˜�šœ3™3Icode™%—˜�Jšœ!™!Jšœ(™(Jšœ&™&—J˜�šÏk	˜	Jšœ
œ˜J˜	Jšœ
œ˜)Jšœ	œ#˜1J˜J˜Jšœ˜Jšœœ˜J˜Jšœœ
œ˜&Jšœ	œ˜&—J˜�šœ˜Jšœ+œ/˜c—J˜�Jš˜J˜�Jšœœ˜0J˜�JšœœÏc ˜7Jšœ ž˜6Jšœœž˜=Jšœ ž˜<Jšœœœž˜<Jšœœž˜<J˜�š
Ïn
œœœœœ˜?Jšœ ˜.Jš˜Jšœ˜šŸ	œœœ˜6Jš˜J˜ J˜J˜(J˜
J˜Jšœ˜—Jšœœœ˜šŸœœœ˜Jš˜J˜MJ˜5Jšœ˜—šŸœœœ˜Jš˜Jšœœœ
˜Jšœœ˜
Jšœ˜—šŸœœœœœ˜EJš˜Jš
œœœœœ˜PJšœœœœ˜2Jšœ˜šœ˜
J˜Jšœœ	˜!Jšœœ˜Jšœ˜—Jšœœ˜
Jšœœœ˜Jšœ˜—š˜Jšœ˜šœ˜
Jšœœ˜-Jšœ˜Jšœ˜—Jšœ˜šœ˜
Jšœ
˜Jšœœ
˜'šœ˜
J˜,Jšœœœœ
˜ Jšœ˜—Jšœœ˜)Jšœ˜—Jšœ˜Jšœ
˜Jšœ
œ˜—Jšœ#œ˜/Jšœ˜Jšœ˜	šœœž'˜2J˜Kšœœ˜J˜&J˜#˜Jš˜Jšœœ˜'šœ˜
J˜'—Jšœ˜——Jšœœ˜Jšœ˜—Jšœ˜J˜�J˜�J˜�—šŸ
œœœœ˜4Jš˜˜J˜[—Jšœ˜J˜�—šŸ	œœœœ˜2Jš˜šœœ˜J˜J˜J˜J˜Jšœœ˜—Jšœ˜J˜�—šŸœœœœ˜/Jš˜šœœ	˜J˜
J˜J˜J˜J˜ J˜J˜J˜J˜J˜)Jšœœ˜—Jšœœ˜3Jšœ˜J˜�—š
Ÿœœœœœ˜DJš˜˜!šœ˜šœ	œ˜J˜J˜J˜J˜J˜J˜J˜J˜J˜J˜J˜J˜J˜J˜Jšœœ˜—šœœ˜J˜J˜J˜J˜J˜J˜J˜J˜J˜J˜J˜Jšœœ˜—šœœ˜J˜Jšœœ˜——Jšœœ˜—Jšœ˜J˜�—Jšœœ˜.J˜�šŸœœœœ˜OJš˜šœ˜šœ	œ˜Jšœœ
˜&Jšœœ˜'Jšœ˜—šœœ˜Jšœœ
˜Jšœœ˜ Jšœ˜—Jšœ˜—Jšœ˜Jšœ˜J˜�—š
Ÿ	œœœœœ ˜QJš˜J˜6J˜Jšœ˜J˜�—š
Ÿœœœœ œ˜BJš˜J˜EJšœ˜J˜�—šŸ
œœœ˜0Jš˜Jšœœ˜&Jšœœ˜)J˜NJ˜Jšœ˜J˜�—š
Ÿœœœœœ˜WJš˜Jšœœœ˜Jšœ	œ˜Jš	œ	œœœ	œ˜0Jš	œ	œœœ	œ˜0Jšœ	œ˜Jšœ	œ˜Jšœ	œ˜,Jšœ	œ˜&J˜J˜Jšœ˜šœ˜
J˜&J˜!Jšœ˜—šœ˜)šœ
œ
œ
˜-J˜J˜šœ
œ˜J˜"J˜1Jšœ˜—šœ
œ˜J˜J˜1Jšœ˜—Jšœ ˜"Jšœ
˜šœœœœ˜+Jš
œœœ
œœ˜:—Jšœ˜—šœ
œ
œ
˜-šœ
œ˜J˜%Jšœœ˜$Jšœ˜—šœ
œ˜J˜Jšœœ˜$Jšœ˜—Jšœœ
œ˜'Jšœ˜—šœœ
œ
˜.Jšœ	œœž	œ˜8Jšœ	œœ˜.Jšœ
œ œœ˜^Jšœ
œœœ˜XJ˜J˜š
œœœœœœ˜AJšœœ
œ˜.Jšœ˜—Jšœ˜——Jšœœ
˜šœ*˜.J˜"—Jšœ˜J˜�—šŸ
œœ˜&Jš˜Jšœ	œ˜Jš	œ	œœœ	œ˜0Jšœ	œ˜,Jšœ˜šœ
œœž	œ˜;Jšœœœ˜HJ˜—Jšœ˜Jšœ˜J˜�—šŸœœœœœœ˜HJš˜Jšœœœ˜J˜J˜J˜?J˜*Jšœœ ˜7J˜Jšœ˜Jšœ2œ˜>Jšœ˜Jšœ1œ˜=Jšœ˜Jšœ"œ˜.š˜šœœœœ˜,Jšœ˜—šœœœœ˜,Jšœ˜—Jšœœœœ˜%Jšœœœ$˜Bšœœœœœœ˜5šœ˜J˜J˜)—Jšœ˜—Jšœ
œœ˜8Jšœ
œœ˜2—Jšœ˜šœœ˜5Jšœœ˜*—Jšœ˜J˜�—šŸœœœœ˜8Jšœœœœœœ˜?Jšœ=™=Jš˜J˜J˜š˜J˜>Jšœ
œœ1˜Hšœœ˜šœ	˜J˜Jš	œœœ"œ˜=Jš
œœœœœ˜+Jšœ˜—J˜0J˜(—Jšœœ˜Jšœ
œœ œ˜=—Jšœ˜Jšœ˜J˜�—šŸœœœœœœ˜VJš˜Jšœœ˜!Jšœ
œ˜+Jšœ
œœ œ˜=šœ.˜8Jšœœ˜Jšœœœž#˜8Jšœœ˜Jšœœ˜—Jšœœ˜Jšœ!œ˜9J˜.J˜3J˜,Jšœœ˜
Jšœ˜J˜�—šŸ	œœœ˜Jš˜Jšœœœ>˜LJšœ>œ˜]˜@Jšœ$œ˜7—Jšœœ	˜*Jšœ˜J˜�—šŸ	œœ
œœœœ˜:Jš˜J˜J˜ J˜%Jšœ˜Jšœ7˜;Jšœ3˜7šœ˜Jšœœ˜—Jšœ œœ˜7J˜1Jšœœ˜
Jšœ˜J˜�J˜�—šœœ˜+Jšœœœœ˜Jšœœœ˜.Jšœœ˜&Jšœ
œœ˜*Jšœœœ
˜Jšœ˜J˜�—J˜MJ˜�J˜�Jšœ˜J˜�J˜�—�…—����+ ��;î��