-- RStatsImpl.Mesa, last edit January 18, 1983 1:20 pm
-- Pilot 6.0/ Mesa 7.0
--
-- to compute statistics
--
-- RStats -i Cedar3.1.DF
--
-- then
-- RStats -b for the bcds
-- (counts bcds, sizes, etc.)
-- RStats -d Cedar3.1.DF for the DF files
-- (counts DF files, produces DF-DF xref and file-DF xref)
-- RStats -e for the bcd dependency analysis
-- (for bcd definitions file - DF file xref)
-- RStats -f for the bcd dependency analysis
-- (where Special files (from a list) are looked for, e.g. Exec)
-- RStats -r for the source files (broken down by every subdirectory)
-- (source file totals)
-- RStats -s for the source files
-- (source file totals)
-- RStats -t for totals on # and size of files
-- (overall totals)
-- alternatively
-- RStats -a Cedar3.1.DF
-- which does a "-i, -d, -b, -s, -t" in sequence (takes about 3 hours)
DIRECTORY
BcdDefs: TYPE USING[Base, MTIndex],
BcdOps: TYPE USING[BcdBase, MTHandle],
ConvertUnsafe: TYPE USING[ToRope],
CS: TYPE USING[EquivalentRS, Flat, Init, z],
CWF: TYPE USING[SWF4, SetWriteProcedure],
DFSubr: TYPE USING[StripLongName],
Directory: TYPE USING[DeleteFile, Error],
FileIO: TYPE USING[Open],
FQ: TYPE USING[FileQuery, Result],
IFSFile: TYPE USING[CantOpen, Close, Error, FileHandle, GetLength, UnableToLogin],
IO: TYPE USING[card, CharProc, Close, CreateDribbleStream,
EndOf, Flush, GetSequence, Handle, int,
Put, PutChar, PutF, PutFR, ResetUserAbort, rope, string, UserAborted],
LeafSubr: TYPE USING[PrintLeafAccessFailure, PrintLeafProblem, RemoteMap, StopLeaf],
Process: TYPE USING[Detach],
Rope: TYPE USING[Cat, Fetch, Flatten, ROPE, Text],
RStatsSupport: TYPE USING[BcdDep, BreakUp, ComputeFileList, DFDep, DFRec, GetLine,
LeafOpenWithCreate, ProcessBcdAnalysis, ProcessDFList, SubString],
Space: TYPE USING[Create, Handle, LongPointer, nullHandle, Unmap, virtualMemory],
UnsafeSTP: TYPE USING[Error, FileInfo, GetFileInfo, Handle],
STPSubr: TYPE USING [EnumerateForRetrieve,
RetrieveProcType, StopSTP],
Subr: TYPE USING [AbortMyself, EndsIn, errorflg,
FileError, MakeTTYProcs, Prefix, PrintGreeting, strcpy, SubrStop, TTYProcs],
Time: TYPE USING[Current],
TypeScript: TYPE USING[TS, Create, UserAbort, ResetUserAbort],
UECP: TYPE USING[Argv, Parse],
UserExec: TYPE USING[AskUser, CommandProc, RegisterCommand],
ViewerClasses: TYPE USING[Viewer],
ViewerEvents: TYPE USING[EventProc, EventRegistration, RegisterEventProc,
UnRegisterEventProc],
ViewerIO: TYPE USING[CreateViewerStreams];
RStatsImpl: PROGRAM
IMPORTS ConvertUnsafe, CS, CWF, DFSubr, Directory, FileIO, FQ,
IFSFile, IO, LeafSubr, Process, Rope, RStatsSupport,
Space, STP: UnsafeSTP, STPSubr, Subr, Time,
TypeScript, UECP, UserExec, ViewerEvents, ViewerIO = {
Stat: TYPE = {nBcd, nBcdDefn, nBcdConfig, nBcdImpl, nBcdCodeBytes,
nBcdFileBytes, nSrc, nSrcConfig, nSrcMesa, nSrcImpl, nSrcDefn, nSrcLines, nSrcBytes,
nDF, nOtherFiles, nOtherBytes, nTotalFiles, nTotalBytes};
StatArray: TYPE = ARRAY Stat OF INT;
DFRec: TYPE = RStatsSupport.DFRec;
DFDep: TYPE = RStatsSupport.DFDep;
BcdDep: TYPE = RStatsSupport.BcdDep;
-- check ResetCtrs[] before adding more global state
Global: TYPE = RECORD[
fileListName: Rope.Text ← NIL, -- this is readonly
xrefFileName: Rope.Text ← NIL, -- this is readonly
space: Space.Handle ← Space.nullHandle,
stat: REF StatArray ← NIL,
totalStat: REF StatArray ← NIL,
dfrec: LIST OF REF DFRec ← NIL, -- stores list of DF files
lastHost: Rope.Text ← NIL,
lastDirectory: Rope.Text ← NIL,
lastShortname: Rope.Text ← NIL,
lastTopDirectory: Rope.Text ← NIL,
lastWholeDirectory: Rope.Text ← NIL,
--
-- viewer data
typeScript: TypeScript.TS ← NIL,
in: IO.Handle ← NIL,
out: IO.Handle ← NIL, -- dribblestream
fout: IO.Handle ← NIL, -- backing file
tty: Subr.TTYProcs ← NIL
];
-- MDS usage
g: REF Global ← NEW[Global ← [space: Space.Create[1, Space.virtualMemory],
stat: NEW[StatArray], totalStat: NEW[StatArray], fileListName: "ReleaseStats.Files",
xrefFileName: "XRef.DFXRef"]];
destroyEventRegistration: ViewerEvents.EventRegistration;
-- end of MDS usage
Choice: TYPE = {none, all, bcd, df, bcdAnalysis, bcdAnalysisSpecialFiles, init, src,
detailSrc, total};
Main: UserExec.CommandProc = TRUSTED
{
argv: UECP.Argv;
token, filename: Rope.Text;
choice: Choice ← none;
p: PROCESS;
CS.Init[]; -- to make sure module is started
argv ← UECP.Parse[event.commandLine];
FOR i: CARDINAL IN [1 .. argv.argc) DO
token ← CS.Flat[argv[i]];
IF token.Fetch[0] = '- THEN
SELECT token.Fetch[1] FROM
'a => choice ← all;
'b => choice ← bcd;
'd => choice ← df;
'e => choice ← bcdAnalysis;
'f => choice ← bcdAnalysisSpecialFiles;
'i => choice ← init;
'r => choice ← detailSrc;
's => choice ← src;
't => choice ← total;
ENDCASE => NULL
ELSE IF choice = init OR choice = df OR choice = all THEN filename ← token;
ENDLOOP;
IF choice = all THEN
p ← FORK RunAll[filename]
ELSE
p ← FORK ChooseOne[choice, filename];
Process.Detach[p];
};
-- this procedure is FORKed
RunAll: PROC[filename: Rope.Text] = {
ChooseOne[init, filename];
ChooseOne[df, filename];
ChooseOne[src, filename];
ChooseOne[bcd, filename];
ChooseOne[total, filename];
};
-- this procedure is FORKed (or called by RunAll)
ChooseOne: PROC[choice: Choice, filename: Rope.Text] = {
timeStarted: LONG CARDINAL ← Time.Current[];
Cleanup: PROC = {
TypeScript.ResetUserAbort[g.typeScript];
PrintStat[g.out, g.stat, choice];
PrintStat[g.out, g.totalStat, choice];
ResetCtrs[];
LeafSubr.StopLeaf[];
STPSubr.StopSTP[];
Subr.SubrStop[];
timeStarted ← Time.Current[] - timeStarted;
g.out.PutF["Elapsed time for RStats: %r\n", IO.card[timeStarted]];
g.out.Put[IO.string["-----------------------\n\n"L]];
g.fout.Flush[]; -- since g.out.Flush may not flush it (bugs)
g.out.Flush[]; -- cannot g.out.Close, since it destroys the viewer
};
{
ENABLE {
UNWIND => Cleanup[];
STP.Error => {
lcode: LONG CARDINAL ← LOOPHOLE[code, CARDINAL];
g.out.Put[IO.string["FTP Error. "L]];
IF error ~= NIL THEN
g.out.PutF["message: %s, code %d in Stp.Mesa\n", IO.string[error],
IO.card[lcode]];
Subr.errorflg ← TRUE;
GOTO leave;
};
IFSFile.Error, IFSFile.UnableToLogin => {
LeafSubr.PrintLeafProblem[reason];
GOTO leave;
};
IFSFile.CantOpen => {
LeafSubr.PrintLeafAccessFailure[reason];
GOTO leave;
};
Subr.AbortMyself, ABORTED, IO.UserAborted => {
g.out.ResetUserAbort[];
g.out.Put[IO.string["RStats Aborted.\n"L]];
GOTO leave;
};
};
Subr.PrintGreeting["RStats"L];
ResetCtrs[];
IF choice = init THEN
RStatsSupport.ComputeFileList[LOOPHOLE[filename], g.fileListName, g.typeScript, g.out, g.tty]
ELSE AnalyzeAll[choice, filename, g.typeScript, g.out, g.tty];
EXITS
leave => NULL;
};
Cleanup[];
};
AnalyzeAll: PROC[choice: Choice, fileName: Rope.Text, typeScript: TypeScript.TS, out: IO.Handle,
tty: Subr.TTYProcs] =
{
file: IO.Handle ← FileIO.Open[g.fileListName, read];
sfn: Rope.Text;
version: CARDINAL;
createtime: LONG CARDINAL;
host: STRING ← [100];
directory: STRING ← [100];
shortname: STRING ← [100];
out.PutF["Reading list of files from %s\n", IO.rope[g.fileListName]];
WHILE NOT file.EndOf[] DO
[sfn, createtime] ← RStatsSupport.BreakUp[file.GetSequence[LinePlusCR]];
version ← DFSubr.StripLongName[LOOPHOLE[sfn], host, directory, shortname];
IF choice = src
AND (Subr.EndsIn[shortname, ".Mesa"L] OR Subr.EndsIn[shortname, ".Config"L]) THEN {
IF SkipFile[host, directory, shortname] THEN LOOP;
CheckForTopDirSwitch[directory, out, choice];
AnalyzeSrc[host, directory, shortname, version, createtime, out, tty]
}
ELSE IF choice = detailSrc
AND (Subr.EndsIn[shortname, ".Mesa"L] OR Subr.EndsIn[shortname, ".Config"L]) THEN {
IF SkipFile[host, directory, shortname] THEN LOOP;
CheckForWholeDirSwitch[directory, out, choice];
AnalyzeSrc[host, directory, shortname, version, createtime, out, tty]
}
ELSE IF choice = bcd AND Subr.EndsIn[shortname, ".Bcd"L] THEN {
IF SkipFile[host, directory, shortname] THEN LOOP;
CheckForTopDirSwitch[directory, out, choice];
AnalyzeBcd[host, directory, shortname, version, createtime, out, tty]
}
ELSE IF (choice = df OR choice = bcdAnalysis OR choice = bcdAnalysisSpecialFiles)
AND Subr.EndsIn[shortname, ".DF"L] THEN
g.dfrec ← CONS[CS.z.NEW[DFRec ← [host: ConvertUnsafe.ToRope[host],
directory: ConvertUnsafe.ToRope[directory],
shortname: ConvertUnsafe.ToRope[shortname],
version: version, dep: NIL, refBy: NIL]], g.dfrec]
ELSE IF choice = total THEN {
IF SkipFile[host, directory, shortname] THEN LOOP;
CheckForTopDirSwitch[directory, out, choice];
AnalyzeTotals[LOOPHOLE[sfn], host, directory, shortname, version, createtime, out, tty]
};
IF TypeScript.UserAbort[typeScript] THEN SIGNAL Subr.AbortMyself;
out.Flush[];
ENDLOOP;
file.Close[];
out.Flush[];
IF choice = df THEN
g.stat[nDF] ← RStatsSupport.ProcessDFList[fileName, typeScript, out, tty,
g.dfrec, g.xrefFileName];
IF choice = bcdAnalysis OR choice = bcdAnalysisSpecialFiles THEN
RStatsSupport.ProcessBcdAnalysis[typeScript, out, tty,
g.dfrec, choice = bcdAnalysisSpecialFiles];
};
LinePlusCR: IO.CharProc = TRUSTED {
RETURN[quit: char = '\n, include: TRUE];
};
SkipFile: PROC[host, directory, shortname: LONG STRING] RETURNS[skipIt: BOOL] =
{
IF g.lastHost ~= NIL THEN
skipIt ← CS.EquivalentRS[g.lastShortname, shortname]
AND CS.EquivalentRS[g.lastDirectory, directory]
AND CS.EquivalentRS[g.lastHost, host]
ELSE skipIt ← FALSE;
IF NOT skipIt THEN {
g.lastShortname ← ConvertUnsafe.ToRope[shortname];
IF NOT CS.EquivalentRS[g.lastDirectory, directory] THEN
g.lastDirectory ← ConvertUnsafe.ToRope[directory];
IF NOT CS.EquivalentRS[g.lastHost, host] THEN
g.lastHost ← ConvertUnsafe.ToRope[host];
};
};
CheckForWholeDirSwitch: PROC[directory: LONG STRING, out: IO.Handle, choice: Choice] =
{
IF NOT CS.EquivalentRS[g.lastWholeDirectory, directory] THEN {
IF g.lastWholeDirectory ~= NIL THEN
PrintStat[out, g.stat, choice];
g.lastWholeDirectory ← ConvertUnsafe.ToRope[directory];
};
};
CheckForTopDirSwitch: PROC[directory: LONG STRING, out: IO.Handle, choice: Choice] =
{
len: CARDINAL ← directory.length;
dir: STRING ← [100];
FOR i: CARDINAL IN [0 .. len) DO
IF directory[i] = '> THEN {
len ← i;
EXIT;
};
ENDLOOP;
Subr.strcpy[dir, directory];
dir.length ← len;
IF NOT CS.EquivalentRS[g.lastTopDirectory, dir] THEN {
IF g.lastTopDirectory ~= NIL THEN
PrintStat[out, g.stat, choice];
g.lastTopDirectory ← ConvertUnsafe.ToRope[dir];
};
};
-- total analysis
AnalyzeTotals: PROC[filename, host, directory, shortname: LONG STRING, version: CARDINAL,
createtime: LONG CARDINAL, out: IO.Handle, tty: Subr.TTYProcs] =
{
fres: FQ.Result;
targetFileName: STRING ← [125];
vnum: CARDINAL;
byteLength: LONG CARDINAL;
-- out.PutF["<%s>%s", IO.string[directory], IO.string[shortname]];
[fres: fres, remoteVersion: vnum, remoteByteLength: byteLength] ←
FQ.FileQuery[host, directory, shortname, version, createtime, FALSE, tty, targetFileName];
SELECT fres FROM
foundCorrectVersion => {
IF Subr.EndsIn[shortname, ".Mesa"L] OR Subr.EndsIn[shortname, ".Config"L] THEN {
g.stat[nSrc] ← g.stat[nSrc] + 1;
g.stat[nSrcBytes] ← g.stat[nSrcBytes] + byteLength
}
ELSE IF Subr.EndsIn[shortname, ".Bcd"L] THEN {
g.stat[nBcd] ← g.stat[nBcd] + 1;
g.stat[nBcdFileBytes] ← g.stat[nBcdFileBytes] + byteLength
}
ELSE {
g.stat[nOtherFiles] ← g.stat[nOtherFiles] + 1;
g.stat[nOtherBytes] ← g.stat[nOtherBytes] + byteLength;
};
g.stat[nTotalFiles] ← g.stat[nTotalFiles] + 1;
g.stat[nTotalBytes] ← g.stat[nTotalBytes] + byteLength;
};
foundWrongVersion, notFound =>
out.PutF[" Error - cannot open %s.", IO.string[targetFileName]];
ENDCASE => ERROR;
-- out.PutChar['\n];
};
-- source procedures
AnalyzeSrc: PROC[host, directory, shortname: LONG STRING, version: CARDINAL,
createtime: LONG CARDINAL, out: IO.Handle, tty: Subr.TTYProcs] =
{
line: STRING ← [2000];
-- this is only called once
OneFile: STPSubr.RetrieveProcType = {
info: STP.FileInfo ← STP.GetFileInfo[stp];
nImpl, nDefs: INT ← 0;
nLines: INT ← 0;
checkForType: BOOL ← TRUE;
IF Subr.EndsIn[shortname, ".Config"L] THEN g.stat[nSrcConfig] ← g.stat[nSrcConfig] + 1
ELSE g.stat[nSrcMesa] ← g.stat[nSrcMesa] + 1;
-- out.PutF["<%s>%s", IO.string[directory], IO.string[shortname]];
WHILE RStatsSupport.GetLine[remoteStream, line, out] DO
nLines ← nLines + 1;
IF checkForType THEN {
IF RStatsSupport.SubString[line, "PROGRAM"L] THEN {
checkForType ← FALSE;
nImpl ← nImpl + 1;
};
IF RStatsSupport.SubString[line, "DEFINITIONS"L] THEN {
checkForType ← FALSE;
nDefs ← nDefs + 1;
};
IF RStatsSupport.SubString[line, "MONITOR"L] THEN {
checkForType ← FALSE;
nImpl ← nImpl + 1;
};
};
IF line[line.length - 1] ~= '\n THEN EXIT; -- eof
ENDLOOP;
g.stat[nSrc] ← g.stat[nSrc] + 1;
g.stat[nSrcBytes] ← g.stat[nSrcBytes] + info.size;
IF nImpl > 0 AND nDefs > 0 THEN
out.Put[IO.string["Warning - counted twice as impl and defs.\n"L]];
IF nImpl > 0 THEN g.stat[nSrcImpl] ← g.stat[nSrcImpl] + 1;
IF nDefs > 0 THEN g.stat[nSrcDefn] ← g.stat[nSrcDefn] + 1;
g.stat[nSrcLines] ← g.stat[nSrcLines] + nLines;
-- out.PutChar['\n];
};
{
fres: FQ.Result;
targetFileName: STRING ← [100];
vstring: STRING ← IF Subr.Prefix[host, "maxc"L] THEN ";"L ELSE "!"L;
[fres: fres, remoteVersion: version] ← FQ.FileQuery[host, directory, shortname, version,
createtime, FALSE, tty, targetFileName];
SELECT fres FROM
foundCorrectVersion => {
sfn: STRING ← [100];
CWF.SWF4[sfn, "<%s>%s%s%u"L, directory, shortname, vstring, @version];
STPSubr.EnumerateForRetrieve[host, sfn, OneFile, tty, TRUE];
};
foundWrongVersion, notFound =>
out.PutF["%s not found.\n", IO.string[targetFileName]];
ENDCASE => ERROR;
}};
-- BCD procedures
AnalyzeBcd: PROC[host, directory, shortname: LONG STRING, version: CARDINAL,
createtime: LONG CARDINAL, out: IO.Handle, tty: Subr.TTYProcs] =
{{
bcd: BcdOps.BcdBase;
fh: IFSFile.FileHandle;
sfn: Rope.ROPE;
mth: BcdOps.MTHandle;
g.stat[nBcd] ← g.stat[nBcd] + 1;
sfn ← IO.PutFR["<%s>%s", IO.string[directory], IO.string[shortname]];
IF version > 0 THEN sfn ← Rope.Cat[sfn, IO.PutFR["!%d", IO.card[version]]];
-- out.PutF["%s ", IO.rope[sfn]];
fh ← RStatsSupport.LeafOpenWithCreate[host, directory, shortname, version, createtime, out, tty
! Subr.FileError => {
out.PutF["Error - cannot open %s\n", IO.rope[sfn]];
GOTO leave;
}];
IF fh = NIL THEN GOTO leave;
LeafSubr.RemoteMap[g.space, fh, 0];
bcd ← Space.LongPointer[g.space];
IF bcd.definitions THEN g.stat[nBcdDefn] ← g.stat[nBcdDefn] + 1
ELSE IF bcd.nConfigs > 0 THEN g.stat[nBcdConfig] ← g.stat[nBcdConfig] + 1
ELSE {
mth ← @LOOPHOLE[bcd + bcd.mtOffset, BcdDefs.Base][FIRST[BcdDefs.MTIndex]];
g.stat[nBcdCodeBytes] ← g.stat[nBcdCodeBytes] + mth.code.length;
g.stat[nBcdImpl] ← g.stat[nBcdImpl] + 1;
};
Space.Unmap[g.space];
g.stat[nBcdFileBytes] ← g.stat[nBcdFileBytes] + IFSFile.GetLength[fh];
IFSFile.Close[fh];
EXITS
leave => NULL;
};
-- out.PutChar['\n];
};
-- support procedures
SetUpTypeScriptStreams: PROC =
{
vout: IO.Handle;
g.typeScript ← TypeScript.Create[info: [name: "RStats Log Viewer", iconic: FALSE]];
destroyEventRegistration ← ViewerEvents.RegisterEventProc[DestroyProc, destroy];
g.fout ← FileIO.Open["RStats.Log", overwrite];
[g.in, vout] ← ViewerIO.CreateViewerStreams[viewer: g.typeScript, name: "RStats Log Viewer",
editedStream: FALSE];
g.out ← IO.CreateDribbleStream[vout, g.fout];
g.tty ← Subr.MakeTTYProcs[g.in, g.out, g.typeScript, MyConfirm];
[] ← CWF.SetWriteProcedure[ViewerTTYProc];
};
-- cannot print anything in this, monitor is locked
DestroyProc: ViewerEvents.EventProc = TRUSTED
{
IF g = NIL OR event ~= destroy OR viewer ~= g.typeScript THEN RETURN;
g.out.Flush[];
g.fout.Close[]; -- needed because g.out.Close[] does not close the backing file
-- g.out.Close[]; can't close, since this will destroy the viewer
g↑ ← []; -- erase all pointers
viewer.newVersion ← FALSE; -- so it won't ask to confirm new edits
ViewerEvents.UnRegisterEventProc[destroyEventRegistration, destroy];
};
ViewerTTYProc: PROC[ch: CHAR] = {
g.out.PutChar[ch];
};
MyConfirm: PROC[in, out: IO.Handle, data: REF ANY, msg: Rope.ROPE, dch: CHAR]
RETURNS[CHAR] = {
value: ATOM;
value ← UserExec.AskUser[msg: msg, viewer: NARROW[data],
keyList: LIST[$Yes, $No, $All, $Local, $Quit]]; -- order is important
SELECT value FROM
$All => RETURN['a];
$Local => RETURN['l];
$No => RETURN['n];
$Quit => RETURN['q];
$Yes => RETURN['y];
ENDCASE => ERROR;
};
PrintStat: PROC[out: IO.Handle, st: REF StatArray, choice: Choice] =
{
out.PutChar['\n];
SELECT choice FROM
bcd => {
out.PutF["Bcd: %d files, %d defs, %d impls, %d configs,\n\t",
IO.int[st[nBcd]], IO.int[st[nBcdDefn]], IO.int[st[nBcdImpl]], IO.int[st[nBcdConfig]]];
out.PutF["%d code bytes, %d file bytes\n",
IO.int[st[nBcdCodeBytes]], IO.int[st[nBcdFileBytes]]];
};
df => {
out.PutF["DF: %d dffiles\n", IO.int[st[nDF]]];
};
src, detailSrc => {
out.PutF["Source: %d files, %d mesa, %d defs, %d impls, %d config,\n\t",
IO.int[st[nSrc]], IO.int[st[nSrcMesa]], IO.int[st[nSrcDefn]],IO.int[st[nSrcImpl]],
IO.int[st[nSrcConfig]]];
out.PutF["%d lines, %d file bytes\n", IO.int[st[nSrcLines]], IO.int[st[nSrcBytes]]];
};
total => {
out.PutF["Total: %d files, %d total bytes, %d bcd files, %d bcd file bytes,\n\t",
IO.int[st[nTotalFiles]], IO.int[st[nTotalBytes]], IO.int[st[nBcd]], IO.int[st[nBcdFileBytes]]];
out.PutF["%d src files, %d src file bytes, %d other files, %d other bytes\n",
IO.int[st[nSrc]], IO.int[st[nSrcBytes]], IO.int[st[nOtherFiles]], IO.int[st[nOtherBytes]]];
};
init, bcdAnalysis => NULL;
ENDCASE => ERROR;
FOR s: Stat IN Stat DO
g.totalStat[s] ← g.totalStat[s] + g.stat[s];
g.stat[s] ← 0;
ENDLOOP;
out.PutChar['\n];
};
ResetCtrs: PROC =
{
FOR s: Stat IN Stat DO
g.totalStat[s] ← g.stat[s] ← 0;
ENDLOOP;
g.dfrec ← NIL;
g.lastHost ← g.lastDirectory ← g.lastShortname ← g.lastTopDirectory ← NIL;
};
Init: PROC =
{
Directory.DeleteFile[fileName: "RStats.Log"L ! Directory.Error => CONTINUE];
SetUpTypeScriptStreams[]; -- must be first
UserExec.RegisterCommand["RStats.~", Main];
};
Init[];
}.