ButtonLSImpl.mesa
Copyright Ó 1990, 1992 by Xerox Corporation. All rights reserved.
Doug Wyatt, August 16, 1990 1:59 pm PDT
Bier, February 12, 1992 3:40 pm PST
Kenneth A. Pier, October 30, 1990 2:01 pm PST
Chauser, May 27, 1992 11:35 am PDT
DIRECTORY
ProcessProps, Atom, BasicTime, Commander, CommanderOps, IO, IOTioga, List, NodeProps, PFS, PFSNames, PriorityQueue, Process, Rope;
ButtonLSImpl: CEDAR PROGRAM
IMPORTS ProcessProps, BasicTime, Commander, CommanderOps, IO, IOTioga, List, NodeProps, PFS, PFSNames, PriorityQueue, Process, Rope
SHARES IO
~ BEGIN
ROPE: TYPE ~ Rope.ROPE;
PATH: TYPE ~ PFSNames.PATH;
STREAM: TYPE ~ IO.STREAM;
GMT: TYPE ~ BasicTime.GMT;
LORA: TYPE ~ LIST OF REF ANY;
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
RETURN [BasicTime.Period[xx.created, yy.created] < 0];
$LessRecent =>
IF xx.created#yy.created
THEN
RETURN [BasicTime.Period[xx.created, yy.created] > 0];
$Larger =>
IF xx.bytes#yy.bytes
THEN
RETURN [xx.bytes > yy.bytes];
$Smaller =>
IF xx.bytes#yy.bytes
THEN
RETURN [xx.bytes < yy.bytes];
ENDCASE;
ENDLOOP;
RETURN [PFSNames.Compare[xx.fullUName, yy.fullUName, FALSE] = less];
};
QuotedStringError:
ERROR =
CODE;
CmdTokenBreak:
PROC [char:
CHAR]
RETURNS [
IO.CharClass] = {
IF char = '" THEN RETURN [break];
IF char = ' OR char = '\t OR char = ', OR char = '\l OR char = '\r THEN RETURN [sepr];
RETURN [other];
};
Token:
TYPE =
RECORD [value, literal:
ROPE];
GetCmdToken:
PROC [stream:
IO.
STREAM]
RETURNS [token: Token ¬ [
NIL,
NIL]] = {
token.value ¬ token.literal ¬ IO.GetTokenRope[stream, CmdTokenBreak ! IO.EndOfStream => CONTINUE].token;
IF Rope.Equal[token.literal, "\""]
THEN {
ref: REF;
IO.Backup[self: stream, char: '"];
ref ¬ IO.GetRefAny[stream ! IO.Error, IO.EndOfStream => ERROR QuotedStringError];
WITH ref
SELECT
FROM
rope: ROPE => token.value ¬ rope;
ENDCASE => ERROR QuotedStringError;
};
};
monthName: ARRAY BasicTime.MonthOfYear[January..December] OF ROPE ~ [
"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
];
-- 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;
};
theButtonDataKey: ATOM ~ $ButtonData;
theButtonDataRope: ROPE ~ "Poppy1
Class: PopUpButton
MessageHandler: CommandTool
Menu: (
((<Concat \"Open \\\"\" <PropertyText ButtonLSFileName> \"\\\"\">) \"Open\" \"Opens a viewer on the file\")
()
(<Send Tioga <Concat \" \\\"\" <PropertyText ButtonLSFileName> \"\\\"\">> \"Stuff filename\" \"Stuff filename at the current insertion point\")
)
Feedback: (
(MouseMoved <SetCursor bullseye>)
)";
theButtonDataValue: REF ~ NodeProps.DoSpecs[theButtonDataKey, theButtonDataRope];
theDFButtonDataRope: ROPE ~ "Poppy1
Class: PopUpButton
MessageHandler: CommandTool
Menu: (
((<Concat \"Open \\\"\" <PropertyText ButtonLSFileName> \"\\\"\">) \"Open\" \"Opens a viewer on the df file\")
((<Concat \"cd \\\"\" <DirectoryPart <PropertyText ButtonLSFileName>> \"\\\"; ButtonOpen \\\"\" <PropertyText ButtonLSFileName> \"\\\"\">) \"ButtonOpen\" \"Opens a browser for the files in the DF file\")
(<Send Tioga <Concat \" \\\"\" <PropertyText ButtonLSFileName> \"\\\"\">> \"Stuff filename\" \"Stuff filename at the current insertion point\")
)
Feedback: (
(MouseMoved <SetCursor bullseye>)
)";
theDFButtonDataValue: REF ~ NodeProps.DoSpecs[theButtonDataKey, theDFButtonDataRope];
theDirectoryButtonDataRope: ROPE ~ "Poppy1
Class: PopUpButton
MessageHandler: ThisCommandTool
Menu: (
((<Concat \"ButtonLS \\\"\" <PropertyText ButtonLSFileName> \"/*!H\\\"\">) \"ButtonLS\" \"Recursively invoke ButtonLS on this directory\")
((<Send CommandTool <Concat \"Fork ButtonLS \\\"\" <PropertyText ButtonLSFileName> \"/*!H\\\"\">>) \"Fork ButtonLS\" \"Recursively invoke ButtonLS on this directory\")
(<Send Tioga <Concat \" \\\"\" <PropertyText ButtonLSFileName> \"/\\\"\">> \"Stuff directory name\" \"Stuff directory name at the current insertion point\")
)
Feedback: (
(MouseMoved <SetCursor bullseye>)
)";
theDirectoryButtonDataValue:
REF ~ NodeProps.DoSpecs[theButtonDataKey, theDirectoryButtonDataRope];
thePostfixKey: ATOM ~ $Postfix;
thePostfixRope: ROPE ~ "-2.0 outlineBoxBearoff 1.0 outlineBoxThickness";
thePostfixValue:
REF ~ NodeProps.DoSpecs[thePostfixKey, thePostfixRope];
theFileNameKey:
ATOM ~ $ButtonLSFileName;
lookF: IOTioga.Looks ~ IOTioga.LooksFromRope["f"];
SetCharProp:
PROC [stream:
STREAM, key:
ATOM, val:
REF] ~ {
old: IOTioga.PropList ~ IOTioga.GetCharProps[stream];
new: IOTioga.PropList ~ IOTioga.PropPut[old, key, val];
IOTioga.SetCharProps[stream, new];
};
SetLagPrefix:
PROC [fileName:
PATH, lagPrefix:
PATH, lagPrefixLen:
INT]
RETURNS [newLagPrefix:
PATH, newLagPrefixLen:
INT] = {
... 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;
newLagPrefix ¬ lagPrefix;
newLagPrefixLen ¬ lagPrefixLen;
IF lagPrefix #
NIL
THEN {
IF PFSNames.Equal[lagPrefix, newPrefix] THEN RETURN; -- new prefix same as old
};
We have a new lagging prefix, so scan backwards for the LAST directory
newLagPrefix ¬ newPrefix;
newLagPrefixLen ¬ PFSNames.ComponentCount[newPrefix];
};
ListCommandAux:
PROC [cmd: Commander.Handle, printOneFile: PrintOneFileProc]
RETURNS [result:
REF ¬
NIL, msg:
ROPE ¬
NIL] = {
PrintOneFile:
PROC [item: FileItem] = {
[lagPrefix, lagPrefixLen] ¬ printOneFile[out, item, lagPrefix, lagPrefixLen, briefPrint, narrowPrint, fullPrint, attachPrint, oneLine, prefixOnly, typePrint];
};
EachFileName:
PFS.NameProc = {
[name: PATH] RETURNS [continue: BOOL]
attachedTo: PATH ¬ NIL;
created: BasicTime.GMT ¬ BasicTime.nullGMT;
bytes: INT ¬ -1;
fileType: PFS.FileType ¬ PFS.tUnspecified;
uniqueID: PFS.UniqueID;
item: FileItem ¬ NIL;
continue ¬ TRUE;
Process.CheckForAbort[];
IF xactLevelMatch AND componentsRequired # PFSNames.ComponentCount[name] THEN RETURN;
IF
NOT prefixOnly
THEN {
We may need the info
[attachedTo: attachedTo, bytes: bytes, uniqueID: uniqueID, fileType: fileType] ¬ PFS.FileInfo[name: name];
};
IF bytes >=0 AND lostFilesOnly THEN RETURN;
IF attachedTo#NIL AND unattachedOnly THEN RETURN;
item ¬ NEW[FileItemRep ¬ [name, attachedTo, uniqueID.egmt.gmt, bytes, PFS.tUnspecified]];
filesSeen ¬ filesSeen + 1;
IF bytes > 0 THEN bytesTotal ¬ bytesTotal + bytes;
SELECT
TRUE
FROM
prefixOnly => {
oldLag: PATH ¬ lagPrefix;
[lagPrefix, lagPrefixLen] ¬ SetLagPrefix[name, lagPrefix, lagPrefixLen];
IF oldLag # lagPrefix
THEN {
item.fullUName ¬ lagPrefix;
PriorityQueue.Insert[pq, item];
};
};
complexSorting => PriorityQueue.Insert[pq, item];
ENDCASE => PrintOneFile[item];
};
EachFileInfo:
PFS.InfoProc = {
item: FileItem ¬ NIL;
continue ¬ TRUE;
Process.CheckForAbort[];
IF xactLevelMatch AND componentsRequired # PFSNames.ComponentCount[fullFName] THEN RETURN;
IF bytes >=0 AND lostFilesOnly THEN RETURN;
IF attachedTo#NIL AND unattachedOnly THEN RETURN;
item ¬ NEW[FileItemRep ¬ [fullFName, attachedTo, uniqueID.egmt.gmt, bytes, fileType]];
filesSeen ¬ filesSeen + 1;
IF bytes > 0 THEN bytesTotal ¬ bytesTotal + bytes;
SELECT
TRUE
FROM
prefixOnly => {
oldLag: PATH ¬ lagPrefix;
[lagPrefix, lagPrefixLen] ¬ SetLagPrefix[fullFName, lagPrefix, lagPrefixLen];
IF oldLag # lagPrefix
THEN {
item.fullUName ¬ lagPrefix;
PriorityQueue.Insert[pq, item];
};
};
complexSorting => PriorityQueue.Insert[pq, item];
ENDCASE => PrintOneFile[item];
};
TryPattern:
PROC [pattern:
PATH, allVersions:
BOOL¬
FALSE] = {
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;
IF allVersions
THEN {
pattern ¬ PFSNames.SetVersionNumber[pattern, [all]];
highestPrint ¬ FALSE;
};
IF highestPrint
THEN
pattern ¬ PFSNames.SetVersionNumber[pattern, [highest]];
IF xactLevelMatch
THEN {
pattern ¬ PFS.AbsoluteName[pattern];
componentsRequired ¬ PFSNames.ComponentCount[pattern];
};
complexSorting ¬ sortData # NIL;
SELECT
TRUE
FROM
prefixOnly => pq ¬ PriorityQueue.Create[SortPred, NIL];
complexSorting => pq ¬ PriorityQueue.Create[SortPred, sortData];
ENDCASE => pq ¬ NIL;
[lagPrefix, lagPrefixLen] ¬ SetLagPrefix[NIL, lagPrefix, lagPrefixLen];
IF prefixOnly
OR xactLevelMatch
THEN
PFS.EnumerateForNames[pattern, EachFileName]
In these cases it is better to enumerate the names, then get the info
ELSE
PFS.EnumerateForInfo[pattern, EachFileInfo];
In these cases it is better to enumerate for info, to reduce server traffic
[lagPrefix, lagPrefixLen] ¬ SetLagPrefix[NIL, lagPrefix, lagPrefixLen];
IF pq #
NIL
THEN {
lagName: PATH ¬ NIL;
THROUGH [0..PriorityQueue.Size[pq])
DO
item: FileItem = NARROW[PriorityQueue.Remove[pq]];
IF prefixOnly
THEN {
IF PFSNames.Equal[item.fullUName, lagName] THEN LOOP;
lagName ¬ item.fullUName;
};
PrintOneFile[item];
ENDLOOP;
};
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];
};
AddSortOption:
PROC [option:
ATOM] = {
new: LORA ¬ LIST[option];
IF sortDataTail = NIL THEN sortData ¬ new ELSE sortDataTail.rest ¬ new;
sortDataTail ¬ new;
};
RemSortOption:
PROC [option:
ATOM] = {
lag: LORA ¬ sortData;
IF lag = NIL THEN RETURN;
IF lag.first = option
THEN {
sortData ¬ sortData.rest;
RETURN};
FOR each:
LORA ¬ lag.rest, each.rest
WHILE each #
NIL
DO
IF each.first = option THEN {lag.rest ¬ each.rest; EXIT};
lag ¬ each;
ENDLOOP;
};
ProcessSwitches:
PROC [arg:
ROPE] = {
sense: BOOL ¬ TRUE;
direction: {up, down} ¬ down;
FOR index:
INT
IN [1..Rope.Length[arg])
DO
SELECT Rope.Fetch[arg, index]
FROM
'~ => {sense ¬ NOT sense; LOOP};
'> => direction ¬ down;
'< => direction ¬ up;
'a, 'A => attachPrint ¬ sense;
'b, 'B => briefPrint ¬ sense;
'd, 'D => {
RemSortOption[$MoreRecent];
RemSortOption[$LessRecent];
IF sense
THEN
AddSortOption[IF direction = up THEN $LessRecent ELSE $MoreRecent];
};
'f, 'F => fullPrint ¬ sense;
'h, 'H => highestPrint ¬ sense;
'k, 'K => keepPrint ¬ sense;
'n, 'N => narrowPrint ¬ sense;
'o, 'O => oneLine ¬ sense;
'p, 'P => prefixOnly ¬ sense;
'r, 'R => {
-- remoteCheck ← sense;
};
's, 'S => {
RemSortOption[$Larger];
RemSortOption[$Smaller];
IF sense
THEN
AddSortOption[IF direction = up THEN $Smaller ELSE $Larger];
};
't, 'T => typePrint ¬ sense;
'u, 'U => unattachedOnly ¬ sense;
'x, 'X => xactLevelMatch ¬ sense;
'z, 'Z => lostFilesOnly ¬ sense;
ENDCASE => {
result ¬ $Failure;
msg ¬ Rope.Cat["Unknown switch: ", arg.Substr[0, index], " ", arg.Substr[index]];
RETURN;
};
sense ¬ TRUE;
ENDLOOP;
};
gHost, gDir: ROPE ¬ NIL;
out: STREAM = cmd.out;
lagPrefix: PATH ¬ NIL;
lagPrefixLen: INT ¬ 0;
patternsTried, filesSeen, bytesTotal: INT ¬ 0;
briefPrint, complexSorting, fullPrint, attachPrint, keepPrint, lostFilesOnly, narrowPrint, oneLine, prefixOnly, typePrint, unattachedOnly, xactLevelMatch: BOOL ¬ FALSE;
highestPrint: BOOL ¬ cmd.procData.clientData = $Highest;
componentsRequired: INT ¬ 0;
sortData: LORA ¬ NIL;
sortDataTail: LORA ¬ NIL;
pq: PriorityQueue.Ref ¬ NIL;
argStream: IO.STREAM ~ IO.RIS[cmd.commandLine];
DO
arg: Token = GetCmdToken[argStream ! QuotedStringError => {msg ¬ "Mismatched quotes"; GO TO failed}];
IF arg.value = NIL THEN EXIT;
IF Rope.Length[arg.value] = 0 THEN LOOP;
IF Rope.Fetch[arg.literal, 0] = '-
THEN {
This argument sets switches for the remaining patterns
ProcessSwitches[arg.literal];
IF result = $Failure THEN RETURN;
LOOP;
};
Now the argument is assumed to be a file pattern.
TryPattern[
PFS.PathFromRope[arg.value ! 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}]];
ENDLOOP;
IF patternsTried = 0
THEN TryPattern[
PFS.PathFromRope["*" ! 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}]];
IF oneLine THEN IO.PutChar[out, '\n];
IF filesSeen > 0
THEN {
out: IO.STREAM ~ IO.ROS[];
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};
}; -- end of ListCommandProc
PrintOneFileProc: TYPE = PROC [out: IO.STREAM, item: FileItem, lagPrefix: PATH, lagPrefixLen: INT, briefPrint, narrowPrint, fullPrint, attachPrint, oneLine, prefixOnly, typePrint: BOOL] RETURNS [newLagPrefix: PATH, newLagPrefixLen: INT];
ButtonPrintOneFile: PrintOneFileProc = {
PROC [out: IO.STREAM, item: FileItem, lagPrefix: PATH, lagPrefixLen: INT, briefPrint, narrowPrint, fullPrint, attachPrint, oneLine, prefixOnly, typePrint: BOOL] RETURNS [newLagPrefix: PATH, newLagPrefixLen];
item: REF [fullUName, attachedTo: PATH, created: GMT, bytes: INT, keep: CARDINAL, PFS.FileType]
IsDF:
PROC [p:
PATH]
RETURNS [
BOOL] ~ {
name: ROPE ¬ PFSNames.ComponentRope[PFSNames.ShortName[p]];
RETURN [ Rope.Match[pattern: "*.df", object: name, case: FALSE] ];
};
oldLag: PATH ¬ lagPrefix;
printName: ROPE ¬ PFS.RopeFromPath[item.fullUName];
fullName: ROPE ¬ printName;
oldCharProps, newCharProps: IOTioga.PropList ¬ NIL;
newLagPrefix ¬ lagPrefix; newLagPrefixLen ¬ lagPrefixLen;
Process.CheckForAbort[];
IF
NOT fullPrint
AND
NOT prefixOnly
THEN {
Factor out the directories
[lagPrefix, lagPrefixLen] ¬ SetLagPrefix[item.fullUName, lagPrefix, lagPrefixLen];
IF oldLag # lagPrefix
THEN {
IO.PutRope[out, PFS.RopeFromPath[lagPrefix]];
IO.PutChar[out, IF oneLine THEN ' ELSE '\n];
};
printName ¬ PFS.RopeFromPath[PFSNames.SubName[item.fullUName, lagPrefixLen]];
IF NOT oneLine THEN IO.PutRope[out, " "];
};
Emit the button and a space.
SetCharProp[out, theFileNameKey, fullName];
SetCharProp[out, theButtonDataKey, IF item.fileType=PFS.tDirectory THEN theDirectoryButtonDataValue ELSE IF IsDF[item.fullUName] THEN theDFButtonDataValue ELSE theButtonDataValue];
SetCharProp[out, thePostfixKey, thePostfixValue];
IO.PutRope[out, " "];
SetCharProp[out, thePostfixKey, NIL];
SetCharProp[out, theButtonDataKey, NIL];
IO.PutRope[out, " "];
SELECT
TRUE
FROM
prefixOnly => IO.PutRope[out, printName];
briefPrint => {
IO.PutRope[out, printName];
};
ENDCASE => {
form: ROPE = IF narrowPrint THEN "%g\n%12g " ELSE "%-24g %6g ";
IO.PutF[out, form, [rope[printName]], [integer[item.bytes]] ];
IF item.created = BasicTime.nullGMT
THEN IO.PutRope[out, "??"]
ELSE DateToStream[out, [explicit, item.created] ];
IF typePrint
THEN {
r:
ROPE ~
SELECT item.fileType
FROM
PFS.tUnspecified => " tUnspec",
PFS.tDirectory => " tDir",
PFS.tText => " tText",
ENDCASE => IO.PutFR1[" t%g", [integer[item.fileType]]];
IO.PutRope[out, r];
};
IF attachPrint AND item.attachedTo#NIL THEN IO.PutF[out, "%g=> %g", [rope[IF oneLine THEN " " ELSE "\n "]], [rope[PFS.RopeFromPath[item.attachedTo]]]];
};
SetCharProp[out, theFileNameKey, NIL];
IO.PutChar[out, IF oneLine THEN ' ELSE '\n];
RETURN[lagPrefix, lagPrefixLen];
};
PlainPrintOneFile: PrintOneFileProc = {
PROC [out: IO.STREAM, item: FileItem, lagPrefix: PATH, lagPrefixLen: INT, briefPrint, narrowPrint, fullPrint, attachPrint, oneLine, prefixOnly, typePrint: BOOL] RETURNS [newLagPrefix: PATH, newLagPrefixLen];
item: REF [fullUName, attachedTo: PATH, created: GMT, bytes: INT, keep: CARDINAL, PFS.FileType]
oldLag: PATH ¬ lagPrefix;
printName: ROPE ¬ PFS.RopeFromPath[item.fullUName];
newLagPrefix ¬ lagPrefix; newLagPrefixLen ¬ lagPrefixLen;
Process.CheckForAbort[];
IF
NOT fullPrint
AND
NOT prefixOnly
THEN {
Factor out the directories
[lagPrefix, lagPrefixLen] ¬ SetLagPrefix[item.fullUName, lagPrefix, lagPrefixLen];
IF oldLag # lagPrefix
THEN {
IO.PutRope[out, PFS.RopeFromPath[lagPrefix]];
IO.PutChar[out, IF oneLine THEN ' ELSE '\n];
};
printName ¬ PFS.RopeFromPath[PFSNames.SubName[item.fullUName, lagPrefixLen]];
IF NOT oneLine THEN IO.PutRope[out, " "];
};
SELECT
TRUE
FROM
prefixOnly => IO.PutRope[out, printName];
briefPrint => {
IO.PutRope[out, printName];
};
ENDCASE => {
form: ROPE = IF narrowPrint THEN "%g\n%12g " ELSE "%-24g %6g ";
IO.PutF[out, form, [rope[printName]], [integer[item.bytes]] ];
IF item.created = BasicTime.nullGMT
THEN IO.PutRope[out, "??"]
ELSE DateToStream[out, [explicit, item.created] ];
IF typePrint
THEN {
r:
ROPE ~
SELECT item.fileType
FROM
PFS.tUnspecified => " tUnspec",
PFS.tDirectory => " tDir",
PFS.tText => " tText",
ENDCASE => IO.PutFR1[" t%g", [integer[item.fileType]]];
IO.PutRope[out, r];
};
IF attachPrint AND item.attachedTo#NIL THEN IO.PutF[out, "%g=> %g", [rope[IF oneLine THEN " " ELSE "\n "]], [rope[PFS.RopeFromPath[item.attachedTo]]]];
};
IO.PutChar[out, IF oneLine THEN ' ELSE '\n];
RETURN[lagPrefix, lagPrefixLen];
};
listDoc:
ROPE = "{switch | pattern}*
Lists files matching pattern.
-a print attachments
-b brief format
-d date sort
-f full name print
-h highest version
-k keep print
-n narrow print
-o one line
-p prefixes only
-s size sort
-t file type print
-x exact level match
-u unattached files only
-z 0-length files only
-> sort decreasing
-< sort increasing";
ListCommand: Commander.CommandProc = {
[cmd: Handle] RETURNS [result: REF ← NIL, msg: ROPE ← NIL]
CommandObject = [in, out, err: STREAM, commandLine, command: ROPE, ...]
RETURN ListCommandAux[cmd, PlainPrintOneFile];
};
ButtonListCommand: Commander.CommandProc = {
[cmd: Handle] RETURNS [result: REF ← NIL, msg: ROPE ← NIL]
CommandObject = [in, out, err: STREAM, commandLine, command: ROPE, ...]
[] ¬ CommanderOps.DoCommand["Buttons on -quiet", cmd];
RETURN ListCommandAux[cmd, ButtonPrintOneFile];
};
ButtonGenTest: Commander.CommandProc ~ {
italic: IOTioga.Looks ~ IOTioga.LooksFromRope["i"];
bold: IOTioga.Looks ~ IOTioga.LooksFromRope["b"];
keyPostfix: ATOM ~ $Postfix;
purplePostfix: REF ~ NodeProps.DoSpecs[keyPostfix, "0.75 1 0.9 textColor"];
purpleProps: IOTioga.PropList ~ IOTioga.PropPut[NIL, $Postfix, purplePostfix];
IO.PutRope[cmd.out, "Here is some "];
IOTioga.AddLooks[cmd.out, italic];
IO.PutRope[cmd.out, "italic"];
IOTioga.RemoveLooks[cmd.out, italic];
IO.PutRope[cmd.out, " and "];
IOTioga.AddLooks[cmd.out, bold];
IO.PutRope[cmd.out, "bold"];
IOTioga.RemoveLooks[cmd.out, bold];
IO.PutRope[cmd.out, " text, and a "];
IOTioga.SetCharProps[cmd.out, purpleProps];
IO.PutRope[cmd.out, "purple"];
IOTioga.SetCharProps[cmd.out, NIL];
IO.PutRope[cmd.out, " word.\n"];
};
Commander.Register["ButtonLS", ButtonListCommand, listDoc];
Commander.Register["PlainLS", ListCommand, listDoc];
Commander.Register["ButtonGenTest", ButtonGenTest];
END.