DIRECTORY Ascii, Atom, Basics, EditedStream, IO, IOUtils, Rope, RefText, RuntimeError USING [BoundsFault]; IOEditedStreamImpl: CEDAR PROGRAM IMPORTS Basics, IO, IOUtils, RefText, Rope, RuntimeError EXPORTS EditedStream SHARES IO --for representation of StreamProcs = BEGIN STREAM: TYPE = IO.STREAM; ROPE: TYPE = Rope.ROPE; StreamProcs: TYPE = IO.StreamProcs; TypeOfSetEcho: TYPE = PROC [self: STREAM, echoTo: STREAM]; TypeOfGetEcho: TYPE = PROC [self: STREAM] RETURNS [oldEcho: STREAM]; 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 ¬ NIL]; 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]; InlineLookupProc: PROC [self: STREAM, operation: ATOM] RETURNS [proc: REF ANY] = --INLINE-- { FOR l: Atom.PropList ¬ self.streamProcs.propList, l.rest UNTIL l = NIL DO IF l.first.key = operation THEN RETURN[l.first.val]; ENDLOOP; }; GetDeliverWhen: PUBLIC PROC [self: STREAM] RETURNS [proc: DeliverWhenProc, context: REF ANY] = { p: REF ANY = InlineLookupProc[self, $GetDeliverWhen]; IF p # NIL THEN { [proc, context] ¬ (NARROW[p, REF TypeOfGetDeliverWhen])­ [self]; RETURN } ELSE ERROR IO.Error[$NotImplementedForThisStream, self]; }; SetDeliverWhen: PUBLIC PROC [self: STREAM, proc: DeliverWhenProc, context: REF ANY] = { p: REF ANY = InlineLookupProc[self, $SetDeliverWhen]; IF p # NIL THEN { (NARROW[p, REF TypeOfSetDeliverWhen])­ [self, proc, context]; RETURN } ELSE ERROR IO.Error[$NotImplementedForThisStream, self]; }; AppendBufferChars: PUBLIC PROC [stream: STREAM, chars: ROPE] = { p: REF ANY = InlineLookupProc[stream, $AppendBufferChars]; IF p # NIL THEN { (NARROW[p, REF TypeOfAppendBufferChars])­ [stream, chars]; RETURN } ELSE ERROR IO.Error[$NotImplementedForThisStream, stream]; }; UnAppendBufferChars: PUBLIC PROC [stream: STREAM, nChars: NAT] = { p: REF ANY = InlineLookupProc[stream, $UnAppendBufferChars]; IF p # NIL THEN { (NARROW[p, REF TypeOfUnAppendBufferChars])­ [stream, nChars]; RETURN } ELSE ERROR IO.Error[$NotImplementedForThisStream, stream]; }; SetMode: PUBLIC PROC [stream: STREAM, stuff: ROPE, pendingDelete: BOOL, echoAsterisks: BOOL] = { p: REF ANY = InlineLookupProc[stream, $SetMode]; IF p # NIL THEN { (NARROW[p, REF TypeOfSetMode])­ [stream, stuff, pendingDelete, echoAsterisks]; RETURN } ELSE ERROR IO.Error[$NotImplementedForThisStream, stream]; }; EditedStreamData: TYPE = REF EditedStreamRecord; EditedStreamRecord: TYPE = RECORD[ ready: REF TEXT, readyPos: INT ¬ 0, -- ready[readyPos .. ready.length) are the already-activated characters buffer: REF TEXT, echoStream: STREAM ¬ NIL, deliverWhen: DeliverWhenProc, context: REF ANY, echoAsterisks: BOOL ¬ FALSE, pendingDelete: BOOL ¬ FALSE ]; EditedStreamProcs: REF StreamProcs; IsACR: PUBLIC DeliverWhenProc = { RETURN [appendChar: TRUE, activate: (char = IO.CR) OR (char = IO.LF)] }; IsANL: PUBLIC DeliverWhenProc = { RETURN [appendChar: TRUE, activate: (char = IO.CR) OR (char = IO.LF)] }; Create: PUBLIC PROC [in: STREAM, echoTo: STREAM, deliverWhen: DeliverWhenProc, context: REF ANY] RETURNS [STREAM] = { h: STREAM ¬ IO.CreateStream[ streamProcs: EditedStreamProcs, streamData: NEW[EditedStreamRecord ¬ [ buffer: NEW[TEXT[256]], ready: NEW[TEXT[256]], deliverWhen: deliverWhen, context: context]], backingStream: in ]; SetEcho[in, NIL]; SetEcho[h, echoTo]; RETURN [h] }; EditedStreamAppendBufferChars: PROC [stream: STREAM, chars: ROPE] = { data: EditedStreamData = NARROW[stream.streamData]; Append1: PROC [c: CHAR] RETURNS [quit: BOOL] = { AppendBufferChar[data, c]; RETURN [quit: FALSE] }; [] ¬ chars.Map[action: Append1]; }; AppendBufferChar: PROC [data: EditedStreamData, char: CHAR] = --INLINE-- { data.buffer ¬ RefText.InlineAppendChar[data.buffer, char]; IF data.echoStream # NIL THEN { IF data.echoAsterisks AND char > IO.SP THEN data.echoStream.PutChar['*] ELSE data.echoStream.PutChar[char]; }; }; EditedStreamUnAppendBufferChars: PUBLIC PROC [stream: STREAM, nChars: NAT] = { data: EditedStreamData = NARROW[stream.streamData]; FOR i: NAT IN [0 .. MIN[nChars, data.buffer.length]) DO UnAppendBufferChar[data] ENDLOOP; }; UnAppendBufferChar: PROC [data: EditedStreamData] = --INLINE-- { IF data.echoStream # NIL THEN { char: CHAR = data.buffer[data.buffer.length - 1]; IF data.echoAsterisks AND char > IO.SP THEN data.echoStream.EraseChar['*] ELSE data.echoStream.EraseChar[char] }; data.buffer.length ¬ data.buffer.length - 1; }; EditedStreamSetMode: PROC [stream: STREAM, stuff: ROPE, pendingDelete: BOOL, echoAsterisks: BOOL] = { data: EditedStreamData = NARROW[stream.streamData]; data.buffer.length ¬ 0; data.readyPos ¬ data.ready.length; data.pendingDelete ¬ pendingDelete; data.echoAsterisks ¬ echoAsterisks; AppendBufferChars[stream, stuff]; }; EditedStreamGetDeliverWhen: PROC [self: STREAM] RETURNS [proc: DeliverWhenProc, context: REF ANY] = { data: EditedStreamData = NARROW[self.streamData]; RETURN [data.deliverWhen, data.context]; }; EditedStreamSetDeliverWhen: PROC [self: STREAM, proc: DeliverWhenProc, context: REF ANY] = { data: EditedStreamData = NARROW[self.streamData]; data.deliverWhen ¬ proc; data.context ¬ context; }; EditedStreamGetChar: PROC [self: STREAM] RETURNS [char: CHAR] = { data: EditedStreamData = 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 c: CHAR ~ data.buffer[data.buffer.length - 1]; IF (c = IO.CR) OR (c = IO.LF) 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, data.buffer, self, data.context]; IF data.pendingDelete THEN { data.pendingDelete ¬ FALSE; IF NOT activate AND appendChar AND NOT IsEditCommand[char] THEN UnAppendBufferChars[self, data.buffer.length]; }; IF appendChar THEN { SELECT char FROM Ascii.DEL => { ENABLE UNWIND => data.buffer.length ¬ 0; ERROR IO.Rubout[self]; }; Ascii.ControlA, Ascii.BS => BackChar[]; Ascii.ControlW => BackWord[]; Ascii.ControlQ => BackLine[]; Ascii.ESC => IF data.buffer.length = 0 THEN { FOR i: NAT IN [0..data.ready.length-1) DO AppendBufferChar[data, data.ready[i]]; ENDLOOP }; ENDCASE => AppendBufferChar[data, char]; }; IF activate THEN GOTO activateBuffer; EXITS activateBuffer => { data.ready.length ¬ 0; data.ready ¬ RefText.Append[data.ready, data.buffer]; data.readyPos ¬ 0; data.buffer.length ¬ 0; data.echoAsterisks ¬ FALSE; } } ENDLOOP; }; EditedStreamEndOf: PROC [self: STREAM] RETURNS [BOOL] = { data: EditedStreamData = NARROW[self.streamData]; RETURN[data.readyPos = data.ready.length AND self.backingStream.EndOf[]]; }; EditedStreamCharsAvail: PROC [self: STREAM, wait: BOOL] RETURNS [INT] = { data: EditedStreamData = NARROW[self.streamData]; IF data.readyPos < data.ready.length THEN RETURN [data.ready.length-data.readyPos]; RETURN[self.backingStream.CharsAvail[wait]]; }; EditedStreamBackup: PROC [self: STREAM, char: CHAR] = { data: EditedStreamData = NARROW[self.streamData]; IF data.readyPos = 0 OR data.ready[data.readyPos - 1] # char THEN IO.Error[$IllegalBackup, self]; data.readyPos ¬ data.readyPos - 1; }; EditedStreamSetEcho: PROC [self: STREAM, echoTo: STREAM] = { data: EditedStreamData = NARROW[self.streamData]; data.echoStream ¬ echoTo; }; EditedStreamGetEcho: PROC [self: STREAM] RETURNS [STREAM] = { data: EditedStreamData = NARROW[self.streamData]; RETURN [data.echoStream]; }; EditedStreamReset: PROC [self: STREAM] = { data: EditedStreamData = NARROW[self.streamData]; data.buffer.length ¬ 0; data.ready.length ¬ 0; data.readyPos ¬ 0; self.backingStream.Reset[]; IF data.echoStream # NIL THEN data.echoStream.Reset[]; }; SetEchoData: TYPE = REF SetEchoRecord; SetEchoRecord: TYPE = RECORD [echoStream: STREAM, buffer: REF TEXT]; setEchoProcs: REF StreamProcs; SetEcho: PUBLIC PROC [self: STREAM, echoTo: STREAM] = { origSelf: STREAM ¬ self; DO proc: REF ANY ¬ InlineLookupProc[self, $SetEcho]; IF proc # NIL THEN { (NARROW[proc, REF TypeOfSetEcho])­ [self, echoTo]; RETURN; } ELSE IF self.backingStream # NIL THEN self ¬ self.backingStream ELSE EXIT; ENDLOOP; IF echoTo = NIL THEN RETURN; IOUtils.AmbushStream[ self: origSelf, streamProcs: setEchoProcs, streamData: NEW[SetEchoRecord ¬ [echoStream: NIL, buffer: RefText.New[8]]]]; DefaultSetEchoSetEcho[origSelf, echoTo]; }; GetEcho: PUBLIC PROC [self: STREAM] RETURNS [oldEcho: STREAM] = { origSelf: STREAM ¬ self; DO proc: REF ANY ¬ InlineLookupProc[self, $GetEcho]; IF proc # NIL THEN RETURN[(NARROW[proc, REF TypeOfGetEcho])­ [self] ] ELSE IF self.backingStream # NIL THEN self ¬ self.backingStream ELSE EXIT; ENDLOOP; RETURN [NIL]; }; DefaultSetEchoSetEcho: PROC [self: STREAM, echoTo: STREAM] = { data: SetEchoData = NARROW[self.streamData]; data.echoStream ¬ echoTo; IF echoTo = NIL AND data.buffer.length = 0 THEN IOUtils.UnAmbushStream[self]; }; DefaultSetEchoGetEcho: PROC [self: STREAM] RETURNS [oldEcho: STREAM] = { data: SetEchoData = NARROW[self.streamData]; RETURN[data.echoStream]; }; DefaultSetEchoBackup: PROC [self: STREAM, char: CHAR] = { data: SetEchoData = NARROW[self.streamData]; data.buffer ¬ RefText.InlineAppendChar[data.buffer, char ! RuntimeError.BoundsFault => ERROR IO.Error[$BufferOverflow, self]]; }; DefaultSetEchoGetChar: PROC [self: STREAM] RETURNS [char: CHAR] = { data: SetEchoData = NARROW[self.streamData]; IF data.buffer.length > 0 THEN { data.buffer.length ¬ data.buffer.length - 1; char ¬ data.buffer[data.buffer.length]; RETURN[char]; }; char ¬ self.backingStream.GetChar[]; IF data.echoStream # NIL THEN data.echoStream.PutChar[char]; }; DefaultSetEchoGetBlock: PROC [self: STREAM, block: REF TEXT, startIndex: NAT, count: NAT] RETURNS [nBytesRead: NAT] = { data: SetEchoData = NARROW[self.streamData]; nBytesRead ¬ 0; WHILE data.buffer.length > 0 DO IF count = 0 OR startIndex >= block.maxLength THEN RETURN [nBytesRead]; data.buffer.length ¬ data.buffer.length - 1; block[startIndex] ¬ data.buffer[data.buffer.length]; startIndex ¬ startIndex + 1; count ¬ count - 1; nBytesRead ¬ nBytesRead + 1; ENDLOOP; nBytesRead ¬ nBytesRead + self.backingStream.GetBlock[block, startIndex, count]; IF data.echoStream # NIL THEN data.echoStream.PutBlock[block, startIndex, block.length-startIndex]; RETURN [nBytesRead]; }; DefaultSetEchoUnsafeGetBlock: UNSAFE PROC [self: STREAM, block: IO.UnsafeBlock] RETURNS [nBytesRead: INT ¬ 0] = UNCHECKED { data: SetEchoData = NARROW[self.streamData]; IF Basics.NonNegative[block.startIndex]>NAT.LAST OR data.buffer.length>(NAT.LAST-block.startIndex) THEN { offset: INT ~ block.startIndex/Basics.charsPerWord; block.base ¬ block.base+offset; block.startIndex ¬ block.startIndex-offset*Basics.charsPerWord; }; [] ¬ Basics.NonNegative[block.count]; WHILE data.buffer.length > 0 DO IF block.count = 0 THEN RETURN [nBytesRead]; data.buffer.length ¬ data.buffer.length - 1; LOOPHOLE[block.base, LONG POINTER TO Basics.RawChars][block.startIndex] ¬ data.buffer[data.buffer.length]; block.startIndex ¬ block.startIndex + 1; block.count ¬ block.count - 1; nBytesRead ¬ nBytesRead + 1; ENDLOOP; block.count ¬ self.backingStream.UnsafeGetBlock[block]; IF data.echoStream # NIL THEN data.echoStream.UnsafePutBlock[block]; RETURN [nBytesRead + block.count]; }; DefaultSetEchoEndOf: PROC [self: STREAM] RETURNS [BOOL] = { data: SetEchoData = NARROW[self.streamData]; RETURN[data.buffer.length = 0 AND self.backingStream.EndOf[]]; }; DefaultSetEchoCharsAvail: PROC [self: STREAM, wait: BOOL] RETURNS [INT] = { data: SetEchoData = NARROW[self.streamData]; IF data.buffer.length > 0 THEN RETURN [data.buffer.length]; RETURN[self.backingStream.CharsAvail[wait]]; }; DefaultSetEchoReset: PROC [self: STREAM] = { data: SetEchoData = NARROW[self.streamData]; data.buffer.length ¬ 0; self.backingStream.Reset[]; }; AddStreamProcs: PUBLIC PROC [ to: REF IO.StreamProcs, setEcho: PROC [self: STREAM, echoTo: STREAM], getEcho: PROC [self: STREAM] RETURNS [oldEcho: STREAM] ] RETURNS [REF IO.StreamProcs] = { IF setEcho # NIL THEN IOUtils.StoreProc[to, $SetEcho, NEW[TypeOfSetEcho ¬ setEcho]]; IF getEcho # NIL THEN IOUtils.StoreProc[to, $GetEcho, NEW[TypeOfGetEcho ¬ getEcho]]; RETURN [to]; }; EditedStreamProcs ¬ AddStreamProcs[to: IO.CreateStreamProcs[ variety: $input, class: $Edited, getChar: EditedStreamGetChar, endOf: EditedStreamEndOf, charsAvail: EditedStreamCharsAvail, backup: EditedStreamBackup, reset: EditedStreamReset], setEcho: EditedStreamSetEcho, getEcho: EditedStreamGetEcho]; IOUtils.StoreProc[EditedStreamProcs, $GetDeliverWhen, NEW[TypeOfGetDeliverWhen ¬ EditedStreamGetDeliverWhen]]; IOUtils.StoreProc[EditedStreamProcs, $SetDeliverWhen, NEW[TypeOfSetDeliverWhen ¬ EditedStreamSetDeliverWhen]]; IOUtils.StoreProc[EditedStreamProcs, $AppendBufferChars, NEW[TypeOfAppendBufferChars ¬ EditedStreamAppendBufferChars]]; IOUtils.StoreProc[EditedStreamProcs, $UnAppendBufferChars, NEW[TypeOfUnAppendBufferChars ¬ EditedStreamUnAppendBufferChars]]; IOUtils.StoreProc[EditedStreamProcs, $SetMode, NEW[TypeOfSetMode ¬ EditedStreamSetMode]]; setEchoProcs ¬ AddStreamProcs[to: IO.CreateStreamProcs[ variety: $inputOutput, class: $SetEcho, getChar: DefaultSetEchoGetChar, getBlock: DefaultSetEchoGetBlock, unsafeGetBlock: DefaultSetEchoUnsafeGetBlock, endOf: DefaultSetEchoEndOf, charsAvail: DefaultSetEchoCharsAvail, backup: DefaultSetEchoBackup, reset: DefaultSetEchoReset], setEcho: DefaultSetEchoSetEcho, getEcho: DefaultSetEchoGetEcho]; END. h IOEditedStreamImpl.mesa Copyright Σ 1985, 1986, 1991 by Xerox Corporation. All rights reserved. MBrown on December 9, 1983 11:24 am Russ Atkinson (RRA) February 2, 1985 1:54:31 pm PST Doug Wyatt, December 11, 1986 10:46:07 pm PST JKF August 29, 1988 8:55:36 am PDT Eduardo Pelegri-Llopart December 5, 1988 9:28:49 am PST Michael Plass, August 6, 1991 12:13 pm PDT Things to consider: 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? Break out separate proc to read up to activation, return buffer length. Edited Input Stream 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 looks wrong ... why can't you backup past ready chars? Default SetEcho implementation Default implementation: ambush the stream (it will be un-ambushed when self.SetEcho[NIL] is performed). To prevent backed-up chars from being echoed twice (see SetEchoGetChar below). Implementing a stream class Module Initialization Κ•NewlineDelimiter –(cedarcode) style™codešœ™Kšœ Οeœ=™HKšœ Οc™#J™3K™-K™"K™7K™*—K˜™KšœG™GKšœH™HKšœG™G—K˜šΟk ˜ Kšœ˜Kšœ˜Kšœ˜Kšœ ˜ KšŸœ˜Kšœ˜Kšœ˜Kšœ ˜ Kšœ Ÿœ˜!K˜—šΠblœŸœŸœ˜#KšŸœ Ÿœ&˜8KšŸœ ˜KšŸœŸœž#˜-KšŸ˜K˜KšŸœŸœŸœŸœ˜KšŸœŸœŸœ˜Kšœ ŸœŸœ ˜#Kš Οn œŸœŸœŸœ Ÿœ˜:Kš ‘ œŸœŸœŸœŸœ Ÿœ˜DKšœŸœ ˜5Kš œŸœŸœŸœŸœ"ŸœŸœ˜cKš œŸœŸœŸœ"Ÿ œ˜`Kš œŸœŸœ Ÿœ Ÿœ˜CKš œŸœŸœ Ÿœ Ÿœ˜Eš œŸœŸœ Ÿœ ŸœŸœ˜MKšœŸœ˜K˜—š‘œŸœŸœ ŸœŸœŸœŸœ˜NKšœŸ œ˜šŸœ6ŸœŸœŸ˜IKšŸœŸœŸœ˜5KšŸœ˜—K˜K˜—š‘œŸœŸœŸœŸœ"ŸœŸœ˜`KšœŸœŸœ+˜5š ŸœŸœŸœŸœŸœ ˜RKšŸœ˜—KšŸœŸœŸœ+˜8K˜K˜—š ‘œŸœŸœŸœ"ŸœŸœ˜WKšœŸœŸœ+˜5š ŸœŸœŸœŸœŸœ/˜OKšŸœ˜—KšŸœŸœŸœ+˜8K˜K˜—š ‘œŸœŸœ Ÿœ Ÿœ˜@KšœŸœŸœ0˜:š ŸœŸœŸœŸœŸœ,˜LKšŸœ˜—KšŸœŸœŸœ-˜:K˜K˜—š ‘œŸœŸœ Ÿœ Ÿœ˜BKšœŸœŸœ2˜<š ŸœŸœŸœŸœŸœ/˜OKšŸœ˜—KšŸœŸœŸœ-˜:K˜K˜—š ‘œŸœŸœ Ÿœ ŸœŸœ˜GKšœŸœ˜KšœŸœŸœ&˜0š ŸœŸœŸœŸœŸœ@˜`KšŸœ˜—KšŸœŸœŸœ-˜:K˜K˜——™KšœŸœŸœ˜0šœŸœŸœ˜"KšœŸœŸœ˜Kšœ ŸœžG˜ZKšœŸœŸœ˜Kšœ ŸœŸ˜K˜Kšœ ŸœŸœ˜KšœŸœŸœ˜KšœŸœŸ˜K˜—KšœŸœ ˜#K˜š‘œŸœ˜!KšŸœ ŸœŸœŸœŸœ ŸœŸœ˜EKšœ˜K˜—š‘œŸœ˜!KšŸœŸœŸœŸœŸœ ŸœŸœ˜EKšœ˜K˜—˜K˜—š ‘œŸœŸœŸœ Ÿœ)Ÿœ˜`KšŸœŸœ˜šœŸœ˜K˜šœ Ÿœ˜(KšœŸœŸœ˜KšœŸœŸœ˜Kšœ-˜-—K˜K˜—Kšœ Ÿœ˜Kšœž˜KšŸœ˜ Kšœ˜K˜—š‘œŸœ Ÿœ Ÿœ˜EKšœŸœ˜3š ‘œŸœŸœŸœŸœ˜0KšœŸœŸœ˜2—Kšœ ˜ K˜K˜—š‘œŸœ ŸœŸ œ˜JK˜:šŸœŸœŸœ˜Kš ŸœŸœŸœŸœŸœ˜GKšŸœ˜#Kšœ˜—Kšœ˜K˜—š ‘œŸœŸœ Ÿœ Ÿœ˜NKšœŸœ˜3š ŸœŸœŸœŸœŸ˜7Kšœ˜KšŸœ˜—K˜K˜—š‘œŸœŸ œ˜@šŸœŸœŸœ˜KšœŸœ'˜1Kš ŸœŸœŸœŸœŸœ˜IKšŸœ ˜$K˜—K˜,Kšœ˜K˜—š ‘œŸœ Ÿœ ŸœŸœ˜LKšœŸœ˜KšœŸœ˜3K˜Kšœ"˜"Kšœ#˜#Kšœ#˜#Kšœ!˜!K˜K˜—š ‘œŸœŸœŸœ"Ÿœ˜eKšœŸœ˜1KšŸœ"˜(K˜K˜—š‘œŸœŸœ"Ÿœ˜\KšœŸœ˜1Kšœ1˜1K˜K˜—š ‘œŸœŸœŸœŸœ˜AKšœŸœ˜1š ‘ œŸœŸœŸœŸœ˜3šŸœŸœŸ˜KšœŸœŸœ$Ÿœ˜LKšŸœŸœ˜—Kšœ˜—š‘œŸœ˜Kšœ)™)šŸœŸœ˜ K˜Kšœ˜—Kšœ˜—š‘œŸœ˜KšœN™NKšœ ŸœŸœ˜šŸœŸ˜šŸœ%Ÿ˜/KšŸœ Ÿœ ŸœŸœ˜:KšŸœŸœ ŸœŸœ˜"—K˜KšŸœ˜—Kšœ˜—š‘œŸœ˜Kšœ/Ÿœ™9šŸœŸ˜KšœŸœ'˜.KšŸœŸœŸœŸœŸœŸœŸœŸœ˜(K˜KšŸœ˜—Kšœ˜—šŸ˜šŸœ#Ÿœ˜+Kšœ!˜!K˜"KšŸœ˜Kšœ˜—˜KšœŸœ˜šœ%Ÿœ˜6Kš ŸœŸœŸœŸœŸœ˜@—šœ.˜.Kšœ8˜8—šŸœŸœ˜KšœŸœ˜š ŸœŸœ Ÿœ ŸœŸœŸ˜?Kšœ.˜.—K˜—šŸœ Ÿœ˜šŸœŸ˜šœŸœŸœŸœ˜7KšŸœŸœ˜K˜—KšœŸœ˜'K˜Kšœ˜šœŸœŸœŸœ˜-šŸœŸœŸœŸ˜)K˜&KšŸ˜—K˜—KšŸœ!˜(—K˜—KšŸœ ŸœŸœ˜%šŸœ˜Kšœ˜K˜5Kšœ˜K˜KšœŸœ˜K˜—K˜—KšŸœ˜—Kšœ˜K˜—š ‘œŸœŸœŸœŸœ˜9KšœŸœ˜1KšŸœ#Ÿœ˜JKšœ˜K˜—š ‘œŸœŸœŸœŸœŸœ˜IKšœŸœ˜1KšŸœ#ŸœŸœ#˜SKšŸœ&˜,Kšœ˜K˜—š‘œŸœŸœŸœ˜7Kšœ6™6KšœŸœ˜1šŸœŸœ&Ÿ˜AKšœ˜—Kšœ"˜"Kšœ˜K˜—š‘œŸœŸœ Ÿœ˜KšœŸœ˜,Kšœ˜KšŸœ ŸœŸœŸœ˜MKšœ˜K˜—š ‘œŸœŸœŸœ Ÿœ˜HKšœŸœ˜,KšŸœ˜Kšœ˜K˜—š‘œŸœŸœŸœ˜9KšœN™NKšœŸœ˜,šœ:˜:KšœŸœŸœ˜C—Kšœ˜K˜—š ‘œŸœŸœŸœŸœ˜CKšœŸœ˜,šŸœŸœ˜ Kšœ,˜,Kšœ'˜'KšŸœ˜ Kšœ˜—K˜$KšŸœŸœŸœ˜Kšœ˜K˜—š ‘œŸœŸœŸœŸœŸœ˜KKšœŸœ˜,KšŸœŸœŸœ˜;KšŸœ&˜,Kšœ˜K˜—š‘œŸœŸœ˜-KšœŸœ˜,Kšœ˜K˜Kšœ˜K˜——šœ™š‘œŸœŸœ˜KšœŸœŸœ ˜Kšœ ŸœŸœ Ÿœ˜-Kš œ ŸœŸœŸœ Ÿœ˜6Kšœ˜KšŸœŸœŸœ˜ šŸœ ŸœŸ˜Kšœ Ÿœ˜>—šŸœ ŸœŸ˜Kšœ Ÿœ˜>—KšŸœ˜ Kšœ˜K˜——™šœŸœ)˜