DIRECTORY Arpa USING [Address, nullAddress], Atom USING [GetPName, MakeAtom], Basics USING [HFromCard16, LowHalf], Convert USING [ArpaAddressFromRope], IO USING [rope, PutFR1], LoganBerry, LoganBerryClass USING [Class, ClassObject, Register], LoganBerrySunRPC, LoganBerrySunRPCClient, RefID USING [Seal, Unseal], Rope USING [Concat, Find, FromRefText, Length, Replace, ROPE, Substr], SunPMap USING [ipProtocolUDP, udpPort], SunPMapClient USING [GetPort], SunRPC USING [Error, Handle], SunRPCOnUDP USING [Create, SetRemote], SunRPCAuth USING [Initiate], SunYPAgent USING [Error, Handle, Match, ObtainHandle, ReleaseHandle, Tokenize]; LoganBerrySunStubImpl: CEDAR MONITOR LOCKS dbinfo USING dbinfo: OpenDBInfo IMPORTS Atom, Basics, Convert, IO, LoganBerry, LoganBerryClass, LoganBerrySunRPCClient, RefID, Rope, SunPMapClient, SunRPC, SunRPCAuth, SunRPCOnUDP, SunYPAgent ~ BEGIN ROPE: TYPE = Rope.ROPE; Conv: TYPE ~ LoganBerry.Conv; OpenDB: TYPE ~ LoganBerry.OpenDB; AttributeType: TYPE ~ LoganBerry.AttributeType; AttributeValue: TYPE ~ LoganBerry.AttributeValue; Entry: TYPE ~ LoganBerry.Entry; EntryProc: TYPE ~ LoganBerry.EntryProc; Cursor: TYPE ~ LoganBerry.Cursor; CursorDirection: TYPE ~ LoganBerry.CursorDirection; LogID: TYPE ~ LoganBerry.LogID; activityLog: LogID ~ LoganBerry.activityLog; SchemaInfo: TYPE ~ LoganBerry.SchemaInfo; nilconv: INT = 0; OpenDBInfo: TYPE = REF OpenDBRecord; OpenDBRecord: TYPE = MONITORED RECORD [ h: SunRPC.Handle ¬ NIL, db: CARD32 ]; CursorInfo: TYPE = REF CursorRecord; CursorRecord: TYPE = RECORD[ dbinfo: OpenDBInfo, cursor: CARD32 ]; CheckName: PROC [dbName: ROPE] RETURNS [accept: BOOLEAN, server: ROPE, nameOnServer: ROPE] ~ { fullPathName: ROPE; serverIndex: INT; sunName: BOOL ¬ TRUE; fullPathName ¬ dbName; accept ¬ FALSE; serverIndex ¬ Rope.Find[s1: fullPathName, s2: "-SUN", case: FALSE]; IF serverIndex = -1 THEN { sunName ¬ FALSE; serverIndex ¬ Rope.Find[s1: fullPathName, s2: "-ARPA", case: FALSE]}; IF serverIndex > -1 THEN { -- found either -sun or -arpa suffix newlength: INT; fullServerName: ROPE; serverNameLength: INT ¬ serverIndex - 1; accept ¬ TRUE; server ¬ Rope.Substr[base: fullPathName, start: 1, len: serverNameLength]; IF sunName THEN fullServerName ¬ Rope.Concat[base: server, rest: "-SUN"] ELSE fullServerName ¬ Rope.Concat[base: server, rest: "-ARPA"]; newlength ¬ Rope.Length[fullServerName] ; nameOnServer ¬ NIL; nameOnServer ¬ Rope.Replace[base: fullPathName, start: 1, len: newlength, with: NIL]; newlength ¬ Rope.Length[nameOnServer]; nameOnServer ¬ Rope.Substr[base: nameOnServer, start: 1, len: newlength]; }; }; ypMap: ROPE ¬ "hosts.byname"; SunYPNameToAddress: PROC [name: ROPE] RETURNS [addr: Arpa.Address ¬ Arpa.nullAddress] ~ { ENABLE SunYPAgent.Error => CONTINUE; mapEntry: REF TEXT ¬ NIL; h: SunYPAgent.Handle ¬ SunYPAgent.ObtainHandle[]; -- use default domain mapEntry ¬ SunYPAgent.Match[h, ypMap, name]; SunYPAgent.ReleaseHandle[h]; IF mapEntry # NIL THEN { addrText: REF TEXT ¬ SunYPAgent.Tokenize[mapEntry][0]; -- address is at front of mapEntry addr ¬ Convert.ArpaAddressFromRope[Rope.FromRefText[addrText]]; }; }; Open: PROC [conv: Conv ¬ NIL, dbName: ROPE] RETURNS [db: OpenDB] ~ { ENABLE { SunRPC.Error => PostSunError[code]; LoganBerrySunRPCClient.Error => ERROR LoganBerry.Error[Atom.MakeAtom[ec], explanation]; }; accept: BOOLEAN; server, nameOnServer: ROPE; dbinfo: OpenDBInfo; addr: Arpa.Address; port: CARD; [accept, server, nameOnServer] ¬ CheckName[dbName]; IF NOT accept THEN RETURN[LoganBerry.nullDB]; dbinfo ¬ NEW[OpenDBRecord]; addr ¬ SunYPNameToAddress[server]; IF ( addr = Arpa.nullAddress ) THEN ERROR LoganBerry.Error[$CantOpenSchema, IO.PutFR1["Unknown server: %g.", IO.rope[server]]]; dbinfo.h ¬ SunRPCOnUDP.Create[addr, Basics.HFromCard16[SunPMap.udpPort]]; port ¬ SunPMapClient.GetPort[dbinfo.h, SunRPCAuth.Initiate[], 390905, 1, SunPMap.ipProtocolUDP ! SunRPC.Error => ERROR LoganBerry.Error[$CantOpenSchema, IO.PutFR1["Can't contact remote server: %g.", IO.rope[server]]]]; IF port = 0 THEN ERROR LoganBerry.Error[$CantOpenSchema, IO.PutFR1["LoganBerry not exported on server: %g.", IO.rope[server]]]; dbinfo.h ¬ SunRPCOnUDP.SetRemote[dbinfo.h, addr, Basics.HFromCard16[Basics.LowHalf[port]]]; dbinfo.db ¬ LoganBerrySunRPCClient.Open[dbinfo.h, nilconv, nameOnServer]; RETURN[RefID.Seal[dbinfo]]; }; ReadEntry: PROC [conv: Conv ¬ NIL, db: OpenDB, key: AttributeType, value: AttributeValue] RETURNS [entry: Entry, others: BOOLEAN] ~ { ENABLE { SunRPC.Error => PostSunError[code]; LoganBerrySunRPCClient.Error => ERROR LoganBerry.Error[Atom.MakeAtom[ec], explanation]; }; dbinfo: OpenDBInfo ¬ GetInfo[db]; [entry, others] ¬ ReadEntryI[dbinfo, key, value]; RETURN[entry, others]; }; ReadEntryI: PUBLIC ENTRY PROC [dbinfo: OpenDBInfo, key: AttributeType, value: AttributeValue] RETURNS [entry: Entry, others: BOOLEAN] ~ { ENABLE UNWIND => NULL; srEntry: LoganBerrySunRPC.Entry; [srEntry, others] ¬ LoganBerrySunRPCClient.ReadEntry[dbinfo.h, nilconv, dbinfo.db, Atom.GetPName[key], value]; entry ¬ SrToEntry[srEntry]; }; EnumerateEntries: PROC [db: OpenDB, key: AttributeType, start: AttributeValue ¬ NIL, end: AttributeValue ¬ NIL, proc: EntryProc] RETURNS [] ~ { entry: Entry; c: Cursor ¬ GenerateEntries[NIL, db, key, start, end]; entry ¬ NextEntry[NIL, c]; WHILE entry # NIL DO IF NOT proc[entry] THEN EXIT; entry ¬ NextEntry[NIL, c]; ENDLOOP; EndGenerate[NIL, c]; }; GenerateEntries: PROC [conv: Conv ¬ NIL, db: OpenDB, key: AttributeType, start: AttributeValue ¬ NIL, end: AttributeValue ¬ NIL] RETURNS [cursor: Cursor] ~ { ENABLE { SunRPC.Error => PostSunError[code]; LoganBerrySunRPCClient.Error => ERROR LoganBerry.Error[Atom.MakeAtom[ec], explanation]; }; cinfo: CursorInfo; dbinfo: OpenDBInfo ¬ GetInfo[db]; cinfo ¬ GenerateEntriesI[dbinfo, key, start, end]; RETURN[RefID.Seal[cinfo]]; }; GenerateEntriesI: PUBLIC ENTRY PROC [dbinfo: OpenDBInfo, key: AttributeType, start: AttributeValue ¬ NIL, end: AttributeValue ¬ NIL] RETURNS [cinfo: CursorInfo] ~ { ENABLE UNWIND => NULL; cinfo ¬ NEW[CursorRecord]; cinfo.dbinfo ¬ dbinfo; cinfo.cursor ¬ LoganBerrySunRPCClient.GenerateEntries[dbinfo.h, nilconv, dbinfo.db, Atom.GetPName[key], start, end]; }; NextEntry: PROC [conv: Conv ¬ NIL, cursor: Cursor, dir: CursorDirection ¬ increasing] RETURNS [entry: Entry] ~ { ENABLE { SunRPC.Error => PostSunError[code]; LoganBerrySunRPCClient.Error => ERROR LoganBerry.Error[Atom.MakeAtom[ec], explanation]; }; cinfo: CursorInfo ¬ GetCursorInfo[cursor]; entry ¬ NextEntryI[cinfo.dbinfo, cinfo, dir]; RETURN[entry]; }; NextEntryI: PUBLIC ENTRY PROC [dbinfo: OpenDBInfo, cinfo: CursorInfo, dir: CursorDirection ¬ increasing] RETURNS [entry: Entry] ~ { ENABLE UNWIND => NULL; srEntry: LoganBerrySunRPC.Entry; srEntry ¬ LoganBerrySunRPCClient.NextEntry[cinfo.dbinfo.h, nilconv, cinfo.cursor, IF dir=increasing THEN increasing ELSE decreasing]; entry ¬ SrToEntry[srEntry]; }; EndGenerate: PROC [conv: Conv ¬ NIL, cursor: Cursor] RETURNS [] ~ { ENABLE { SunRPC.Error => PostSunError[code]; LoganBerrySunRPCClient.Error => ERROR LoganBerry.Error[Atom.MakeAtom[ec], explanation]; }; cinfo: CursorInfo ¬ GetCursorInfo[cursor]; EndGenerateI[cinfo.dbinfo, cinfo]; }; EndGenerateI: PUBLIC ENTRY PROC [dbinfo: OpenDBInfo, cinfo: CursorInfo] RETURNS [] ~ { ENABLE UNWIND => NULL; LoganBerrySunRPCClient.EndGenerate[cinfo.dbinfo.h, nilconv, cinfo.cursor]; }; WriteEntry: PROC [conv: Conv ¬ NIL, db: OpenDB, entry: Entry, log: LogID ¬ activityLog, replace: BOOLEAN ¬ FALSE] RETURNS [] ~ { ENABLE { SunRPC.Error => PostSunError[code]; LoganBerrySunRPCClient.Error => ERROR LoganBerry.Error[Atom.MakeAtom[ec], explanation]; }; dbinfo: OpenDBInfo ¬ GetInfo[db]; WriteEntryI[dbinfo, entry, log, replace]; }; WriteEntryI: PUBLIC ENTRY PROC [dbinfo: OpenDBInfo, entry: Entry, log: LogID ¬ activityLog, replace: BOOLEAN ¬ FALSE] RETURNS [] ~ { ENABLE UNWIND => NULL; LoganBerrySunRPCClient.WriteEntry[dbinfo.h, nilconv, dbinfo.db, EntryToSr[entry], log, replace]; }; DeleteEntry: PROC [conv: Conv ¬ NIL, db: OpenDB, key: AttributeType, value: AttributeValue] RETURNS [deleted: Entry] ~ { ENABLE { SunRPC.Error => PostSunError[code]; LoganBerrySunRPCClient.Error => ERROR LoganBerry.Error[Atom.MakeAtom[ec], explanation]; }; dbinfo: OpenDBInfo ¬ GetInfo[db]; deleted ¬ DeleteEntryI[dbinfo, key, value]; RETURN[deleted]; }; DeleteEntryI: PUBLIC ENTRY PROC [dbinfo: OpenDBInfo, key: AttributeType, value: AttributeValue] RETURNS [deleted: Entry] ~ { ENABLE UNWIND => NULL; srEntry: LoganBerrySunRPC.Entry; srEntry ¬ LoganBerrySunRPCClient.ReadEntry[dbinfo.h, nilconv, dbinfo.db, Atom.GetPName[key], value].entry; LoganBerrySunRPCClient.DeleteEntry[dbinfo.h, nilconv, dbinfo.db, Atom.GetPName[key], value]; deleted ¬ SrToEntry[srEntry]; }; Close: PROC [conv: Conv ¬ NIL, db: OpenDB] RETURNS [] ~ { ENABLE { SunRPC.Error => PostSunError[code]; LoganBerrySunRPCClient.Error => ERROR LoganBerry.Error[Atom.MakeAtom[ec], explanation]; }; dbinfo: OpenDBInfo ¬ GetInfo[db]; CloseI[dbinfo]; }; CloseI: PUBLIC ENTRY PROC [dbinfo: OpenDBInfo] RETURNS [] ~ { ENABLE UNWIND => NULL; LoganBerrySunRPCClient.Close[dbinfo.h, nilconv, dbinfo.db]; }; BuildIndices: PROC [conv: Conv ¬ NIL, db: OpenDB] RETURNS [] ~ { ENABLE { SunRPC.Error => PostSunError[code]; LoganBerrySunRPCClient.Error => ERROR LoganBerry.Error[Atom.MakeAtom[ec], explanation]; }; dbinfo: OpenDBInfo ¬ GetInfo[db]; BuildIndicesI[dbinfo]; }; BuildIndicesI: PUBLIC ENTRY PROC [dbinfo: OpenDBInfo] RETURNS [] ~ { ENABLE UNWIND => NULL; LoganBerrySunRPCClient.BuildIndices[dbinfo.h, nilconv, dbinfo.db]; }; CompactLogs: PROC [conv: Conv ¬ NIL, db: OpenDB] RETURNS [] ~ { ENABLE { SunRPC.Error => PostSunError[code]; LoganBerrySunRPCClient.Error => ERROR LoganBerry.Error[Atom.MakeAtom[ec], explanation]; }; dbinfo: OpenDBInfo ¬ GetInfo[db]; CompactLogsI[dbinfo]; }; CompactLogsI: PUBLIC ENTRY PROC [dbinfo: OpenDBInfo] RETURNS [] ~ { ENABLE UNWIND => NULL; LoganBerrySunRPCClient.CompactLogs[dbinfo.h, nilconv, dbinfo.db]; }; Describe: PROC [conv: Conv ¬ NIL, db: OpenDB] RETURNS [info: SchemaInfo] ~ { ENABLE { SunRPC.Error => PostSunError[code]; LoganBerrySunRPCClient.Error => ERROR LoganBerry.Error[Atom.MakeAtom[ec], explanation]; }; dbinfo: OpenDBInfo ¬ GetInfo[db]; info ¬ DescribeI[dbinfo]; RETURN[info]; }; DescribeI: PUBLIC ENTRY PROC [dbinfo: OpenDBInfo] RETURNS [info: SchemaInfo] ~ { ENABLE UNWIND => NULL; srInfo: LoganBerrySunRPC.SchemaInfo; srInfo ¬ LoganBerrySunRPCClient.Describe[dbinfo.h, nilconv, dbinfo.db]; info ¬ SrToSchemaInfo[srInfo]; }; IsLocal: PROC [db: LoganBerry.OpenDB] RETURNS [local: BOOL ¬ TRUE] = { RETURN[FALSE]; }; PostSunError: PROC [code: ATOM] RETURNS [] ~ { ERROR LoganBerry.Error[$CallFailed, IO.PutFR1["SUN RPC stub problem - %g.", IO.rope[SELECT code FROM $timeout, $unreachable => "server unreachable", $protocolError => "protocol error", $wrongRPCVersion, $wrongProgram, $wrongProgramVersion, $wrongProc => "using wrong program, version, or procedure", ENDCASE => "unknown error" ]]]; }; GetInfo: PROC [db: OpenDB] RETURNS [OpenDBInfo] ~ INLINE { ref: REF = RefID.Unseal[db]; IF ref = NIL THEN ERROR LoganBerry.Error[$BadDBHandle, "NIL OpenDB handle."]; WITH ref SELECT FROM dbinfo: OpenDBInfo => { RETURN[dbinfo]; }; ENDCASE => ERROR LoganBerry.Error[$BadDBHandle, "Invalid OpenDB handle."]; }; GetCursorInfo: PROC [cursor: Cursor] RETURNS [CursorInfo] ~ INLINE { ref: REF = RefID.Unseal[cursor]; IF ref = NIL THEN ERROR LoganBerry.Error[$BadCursor, "NIL cursor."]; WITH ref SELECT FROM cinfo: CursorInfo => { RETURN[cinfo]; }; ENDCASE => ERROR LoganBerry.Error[$BadCursor, "Invalid cursor."]; }; SrToEntry: PROC [srEntry: LoganBerrySunRPC.Entry] RETURNS [entry: LoganBerry.Entry] ~ { entry ¬ NIL; FOR i: CARDINAL DECREASING IN [0..srEntry.length) DO attr: LoganBerry.Attribute ¬ [Atom.MakeAtom[srEntry[i].type], srEntry[i].value]; entry ¬ CONS[attr, entry]; ENDLOOP; }; EntryToSr: PROC [entry: LoganBerry.Entry] RETURNS [srEntry: LoganBerrySunRPC.Entry] ~ { count: CARDINAL ¬ 0; FOR e: Entry ¬ entry, e.rest WHILE e # NIL DO count ¬ count + 1; ENDLOOP; srEntry ¬ NEW[LoganBerrySunRPC.EntryObject[count]]; count ¬ 0; FOR e: Entry ¬ entry, e.rest WHILE e # NIL DO srEntry[count].type ¬ Atom.GetPName[e.first.type]; srEntry[count].value ¬ e.first.value; count ¬ count + 1; ENDLOOP; }; SrToSchemaInfo: PROC [srInfo: LoganBerrySunRPC.SchemaInfo] RETURNS [info: LoganBerry.SchemaInfo] ~ { SrToLogList: PROC [srLogs: LoganBerrySunRPC.LogList] RETURNS [logs: LIST OF LoganBerry.LogInfo] ~ { logs ¬ NIL; FOR i: CARDINAL DECREASING IN [0..srLogs.length) DO loginfo: LoganBerry.LogInfo ¬ NEW[LoganBerry.LogInfoRec ¬ [srLogs[i].id, srLogs[i].file]]; logs ¬ CONS[loginfo, logs]; ENDLOOP; }; SrToIndexList: PROC [srIndices: LoganBerrySunRPC.IndexList] RETURNS [indices: LIST OF LoganBerry.IndexInfo] ~ { indices ¬ NIL; FOR i: CARDINAL DECREASING IN [0..srIndices.length) DO indexinfo: LoganBerry.IndexInfo ¬ NEW[LoganBerry.IndexInfoRec ¬ [Atom.MakeAtom[srIndices[i].key], srIndices[i].file, Atom.MakeAtom[srIndices[i].order]]]; indices ¬ CONS[indexinfo, indices]; ENDLOOP; }; info ¬ NEW[LoganBerry.SchemaInfoRec ¬ [srInfo.dbName, SrToLogList[srInfo.logs], SrToIndexList[srInfo.indices]]]; }; class: LoganBerryClass.Class; Init: PROC ~ { LoganBerryClass.Register[name: class.name, class: class]; }; class ¬ NEW[LoganBerryClass.ClassObject ¬ [ name: $SunRPCStub, open: Open, describe: Describe, readEntry: ReadEntry, enumerateEntries: EnumerateEntries, generateEntries: GenerateEntries, nextEntry: NextEntry, endGenerate: EndGenerate, writeEntry: WriteEntry, deleteEntry: DeleteEntry, close: Close, buildIndices: BuildIndices, compactLogs: CompactLogs, isLocal: IsLocal, classData: NIL ]]; Init[]; END. ξ LoganBerrySunStubImpl.mesa Copyright Σ 1985, 1989, 1992 by Xerox Corporation. All rights reserved. Doug Terry, February 10, 1991 6:59 pm PST Brian Oki, March 30, 1990 4:38 pm PST Willie-s, April 23, 1992 5:14 pm PDT Allows one to invoke operations on LoganBerry servers using SUN RPC. -- Operations are monitored to avoid concurrent callers using the same RPC handle. EXPORTS LoganBerry indirectly by registering with LoganBerryClass Naming Database name should be of the form: /server-Sun/rest/...LB.df. This becomes: server="server", nameOnServer="//rest/...LB.df". Now the full server name is of the form name-sun or name-arpa. Now from this extract the full path name to get the actual name on server. map entry is something like "13.0.208.72 baobab baobab.parc.xerox.com" Class methods Simply turn SUN RPC errors into LoganBerry errors. Handles Unseals the database handle and ensures that it's valid. Unseals the cursor and ensures that it's valid. Data conversion Class registration Κš–(cedarcode) style•NewlineDelimiter ™šœ™Icodešœ Οeœ=™HKšœ)™)K™%K™$K™KšœF™FK™—šΟk ˜ Kšœžœ˜"Kšœžœ˜ Kšœžœ˜$Kšœžœ˜$Kšžœžœ˜K˜ Kšœžœ ˜5Kšœ˜Kšœ˜Kšœžœ˜Kšœžœ/žœ ˜GKšœžœ˜'Kšœžœ ˜Kšœžœ˜Kšœ žœ˜&Kšœ žœ ˜Kšœ žœ?˜O—K˜š Ρblnœžœžœžœžœ˜JKšœR™R—Kšžœžœ~˜ Kšžœ:™Ašœž˜K˜Icode0šžœžœžœ˜Kšœžœ˜Kšœžœ˜!Kšœžœ˜/Kšœžœ˜1Kšœžœ˜Kšœ žœ˜'Kšœžœ˜!Kšœžœ˜3Kšœžœ˜Kšœ,˜,Kšœ žœ˜)L˜Lšœ žœ˜L˜Lšœ žœžœ˜$šœžœžœ˜'Lšœžœ˜LšœžΟc˜ L˜—L˜Lšœ žœžœ˜$šœžœžœ˜Lšœ˜Lšœžœ˜L˜——head™Kšœ€™€K˜šΟn œžœ žœžœ žœ žœžœ˜^Kšœžœ˜Kšœ žœ˜Kšœ žœžœ˜K˜K˜Kšœ žœ˜K˜Kšœ<žœ˜Cšžœžœ˜Kšœ žœ˜Kšœ=žœ˜E—K˜šžœžœ $˜@Kšœ žœ˜Kšœžœ˜Kšœžœ˜(Kšœ žœ˜K•StartOfExpansion9[base: ROPE, start: INT _ 0, len: INT _ 2147483647]˜JK˜šžœ ž˜K˜8Kšžœ;˜?K˜—K™‰K˜)Kšœžœ˜KšœPžœ˜UK˜&K˜IK˜K˜—K˜K˜—Kšœžœ˜K˜š‘œžœžœžœ,˜YKšžœžœ˜$Kšœ žœžœžœ˜Kšœ3 ˜HK˜,Kšœ˜šžœ žœžœ˜KšœF™FKšœ žœžœ& "˜ZK˜?K˜—K˜——™ š ‘œžœžœ žœžœ˜Dšžœ˜Kšœ#˜#Kšœ žœ2˜WKšœ˜—Kšœžœ˜Kšœžœ˜Kšœ˜Kšœ˜Kšœžœ˜ K˜K˜3Kšžœžœžœžœ˜-K˜Kšœ žœ˜K˜"šžœžœ˜$Kšžœ#žœžœ˜[—K˜IKšœqžœ#žœ,žœ˜Ϊšžœ žœ˜Kšžœ#žœ2žœ˜n—K˜K˜[K˜IKšžœ˜Kšœ˜—K˜š ‘ œžœžœ9žœžœ˜…šžœ˜Kšœ#˜#Kšœ žœ2˜WKšœ˜—K˜!K˜1Kšžœ˜K˜K˜—š ‘ œžœžœžœAžœžœ˜‰Kšžœžœžœ˜Kšœ ˜ K˜nK˜K˜—K˜š ‘œžœ:žœžœžœ˜Kšœ ˜ Kšœžœ˜6Kšœžœ˜šžœ žœžœ˜Kšžœžœ žœžœ˜Kšœžœ˜Kšžœ˜—Kšœ žœ˜K˜—K˜š ‘œžœžœ:žœžœžœ˜šžœ˜Kšœ#˜#Kšœ žœ2˜WKšœ˜—Kšœ˜K˜!K˜2Kšžœ˜Kšœ˜K˜—š‘œžœžœžœBžœžœžœ˜€Kšžœžœžœ˜Kšœžœ˜K˜K˜tK˜—K˜š‘ œžœžœ5žœ˜pšžœ˜Kšœ#˜#Kšœ žœ2˜WKšœ˜—K˜*K˜-Kšžœ˜K˜K˜—š ‘ œžœžœžœLžœ˜ƒKšžœžœžœ˜Kšœ ˜ KšœRžœžœ žœ ˜…K˜K˜—K˜š‘ œžœžœžœ˜Cšžœ˜Kšœ#˜#Kšœ žœ2˜WKšœ˜—K˜*Kšœ"˜"K˜K˜—š ‘ œžœžœžœ)žœ˜VKšžœžœžœ˜KšœJ˜JKšœ˜—K˜š ‘ œžœžœ?žœžœžœ˜€šžœ˜Kšœ#˜#Kšœ žœ2˜WKšœ˜—K˜!Kšœ)˜)K˜K˜—š‘ œžœžœžœGžœžœžœ˜„Kšžœžœžœ˜Kšœ`˜`K˜—K˜š‘ œžœžœ9žœ˜xšžœ˜Kšœ#˜#Kšœ žœ2˜WKšœ˜—K˜!K˜+Kšžœ ˜K˜K˜—š ‘ œžœžœžœAžœ˜|Kšžœžœžœ˜Kšœ ˜ K˜jKšœ\˜\K˜K˜—K˜š‘œžœžœžœ˜9šžœ˜Kšœ#˜#Kšœ žœ2˜WKšœ˜—K˜!Kšœ˜K˜K˜—š ‘œžœžœžœžœ˜=Kšžœžœžœ˜Kšœ;˜;K˜—K˜š‘ œžœžœžœ˜@šžœ˜Kšœ#˜#Kšœ žœ2˜WKšœ˜—K˜!Kšœ˜Kšœ˜K˜—š ‘ œžœžœžœžœ˜DKšžœžœžœ˜KšœB˜BK˜—K˜š‘ œžœžœžœ˜?šžœ˜Kšœ#˜#Kšœ žœ2˜WKšœ˜—K˜!Kšœ˜K˜K˜—š ‘ œžœžœžœžœ˜CKšžœžœžœ˜KšœA˜AK˜—K˜š‘œžœžœžœ˜Lšžœ˜Kšœ#˜#Kšœ žœ2˜WKšœ˜—K˜!K˜Kšžœ˜ K˜K˜—š ‘ œžœžœžœžœ˜PKšžœžœžœ˜Kšœ$˜$K˜GK˜K˜K˜—š ‘œžœžœ žœžœ˜FKšžœžœ˜K˜K˜—š‘ œžœžœžœ˜.Jšœ2™2šžœžœ0žœž˜dKšœ/˜/Kšœ#˜#Kšœr˜rKšžœ˜Kšœ˜—K˜——™š‘œžœžœžœ˜:K™8Kšœžœ˜šžœžœž˜Kšžœ6˜;—šžœžœž˜šœž˜Kšžœ ˜K˜—Kšžœžœ:˜J—K˜K˜—š‘ œžœžœžœ˜DKšœ/™/Kšœžœ˜ šžœžœž˜Kšžœ-˜2—šžœžœž˜šœž˜Kšžœ˜K˜—Kšžœžœ1˜A—K˜K˜——™š‘ œžœ#žœ˜WKšœžœ˜ š žœžœž œžœž˜4K˜PKšœžœ˜Kšžœ˜—K˜K˜—š‘ œžœžœ&˜WKšœžœ˜šžœžœžœž˜-K˜Kšžœ˜—Kšœ žœ&˜3K˜ šžœžœžœž˜-K˜2K˜%K˜Kšžœ˜—K˜K˜—š‘œžœ'žœ"˜dš ‘ œžœ$žœžœžœ˜cKšœžœ˜ š žœžœž œžœž˜3Kšœžœ9˜ZKšœžœ˜Kšžœ˜—K˜—š ‘ œžœ)žœ žœžœ˜oKšœ žœ˜š žœžœž œžœž˜6Kšœ"žœt˜™Kšœ žœ˜#Kšžœ˜—K˜—Kšœžœf˜pK˜K˜—K˜—™Kšœ˜K˜š‘œžœ˜Kšœ9˜9K˜K˜—K˜šœžœ ˜+K˜Kšœ ˜ Kšœ˜Kšœ˜Kšœ#˜#Kšœ!˜!Kšœ˜Kšœ˜Kšœ˜Kšœ˜K˜ Kšœ˜Kšœ˜K˜Kšœ ž˜Kšœ˜K˜—K˜K˜—Kšžœ˜K™—…—5 I(