-- 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.