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 = { el: LIST OF REF ANY; eCell: REF ECell; matchList: LIST OF Rope.ROPE _ NIL; argv: CommandTool.ArgumentVector; lookupCmd: Commander.Handle _ NEW[Commander.CommandObject]; i: NAT _ 1; flag: BOOL _ FALSE; EProc: PROC [key: Rope.ROPE, procData: Commander.CommandProcHandle] RETURNS [stop: BOOL] = { ProcessExtras.CheckForAbort[]; IF procData # NIL THEN { 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 { el _ CONS[NEW[ECell _ [name: key, doc: procData.doc]], el]; ProcessExtras.CheckForAbort[]; EXIT; }; ENDLOOP; }; RETURN[FALSE]; }; FSProc: FS.NameProc = { commandName: Rope.ROPE _ Rope.Substr[base: fullFName, start: 0, len: Rope.Find[s1: fullFName, pos1: 0, s2: ".load", case: FALSE]]; 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; 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]; DO match: Rope.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; 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 { cmd.out.PutRope["Usage: "]; cmd.out.PutRope[cmd.command]; cmd.out.PutRope[" list-of-patterns ...\n"]; RETURN; }; lookupCmd.err _ cmd.out; FOR list: LIST OF Rope.ROPE _ matchList, list.rest WHILE list # NIL DO IF list.first.Find["*"] = -1 THEN { 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 { 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]; 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 ANY _ NARROW[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 { RETURN[$Failure, "No matching registered commands."]; }; 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.ROPE _ NIL] = { loadStream: IO.STREAM; line: Rope.ROPE; loadStream _ FS.StreamOpen[fileName: fName ! FS.Error => IF error.group # bug THEN CONTINUE]; IF loadStream = NIL THEN RETURN; 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: BOOLEAN _ FALSE, 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 [BOOL _ FALSE] = {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 TEXT _ NEW[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.STREAM _ NIL; block: REF TEXT _ NEW[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: 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", 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 À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 [cmd: Handle] RETURNS [result: REF _ NIL, msg: ROPE _ NIL] see if we have a match there is a match with the command name, so add it to the list (once only) strip off the .load and any version number Linear search the batch already loaded and don't bother with duplicates At this point, we've got the fullFName of a new .load file First, construct the list of patterns to match against the command names. Preprocess it! If no patterns given, remind the user to supply at least one. First handle any non-star cases using LookupProc This one has no pattern Try looking for a .load file Now search the file system If no matches found, indicate this result explicitly. Now there has been at least one match, so sort the results and display them to the user. Now we want the last line of the first group of comments ʾ˜šœ™J™&J™'J™&—J˜šÏk ˜ Jšœ œS˜bJšœœ˜!J˜ Jšœœ˜(Jšœ œ ˜±Jšœœ0˜=Jšœ œ ˜Jšœ œ[˜jJšœœ2˜:Jšœœ~œ˜šJšœœ3˜=Jšœœ,˜9Jšœœ˜$Jšœœ˜ JšœœDœ ˜\J˜ Jšœœ˜,Jšœ œ˜Jšœœ˜Jšœœ ˜Jšœ œ˜Jšœ œ˜ —J˜Jšœœ˜"š˜Jšœ]œœ€˜ã—Jš˜J˜šœ˜Jšœœ@œ˜QJ˜—J˜šœ˜Jšœ œ˜-Jšœœ˜'Jšœœ˜J˜—J˜šœ!œ˜*Jšœœ#œ#˜fJšœ!œœ#˜aJšœœ:˜UJ˜—J˜šœœ˜'Jšœ(˜.J˜—J˜šœœ˜'Jšœ!˜!Jšœ œ˜JšœMœ˜ZJšœœœœ˜)Jšœœœ œ ˜5Jšœ˜£J˜—J˜Jšœœœœ˜,J˜šœ,˜,Jšœ:™:Jš œœœœœ˜Jšœœ˜Jš œ œœœœ˜#Jšœ!˜!Jšœœ˜;Jšœœ˜ Jšœœœ˜š Ïnœœ œ)œœ˜\J˜šœ œœ˜Jšœ™š œœœœœœ˜FJšœœœÏc˜=Jšœœ˜ šœœœ˜&JšœI™IJšœœœ.˜;J˜Jšœ˜J˜—Jšœ˜—J˜—Jšœœ˜J˜—šœœ ˜J™*Jšœœdœ˜‚J™Gšœœœœœœœ˜=Jšœœ œœœœœ˜XJšœ˜—Jšœ:™:Jšœœœ:˜GJ˜Jšœœ˜ Jšœ˜—šœ˜Jšœœ˜Jšœœ˜Jšœœ˜Jšœœ˜,J˜—JšœMœ˜ZJšœœœœ˜)JšœI™Iš˜Jšœ œœ˜J˜Jšœœœ˜Jšœ˜J˜ Jšœœœœ˜2J™šœœŸ ˜'Jšœ œ˜Jšœ˜J˜—šœ˜Jšœ-˜-Jšœœœœ!˜mJšœ&œ!˜MJ˜—Jšœ œ˜#Jšœ˜—šœ œœ˜Jšœ=™=Jšœ˜Jšœ˜Jšœ+˜+Jšœ˜J˜—J™0J˜š œœœœœœ˜Fšœœ˜#J™Jšœ˜Jšœœ˜Jšœœ˜Jšœ*˜*Jšœ%˜%šœœœ˜"Jšœœœ'˜JJšœœœœ˜{J˜—šœ˜J™Jšœœ`œP˜ËJšœœjœ˜ˆJšœœ'˜9Jšœ,œœ˜TJ˜—J˜J˜—Jšœ˜—J˜ J™š œœœœœœ˜FJšœœœŸ˜=J˜Jšœœ%˜;Jšœ=œ˜Dšœœœ˜?Jš œœœœœœ9˜Xšœ œ˜Jšœ=œœ˜_Jšœ˜Jšœ˜—J˜—J˜Jšœ˜—š œœœœœ˜"Jšœ5™5Jšœ/˜5J˜—JšœX™Xšœœœ˜J˜3Jšœ1˜1šœœ˜Jšœœ ˜Jšœœœ˜DJ˜ J˜Jšœ˜—J˜—J˜—J˜š žœœœœœœ˜NJšœ œœ˜Jšœ œ˜Jš œ œœ œœœ˜]Jšœœœœ˜ J™8š˜Jšœœ˜ Jšœ œœ˜J˜—J˜Jšœ-œ-˜]Jšœ,œ,˜[J˜šœœœ˜Jšœœ˜Jšœ˜Jšœ œ˜Jšœ ˜J˜—J˜Jšœ œ˜J˜šœ œœ˜Jšœœœ˜5J˜J˜—Jšœœ œ˜0J˜šœ œ˜+šœœœ˜šœœœ˜šœ˜Jšœ ˜ Jšœ&˜&Jšœ%˜%Jšœ)˜)J˜—Jšœ˜J˜—Jšœ˜—J˜—J˜šœ œ˜*Jš žœœœœœœ˜Hšœœœ˜šœœ˜Jšœœ˜Jšœœ˜Jšœœ˜ Jšœœ˜Jšœ;˜;Jšœ˜JšœR˜RJšœ:œœœ˜RJšœSœ˜`Jšœ œœ˜CJšœœœœ ˜IJšœ9˜@Jšœœœ#œ ˜PJšœ˜Jšœœ˜Jšœ˜J˜—Jšœ˜—J˜—J˜šœ%˜%Jšœœœ5˜@Jš œœœœœ˜Jšœœ˜ Jšœœœœœœœœœœ˜BJšœœœ*˜@šœœ˜Jšœr˜rJšœp˜pJ˜—šœ˜Jšœm˜mJšœk˜kJ˜—J˜—J˜šœ˜Jšœ!˜!Jšœ˜JšœHœ˜UJšœœœœ˜)šœœœ˜J˜J˜J˜Jšœ˜—J˜J˜—J˜šœ+˜+Jš œœœœœ˜Jšœœ9˜GJšœ˜Jšœ œœ˜(šœ œ˜Jšœœœ˜0Jšœ˜Jšœ˜J˜Jšœ˜—Jšœ˜J˜—J˜šœ)˜)Jšœœœœ˜Jšœ œ˜Jšœœ˜ Jšœ˜JšœNœ ˜`Jšœœœœ˜)Jšœ+œ:œ˜‰šœœ˜Jšœ˜Jšœ)˜)Jšœœœ;˜ZJšœ˜Jšœ œ œœ5˜]Jšœ6œ˜šœœ˜Jšœ ˜ Jšœ ˜ J˜—šœ˜JšœIœ˜VJšœœœœ˜(Jš œœœ œœ(˜KJšœ˜J˜—Jšœ œœ ˜Jš œœœœœ˜7Jšœ˜š˜Jš œœÏr œœœ˜/šœ%˜%Jšœ¡Ðkr¡ ˜ Jšœ¡ ¡˜Jšœ¢¡ ˜%Jšœ˜—Jšœ!œ¡ œ˜3Jšœœ¡ œ˜1Jšœœ¡ œ˜(Jšœ˜—Jš˜Jšœ œ˜J˜—J˜šœ˜Jšœ!˜!Jšœ œœœ˜Jš œœœœœ˜!Jšœœ˜ J˜Jš œœœœœ˜=Jšœ˜JšœHœ˜UJšœœœœ˜)Jšœœœ"˜>šœœœ8œ ˜jJšœœ˜4Jšœ˜ Jšœ˜—š˜Jšœ˜Jšœœœ˜šœ'˜'Jšœ¡¡˜Jšœ¡ ¡˜Jšœ¡˜Jšœ˜—Jšœ œœ˜Jš œœœ$œ¡ œ˜NJšœ œ¡ œ˜2J˜Jšœ˜—Jšœœ¡ œ˜*Jš˜Jšœœ˜J˜J˜—J˜šœœ˜(Jšœœ œ˜;J˜—J˜šœ ˜ JšœJ˜JJ˜—J˜šœ&˜&JšœJ˜JJ˜—J˜šœ˜Jšœ5˜5Jšœ˜Jšœœœ˜%Jšœœ˜Jšœœœ˜Jš œœœœœ˜JšœJ˜JJšœ"˜"Jšœ<˜˜>Jšœ9˜9J˜J˜cJšœZ˜ZJšœ‘˜‘J˜•Jšœ`˜`J˜TJšœS˜SJ˜ZJ˜`Jšœ`˜`J˜SJ˜^Jšœs˜sJšœi˜iJšœ®˜®Jšœe˜eJšœG˜GJšœu˜uJšœa˜aJ˜oJšœf˜fJšœb˜bJšœF˜FJšœa˜aJ˜]J˜aJ˜—J˜J˜J˜Jšœ˜J˜'J˜PJ˜,J˜2J˜1Jšœ>˜>J˜J˜J˜J˜J˜—…—Xtsò