-- Stats.Mesa Edited by Sandman on October 14, 1980 11:10 AM -- Copyright Xerox Corporation 1979, 1980 DIRECTORY AltoDefs USING [BytesPerWord, PageCount], AltoFileDefs USING [CFA, NullFP], BcdDefs USING [Base, MTIndex, VersionID], BcdOps USING [BcdBase, MTHandle], DisplayDefs USING [GetTypeScript], FastDirDefs USING [ FileType, ItemHandle, LookupItem, ScanDir, LocalDir, StripExtension], ImageDefs USING [StopMesa], IODefs USING [ CR, NUL, NumberFormat, ReadID, SP, WriteChar, WriteDecimal, WriteLine, WriteNumber, WriteString], MiscDefs USING [CallDebugger, CommandLineCFA], SegmentDefs USING [ DeleteFileSegment, FileHandle, FileNameError, FileSegmentAddress, FileSegmentHandle, InsertFile, InvalidFP, MoveFileSegment, NewFileSegment, Read, SwapIn, Unlock], StreamDefs USING [ CreateByteStream, JumpToFA, Read, ReadBlock, StreamError, StreamHandle, DiskHandle], String USING [AppendChar, AppendLongNumber, AppendString], Storage USING [Words, FreeWords, PagesForWords], TimeDefs USING [AppendDayTime, CurrentDayTime, UnpackDT]; Stats: PROGRAM IMPORTS DisplayDefs, FastDirDefs, ImageDefs, IODefs, MiscDefs, SegmentDefs, StreamDefs, String, Storage, TimeDefs =PUBLIC BEGIN OPEN AltoDefs, AltoFileDefs, IODefs, SegmentDefs, String, StreamDefs; -- types and globals StatType: TYPE = { char, line, codebytes, framesize, ngfi, nlinks, codepages, sympages}; charField, codebyteField: CARDINAL = 7; nlinksField, lineField, framesizeField: CARDINAL = 6; codepageField: CARDINAL = 5; sympageField: CARDINAL = 4; fsiField: CARDINAL = 4; ngfiField: CARDINAL = 3; filenameField: CARDINAL = 26; file: FileHandle ← NIL; bcd: FileSegmentHandle ← NIL; cmdstream: StreamHandle; lc: INTEGER; full: INTEGER = 18; buffer: POINTER; BufSize: CARDINAL = 40*256; stats: ARRAY StatType OF CARDINAL ← [0, 0, 0, 0, 0, 0, 0, 0]; total, subtotal: ARRAY StatType OF LONG CARDINAL ← [0, 0, 0, 0, 0, 0, 0, 0]; format: ARRAY StatType OF IODefs.NumberFormat = [[base: 10, zerofill: FALSE, unsigned: TRUE, columns: charField], [base: 10, zerofill: FALSE, unsigned: TRUE, columns: lineField], [base: 10, zerofill: FALSE, unsigned: TRUE, columns: codebyteField], [base: 10, zerofill: FALSE, unsigned: TRUE, columns: framesizeField], [base: 10, zerofill: FALSE, unsigned: TRUE, columns: ngfiField], [base: 10, zerofill: FALSE, unsigned: TRUE, columns: nlinksField], [base: 10, zerofill: FALSE, unsigned: TRUE, columns: codepageField], [base: 10, zerofill: FALSE, unsigned: TRUE, columns: sympageField]]; -- the following should be sets StatsWanted, localStatsWanted: PACKED ARRAY StatType OF BOOLEAN ← [TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE]; StatsDesired: TYPE = POINTER TO PACKED ARRAY StatType OF BOOLEAN; -- procedures BcdSwitches: PROCEDURE [wanted: BOOLEAN, st: StatsDesired] = BEGIN st[codebytes] ← wanted; st[framesize] ← wanted; st[ngfi] ← wanted; st[nlinks] ← wanted; st[codepages] ← wanted; st[sympages] ← wanted; END; DefsStatsAndSwitches: PROCEDURE = BEGIN localStatsWanted[codebytes] ← FALSE; localStatsWanted[framesize] ← FALSE; localStatsWanted[ngfi] ← FALSE; localStatsWanted[nlinks] ← FALSE; localStatsWanted[codepages] ← FALSE; stats[nlinks] ← 0; stats[ngfi] ← 0; stats[framesize] ← 0; stats[codepages] ← 0; stats[codebytes] ← 0; END; GetBcdStats: PROCEDURE [bcdseg: FileSegmentHandle] = BEGIN OPEN BcdDefs, SegmentDefs; bcd: BcdOps.BcdBase; mth: BcdOps.MTHandle; sgb: Base; SwapIn[bcdseg]; bcd ← FileSegmentAddress[bcdseg]; mth ← @LOOPHOLE[bcd + bcd.mtOffset, Base][FIRST[MTIndex]]; sgb ← LOOPHOLE[bcd + bcd.sgOffset]; IF ~bcd.definitions THEN BEGIN stats[nlinks] ← mth.frame.length; stats[ngfi] ← mth.ngfi; stats[framesize] ← mth.framesize; stats[codepages] ← sgb[mth.code.sgi].pages; stats[codebytes] ← mth.code.length; END ELSE DefsStatsAndSwitches[]; stats[sympages] ← sgb[mth.sseg].pages; Unlock[bcdseg]; RETURN END; GetModule: PROCEDURE [file: FileHandle] RETURNS [bcdseg: FileSegmentHandle] = BEGIN bcd: BcdOps.BcdBase; pages: AltoDefs.PageCount; bcdseg ← NewFileSegment[file, 1, 1, Read]; SwapIn[bcdseg]; bcd ← FileSegmentAddress[bcdseg]; IF (pages ← bcd.nPages) # 1 THEN BEGIN Unlock[bcdseg]; MoveFileSegment[bcdseg, 1, pages]; SwapIn[bcdseg]; bcd ← FileSegmentAddress[bcdseg]; END; IF bcd.versionIdent # BcdDefs.VersionID THEN BEGIN WriteString[" bad version ID "L]; WriteDecimal[bcd.versionIdent]; Unlock[bcdseg]; DeleteFileSegment[bcdseg]; RETURN[NIL] END; IF bcd.nModules # 1 THEN BEGIN WriteString[" too many modules: "L]; WriteDecimal[bcd.nModules]; Unlock[bcdseg]; DeleteFileSegment[bcdseg]; RETURN[NIL] END; Unlock[bcdseg]; RETURN END; CheckForExtension: PROCEDURE [name, ext: STRING] = BEGIN i: CARDINAL; FOR i IN [0..name.length) DO IF name[i] = '. THEN RETURN; ENDLOOP; String.AppendString[name, ext]; RETURN END; GetSrcStats: PROCEDURE [stream: StreamHandle] = BEGIN count, i: CARDINAL; nc, nl: CARDINAL ← 0; crock: POINTER TO PACKED ARRAY OF CHARACTER = buffer; UNTIL (count ← ReadBlock[ stream: stream, address: buffer, words: BufSize ! StreamError => CONTINUE]) = 0 DO FOR i IN [0..count*2) DO IF crock[i] = IODefs.CR THEN nl ← nl + 1; nc ← nc + 1; ENDLOOP; ENDLOOP; stats[char] ← nc; stats[line] ← nl; RETURN END; GetStats: PROCEDURE = BEGIN name: STRING ← [40]; switches: STRING ← [10]; command: BOOLEAN; i: CARDINAL ← 0; headingWanted: BOOLEAN ← TRUE; buffer ← Storage.Words[BufSize]; SetCommandInput[]; WHILE NextFile[name, switches] DO IF name[0] = '? THEN HelpMessage[] ELSE BEGIN localStatsWanted ← StatsWanted; command ← FALSE; i ← 0; WHILE i < switches.length DO SELECT switches[ i] FROM 'b, 'B => BcdSwitches[TRUE, @localStatsWanted]; 'm, 'M => SourceSwitches[TRUE, @localStatsWanted]; 'x, 'X => BEGIN BcdSwitches[FALSE, @localStatsWanted]; ManagerSwitches[TRUE, @localStatsWanted]; END; '- => BEGIN i ← i + 1; SELECT switches[ i] FROM 'b, 'B => BcdSwitches[FALSE, @localStatsWanted]; 'm, 'M => SourceSwitches[FALSE, @localStatsWanted]; 'x, 'X => ManagerSwitches[FALSE, @localStatsWanted]; ENDCASE => HelpMessage[]; END; 'c, 'C => BEGIN SELECT name[ 0] FROM 'b, 'B => BcdSwitches[TRUE, @StatsWanted]; 'd, 'D => MiscDefs.CallDebugger[NIL]; 'h, 'H => Heading[]; 'm, 'M => SourceSwitches[TRUE, @StatsWanted]; 'x, 'X => BEGIN BcdSwitches[FALSE, @StatsWanted]; ManagerSwitches[TRUE, @StatsWanted]; END; 't, 'T => Total["TOTAL:"L, FALSE]; 's, 'S => Total["SUBTOTAL:"L, TRUE]; '- => SELECT name[ 1] FROM 'b, 'B => BcdSwitches[FALSE, @StatsWanted]; 'm, 'M => SourceSwitches[FALSE, @StatsWanted]; 'x, 'X => ManagerSwitches[FALSE, @StatsWanted]; ENDCASE => HelpMessage[]; ENDCASE => HelpMessage[]; command ← TRUE; END; ENDCASE => HelpMessage[]; i ← i + 1; ENDLOOP; IF NOT command THEN BEGIN IF headingWanted THEN BEGIN headingWanted ← FALSE; Heading[] END; FastDirDefs.StripExtension[name, NIL]; WriteString[name]; THROUGH [name.length..filenameField) DO WriteChar[' ] ENDLOOP; RecordSrcStats[ name ! FileNameError => BEGIN SourceSwitches[FALSE, @localStatsWanted]; CONTINUE END]; RecordBcdStats[ name ! FileNameError => BEGIN BcdSwitches[FALSE, @localStatsWanted]; CONTINUE END]; TallyStats[]; PrintStats[]; WriteChar[CR]; END; END; ENDLOOP; Storage.FreeWords[buffer]; END; Heading: PROCEDURE = BEGIN type: StatType; header: ARRAY StatType OF STRING = [" chars "L, " lines "L, " code "L, " frame "L, " ngfi "L, "nlinks "L, " code "L, "symbol"L]; header2: ARRAY StatType OF STRING = [" "L, " "L, " bytes "L, " size "L, " "L, " "L, " pages "L, "pages"L]; WriteChar[CR]; THROUGH [0..filenameField) DO WriteChar[' ] ENDLOOP; FOR type IN StatType DO IF localStatsWanted[type] THEN WriteString[header[type]] ENDLOOP; WriteChar[CR]; THROUGH [0..filenameField) DO WriteChar[' ] ENDLOOP; FOR type IN StatType DO IF localStatsWanted[type] THEN WriteString[header2[type]] ENDLOOP; WriteChar[CR]; THROUGH [0..filenameField) DO WriteChar[' ] ENDLOOP; WriteDashes[]; END; WriteDashes: PROCEDURE = BEGIN type: StatType; FOR type IN StatType DO IF localStatsWanted[type] THEN BEGIN IF type = LAST[StatType] THEN WriteChar[' ]; THROUGH [0..format[type].columns) DO WriteChar['-]; ENDLOOP; WriteString[" "L]; END; ENDLOOP; WriteChar[CR]; WriteChar[CR]; END; HelpMessage: PROCEDURE = BEGIN WriteLine["The following commands are available: "L]; WriteLine[" b,B - bcd stats"L]; WriteLine[" d,D - enter debugger"L]; WriteLine[" h,H - print column headings"L]; WriteLine[" m,M - source stats"L]; WriteLine[" s,S - subtotal"L]; WriteLine[" t,T - total"L]; WriteLine[ " x,X - source characters, source lines, code bytes, and frame size only"L]; WriteLine[ "b, m, and x are also valid file switches which override the global settings for the file. c and C are switches which indicate a command, i.e. d/c is the command to call the debugger. To suppress the printing of a set of stats, either locally or globally, precede the switch or command by a minus ('-'). Hence -b/c suppresses the printing of all bcd stats, and foo/-b suppresses the printing of bcd stats for file foo. The default settings are b and m."L]; END; ManagerSwitches: PROCEDURE [wanted: BOOLEAN, st: StatsDesired] = BEGIN st[char] ← wanted; st[line] ← wanted; st[codebytes] ← wanted; st[framesize] ← wanted; END; NextFile: PROCEDURE [name, switches: STRING] RETURNS [BOOLEAN] = BEGIN temp: STRING ← [80]; i: CARDINAL ← 0; get1: PROCEDURE RETURNS [CHARACTER] = BEGIN RETURN[ IF cmdstream.endof[cmdstream] THEN IODefs.NUL ELSE cmdstream.get[cmdstream]] END; get2: PROCEDURE RETURNS [c: CHARACTER] = BEGIN IF i < temp.length THEN BEGIN c ← temp[i]; i ← i + 1 END ELSE c ← IODefs.NUL; RETURN[c] END; IF cmdstream # NIL THEN GetToken[get1, name, switches] ELSE BEGIN WriteString["File: "L]; ReadID[temp]; WriteChar[CR]; GetToken[get2, name, switches]; END; RETURN[name.length # 0]; END; OpenFile: PROCEDURE [file: STRING, ft: FastDirDefs.FileType] RETURNS [fh: FileHandle] = BEGIN ih: FastDirDefs.ItemHandle ← FastDirDefs.LookupItem[file, ft]; IF ih = NIL THEN BEGIN SIGNAL SegmentDefs.FileNameError[file]; RETURN[NIL] END; fh ← SegmentDefs.InsertFile[@ih.fp, SegmentDefs.Read]; RETURN; END; GetToken: PROCEDURE [ get: PROCEDURE RETURNS [CHARACTER], token, switches: STRING] = BEGIN OPEN IODefs; s: STRING; c: CHARACTER; token.length ← switches.length ← 0; s ← token; WHILE (c ← get[]) # NUL DO SELECT c FROM SP, CR => IF token.length # 0 OR switches.length # 0 THEN RETURN; '/ => s ← switches; ENDCASE => String.AppendChar[s, c]; ENDLOOP; RETURN END; PrintStats: PROCEDURE = BEGIN OPEN IODefs; type: StatType; i: CARDINAL; FOR type IN StatType DO IF localStatsWanted[type] THEN BEGIN WriteNumber[stats[type], format[type]]; IF type = codepages THEN BEGIN bytes: CARDINAL ← stats[codebytes] + stats[nlinks]*2; bytes ← (IF stats[nlinks] MOD 2 = 0 THEN 4 ELSE 2) + bytes; IF Storage.PagesForWords[bytes/AltoDefs.BytesPerWord] # stats[codepages] THEN WriteChar['*] ELSE WriteChar[SP]; END; IF type # LAST[StatType] THEN WriteString[" "L]; END ELSE BEGIN IF type = codepages THEN WriteChar[' ]; FOR i IN [0..format[type].columns) DO WriteChar[' ] ENDLOOP; IF type # LAST[StatType] THEN WriteString[" "L]; END ENDLOOP; END; RecordBcdStats: PROCEDURE [name: STRING] = BEGIN OPEN String; type: StatType; any: BOOLEAN ← FALSE; FOR type IN [codebytes..sympages] DO any ← any OR localStatsWanted[type] ENDLOOP; IF any THEN BEGIN file ← OpenFile[name, bcd]; bcd ← GetModule[file]; IF bcd # NIL THEN BEGIN GetBcdStats[bcd]; DeleteFileSegment[bcd]; END ELSE BcdSwitches[FALSE, @localStatsWanted]; END; RETURN END; RecordSrcStats: PROCEDURE [name: STRING] = BEGIN OPEN String, SegmentDefs; stream: StreamHandle; IF localStatsWanted[char] OR localStatsWanted[line] THEN BEGIN file ← OpenFile[name, source]; stream ← CreateByteStream[file, Read]; GetSrcStats[stream]; stream.destroy[stream]; END; RETURN; END; SetCommandInput: PROCEDURE = BEGIN OPEN SegmentDefs; cfa: POINTER TO AltoFileDefs.CFA ← MiscDefs.CommandLineCFA[]; IF cfa.fp = AltoFileDefs.NullFP THEN BEGIN cmdstream ← NIL; RETURN END; cmdstream ← StreamDefs.CreateByteStream[ InsertFile[@cfa.fp, Read], Read ! InvalidFP => GOTO noCommandLine]; StreamDefs.JumpToFA[cmdstream, @cfa.fa ! ANY => GOTO noCommandLine]; IF cmdstream.endof[cmdstream] THEN BEGIN cmdstream.destroy[cmdstream]; cmdstream ← NIL; END; EXITS noCommandLine => cmdstream ← NIL; END; SourceSwitches: PROCEDURE [wanted: BOOLEAN, st: StatsDesired] = BEGIN st[char] ← wanted; st[line] ← wanted; END; TallyStats: PROCEDURE = BEGIN type: StatType; FOR type IN StatType DO IF localStatsWanted[type] THEN subtotal[type] ← subtotal[type] + stats[type]; ENDLOOP; END; Total: PROCEDURE [name: STRING, subt: BOOLEAN] = BEGIN type: StatType; THROUGH [0..filenameField) DO WriteChar[' ] ENDLOOP; WriteDashes[]; WriteString[name]; THROUGH [name.length..filenameField) DO WriteChar[' ] ENDLOOP; FOR type IN StatType DO IF localStatsWanted[type] THEN BEGIN WriteDouble[ IF subt THEN subtotal[type] ELSE subtotal[type] + total[type], format[ type]]; IF type = codepages THEN WriteChar[' ]; IF type # LAST[StatType] THEN WriteString[" "L]; END; ENDLOOP; WriteChar[CR]; WriteChar[CR]; IF subt THEN BEGIN FOR type IN StatType DO total[type] ← total[type] + subtotal[type]; ENDLOOP; END ELSE total ← [0, 0, 0, 0, 0, 0, 0, 0]; subtotal ← [0, 0, 0, 0, 0, 0, 0, 0]; END; WriteDouble: PROCEDURE [num: LONG INTEGER, format: IODefs.NumberFormat] = BEGIN i: CARDINAL; temp: STRING ← [20]; String.AppendLongNumber[temp, num, 10]; IF temp.length > format.columns THEN FOR i IN [0..format.columns) DO WriteChar['*] ENDLOOP ELSE BEGIN FOR i IN [temp.length..format.columns) DO WriteChar[' ]; ENDLOOP; WriteString[temp]; END; END; WriteStatsHerald: PROCEDURE = BEGIN dh: StreamDefs.DiskHandle = DisplayDefs.GetTypeScript[]; time: STRING ← [18]; dh.reset[dh]; TimeDefs.AppendDayTime[time, TimeDefs.UnpackDT[TimeDefs.CurrentDayTime[]]]; IODefs.WriteString[" Alto/Mesa Statistics Package 6.0 Statistics as of "L]; IODefs.WriteLine[time]; END; -- main body WriteStatsHerald[]; FastDirDefs.ScanDir[ [TRUE, TRUE, FALSE, FALSE, FALSE, FALSE], FastDirDefs.LocalDir]; GetStats[]; ImageDefs.StopMesa[]; END.