<> DIRECTORY ActionAreasDefs USING [RestoreActionArea], AMTypes USING [TVToName], BBAction USING [Abort], BBContext USING [GetContents], BBEval USING [EvalHead], BBNub USING [FindWorld, TurnADeafEar], IO USING [AppendStreams, ChangeDeliverWhen, CharProc, CharsAvail, Close, ControlX, CR, CreateDribbleStream, CreateEditedStream, CreateRefStreamProcs, CreateProcsStream, CurrentPosition, DeliverWhenProc, EndOfStream, EraseChar, Error, ESC, Flush, GetBufferContents, GetChar, GetCedarToken, GetSequence, int, LookupData, NewLine, noWhereStream, PFStarCodeProc, PeekChar, PutChar, PutF, PutRope, Reset, RIS, rope, ROPE, SetEcho, SetPFStarCodeProc, Signal, StoreData, STREAM, SyntaxError, TAB, UserAborted], IOExtras USING [GetCedarScannerToken, noInputStream, WaitUntilCharsAvail], List USING [DotCons, DRemove], MessageWindow USING [Append, Blink], Process USING [Detach, GetCurrent, SetPriority, priorityNormal, Yield], ProcessProps USING [AddPropList], Rope USING [Cat, Concat, Equal, Fetch, Find, FromChar, IsEmpty, Length, Substr], SafeStorage USING [NewZone], TiogaOps USING [LastLocWithin, PutTextKey, ViewerDoc, Location], TTYIO USING [CreateTTYHandleFromStreams], TypeScript USING [Create], UserCredentials USING [GetUserCredentials, SetUserCredentials], UserExec USING [AcquireResource, AskUser, DoIt, ErrorThisEvent, ExecHandle, ExecRecord, Expression, HistoryEvent, ReleaseResource, ResetUserResponse, RopeSubst, Viewer], UserExecExtras USING [CreateEvent, NewErrorThisEvent], UserExecPrivate USING [ActionAreaData, Advice, AdviceRecord, execMenu, CreateExecLogFile, CloseExecLogFile, EvalHeadData, ExecPrivateRecord, GetEvalHead, GlobalExecState, MethodList, execHandleList, HistoryError, HistoryEventPrivateRecord, ForAllSplitViewers, SplitViewerProc], UserProfile USING [Boolean, CallWhenProfileChanges, ProfileChanged, ProfileChangedProc, Token], ViewerAbort USING [UserAbort, SetUserAbort, ResetUserAbort], ViewerIO USING [CreateViewerStreams, GetViewerFromStream, NotAllowed], ViewerOps USING [AddProp, DestroyViewer, PaintViewer, SetMenu], ViewerTools USING [GetSelectedViewer, SetSelection], WorldVM USING [World, LocalWorld] ; UserExecImpl: CEDAR MONITOR IMPORTS ActionAreasDefs, AMTypes, BBAction, BBContext, BBNub, IO, List, IOExtras, MessageWindow, Process, ProcessProps, Rope, SafeStorage, TiogaOps, TTYIO, TypeScript, UserCredentials, UserExec, UserExecExtras, UserExecPrivate, UserProfile, ViewerAbort, ViewerIO, ViewerOps, ViewerTools, WorldVM EXPORTS UserExec, UserExecExtras, UserExecPrivate SHARES ViewerIO <> = BEGIN OPEN IO, UserExec, UserExecPrivate; <> ExecPrivateRecord: PUBLIC TYPE = UserExecPrivate.ExecPrivateRecord; HistoryEventPrivateRecord: PUBLIC TYPE = UserExecPrivate.HistoryEventPrivateRecord; <> GoToNextEvent: PUBLIC ERROR = CODE; -- used by history ParseFailed: PUBLIC ERROR [expr: Expression, msg: ROPE] = CODE; EvaluationFailed: PUBLIC ERROR [expr: Expression, msg: ROPE] = CODE; ErrorThisEvent: PUBLIC ERROR[event: HistoryEvent, msg: ROPE] = CODE; NewErrorThisEvent: PUBLIC ERROR[event: HistoryEvent, msg: ROPE, offender: ROPE _ NIL] = CODE; GoToSleep: PUBLIC ERROR = CODE; InvalidExecHandle: PUBLIC ERROR[exec: ExecHandle, msg: ROPE] = CODE; <> version: PUBLIC ROPE _ "*mCedar Executive June 20, 1983 9:14 am. Type ? for Commands.\n\n"; execHandleList: PUBLIC LIST OF ExecHandle _ NIL; methodList: PUBLIC UserExecPrivate.MethodList _ NIL; Zone: PUBLIC ZONE _ SafeStorage.NewZone[]; <> CreateUserExecutive: PUBLIC PROCEDURE [name: ROPE _ NIL, viewer: Viewer _ NIL, paint: BOOL _ TRUE, iconic: BOOL _ FALSE, msg: ROPE _ NIL, startListening: BOOL _ TRUE] RETURNS[exec: ExecHandle] = { exec _ CreateUserExec[name: name, viewer: viewer, paint: paint, iconic: iconic, msg: IF msg = NIL THEN version ELSE msg]; IF startListening THEN StartListening[exec]; }; SetEditableTypescripts: UserProfile.ProfileChangedProc = { useViewerEditedStream _ UserProfile.Boolean["EditTypeScripts", TRUE]; }; useViewerEditedStream: BOOL; idNumber: CARDINAL _ 0; CreateUserExec: PROCEDURE [name: ROPE _ NIL, viewer: Viewer _ NIL, paint: BOOL _ TRUE, iconic: BOOL _ FALSE, msg: ROPE _ NIL] RETURNS[exec: ExecHandle] = { in, out, log, execDotWStream: STREAM; private: REF ExecPrivateRecord; id: ROPE = ComputeExecName[idNumber]; SetPFStuff: PROC [stream: STREAM] = { [] _ stream.CurrentPosition[]; -- makes out keep track of position. stream.SetPFStarCodeProc['n, NewLine]; FOR i: INT IN [0..Rope.Length[looks]) DO stream.SetPFStarCodeProc[Rope.Fetch[looks, i], ChangeFont]; ENDLOOP; }; IF idNumber = 0 -- first time -- THEN SetPFStuff[IO.noWhereStream]; idNumber _ idNumber + 1; private _ Zone.NEW[ExecPrivateRecord _ [ -- deliverWhen: DefaultDeliverWhen, historyList: NIL, id: id, process: NULL, execDotW: NULL ]]; exec _ Zone.NEW[ExecRecord _ [ viewer: NIL, privateStuff: private ]]; IF name = NIL THEN name _ CaptionForExec[exec]; IF viewer = NIL THEN viewer _ TypeScript.Create[info: [name: name, iconic: iconic, column: right], paint: paint]; ViewerOps.SetMenu[viewer: viewer, menu: execMenu, paint: paint]; [in, out] _ ViewerIO.CreateViewerStreams[name: viewer.name, viewer: viewer, editedStream: useViewerEditedStream]; IF NOT useViewerEditedStream THEN in _ IO.CreateEditedStream[in, out]; SetPFStuff[out]; IF (log _ CreateExecLogFile[exec]) # NIL THEN { out _ IO.CreateDribbleStream[out, log]; out.SetPFStarCodeProc['n, LogNewLine]; -- font changes just go to the viewer stream, not to dribble stream IO.StoreData[self: in, key: $EXEC, data: exec]; -- another stream (an EchoWhenDeliverStream) will be built on top of in, but in is the edited stream upon which the deliverwhenproc has to work. in _ CreateEchoWhenDeliverStream[in, log]; <<[] _ IO.SetEcho[in, out]; -- characters read from the viewer by the edited stream continued to be echoed to the viewer output stream, rather than the dribble stream. When they are delivered, then they are also echoed to the log. This is so any edits are seen in the log.>> }; private.in _ in; private.out _ out; exec.viewer _ viewer; [] _ IO.ChangeDeliverWhen[in, ExecDeliverWhen]; [execDotWStream,] _ ViewerIO.CreateViewerStreams[name: viewer.name, viewer: viewer, editedStream: FALSE]; -- in is an editedStream. Need a stream for input upon which to build a TTYHandle for execDotW. [] _ execDotWStream.SetEcho[out]; TRUSTED {private.execDotW _ TTYIO.CreateTTYHandleFromStreams[execDotWStream, out]}; IO.StoreData[self: in, key: $EXEC, data: exec]; -- so ExecDeliverWhen can find which exec this stream is connected to. IO.StoreData[self: out, key: $EXEC, data: exec]; -- for consistency out.PutF[msg]; {AddIt: ENTRY PROC = {execHandleList _ CONS[exec, execHandleList]}; AddIt[]; }; }; -- of CreateUserExec <> EchoedWhenDelivered: TYPE = RECORD[echoTo: IO.STREAM]; <<>> CreateEchoWhenDeliverStream: PROCEDURE [in, echoTo: IO.STREAM] RETURNS [IO.STREAM] = { RETURN[IO.CreateProcsStream[ streamProcs: IO.CreateRefStreamProcs[getChar: EchoWhenDeliverGetChar, name: "Echo When Delivered"], backingStream: in, streamData: NEW[EchoedWhenDelivered _ [echoTo]] ]]; }; -- of CreateEchoWhenDeliverStream EchoWhenDeliverGetChar: PROC[self: IO.STREAM] RETURNS[char: CHAR] = { data: REF EchoedWhenDelivered = NARROW[self.streamData]; char _ self.backingStream.GetChar[]; data.echoTo.PutChar[char]; }; -- of EchoWhenDeliverGetChar CaptionForExec: PUBLIC PROC [exec: ExecHandle, paint: BOOL _ TRUE] RETURNS[caption: ROPE] = { private: REF ExecPrivateRecord = exec.privateStuff; viewer: Viewer = exec.viewer; actionAreaData: REF ActionAreaData _ private.actionAreaData; ropeForIcon, r: ROPE; r _ ropeForIcon _ Rope.Concat[private.id, ": "]; IF private.execState = suspended THEN r _ Rope.Concat[r, "suspended "] ELSE IF actionAreaData = NIL OR actionAreaData.action # NIL THEN NULL <> ELSE IF actionAreaData.aborted THEN r _ Rope.Concat[r, "aborted "] ELSE r _ Rope.Concat[r, "proceeded "]; IF actionAreaData # NIL THEN TRUSTED { -- action area caption _ Rope.Cat["Action Area ", r, actionAreaData.abbrevMsg]; IF actionAreaData.world # WorldVM.LocalWorld[] THEN caption _ Rope.Concat["WorldSwap ", caption]; ropeForIcon _ Rope.Cat[ropeForIcon, actionAreaData.iconMsg]; } ELSE { caption _ Rope.Cat["Work Area ", r]; IF NOT private.evalMode THEN { caption _ Rope.Concat[caption, "Executive"]; ropeForIcon _ Rope.Concat[ropeForIcon, "Executive"]; } ELSE { evalHead: BBEval.EvalHead = UserExecPrivate.GetEvalHead[expr: NIL, exec: exec]; evalHeadData: REF UserExecPrivate.EvalHeadData = NARROW[evalHead.data]; caption _ Rope.Concat[caption, "Interpreter"]; ropeForIcon _ Rope.Concat[ropeForIcon, "Interpreter"]; IF private.spawnedActionArea # NIL THEN NULL -- if suspended, no point in including default context in caption ELSE IF evalHead.globalContext # NIL THEN caption _ Rope.Cat[caption, " {global context: ", AMTypes.TVToName[BBContext.GetContents[evalHead.globalContext].gf], "}"] ELSE IF evalHeadData.defaultInterface # NIL THEN caption _ Rope.Cat[caption, " {default interface: ", evalHeadData.defaultInterface, "}"]; }; }; IF viewer # NIL THEN { viewerProc: UserExecPrivate.SplitViewerProc = TRUSTED { viewer.name _ caption; ViewerOps.AddProp[viewer, $IconLabel, ropeForIcon]; IF paint THEN ViewerOps.PaintViewer[viewer: viewer, hint: IF viewer.iconic THEN all ELSE caption]; }; UserExecPrivate.ForAllSplitViewers[viewer, viewerProc]; }; }; ComputeExecName: PROC [n: INT] RETURNS [r: ROPE] = { m: INT; DO m _ n/26; IF m > 0 THEN {r _ Rope.Concat[r, Rope.FromChar['A + m]]; n _ n MOD 26; } ELSE {r _ Rope.Concat[r, Rope.FromChar['A + n]]; EXIT}; ENDLOOP; }; <<>> <> StartListening: PUBLIC ENTRY PROC [exec: ExecHandle] = TRUSTED { -- Process private: REF ExecPrivateRecord = exec.privateStuff; IF private.execState = notListening OR private.execState = dormant THEN { private.execState _ listening; -- only want this done once. that is why it is monitored, and we set the state before releasing the monitor Process.Detach[FORK ReadEvalPrint[exec]]; }; }; DestroyExec: PUBLIC PROC [exec: ExecHandle] = { private: REF ExecPrivateRecord _ exec.privateStuff; viewer: Viewer = exec.viewer; RemoveExec: ENTRY PROC = TRUSTED { -- LOOPHOLE for Polymorphism ENABLE UNWIND => NULL; UserExecPrivate.execHandleList _ LOOPHOLE[List.DRemove[exec, LOOPHOLE[UserExecPrivate.execHandleList, LIST OF REF ANY]]]; UserExecPrivate.CloseExecLogFile[exec]; }; IF private.execState = destroyed THEN RETURN; -- already destroyed private.execState _ destroyed; private.in.Close[! IO.Error => IF ec = StreamClosed THEN CONTINUE]; private.out.Close[! IO.Error => IF ec = StreamClosed THEN CONTINUE]; RemoveExec[]; IF private.actionAreaData # NIL AND private.actionAreaData.action # NIL THEN { actionAreaData: REF UserExecPrivate.ActionAreaData = private.actionAreaData; IF ViewerTools.GetSelectedViewer[] = viewer AND actionAreaData.viewer # NIL THEN -- not sure latter can fail, but it did, causing an addressfault ViewerTools.SetSelection[actionAreaData.viewer, NIL]; IF private.spawnedActionArea # NIL THEN { DestroyExec[private.spawnedActionArea]; private.spawnedActionArea _ NIL; }; IF actionAreaData.parentExec # NIL THEN ActionAreasDefs.RestoreActionArea[actionAreaData.parentExec]; IF actionAreaData.action.status = pendingOut THEN BBAction.Abort[actionAreaData.action]; }; TRUSTED {FREE [@private.actionAreaData]; FREE[@private]}; }; <> GetPrivateStuff: PUBLIC PROC [exec: ExecHandle] RETURNS[REF ExecPrivateRecord] = { private: REF ExecPrivateRecord; IF exec = NIL THEN ERROR InvalidExecHandle[exec: exec, msg: "Non-NIL exec handle required"]; private _ exec.privateStuff; IF private.execState = destroyed THEN InvalidExecHandle[exec: exec, msg: "exec handle has been destroyed"]; RETURN[private]; }; GetExecHandle: PUBLIC PROCEDURE [id: ROPE _ NIL, viewer: Viewer _ NIL, process: UNSAFE PROCESS _ NIL] RETURNS[exec: ExecHandle] = TRUSTED { -- Process current: UNSAFE PROCESS _ NIL; Search: ENTRY PROC RETURNS [exec: ExecHandle] = TRUSTED { FOR l: LIST OF ExecHandle _ UserExecPrivate.execHandleList, l.rest UNTIL l = NIL DO private: REF UserExecPrivate.ExecPrivateRecord = l.first.privateStuff; IF id # NIL AND Rope.Equal[private.id, id] THEN RETURN[l.first]; IF viewer # NIL THEN { v: Viewer _ l.first.viewer; DO IF v = viewer THEN RETURN[l.first]; IF (v _ v.link) = NIL OR (v = l.first.viewer) THEN EXIT; ENDLOOP; }; IF process # NIL AND private.process = process THEN RETURN[l.first]; IF current # NIL AND private.process = current THEN RETURN[l.first]; ENDLOOP; RETURN[NIL]; }; IF id = NIL AND viewer = NIL AND process = NIL THEN current _ Process.GetCurrent[]; exec _ Search[]; RETURN[ IF exec # NIL THEN exec ELSE IF current # NIL -- i.e. called with all three arguments NIL -- THEN GetDefaultExecHandle[] ELSE NIL]; }; GetDefaultExecHandle: PUBLIC PROCEDURE RETURNS[exec: ExecHandle] = { CreateIt: PROC RETURNS[exec: ExecHandle] = { defaultExec _ CreateUserExecutive[msg: Rope.Substr[base: version, len: Rope.Find[version, "."]], paint: FALSE, iconic: TRUE, startListening: FALSE]; private _ defaultExec.privateStuff; private.out.PutRope["\nThis is a \"deaf\" executive: it does not listen to the keyboard, but instead is used only for the execution of events from an application program.\n"]; defaultExec.viewer.name _ UserExec.RopeSubst[old: "Executive", new: "Default Executive", base: defaultExec.viewer.name]; ViewerOps.PaintViewer[defaultExec.viewer, all]; private.in _ IOExtras.noInputStream; RETURN[defaultExec]; }; private: REF UserExecPrivate.ExecPrivateRecord; IF defaultExec = NIL THEN RETURN[CreateIt[]]; private _ defaultExec.privateStuff; IF private.execState = destroyed THEN RETURN[CreateIt[]]; RETURN[defaultExec]; }; defaultExec: PUBLIC UserExec.ExecHandle _ NIL; GetWorld: PUBLIC PROCEDURE RETURNS[WorldVM.World] = TRUSTED { -- Process exec: ExecHandle = GetExecHandle[process: Process.GetCurrent[]]; private: REF UserExecPrivate.ExecPrivateRecord; IF exec = NIL THEN RETURN[WorldVM.LocalWorld[]]; private _ GetPrivateStuff[exec]; RETURN[IF private.actionAreaData # NIL THEN private.actionAreaData.world ELSE WorldVM.LocalWorld[]]; }; NewLine: IO.PFStarCodeProc -- [stream: STREAM, char: CHAR] -- = { IO.NewLine[stream] }; LogNewLine: IO.PFStarCodeProc -- [stream: STREAM, char: CHAR] -- = { IF stream.backingStream.CurrentPosition[] # 0 THEN stream.PutChar['\n]; -- reason for this is because the CR is put to the viewer and the log separately (the viewer when typed, the log when edited stream passes it up), but without this, would get extra crs because this stream does not know that position has changed. }; AdviseExec: PUBLIC PROC [exec: ExecHandle, before, after: PROC[data: REF ANY], data: REF ANY] = { private: REF UserExecPrivate.ExecPrivateRecord = GetPrivateStuff[exec]; private.advice _ CONS[ NEW[UserExecPrivate.AdviceRecord _ [before: before, after: after, data: data] ], private.advice]; }; <> SetWorldSwapDebug: UserProfile.ProfileChangedProc -- [reason: ProfileChangeReason] -- = { new: BOOLEAN = UserProfile.Boolean[key: "WorldSwapDebug", default: FALSE]; IF reason = firstTime OR new # worldSwapDebug THEN {worldSwapDebug _ new; SetupWorldSwapDebug[]; }; }; worldSwapDebug: BOOLEAN; world: WorldVM.World; SetupWorldSwapDebug: PROC = { IF NOT worldSwapDebug THEN world _ BBNub.FindWorld[name: "Local", new: TRUE] ELSE IF world # NIL THEN BBNub.TurnADeafEar[world]; }; <> ReadEvalPrint: PROC [exec: ExecHandle] = { private: REF ExecPrivateRecord = exec.privateStuff; event: HistoryEvent; TypeOfAskUser: TYPE = PROC [msg: ROPE, in, out: IO.STREAM, defaultKey: ATOM, keyList: LIST OF ATOM, timeout: INT] RETURNS [value: ATOM]; askUser: TypeOfAskUser = {RETURN[UserExec.AskUser[msg: msg, timeout: timeout, defaultKey: defaultKey, exec: exec, keyList: keyList]]}; inner: PROC = { DO ENABLE { IO.Error => IF ec = StreamClosed THEN { IF exec.viewer = NIL OR exec.viewer.destroyed THEN GOTO Destroy; }; -- user destroyed the viewer, then somebody tried to do input or output ABORTED => { -- breakpoint or signal, then aborted IF exec.privateStuff = NIL OR private.execState = destroyed THEN GOTO Destroy; WasAborted[exec, ""]; LOOP; }; IO.UserAborted => { -- ctrl-del or clicked stop WasAborted[exec, msg]; LOOP; }; }; IF private.execState = destroyed THEN { IF NOT exec.viewer.destroyed THEN ViewerOps.DestroyViewer[exec.viewer]; EXIT; }; event _ GetEvent[exec]; private.eventState _ running; ProcessEvent[event, exec ! GoToSleep => { private.execState _ dormant; ReleaseStreams[exec]; GOTO Sleep; } ]; ENDLOOP; EXITS Destroy => DestroyExec[exec]; Sleep => NULL; }; TRUSTED { private.process _ Process.GetCurrent[]; Process.SetPriority[Process.priorityNormal]; -- if exec was created via New button, then would be running at process foreground }; private.execState _ listening; ProcessProps.AddPropList[ LIST[List.DotCons[$AskUser, NEW[TypeOfAskUser _ askUser]]], inner]; -- "redefines" IO.AskUser to call this AskUser. Primarily so Bringover, SModel etc. use UserExec. AskUser when running in workarea }; GetEvent: PROC [exec: ExecHandle] RETURNS [event: HistoryEvent] = { private: REF ExecPrivateRecord = exec.privateStuff; input: ROPE; in, out, bottom: STREAM; charsAvail: BOOL _ FALSE; UserExec.ResetUserResponse[exec.viewer]; FOR lst: LIST OF Advice _ private.advice, lst.rest UNTIL lst = NIL DO IF lst.first.before # NIL THEN lst.first.before[lst.first.data]; ENDLOOP; bottom _ private.in; UNTIL bottom.backingStream = NIL DO bottom _ bottom.backingStream; ENDLOOP; DO eventNum: INT _ IF private.continuation THEN private.eventNum - 1 ELSE private.eventNum; All: IO.CharProc = { include _ TRUE; quit _ NOT in.CharsAvail[]; -- basically want the entire buffer. Use DeliverWhen to decide. }; CharsAvail: PROC RETURNS [BOOL] = { -- the reason for this (kluge) rather than checking just bottom.charsAvail is that there may be characters available at a higher level, in particular this occurs when you do an AppendStreams to stuff characters to implement a control-x. stream: IO.STREAM _ private.in; UNTIL stream = NIL DO IF stream.CharsAvail[] THEN RETURN[TRUE]; stream _ stream.backingStream; ENDLOOP; RETURN[FALSE]; }; IF NOT private.dontPrompt THEN Prompt[eventNum, exec]; private.eventState _ readyForNext; -- IF NOT private.continuation THEN -- ReleaseStreams[exec]; { ENABLE { IO.Signal => CHECKED { SELECT ec FROM Rubout => { out.PutRope[" XXX\n"]; private.dontPrompt _ FALSE; in.Reset[]; [] _ in.SetEcho[out]; LOOP; }; -- reprints event number. ENDCASE}; IO.SyntaxError => IF stream = in THEN {out.PutF["*n*eSyntax Error: %g", rope[msg]]; Reset[in]; LOOP}; ViewerIO.NotAllowed => GOTO NotAllowed; -- must unwind before calling WasAborted or else could cause monitor lock }; <> <> IF NOT CharsAvail[] THEN IOExtras.WaitUntilCharsAvail[bottom]; -- gives somebody else a chance to get in. [in, out] _ AcquireStreams[exec]; IF NOT CharsAvail[] THEN { -- between the time that charas were available and AcquireStreams returns, no more chars. Means that the character was really typed to somebody else and has been consumed, e.g. an event ran in this exec that asked for confirmation. WaitUntilCharsAvail woke up when user typed Y, but AcquireStreams did not return until after the streams were released. At that point, the character had already been read. So we are back where we were. private.dontPrompt _ TRUE; LOOP; }; private.eventState _ receivingInput; [] _ in.PeekChar[]; -- wait for first character, thereby guaranteeing that deliverWhenProc has returned TRUE. input _ IO.GetSequence[in, All]; -- now get everything. EXITS NotAllowed => { WasAborted[exec, "buffered characters and document don't agree"]; LOOP; }; }; IF input = NIL THEN ERROR; EXIT; ENDLOOP; private.lastLine _ input; IF private.continuation THEN {event _ private.historyList.first; event.input _ input; private.continuation _ FALSE; } ELSE event _ UserExecExtras.CreateEvent[exec, input]; private.dontPrompt _ FALSE; out.PutF["*s"]; -- return to system font RETURN[event]; }; -- of GetEvent ProcessEvent: PROC [event: HistoryEvent, exec: ExecHandle] = { private: REF ExecPrivateRecord = exec.privateStuff; abortMsg: ROPE; IF event.input.IsEmpty[] THEN ERROR; BEGIN -- inner block ENABLE { UserExec.ErrorThisEvent => GOTO Exit; UserExecExtras.NewErrorThisEvent => GOTO Exit; IO.UserAborted => {-- user typed control-del abortMsg _ msg; GOTO Aborted; }; ABORTED => { abortMsg _ ""; GOTO Aborted; }; UserExecPrivate.HistoryError => { out: STREAM = GetStreams[exec].out; SELECT ec FROM NotFound => out.PutRope["not found"]; UseWhat => out.PutRope["Use What?"]; -- from ParseUseCommand UseForWhat => out.PutRope["For What?"];-- from ParseUseCommand UseHuh => out.PutRope["Huh?"];-- from ParseUseCommand ENDCASE => ERROR; GOTO Exit; }; UNWIND => NULL; }; -- of Catch Phrases FOR lst: LIST OF Advice _ private.advice, lst.rest UNTIL lst = NIL DO IF lst.first.after # NIL THEN lst.first.after[lst.first.data]; ENDLOOP; ResetUserAbort[exec]; -- if it wasn't seen before, then somebody missed it. dont want to abort the next thing. [] _ UserExec.DoIt[event.input, exec, event]; EXITS -- still inside inner block Aborted => WasAborted[exec, abortMsg]; Exit => NULL; END; -- of inner block }; -- of ProcessEvent Prompt: PUBLIC PROC [eventNum: INT, exec: ExecHandle] = { private: REF ExecPrivateRecord = GetPrivateStuff[exec]; viewer: Viewer; loc: TiogaOps.Location; viewer _ exec.viewer; private.out.Flush[]; loc _ TiogaOps.LastLocWithin[TiogaOps.ViewerDoc[viewer]]; -- location of the current end of typescript. Don't do PutTextKey until some characters are printed, because characters woould be inserted in front of this place, i.e. the addr would always refer to the end of the document. private.out.PutF["*n*p&%d *u", int[eventNum]]; TiogaOps.PutTextKey[node: loc.node, where: loc.where, key: exec]; -- saves sticky addr for beginning of next event. However, don't know yet who is going to get the event, i.e. somebody might call DoIt from outside. IF NOT private.evalMode THEN RETURN; <> AppendStreams[in: private.in, from: IO.RIS["_ "]]; }; WasAborted: PROC [exec: ExecHandle, msg: ROPE] = { private: REF ExecPrivateRecord = exec.privateStuff; IF private.execState = destroyed THEN RETURN; ResetUserAbort[exec]; [] _ private.in.SetEcho[NIL]; private.in.Reset[]; -- clears the edited stream buffer. Note that clicking STOP will clear everything that has been typed ahead up until that point. Can't wait until here, because means user can't start typing as soon as he clicks stop. This means that CTRL-DEL and stop behave differently, i.e. CTRL-DEL will not clear any typeahead. [] _ private.in.SetEcho[private.out]; private.continuation _ FALSE; private.dontPrompt _ FALSE; private.out.PutF["*n*maborted %g\n*s", rope[msg]]; }; GetExecFromStream: PUBLIC PROC [stream: STREAM] RETURNS[UserExec.ExecHandle] = { RETURN[NARROW[IO.LookupData[stream, $EXEC]]]; }; ExecDeliverWhen: PUBLIC IO.DeliverWhenProc -- PROC[char: CHAR, stream: STREAM] RETURNS[BOOL] -- = { exec: ExecHandle = GetExecFromStream[stream]; private: REF ExecPrivateRecord = GetPrivateStuff[exec]; yes: BOOLEAN _ FALSE; SELECT char FROM <<'^ => >> <