DIRECTORY AbbrevExpand USING [Event], 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], SystemNames USING [LocalDir], TextEdit USING [ChangeCaps, ChangeFormat, ChangeLooks, DeleteText, FetchLooks, MoveText, ReplaceText, Size], TextLooks USING [Looks, noLooks], TextNode USING [FirstChild, LastWithin, MakeNodeLoc, MakeNodeSpan, Next, Node, NodeFormat, Root, Span], UserProfile USING [ListOfTokens]; AbbrevExpandImpl: CEDAR MONITOR IMPORTS Ascii, Atom, Basics, Convert, EditSpan, FS, IO, MessageWindow, Process, PutGet, RefTab, Rope, RopeEdit, RopeReader, RuntimeError, SystemNames, TextEdit, TextNode, UserProfile EXPORTS AbbrevExpand = BEGIN Node: TYPE = TextNode.Node; 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: Node, commands: LIST OF REF ANY ]; LoadInternal: PROC [dict: ATOM, fileID: FileID, start: INT _ 0, len: INT _ MaxLen] RETURNS [ok: BOOL _ FALSE, count: NAT _ 0] ~ { rdr: RopeReader.Ref ~ RopeReader.GetRopeReader[]; root: Node _ NIL; Locked: PROC ~ { [] _ RefTab.Store[dictTable, dict, NoDictAtom]; -- clears the dictionary FOR node: Node _ 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]; ok _ TRUE; }; Load: PUBLIC PROC [fileName, dictName: ROPE, start: INT _ 0, len: INT _ MaxLen] RETURNS [count: NAT _ 0] = { dict: ATOM = GetDictAtom[dictName]; [count: count] _ LoadInternal[dict, GetFileID[dict], start, len]; }; AddToDict: ENTRY PROC [root: Node, node: Node, dictAtom: ATOM, rdr: RopeReader.Ref _ NIL] = { ENABLE UNWIND => NULL; dict: Entry _ GetDict[dictAtom]; keyRope: ROPE _ node.rope; keyStart: INT = 0; hash: CARDINAL; entry: Entry; keyLen: NAT; freeRdr: BOOL _ FALSE; resultStart: INT; 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: INT, rdr: RopeReader.Ref] RETURNS [end: INT, 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: Node, keyEnd: INT, dict: ROPE, event: Event _ NIL] RETURNS [foundIt: BOOL, keyDeterminesDict: BOOL, keyStart, keyLen, resultLen: INT, 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: INT; 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: INT _ 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: INT IN [0..kLen) DO IF RopeReader.Get[rdr] NOT IN ['A..'Z] THEN RETURN; ENDLOOP; allcaps _ TRUE; }; node: Node = entry.node; root: Node = TextNode.Root[node]; keyRoot: Node = TextNode.Root[keyNode]; nodeRope: ROPE _ node.rope; looks: TextLooks.Looks _ TextEdit.FetchLooks[keyNode,kStart]; allcaps, initialcap: BOOL _ FALSE; format: ATOM; child: Node _ TextNode.FirstChild[node]; textLen: INT _ 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.ChangeLooks[keyRoot,keyNode,TextLooks.noLooks,looks,keyStart,textLen,event]; IF allcaps THEN TextEdit.ChangeCaps[keyRoot,keyNode,keyStart,textLen,allCaps,event] ELSE IF initialcap AND textLen > 0 THEN -- make first letter uppercase TextEdit.ChangeCaps[keyRoot,keyNode,keyStart,1,allCaps,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: Node _ 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: INT, 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: INT, 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: INT, 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: INT _ 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 ok: BOOL _ FALSE; [ok: ok] _ LoadInternal[dictName, GetFileID[dictName] ! RuntimeError.UNCAUGHT => CONTINUE]; IF ok THEN RETURN [GetDict[dictName]] ELSE RETURN [NIL]; }; }; systemDir: ROPE ~ SystemNames.LocalDir["System"]; commandsDir: ROPE ~ SystemNames.LocalDir["Commands"]; defaultSearch: LIST OF ROPE _ LIST[commandsDir, systemDir]; 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. jAbbrevExpandImpl.mesa Copyright Σ 1985, 1986, 1987, 1988 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 Michael Plass, May 1, 1986 11:56:28 am PDT Rick Beach, May 30, 1985 3:02:37 pm PDT Doug Wyatt, February 17, 1988 6:19:27 pm PST 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 an abbr. Does not attempt to refresh screen. Κn˜codešœ™KšœN™NKšœ ™ Kšœ ™ K™)K™*K™'K™,—K˜Kšœ+™+K˜šΟk ˜ Kšœ œ ˜Kšœœ ˜Kšœœ˜ Kšœœœœ˜ Kšœ œœ ˜Kšœœ˜Kšœ œ˜Kšœœ˜Kšœœœœ˜3Kšœœ˜$Kšœœ˜Kšœœ˜Kšœœ ˜Kšœœ$˜0Kšœœœ#˜OKšœ œK˜YKšœ œ~˜ŽKšœ œœ˜Kšœ œ ˜Kšœ œ^˜lKšœ œ˜!Kšœ œY˜gKšœ œ˜!—K˜KšΠblœœ˜Kšœ)œœ€˜ΆKšœ ˜Kšœ˜headšœ ™ Kšœœ˜Kšœœœœ˜Kšœœœ˜Kšœœ˜!K˜K˜.K˜Kšœœœ ˜šœ œœ˜K˜ Kšœœ˜Kšœ œ˜K˜ Kšœ œœœ˜K˜——šœ ™ šΟn œœœœ œ œœœ œ ˜K˜1Kšœ œ˜šŸœœ˜Kšœ0Οc˜Hšœ=œœ˜QKšœœœ œœœœ˜7K˜!K˜Kšœ˜—Kšœ-˜-Kšœ˜—šŸœœ˜Kšœ0 ˜HKšœ˜—Kšœ œœœ˜/Kšœ?œ˜Ešœ1œ ˜>Kšœ(œ˜/Kšœ˜Kšœ˜ Kšœ˜—Kšœœœœ˜Kšœ˜Kšœ1œ˜8Kšœ"œ˜)K˜Kšœœ˜ Kšœ˜K˜—šŸœœœœ œ œ œ œ ˜lKšœ™Kšœœ˜#KšœA˜AK˜K˜—š Ÿ œœœ$œœ˜]Kšœœœ˜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˜—šŸ œœ œ œœœ œœœœ˜vKšœ ˜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˜—šŸœœœœœœœ œœœ œœœœ˜Β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šœ œ(˜6šœœ˜ 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šœU˜U—Kšœ œD˜Sš œœ œ œ ˜FKšœ>˜>—šœ$˜.Kšœ"œ˜*Kšœ8˜?—šœ œœ  ˜6Kšœœ œœ ˜΅K˜šœœœ+˜@Kš .˜.K˜’K˜——Kšœ œ˜K˜K˜—˜K˜——š Ÿœœ œœœœ˜cKšœ,™,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˜——š Ÿ œœ œ œœ œ˜]Kšœœ œœ˜8K˜+Kšœ9œœ ˜RK˜šœ œ6˜IKšœ˜Kšœ˜—šœ˜K˜——Kšœ œ˜*K˜šŸœœ œœ˜9Kšœœœ˜K˜-šœœ˜Kšœ œ˜Kšœœœœœœœœ˜:šœ ˜Kšœœœ˜KšœEœœ˜[Kš œœœœœœ˜8Kšœ˜——Kšœ˜K˜—Kšœ œ"˜1Kšœ œ$˜5K˜š œœœœœ˜;K˜—Kšœœœ ˜Kš œ œœœœœ˜UšŸœœœœ˜,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˜—š Ÿ œœœœ œœ˜