DIRECTORY AbbrevExpand USING [], AbbrevExpandExtras USING [FindEntryProc, FindEntryVal], Atom USING [GetPName, MakeAtom], Basics USING [BITSHIFT, BITXOR], CharOps USING [AlphaNumeric, Blank, Punctuation, Upper, Lower], EditSpan USING [Copy], IO USING [Close, GetIndex, GetRefAny, RIS, STREAM], PFS USING [AbsoluteName, Error, FileInfo, nullUniqueID, PATH, PathFromRope, RopeFromPath, UniqueID], PFSNames USING [Equal], Process USING [GetCurrent], Prop USING [PropList], RefTab USING [Create, Fetch, Pairs, Ref, Store], Rope USING [Cat, Concat, ROPE, Size, Substr, Translate, TranslatorType], RopeReader USING [Backwards, CompareSubstrs, FreeRopeReader, Get, GetIndex, GetRopeReader, Peek, PeekBackwards, ReadOffEnd, Ref, SetPosition], RuntimeError USING [UNCAUGHT], SimpleFeedback USING [Append, Blink, PutF], TextEdit USING [ChangeLooks, ChangeCaps, GetFormat, PutFormat, DeleteText, FetchLooks, MoveText, ReplaceText, Size], TextEditBogus USING [GetRope], TextNode USING [FirstChild, LastWithin, MakeNodeLoc, MakeNodeSpan, Next, Root], Tioga USING [Node, Span, Looks, noLooks, Event], TiogaIO USING [FromFile], UserProfile USING [ListOfTokens]; AbbrevExpandImpl: CEDAR MONITOR IMPORTS Atom, Basics, CharOps, EditSpan, IO, PFS, PFSNames, Process, RefTab, Rope, RopeReader, RuntimeError, SimpleFeedback, TextEdit, TextEditBogus, TextNode, TiogaIO, UserProfile EXPORTS AbbrevExpand, AbbrevExpandExtras = BEGIN Node: TYPE = Tioga.Node; MaxLen: INT = LAST[INT]; ROPE: TYPE = Rope.ROPE; PATH: TYPE = PFS.PATH; dictTable: RefTab.Ref ¬ RefTab.Create[mod: 5]; Entry: TYPE = REF EntryRec; EntryRec: TYPE = RECORD [ next: Entry ¬ NIL, hash: CARDINAL ¬ 0, keyRope: ROPE, node: Node, commands: LIST OF REF ANY ]; LoadInternal: PROC [dict: ATOM, fileID: FileID] 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.comment OR TextEdit.Size[node]=0 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}; SimpleFeedback.Append[$Tioga, begin, $Progress, Rope.Cat["New ", PFS.RopeFromPath[fileID.name], " . . . "]]; [root: root] ¬ TiogaIO.FromFile[fileID.name, fileID.uid ! PFS.Error => { SimpleFeedback.Append[$Tioga, end, $Error, error.explanation]; SimpleFeedback.Blink[$Tioga, $Error]; CONTINUE; }; ]; IF root = NIL THEN RETURN; DoLocked[Locked]; SimpleFeedback.PutF[$Tioga, end, $Progress, "%g entries", [integer[count]] ]; RopeReader.FreeRopeReader[rdr]; ok ¬ TRUE; }; Load: PUBLIC PROC [dictName: ROPE] RETURNS [count: NAT ¬ 0] = { dict: ATOM = GetDictAtom[dictName]; [count: count] ¬ LoadInternal[dict, GetFileID[dict]]; }; AddToDict: ENTRY PROC [root: Node, node: Node, dictAtom: ATOM, rdr: RopeReader.Ref ¬ NIL] = { ENABLE UNWIND => NULL; dict: Entry ¬ GetDict[dictAtom]; keyRope: ROPE ¬ TextEditBogus.GetRope[node]; 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 ¬ Rope.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 CharOps.Blank[Peek[rdr]] THEN { blank ¬ TRUE; [] ¬ Get[rdr] } ELSE blank ¬ FALSE; IF Peek[rdr] = '( THEN { -- parse command list h: IO.STREAM ¬ IO.RIS[Rope.Substr[keyRope,start ¬ GetIndex[rdr]]]; commands ¬ NARROW[IO.GetRefAny[h]]; start ¬ start+IO.GetIndex[h]; IO.Close[h]; SetPosition[rdr, keyRope, start]; IF CharOps.Blank[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 [CharOps.Lower[old]] }; RETURN [Atom.MakeAtom[Rope.Translate[base~dictName, translator~ForceLower]]]; }; findEntryRegistry: Prop.PropList ¬ NIL; ChangeRegistry: PUBLIC PROC [change: PROC [Prop.PropList] RETURNS [Prop.PropList]] ~ { findEntryRegistry ¬ change[findEntryRegistry]; }; Expand: PUBLIC PROC [keyNode: Node, keyEnd: INT, dict: ROPE, event: Tioga.Event ¬ NIL] RETURNS [foundIt: BOOL, keyDeterminesDict: BOOL, keyStart, keyLen, resultLen: INT, commands: LIST OF REF ANY] = { registry: Prop.PropList ~ findEntryRegistry; -- take a snapshot dictAtom: ATOM ~ GetDictAtom[dict]; keyRope: ROPE ¬ TextEditBogus.GetRope[keyNode]; kLen: NAT; kStart: INT; entry: Entry ¬ NIL; hash: CARDINAL; rdr: RopeReader.Ref ~ RopeReader.GetRopeReader[]; FindEntry: PROC [dictAtom: ATOM] RETURNS [entry: Entry ¬ NIL] ~ { IF registry#NIL THEN { key: ROPE ~ Rope.Substr[base: keyRope, start: keyStart, len: kLen]; FOR list: Prop.PropList ¬ registry, list.rest UNTIL entry#NIL OR list=NIL DO WITH list.first.val SELECT FROM val: AbbrevExpandExtras.FindEntryVal => { proc: AbbrevExpandExtras.FindEntryProc ~ val­; kR: ROPE; node: Node; commands: LIST OF REF ANY; [kR, node, commands] ¬ proc[key, dictAtom, keyDeterminesDict]; IF kR#NIL THEN entry ¬ NEW[EntryRec ¬ [ keyRope: kR, node: node, commands: commands]]; }; ENDCASE; ENDLOOP; }; IF entry=NIL THEN { dict: Entry ~ GetDict[dictAtom]; IF dict#NIL THEN entry ¬ Lookup[dict,hash,keyRope,keyStart,kLen,rdr]; }; }; foundIt ¬ keyDeterminesDict ¬ FALSE; resultLen ¬ 0; 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[Rope.Substr[keyRope,nameStart,keyStart-1-nameStart]]; entry ¬ FindEntry[dictName]; keyDeterminesDict ¬ TRUE; keyLen ¬ keyEnd - (keyStart ¬ nameStart); }; }; IF entry = NIL THEN entry ¬ 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; child: Node ~ TextNode.FirstChild[node]; textLen: INT ~ TextEdit.Size[node]; root: Node ~ TextNode.Root[node]; keyRoot: Node = TextNode.Root[keyNode]; looks: Tioga.Looks ¬ TextEdit.FetchLooks[keyNode,kStart]; allcaps, initialcap: BOOL ¬ FALSE; format: ATOM; 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 # Tioga.noLooks THEN TextEdit.ChangeLooks[root: keyRoot, text: keyNode, add: looks, start: keyStart, len: textLen, event: event]; IF allcaps THEN TextEdit.ChangeCaps[root: keyRoot, dest: keyNode, start: keyStart, len: textLen, how: allCaps, event: event] ELSE IF initialcap AND textLen > 0 THEN -- make first letter uppercase TextEdit.ChangeCaps[root: keyRoot, dest: keyNode, start: keyStart, len: 1, how: allCaps, event: event]; SELECT format ¬ TextEdit.GetFormat[node] FROM NIL, TextEdit.GetFormat[keyNode] => NULL; ENDCASE => TextEdit.PutFormat[keyNode, format, event]; IF child # NIL THEN { -- insert as children of keyNode new: Tioga.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], CharOps.Upper[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], CharOps.Upper[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 CharOps.Punctuation[RopeReader.Peek[rdr]] THEN RETURN [1]; len ¬ 0; WHILE len0 AND CharOps.AlphaNumeric[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]; }; }; styleDir: ROPE ~ "/cedar/styles"; defaultSearch: LIST OF ROPE ¬ LIST[styleDir]; FileID: TYPE ~ REF FileIDRep; FileIDRep: TYPE ~ RECORD [name: PFS.PATH, uid: PFS.UniqueID]; Same: PROC [a, b: FileID] RETURNS [BOOL] ~ { RETURN [a.uid = b.uid AND PFSNames.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"]; fileNamePath: PATH ¬ NIL; uid: PFS.UniqueID ¬ PFS.nullUniqueID; WHILE fileNamePath = NIL AND dirs # NIL DO [fullFName: fileNamePath, uniqueID: uid] ¬ PFS.FileInfo[name: PFS.AbsoluteName[short: PFS.PathFromRope[name], wDir: PFS.PathFromRope[dirs.first] ] ! PFS.Error => CONTINUE]; dirs ¬ dirs.rest; ENDLOOP; IF fileNamePath = NIL THEN RETURN [NIL]; RETURN [NEW[FileIDRep ¬ [fileNamePath, uid]]]; }; 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. œ AbbrevExpandImpl.mesa Copyright Σ 1985, 1986, 1987, 1989, 1991, 1992 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, September 24, 1991 2:06 pm PDT Rick Beach, May 30, 1985 3:02:37 pm PDT Spreitze, July 9, 1990 4:51 pm PDT Willie-s, June 27, 1991 11:38 am PDT Doug Wyatt, March 6, 1992 2:41 pm PST Implements abbreviation expansion in Tioga. Declarations Operations -- 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. Κc•NewlineDelimiter –(cedarcode) style™codešœ™Kšœ ΟeœO™ZKšΟy ™ Kšž ™ K™)K™-K™'K™"K™$K™%—K˜Kšœ+™+K˜šΟk ˜ Kšœ Ÿœ˜KšœŸœ˜7KšœŸœ˜ KšœŸœŸœŸœ˜ KšœŸœ2˜?Kšœ Ÿœ˜KšŸœŸœŸœŸœ˜3KšŸœŸœ/Ÿœ(˜dKšœ Ÿœ ˜KšœŸœ˜KšœŸœ ˜KšœŸœ$˜0KšœŸœŸœ+˜HKšœ Ÿœ~˜ŽKšœ ŸœŸœ˜KšœŸœ˜+Kšœ Ÿœf˜tKšœŸœ ˜Kšœ ŸœA˜OKšœŸœ%˜0KšœŸœ ˜Kšœ Ÿœ˜!—K˜KšΟnœŸœŸ˜KšŸœ"ŸœŸœ„˜΄KšŸœ!˜(KšœŸ˜headšœ ™ KšœŸœ˜KšœŸœŸœŸœ˜KšŸœŸœŸœ˜KšŸœŸœŸœŸœ˜K˜K˜.K˜KšœŸœŸœ ˜šœ ŸœŸœ˜KšœŸœ˜KšœŸœ˜Kšœ Ÿœ˜K˜ Kšœ ŸœŸœŸœŸ˜K˜——šœ ™ š  œŸœŸœŸœŸœŸœ Ÿœ ˜_K˜1Kšœ Ÿœ˜š œŸœ˜Kšœ0Οc˜HšŸœ=ŸœŸœŸ˜QKš ŸœŸœŸœŸœŸœŸœ˜?K˜!K˜KšŸœ˜—Kšœ-˜-Kšœ˜—š œŸœ˜Kšœ0‘˜HKšœ˜—KšŸœ ŸœŸœŸœ˜/KšœAŸœ(˜lšœ9˜9šŸœ ˜Kšœ>˜>Kšœ%˜%KšŸœ˜ K˜—Kšœ˜—KšŸœŸœŸœŸœ˜Kšœ˜KšœM˜MK˜KšœŸœ˜ Kšœ˜K˜—š  œŸœŸœ ŸœŸœ Ÿœ ˜?KšœŸœ˜#Kšœ5˜5K˜K˜—š   œŸœŸœ$ŸœŸœ˜]KšŸœŸœŸœ˜K˜K˜ Kšœ Ÿœ˜,Kšœ Ÿœ˜KšœŸœ˜K˜ KšœŸœ˜ Kšœ ŸœŸœ˜Kšœ Ÿœ˜Kš œ ŸœŸœŸœŸœ˜K˜KšŸœŸœŸœ/Ÿœ˜EK˜!Kš Ÿœ ŸœŸœ Ÿœ!Ÿœ‘˜\K˜,K˜>šŸœ ŸœŸœ‘˜&KšœŸœ ˜KšŸœŸœŸœ.˜@KšŸœ/˜3—K˜K˜5K˜K˜DK˜K˜-KšŸœ Ÿœ ˜/K˜K˜—š  œŸœ Ÿœ ŸœŸœŸœ ŸœŸœŸœŸœ˜vKšŸœ ˜KšŸœŸœ˜KšœŸœ˜ K˜!Kš ŸœŸœ ŸœŸœ Ÿœ˜TšŸœŸœ‘˜.Kš œŸœŸœŸœŸœ-˜BKšœ ŸœŸœ˜#KšœŸœ ˜KšŸœ ˜ K˜!Kš ŸœŸœ ŸœŸœ Ÿœ˜TK˜—šŸœŸœ‘˜+K˜KšŸœŸœŸœ‘-˜]K˜K˜—KšŸœ˜KšŸœŸœ˜2K˜K˜—š  œŸœŸœŸœ Ÿœ˜-Kšœ.™.KšŸœŸœŸœ˜Kšœ4Ÿœ˜9Kšœ˜K˜—Kš  œŸœŸœŸœ˜"K˜š   œŸœ ŸœŸœŸœ˜;Kšœ2™2Kš  œŸœ˜BKšŸœG˜MK˜K˜—Kšœ#Ÿœ˜'š  œŸœŸœ ŸœŸœ˜VKšœ.˜.K˜K˜—š œŸœŸœŸœŸœŸœŸœ ŸœŸœŸœ ŸœŸœŸœŸœ˜ΘKšœ-‘˜?Kšœ Ÿœ˜#Kšœ Ÿœ"˜/KšœŸœ˜ KšœŸœ˜ KšœŸœ˜KšœŸœ˜K˜1š   œŸœ ŸœŸœŸœ˜AšŸœ ŸœŸœ˜KšœŸœ:˜Cš Ÿœ+ŸœŸœŸœŸœŸ˜LšŸœŸœŸ˜šœ)˜)Kšœ.˜.Kš œŸœŸœŸœŸœŸœ˜0Kšœ>˜>KšŸœŸœŸœ Ÿœ<˜VKšœ˜—KšŸœ˜—KšŸœ˜—K˜—šŸœŸœŸœ˜K˜ KšŸœŸœŸœ5˜EK˜—Kšœ˜—KšœŸœ˜$K˜Kšœ ŸœŸœ˜/K˜5K˜ K˜,Kšœ-™-K˜-šŸœŸœŸœ˜7Kš‘'˜'Kšœ Ÿœ(˜6šŸœŸœ˜ Kšœ ŸœF˜TK˜KšœŸœ˜K˜)K˜—K˜—KšŸœ ŸœŸœ˜0K˜KšŸœ ŸœŸœ#Ÿœ˜?Kšœ‘˜ ˜š  œŸœ˜Kšœ Ÿœ˜K˜+Kšœ"Ÿœ ˜.šŸœŸœŸœ Ÿ˜Kš ŸœŸœŸœ ŸœŸœ˜3KšŸœ˜—Kšœ Ÿœ˜Kšœ˜K˜—K˜K˜(Kšœ Ÿœ˜#K˜!K˜'K˜9KšœŸœŸœ˜"KšœŸœ˜ K˜ K˜K˜·KšŸœŸœŸœ˜"KšŸœŸœn˜‹KšŸœ Ÿœm˜|š ŸœŸœ Ÿœ Ÿœ‘˜FKšœg˜g—šŸœ#Ÿ˜-KšŸœ!Ÿœ˜)KšŸœ/˜6—šŸœ ŸœŸœ‘ ˜6KšœŸœ ŸœŸœ ˜²K˜šŸœŸœŸœ+Ÿ˜@Kš‘.˜.K˜’K˜——Kšœ Ÿœ˜K˜K˜—˜K˜——š  œŸœ ŸœŸœŸœŸœ˜cKšœ,™,KšœŸœ˜K˜-K˜š ŸœŸœŸœŸœ Ÿœ‘˜>šœ ŸœŸœ˜'K˜'—KšŸœ˜—šŸœ Ÿœ‘˜/K˜+šŸœŸœŸœŸ˜šœ ŸœŸœ˜'K˜-—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šŸœ+ŸœŸœ˜=K˜šŸœ Ÿœ+Ÿ˜?Kšœ ˜ KšŸœ˜—šœ˜K˜——š   œŸœ Ÿœ ŸœŸœ Ÿœ˜]KšŸœŸœ ŸœŸœ˜8K˜+KšŸœ4ŸœŸœ ˜MK˜šŸœ Ÿœ1Ÿ˜DKšœ˜KšŸœ˜—šœ˜K˜——Kšœ Ÿœ˜*K˜š œŸœ ŸœŸœ˜9KšœŸœŸœ˜K˜-šŸœŸœŸ˜Kšœ Ÿœ˜KšœŸœŸœŸœŸœŸœŸœŸœ˜:šŸœ‘˜KšœŸœŸœ˜KšœEŸœŸœ˜[Kš ŸœŸœŸœŸœŸœŸœ˜8Kšœ˜——Kšœ˜K˜—Kšœ Ÿœ˜!K˜š œŸœŸœŸœŸœ ˜-K˜—KšœŸœŸœ ˜Kš œ ŸœŸœŸœŸœŸœ ˜=š œŸœŸœŸœ˜,KšŸœŸœ Ÿœ˜@Kšœ˜—š  œŸœ ŸœŸœ ˜6KšœŸœŸœŸœE˜WKšœŸœ;˜EKšœŸœŸœ˜KšœŸœ Ÿœ˜%š ŸœŸœŸœŸœŸ˜*Kš œ+ŸœŸœŸœŸœŸœ Ÿœ˜¬Kšœ˜KšŸœ˜—Kš ŸœŸœŸœŸœŸœ˜(KšŸœŸœ#˜.Kšœ˜K˜—KšœŸœŸœ˜&KšœŸœ˜KšœŸ œ˜š œŸœŸœ Ÿœ˜(KšœŸœ˜*š œŸœŸœ˜Kš ŸœŸœŸœŸœŸœ˜MKšœ8˜8Kšœ˜—š œŸœŸœ˜Kšœ"˜"KšŸœŸœŸœŸœ˜GKšœ˜—Kšœ˜Kšœ Ÿœ˜Kšœ ˜ Kšœ˜K˜—šœ+˜+K˜—š   œŸœŸœŸœ ŸœŸœ˜