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