InitialCommandsImpl.mesa
Copyright © 1984 by Xerox Corporation. All rights reserved.
L. Stewart, January 17, 1984 5:03 pm
Russ Atkinson, October 22, 1984 8:15:27 pm PDT
Paul Rovner, November 29, 1983 8:51 am
Doug Wyatt, May 25, 1984 2:52:01 pm PDT
DIRECTORY
BasicTime USING [FromPupTime, GetClockPulses, GMT, Now, Period, Pulses, PulsesToMicroseconds, PulsesToSeconds],
Booting USING [Boot, Checkpoint],
Commander USING [CommandObject, CommandProc, CommandProcHandle, CommandProcObject, Enumerate, Handle, Lookup, Register],
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, ResolveRelativePath, StripVersionNumber],
FS USING [EnumerateForNames, Error, NameProc, StreamOpen],
IO USING [Close, EndOf, EndOfStream, Error, GetBlock, GetLine, PutBlock, PutChar, PutF, PutFR, PutRope, STREAM],
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],
RopeList USING [DReverse],
SafeStorage USING [NWordsAllocated],
SystemVersion USING [bootFileDate, release],
TiogaMenuOps USING [Open],
UserCredentials USING [Get],
ViewerClasses USING [Viewer],
VMStatistics USING [pageFaults];
InitialCommandsImpl: CEDAR MONITOR
IMPORTS BasicTime, Booting, Commander, CommandExtras, CommandTool, Convert, EditedStream, FileNames, FS, IO, List, Process, ProcessExtras, Real, Rope, RopeList, SafeStorage, SystemVersion, TiogaMenuOps, UserCredentials, VMStatistics
= BEGIN
ROPE: TYPE = Rope.ROPE;
STREAM: TYPE = IO.STREAM;
Date: Commander.CommandProc = {
RETURN[NIL, Convert.RopeFromTime[from: BasicTime.Now[], includeDayOfWeek: TRUE]];
};
User: Commander.CommandProc = {
user: ROPE ← UserCredentials.Get[].name;
IF user.Length[] = 0 THEN user ← "??";
RETURN[NIL, user];
};
Version: Commander.CommandProc = TRUSTED {
PutVersion[cmd.out];
};
PutVersion: PROC [out: STREAM] = {
IO.PutF[out, "Cedar %g.%g",
[integer[SystemVersion.release.major]], [integer[SystemVersion.release.minor]]];
IF SystemVersion.release.patch # 0 THEN
IO.PutF[out, ".%g", [integer[SystemVersion.release.patch]]];
IO.PutF[out, " of %t\n", [time[BasicTime.FromPupTime[SystemVersion.bootFileDate]]]];
};
Fail: Commander.CommandProc = {
RETURN[$Failure, "This Command Always Fails"];
};
ECell: TYPE = RECORD [name, doc: ROPE];
EnumerateCommands: Commander.CommandProc = {
[cmd: Handle] RETURNS [result: REFNIL, msg: ROPENIL]
el: LIST OF REF ANY;
eCell: REF ECell;
matchList: LIST OF ROPENIL;
lookupCmd: Commander.Handle ← NEW[Commander.CommandObject];
i: NAT ← 1;
flag: BOOLFALSE;
full: BOOL ← cmd.procData.clientData = $Full;
EProc: PROC [key: ROPE, procData: Commander.CommandProcHandle] RETURNS [stop: BOOL] = {
ProcessExtras.CheckForAbort[];
IF procData # NIL THEN {
see if we have a match
FOR list: LIST OF 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.Substr[base: FileNames.ConvertToSlashFormat[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
ProcessExtras.CheckForAbort[];
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];
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.ArgumentVector ← CommandTool.Parse[cmd: cmd
! CommandTool.Failed => {msg ← errorMsg; GO TO oops}];
First, construct the list of patterns to match against the command names.
DO
match: 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 ← matchList, list.rest WHILE list # NIL DO
IF list.first.Find["*"] = -1 THEN {
This one has no pattern
ProcessExtras.CheckForAbort[];
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",
[rope[lookupCmd.command]], [rope[lookupCmd.commandLine]], [rope[lookupCmd.procData.doc]]];
}
ELSE cmd.out.PutF["%-20g Not found\n", [rope[lookupCmd.command]]];
ProcessExtras.CheckForAbort[];
};
ENDLOOP;
[] ← Commander.Enumerate[EProc];
IF full THEN
Now search the file system
FOR list: LIST OF ROPE ← matchList, list.rest WHILE list # NIL DO
IF list.first.Find["*"] # -1 THEN {
withStarExt: 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 ANY
NARROW[List.Assoc[key: $SearchRules, aList: cmd.propertyList]];
WHILE rules # NIL DO
ProcessExtras.CheckForAbort[];
FS.EnumerateForNames[
pattern: withStarExt, proc: FSProc, wDir: NARROW[rules.first, ROPE]];
rules ← rules.rest;
ENDLOOP;
};
};
ENDLOOP;
IF el = NIL AND flag = TRUE THEN {
If no matches found, indicate this result explicitly.
msg ← "No matching registered commands.";
RETURN;
};
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", [rope[eCell.name]], [rope[eCell.doc]]];
el ← el.rest;
ProcessExtras.CheckForAbort[];
ENDLOOP;
};
EXITS oops => result ← $Failure;
};
CopyCommand: Commander.CommandProc = {
argv: CommandTool.ArgumentVector
← ExpandAndParse[cmd ! CommandTool.Failed => {msg ← errorMsg; GO TO oops}];
IF argv.argc # 4 OR NOT Rope.Equal[argv[2], "←"] THEN {
msg ← "Usage: CopyCommand newname ← oldname";
GO TO oops;
};
{
cth: Commander.Handle ← NEW[Commander.CommandObject ← []];
lresult: REF ANY;
lmsg: ROPE;
newName: 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 {msg ← lmsg; GO TO oops};
IF cth.commandLine # NIL THEN {
msg ← Rope.Concat[argv[2], " uses a generic command, can't copy or rename it"];
GO TO oops};
IF cth.procData = NIL OR cth.procData.proc = NIL THEN {
msg ← Rope.Concat["CopyCommand: Can't find ", argv[3]]; GO TO oops};
newName ← FileNames.ResolveRelativePath[argv[1]];
IF newName.Fetch[0] # '/ AND newName.Fetch[0] # '[ THEN {
cwd: ROPE ← FileNames.CurrentWorkingDirectory[];
IF NOT cwd.Equal["///"] THEN newName ← Rope.Concat[cwd, newName];
};
IF Commander.Lookup[newName] # NIL THEN {
msg ← Rope.Cat["CopyCommand: ", newName, " already exists"]; GO TO oops};
Commander.Register[
key: newName, proc: cth.procData.proc, doc: cth.procData.doc, clientData: cth.procData.clientData];
cmd.out.PutF["CopyCommand %g ← %g\n", [rope[newName]], [rope[cth.command]]];
};
EXITS oops => result ← $Failure;
};
Unregister: Commander.CommandProc = {
procData: Commander.CommandProcHandle;
argv: CommandTool.ArgumentVector
← ExpandAndParse[cmd ! CommandTool.Failed => {msg ← errorMsg; GO TO oops}];
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", [rope[argv[1]]]];
}
ELSE cmd.out.PutRope["Usage: Unregister commandname\n"];
EXITS oops => result ← $Failure;
};
Open: Commander.CommandProc = {
argv: CommandTool.ArgumentVector
← CommandTool.Parse[cmd ! CommandTool.Failed => {msg ← errorMsg; GO TO oops}];
IF argv.argc < 2 THEN {OpenViewer[NIL, cmd.out]; RETURN};
FOR i: NAT IN [1..argv.argc) DO
list: LIST OF ROPE ← ExpandStar[argv[i]];
WHILE list # NIL DO
name: ROPE ← FileNames.ResolveRelativePath[list.first];
OpenViewer[name, cmd.out];
list ← list.rest;
ENDLOOP;
ENDLOOP;
EXITS oops => result ← $Failure;
};
ExpandStar: PROC [token: ROPE] RETURNS [LIST OF ROPE] = {
IF Rope.Find[token, "*"] # -1 THEN {
stripVersion: BOOL;
listOfTokens: LIST OF ROPENIL;
ConsProc: FS.NameProc = {
fullFName ← FileNames.ConvertToSlashFormat[fullFName];
IF stripVersion THEN fullFName ← FileNames.StripVersionNumber[fullFName];
listOfTokens ← CONS[fullFName, listOfTokens]; -- on front of list
RETURN[TRUE];
};
IF token.Find["!"] = -1 THEN token ← Rope.Concat[token, "!H"];
stripVersion ← token.Find["!H", 0, FALSE] # -1;
FS.EnumerateForNames[pattern: FileNames.ResolveRelativePath[token], proc: ConsProc];
RETURN[RopeList.DReverse[listOfTokens]];
}
ELSE RETURN[LIST[token]];
};
New: Commander.CommandProc = {
OpenViewer[NIL, cmd.out];
};
Comment: Commander.CommandProc = {
};
OpenViewer: PROC [name: ROPE, out: STREAM] = {
viewer: ViewerClasses.Viewer;
IF Rope.IsEmpty[name] THEN name ← FileNames.CurrentWorkingDirectory[];
viewer ← TiogaMenuOps.Open[name];
IF viewer = NIL THEN out.PutF["\tViewer file not found: %g\n", [rope[name]]]
ELSE out.PutF["\tCreated Viewer: %g\n", [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.GMT,
startPulses: 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 = {
ENABLE UNWIND => NULL;
FOR i: NAT IN [0..MaxStats) DO
IF stats.a[i].cmd = NIL THEN {
stats.a[i] ← [
cmd: cmd,
startTime: BasicTime.Now[],
startPulses: BasicTime.GetClockPulses[],
startFaults: VMStatistics.pageFaults,
startWords: SafeStorage.NWordsAllocated[] ];
EXIT;
};
ENDLOOP;
result ← $Preserve;
};
StatAfter: ENTRY Commander.CommandProc = {
ENABLE UNWIND => NULL;
pulses: BasicTime.Pulses = BasicTime.GetClockPulses[];
now: BasicTime.GMT = BasicTime.Now[];
wordsNow: INT = SafeStorage.NWordsAllocated[];
faultsNow: INT = VMStatistics.pageFaults;
FOR i: NAT IN [0..MaxStats) DO
statObj: StatDataObject = stats.a[i];
IF statObj.cmd = cmd THEN {
seconds: INT ← BasicTime.Period[statObj.startTime, now];
nw: INT = wordsNow - statObj.startWords;
nf: INT = faultsNow - statObj.startFaults;
stats.a[i].cmd ← NIL;
cmd.out.PutF[" {"];
SELECT seconds FROM
< 0 => cmd.out.PutRope["?? seconds"];
>= 60 => {
Long elapsed time, so use low precision clock to avoid overflow
cmd.out.PutF["%r seconds", [integer[seconds]]];
};
ENDCASE => {
High precision elapsed time
tt: REAL ← BasicTime.PulsesToSeconds[pulses - statObj.startPulses];
fraction: INT ← 0;
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["%d.%02d seconds", [integer[seconds]], [integer[fraction]]]
};
IF nw > 0 AND nw < 10000000 THEN cmd.out.PutF[", %d words", [integer[nw]]];
IF nf > 0 AND nf < 10000000 THEN cmd.out.PutF[", %d page faults", [integer[nf]]];
cmd.out.PutRope["}\n"];
EXIT;
};
ENDLOOP;
Now we have to preserve the result, or else we will not preserve $Failure!
result ← $Preserve;
};
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
← ExpandAndParse[cmd ! CommandTool.Failed => {msg ← errorMsg; GO TO oops}];
FOR i: NAT IN [1..argv.argc) DO
cmd.out.PutRope[argv[i]];
cmd.out.PutChar[' ];
ProcessExtras.CheckForAbort[];
ENDLOOP;
cmd.out.PutChar['\n];
EXITS oops => result ← $Failure;
};
PrintSearchRules: Commander.CommandProc = {
rules: LIST OF REF ANY
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]];
rules ← rules.rest;
cmd.out.PutChar[' ];
ProcessExtras.CheckForAbort[];
ENDLOOP;
cmd.out.PutRope[")\n"];
};
SetSearchRules: Commander.CommandProc = {
args: LIST OF ROPE;
dir: ROPE;
first: CHAR;
CommandTool.StarExpansion[cmd
! CommandTool.Failed => {msg ← errorMsg; GO TO oops}];
args ← CommandTool.ParseToList[cmd
! CommandTool.Failed => {msg ← errorMsg; GO TO oops}].list;
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 {
msg ← Rope.Concat["Directory name too short: ", dir]; GO TO oops};
first ← dir.Fetch[0];
IF first # '[ AND first # '/ THEN {
msg ← Rope.Concat["Bad directory name: ", dir]; GO TO oops};
CommandTool.AddSearchRule[cmd: cmd, dir: dir, append: TRUE];
args ← args.rest;
ProcessExtras.CheckForAbort[];
ENDLOOP;
EXITS oops => result ← $Failure;
};
Sleep: Commander.CommandProc = {
seconds: INT;
secondsCard: CARDINAL;
argv: CommandTool.ArgumentVector
← ExpandAndParse[cmd ! CommandTool.Failed => {msg ← errorMsg; GO TO oops}];
IF argv.argc # 2 THEN {msg ← "Usage: Sleep seconds"; GO TO oops};
seconds ← Convert.IntFromRope[argv[1] ! Convert.Error => {msg ← "Bad arg"; GO TO oops}];
IF seconds NOT IN [0..LAST[CARDINAL]] THEN {msg ← "Bad arg"; GO TO oops};
secondsCard ← seconds;
Process.Pause[Process.SecondsToTicks[seconds]];
EXITS oops => result ← $Failure;
};
Indent: Commander.CommandProc = {
depth: INT;
depthNAT: NAT;
spaces: REF TEXT;
line: REF TEXTNEW[TEXT[200]];
argv: CommandTool.ArgumentVector
← ExpandAndParse[cmd ! CommandTool.Failed => {msg ← errorMsg; GO TO oops}];
IF argv.argc > 2 THEN {msg ← "Usage: Indent depth"; GO TO oops};
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;
};
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;
oops => result ← $Failure;
};
Tee: Commander.CommandProc = {
fileStream: STREAMNIL;
block: REF TEXTNEW[TEXT[512]];
{
ENABLE UNWIND => IF fileStream # NIL THEN fileStream.Close[];
argv: CommandTool.ArgumentVector
← ExpandAndParse[cmd ! CommandTool.Failed => {msg ← errorMsg; GO TO oops}];
IF argv.argc > 2 THEN {msg ← "Usage: Tee filename"; GO TO oops};
IF argv.argc = 2 THEN
fileStream ← FS.StreamOpen[fileName: argv[1], accessOptions: $create
! FS.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;
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 oops => 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", [cardinal[seconds]], [text[text]]]];
};
Checkpoint: Commander.CommandProc = {
errmsg: ROPE;
out: STREAM = cmd.out;
IO.PutF[out, "Creating checkpoint at %g\n", [time[BasicTime.Now[]]]];
IO.PutF[out, " made by %g\n", [rope[UserCredentials.Get[].name]]];
errmsg ← Booting.Checkpoint[];
IF errmsg.Length[] # 0 THEN RETURN[$Failure, errmsg];
IO.PutF[out, "Rollback at %g\n User: %g\n Version: ",
[time[BasicTime.Now[]]], [rope[UserCredentials.Get[].name]]];
PutVersion[cmd.out];
};
Rollback: Commander.CommandProc = {
RETURN[$Failure, Booting.Boot[[self[]], [r: TRUE]]];
};
ExpandAndParse: PROC [cmd: Commander.Handle] RETURNS [CommandTool.ArgumentVector] = {
CommandTool.StarExpansion[cmd];
RETURN [CommandTool.Parse[cmd]];
};
Init: PROC = {
Register: PROC [name: ROPE, proc: Commander.CommandProc, doc: ROPE, data: REFNIL, un: BOOLFALSE] = {
name ← Rope.Concat["///Commands/", name];
Commander.Register[name, proc, doc, data];
IF un THEN CommandExtras.MakeUninterpreted[Commander.Lookup[name]];
};
Commander.Register["-", Comment, "Comment"];
CommandExtras.MakeUninterpreted[Commander.Lookup["-"]];
Commander.Register["--", Comment, "Comment"];
CommandExtras.MakeUninterpreted[Commander.Lookup["--"]];
Commander.Register["/", Comment, "Comment"];
CommandExtras.MakeUninterpreted[Commander.Lookup["/"]];
Commander.Register["//", Comment, "Comment"];
CommandExtras.MakeUninterpreted[Commander.Lookup["//"]];
Commander.Register["///", Comment, "Comment"];
CommandExtras.MakeUninterpreted[Commander.Lookup["///"]];
Register["?", EnumerateCommands, "List registered commands"];
Register["??", EnumerateCommands, "List registered commands (including those not yet loaded)", $Full];
Register["Help", EnumerateCommands, "List registered commands"];
Register["HelpFull", EnumerateCommands, "List registered commands (including those not yet loaded)", $Full];
Register["Abort", Abort, "Abort -- Raises ABORTED"];
Register["AddSearchRules", SetSearchRules, "Add command search rules: AddSearchRules list-of-directories"];
Register["CopyCommand", CopyCommand, "CopyCommand newName ← oldName - Attach a new name to an existing command"];
Register["Checkpoint", Checkpoint, "Create a checkpoint"];
Register["Date", Date, "Print date and time"];
Register["Echo", Echo, "Print command line"];
Register["Fail", Fail, "This command always fails"];
Register["Indent", Indent, "Indent n -- Indent n spaces"];
Register["New", New, "Open an empty viewer"];
Register["Open", Open, "Open fileName - open a viewer"];
Register["PrintSearchRules", PrintSearchRules, "Print command search rules"];
Register["Rollback", Rollback, "Roll back to previous checkpoint"];
Register["SetSearchRules", SetSearchRules, "Set command search rules: SetSearchRules directory*", $SetSearchRules];
Register["Shift", Shift, "Shift command-line (uninterpreted)", NIL, TRUE];
Register["ShiftInterp", ShiftInterp, "ShiftInterp command-line (interpreted)"];
Register["Sleep", Sleep, "Sleep n -- pause for n seconds"];
Register["Statistics", Statistics, "Turn statistics printing on or off"];
Register["Tee", Tee, "Tee file -- copy standard input to file"];
Register["Time", Time, "Time command-line (uninterpreted)", NIL, TRUE];
Register["Unregister", Unregister, "Unregister a command"];
Register["User", User, "Print name of logged in user"];
Register["Version", Version, "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
January 14, 1984 8:18 pm, Stewart, new Open