DIRECTORY Ascii USING [Lower], Atom USING [GetPName, MakeAtom], Basics USING [BITSHIFT, BITXOR], BasicTime USING [GMT, nullGMT], Convert USING [RopeFromInt], FS USING [Error, FileInfo], IO USING [Close, GetIndex, GetRefAny, RIS, STREAM], MessageWindow USING [Append, Blink], PrincOpsUtils USING [], Process USING [GetCurrent], RefTab USING [Create, Fetch, Pairs, Ref, Store], Rope USING [Cat, Concat, Equal, ROPE, Size, Translate, TranslatorType], RopeEdit USING [AlphaNumericChar, BlankChar, PunctuationChar, Substr, UpperCase], RopeReader USING [Backwards, CompareSubstrs, FreeRopeReader, Get, GetIndex, GetRopeReader, Peek, PeekBackwards, ReadOffEnd, Ref, SetPosition], RuntimeError USING [UNCAUGHT], Tioga, UserProfile USING [ListOfTokens]; TiogaAbbrevImpl: CEDAR MONITOR IMPORTS Ascii, Atom, Convert, FS, Basics, IO, MessageWindow, RefTab, Rope, RopeEdit, RopeReader, Process, RuntimeError, Tioga, UserProfile EXPORTS Tioga = BEGIN Node: TYPE = Tioga.Node; Location: TYPE = Tioga.Location; maxLen: INT = LAST[INT]; ROPE: TYPE = Rope.ROPE; Looks: TYPE ~ Tioga.Looks; noLooks: Looks ~ Tioga.noLooks; World: TYPE ~ Tioga.World; 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 ]; defaultSearch: LIST OF ROPE _ LIST["[]<>Commands>", "[]<>"]; fileForAbbr: RefTab.Ref ~ RefTab.Create[5]; 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[]; }; 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 _ Tioga.FirstChild[root], Tioga.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 _ Tioga.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; }; LoadAbbreviations: 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; Tioga.DeleteText[NIL, 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]; }; ClearAbbreviations: PUBLIC ENTRY PROC [dictName: ROPE] = { ENABLE UNWIND => NULL; [] _ RefTab.Store[dictTable, GetDictAtom[dictName], NIL]; }; GetDictAtom: PROC [dictName: ROPE] RETURNS [dict: ATOM] = { ForceLower: Rope.TranslatorType = { RETURN [Ascii.Lower[old]] }; RETURN [Atom.MakeAtom[Rope.Translate[base~dictName, translator~ForceLower]]]; }; ExpandAbbreviation: PUBLIC PROC [world: World, key: Location, dict: ROPE] RETURNS [foundIt: BOOL, keyDeterminesDict: BOOL, keyStart, keyLen, resultLen: INT, commands: LIST OF REF ANY] = { keyNode: Node ~ key.node; keyEnd: INT _ key.where; rdr: RopeReader.Ref; dictAtom: ATOM = GetDictAtom[dict]; keyRope: ROPE _ keyNode.rope; kLen: NAT; kStart: INT; hash: CARDINAL; entry: Entry; 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]; }; 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 = Tioga.Root[node]; keyRoot: Node = Tioga.Root[keyNode]; nodeRope: ROPE _ node.rope; looks: Looks _ Tioga.FetchLooks[keyNode,kStart]; allcaps, initialcap: BOOL _ FALSE; format: ATOM; child: Node _ Tioga.FirstChild[node]; textLen: INT _ Rope.Size[nodeRope]; CheckCaps[]; RopeReader.FreeRopeReader[rdr]; [[len: resultLen]] _ Tioga.ReplaceText[world: world, destRoot: keyRoot, sourceRoot: root, dest: [keyNode, keyStart, keyLen], source: [node, 0, textLen]]; IF resultLen#textLen THEN ERROR; IF looks#noLooks THEN Tioga.ChangeTextLooks[world: world, root: keyRoot, text: [keyNode, keyStart, textLen], remove: noLooks, add: looks]; IF allcaps THEN Tioga.ChangeTextCaps[world: world, root: keyRoot, text: [keyNode, keyStart, textLen], how: allCaps] ELSE IF initialcap AND textLen > 0 THEN -- make first letter uppercase Tioga.ChangeTextCaps[world: world, root: keyRoot, text: [keyNode, keyStart, 1], how: allCaps]; SELECT format _ Tioga.GetFormat[node] FROM NIL, Tioga.GetFormat[keyNode] => NULL; ENDCASE => Tioga.SetFormat[world: world, node: keyNode, formatName: format, root: keyRoot]; IF child # NIL THEN { -- insert as children of keyNode new: Tioga.Span _ Tioga.Copy[world: world, destRoot: keyRoot, sourceRoot: root, dest: Tioga.MakeNodeLoc[keyNode], source: Tioga.MakeNodeSpan[child, Tioga.LastWithin[node]], where: after, nesting: IF textLen=0 THEN 0 ELSE 1]; last: Node _ new.end.node; IF last # NIL AND keyStart+textLen < Tioga.Size[keyNode] THEN -- move text after key to end of last new node [] _ Tioga.MoveText[world: world, destRoot: keyRoot, sourceRoot: keyRoot, dest: [last, maxLen], source: [keyNode, keyStart+textLen, maxLen]]; }; 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]; }; }; 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]]]; }; ValidateAbbreviations: 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]; }; ValidateAllAbbreviations: PUBLIC PROC RETURNS [changed: BOOL _ FALSE] ~ { Locked: PROC ~ { Action: PROC [key: REF, val: REF] RETURNS [quit: BOOLEAN] ~ { IF ValidateAbbreviations[NARROW[key]] THEN changed _ TRUE; RETURN [FALSE] }; [] _ RefTab.Pairs[fileForAbbr, Action]; }; DoLocked[Locked]; }; END. HTiogaAbbrevImpl.mesa Copyright c 1985, 1986 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, September 24, 1986 11:32:45 am PDT Implements abbreviation expansion in Tioga. 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 an abbr. Does not attempt to refresh screen. Called from elsewhere in Tioga when something changes that may have changed any abbr. Does not attempt to refresh screen. Κ˜codešœ™Kšœ Οmœ7™BKšœ ™ Kšœ ™ K™)K™*K™'K™.—K˜Kšœ+™+K˜šΟk ˜ Kšœžœ ˜Kšœžœ˜ Kšœžœžœžœ˜ Kšœ žœžœ ˜Kšœžœ˜Kšžœžœ˜Kšžœžœžœžœ˜3Kšœžœ˜$Kšœžœ˜Kšœžœ˜Kšœžœ$˜0Kšœžœžœ#˜GKšœ žœC˜QKšœ žœ~˜ŽKšœ žœžœ˜Kšœ˜Kšœ žœ˜!—K˜KšΟnœžœž˜Kšžœžœ žœ^˜ŠKšžœ˜ Kšœž˜K˜Kšœžœ˜Kšœ žœ˜ Kšœžœžœžœ˜Kšžœžœžœ˜Kšœžœ˜K˜K˜Kšœžœ˜K˜K˜.K˜Kšœžœžœ ˜šœ žœžœ˜K˜ Kšœžœ˜Kšœ žœ˜K˜ Kšœ žœžœžœž˜K˜—K˜š œžœžœžœžœ˜šžœ žœžœ ˜&Kšœžœ ˜Kšžœžœžœ.˜@Kšžœ/˜3—K˜K˜9K˜K˜DK˜Kšœžœ ˜4Kšžœ žœ ˜/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šœ2™2KšŸ œžœ˜@KšžœG˜MK˜K˜—šŸœžœžœ%žœžœ žœžœžœ žœžœžœžœ˜»K˜Kšœžœ ˜K˜Kšœ žœ˜#Kšœ žœ˜Kšœžœ˜ Kšœžœ˜ Kšœžœ˜K˜ š Ÿ œžœ žœžœžœ˜3K˜ Kšžœžœžœ5˜EKšžœ žœ˜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˜0Kšœžœžœ˜"Kšœžœ˜ K˜%Kšœ žœ˜#K˜ K˜K˜™Kšžœžœžœ˜ šžœž˜K˜t—Kšžœ žœd˜sš žœžœ žœ žœ ˜FKšœ^˜^—šžœ ž˜*Kšžœžœ˜&KšžœT˜[—šžœ žœžœ  ˜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˜Kšœžœžœ ˜š œ žœžœžœžœžœ˜UK˜—šŸœžœžœžœ˜,Kšžœžœžœ˜>Kšœ˜K˜—šŸ œžœ žœžœ ˜6KšœžœžœžœE˜WKšœžœ;˜EKšœ žœžœ˜Kšœžœ˜+š žœ žœžœžœž˜&Kšœ*žœ)žœ žœ˜kKšœ˜Kšžœ˜—Kš žœ žœžœžœžœ˜$Kšžœžœ#˜.Kšœ˜K˜—šŸœžœžœžœžœ žœžœ˜SK™TK™#šŸœžœ˜Kšœ!˜!Kšœžœ&˜@š žœ žœžœ žœžœ˜=Kšžœ ž˜Kšžœ.žœ˜9—Kšœ˜—Kšœ˜Kšœ˜K˜—š Ÿœžœžœžœ žœžœ˜IK™UK™#šŸœžœ˜š Ÿœžœžœžœžœžœ˜=Kšžœžœžœ žœ˜:Kšžœžœ˜Kšœ˜—Kšœ'˜'Kšœ˜—Kšœ˜Kšœ˜K˜—K˜Kšžœ˜—…—4H{