<<>> <> <> <> <> <> <> <> <> <> 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; <> DListCommandProc: Commander.CommandProc = { <<[cmd: Handle] RETURNS [result: REF _ NIL, msg: ROPE _ NIL]>> <> 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] = { <> oldLag: PATH ¬ lagPrefix; printName: ROPE ¬ PFS.RopeFromPath[item.fullUName]; Process.CheckForAbort[]; <> 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]; <> 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 { <> IF PFSNames.Equal[lagPrefix, newPrefix] THEN RETURN; }; <> 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; <> 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]; }; <> <<>> -- 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).)>> ]; <> 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; }; <> Commander.Register["DList", DListCommandProc, "sort a list of file names (not a pattern) by date"]; END.