DIRECTORY Ascii, Atom, FS USING [StreamOpen], EditedStream, IO, IOClasses USING [CreateDribbleOutputStream], IOUtils, MessageWindow USING [Append, Clear], RefText, Rope, TiogaOps USING [GetRope, GetTextKey, LastLocWithin, Location, Offset, PutTextKey, Ref, ViewerDoc], TypeScript USING [BackSpace, ChangeLooks, CharsAvailable, Create, Destroyed, Flush, GetChar, IsATypeScript, PutChar, TypeIn, WaitUntilCharsAvail], ViewerClasses USING [Viewer], ViewerEvents USING [EventProc, RegisterEventProc], ViewerIO USING [], ViewerIOExtras USING [], ViewerOps USING [AddProp, FetchProp]; ViewerIOImpl: CEDAR PROGRAM IMPORTS Atom, EditedStream, FS, IO, IOClasses, IOUtils, MessageWindow, RefText, Rope, TiogaOps, TypeScript, ViewerEvents, ViewerOps EXPORTS ViewerIO, ViewerIOExtras = BEGIN STREAM: TYPE = IO.STREAM; ROPE: TYPE = Rope.ROPE; Viewer: TYPE = ViewerClasses.Viewer; StreamProcs: TYPE = IO.StreamProcs; DeliverWhenProc: TYPE = EditedStream.DeliverWhenProc; TypeOfGetDeliverWhen: TYPE = PROC [self: STREAM] RETURNS [proc: DeliverWhenProc, context: REF ANY]; TypeOfSetDeliverWhen: TYPE = PROC [self: STREAM, proc: DeliverWhenProc, context: REF ANY]; TypeOfAppendBufferChars: TYPE = PROC [stream: STREAM, chars: ROPE]; TypeOfUnAppendBufferChars: TYPE = PROC [stream: STREAM, nChars: NAT]; TypeOfSetMode: TYPE = PROC [stream: STREAM, stuff: ROPE, pendingDelete: BOOL, echoAsterisks: BOOL]; ViewerStreamData: TYPE = REF ViewerStreamDataRecord; ViewerStreamDataRecord: TYPE = RECORD [ viewer: Viewer, echoStream: STREAM _ NIL ]; ViewerInStreamProcs: REF StreamProcs; ViewerOutStreamProcs: REF StreamProcs; ViewerOutPFProcs: IOUtils.PFProcs; CreateViewerStreams: PUBLIC PROC [ name: ROPE, viewer: Viewer, backingFile: ROPE, editedStream: BOOL] RETURNS [in: STREAM, out: STREAM] = { streams: LIST OF STREAM; IF viewer = NIL THEN viewer _ TypeScript.Create[info: [name: name, iconic: FALSE]] ELSE IF NOT TypeScript.IsATypeScript[viewer] THEN ERROR IO.Error[$Failure, NIL]; in _ IO.CreateStream[ streamProcs: ViewerInStreamProcs, streamData: NEW[ViewerStreamDataRecord _ [viewer: viewer]]]; IOUtils.StoreData[in, $Name, name]; out _ IO.CreateStream[ streamProcs: ViewerOutStreamProcs, streamData: NEW[ViewerStreamDataRecord _ [viewer: viewer]]]; IOUtils.StoreData[out, $Name, name]; [] _ IOUtils.SetPFProcs[out, ViewerOutPFProcs]; IF backingFile # NIL THEN out _ IOClasses.CreateDribbleOutputStream[ output1: out, output2: FS.StreamOpen[fileName: backingFile, accessOptions: $create]]; streams _ NARROW[ViewerOps.FetchProp[viewer: viewer, prop: $Streams]]; streams _ CONS[in, CONS[out, streams]]; ViewerOps.AddProp[viewer: viewer, prop: $Streams, val: streams]; IF editedStream THEN in _ CreateEditedViewerStream[in: in, echoTo: out, deliverWhen: EditedStream.IsACR] ELSE EditedStream.SetEcho[self: in, echoTo: out]; }; GetViewerFromStream: PUBLIC PROC [stream: STREAM] RETURNS [Viewer] = { WHILE stream # NIL DO WITH stream.streamData SELECT FROM r: ViewerStreamData => RETURN[r.viewer] ENDCASE => stream _ stream.backingStream; ENDLOOP; RETURN [NIL]; }; WasAStreamViewerDestroyed: ViewerEvents.EventProc = { streams: LIST OF STREAM = NARROW[ViewerOps.FetchProp[viewer, $Streams]]; v: Viewer _ viewer; IF streams = NIL THEN RETURN; WHILE (v _ v.link) # NIL AND (v # viewer) DO -- split viewer IF NOT v.destroyed THEN { FOR l: LIST OF STREAM _ streams, l.rest UNTIL l = NIL DO data: ViewerStreamData = NARROW[l.first.streamData]; data.viewer _ v; ENDLOOP; ViewerOps.AddProp[v, $Streams, streams]; RETURN; }; ENDLOOP; }; ViewerGetChar: PROC [self: STREAM] RETURNS [char: CHAR] = { data: ViewerStreamData = NARROW[self.streamData]; IF data.viewer.destroyed THEN ERROR IO.Error[$StreamClosed, self]; char _ TypeScript.GetChar[data.viewer ! TypeScript.Destroyed => IF data.viewer.destroyed THEN ERROR IO.Error[$StreamClosed, self] ELSE RETRY]; IF data.echoStream # NIL THEN data.echoStream.PutChar[char]; }; ViewerEndOf: PROC [self: STREAM] RETURNS [BOOL] = CHECKED { data: ViewerStreamData = NARROW[self.streamData]; IF data.viewer.destroyed THEN ERROR IO.Error[$StreamClosed, self]; RETURN[FALSE]; }; ViewerCharsAvail: PROC [self: STREAM, wait: BOOL] RETURNS [INT] = { data: ViewerStreamData = NARROW[self.streamData]; viewer: Viewer _ data.viewer; IF viewer = NIL OR viewer.destroyed THEN ERROR IO.Error[$StreamClosed, self]; IF wait THEN { TypeScript.WaitUntilCharsAvail[viewer ! TypeScript.Destroyed => IF viewer.destroyed THEN ERROR IO.Error[StreamClosed, self] ELSE RETRY]; RETURN[1] } ELSE RETURN[IF TypeScript.CharsAvailable[viewer] THEN 1 ELSE 0]; }; ViewerSetEcho: PROC [self: STREAM, echoTo: STREAM] = { data: ViewerStreamData = NARROW[self.streamData]; IF data.viewer.destroyed THEN ERROR IO.Error[$StreamClosed, self]; data.echoStream _ echoTo; }; ViewerGetEcho: PROC [self: STREAM] RETURNS [oldEcho: STREAM] = { data: ViewerStreamData = NARROW[self.streamData]; IF data.viewer.destroyed THEN ERROR IO.Error[$StreamClosed, self]; RETURN [data.echoStream]; }; ViewerPutChar: PROC[self: STREAM, char: CHAR] = { data: ViewerStreamData = NARROW[self.streamData]; IF data.viewer.destroyed THEN ERROR IO.Error[$StreamClosed, self]; TypeScript.PutChar[data.viewer, char ! TypeScript.Destroyed => IF data.viewer.destroyed THEN ERROR IO.Error[$StreamClosed, self] ELSE RETRY]; }; ViewerFlush: PROC[self: STREAM] = { data: ViewerStreamData = NARROW[self.streamData]; IF data.viewer.destroyed THEN ERROR IO.Error[$StreamClosed, self]; TypeScript.Flush[data.viewer]; }; ViewerEraseChar: PROC[self: STREAM, char: CHAR] = { data: ViewerStreamData = NARROW[self.streamData]; WHILE data.viewer.destroyed DO IF data.viewer.link # NIL THEN data.viewer _ data.viewer.link ELSE ERROR IO.Error[$StreamClosed, self]; ENDLOOP; TypeScript.BackSpace[data.viewer]; }; ViewerReset: PROC [self: STREAM] = { ENABLE TypeScript.Destroyed => GO TO bye; data: ViewerStreamData = NARROW[self.streamData]; viewer: Viewer = data.viewer; IF viewer = NIL OR viewer.destroyed THEN RETURN; TypeScript.ChangeLooks[viewer, ' ]; DO IF NOT TypeScript.CharsAvailable[viewer] THEN GO TO bye; [] _ TypeScript.GetChar[viewer]; ENDLOOP; EXITS bye => {}; }; ViewerSetLooks: IOUtils.PFCodeProc = { viewer: Viewer = GetViewerFromStream[stream]; looks: ROPE _ NIL; IF viewer = NIL THEN ERROR IO.Error[$NotImplementedForThisStream, stream]; TRUSTED { WITH v: val SELECT FROM null => NULL; atom => looks _ Atom.GetPName[v.value]; rope => looks _ v.value; character => looks _ Rope.FromChar[v.value]; ENDCASE => ERROR IO.Error[$NotImplementedForThisStream, stream] }; FOR i: INT IN [0..Rope.Length[looks]) DO TypeScript.ChangeLooks[viewer, Rope.Fetch[looks, i]]; ENDLOOP; }; Location: TYPE = TiogaOps.Location; Offset: TYPE = TiogaOps.Offset; EditedViewerStreamData: TYPE = REF EditedViewerStreamRecord; EditedViewerStreamRecord: TYPE = RECORD[ ready: REF TEXT, readyPos: INT _ 0, -- ready[readyPos .. ready.length) are the already-activated characters buffer: REF TEXT, echoStream: STREAM, deliverWhen: DeliverWhenProc, context: REF ANY, bufferIsConsistent: BOOL _ FALSE, viewer: Viewer, node: TiogaOps.Ref ]; EditedViewerStreamProcs: REF StreamProcs; CreateEditedViewerStream: PROC [ in: STREAM, echoTo: STREAM, deliverWhen: DeliverWhenProc, context: REF ANY _ NIL] RETURNS [STREAM] = { data: EditedViewerStreamData; data _ NEW[EditedViewerStreamRecord _ [ buffer: NEW[TEXT[256]], ready: NEW[TEXT[256]], readyPos: 0, echoStream: echoTo, deliverWhen: deliverWhen, context: context, viewer: NARROW[in.streamData, ViewerStreamData].viewer, node: NIL]]; RETURN [IO.CreateStream[ streamProcs: EditedViewerStreamProcs, streamData: data, backingStream: in]] }; GetBuffer: PUBLIC PROC [editedViewerStream: STREAM] RETURNS [REF TEXT] = { data: EditedViewerStreamData = NARROW[editedViewerStream.streamData]; GetCurrentBuffer1[data]; RETURN [data.buffer]; }; TypeChars: PUBLIC PROC [editedViewerStream: STREAM, chars: ROPE] = { data: EditedViewerStreamData = NARROW[editedViewerStream.streamData]; TypeScript.TypeIn[data.viewer, chars]; }; GetCurrentBuffer: PROC [data: EditedViewerStreamData] = INLINE { IF data.bufferIsConsistent THEN RETURN ELSE GetCurrentBuffer1[data]; }; GetCurrentBuffer1: PROC [data: EditedViewerStreamData] = { r: ROPE; loc: Location; data.buffer.length _ 0; data.bufferIsConsistent _ TRUE; IF data.node = NIL THEN RETURN; TypeScript.Flush[data.viewer]; r _ TiogaOps.GetRope[data.node]; loc _ TiogaOps.GetTextKey[node: data.node, key: data]; IF loc.node # data.node THEN ERROR; { Put: PROC [c: CHAR] RETURNS [quit: BOOL] = { data.buffer _ RefText.InlineAppendChar[data.buffer, c]; RETURN [quit: FALSE] }; [] _ Rope.Map[base: r, start: loc.where + 1, len: INT.LAST, action: Put]; }; }; EditedViewerStreamAppendBufferChars: PROC [stream: STREAM, chars: ROPE] = { data: EditedViewerStreamData = NARROW[stream.streamData]; Append1: PROC [c: CHAR] RETURNS [quit: BOOL] = { AppendBufferChar[data, c]; RETURN [quit: FALSE] }; GetCurrentBuffer1[data]; [] _ chars.Map[action: Append1]; }; AppendBufferChar: PROC [data: EditedViewerStreamData, char: CHAR] = INLINE { loc: Location; data.buffer _ RefText.InlineAppendChar[data.buffer, char]; IF data.node = NIL THEN { TypeScript.Flush[data.viewer]; loc _ TiogaOps.LastLocWithin[TiogaOps.ViewerDoc[data.viewer]]; IF loc.where = 0 THEN data.echoStream.PutChar[' ] ELSE loc.where _ loc.where - 1; }; data.echoStream.PutChar[char]; IF data.node = NIL THEN { TypeScript.Flush[data.viewer]; TiogaOps.PutTextKey[node: loc.node, where: loc.where, key: data]; data.node _ loc.node; }; }; EditedViewerStreamUnAppendBufferChars: PROC [stream: STREAM, nChars: NAT] = { data: EditedViewerStreamData = NARROW[stream.streamData]; GetCurrentBuffer1[data]; FOR i: NAT IN [0 .. MIN[nChars, data.buffer.length]) DO UnAppendBufferChar[data] ENDLOOP; }; UnAppendBufferChar: PROC [data: EditedViewerStreamData] = INLINE { char: CHAR = data.buffer[data.buffer.length - 1]; data.echoStream.EraseChar[char]; data.buffer.length _ data.buffer.length - 1; IF data.buffer.length = 0 THEN data.node _ NIL; }; EditedViewerStreamSetMode: PROC [stream: STREAM, stuff: ROPE, pendingDelete: BOOL, echoAsterisks: BOOL] = { data: EditedViewerStreamData = NARROW[stream.streamData]; data.buffer.length _ 0; data.readyPos _ data.ready.length; EditedViewerStreamAppendBufferChars[stream, stuff]; }; EditedViewerStreamGetDeliverWhen: PROC [self: STREAM] RETURNS [proc: DeliverWhenProc, context: REF ANY] = { data: EditedViewerStreamData = NARROW[self.streamData]; RETURN [data.deliverWhen, data.context]; }; EditedViewerStreamSetDeliverWhen: PROC [self: STREAM, proc: DeliverWhenProc, context: REF ANY] = { data: EditedViewerStreamData = NARROW[self.streamData]; data.deliverWhen _ proc; data.context _ context; }; EditedViewerStreamGetChar: PROC [self: STREAM] RETURNS [char: CHAR] = { data: EditedViewerStreamData = NARROW[self.streamData]; IsEditCommand: PROC [char: CHAR] RETURNS [BOOL] = { RETURN [SELECT char FROM Ascii.DEL, Ascii.ControlA, Ascii.BS, Ascii.ControlW, Ascii.ControlQ => TRUE, ENDCASE => FALSE]; }; BackChar: PROC = { IF data.buffer.length > 0 THEN { UnAppendBufferChar[data]; } }; BackWord: PROC = { alphaSeen: BOOL _ FALSE; UNTIL data.buffer.length = 0 DO SELECT data.buffer[data.buffer.length - 1] FROM IN ['A..'Z], IN ['a..'z], IN ['0..'9] => alphaSeen _ TRUE; ENDCASE => IF alphaSeen THEN EXIT; UnAppendBufferChar[data]; ENDLOOP; }; BackLine: PROC = { UNTIL data.buffer.length = 0 DO IF data.buffer[data.buffer.length - 1] = IO.CR THEN EXIT; UnAppendBufferChar[data]; ENDLOOP; }; DO IF data.readyPos < data.ready.length THEN { char _ data.ready[data.readyPos]; data.readyPos _ data.readyPos + 1; RETURN [char]; }; { appendChar, activate: BOOL; char _ self.backingStream.GetChar[ ! IO.EndOfStream => IF data.buffer.length = 0 THEN REJECT ELSE GOTO activateBuffer]; [appendChar: appendChar, activate: activate] _ data.deliverWhen[char, NIL --data.buffer--, self, data.context]; IF appendChar THEN { SELECT char FROM Ascii.DEL => { ENABLE UNWIND => data.buffer.length _ 0; ERROR EditedStream.Rubout[self]; }; Ascii.ControlA, Ascii.BS => { GetCurrentBuffer1[data]; BackChar[] }; Ascii.ControlW => { GetCurrentBuffer1[data]; BackWord[] }; Ascii.ControlQ => { GetCurrentBuffer1[data]; BackLine[] }; Ascii.ESC => { GetCurrentBuffer1[data]; IF data.buffer.length = 0 THEN { FOR i: NAT IN [0..data.ready.length-1) DO AppendBufferChar[data, data.ready[i]]; ENDLOOP } }; ENDCASE => { IF data.buffer.length = 0 THEN GetCurrentBuffer1[data]; AppendBufferChar[data, char]; } }; IF activate THEN GOTO activateBuffer; EXITS activateBuffer => { GetCurrentBuffer1[data]; data.ready.length _ 0; data.ready _ RefText.Append[data.ready, data.buffer]; data.readyPos _ 0; data.buffer.length _ 0; data.node _ NIL; } } ENDLOOP; }; EditedViewerStreamEndOf: PROC [self: STREAM] RETURNS [BOOL] = { data: EditedViewerStreamData = NARROW[self.streamData]; RETURN[data.readyPos = data.ready.length AND self.backingStream.EndOf[]]; }; EditedViewerStreamCharsAvail: PROC [self: STREAM, wait: BOOL] RETURNS [INT] = { data: EditedViewerStreamData = NARROW[self.streamData]; IF data.readyPos < data.ready.length THEN RETURN [data.ready.length-data.readyPos]; RETURN[self.backingStream.CharsAvail[wait]]; }; EditedViewerStreamBackup: PROC [self: STREAM, char: CHAR] = { data: EditedViewerStreamData = NARROW[self.streamData]; IF data.readyPos = 0 OR data.ready[data.readyPos - 1] # char THEN ERROR IO.Error[$IllegalBackup, self]; data.readyPos _ data.readyPos - 1; }; EditedViewerStreamSetEcho: PROC [self: STREAM, echoTo: STREAM] = { data: EditedViewerStreamData = NARROW[self.streamData]; IF echoTo = NIL THEN RETURN; -- ignore request to turn off echoing IF data.echoStream # echoTo THEN ERROR IO.Error[$Failure, self]; }; EditedViewerStreamGetEcho: PROC [self: STREAM] RETURNS [STREAM] = { data: EditedViewerStreamData = NARROW[self.streamData]; RETURN [data.echoStream]; }; EditedViewerStreamReset: PROC [self: STREAM] = { data: EditedViewerStreamData = NARROW[self.streamData]; data.node _ NIL; data.buffer.length _ 0; data.ready.length _ 0; data.readyPos _ 0; self.backingStream.Reset[]; data.echoStream.Reset[]; }; MessageWindowStreamProcs: REF StreamProcs; CreateMessageWindowStream: PUBLIC PROC [] RETURNS [STREAM] = { RETURN[IO.CreateStream[ streamProcs: MessageWindowStreamProcs, backingStream: IO.ROS[], streamData: NIL]]; }; MessageWindowStreamFlush: PROC[self: STREAM] = { r: ROPE _ self.backingStream.RopeFromROS[]; i: INT _ 0; self.backingStream.Reset[]; WHILE (i _ Rope.Find[s1: r, s2: "\n", pos1: i]) # -1 DO r _ Rope.Replace[base: r, start: i, len: 1, with: " "]; ENDLOOP; MessageWindow.Append[message: r, clearFirst: TRUE] }; MessageWindowStreamReset: PROC[self: STREAM] = { self.backingStream.Reset[]; MessageWindow.Clear[] }; [] _ ViewerEvents.RegisterEventProc [ proc: WasAStreamViewerDestroyed, filter: $Typescript, event: $destroy]; ViewerInStreamProcs _ EditedStream.AddStreamProcs[to: IO.CreateStreamProcs[ variety: $input, class: $ViewersInput, getChar: ViewerGetChar, endOf: ViewerEndOf, charsAvail: ViewerCharsAvail, reset: ViewerReset], setEcho: ViewerSetEcho, getEcho: ViewerGetEcho]; ViewerOutStreamProcs _ IO.CreateStreamProcs[ variety: $output, class: $ViewersOutput, putChar: ViewerPutChar, flush: ViewerFlush, eraseChar: ViewerEraseChar, reset: ViewerReset]; ViewerOutPFProcs _ IOUtils.CopyPFProcs[NIL]; [] _ IOUtils.SetPFCodeProc[ViewerOutPFProcs, 'l, ViewerSetLooks]; EditedViewerStreamProcs _ EditedStream.AddStreamProcs[to: IO.CreateStreamProcs[ variety: $input, class: $EditedViewer, getChar: EditedViewerStreamGetChar, endOf: EditedViewerStreamEndOf, charsAvail: EditedViewerStreamCharsAvail, backup: EditedViewerStreamBackup, reset: EditedViewerStreamReset], setEcho: EditedViewerStreamSetEcho, getEcho: EditedViewerStreamGetEcho]; IOUtils.StoreProc[EditedViewerStreamProcs, $GetDeliverWhen, NEW[TypeOfGetDeliverWhen _ EditedViewerStreamGetDeliverWhen]]; IOUtils.StoreProc[EditedViewerStreamProcs, $SetDeliverWhen, NEW[TypeOfSetDeliverWhen _ EditedViewerStreamSetDeliverWhen]]; IOUtils.StoreProc[EditedViewerStreamProcs, $AppendBufferChars, NEW[TypeOfAppendBufferChars _ EditedViewerStreamAppendBufferChars]]; IOUtils.StoreProc[EditedViewerStreamProcs, $UnAppendBufferChars, NEW[TypeOfUnAppendBufferChars _ EditedViewerStreamUnAppendBufferChars]]; IOUtils.StoreProc[EditedViewerStreamProcs, $SetMode, NEW[TypeOfSetMode _ EditedViewerStreamSetMode]]; MessageWindowStreamProcs _ IO.CreateStreamProcs[ variety: $output, class: $MessageWindow, flush: MessageWindowStreamFlush, reset: MessageWindowStreamReset ]; END. tViewerIOImpl.mesa Copyright c 1984 by Xerox Corporation. All rights reserved. Last edited by: MBrown on January 13, 1984 1:42 pm Russ Atkinson, November 5, 1984 8:56:31 pm PST -- CreateViewerStreams should not raise IO.Error[$Failure], should not raise FS.Error? -- Should Destroy of a viewer cause implicit Close? -- WasAStreamViewerDestroyed seems to require monitor protection in its access to the stream. -- Why is Close not implemented for Viewer streams? -- What if node goes away in AppendBufferChar? -- Edited stream backup does not allow backup past ready chars, should it? -- SetEcho with stream in the backed-up state should be some sort of error? Viewer Input and Output Streams (Typescript) Types This type is used for both input and output stream; the echoStream field is ignored for output streams. A ViewerStreamDataRecord is not shared between multiple streams. Creation make all streams attached to the viewer about to be destroyed attached to this undestroyed child Input procs If a split viewer is destroyed while a process is inside of a call to TypeScript.GetChar on that viewer, TypeScript.Destroyed is raised in that process. Before TypeScript.Destroyed is raised, data.viewer on any stream for this viewer will have been replaced by one of the other viewers (viewers calls WasAStreamViewerDestroyed below); hence the RETRY will call TypeScript.GetChar on another viewer. The same logic is used in ViewerCharsAvail and ViewerPutChar. Output procs Note, we cannot use TypeScript.Flush, since it tosses all of our nice state on the floor. Put the looks back into their ground state. Edited Viewer Input Stream (Editable Typescript) in is a Viewer input stream and EditedStream.GetEcho[in] = NIL. echoTo is a Viewer output stream for the same viewer as in, or is a dribble stream. Called from EditedViewerStreamAppendBufferChars, EditedViewerStreamUnAppendBufferChars, EditedViewerStreamGetChar Makes data.buffer consistent with current state of the underlying viewer This is the only proc that tests the state of data.bufferIsConsistent -- RefText.AppendRope[to: data.buffer, from: r, start: loc.where + 1]; Assumes data.buffer is consistent What if node goes away? What if node goes away? Assumes data.buffer is consistent erases last character, if any, in buffer erases last "word" (consecutive run of letters and numbers), if any, in buffer erases buffer back to (not including) previous CR, if any GetCurrentBuffer[data]; looks wrong ... why can't you backup past ready chars? Can't change echo stream, because "truth" about buffered characters is stored in Tioga document behind echo stream. Message Window Stream Module Initialization ΚC– "Cedar" style˜headšΠblœ ™Jšœ Οmœ1™<šœ™Jšœ Οc™"J™.—IbodyšŸβ™β—˜codešΟk ˜ Mšœ˜Mšœ˜Mš œ œ˜Mšœ ˜ Mš œ˜Mšœ  œ˜,Mšœ˜Mšœ œ˜$Mšœ ˜ Mšœ˜Mšœ  œT˜bMšœ  œ‚˜’Mšœ œ ˜Mšœ  œ ˜2Mšœ  œ˜Mšœ œ˜Mšœ  œ˜%M˜——Jš œ œ ˜šœ˜Mš œ œ œa˜ƒMš œ˜ Mšœ ˜M˜Mš œ œ œ œ˜Mš œ œ œ˜M˜Mšœ œ˜$Mšœ  œ œ ˜#Mšœ œ ˜5MšΟnœ œ œ œ œ" œ œ˜cMš ‘œ œ œ œ" œ œ˜ZMš ‘œ œ œ  œ  œ˜CMš ‘œ œ œ  œ  œ˜Eš ‘ œ œ œ  œ  œ œ˜MMšœ œ˜——™,šœ™šœ œ œ˜4Jšœ©™©—šœ œ œ˜'J˜Jšœ  œ ˜J˜—Iunitšœ œ ˜%Jšœ œ ˜&Jšœ"˜"—™š‘œ œ œ˜"Jšœ œ œ œ˜BJš œ œ œ˜%Jšœ  œ œ œ˜Jš œ  œ œ7 œ˜RJš œ œ œ" œ œ œ œ˜Pšœ œ˜Jšœ!˜!Jšœ  œ-˜<—Jšœ#˜#šœ œ˜Jšœ"˜"Jšœ  œ-˜<—Jšœ$˜$Jšœ/˜/š œ œ œ+˜DJšœ ˜ Jšœ  œ<˜G—Jšœ  œ6˜FJšœ  œ œ˜'Jšœ@˜@š œ ˜JšœS˜S—Jš œ-˜1J˜—š ‘œ œ œ  œ œ ˜Fš œ  œ ˜š œ œ ˜"Jšœ œ ˜'Jš œ"˜)—Jš œ˜—Jš œ œ˜ J˜—šΟbœ˜5Jš œ  œ œ œ œ(˜HJ˜Jš œ  œ œ œ˜š  œ œ œ œŸ˜<š œ œ  œ˜Jšœa™aš  œ œ œ œ œ œ ˜8Jšœ œ˜4J˜Jš œ˜—Jšœ)˜)Jš œ˜J˜—Jš œ˜ —J˜——™ š ‘ œ œ œ œ œ˜˜>Jš  œ œ œ œ œ œ˜N—Jšœ˜—š‘ œ œ œ˜#Jšœ œ˜1Jš œ œ œ œ˜BJ˜Jšœ˜—š‘œ œ œ œ˜3Jšœ œ˜1š œ ˜Jš œ œ œ˜=Jš œ œ œ˜)Jš œ˜—J˜"Jšœ˜—š‘ œ œ œ˜$JšœY™YJš œ œ œ˜)Jšœ œ˜1J˜Jš  œ  œ œ œ œ˜0šœ$˜$Jšœ+™+—š ˜Jš  œ œ# œ œ œ˜8Jšœ ˜ Jš œ˜—Jš œ ˜Jšœ˜—š’œ˜'J˜-Jšœ œ œ˜Jš  œ  œ œ œ œ-˜Jš œ œ œ ˜!Jšœ œ˜ J˜'J˜J˜,Jš œ œ œ/˜B—š œ œ œ ˜(Jšœ5˜5Jš œ˜—J˜———™0Nšœ  œ˜#Jšœ œ˜Nšœ œ œ˜<šœ œ œ˜(Jšœ œ œ˜Jšœ  œŸG˜ZJšœ œ œ˜Jšœ  œ˜J˜Jšœ  œ œ˜Jšœ œ œ˜!Jšœ˜Jšœ˜J˜—Nšœ œ ˜)š‘œ œ˜ Jš œ œ  œ) œ œ œ˜QJš œ œ˜Jšœ; œ™?JšœS™SJšœ˜šœ œ˜(Jšœ œ œ˜Jšœ œ œ˜Jšœ ˜ Jšœ˜Jšœ+˜+Jšœ œ)˜7Jšœ œ˜ —š œ œ˜JšœK˜K—Jšœ˜—š‘ œ œ œ œ œ œ œ˜JJšœ œ ˜EJšœ˜Jš œ˜Jšœ˜—š ‘ œ œ œ œ  œ˜DJšœ œ ˜EJšœ&˜&Jšœ˜—š‘œ œ" œ˜@Jšœq™qJšœH™HJšœE™EJš œ œ œ œ˜DJšœ˜—š‘œ œ#˜:Jšœ œ˜Jšœ˜Jšœ œ˜Jš œ  œ œ œ˜Jšœ˜Jšœ ˜ Jšœ6˜6Jš œ œ œ˜#šœ˜JšœF™Fš ‘œ œ œ œ œ˜,Jšœ7˜7Jš œ œ˜Jšœ˜—Jšœ2 œ œ˜IJšœ˜—J˜—š‘#œ œ  œ  œ˜KJšœ œ˜9š ‘œ œ œ œ œ˜0Jšœ œ œ˜2—Jšœ˜Jšœ ˜ J˜—š‘œ œ& œ œ˜LJšœ!™!J˜J˜:š œ  œ œ˜Jšœ˜šœ>˜>Jšœ™—Jš œ œ œ˜QJšœ˜—Jšœ˜š œ  œ œ˜Jšœ˜šœA˜AJšœ™—Jšœ˜J˜—Jšœ˜—š‘%œ œ  œ  œ˜MJšœ œ˜9Jšœ˜š  œ œ œ œ ˜7Jšœ˜Jš œ˜—J˜—š‘œ œ" œ˜BJšœ!™!Jšœ œ'˜1Jšœ ˜ J˜,Jš œ œ  œ˜/Jšœ˜—š ‘œ œ  œ  œ œ œ˜lJšœ œ˜9J˜Jšœ"˜"Jšœ3˜3M˜—š ‘ œ œ œ œ" œ œ˜kJšœ œ˜7Jš œ"˜(M˜—š ‘ œ œ œ" œ œ˜bJšœ œ˜7Jšœ1˜1M˜—š ‘œ œ œ œ œ˜GJšœ œ˜7š ‘ œ œ œ œ œ˜3š œ œ ˜Jšœ œ œ$ œ˜LJš œ œ˜—Jšœ˜—š‘œ œ˜Jšœ(™(š œ œ˜ J˜Jšœ˜—Jšœ˜—š‘œ œ˜JšœN™NMšœ  œ œ˜š œ ˜š œ% ˜/Mš œ  œ  œ œ˜:Mš œ œ  œ œ˜"—J˜Mš œ˜—Mšœ˜—š‘œ œ˜Jšœ/ œ™9š œ ˜Jš  œ' œ œ œ œ˜9J˜Jš œ˜—Jšœ˜—š ˜š œ# œ˜+Jšœ!˜!J˜"Jš œ˜Jšœ˜—˜Jšœ œ˜šœ% œ˜6Jš  œ œ œ œ œ˜@—Jšœ™šœ.˜.Jšœ œŸœ˜@—š œ  œ˜š œ ˜šœ œ œ œ˜7Jš œ˜ J˜—Jšœ œ,˜DJšœ:˜:Jšœ:˜:šœ œ˜Jšœ˜š œ œ˜ š œ œ œ ˜)J˜&Jš ˜—J˜—J˜—š œ˜ Jš œ œ˜7Jšœ˜J˜——J˜—Jš œ  œ œ˜%š œ˜Jšœ˜Jšœ˜J˜5Jšœ˜J˜Jšœ  œ˜J˜—J˜—Jš œ˜—Jšœ˜—š ‘œ œ œ œ œ˜?Jšœ œ˜7Jš œ# œ˜JJšœ˜—š ‘œ œ œ œ œ œ˜OJšœ œ˜7Jš œ# œ œ#˜SJš œ&˜,Jšœ˜—š‘œ œ œ œ˜=Jšœ6™6Jšœ œ˜7š œ œ& ˜AJš œ œ˜%—Jšœ"˜"Jšœ˜—š‘œ œ œ  œ˜BJšœ œ˜7Jš  œ  œ œ œŸ%˜Cš œ œ œ œ˜@Jšœs™s—Jšœ˜—š ‘œ œ œ œ œ˜CJšœ œ˜7Jš œ˜Jšœ˜—š‘œ œ œ˜0Jšœ œ˜7Jšœ  œ˜J˜J˜J˜J˜Jšœ˜Jšœ˜——™šœ œ ˜*J˜—š ‘œ œ œ œ œ˜?š œ œ˜Jšœ6 œ œ œ˜R—Jšœ˜—J˜š‘œ œ œ˜0Jšœ œ$˜+Jšœ œ˜ Jšœ˜š œ0 ˜7Jšœ7˜7Jš œ˜—Jšœ- œ˜2Jšœ˜—J˜š‘œ œ œ˜0Jšœ˜Jšœ˜Jšœ˜——™˜%J˜G—šœ6 œ˜KJšœ&˜&J˜J˜J˜Jšœ˜Jšœ˜Jšœ˜—šœ œ˜,Jšœ(˜(J˜J˜Jšœ˜Jšœ˜—Nšœ' œ˜,JšœA˜Ašœ: œ˜OJ˜&J˜#J˜J˜)J˜!J˜ Jšœ#˜#Jšœ$˜$šœ*˜*Jšœ œ;˜O—šœ*˜*Jšœ œ;˜O—šœ*˜*Jšœ œA˜X—šœ*˜*Jšœ œE˜^—šœ*˜*Jšœ  œ-˜:——šœ œ˜0Jšœ(˜(J˜ J˜J˜—Nš œ˜——…—@’`I