InitialCommandsImpl.mesa
L. Stewart, December 20, 1983 2:04 pm
Russ Atkinson, October 5, 1983 12:15 pm
Paul Rovner, November 29, 1983 8:51 am
DIRECTORY
BasicTime USING [FromPupTime, GetClockPulses, Now, Pulses, PulsesToMicroseconds, PulsesToSeconds],
Booting USING [Boot, Checkpoint],
Commander,
CommandExtras USING [MakeUninterpreted],
CommandTool USING [AddProcToList, AddSearchRule, ArgumentVector, DoCommand, Failed, LookupCommand, LookupWithSearchRules, Parse, ParseToList, RemoveProcFromList, StarExpansion],
Convert USING [AppendChar, Error, IntFromRope, RopeFromTime],
EditedStream USING [Rubout],
FileNames USING [ConvertToSlashFormat, CurrentWorkingDirectory, FileWithSearchRules, ResolveRelativePath],
FS USING [EnumerateForNames, Error, NameProc, StreamOpen],
IO USING [card, Close, EndOf, EndOfStream, Error, GetBlock, GetLine, GetLineRope, int, PutBlock, PutChar, PutF, PutFR, PutRope, rope, STREAM, text, time],
List USING [AList, Assoc, CompareProc, Memb, PutAssoc, Sort],
Process USING [Abort, GetCurrent, Pause, SecondsToTicks],
ProcessExtras USING [CheckForAbort],
Real USING [Fix, RealException],
Rope USING [Cat, Compare, Concat, Equal, Fetch, Find, IsEmpty, Length, Match, ROPE, Substr],
SafeStorage,
SystemVersion USING [bootFileDate, release],
TiogaMenuOps USING [Open],
UserCredentials USING [Get],
ViewerClasses USING [Viewer],
ViewerOps USING [CreateViewer],
VMStatistics USING [pageFaults];
InitialCommandsImpl: CEDAR MONITOR
IMPORTS
BasicTime, Booting, Commander, CommandExtras, CommandTool, Convert, EditedStream, FileNames, FS, IO, List, Process, ProcessExtras, Real, Rope, SafeStorage, SystemVersion, TiogaMenuOps, UserCredentials, ViewerOps, VMStatistics =
BEGIN
Date: Commander.CommandProc = {
RETURN[NIL, Convert.RopeFromTime[from: BasicTime.Now[], includeDayOfWeek: TRUE]];
};
User: Commander.CommandProc = {
user: Rope.ROPE ← UserCredentials.Get[].name;
IF user.Length[] = 0 THEN user ← "NIL";
RETURN[NIL, user];
};
Version: Commander.CommandProc = TRUSTED {
cmd.out.PutF["Cedar %g.%g", IO.int[SystemVersion.release.major], IO.int[SystemVersion.release.minor]];
IF SystemVersion.release.patch # 0 THEN cmd.out.PutF[".%g", IO.int[SystemVersion.release.patch]];
cmd.out.PutF[" of %t\n", IO.time[BasicTime.FromPupTime[SystemVersion.bootFileDate]]];
};
Fail: Commander.CommandProc = TRUSTED {
RETURN[$Failure, "This Command Always Fails"];
};
Help: Commander.CommandProc = TRUSTED {
argv: CommandTool.ArgumentVector;
cName: Rope.ROPE;
argv ← CommandTool.Parse[cmd: cmd ! CommandTool.Failed => { msg ← errorMsg; CONTINUE; }];
IF argv = NIL THEN RETURN[$Failure, msg];
cName ← IF argv.argc = 2 THEN argv[1] ELSE "Command";
RETURN[$Failure, Rope.Cat["Help not implemented.\nTry looking on the release documentation directory,\ne.g. /Indigo/Cedar/Documentation/", cName, "Doc.tioga.\n"]];
};
ECell: TYPE = RECORD [name, doc: Rope.ROPE];
EnumerateCommands: Commander.CommandProc = {
[cmd: Handle] RETURNS [result: REF ← NIL, msg: ROPE ← NIL]
el: LIST OF REF ANY;
eCell: REF ECell;
matchList: LIST OF Rope.ROPENIL;
argv: CommandTool.ArgumentVector;
lookupCmd: Commander.Handle ← NEW[Commander.CommandObject];
i: NAT ← 1;
flag: BOOLFALSE;
EProc: PROC [key: Rope.ROPE, procData: Commander.CommandProcHandle] RETURNS [stop: BOOL] = {
ProcessExtras.CheckForAbort[];
IF procData # NIL THEN {
see if we have a match
FOR list: LIST OF Rope.ROPE ← matchList, list.rest WHILE list # NIL DO
IF list.first.Find["*"] = -1 THEN LOOP; -- handled elsewhere
flag ← TRUE;
IF list.first.Match[key, FALSE] THEN {
there is a match with the command name, so add it to the list (once only)
el ← CONS[NEW[ECell ← [name: key, doc: procData.doc]], el];
ProcessExtras.CheckForAbort[];
EXIT;
};
ENDLOOP;
};
RETURN[FALSE];
};
FSProc: FS.NameProc = {
strip off the .load and any version number
commandName: Rope.ROPE ← Rope.Substr[base: fullFName, start: 0, len: Rope.Find[s1: fullFName, pos1: 0, s2: ".load", case: FALSE]];
Linear search the batch already loaded and don't bother with duplicates
FOR list: LIST OF REF ANY ← el, list.rest WHILE list # NIL DO
IF Rope.Equal[commandName, NARROW[list.first, REF ECell].name, FALSE] THEN RETURN[TRUE];
ENDLOOP;
At this point, we've got the fullFName of a new .load file
el ← CONS[NEW[ECell ← [name: commandName, doc: "not yet loaded"]], el];
ProcessExtras.CheckForAbort[];
RETURN[TRUE];
};
MyCompare: List.CompareProc = {
a, b: REF ECell;
a ← NARROW[ref1];
b ← NARROW[ref2];
RETURN[Rope.Compare[a.name, b.name, FALSE]];
};
argv ← CommandTool.Parse[cmd: cmd ! CommandTool.Failed => { msg ← errorMsg; CONTINUE; }];
IF argv = NIL THEN RETURN[$Failure, msg];
First, construct the list of patterns to match against the command names.
DO
match: Rope.ROPENIL;
ProcessExtras.CheckForAbort[];
IF i = argv.argc THEN EXIT;
match ← argv[i];
i ← i + 1;
IF match.IsEmpty[] OR match.Equal["\n"] THEN EXIT;
Preprocess it!
IF match.Equal["*"] THEN { --exactly *
matchList ← LIST["*"];
EXIT;
}
ELSE {
match ← FileNames.ResolveRelativePath[match];
IF match.Fetch[0] # '/ AND match.Fetch[0] # '* AND match.Find["*"] # -1 THEN match ← Rope.Concat["*", match];
IF match.Fetch[match.Length[] - 1] = '/ THEN match ← Rope.Concat[match, "*"];
};
matchList ← CONS[match, matchList];
ENDLOOP;
IF matchList = NIL THEN {
If no patterns given, remind the user to supply at least one.
cmd.out.PutRope["Usage: "];
cmd.out.PutRope[cmd.command];
cmd.out.PutRope[" list-of-patterns ...\n"];
RETURN;
};
First handle any non-star cases using LookupProc
lookupCmd.err ← cmd.out;
FOR list: LIST OF Rope.ROPE ← matchList, list.rest WHILE list # NIL DO
IF list.first.Find["*"] = -1 THEN {
This one has no pattern
lookupCmd.command ← list.first;
lookupCmd.commandLine ← NIL;
lookupCmd.procData ← NIL;
lookupCmd.propertyList ← cmd.propertyList;
CommandTool.LookupCommand[lookupCmd];
IF lookupCmd.procData # NIL THEN {
IF lookupCmd.commandLine # NIL THEN cmd.out.PutRope["(Generic command) "];
cmd.out.PutF["%-20g %g %g\n", IO.rope[lookupCmd.command], IO.rope[lookupCmd.commandLine], IO.rope[lookupCmd.procData.doc]];
}
ELSE {
Try looking for a .load file
loadFileName: Rope.ROPE ← FileNames.FileWithSearchRules[root: list.first, defaultExtension: ".load", requireExtension: TRUE, searchRules: List.Assoc[key: $SearchRules, aList: cmd.propertyList]].fullPath;
commandName: Rope.ROPE ← Rope.Substr[base: loadFileName, start: 0, len: Rope.Find[s1: loadFileName, pos1: 0, s2: ".load", case: FALSE]];
comment: Rope.ROPE ← GetCommentLine[fName: loadFileName];
cmd.out.PutF["%-20g (not loaded yet) %g\n", IO.rope[commandName], IO.rope[comment]];
};
ProcessExtras.CheckForAbort[];
};
ENDLOOP;
[] ← Commander.Enumerate[EProc];
Now search the file system
FOR list: LIST OF Rope.ROPE ← matchList, list.rest WHILE list # NIL DO
IF list.first.Find["*"] = -1 THEN LOOP; -- handled elsewhere
{
withStarExt: Rope.ROPE ← Rope.Concat[list.first, "*.load"];
FS.EnumerateForNames[pattern: withStarExt, proc: FSProc, wDir: NIL];
IF list.first.Fetch[0] # '/ AND list.first.Fetch[0] # '[ THEN {
rules: LIST OF REF ANYNARROW[List.Assoc[key: $SearchRules, aList: cmd.propertyList]];
WHILE rules # NIL DO
FS.EnumerateForNames[pattern: withStarExt, proc: FSProc, wDir: NARROW[rules.first, Rope.ROPE]];
rules ← rules.rest;
ENDLOOP;
};
};
ENDLOOP;
IF el = NIL AND flag = TRUE THEN {
If no matches found, indicate this result explicitly.
RETURN[$Failure, "No matching registered commands."];
};
Now there has been at least one match, so sort the results and display them to the user.
IF el # NIL THEN {
cmd.out.PutRope["Matching registered commands:\n"];
el ← List.Sort[list: el, compareProc: MyCompare];
WHILE el # NIL DO
eCell ← NARROW[el.first];
cmd.out.PutF["%-20g %g\n", IO.rope[eCell.name], IO.rope[eCell.doc]];
el ← el.rest;
ProcessExtras.CheckForAbort[];
ENDLOOP;
};
};
GetCommentLine: PROC [fName: Rope.ROPE] RETURNS [comment: Rope.ROPENIL] = {
loadStream: IO.STREAM;
line: Rope.ROPE;
loadStream ← FS.StreamOpen[fileName: fName ! FS.Error => IF error.group # bug THEN CONTINUE];
IF loadStream = NIL THEN RETURN;
Now we want the last line of the first group of comments
DO
line ← NIL;
line ← loadStream.GetLineRope[! IO.EndOfStream => CONTINUE];
IF line.IsEmpty[] THEN RETURN;
IF line.Find["--"] # -1 THEN comment ← line
ELSE RETURN;
ENDLOOP;
};
CopyCommand: Commander.CommandProc = {
argv: CommandTool.ArgumentVector;
CommandTool.StarExpansion[cmd];
argv ← CommandTool.Parse[cmd ! CommandTool.Failed => { msg ← errorMsg; CONTINUE; }];
IF argv = NIL THEN RETURN[$Failure, msg];
IF argv.argc # 4 OR NOT Rope.Equal[argv[2], "←"] THEN RETURN[$Failure, "Usage: CopyCommand newname ← oldname"]
ELSE {
cth: Commander.Handle ← NEW[Commander.CommandObject ← []];
lresult: REF ANY;
lmsg: Rope.ROPE;
newName: Rope.ROPE;
cth.err ← cmd.out;
cth.command ← argv[3]; -- oldname
cth.propertyList ← cmd.propertyList; -- so the search rules are accessible
[result: lresult, msg: lmsg] ← CommandTool.LookupWithSearchRules[cth];
IF lresult = $Failure OR lmsg # NIL THEN RETURN[$Failure, lmsg];
IF cth.commandLine # NIL THEN RETURN[$Failure, Rope.Concat[argv[2], " uses a generic command, can't copy or rename it"]];
IF cth.procData = NIL OR cth.procData.proc = NIL THEN RETURN[$Failure, Rope.Concat["CopyCommand: Can't find ", argv[3]]];
newName ← FileNames.ConvertToSlashFormat[FileNames.ResolveRelativePath[argv[1]]];
IF newName.Fetch[0] # '/ AND newName.Fetch[0] # '[ THEN {
cwd: Rope.ROPE ← FileNames.CurrentWorkingDirectory[];
IF NOT cwd.Equal["///"] THEN newName ← Rope.Concat[cwd, newName];
};
IF Commander.Lookup[newName] # NIL THEN RETURN[$Failure, Rope.Cat["CopyCommand: ", newName, " already exists"]];
Commander.Register[key: newName, proc: cth.procData.proc, doc: cth.procData.doc, clientData: cth.procData.clientData];
cmd.out.PutF["CopyCommand %g ← %g\n", IO.rope[newName], IO.rope[cth.command]];
};
};
Unregister: Commander.CommandProc = {
argv: CommandTool.ArgumentVector;
procData: Commander.CommandProcHandle;
CommandTool.StarExpansion[cmd];
argv ← CommandTool.Parse[cmd ! CommandTool.Failed => { msg ← errorMsg; CONTINUE; }];
IF argv = NIL THEN RETURN[$Failure, msg];
IF argv.argc = 2 THEN {
procData ← Commander.Lookup[argv[1]];
IF procData # NIL AND procData.proc # NIL THEN Commander.Register[key: argv[1], proc: NIL, doc: NIL]
ELSE cmd.out.PutF["Unregister: %g not found\n", IO.rope[argv[1]]];
}
ELSE cmd.out.PutRope["Usage: Unregister commandname\n"];
};
Open: Commander.CommandProc = {
argv: CommandTool.ArgumentVector;
name: Rope.ROPE;
CommandTool.StarExpansion[cmd];
argv ← CommandTool.Parse[cmd ! CommandTool.Failed => { msg ← errorMsg; CONTINUE; }];
IF argv = NIL THEN RETURN[$Failure, msg];
IF argv.argc < 2 THEN {
OpenViewer[NIL, TRUE, cmd.out];
RETURN;
};
IF argv.argc # 2 THEN {
cmd.out.PutRope["Usage: Open filename\n"];
RETURN;
};
name ← FileNames.ResolveRelativePath[argv[1]];
IF name.Length[] > 0 AND name.Fetch[0] # '/ AND name.Fetch[0] # '[ THEN name ← Rope.Concat[FileNames.CurrentWorkingDirectory[], FileNames.ConvertToSlashFormat[name]];
OpenViewer[name, FALSE, cmd.out];
};
New: Commander.CommandProc = {
OpenViewer[NIL, TRUE, cmd.out];
};
Comment: Commander.CommandProc = {
};
OpenViewer: PROC [name: Rope.ROPE, newViewer: BOOLEANFALSE, out: IO.STREAM] = {
viewer: ViewerClasses.Viewer;
IF newViewer THEN viewer ← ViewerOps.CreateViewer[flavor: $Text, info: [name: IF Rope.IsEmpty[name] THEN "No Name" ELSE name,
file: name, iconic: FALSE]]
ELSE viewer ← TiogaMenuOps.Open[name]; -- workaround
IF viewer = NIL THEN out.PutF["\tViewer file not found: %g\n", IO.rope[name]]
ELSE out.PutF["\tCreated Viewer: %g\n", IO.rope[viewer.name]];
};
statBeforeRef: Commander.CommandProcHandle ← NEW[Commander.CommandProcObject ← [StatBefore]];
statAfterRef: Commander.CommandProcHandle ← NEW[Commander.CommandProcObject ← [StatAfter]];
StatDataObject: TYPE = RECORD [
cmd: Commander.Handle ← NIL,
startTime: BasicTime.Pulses,
startFaults: INT,
startWords: INT
];
MaxStats: NAT = 10;
StatArray: TYPE = RECORD [
a: SEQUENCE length: [0..MaxStats+1) OF StatDataObject
];
stats: REF StatArray ← NEW[StatArray[MaxStats]];
StatBefore: ENTRY Commander.CommandProc = {
FOR i: NAT IN [0..MaxStats) DO
IF stats.a[i].cmd = NIL THEN {
stats.a[i] ← [
cmd: cmd,
startTime: BasicTime.GetClockPulses[],
startFaults: VMStatistics.pageFaults,
startWords: SafeStorage.NWordsAllocated[]
];
EXIT;
};
ENDLOOP;
};
StatAfter: ENTRY Commander.CommandProc = {
Put: PROC [char: CHAR] RETURNS [BOOLFALSE] = {cmd.out.PutChar[char]};
FOR i: NAT IN [0..MaxStats) DO
IF stats.a[i].cmd = cmd THEN {
nw: INT;
nf: INT;
tt: REAL;
seconds, fraction: INT;
nw ← SafeStorage.NWordsAllocated[] - stats.a[i].startWords;
cmd.out.PutF[" {"];
tt ← BasicTime.PulsesToSeconds[BasicTime.GetClockPulses[] - stats.a[i].startTime];
seconds ← Real.Fix[tt ! Real.RealException => { seconds ← LAST[INT]; CONTINUE; }];
fraction ← Real.Fix[(tt - seconds) * 100.0 ! Real.RealException => { fraction ← 0; CONTINUE; }];
cmd.out.PutF["%r.%02d seconds", IO.int[seconds], IO.int[fraction]];
IF nw > 0 AND nw < 10000000 THEN cmd.out.PutF[", %d words", IO.int[nw]];
TRUSTED {nf ← VMStatistics.pageFaults - stats.a[i].startFaults};
IF nf > 0 AND nf < 10000000 THEN cmd.out.PutF[", %d page faults", IO.int[nf]];
cmd.out.PutRope["}\n"];
stats.a[i].cmd ← NIL;
EXIT;
};
ENDLOOP;
};
Statistics: Commander.CommandProc = {
rb: REF ANY ← List.Assoc[key: $Before, aList: cmd.propertyList];
rbl: LIST OF REF ANY;
exists: BOOL;
IF rb # NIL AND ISTYPE[rb, LIST OF REF ANY] THEN rbl ← NARROW[rb];
exists ← rbl # NIL AND List.Memb[ref: statBeforeRef, list: rbl];
IF exists THEN {
cmd.propertyList ← CommandTool.RemoveProcFromList[aList: cmd.propertyList, listKey: $Before, proc: statBeforeRef];
cmd.propertyList ← CommandTool.RemoveProcFromList[aList: cmd.propertyList, listKey: $After, proc: statAfterRef];
}
ELSE {
cmd.propertyList ← CommandTool.AddProcToList[aList: cmd.propertyList, listKey: $Before, proc: statBeforeRef];
cmd.propertyList ← CommandTool.AddProcToList[aList: cmd.propertyList, listKey: $After, proc: statAfterRef];
};
};
Echo: Commander.CommandProc = {
argv: CommandTool.ArgumentVector;
CommandTool.StarExpansion[cmd];
argv ← CommandTool.Parse[cmd ! CommandTool.Failed => { msg ← errorMsg; CONTINUE; }];
IF argv = NIL THEN RETURN[$Failure, msg];
FOR i: NAT IN [1..argv.argc) DO
cmd.out.PutRope[argv[i]];
cmd.out.PutChar[' ];
ProcessExtras.CheckForAbort[];
ENDLOOP;
cmd.out.PutChar['\n];
};
PrintSearchRules: Commander.CommandProc = {
rules: LIST OF REF ANY;
rules ← NARROW[List.Assoc[key: $SearchRules, aList: cmd.propertyList]];
cmd.out.PutRope["( "];
IF rules = NIL THEN cmd.out.PutChar[' ];
WHILE rules # NIL DO
cmd.out.PutRope[NARROW[rules.first, Rope.ROPE]];
rules ← rules.rest;
cmd.out.PutChar[' ];
ProcessExtras.CheckForAbort[];
ENDLOOP;
cmd.out.PutRope[")\n"];
};
SetSearchRules: Commander.CommandProc = {
args: LIST OF Rope.ROPE;
dir: Rope.ROPE;
first: CHAR;
CommandTool.StarExpansion[cmd];
args ← CommandTool.ParseToList[cmd ! CommandTool.Failed => { msg ← errorMsg; CONTINUE; }].list;
IF args = NIL THEN RETURN[$Failure, msg];
IF cmd.procData.clientData = $SetSearchRules THEN cmd.propertyList ← List.PutAssoc[key: $SearchRules, val: NIL, aList: cmd.propertyList];
WHILE args # NIL DO
dir ← args.first;
dir ← FileNames.ResolveRelativePath[dir];
IF dir.Length[] < 3 THEN RETURN[$Failure, Rope.Concat["Directory name too short: ", dir]];
first ← dir.Fetch[0];
IF first # '[ AND first # '/ THEN RETURN[$Failure, Rope.Concat["Bad directory name: ", dir]];
CommandTool.AddSearchRule[cmd: cmd, dir: dir, append: TRUE];
args ← args.rest;
ProcessExtras.CheckForAbort[];
ENDLOOP;
};
Sleep: Commander.CommandProc = {
argv: CommandTool.ArgumentVector;
seconds: INT;
secondsCard: CARDINAL;
CommandTool.StarExpansion[cmd];
argv ← CommandTool.Parse[cmd ! CommandTool.Failed => { msg ← errorMsg; CONTINUE; }];
IF argv = NIL THEN RETURN[$Failure, msg];
IF argv.argc # 2 THEN RETURN[$Failure, "Usage: Sleep seconds"];
seconds ← Convert.IntFromRope[argv[1] ! Convert.Error => {msg ← "Bad arg"; CONTINUE; }];
IF seconds NOT IN [0..LAST[CARDINAL]] THEN msg ← "Bad arg";
IF msg # NIL THEN RETURN[$Failure, msg];
secondsCard ← seconds;
Process.Pause[Process.SecondsToTicks[seconds]];
};
Indent: Commander.CommandProc = {
argv: CommandTool.ArgumentVector;
depth: INT;
depthNAT: NAT;
spaces: REF TEXT;
line: REF TEXTNEW[TEXT[200]];
CommandTool.StarExpansion[cmd];
argv ← CommandTool.Parse[cmd ! CommandTool.Failed => { msg ← errorMsg; CONTINUE; }];
IF argv = NIL THEN RETURN[$Failure, msg];
IF argv.argc > 2 THEN RETURN[$Failure, "Usage: Indent depth"];
IF argv.argc < 2 THEN {
depth ← 8;
depthNAT ← 8;
}
ELSE {
depth ← Convert.IntFromRope[argv[1] ! Convert.Error => {msg ← "Bad arg"; CONTINUE; }];
IF msg # NIL THEN RETURN[$Failure, msg];
IF depth NOT IN [0..80] THEN RETURN[$Failure, "depth should be in [0..80]"]
ELSE depthNAT ← depth;
};
spaces ← NEW[TEXT[depthNAT]];
FOR i: NAT IN [0..depthNAT) DO spaces[i] ← ' ; ENDLOOP;
spaces.length ← depthNAT;
DO
IF cmd.in.EndOf[ ! IO.Error => EXIT] THEN EXIT;
line ← cmd.in.GetLine[buffer: line !
IO.EndOfStream => GOTO Finished;
IO.Error => EXIT;
EditedStream.Rubout => GOTO Finished;
];
cmd.out.PutBlock[block: spaces ! IO.Error => EXIT];
cmd.out.PutBlock[block: line ! IO.Error => EXIT];
cmd.out.PutChar['\n ! IO.Error => EXIT];
ENDLOOP;
EXITS
Finished => NULL;
};
Tee: Commander.CommandProc = {
argv: CommandTool.ArgumentVector;
fileStream: IO.STREAMNIL;
block: REF TEXTNEW[TEXT[512]];
count: NAT;
{
ENABLE UNWIND => IF fileStream # NIL THEN fileStream.Close[];
CommandTool.StarExpansion[cmd];
argv ← CommandTool.Parse[cmd ! CommandTool.Failed => { msg ← errorMsg; CONTINUE; }];
IF argv = NIL THEN RETURN[$Failure, msg];
IF argv.argc > 2 THEN RETURN[$Failure, "Usage: Tee filename"];
IF argv.argc = 2 THEN fileStream ← FS.StreamOpen[fileName: argv[1], accessOptions: $create ! FS.Error => {
IF error.group = $user THEN msg ← error.explanation;
GOTO Die;
}];
DO
block.length ← 0;
IF cmd.in.EndOf[] THEN EXIT;
count ← cmd.in.GetBlock[block: block !
IO.EndOfStream => EXIT;
IO.Error => EXIT;
EditedStream.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];
ProcessExtras.CheckForAbort[];
ENDLOOP;
fileStream.Close[ ! IO.Error => CONTINUE];
EXITS
Die => RETURN[$Failure, msg];
};
};
Abort: Commander.CommandProc = TRUSTED {
Process.Abort[LOOPHOLE[Process.GetCurrent[], UNSPECIFIED]];
};
Shift: Commander.CommandProc = {
result ← CommandTool.DoCommand[commandLine: cmd.commandLine, parent: cmd];
};
ShiftInterp: Commander.CommandProc = {
result ← CommandTool.DoCommand[commandLine: cmd.commandLine, parent: cmd];
};
Time: Commander.CommandProc = {
start: BasicTime.Pulses ← BasicTime.GetClockPulses[];
stop: BasicTime.Pulses;
microseconds, seconds: LONG CARDINAL;
digit: CARDINAL;
any: BOOLFALSE;
text: REF TEXTNEW[TEXT[8]];
result ← CommandTool.DoCommand[commandLine: cmd.commandLine, parent: cmd];
stop ← BasicTime.GetClockPulses[];
microseconds ← BasicTime.PulsesToMicroseconds[stop - start];
seconds ← microseconds / 1000000;
microseconds ← microseconds MOD 1000000;
THROUGH [0..6) DO
microseconds ← microseconds * 10;
digit ← microseconds / 1000000;
microseconds ← microseconds MOD 1000000;
IF NOT any THEN {
text ← Convert.AppendChar[to: text, from: '., quote: FALSE];
any ← TRUE;
};
text ← Convert.AppendChar[to: text, from: digit + '0, quote: FALSE];
IF microseconds = 0 THEN EXIT;
ENDLOOP;
RETURN [NIL, IO.PutFR["Running time: %r%g\n", IO.card[seconds], IO.text[text]]];
};
Checkpoint: Commander.CommandProc = {
okMsg: Rope.ROPE = IO.PutFR["Checkpoint created at %t.", IO.time[BasicTime.Now[]]];
errmsg: Rope.ROPE;
cmd.out.PutRope["Creating checkpoint.\n"];
errmsg ← Booting.Checkpoint[];
IF errmsg.Length[] = 0 THEN RETURN[NIL, okMsg]
ELSE RETURN[$Failure, errmsg];
};
Rollback: Commander.CommandProc = {
RETURN[$Failure, Booting.Boot[[self[]], [r: TRUE]]];
};
Init: PROC = {
Commander.Register[key: "-", proc: Comment, doc: "Comment"];
CommandExtras.MakeUninterpreted[Commander.Lookup["-"]];
Commander.Register[key: "--", proc: Comment, doc: "Comment"];
CommandExtras.MakeUninterpreted[Commander.Lookup["--"]];
Commander.Register[key: "/", proc: Comment, doc: "Comment"];
CommandExtras.MakeUninterpreted[Commander.Lookup["/"]];
Commander.Register[key: "//", proc: Comment, doc: "Comment"];
CommandExtras.MakeUninterpreted[Commander.Lookup["//"]];
Commander.Register[key: "///", proc: Comment, doc: "Comment"];
CommandExtras.MakeUninterpreted[Commander.Lookup["///"]];
Commander.Register[key: "///Commands/?", proc: EnumerateCommands, doc: "List registered commands"];
Commander.Register[key: "///Commands/Abort", proc: Abort, doc: "Abort -- Raises ABORTED"];
Commander.Register[key: "///Commands/AddSearchRules", proc: SetSearchRules, doc: "Add command search rules: AddSearchRules list-of-directories"];
Commander.Register[key: "///Commands/CopyCommand", proc: CopyCommand, doc: "CopyCommand newName oldName - Attach a new name to an existing command"];
Commander.Register[key: "///Commands/Checkpoint", proc: Checkpoint, doc: "Create a checkpoint"];
Commander.Register[key: "///Commands/Date", proc: Date, doc: "Print date and time"];
Commander.Register[key: "///Commands/Echo", proc: Echo, doc: "Print command line"];
Commander.Register[key: "///Commands/Fail", proc: Fail, doc: "This command always fails"];
Commander.Register[key: "///Commands/Help", proc: Help, doc: "Get command documentation file."];
Commander.Register[key: "///Commands/Indent", proc: Indent, doc: "Indent n -- Indent n spaces"];
Commander.Register[key: "///Commands/New", proc: New, doc: "Open an empty viewer"];
Commander.Register[key: "///Commands/Open", proc: Open, doc: "Open fileName - open a viewer"];
Commander.Register[key: "///Commands/PrintSearchRules", proc: PrintSearchRules, doc: "Print command search rules"];
Commander.Register[key: "///Commands/Rollback", proc: Rollback, doc: "Roll back to previous checkpoint"];
Commander.Register[key: "///Commands/SetSearchRules", proc: SetSearchRules, doc: "Set command search rules: SetSearchRules list-of-directories", clientData: $SetSearchRules];
Commander.Register[key: "///Commands/Shift", proc: Shift, doc: "Shift command-line (uninterpreted)"];
CommandExtras.MakeUninterpreted[Commander.Lookup["///Commands/Shift"]];
Commander.Register[key: "///Commands/ShiftInterp", proc: ShiftInterp, doc: "ShiftInterp command-line (interpreted)"];
Commander.Register[key: "///Commands/Sleep", proc: Sleep, doc: "Sleep n -- pause for n seconds"];
Commander.Register[key: "///Commands/Statistics", proc: Statistics, doc: "Turn statistics printing on or off"];
Commander.Register[key: "///Commands/Tee", proc: Tee, doc: "Tee file -- copy standard input to file"];
Commander.Register[key: "///Commands/Time", proc: Time, doc: "Time command-line (uninterpreted)"];
CommandExtras.MakeUninterpreted[Commander.Lookup["///Commands/Time"]];
Commander.Register[key: "///Commands/Unregister", proc: Unregister, doc: "Unregister a command"];
Commander.Register[key: "///Commands/User", proc: User, doc: "Print name of logged in user"];
Commander.Register[key: "///Commands/Version", proc: Version, doc: "Print Cedar version number"];
};
Init[];
END.
April 4, 1983 4:42 pm, Stewart, Created
April 20, 1983 7:10 pm, Stewart, add commandfile arguments and auto commandfiles
September 7, 1983 11:10 am, Stewart, Cedar 5
October 24, 1983 9:42 am, Stewart, New CommandTool
November 3, 1983 9:37 am, Stewart, Bulletproofing
November 3, 1983 9:37 am, Stewart, add Checkpoint and Rollback