DIRECTORY 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 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 3, 1983 9:54 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]; }; 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 CreateMonitoredStream 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 { parentsPrivate: REF ExecPrivateRecord = actionAreaData.parentExec.privateStuff; parentsPrivate.spawnedActionArea _ NIL; parentsPrivate.execState _ listening; [] _ CaptionForExec[actionAreaData.parentExec, TRUE]; }; 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; 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 CR, TAB, ControlX, '? => SELECT private.lastChar FROM '\\ => NULL; ENDCASE => { r: ROPE = IO.GetBufferContents[private.in]; IF NOT (yes _ IsWellFormed[Rope.Substr[base: r, len: Rope.Length[r] - 1]]) THEN MessageWindow.Append["Event not terminated, continue with input", TRUE]; }; ESC => { r: ROPE = IO.GetBufferContents[private.in]; IF Rope.IsEmpty[r] THEN ERROR; -- in this case, stream is supposed to take care of supplying characters. IF Rope.Equal[r, "_ \033"] THEN { private.in.EraseChar[char]; private.in.EraseChar[' ]; private.in.EraseChar['_]; AppendStreams[in: private.in, from: IO.RIS[Rope.Substr[base: private.lastLine, len: Rope.Length[private.lastLine] - 1]]];-- ignore the last character since it activated. } ELSE SELECT Rope.Fetch[r, Rope.Length[r] - 2] FROM -- -1 is the ESC IN ['A..'Z], IN['a..'z], IN['0..'9] => yes _ TRUE; ENDCASE => { private.in.EraseChar[char]; MessageWindow.Append["No match", TRUE]; MessageWindow.Blink[]; }; }; ENDCASE; private.lastChar _ char; RETURN[yes]; }; IsWellFormed: PROC [rope: ROPE] RETURNS[BOOLEAN] = { stream: STREAM; stream _ IO.RIS[rope]; -- since streams already acquired, we might consider saving a scratch stream and using it. IF Rope.IsEmpty[rope] OR NOT Rope.Equal["_", IO.GetCedarToken[stream ! IO.EndOfStream => GOTO Yes]] THEN GOTO Yes; DO IOExtras.GetCedarScannerToken[stream, NIL ! IO.SyntaxError => GOTO No; IO.EndOfStream => GOTO Yes ]; ENDLOOP; EXITS Yes => RETURN[TRUE]; No => RETURN[FALSE]; }; AcquireExec: PUBLIC PROC [exec: ExecHandle] RETURNS[newlyAcquired: BOOL] = TRUSTED { -- Process private: REF ExecPrivateRecord = GetPrivateStuff[exec]; process: UNSAFE PROCESS = Process.GetCurrent[]; thisExec: ExecHandle = GetExecHandle[process: process]; IF private.execOwner = process THEN { -- already acquired by this process private.execOwnerCount _ private.execOwnerCount + 1; RETURN[FALSE]; }; newlyAcquired _ UserExec.AcquireResource[resource: exec, owner: NIL, exec: thisExec]; -- the exec argument is where abort is checked. private.execOwner _ process; private.execOwnerCount _ 1; }; ReleaseExec: PUBLIC PROC [exec: ExecHandle] RETURNS[released: BOOL] = TRUSTED { private: REF ExecPrivateRecord = exec.privateStuff; -- don't use GetPrivateStuff because exec may be destroyed process: UNSAFE PROCESS = Process.GetCurrent[]; IF private.execState = destroyed THEN RETURN[FALSE]; IF private.execOwner # process OR private.execOwnerCount = 0 THEN RETURN[FALSE]; private.execOwnerCount _ private.execOwnerCount - 1; IF private.execOwnerCount = 0 THEN { private.execOwner _ NIL; IF NOT UserExec.ReleaseResource[resource: exec] THEN ERROR; Process.Yield[]; RETURN[TRUE]; } ELSE RETURN[FALSE]; }; AcquireStreams: PUBLIC PROC [exec: ExecHandle] RETURNS [in, out: STREAM] = { private: REF ExecPrivateRecord = GetPrivateStuff[exec]; [] _ AcquireExec[exec: exec]; in _ private.in; out _ private.out; }; ReleaseStreams: PUBLIC PROC [exec: ExecHandle] = TRUSTED { -- Process private: REF ExecPrivateRecord = exec.privateStuff; -- don't use GetPrivateStuff because exec may be destroyed owner: UNSAFE PROCESS = private.execOwner; process: UNSAFE PROCESS = Process.GetCurrent[]; IF owner = NIL THEN RETURN; -- to make it convenient to simply call ReleaseStreams from Doit, rather than checking IF owner # process THEN RETURN; [] _ ReleaseExec[exec: exec]; }; GetStreams: PUBLIC PROC [exec: ExecHandle] RETURNS [in, out: STREAM] = TRUSTED { -- Process IF exec = NIL THEN RETURN[in: IOExtras.noInputStream, out: IO.noWhereStream] ELSE { private: REF ExecPrivateRecord = GetPrivateStuff[exec]; process: UNSAFE PROCESS = Process.GetCurrent[]; IF private.execOwner = process THEN NULL -- already owns it ELSE IF private.execOwner = NIL AND ( exec = defaultExec -- anybody can have this -- OR private.execState # listening) -- e.g. not started yet THEN ERROR InvalidExecHandle[exec, "Streams Not Acquired"]; in _ private.in; out _ private.out; }; }; GetNameAndPassword: PUBLIC PROC RETURNS[name, password: ROPE] = { [name, password] _ UserCredentials.GetUserCredentials[]; }; SetNameAndPassword: PUBLIC PROC [name, password: ROPE] = { IF Rope.Find[name, "."] = -1 THEN name _ Rope.Concat[name, ".pa"]; -- should I still be doing this. UserCredentials.SetUserCredentials[name, password]; UserProfile.ProfileChanged[edit]; }; UserAbort: PUBLIC PROC [exec: ExecHandle] RETURNS [BOOLEAN] = { RETURN[exec # NIL AND exec.viewer # NIL AND NOT exec.viewer.destroyed AND ViewerAbort.UserAbort[exec.viewer]]; }; SetUserAbort: PUBLIC PROC [exec: ExecHandle] = { IF exec # NIL AND exec.viewer # NIL AND NOT exec.viewer.destroyed THEN ViewerAbort.SetUserAbort[exec.viewer]; }; ResetUserAbort: PUBLIC PROC [exec: ExecHandle] = { IF exec # NIL AND exec.viewer # NIL AND NOT exec.viewer.destroyed THEN ViewerAbort.ResetUserAbort[exec.viewer]; }; UserAborted: PUBLIC PROC [exec: ExecHandle, msg: ROPE _ NIL] = { IO.UserAborted[exec, msg]; }; ChangeFont: IO.PFStarCodeProc -- [stream: STREAM, char: CHAR] -- = { viewer: Viewer = ViewerIO.GetViewerFromStream[stream]; looks: ROPE; IF viewer = NIL THEN RETURN; SELECT char FROM 'u => looks _ userLooks; -- what the user types in 's => looks _ systemLooks; 'e => looks _ errorLooks; 'm => looks _ messageLooks; 'p => looks _ promptLooks; ENDCASE; stream.PutF["%l", rope[looks]]; }; userLooks, systemLooks, errorLooks, messageLooks, promptLooks: ROPE; looks: ROPE _ "usemp"; -- those characters to make into * commands for pf. SetLooks: UserProfile.ProfileChangedProc -- [reason: ProfileChangeReason] -- = { userLooks _ Rope.Concat[" ", UserProfile.Token["Looks.typein", " "]]; systemLooks _ Rope.Concat[" ", UserProfile.Token["Looks.output", " "]]; errorLooks _ Rope.Concat[" ", UserProfile.Token["Looks.errors", "ib"]]; messageLooks _ Rope.Concat[" ", UserProfile.Token["Looks.messages", "i"]]; promptLooks _ Rope.Concat[" ", UserProfile.Token["Looks.prompt", "b"]]; }; UserProfile.CallWhenProfileChanges[SetWorldSwapDebug]; UserProfile.CallWhenProfileChanges[SetLooks]; UserProfile.CallWhenProfileChanges[SetEditableTypescripts]; END. -- of UserExecImpl ’Last Edited by: Teitelman, June 3, 1983 9:56 am ViewerIO: NotAllowed connecting concrete and opaque types signals global constants creating, starting, and destroying execs [] _ 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. Echo When Deliver Stream ELSE IF actionAreaData.booted THEN ": booted " StartListening is a separate procedure in order to give client a chance to print something to the exec, or change some menus, before the thing takes off Manipulating execHandles world swap debug Read/Eval/Print Loop IF private.continuation THEN [in, out] _ GetStreams[exec] ELSE viewer.class.notify[viewer, LIST["_ "]]; not synchronized with typeahead. '^ => SELECT private.lastChar FROM SP, '\t, '\n => NULL; ENDCASE => char _ NUL; -- ^ only has its escape interpretation when preceded by white space, because ^ is a legitimate way to end a mesa expression. the purpose of this procedure is to determine whether the last character in the rope really terminates the rope, viewed as a sequence of characters, or is a part of a token itself, e.g. to distinguish _ foo? from _"foo?". Acquiring Streams Passwords Aborting Changing Fonts Initialization Edited on January 26, 1983 12:01 am, by Teitelman updated time changes to: version, SetBackingFile Edited on January 29, 1983 3:00 pm, by Teitelman changes to: DIRECTORY, CreateUserExec Edited on February 27, 1983 2:53 pm, by Teitelman changes to: version, IsWellFormed Edited on March 2, 1983 12:00 am, by Teitelman changes to: CreateUserExec Edited on March 3, 1983 11:27 am, by Teitelman changes to: version, CaptionForExec Edited on March 7, 1983 12:48 pm, by Teitelman changes to: version, CaptionForExec, CreateUserExec, EchoedWhenDelivered, CreateEchoWhenDeliverStream, EchoWhenDeliverGetChar, CaptionForExec, ComputeExecName, StartListening, DestroyExec, CreateUserExec, EchoedWhenDelivered, CreateEchoWhenDeliverStream, EchoWhenDeliverGetChar, CreateUserExec, EchoedWhenDelivered, DIRECTORY, NewErrorThisEvent, DirectoryNotFound, ProcessEvent, ReadEvalPrint, ReadEvalPrint Edited on March 10, 1983 3:01 am, by Teitelman changes to: DIRECTORY, EXPORTS, version, GetWorld, NewLine Edited on March 11, 1983 6:24 pm, by Teitelman changes to: version, DestroyExec, WasAborted Edited on March 28, 1983 8:14 pm, by Teitelman changes to: DIRECTORY, version, viewerProc (local of CaptionForExec), CaptionForExec, DefaultDeliverWhen, IMPORTS, DefaultDeliverWhen, version, DefaultDeliverWhen, versi Edited on April 7, 1983 1:02 pm, by Teitelman changes to: version Edited on April 11, 1983 1:43 pm, by Teitelman changes to: DIRECTORY, version, ReadEvalPrint Edited on April 15, 1983 3:30 pm, by Teitelman changes to: DIRECTORY, IMPORTS, ReadEvalPrint, askUser (local of ReadEvalPrint), inner (local of ReadEvalPrint), version, StartListening, GetEvent, GetEvent, inner (local of ReadEvalPrint), version, GetEvent, GetEvent, All (local of GetEvent), version, GetEvent, WasAborte Edited on April 18, 1983 9:11 pm, by Teitelman changes to: AcquireExec, ReleaseExec, ReleaseExec, ReleaseStreams, GetStreams, DIRECTORY, ErrorThisEvent, version, ProcessEvent, DestroyExec, NewErrorThisEvent, foo, GetEvent, DIRECTORY, IMPORTS, ReadEvalPrint, StartListening, DIRECTORY, StartListening, ReadEvalPrint, ReleaseExec Edited on April 20, 1983 6:07 pm, by Teitelman changes to: version, foo, foo, DIRECTORY, IMPORTS, ReadEvalPrint, ReadEvalPrint, GetEvent Edited on May 3, 1983 8:48 pm, by Teitelman changes to: version, inner (local of ReadEvalPrint) Edited on May 9, 1983 4:56 pm, by Teitelman changes to: version Edited on May 12, 1983 1:11 am, by Teitelman changes to: version Edited on May 13, 1983 11:02 am, by Teitelman changes to: version, methodList, ProcessEvent Edited on May 23, 1983 6:33 pm, by Teitelman changes to: version Edited on May 27, 1983 11:57 am, by Teitelman changes to: GetEvent, CharsAvail (local of GetEvent), CharsAvail (local of GetEvent) Edited on June 3, 1983 9:56 am, by Teitelman changes to: HistoryEventPrivateRecord, version, DestroyExec Κk– "Cedar" style˜J™/šΟk ˜ Jšœœ ˜Jšœ œ ˜Jšœ œ˜Jšœœ ˜Jšœœ˜&JšœœKœ•œ£œœ1œœ˜χJšœ œ<˜JJšœœ˜Jšœœ˜$Jšœœ;˜HJšœ œ˜!JšœœF˜PJšœ œ˜Jšœ œ2˜@Jšœœ˜)Jšœ œ ˜Jšœœ*˜?Jšœ œ›˜©Jšœœ"˜6Jšœœ€˜•Jšœ œO˜`Jšœ œ+˜œ˜QJšœœ œ˜GJ˜.Jšœ6˜6Jš œœœœ A˜oJšœœœ|˜₯Jšœ!œœ[˜‹J˜—J˜—šœ œœ˜šŸ œ$œ˜7J˜Jšœ3˜3Jš œœ-œœœ ˜bJ˜—Jšœ7˜7J˜—J˜J˜—J˜š Ÿœœœœœ˜4Jšœœ˜š˜J˜ šœ˜ J˜+Jšœœ˜ J˜—š˜J˜+Jšœ˜—Jšœ˜—J˜—™Jšœ˜™˜—š Ÿœœ œœ  ˜LJšœ œ'˜3šœ"œ˜IJšœ  k˜‹Jšœœ˜)J˜—˜J˜——šŸ œœœ˜/Jšœ œ'˜3Jšœ˜šŸ œœœ&˜?Jšœœœ˜Jš œ!œœ!œœœœ˜yJšœ'˜'J˜—Jšœœœ ˜CJšœ˜Jš œœ œœœ˜CJš œœ œœœ˜DJ˜ š œœœ!œœ˜NJšœœ9˜Lš œ*œœœ @˜’Jšœ0œ˜5—šœœœ˜*Jšœ'˜'Jšœœ˜ Jšœ˜—šœœ˜)Jšœœ<˜OJšœ#œ˜'J˜%Jšœ/œ˜5J˜—Jšœ+œ'˜XJ˜—Jšœœœ ˜9J˜——™š Ÿœ œœœœ˜SJšœ œ˜JšœœœœD˜\Jšœ˜JšœœF˜kJšœ ˜Jšœ˜—J˜šŸ œœ œœœœ œœœœœ  ˜˜Jšœ œœ˜š Ÿœœœœœ˜9š œœœ5œœ˜SJšœ œ:˜FJš œœœœœ ˜@šœ œ˜J˜šœ˜Jšœ  œ ˜#Jšœœœ ˜8Jšœ˜ —J˜—Jš œ œœœœ ˜DJš œ œœœœ ˜DJš˜Jšœœ˜ Jšœ˜——Jšœœœ œœ œœ ˜SJšœ˜šœ˜Jšœœœ˜Jš œœ œ .œœ˜aJšœœ˜ —J˜—J˜šŸœœœ˜DšŸœœœœ˜,Jšœhœ œœ˜”J˜#Jšœ―˜―Jšœx˜xJ˜/Jšœ$˜$Jšœ˜J˜—Jšœ œ#˜/Jšœœœœ ˜-Jšœ"˜#Jšœœœ ˜9Jšœ˜—˜J˜—JšŸ œœœ˜.J˜š Ÿœœ œœœ  ˜JJšœ@˜@Jšœ œ#˜/Jšœœœœ˜0Jšœ ˜ Jšœœœœ˜IJšœ˜Jšœ˜—J˜šŸœœ "œ˜AJšœ˜J˜J˜—šŸ œœ "œ˜DJšœ,œ˜GJš υ˜υJ˜J˜—šŸ œ œ#œœœ œœ˜aJšœ œ;˜Gšœœ˜šœJ˜MJ˜—J˜—J˜——™šŸœ! #œ˜ZJšœœ7œ˜Jšœœ˜2J˜J˜J˜—J˜—JšŸœœ˜JšŸœ˜šŸœœ˜Jšœœœ-œ˜LJšœœ œœ˜3J˜——™šŸ œœ˜*Jšœ œ'˜3Jšœ˜JšŸ œ œœ œœœ œœœ œœ œ˜ˆJšœœf˜†šŸœœ˜š˜š˜šœ œ˜'Jš œœœœœ ˜@Jšœ G˜J—šœ %˜3Jš œœœœœ ˜NJ˜Jšœ˜J˜—šœ ˜0Jšœ˜Jšœ˜J˜—J˜—šœœ˜'Jšœœœ&˜GJšœ˜J˜—J˜J˜šœ)˜)Jšœ˜JšΠboœ˜Jšœ˜ Jšœ˜Jšœ˜—Jšœ˜—š˜Jšœ˜Jšœ œ˜—J˜—šœ˜ Jšœ'˜'Jšœ. R˜€J˜—Jšœ˜šœ˜Jšœœ˜;Jšœ  ‚˜‹—J˜J˜—šŸœœœ˜DJšœ œ'˜3Jšœœ˜ Jšœœ˜Jšœ œœ˜Jšœ(˜(š œœœ#œœ˜EJšœœœ"˜@Jšœ˜—J˜šœœ˜#J˜Jšœ˜—šœ˜Jš œ œœœœ˜XšŸœ˜Jšœ œ˜Jšœœ @˜\J˜—š Ÿ œœœœ μ˜Jšœœœ˜šœ œ˜Jšœœ œ˜+Jšœ˜Jšœ˜—Jšœœ˜J˜—Jšœœœ˜6J˜"Jšœœœ’œ˜<šœ˜š˜šœ  ˜šœ˜˜ J˜Jšœœ˜J˜ J˜Jšœ˜Jšœ ˜—Jšœ˜ J˜——šœœ ˜%Jšœ9œ˜A—J˜Jšœœ I˜rJ˜—Jšœœ™9š™Jšœœœ' *˜iJšœ ’œ˜!šœœœ ±˜ΜJšœœ˜Jšœ˜J˜——Jšœ$˜$Jšœ Y˜mJšœœ ˜8š˜šœ˜JšœA˜AJšœ˜Jšœ˜——Jšœ˜—Jšœ œœœ˜Jšœ˜Jšœ˜—J˜šœœ˜Jšœ#˜#Jšœ˜Jšœœ˜J˜—Jšœ1˜5Jšœœ˜Jšœ ˜)Jšœ˜Jšœ ˜J˜—šŸ œœ-˜@Jšœ œ'˜3Jšœ œ˜Jšœœœ˜%J˜šœ ˜š˜JšŸœœ˜&JšŸ œœ˜/šŸœ ˜-Jšœ˜Jšœ ˜ J˜—šΠknœ˜ Jšœ˜Jšœ ˜ J˜—šŸœ˜!Jšœœ˜#šœ˜J˜%Jšœ% ˜=Jšœ' ˜?Jšœ ˜6Jšœœ˜—Jšœ˜ J˜—Jš£œœ˜Jšœ ˜—š œœœ#œœ˜EJšœœœ!˜>Jšœ˜—Jšœ X˜nJ˜-Jšœ ˜!Jšœ&˜&Jšœœ˜ Jšœ ˜—Jšœ ˜—J˜šŸœœœ œ˜9Jšœ œ+˜7Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ: ΰ˜šJšœ0˜0JšœC ”˜ΧJšœœœœ˜$Jš J™JJšœ$œœ˜2J˜J˜—šŸ œœœ˜2Jšœ œ'˜3J˜Jšœœ˜Jšœ »˜ΠJ˜%Jšœœ˜Jšœœ˜J˜2Jšœ˜—J˜šŸœ œ œœ˜PJšœœœ˜-J˜—J˜šŸœ œœœ œœœ ˜dJšœ-˜-Jšœ œ+˜7Jšœœœ˜šœ˜šœ™šœ™Jšœœ™Jšœ œ }™”——šœœ˜šœ˜Jšœœ˜ šœ˜ Jšœœœ˜+JšœœEœCœ˜˜J˜———šœ˜Jšœœœ˜+Jšœœœ I˜hšœœ˜!J˜J˜J˜Jšœ$œœO 0˜©J˜—šœœ#œ ˜DJšœ œ œœ˜2šœ˜ J˜Jšœ!œ˜'J˜J˜——J˜—Jšœ˜—J˜Jšœ˜ J˜J˜J™έ—š Ÿ œœœœœ˜4Jšœœ˜Jšœ œœ  Z˜rJšœœœœœœœ ˜rš˜šœ&œ˜+Jšœœ˜Jšœœ˜Jšœ˜—Jšœ˜—š˜Jšœœœ˜Jšœœœ˜—J˜——™š Ÿ œ œœœœ  ˜`Jšœ œ+˜7Jšœ œ œ˜/Jšœ7˜7šœœ #˜JJšœ4˜4Jšœœ˜Jšœ˜—Jšœ@œœ /˜†Jšœ˜J˜J˜J™—š Ÿ œœœœ œœ˜OJšœ œ) :˜oJšœ œ œ˜/Jšœœ œ˜6Jš œœœœœ˜PJšœ4˜4šœœ˜$Jšœœ˜Jšœœ* ˜;Jšœ˜Jšœœ˜ J˜—Jšœœœ˜J˜—J˜š Ÿœœœœ œ˜LJšœ œ+˜7Jšœœ˜Jšœ˜J˜J˜—J˜š Ÿœœœœ  ˜FJšœ œ) :˜oJšœœœ˜*Jšœ œ œ˜/Jš œ œœœ V˜sJšœœœ˜Jšœ˜Jšœ˜—J˜š Ÿ œœœœ œœ  ˜[Jš œœœœ"œ˜Lšœ˜Jšœ œ+˜7Jšœ œ œ˜/Jšœœœ ˜<šœœœ˜%Jšœ œ˜1Jšœ  ˜7Jšœœ1˜;—Jšœ˜J˜J˜—J˜——šœ ™ š Ÿœœœœœ˜BJ˜8J˜—J˜šŸœœœœ˜:Jšœœ#  ˜dJ˜3J˜!J˜——šœ™š Ÿ œœœœœ˜?Jšœœœœœœœ%˜nJ˜J˜—šŸ œœœ˜0Jšœœœœœœœ'˜mJ˜J˜—šŸœœœ˜2Jšœœœœœœœ)˜oJ˜—J˜šŸ œ œœœ˜@J˜J˜——™šŸ œœ #œ˜EJ˜6Jšœœ˜ Jšœ œœœ˜šœ˜Jšœ ˜3J˜J˜J˜J˜Jšœ˜—J˜J˜J˜—Jš Ÿ œŸ œŸ œŸ œŸ œœ˜DJšŸœœ  3˜KJ˜šŸœ! #œ˜PJ˜EJ˜GJ˜GJ˜JJ˜GJ˜——™J˜6J˜-J˜;J˜J˜—J˜Jšœ ˜™1J™ Jšœ Οr™#—™0Jšœ €™%—™1Jšœ €™!—™.Jšœ €™—™.Jšœ €™#—™.Jšœ €‹™——™.Jšœ €.™:—™.Jšœ € ™,—™.Jšœ €œ€e™©—™-Jšœ €™—™.Jšœ €!™-—™.Jš œ €*œ€œ€4œ€"œ€™—™.Jšœ €Œ™˜—™.Jšœ €M™Y—J™J™J™™+Jšœ €œ™3—™+Jšœ €™—™,Jšœ €™—™-Jšœ €!™-—™,Jšœ €™—™-Jšœ €œ€ œ™T—J™™,Jšœ €/™;—J™—…—pΎ »