DIRECTORY AlpineDirectory, AlpineDirectoryBTree USING [DeleteEntry, EntryHandle, FreeEntryHandle, FullPathName, GetDirectory, NextEntry, ReadEntry, WriteEntry], AlpineEnvironment, AlpFile USING [AccessFailed, Close, Create, Delete, GetSize, Handle, Open, PropertySet, ReadPages, ReadProperties, RESULTPageBuffer, WritePages, WriteProperties], AlpTransaction USING [Handle], Basics USING [LowHalf], BasicTime USING [Now], PrincOps USING [wordsPerPage], Rope USING [Cat, Equal, Find, Flatten, ROPE, Substr, Text], VM USING [AddressForPageNumber, Allocate, Free, Interval]; AlpineDirectoryImpl: CEDAR PROGRAM IMPORTS AlpineDirectoryBTree, AlpFile, Basics, BasicTime, Rope, VM EXPORTS AlpineDirectory = BEGIN OPEN AD: AlpineDirectory, ADBTree: AlpineDirectoryBTree, AE: AlpineEnvironment; CreateFile: PUBLIC PROC [trans: AlpTransaction.Handle, name: Rope.ROPE, initialSize: AE.PageCount _ 10, recoveryOption: AE.RecoveryOption, referencePattern: AE.ReferencePattern] RETURNS [openFileID: AlpFile.Handle, fullPathName: Rope.ROPE] = BEGIN entry: ADBTree.EntryHandle; [entry, openFileID] _ CreateEntry[trans: trans, name: name, saveOne: TRUE]; fullPathName _ entry.name; IF openFileID = NIL THEN { fileRef: REF AE.UniversalFile; [openFileID, fileRef] _ AlpFile.Create[ trans, entry.volGroupID, entry.owner, initialSize, recoveryOption, referencePattern]; entry.file _ fileRef^}; AlpFile.WriteProperties[openFileID, LIST[ [byteLength[0]], [createTime[BasicTime.Now[]]], [owner[entry.owner]], -- [keep[entry.keep]], [stringName[StringName[entry.name]]], -- volume & owner come from other properties [highWaterMark[0]]]]; ADBTree.WriteEntry[entry]; ADBTree.FreeEntryHandle[entry]; END; DeleteFile: PUBLIC PROC [trans: AlpTransaction.Handle, name: Rope.ROPE] RETURNS[fullPathName: Rope.ROPE] = BEGIN entry: ADBTree.EntryHandle; entry _ ADBTree.ReadEntry[trans, name, AD.lowest]; IF ~entry.directory THEN { IF ~entry.found THEN Error[entryNotFound]; ADBTree.DeleteEntry[entry]; fullPathName _ entry.name; IF entry.file # AE.nullUniversalFile THEN DeleteFileFromID[trans, entry.file]}; ADBTree.FreeEntryHandle[entry]; END; OpenFile: PUBLIC PROC [trans: AlpTransaction.Handle, name: Rope.ROPE, updateCreateTime: BOOL, access: AE.AccessRights, lock: AE.LockOption, recoveryOption: AE.RecoveryOption, referencePattern: AE.ReferencePattern, createOptions: AD.CreateOptions _ none] RETURNS [openFileID: AlpFile.Handle, createdFile: BOOL _ FALSE, fullPathName: Rope.ROPE] = BEGIN fileID: AE.FileID; entry: ADBTree.EntryHandle; entry _ ADBTree.ReadEntry[trans, name]; IF ~entry.found THEN { IF createOptions = oldOnly THEN Error[entryNotFound]; [openFileID, fullPathName] _ CreateFile[ trans: trans, name: name, recoveryOption: recoveryOption, referencePattern: referencePattern ]; createdFile _ TRUE; RETURN; }; IF entry.link # NIL THEN entry.file _ FollowLinks[entry].file; IF entry.file = AE.nullUniversalFile THEN { IF createOptions = oldOnly THEN Error[entryNotFound]; [openFileID, fullPathName] _ CreateFile[ trans: trans, name: entry.name, recoveryOption: recoveryOption, referencePattern: referencePattern ]; createdFile _ TRUE; RETURN; }; fullPathName _ entry.name; [openFileID, fileID] _ AlpFile.Open[trans, entry.file, access, lock, recoveryOption, referencePattern]; IF access = readWrite AND updateCreateTime THEN AlpFile.WriteProperties[openFileID, LIST[[createTime[BasicTime.Now[]]]]]; IF fileID # entry.file.fileID AND entry.link = NIL THEN { entry.file.fileID _ fileID; ADBTree.WriteEntry[entry]}; ADBTree.FreeEntryHandle[entry]; END; Copy: PUBLIC PROC [trans: AlpTransaction.Handle, from, to: Rope.ROPE] RETURNS[fromName, toName: Rope.ROPE] = BEGIN pages: AE.PageCount; entry: ADBTree.EntryHandle; bufferSize: CARDINAL = 10; propertySet: AlpFile.PropertySet; properties: LIST OF AE.PropertyValuePair; openFromFile, openToFile: AlpFile.Handle; entry _ ADBTree.ReadEntry[trans, from]; IF ~entry.found THEN Error[entryNotFound]; fromName _ entry.name; IF entry.link # NIL THEN entry.file _ FollowLinks[entry].file; IF entry.file = AE.nullUniversalFile THEN Error[entryNotFound]; [openFromFile, entry.file.fileID] _ AlpFile.Open[trans, entry.file]; ADBTree.FreeEntryHandle[entry]; pages _ AlpFile.GetSize[openFromFile]; [openToFile, toName] _ CreateFile[trans, to, pages, log, sequential]; propertySet _ ALL[FALSE]; propertySet[byteLength] _ TRUE; propertySet[createTime] _ TRUE; properties _ AlpFile.ReadProperties[openFromFile, propertySet]; AlpFile.WriteProperties[openToFile, properties]; TRUSTED { IF pages # 0 THEN { wordsPerPage: CARDINAL = PrincOps.wordsPerPage; interval: VM.Interval _ VM.Allocate[bufferSize]; address: LONG POINTER _ VM.AddressForPageNumber[interval.page]; FOR i: INT IN [0..pages/bufferSize] DO ENABLE UNWIND => VM.Free[interval]; count: CARDINAL _ Basics.LowHalf[MIN[bufferSize, pages - i*bufferSize]]; buffer: AlpFile.RESULTPageBuffer _ DESCRIPTOR[address, count*wordsPerPage]; pageRun: AE.PageRun _ [i*bufferSize, count]; AlpFile.ReadPages[openFromFile, pageRun, buffer]; AlpFile.WritePages[openToFile, pageRun, buffer]; ENDLOOP; VM.Free[interval]; }; }; AlpFile.Close[openFromFile]; AlpFile.Close[openToFile]; END; Rename: PUBLIC PROC [trans: AlpTransaction.Handle, old, new: Rope.ROPE] RETURNS[oldName, newName: Rope.ROPE] = BEGIN version: AD.Version; file: AE.UniversalFile; openFileID: AlpFile.Handle; propertySet: AlpFile.PropertySet; props: LIST OF AE.PropertyValuePair; oldEntry, newEntry: ADBTree.EntryHandle; oldEntry _ ADBTree.ReadEntry[trans, old]; IF ~oldEntry.found THEN Error[entryNotFound]; IF oldEntry.link # NIL THEN { -- replace the link ADBTree.FreeEntryHandle[oldEntry]; [oldName, newName] _ CreateLink[trans, old, new]; RETURN}; oldName _ oldEntry.name; file _ oldEntry.file; ADBTree.DeleteEntry[oldEntry]; ADBTree.FreeEntryHandle[oldEntry]; newEntry _ CreateEntry[trans, new].entry; newName _ newEntry.name; newEntry.file _ file; [openFileID, newEntry.file.fileID] _ AlpFile.Open[trans, newEntry.file, readWrite]; AlpFile.WriteProperties[openFileID, LIST[ [owner[newEntry.owner]], [stringName[StringName[newEntry.name]]]]]; propertySet _ ALL[FALSE]; propertySet[version] _ TRUE; props _ AlpFile.ReadProperties[openFileID, propertySet]; TRUSTED { WITH p: props.first SELECT FROM version => version _ p.version; ENDCASE => ERROR }; AlpFile.Close[openFileID]; ADBTree.WriteEntry[newEntry]; ADBTree.FreeEntryHandle[newEntry]; END; SetKeep: PUBLIC PROC [trans: AlpTransaction.Handle, fileName: Rope.ROPE, keep: CARDINAL] RETURNS[fullPathName: Rope.ROPE] = BEGIN entry: ADBTree.EntryHandle; entry _ ADBTree.ReadEntry[trans, fileName, AD.all, AD.highest]; IF ~entry.found THEN Error[entryNotFound]; -- should this be an error? fullPathName _ entry.name; IF keep = keepDisabled THEN keep _ 0; -- must have meant keep all. entry.keep _ keep; ADBTree.WriteEntry[entry]; IF entry.file # AE.nullUniversalFile THEN { -- set the keep property of the file openFileID: AlpFile.Handle _ AlpFile.Open[trans, entry.file, readWrite].handle; AlpFile.Close[openFileID]}; [] _ RemoveExtraVersions[entry, keep]; ADBTree.FreeEntryHandle[entry]; END; DisableKeep: PUBLIC PROC [trans: AlpTransaction.Handle, fileName: Rope.ROPE] RETURNS[fullPathName: Rope.ROPE] = BEGIN entry: ADBTree.EntryHandle; entry _ ADBTree.ReadEntry[trans, fileName]; IF ~entry.found THEN Error[entryNotFound]; -- should this be an error? fullPathName _ entry.name; entry.keep _ keepDisabled; ADBTree.WriteEntry[entry]; IF entry.file # AE.nullUniversalFile THEN { -- set the keep property of the file openFileID: AlpFile.Handle _ AlpFile.Open[trans, entry.file, readWrite].handle; AlpFile.Close[openFileID]}; ADBTree.FreeEntryHandle[entry]; END; keepDisabled: CARDINAL = LAST[CARDINAL]; SetDefaultKeep: PUBLIC PROC [trans: AlpTransaction.Handle, volume: Rope.ROPE, owner: AE.OwnerName, defaultKeep: CARDINAL] = BEGIN entry: ADBTree.EntryHandle; fileName: Rope.ROPE; fileName _ Rope.Cat["[", volume, "]<", owner, ">", "$$$.btree"]; entry _ ADBTree.ReadEntry[trans, fileName]; IF defaultKeep = keepDisabled THEN defaultKeep _ 0; -- must have meant keep all. entry.keep _ defaultKeep; entry.directory _ FALSE; -- avoid generating error ADBTree.WriteEntry[entry]; ADBTree.FreeEntryHandle[entry]; END; Insert: PUBLIC PROC [trans: AlpTransaction.Handle, name: Rope.ROPE, file: AE.UniversalFile, keep: CARDINAL] RETURNS[oldFile: AE.UniversalFile, fullPathName: Rope.ROPE] = BEGIN entry: ADBTree.EntryHandle; entry _ CreateEntry[trans, name].entry; fullPathName _ entry.name; IF entry.directory THEN { -- the scavenger found the directory file directory: AE.UniversalFile _ ADBTree.GetDirectory[trans, entry.volume, entry.owner]; IF file # directory THEN DeleteFileFromID[trans, file]; -- obsolete ADBTree.FreeEntryHandle[entry]; RETURN}; oldFile _ entry.file; entry.file _ file; ADBTree.WriteEntry[entry]; ADBTree.FreeEntryHandle[entry]; END; Remove: PUBLIC PROC [trans: AlpTransaction.Handle, fileName: Rope.ROPE] RETURNS[file: AE.UniversalFile, fullPathName: Rope.ROPE] = BEGIN entry: ADBTree.EntryHandle; entry _ ADBTree.ReadEntry[trans, fileName, AD.lowest]; IF ~entry.found THEN Error[entryNotFound]; fullPathName _ entry.name; file _ entry.file; IF ~entry.directory THEN ADBTree.DeleteEntry[entry]; ADBTree.FreeEntryHandle[entry]; END; Lookup: PUBLIC PROC [trans: AlpTransaction.Handle, fileName: Rope.ROPE] RETURNS[file: AE.UniversalFile, fullPathName, link: Rope.ROPE] = BEGIN entry: ADBTree.EntryHandle; entry _ ADBTree.ReadEntry[trans, fileName]; IF entry.found THEN { fullPathName _ entry.name; link _ entry.link; file _ entry.file} ELSE file _ AE.nullUniversalFile; ADBTree.FreeEntryHandle[entry]; END; Enumerate: PUBLIC PROC [trans: AlpTransaction.Handle, pattern, previousFile: Rope.ROPE, defaultVersion: AD.Version _ AD.all] RETURNS[file: AE.UniversalFile, fullPathName, link: Rope.ROPE _ NIL] = BEGIN entry: ADBTree.EntryHandle; entry _ ADBTree.NextEntry[trans, pattern, previousFile, defaultVersion]; IF entry # NIL AND entry.found THEN { fullPathName _ entry.name; link _ entry.link; file _ entry.file} ELSE file _ AE.nullUniversalFile; ADBTree.FreeEntryHandle[entry]; END; CreateLink: PUBLIC PROC [trans: AlpTransaction.Handle, name, referent: Rope.ROPE] RETURNS[fullPathName, referentName: Rope.ROPE] = BEGIN entry, linkEntry: ADBTree.EntryHandle; entry _ CreateEntry[trans, name].entry; fullPathName _ entry.name; referentName _ referent; entry.file _ AE.nullUniversalFile; entry.link _ NIL; ADBTree.WriteEntry[entry]; -- create a termination point for FollowLinks IF referent # NIL THEN { entry.link _ Rope.Flatten[referent]; linkEntry _ FollowLinks[entry]; IF linkEntry.found AND Equal[linkEntry.nameBody, entry.nameBody] THEN Error[circularLink]; ADBTree.WriteEntry[entry]}; ADBTree.FreeEntryHandle[linkEntry]; ADBTree.FreeEntryHandle[entry]; END; CreateEntry: PROC [trans: AlpTransaction.Handle, name: Rope.ROPE, saveOne: BOOLEAN _ FALSE] RETURNS[entry: ADBTree.EntryHandle, openFileID: AlpFile.Handle] = BEGIN entry _ ADBTree.ReadEntry[trans, name]; IF ~entry.found THEN entry.keep _ GetDefaultKeep[entry]; IF entry.desiredVersion = AD.highest THEN { entry.link _ NIL; entry.found _ FALSE; entry.file _ AE.nullUniversalFile; entry.version _ entry.version + 1; entry.name _ ADBTree.FullPathName[entry]; IF entry.keep # 0 THEN openFileID _ RemoveExtraVersions[entry, entry.keep, saveOne]}; END; FollowLinks: PROC [entry: ADBTree.EntryHandle] RETURNS[linkEntry: ADBTree.EntryHandle] = BEGIN linkEntry _ ADBTree.ReadEntry[entry.trans, entry.link]; WHILE linkEntry.link # NIL DO entry _ ADBTree.ReadEntry[linkEntry.trans, linkEntry.link]; ADBTree.FreeEntryHandle[linkEntry]; linkEntry _ entry; ENDLOOP; END; GetDefaultKeep: PROC [entry: ADBTree.EntryHandle] RETURNS[newKeep: CARDINAL] = BEGIN btreeEntry: ADBTree.EntryHandle; fileName: Rope.ROPE; fileName _ Rope.Cat["[", entry.volume, "]<", entry.owner, ">", "$$$.btree"]; btreeEntry _ ADBTree.ReadEntry[entry.trans, fileName]; newKeep _ btreeEntry.keep; ADBTree.FreeEntryHandle[btreeEntry]; END; RemoveExtraVersions: PROC [entry: ADBTree.EntryHandle, keep: CARDINAL, saveOne: BOOL _ FALSE] RETURNS[openFileID: AlpFile.Handle] = BEGIN bangIndex: INT; start, pattern: Rope.ROPE; lowestEntry: ADBTree.EntryHandle; IF keep = 0 THEN RETURN; bangIndex _ Rope.Find[entry.name, "!"]; pattern _ IF bangIndex < 0 THEN entry.name ELSE Rope.Substr[entry.name, 0, bangIndex]; WHILE TRUE DO lowestEntry _ ADBTree.NextEntry[entry.trans, pattern, start, AD.all]; IF lowestEntry = NIL OR ~lowestEntry.found THEN EXIT; -- enumeration terminated IF entry.version - lowestEntry.version < keep THEN EXIT; -- keep satisfied start _ lowestEntry.name; IF lowestEntry.keep # keepDisabled AND lowestEntry.file # AE.nullUniversalFile THEN { IF saveOne AND openFileID = NIL THEN { props: LIST OF AE.PropertyValuePair; propertySet: AlpFile.PropertySet _ ALL[FALSE]; openFileID _ AlpFile.Open[entry.trans, lowestEntry.file, readWrite ! AlpFile.AccessFailed => {ADBTree.FreeEntryHandle[lowestEntry]; LOOP}].handle; AlpFile.Delete[openFileID]; openFileID _ NIL} ELSE DeleteFileFromID[lowestEntry.trans, lowestEntry.file ! AlpFile.AccessFailed => {ADBTree.FreeEntryHandle[lowestEntry]; LOOP}]; ADBTree.DeleteEntry[lowestEntry]}; ADBTree.FreeEntryHandle[lowestEntry]; ENDLOOP; IF lowestEntry # NIL THEN ADBTree.FreeEntryHandle[lowestEntry]; END; DeleteFileFromID: PROC [trans: AlpTransaction.Handle, file: AE.UniversalFile] = INLINE BEGIN openFileID: AlpFile.Handle; openFileID _ AlpFile.Open[trans, file, readWrite].handle; AlpFile.Delete[openFileID]; END; StringName: PROC [name: Rope.ROPE] RETURNS[stringName: Rope.ROPE] = INLINE { RETURN[Rope.Substr[name, Rope.Find[name, ">"]+1]]}; Equal: PROC [s1, s2: Rope.ROPE] RETURNS[BOOL] = INLINE { RETURN[Rope.Equal[s1, s2, FALSE]]}; -- case is never significant Error: PUBLIC ERROR [type: AD.DirectoryErrors] = CODE; END. rAlpineDirectoryImpl.mesa Last Edited by: Maxwell, November 23, 1983 8:59 am Last Edited by: Hauser, January 9, 1985 11:59:47 am PST -- ************************************************************ -- Procedures that manipulate files. -- ************************************************************ Create a new entry. (If openFileID # NIL then entry.file # AE.nullUniversalFile.) Create a new file. initialize the file. [readAccess[AccessList]], [modifyAccess[AccessList]], Follow the links until we get to a file. Open the file and set the create date. Rewrite the entry if the fileID is different. Follow the links until we get to a file. Create the new file. Copy the properties. Copy the data. Get the BTree entry for `old'. Remove the old entry. Create a new entry for `new'. Fix up the keep and file name. [keep[newEntry.keep]], Fix up the version. Set the keep on the highest version of the file. AlpFile.WriteProperties[openFileID, LIST[[keep[keep]]]; Delete any extraneous files as necessary. AlpFile.WriteProperties[openFileID, LIST[[keep[keep]]]; Eventually this procedure should set the default keep in the owner data base. -- ************************************************************ -- Procedures that manipulate directories only. -- ************************************************************ entry.keep _ keep; Create a new entry in the directory. Check for circularity. Write the link. -- ************************************************************ -- Utility procedures -- ************************************************************ Follow the links as long as there are links to follow. Free every entry handle but the one passed as a parameter. propertySet: OwnerPropertySet; properties: LIST OF OwnerPropertyValuePair; propertySet _ ALL[FALSE]; propertySet.keep _ TRUE; properties _ AlpTransaction.ReadOwnerProperties[ entry.trans, entry.volGroupID, entry.owner, propertySet]; IF properties # NIL THEN WITH properties.first SELECT FROM p: keep => newKeep _ p.keep; ENDCASE; returns the carcass of one file for possible reuse Create a pattern that will enumerate all of the versions of this file. pattern _ Rope.Cat[name, "!*"]; Check whether client is allowed to modify this file. The name stored as the stringName of the file is all of the sub-directories plus the file name plus the version number. The volume group name and owner name are derived from other properties. ÊD˜Jšœ™Jšœ2™2J™7J™šÏk ˜ Jšœ˜Jšœœk˜…Jšœ˜Jšœœ•˜¢Jšœœ ˜Jšœœ ˜Jšœ œ˜Jšœ œ˜Jšœœœ˜;Jšœœ2˜:—JšÏl˜šÐblœ œ˜#Jšœ<˜CJšœ˜J˜JšœK˜O—J˜JšÏc?™?Jš $™$Jš ?™?J™šÏn œœœ˜Jšœ*œl˜šJšœ1œ˜EJšœ˜Jšœ&œ)™RJšœEœ˜KJ˜J™šœœœ˜Jšœ œ˜šœ'˜'Jšœ&˜&Jšœ0˜0—Jšœ˜—J™šœ$œ˜)Jšœ0˜0Jšœ ˜,Jšœ5™5Jšœ' ,˜SJšœ˜—Jšœ˜Jšœ˜Jšœ˜—J˜š¡ œœœ˜Jšœ*œ˜0Jšœœ˜(Jšœ˜Jšœ2˜2šœœ˜Jšœœ˜*Jšœ˜J˜Jšœ#œ&˜O—Jšœ˜Jšœ˜—J™š¡œœœ˜Jšœ*œœ¢˜èJšœ+Ïs œœ˜`Jšœ˜Jšœ˜J™(Jšœ'˜'šœœ˜Jšœ5˜5Jšœ‰˜‰Jšœ¢œ˜Jš¢˜J˜—Jšœœœ&˜>šœ#œ˜+Jšœ5˜5Jšœ˜Jšœ¢œ˜Jš¢œ˜J˜—Jšœ˜J™&šœ7˜7Jšœ0˜0—šœœœ%˜TJšœ!˜%—J™-šœœœœ˜9Jšœ˜Jšœ˜—Jšœ˜Jšœ˜—J˜š¡œœ˜Jšœ.œ˜3Jšœœ˜,Jšœ˜Jšœ˜Jšœ œ˜Jšœ!˜!Jšœ œœ˜)J˜)J™(Jšœ'˜'Jšœœ˜*J˜Jšœœœ&˜>Jšœ#œ˜?JšœD˜DJšœ˜J™Jšœ&˜&JšœE˜EJ™Jšœœœ˜Jšœœ˜Jšœœ˜Jšœ?˜?Jšœ0˜0J™š œ œ˜Jšœœ˜/Jšœ0˜0Jšœ œœ*˜?šœœœ˜&Jšœœ˜#Jšœœœ$˜HJšœ# œ˜KJšœ,˜,Jšœ1˜1Jšœ0˜0Jšœ˜—Jšœ˜J˜—Jšœ˜Jšœ˜Jšœ˜—J™š¡œœ˜Jšœ.œ˜3Jšœœ˜,Jšœ˜Jšœ˜Jšœ˜J˜!Jšœœœ˜$Jšœ(˜(J™Jšœ)˜)Jšœœ˜-šœœœ ˜1Jšœ"˜"Jšœ1˜1Jšœ˜—J™Jšœ˜J˜Jšœ˜Jšœ"˜"J™Jšœ)˜)Jšœ˜J˜J™šœ%˜%Jšœ.˜.—šœ$œ˜)Jšœ™Jšœ˜Jšœ*˜*—J™Jšœœœ˜Jšœœ˜Jšœ8˜8Jš œœœ!œœ˜]Jšœ˜Jšœ˜Jšœ"˜"Jšœ˜—J™š¡œœ˜Jšœ.œœ˜CJšœœ˜(Jšœ˜J™0Jšœ?˜?Jšœœ ˜FJ˜Jšœœ  ˜BJ˜Jšœ˜šœ#œ $˜PJšœP˜PJšœ$œ™7Jšœ˜—J™)J˜&Jšœ˜Jšœ˜—J˜š¡ œœ˜Jšœ.œ˜3Jšœœ˜(Jšœ˜Jšœ+˜+Jšœœ ˜FJ˜Jšœ˜Jšœ˜šœ#œ $˜PJšœP˜PJšœ$œ™7Jšœ˜—Jšœ˜Jšœ˜J˜Jšœœœœ˜(—J˜š¡œœ˜Jšœ,œ$œ˜eJ™MJšœ˜Jšœœ˜J˜@Jšœ+˜+Jšœœ ˜PJšœ˜Jšœœ ˜2Jšœ˜Jšœ˜Jšœ˜—J˜J™?J™/Jš ?™?J™š¡œœ˜Jšœ*œ œ˜WJšœ/œ˜CJšœ˜Jšœ'˜'Jšœ˜šœœ )˜CJšœU˜UJšœœ   ˜CJšœ˜Jšœ˜—Jšœ˜J˜J™Jšœ˜Jšœ˜Jšœ˜—J˜š¡œœ˜Jšœ.œ˜3Jšœ,œ˜@Jšœ˜Jšœ6˜6Jšœœ˜+Jšœ˜J˜Jšœœ˜4Jšœ˜Jšœ˜—J™š¡œœ˜Jšœ.œ˜3Jšœ2œ˜FJšœ˜Jšœ+˜+šœ œ˜Jšœ˜Jšœ˜Jšœ˜—Jšœ˜!Jšœ˜Jšœ˜—J™š¡ œœ˜Jšœ;œ&˜eJšœ2œœ˜LJšœ˜JšœH˜Hšœ œœ œ˜%Jšœ˜Jšœ˜Jšœ˜—Jšœ˜!Jšœ˜Jš˜—J˜š¡ œœ˜Jšœ4œ˜9Jšœ"œ˜6Jšœ&˜&J™$Jšœ'˜'Jšœ˜Jšœ˜J™Jšœ"˜"Jšœ œ˜Jšœ -˜Hšœ œœ˜Jšœ$˜$Jšœ ˜ šœœ+˜AJšœ˜—J™Jšœ˜—Jšœ#˜#Jšœ˜Jšœ˜—J˜Jš ?™?J™Jš ?™?J™š¡ œ˜Jšœ*œ œœ˜IJšœ;˜GJšœ'˜'Jšœœ%˜9šœ#œ˜+Jšœ œ˜Jšœœ˜Jšœ#˜#J˜"Jšœ)˜)šœœ˜$Jšœ1˜1——Jšœ˜—J™š¡ œ˜Jšœ˜Jšœ#˜/J™6J™:Jšœ7˜7šœœ˜Jšœ;˜;Jšœ#˜#Jšœ˜Jšœ˜—Jšœ˜—J˜š¡œ˜Jšœ˜Jšœ œ˜"Jšœ ˜ Jšœœ˜J˜LJšœ6˜6Jšœ˜Jšœ$˜$J™Jšœ œœ™+Jšœœœ™Jšœœ™šœ0™0Jšœ9™9—š œœœœœ™:J™Jšœ™—Jšœ˜—J˜š¡œ˜Jšœ#œ œœ˜CJšœ˜+Jš 2™2Jšœ œ˜Jšœœ˜Jšœ  ˜!Jšœ œœ˜J™FJšœ'˜'Jšœ œœ œ'˜VJ™šœœ˜ JšœE˜EJš œœœœœ ˜OJšœ,œœ ˜JJšœ˜šœ!œ)œ˜Ušœ œœœ˜&Jšœœœ˜$Jšœ#œœ˜.Jšœ4™4šœB˜BJšœAœ ˜O—Jšœ)œ˜.—š¢œ5˜9JšœAœ˜H—Jšœ"˜"—Jšœ%˜%Jšœ˜—Jšœœœ&˜?Jšœ˜—J˜š¡œ˜Jšœ9œ˜EJšœ˜Jšœ9˜9Jšœ˜Jšœ˜—J˜š¡ œ˜Jšœ œ˜Jšœœœ˜)Jšœ-˜3J™À—J™š¡œœ˜ Jšœœ˜Jšœœœ˜Jšœœ ˜@—J˜JšÏbœœœœ˜6J˜Jšœ˜J˜J˜—…—6NÒ