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: REF ← NIL, msg: ROPE ← NIL]
el: LIST OF REF ANY;
eCell: REF ECell;
matchList: LIST OF ROPE ← NIL;
lookupCmd: Commander.Handle ← NEW[Commander.CommandObject];
i: NAT ← 1;
flag: BOOL ← FALSE;
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: ROPE ← NIL;
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 ROPE ← NIL;
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 TEXT ← NEW[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: STREAM ← NIL;
block: REF TEXT ← NEW[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: BOOL ← FALSE;
text: REF TEXT ← NEW[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:
REF ←
NIL, un:
BOOL ←
FALSE] = {
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