DIRECTORY Basics USING [DIVMOD], FS USING [ComponentPositions, ComponentRopes, maxFNameLength, Position], FSBackdoor USING [highestVersion, lowestVersion, ProduceError, Version], FSFileOps USING [GetVolumeDesc, VolumeDesc], FSName USING [ParsedFName, VersionInfo], List USING [Assoc], ProcessProps USING [GetPropList], Rope USING [Cat, Concat, Fetch, Flatten, Index, Length, NewText, Replace, Substr, ROPE, Text]; FSNameImpl: CEDAR MONITOR IMPORTS Basics, FSBackdoor, FSFileOps, List, ProcessProps, Rope EXPORTS FS, FSBackdoor, FSName = BEGIN initialDefaultWDir: Rope.ROPE = "[]<>"; defaultWDir: Rope.ROPE _ initialDefaultWDir; SetDefaultWDir: PUBLIC PROC [dir: Rope.ROPE] = BEGIN Inner: ENTRY PROC = { defaultWDir _ dir }; places: Places; IF Rope.Length[dir] = 0 THEN dir _ initialDefaultWDir ELSE BEGIN [dir, places] _ ExpandAndFindPlaces[NIL, dir, directory]; IF places.serverEnd # 1 THEN DirNotLocal[dir]; IF places.dirEnd = places.serverEnd THEN NoVolumePart[dir]; dir _ Rope.Flatten[dir]; END; Inner[]; END; GetDefaultWDir: PUBLIC ENTRY PROC RETURNS [Rope.ROPE] = { RETURN [defaultWDir] }; ExpandName: PUBLIC PROC[name: Rope.ROPE, wDir: Rope.ROPE] RETURNS [fullFName: Rope.ROPE, cp: FS.ComponentPositions, dirOmitted: BOOLEAN _ FALSE] = BEGIN places: Places; fNLen: INT; [fullFName, places] _ ExpandAndFindPlaces[name, wDir, fName]; fNLen _ Rope.Length[fullFName]; cp.server _ [1, places.serverEnd-1]; IF places.dirEnd >= places.serverEnd+2 THEN cp.dir _ [places.serverEnd+2, places.dirEnd-places.serverEnd-2] ELSE { dirOmitted _ TRUE; cp.dir _ [places.serverEnd+1, 0] }; cp.subDirs _ IF places.subDirEnd > places.dirEnd THEN [places.dirEnd+1, places.subDirEnd-places.dirEnd-1] ELSE [places.dirEnd+1, 0]; cp.base _ [places.subDirEnd+1, places.rootEnd-places.subDirEnd-1]; cp.ext _ IF places.bang > places.rootEnd THEN [places.rootEnd+1, places.bang-places.rootEnd-1] ELSE [places.rootEnd, 0]; IF places.bang < fNLen THEN BEGIN [] _ ParseVersion[fullFName, places.bang+1, FALSE]; cp.ver _ [places.bang+1, fNLen-places.bang-1]; END ELSE cp.ver _ [fNLen, 0]; END; ConstructFName: PUBLIC PROC [cr: FS.ComponentRopes, omitDir: BOOLEAN] RETURNS [fName: Rope.ROPE] = BEGIN fName _ Rope.Cat[ "[", cr.server, "]" ]; IF NOT omitDir THEN fName _ Rope.Cat[ fName, "<", cr.dir, ">" ]; IF Rope.Length[cr.subDirs] > 0 THEN fName _ Rope.Cat[ fName, cr.subDirs, ">" ]; fName _ Rope.Cat[ fName, cr.base ]; IF Rope.Length[cr.ext] > 0 THEN fName _ Rope.Cat[ fName, ".", cr.ext ]; IF Rope.Length[cr.ver] > 0 THEN fName _ Rope.Cat[ fName, "!", cr.ver ]; END; MakeFName: PUBLIC PROC [nameBody: Rope.ROPE, version: FSBackdoor.Version, prefix: Rope.ROPE] RETURNS [Rope.ROPE] = BEGIN IF Rope.Fetch[nameBody, 0] = '[ THEN RETURN [ Rope.Concat [ nameBody, VersionPartFromVersion[version] ] ] ELSE BEGIN IF prefix = NIL THEN prefix _ initialDefaultWDir; RETURN [ Rope.Cat [ prefix, nameBody, VersionPartFromVersion[version] ] ]; END; END; ParseClientName: PUBLIC PROC [clientName, wDir: Rope.ROPE, defaultVersionHigh, pattern: BOOLEAN] RETURNS [pn: FSName.ParsedFName, vI: FSName.VersionInfo] = BEGIN vName: Rope.ROPE _ NIL; places: Places; [pn.fullName, places] _ ExpandAndFindPlaces[clientName, wDir, IF pattern THEN pattern ELSE fName]; IF places.bang < Rope.Length[pn.fullName] THEN [pn.version, vI] _ ParseVersion[pn.fullName, places.bang+1, pattern] ELSE BEGIN vI _ missing; pn.version _ IF defaultVersionHigh THEN FSBackdoor.highestVersion ELSE FSBackdoor.lowestVersion; END; IF IsLocal[pn.fullName] THEN BEGIN vL: CARDINAL _ places.dirEnd - places.serverEnd - 2; IF vL # 0 THEN vName _ Rope.Substr[pn.fullName, places.serverEnd + 2, vL]; pn.nameBody _ Rope.Flatten[ Rope.Substr[pn.fullName, places.dirEnd + 1, places.bang - places.dirEnd - 1] ]; END ELSE pn.nameBody _ Rope.Flatten[ Rope.Substr[pn.fullName, 0, places.bang] ]; pn.volDesc _ FSFileOps.GetVolumeDesc[vName]; END; ParseCacheName: PUBLIC PROC [volName, cacheName: Rope.ROPE, pattern: BOOLEAN] RETURNS [pn: FSName.ParsedFName, vI: FSName.VersionInfo] = BEGIN firstChar: CHARACTER; length, bangIndex: CARDINAL; nameUse: NameUse = IF pattern THEN pattern ELSE fName; pn _ [NIL, NIL, FSBackdoor.highestVersion, FSFileOps.GetVolumeDesc[volName]]; vI _ missing; IF Rope.Length[cacheName] = 0 THEN BEGIN IF pattern THEN { pn.nameBody _ "[*"; RETURN } ELSE IllegalName[cacheName, nameUse]; END; FOR i: INT IN [0 .. Rope.Length[cacheName]) DO -- determine syntax convention SELECT Rope.Fetch[cacheName, i] FROM '[, '], '<, '> => EXIT; '/ => { cacheName _ ConvertSlashName[cacheName]; EXIT }; ENDCASE; ENDLOOP; firstChar _ Rope.Fetch[cacheName, 0]; IF firstChar # '[ THEN BEGIN IF firstChar = '* THEN cacheName _ Rope.Concat["[", cacheName] ELSE IllegalName[cacheName, nameUse]; END; length _ Rope.Length[cacheName]; bangIndex _ Rope.Index[cacheName, IF length > 6 THEN length - 6 ELSE 0, "!"]; IF bangIndex # length AND NOT (bangIndex = length-2 AND Rope.Fetch[cacheName, length-1] = '*) THEN [pn.version, vI] _ ParseVersion[cacheName, bangIndex+1, pattern]; pn.nameBody _ Rope.Flatten[ Rope.Substr[cacheName, 0, bangIndex] ]; END; ParseName: PUBLIC PROC [volName, fName: Rope.ROPE] RETURNS [p: FSName.ParsedFName] = BEGIN length: CARDINAL = Rope.Length[fName]; bangIndex: CARDINAL = Rope.Index[fName, IF length > 6 THEN length - 6 ELSE 0, "!"]; IF bangIndex = length THEN p.version _ FSBackdoor.highestVersion ELSE [p.version, ] _ ParseVersion[fName, bangIndex+1, FALSE]; p.nameBody _ Rope.Flatten[ Rope.Substr[fName, 0, bangIndex] ]; p.fullName _ fName; p.volDesc _ FSFileOps.GetVolumeDesc[volName]; END; IsLocal: PUBLIC PROC [name: Rope.ROPE] RETURNS [BOOLEAN] = {RETURN[ Rope.Length[name]=0 OR Rope.Fetch[name, 0]#'[ OR Rope.Fetch[name, 1]='] ]}; ServerAndFileRopes: PUBLIC PROC [gName: Rope.ROPE] RETURNS [server, file: Rope.ROPE] = BEGIN closingBracket: CARDINAL = Rope.Index[gName, 0, "]"]; server _ Rope.Substr[gName, 1, closingBracket-1]; file _ Rope.Substr[gName, closingBracket+1]; END; BangStarFile: PUBLIC PROC [file: Rope.ROPE] RETURNS [Rope.ROPE] = BEGIN length: CARDINAL = Rope.Length[file]; bangIndex: CARDINAL = Rope.Index[file, IF length > 6 THEN length - 6 ELSE 0, "!"]; RETURN [ Rope.Replace [ file, bangIndex, length - bangIndex, "!*" ] ]; END; BangVersionFile: PUBLIC PROC [file: Rope.ROPE, version: FSBackdoor.Version] RETURNS [Rope.ROPE] = BEGIN length: CARDINAL = Rope.Length[file]; bangIndex: CARDINAL = Rope.Index[file, IF length > 6 THEN length - 6 ELSE 0, "!"]; RETURN [ Rope.Replace [ file, bangIndex, length - bangIndex, VersionPartFromVersion[version] ] ]; END; VersionFromRope: PUBLIC PROC [r: Rope.ROPE] RETURNS [v: FSBackdoor.Version] = BEGIN IF Rope.Length[r] = 0 THEN v _ FSBackdoor.highestVersion ELSE [v, ] _ ParseVersion[r, 0, FALSE]; END; VersionPartFromVersion: PUBLIC PROC [version: FSBackdoor.Version] RETURNS [r: Rope.Text] = BEGIN Decimate: PROC [num: CARDINAL] = BEGIN q, r: CARDINAL; [q, r] _ Basics.DIVMOD[num, 10]; IF q # 0 THEN Decimate[q]; AppendChar[t, r+'0]; END; t: REF TEXT; SELECT version FROM FSBackdoor.lowestVersion, FSBackdoor.highestVersion => r _ NIL; ENDCASE => TRUSTED BEGIN r _ Rope.NewText[6]; t _ LOOPHOLE[r]; t[0] _ '!; t.length _ 1; Decimate[version]; END; END; NameUse: TYPE = {fName, pattern, directory}; Places: TYPE = RECORD[serverEnd, dirEnd, subDirEnd, bang, rootEnd, firstStar: CARDINAL _ 0]; ExpandAndFindPlaces: PROC [name, wDir: Rope.ROPE, nameUse: NameUse] RETURNS [fN: Rope.ROPE, p: Places] = BEGIN i: INT; leftPoint: BOOLEAN _ FALSE; fNLen: CARDINAL _ Rope.Length[name]; fN _ name; IF fNLen # 0 THEN SELECT Rope.Fetch[fN, 0] FROM '[ => NULL; '/ => { fN _ ConvertSlashName[fN]; fNLen _ Rope.Length[fN] }; ENDCASE => BEGIN -- need a working directory IF Rope.Length[wDir] # 0 THEN wDir _ ConvertWDir[wDir] -- use working directory argument ELSE BEGIN wDir _ NARROW [ List.Assoc[key: $WorkingDirectory, aList: ProcessProps.GetPropList[]] ]; IF Rope.Length[wDir] # 0 THEN wDir _ ConvertWDir[wDir] -- use process working directory ELSE wDir _ GetDefaultWDir[]; -- use default working directory END; FOR i IN [1 .. fNLen) DO -- convert the name part if necessary SELECT Rope.Fetch[fN, i] FROM '], '<, '>, '! => NULL; '/ => fN _ ConvertSlashName[fN]; ENDCASE => LOOP; EXIT; ENDLOOP; fN _ Rope.Cat[wDir, fN]; fNLen _ Rope.Length[fN]; END; -- of need a working directory i _ 0; DO i _ i + 1; IF i >= fNLen THEN EXIT; SELECT Rope.Fetch[fN, i] FROM IN ['a .. 'z], IN ['A .. 'Z], IN ['0 .. '9], '$, '-, '+ => NULL; '] => IF p.serverEnd = 0 THEN p.subDirEnd _ p.dirEnd _ p.serverEnd _ i ELSE IllegalName[fN, nameUse]; -- this is the second '] '< => IF p.serverEnd = i - 1 THEN leftPoint _ TRUE ELSE IllegalName[fN, nameUse]; -- '< does not immediately follow '] '> => IF leftPoint AND ( i > p.subDirEnd + 1 ) THEN BEGIN IF p.dirEnd = p.serverEnd THEN p.dirEnd _ i; -- end of directory part p.subDirEnd _ i; -- end of subdirectory part END ELSE IllegalName[fN, nameUse]; -- '> in the wrong place '. => p.rootEnd _ i; '! => {p.bang _ i; EXIT}; '# => IF p.serverEnd # 0 THEN BEGIN -- not in server part IF p.serverEnd = 1 AND leftPoint AND i = 3 THEN DO -- LName with a max 20 digit hex number as a volume id i _ i + 1; IF i >= fNLen THEN EXIT; IF i > 23 THEN IllegalName[fN, nameUse]; SELECT Rope.Fetch[fN, i] FROM IN ['0 .. '9], IN ['A .. 'H] => NULL; ENDCASE => IllegalName[fN, nameUse]; ENDLOOP ELSE IllegalName[fN, nameUse]; -- sharpSign in the wrong place END; '* => IF nameUse = pattern THEN { IF p.firstStar = 0 THEN p.firstStar _ i } ELSE NoPatterns[fN]; -- star and we're not parsing a pattern ENDCASE => IllegalCharacter[fN]; -- illegal character ENDLOOP; IF p.bang = 0 THEN p.bang _ fNLen; IF p.bang > FS.maxFNameLength - 6 THEN TooLong[fN]; IF p.firstStar = 0 THEN BEGIN -- not a pattern IF nameUse # directory AND p.subDirEnd+1 >= p.bang THEN IllegalName[fN, nameUse]; -- no simple name END ELSE BEGIN -- have a pattern IF (p.serverEnd = 1 AND p.firstStar < p.dirEnd) OR (p.firstStar < p.serverEnd) THEN IllegalName[fN, nameUse]; -- first star is in volume part of LName or in server part END; IF p.rootEnd <= p.subDirEnd THEN p.rootEnd _ p.bang; END; ParseVersion: PROC [name: Rope.ROPE, index: CARDINAL, pattern: BOOLEAN _ FALSE] RETURNS [value: FSBackdoor.Version, vI: FSName.VersionInfo] = BEGIN c: CHAR; lastIndex: CARDINAL = Rope.Length[name] - 1; IF lastIndex IN [index .. index+4] THEN SELECT c _ Rope.Fetch[name, index] FROM IN ['0 .. '9] => BEGIN num: LONG CARDINAL _ 0; DO num _ num*10 + (c - '0); IF index = lastIndex THEN BEGIN IF num NOT IN (FSBackdoor.lowestVersion .. FSBackdoor.highestVersion) THEN EXIT; value _ [num]; vI _ number; RETURN; END; index _ index + 1; c _ Rope.Fetch[name, index]; IF c NOT IN ['0 .. '9] THEN EXIT; ENDLOOP; END; 'H, 'h => IF index = lastIndex THEN { value _ FSBackdoor.highestVersion; vI _ bangH; RETURN }; 'L, 'l => IF index = lastIndex THEN { value _ FSBackdoor.lowestVersion; vI _ bangL; RETURN }; '* => IF index = lastIndex AND pattern THEN { value _ FSBackdoor.highestVersion; vI _ bangStar; RETURN }; ENDCASE; IllegalVersion[name]; END; QuotedName: PROC [n: Rope.ROPE] RETURNS [Rope.ROPE] = BEGIN quoteRope: Rope.ROPE = "\""; RETURN [ Rope.Cat[quoteRope, n, quoteRope] ]; END; DirNotLocal: PROC [n: Rope.ROPE] = { FSBackdoor.ProduceError[badWorkingDir, Rope.Concat[QuotedName[n], " is not a local directory."] ] }; NoVolumePart: PROC [n: Rope.ROPE] = { FSBackdoor.ProduceError[badWorkingDir, Rope.Concat[QuotedName[n], " needs a volume part."] ] }; TooLong: PROC [n: Rope.ROPE] = { FSBackdoor.ProduceError[ illegalName, Rope.Concat[QuotedName[n], " has more than 120 characters."] ] }; IllegalCharacter: PROC [n: Rope.ROPE] = { FSBackdoor.ProduceError[ illegalName, Rope.Concat[QuotedName[n], " contains an illegal character."] ] }; IllegalVersion: PROC [n: Rope.ROPE] = { FSBackdoor.ProduceError[ illegalName, Rope.Concat[QuotedName[n], " has an illegal version part."] ] }; IllegalName: PROC [n: Rope.ROPE, nameUse: NameUse] = BEGIN e: Rope.ROPE = SELECT nameUse FROM fName => " is not a legal FName.", pattern => " is not a legal pattern.", directory => " is not a legal directory name", ENDCASE => ERROR; FSBackdoor.ProduceError [illegalName, Rope.Concat[QuotedName[n], e] ]; END; NoPatterns: PROC [n: Rope.ROPE] = { FSBackdoor.ProduceError[ patternNotAllowed, Rope.Concat[QuotedName[n], " contains a \"*\", but patterns are not allow for this operation."] ] }; AppendChar: PROC [text: REF TEXT, c: CHAR] = INLINE BEGIN text[text.length] _ c; text.length _ text.length + 1; END; ConvertWDir: PROC [wDir: Rope.ROPE] RETURNS [w: Rope.ROPE] = BEGIN SELECT Rope.Fetch[wDir, 0] FROM '[ => BEGIN c: CHAR = Rope.Fetch[wDir, Rope.Length[wDir] - 1]; IF c # '> AND c # '] THEN wDir _ Rope.Cat[wDir, ">"]; w _ wDir; END; '/ => BEGIN c: CHAR = Rope.Fetch[wDir, Rope.Length[wDir] - 1]; IF c # '/ THEN wDir _ Rope.Cat[wDir, "/"]; w _ ConvertSlashName[wDir]; END; ENDCASE => IllegalName[wDir, directory]; END; ConvertSlashName: PROC [r: Rope.ROPE] RETURNS [t: Rope.Text] = TRUSTED BEGIN rL: INT = Rope.Length[r]; text: REF TEXT; slashCount: CARDINAL _ IF Rope.Fetch[r, 0] = '/ THEN 0 ELSE 2; t _ Rope.NewText[rL+1]; text _ LOOPHOLE[t]; text.length _ 0; FOR i: INT IN [0 .. rL) DO c: CHAR = Rope.Fetch[r, i]; IF c = '/ THEN BEGIN slashCount _ slashCount + 1; SELECT slashCount FROM 1 => AppendChar[text, '[]; 2 => BEGIN AppendChar[text, ']]; FOR j: INT IN [i+1 .. rL) DO -- scan ahead for another slash IF Rope.Fetch[r, j] = '/ THEN {AppendChar[text, '<]; EXIT}; ENDLOOP; END; ENDCASE => AppendChar[text, '>]; END ELSE AppendChar[text, c]; ENDLOOP; END; END. βFSNameImpl.Mesa Last Edited by: Schroeder, November 15, 1983 1:33 pm Last Edited by: Levin, August 9, 1983 11:28 am Global State Exported to FS Exported to FSBackdoor Exported to FSName Internal procedures If an FS.Error is not generated then the full FName is returned as "fN" and the parts of this FName are located by the indexes in "p" according to the following rules (first means left-most, last means right-most, and after means to-the-right-of): p.serverEnd = index of the "]"; p.dirEnd = IF no ">" THEN p.serverEnd ELSE index of first ">"; p.subDirEnd = IF no ">" THEN p.dirEnd ELSE index of last ">"; p.bang = IF no "!" THEN Rope.Length[fN] ELSE index of first "!"; p.rootEnd = IF no "." after p.subDirEnd THEN p.bang ELSE index of last "." after p.subDirEnd; p.firstStar = index of the first "*". Note that all parsing stops when the first "!" is encountered. It is the client's responsibility to verify that the characters following the "!" constitute a valid version part. Κ/– "cedar" style˜Icode2šœ™K™4K™.code1šΟk ˜ Lšœœœ˜Lšœœ@˜HLšœ œ8˜HLšœ œ˜,Lšœœ˜(Lšœœ ˜Lšœ œ˜!LšœœHœ˜^—šœ  ˜Lšœ8˜?Lšœœ˜Lšœ˜—™ Kšœœ ˜'Kšœœ˜-—šœ™šΟnœœœ œ˜.Kš˜šžœœœ˜Kšœ˜—Kšœ˜Kšœ˜Kšœ˜šœ˜ Kšœ$œ˜9Kšœœ˜.Kšœ!œ˜;K˜Kšœ˜—K˜Kšœ˜—š žœœœœœœ˜7Kšœœ˜—šž œœœ œ œœœœ!œœ˜’Kš˜Kšœ˜Kšœœ˜ Kšœ=˜=Kšœ˜Kšœ$˜$Kšœ$˜&Kšœ@˜DKšœœ%˜=šœ œ!˜0Kšœ4˜8Kšœ˜—KšœB˜Bšœ œ˜(Kšœ1˜5Kšœ˜—Kšœ˜šœ˜ Kšœ,œ˜3Kšœ.˜.Kš˜—Kšœ˜Kšœ˜K˜—šžœœœœœœœ˜bKš˜K˜(Kšœœ œ-˜@Kšœœ,˜OK˜#Kšœœ(˜GKšœœ(˜GKšœ˜——šœ™šž œœœœ,œœœ˜rKš˜Kšœ˜Kšœœ>˜Išœ˜ Kšœ œœ˜1KšœD˜JKšœ˜—Kšœ˜——šœ™š žœœœœœœ3˜›Kš˜Kšœ œœ˜Kšœ˜Kšœ>œ œ œ˜bKšœ'˜)KšœE˜Išœ˜ Kšœ ˜ Kšœ œœœ˜`Kšœ˜—Kšœ˜šœœ˜ Kšœœ(˜4Kšœœ<˜JKšœk˜kKš˜—KšœH˜LKšœ,˜,Kšœ˜—š žœœœœ œœ3˜ˆKš˜Kšœ  œ˜Kšœœ˜Kšœœ œ œ˜6Kšœœœ?˜MK˜ Kšœ˜šœ˜ Kšœ˜ Kšœœ˜#Kšœ!˜%Kšœ˜—š œœœœΟc˜Mšœ˜$Kšœœ˜Kšœ1œ˜8Kšœ˜—Kšœ˜—Kšœ%˜%Kšœ˜šœ˜ Kšœ˜Kšœ(˜,Kšœ!˜%Kšœ˜—Kšœ ˜ Kšœ"œ œ œ ˜MKšœœœœ&˜]KšœB˜FKšœC˜CKšœ˜—š ž œœœœœ˜TKš˜Kšœœ˜&Kš œ œœ œ œ ˜SKšœ˜Kšœ&˜*Kšœ2œ˜=Kšœ>˜>Kšœ˜Kšœ-˜-Kšœ˜—š žœœœ œœœ˜:Kšœœœœ˜T—š žœ œœœœ˜VKš˜Kšœœ˜5Kšœ1˜1Kšœ,˜,Kšœ˜—š ž œ œ œœœ˜AKš˜Kšœœ˜%Kš œ œœ œ œ ˜RKšœ@˜FKšœ˜—š žœœœ œœœ˜aKš˜Kšœœ˜%Kš œ œœ œ œ ˜RKšœ[˜aKšœ˜—šžœ œ œœ˜MKš˜Kšœ˜Kšœ˜"Kšœœ˜'Kšœ˜—šžœœœœ˜ZKš˜šžœœœ˜ Kš˜Kšœœ˜Kšœœ ˜ Kšœœ ˜Kšœ˜Kšœ˜—Kšœœœ˜ šœ ˜Kšœ;œ˜?šœ ˜K˜Kšœœ˜K˜ K˜ Kšœ˜Kšœ˜——Kšœ˜——™Kšœ œ˜,Kšœœœ9œ˜\š žœœœœ œ˜hšœœο™χKšœ™Kšœ œœ œ™>Kšœœœ œ™=Kšœ œœœ™@Kšœ œœœ%™]Kšœ%™%—K™²Kš˜Kšœœ˜Kšœ œœ˜Kšœ œ˜$Kšœ ˜ Kšœ ˜ šœœ˜"Kšœœ˜ Kšœ=˜=šœ˜ KšœŸ˜!Kšœ˜KšœŸ!˜?šœ˜ KšœœJ˜XKšœ˜KšœŸ ˜>KšœŸ ˜>Kš˜—šœœœŸ%˜>šœ˜Kšœœ˜Kšœ ˜ Kšœœ˜—Kšœ˜Kšœ˜—Kšœ˜Kšœ˜KšœŸ˜#——K˜š˜K˜ Kšœ œœ˜šœ˜šœ œ œ˜;Kšœ˜—šœ˜Kšœ˜Kšœ)˜-KšœŸ˜8—šœ˜Kšœ˜Kšœ ˜KšœŸ$˜C—šœ˜Kšœ œ˜(šœ˜ Kšœ˜KšœŸ˜+KšœŸ˜,Kš˜—KšœŸœŸ˜7—šœ˜Kšœ˜—šœ˜Kšœ œ˜—šœ˜Kšœ˜šœœŸ˜ Kšœœ œ˜*šœœŸ6˜>K˜ Kšœ œœ˜Kšœœ˜(šœ˜Kšœ œœ˜%Kšœ˜$—Kš˜—KšœŸ˜>Kšœ˜——šœ˜Kšœ˜Kšœœœ˜0KšœŸ'˜<—KšœŸ˜5—Kšœ˜—Kšœ œ˜"Kšœ œ˜!Kšœ ˜Kšœ˜šœœŸ˜Kšœœ˜2KšœŸ˜1Kš˜—šœœŸ˜Kšœœœ˜OKšœŸ:˜YKšœ˜—Kšœœ˜5Kšœ˜—šž œœ œ œ œœœ6˜Kš˜Kšœœ˜Kšœ œ˜,Kšœ œ˜"šœœ˜,šœ˜Kšœœœ˜š˜Kšœ˜Kšœ˜šœ˜ Kšœœœ8˜EKšœœ˜ Kšœ˜Kšœ ˜ Kšœ˜Kšœ˜—Kšœ˜Kšœ˜Kšœœœ ˜Kšœœ˜ Kšœ˜—Kšœ˜—šœ ˜ Kšœ˜Kšœ3œ˜@—šœ ˜ Kšœ˜Kšœ1œ˜>—šœ˜Kšœœ˜ Kšœ5œ˜B—Kšœ˜—Kšœ˜Kšœ˜—š ž œœ œœœ˜5Kš˜Kšœœ˜Kšœ'˜-Kšœ˜—šž œœ œ˜"šœ(˜(Kšœ=˜=——šž œœ œ˜#šœ(˜(Kšœ8˜8——šžœœ œ˜šœ'˜'KšœA˜A——šžœœ œ˜'šœ'˜'KšœB˜B——šžœœ œ˜%šœ'˜'Kšœ@˜@——šž œœ œ˜4Kš˜šœœœ ˜"Kšœ"˜"Kšœ&˜&Kšœ.˜.Kšœœ˜—KšœF˜FKšœ˜—šž œœ œ˜!šœ-˜-Kšœd˜d——š ž œœœœœ˜3Kš˜K˜K˜Kšœ˜—š ž œœ œœ œ˜Kšœ˜Kšœœ˜K˜šœœœ ˜Kšœœ˜Kšœ˜ šœ˜ Kšœ˜šœ ˜Kšœ˜šœ˜ Kšœ˜š œœœ œŸ˜