CountReleaseSize.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Russ Atkinson, April 25, 1985 3:52:56 am PST
DIRECTORY
Basics,
Commander,
CommandTool,
CountedVM,
FS,
FSBackdoor,
IO,
MessageWindow USING [Append],
Process,
Rope,
VersionMap;
CountReleaseSize: CEDAR PROGRAM
IMPORTS Basics, Commander, CommandTool, CountedVM, FS, FSBackdoor, IO, MessageWindow, Process, Rope, VersionMap
= BEGIN
CARD: TYPE = Basics.CARD;
Map: TYPE = VersionMap.Map;
ROPE: TYPE = Rope.ROPE;
STREAM: TYPE = IO.STREAM;
CountCommand: Commander.CommandProc = {
InnerCount: PROC [name: ROPE, match: ROPENIL] = {
files: INT ← 0;
pages: INT ← 0;
ifsPages: INT ← 0;
totalBytes: INT ← 0;
map: Map ← VersionMap.RestoreMapFromFile[name];
IF match = NIL
THEN IO.PutF[cmd.out, " Processing: %g\n", [rope[name]]]
ELSE IO.PutF[cmd.out, " Processing: %g (%g)\n", [rope[name]], [rope[match]] ];
FOR i: INT IN [0..VersionMap.Length[map]) DO
eachName: ROPE ← VersionMap.FetchName[map, i];
IF match = NIL OR Rope.Match[match, eachName, FALSE] THEN {
bytes: INTFS.FileInfo[name: eachName, remoteCheck: FALSE
! FS.Error =>
IF error.group # bug THEN {
IO.PutF[cmd.out, "File not found: %g\n", [rope[eachName]]];
LOOP;
}].bytes;
totalBytes ← totalBytes + bytes;
pages ← pages + (bytes+511) / 512;
ifsPages ← ifsPages + (bytes+2047) / 2048;
files ← files + 1;
MessageWindow.Append[
msg ← IO.PutFR[
" files: %g, bytes: %g, pages: %g, ifsPages: %g",
[integer[files]], [integer[totalBytes]], [integer[pages]], [integer[ifsPages]]],
TRUE];
};
ENDLOOP;
IO.PutF[cmd.out,
" cmd.out: %g, bytes: %g, pages: %g, ifsPages: %g\n",
[integer[files]], [integer[totalBytes]], [integer[pages]], [integer[ifsPages]]
];
};
InnerCount["CedarSource.VersionMap", "*.mesa*"];
InnerCount["CedarSource.VersionMap"];
InnerCount["CedarSymbols.VersionMap"];
};
CountLinesCommand: Commander.CommandProc = {
[cmd: Handle] RETURNS [result: REFNIL, msg: ROPENIL]
CommandObject = [in, out, err: STREAM, commandLine, command: ROPE, ...]
out: STREAM = cmd.out;
switches: PACKED ARRAY CHAR['a..'z] OF BOOLALL[FALSE];
ProcessSwitches: PROC [arg: ROPE] = {
sense: BOOLTRUE;
FOR index: INT IN [0..Rope.Length[arg]) DO
char: CHAR ← Rope.Fetch[arg, index];
SELECT char FROM
'- => LOOP;
'~ => {sense ← NOT sense; LOOP};
IN ['a..'z] => switches[char] ← sense;
IN ['A..'Z] => switches[char + ('a-'A)] ← sense;
ENDCASE;
sense ← TRUE;
ENDLOOP;
};
ProcessArgument: PROC [arg: ROPE] = {
bytes, pos, lines, empties, charsPerLine: CARD ← 0;
Process.CheckForAbort[];
[bytes, pos, lines, empties] ← CountThingsInFile[arg];
argsProcessed ← argsProcessed + 1;
totalChars ← totalChars + pos;
totalLines ← totalLines + lines;
IF lines # 0 THEN charsPerLine ← (pos + lines/2) / lines;
IO.PutF1[cmd.out, "%g\n", [rope[arg]] ];
IO.PutF[cmd.out, " chars: %g, lines: %g, lineLen: %g",
[integer[pos]], [integer[lines]], [integer[charsPerLine]] ];
IO.PutF[cmd.out, ", bytes: %g, empties: %g\n",
[integer[bytes]], [integer[empties]] ];
};
totalChars: CARD ← 0;
totalLines: CARD ← 0;
argsProcessed: NAT ← 0;
# of arguments processed
argv: CommandTool.ArgumentVector ← CommandTool.Parse[cmd: cmd, starExpand: TRUE
! CommandTool.Failed => {msg ← errorMsg; GO TO failed}];
When parsing the command line, be prepared for failure. The error is reported to the user
FOR i: NAT IN [1..argv.argc) DO
Each argument can either be a switch specification or a genuine argument to be processed. The first argument (argv[0]) is not examined, because by convention it is the name of the command as given by the user.
arg: ROPE = CommandTool.FileWithSearchRules[argv[i], ".mesa", cmd];
Process.CheckForAbort[];
It is a good idea to periodically check for a process abort request.
IF Rope.Length[arg] = 0 THEN LOOP;
Ignore null arguments (it is not easy to generate them, even).
IF Rope.Fetch[arg, 0] = '- THEN {
This argument sets switches for the remaining patterns. By convention, switches are normally "sticky", in that they stay set until explicitly changed.
ProcessSwitches[arg];
LOOP;
};
ProcessArgument[arg];
Perform whatever processing is necessary for a normal argument.
ENDLOOP;
IO.PutF[cmd.out, "Total - files: %g, chars: %g, lines: %g, lineLen: %g\n",
[integer[argsProcessed]], [integer[totalChars]], [integer[totalLines]], [integer[(totalChars+totalLines/2) / totalLines]] ];
EXITS
failed => {result ← $Failure};
};
CountReleaseLinesCommand: Commander.CommandProc = {
[cmd: Handle] RETURNS [result: REFNIL, msg: ROPENIL]
CommandObject = [in, out, err: STREAM, commandLine, command: ROPE, ...]
out: STREAM = cmd.out;
switches: PACKED ARRAY CHAR['a..'z] OF BOOLALL[FALSE];
ProcessSwitches: PROC [arg: ROPE] = {
sense: BOOLTRUE;
FOR index: INT IN [0..Rope.Length[arg]) DO
char: CHAR ← Rope.Fetch[arg, index];
SELECT char FROM
'- => LOOP;
'~ => {sense ← NOT sense; LOOP};
IN ['a..'z] => switches[char] ← sense;
IN ['A..'Z] => switches[char + ('a-'A)] ← sense;
ENDCASE;
sense ← TRUE;
ENDLOOP;
};
ProcessArgument: PROC [arg: ROPE] = {
bytes, pos, lines, empties, charsPerLine: CARD ← 0;
Process.CheckForAbort[];
[bytes, pos, lines, empties] ← CountThingsInFile[arg];
argsProcessed ← argsProcessed + 1;
totalChars ← totalChars + pos;
totalLines ← totalLines + lines;
IF lines # 0 THEN charsPerLine ← (pos + lines/2) / lines;
IO.PutF1[cmd.out, "%g\n", [rope[arg]] ];
IO.PutF[cmd.out, " chars: %g, lines: %g, lineLen: %g",
[integer[pos]], [integer[lines]], [integer[charsPerLine]] ];
IO.PutF[cmd.out, ", bytes: %g, empties: %g\n",
[integer[bytes]], [integer[empties]] ];
};
totalChars: CARD ← 0;
totalLines: CARD ← 0;
argsProcessed: NAT ← 0;
# of arguments processed
argv: CommandTool.ArgumentVector ← CommandTool.Parse[cmd: cmd, starExpand: TRUE
! CommandTool.Failed => {msg ← errorMsg; GO TO failed}];
When parsing the command line, be prepared for failure. The error is reported to the user
FOR i: NAT IN [1..argv.argc) DO
Each argument can either be a switch specification or a genuine argument to be processed. The first argument (argv[0]) is not examined, because by convention it is the name of the command as given by the user.
arg: ROPE = CommandTool.FileWithSearchRules[argv[i], ".mesa", cmd];
Process.CheckForAbort[];
It is a good idea to periodically check for a process abort request.
IF Rope.Length[arg] = 0 THEN LOOP;
Ignore null arguments (it is not easy to generate them, even).
IF Rope.Fetch[arg, 0] = '- THEN {
This argument sets switches for the remaining patterns. By convention, switches are normally "sticky", in that they stay set until explicitly changed.
ProcessSwitches[arg];
LOOP;
};
ProcessArgument[arg];
Perform whatever processing is necessary for a normal argument.
ENDLOOP;
IO.PutF[cmd.out, "Total - files: %g, chars: %g, lines: %g, lineLen: %g\n",
[integer[argsProcessed]], [integer[totalChars]], [integer[totalLines]], [integer[(totalChars+totalLines/2) / totalLines]] ];
EXITS
failed => {result ← $Failure};
};
CountThingsInFile: PROC [name: ROPE] RETURNS [bytes, pos, lines, empties: CARD ← 0] = TRUSTED {
vm: CountedVM.Handle ← NIL;
file: FS.OpenFile ← FS.Open[name: name, remoteCheck: FALSE
! FS.Error => GO TO fail];
tempName: ROPENIL;
noteFound: FSBackdoor.NameProc = TRUSTED {
[fullGName: ROPE] RETURNS [continue: BOOLEAN]
tempName ← fullGName;
continue ← TRUE;
};
FSBackdoor.EnumerateCacheForNames[noteFound, NIL, name];
IF tempName = NIL THEN {
tempName ← FS.Copy[from: name, to: "///Temp/Count.temp$"
! FS.Error => CONTINUE;
];
};
file ← FS.Open[name: tempName, remoteCheck: FALSE
! FS.Error => GO TO fail];
{
ENABLE UNWIND => {
IF vm # NIL THEN CountedVM.Free[vm];
FS.Close[file];
};
Buffer: TYPE = PACKED ARRAY [0..charsPerBuffer) OF CHAR;
charsPerBuffer: NAT = 16;
ptr: LONG POINTER TO Buffer ← NIL;
lag: CHAR ← '\n;
GetChar: PROC [peek: BOOLFALSE] RETURNS [c: CHAR] = TRUSTED INLINE {
mod: [0..charsPerBuffer) ← Basics.LowHalf[pos] MOD charsPerBuffer;
IF pos >= bytes THEN RETURN [0C];
c ← ptr[mod];
IF peek THEN RETURN;
pos ← pos + 1;
IF mod = charsPerBuffer - 1 THEN {
ptr ← ptr + SIZE[Buffer];
pos ← pos + 1;
};
};
bytes ← FS.GetInfo[file].bytes;
vm ← CountedVM.SimpleAllocate[(bytes + Basics.bytesPerWord-1)/Basics.bytesPerWord];
ptr ← vm.pointer;
FS.Read[file, 0, vm.interval.count, ptr];
DO
c: CHAR ← GetChar[];
SELECT c FROM
'\n => IF lag = '\n THEN empties ← empties + 1 ELSE lines ← lines + 1;
0C => IF lag = 0C THEN EXIT;
ENDCASE;
lag ← c;
ENDLOOP;
CountedVM.Free[vm];
FS.Close[file];
};
EXITS fail => RETURN;
};
Commander.Register["CountReleaseSize", CountCommand];
Commander.Register["CountLines", CountLinesCommand];
END.