-- CheckCodeImpl.mesa -- Last edited by Sweet on 22-Aug-83 14:02:53 DIRECTORY BcdDefs, BcdOps, CatchFormat, Environment, Exec, FileName, FileTransfer, Format, Heap, Mopcodes, MSegment, OpTableDefs, PrincOps, Stream, String, SymbolPack, Symbols, SymbolSegment, SymbolTable, Table; CheckCodeImpl: PROGRAM IMPORTS mySymbols: SymbolPack, Exec, FileName, FileTransfer, Format, Heap, MSegment, OpTableDefs, Stream, String = BEGIN OPEN Symbols; IgnoreReason: TYPE = {long, old, binder, defs, table, other}; why: ARRAY IgnoreReason OF LONG CARDINAL; BYTE: TYPE = Environment.Byte; SymbolHandle: TYPE = LONG POINTER TO FRAME [SymbolPack]; exec: Exec.Handle ← NIL; conn: FileTransfer.Connection ← NIL; vfn: FileName.VFN ← NIL; buffer: LONG POINTER ← NIL; bufferPages: Environment.PageCount ← 100; bufferSegment: MSegment.Handle ← NIL; last: CARDINAL = CARDINAL[bufferPages*Environment.bytesPerPage]; currentDir: LONG STRING ← NIL; bcd: BcdOps.BcdBase ← NIL; mtb, sgb, enb: BcdDefs.Base ← NIL; mth: BcdOps.MTHandle ← NIL; sgh: BcdOps.SGHandle ← NIL; symHeader: LONG POINTER TO SymbolSegment.STHeader ← NIL; codebase: PrincOps.PrefixHandle; code: LONG POINTER TO PACKED ARRAY [0..0) OF BYTE; totalFiles, totalBad, totalIgnored: LONG CARDINAL ← 0; -- Exec window output execProc: Format.StringProc; PutCR: PROC = {Format.CR[execProc]}; PutString: PROC [s: LONG STRING] = {execProc[s]}; PutSubString: PROC [ss: String.SubString] = {Format.SubString[execProc, ss]}; PutLine: PROC [s: LONG STRING] = {execProc[s]; Format.CR[execProc]}; PutChar: PROC [c: CHARACTER] = {Format.Char[execProc, c]}; PutDecimal: PROC [n: INTEGER] = {Format.Decimal[execProc, n]}; PutLongDecimal: PROC [n: LONG INTEGER] = {Format.LongDecimal[execProc, n]}; PutNumber: PROC [n: INTEGER, f: Format.NumberFormat] = { Format.Number[execProc, n, f]}; PutOctal: PROC [n: CARDINAL] = { Format.Number[execProc, n, [base: 8, zerofill: FALSE, unsigned: TRUE, columns: 1]]}; -- a few procs stolen from SymbolCache InstallTable: PROC [base: SymbolTable.Base, sym: LONG POINTER] = { SetBases[base, sym]; base.notifier ← base.NullNotifier}; SetBases: PROC [base: SymbolTable.Base, b: LONG POINTER] = { tB: Table.Base = LOOPHOLE[b]; p: LONG POINTER TO SymbolSegment.STHeader = b; q: LONG POINTER TO SymbolSegment.FGHeader; base.cacheInfo ← NIL; base.hashVec ← b+p.hvBlock.offset; base.ht ← DESCRIPTOR[ b+p.htBlock.offset, p.htBlock.size/SIZE[Symbols.HTRecord]]; base.ssb ← b + p.ssBlock.offset; base.seb ← tB + p.seBlock.offset; base.ctxb ← tB + p.ctxBlock.offset; base.mdb ← tB + p.mdBlock.offset; base.bb ← tB + p.bodyBlock.offset; base.tb ← tB + p.treeBlock.offset; base.ltb ← tB + p.litBlock.offset; base.extb ← tB + p.extBlock.offset; base.mdLimit ← FIRST[Symbols.MDIndex] + p.mdBlock.size; base.extLimit ← FIRST[SymbolSegment.ExtIndex] + p.extBlock.size; base.mainCtx ← p.outerCtx; base.stHandle ← p; IF p.fgRelPgBase = 0 THEN {base.sourceFile ← NIL; base.fgTable ← NIL} ELSE { offset: CARDINAL = LOOPHOLE[ @(NIL[POINTER TO SymbolSegment.FGHeader]).sourceFile]; q ← b + p.fgRelPgBase*Environment.wordsPerPage; base.sourceFile ← (b + p.fgRelPgBase*Environment.wordsPerPage) + offset; base.fgTable ← DESCRIPTOR[ b + p.fgRelPgBase*Environment.wordsPerPage + q.offset, q.length]}}; -- end of SymbolPack stuff Octal4: Format.NumberFormat = [ base: 8, unsigned: TRUE, zerofill: FALSE, columns: 4]; Octal6: Format.NumberFormat = [ base: 8, unsigned: TRUE, zerofill: FALSE, columns: 6]; PutHash: PROC [symbols: SymbolHandle, hti: Symbols.HTIndex] = { ss: String.SubStringDescriptor; IF hti = Symbols.HTNull THEN PutString["[Anon]"L] ELSE { symbols.ops.SubStringForHash[@ss, hti]; PutSubString[@ss]}}; CheckRemoteCode: Exec.ExecProc = { ENABLE { ABORTED => GO TO aborted; FileTransfer.Error --[code]-- => SELECT code FROM retry => GOTO timedOut; ENDCASE => GOTO fileTransferProblem; UNWIND => Finalize[]}; exec ← h; execProc ← exec.OutputProc[]; Initialize[]; OpenConnection[]; DoChecks[]; Finalize[]; outcome ← normal; EXITS aborted => {outcome ← abort; Finalize[]; PutCR[]; PutLine["...aborted"L]}; timedOut => { outcome ← error; Finalize[]; PutCR[]; PutLine["...connection timed out!"L]}; fileTransferProblem => { outcome ← error; Finalize[]; PutCR[]; PutLine["...FileTransfer problem!"L]}}; Initialize: PROC = { PutHeading[]; conn ← NIL; bufferSegment ← MSegment.Create[pages: bufferPages, release: []]; buffer ← MSegment.Address[bufferSegment]; totalFiles ← totalBad ← totalIgnored ← 0; currentDir ← Heap.systemZone.NEW[StringBody[200]]; why ← ALL[0]}; Finalize: PROC = { IF bufferSegment # NIL THEN { MSegment.Delete[bufferSegment]; bufferSegment ← buffer ← NIL}; IF currentDir # NIL THEN Heap.systemZone.FREE[@currentDir]; IF vfn # NIL THEN {FileName.FreeVFN[vfn]; vfn ← NIL}; CloseConnection[]}; PutHeading: PROC = {PutCR[]; PutLine["Remote Code Checker"L]; PutCR[]}; OpenConnection: PROC = { conn ← FileTransfer.Create[]; conn.SetProcs[clientData: NIL, messages: PutMessages]; }; PutMessages: FileTransfer.MessageProc = { IF level = fatal THEN { PutString["FileTransfer error: "L]; IF s1 # NIL THEN PutString[s1]; IF s2 # NIL THEN PutString[s2]; IF s3 # NIL THEN PutString[s3]; IF s4 # NIL THEN PutString[s4]}}; CloseConnection: PROC = { IF conn # NIL THEN {conn.Close[]; conn.Destroy[]; conn ← NIL}}; verbose: BOOLEAN ← FALSE; DoChecks: PROC = { token, switches: LONG STRING; Stats: PROC = { name: ARRAY IgnoreReason OF STRING = [ long: " too long"L, old: " old bcd version"L, binder: " binder output"L, defs: " defs"L, table: "table compiled"L, other: " other"L]; PutCR[]; PutCR[]; PutLongDecimal[totalBad]; PutString[" bad files out of "L]; PutLongDecimal[totalFiles]; PutLine[" files"L]; PutLongDecimal[totalIgnored]; PutLine[" were ignored"L]; FOR r: IgnoreReason IN IgnoreReason DO IF why[r] # 0 THEN { PutLongDecimal[why[r]]; PutLine[name[r]]}; ENDLOOP; }; verbose ← FALSE; DO ENABLE ABORTED => Stats[]; sense: BOOLEAN ← TRUE; [token: token, switches: switches] ← exec.GetToken[]; IF token = NIL AND switches = NIL THEN EXIT; IF switches # NIL THEN FOR n: CARDINAL IN [0..switches.length) DO SELECT switches[n] FROM 'v, 'V => {verbose ← sense; sense ← TRUE}; '-, '~ => sense ← ~sense; ENDCASE; ENDLOOP; switches ← Exec.FreeTokenString[switches]; IF token # NIL THEN Check[token ! UNWIND => token ← Exec.FreeTokenString[token]]; token ← Exec.FreeTokenString[token]; ENDLOOP; Stats[]}; Check: PROC [token: LONG STRING] = { stream: Stream.Handle ← NIL; vfn ← FileName.AllocVFN[token]; stream ← conn.ReadStream[vfn, NIL, FALSE, remote]; WHILE stream # NIL DO ENABLE FileTransfer.Error => IF code = skip OR code = spare1 THEN LOOP; stream.options.signalEndOfStream ← TRUE; CheckFile[stream]; stream ← FileTransfer.ReadNextStream[stream]; ENDLOOP; FileName.FreeVFN[vfn]; vfn ← NIL}; CheckFile: PROC [stream: Stream.Handle] = { source: FileTransfer.FileInfo; nameShown: BOOLEAN ← FALSE; problems: BOOLEAN; Complain: PROC [text: LONG STRING, reason: IgnoreReason] = { IF verbose THEN { IF ~nameShown THEN PutString[source.body]; PutLine[text]} ELSE IF nameShown THEN PutCR[]; totalIgnored ← totalIgnored + 1; why[reason] ← why[reason] + 1}; BEGIN -- to make Complain visible in EXITS clause more: BOOLEAN ← TRUE; IF exec.CheckForAbort[] THEN ERROR ABORTED; source ← FileTransfer.GetStreamInfo[stream]; IF ~String.EquivalentString[source.directory, currentDir] THEN { PutLine[source.directory]; currentDir.length ← 0; String.AppendString[currentDir, source.directory]}; IF (((totalFiles ← totalFiles + 1) MOD 10) = 0) OR verbose THEN { IF ~verbose THEN { PutString[" Checking file "L]; PutLongDecimal[totalFiles]; PutString[": "L]}; PutString[source.body]; nameShown ← TRUE}; [] ← stream.GetBlock[ [buffer, 0, last] ! Stream.EndOfStream => {more ← FALSE; CONTINUE}]; IF more THEN GO TO tooLong; bcd ← LOOPHOLE[buffer, BcdOps.BcdBase]; IF bcd.definitions THEN GO TO defs; IF bcd.versionIdent # BcdDefs.VersionID THEN GOTO obsoleteBcd; IF bcd.nConfigs # 0 THEN GOTO binderBcd; IF bcd.nPages > bufferPages THEN GOTO tooLong; mtb ← LOOPHOLE[bcd + bcd.mtOffset]; mth ← @mtb[FIRST[BcdDefs.MTIndex]]; sgb ← LOOPHOLE[bcd + bcd.sgOffset]; enb ← LOOPHOLE[bcd + bcd.enOffset]; sgh ← @sgb[mth.code.sgi]; -- Bcd's code segment table entry IF sgh.pages > bufferPages THEN GOTO tooLong; IF mth.tableCompiled THEN GOTO table; IF sgh.file # BcdDefs.FTSelf THEN GOTO punt; codebase ← LOOPHOLE[buffer + (sgh.base - 1)*Environment.wordsPerPage]; codebase ← codebase + mth.code.offset; code ← LOOPHOLE[codebase]; sgh ← @sgb[mth.sseg]; -- Bcd's symbol segment table entry IF sgh.base + sgh.pages > bufferPages THEN GOTO tooLong; IF sgh.file # BcdDefs.FTSelf THEN GOTO punt; -- tablecompiled, or ... symHeader ← LOOPHOLE[buffer + (sgh.base - 1)*Environment.wordsPerPage]; IF symHeader.versionIdent # SymbolSegment.VersionID THEN GOTO badSymbols; InstallTable[mySymbols, symHeader]; problems ← ExamineModule[ file: source.body, nameShown: nameShown, entries: @enb[mth.entries], symbols: mySymbols]; IF problems THEN {totalBad ← totalBad + 1; PutCR[]} ELSE IF nameShown THEN PutCR[]; EXITS tooLong => {Complain[" too long"L, long]}; obsoleteBcd => {Complain[" obsolete BCD format"L, old]}; binderBcd => {Complain[" binder output"L, binder]}; defs => {Complain[" definitions"L, defs]}; punt => {Complain[" other problem"L, other]}; table => {Complain[" table compiled"L, table]}; badSymbols => {Complain[" bad symbols"L, other]}; END}; -- problem specific stuff ExamineModule: PROC [ file: LONG STRING, nameShown: BOOLEAN, entries: BcdOps.ENHandle, symbols: SymbolHandle] RETURNS [problems: BOOLEAN ← FALSE] = BEGIN catchEV: CatchFormat.CatchEV = LOOPHOLE[codebase.header.catchCode/2]; catchEntry: CatchFormat.CatchEVHandle = @codebase[catchEV]; OneBody: PROC [bti: Symbols.BTIndex] RETURNS [BOOLEAN] = { start, nBytes: CARDINAL; WITH b: symbols.bb[bti] SELECT FROM Callable => IF ~b.inline THEN { WITH info: b.info SELECT FROM External => nBytes ← info.bytes; ENDCASE; WITH cc: b SELECT FROM Catch => start ← catchEntry[cc.index]; ENDCASE => start ← entries.initialPC[b.entryIndex]; problems ← problems OR ExamineBody[ start, nBytes, symbols, LOOPHOLE[bti], file, nameShown OR problems]}; ENDCASE; RETURN[FALSE]}; [] ← symbols.ops.EnumerateBodies[Symbols.RootBti, OneBody]; IF problems THEN PutCR[]; END; Pair: TYPE = RECORD [fill: BYTE, first, last: [0..16)]; ExamineBody: PROC [ start, nBytes: CARDINAL, symbols: SymbolHandle, bti: Symbols.CBTIndex, file: LONG STRING, nameShown: BOOLEAN] RETURNS [problems: BOOLEAN ← FALSE] = { catch: BOOLEAN = symbols.bb[bti].nesting = Catch; inst: BYTE; il: CARDINAL; pc: CARDINAL; none: CARDINAL = CARDINAL.LAST; rilOffset, rigOffset, linkLoaded: CARDINAL ← none; offset: CARDINAL; extra: CARDINAL; IF catch THEN IF code[start] = Mopcodes.zJ2 THEN pc ← start + 2 ELSE pc ← start ELSE pc ← start + 1; WHILE pc < start + nBytes DO OPEN Mopcodes; inst ← code[pc]; BEGIN -- to set up bingo BEGIN -- to set up checkLocal and checkGlobal SELECT inst FROM zRLI00, zRLI01, zRLI02, zRLI03 => { rilOffset ← 0; rigOffset ← linkLoaded ← none; extra ← 0}; zRLIP, zRLIPF => { pair: Pair; extra ← 0; pair ← LOOPHOLE[code[pc+1]]; rigOffset ← linkLoaded ← none; rilOffset ← pair.first}; zRLILP, zRLILPF => { pair: Pair; extra ← 1; pair ← LOOPHOLE[code[pc+1]]; rigOffset ← linkLoaded ← none; rilOffset ← pair.first}; zRGIP => { pair: Pair; extra ← 0; pair ← LOOPHOLE[code[pc+1]]; rilOffset ← linkLoaded ← none; rigOffset ← pair.first}; zRGILP => { pair: Pair; extra ← 1; pair ← LOOPHOLE[code[pc+1]]; rilOffset ← linkLoaded ← none; rigOffset ← pair.first}; zLLKB => { linkLoaded ← LOOPHOLE[code[pc+1]]; rilOffset ← rigOffset ← none}; zDUP => IF linkLoaded # none THEN GO TO bingo; IN [zSLD0..zSLD6] => {offset ← inst - zSLD0; GO TO checkLocal}; zSLD8 => {offset ← 8; GO TO checkLocal}; zSLDB, zPLDB => {offset ← code[pc+1]; GO TO checkLocal}; zPLD0 => {offset ← 0; GO TO checkLocal}; zSGDB => {offset ← code[pc+1]; GO TO checkGlobal}; ENDCASE => rilOffset ← rigOffset ← linkLoaded ← none; EXITS checkLocal => { IF offset >= rilOffset AND offset <= rilOffset + extra THEN GO TO bingo; rilOffset ← rigOffset ← linkLoaded ← none}; checkGlobal => { IF offset >= rigOffset AND offset <= rigOffset + extra THEN GO TO bingo; rilOffset ← rigOffset ← linkLoaded ← none}; END; EXITS bingo => { IF ~nameShown THEN PutString[file]; PutString["*****"L]; PutHash[symbols, symbols.ops.HashForSe[symbols.bb[bti].id]]; PutChar['(]; PutOctal[pc]; PutChar[')]; RETURN[TRUE]}; END; il ← OpTableDefs.InstLength[inst]; IF il = 0 THEN EXIT; pc ← pc + il; ENDLOOP; }; -- User niceness CheckHelp: Exec.ExecProc = { h.OutputProc[][ "This command takes all bcds on a remote directory and looks for bad code CheckRemoteCode.~ remotefilename(/v for verbose)"L]}; -- MAIN BODY CODE RegisterSelf: PROC = { Exec.AddCommand[name: "CheckCode.~"L, proc: CheckRemoteCode, help: CheckHelp]}; RegisterSelf[]; END.