GenSortedClosure.mesa
Copyright © 1984 by Xerox Corporation. All rights reserved.
Russ Atkinson, July 9, 1984 11:21:52 pm PDT
Russ Atkinson (RRA) June 25, 1985 2:43:01 pm PDT
DIRECTORY
Commander USING [CommandProc, Register],
CommandTool USING [ArgumentVector, Failed, Parse],
FS,
GenerateDFClosure,
IO,
PriorityQueue,
Rope;
GenSortedClosure: CEDAR PROGRAM
IMPORTS Commander, CommandTool, FS, GenerateDFClosure, IO, PriorityQueue, Rope
= BEGIN
LORA: TYPE = LIST OF REF ANY;
ROPE: TYPE = Rope.ROPE;
RopeList: TYPE = LIST OF ROPE;
STREAM: TYPE = IO.STREAM;
Wombat: Commander.CommandProc = {
[cmd: Handle] RETURNS [result: REFNIL, msg: ROPENIL]
CommandObject = [in, out, err: STREAM, commandLine, command: ROPE, ...]
err: STREAM = cmd.err;
outName: ROPE ← "[]<>Temp>GenSortedClosure.$";
out: STREAMNIL;
fx: NAT ← 1;
lag: ROPENIL;
argv: CommandTool.ArgumentVector ← CommandTool.Parse[cmd: cmd
! CommandTool.Failed => {msg ← errorMsg; GO TO failed}];
GenerateDir: PROC [dirName: ROPE] RETURNS [head: RopeList ← NIL] = {
... returns a list of names provided by the enumeration of the given pattern (no error handling if the pattern is illegal).
tail: RopeList ← NIL;
eachName: FS.NameProc = {
[fullFName: ROPE] RETURNS [continue: BOOL]
new: RopeList ← LIST[fullFName];
continue ← TRUE;
IF tail = NIL THEN head ← new ELSE tail.rest ← new;
tail ← new;
};
IF Rope.SkipTo[dirName, 0, "*"] = Rope.Length[dirName] THEN
dirName ← Rope.Concat[dirName, "*"];
FS.EnumerateForNames[dirName, eachName];
};
GenSortedClosure: PROC [dfName: ROPE] RETURNS [head: RopeList ← NIL] = {
tail: RopeList ← NIL;
pq: PriorityQueue.Ref ← PriorityQueue.Predict[100, SortPred, NIL];
eachData: EachData ← NEW[EachDataRep ← [pq, err]];
[] ← GenerateDFClosure.GenerateClosureToProc[dfName, err, EachFile, eachData, [messages: TRUE, toFork: IF switches['m] THEN 2 ELSE 0, followImports: NOT switches['s]]];
WHILE NOT PriorityQueue.Empty[pq] DO
name: ROPENARROW[PriorityQueue.Remove[pq]];
new: RopeList ← LIST[name];
IF tail = NIL THEN head ← new ELSE tail.rest ← new;
tail ← new;
ENDLOOP;
};
DumpList: PROC [list: RopeList, out: STREAM] = {
WHILE list # NIL DO
IO.PutRope[out, list.first];
IO.PutChar[out, '\n];
list ← list.rest;
ENDLOOP;
};
RemoveNames: PROC [list: RopeList, subList: RopeList] RETURNS [RopeList] = {
... destructively removes names from the list that are in the subList. No alteration is made to the subList.
lag: RopeList ← NIL;
current: RopeList ← list;
WHILE current # NIL AND subList # NIL DO
top: ROPE = subList.first;
SELECT Rope.Compare[top, current.first, FALSE] FROM
less => {
The name from the subList is less than the current name from the list, so just remove the name from the subList. The list should not be touched.
subList ← subList.rest;
};
equal => {
The name from the subList is equal to the name from the list, so splice out the current element from the list. Also advance to the next element in the subList.
subList ← subList.rest;
current ← current.rest;
lag.rest ← current;
};
greater => {
The name from the subList is greater than the name from the list, so just leave the name from the list as it is, and advance to the next list element.
lag ← current;
current ← lag.rest;
};
ENDCASE;
ENDLOOP;
RETURN [list];
};
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;
};
switches: PACKED ARRAY CHAR['a..'z] OF BOOLALL[FALSE];
dfName: ROPENIL;
directory: ROPENIL;
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 = argv[i];
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;
};
SELECT TRUE FROM
Rope.Equal[arg, "←"] => {};
i+1 < argv.argc AND Rope.Equal[argv[i+1], "←"] => outName ← arg;
dfName = NIL => dfName ← arg;
directory = NIL => directory ← arg;
ENDCASE => GO TO usage;
ENDLOOP;
out ← FS.StreamOpen[outName, create
! FS.Error => IF error.group # bug THEN {msg ← error.explanation; GO TO failed}
];
IO.PutRope[err, "Output to "];
IO.PutRope[err, outName];
DumpList[RemoveNames[GenerateDir[directory], GenSortedClosure[dfName]], out];
IO.PutChar[err, '\n];
IO.Close[out];
EXITS
usage => {
result ← $Failure;
msg ← "Usage: GenSortedClosure (output ←) dfName dirName\n"};
failed => {result ← $Failure};
};
EachData: TYPE = REF EachDataRep;
EachDataRep: TYPE = RECORD [pq: PriorityQueue.Ref, err: STREAM];
EachFile: GenerateDFClosure.ActionProc = TRUSTED {
eachData: EachData = NARROW[data];
pq: PriorityQueue.Ref = eachData.pq;
IF date.format # explicit OR Rope.SkipTo[name, 0, "!"] = Rope.Length[name] THEN {
Full name not present so get it from the server (if possible)
name ← FS.FileInfo[name
! FS.Error => IF error.group # bug THEN {
IO.PutRope[eachData.err, "\n"];
IO.PutRope[eachData.err, error.explanation];
GO TO bogus;
}
].fullFName;
};
PriorityQueue.Insert[pq, name];
EXITS bogus => {};
};
SortPred: PriorityQueue.SortPred = {
[x: Item, y: Item, data: REF] RETURNS [BOOL]
xx: ROPE = NARROW[x];
yy: ROPE = NARROW[y];
RETURN [Rope.Compare[xx, yy, FALSE] = less];
};
Commander.Register[key: "///Commands/GenSortedClosure", proc: Wombat, doc: "GenSortedClosure (output ←) dfName dirName\n -m: multiple processes (2 helpers)\n -s: shallow (don't follow imports)"];
END.