<> <> <> <> <> <> DIRECTORY BasicTime USING [FromPupTime, GetClockPulses, GMT, Now, Period, Pulses, PulsesToMicroseconds, PulsesToSeconds], Booting USING [Boot, Checkpoint], Commander USING [CommandObject, CommandProc, CommandProcHandle, CommandProcObject, Handle, Lookup, Register], CommandTool USING [AddProcToList, AddSearchRule, ArgumentVector, DoCommand, Failed, GetProp, Parse, ParseToList, RemoveProcFromList], CommandToolLookup USING [DoLookup, FindMatchingCommands], 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, PutF1, PutFR, PutRope, STREAM], List USING [AList, Assoc, Memb, PutAssoc], Process USING [Abort, CheckForAbort, GetCurrent, Pause, SecondsToTicks], Real USING [Fix, RealException], Rope USING [Concat, Equal, Fetch, Find, IsEmpty, Length, Match, ROPE], 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, CommandTool, CommandToolLookup, Convert, EditedStream, FileNames, FS, IO, List, Process, Real, Rope, RopeList, SafeStorage, SystemVersion, TiogaMenuOps, UserCredentials, VMStatistics = BEGIN ProcAny: TYPE = PROC ANY RETURNS ANY; 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.PutF1[out, ".%g", [integer[SystemVersion.release.patch]]]; IO.PutF1[out, " of %t\n", [time[BasicTime.FromPupTime[SystemVersion.bootFileDate]]]]; }; Fail: Commander.CommandProc = { RETURN[$Failure, "This Command Always Fails"]; }; HelpCommand: Commander.CommandProc = { <<[cmd: Handle] RETURNS [result: REF _ NIL, msg: ROPE _ NIL]>> full: BOOL _ cmd.procData.clientData = $Full; out: STREAM _ cmd.out; argv: CommandTool.ArgumentVector _ CommandTool.Parse[cmd: cmd ! CommandTool.Failed => {msg _ errorMsg; GO TO oops}]; IF argv.argc <= 1 THEN {msg _ "Usage: Help pattern ...\n"; GO TO oops}; FOR i: NAT IN [1..argv.argc) DO arg: ROPE _ argv[i]; matchList: LIST OF ROPE _ IF full THEN CommandToolLookup.DoLookup[cmd, arg].paths ELSE CommandToolLookup.FindMatchingCommands[arg, FALSE, CommandTool.GetProp[cmd, $SearchRules]].paths; IF matchList = NIL THEN { IO.PutF1[out, "No matching commands for %g\n", [rope[arg]] ]; LOOP; }; FOR each: LIST OF ROPE _ matchList, each.rest WHILE each # NIL DO name: ROPE _ each.first; pData: Commander.CommandProcHandle _ Commander.Lookup[name]; IO.PutF1[out, "%-20g", [rope[name]] ]; IF pData = NIL THEN { <> SELECT TRUE FROM NOT full => IO.PutRope[out, " {helpless}\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, " {helpless}\n"]; LOOP; }; IO.PutRope[out, " "]; IO.PutRope[out, pData.doc]; IO.PutRope[out, "\n"]; ENDLOOP; IF matchList.rest = NIL THEN { <> pData: Commander.CommandProcHandle _ Commander.Lookup[matchList.first]; IF pData # NIL AND pData.proc # NIL THEN { new: REF ProcAny _ NEW[ProcAny _ pData.proc]; IO.PutF1[out, "{implementor: %g}\n", [refAny[new]] ]; }; }; ENDLOOP; EXITS oops => result _ $Failure; }; Unregister: Commander.CommandProc = { procData: Commander.CommandProcHandle; argv: CommandTool.ArgumentVector _ CommandTool.Parse[cmd, TRUE ! 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.PutF1["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 ! FS.Error => IF error.group # bug THEN CONTINUE]; 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.PutF1["\tViewer file not found: %g\n", [rope[name]]] ELSE out.PutF1["\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.PutRope[" {"]; SELECT seconds FROM < 0 => cmd.out.PutRope["?? seconds"]; >= 60 => { <> cmd.out.PutF1["%r seconds", [integer[seconds]]]; }; ENDCASE => { <> 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.PutF1[", %d words", [integer[nw]]]; IF nf > 0 AND nf < 10000000 THEN cmd.out.PutF1[", %d page faults", [integer[nf]]]; cmd.out.PutRope["}\n"]; EXIT; }; ENDLOOP; <> result _ $Preserve; }; Statistics: Commander.CommandProc = { argv: CommandTool.ArgumentVector _ CommandTool.Parse[cmd: cmd ! CommandTool.Failed => {msg _ errorMsg; GO TO oops}]; which: {on, off, swap} _ swap; IF argv.argc > 1 THEN { arg: ROPE _ argv[1]; SELECT TRUE FROM Rope.Equal[arg, "on", FALSE] => which _ on; Rope.Equal[arg, "off", FALSE] => which _ off; ENDCASE => {msg _ "Illegal option.\n"; GO TO oops}; }; WITH List.Assoc[key: $Before, aList: cmd.propertyList] SELECT FROM rbl: LIST OF REF ANY => { IF List.Memb[ref: statBeforeRef, list: rbl] THEN { <> SELECT which FROM swap, off => { cmd.propertyList _ CommandTool.RemoveProcFromList[aList: cmd.propertyList, listKey: $Before, proc: statBeforeRef]; cmd.propertyList _ CommandTool.RemoveProcFromList[aList: cmd.propertyList, listKey: $After, proc: statAfterRef]; GO TO showOff; }; ENDCASE; GO TO showOn; }; }; ENDCASE; SELECT which FROM swap, on => { cmd.propertyList _ CommandTool.AddProcToList[aList: cmd.propertyList, listKey: $Before, proc: statBeforeRef]; cmd.propertyList _ CommandTool.AddProcToList[aList: cmd.propertyList, listKey: $After, proc: statAfterRef]; GO TO showOn; }; ENDCASE; GO TO showOff; EXITS showOff => msg _ "Statistics off.\n"; showOn => msg _ "Statistics on.\n"; oops => result _ $Failure; }; Echo: Commander.CommandProc = { argv: CommandTool.ArgumentVector _ CommandTool.Parse[cmd, TRUE ! CommandTool.Failed => {msg _ errorMsg; GO TO oops}]; FOR i: NAT IN [1..argv.argc) DO cmd.out.PutRope[argv[i]]; cmd.out.PutChar[' ]; Process.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[' ]; Process.CheckForAbort[]; ENDLOOP; cmd.out.PutRope[")\n"]; }; SetSearchRules: Commander.CommandProc = { args: LIST OF ROPE; dir: ROPE; first: CHAR; args _ CommandTool.ParseToList[cmd, TRUE ! 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; Process.CheckForAbort[]; ENDLOOP; EXITS oops => result _ $Failure; }; Sleep: Commander.CommandProc = { seconds: INT; secondsCard: CARDINAL; argv: CommandTool.ArgumentVector _ CommandTool.Parse[cmd, TRUE ! 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 _ CommandTool.Parse[cmd, TRUE ! 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 _ CommandTool.Parse[cmd, TRUE ! 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]; Process.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.PutF1[out, "Creating checkpoint at %g\n", [time[BasicTime.Now[]]]]; IO.PutF1[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]]]; }; 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, NOT un]; }; Register["-", Comment, "Comment", NIL, TRUE]; Register["--", Comment, "Comment", NIL, TRUE]; Register["/", Comment, "Comment", NIL, TRUE]; Register["//", Comment, "Comment", NIL, TRUE]; Register["?", HelpCommand, "List registered commands"]; Register["??", HelpCommand, "List registered commands (including those not yet loaded)", $Full]; Register["Help", HelpCommand, "List registered commands"]; Register["HelpFull", HelpCommand, "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["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