<> <> <> <> DIRECTORY AMFiles USING [pathPrefixes], BcdDefs USING [NullVersion, VersionStamp, BcdBase, VersionID], Buttons USING [ButtonProc, Create], CedarProcess USING [DoWithPriority], Commander USING [CommandObject, CommandProcHandle, CommandProcObject, CommandProc, Handle, Register], CommandTool USING [AmpersandSubstitution, ArgumentVector, CallList, CommandFile, ConvertToSlashFormat, CopyAList, CopyListOfRefAny, DollarSubstitution, ExecuteCommand, Failed, GetProp, Insulate, IORedirection, Parse, ParseToList, Pass1, PutLocalProperty, ResolveRelativePath, StarExpansion], CommandToolLookup USING [DoLookup, FindMatchingCommands, FindMatchingFiles, ShowAmbiguous], EndOps USING [Register], FileNames USING [ConvertToSlashFormat, CurrentWorkingDirectory, Directory, IsADirectory, ResolveRelativePath], FS USING [Close, Error, GetName, nullOpenFile, Open, OpenFile, StreamOpen, Read], IO USING [Close, EndOf, Error, Flush, GetChar, GetInfo, noInputStream, noWhereStream, PutChar, PutF, PutF1, PutFR1, PutRope, RIS, RopeFromROS, ROS, SetIndex, STREAM], IOClasses USING [CreatePipe], List USING [AList, Assoc, PutAssoc], Loader USING [Error, Instantiate, IRItem, Start], LoadState USING [Acquire, ConfigID, ConfigInfo, EnumerateConfigs, local, nullConfig, Release], Menus USING [CreateEntry, InsertMenuEntry, MenuProc], PrincOps USING [ControlModule, NullControl], Process USING [Abort, CheckForAbort, Detach, GetCurrent, InvalidProcess], ProcessProps USING [AddPropList, GetPropList, PushPropList], ReadEvalPrint USING [ClientProc, CreateStreamEvaluator, CreateViewerEvaluator, Handle, MainLoop, RObject], Rope USING [Cat, Concat, Equal, Fetch, Flatten, Find, FromChar, Index, IsEmpty, Length, Match, ROPE, Substr], RopeList USING [CopyTopList, DAppend, Memb], UserCredentials USING [Get], ViewerClasses USING [Viewer], ViewerIO USING [CreateViewerStreams, GetViewerFromStream], ViewerOps USING [PaintViewer], VM USING [AddressForPageNumber, Allocate, Free, Interval]; CommandToolImpl: CEDAR MONITOR IMPORTS AMFiles, Buttons, CedarProcess, Commander, CommandTool, CommandToolLookup, EndOps, FileNames, FS, IO, IOClasses, List, Loader, LoadState, Menus, Process, ProcessProps, ReadEvalPrint, Rope, RopeList, UserCredentials, ViewerIO, ViewerOps, VM EXPORTS CommandTool = BEGIN ROPE: TYPE = Rope.ROPE; STREAM: TYPE = IO.STREAM; defaultPrompt: ROPE _ "%l%% %l"; Controls: TYPE = RECORD [ terminator: CHAR _ '\n, uninterpreted: BOOL _ FALSE, quitOnFailure: BOOL _ FALSE, background: BOOL _ FALSE, createViewer: BOOL _ FALSE, pipe: BOOL _ FALSE, inRedirected: BOOL _ FALSE, outRedirected: BOOL _ FALSE, starExpand: BOOL _ FALSE ]; EachCommand: PUBLIC ReadEvalPrint.ClientProc = { <> cmd: Commander.Handle; propertyList: List.AList _ NIL; errorOut: STREAM _ NIL; remainingCommands: ROPE; someExpansion: BOOL; expansion: BOOL _ FALSE; cth: Commander.Handle; pipePullStream: STREAM _ NIL; lastControls: Controls _ []; controls: Controls _ []; { -- solely for exits clauses cth _ NARROW[h.clientData]; IF cth = NIL THEN ERROR; -- there must be one! <> IF h.viewer = NIL THEN h.out.PutRope[command]; propertyList _ cth.propertyList; errorOut _ cth.err; <<>> <> remainingCommands _ command; DO result _ NIL; IF Rope.IsEmpty[remainingCommands] THEN { <> IF pipePullStream # NIL THEN result _ "[[Last command fed a pipe, output discarded]]"; GOTO PipeClose; }; <<>> <> cmd _ NEW[Commander.CommandObject _ []]; cmd.err _ errorOut; cmd.propertyList _ propertyList; controls _ []; <> [first: cmd.command, rest: remainingCommands, terminator: controls.terminator, someExpansion: someExpansion] _ CommandTool.Pass1[initial: remainingCommands, nameOnly: TRUE ! CommandTool.Failed => { IF pipePullStream # NIL THEN errorMsg _ Rope.Concat[ errorMsg, "\n Previous command fed a pipe, output discarded."]; GOTO PipeClose; }]; expansion _ expansion OR someExpansion; <> cmd.procData _ NIL; IF cmd.command.IsEmpty[] THEN { controls.uninterpreted _ FALSE; controls.starExpand _ FALSE; } ELSE { LookupCommand[cmd]; IF cmd.procData = NIL THEN controls.uninterpreted _ TRUE ELSE { controls.uninterpreted _ NOT cmd.procData.interpreted; controls.starExpand _ FALSE; }; }; remainingCommands _ Rope.Cat[cmd.commandLine, Rope.FromChar[controls.terminator], remainingCommands]; IF controls.uninterpreted THEN { pos: INT _ Rope.Index[s1: remainingCommands, pos1: 0, s2: "\n", case: FALSE] + 1; line: ROPE _ Rope.Substr[base: remainingCommands, start: 0, len: pos]; IF Rope.Length[line] < pos THEN { <> line _ Rope.Concat[line, "\n"]; remainingCommands _ NIL; } ELSE <> remainingCommands _ Rope.Substr[base: remainingCommands, start: pos]; cmd.commandLine _ line; controls.terminator _ '\n; } ELSE { <> [first: cmd.commandLine, rest: remainingCommands, terminator: controls.terminator, someExpansion: someExpansion] _ CommandTool.Pass1[initial: remainingCommands, nameOnly: FALSE ! CommandTool.Failed => { IF pipePullStream # NIL THEN errorMsg _ Rope.Concat[errorMsg, "\n Previous command fed a pipe, output discarded."]; result _ errorMsg; GOTO PipeClose; }]; expansion _ expansion OR someExpansion; }; <> IF cmd.procData = NIL THEN { IF pipePullStream # NIL THEN { result _ "[[Last command fed a pipe, output discarded.]]"; GOTO PipeClose; }; lastControls _ controls; Process.CheckForAbort[]; LOOP; }; <> IF controls.uninterpreted THEN { IF pipePullStream # NIL THEN { result _ "[[Cannot pipe into an uninterpreted command]]"; GOTO PipeClose; }; } ELSE { <> <> SELECT controls.terminator FROM '\n => NULL; '; => controls.quitOnFailure _ TRUE; '& => controls.background _ controls.createViewer _ TRUE; '| => controls.background _ controls.pipe _ TRUE; ENDCASE => { result _ IO.PutFR1["[[Unknown terminating character: %g]]", [character[controls.terminator]] ]; GOTO PipeClose; }; { ENABLE CommandTool.Failed => { result _ errorMsg; GO TO failed; }; CommandTool.AmpersandSubstitution[cmd]; CommandTool.DollarSubstitution[cmd]; IF controls.starExpand THEN CommandTool.StarExpansion[cmd]; [inRedirected: controls.inRedirected, outRedirected: controls.outRedirected] _ CommandTool.IORedirection[cmd]; EXITS failed => {}; }; IF result # NIL THEN { result _ Rope.Cat["[[", result, "]]"]; GOTO PipeClose; -- discards remaining commands }; <> <<>> <> IF controls.pipe AND controls.outRedirected THEN { result _ "[[Cannot redirect output and have a pipe]]"; GOTO PipeClose; -- discards remaining commands on line }; IF lastControls.pipe AND controls.inRedirected THEN { result _ "[[Previous command was a pipe, cannot redirect input]]"; GOTO PipeClose; -- discards remaining commands on line }; <> controls.createViewer _ controls.terminator = '& AND NOT ((controls.inRedirected AND controls.outRedirected) OR (lastControls.pipe AND controls.outRedirected)); <> IF controls.createViewer THEN { viewerOut: STREAM; viewerIn: STREAM; <> [in: viewerIn, out: viewerOut] _ ViewerIO.CreateViewerStreams[ name: Rope.Flatten[Rope.Cat[cmd.command, " ", cmd.commandLine], 0, 256]]; cmd.err _ viewerOut; <> cmd.propertyList _ CommandTool.PutLocalProperty[key: $ErrorInputStream, val: viewerIn, aList: cmd.propertyList, origList: cmd.propertyList]; { v: ViewerClasses.Viewer _ ViewerIO.GetViewerFromStream[viewerOut]; Menus.InsertMenuEntry[v.menu, Menus.CreateEntry["STOP!", StopHit, cmd] ]; ViewerOps.PaintViewer[v, menu]; }; IF cmd.in = NIL THEN cmd.in _ viewerIn; IF cmd.out = NIL THEN cmd.out _ viewerOut; }; <> IF lastControls.pipe THEN { cmd.in _ pipePullStream; pipePullStream _ NIL; -- note that the pipe is finished! }; IF controls.pipe THEN { pipePushStream: STREAM; [push: pipePushStream, pull: pipePullStream] _ IOClasses.CreatePipe[]; cmd.out _ pipePushStream; pipePushStream _ NIL; }; }; <<>> <> IF cmd.in = NIL THEN cmd.in _ CommandTool.Insulate[cth.in]; IF cmd.out = NIL THEN cmd.out _ CommandTool.Insulate[cth.out]; <> IF expansion THEN h.out.PutF["[[%g%g]]\n", [rope[cmd.command]], [rope[Rope.Substr[cmd.commandLine, 0, cmd.commandLine.Length[] - 1]]]]; <<>> { <> savedOutStream: STREAM _ cmd.out; ch: Commander.Handle _ cmd; cmd.out _ h.out; CommandTool.CallList[property: $Before, cmd: cmd, proc: NIL]; cmd.out _ savedOutStream; IF controls.background THEN ch _ NEW[Commander.CommandObject _ cmd^]; ExecuteCommand[cmd: cmd, background: controls.background]; ch.out _ h.out; CommandTool.CallList[property: $After, cmd: ch, proc: NIL]; }; <> WITH List.Assoc[key: $Prompt, aList: propertyList] SELECT FROM rope: ROPE => h.prompt _ rope; ENDCASE => h.prompt _ defaultPrompt; Process.CheckForAbort[]; <<>> lastControls _ controls; <> IF controls.quitOnFailure AND CommandTool.GetProp[cmd, $Result] = $Failure THEN { IF pipePullStream # NIL THEN ERROR; -- can't happen! RETURN["[[Command failed]]"]; }; ENDLOOP; EXITS PipeClose => { IF pipePullStream # NIL THEN { pipePullStream.Close[! IO.Error => CONTINUE; ]; pipePullStream _ NIL; }; }; }; }; LookupCommand: PUBLIC PROC [cmd: Commander.Handle] = { <> err: STREAM = cmd.err; name: ROPE = cmd.command; list: LIST OF ROPE; [list, cmd.procData] _ CommandToolLookup.DoLookup[cmd, name]; SELECT TRUE FROM list = NIL => {IO.PutF1[err, "[[%g not found]]\n", [rope[name]] ]; GO TO fail}; list.rest # NIL => {CommandToolLookup.ShowAmbiguous[err, list]; GO TO fail}; cmd.procData = NIL => { <> cmd.procData _ NEW[Commander.CommandProcObject _ [ ReallyLoadAndRun, "Command not yet loaded", NEW[SavedCommandObject _ [command: name, commandFileName: list.first, runCommandAfter: Rope.Match["*.load*", list.first, FALSE]]]]]; }; ENDCASE; cmd.command _ list.first; EXITS fail => cmd.procData _ NIL; }; SavedCommand: TYPE = REF SavedCommandObject; SavedCommandObject: TYPE = RECORD [ command: ROPE _ NIL, commandFileName: ROPE _ NIL, runCommandAfter: BOOL _ FALSE ]; commandFileProcData: Commander.CommandProcHandle _ NEW[Commander.CommandProcObject _ [CommandTool.CommandFile]]; ReallyLoadAndRun: Commander.CommandProc = { <> sc: SavedCommand _ NARROW[cmd.procData.clientData]; commandFileName: ROPE _ sc.commandFileName; originalCommandLine: ROPE _ cmd.commandLine; oldOut: IO.STREAM; oldIn: IO.STREAM; loadFileDirectory: ROPE; inner: PROC = { ENABLE UNWIND => {cmd.out _ oldOut; cmd.in _ oldIn}; [result, msg] _ CommandTool.CommandFile[cmd]; cmd.out _ oldOut; cmd.in _ oldIn; }; commandFileName _ FileNames.ConvertToSlashFormat[commandFileName]; loadFileDirectory _ FileNames.Directory[path: commandFileName]; cmd.command _ "CommandTool"; cmd.commandLine _ Rope.Concat[commandFileName, originalCommandLine]; cmd.procData _ commandFileProcData; oldOut _ cmd.out; cmd.out _ CommandTool.Insulate[cmd.err]; oldIn _ cmd.in; cmd.in _ CommandTool.Insulate[oldIn]; <> ProcessProps.AddPropList[ List.PutAssoc[key: $WorkingDirectory, val: loadFileDirectory, aList: NIL], inner]; IF result = $Failure THEN RETURN[result, msg]; cmd.command _ sc.command; cmd.commandLine _ originalCommandLine; IF sc.runCommandAfter THEN { cmd.procData _ CommandToolLookup.FindMatchingCommands[cmd.command, FALSE, CommandTool.GetProp[cmd, $SearchRules]].data; IF cmd.procData # NIL AND cmd.procData.proc # NIL THEN { <> CommandTool.ExecuteCommand[cmd, FALSE]; result _ CommandTool.GetProp[cmd, $Result]; } ELSE RETURN[$Failure, ".load file failed to register command"]; }; }; StopHit: Menus.MenuProc = TRUSTED { <<[parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL]>> WITH clientData SELECT FROM cmd: Commander.Handle => { WITH CommandTool.GetProp[cmd, $ForkedProcess] SELECT FROM rp: REF PROCESS => { Process.Abort[rp^ ! Process.InvalidProcess => CONTINUE]; }; ENDCASE; }; ENDCASE; }; Base: PROC [list: List.AList, cmd: Commander.Handle, detached: BOOL] = { cleanup: PROC = { <> IF cmd.in # NIL THEN cmd.in.Close[! IO.Error => CONTINUE]; IF cmd.out # NIL THEN { cmd.out.Flush[! IO.Error => CONTINUE]; cmd.out.Close[! IO.Error => CONTINUE]; }; IF cmd.err # NIL THEN { cmd.err.Flush[! IO.Error => CONTINUE]; }; IF detached THEN <> cmd.propertyList _ NIL; }; innerExecute: PROC = { ENABLE UNWIND => cleanup[]; result: REF ANY; msg: ROPE; out: STREAM _ cmd.out; originalPropertyList: List.AList _ cmd.propertyList; [result: result, msg: msg] _ cmd.procData.proc[cmd: cmd ! ABORTED => IF detached THEN IO.PutRope[out, "\n-- Aborting ... "] ]; IF msg # NIL THEN { out.PutRope[msg]; IF msg.Fetch[msg.Length[] - 1] # '\n THEN out.PutChar['\n]; }; IF detached AND IO.GetInfo[out].class # $Pipe THEN IO.PutRope[out, IF result = $Failure THEN "-- Failed!\n" ELSE "-- Done.\n"] ELSE cmd.propertyList _ List.PutAssoc[key: $Result, val: result, aList: originalPropertyList]; }; IF detached THEN TRUSTED { <> rp: REF PROCESS _ NEW[PROCESS _ LOOPHOLE[Process.GetCurrent[], PROCESS]]; cmd.propertyList _ CommandTool.PutLocalProperty[key: $ForkedProcess, val: rp, aList: cmd.propertyList, origList: cmd.propertyList]; }; <
> { <> ENABLE IO.Error => { IF ec = StreamClosed AND (stream = cmd.in OR stream = cmd.out) THEN GO TO closed; }; IF list # NIL THEN ProcessProps.PushPropList[list, innerExecute] ELSE innerExecute[]; EXITS closed => {}; }; <> cleanup[]; }; ExecuteCommand: PUBLIC PROC [cmd: Commander.Handle, background: BOOL] = { IF background THEN { <> processProperties: List.AList _ CommandTool.CopyAList[ProcessProps.GetPropList[]]; <> processProperties _ List.PutAssoc[key: $CommanderHandle, val: cmd, aList: processProperties]; TRUSTED { Process.Detach[FORK Base[list: processProperties, cmd: cmd, detached: background]]; }; } ELSE { <> [] _ List.PutAssoc[key: $CommanderHandle, val: cmd, aList: ProcessProps.GetPropList[]]; Base[list: NIL, cmd: cmd, detached: background]; }; }; DoCommand: PUBLIC PROC [commandLine: ROPE, parent: Commander.Handle] RETURNS [result: REF ANY] = { <> rep: ReadEvalPrint.Handle; oldREP: ReadEvalPrint.Handle; cll: INT _ commandLine.Length[]; msg: ROPE; rep _ NEW[ReadEvalPrint.RObject _ [menuHitQueue: NIL]]; IF cll = 0 THEN RETURN[NIL]; IF commandLine.Fetch[cll - 1] # '\n THEN commandLine _ Rope.Concat[commandLine, "\n"]; IF parent = NIL THEN { parent _ NEW[Commander.CommandObject _ []]; parent.err _ IO.noWhereStream; parent.out _ IO.noWhereStream; parent.in _ IO.noInputStream; parent.propertyList _ List.PutAssoc[ key: $ErrorInputStream, val: IO.noInputStream, aList: parent.propertyList]; parent.propertyList _ List.PutAssoc[ key: $Prompt, val: defaultPrompt, aList: parent.propertyList]; parent.propertyList _ List.PutAssoc[ key: $SearchRules, val: ConsRope[ IO.PutFR1["///Users/%g", [rope[UserCredentials.Get[].name]]], ConsRope["///Commands/", ConsRope["///"]]], aList: parent.propertyList]; }; rep.prompt _ NARROW[CommandTool.GetProp[parent, $Prompt]]; oldREP _ NARROW[CommandTool.GetProp[parent, $ReadEvalPrintHandle]]; IF rep.prompt.IsEmpty[] THEN rep.prompt _ defaultPrompt; rep.out _ parent.out; rep.clientData _ parent; rep.viewer _ IF oldREP # NIL THEN oldREP.viewer ELSE NIL; IF rep.viewer # NIL THEN { parent.out.PutF[rep.prompt, [rope["b"]], [rope["B"]] ]; parent.out.PutRope[commandLine]; }; msg _ EachCommand[h: rep, command: commandLine]; parent.out.PutRope[msg]; IF msg.Length[] > 0 AND msg.Fetch[msg.Length[] - 1] # '\n THEN parent.out.PutRope["\n"]; result _ CommandTool.GetProp[parent, $Result]; }; ConsRope: PROC [r: ROPE, list: LIST OF REF ANY _ NIL] RETURNS [LIST OF REF ANY] = { RETURN [CONS[r, list]]; }; DoCommandRope: PUBLIC PROC [commandLine, in: ROPE _ NIL, parent: Commander.Handle] RETURNS [out: ROPE, result: REF ANY] = { <> outS: STREAM _ IO.ROS[]; rep: ReadEvalPrint.Handle; oldREP: ReadEvalPrint.Handle _ NIL; cmd: Commander.Handle _ NEW[Commander.CommandObject _ []]; rep _ NEW[ReadEvalPrint.RObject _ [menuHitQueue: NIL]]; cmd.in _ IO.RIS[in]; cmd.out _ CommandTool.Insulate[outS]; IF parent # NIL THEN { cmd.err _ parent.err; cmd.propertyList _ parent.propertyList; oldREP _ NARROW[CommandTool.GetProp[parent, $ReadEvalPrintHandle]]; rep.prompt _ NARROW[CommandTool.GetProp[parent, $Prompt]]; rep.out _ parent.out; } ELSE { cmd.propertyList _ NIL; cmd.err _ IO.noWhereStream; rep.out _ IO.noWhereStream; cmd.propertyList _ List.PutAssoc[key: $ErrorInputStream, val: IO.noInputStream, aList: cmd.propertyList]; cmd.propertyList _ List.PutAssoc[key: $Prompt, val: defaultPrompt, aList: cmd.propertyList]; }; IF rep.prompt.IsEmpty[] THEN rep.prompt _ defaultPrompt; rep.clientData _ cmd; rep.viewer _ IF oldREP # NIL THEN oldREP.viewer ELSE NIL; <<>> rep.out.PutRope[EachCommand[h: rep, command: commandLine]]; result _ CommandTool.GetProp[parent, $Result]; out _ IO.RopeFromROS[outS ! IO.Error => CONTINUE]; outS.Close[]; }; Create: PROC [parentCommander: Commander.Handle, newViewer: BOOL, fork: BOOL, copyProps: BOOL, readProfile: BOOL _ FALSE] = { <> <>> <> <<(see CommandToolBase for the code to do this)>> <>> <> <> <> <> <> <> cmd: Commander.Handle _ NEW[Commander.CommandObject]; readEvalPrint: ReadEvalPrint.Handle; originalReadEvalPrint: ReadEvalPrint.Handle; prompt: ROPE; errorInputStream: STREAM; props: List.AList _ NIL; inner: PROC = { <> fork _ fork OR newViewer; <> copyProps _ copyProps OR fork; <<>> IF parentCommander # NIL THEN { props _ parentCommander.propertyList; IF copyProps THEN { <> props _ CommandTool.CopyAList[props]; <<>> <> props _ CommandTool.CopyListOfRefAny[key: $Before, aList: props]; props _ CommandTool.CopyListOfRefAny[key: $After, aList: props]; props _ CommandTool.CopyListOfRefAny[key: $SearchRules, aList: props]; <<>> <> props _ List.PutAssoc[key: $ParentCommander, val: parentCommander, aList: props]; }; }; <> prompt _ NARROW[List.Assoc[key: $Prompt, aList: props]]; IF prompt.Length[] = 0 THEN { props _ List.PutAssoc[key: $Prompt, val: defaultPrompt, aList: props]; prompt _ defaultPrompt; }; <> IF NOT copyProps THEN originalReadEvalPrint _ NARROW[List.Assoc[key: $ReadEvalPrintHandle, aList: props]]; <<>> <> IF newViewer OR parentCommander = NIL THEN { readEvalPrint _ ReadEvalPrint.CreateViewerEvaluator[clientProc: EachCommand, prompt: prompt, info: [name: Rope.Concat["CommandTool: WD = ", FileNames.CurrentWorkingDirectory[]], column: right, iconic: FALSE], edited: TRUE, deliverWhen: NIL, clientData: cmd, topLevel: TRUE]; cmd.err _ readEvalPrint.out; errorInputStream _ readEvalPrint.in; } ELSE { readEvalPrint _ ReadEvalPrint.CreateStreamEvaluator[clientProc: EachCommand, prompt: prompt, in: parentCommander.in, out: parentCommander.out, deliverWhen: NIL, clientData: cmd, topLevel: FALSE]; cmd.err _ parentCommander.err; <> errorInputStream _ NARROW[List.Assoc[key: $ErrorInputStream, aList: props]]; }; <> cmd.in _ readEvalPrint.in; cmd.out _ readEvalPrint.out; <> props _ List.PutAssoc[key: $ErrorInputStream, val: errorInputStream, aList: props]; props _ List.PutAssoc[key: $ReadEvalPrintHandle, val: readEvalPrint, aList: props]; WITH List.Assoc[key: $SearchRules, aList: props] SELECT FROM list: LIST OF REF ANY => { <> }; ENDCASE => { <> rope: ROPE _ "///Commands/"; props _ List.PutAssoc[key: $SearchRules, val: LIST[rope], aList: props]; }; IF parentCommander # NIL THEN { cmd.commandLine _ parentCommander.commandLine; cmd.command _ parentCommander.command; cmd.procData _ parentCommander.procData; } ELSE { cmd.commandLine _ NIL; cmd.command _ NIL; cmd.procData _ NIL; }; cmd.propertyList _ props; <<>> TRUSTED { processProperties: List.AList _ ProcessProps.GetPropList[]; <> IF copyProps THEN processProperties _ CommandTool.CopyAList[processProperties]; <<>> <> processProperties _ List.PutAssoc[key: $CommanderHandle, val: cmd, aList: processProperties]; IF fork THEN TRUSTED { Process.Detach[FORK CommandToolBase[ props: IF copyProps THEN processProperties ELSE NIL, rep: readEvalPrint, readProfile: readProfile, readInit: newViewer]]; } ELSE CommandToolBase[ props: IF copyProps THEN processProperties ELSE NIL, rep: readEvalPrint, readProfile: readProfile, readInit: newViewer]; <<>> <> IF parentCommander # NIL AND NOT copyProps THEN { parentCommander.propertyList _ List.PutAssoc[ key: $ReadEvalPrintHandle, val: originalReadEvalPrint, aList: parentCommander.propertyList]; [] _ List.PutAssoc[ key: $CommanderHandle, val: parentCommander, aList: processProperties]; }; }; }; CedarProcess.DoWithPriority[priority: normal, action: inner]; }; CommandToolBase: PROC [props: List.AList, rep: ReadEvalPrint.Handle, readProfile: BOOL, readInit: BOOL] = { inner: PROC = { cmd: Commander.Handle _ NARROW[rep.clientData]; SELECT TRUE FROM readProfile => [] _ DoCommand["///Commands/NoteBootCommands\n", cmd]; readInit => [] _ DoCommand["///Commands/NotePerCommandTool\n", cmd]; ENDCASE; ReadEvalPrint.MainLoop[h: rep, forkAndDetach: FALSE, properties: NIL]; }; IF props # NIL THEN ProcessProps.PushPropList[props, inner] ELSE inner[]; }; CreateCommanderButtonProc: Buttons.ButtonProc = { <> Create[parentCommander: NIL, newViewer: TRUE, copyProps: TRUE, fork: TRUE]; }; CommandFile: PUBLIC Commander.CommandProc = { argv: CommandTool.ArgumentVector; commandStream: STREAM; commandFileName: ROPE; source: BOOL _ cmd.procData.clientData = $Source; prompt: ROPE; list: LIST OF ROPE _ NIL; argv _ CommandTool.Parse[cmd: cmd ! CommandTool.Failed => { msg _ errorMsg; GO TO oops; }]; IF argv.argc = 1 THEN { <> IF source THEN {msg _ "Usage: Source commandFileName {argument list}"; GO TO oops}; <<>> <> Create[parentCommander: cmd, newViewer: TRUE, copyProps: TRUE, fork: TRUE]; RETURN; }; list _ CommandToolLookup.FindMatchingFiles[root: argv[1], defaultExtension: ".cm", requireExact: FALSE, searchRules: CommandTool.GetProp[cmd, $SearchRules]]; SELECT TRUE FROM list = NIL => { msg _ IO.PutFR1["[[Command file '%g' not found]]\n", [rope[argv[1]]] ]; GO TO oops; }; list.rest # NIL => { err: STREAM _ cmd.err; IO.PutRope[err, "[[Ambiguous command files:\n"]; FOR each: LIST OF ROPE _ list, each.rest WHILE each # NIL DO IO.PutF1[err, " %g\n", [rope[each.first]] ]; ENDLOOP; IO.PutRope[err, " ]]\n"]; GO TO oops; }; ENDCASE; commandFileName _ list.first; commandStream _ FS.StreamOpen[fileName: commandFileName ! FS.Error => IF error.group # bug THEN {msg _ error.explanation; GO TO oops; }]; <> WHILE NOT commandStream.EndOf[] DO c: CHAR _ commandStream.GetChar[]; IF c = 0C OR c >= 200C THEN { commandStream.Close[]; msg _ Rope.Concat[commandFileName, " appears to be a binary file"]; GO TO oops; }; Process.CheckForAbort[]; ENDLOOP; commandStream.SetIndex[0]; cmd.propertyList _ CommandTool.PutLocalProperty[key: $CommandFileArguments, val: argv, aList: cmd.propertyList, origList: cmd.propertyList]; <<>> < in it, append one, otherwise set the prompt to "> ">> prompt _ NARROW[CommandTool.GetProp[cmd, $Prompt]]; IF Rope.Find[s1: prompt, s2: ">"] # -1 THEN prompt _ Rope.Concat[">", prompt] ELSE prompt _ "%l%l> "; cmd.propertyList _ CommandTool.PutLocalProperty[key: $Prompt, val: prompt, aList: cmd.propertyList, origList: cmd.propertyList]; cmd.propertyList _ CommandTool.PutLocalProperty[key: $Result, val: NIL, aList: cmd.propertyList, origList: cmd.propertyList]; cmd.in _ commandStream; <<>> <> Create[parentCommander: cmd, newViewer: FALSE, copyProps: NOT source, fork: FALSE]; commandStream.Close[]; result _ CommandTool.GetProp[cmd, $Result]; EXITS oops => result _ $Failure; }; RunCommand: Commander.CommandProc = TRUSTED { startIfUnbound: BOOL _ TRUE; runAgain: BOOL _ FALSE; loadOnly: BOOL _ FALSE; argv: CommandTool.ArgumentVector _ CommandTool.Parse[cmd ! CommandTool.Failed => { msg _ errorMsg; GO TO oops; }]; IF argv = NIL THEN RETURN[$Failure, msg]; FOR i: NAT IN [1..argv.argc) DO arg: ROPE _ argv[i]; error: BOOL _ FALSE; errMsg: ROPE; cm: PrincOps.ControlModule _ PrincOps.NullControl; IF Rope.Fetch[arg, 0] = '- THEN { FOR j: INT IN [1..Rope.Length[arg]) DO SELECT Rope.Fetch[arg, j] FROM 'd => startIfUnbound _ FALSE; 'a => runAgain _ TRUE; 'l => loadOnly _ TRUE; ENDCASE; ENDLOOP; LOOP; }; [errMsg: errMsg, error: error] _ RunInternal[bcdName: FileNames.ResolveRelativePath[arg], runEvenIfAlreadyRun: runAgain, runEvenIfUnbound: startIfUnbound, loadOnly: loadOnly]; IF NOT errMsg.IsEmpty[] THEN IO.PutRope[cmd.out, errMsg]; IF error THEN {result _ $Failure; RETURN}; ENDLOOP; EXITS oops => result _ $Failure; }; Run: PUBLIC PROC [bcdName: ROPE, runEvenIfAlreadyRun, runEvenIfUnbound: BOOL _ FALSE] RETURNS [errMsg: ROPE _ NIL, error: BOOL _ FALSE] = { <> [errMsg: errMsg, error: error] _ RunInternal[bcdName, runEvenIfAlreadyRun, runEvenIfUnbound, FALSE]; }; RunInternal: PROC [bcdName: ROPE, runEvenIfAlreadyRun, runEvenIfUnbound, loadOnly: BOOL _ FALSE] RETURNS [errMsg: ROPE _ NIL, error: BOOL _ FALSE] = { bcdVersion: BcdDefs.VersionStamp _ BcdDefs.NullVersion; cm: PrincOps.ControlModule _ PrincOps.NullControl; f: FS.OpenFile _ FS.nullOpenFile; ros: STREAM = IO.ROS[]; put1: PROC [r1: ROPE _ NIL] = {IO.PutRope[ros, r1]}; put2: PROC [r1,r2: ROPE _ NIL] = {IO.PutRope[ros, r1]; IO.PutRope[ros, r2]}; shortName: ROPE _ bcdName _ FileNames.ResolveRelativePath[bcdName]; SELECT TRUE FROM Rope.Match["*.bcd", bcdName, FALSE] => {}; Rope.Match["*!*", bcdName, TRUE] => {}; ENDCASE => bcdName _ Rope.Concat[bcdName, ".bcd"]; {-- start scope for exits unboundImports: LIST OF Loader.IRItem _ NIL; name, fullFName, attachedTo: ROPE _ NIL; f _ FS.Open[bcdName ! FS.Error => {IO.PutRope[ros, error.explanation]; GO TO failed} ]; [fullFName, attachedTo] _ FS.GetName[f]; name _ IF attachedTo # NIL THEN attachedTo ELSE fullFName; IF NOT runEvenIfAlreadyRun THEN TRUSTED { <> duplicateFound: BOOL _ FALSE; LoadState.local.Acquire[]; { ENABLE UNWIND => LoadState.local.Release[]; lookAtConfig: SAFE PROC [config: LoadState.ConfigID] RETURNS [stop: BOOL _ FALSE] = TRUSTED { IF bcdVersion = LoadState.local.ConfigInfo[config].bcd.version THEN RETURN[TRUE]; }; bcdSpace: VM.Interval = VM.Allocate[count: 1]; { <> bcd: BcdDefs.BcdBase _ LOOPHOLE[VM.AddressForPageNumber[bcdSpace.page]]; FS.Read[file: f, from: 0, nPages: 1, to: LOOPHOLE[bcd] ! FS.Error => GO TO nope]; IF bcd.versionIdent = BcdDefs.VersionID AND NOT bcd.definitions AND bcd.spare1 THEN bcdVersion _ bcd.version; -- else error, which will be reported later EXITS nope => {}; }; VM.Free[bcdSpace]; IF bcdVersion # BcdDefs.NullVersion AND LoadState.local.EnumerateConfigs[newestFirst, lookAtConfig] # LoadState.nullConfig THEN duplicateFound _ TRUE; }; LoadState.local.Release[]; IF duplicateFound THEN { put2["Previously loaded and run: ", name]; GO TO done; }; }; TRUSTED { ENABLE { Loader.Error => { SELECT type FROM invalidBcd => put1["InvalidBcd["]; fileNotFound => put1["FileNotFound["]; versionMismatch => put1["VersionMismatch["]; loadStateFull => put1["LoadStateFull["]; insufficientVM => put1["InsufficientVM["]; ENDCASE => ERROR; put2[message, "]"]; GO TO failed; }; ABORTED => { put2["Loading aborted in ", name]; GO TO failed; }; }; [cm, unboundImports] _ Loader.Instantiate[f]; }; AddNewUniqueDebugRule[ FileNames.Directory[FileNames.ConvertToSlashFormat[fullFName]]]; IF unboundImports # NIL THEN { put1["\n-- Unbound imports { "]; FOR l: LIST OF Loader.IRItem _ unboundImports, l.rest UNTIL l = NIL DO IO.PutF[ros, "[%g,%d] ", [rope[l.first.interfaceName]], [integer[l.first.index]] ]; ENDLOOP; put1["}"]; IF NOT runEvenIfUnbound THEN GO TO failed; }; <> Process.CheckForAbort[]; IF NOT loadOnly THEN { Loader.Start[cm]; put2["Ran: ", name]; cm _ PrincOps.NullControl; } ELSE { put2["Loaded: ", shortName]; put2[", from: ", name]; AddControlModule[cm, shortName]; }; EXITS failed => error _ TRUE; done => {}; }; IF f # FS.nullOpenFile THEN FS.Close[f]; put1["\n"]; errMsg _ IO.RopeFromROS[ros]; }; AddControlModule: ENTRY PROC [cm: PrincOps.ControlModule, name: ROPE] = INLINE { ENABLE UNWIND => NULL; IF cm # PrincOps.NullControl THEN unstartedList _ CONS[ [cm, name], unstartedList]; }; RemControlModule: ENTRY PROC [name: ROPE] RETURNS [cm: PrincOps.ControlModule _ PrincOps.NullControl] = INLINE { ENABLE UNWIND => NULL; lag: UnstartedList _ NIL; FOR each: UnstartedList _ unstartedList, each.rest WHILE each # NIL DO IF Rope.Equal[each.first.name, name, FALSE] THEN { IF lag = NIL THEN unstartedList _ each.rest ELSE lag.rest _ each.rest; RETURN [each.first.cm]; }; lag _ each; ENDLOOP; }; unstartedList: UnstartedList _ NIL; UnstartedList: TYPE = LIST OF UnstartedEntry; UnstartedEntry: TYPE = RECORD [ cm: PrincOps.ControlModule, name: ROPE]; StartCommand: Commander.CommandProc = { <> argv: CommandTool.ArgumentVector _ CommandTool.Parse[cmd ! CommandTool.Failed => { msg _ errorMsg; GO TO oops; }]; FOR i: NAT IN [1..argv.argc) DO name: ROPE _ argv[1]; cm: PrincOps.ControlModule _ RemControlModule[name]; IF cm = PrincOps.NullControl THEN { msg _ "Error; no such unstarted module.\n"; GO TO oops}; TRUSTED {Loader.Start[cm]}; IO.PutF1[cmd.out, "Started %g\n", [rope[name]] ]; ENDLOOP; EXITS oops => result _ $Failure; }; LoadCommand: Commander.CommandProc = { <> cmd.commandLine _ Rope.Concat["-l ", cmd.commandLine]; [result, msg] _ RunCommand[cmd]; }; PrintDebugSearchRules: ENTRY Commander.CommandProc = { ENABLE UNWIND => NULL; rules: LIST OF ROPE _ AMFiles.pathPrefixes; out: STREAM = cmd.out; IO.PutRope[out, "( "]; IF rules = NIL THEN IO.PutChar[out, ' ]; WHILE rules # NIL DO IO.PutRope[out, rules.first]; rules _ rules.rest; IO.PutChar[out, ' ]; ENDLOOP; IO.PutRope[out, ")\n"]; }; SetDebugSearchRules: ENTRY Commander.CommandProc = { ENABLE UNWIND => NULL; args: LIST OF ROPE; newList: LIST OF ROPE _ NIL; CommandTool.StarExpansion[cmd]; args _ CommandTool.ParseToList[cmd ! CommandTool.Failed => {msg _ errorMsg; GO TO failed}].list; newList _ args; WHILE newList # NIL DO newList.first _ FileNames.ConvertToSlashFormat[FileNames.ResolveRelativePath[newList.first]]; IF NOT FileNames.IsADirectory[newList.first] THEN { msg _ Rope.Concat[newList.first, " is not a directory\n"]; GO TO failed }; IF newList.first.Length[] < 3 THEN { msg _ Rope.Concat["Directory name too short: ", newList.first]; GO TO failed }; newList _ newList.rest; ENDLOOP; newList _ NIL; IF cmd.procData.clientData = $SetSearchRules THEN { WHILE args # NIL DO IF NOT RopeList.Memb[newList, args.first, FALSE] THEN newList _ RopeList.DAppend[newList, LIST[args.first]]; args _ args.rest; Process.CheckForAbort[]; ENDLOOP; IF NOT RopeList.Memb[newList, "///", FALSE] THEN newList _ RopeList.DAppend[newList, LIST["///"]]; } ELSE { newList _ RopeList.CopyTopList[AMFiles.pathPrefixes]; WHILE args # NIL DO IF NOT RopeList.Memb[newList, args.first, FALSE] THEN newList _ RopeList.DAppend[newList, LIST[args.first]]; args _ args.rest; Process.CheckForAbort[]; ENDLOOP; }; AMFiles.pathPrefixes _ newList; EXITS failed => result _ $Failed; }; AddNewUniqueDebugRule: ENTRY PROC [rule: ROPE] = INLINE { ENABLE UNWIND => NULL; newRules: LIST OF ROPE _ NIL; rule _ CommandTool.ConvertToSlashFormat[CommandTool.ResolveRelativePath[rule]]; IF RopeList.Memb[AMFiles.pathPrefixes, rule, FALSE] THEN RETURN; newRules _ RopeList.CopyTopList[AMFiles.pathPrefixes]; IF RopeList.Memb[newRules, rule, FALSE] THEN RETURN; newRules _ RopeList.DAppend[newRules, LIST[rule]]; AMFiles.pathPrefixes _ newRules; }; CreateFirst: PROC = { Create[parentCommander: NIL, newViewer: TRUE, copyProps: TRUE, fork: TRUE, readProfile: TRUE]; }; { <
> [] _ Buttons.Create[info: [name: "Cmd"], proc: CreateCommanderButtonProc, fork: TRUE, documentation: "Create a Commander viewer"]; Commander.Register[key: "///Commands/Source", proc: CommandFile, doc: "Source commandFileName {argument list}, execute command file in current context", clientData: $Source]; Commander.Register[key: "///Commands/CommandTool", proc: CommandFile, doc: "Create a new Command Tool"]; Commander.Register[key: "///Commands/Cmd", proc: CommandFile, doc: "Create a new Command Tool"]; Commander.Register[key: "///Commands/Run", proc: RunCommand, doc: "Run -d -a {file}* - Load and Start one or more .bcds. -d => don't START if unbound, -a => ignore previously loaded, -l => load only", clientData: NIL]; Commander.Register[key: "///Commands/Load", proc: LoadCommand, doc: "Load -a {file}* - Load one or more .bcds. -a => ignore previously loaded", clientData: NIL]; Commander.Register[key: "///Commands/Start", proc: StartCommand, doc: "Start a previously loaded bcd", clientData: NIL]; Commander.Register[key: "///Commands/AddDebugSearchRules", proc: SetDebugSearchRules, doc: "Add debugger command search rules: AddDebugSearchRules list-of-directories"]; Commander.Register[key: "///Commands/PrintDebugSearchRules", proc: PrintDebugSearchRules, doc: "Print debugger command search rules"]; Commander.Register[key: "///Commands/SetDebugSearchRules", proc: SetDebugSearchRules, doc: "Set debugger command search rules: SetDebugSearchRules list-of-directories", clientData: $SetSearchRules]; EndOps.Register[CreateFirst]; }; END. March 27, 1983 3:27 pm, Stewart, Created from ChatImpl April 20, 1983 8:26 pm, Russ Atkinson, added Process Props stuff September 9, 1983 11:33 am, Stewart, Cedar 5 October 5, 1983 4:23 pm, Stewart, New command line processing October 19, 1983 5:17 pm, Stewart, Additional work December 2, 1983 8:03 pm, Stewart, Bug fixes and more bulletproofing December 12, 1983 12:43 pm, Stewart, FileNames, better default $Lookup December 15, 1983 11:09 am, Stewart, User profile and CommandToolStartup.cm December 15, 1983 5:44 pm, Stewart, Debug search rules January 14, 1984 7:44 pm, Stewart, Run defaults now start even if unbound, bugs fixed May 23, 1984 2:40:31 pm PDT, Spreitzer, Made initial create last thing done, via EndOps.