DIRECTORY AbbrevExpand USING [Event], AbbrevExpandExtras USING [], Ascii USING [Lower], Atom USING [GetPName, MakeAtom], Basics USING [BITSHIFT, BITXOR], BasicTime USING [GMT, nullGMT], Convert USING [RopeFromInt], EditSpan USING [Copy], FS USING [Error, FileInfo], IO USING [Close, GetIndex, GetRefAny, RIS, STREAM], MessageWindow USING [Append, Blink], PrincOpsUtils USING [], Process USING [GetCurrent], PutGet USING [FromFile], RefTab USING [Create, Fetch, Pairs, Ref, Store], Rope USING [Cat, Concat, Equal, MaxLen, ROPE, Size, Translate, TranslatorType], RopeEdit USING [AlphaNumericChar, BlankChar, MaxLen, PunctuationChar, Substr, UpperCase], RopeReader USING [Backwards, CompareSubstrs, FreeRopeReader, Get, GetIndex, GetRopeReader, Peek, PeekBackwards, ReadOffEnd, Ref, SetPosition], RuntimeError USING [UNCAUGHT], TextEdit USING [AddLooks, AllCaps, ChangeFormat, DeleteText, FetchLooks, MoveText, ReplaceText, Size], TextLooks USING [Looks, noLooks], TextNode USING [FirstChild, LastWithin, MakeNodeLoc, MakeNodeSpan, Next, NodeFormat, Offset, Ref, RefTextNode, Root, Span], UserProfile USING [ListOfTokens]; AbbrevExpandImpl: CEDAR MONITOR IMPORTS Ascii, Atom, Convert, EditSpan, FS, Basics, IO, MessageWindow, PutGet, RefTab, Rope, RopeEdit, RopeReader, Process, RuntimeError, TextEdit, TextNode, UserProfile EXPORTS AbbrevExpand, AbbrevExpandExtras = BEGIN RefTextNode: TYPE = TextNode.RefTextNode; Ref: TYPE = TextNode.Ref; Offset: TYPE = TextNode.Offset; MaxLen: INT = LAST[INT]; ROPE: TYPE = Rope.ROPE; Event: TYPE = AbbrevExpand.Event; dictTable: RefTab.Ref _ RefTab.Create[mod: 5]; Entry: TYPE = REF EntryRec; EntryRec: TYPE = RECORD [ next: Entry, hash: CARDINAL, keyRope: ROPE, node: RefTextNode, commands: LIST OF REF ANY ]; LoadInternal: PROC [dict: ATOM, fileID: FileID, start: Offset _ 0, len: Offset _ MaxLen] RETURNS [count: NAT _ 0] ~ { rdr: RopeReader.Ref ~ RopeReader.GetRopeReader[]; root: Ref _ NIL; Locked: PROC ~ { [] _ RefTab.Store[dictTable, dict, NoDictAtom]; -- clears the dictionary FOR node: Ref _ TextNode.FirstChild[root], TextNode.Next[node] UNTIL node=NIL DO IF node=NIL OR node.rope=NIL OR node.comment THEN LOOP; AddToDict[root, node, dict, rdr]; count _ count+1; ENDLOOP; [] _ RefTab.Store[fileForAbbr, dict, fileID]; }; Empty: PROC ~ { [] _ RefTab.Store[dictTable, dict, NoDictAtom]; -- clears the dictionary }; IF fileID = NIL THEN {DoLocked[Empty]; RETURN}; MessageWindow.Append[Rope.Cat["New ", fileID.name, " . . . "], TRUE]; root _ PutGet.FromFile[fileID.name, start, len ! FS.Error => { MessageWindow.Append[error.explanation, FALSE]; MessageWindow.Blink[]; CONTINUE; }]; IF root = NIL THEN RETURN; DoLocked[Locked]; MessageWindow.Append[Convert.RopeFromInt[count], FALSE]; MessageWindow.Append[" entries ", FALSE]; RopeReader.FreeRopeReader[rdr]; }; Load: PUBLIC PROC [fileName, dictName: ROPE, start: Offset _ 0, len: Offset _ MaxLen] RETURNS [count: NAT _ 0] = { dict: ATOM = GetDictAtom[dictName]; count _ LoadInternal[dict, GetFileID[dict], start, len]; }; AddToDict: ENTRY PROC [root: Ref, node: RefTextNode, dictAtom: ATOM, rdr: RopeReader.Ref _ NIL] = { ENABLE UNWIND => NULL; dict: Entry _ GetDict[dictAtom]; keyRope: ROPE _ node.rope; keyStart: Offset = 0; hash: CARDINAL; entry: Entry; keyLen: NAT; freeRdr: BOOL _ FALSE; resultStart: Offset; commands: LIST OF REF ANY; IF rdr=NIL THEN { rdr _ RopeReader.GetRopeReader[]; freeRdr _ TRUE }; keyLen _ FindKeyLen[keyRope,rdr]; IF keyLen=0 THEN { IF freeRdr THEN RopeReader.FreeRopeReader[rdr]; RETURN }; -- not an entry hash _ KeyHash[keyRope,keyStart,keyLen,rdr]; entry _ LookupInternal[dict,hash,keyRope,keyStart,keyLen,rdr]; IF entry = NIL THEN { -- add new entry entry _ NEW[EntryRec]; IF dict # NIL THEN { entry.next _ dict.next; dict.next _ entry } ELSE [] _ RefTab.Store[dictTable,dictAtom,entry] }; entry.hash _ hash; entry.keyRope _ RopeEdit.Substr[keyRope,keyStart,keyLen]; entry.node _ node; [resultStart,commands] _ ParseCommands[keyRope,keyStart+keyLen,rdr]; entry.commands _ commands; TextEdit.DeleteText[root,node,0,resultStart]; IF freeRdr THEN RopeReader.FreeRopeReader[rdr]; }; ParseCommands: PROC [keyRope: ROPE, start: Offset, rdr: RopeReader.Ref] RETURNS [end: Offset, commands: LIST OF REF ANY] = { OPEN RopeReader; ENABLE ReadOffEnd => GOTO Bad; blank: BOOL; SetPosition[rdr, keyRope, start]; IF RopeEdit.BlankChar[Peek[rdr]] THEN { blank _ TRUE; [] _ Get[rdr] } ELSE blank _ FALSE; IF Peek[rdr] = '( THEN { -- parse command list h: IO.STREAM _ IO.RIS[RopeEdit.Substr[keyRope,start _ GetIndex[rdr]]]; commands _ NARROW[IO.GetRefAny[h]]; start _ start+IO.GetIndex[h]; IO.Close[h]; SetPosition[rdr, keyRope, start]; IF RopeEdit.BlankChar[Peek[rdr]] THEN { blank _ TRUE; [] _ Get[rdr] } ELSE blank _ FALSE; }; IF Peek[rdr] = '= THEN { -- check for blank [] _ Get[rdr]; IF blank AND Peek[rdr] = ' THEN [] _ Get[rdr]; -- skip blank after = if there was one before end _ GetIndex[rdr]; } ELSE end _ Rope.Size[keyRope]; EXITS Bad => RETURN [Rope.Size[keyRope],commands]; }; Clear: PUBLIC ENTRY PROC [dictName: ROPE] = { ENABLE UNWIND => NULL; [] _ RefTab.Store[dictTable, GetDictAtom[dictName], NIL]; }; IllFormedDef: PUBLIC ERROR = CODE; GetDictAtom: PROC [dictName: ROPE] RETURNS [dict: ATOM] = { ForceLower: Rope.TranslatorType = { RETURN [Ascii.Lower[old]] }; RETURN [Atom.MakeAtom[Rope.Translate[base~dictName, translator~ForceLower]]]; }; Expand: PUBLIC PROC [keyNode: RefTextNode, keyEnd: Offset, dict: ROPE, event: Event _ NIL] RETURNS [foundIt: BOOL, keyDeterminesDict: BOOL, keyStart, keyLen, resultLen: Offset, commands: LIST OF REF ANY] = { FindEntry: PROC [dictAtom: ATOM] RETURNS [BOOL] = { dict: Entry _ GetDict[dictAtom]; IF dict#NIL THEN entry _ Lookup[dict,hash,keyRope,keyStart,kLen,rdr]; RETURN [entry # NIL]; }; rdr: RopeReader.Ref; dictAtom: ATOM = GetDictAtom[dict]; keyRope: ROPE _ keyNode.rope; kLen: NAT; kStart: Offset; hash: CARDINAL; entry: Entry; foundIt _ keyDeterminesDict _ FALSE; resultLen _ 0; rdr _ RopeReader.GetRopeReader[]; keyEnd _ MAX[0,MIN[keyEnd,Rope.Size[keyRope]]]; kStart _ keyStart _ FindKeyStart[keyRope,keyEnd,rdr]; kLen _ keyLen _ keyEnd-keyStart; hash _ KeyHash[keyRope,keyStart,keyEnd,rdr]; RopeReader.SetPosition[rdr,keyRope,keyStart]; IF keyStart > 0 AND RopeReader.Backwards[rdr]='. THEN { -- try to get the dictName from the key nameStart: Offset _ FindKeyStart[keyRope,keyStart-1,rdr]; IF nameStart < keyStart-1 THEN { dictName: ATOM _ Atom.MakeAtom[RopeEdit.Substr[keyRope,nameStart,keyStart-1-nameStart]]; [] _ FindEntry[dictName]; keyDeterminesDict _ TRUE; keyLen _ keyEnd - (keyStart _ nameStart); }; }; IF entry = NIL THEN [] _ FindEntry[dictAtom]; IF entry = NIL THEN { RopeReader.FreeRopeReader[rdr]; RETURN }; { -- do it CheckCaps: PROC = { allcaps _ FALSE; RopeReader.SetPosition[rdr,keyRope,kStart]; initialcap _ RopeReader.Peek[rdr] IN ['A..'Z]; FOR i:Offset IN [0..kLen) DO IF RopeReader.Get[rdr] NOT IN ['A..'Z] THEN RETURN; ENDLOOP; allcaps _ TRUE; }; node: RefTextNode = entry.node; root: Ref = TextNode.Root[node]; keyRoot: Ref = TextNode.Root[keyNode]; nodeRope: ROPE _ node.rope; looks: TextLooks.Looks _ TextEdit.FetchLooks[keyNode,kStart]; allcaps, initialcap: BOOL _ FALSE; format: ATOM; child: RefTextNode _ TextNode.FirstChild[node]; textLen: Offset _ Rope.Size[nodeRope]; CheckCaps; RopeReader.FreeRopeReader[rdr]; [,resultLen] _ TextEdit.ReplaceText[destRoot: keyRoot, sourceRoot: root, dest:keyNode, destStart:keyStart, destLen:keyLen, source:node, sourceStart:0, sourceLen:textLen, event:event]; IF resultLen # textLen THEN ERROR; IF looks # TextLooks.noLooks THEN TextEdit.AddLooks[keyRoot,keyNode,looks,keyStart,textLen,event]; IF allcaps THEN TextEdit.AllCaps[keyRoot,keyNode,keyStart,textLen,event] ELSE IF initialcap AND textLen > 0 THEN -- make first letter uppercase TextEdit.AllCaps[keyRoot,keyNode,keyStart,1,event]; SELECT format _ TextNode.NodeFormat[node] FROM NIL, TextNode.NodeFormat[keyNode] => NULL; ENDCASE => TextEdit.ChangeFormat[keyNode,format,event,keyRoot]; IF child # NIL THEN { -- insert as children of keyNode new: TextNode.Span _ EditSpan.Copy[keyRoot, root, TextNode.MakeNodeLoc[keyNode], TextNode.MakeNodeSpan[child,TextNode.LastWithin[node]], after, IF textLen = 0 THEN 0 ELSE 1, event]; last: RefTextNode _ new.end.node; IF last # NIL AND keyStart+textLen < TextEdit.Size[keyNode] THEN -- move text after key to end of last new node [] _ TextEdit.MoveText[ destRoot: keyRoot, sourceRoot: keyRoot, dest: last, destLoc: MaxLen, source: keyNode, start: keyStart+textLen, len: MaxLen, event: event]; }; foundIt _ TRUE; commands _ entry.commands; }; }; KeyHash: PROC [keyRope: ROPE, keyStart, keyEnd: Offset, rdr: RopeReader.Ref] RETURNS [h: CARDINAL] = { len: NAT _ keyEnd-keyStart; RopeReader.SetPosition[rdr,keyRope,keyStart]; h _ 0; FOR i: NAT IN [1..MIN[3,len]) DO -- use the first 3 characters h _ Basics.BITXOR[Basics.BITSHIFT[h,2], RopeEdit.UpperCase[RopeReader.Get[rdr]]-0C]; ENDLOOP; IF len >= 2 THEN { -- use the last 2 characters RopeReader.SetPosition[rdr,keyRope,keyEnd]; FOR i: NAT IN [len-2..len) DO h _ Basics.BITXOR[Basics.BITSHIFT[h,2], RopeEdit.UpperCase[RopeReader.Backwards[rdr]]-0C]; ENDLOOP; }; }; Lookup: ENTRY PROC [dict: Entry, hash: CARDINAL, keyRope: ROPE, keyStart: Offset, keyLen: NAT, rdr1, rdr2: RopeReader.Ref _ NIL] RETURNS [entry: Entry] = { ENABLE UNWIND => NULL; RETURN [LookupInternal[dict,hash,keyRope,keyStart,keyLen,rdr1,rdr2]]; }; LookupInternal: PROC [dict: Entry, hash: CARDINAL, keyRope: ROPE, keyStart: Offset, keyLen: NAT, rdr1, rdr2: RopeReader.Ref _ NIL] RETURNS [entry: Entry] = { FreeReaders: PROC = { IF free1 THEN RopeReader.FreeRopeReader[rdr1]; IF free2 THEN RopeReader.FreeRopeReader[rdr2]; }; free1, free2: BOOL _ FALSE; pred: Entry _ NIL; IF keyLen=0 THEN RETURN [NIL]; IF rdr1=NIL THEN { free1 _ TRUE; rdr1 _ RopeReader.GetRopeReader[] }; IF rdr2=NIL THEN { free2 _ TRUE; rdr2 _ RopeReader.GetRopeReader[] }; entry _ NIL; -- will stay nil if don't find key in dict FOR e: Entry _ dict, e.next UNTIL e=NIL DO IF e.hash = hash AND Rope.Size[e.keyRope] = keyLen THEN { -- check the ropes IF RopeReader.CompareSubstrs[keyRope,e.keyRope, keyStart,keyLen,0,keyLen,rdr1,rdr2,FALSE] = equal THEN { IF pred # NIL THEN { -- move entry to front pred.next _ e.next; e.next _ dict.next; dict.next _ e; }; entry _ e; EXIT; }; }; pred _ e; ENDLOOP; FreeReaders[]; }; FindKeyLen: PROC [keyRope: ROPE, rdr: RopeReader.Ref] RETURNS [len: NAT] = { size: Offset _ Rope.Size[keyRope]; IF size=0 THEN RETURN [0]; RopeReader.SetPosition[rdr,keyRope,0]; IF RopeEdit.PunctuationChar[RopeReader.Peek[rdr]] THEN RETURN [1]; len _ 0; WHILE len0 AND RopeEdit.AlphaNumericChar[RopeReader.Backwards[rdr]] DO start _ start-1; ENDLOOP; }; NoDictAtom: ATOM = $NoDictionaryAvailable; GetDict: PROC [dictName: ATOM] RETURNS [entry: Entry] = { prop: REF ANY; prop _ RefTab.Fetch[dictTable, dictName].val; WITH prop SELECT FROM x: Entry => RETURN [x]; x: ATOM => IF x = NoDictAtom THEN RETURN [NIL] ELSE ERROR; ENDCASE => { -- try to load it [] _ LoadInternal[dictName, GetFileID[dictName] ! RuntimeError.UNCAUGHT => CONTINUE]; RETURN [GetDict[dictName]]; }; }; defaultSearch: LIST OF ROPE _ LIST["[]<>Commands>", "[]<>"]; FileID: TYPE ~ REF FileIDRep; FileIDRep: TYPE ~ RECORD [name: ROPE _ NIL, time: BasicTime.GMT _ BasicTime.nullGMT]; Same: PROC [a, b: FileID] RETURNS [BOOL] ~ { RETURN [a.time = b.time AND Rope.Equal[a.name, b.name, FALSE]] }; GetFileID: PROC [shortName: ATOM] RETURNS [FileID] ~ { dirs: LIST OF ROPE _ UserProfile.ListOfTokens["Tioga.StyleSearchRules", defaultSearch]; name: ROPE ~ Rope.Concat[Atom.GetPName[shortName], ".abbreviations"]; fileName: ROPE _ NIL; created: BasicTime.GMT _ BasicTime.nullGMT; WHILE fileName = NIL AND dirs # NIL DO [fullFName: fileName, created: created] _ FS.FileInfo[name: name, wDir: dirs.first ! FS.Error => CONTINUE]; dirs _ dirs.rest; ENDLOOP; IF fileName = NIL THEN RETURN [NIL]; RETURN [NEW[FileIDRep _ [fileName, created]]]; }; abbrLockProcess: UNSAFE PROCESS _ NIL; abbrLockCount: CARDINAL _ 0; abbrLockFree: CONDITION; DoLocked: PUBLIC PROC [action: PROC] ~ { me: UNSAFE PROCESS ~ Process.GetCurrent[]; Lock: ENTRY PROC ~ { UNTIL abbrLockProcess = me OR abbrLockCount = 0 DO WAIT abbrLockFree ENDLOOP; abbrLockProcess _ me; abbrLockCount _ abbrLockCount + 1; }; Unlock: ENTRY PROC ~ { abbrLockCount _ abbrLockCount - 1; IF abbrLockCount = 0 THEN {abbrLockProcess _ NIL; NOTIFY abbrLockFree}; }; Lock[]; action[ ! UNWIND => Unlock[]]; Unlock[]; }; fileForAbbr: RefTab.Ref ~ RefTab.Create[5]; ValidateAll: PUBLIC PROC RETURNS [changed: BOOL _ FALSE] ~ { Locked: PROC ~ { Action: PROC [key: REF, val: REF] RETURNS [quit: BOOLEAN] ~ { IF Validate[NARROW[key]] THEN changed _ TRUE; RETURN [FALSE] }; [] _ RefTab.Pairs[fileForAbbr, Action]; }; DoLocked[Locked]; }; Validate: PUBLIC PROC [name: ATOM] RETURNS [changed: BOOL _ FALSE] ~ { Locked: PROC ~ { fileID: FileID ~ GetFileID[name]; oldFileID: FileID ~ NARROW[RefTab.Fetch[fileForAbbr, name].val]; IF oldFileID = NIL OR fileID = NIL OR Same[fileID, oldFileID] THEN changed _ FALSE ELSE { [] _ LoadInternal[name, fileID]; changed _ TRUE }; }; DoLocked[Locked]; }; END. TAbbrevExpandImpl.mesa Copyright c 1985 by Xerox Corporation. All rights reserved. written by Bill Paxton, May 1981 Paxton, November 8, 1982 1:31 pm Russ Atkinson, September 26, 1983 1:21 pm Doug Wyatt, March 3, 1985 3:54:40 pm PST Michael Plass, May 1, 1986 11:56:28 am PDT Rick Beach, May 30, 1985 3:02:37 pm PDT Implements abbreviation expansion in Tioga. Declarations Operations Note that fileName is ignored. -- clear the specified abbreviation dictionary -- force dictName lowercase before create the atom -- see if have '. before -- hash must be independent of case of chars Called from elsewhere in Tioga when something changes that may have changed any abbr. Does not attempt to refresh screen. Called from elsewhere in Tioga when something changes that may have changed a abbr. Does not attempt to refresh screen. ΚΛ˜codešœ™Kšœ Οmœ1™Kšœ(žœ˜/Kšœ˜Kšžœ˜ Kšœ˜—Kšžœžœžœžœ˜Kšœ˜Kšœ1žœ˜8Kšœ"žœ˜)K˜Kšœ˜K˜—š  œžœžœžœ+žœ žœ ˜rKšœ™Kšœžœ˜#Kšœ8˜8K˜K˜—š   œžœžœ*žœžœ˜cKšžœžœžœ˜K˜K˜ Kšœ žœ ˜K˜Kšœžœ˜K˜ Kšœžœ˜ Kšœ žœžœ˜K˜Kš œ žœžœžœžœ˜K˜Kšžœžœžœ/žœ˜EK˜!Kš žœ žœžœ žœ!žœ‘˜\K˜,K˜>šžœ žœžœ‘˜&Kšœžœ ˜Kšžœžœžœ.˜@Kšžœ/˜3—K˜K˜9K˜K˜DK˜K˜-Kšžœ žœ ˜/K˜K˜—š  œžœ žœ%žœžœžœžœžœ˜|Kšžœ ˜Kšžœžœ˜Kšœžœ˜ K˜!Kš žœžœ žœžœ žœ˜Yšžœžœ‘˜.Kš œžœžœžœžœ1˜FKšœ žœžœ˜#Kšœžœ ˜Kšžœ ˜ K˜!Kš žœžœ žœžœ žœ˜YK˜—šžœžœ‘˜+K˜Kšžœžœžœ‘-˜]K˜K˜—Kšžœ˜Kšžœžœ˜2K˜K˜—š  œžœžœžœ žœ˜-Kšœ.™.Kšžœžœžœ˜Kšœ4žœ˜9Kšœ˜K˜—Kšœžœžœžœ˜"K˜š   œžœ žœžœžœ˜;Kšœ2™2Kš  œžœ˜@KšžœG˜MK˜K˜—š œžœžœ-žœžœžœ žœžœ1žœžœžœžœ˜ΟK˜š   œžœ žœžœžœ˜3K˜ Kšžœžœžœ5˜EKšžœ žœ˜Kšœ˜K˜—K˜Kšœ žœ˜#Kšœ žœ˜Kšœžœ˜ K˜Kšœžœ˜K˜ Kšœžœ˜$K˜K˜!Kšœ žœžœ˜/K˜5K˜ K˜,Kšœ-™-K˜-šžœžœžœ˜7Kš‘'˜'K˜9šžœžœ˜ Kšœ žœJ˜XK˜Kšœžœ˜K˜)K˜—K˜—Kšžœ žœžœ˜-K˜Kšžœ žœžœ#žœ˜?Kšœ‘˜ ˜š  œžœ˜Kšœ žœ˜K˜+Kšœ"žœ ˜.šžœ žœ ž˜Kš žœžœžœ žœžœ˜3Kšžœ˜—Kšœ žœ˜Kšœ˜K˜—K˜K˜ K˜&Kšœ žœ ˜K˜=Kšœžœžœ˜"Kšœžœ˜ K˜/K˜&K˜ K˜K˜·Kšžœžœžœ˜"šžœž˜!K˜@—Kšžœ žœ9˜Hš žœžœ žœ žœ‘˜FK˜3—šžœ$ž˜.Kšžœ"žœ˜*Kšžœ8˜?—šžœ žœžœ‘ ˜6Kšœžœ žœžœ ˜΅K˜!šžœžœžœ+ž˜@Kš‘.˜.K˜’K˜——Kšœ žœ˜K˜K˜—˜K˜——š  œžœ žœ0žœžœ˜fKšœ,™,Kšœžœ˜K˜-K˜š žœžœžœžœ žœ‘˜>šœ žœžœ˜'K˜,—Kšžœ˜—šžœ žœ‘˜/K˜+šžœžœžœž˜šœ žœžœ˜'K˜2—Kšžœ˜—Kšœ˜—šœ˜K˜——š œžœžœžœ žœžœžœžœžœ˜›Kšžœžœžœ˜Kšžœ?˜EKšœ˜K˜—š œžœžœ žœžœžœžœ˜š  œžœ˜Kšžœžœ!˜.Kšžœžœ!˜.Kšœ˜—Kšœžœžœ˜Kšœžœ˜Kšžœ žœžœžœ˜Kšžœžœžœ žœ&˜EKšžœžœžœ žœ&˜EKšœžœ‘*˜7šžœžœžœž˜*šžœžœžœ‘˜LšžœQžœ žœ˜hšžœžœžœ‘˜+K˜K˜K˜K˜—Kšœ ˜ Kšžœ˜Kšœ˜—Kšœ˜—K˜ Kšžœ˜—K˜K˜K˜—š   œžœ žœžœžœ˜LK˜"Kšžœžœžœ˜K˜&Kšžœ0žœžœ˜BK˜šžœ žœ0ž˜DKšœ ˜ Kšžœ˜—šœ˜K˜——š  œžœ žœ&žœ˜cKšžœžœ žœžœ˜8K˜+Kšžœ9žœžœ ˜RK˜šžœ žœ6ž˜IKšœ˜Kšžœ˜—šœ˜K˜——Kšœ žœ˜*K˜š œžœ žœžœ˜9Kšœžœžœ˜K˜-šžœžœž˜Kšœ žœ˜Kšœžœžœžœžœžœžœžœ˜:šžœ‘˜Kšœ?žœžœ˜UKšžœ˜Kšœ˜——Kšœ˜K˜—K˜š œžœžœžœžœ˜Kšœ˜—š  œžœ žœžœ ˜6KšœžœžœžœE˜WKšœžœ;˜EKšœ žœžœ˜Kšœžœ˜+š žœ žœžœžœž˜&Kšœ*žœ)žœ žœ˜kKšœ˜Kšžœ˜—Kš žœ žœžœžœžœ˜$Kšžœžœ#˜.Kšœ˜K˜—Kšœžœžœ˜&Kšœžœ˜Kšœž œ˜š œžœžœ žœ˜(Kšœžœ˜*š œžœžœ˜Kš žœžœžœžœžœ˜MKšœ8˜8Kšœ˜—š œžœžœ˜Kšœ"˜"Kšžœžœžœžœ˜GKšœ˜—Kšœ˜Kšœ žœ˜Kšœ ˜ Kšœ˜K˜—šœ+˜+K˜—š   œžœžœžœ žœžœ˜