-- CheckIncludes.Mesa -- Last modified by Sandman on July 8, 1980 9:19 AM -- Last modified by Lewis on 13-Apr-81 13:51:31 -- Last modified by Paul Rovner on May 2, 1983 11:43 am DIRECTORY Ascii USING [CR, SP, FF], BcdDefs USING [ Base, BCD, CTIndex, CTNull, CTRecord, FTNull, FTRecord, FTSelf, MTIndex, PackedString, SGIndex, SGRecord, VersionID, VersionStamp], BcdOps USING [MTHandle, ProcessModules], Commander USING [Register, CommandProc, Handle], ConvertUnsafe USING [ToRope], Directory USING [Error, ignore, Lookup], Environment USING [PageCount, PageNumber], File USING [Capability, nullCapability], FileLists USING [ feb, ifb, ifdb, fileList, FE, FEnil, IncFile, InclusionDepth, IFnil, NullStamp, NullTime, IncFileDesc, IFDnil, Initialize, Finalize, InsertInUserList, IsInUserList, InsertInFileList, InsertIncludeFileItem, EnumerateUserList, UserListLength], IncludesSymTables USING [ LoadSymTables, mdb, mdLimit, ssb, ht, ObsoleteSymbolTable, ReleaseSymTables], Inline USING [LowHalf], IO USING [Handle, PutRope, PutChar, UserAbort, ResetUserAbort, UserAborted], LongString USING [ AppendChar, AppendDecimal, AppendString, AppendSubString, EquivalentString, SubString, SubStringDescriptor], OutputDefs USING [ CloseOutput, OpenOutput, PutChar, PutCR, PutLongString, PutNumber, PutString, PutTime], Rope USING [ROPE, Length, Fetch], Segments USING [ DeleteSegment, EnumerateDirectory, FHandle, FileFromSegment, FileNameProblem, FPHandle, GetFileTimes, InsertFile, LockFile, MoveSegment, NewFile, NewSegment, OldFileOnly, Read, SHandle, SegmentAddress, SwapIn, Unlock, UnlockFile], Symbols USING [HTIndex, HTNull, MDIndex, MDRecord, NullFileIndex], Time USING [Current, Packed]; CheckIncludes: MONITOR IMPORTS BcdOps, Commander, ConvertUnsafe, Directory, FileLists, IncludesSymTables, Inline, IO, LongString, OutputDefs, Rope, Segments, Time = BEGIN OPEN FileLists; FilenameChars: CARDINAL = 39; CR: CHARACTER = Ascii.CR; SP: CHARACTER = Ascii.SP; FF: CHARACTER = Ascii.FF; outputFileName: STRING _ [FilenameChars + 1]; sourceCount, bcdCount: CARDINAL; startTime: LONG CARDINAL; inStream: IO.Handle; outStream: IO.Handle; param: Rope.ROPE; cmdIndex: CARDINAL; SwitchTypes: TYPE = -- default is /IO~C~M~N~P {consistent, includes, multiple, order, pause, noCompIfNotOnDisk}; switches: ARRAY SwitchTypes OF BOOLEAN _ [consistent: FALSE, includes: TRUE, multiple: FALSE, order: TRUE, pause: FALSE, noCompIfNotOnDisk: FALSE]; AbortChecking: SIGNAL = CODE; Ugly: PROC[rope: Rope.ROPE] = {outStream.PutRope[rope]}; CheckFiles: ENTRY Commander.CommandProc = TRUSTED --[cmd: Handle] BEGIN ENABLE {UNWIND => NULL; IO.UserAborted, AbortChecking => GO TO Aborted}; Initialize[cmd]; PutHeading[]; GetOutputFileAndSwitches[]; GetInputFileNames[]; EchoCommands[]; AddUserSpecifiedFiles[]; CheckForSourceButNoBcd[]; MarkDirectBads[]; DoOutput[]; Finalize[]; EXITS Aborted => { ReleaseBcd[]; IncludesSymTables.ReleaseSymTables[]; FileLists.Finalize[]; cmd.out.PutRope["\n...aborted\n"]}; END; Initialize: PROC [cmd: Commander.Handle]= BEGIN inStream _ cmd.in; outStream _ cmd.out; -- Commander's TypeScript window param _ cmd.commandLine; cmdIndex _ 0; FileLists.Initialize[]; startTime _ Time.Current[]; sourceCount _ bcdCount _ largestDepthCount _ 0; badFilesExist _ lastFileCompiledHadSwitch _ FALSE; bcdSeg _ NIL; bcd _ NIL; symFile _ NIL END; Finalize: PROC = BEGIN stats: STRING _ [50]; FileLists.Finalize[]; LongString.AppendDecimal[stats, sourceCount]; LongString.AppendString[stats, " source files, "L]; LongString.AppendDecimal[stats, bcdCount]; LongString.AppendString[stats, " Bcds, "L]; LongString.AppendDecimal[stats, Inline.LowHalf[Time.Current[] - startTime]]; LongString.AppendString[stats, " seconds"L]; outStream.PutChar['\n]; Ugly[ConvertUnsafe.ToRope[stats]]; outStream.PutChar['\n]; END; PutHeading: PROC = BEGIN Ugly["\nCedar IncludeChecker 1.0\n"]; END; GetOutputFileAndSwitches: PROC = BEGIN GetToken[outputFileName]; SetSwitches[outputFileName]; IF outputFileName.length = 0 THEN LongString.AppendString[outputFileName, "Includes"L]; END; GetInputFileNames: PROC = BEGIN fileName: STRING _ [FilenameChars + 1]; GetToken[fileName]; UNTIL fileName.length = 0 DO FileLists.InsertInUserList[fileName]; GetToken[fileName]; ENDLOOP; END; EchoCommands: PROC = BEGIN fullOutputName: STRING _ [40]; IF FileLists.UserListLength[] = 0 THEN Ugly["Processing all sources and Bcds on the volume\n"]; IF switches[consistent] THEN Ugly["Compile/bind command to Line.cm\n"]; IF ~switches[multiple] THEN BEGIN LongString.AppendString[fullOutputName, outputFileName]; FOR i: CARDINAL IN [0..fullOutputName.length) DO IF fullOutputName[i] = '. THEN {fullOutputName.length _ i; EXIT}; ENDLOOP; LongString.AppendString[fullOutputName, ".list"L]; IF switches[order] THEN {Ugly["Compilation order to "]; Ugly[ConvertUnsafe.ToRope[fullOutputName]]}; IF switches[includes] THEN {Ugly["Includes/included by list to "]; Ugly[ConvertUnsafe.ToRope[fullOutputName]]}; END ELSE BEGIN Ugly["Multiple output files "]; SELECT TRUE FROM switches[order] AND switches[includes] => Ugly["with compilation order and includes/included by list\n"]; switches[order] => Ugly["with compilation order\n"]; switches[includes] => Ugly["with includes/included by list\n"]; ENDCASE => outStream.PutChar['\n]; END; outStream.PutChar['\n]; END; GetToken: PROC [token: STRING] = BEGIN c: CHARACTER; token.length _ 0; UNTIL cmdIndex >= Rope.Length[param] DO c _ Rope.Fetch[param, cmdIndex]; cmdIndex _ cmdIndex + 1; SELECT c FROM SP, CR => IF token.length > 0 THEN RETURN; ENDCASE => LongString.AppendChar[token, c]; ENDLOOP; END; SetSwitches: PROC [s: STRING] = BEGIN start: CARDINAL; notMinus: BOOLEAN _ TRUE; FOR i: CARDINAL IN [0..s.length) DO IF s[i] = '/ THEN {start _ i; EXIT}; REPEAT FINISHED => RETURN; ENDLOOP; FOR i: CARDINAL IN (start..s.length) DO SELECT s[i] FROM '/ => LOOP; '-, '~ => {notMinus _ FALSE; LOOP}; 'c, 'C => switches[consistent] _ notMinus; 'i, 'I => switches[includes] _ notMinus; 'm, 'M => switches[multiple] _ notMinus; 'n, 'N => switches[noCompIfNotOnDisk] _ notMinus; 'o, 'O => switches[order] _ notMinus; 's, 'S => IF notMinus THEN -- star hack (=C~I~O) BEGIN switches[consistent] _ notMinus; switches[includes] _ switches[order] _ ~notMinus; END; 'p, 'P => switches[pause] _ notMinus; ENDCASE => LOOP; notMinus _ TRUE; ENDLOOP; s.length _ start; END; -- ADD USER-SPECIFIED FILES TO FILELIST, AND ANY FILES THEY DIRECTLY INCLUDE AddUserSpecifiedFiles: PROC = BEGIN IF FileLists.UserListLength[] = 0 THEN -- process all files on volume EnumerateDirectoryForFiles[] ELSE LookupFilesDirectly[]; END; EnumerateDirectoryForFiles: PROC = BEGIN CheckOneFile: PROC [ fp: Segments.FPHandle, dirName: STRING] RETURNS [stop: BOOLEAN] = BEGIN fileName: STRING _ [FilenameChars + 1]; ext: STRING _ [FilenameChars + 1]; mesa, config, bcd: BOOLEAN _ FALSE; file: Segments.FHandle; IF userHitStop[inStream] THEN SIGNAL AbortChecking; SplitFileName[wholename: dirName, name: fileName, ext: ext]; IF LongString.EquivalentString[ext, "mesa"] THEN mesa _ TRUE ELSE IF LongString.EquivalentString[ext, "config"] THEN config _ TRUE ELSE IF LongString.EquivalentString[ext, "bcd"] THEN bcd _ TRUE ELSE RETURN[FALSE]; IF FileLists.UserListLength[] = 0 OR FileLists.IsInUserList[fileName] THEN BEGIN file _ Segments.InsertFile[file: fp, access: Segments.Read]; Segments.LockFile[file]; IF mesa OR config THEN AddSourceFile[sourceFile: file, fileName: fileName, config: config] ELSE AddObjectFile[bcdFile: file, fileName: fileName]; Segments.UnlockFile[file]; END; RETURN[FALSE] END; Segments.EnumerateDirectory[CheckOneFile]; END; SplitFileName: PROC [wholename, name, ext: STRING] = BEGIN active: STRING _ name; name.length _ ext.length _ 0; FOR i: CARDINAL IN [0..wholename.length) DO IF wholename[i] = '. THEN active _ ext ELSE LongString.AppendChar[active, wholename[i]]; ENDLOOP; END; LookupFilesDirectly: PROC = BEGIN AddRequestedFile: PROC [fileName: LONG STRING] RETURNS [stop: BOOLEAN] = BEGIN sourceName: STRING _ [FilenameChars + 1]; bcdName: STRING _ [FilenameChars + 1]; sourceFile, objectFile: Segments.FHandle _ NIL; IF userHitStop[inStream] THEN SIGNAL AbortChecking; LongString.AppendString[to: sourceName, from: fileName]; LongString.AppendString[to: sourceName, from: ".mesa"L]; sourceFile _ Segments.NewFile[ name: sourceName, access: Segments.Read, version: Segments.OldFileOnly ! Segments.FileNameProblem[] => CONTINUE]; IF sourceFile # NIL THEN BEGIN Segments.LockFile[sourceFile]; AddSourceFile[ sourceFile: sourceFile, fileName: fileName, config: FALSE]; Segments.UnlockFile[sourceFile]; END ELSE -- try for config with that name BEGIN sourceName.length _ 0; LongString.AppendString[to: sourceName, from: fileName]; LongString.AppendString[to: sourceName, from: ".config"L]; sourceFile _ Segments.NewFile[ name: sourceName, access: Segments.Read, version: Segments.OldFileOnly ! Segments.FileNameProblem[] => CONTINUE]; IF sourceFile # NIL THEN BEGIN Segments.LockFile[sourceFile]; AddSourceFile[ sourceFile: sourceFile, fileName: fileName, config: TRUE]; Segments.UnlockFile[sourceFile]; END; END; LongString.AppendString[to: bcdName, from: fileName]; LongString.AppendString[to: bcdName, from: ".bcd"L]; objectFile _ Segments.NewFile[ name: bcdName, access: Segments.Read, version: Segments.OldFileOnly ! Segments.FileNameProblem[] => CONTINUE]; IF objectFile # NIL THEN BEGIN Segments.LockFile[objectFile]; AddObjectFile[objectFile, fileName]; Segments.UnlockFile[objectFile]; END; RETURN[FALSE]; END; FileLists.EnumerateUserList[AddRequestedFile]; END; AddSourceFile: PROC [sourceFile: Segments.FHandle, fileName: LONG STRING, config: BOOLEAN] = BEGIN fe: FileLists.FE; outStream.PutChar['.]; sourceCount _ sourceCount+1; fe _ FileLists.InsertInFileList[fileName]; feb[fe].source _ TRUE; feb[fe].config _ config; feb[fe].sourceTime _ Segments.GetFileTimes[file: sourceFile].create; END; ObsoleteBcd: ERROR = CODE; -- Bcd or symbol table version not current CompilerSmashedBcd: ERROR = CODE; -- File had compilation errors ErroneousBcd: ERROR = CODE; bcdSeg: Segments.SHandle _ NIL; bcd: LONG POINTER TO BcdDefs.BCD _ NIL; symFile: Segments.FHandle _ NIL; symBase, symSize: Environment.PageNumber; AddObjectFile: PROC [bcdFile: Segments.FHandle, fileName: LONG STRING] = BEGIN bcdVersion: BcdDefs.VersionStamp; bcdSourceTime: Time.Packed; defModule, dStar, crossJumped, longAlto, tableCompiled, config: BOOLEAN; fe: FileLists.FE; BEGIN [bcdVersion, bcdSourceTime, defModule, dStar, crossJumped, longAlto, tableCompiled, config] _ ExamineBcd[bcdFile ! ObsoleteBcd => GOTO obsoleteBcd; CompilerSmashedBcd => GOTO compilerSmashedBcd; ANY => GOTO processingError]; IF ~config THEN { ReleaseBcd[]; IF symFile # NIL THEN IncludesSymTables.LoadSymTables[symFile, symBase, symSize ! IncludesSymTables.ObsoleteSymbolTable => GOTO obsoleteBcd] ELSE IF ~tableCompiled THEN GOTO ignoreBcd}; outStream.PutChar['.]; bcdCount _ bcdCount+1; fe _ FileLists.InsertInFileList[fileName]; feb[fe].stamp _ bcdVersion; feb[fe].bcdSourceTime _ bcdSourceTime; feb[fe].tableCompiled _ tableCompiled; IF defModule THEN feb[fe].depth _ 50 + (feb[fe].depth MOD 50); -- if defs file was included by another defs file feb[fe].config _ config; IF config THEN BEGIN ProcessIncludedModules[includer: fe, includerName: fileName]; ReleaseBcd[]; END ELSE BEGIN feb[fe].dStar _ dStar; feb[fe].crossJumped _ crossJumped; feb[fe].longAlto _ longAlto; IF symFile # NIL THEN { ProcessIncludedFiles[fe, fileName ! ErroneousBcd => GOTO erroneousBcd]; IncludesSymTables.ReleaseSymTables[]; IF symFile # bcdFile THEN Segments.UnlockFile[symFile]}; END; EXITS ignoreBcd => NULL; obsoleteBcd => BEGIN Ugly["\n "]; Ugly[ConvertUnsafe.ToRope[fileName]]; Ugly[".bcd has an incompatible version\n"]; fe _ FileLists.InsertInFileList[fileName]; feb[fe].obsolete _ TRUE; END; compilerSmashedBcd => BEGIN Ugly["\n "]; Ugly[ConvertUnsafe.ToRope[fileName]]; Ugly[".bcd was marked invalid due to compilation errors\n"]; fe _ FileLists.InsertInFileList[fileName]; feb[fe].erroneous _ TRUE; END; processingError => BEGIN Ugly["\n "]; Ugly[ConvertUnsafe.ToRope[fileName]]; Ugly[".bcd -- processing error (bad Bcd?)\n"]; END; erroneousBcd => BEGIN Ugly["\n "]; Ugly[ConvertUnsafe.ToRope[fileName]]; Ugly[".bcd is invalid\n"]; IncludesSymTables.ReleaseSymTables[]; IF symFile # bcdFile THEN Segments.UnlockFile[symFile]; END; END; END; ExamineBcd: PROC [bcdFile: Segments.FHandle] RETURNS [ bcdVersion: BcdDefs.VersionStamp, bcdSourceTime: Time.Packed, defModule, dStar, crossJumped, longAlto, tableCompiled, config: BOOLEAN] = BEGIN bcdPages: Environment.PageCount; mtb: BcdDefs.Base; modulesMti: BcdDefs.MTIndex = FIRST[BcdDefs.MTIndex]; bcdSeg _ Segments.NewSegment[file: bcdFile, base: 1, pages: 10, access: Segments.Read]; Segments.SwapIn[bcdSeg]; bcd _ Segments.SegmentAddress[bcdSeg]; IF (bcdPages _ bcd.nPages) > 10 THEN BEGIN Segments.Unlock[bcdSeg]; Segments.MoveSegment[seg: bcdSeg, base: 1, pages: bcdPages]; Segments.SwapIn[bcdSeg]; bcd _ Segments.SegmentAddress[bcdSeg]; END; IF bcd.versionIdent # BcdDefs.VersionID THEN IF bcd.versionIdent = FileLists.NullStamp.time THEN ERROR CompilerSmashedBcd[ ! UNWIND => ReleaseBcd[]] ELSE ERROR ObsoleteBcd[ ! UNWIND => ReleaseBcd[]]; config _ (bcd.nConfigs # 0); mtb _ LOOPHOLE[bcd + bcd.mtOffset]; bcdVersion _ bcd.version; bcdSourceTime _ LOOPHOLE[bcd.sourceVersion.time, Time.Packed]; defModule _ bcd.definitions; tableCompiled _ bcd.tableCompiled; IF ~config THEN { dStar _ ~mtb[modulesMti].altoCode; crossJumped _ mtb[modulesMti].crossJumped; longAlto _ mtb[modulesMti].long AND mtb[modulesMti].altoCode; FindSymbolSeg[bcdSeg: bcdSeg, sgi: mtb[modulesMti].sseg ! UNWIND => ReleaseBcd[]]} ELSE {symFile _ NIL; symBase _ symSize _ 0}; END; ReleaseBcd: PROC = { IF bcdSeg # NIL THEN { Segments.Unlock[bcdSeg]; Segments.DeleteSegment[bcdSeg]; bcdSeg _ NIL; bcd _ NIL}}; FindSymbolSeg: PROC [bcdSeg: Segments.SHandle, sgi: BcdDefs.SGIndex] = BEGIN segHandle: LONG POINTER TO BcdDefs.SGRecord _ @LOOPHOLE[bcd + bcd.sgOffset, BcdDefs.Base][sgi]; IF segHandle.file = BcdDefs.FTNull THEN {symFile _ NIL; symBase _ symSize _ 0} ELSE BEGIN symBase _ segHandle.base; symSize _ segHandle.pages; IF segHandle.file = BcdDefs.FTSelf THEN symFile _ Segments.FileFromSegment[bcdSeg] ELSE BEGIN f: LONG POINTER TO BcdDefs.FTRecord = @LOOPHOLE[bcd + bcd.ftOffset, BcdDefs.Base][segHandle.file]; ssb: LONG POINTER TO BcdDefs.PackedString _ LOOPHOLE[bcd + bcd.ssOffset]; ss: LongString.SubStringDescriptor _ [ base: @ssb.string, offset: f.name, length: ssb.size[f.name]]; symFileName: STRING _ [FilenameChars + 1]; LongString.AppendSubString[symFileName, @ss]; AddExtension[symFileName, ".bcd"]; symFile _ Segments.NewFile[ name: symFileName, access: Segments.Read, version: Segments.OldFileOnly]; Segments.LockFile[symFile]; END; END; END; AddExtension: PROC [name, ext: STRING] = BEGIN FOR i: CARDINAL IN [0..name.length) DO IF name[i] = '. THEN RETURN; ENDLOOP; LongString.AppendString[name, ext]; END; ProcessIncludedFiles: PROC [includer: FileLists.FE, includerName: LONG STRING] = BEGIN secondMdi: Symbols.MDIndex = (FIRST[Symbols.MDIndex] + SIZE[Symbols.MDRecord]); includedFile: FileLists.FE; includedName: STRING _ [FilenameChars + 1]; ss: LongString.SubStringDescriptor; FOR mdi: Symbols.MDIndex _ secondMdi, (mdi + SIZE[Symbols.MDRecord]) UNTIL mdi >= IncludesSymTables.mdLimit DO SubStringForHash[@ss, IncludesSymTables.mdb[mdi].fileId]; IF ss.length > (FilenameChars + 1) THEN ERROR ErroneousBcd; includedName.length _ 0; FOR i: CARDINAL IN [0..ss.length) DO IF ss.base[ss.offset + i] = '. THEN EXIT; LongString.AppendChar[includedName, ss.base[ss.offset + i]]; ENDLOOP; includedFile _ FileLists.InsertInFileList[includedName]; feb[includer].includes _ FileLists.InsertIncludeFileItem[ incList: feb[includer].includes, fe: includedFile, feName: includedName, stamp: IncludesSymTables.mdb[mdi].stamp, fileOpenedByCompiler: -- were items actually read by Compiler (IncludesSymTables.mdb[mdi].file # Symbols.NullFileIndex)]; IncreaseDepth[root: includedFile, minDepth: (feb[includer].depth + 1)]; feb[includedFile].includedBy _ FileLists.InsertIncludeFileItem[ incList: feb[includedFile].includedBy, fe: includer, feName: includerName, stamp: FileLists.NullStamp, fileOpenedByCompiler: FALSE]; ENDLOOP; END; ProcessIncludedModules: PROC [includer: FileLists.FE, includerName: LONG STRING] = BEGIN cti, parentCti: BcdDefs.CTIndex; ctb: BcdDefs.Base _ LOOPHOLE[bcd + bcd.ctOffset]; ftb: BcdDefs.Base _ LOOPHOLE[bcd + bcd.ftOffset]; mtb: BcdDefs.Base _ LOOPHOLE[bcd + bcd.mtOffset]; includee: FileLists.FE; includeeName: STRING _ [FilenameChars + 1]; includeeStamp: BcdDefs.VersionStamp; names: LONG POINTER TO BcdDefs.PackedString _ LOOPHOLE[bcd + bcd.ssOffset]; name: CARDINAL; FOR cti _ FIRST[BcdDefs.CTIndex], (cti + SIZE[BcdDefs.CTRecord] + ctb[cti].nControls) UNTIL cti = bcd.ctLimit DO IF ctb[cti].config = BcdDefs.CTNull THEN {parentCti _ cti; LOOP}; includeeName.length _ 0; name _ ctb[cti].name; FOR i: CARDINAL IN [name..name+names.size[name]) DO LongString.AppendChar[includeeName, names.string[i]]; ENDLOOP; includee _ FileLists.InsertInFileList[includeeName]; includeeStamp _ (IF ctb[cti].file = BcdDefs.FTSelf THEN bcd.version ELSE ftb[ctb[cti].file].version); feb[includer].includes _ FileLists.InsertIncludeFileItem[ incList: feb[includer].includes, fe: includee, feName: includeeName, stamp: includeeStamp, fileOpenedByCompiler: TRUE ! UNWIND => ReleaseBcd[]]; IncreaseDepth[root: includee, minDepth: (feb[includer].depth + 1)]; feb[includee].includedBy _ FileLists.InsertIncludeFileItem[ incList: feb[includee].includedBy, fe: includer, feName: includerName, stamp: FileLists.NullStamp, fileOpenedByCompiler: FALSE ! UNWIND => ReleaseBcd[]]; ENDLOOP; {DoModule: PROC [mth: BcdOps.MTHandle, mti: BcdDefs.MTIndex] RETURNS [BOOLEAN] = { IF mth.config # parentCti THEN RETURN[FALSE]; includeeName.length _ 0; name _ mth.name; FOR i: CARDINAL IN [name..name+names.size[name]) DO LongString.AppendChar[includeeName, names.string[i]]; ENDLOOP; includee _ FileLists.InsertInFileList[includeeName]; includeeStamp _ (IF mth.file = BcdDefs.FTSelf THEN bcd.version ELSE ftb[mth.file].version); feb[includer].includes _ FileLists.InsertIncludeFileItem[ incList: feb[includer].includes, fe: includee, feName: includeeName, stamp: includeeStamp, fileOpenedByCompiler: TRUE ! UNWIND => ReleaseBcd[]]; IncreaseDepth[includee, (feb[includer].depth + 1)]; feb[includee].includedBy _ FileLists.InsertIncludeFileItem[ incList: feb[includee].includedBy, fe: includer, feName: includerName, stamp: FileLists.NullStamp, fileOpenedByCompiler: FALSE ! UNWIND => ReleaseBcd[]]; RETURN[FALSE]}; [] _ BcdOps.ProcessModules[bcd, DoModule]}; END; SubStringForHash: PROC [ss: LongString.SubString, hti: Symbols.HTIndex] = BEGIN ss.base _ IncludesSymTables.ssb; IF hti = Symbols.HTNull THEN ss.offset _ ss.length _ 0 ELSE BEGIN ss.offset _ IncludesSymTables.ht[hti - 1].ssIndex; ss.length _ (IncludesSymTables.ht[hti].ssIndex - ss.offset); END; END; largestDepthCount: FileLists.InclusionDepth _ 0; IncreaseDepth: PROC [ root: FileLists.FE, minDepth: FileLists.InclusionDepth] = BEGIN includedFileDesc: FileLists.IncFileDesc; includedFile: FileLists.FE; IF feb[root].busy THEN BEGIN Ugly["\n "]; Ugly[ConvertUnsafe.ToRope[@feb[root].name]]; Ugly[" depends on a module that, in turn, depends on it\n"]; RETURN; END; IF feb[root].depth >= minDepth THEN RETURN; feb[root].busy _ TRUE; largestDepthCount _ MAX[minDepth, largestDepthCount]; feb[root].depth _ minDepth; FOR i: FileLists.IncFile _ feb[root].includes, ifb[i].link UNTIL i = FileLists.IFnil DO includedFileDesc _ ifb[i].includeFileDesc; includedFile _ ifdb[includedFileDesc].file; IncreaseDepth[includedFile, (minDepth + 1)]; ENDLOOP; feb[root].busy _ FALSE; END; -- DISPLAY FILES WITH SOURCE BUT NO BCD ON DISK CheckForSourceButNoBcd: PROC = BEGIN fe: FileLists.FE; firstTime: BOOLEAN _ TRUE; anyWritten: BOOLEAN _ FALSE; numOnLine: CARDINAL _ 0; IF userHitStop[inStream] THEN SIGNAL AbortChecking; FOR fe _ FileLists.fileList, feb[fe].link UNTIL fe = FileLists.FEnil DO IF feb[fe].source AND feb[fe].stamp = FileLists.NullStamp -- no Bcd AND ~(feb[fe].obsolete OR feb[fe].erroneous) AND ~feb[fe].tableCompiled THEN BEGIN IF firstTime THEN Ugly["\n\nSource files with no Bcds on the volume:\n"]; IF (numOnLine _ numOnLine + 1) > 4 THEN {outStream.PutChar['\n]; numOnLine _ 1}; Ugly[" "]; Ugly[ConvertUnsafe.ToRope[@feb[fe].name]]; Ugly[(IF feb[fe].config THEN ".config" ELSE ".mesa")]; firstTime _ FALSE; anyWritten _ TRUE; END; ENDLOOP; IF anyWritten THEN outStream.PutChar['\n]; END; -- MARK FILES THAT DIRECTLY (THEMSELVES) NEED RECOMPILATION badFilesExist: BOOLEAN _ FALSE; MarkDirectBads: PROC = BEGIN incFileDesc: FileLists.IncFileDesc; incStamp, diskStamp: BcdDefs.VersionStamp; IF userHitStop[inStream] THEN SIGNAL AbortChecking; ClearAllTags[]; -- prepare to tag files referenced in different versions FOR fe: FileLists.FE _ FileLists.fileList, feb[fe].link UNTIL fe = FileLists.FEnil DO SELECT TRUE FROM feb[fe].obsolete, feb[fe].erroneous => MarkFileBad[fe]; feb[fe].stamp # FileLists.NullStamp => -- Bcd is on disk BEGIN IF feb[fe].source AND feb[fe].sourceTime # FileLists.NullTime AND feb[fe].bcdSourceTime # FileLists.NullTime AND feb[fe].sourceTime # feb[fe].bcdSourceTime THEN MarkFileBad[fe] ELSE FOR i: FileLists.IncFile _ feb[fe].includes, ifb[i].link UNTIL i = FileLists.IFnil DO incFileDesc _ ifb[i].includeFileDesc; incStamp _ ifdb[incFileDesc].stamp; diskStamp _ feb[ifdb[incFileDesc].file].stamp; IF incStamp # diskStamp AND incStamp # FileLists.NullStamp AND diskStamp # FileLists.NullStamp THEN BEGIN MarkFileBad[fe]; feb[ifdb[incFileDesc].file].tag _ TRUE; -- tag included file EXIT; END; ENDLOOP; END; feb[fe].source => -- source but no bcd on disk IF ~switches[noCompIfNotOnDisk] AND ~feb[fe].tableCompiled THEN MarkFileBad[fe]; ENDCASE; ENDLOOP; PrintFilesInDifferentVersions[]; ClearAllTags[]; END; MarkFileBad: PROC [fe: FileLists.FE] = {feb[fe].bad _ badFilesExist _ TRUE}; ClearAllTags: PROC = BEGIN FOR fe: FileLists.FE _ FileLists.fileList, feb[fe].link UNTIL fe = FileLists.FEnil DO feb[fe].tag _ FALSE ENDLOOP; END; PrintFilesInDifferentVersions: PROC = BEGIN firstTime: BOOLEAN _ TRUE; anyWritten: BOOLEAN _ FALSE; numOnLine: CARDINAL _ 0; PrintFile: PROC [fe: FileLists.FE] = BEGIN IF firstTime THEN Ugly["\nBcds included in a version different than that on the volume:\n"]; IF (numOnLine _ numOnLine + 1) > 4 THEN {outStream.PutChar['\n]; numOnLine _ 1}; Ugly[" "]; Ugly[ConvertUnsafe.ToRope[@feb[fe].name]]; Ugly[".bcd"]; firstTime _ FALSE; anyWritten _ TRUE; END; FOR fe: FileLists.FE _ FileLists.fileList, feb[fe].link UNTIL fe = FileLists.FEnil DO IF feb[fe].tag THEN PrintFile[fe]; ENDLOOP; IF anyWritten THEN outStream.PutChar['\n]; END; -- OUTPUT COMPILATION ORDER, INCLUDE/INCLUDED BY RELATIONS, COMPILATION COMMAND DoOutput: PROC = BEGIN OPEN OutputDefs; IF userHitStop[inStream] THEN SIGNAL AbortChecking; IF switches[order] THEN -- print compilation order BEGIN IF switches[multiple] THEN BEGIN s: STRING _ [FilenameChars + 1]; LongString.AppendChar[s, '.]; LongString.AppendString[s, outputFileName]; IF s[s.length] = '. THEN s.length _ s.length - 1; OpenOutput["Source"L, s] END ELSE OpenOutput[outputFileName, ".list"L]; PrintCompileOrder[]; END; IF switches[includes] THEN -- print includes/included by relations BEGIN IF userHitStop[inStream] THEN SIGNAL AbortChecking; IF switches[multiple] THEN BEGIN IF switches[order] THEN CloseOutput[]; OpenOutput[outputFileName, ".includes"L]; END ELSE IF ~switches[order] THEN OpenOutput[outputFileName, ".list"L]; PrintIncludesRelation[]; IF userHitStop[inStream] THEN SIGNAL AbortChecking; IF switches[multiple] THEN BEGIN CloseOutput[]; OpenOutput[outputFileName, ".includedBy"L]; END ELSE PutChar[FF]; PrintIncludedByRelation[]; CloseOutput[]; END ELSE IF switches[order] THEN CloseOutput[]; IF userHitStop[inStream] THEN SIGNAL AbortChecking; IF switches[consistent] THEN -- put compilation/binding command in Line.Cm BEGIN OpenOutput["Line"L, ".cm"L]; IF badFilesExist THEN { ExtendBadMarks[]; -- mark bad all files depending on directly bad files OutputConsistencyCmd[bind: FALSE]; PutCR[]; OutputConsistencyCmd[bind: TRUE]} ELSE { PutString["// The files appear consistent"L]; PutCR[]; outStream.PutChar['\n]; outStream.PutChar['\n]; Ugly["The files appear consistent\n"]}; CloseOutput[]; END; END; PrintCompileOrder: PROC = BEGIN OPEN OutputDefs; currentDepth, nextLargestDepth: FileLists.InclusionDepth; anyConfigs: BOOLEAN _ FALSE; OutputFilesOfDepth: PROC [depth: FileLists.InclusionDepth, doConfigs: BOOLEAN] = BEGIN numOnLine: CARDINAL _ 0; PutString[" "L]; nextLargestDepth _ 0; FOR fe: FileLists.FE _ FileLists.fileList, feb[fe].link UNTIL fe = FileLists.FEnil DO IF feb[fe].config THEN anyConfigs _ TRUE; IF doConfigs # feb[fe].config THEN LOOP; IF ~feb[fe].tag THEN IF feb[fe].depth = depth THEN BEGIN IF (numOnLine _ numOnLine + 1) > 6 THEN {PutCR[]; PutString[" "L]; numOnLine _ 1}; PutLongString[@feb[fe].name]; PutChar[SP]; feb[fe].tag _ TRUE; END ELSE nextLargestDepth _ MAX[feb[fe].depth, nextLargestDepth]; ENDLOOP; PutCR[]; END; PutString["Compilation Order (by inclusion depth):"L]; PutCR[]; ClearAllTags[]; currentDepth _ largestDepthCount; WHILE currentDepth > 0 DO OutputFilesOfDepth[depth: currentDepth, doConfigs: FALSE]; currentDepth _ nextLargestDepth; ENDLOOP; ClearAllTags[]; IF anyConfigs THEN BEGIN PutCR[]; PutCR[]; PutString["Binding Order (by inclusion depth):"L]; PutCR[]; currentDepth _ largestDepthCount; WHILE currentDepth > 0 DO OutputFilesOfDepth[depth: currentDepth, doConfigs: TRUE]; currentDepth _ nextLargestDepth; ENDLOOP; ClearAllTags[]; END; PutCR[]; PutCR[]; END; PrintIncludesRelation: PROC = BEGIN OPEN OutputDefs; sourceTimeOutput: BOOLEAN; FOR fe: FileLists.FE _ FileLists.fileList, feb[fe].link UNTIL fe = FileLists.FEnil DO sourceTimeOutput _ FALSE; SELECT TRUE FROM feb[fe].obsolete => PrintObsoleteFile[fe]; feb[fe].erroneous => PrintErroneousFile[fe]; feb[fe].stamp # FileLists.NullStamp => -- bcd is on the volume BEGIN PutFileName[fe, printStamp, 0]; IF feb[fe].bcdSourceTime # FileLists.NullStamp.time THEN BEGIN PutString[" (source: "L]; PutTime[feb[fe].bcdSourceTime]; PutChar[')]; sourceTimeOutput _ TRUE; END; IF feb[fe].source AND feb[fe].sourceTime # FileLists.NullStamp.time THEN BEGIN IF sourceTimeOutput THEN {PutCR[]; PutString[" "L]} ELSE PutChar[SP]; IF feb[fe].bcdSourceTime # FileLists.NullStamp.time AND feb[fe].sourceTime # feb[fe].bcdSourceTime THEN PutChar['*]; PutString["(source on volume: "L]; IF feb[fe].bcdSourceTime # FileLists.NullStamp.time AND feb[fe].bcdSourceTime = feb[fe].sourceTime THEN PutString["[same]"L] ELSE PutTime[feb[fe].sourceTime]; PutChar[')]; END; PutString[" includes"L]; IF feb[fe].includes = FileLists.IFnil THEN PutString[" nothing"L] ELSE PrintIncludedFiles[fe]; PutCR[]; PutCR[]; END; ENDCASE; ENDLOOP; END; PrintObsoleteFile: PROC [fe: FileLists.FE] = BEGIN OPEN OutputDefs; PutChar['*]; PutFileName[fe, noStamp, 0]; PutString[" was compiled by an obsolete version of the compiler"L]; PutCR[]; PutCR[]; END; PrintErroneousFile: PROC [fe: FileLists.FE] = BEGIN OPEN OutputDefs; PutChar['*]; PutFileName[fe, noStamp, 0]; PutString[" was marked invalid because of compilation errors"L]; PutCR[]; PutCR[]; END; PrintIncludedFiles: PROC [fe: FileLists.FE] = BEGIN OPEN OutputDefs; FOR i: FileLists.IncFile _ feb[fe].includes, ifb[i].link UNTIL i = FileLists.IFnil DO BEGIN incFileDesc: FileLists.IncFileDesc = ifb[i].includeFileDesc; incStamp: BcdDefs.VersionStamp = ifdb[incFileDesc].stamp; incFile: FileLists.FE = ifdb[incFileDesc].file; incBad: BOOLEAN = incStamp # feb[incFile].stamp AND feb[incFile].stamp # FileLists.NullStamp; PutCR[]; PutChar[SP]; PutChar[IF incBad THEN '* ELSE SP]; PutFileName[incFile, printStamp, 0]; IF incBad THEN {PutString[", but version included was ("L]; PutStamp[incStamp]; PutChar[')]} ELSE IF incStamp = FileLists.NullStamp THEN PutString[" ** never referenced"L] ELSE IF feb[incFile].stamp.time = FileLists.NullStamp.time THEN {PutString[" [version included was "L]; PutStamp[incStamp]; PutString["]"L]}; END; ENDLOOP; END; PutStamp: PROC [s: BcdDefs.VersionStamp] = BEGIN OPEN OutputDefs; PutTime[LOOPHOLE[s.time, Time.Packed]]; PutChar[SP]; PutNumber[s.net, [8, FALSE, FALSE, 1]]; PutChar['#]; PutNumber[s.host, [8, FALSE, FALSE, 1]]; PutChar['#]; END; PutFileName: PROC [ fe: FileLists.FE, stampFlag: {printStamp, noStamp}, padLen: CARDINAL] = BEGIN OPEN OutputDefs; PutLongString[@feb[fe].name]; IF padLen # 0 THEN BEGIN IF feb[fe].name.length < padLen THEN THROUGH [0..(padLen - feb[fe].name.length)) DO PutChar[SP]; ENDLOOP ELSE PutChar[SP]; END; IF stampFlag = printStamp THEN IF feb[fe].stamp.time # FileLists.NullStamp.time THEN {PutString[" ("L]; PutStamp[feb[fe].stamp]; PutChar[')]}; END; PrintIncludedByRelation: PROC = BEGIN OPEN OutputDefs; fe, incByFile: FileLists.FE; i: FileLists.IncFile; incFileDesc: FileLists.IncFileDesc; lhs: BOOLEAN; FOR fe _ FileLists.fileList, feb[fe].link UNTIL fe = FileLists.FEnil DO IF feb[fe].stamp # FileLists.NullStamp OR (feb[fe].obsolete OR feb[fe].erroneous) THEN BEGIN PutFileName[fe, noStamp, 0]; PutString[" is included by"L]; IF feb[fe].includedBy = FileLists.IFnil THEN PutString[" nothing"L] ELSE BEGIN lhs _ TRUE; FOR i _ feb[fe].includedBy, ifb[i].link UNTIL i = FileLists.IFnil DO incFileDesc _ ifb[i].includeFileDesc; incByFile _ ifdb[incFileDesc].file; IF lhs THEN {PutCR[]; PutString[" "L]; PutFileName[incByFile, noStamp, 22]} ELSE PutFileName[incByFile, noStamp, 0]; lhs _ ~lhs; ENDLOOP; END; PutCR[]; PutCR[]; END; ENDLOOP; END; nextLargestBadDepth: FileLists.InclusionDepth; lastFileCompiledHadSwitch: BOOLEAN; OutputConsistencyCmd: PROC [bind: BOOLEAN] = BEGIN OPEN OutputDefs; badDepth: FileLists.InclusionDepth; putCommand: BOOLEAN _ FALSE; badDepth _ nextLargestBadDepth _ 0; FOR fe: FileLists.FE _ FileLists.fileList, feb[fe].link UNTIL fe = FileLists.FEnil DO IF feb[fe].bad AND feb[fe].config = bind THEN {putCommand _ TRUE; badDepth _ MAX[feb[fe].depth, badDepth]}; ENDLOOP; IF ~putCommand THEN RETURN; ClearAllTags[]; PutString[IF bind THEN "Bind"L ELSE "Compile"L]; WHILE badDepth > 0 DO CompileBadFilesOfDepth[badDepth, bind]; IF switches[pause] AND nextLargestBadDepth > 0 THEN PutString[IF lastFileCompiledHadSwitch THEN "p"L ELSE "/p"L]; badDepth _ nextLargestBadDepth; ENDLOOP; ClearAllTags[]; PutCR[]; PutCR[]; IF ~bind THEN ListNeededFiles[]; END; ExtendBadMarks: PROC = BEGIN MarkBad: PROC [fe: FileLists.FE] = BEGIN incFileDesc: FileLists.IncFileDesc; IF feb[fe].tag THEN RETURN; -- fe & includers already marked bad IF CheckCycle[fe] THEN RETURN; feb[fe].busy _ TRUE; FOR i: FileLists.IncFile _ feb[fe].includedBy, ifb[i].link UNTIL i = FileLists.IFnil DO incFileDesc _ ifb[i].includeFileDesc; MarkBad[ifdb[incFileDesc].file]; ENDLOOP; feb[fe].bad _ TRUE; feb[fe].tag _ TRUE; feb[fe].busy _ FALSE; END; ClearAllTags[]; FOR fe: FileLists.FE _ FileLists.fileList, feb[fe].link UNTIL fe = FileLists.FEnil DO IF feb[fe].bad THEN MarkBad[fe]; ENDLOOP; ClearAllTags[]; END; CheckCycle: PROC [fe: FileLists.FE] RETURNS [BOOLEAN] = {RETURN[feb[fe].busy]}; CompileBadFilesOfDepth: PROC [depth: FileLists.InclusionDepth, bind: BOOLEAN] = BEGIN OPEN OutputDefs; nextLargestBadDepth _ 0; FOR fe: FileLists.FE _ FileLists.fileList, feb[fe].link UNTIL fe = FileLists.FEnil DO IF feb[fe].config # bind THEN LOOP; IF feb[fe].bad AND ~feb[fe].tag THEN IF feb[fe].depth = depth THEN BEGIN lastFileCompiledHadSwitch _ FALSE; PutChar[SP]; PutLongString[@feb[fe].name]; IF ~bind AND (~feb[fe].crossJumped OR ~feb[fe].dStar OR feb[fe].longAlto) THEN BEGIN PutChar['/]; IF ~feb[fe].crossJumped THEN PutString["-j"L]; IF ~feb[fe].dStar THEN PutChar['a]; IF feb[fe].longAlto THEN PutChar['l]; lastFileCompiledHadSwitch _ TRUE; END; feb[fe].tag _ TRUE; END ELSE nextLargestBadDepth _ MAX[feb[fe].depth, nextLargestBadDepth]; ENDLOOP; END; ListNeededFiles: PROC = BEGIN TagNeededFilesNotOnDisk[]; ListNeededSourceFiles[]; ListNeededBcdFiles[]; END; TagNeededFilesNotOnDisk: PROC = BEGIN fe: FileLists.FE; ConditionallyRemoveTag: PROC [fe: FileLists.FE] = BEGIN bcdName: STRING _ [FilenameChars + 1]; sourceName: STRING _ [FilenameChars + 1]; fileCap: File.Capability _ File.nullCapability; IF ~feb[fe].bad THEN -- look for Bcd BEGIN LongString.AppendString[to: bcdName, from: @feb[fe].name]; LongString.AppendString[to: bcdName, from: ".bcd"L]; fileCap _ Directory.Lookup[fileName: bcdName, permissions: Directory.ignore ! Directory.Error => CONTINUE]; IF fileCap # File.nullCapability THEN {feb[fe].tag _ FALSE; RETURN}; END ELSE BEGIN LongString.AppendString[to: sourceName, from: @feb[fe].name]; LongString.AppendString[to: sourceName, from: ".mesa"L]; fileCap _ Directory.Lookup[fileName: sourceName, permissions: Directory.ignore ! Directory.Error => CONTINUE]; IF fileCap # File.nullCapability THEN {feb[fe].tag _ FALSE; RETURN}; sourceName.length _ 0; LongString.AppendString[to: sourceName, from: @feb[fe].name]; LongString.AppendString[to: sourceName, from: ".config"L]; fileCap _ Directory.Lookup[fileName: sourceName, permissions: Directory.ignore ! Directory.Error => CONTINUE]; IF fileCap # File.nullCapability THEN {feb[fe].tag _ FALSE; RETURN}; END; END; ClearAllTags[]; -- tag bad files and those they need (include) FOR fe _ FileLists.fileList, feb[fe].link UNTIL fe = FileLists.FEnil DO IF feb[fe].bad THEN BEGIN feb[fe].tag _ TRUE; FOR i: FileLists.IncFile _ feb[fe].includes, ifb[i].link UNTIL i = IFnil DO IF ifb[i].fileOpenedByCompiler THEN BEGIN incFileDesc: FileLists.IncFileDesc = ifb[i].includeFileDesc; incFile: FileLists.FE = ifdb[incFileDesc].file; feb[incFile].tag _ TRUE; END; ENDLOOP; END; ENDLOOP; -- Remove tags on files when a good bcd or a source exists. FOR fe _ FileLists.fileList, feb[fe].link UNTIL fe = FileLists.FEnil DO IF feb[fe].tag THEN ConditionallyRemoveTag[fe]; ENDLOOP; END; ListNeededSourceFiles: PROC = BEGIN firstTime: BOOLEAN _ TRUE; anyWritten: BOOLEAN _ FALSE; numOnLine: CARDINAL _ 0; FOR fe: FileLists.FE _ FileLists.fileList, feb[fe].link UNTIL fe = FileLists.FEnil DO IF ~feb[fe].source AND (feb[fe].tag AND feb[fe].bad) THEN BEGIN -- by def. of tag, source not on volume IF firstTime THEN BEGIN Ugly["\nSource files needed but not on the volume:\n "]; firstTime _ FALSE; END; IF (numOnLine _ numOnLine + 1) > 4 THEN {Ugly["\n "]; numOnLine _ 1}; Ugly[ConvertUnsafe.ToRope[@feb[fe].name]]; Ugly[(IF feb[fe].config THEN ".config " ELSE ".mesa ")]; anyWritten _ TRUE; feb[fe].tag _ FALSE; END; ENDLOOP; IF anyWritten THEN outStream.PutChar['\n]; END; ListNeededBcdFiles: PROC = BEGIN firstTime: BOOLEAN _ TRUE; anyWritten: BOOLEAN _ FALSE; numOnLine: CARDINAL _ 0; FOR fe: FileLists.FE _ FileLists.fileList, feb[fe].link UNTIL fe = FileLists.FEnil DO IF feb[fe].tag --AND NOT feb[fe].bad-- THEN BEGIN -- by def. of tag, bcd not on disk IF firstTime THEN BEGIN Ugly["\nBcds needed but not on the volume:\n "]; firstTime _ FALSE; END; IF (numOnLine _ numOnLine + 1) > 4 THEN {Ugly["\n "]; numOnLine _ 1}; Ugly[ConvertUnsafe.ToRope[@feb[fe].name]]; Ugly[".bcd "]; anyWritten _ TRUE; feb[fe].tag _ FALSE; END; ENDLOOP; IF anyWritten THEN outStream.PutChar['\n]; END; userHitStop: PROC[in: IO.Handle] RETURNS[abort: BOOL] = { abort _ in.UserAbort[]; IF abort THEN in.ResetUserAbort[]; }; -- MAIN BODY CODE Commander.Register[ "IncludeChecker", CheckFiles, "..for determining correct compilation order after changes are made" ]; END.