CommanderBasicCommandsImpl.mesa
Copyright Ó 1989, 1990, 1991, 1993 by Xerox Corporation. All rights reserved.
Pier, February 3, 1989 4:20:59 pm PST
Michael Plass, July 14, 1992 9:16 am PDT
JKF, May 29, 1990 12:49:20 pm PDT
Willie-s, January 15, 1993 5:40 pm PST
Chauser, July 16, 1991 9:38 am PDT
Last tweaked by Mike Spreitzer May 29, 1992 10:53 am PDT
Russ Atkinson (RRA) October 7, 1992 7:55 pm PDT
DIRECTORY
Atom USING [DottedPair, GetPName, MakeAtom, PropList, PutPropOnList],
BasicTime USING [GetClockPulses, GMT, Now, Period, Pulses, PulsesToMicroseconds, PulsesToSeconds],
Commander,
CommanderBackdoor,
CommanderOps,
CommanderRegistry USING [ EnumeratePattern ],
Convert,
ConvertUnsafe USING [ToRope],
IO,
List,
Process USING [CheckForAbort, GetPriority, Pause, Priority, priorityBackground, priorityForeground, priorityNormal, SecondsToTicks, SetPriority],
ProcessProps USING [GetPropList],
RefText USING [TrustTextAsRope],
Rope,
RopeList USING [IgnoreCase, Reverse, Sort],
SymTab,
SystemVersion USING [release];
CommanderBasicCommandsImpl: CEDAR PROGRAM
IMPORTS Atom, BasicTime, Commander, CommanderBackdoor, CommanderOps, CommanderRegistry, Convert, ConvertUnsafe, IO, List, Process, ProcessProps, Rope, RefText, RopeList, SymTab, SystemVersion
~ BEGIN
ProcAny: TYPE = PROC ANY RETURNS ANY;
ROPE: TYPE ~ Rope.ROPE;
AliasCell: TYPE = REF AliasCellObject;
AliasCellObject: TYPE = RECORD [
args: LIST OF ROPE ¬ NIL,
def: ROPE,
quiet: BOOL ¬ FALSE, -- don't expand the alias at command time
originalProc: Commander.CommandProc ¬ NIL
];
Command Procedures
AbortCommand: Commander.CommandProc = { ERROR ABORTED };
AliasCommand: Commander.CommandProc = {
commandLineStream: IO.STREAM = IO.RIS[cmd.commandLine];
quiet: BOOL ~ FALSE;
name, def: ROPE;
args: LIST OF ROPE ¬ NIL;
aliasCell: AliasCell;
originalProc: Commander.CommandProc ¬ NIL;
{
ENABLE {
IO.EndOfStream, IO.Error => GOTO Die;
};
name ¬ GetWord[commandLineStream];
[] ¬ IO.SkipWhitespace[commandLineStream, FALSE];
IF NOT IO.EndOf[commandLineStream] AND IO.PeekChar[commandLineStream] = '( THEN {
FOR l: LIST OF REF ANY ¬ NARROW[IO.GetRefAny[commandLineStream]], l.rest UNTIL l = NIL DO
WITH l.first SELECT FROM
r: ROPE => args ¬ CONS[r, args];
a: ATOM => args ¬ CONS[Atom.GetPName[a], args];
ENDCASE => GOTO Die;
ENDLOOP;
TRUSTED {args ¬ RopeList.Reverse[args]; };
};
def ¬ RopeSubst[old: "\n", new: " ", base: IO.GetRope[commandLineStream]];
IF Rope.Equal[GetWord[IO.RIS[def]], name, FALSE] THEN { -- recursive definition
procData: Commander.CommandProcHandle ¬ Commander.Lookup[name];
IF procData # NIL
THEN originalProc ¬ procData.proc
ELSE RETURN[$Failure, "undefined recursive alias"];
};
aliasCell ¬ NEW[AliasCellObject ¬ [args, def, quiet, originalProc]];
Commander.Register[
key: name,
proc: AliasImplProc,
doc: Rope.Concat["Alias ", cmd.commandLine],
clientData: aliasCell];
IO.Close[commandLineStream];
EXITS Die => CommanderOps.Failed["bad parameter list for alias definition"];
};
};
AliasImplProc: Commander.CommandProc = {
aliasCell: AliasCell ¬ NARROW[cmd.procData.clientData];
aliasMode: REF ← CommanderOps.GetProp[cmd, $AliasMode];
newCommandLine: ROPE ¬ aliasCell.def;
commandLineStream: IO.STREAM = IO.RIS[cmd.commandLine];
SynonymRecord: TYPE = RECORD [key, val: ROPE];
synonyms: LIST OF REF SynonymRecord ¬ NIL;
gennum: CARD ¬ 1;
UniqueRope: PROC RETURNS [ROPE] = {
gennum ¬ gennum + 1;
RETURN[Rope.Cat["\200", Convert.RopeFromInt[gennum, 10, FALSE], "\201"]];
};
FOR l: LIST OF ROPE ¬ aliasCell.args, l.rest UNTIL l = NIL DO
token: CommanderOps.Token ~ CommanderOps.GetCmdToken[commandLineStream];
new: ROPE ¬ token.literal;
IF aliasCell.args.rest # NIL THEN {
e.g. args are (x y) and substituting y gorp.
dummy: ROPE = UniqueRope[];
synonyms ¬ CONS[NEW[SynonymRecord ¬ [new, dummy]], synonyms];
new ¬ dummy;
};
newCommandLine ¬ RopeSubst[old: l.first, new: new, base: newCommandLine];
ENDLOOP;
FOR l: LIST OF REF SynonymRecord ¬ synonyms, l.rest UNTIL l = NIL DO
newCommandLine ¬ RopeSubst[old: l.first.val, new: l.first.key, base: newCommandLine];
ENDLOOP;
newCommandLine ¬ Rope.Concat[newCommandLine, IO.GetRope[commandLineStream]];
IO.Close[commandLineStream];
IF aliasCell.originalProc # NIL
THEN {
s: IO.STREAM = IO.RIS[newCommandLine];
cmd.command ¬ GetWord[s];
cmd.commandLine ¬ IO.GetTokenRope[s, NewlineBreak].token;
[result, msg] ¬ aliasCell.originalProc[cmd];
}
ELSE result ¬ CommanderOps.DoCommand[newCommandLine, cmd];
};
AliasesCommand: Commander.CommandProc = {
matchList: LIST OF ROPE ¬ NIL;
EachCommand: Commander.EnumerateAction = {
[key: ROPE, procData: Commander.CommandProcHandle] RETURNS [stop: BOOL ← FALSE]
WITH procData.clientData SELECT FROM
aliasCell: AliasCell => {
matchList ¬ CONS[key, matchList];
};
ENDCASE => NULL;
};
[] ¬ Commander.Enumerate[EachCommand];
matchList ¬ RopeList.Sort[list: matchList, compareProc: RopeList.IgnoreCase];
FOR each: LIST OF ROPE ¬ matchList, each.rest WHILE each # NIL DO
name: ROPE ¬ each.first;
procData: Commander.CommandProcHandle ¬ Commander.Lookup[name];
WITH procData.clientData SELECT FROM
aliasCell: AliasCell => {
IF NOT Rope.Match["(formerly *)*", procData.doc] THEN IO.PutRope[cmd.out, procData.doc]; -- there are already newlines there
};
ENDCASE => NULL;
ENDLOOP;
};
GetWord: PROC [s: IO.STREAM] RETURNS [r: ROPE] ~ { r ¬ CommanderOps.GetCmdToken[s].value };
RopeMemb: PROC [rope: ROPE, lst: LIST OF ROPE] RETURNS[BOOL] = {
FOR l: LIST OF ROPE ¬ lst, l.rest UNTIL l = NIL DO
IF Rope.Equal[rope, l.first, FALSE] THEN RETURN[TRUE];
ENDLOOP;
RETURN[FALSE]
};
AnswerbackCommand: PROC [cmd: Commander.Handle] RETURNS [result: REF ANYNIL, msg: ROPENIL] --Commander.CommandProc-- = {
s: IO.STREAMIO.RIS[cmd.commandLine];
result ← s.GetRefAny[ ! IO.EndOfStream => CONTINUE];
IF result # NIL THEN msg ← s.GetLineRope[];
s.Close[];
};
CommentCommand: Commander.CommandProc = {
};
CommanderCommand: Commander.CommandProc = {
child: Commander.Handle ~ CommanderOps.CreateFromStreams[parentCommander: cmd];
add: Atom.PropList ¬ NIL;
DO
keyRope: ROPE ~ CommanderOps.NextArgument[cmd];
val: ROPE ~ CommanderOps.NextArgument[cmd];
IF keyRope = NIL THEN EXIT;
add ¬ Atom.PutPropOnList[propList: add, prop: Atom.MakeAtom[keyRope], val: val];
ENDLOOP;
UNTIL add = NIL DO
t: Atom.PropList ~ add;
add ¬ t.rest;
t.rest ¬ child.propertyList;
child.propertyList ¬ t;
ENDLOOP;
IF CommanderOps.ReadEvalPrintLoop[child].hadFailure THEN result ¬ $Failure;
};
DateCommand: Commander.CommandProc = {
IO.PutF1[cmd.out, "%g\n", [time[BasicTime.Now[]]]];
};
EchoCommand: Commander.CommandProc = {
FirstArg : BOOLEAN ¬ TRUE;
nFlag : BOOLEAN ¬ TRUE;
sFlag : BOOLEAN ¬ TRUE;
argv: CommanderOps.ArgumentVector ¬ CommanderOps.Parse[cmd];
FOR i: NAT IN [1..argv.argc) DO
arg : ROPE ¬ argv[i];
FirstArg ¬ FALSE;
SELECT TRUE FROM
Rope.Equal[arg, "-n", TRUE] => nFlag ¬ ~ nFlag;
Rope.Equal[arg, "-s", TRUE] => sFlag ¬ ~ sFlag;
ENDCASE => {
IF sFlag AND ~ FirstArg THEN cmd.out.PutChar[' ];
cmd.out.PutRope[argv[i]];
};
Process.CheckForAbort[];
ENDLOOP;
IF nFlag THEN cmd.out.PutChar['\n];
};
ErrorCommand: Commander.CommandProc = { ERROR };
FailCommand: Commander.CommandProc = {
result ¬ $Failure;
msg ¬ cmd.commandLine;
IF Rope.Match[" *\n", msg] THEN msg ¬ Rope.Substr[msg, 1, Rope.Size[msg]-2];
};
FailSkipCommand: Commander.CommandProc = {
result ¬ CommanderOps.GetProp[cmd, $Result];
IF result = $Failure THEN {
IF Rope.SkipOver[s: cmd.commandLine, skip: " \t\r\l"] = Rope.Size[cmd.commandLine]
THEN result ¬ NIL
ELSE msg ¬ Rope.Concat["Skipped: ", cmd.commandLine];
RETURN
};
[result: result] ¬ CommanderOps.ExecuteCommand[cmd, cmd.commandLine];
};
IsCommandPrefix: PROC [prefix, command: ROPE] RETURNS [is: BOOL] = {
pLen: INT = prefix.Size[];
cLen: INT = command.Size[];
c: CHAR;
first, afterLast: INT ¬ 0;
FOR afterLast ¬ 0, afterLast+1
WHILE afterLast < cLen AND (c ¬ command.Fetch[afterLast]) # ' DO
SELECT c FROM
'/, '> => first ¬ afterLast+1;
ENDCASE;
ENDLOOP;
is ¬ pLen <= afterLast-first AND prefix.Equal[command.Substr[start: first, len: pLen], FALSE];
};
HistoryCommand: Commander.CommandProc = {
prompt: ROPE = NARROW[List.Assoc[key: $Prompt, aList: cmd.propertyList]];
toGive: LIST OF ROPE ¬ NIL;
includeDuplicates: BOOL ¬ TRUE;
includePrompt: BOOL ¬ TRUE;
n: INT ¬ 10;
seen: SymTab.Ref;
commandToolData: CommanderBackdoor.CommandToolData ¬ CommanderBackdoor.GetCommandToolData[cmd];
h: CommanderBackdoor.CommandHistoryList ¬ commandToolData.history;
cls: IO.STREAM = IO.RIS[cmd.commandLine];
FOR i: INT ¬ cls.SkipWhitespace[], cls.SkipWhitespace[] WHILE NOT cls.EndOf[] DO
toke: ROPE = cls.GetTokenRope[IO.IDProc].token;
SELECT TRUE FROM
toke.Equal["-%"] => includePrompt ¬ FALSE;
toke.Equal["+%"] => includePrompt ¬ TRUE;
toke.Equal["-d"] => includeDuplicates ¬ FALSE;
toke.Equal["+d"] => includeDuplicates ¬ TRUE;
ENDCASE => n ¬ Convert.IntFromRope[toke !Convert.Error => GOTO GiveUsage];
ENDLOOP;
cls.Close[];
IF NOT includeDuplicates THEN seen ¬ SymTab.Create[case: FALSE];
WHILE n > 0 DO
cr: ROPE;
IF h = NIL THEN EXIT;
cr ¬ h.first.wholeCommandLine;
IF NOT Rope.Equal[cr, ""] THEN {
IF includeDuplicates OR NOT seen.Fetch[cr].found THEN {
toGive ¬ CONS[cr, toGive];
IF NOT includeDuplicates THEN [] ¬ seen.Insert[cr, $seen];
n ¬ n - 1;
};
};
h ¬ h.rest;
ENDLOOP;
FOR toGive ¬ toGive, toGive.rest WHILE toGive # NIL DO
IF includePrompt THEN cmd.out.PutRope["% "];
cmd.out.PutF1["%g\n", [rope[toGive.first]]];
ENDLOOP;
result ¬ $OK;
EXITS
GiveUsage => {result ¬ $Failure; msg ¬ "Usage: History <number>"}
};
IndentCommand: Commander.CommandProc = {
prefix: REF TEXT;
terminator: ROPE ¬ NIL;
terminatorSize: NAT ¬ 0;
line: REF TEXT ¬ NEW[TEXT[200]];
argv: CommanderOps.ArgumentVector ¬ CommanderOps.Parse[cmd];
IF argv.argc > 2 OR (argv.argc < 2 AND cmd.procData.clientData # $Indent) THEN {msg ¬ Rope.Concat["Usage: ", cmd.procData.doc]; GO TO oops};
SELECT cmd.procData.clientData FROM
$Indent => {
depth: INT;
depthNAT: NAT;
IF argv.argc < 2
THEN {
depth ¬ 8;
depthNAT ¬ 8;
}
ELSE {
depth ¬ Convert.IntFromRope[argv[1] ! Convert.Error => {msg ¬ "Bad arg"; GO TO oops}];
IF depth NOT IN [0..80] THEN {msg ¬ "depth should be in [0..80]"; GO TO oops};
depthNAT ¬ depth;
};
prefix ¬ NEW[TEXT[depthNAT]];
FOR i: NAT IN [0..depthNAT) DO prefix[i] ¬ ' ; ENDLOOP;
prefix.length ¬ depthNAT;
};
$PrefixLines => { prefix ¬ Rope.ToRefText[argv[1]] };
$ReadTo => { terminator ¬ argv[1]; terminatorSize ¬ Rope.Size[terminator] };
ENDCASE => ERROR;
DO
IF cmd.in.EndOf[ ! IO.Error => EXIT] THEN EXIT;
line ¬ cmd.in.GetLine[buffer: line !
IO.EndOfStream => GOTO Finished;
IO.Error => EXIT;
IO.Rubout => GOTO Finished;
];
IF terminator # NIL AND Rope.Equal[terminator, RefText.TrustTextAsRope[line]] THEN EXIT;
IF prefix # NIL THEN cmd.out.PutBlock[block: prefix ! IO.Error => EXIT];
cmd.out.PutBlock[block: line ! IO.Error => EXIT];
cmd.out.PutChar['\n ! IO.Error => EXIT];
ENDLOOP;
EXITS
Finished => NULL;
oops => result ¬ $Failure;
};
PriorityCommand: Commander.CommandProc = TRUSTED {
old: Process.Priority ~ Process.GetPriority[];
new: Process.Priority ~ SELECT cmd.procData.clientData FROM
$Foreground => Process.priorityForeground,
$Background => Process.priorityBackground,
$Normal => Process.priorityNormal,
ENDCASE => ERROR;
Process.SetPriority[new];
[result: result, msg: msg] ¬ CommanderOps.ExecuteCommand[cmd, cmd.commandLine !
UNWIND => Process.SetPriority[old];
];
Process.SetPriority[old];
};
docPropCommand: ROPE ~ "print a property value, providing a default
args: [ switch ] propname [ defaultvalue ]
switches: -q => always quote; -s => never quote; -f => fail if property is undefined";
SpecialChar: Rope.ActionType ~ {
RETURN [SELECT c FROM IN ['a..'z], IN ['A..'Z], IN ['0..'9], '/, '*, '. => FALSE ENDCASE => TRUE ]
};
PrevResultCommand: PROC [cmd: Commander.Handle] RETURNS [result: REF ANYNIL, msg: ROPENIL] --Commander.CommandProc-- = {
result ← List.Assoc[$Result, cmd.propertyList];
msg ← IO.PutFR1["previous Result = %g", IO.refAny[result]];
};
ResultOfCommand: PROC [cmd: Commander.Handle] RETURNS [result: REF ANYNIL, msg: ROPENIL] --Commander.CommandProc-- = {
result ← CommanderOps.DoCommand[cmd.commandLine, cmd];
msg ← IO.PutFR1["Result = %g", IO.refAny[result]];
};
PropCommand: Commander.CommandProc ~ {
n: NAT ¬ 0;
arg: ROPE ← CommanderOps.NextArgument[cmd];
v: REF ¬ NIL;
quote: [0..2] ¬ 1;
demand: BOOL ¬ FALSE;
value: ROPE ¬ NIL;
default: ROPE ¬ NIL;
WHILE Rope.Match["-*", arg] DO
FOR i: INT IN (0..Rope.Size[arg]) DO
SELECT Rope.Lower[Rope.Fetch[arg, i]] FROM
'q => quote ← 2;
's => quote ← 0;
'f => demand ← TRUE;
ENDCASE => CommanderOps.Failed[cmd.procData.doc];
ENDLOOP;
arg ¬ CommanderOps.NextArgument[cmd];
ENDLOOP;
IF arg = NIL THEN CommanderOps.Failed[cmd.procData.doc];
default ¬ CommanderOps.NextArgument[cmd];
IF CommanderOps.NextArgument[cmd] # NIL THEN CommanderOps.Failed[cmd.procData.doc];
value ¬ default;
v ¬ CommanderOps.GetProp[cmd, Atom.MakeAtom[arg]];
IF v = NIL THEN WITH CommanderOps.GetProp[cmd, $CommandFileArgumentVector] SELECT FROM
argv: CommanderOps.ArgumentVector => {
ENABLE Convert.Error => CONTINUE;
i: INT ~ Convert.IntFromRope[arg];
IF i+1 IN [0..argv.argc) THEN {
v ¬ argv[i+1];
};
};
ENDCASE;
WITH v SELECT FROM
rope: ROPE => value ¬ rope;
atom: ATOM => value ¬ Atom.GetPName[atom];
ENDCASE => {
IF demand THEN CommanderOps.Failed[Rope.Cat["The required property ", arg, IF v = NIL THEN " is not defined." ELSE " is not printable."]];
};
IF quote = 1 THEN {
quote ¬ IF Rope.IsEmpty[value] OR Rope.Map[base: value, action: SpecialChar] THEN 2 ELSE 0;
};
IF quote # 0 THEN value ¬ Convert.RopeFromRope[value, TRUE];
IO.PutRope[cmd.out, value];
IO.PutRope[cmd.out, "\n"];
};
PropertiesCommand: Commander.CommandProc = TRUSTED {
depth: INT ¬ 5;
width: INT ¬ 32;
props: List.AList;
i: NAT ¬ 1;
token: Rope.ROPE;
any: BOOL ¬ FALSE;
verboseFlag: BOOL ¬ FALSE;
key: REF ANY;
argv: CommanderOps.ArgumentVector ¬ CommanderOps.Parse[cmd
! CommanderOps.Failed => { msg ¬ errorMsg; GO TO oops}];
{
IF cmd.procData.clientData = $ProcessProperties
THEN {
props ¬ ProcessProps.GetPropList[];
cmd.out.PutRope["process properties:\n"];
}
ELSE {
props ¬ cmd.propertyList;
cmd.out.PutRope["commander properties:\n"];
};
DO
Process.CheckForAbort[];
IF i >= argv.argc THEN EXIT;
token ¬ argv[i];
i ¬ i + 1;
IF token.Equal["-d", FALSE] THEN {
IF i = argv.argc THEN GOTO BadArgs;
depth ¬ Convert.IntFromRope[argv[i] ! Convert.Error => GOTO BadArgs];
IF width < 0 THEN GOTO BadArgs;
i ¬ i + 1;
LOOP;
};
IF token.Equal["-w", FALSE] THEN {
IF i = argv.argc THEN GOTO BadArgs;
width ¬ Convert.IntFromRope[argv[i] ! Convert.Error => GOTO BadArgs];
IF width < 0 THEN GOTO BadArgs;
i ¬ i + 1;
LOOP;
};
IF token.Equal["-v", FALSE] THEN {
verboseFlag ¬ TRUE;
LOOP;
};
any ¬ TRUE;
IF token.Size[] > 1 AND token.Fetch[0] = '$
THEN key ¬ Atom.MakeAtom[Rope.Substr[base: token, start: 1]]
ELSE key ¬ Atom.MakeAtom[token];
cmd.out.PutRope[token];
cmd.out.PutRope[" = "];
PrintAny[List.Assoc[key: key, aList: props], cmd.out];
cmd.out.PutChar['\n];
ENDLOOP;
IF NOT any THEN {
FOR props ¬ props, props.rest WHILE props#NIL DO
PrintAny[props.first.key, cmd.out];
cmd.out.PutRope[" = "];
PrintAny[props.first.val, cmd.out];
cmd.out.PutRope["\n"];
ENDLOOP;
<<
PrintTV.Print[tv: AMBridge.TVForReferent[NEW[REF ANY ¬ props]], put: cmd.out, depth: depth, width: width, verbose: verboseFlag];>>
cmd.out.PutChar['\n];
};
EXITS
BadArgs => RETURN[$Failure, "Bad args"];
};
EXITS oops => result ¬ $Failure;
};
RedoCommand: Commander.CommandProc = {
commandToolData: CommanderBackdoor.CommandToolData ¬ CommanderBackdoor.GetCommandToolData[cmd];
cls: IO.STREAM = IO.RIS[cmd.commandLine];
trash: INT = cls.SkipWhitespace[];
key: ROPE = IF cls.EndOf[] THEN NIL ELSE cls.GetTokenRope[IO.IDProc].token;
[] ¬ cls.SkipWhitespace[];
IF NOT cls.EndOf[] THEN GOTO GiveUsage;
cls.Close[];
FOR h: CommanderBackdoor.CommandHistoryList ¬ commandToolData.history, h.rest WHILE h # NIL DO
cr: ROPE ¬ h.first.wholeCommandLine;
IF Rope.Equal[cr, ""] THEN LOOP;
IF key = NIL OR IsCommandPrefix[key, cr] THEN
BEGIN
cmd.out.PutF1["%g\n", [rope[cr]]];
[result, msg] ¬ CommanderOps.ExecuteCommand[cmd, cr];
commandToolData.recent.wholeCommandLine ¬ cr;
RETURN [result, msg];
END;
REPEAT
FINISHED => RETURN [$Failure, IO.PutFR1["No command in my history begins with \"%q\"", [rope[key]]]];
ENDLOOP;
EXITS
GiveUsage => {result ¬ $Failure; msg ¬ "Usage: Redo commandNamePrefix"}
};
RegisteredHelpCommand: Commander.CommandProc = {
[cmd: Handle] RETURNS [result: REF ← NIL, msg: ROPE ← NIL]
out: IO.STREAM ¬ cmd.out;
argv: CommanderOps.ArgumentVector ¬ CommanderOps.Parse[cmd: cmd];
IF argv.argc <= 1 THEN {
msg ¬ IO.PutFR1["Usage: %g pattern ...\n", [rope[cmd.command]]];
GO TO oops
};
BEGIN
matchList: LIST OF ROPE ¬ NIL;
prev: ROPE ¬ NIL;
EachCommand: Commander.EnumerateAction = {
[key: ROPE, procData: Commander.CommandProcHandle] RETURNS [stop: BOOL ← FALSE]
FOR i: NAT IN [1..argv.argc) DO
IF Rope.Match[pattern: argv[i], object: key, case: FALSE] THEN {
matchList ¬ CONS[key, matchList];
EXIT;
};
ENDLOOP;
};
pattern: ROPE ¬ IF argv.argc#1 THEN "*" ELSE argv[1];
[] ¬ CommanderRegistry.EnumeratePattern[pattern, EachCommand];
IF matchList = NIL THEN {
Add stars and try again
FOR i: NAT IN [1..argv.argc) DO
rope: ROPE ~ argv[i];
len: INT ~ Rope.Size[argv[i]];
IF len > 0 AND rope.Fetch[len-1] # '* THEN {argv[i] ¬ rope.Concat["*"]};
ENDLOOP;
pattern ¬ IF argv.argc#1 THEN "*" ELSE argv[1];
[] ¬ CommanderRegistry.EnumeratePattern[pattern, EachCommand];
};
IF matchList = NIL THEN {
IO.PutF1[cmd.err, "No matching commands for %g", [rope[cmd.commandLine]] ];
GOTO oops
};
matchList ¬ RopeList.Sort[list: matchList, compareProc: RopeList.IgnoreCase];
FOR each: LIST OF ROPE ¬ matchList, each.rest WHILE each # NIL DO
name: ROPE ¬ each.first;
IF NOT Rope.Equal[s1: name, s2: prev, case: FALSE] THEN {
pData: Commander.CommandProcHandle ¬ Commander.Lookup[name];
prev ¬ name;
IO.PutF[out, "%L%-20g%L", [rope["bf"]], [rope[name]], [rope["BF"]] ];
IF pData = NIL THEN {
Maybe this is a file, really
<<SELECT TRUE FROM
NOT full => IO.PutRope[out, " {documentation not available}\n"];
Rope.Match["*.load*", name, FALSE] => IO.PutRope[out, " {load file}\n"];
Rope.Match["*.cm*", name, FALSE] => IO.PutRope[out, " {command file}\n"];
ENDCASE => IO.PutRope[out, " {documentation not available}\n"];>>
LOOP;
};
{
next: INT ¬ 0;
size: INT ¬ Rope.Size[pData.doc];
IO.PutRope[out, " "];
FOR i: INT ¬ 0, next WHILE i < size DO
next ¬ Rope.SkipTo[pData.doc, i, "\l\r"]+1;
IF next > size OR Rope.Fetch[pData.doc, next-1] = '\n
THEN IO.PutRope[self: out, r: pData.doc, start: i, len: next-i]
ELSE {
IO.PutRope[self: out, r: pData.doc, start: i, len: next-i-1];
IO.PutRope[out, "\n"];
};
IF next < size THEN IO.PutRope[out, " "];
ENDLOOP;
IO.PutRope[out, "\n"];
};
};
ENDLOOP;
END;
EXITS oops => result ¬ $Failure;
};
usageReregisterAs: ROPE = "Give a command a new name. [ -i | -~i ] newname oldname\n -i newname should be interpreted\n -~i newname should be uninterpreted";
ReregisterAsCommand: Commander.CommandProc ~ {
a: ARRAY [0..1] OF ROPE;
i: NAT ¬ 0;
imode: INTEGER ¬ 0;
DO
arg: ROPE ~ CommanderOps.NextArgument[cmd];
IF arg = NIL THEN EXIT;
IF Rope.Match[pattern: "-*", object: arg]
THEN {
SELECT TRUE FROM
Rope.Equal[arg, "-i", FALSE] => imode ¬ 1;
Rope.Equal[arg, "-~i", FALSE] => imode ¬ -1;
ENDCASE => ERROR CommanderOps.Failed[usageReregisterAs];
}
ELSE {
IF i < 2 THEN {a[i] ¬ arg; i ¬ i + 1} ELSE ERROR CommanderOps.Failed[usageReregisterAs];
};
ENDLOOP;
IF i # 2 THEN GOTO Fail ELSE {
old: Commander.CommandProcHandle ¬ Commander.Lookup[a[1]];
IF old = NIL THEN ERROR CommanderOps.Failed[Rope.Concat[a[1], " not found"]];
Commander.Register[key: a[0], proc: old.proc, doc: Rope.Cat["(formerly ", a[1], ") ", old.doc], clientData: old.clientData, interpreted: SELECT imode FROM -1 => FALSE, 0 => old.interpreted, 1 => TRUE, ENDCASE => ERROR];
};
EXITS Fail => ERROR CommanderOps.Failed[usageReregisterAs];
};
SetPropertyCommand: Commander.CommandProc = {
aList: List.AList;
key: REF ANY;
spec: RECORD [val: {list, rope}, place: {process, tool}] ~ SELECT cmd.procData.clientData FROM
$ProcessProperties => [rope, process],
$ListProcessProperties => [list, process],
$List => [list, tool],
ENDCASE => [rope, tool];
argv: CommanderOps.ArgumentVector ¬ CommanderOps.Parse[cmd];
IF spec.place=process
THEN aList ¬ ProcessProps.GetPropList[]
ELSE aList ¬ cmd.propertyList;
IF spec.val=rope AND argv.argc # 3
THEN CommanderOps.Failed["Usage: SetProperty key value\n"]
ELSE {
dp: Atom.DottedPair;
val: REF ANY;
IF argv[1].Size[] > 1 AND argv[1].Fetch[0] = '$
THEN key ¬ Atom.MakeAtom[Rope.Substr[base: argv[1], start: 1]]
ELSE key ¬ Atom.MakeAtom[argv[1]];
SELECT spec.val FROM
rope => val ¬ IF Rope.Equal[argv[2], "NIL"] THEN NIL ELSE argv[2];
list => {
rl: LIST OF Rope.ROPE ¬ NIL;
FOR i: NAT DECREASING IN [2..argv.argc) DO
rl ¬ CONS[IF Rope.Equal[argv[i], "NIL"] THEN NIL ELSE argv[i], rl]
ENDLOOP;
val ¬ rl};
ENDCASE => ERROR;
dp ¬ FindLastProp[key: key, aList: aList];
IF dp # NIL
THEN dp.val ¬ val
ELSE [] ¬ List.PutAssoc[key: key, val: val, aList: aList];
};
};
ShiftCommand: Commander.CommandProc = {
result ¬ CommanderOps.DoCommand[commandLine: cmd.commandLine, parent: cmd];
};
SleepCommand: Commander.CommandProc = {
seconds: CARD;
argv: CommanderOps.ArgumentVector ¬ CommanderOps.Parse[cmd];
IF argv.argc # 2 THEN {msg ¬ "Usage: Sleep seconds"; GO TO oops};
seconds ¬ Convert.CardFromRope[argv[1] ! Convert.Error => {msg ¬ "Bad arg"; GO TO oops}];
Process.Pause[Process.SecondsToTicks[seconds]];
EXITS oops => result ¬ $Failure;
};
TeeCommand: Commander.CommandProc = {
fileStream: IO.STREAM ← NIL;
block: REF TEXT ← NEW[TEXT[512]];
{
ENABLE UNWIND => IF fileStream # NIL THEN fileStream.Close[];
argv: CommanderOps.ArgumentVector ← CommanderOps.Parse[cmd, FALSE
! CommanderOps.Failed => {msg ← errorMsg; GO TO oops}];
IF argv.argc > 2 THEN {msg ← "Usage: Tee filename"; GO TO oops};
IF argv.argc = 2 THEN
fileStream ← UXIO.CreateFileStream[name: argv[1], access: write
! UXIO.Error => IF error.group # $bug THEN {msg ← error.explanation; GO TO oops}];
DO
count: NAT;
block.length ← 0;
IF cmd.in.EndOf[] THEN EXIT;
count ← cmd.in.GetBlock[block: block !
IO.EndOfStream => EXIT;
IO.Error => EXIT;
IO.Rubout => EXIT;
];
IF count = 0 THEN EXIT;
IF fileStream # NIL THEN fileStream.PutBlock[block: block ! IO.Error => EXIT];
cmd.out.PutBlock[block: block ! IO.Error => EXIT];
Process.CheckForAbort[];
ENDLOOP;
fileStream.Close[ ! IO.Error => CONTINUE];
};
EXITS oops => RETURN[$Failure, msg];
};
TimeCommand: Commander.CommandProc = {
[cmd: Commander.Handle] RETURNS [result: REF ANY ← NIL, msg: ROPE ← NIL]
elapsedPulses: BasicTime.Pulses ¬ 0;
seconds: CARD ¬ 0;
startTime: BasicTime.GMT ¬ BasicTime.Now[];
startPulses: BasicTime.Pulses ¬ BasicTime.GetClockPulses[];
result ¬ CommanderOps.DoCommand[commandLine: cmd.commandLine, parent: cmd];
elapsedPulses ¬ BasicTime.GetClockPulses[] - startPulses;
seconds ¬ BasicTime.Period[from: startTime, to: BasicTime.Now[]];
IF seconds >= 10000
THEN {
Long time, so don't trust the fine grain
IO.PutF[cmd.out, "Running time: %g seconds (%r)\n", [cardinal[seconds]], [cardinal[seconds]]];
}
ELSE {
Short time, so use the more exact number
real: REAL ¬ BasicTime.PulsesToSeconds[elapsedPulses];
microseconds: CARD ¬ BasicTime.PulsesToMicroseconds[elapsedPulses];
IF real < 60.0
THEN IO.PutF1[cmd.out, "Running time: %g seconds\n", [real[real]]]
ELSE {
seconds ¬ microseconds / 1000000;
microseconds ¬ microseconds MOD 1000000;
IO.PutF[cmd.out, "Running time: %g seconds (%r.%03g)\n", [real[real]], [cardinal[seconds]], [cardinal[microseconds / 1000]]];
};
};
};
UnregisterCommand: Commander.CommandProc = {
procData: Commander.CommandProcHandle;
n: INT ¬ 0;
FOR arg: ROPE ¬ CommanderOps.NextArgument[cmd], CommanderOps.NextArgument[cmd] UNTIL arg = NIL DO
procData ¬ Commander.Lookup[arg];
IF procData # NIL AND procData.proc # NIL
THEN Commander.Register[key: arg, proc: NIL, doc: NIL]
ELSE CommanderOps.Failed[IO.PutFR1["Unregister: %g not found\n", [rope[arg]]]];
n ¬ n + 1;
ENDLOOP;
IF n = 0 THEN { result¬$Failure; msg ¬ cmd.procData.doc };
};
ControlCommand: Commander.CommandProc = {
commandToolData: CommanderBackdoor.CommandToolData ~ CommanderBackdoor.GetCommandToolData[cmd];
arg: ROPE ¬ CommanderOps.NextArgument[cmd];
sense: BOOL ¬ TRUE;
toggle: BOOL ¬ FALSE;
query: BOOL ¬ FALSE;
local: BOOL ¬ FALSE;
Do: PROC [old: BOOL] RETURNS [BOOL] = INLINE {
IF query THEN {
WITH cmd.procData.clientData SELECT FROM
atom: ATOM => IO.PutRope[cmd.out, Atom.GetPName[atom]];
ENDCASE => ERROR;
IO.PutRope[cmd.out, IF old THEN " on\n" ELSE " off\n"];
RETURN[old];
};
RETURN[IF toggle THEN NOT old ELSE sense];
};
IF Rope.Equal[arg, "locally", FALSE] THEN {
local ¬ TRUE;
arg ¬ CommanderOps.NextArgument[cmd];
};
SELECT TRUE FROM
CommanderOps.NextArgument[cmd]# NIL => GOTO Usage;
(commandToolData = NIL) => NULL;
(arg = NIL) => sense ¬ TRUE;
(Rope.Equal[arg, "on", FALSE]) => sense ¬ TRUE;
(Rope.Equal[arg, "off", FALSE]) => sense ¬ FALSE;
(Rope.Equal[arg, "toggle", FALSE]) => toggle ¬ TRUE;
(Rope.Equal[arg, "?", FALSE]) => query ¬ TRUE;
ENDCASE => GOTO Usage;
FOR data: CommanderBackdoor.CommandToolData ¬ commandToolData, CommanderBackdoor.GetCommandToolData[data.parentCommander] UNTIL data = NIL DO
SELECT cmd.procData.clientData FROM
$Verbose => data.verbose ¬ Do[commandToolData.verbose];
$Terse => data.verbose ¬ NOT Do[NOT commandToolData.verbose];
$Statistics => data.statistics ¬ Do[commandToolData.statistics];
$HistoryLog => data.keepHistory ¬ Do[commandToolData.keepHistory];
ENDCASE => ERROR;
IF query OR local THEN EXIT;
ENDLOOP;
EXITS Usage => ERROR CommanderOps.Failed[cmd.procData.doc];
};
Utilities
FindLastProp: PROC [key: REF, aList: List.AList] RETURNS [dp: Atom.DottedPair ¬ NIL] = {
UNTIL aList = NIL DO
IF aList.first.key = key THEN dp ¬ NARROW[aList.first];
aList ¬ aList.rest;
ENDLOOP;
RETURN[dp];
};
PrintAny: PROC [any: REF ANY, out: IO.STREAM] ~ {
IF any=NIL THEN out.PutRope["NIL"]
ELSE WITH any SELECT FROM
x: ATOM => out.PutF1["$%g", [atom[x]]];
x: Rope.ROPE => out.PutF1["\"%q\"", [rope[x]]];
x: LIST OF Rope.ROPE => {
out.PutRope["RopeList["];
FOR y: LIST OF Rope.ROPE ¬ x, y.rest WHILE y#NIL DO
out.PutF1["\"%q\"", [rope[y.first]]];
IF y.rest#NIL THEN out.PutRope[", "];
ENDLOOP;
out.PutRope["]"]};
x: LIST OF REF ANY => {
out.PutRope["LIST["];
FOR y: LIST OF REF ANY ¬ x, y.rest WHILE y#NIL DO
PrintAny[y.first, out];
IF y.rest#NIL THEN out.PutRope[", "];
ENDLOOP;
out.PutRope["]"]};
ENDCASE => { IO.PutF1[out, "%g", [refAny[any]]] };
RETURN;
};
VersionCommand: Commander.CommandProc = TRUSTED {
ENABLE Convert.Error => CommanderOps.Failed[cmd.procData.doc];
arg: ROPE ~ CommanderOps.NextArgument[cmd];
PutVersion[cmd.out, IF arg = NIL THEN 2 ELSE Convert.IntFromRope[arg]];
};
MapCommand: Commander.CommandProc = {
[cmd: Handle] RETURNS [result: REFNIL, msg: ROPENIL]
CommandObject = [
in, out, err: STREAM, commandLine, command: ROPE,
propertyList: List.AList, procData: CommandProcHandle]
out: IO.STREAM = cmd.out;
SwitchIndex: TYPE = CHAR['a..'z];
switches: PACKED ARRAY SwitchIndex OF BOOLALL[FALSE];
debug: BOOLFALSE;
args: LIST OF ROPE ← CommanderOps.ParseToList[cmd
! CommanderOps.Failed => {msg ← errorMsg; GO TO oops}].list;
zorch: ROPENIL; -- holds the command to map
nTried: INT ← 0;
nFailed: INT ← 0;
WHILE args # NIL DO
arg: ROPE = args.first;
argLen: INT = Rope.Length[arg];
args ← args.rest;
IF Rope.Match["-*", arg] THEN {
sense: BOOLTRUE;
Digit: TYPE = [0..9];
FOR j: NAT IN [1..Rope.Length[arg]) DO
c: CHAR = Rope.Fetch[arg, j];
SELECT c FROM
IN ['A..'Z] => switches[c + ('a-'A)] ← sense;
IN ['a..'z] => switches[c] ← sense;
'~ => {sense ← NOT sense; LOOP};
ENDCASE;
sense ← TRUE;
ENDLOOP;
LOOP;
};
IF Rope.SkipTo[arg, 0, "*"] < argLen AND switches['x] THEN {
Expand the star pattern and process the arguments
IF zorch = NIL THEN {
msg ← "Star expansion in command position not allowed.\n";
GO TO oops;
};
};
IF zorch # NIL THEN {
line: ROPE = Rope.Concat[zorch, arg];
eachResult: REF = CommanderOps.DoCommand[line, cmd];
nTried ← nTried + 1;
result ← eachResult;
IF eachResult = $Failure
THEN {
nFailed ← nFailed + 1;
IF switches['q] THEN GO TO oops;
}
ELSE
IF switches['s] THEN RETURN;
LOOP;
};
zorch ← Rope.Concat[arg, " "];
ENDLOOP;
IF switches['a] AND nFailed # 0 THEN GO TO oops;
IF switches['e] AND nFailed = nTried THEN GO TO oops;
EXITS oops => result ← $Failure;
};
PutVersion: PROC [out: IO.STREAM, n: INT] = {
P: PROC [i: INT] ~ { IF n > 0 THEN IO.PutF1[out, ".%g", [integer[i]]]; n ← n - 1 };
IO.Put1[out, [integer[SystemVersion.release.major]]]; n ← n - 1;
P[SystemVersion.release.minor];
P[SystemVersion.release.patch];
IO.PutRope[out, "\n"];
};
RopeSubst: PROC [old, new, base: ROPE, case: BOOL ¬ FALSE, allOccurrences: BOOL ¬ TRUE] RETURNS [ROPE] = {
if old is not found in base, then value = base.
if allOccurrences THEN substitute for each occurrence of old, otherwise only for first.
lenOld: INT = Rope.Size[old];
lenNew: INT = Rope.Size[new];
i: INT ¬ 0;
WHILE (i ¬ Rope.Find[s1: base, s2: old, case: case, pos1: i]) # -1 DO
base ¬ Rope.Replace[base: base, start: i, len: lenOld, with: new];
IF NOT allOccurrences THEN EXIT;
i ¬ i + lenNew;
ENDLOOP;
RETURN[base];
};
NewlineBreak: IO.BreakProc = {
IF char = '\l OR char = '\r THEN RETURN[break];
RETURN[other];
};
Registration
Init: PROC = {
Register: PROC [name: LONG STRING, proc: Commander.CommandProc, doc: LONG STRING, data: REF ¬ NIL, un: BOOL ¬ FALSE] = {
Commander.Register[ConvertUnsafe.ToRope[name], proc, ConvertUnsafe.ToRope[doc], data, NOT un];
};
Register["GetProperties", PropertiesCommand, "Display command tool property list"];
Register["GetProcessProperties", PropertiesCommand, "Display process properties list", $ProcessProperties];
Register["SetProperty", SetPropertyCommand, "SetProperty propertyName value - change or add to the command tool property list"];
Register["SetProcessProperty", SetPropertyCommand, "SetProperty propertyName value - change or add to the process property list", $ProcessProperties];
Register["SetListProperty", SetPropertyCommand, "SetProperty propertyName elt elt ... - bind propertyName in the command tool property list to the LIST OF ROPE = [elt, elt, ...]", $List];
Register["SetListProcessProperty", SetPropertyCommand, "bind propertyName in the process property list to the LIST OF ROPE = [elt, elt, ...]", $ListProcessProperties];
Register["-", CommentCommand, "Comment", NIL, TRUE];
Register["--", CommentCommand, "Comment", NIL, TRUE];
Register["/", CommentCommand, "Comment", NIL, TRUE];
Register["//", CommentCommand, "Comment", NIL, TRUE];
Register["?", RegisteredHelpCommand, "List registered commands"];
Register["Abort", AbortCommand, "Abort -- Raises ABORTED"];
Register["Alias", AliasCommand, "Define a new command in terms of others, using blind string-matching for parameters.", NIL, TRUE];
Register["Aliases", AliasesCommand, "List all Aliases"];
Register["Answerback", AnswerbackCommand, "reads result and msg from command line"];
Register["Date", DateCommand, "Print date and time"];
Register["Echo", EchoCommand, "Print command line arguments\n\t-s supress whitespace\n\t-n no newline"];
Register["ERROR", ErrorCommand, "Error -- Raises unnamed ERROR"];
Register["Fail", FailCommand, "This command always fails"];
Register[";", FailSkipCommand, "Skip on failure"];
Register["Help", RegisteredHelpCommand, "List registered commands"];
Register["Indent", IndentCommand, "Indent n -- Indent n spaces", $Indent];
Register["PrefixLines", IndentCommand, "PrefixLines <string> - prefix each line of stdin with <string>", $PrefixLines];
Register["PrevResult", PrevResultCommand, "- print the result of the previous command"];
Register["ResultOf", ResultOfCommand, "<command> - print the result of the given command (uninterpreted)", NIL, TRUE];
Register["ReadTo", IndentCommand, "ReadTo <string> - Copy stdin to stdout until a line with just <string> is encountered.", $ReadTo];
Commander.Register["Synonym", ReregisterAsCommand, usageReregisterAs, NIL, TRUE];
Register["Shift", ShiftCommand, "Shift command-line (uninterpreted)", NIL, TRUE];
Register["ShiftInterp", ShiftCommand, "ShiftInterp command-line (interpreted)"];
Register["Command", ShiftCommand, "do command-line as a command"];
Register["Commander", CommanderCommand, "Make a nested commander"];
Register["Sleep", SleepCommand, "Sleep n -- pause for n seconds"];
Register["Time", TimeCommand, "Time command-line (uninterpreted)", NIL, TRUE];
Register["Unregister", UnregisterCommand, "Unregister commands\nUsage: Unregister commandname ..."];
Register["Verbose", ControlCommand, "Control command tracing (inverse of Terse)\nUsage: Verbose [locally] [on|off|toggle|?]", $Verbose];
Register["Terse", ControlCommand, "Control command tracing (inverse of Verbose)\nUsage: Terse [locally] [on|off|toggle|?]", $Terse];
Register["Statistics", ControlCommand, "Control statistics printing\nUsage: Statistics [locally] [on|off|toggle|?]", $Statistics];
Register["HistoryLog", ControlCommand, "Control history logging\nUsage: HistoryLog [locally] [on|off|toggle|?]", $HistoryLog];
Register["Version", VersionCommand, "Print Cedar version number"];
Register["ForegroundPriority", PriorityCommand, "(uninterpreted) Execute rest of command line at foreground priority", $Foreground, FALSE];
Register["BackgroundPriority", PriorityCommand, "(uninterpreted) Execute rest of command line at background priority", $Background, FALSE];
Register["NormalPriority", PriorityCommand, "(uninterpreted) Execute rest of command line at normal priority", $Normal, FALSE];
Commander.Register[key: "Redo", proc: RedoCommand, doc: "Redo <string> — redoes the last command that begins with string"];
Commander.Register[key: "History", proc: HistoryCommand, doc: "History <option>* <n> — lists the last n commands in this CommandTool viewer\n -%  omit % prompt\n +% include % prompt\n -d remove duplicate commands\n +d include duplicates"];
Commander.Register["Prop", PropCommand, docPropCommand];
Commander.Register["Map", MapCommand,
"maps the first argument as a command over the other arguments
-a return failure if any command fails
-e return failure if every command fails
-q quit on failure, return failure
-s quit on success, return success
"];
};
Init[];
END.