DListImpl.mesa
Copyright Ó 1989, 1990, 1991, 1993 by Xerox Corporation. All rights reserved.
Carl Hauser, August 24, 1989 4:43:16 pm PDT
Michael Plass, November 25, 1991 1:19 pm PST
Doug Wyatt, April 15, 1992 12:28 pm PDT
Last tweaked by Mike Spreitzer on February 7, 1990 12:12:30 pm PST
Chauser, September 28, 1990 11:16 am PDT
Willie-s, March 18, 1993 7:45 pm PST
taken from PFSCommandsImpl.ListCommandProc
DIRECTORY
BasicTime USING [GMT, minutesPerHour, MonthOfYear, nullGMT, OutOfRange, Period, TimeParametersNotKnown, Unpack, Unpacked, unspecifiedZone, Zone],
Commander USING [CommandProc, Register],
CommanderOps USING [NextArgument],
IO,
List USING [AList, PutAssoc],
PFS USING [EnumerateForInfo, Error, ErrorGroup, FileType, InfoProc, PathFromRope, RopeFromPath],
PFSNames USING [Compare, ComponentCount, Equal, Parent, PATH, SubName],
PriorityQueue USING [Create, Insert, Item, Ref, Remove, Size, SortPred],
Process USING [CheckForAbort],
ProcessProps USING [AddPropList, GetProp],
Rope;
DListImpl: CEDAR PROGRAM
IMPORTS BasicTime, Commander, CommanderOps, IO, List, PFS, PFSNames, PriorityQueue, Process, ProcessProps, Rope
= BEGIN
GMT: TYPE = BasicTime.GMT;
LORA: TYPE = LIST OF REF ANY;
ROPE: TYPE = Rope.ROPE;
PATH: TYPE = PFSNames.PATH;
STREAM: TYPE = IO.STREAM;
DList Command
DListCommandProc: Commander.CommandProc = {
[cmd: Handle] RETURNS [result: REFNIL, msg: ROPENIL]
CommandObject = [in, out, err: STREAM, commandLine, command: ROPE, ...]
EachFileInfo: PFS.InfoProc = {
item: FileItem ¬ NIL;
continue ¬ TRUE;
Process.CheckForAbort[];
item ¬ NEW[FileItemRep ¬ [fullFName, attachedTo, uniqueID.egmt.gmt, bytes, fileType]];
filesSeen ¬ filesSeen + 1;
IF bytes > 0 THEN bytesTotal ¬ bytesTotal + bytes;
PriorityQueue.Insert[pq, item];
};
PrintOneFile: PROC [item: FileItem] = {
item: REF [fullUName, attachedTo: PATH, created: GMT, bytes: INT, keep: CARDINAL, PFS.FileType]
oldLag: PATH ¬ lagPrefix;
printName: ROPE ¬ PFS.RopeFromPath[item.fullUName];
Process.CheckForAbort[];
Factor out the directories
SetLagPrefix[item.fullUName];
IF oldLag # lagPrefix THEN {
IO.PutRope[out, PFS.RopeFromPath[lagPrefix]];
IO.PutChar[out, '\n];
};
printName ¬ PFS.RopeFromPath[PFSNames.SubName[item.fullUName, lagPrefixLen]];
IO.PutRope[out, " "];
IO.PutF1[out, "%-24g ", [rope[printName]] ];
IF item.created = BasicTime.nullGMT
THEN IO.PutRope[out, "??"]
ELSE DateToStream[out, [explicit, item.created] ];
IO.PutChar[out, '\n];
};
AddFile: PROC [pattern: PATH, allVersions: BOOL] = {
Do: PROC [] ~ {
ENABLE PFS.Error => IF error.group # $bug THEN {
IO.PutRope[cmd.err, " -- "];
IO.PutRope[cmd.err, error.explanation];
IO.PutRope[cmd.err, "\n"];
GO TO err};
patternsTried ¬ patternsTried + 1;
PFS.EnumerateForInfo[pattern, EachFileInfo];
In these cases it is better to enumerate for info, to reduce server traffic
EXITS
err => {IO.PutRope[cmd.err, "\n"]; RETURN};
};
wDir: ROPE ~ NARROW[ProcessProps.GetProp[$WorkingDirectory]];
newProp: List.AList ~ List.PutAssoc[$WDir, PFS.PathFromRope[wDir], NIL];
ProcessProps.AddPropList[newProp, Do];
};
SetLagPrefix: PROC [fileName: PATH] = {
... sets the lagging prefix from the given file name, which is presumed to be syntactically correct, although it need not be complete. A file name without a prefix will set the lagPrefix to NIL. We also enforce lagPrefixLen = Rope.Length[lagPrefix] at exit, assuming that no other routine sets lagPrefix.
newPrefix: PATH ¬ IF fileName# NIL THEN PFSNames.Parent[fileName] ELSE NIL;
IF lagPrefix # NIL THEN {
do we have a new prefix?
IF PFSNames.Equal[lagPrefix, newPrefix] THEN RETURN;
};
We have a new lagging prefix, so scan backwards for the LAST directory
lagPrefix ¬ newPrefix;
lagPrefixLen ¬ PFSNames.ComponentCount[newPrefix];
};
AddSortOption: PROC [option: ATOM] = {
new: LORA ¬ LIST[option];
IF sortDataTail = NIL THEN sortData ¬ new ELSE sortDataTail.rest ¬ new;
sortDataTail ¬ new;
};
out: STREAM = cmd.out;
lagPrefix: PATH ¬ NIL;
lagPrefixLen: INT ¬ 0;
patternsTried, filesSeen, bytesTotal: INT ¬ 0;
complexSorting: BOOL ¬ TRUE;
sortData: LORA ¬ NIL;
sortDataTail: LORA ¬ NIL;
pq: PriorityQueue.Ref;
AddSortOption[$MoreRecent];
pq ¬ PriorityQueue.Create[SortPred, sortData];
DO
arg: ROPE = CommanderOps.NextArgument[cmd];
IF arg = NIL THEN EXIT;
the argument is assumed to be a file name.
AddFile[PFS.PathFromRope[arg ! PFS.Error => IF error.group # $bug THEN {
IO.PutRope[cmd.err, " -- "];
IO.PutRope[cmd.err, error.explanation];
IO.PutRope[cmd.err, "\n"];
GO TO failed}], FALSE];
ENDLOOP;
IF filesSeen > 0 THEN {
out: IO.STREAM ~ IO.ROS[];
IF pq # NIL THEN {
lagName: PATH ¬ NIL;
THROUGH [0..PriorityQueue.Size[pq]) DO
item: FileItem = NARROW[PriorityQueue.Remove[pq]];
PrintOneFile[item];
ENDLOOP;
};
IO.PutF1[out, "-- %g files", [integer[filesSeen]] ];
IF bytesTotal > 0 THEN IO.PutF1[out, ", %g total bytes", [integer[bytesTotal]] ];
IO.PutChar[out, '\n];
msg ¬ IO.RopeFromROS[out];
};
EXITS
failed => {result ¬ $Failure};
};
FileItem: TYPE = REF FileItemRep;
FileItemRep: TYPE = RECORD [fullUName, attachedTo: PATH, created: GMT, bytes: INT, fileType: PFS.FileType];
SortPred: PriorityQueue.SortPred = {
[x: Item, y: Item, data: REF] RETURNS [BOOL]
xx: FileItem = NARROW[x];
yy: FileItem = NARROW[y];
options: LORA = NARROW[data];
FOR each: LORA ¬ options, each.rest WHILE each # NIL DO
SELECT each.first FROM
$MoreRecent => {
IF xx.created = yy.created THEN LOOP;
RETURN [BasicTime.Period[xx.created, yy.created] < 0];
};
$LessRecent => {
IF xx.created = yy.created THEN LOOP;
RETURN [BasicTime.Period[xx.created, yy.created] > 0];
};
$Larger => {
IF xx.bytes = yy.bytes THEN LOOP;
RETURN [xx.bytes > yy.bytes];
};
$Smaller => {
IF xx.bytes = yy.bytes THEN LOOP;
RETURN [xx.bytes < yy.bytes];
};
ENDCASE;
ENDLOOP;
RETURN [PFSNames.Compare[xx.fullUName, yy.fullUName, FALSE] = less];
};
Utilities
-- Stolen from DFUtilitiesImpl
DateFormat: TYPE = {explicit, omitted, greaterThan, notEqual};
Date: TYPE = RECORD [
format: DateFormat ¬ $omitted,
gmt: GMT ¬ BasicTime.nullGMT
'gmt' is valid only if dateFormat = $explicit. (We don't use a variant record because it complicates the client's life too much (i.e., the compiler is unnecessarily picky about assignments involving variant records that aren't REF-containing).)
];
Note: Dates returned by ParseFromStream are guaranteed to have `gmt' = nullGMT if `format' ~= $explicit. The other procedures of this interface ignore the 'gmt' field if 'format' ~= $explicit.
DateToStream: PROC [s: STREAM, date: Date] = {
SELECT date.format FROM
$explicit => {
months: ROPE = "JanFebMarAprMayJunJulAugSepOctNovDec";
up: BasicTime.Unpacked = BasicTime.Unpack[date.gmt
! BasicTime.OutOfRange, BasicTime.TimeParametersNotKnown => GO TO noDate];
ConvertZone: PROC = {
dst: BOOL = up.dst = yes;
SELECT up.zone FROM
0 => IF ~dst THEN s.PutRope["GMT"];
NAT[5*BasicTime.minutesPerHour] => s.PutRope[IF dst THEN "EDT" ELSE "EST"];
NAT[6*BasicTime.minutesPerHour] => s.PutRope[IF dst THEN "CDT" ELSE "CST"];
NAT[7*BasicTime.minutesPerHour] => s.PutRope[IF dst THEN "MDT" ELSE "MST"];
NAT[8*BasicTime.minutesPerHour] => s.PutRope[IF dst THEN "PDT" ELSE "PST"];
ENDCASE =>
s.PutF["%g%02d%02d",
[character[IF up.zone < 0 THEN '- ELSE '+]],
[cardinal[up.zone.ABS/BasicTime.minutesPerHour]],
[cardinal[up.zone.ABS MOD BasicTime.minutesPerHour]]
]
};
s.PutF["%02d-%g-%02d ",
[cardinal[up.day]],
[rope[months.Substr[start: up.month.ORD*3, len: 3]]],
[cardinal[up.year MOD 100]]
];
s.PutF["%02d:%02d:%02d ",
[cardinal[up.hour]], [cardinal[up.minute]], [cardinal[up.second]]];
ConvertZone[];
};
$notEqual => s.PutRope["~="];
$greaterThan => s.PutChar['>];
ENDCASE;
EXITS
noDate => NULL;
};
Initialization
Commander.Register["DList", DListCommandProc, "sort a list of file names (not a pattern) by date"];
END.