<<>> <> <> <> <> <> <> <> <> 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 = { <<[cmd: Handle] RETURNS[result: REF _ NIL, msg: Rope.ROPE _ NIL]>> 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; <> <<(stats.out).PutF[" (%g, %g, ", [cardinal[version.net]], [cardinal[version.host]]]>> <> <> <<{ENABLE RuntimeError.UNCAUGHT => GO TO dreck;>> <<(stats.out).PutF[>> <<"%g)",>> <<[time[BasicTime.FromNSTime[version.time]]]>> <<];>> <> < (stats.out).PutRope["??)"];>> <<};>> }; 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.