DIRECTORY Ascii USING [Upper], Basics USING [Comparison], BasicTime USING [GMT, nullGMT], BTree USING [DeleteKey, Entry, EntSize, EnumerateEntries, Key, PathStk, ReadEntry, Relation, Tree, UpdateEntry, UpdateType], File USING [FP, nullFP, Volume], FS USING [Error, ErrorDesc, maxFNameLength], FSBackdoor USING [Entry, EntryPtr, EntryType, highestVersion, lowestVersion, MakeFName, TextRep, TextRP, ProduceError, Version], FSDir USING [VersionMatching], FSFileOps USING [DeleteFile, LPCreatedTime, OpenFile, VolumeDesc], FSLock USING [ActiveFile, ReleaseRecord, LockRecord, WaitForRecord], PrincOpsUtils USING [ByteBlt], Rope USING [Cat, Length, NewText, ROPE, Text]; FSDirImpl: CEDAR MONITOR IMPORTS Ascii, BTree, FS, FSBackdoor, FSFileOps, FSLock, PrincOpsUtils, Rope EXPORTS FSBackdoor, FSDir = BEGIN AppendText: UNSAFE PROC [eP: FSBackdoor.EntryPtr, text: Rope.Text] RETURNS [textRP: FSBackdoor.TextRP] = UNCHECKED BEGIN textRep: LONG POINTER TO FSBackdoor.TextRep; textRP _ LOOPHOLE[eP.size]; textRep _ @eP[textRP]; LOOPHOLE[textRep, LONG POINTER TO CARDINAL]^ _ Rope.Length[text]; -- sets textRep.length [] _ PrincOpsUtils.ByteBlt [ to: [ BASE[DESCRIPTOR[textRep]], 0, textRep.length ], from: [ BASE[DESCRIPTOR[text]], 0, textRep.length ] ]; eP.size _ eP.size+SIZE[FSBackdoor.TextRep[textRep.length]]; END; GetText: UNSAFE PROC [textRep: LONG POINTER TO FSBackdoor.TextRep, text: REF TEXT] = UNCHECKED BEGIN text.length _ textRep.length; [] _ PrincOpsUtils.ByteBlt [ to: [ BASE[DESCRIPTOR[text]], 0, textRep.length ], from: [ BASE[DESCRIPTOR[textRep]], 0, textRep.length ] ]; END; EntryNameMatches: UNSAFE PROC [name: Rope.Text, entryPtr: FSBackdoor.EntryPtr] RETURNS[BOOLEAN] = UNCHECKED BEGIN entryName: LONG POINTER TO FSBackdoor.TextRep = @entryPtr[entryPtr.nameBody]; nameBody: REF READONLY TEXT = LOOPHOLE[name]; IF entryName.length # nameBody.length THEN RETURN[FALSE]; FOR i: CARDINAL IN [ 0 .. nameBody.length ) DO IF Ascii.Upper[entryName[i]] # Ascii.Upper[nameBody[i]] THEN RETURN[FALSE]; ENDLOOP; RETURN[TRUE]; END; KeyObject: TYPE = RECORD [version: FSBackdoor.Version, nameBody: Rope.Text]; stockKey: REF KeyObject _ NIL; CheckoutKey: ENTRY PROC [v: FSBackdoor.Version, nB: Rope.Text] RETURNS [k: REF KeyObject] = BEGIN IF stockKey = NIL THEN k _ NEW [KeyObject] ELSE { k _ stockKey; stockKey _ NIL }; k^ _ KeyObject[v, nB]; END; RecycleKey: ENTRY PROC [k: REF KeyObject] = { stockKey _ k }; NoMoreVersions: PROC [prefix, nameBody: Rope.ROPE, version: FSBackdoor.Version] = BEGIN FSBackdoor.ProduceError[noMoreVersions, Rope.Cat["Next version of \"", FSBackdoor.MakeFName[nameBody, version, prefix], "\" would be too big."]]; END; Compare: PUBLIC UNSAFE PROC [key: BTree.Key, entry: BTree.Entry] RETURNS [Basics.Comparison] = UNCHECKED BEGIN keyRef: REF KeyObject = NARROW[key]; keyName: REF READONLY TEXT = LOOPHOLE[keyRef.nameBody]; entryPtr: FSBackdoor.EntryPtr = LOOPHOLE[entry]; entryName: LONG POINTER TO FSBackdoor.TextRep = @entryPtr[entryPtr.nameBody]; lenKey, lenEntry: CARDINAL; IF keyName = NIL THEN RETURN [less]; lenKey _ keyName.length; lenEntry _ entryName.length; FOR i: CARDINAL IN [ 0 .. MIN[lenKey, lenEntry] ) DO cKey: CHAR = Ascii.Upper[keyName[i]]; cEntry: CHAR = Ascii.Upper[entryName[i]]; SELECT cKey FROM < cEntry => RETURN [less]; > cEntry => RETURN [greater]; ENDCASE; ENDLOOP; SELECT lenKey FROM < lenEntry => RETURN [less]; > lenEntry => RETURN [greater]; ENDCASE => SELECT keyRef.version FROM < entryPtr.version => RETURN [less]; > entryPtr.version => RETURN [greater]; ENDCASE => RETURN [equal]; END; EntrySize: PUBLIC UNSAFE PROC [entry: BTree.Entry] RETURNS [words: BTree.EntSize] = UNCHECKED { RETURN [ LOOPHOLE[entry, FSBackdoor.EntryPtr].size ] }; TextFromTextRep: PUBLIC PROC [textRep: LONG POINTER TO FSBackdoor.TextRep] RETURNS [r: Rope.Text] = TRUSTED { r _ Rope.NewText[textRep.length]; GetText[textRep, LOOPHOLE[r]] }; EnumerateEntries: PUBLIC PROC [ vDesc: FSFileOps.VolumeDesc, start: Rope.Text, versions: FSDir.VersionMatching, matchProc: UNSAFE PROC [entry: FSBackdoor.EntryPtr] RETURNS [accept, stop: BOOLEAN], acceptProc: PROC RETURNS [stop: BOOLEAN] ] = TRUSTED BEGIN GetAll: UNSAFE PROC [entry: BTree.Entry] RETURNS [continue: BOOLEAN] = BEGIN entryPtr: FSBackdoor.EntryPtr = LOOPHOLE[entry]; [accept, stop] _ matchProc[ entryPtr ]; IF stop THEN continue _ FALSE ELSE BEGIN IF accept THEN BEGIN -- save nameBody for restarting enumeration GetText[@entryPtr[entryPtr.nameBody], keyText]; keyRef.version _ entryPtr.version; continue _ FALSE; END ELSE continue _ TRUE; END; END; MatchEntry: UNSAFE PROC [entry: BTree.Entry] = BEGIN entryPtr: FSBackdoor.EntryPtr = LOOPHOLE[entry]; IF entryPtr = NIL THEN stop _ TRUE ELSE BEGIN GetText[@entryPtr[entryPtr.nameBody], keyText]; [accept, stop] _ matchProc[ entryPtr ]; END; END; FindL: UNSAFE PROC [entry: BTree.Entry] = BEGIN entryPtr: FSBackdoor.EntryPtr = LOOPHOLE[entry]; IF entryPtr = NIL THEN stop _ TRUE ELSE GetText[@entryPtr[entryPtr.nameBody], keyText]; END; keyText: REF TEXT = NEW [ TEXT[FS.maxFNameLength] ]; keyRef: REF KeyObject = CheckoutKey[FSBackdoor.lowestVersion, start]; stop, accept: BOOLEAN; SELECT versions FROM all => BEGIN UNTIL BTree.EnumerateEntries[tree: vDesc.tree, relation: greater, key: keyRef, Proc: GetAll] DO IF stop OR (accept AND acceptProc[]) THEN EXIT; keyRef.nameBody _ LOOPHOLE[keyText]; -- key from last iteration ENDLOOP; END; bangLOnly => BEGIN BTree.ReadEntry[tree: vDesc.tree, relation: greater, key: keyRef, Proc: MatchEntry]; keyRef^ _ [FSBackdoor.highestVersion, LOOPHOLE[keyText]]; -- for later interations UNTIL stop DO IF accept AND acceptProc[] THEN EXIT; BTree.ReadEntry[tree: vDesc.tree, relation: greater, key: keyRef, Proc: MatchEntry]; ENDLOOP; END; bangHOnly => BEGIN stop _ FALSE; BTree.ReadEntry[tree: vDesc.tree, relation: greater, key: keyRef, Proc: FindL]; keyRef^ _ [FSBackdoor.highestVersion, LOOPHOLE[keyText]]; -- for later interations UNTIL stop DO BTree.ReadEntry[tree: vDesc.tree, relation: lessEqual, key: keyRef, Proc: MatchEntry]; IF accept AND acceptProc[] THEN EXIT; BTree.ReadEntry[tree: vDesc.tree, relation: greater, key: keyRef, Proc: FindL]; ENDLOOP; END; ENDCASE => ERROR; RecycleKey[keyRef]; END; AcquireOldFName: PUBLIC PROC [vDesc: FSFileOps.VolumeDesc, nameBody: Rope.Text, wantedVersion: FSBackdoor.Version, wantedTime: BasicTime.GMT] RETURNS [type: FSBackdoor.EntryType, version: FSBackdoor.Version, keep: CARDINAL, fp: File.FP, time: BasicTime.GMT, attachedTo: Rope.Text, a: FSLock.ActiveFile] = TRUSTED BEGIN GetEntry: UNSAFE PROC [entry: BTree.Entry] = BEGIN entryPtr: FSBackdoor.EntryPtr = LOOPHOLE[entry]; type _ notFound; IF entry = NIL OR NOT EntryNameMatches[nameBody, entryPtr] THEN {outcome _ notFound; RETURN}; version _ entryPtr.version; IF wantedTime # BasicTime.nullGMT THEN BEGIN time _ WITH e: entryPtr^ SELECT FROM local => FSFileOps.LPCreatedTime[vDesc.vol, e.fp], attached => e.created, cached => FSFileOps.LPCreatedTime[vDesc.vol, e.fp], ENDCASE => ERROR; IF wantedTime # time THEN {outcome _ wrongTime; RETURN}; END; type _ entryPtr.type; [a, lockSet] _ FSLock.LockRecord[vDesc.prefix, nameBody, version]; IF lockSet THEN WITH e: entryPtr^ SELECT FROM local => BEGIN keep _ e.keep; fp _ e.fp; END; attached => BEGIN keep _ e.keep; time _ e.created; attachedTo _TextFromTextRep[@entryPtr[e.attachedTo]]; END; cached => BEGIN time _ e.used; fp _ e.fp; END; ENDCASE => ERROR; outcome _ seeLockSet; END; FindAndLock: PROC RETURNS [BOOLEAN] = TRUSTED BEGIN DO errorDesc: FS.ErrorDesc; -- this GetEntry checks the created-time from a leader page sometimes BTree.ReadEntry[tree: vDesc.tree, relation: search, key: keyRef, Proc: GetEntry ! FS.Error => {errorDesc _ error; CONTINUE} ]; IF errorDesc.group # ok THEN ERROR FS.Error[errorDesc]; SELECT outcome FROM notFound => IF search = equal AND wantedTime # BasicTime.nullGMT THEN RETURN[TRUE] -- useful to keep looking ELSE RETURN[FALSE]; wrongTime => RETURN[TRUE]; seeLockSet => IF lockSet THEN RETURN[FALSE] ELSE FSLock.WaitForRecord[a]; ENDCASE => ERROR; ENDLOOP; END; outcome: {notFound, wrongTime, seeLockSet}; lockSet, keepLooking: BOOLEAN; keyRef: REF KeyObject = CheckoutKey [wantedVersion, nameBody]; search: BTree.Relation _ SELECT wantedVersion FROM FSBackdoor.highestVersion => lessEqual, FSBackdoor.lowestVersion => greater, ENDCASE => equal; IF keepLooking _ FindAndLock[] THEN BEGIN -- search for version with desired createdTime IF search = lessEqual THEN keyRef.version _ [version - 1] -- already found highest version ELSE { keyRef.version _ FSBackdoor.highestVersion; search _ lessEqual }; WHILE keepLooking DO keepLooking _ FindAndLock[]; keyRef.version _ [version - 1]; ENDLOOP; END; RecycleKey[keyRef]; END; AcquireNextLName: PUBLIC PROC [vDesc: FSFileOps.VolumeDesc, nameBody: Rope.Text, newKeep: CARDINAL] RETURNS [a: FSLock.ActiveFile, keep: CARDINAL, fp: File.FP] = TRUSTED BEGIN GetEntry: UNSAFE PROC [entry: BTree.Entry] = BEGIN entryPtr: FSBackdoor.EntryPtr = LOOPHOLE[entry]; type _ notFound; IF entry # NIL AND EntryNameMatches[nameBody, entryPtr] THEN BEGIN keyRef.version _ entryPtr.version; type _ entryPtr.type; WITH e: entryPtr^ SELECT FROM local => BEGIN IF keep = 0 THEN keep _ e.keep; entryFP _ e.fp; END; attached => BEGIN IF keep = 0 THEN keep _ e.keep; END; ENDCASE => ERROR; END ELSE keyRef.version _ FSBackdoor.lowestVersion; IF count = 0 THEN BEGIN IF keyRef.version + 1 = FSBackdoor.highestVersion THEN { noMoreVersions _ TRUE; RETURN }; -- version too big [a, nextLockSet] _ FSLock.LockRecord[vDesc.prefix, nameBody, [keyRef.version + 1]]; IF NOT nextLockSet OR keyRef.version < keep THEN RETURN; END; IF type # notFound THEN BEGIN count _ count + 1; IF count < keep THEN entryLockSet _ FALSE ELSE [entryA, entryLockSet] _ FSLock.LockRecord[vDesc.prefix, nameBody, keyRef.version]; END; END; keyRef: REF KeyObject = CheckoutKey[ , nameBody]; entryFP: File.FP; entryA: FSLock.ActiveFile; entryLockSet, nextLockSet: BOOLEAN; noMoreVersions: BOOLEAN _ FALSE; type: FSBackdoor.EntryType; count: CARDINAL _ 0; fp _ File.nullFP; keep _ newKeep; -- will get reset if =0 and any entries are found DO -- get a name lock on !N keyRef.version _ FSBackdoor.highestVersion; BTree.ReadEntry[tree: vDesc.tree, relation: less, key: keyRef, Proc: GetEntry]; IF noMoreVersions THEN NoMoreVersions[vDesc.prefix, nameBody, FSBackdoor.highestVersion]; IF nextLockSet THEN EXIT ELSE FSLock.WaitForRecord[a]; ENDLOOP; IF keyRef.version >= keep THEN UNTIL type = notFound DO -- do keep processing IF entryLockSet THEN BEGIN IF entryA.fileLock = none THEN BEGIN -- can delete or reuse this file IF NOT BTree.DeleteKey[tree: vDesc.tree, key: keyRef] THEN ERROR; IF type = local THEN BEGIN IF fp = File.nullFP THEN fp _ entryFP ELSE FSFileOps.DeleteFile[ FSFileOps.OpenFile[vDesc.vol, entryFP] ]; END; END; FSLock.ReleaseRecord[entryA]; END; BTree.ReadEntry[tree: vDesc.tree, relation: less, key: keyRef, Proc: GetEntry]; ENDLOOP; RecycleKey[keyRef]; END; DoKeeps: PUBLIC PROC [vDesc: FSFileOps.VolumeDesc, nameBody: Rope.Text] = TRUSTED BEGIN GetEntry: UNSAFE PROC [entry: BTree.Entry] = BEGIN entryPtr: FSBackdoor.EntryPtr = LOOPHOLE[entry]; IF entry = NIL OR NOT EntryNameMatches[nameBody, entryPtr] THEN {type _ notFound; RETURN}; keyRef.version _ entryPtr.version; type _ entryPtr.type; WITH e: entryPtr^ SELECT FROM local => { IF count = 0 THEN keep _ e.keep; fp _ e.fp }; attached => { IF count = 0 THEN keep _ e.keep }; ENDCASE => ERROR; count _ count + 1; IF count <= keep THEN lockSet _ FALSE ELSE [a, lockSet] _ FSLock.LockRecord[vDesc.prefix, nameBody, keyRef.version]; END; keyRef: REF KeyObject = CheckoutKey[FSBackdoor.highestVersion, nameBody]; count: CARDINAL _ 0; fp: File.FP; a: FSLock.ActiveFile; lockSet: BOOLEAN; type: FSBackdoor.EntryType; keep: CARDINAL; BTree.ReadEntry[tree: vDesc.tree, relation: less, key: keyRef, Proc: GetEntry]; IF type # notFound AND keyRef.version > keep THEN DO -- do keep processing BTree.ReadEntry[tree: vDesc.tree, relation: less, key: keyRef, Proc: GetEntry]; IF type = notFound THEN EXIT; IF lockSet THEN BEGIN IF a.fileLock = none THEN BEGIN -- can delete this file IF NOT BTree.DeleteKey[tree: vDesc.tree, key: keyRef] THEN ERROR; IF type = local THEN FSFileOps.DeleteFile[ FSFileOps.OpenFile[vDesc.vol, fp] ]; END; FSLock.ReleaseRecord[a]; END; ENDLOOP; RecycleKey[keyRef]; END; AcquireOldGName: PUBLIC PROC [vDesc: FSFileOps.VolumeDesc, nameBody: Rope.Text, wantedVersion: FSBackdoor.Version] RETURNS [type: FSBackdoor.EntryType, time: BasicTime.GMT, fp: File.FP, a: FSLock.ActiveFile] = TRUSTED BEGIN GetEntry: UNSAFE PROC [entry: BTree.Entry] = BEGIN entryPtr: FSBackdoor.EntryPtr = LOOPHOLE[entry]; lockSet: BOOLEAN; IF entry = NIL THEN type _ notFound ELSE BEGIN type _ entryPtr.type; IF type = cached THEN BEGIN [a, lockSet] _ FSLock.LockRecord[vDesc.prefix, nameBody, wantedVersion]; IF lockSet THEN BEGIN WITH e: entryPtr^ SELECT FROM cached => { time _ e.used; fp _ e.fp }; ENDCASE => ERROR; END ELSE a _ NIL; END; END; END; keyRef: REF KeyObject = CheckoutKey[wantedVersion, nameBody]; BTree.ReadEntry[tree: vDesc.tree, key: keyRef, Proc: GetEntry]; RecycleKey[keyRef]; END; AcquireOldOrNewGName: PUBLIC PROC [vDesc: FSFileOps.VolumeDesc, nameBody: Rope.Text, wantedVersion: FSBackdoor.Version] RETURNS [fp: File.FP, a: FSLock.ActiveFile] = TRUSTED BEGIN GetEntry: UNSAFE PROC [entry: BTree.Entry] = BEGIN entryPtr: FSBackdoor.EntryPtr = LOOPHOLE[entry]; type _ notFound; fp _ File.nullFP; IF entry # NIL THEN BEGIN type _ entryPtr.type; WITH e: entryPtr^ SELECT FROM cached => fp _ e.fp; ENDCASE => ERROR; END; [a, lockSet] _ FSLock.LockRecord[vDesc.prefix, nameBody, wantedVersion] END; keyRef: REF KeyObject = CheckoutKey[wantedVersion, nameBody]; type: FSBackdoor.EntryType; lockSet: BOOLEAN; DO BTree.ReadEntry[tree: vDesc.tree, key: keyRef, Proc: GetEntry]; IF NOT lockSet THEN FSLock.WaitForRecord[a] ELSE EXIT; ENDLOOP; RecycleKey[keyRef]; END; UpdateLocalEntry: PUBLIC PROC [vDesc: FSFileOps.VolumeDesc, nameBody: Rope.Text, version: FSBackdoor.Version, keep: CARDINAL, fp: File.FP, update: BTree.UpdateType] = TRUSTED BEGIN WriteEntry: UNSAFE PROCEDURE [entry: BTree.Entry] = BEGIN localPtr: LONG POINTER TO FSBackdoor.Entry.local = LOOPHOLE[entry]; localPtr^ _ [ SIZE[FSBackdoor.Entry.local], version, , local [keep, fp] ]; localPtr.nameBody _ AppendText[localPtr, nameBody]; END; keyRef: REF KeyObject = CheckoutKey[version, nameBody]; BTree.UpdateEntry[tree: vDesc.tree, key: keyRef, words: SIZE[FSBackdoor.Entry.local] + SIZE[FSBackdoor.TextRep[Rope.Length[nameBody]]], Proc: WriteEntry, updateType: update]; RecycleKey[keyRef]; END; UpdateCachedEntry: PUBLIC PROC [vDesc: FSFileOps.VolumeDesc, nameBody: Rope.Text, version: FSBackdoor.Version, used: BasicTime.GMT, fp: File.FP, update: BTree.UpdateType] = TRUSTED BEGIN WriteEntry: UNSAFE PROCEDURE [entry: BTree.Entry] = BEGIN cachedPtr: LONG POINTER TO FSBackdoor.Entry.cached = LOOPHOLE[entry]; cachedPtr^ _ [ SIZE[FSBackdoor.Entry.cached], version, , cached [used, fp] ]; cachedPtr.nameBody _ AppendText[cachedPtr, nameBody]; END; keyRef: REF KeyObject = CheckoutKey[version, nameBody]; BTree.UpdateEntry[tree: vDesc.tree, key: keyRef, words: SIZE[FSBackdoor.Entry.cached] + SIZE[FSBackdoor.TextRep[Rope.Length[nameBody]]], Proc: WriteEntry, updateType: update]; RecycleKey[keyRef]; END; UpdateAttachedEntry: PUBLIC PROC [vDesc: FSFileOps.VolumeDesc, nameBody: Rope.Text, version: FSBackdoor.Version, keep: CARDINAL, created: BasicTime.GMT, attachedTo: Rope.Text, update: BTree.UpdateType] = TRUSTED BEGIN WriteEntry: UNSAFE PROCEDURE [entry: BTree.Entry] = BEGIN attachedPtr: LONG POINTER TO FSBackdoor.Entry.attached = LOOPHOLE[entry]; attachedPtr^ _ [ SIZE[FSBackdoor.Entry.attached], version, , attached [keep, created, ] ]; attachedPtr.nameBody _ AppendText[attachedPtr, nameBody]; attachedPtr.attachedTo _ AppendText[attachedPtr, attachedTo]; END; keyRef: REF KeyObject = CheckoutKey[version, nameBody]; BTree.UpdateEntry[tree: vDesc.tree, key: keyRef, words: SIZE[FSBackdoor.Entry.attached] + SIZE[FSBackdoor.TextRep[Rope.Length[nameBody]]] + SIZE[FSBackdoor.TextRep[Rope.Length[attachedTo]]], Proc: WriteEntry, updateType: update]; RecycleKey[keyRef]; END; DeleteEntry: PUBLIC PROC [vDesc: FSFileOps.VolumeDesc, nameBody: Rope.Text, version: FSBackdoor.Version] = BEGIN keyRef: REF KeyObject = CheckoutKey[version, nameBody]; [] _ BTree.DeleteKey[tree: vDesc.tree, key: keyRef]; RecycleKey[keyRef]; END; END. @FSDirImpl.mesa Last Edited by: Schroeder, November 15, 1983 1:25 pm Last Edited by: Levin, September 22, 1983 12:54 pm Directory Entry Representation BTree key management Error generation BTree access primitives, exported to FSDir Exported to FSBackdoor Directory/cache manipulation procedures, exported to FSDir สT– "cedar" style˜Icode2šœ™K™4K™2code1šฯk ˜ Lšœœ ˜Lšœœ˜Lšœ œœ ˜Lšœœq˜|Lšœœœ˜ Lšœœ$˜,Lšœ œp˜€Lšœœ˜Lšœ œ3˜BLšœœ8˜DLšœœ ˜Lšœœœ˜.—šœ œ˜Lšœœ4˜LLšœ˜Lšœ˜—™šฯn œ œ,œ ˜rKš˜Kšœ œœœ˜,Kšœ œ ˜K˜Kš œ œœœœฯc˜Xšœ˜Kšœœ œ ˜5Kšœœ œ˜3Kšœ˜—Kšœœ%˜;Kšœ˜—šžœ œ œœœœœ ˜^Kš˜Kšœ˜šœ˜Kšœœ œ˜2Kšœœ œ˜6Kšœ˜—Kšœ˜—š žœ œ2œœ ˜kKš˜Kšœ œœœ3˜MKšœ œ œœ˜-Kšœ$œœœ˜9šœœœ˜.Kšœ6œœœ˜KKšœ˜—Kšœœ˜ Kšœ˜——™Kšœ œœ4˜LKšœ œ œ˜š ž œœœ(œœ ˜[Kš˜Kš œ œœ œœ œ ˜QKšœ˜Kšœ˜—šž œœœœ ˜+Kšœ ˜——™šžœœœ ˜QKš˜šœ'˜'Kšœi˜i—Kšœ˜——™*š žœ œœ&œ ˜hKš˜Kšœœ œ˜$Kš œ œœœœ˜7Kšœ œ˜0Kšœ œœœ3˜MKšœœ˜Kšœ œœœ˜$Kšœ˜Kšœœ˜š œœœœ˜4Kšœœ˜%Kšœœ˜)šœ˜Kšœ œ˜Kšœ œ ˜Kšœ˜—Kšœ˜—šœ˜Kšœœ˜Kšœœ ˜šœ˜ šœ˜Kšœœ˜$Kšœœ ˜'Kšœœ ˜———Kšœ˜—š ž œ œœœ ˜]Kšœœœ&˜9——šœ™š žœ œ œœœœ˜kKšœ!œœ˜D——šœ:™:šžœœœ^œœœœœœœ˜๙Kš˜š žœœœœ œ˜FKš˜Kšœ œ˜0Kšœ'˜'Kšœ˜Kšœ ˜šœ˜ Kšœ˜ šœœŸ+˜6Kšœ/˜/Kšœ"˜"Kšœ œ˜Kš˜—Kšœ œ˜Kšœ˜—Kšœ˜—šž œœœ˜.Kš˜Kšœ œ˜0Kšœ ˜Kšœ˜šœ˜ Kšœ/˜/Kšœ'˜'Kšœ˜—Kšœ˜—šžœœœ˜)Kš˜Kšœ œ˜0Kšœ ˜Kšœ˜Kšœ0˜4Kšœ˜—Kš œ œœœœœ˜4Kšœœ:˜EKšœœ˜šœ ˜šœ˜ šœX˜_Kš œœ œœœ˜/Kšœœ Ÿ˜?Kšœ˜—Kšœ˜—šœ ˜KšœT˜TKšœ&œ Ÿ˜Ršœ˜ Kšœœœœ˜%KšœT˜TKšœ˜—Kšœ˜—šœ ˜Kšœœ˜ KšœO˜OKšœ&œ Ÿ˜Ršœ˜ KšœV˜VKšœœœœ˜%KšœO˜OKšœ˜—Kšœ˜—Kšœœ˜—Kšœ˜Kšœ˜—šžœœœmœœAœ œœ1˜ธKš˜šžœ œ˜,Kš˜Kšœ œ˜0K˜Kšœ œœœ%˜:Kšœœ˜"K˜Kšœ˜!šœ˜ šœœœ˜$Kšœ2˜2Kšœ˜Kšœ3˜3Kšœœ˜—Kšœœœ˜8Kšœ˜—K˜KšœB˜Bš œ œœœ˜-šœ ˜K˜K˜ Kšœ˜—šœ ˜K˜K˜K˜5Kšœ˜—šœ ˜K˜K˜ Kšœ˜—Kšœœ˜—Kšœ˜Kšœ˜—š ž œœœœ˜-Kš˜š˜Kšœ œ ŸE˜^šœO˜OKšœœœ˜.—Kšœœœœ˜7šœ ˜šœ ˜ Kšœœ˜4KšœœœŸ˜+Kšœœœ˜—šœ ˜ Kšœœ˜ —šœ ˜ Kšœ˜ Kšœœœ˜Kšœ˜—šœ˜ Kšœ˜——Kšœ˜—Kšœ˜—Kšœ+˜+Kšœœ˜Kšœ œ3˜?šœœ˜2Kšœ'˜'Kšœ$˜$Kšœ ˜—Kšœ˜šœœŸ.˜9Kšœ˜Kšœ Ÿ ˜DKšœD˜Hšœ ˜Kšœ˜Kšœ˜Kšœ˜—Kšœ˜—Kšœ˜Kšœ˜—šžœœœ=œœœ œ˜ฉKš˜šžœœœ˜,Kš˜Kšœ œ˜0Kšœ˜Kšœ œœ&˜8šœ˜ Kšœ"˜"Kšœ˜šœœ˜šœ ˜Kšœ œ˜Kšœ˜Kšœ˜—šœ ˜Kšœ œ˜Kšœ˜—Kšœœ˜—Kš˜—Kšœ+˜/Kšœ ˜ šœ˜ Kšœ/˜1KšœœœŸ˜:KšœS˜SKšœœ œ˜+Kšœœ˜ Kšœ˜—Kšœ˜šœ˜ Kšœ˜Kšœ ˜Kšœ˜KšœT˜XKšœ˜—Kšœ˜—Kšœœ&˜1Kšœœ˜K˜Kšœœ˜#Kšœœœ˜ K˜Kšœœ˜K˜KšœŸ1˜AšœŸ˜K˜+KšœO˜OKšœœC˜YKšœ œœœ˜6Kšœ˜—Kšœ˜šœœœŸ˜3Kšœ ˜šœ˜ Kšœ˜šœœŸ ˜+Kšœœ0œœ˜AKšœ ˜šœ˜ Kšœ˜Kšœ ˜Kšœ@˜DKšœ˜—Kšœ˜—Kšœ˜Kšœ˜—KšœO˜OKšœ˜—K˜Kšœ˜—šžœ œ6˜QKš˜šžœœœ˜,Kš˜Kšœ œ˜0Kšœ œœ%˜:Kšœœ˜Kšœ"˜"Kšœ˜šœœ˜Kšœ œ œœ ˜8Kšœ œ œ˜0Kšœœ˜—Kšœ˜Kšœ˜Kšœ ˜KšœJ˜NKšœ˜—Kšœœ>˜IKšœœ˜Kšœ œ˜ K˜Kšœ œ˜K˜Kšœœ˜KšœO˜OKšœœ˜,šœœŸ˜KšœO˜OKšœœœ˜Kšœ˜ šœ˜ Kšœ˜šœœŸ˜"Kšœœ0œœ˜AKšœ œ;˜OKšœ˜—Kšœ˜Kšœ˜—Kšœ˜—K˜Kšœ˜—š žœ œWœ.œ œ˜ูKš˜šžœœœ˜,Kš˜Kšœ œ˜0Kšœ œ˜Kšœ ˜Kšœ˜šœ˜ K˜Kšœ˜šœ˜ KšœH˜HKšœ˜ š ˜ Kšœœ˜Kšœ'˜'Kšœ˜Kš˜—Kšœœ˜ Kšœ˜—Kšœ˜—Kšœ˜—Kšœœ2˜=Kšœ?˜?K˜Kšœ˜—š žœœœWœ œ˜ญKš˜šžœœœ˜,Kš˜Kšœ œ˜0K˜Kšœ˜Kšœ ˜šœ˜ Kšœ˜šœœ˜Kšœ˜Kšœ˜—Kšœ˜—KšœG˜GKšœ˜—Kšœœ2˜=K˜Kšœ œ˜š˜Kšœ?˜?Kš œœ œœœ˜6Kšœ˜—K˜Kšœ˜—š žœœœWœ œ˜ฎKš˜šž œœ œ˜3Kš˜Kš œ œœœœ˜CKšœœ8˜JKšœ3˜3Kšœ˜—Kšœœ,˜7Kšœ8œœS˜ฎKšœ˜Kšœ˜—š žœœœoœ˜ดKš˜šž œœ œ˜3Kš˜Kš œ œœœœ˜EKšœœ:˜MKšœ5˜5Kšœ˜—Kšœœ,˜7Kšœ8œœS˜ฏKšœ˜Kšœ˜—š žœœœWœM˜ำKš˜šž œœ œ˜3Kš˜Kš œ œœœœ˜IKšœœE˜ZKšœ9˜9Kšœ=˜=Kšœ˜—Kšœœ,˜7Kšœ8œœ.œU˜ๅKšœ˜Kšœ˜—šž œœœR˜jKš˜Kšœœ,˜7Kšœ4˜4K˜Kšœ˜——Kšœ˜—…—@<Uะ