DIRECTORY Commander USING [CommandProc, Handle, Register], IO, MobDefs, PFS USING [Error, PathFromRope, RopeOpen], Rope USING [ActionType, Cat, Concat, Equal, Fetch, Flatten, Length, Map, Match, ROPE, Run, Substr], SymbolSegment USING [BlockDescriptor, FGHeader, STHeader]; MobCompare: CEDAR PROGRAM IMPORTS Commander, IO, PFS, Rope = BEGIN bytesPerUnit: CARDINAL = BYTES[WORD]; Mob: TYPE = MobDefs.Mob; MobPtr: TYPE = LONG POINTER TO Mob; BlockDescriptor: TYPE = SymbolSegment.BlockDescriptor; Buffer: TYPE = REF BufferRep; BufferRep: TYPE = RECORD[start: INT¬0, len: NAT¬0, words: SEQUENCE max: NAT OF WORD]; FGHeader: TYPE = SymbolSegment.FGHeader; NameRecord: TYPE = MobDefs.NameRecord; ROPE: TYPE = Rope.ROPE; Stats: TYPE = REF StatsRep; StatsRep: TYPE = RECORD[ debug, doCode, doSymbols, doAll: BOOL¬FALSE, name1,name2: ROPE¬NIL, rope1,rope2: ROPE¬NIL, base1,base2: LONG POINTER¬NIL, symOff1,symOff2: INT¬0, leader: ROPE¬NIL, out: STREAM¬NIL, maxTabLim1: CARD¬0, maxTabLim2: CARD¬0, errors: NAT¬0, maxErrors: NAT¬0 ]; STREAM: TYPE = IO.STREAM; SymbolTableHeader: TYPE = REF SymbolSegment.STHeader; VersionStamp: TYPE = MobDefs.VersionStamp; WordPtr: TYPE = LONG POINTER TO WORD; GiveUp: ERROR[err: ROPE] = CODE; initMaxErrors: NAT ¬ 24; blockList: LIST OF ROPE = LIST[ "hvBlock", "htBlock", "ssBlock", "outerPackBlock", "innerPackBlock", "constBlock", "seBlock", "ctxBlock", "mdBlock", "bodyBlock", "extBlock", "treeBlock", "litBlock", "sLitBlock", "epMapBlock", "spareBlock"]; DoCompare: Commander.CommandProc = { in: STREAM = IO.RIS[cmd.commandLine]; name1,name2: ROPE; name1 ¬ in.GetTokenRope[IO.IDProc ! IO.EndOfStream => GO TO usage].token; name2 ¬ in.GetTokenRope[IO.IDProc ! IO.EndOfStream => GO TO usage].token; CompareFilesAsMobs[ cmd, name1, name2 ! GiveUp => { msg ¬ err; GO TO errOut }; PFS.Error => { msg ¬ error.explanation; GO TO errOut }]; msg ¬ "No differences found."; EXITS errOut => result ¬ $Failure; usage => {result ¬ $Failure; msg ¬ "Usage: CodeCompare file1 file2"}; }; RopeFileCreate: PROC [name: ROPE] RETURNS [ROPE] ~ { RETURN [PFS.RopeOpen[PFS.PathFromRope[name]].rope] }; CompareFilesAsMobs: PROC[cmd: Commander.Handle, name1,name2: ROPE] = { rope1: ROPE = RopeFileCreate[name1 ¬ ForceExtension[name1, ".mob"]]; rope2: ROPE = RopeFileCreate[name2 ¬ ForceExtension[name2, ".mob"]]; stats: Stats = NEW[StatsRep]; ProcessOptions[stats, cmd.procData.clientData]; stats.maxErrors ¬ (IF stats.debug THEN initMaxErrors*4 ELSE initMaxErrors); stats.out ¬ cmd.out; stats.name1 ¬ name1; stats.name2 ¬ name2; stats.rope1 ¬ rope1; stats.rope2 ¬ rope2; CompareRopesAsMobs[rope1, rope2, stats]}; ProcessOptions: PROC[stats: Stats, data: REF] = { WITH data SELECT FROM atom: ATOM => SELECT atom FROM $Code => stats.doCode ¬ TRUE; $All => stats.doAll ¬ TRUE; $Symbols => stats.doSymbols ¬ TRUE; $Debug => stats.debug ¬ stats.doAll ¬ TRUE; ENDCASE; list: LIST OF REF => WHILE list # NIL DO ProcessOptions[stats, list.first]; list ¬ list.rest; ENDLOOP; ENDCASE; }; ForceExtension: PROC[name: ROPE, ext: ROPE] RETURNS[ROPE] = { match: ROPE ¬ Rope.Concat["*", ext]; SELECT TRUE FROM Rope.Match["*!*", name], match.Match[name, FALSE] => {}; ENDCASE => name ¬ name.Concat[ext]; RETURN[name]}; CompareRopesAsMobs: PROC[rope1,rope2: ROPE, stats: Stats] = TRUSTED { MobBuf1: Buffer = NEW[BufferRep[SIZE[Mob]]]; Mob1: MobPtr = LOOPHOLE[@MobBuf1[0]]; MobBuf2: Buffer = NEW[BufferRep[SIZE[Mob]]]; Mob2: MobPtr = LOOPHOLE[@MobBuf2[0]]; len: INT = MIN[rope1.Length[], rope2.Length[]]; GetWords[rope1, 0, SIZE[Mob], MobBuf1]; GetWords[rope2, 0, SIZE[Mob], MobBuf2]; stats.base1 ¬ Mob1; stats.base2 ¬ Mob2; (stats.out).PutRope[stats.name1]; (stats.out).PutRope["\n version: "]; VersionOut[Mob1.version, stats, TRUE]; (stats.out).PutF1[", bytes: %g\n source: ", [integer[rope1.Length[]]]]; NameOut[rope1, Mob1.ssOffset.units, Mob1.source, stats]; (stats.out).PutRope["\n sourceVersion: "]; VersionOut[Mob1.sourceVersion, stats]; (stats.out).PutRope["\n"]; (stats.out).PutRope[stats.name2]; (stats.out).PutRope["\n version: "]; VersionOut[Mob2.version, stats, TRUE]; (stats.out).PutF1[", bytes: %g\n source: ", [integer[rope2.Length[]]]]; NameOut[rope2, Mob2.ssOffset.units, Mob2.source, stats]; (stats.out).PutRope["\n sourceVersion: "]; VersionOut[Mob2.sourceVersion, stats]; (stats.out).PutRope["\n"]; IF rope1.Length[] # rope2.Length[] THEN { (stats.out).PutRope["Warning, files have different lengths.\n"]; stats.errors ¬ stats.errors +1}; CompareInts[ "In versionIdent\n", Mob1.versionIdent, Mob2.versionIdent, stats]; CompareVersionStamps[ "In version\n", Mob1.version, Mob2.version, stats]; CompareVersionStamps[ "In creator\n", Mob1.creator, Mob2.creator, stats]; CompareVersionStamps[ "In sourceVersion\n", Mob1.sourceVersion, Mob2.sourceVersion, stats]; CompareInts[ "In source\n", Mob1.source, Mob2.source, stats]; CompareInts[ "In nBytes\n", Mob1.nBytes, Mob2.nBytes, stats]; CompareCard16s[ "In nConfigs\n", Mob1.nConfigs, Mob2.nConfigs, stats]; CompareCard16s[ "In nModules\n", Mob1.nModules, Mob2.nModules, stats]; CompareCard16s[ "In nImports\n", Mob1.nImports, Mob2.nImports, stats]; CompareBools[ "In definitions\n", Mob1.definitions, Mob2.definitions, stats]; CompareBools[ "In repackaged\n", Mob1.repackaged, Mob2.repackaged, stats]; CompareBools[ "In typeExported\n", Mob1.typeExported, Mob2.typeExported, stats]; CompareBools[ "In inlineFloat\n", Mob1.inlineFloat, Mob2.inlineFloat, stats]; CompareBools[ "In mappingStarted\n", Mob1.mappingStarted, Mob2.mappingStarted, stats]; CompareBools[ "In mappingFinished\n", Mob1.mappingFinished, Mob2.mappingFinished, stats]; CompareBools[ "In versions\n", Mob1.versions, Mob2.versions, stats]; CompareBools[ "In extended\n", Mob1.extended, Mob2.extended, stats]; CompareCard16s[ "In nDummies\n", Mob1.nDummies, Mob2.nDummies, stats]; IF stats.debug THEN (stats.out).PutRope["** Tables\n"]; CompareTables[ "string table", @Mob1.ssOffset, @Mob2.ssOffset, stats]; CompareTables[ "config table", @Mob1.ctOffset, @Mob2.ctOffset, stats]; CompareTables[ "module table", @Mob1.mtOffset, @Mob2.mtOffset, stats]; CompareTables[ "import table", @Mob1.impOffset, @Mob2.impOffset, stats]; CompareTables[ "export table", @Mob1.expOffset, @Mob2.expOffset, stats]; CompareTables[ "external variable table", @Mob1.evOffset, @Mob2.evOffset, stats]; CompareTables[ "segment table", @Mob1.sgOffset, @Mob2.sgOffset, stats]; CompareTables[ "file table", @Mob1.ftOffset, @Mob2.ftOffset, stats]; CompareTables[ "space table", @Mob1.spOffset, @Mob2.spOffset, stats]; CompareTables[ "name table", @Mob1.ntOffset, @Mob2.ntOffset, stats]; CompareTables[ "type table", @Mob1.typOffset, @Mob2.typOffset, stats]; CompareTables[ "type map table", @Mob1.tmOffset, @Mob2.tmOffset, stats]; CompareTables[ "frame pack table", @Mob1.fpOffset, @Mob2.fpOffset, stats]; IF Mob1.extended AND Mob2.extended THEN { CompareTables[ "link fragment table", @Mob1.lfOffset, @Mob2.lfOffset, stats, TRUE]; CompareTables[ "ref literal fragment table", @Mob1.rfOffset, @Mob2.rfOffset, stats, TRUE]; CompareTables[ "type fragment table", @Mob1.tfOffset, @Mob2.tfOffset, stats, TRUE]; CompareInts[ "In rtPages\n", Mob1.rtOffset.units, Mob2.rtOffset.units, stats]}; IF stats.debug THEN { (stats.out).PutRope["** Segments for file 1\n"]; SegmentsOut[rope1, Mob1, stats]; (stats.out).PutRope["** Segments for file 2\n"]; SegmentsOut[rope2, Mob2, stats]}; IF stats.debug THEN { (stats.out).PutRope["** Files for file 1\n"]; FilesOut[rope1, Mob1, stats]; (stats.out).PutRope["** Files for file 2\n"]; FilesOut[rope2, Mob2, stats]}; {-- compare the code in the various modules mti1: CARD ¬ LOOPHOLE[FIRST[MobDefs.MTIndex], CARD]+Mob1.mtOffset.units; mti2: CARD ¬ LOOPHOLE[FIRST[MobDefs.MTIndex], CARD]+Mob2.mtOffset.units; FOR i: NAT IN [0..MIN[Mob1.nModules,Mob2.nModules]) DO mtr1: MobDefs.MTRecord; mtr2: MobDefs.MTRecord; sgr1: MobDefs.SGRecord; sgr2: MobDefs.SGRecord; nw1, nw2, sgi1, sgi2: CARD; byteOff1,byteOff2: INT; sb1,sb2: INT; GetWordsToPtr[rope1, mti1, SIZE[MobDefs.MTRecord], @mtr1]; GetWordsToPtr[rope2, mti2, SIZE[MobDefs.MTRecord], @mtr2]; nw1 ¬ SIZE[MobDefs.MTRecord]; nw2 ¬ SIZE[MobDefs.MTRecord]; IF stats.doCode OR stats.doAll THEN { sgi1 ¬ Mob1.sgOffset.units + LOOPHOLE[mtr1.code.sgi, CARD]; sgi2 ¬ Mob2.sgOffset.units + LOOPHOLE[mtr2.code.sgi, CARD]; GetWordsToPtr[rope1, sgi1, SIZE[MobDefs.SGRecord], @sgr1]; GetWordsToPtr[rope2, sgi2, SIZE[MobDefs.SGRecord], @sgr2]; sb1 ¬ sgr1.base.units; sb2 ¬ sgr2.base.units; byteOff1 ¬ (mtr1.code.offset+sb1)*bytesPerUnit; byteOff2 ¬ (mtr2.code.offset+sb2)*bytesPerUnit; SELECT TRUE FROM mtr1.code.length = 0 AND mtr2.code.length = 0 => (stats.out).PutRope["No code.\n"]; mtr1.code.length = 0 => (stats.out).PutF1[ "No code for file 1, %g bytes for file 2.\n", [integer[mtr2.code.length]]]; mtr1.code.length = 0 => (stats.out).PutF1[ "No code for file 2, %g bytes for file 1.\n", [integer[mtr1.code.length]]]; ENDCASE => CompareBytes[ IO.PutFR[ "In code for %g (module %g)\n", [rope[GetName[rope1, Mob1.ssOffset.units, mtr1.name]]], [integer[i]]], byteOff1, byteOff2, MIN[mtr1.code.length, mtr2.code.length], stats]; }; IF stats.doSymbols OR stats.doAll THEN { sgi1 ¬ Mob1.sgOffset.units + LOOPHOLE[mtr1.sseg, CARD]; sgi2 ¬ Mob2.sgOffset.units + LOOPHOLE[mtr2.sseg, CARD]; GetWordsToPtr[rope1, sgi1, SIZE[MobDefs.SGRecord], @sgr1]; GetWordsToPtr[rope2, sgi2, SIZE[MobDefs.SGRecord], @sgr2]; sb1 ¬ sgr1.base.units; sb2 ¬ sgr2.base.units; stats.symOff1 ¬ sb1; stats.symOff2 ¬ sb2; CompareWords[ IO.PutFR[ "In basic symbols for %g (module %g)\n", [rope[GetName[rope1, Mob1.ssOffset.units, mtr1.name]]], [integer[i]]], sb1, sb2, MIN[sgr1.units.units, sgr2.units.units], stats]; stats.symOff1 ¬ stats.symOff2 ¬ 0; -- inhibit Diagnose CompareWords[ IO.PutFR[ "In extended symbols for %g (module %g)\n", [rope[GetName[rope1, Mob1.ssOffset.units, mtr1.name]]], [integer[i]]], sb1+sgr1.units.units, sb2+sgr2.units.units, MIN[sgr1.extraUnits.units, sgr2.extraUnits.units], stats ]; }; mti1 ¬ mti1 + nw1; mti2 ¬ mti2 + nw2; ENDLOOP; }; IF stats.debug THEN { stats.maxTabLim1 ¬ stats.maxTabLim2 ¬ SIZE[Mob]; CompareWords[ "In whole file\n", stats.maxTabLim1, stats.maxTabLim2, MIN[len/bytesPerUnit-stats.maxTabLim1, len/bytesPerUnit-stats.maxTabLim2], stats]; }; SELECT stats.errors FROM 0 => {}; 1 => ERROR GiveUp["Too bad, only one little difference."]; ENDCASE => ERROR GiveUp[ IO.PutFR1["%g differences encountered.", [integer[stats.errors]]]]; }; CompareWords: PROC [name: ROPE, offset1,offset2: INT, nWords: INT, stats: Stats] = TRUSTED { r1: ROPE = (stats.rope1).Substr[0, (offset1+nWords)*bytesPerUnit]; lim1: INT = r1.Length[]; r2: ROPE = (stats.rope2).Substr[0, (offset2+nWords)*bytesPerUnit]; lim2: INT = r2.Length[]; sth1,sth2: SymbolTableHeader ¬ NIL; off1: INT ¬ offset1*bytesPerUnit; off2: INT ¬ offset2*bytesPerUnit; IF stats.debug THEN (stats.out).PutFL[ "** %g CompareWords[offset1: %g, offset2: %g, nWords: %g]\n", LIST[[rope[name]], [integer[offset1]], [integer[offset2]], [integer[nWords]]]]; WHILE off1 < lim1 AND off2 < lim2 DO nc: INT = r1.Run[off1, r2, off2]; nOff1: INT ¬ off1+nc; nOff2: INT ¬ off2+nc; nwOff1,nwOff2: INT; w1,w2: WORD; f1,f2: ROPE; which1,which2: ROPE ¬ NIL; IF nOff1 >= lim1 OR nOff2 >= lim2 THEN EXIT; IF name # NIL THEN {(stats.out).PutRope[name]; name ¬ NIL}; nwOff1 ¬ nOff1/bytesPerUnit; nwOff2 ¬ nOff2/bytesPerUnit; f1 ¬ r1.Flatten[nOff1 ¬ nwOff1*bytesPerUnit, bytesPerUnit]; w1 ¬ (LOOPHOLE[f1, WordPtr]+SIZE[TEXT])­; f2 ¬ r2.Flatten[nOff2 ¬ nwOff2*bytesPerUnit, bytesPerUnit]; w2 ¬ (LOOPHOLE[f2, WordPtr]+SIZE[TEXT])­; IF nOff1 = nOff2 THEN (stats.out).PutF1[" at word offset %g", [integer[nwOff1]]] ELSE (stats.out).PutF[ " at word offsets (%g, %g)", [integer[nwOff1]], [integer[nwOff2]]]; (stats.out).PutFL[ ", %b # %b (\"%q\" # \"%q\")\n", LIST[[cardinal[w1]], [cardinal[w2]], [rope[f1]], [rope[f2]]]]; IF stats.symOff1 > 0 THEN { IF sth1 = NIL THEN { sth1 ¬ NEW[SymbolSegment.STHeader]; GetWordsToPtr[ stats.rope1, stats.symOff1, SIZE[SymbolSegment.STHeader], LOOPHOLE[sth1]] }; which1 ¬ Diagnose[stats.rope1, sth1, offset1, nwOff1]; (stats.out).PutRope[which1]; (stats.out).PutRope["\n"]}; IF stats.symOff2 > 0 THEN { IF sth2 = NIL THEN { sth2 ¬ NEW[SymbolSegment.STHeader]; GetWordsToPtr[ stats.rope2, stats.symOff2, SIZE[SymbolSegment.STHeader], LOOPHOLE[sth2]]; }; which2 ¬ Diagnose[stats.rope2, sth2, offset2, nwOff2]; IF NOT which1.Equal[which2] THEN { (stats.out).PutRope[" (Mob1)\n"]; (stats.out).PutRope[which2]; (stats.out).PutRope[" (Mob2)\n"]}; }; IF (stats.errors ¬ stats.errors + 1) > stats.maxErrors THEN { ERROR GiveUp["Too many differences encountered."]}; off1 ¬ nOff1 + bytesPerUnit; off2 ¬ nOff2 + bytesPerUnit; ENDLOOP; }; CompareBytes: PROC [name: ROPE, offset1,offset2: INT, nBytes: INT, stats: Stats] = TRUSTED { r1: ROPE = (stats.rope1).Substr[0, offset1+nBytes]; lim1: INT = r1.Length[]; r2: ROPE = (stats.rope2).Substr[0, offset2+nBytes]; lim2: INT = r2.Length[]; IF stats.debug THEN (stats.out).PutFL[ "** %g CompareBytes[offset1: %g, offset2: %g, nBytes: %g]\n", LIST[[rope[name]], [integer[offset1]], [integer[offset2]], [integer[nBytes]]]]; WHILE offset1 < lim1 AND offset2 < lim2 DO nc: INT = r1.Run[offset1, r2, offset2]; nOff1: INT = offset1+nc; nOff2: INT = offset2+nc; f1,f2: ROPE; IF nOff1 >= lim1 OR nOff2 >= lim2 THEN EXIT; IF name # NIL THEN { (stats.out).PutRope[name]; name ¬ NIL}; f1 ¬ r1.Flatten[nOff1, 1]; f2 ¬ r2.Flatten[nOff2, 1]; IF nOff1 = nOff2 THEN (stats.out).PutF1[ " at byte offset %g", [integer[nOff1]]] ELSE (stats.out).PutF[ " at byte offsets (%g, %g)", [integer[nOff1]], [integer[nOff2]]]; (stats.out).PutFL[ ", %b # %b ('%q # '%q)\n", LIST[[cardinal[f1.Fetch[0]-0C]], [cardinal[f2.Fetch[0]-0C]], [rope[f1]], [rope[f2]]]]; IF (stats.errors ¬ stats.errors + 1) > stats.maxErrors THEN { ERROR GiveUp["Too many differences encountered."]}; offset1 ¬ nOff1+1; offset2 ¬ nOff2+1; ENDLOOP; }; CompareCard16s: PROC [name: ROPE, src1, src2: CARD16, stats: Stats] = TRUSTED { IF src1 # src2 THEN { IF name # NIL THEN { (stats.out).PutRope[name]; name ¬ NIL}; (stats.out).PutF[" %g # %g\n", [cardinal[CARD[src1]]], [cardinal[CARD[src2]]]]; }; }; CompareInts: PROC [name: ROPE, src1, src2: INT, stats: Stats] = TRUSTED { IF src1 # src2 THEN { IF name # NIL THEN { (stats.out).PutRope[name]; name ¬ NIL}; (stats.out).PutF[" %g # %g\n", [integer[src1]], [integer[src2]]]; }; }; CompareVersionStamps: PROC [name: ROPE, src1, src2: MobDefs.VersionStamp, stats: Stats] = TRUSTED { IF src1 # src2 THEN { IF name # NIL THEN { (stats.out).PutRope[name]; name ¬ NIL}; (stats.out).PutFL[" %g,%g # %g, %g\n", LIST[[cardinal[src1[0]]], [cardinal[src1[1]]], [cardinal[src2[0]]], [cardinal[src2[1]]]]]; }; }; CompareBools: PROC [name: ROPE, src1, src2: BOOL, stats: Stats] = TRUSTED { IF src1 # src2 THEN { IF name # NIL THEN { (stats.out).PutRope[name]; name ¬ NIL}; (stats.out).PutF[" %g # %g\n", [boolean[src1]], [boolean[src2]]]; }; }; << CompareFileTables: PROC [name: ROPE, mob1, mob2: MobPtr, stats: Stats] = TRUSTED { ftb1: MobDefs.Base = LOOPHOLE[mob1 + mob1.ftOffset.units]; ftb2: MobDefs.Base = LOOPHOLE[mob2 + mob2.ftOffset.units]; ftLimit: MobDefs.FTIndex; IF mob1.ftOffset.units # mob2.ftOffset.units THEN { (stats.out).PutF[ "Offsets for %g not equal, %g # %g\n", [rope[name]], [cardinal[mob1.ftOffset.units]], [cardinal[mob1.ftOffset.units]]]; IF (stats.errors ¬ stats.errors + 1) > stats.maxErrors THEN ERROR GiveUp["Too many differences encountered."]; }; IF mob1.ftLimit # mob2.ftLimit THEN { (stats.out).PutF[ "Limits for %g not equal, %g # %g\n", [rope[name]], [cardinal[LOOPHOLE[mob1.ftLimit]]], [cardinal[LOOPHOLE[mob1.ftLimit]]]]; IF (stats.errors ¬ stats.errors + 1) > stats.maxErrors THEN ERROR GiveUp["Too many differences encountered."]; }; IF LOOPHOLE[mob1.ftLimit, CARD] > LOOPHOLE[mob2.ftLimit, CARD] THEN ftLimit ¬ mob2.ftLimit ELSE ftLimit ¬ mob1.ftLimit; FOR fti: MobDefs.FTIndex ¬ FIRST[MobDefs.FTIndex], fti + SIZE[MobDefs.FTRecord] UNTIL fti = ftLimit DO IF ftb1[fti] # ftb2[fti] THEN { (stats.out).PutF[ "Entries for %g not equal, %g # %g\n", [rope[name]], [cardinal[LOOPHOLE[mob1.ftLimit]]], [cardinal[LOOPHOLE[mob1.ftLimit]]]]; IF (stats.errors ¬ stats.errors + 1) > stats.maxErrors THEN ERROR GiveUp["Too many differences encountered."]; }; ENDLOOP; }; CompareSpaceTables: PROC [name: ROPE, mob1, mob2: MobPtr, stats: Stats] = TRUSTED { spb1: MobDefs.Base = LOOPHOLE[mob1 + mob1.spOffset.units]; spb2: MobDefs.Base = LOOPHOLE[mob2 + mob2.spOffset.units]; spLimit: MobDefs.SPIndex; IF mob1.spOffset.units # mob2.spOffset.units THEN { (stats.out).PutF[ "Offsets for %g not equal, %g # %g\n", [rope[name]], [cardinal[mob1.spOffset.units]], [cardinal[mob1.spOffset.units]]]; IF (stats.errors ¬ stats.errors + 1) > stats.maxErrors THEN ERROR GiveUp["Too many differences encountered."]; }; IF mob1.spLimit # mob2.spLimit THEN { (stats.out).PutF[ "Limits for %g not equal, %g # %g\n", [rope[name]], [cardinal[LOOPHOLE[mob1.spLimit]]], [cardinal[LOOPHOLE[mob1.spLimit]]]]; IF (stats.errors ¬ stats.errors + 1) > stats.maxErrors THEN ERROR GiveUp["Too many differences encountered."]; }; IF LOOPHOLE[mob1.spLimit, CARD] > LOOPHOLE[mob2.spLimit, CARD] THEN spLimit ¬ mob2.spLimit ELSE spLimit ¬ mob1.spLimit; FOR spi: MobDefs.SPIndex ¬ FIRST[MobDefs.SPIndex], spi + SIZE[MobDefs.SPRecord] UNTIL spi = spLimit DO IF (spb1[spi].seg # spb2[spi].seg) OR (spb1[spi].name # spb2[spi].name) OR (spb1[spi].length # spb2[spi].length) THEN { (stats.out).PutF[ "Entries for %g not equal, %g # %g\n", [rope[name]], [cardinal[LOOPHOLE[mob1.spLimit]]], [cardinal[LOOPHOLE[mob1.spLimit]]]]; IF (stats.errors ¬ stats.errors + 1) > stats.maxErrors THEN ERROR GiveUp["Too many differences encountered."]; } ELSE { FOR i: CARDINAL IN [0..spb1[spi].length) DO IF spb1[spi].spaces[i] # spb2[spi].spaces[i] THEN { (stats.out).PutF[ "Entries for %g not equal, %g # %g\n", [rope[name]], [cardinal[LOOPHOLE[mob1.spLimit]]], [cardinal[LOOPHOLE[mob1.spLimit]]]]; IF (stats.errors ¬ stats.errors + 1) > stats.maxErrors THEN ERROR GiveUp["Too many differences encountered."]; }; ENDLOOP; }; ENDLOOP; }; >> CompareTables: PROC [name: ROPE, ptr1, ptr2: LONG POINTER, stats: Stats, rt: BOOL ¬ FALSE] = TRUSTED { src1: WordPtr ¬ LOOPHOLE[ptr1, WordPtr]; targetBitsPerUnit: CARD ¬ LOOPHOLE[ptr1, MobPtr].format.bitsPerUnit[0]; targetBytesPerUnit: CARD ¬ targetBitsPerUnit/BITS[BYTE]; offset1: CARD ¬ (LOOPHOLE[src1, WordPtr]­ * targetBytesPerUnit)/BYTES[UNIT]; len1: CARD = (src1+SIZE[MobDefs.MobOffset])­; lim1: CARD = len1+offset1; src2: WordPtr ¬ LOOPHOLE[ptr2, WordPtr]; offset2: CARD ¬ src2­; len2: CARD = (src2+SIZE[MobDefs.MobOffset])­; lim2: CARD = len2+offset2; nWords: CARD = MIN[lim1, lim2]; oldErrors: CARDINAL; rtOff1,rtOff2: INT ¬ 0; IF stats.debug THEN { (stats.out).PutFL[ " %g, range1 = [%g, %g), range2 = [%g, %g).\n", LIST[[rope[name]], [integer[offset1]], [integer[lim1]], [integer[offset2]], [integer[lim2]]]]; }; stats.maxTabLim1 ¬ MAX[stats.maxTabLim1, lim1]; stats.maxTabLim2 ¬ MAX[stats.maxTabLim2, lim2]; IF offset1 # offset2 THEN { (stats.out).PutF[ "Offsets for %g not equal, %g # %g\n", [rope[name]], [cardinal[offset1]], [cardinal[offset2]]]; IF (stats.errors ¬ stats.errors + 1) > stats.maxErrors THEN { ERROR GiveUp["Too many differences encountered."]}; }; IF len1 # len2 THEN { (stats.out).PutF[ "Lengths for %g not equal, %g # %g\n", [rope[name]], [cardinal[len1]], [cardinal[len2]]]; IF (stats.errors ¬ stats.errors + 1) > stats.maxErrors THEN { ERROR GiveUp["Too many differences encountered."]}; }; oldErrors ¬ stats.errors; IF rt THEN { rtOff1 ¬ LOOPHOLE[stats.base1, MobPtr].rtOffset.units; rtOff2 ¬ LOOPHOLE[stats.base2, MobPtr].rtOffset.units}; CompareWords[ Rope.Cat["In ", name, "\n"], rtOff1+offset1, rtOff2+offset2, MIN[len1, len2], stats ! GiveUp => { stats.errors ¬ oldErrors+1; (stats.out).PutRope["... too many differences for this table.\n"]; CONTINUE}]; }; GetWords: PROC[rope: ROPE, wordOffset: INT, nWords: NAT, buffer: Buffer] = TRUSTED { wordIndex: NAT ¬ 0; byteIndex: [0..bytesPerUnit) ¬ 0; word: PACKED ARRAY [0..bytesPerUnit) OF CHAR ¬ ALL[0C]; action: Rope.ActionType = TRUSTED { word[byteIndex] ¬ c; IF byteIndex = bytesPerUnit-1 THEN { byteIndex ¬ 0; buffer[wordIndex] ¬ LOOPHOLE[word]; wordIndex ¬ wordIndex + 1} ELSE byteIndex ¬ byteIndex + 1}; buffer.start ¬ wordOffset; buffer.len ¬ nWords; [] ¬ rope.Map[wordOffset*bytesPerUnit, nWords*bytesPerUnit, action]}; GetWordsToPtr: UNSAFE PROC [rope: ROPE, wordOffset: INT, nWords: NAT, ptr: LONG POINTER] = TRUSTED { byteIndex: [0..bytesPerUnit) ¬ 0; word: PACKED ARRAY [0..bytesPerUnit) OF CHAR ¬ ALL[0C]; action: Rope.ActionType = TRUSTED { word[byteIndex] ¬ c; IF byteIndex = bytesPerUnit-1 THEN { byteIndex ¬ 0; LOOPHOLE[ptr, WordPtr]­ ¬ LOOPHOLE[word]; ptr ¬ ptr + UNITS[WORD]} ELSE byteIndex ¬ byteIndex + 1}; [] ¬ rope.Map[wordOffset*bytesPerUnit, nWords*bytesPerUnit, action]}; GetWord: PROC[rope: ROPE, wordOffset: INT] RETURNS[WORD] = { byteIndex: NAT ¬ 0; word: PACKED ARRAY [0..bytesPerUnit) OF CHAR ¬ ALL[0C]; action: Rope.ActionType = TRUSTED { word[byteIndex] ¬ c; byteIndex ¬ byteIndex + 1}; [] ¬ rope.Map[wordOffset*bytesPerUnit, bytesPerUnit, action]; RETURN[LOOPHOLE[word]]}; Diagnose: PROC[rope: ROPE, sth: SymbolTableHeader, base, offset: INT] RETURNS[which: ROPE ¬ NIL] = TRUSTED { ptr: LONG POINTER TO BlockDescriptor ¬ @sth.hvBlock; list: LIST OF ROPE ¬ blockList; fromBase: INT = offset-base; IF sth.fgCount # 0 THEN { fgOffset: INT = base+sth.fgRelBase; IF CARD[(fgOffset+SIZE[FGHeader[0]])*bytesPerUnit] <= CARD[rope.Length[]] THEN { FGHeader: TYPE = RECORD[offset: CARD, length: CARDINAL ¬ 0]; fgHeader: FGHeader; inner: CARD; GetWordsToPtr[rope, fgOffset, SIZE[FGHeader], @fgHeader]; inner ¬ offset - fgHeader.offset; IF inner < fgHeader.length THEN RETURN[ IO.PutFR[ " at offset %g in fine grain table (%g)", [integer[fromBase]], [cardinal[inner]]]]; }; }; WHILE list # NIL DO inner: CARD = fromBase - ptr.offset; IF --inner>=0 AND-- inner < ptr.size THEN RETURN[ IO.PutFR[ " at offset %g in %g (%g)", [integer[fromBase]], [rope[list.first]], [cardinal[inner]]]]; list ¬ list.rest; ptr ¬ ptr + SIZE[BlockDescriptor]; ENDLOOP; which ¬ IO.PutFR1[" at offset %g in symbols ??", [integer[fromBase]]]}; NameOut: PROC[rope: ROPE, base: CARD, nameRecord: INT, stats: Stats] = { byteIndex: INT = base+nameRecord+4; length: CARDINAL = MIN[64, rope.Fetch[byteIndex-1]-0C]; action: Rope.ActionType = { (stats.out).PutChar[c]}; [] ¬ rope.Map[byteIndex, length, action]}; GetName: PROC[rope: ROPE, base: CARD, nameRecord: INT] RETURNS[ROPE] = { byteIndex: INT = base*bytesPerUnit+nameRecord+4; length: CARDINAL = MIN[64, rope.Fetch[byteIndex-1]-0C]; RETURN[rope.Substr[byteIndex, length]]}; FilesOut: PROC[rope: ROPE, Mob: MobPtr, stats: Stats] = TRUSTED { fti: CARD ¬ LOOPHOLE[FIRST[MobDefs.FTIndex]]; ftr: MobDefs.FTRecord; WHILE fti < LOOPHOLE[Mob.ftLimit, CARD] DO GetWordsToPtr[rope, fti+Mob.ftOffset.units, SIZE[MobDefs.FTRecord], @ftr]; (stats.out).PutF1[" file #%g: ", [integer[fti]]]; NameOut[rope, Mob.ssOffset.units, ftr.name, stats]; (stats.out).PutRope[", {"]; VersionOut[ftr.version, stats, TRUE]; (stats.out).PutRope["}\n"]; fti ¬ fti + SIZE[MobDefs.FTRecord]; ENDLOOP; }; SegmentsOut: PROC[rope: ROPE, Mob: MobPtr, stats: Stats] = TRUSTED { sgi: CARD ¬ LOOPHOLE[FIRST[MobDefs.SGIndex], CARD]; sgr: MobDefs.SGRecord; WHILE sgi < LOOPHOLE[Mob.sgLimit, CARD] DO GetWordsToPtr[rope, sgi+Mob.sgOffset.units, SIZE[MobDefs.SGRecord], @sgr]; (stats.out).PutF1[" seg #%g", [integer[sgi]]]; SELECT sgr.class FROM code => (stats.out).PutRope[" (code)"]; symbols => (stats.out).PutRope[" (symbols)"]; acMap => (stats.out).PutRope[" (acMap)"]; other => (stats.out).PutRope[" (other)"]; ENDCASE => ERROR; SELECT sgr.file FROM MobDefs.FTNull => (stats.out).PutRope[", file: null"]; MobDefs.FTSelf => (stats.out).PutRope[", file: self"]; ENDCASE => (stats.out).PutF1[", file: %g", [integer[LOOPHOLE[sgr.file, CARD]]]]; (stats.out).PutF[ ", base: %g, pages: %g, extraPages: %g\n", [integer[sgr.base.units]], [integer[sgr.units.units]], [integer[sgr.extraUnits.units]]]; sgi ¬ sgi + SIZE[MobDefs.SGRecord]; ENDLOOP; }; VersionOut: PROC[version: VersionStamp, stats: Stats, stampOnly: BOOL ¬ FALSE] = { versionNibbles: PACKED ARRAY [0..16) OF [0..16) = LOOPHOLE[version]; FOR i: NAT IN [0..16) DO nibble: [0..16) = versionNibbles[i]; (stats.out).PutChar[IF nibble < 10 THEN '0+nibble ELSE 'a+(nibble-10)]; ENDLOOP; IF stampOnly THEN RETURN; }; Commander.Register[ "CompareCode", DoCompare, "Compare the code of two Mob files, reporting the differences.", $Code]; Commander.Register[ "CompareMob", DoCompare, "Compare two Mob files, reporting the differences.", $All]; Commander.Register[ "CompareSymbol", DoCompare, "Compare the symbols two Mob files, reporting the differences.", $Symbols]; Commander.Register[ "CompareDebug", DoCompare, "Compare two Mob files, reporting the differences.", $Debug]; END.  MobCompare.mesa Copyright Σ 1985, 1986, 1991 by Xerox Corporation. All rights reserved. Satterthwaite March 8, 1986 5:21:03 pm PST Russ Atkinson (RRA) December 22, 1986 6:12:51 pm PST Andy Litman March 3, 1988 3:21:33 pm PST JKF May 25, 1990 10:30:32 am PDT Willie-s, February 11, 1991 6:38 pm PST Michael Plass, November 26, 1991 4:29 pm PST T Y P E S & C O N S T A N T S E R R O R S F L A G S & V A R I A B L E S P R O C S [cmd: Handle] RETURNS[result: REF _ NIL, msg: Rope.ROPE _ NIL] Compare the code portions of this module. First, get the proper SGRecords for the code Finally, compare the code Compare the symbol portions of this module. First, get the proper SGRecords for the symbols Set the symbol offset in stats for use by Diagnose Compare the basic symbols Compare the extended symbols Try to determine which block it is in Try to determine which block it is in Different diagnoses for the two files! This table should be interpreted relative to the RTMob extension Now compare the words within the table. First look at the fine grain table (if any). The difference is in the fine grain table Now try the block descriptors IF version.net # 0 OR version.host # 0 THEN (stats.out).PutF[" (%g, %g, ", [cardinal[version.net]], [cardinal[version.host]]] ELSE (stats.out).PutRope[" ("]; This next statement is separate to deal with potential errors in time conversion. {ENABLE RuntimeError.UNCAUGHT => GO TO dreck; (stats.out).PutF[ "%g)", [time[BasicTime.FromNSTime[version.time]]] ]; EXITS dreck => (stats.out).PutRope["??)"]; }; ΚΜ–(cedarcode) style•NewlineDelimiter ™code™Kšœ Οeœ=™HInotešœ'Οk™*Kšœžœž™4KšΟy%Πky™(Kšžœ™ K™'K™,—K˜šž ˜ Kšœ žœ!˜0Kšžœ˜K˜Kšž)œ˜*K˜KšœžœFžœ˜cKšœžœ'˜:K˜—šΟn œžœž˜Kšžœ žœžœ˜ Kšœž˜Kšœžœžœžœ˜%K˜—™!K˜Kšœžœ˜Kš œžœžœžœžœ˜#Kšœžœ!˜6Kšœžœžœ ˜Kšœ žœžœžœžœ žœžœžœžœ˜UKšœ žœ˜(Kšœ žœ˜&Kšžœžœžœ˜Kšœžœžœ ˜šœ žœžœ˜Kšœ!ž œ˜,Kšœ žœ˜Kšœ žœ˜Kšœ žœž œ˜Kšœžœ˜Kšœžœ˜Kšœž œ˜Kšœ žœ˜Kšœ žœ˜Kšœžœ˜Kšœ žœ˜K˜—Kšžœžœžœžœ˜Kšœžœžœ˜6Kšœžœ˜*Kš œ žœžœžœžœžœ˜%—K˜™ K™Kš‘œžœžœžœ˜ K˜—™K™Kšœžœ˜š œ žœžœžœžœ˜K˜Π—K˜—™ K™š‘ œ˜$Kš œžœ žœžœ žœžœ™>Kšœžœžœžœ˜%Kšœ žœ˜šœžœ˜!Kšœžœžœžœ˜'—šœžœ˜!Kšœžœžœžœ˜'—˜K˜šœžœžœ ˜(Kšžœ%žœžœ ˜8——K˜šž˜K˜K˜E—K˜K˜—š ‘œžœžœžœžœ˜4Kšžœžœ˜2K˜K˜—š‘œžœ%žœ˜FKšœžœ9˜DKšœžœ9˜DKšœžœ ˜K˜/Kšœžœ žœžœ˜KK˜K˜K˜K˜K˜K˜)K˜—š‘œžœžœ˜1šžœžœž˜šœžœ˜ šžœž˜Kšœžœ˜Kšœžœ˜Kšœžœ˜#Kšœ&žœ˜+Kšžœ˜——šœžœžœžœ˜šžœžœž˜K˜"K˜Kšžœ˜——Kšžœ˜—K˜K˜—š ‘œžœžœžœžœžœ˜=Kšœžœ˜$šžœžœž˜Kšœ+žœ˜8Kšžœ˜#—Kšžœ˜K˜—š‘œžœžœžœ˜EKš‘œ žœ žœ˜,Kš‘œ žœ˜%Kš‘œ žœ žœ˜,Kš‘œ žœ˜%Kšœžœžœ!˜/Kšœžœ˜'Kšœžœ˜'K˜K˜K˜K˜!K˜&Kšœ žœ˜&K˜IK˜8K˜,K˜&K˜K˜K˜!K˜&Kšœ žœ˜&K˜IK˜8K˜,K˜&K˜K˜šžœ!žœ˜)K˜@K˜ —K˜˜ K˜C—˜K˜4—˜K˜4—˜K˜F—˜ K˜1—˜ K˜1—˜K˜7—˜K˜7—˜K˜6—˜ K˜?—˜ K˜<—˜ K˜B—˜ K˜?—˜ K˜H—˜ K˜K—˜ K˜6—˜ K˜6—˜K˜6—Kšžœ žœ$˜7˜K˜7—˜K˜7—˜K˜7—˜K˜9—˜K˜9—˜K˜B—˜K˜8—˜K˜5—˜K˜6—˜K˜5—˜K˜7—˜K˜9—˜K˜;—šžœžœžœ˜)˜Kšœ>žœ˜D—˜KšœEžœ˜K—˜Kšœ>žœ˜D—˜ K˜B——šžœ žœ˜K˜0K˜ K˜0K˜!—šžœ žœ˜K˜-K˜K˜-K˜—šœΟc*˜+Kš œžœžœžœžœ˜HKš œžœžœžœžœ˜Hš žœžœžœžœž˜6K˜K˜K˜K˜Kšœžœ˜Kšœžœ˜Kšœ žœ˜ Kšœžœ˜:Kšœžœ˜:Kšœžœ˜Kšœžœ˜šžœžœ žœ˜%K™)K™K™,Kšœžœžœ˜;Kšœžœžœ˜;Kšœžœ˜:Kšœžœ˜:K˜K˜K˜/K˜/K˜šžœžœž˜šœžœ˜0K˜"—˜˜K˜-K˜——˜˜K˜-K˜——šžœ˜ K™˜ šžœ˜ K˜K˜7K˜—Kšœžœ-˜D———K˜—šžœžœ žœ˜(K™+K™K™/Kšœžœ žœ˜7Kšœžœ žœ˜7Kšœžœ˜:Kšœžœ˜:K˜K˜K™K™2K˜K˜K˜K™˜ šžœ˜ K˜(K˜F—Kšœ žœ-˜:—K˜K™Kšœ$’˜7˜ šžœ˜ K˜+K˜7K˜—K˜K˜Kšžœ/˜2K˜K˜—K˜—K˜K˜Kšžœ˜—K˜—šžœ žœ˜Kšœ&žœ˜0˜ K˜K˜#KšžœG˜JK˜—K˜—šžœž˜K˜Kšœžœ0˜:šžœ˜ šžœ˜ KšžœA˜C———K˜K˜—š‘ œž˜Kš œžœžœ žœžœ˜IKšœžœ:˜BKšœžœ˜Kšœžœ:˜BKšœžœ˜Kšœžœ˜#Kšœžœ˜!Kšœžœ˜!šžœ ž˜˜K˜>KšžœK˜O——šžœ žœ ž˜$Kšœžœ˜!Kšœžœ ˜Kšœžœ ˜Kšœžœ˜Kšœžœ˜ Kšœžœ˜ Kšœžœžœ˜Kšžœžœžœžœ˜,Kšžœžœžœ$žœ˜;K˜K˜K˜;Kšœžœžœžœ˜)K˜;Kšœžœžœžœ˜)Kšžœžœ=˜Ršžœ˜K˜E—˜K˜ Kšžœ:˜>—šžœžœ˜šžœžœžœ˜Kšœžœ˜#˜Kšœžœžœ˜I—K˜—K™%K˜6K˜K˜—šžœžœ˜šžœžœžœ˜Kšœžœ˜#˜Kšœžœžœ˜J—K˜—K™%K˜6šžœžœžœ˜"K™&K˜!K˜K˜"—K˜—šžœ5žœ˜=Kšžœ.˜3—K˜K˜Kšžœ˜—K˜K˜—š‘ œž˜Kš œžœžœ žœžœ˜IKšœžœ+˜3Kšœžœ˜Kšœžœ+˜3Kšœžœ˜šžœ ž˜˜K˜>KšžœK˜O——šžœžœž˜*Kšœžœ ˜'Kšœžœ˜Kšœžœ˜Kšœžœ˜ Kšžœžœžœžœ˜,šžœžœžœ˜K˜Kšœžœ˜ —K˜K˜šžœž˜˜K˜K˜——šž˜˜K˜K˜$——˜K˜KšžœR˜V—šžœ5žœ˜=Kšžœ.˜3—K˜K˜Kšžœ˜—K˜K˜—š‘œž˜Kšœžœžœžœ˜:K˜šžœ žœ˜šžœžœžœ˜K˜Kšœžœ˜ —Kšœ+žœžœ ˜QK˜—K˜K˜—š‘ œž˜Kšœžœžœžœ˜7K˜šžœ žœ˜šžœžœžœ˜K˜Kšœžœ˜ —K˜CK˜—K˜K˜—š‘œž˜Kšœžœ4žœ˜HK˜šžœ žœ˜šžœžœžœ˜K˜Kšœžœ˜ —Kšœ)žœV˜ƒK˜—K˜K˜—š‘ œž˜Kšœžœžœžœ˜8K˜šžœ žœ˜šžœžœžœ˜K˜Kšœžœ˜ —K˜CK˜—K˜K˜—š‘œžœžœ&žœ˜RKšœžœ˜:Kšœžœ˜:K˜K˜šžœ+žœ˜4˜K˜&K˜P—šžœ5žœ˜