DIRECTORY Basics: TYPE USING [bytesPerWord, Word], BasicTime: TYPE USING [FromNSTime], BcdDefs: TYPE USING [BCD, FTIndex, FTNull, FTRecord, FTSelf, Link, NameRecord, MTIndex, MTRecord, SGIndex, SGRecord, VersionStamp], Commander: TYPE USING [CommandProc, Handle, Register], FS: TYPE USING [Error], IO: TYPE USING [EndOfStream, GetTokenRope, IDProc, PutChar, PutF, PutFR, PutRope, RIS, STREAM], Rope: TYPE USING [ActionType, Cat, Concat, Equal, Fetch, Flatten, Length, Map, Match, Substr, ROPE, Run], RuntimeError: TYPE USING [UNCAUGHT], SymbolSegment: TYPE USING [BlockDescriptor, FGHeader, STHeader], RopeFile: TYPE USING [SimpleCreate], VM: TYPE USING [wordsPerPage]; BcdCompare: CEDAR PROGRAM IMPORTS BasicTime, Commander, FS, IO, Rope, RuntimeError, RopeFile = BEGIN BCD: TYPE = BcdDefs.BCD; BcdPtr: TYPE = LONG POINTER TO BCD; 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 = BcdDefs.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: INT_0, maxTabLim2: INT_0, errors: NAT_0, maxErrors: NAT_0 ]; STREAM: TYPE = IO.STREAM; SymbolTableHeader: TYPE = REF SymbolSegment.STHeader; VersionStamp: TYPE = BcdDefs.VersionStamp; Word: TYPE = Basics.Word; WordPtr: TYPE = LONG POINTER TO Word; bytesPerWord: NAT = Basics.bytesPerWord; wordsPerPage: NAT = VM.wordsPerPage; bytesPerPage: NAT = wordsPerPage*bytesPerWord; 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; CompareFilesAsBcds[ cmd, name1, name2 ! GiveUp => {msg _ err; GO TO errOut}; FS.Error => {msg _ error.explanation; GO TO errOut}]; msg _ "No differences found."; EXITS errOut => result _ $Failure; usage => {result _ $Failure; msg _ "Usage: CodeCompare file1 file2"}; }; CompareFilesAsBcds: PROC[cmd: Commander.Handle, name1,name2: ROPE] = { rope1: ROPE = RopeFile.SimpleCreate[name1 _ ForceExtension[name1, ".bcd"]]; rope2: ROPE = RopeFile.SimpleCreate[name2 _ ForceExtension[name2, ".bcd"]]; 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; CompareRopesAsBcds[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]}; CompareRopesAsBcds: PROC[rope1,rope2: ROPE, stats: Stats] = TRUSTED { bcdBuf1: Buffer = NEW[BufferRep[SIZE[BCD]]]; bcd1: BcdPtr = LOOPHOLE[@bcdBuf1[0]]; bcdBuf2: Buffer = NEW[BufferRep[SIZE[BCD]]]; bcd2: BcdPtr = LOOPHOLE[@bcdBuf2[0]]; len: INT = MIN[rope1.Length[], rope2.Length[]]; GetWords[rope1, 0, SIZE[BCD], bcdBuf1]; GetWords[rope2, 0, SIZE[BCD], bcdBuf2]; stats.base1 _ bcd1; stats.base2 _ bcd2; (stats.out).PutRope[stats.name1]; (stats.out).PutRope["\n version: "]; VersionOut[bcd1.version, stats, TRUE]; (stats.out).PutF[", bytes: %g\n source: ", [integer[rope1.Length[]]]]; NameOut[rope1, bcd1.ssOffset, bcd1.source, stats]; (stats.out).PutRope["\n sourceVersion: "]; VersionOut[bcd1.sourceVersion, stats]; (stats.out).PutRope["\n"]; (stats.out).PutRope[stats.name2]; (stats.out).PutRope["\n version: "]; VersionOut[bcd2.version, stats, TRUE]; (stats.out).PutF[", bytes: %g\n source: ", [integer[rope2.Length[]]]]; NameOut[rope2, bcd2.ssOffset, bcd2.source, stats]; (stats.out).PutRope["\n sourceVersion: "]; VersionOut[bcd2.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}; CompareEntries[ "In versionIdent\n", @bcd1.versionIdent, @bcd2.versionIdent, SIZE[CARDINAL], stats]; CompareEntries[ "In version\n", @bcd1.version, @bcd2.version, SIZE[VersionStamp], stats]; CompareEntries[ "In creator\n", @bcd1.creator, @bcd2.creator, SIZE[VersionStamp], stats]; CompareEntries[ "In sourceVersion\n", @bcd1.sourceVersion, @bcd2.sourceVersion, SIZE[VersionStamp], stats]; CompareEntries[ "In source\n", @bcd1.source, @bcd2.source, SIZE[NameRecord], stats]; CompareEntries[ "In nPages\n", @bcd1.nConfigs-1, @bcd2.nConfigs-1, SIZE[CARDINAL], stats]; CompareEntries[ "In nConfigs\n", @bcd1.nConfigs, @bcd2.nConfigs, SIZE[CARDINAL], stats]; CompareEntries[ "In nModules\n", @bcd1.nModules, @bcd2.nModules, SIZE[CARDINAL], stats]; CompareEntries[ "In nImports\n", @bcd1.nImports, @bcd2.nImports, SIZE[CARDINAL], stats]; CompareEntries[ "In options (definitions, repackaged, typeExported, tableCompiled, versions, extended)\n", @bcd1.nDummies-1, @bcd2.nDummies-1, SIZE[CARDINAL], stats]; CompareEntries[ "In nDummies\n", @bcd1.nDummies, @bcd2.nDummies, SIZE[CARDINAL], stats]; IF stats.debug THEN (stats.out).PutRope["** Tables\n"]; CompareTables[ "string table", @bcd1.ssOffset, @bcd2.ssOffset, stats]; CompareTables[ "config table", @bcd1.ctOffset, @bcd2.ctOffset, stats]; CompareTables[ "module table", @bcd1.mtOffset, @bcd2.mtOffset, stats]; CompareTables[ "import table", @bcd1.impOffset, @bcd2.impOffset, stats]; CompareTables[ "export table", @bcd1.expOffset, @bcd2.expOffset, stats]; CompareTables[ "external variable table", @bcd1.evOffset, @bcd2.evOffset, stats]; CompareTables[ "segment table", @bcd1.sgOffset, @bcd2.sgOffset, stats]; CompareTables[ "file table", @bcd1.ftOffset, @bcd2.ftOffset, stats]; CompareTables[ "space table", @bcd1.spOffset, @bcd2.spOffset, stats]; CompareTables[ "name table", @bcd1.ntOffset, @bcd2.ntOffset, stats]; CompareTables[ "type table", @bcd1.typOffset, @bcd2.typOffset, stats]; CompareTables[ "type map table", @bcd1.tmOffset, @bcd2.tmOffset, stats]; CompareTables[ "frame pack table", @bcd1.fpOffset, @bcd2.fpOffset, stats]; IF bcd1.extended AND bcd2.extended THEN { CompareTables[ "link fragment table", @bcd1.lfOffset, @bcd2.lfOffset, stats, TRUE]; CompareTables[ "ref literal fragment table", @bcd1.rfOffset, @bcd2.rfOffset, stats, TRUE]; CompareTables[ "type fragment table", @bcd1.tfOffset, @bcd2.tfOffset, stats, TRUE]; CompareEntries[ "In rtPages\n", @bcd1.rtPages, @bcd2.rtPages, SIZE[CARDINAL], stats]}; IF stats.debug THEN { (stats.out).PutRope["** Segments for file 1\n"]; SegmentsOut[rope1, bcd1, stats]; (stats.out).PutRope["** Segments for file 2\n"]; SegmentsOut[rope2, bcd2, stats]}; IF stats.debug THEN { (stats.out).PutRope["** Files for file 1\n"]; FilesOut[rope1, bcd1, stats]; (stats.out).PutRope["** Files for file 2\n"]; FilesOut[rope2, bcd2, stats]}; {-- compare the code in the various modules mti1: CARDINAL _ LOOPHOLE[FIRST[BcdDefs.MTIndex], CARDINAL]+bcd1.mtOffset; mti2: CARDINAL _ LOOPHOLE[FIRST[BcdDefs.MTIndex], CARDINAL]+bcd2.mtOffset; FOR i: NAT IN [0..MIN[bcd1.nModules,bcd2.nModules]) DO mtr1: BcdDefs.MTRecord; mtr2: BcdDefs.MTRecord; sgr1: BcdDefs.SGRecord; sgr2: BcdDefs.SGRecord; nw1,nw2,sgi1,sgi2: CARDINAL; byteOff1,byteOff2: INT; sb1,sb2: INT; GetWordsToPtr[rope1, mti1, SIZE[BcdDefs.MTRecord], @mtr1]; GetWordsToPtr[rope2, mti2, SIZE[BcdDefs.MTRecord], @mtr2]; WITH m~~mtr1 SELECT FROM direct => nw1 _ SIZE[BcdDefs.MTRecord[direct]] + m.length*SIZE[BcdDefs.Link]; indirect => nw1 _ SIZE[BcdDefs.MTRecord[indirect]]; multiple => nw1 _ SIZE[BcdDefs.MTRecord[multiple]]; ENDCASE => ERROR; WITH m~~mtr2 SELECT FROM direct => nw2 _ SIZE[BcdDefs.MTRecord[direct]] + m.length*SIZE[BcdDefs.Link]; indirect => nw2 _ SIZE[BcdDefs.MTRecord[indirect]]; multiple => nw2 _ SIZE[BcdDefs.MTRecord[multiple]]; ENDCASE => ERROR; IF stats.doCode OR stats.doAll THEN { sgi1 _ bcd1.sgOffset + LOOPHOLE[mtr1.code.sgi, CARDINAL]; sgi2 _ bcd2.sgOffset + LOOPHOLE[mtr2.code.sgi, CARDINAL]; GetWordsToPtr[rope1, sgi1, SIZE[BcdDefs.SGRecord], @sgr1]; GetWordsToPtr[rope2, sgi2, SIZE[BcdDefs.SGRecord], @sgr2]; sb1 _ (sgr1.base-1)*INT[wordsPerPage]; sb2 _ (sgr2.base-1)*INT[wordsPerPage]; byteOff1 _ (mtr1.code.offset+sb1)*bytesPerWord; byteOff2 _ (mtr2.code.offset+sb2)*bytesPerWord; SELECT TRUE FROM mtr1.code.length = 0 AND mtr2.code.length = 0 => (stats.out).PutRope["No code.\n"]; mtr1.code.length = 0 => (stats.out).PutF[ "No code for file 1, %g bytes for file 2.\n", [integer[mtr2.code.length]]]; mtr1.code.length = 0 => (stats.out).PutF[ "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, bcd1.ssOffset, mtr1.name]]], [integer[i]]], byteOff1, byteOff2, MIN[mtr1.code.length, mtr2.code.length], stats]; }; IF stats.doSymbols OR stats.doAll THEN { sgi1 _ bcd1.sgOffset + LOOPHOLE[mtr1.sseg, CARDINAL]; sgi2 _ bcd2.sgOffset + LOOPHOLE[mtr2.sseg, CARDINAL]; GetWordsToPtr[rope1, sgi1, SIZE[BcdDefs.SGRecord], @sgr1]; GetWordsToPtr[rope2, sgi2, SIZE[BcdDefs.SGRecord], @sgr2]; sb1 _ (sgr1.base-1)*INT[wordsPerPage]; sb2 _ (sgr2.base-1)*INT[wordsPerPage]; stats.symOff1 _ sb1; stats.symOff2 _ sb2; CompareWords[ IO.PutFR[ "In basic symbols for %g (module %g)\n", [rope[GetName[rope1, bcd1.ssOffset, mtr1.name]]], [integer[i]]], sb1, sb2, MIN[sgr1.pages, sgr2.pages]*wordsPerPage, stats]; stats.symOff1 _ stats.symOff2 _ 0; -- inhibit Diagnose CompareWords[ IO.PutFR[ "In extended symbols for %g (module %g)\n", [rope[GetName[rope1, bcd1.ssOffset, mtr1.name]]], [integer[i]]], sb1+sgr1.pages*wordsPerPage, sb2+sgr2.pages*wordsPerPage, MIN[sgr1.extraPages, sgr2.extraPages]*wordsPerPage, stats ]; }; mti1 _ mti1 + nw1; mti2 _ mti2 + nw2; ENDLOOP; }; IF stats.debug THEN { stats.maxTabLim1 _ stats.maxTabLim2 _ SIZE[BCD]; CompareWords[ "In whole file\n", stats.maxTabLim1, stats.maxTabLim2, MIN[len/bytesPerWord-stats.maxTabLim1, len/bytesPerWord-stats.maxTabLim2], stats]; }; SELECT stats.errors FROM 0 => {}; 1 => ERROR GiveUp["Too bad, only one little difference."]; ENDCASE => ERROR GiveUp[ IO.PutFR["%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)*bytesPerWord]; lim1: INT = r1.Length[]; r2: ROPE = (stats.rope2).Substr[0, (offset2+nWords)*bytesPerWord]; lim2: INT = r2.Length[]; sth1,sth2: SymbolTableHeader _ NIL; off1: INT _ offset1*bytesPerWord; off2: INT _ offset2*bytesPerWord; IF stats.debug THEN (stats.out).PutF[ "** %g CompareWords[offset1: %g, offset2: %g, nWords: %g]\n", [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/bytesPerWord; nwOff2 _ nOff2/bytesPerWord; f1 _ r1.Flatten[nOff1 _ nwOff1*bytesPerWord, bytesPerWord]; w1 _ (LOOPHOLE[f1, WordPtr]+SIZE[TEXT])^; f2 _ r2.Flatten[nOff2 _ nwOff2*bytesPerWord, bytesPerWord]; w2 _ (LOOPHOLE[f2, WordPtr]+SIZE[TEXT])^; IF nOff1 = nOff2 THEN (stats.out).PutF[" at word offset %g", [integer[nwOff1]]] ELSE (stats.out).PutF[ " at word offsets (%g, %g)", [integer[nwOff1]], [integer[nwOff2]]]; (stats.out).PutF[ ", %b # %b (\"%q\" # \"%q\")\n", [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[" (bcd1)\n"]; (stats.out).PutRope[which2]; (stats.out).PutRope[" (bcd2)\n"]}; }; IF (stats.errors _ stats.errors + 1) > stats.maxErrors THEN { ERROR GiveUp["Too many differences encountered."]}; off1 _ nOff1 + bytesPerWord; off2 _ nOff2 + bytesPerWord; 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).PutF[ "** %g CompareBytes[offset1: %g, offset2: %g, nBytes: %g]\n", [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).PutF[ " at byte offset %g", [integer[nOff1]]] ELSE (stats.out).PutF[ " at byte offsets (%g, %g)", [integer[nOff1]], [integer[nOff2]]]; (stats.out).PutF[ ", %b # %b ('%q # '%q)\n", [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; }; CompareEntries: PROC [name: ROPE, src1,src2: LONG POINTER, nWords: NAT, stats: Stats] = TRUSTED { WHILE nWords > 0 DO w1: Word = LOOPHOLE[src1, WordPtr]^; w2: Word = LOOPHOLE[src2, WordPtr]^; IF w1 # w2 THEN { IF name # NIL THEN { (stats.out).PutRope[name]; name _ NIL}; (stats.out).PutF[ " difference at %g, %g # %g\n", [integer[src1-stats.base1]], [cardinal[w1]], [cardinal[w2]]]; IF (stats.errors _ stats.errors + 1) > stats.maxErrors THEN { ERROR GiveUp["Too many differences encountered."]}; }; src1 _ src1 + 1; src2 _ src2 + 1; nWords _ nWords - 1; ENDLOOP; }; CompareTables: PROC [name: ROPE, ptr1,ptr2: LONG POINTER, stats: Stats, rt: BOOL _ FALSE] = TRUSTED { src1: WordPtr _ LOOPHOLE[ptr1, WordPtr]; offset1: CARDINAL _ LOOPHOLE[src1, WordPtr]^; len1: CARDINAL = (src1+1)^; lim1: CARDINAL = len1+offset1; src2: WordPtr _ LOOPHOLE[ptr2, WordPtr]; offset2: CARDINAL _ src2^; len2: CARDINAL = (src2+1)^; lim2: CARDINAL = len2+offset2; nWords: CARDINAL = MIN[lim1, lim2]; oldErrors: CARDINAL; rtOff1,rtOff2: INT _ 0; IF stats.debug THEN { (stats.out).PutF[ " %g, range1 = [%g, %g), range2 = [%g, %g).\n", [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, BcdPtr].rtPages.relPageBase*wordsPerPage; rtOff2 _ LOOPHOLE[stats.base2, BcdPtr].rtPages.relPageBase*wordsPerPage}; 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..bytesPerWord) _ 0; word: PACKED ARRAY [0..bytesPerWord) OF CHAR _ ALL[0C]; action: Rope.ActionType = TRUSTED { word[byteIndex] _ c; IF byteIndex = bytesPerWord-1 THEN { byteIndex _ 0; buffer[wordIndex] _ LOOPHOLE[word]; wordIndex _ wordIndex + 1} ELSE byteIndex _ byteIndex + 1}; buffer.start _ wordOffset; buffer.len _ nWords; [] _ rope.Map[wordOffset*bytesPerWord, nWords*bytesPerWord, action]}; GetWordsToPtr: UNSAFE PROC [rope: ROPE, wordOffset: INT, nWords: NAT, ptr: LONG POINTER] = TRUSTED { byteIndex: [0..bytesPerWord) _ 0; word: PACKED ARRAY [0..bytesPerWord) OF CHAR _ ALL[0C]; action: Rope.ActionType = TRUSTED { word[byteIndex] _ c; IF byteIndex = bytesPerWord-1 THEN { byteIndex _ 0; LOOPHOLE[ptr, WordPtr]^ _ LOOPHOLE[word]; ptr _ ptr + 1} ELSE byteIndex _ byteIndex + 1}; [] _ rope.Map[wordOffset*bytesPerWord, nWords*bytesPerWord, action]}; GetWord: PROC[rope: ROPE, wordOffset: INT] RETURNS[Word] = { byteIndex: NAT _ 0; word: PACKED ARRAY [0..bytesPerWord) OF CHAR _ ALL[0C]; action: Rope.ActionType = TRUSTED { word[byteIndex] _ c; byteIndex _ byteIndex + 1}; [] _ rope.Map[wordOffset*bytesPerWord, bytesPerWord, 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.fgPgCount # 0 THEN { fgOffset: INT = base+sth.fgRelPgBase*INT[wordsPerPage]; IF (fgOffset+SIZE[FGHeader])*bytesPerWord <= rope.Length[] THEN { fgHeader: FGHeader; inner: INT; GetWordsToPtr[rope, fgOffset, SIZE[FGHeader], @fgHeader]; inner _ offset - fgHeader.offset; IF inner >= 0 AND inner < fgHeader.length THEN RETURN[ IO.PutFR[ " at offset %g in fine grain table (%g)", [integer[fromBase]], [integer[inner]]]]; }; }; WHILE list # NIL DO inner: INT = 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]], [integer[inner]]]]; list _ list.rest; ptr _ ptr + SIZE[BlockDescriptor]; ENDLOOP; which _ IO.PutFR[" at offset %g in symbols ??", [integer[fromBase]]]}; NameOut: PROC[rope: ROPE, base: CARDINAL, nameRecord: INT, stats: Stats] = { byteIndex: INT = base*bytesPerWord+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: CARDINAL, nameRecord: INT] RETURNS[ROPE] = { byteIndex: INT = base*bytesPerWord+nameRecord+4; length: CARDINAL = MIN[64, rope.Fetch[byteIndex-1]-0C]; RETURN[rope.Substr[byteIndex, length]]}; FilesOut: PROC[rope: ROPE, bcd: BcdPtr, stats: Stats] = TRUSTED { fti: CARDINAL _ LOOPHOLE[FIRST[BcdDefs.FTIndex]]; ftr: BcdDefs.FTRecord; WHILE fti < LOOPHOLE[bcd.ftLimit, CARDINAL] DO GetWordsToPtr[rope, fti+bcd.ftOffset, SIZE[BcdDefs.FTRecord], @ftr]; (stats.out).PutF[" file #%g: ", [integer[fti]]]; NameOut[rope, bcd.ssOffset, ftr.name, stats]; (stats.out).PutRope[", {"]; VersionOut[ftr.version, stats, TRUE]; (stats.out).PutRope["}\n"]; fti _ fti + SIZE[BcdDefs.FTRecord]; ENDLOOP; }; SegmentsOut: PROC[rope: ROPE, bcd: BcdPtr, stats: Stats] = TRUSTED { sgi: CARDINAL _ LOOPHOLE[FIRST[BcdDefs.SGIndex], CARDINAL]; sgr: BcdDefs.SGRecord; WHILE sgi < LOOPHOLE[bcd.sgLimit, CARDINAL] DO GetWordsToPtr[rope, sgi+bcd.sgOffset, SIZE[BcdDefs.SGRecord], @sgr]; (stats.out).PutF[" 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 BcdDefs.FTNull => (stats.out).PutRope[", file: null"]; BcdDefs.FTSelf => (stats.out).PutRope[", file: self"]; ENDCASE => (stats.out).PutF[", file: %g", [integer[LOOPHOLE[sgr.file, CARDINAL]]]]; (stats.out).PutF[ ", base: %g, pages: %g, extraPages: %g\n", [integer[sgr.base]], [integer[sgr.pages]], [integer[sgr.extraPages]]]; sgi _ sgi + SIZE[BcdDefs.SGRecord]; ENDLOOP; }; VersionOut: PROC[version: VersionStamp, stats: Stats, stampOnly: BOOL _ FALSE] = { versionNibbles: PACKED ARRAY [0..12) OF [0..16) = LOOPHOLE[version]; FOR i: NAT IN [0..12) DO nibble: [0..16) = versionNibbles[i]; (stats.out).PutChar[IF nibble < 10 THEN '0+nibble ELSE 'a+(nibble-10)]; ENDLOOP; IF stampOnly THEN RETURN; IF version.net # 0 OR version.host # 0 THEN (stats.out).PutF[" (%g, %g, ", [cardinal[version.net]], [cardinal[version.host]]] ELSE (stats.out).PutRope[" ("]; {ENABLE RuntimeError.UNCAUGHT => GO TO dreck; (stats.out).PutF[ "%g)", [time[BasicTime.FromNSTime[version.time]]] ]; EXITS dreck => (stats.out).PutRope["??)"]; }; }; Commander.Register[ "CompareCode", DoCompare, "Compare the code of two bcd files, reporting the differences.", $Code]; Commander.Register[ "CompareBcd", DoCompare, "Compare two bcd files, reporting the differences.", $All]; Commander.Register[ "CompareSymbol", DoCompare, "Compare the symbols two bcd files, reporting the differences.", $Symbols]; Commander.Register[ "CompareDebug", DoCompare, "Compare two bcd files, reporting the differences.", $Debug]; END. "BcdCompare.mesa Copyright c 1985 by Xerox Corporation. All rights reserved. Russ Atkinson, March 7, 1985 4:10:06 am PST Satterthwaite March 8, 1986 5:21:03 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 RTBcd 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 This next statement is separate to deal with potential errors in time conversion. ΚΆ˜codešœ™Kšœ Οmœ1™™>Kšœžœžœžœ˜%Kšœ žœ˜šœžœ˜!Kšœžœžœžœ˜'—šœžœ˜!Kšœžœžœžœ˜'—šœ˜Kšœ˜šœžœžœ ˜&Kšžœ$žœžœ ˜5——Kšœ˜šž˜Kšœ˜KšœE˜E—K˜K˜—šΟnœžœ%žœ˜FKšœžœ@˜KKšœžœ@˜KKšœžœ ˜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šœH˜HKšœ2˜2Kšœ,˜,Kšœ&˜&Kšœ˜K˜Kšœ!˜!Kšœ&˜&Kšœ žœ˜&KšœH˜HKšœ2˜2Kšœ,˜,Kšœ&˜&Kšœ˜K˜šžœ!žœ˜)Kšœ@˜@K˜ —K˜šœ˜Kšœ˜Kšœ(žœžœ ˜?—šœ˜Kšœ˜Kšœžœ˜9—šœ˜Kšœ.žœ˜I—šœ˜Kšœ˜Kšœ*žœ˜E—šœ˜Kšœ˜Kšœžœ˜5—šœ˜Kšœ˜Kšœ$žœžœ ˜;—šœ˜Kšœ˜Kšœ žœžœ ˜7—šœ˜Kšœ˜Kšœ žœžœ ˜7—šœ˜Kšœ˜Kšœ žœžœ ˜7—šœ˜KšœZ˜ZKšœ$žœžœ ˜;—šœ˜Kšœ˜Kšœ žœžœ ˜7—Kšžœ žœ$˜7šœ˜Kšœ7˜7—šœ˜Kšœ7˜7—šœ˜Kšœ7˜7—šœ˜Kšœ9˜9—šœ˜Kšœ9˜9—šœ˜KšœB˜B—šœ˜Kšœ8˜8—šœ˜Kšœ5˜5—šœ˜Kšœ6˜6—šœ˜Kšœ5˜5—šœ˜Kšœ7˜7—šœ˜Kšœ9˜9—šœ˜Kšœ;˜;—šžœžœžœ˜)šœ˜Kšœ>žœ˜D—šœ˜KšœEžœ˜K—šœ˜Kšœ>žœ˜D—šœ˜Kšœ.žœžœ ˜F——šžœ žœ˜Kšœ0˜0K˜ Kšœ0˜0K˜!—šžœ žœ˜Kšœ-˜-K˜Kšœ-˜-K˜—šœΟc*˜+Kš œžœžœžœžœ˜JKš œžœžœžœžœ˜Jš žœžœžœžœž˜6Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœžœ˜Kšœžœ˜Kšœ žœ˜ Kšœžœ˜:Kšœžœ˜:šžœ žœž˜Kšœžœ&žœ˜MKšœžœ˜3Kšœžœ˜3Kšžœžœ˜—šžœ žœž˜Kšœžœ&žœ˜MKšœžœ˜3Kšœžœ˜3Kšžœžœ˜—šžœžœ žœ˜%Kšœ)™)K™Kš ,™,Kšœžœžœ˜9Kšœžœžœ˜9Kšœžœ˜:Kšœžœ˜:Kšœžœ˜&Kšœžœ˜&Kšœ/˜/Kšœ/˜/K˜šžœžœž˜šœžœ˜0Kšœ"˜"—šœ˜šœ˜Kšœ-˜-Kšœ˜——šœ˜šœ˜Kšœ-˜-Kšœ˜——šžœ˜ Kšœ™šœ ˜ šžœ˜ Kšœ˜Kšœ1˜1Kšœ˜—Kšœžœ-˜D———K˜—šžœžœ žœ˜(Kšœ+™+K™Kš /™/Kšœžœ žœ˜5Kšœžœ žœ˜5Kšœžœ˜:Kšœžœ˜:Kšœžœ˜&Kšœžœ˜&K™Kšœ2™2Kšœ˜Kšœ˜K˜Kšœ™šœ ˜ šžœ˜ Kšœ(˜(Kšœ@˜@—Kšœ žœ.˜;—K˜Kšœ™Kšœ$ ˜7šœ ˜ šžœ˜ Kšœ+˜+Kšœ1˜1Kšœ˜—Kšœ˜Kšœ˜Kšžœ0˜3K˜K˜—K˜—K˜K˜Kšžœ˜—K˜—šžœ žœ˜Kšœ&žœžœ˜0šœ ˜ Kšœ˜Kšœ#˜#KšžœG˜JKšœ˜—K˜—šžœž˜K˜Kšœžœ0˜:šžœ˜ šžœ˜ Kšžœ@˜B———Kšœ˜K˜—šŸ œž˜Kš œžœžœ žœžœ˜IKšœžœ:˜BKšœžœ˜Kšœžœ:˜BKšœžœ˜Kšœžœ˜#Kšœžœ˜!Kšœžœ˜!šžœ ž˜šœ˜Kšœ>˜>KšœI˜I——šžœ žœ ž˜$Kšœžœ˜!Kšœžœ ˜Kšœžœ ˜Kšœžœ˜K˜ Kšœžœ˜ Kšœžœžœ˜Kšžœžœžœžœ˜,Kšžœžœžœ$žœ˜;Kšœ˜Kšœ˜Kšœ;˜;Kšœžœžœžœ˜)Kšœ;˜;Kšœžœžœžœ˜)Kšžœžœ<˜Qšžœ˜KšœE˜E—šœ˜Kšœ ˜ Kšœ8˜8—šžœžœ˜šžœžœžœ˜Kšœžœ˜#˜Kšœžœžœ˜I—Kšœ˜—Kšœ%™%Kšœ6˜6Kšœ˜Kšœ˜—šžœžœ˜šžœžœžœ˜Kšœžœ˜#˜Kšœžœžœ˜J—K˜—Kšœ%™%Kšœ6˜6šžœžœžœ˜"Kšœ&™&Kšœ!˜!Kšœ˜Kšœ"˜"—K˜—šžœ5žœ˜=Kšžœ.˜3—Kšœ˜Kšœ˜Kšžœ˜—K˜K˜—šŸ œž˜Kš œžœžœ žœžœ˜IKšœžœ+˜3Kšœžœ˜Kšœžœ+˜3Kšœžœ˜šžœ ž˜šœ˜Kšœ>˜>KšœI˜I——šžœžœž˜*Kšœžœ ˜'Kšœžœ˜Kšœžœ˜Kšœžœ˜ Kšžœžœžœžœ˜,šžœžœžœ˜Kšœ˜Kšœžœ˜ —Kšœ˜Kšœ˜šžœž˜šœ˜Kšœ˜Kšœ˜——šž˜šœ˜Kšœ˜Kšœ$˜$——šœ˜Kšœ˜KšœP˜P—šžœ5žœ˜=Kšžœ.˜3—K˜K˜Kšžœ˜—K˜K˜—šŸœž˜Kš œžœ žœžœ žœžœ˜Lšžœ ž˜Kšœ žœ˜$Kšœ žœ˜$šžœ žœ˜šžœžœžœ˜Kšœ˜Kšœžœ˜ —šœ˜Kšœ!˜!Kšœ=˜=—šžœ5žœ˜=Kšžœ.˜3—K˜—Kšœ˜Kšœ˜Kšœ˜Kšžœ˜—K˜K˜—šŸ œž˜Kš œžœ žœžœžœžœžœ˜QKšœžœ˜(Kšœ žœžœ˜-Kšœžœ ˜Kšœžœ˜Kšœžœ˜(Kšœ žœ ˜Kšœžœ ˜Kšœžœ˜Kšœžœžœ ˜#Kšœ žœ˜Kšœžœ˜šžœ žœ˜šœ˜Kšœ0˜0KšœX˜X—K˜—Kšœžœ˜/Kšœžœ˜/šžœžœ˜šœ˜Kšœ&˜&Kšœ8˜8—šžœ5žœ˜=Kšžœ.˜3—K˜—šžœ žœ˜šœ˜Kšœ&˜&Kšœ2˜2—šžœ5žœ˜=Kšžœ.˜3—K˜—K˜šžœžœ˜ Kšœ@™@Kšœ žœ7˜HKšœ žœ8˜I—Kšœ'™'šœ ˜ Kšœ˜Kšœ žœ˜6šœ ˜ Kšœ˜KšœB˜BKšžœ˜ ——K˜K˜—š Ÿœžœžœžœ žœžœ˜TKšœ žœ˜Kšœ!˜!Kš œžœžœžœžœžœ˜7šœžœ˜#Kšœ˜šžœžœ˜$Kšœ˜Kšœžœ˜#Kšœ˜—Kšžœ˜ —Kšœ˜Kšœ˜KšœE˜E——˜šŸ œžœž˜Kš œžœžœ žœžœžœžœ˜IKšœ!˜!Kš œžœžœžœžœžœ˜7šœžœ˜#Kšœ˜šžœžœ˜$Kšœ˜Kšžœžœ˜)Kšœ˜—Kšžœ˜ —KšœE˜EK˜—š Ÿœžœžœžœžœ ˜