DIRECTORY BasicTime USING [GMT, nullGMT], FS USING [InfoProc, NameProc], FSBackdoor USING [ErrorCode, ProduceError, Version], FSName USING [BangStarFile, BangVersionFile, VersionFromRope], FSRemoteFile USING [ConfirmProc, FTPTimeToGMT, Lookup, LookupResult], FSReport USING [UnknownFile], IO USING [STREAM], Process USING [Detach, Seconds, SecondsToTicks], Rope USING [Cat, Equal, Fetch, ROPE], STP USING [Close, ConfirmProcType, Create, Delete, DesiredProperties, Enumerate, Error, ErrorCode, FileInfo, GetFileInfo, Handle, IsOpen, Login, NoteFileProcType, Open, Rename, Retrieve, SetDesiredProperties, SetDirectory, Store, ValidProperties], UserCredentials USING [Get]; FSRemoteFileImpl: CEDAR MONITOR IMPORTS FSBackdoor, FSName, FSRemoteFile, FSReport, Process, Rope, STP , UserCredentials EXPORTS FSRemoteFile = BEGIN Delete: PUBLIC PROC [server, file: Rope.ROPE, wantedCreatedTime: BasicTime.GMT, proc: FSRemoteFile.ConfirmProc] = BEGIN Confirm: STP.ConfirmProcType -- [file: Rope.ROPE] RETURNS [answer: {do, skip, abort}, localStream: IO.STREAM] -- = BEGIN info: STP.FileInfo = STP.GetFileInfo[h]; created: BasicTime.GMT = FSRemoteFile.FTPTimeToGMT[info.create]; SELECT TRUE FROM matchFound => answer _ abort; NOT timeSearch OR wantedCreatedTime = created => BEGIN answer _ IF proc [FSName.VersionFromRope[info.version]] THEN do ELSE abort; matchFound _ TRUE; END; ENDCASE => answer _ skip; END; matchFound: BOOLEAN _ FALSE; timeSearch: BOOLEAN = (wantedCreatedTime # BasicTime.nullGMT); h: STP.Handle _ NIL; stpCode: STP.ErrorCode; BEGIN ENABLE STP.Error => {stpCode _ code; GOTO ReportError}; h _ GetConnection[server]; ConditionConnection[h, file, FALSE]; STP.Delete[h, file, Confirm ! STP.Error => IF code = noSuchFile THEN CONTINUE ]; IF NOT matchFound THEN BEGIN IF timeSearch THEN BEGIN file _ FSName.BangStarFile[file]; STP.Delete[h, file, Confirm]; END; IF NOT matchFound THEN {stpCode _ noSuchFile; GOTO ReportError}; END; EXITS ReportError => ReportSTPError[h, stpCode, server, file, wantedCreatedTime]; END; ReturnConnection[server, h]; END; EnumerateForInfo: PUBLIC PROC [server, pattern: Rope.ROPE, proc: FS.InfoProc] = BEGIN Note: STP.NoteFileProcType -- [file: Rope.ROPE] RETURNS [continue: BOOLEAN] -- = BEGIN info: STP.FileInfo = STP.GetFileInfo[h]; created: BasicTime.GMT = FSRemoteFile.FTPTimeToGMT[info.create]; continue _ IF proc[MakeFullGName[server, file], NIL, created, info.size, 0] THEN yes ELSE no; END; h: STP.Handle _ NIL; stpCode: STP.ErrorCode; BEGIN ENABLE STP.Error => IF code = noSuchFile THEN CONTINUE -- ignore no match ELSE {stpCode _ code; GOTO ReportError}; h _ GetConnection[server]; ConditionConnection[h, pattern, FALSE]; STP.Enumerate[h, pattern, Note]; EXITS ReportError => ReportSTPError[h, stpCode, server, pattern, BasicTime.nullGMT]; END; ReturnConnection[server, h]; END; EnumerateForNames: PUBLIC PROC [server, pattern: Rope.ROPE, proc: FS.NameProc] = BEGIN Note: STP.NoteFileProcType -- [file: Rope.ROPE] RETURNS [continue: BOOLEAN] -- = BEGIN continue _ IF proc[MakeFullGName[server, file]] THEN yes ELSE no; END; h: STP.Handle _ NIL; stpCode: STP.ErrorCode; BEGIN ENABLE STP.Error => IF code = noSuchFile THEN CONTINUE -- ignore no match ELSE {stpCode _ code; GOTO ReportError}; h _ GetConnection[server]; ConditionConnection[h, pattern, TRUE]; STP.Enumerate[h, pattern, Note]; EXITS ReportError => ReportSTPError[h, stpCode, server, pattern, BasicTime.nullGMT]; END; ReturnConnection[server, h]; END; Info: PUBLIC PROC [server, file: Rope.ROPE, wantedCreatedTime: BasicTime.GMT] RETURNS [version: FSBackdoor.Version, bytes: INT, created: BasicTime.GMT] = BEGIN ReportError: PROC [stpCode: STP.ErrorCode] = { ReportSTPError[NIL, stpCode, server, file, wantedCreatedTime] }; result: FSRemoteFile.LookupResult; [result, version, created, bytes] _ FSRemoteFile.Lookup[server, file]; SELECT result FROM noResponse => ReportError[noNameLookupResponse]; noSuchPort => NULL; noSuchFile => IF wantedCreatedTime = BasicTime.nullGMT THEN ReportError[noSuchFile]; noSuchServer => ReportError[noSuchHost]; ok => IF wantedCreatedTime = BasicTime.nullGMT OR wantedCreatedTime = created THEN RETURN; ENDCASE; [version, bytes, created] _ STPInfo[server, file, wantedCreatedTime]; END; Rename: PUBLIC PROC [server, fromFile: Rope.ROPE, fromCreated: BasicTime.GMT, toFile: Rope.ROPE, proc: FSRemoteFile.ConfirmProc] = BEGIN h: STP.Handle _ NIL; stpCode: STP.ErrorCode; version: FSBackdoor.Version = Info[server, fromFile, fromCreated].version; IF proc[version] THEN BEGIN ENABLE STP.Error => {stpCode _ code; GOTO ReportError}; h _ GetConnection[server]; ConditionConnection[h, fromFile, TRUE]; fromFile _ FSName.BangVersionFile[fromFile, version]; STP.Rename[h, fromFile, toFile]; EXITS ReportError => ReportSTPError[h, stpCode, server, toFile, BasicTime.nullGMT]; END; ReturnConnection[server, h]; END; Retrieve: PUBLIC PROC [server, file: Rope.ROPE, wantedCreatedTime: BasicTime.GMT, proc: PROC[fullGName: Rope.ROPE, bytes: INT, created: BasicTime.GMT] RETURNS [IO.STREAM]] = BEGIN Confirm: STP.ConfirmProcType -- [file: Rope.ROPE] RETURNS [answer: {do, skip, abort}, localStream: IO.STREAM] -- = BEGIN info: STP.FileInfo = STP.GetFileInfo[h]; created: BasicTime.GMT = FSRemoteFile.FTPTimeToGMT[info.create]; SELECT TRUE FROM matchFound => answer _ abort; NOT timeSearch OR wantedCreatedTime = created => BEGIN localStream _ proc[MakeFullGName[server, file], info.size, created]; answer _ IF localStream = NIL THEN abort ELSE do; matchFound _ TRUE; END; ENDCASE => answer _ skip; END; matchFound: BOOLEAN _ FALSE; timeSearch: BOOLEAN = (wantedCreatedTime # BasicTime.nullGMT); h: STP.Handle _ NIL; stpCode: STP.ErrorCode; BEGIN ENABLE STP.Error => {stpCode _ code; GOTO ReportError}; h _ GetConnection[server]; ConditionConnection[h, file, FALSE]; STP.Retrieve[h, file, Confirm ! STP.Error => IF code = noSuchFile THEN CONTINUE ]; IF NOT matchFound THEN BEGIN IF timeSearch THEN BEGIN file _ FSName.BangStarFile[file]; STP.Retrieve[h, file, Confirm]; END; IF NOT matchFound THEN {stpCode _ noSuchFile; GOTO ReportError}; END; EXITS ReportError => ReportSTPError[h, stpCode, server, file, wantedCreatedTime]; END; ReturnConnection[server, h]; END; Store: PUBLIC PROC [server, file: Rope.ROPE, str: IO.STREAM, created: BasicTime.GMT, proc: FSRemoteFile.ConfirmProc] = BEGIN Note: STP.NoteFileProcType -- [file: Rope.ROPE] RETURNS [continue: BOOLEAN] -- = BEGIN doIt: BOOLEAN = proc [ FSName.VersionFromRope[ STP.GetFileInfo[h].version ] ]; continue _ IF doIt THEN yes ELSE no; END; h: STP.Handle _ NIL; stpCode: STP.ErrorCode; BEGIN ENABLE STP.Error => {stpCode _ code; GOTO ReportError}; h _ GetConnection[server]; ConditionConnection[h, file, TRUE]; STP.Store[h, file, str, Note, unknown, created]; EXITS ReportError => ReportSTPError[h, stpCode, server, file, BasicTime.nullGMT]; END; ReturnConnection[server, h]; END; STPInfo: PROC[server, file: Rope.ROPE, wantedCreatedTime: BasicTime.GMT] RETURNS [version: FSBackdoor.Version, bytes: INT, created: BasicTime.GMT] = BEGIN Note: STP.NoteFileProcType -- [file: Rope.ROPE] RETURNS [continue: BOOLEAN] -- = BEGIN info: STP.FileInfo = STP.GetFileInfo[h]; created _ FSRemoteFile.FTPTimeToGMT[info.create]; IF NOT timeSearch OR wantedCreatedTime = created THEN BEGIN version _ FSName.VersionFromRope[info.version]; bytes _ info.size; matchFound _ TRUE; continue _ IF timeSearch THEN no ELSE yes; END ELSE continue _ yes; END; matchFound: BOOLEAN _ FALSE; timeSearch: BOOLEAN = (wantedCreatedTime # BasicTime.nullGMT); h: STP.Handle _ NIL; stpCode: STP.ErrorCode; BEGIN ENABLE STP.Error => {stpCode _ code; GOTO ReportError}; h _ GetConnection[server]; ConditionConnection[h, file, FALSE]; STP.Enumerate[h, file, Note ! STP.Error => IF code = noSuchFile THEN CONTINUE ]; IF NOT matchFound THEN BEGIN IF timeSearch THEN BEGIN file _ FSName.BangStarFile[file]; STP.Enumerate[h, file, Note]; END; IF NOT matchFound THEN {stpCode _ noSuchFile; GOTO ReportError}; END; EXITS ReportError => ReportSTPError[h, stpCode, server, file, wantedCreatedTime]; END; ReturnConnection[server, h]; END; MyDesiredProperties: TYPE = PACKED ARRAY STP.ValidProperties OF BoolDefaultFalse; BoolDefaultFalse: TYPE = BOOLEAN _ FALSE; namesOnly: MyDesiredProperties = [directory: TRUE, nameBody: TRUE, version: TRUE]; nameSizeCreated: MyDesiredProperties = [directory: TRUE, nameBody: TRUE, version: TRUE, createDate: TRUE, size: TRUE]; all: STP.DesiredProperties = ALL[TRUE]; -- stops sending of Desired-Property properties ConditionConnection: PROC[h: STP.Handle, nameBody: Rope.ROPE, justNames: BOOLEAN] = BEGIN IF Rope.Fetch[nameBody, 0] # '< THEN BEGIN -- no directory, so turn off directory defaulting and Desired-Property properties STP.SetDirectory[h, " "]; STP.SetDesiredProperties[h, all]; END ELSE BEGIN STP.SetDirectory[h, NIL]; STP.SetDesiredProperties[h, IF justNames THEN namesOnly ELSE nameSizeCreated]; END; END; MakeFullGName: PROC[server, file: Rope.ROPE] RETURNS [Rope.ROPE] = { RETURN [ Rope.Cat[ "[", server, "]", file ] ] }; ReportSTPError: PUBLIC PROC [h: STP.Handle, stpCode: STP.ErrorCode, server, file: Rope.ROPE, time: BasicTime.GMT] = BEGIN gName: Rope.ROPE = MakeFullGName[server, file]; e1: Rope.ROPE _ "Server for \""; e2: Rope.ROPE _ "\"."; code: FSBackdoor.ErrorCode; IF stpCode = noSuchFile THEN BEGIN IF h # NIL THEN ReturnConnection[server, h]; FSReport.UnknownFile[gName, time]; END ELSE BEGIN IF h # NIL THEN STP.Close[h ! STP.Error => CONTINUE]; SELECT stpCode FROM noRouteToNetwork, noNameLookupResponse => { code _ serverInaccessible; e2 _ "\" is inaccessible." }; connectionRejected => { code _ connectionRejected; e2 _ "\" rejected the connection attempt." }; connectionTimedOut => { code _ connectionTimedOut; e2 _ "\" timed-out the connection." }; accessDenied => { code _ accessDenied; e2 _ "\" denied permission to access the file." }; requestRefused => { code _ quotaExceeded; e1 _ "Request refused (possibily no quota for storing) when accessing \"" }; accessError => { code _ fileBusy; e1 _ "\""; e2 _ "\" is locked on the server." }; illegalUserName => { code _ badCredentials; e1 _ "Credentials rejected when accessing \"" }; illegalFileName => { code _ illegalName; e2 _ "\" says that the file name is illegal." }; noSuchHost => { code _ unknownServer; e1 _ "Couldn't find the server for \"" }; ENDCASE => ERROR; FSBackdoor.ProduceError[code, Rope.Cat[e1, gName, e2]]; END; END; maxSlots: CARDINAL = 4; -- only need enough for all used in last TimeOut seconds TimeOut: Process.Seconds _ 10; -- keep connection around for this long after use Slot: TYPE = RECORD [ server: Rope.ROPE, h: STP.Handle, used: BOOLEAN ]; emptySlot: Slot = [NIL, NIL, FALSE]; new, reused: INT _ 0; -- statistics slot: ARRAY [0..maxSlots) OF Slot _ ALL [emptySlot]; haveSlotTimer: BOOLEAN _ FALSE; ForTimeout: CONDITION _ [Process.SecondsToTicks[TimeOut]]; GetConnection: PROC[server: Rope.ROPE] RETURNS [h: STP.Handle] = BEGIN h _ LookForConnection[server]; IF h = NIL THEN BEGIN -- need a new connection user, password: Rope.ROPE; h _ STP.Create[]; [user, password] _ UserCredentials.Get[]; STP.Login[h, user, password]; [] _ STP.Open[h, server]; END ELSE BEGIN -- already had a connection IF NOT STP.IsOpen[h] THEN [] _ STP.Open[h, server]; END; END; LookForConnection: ENTRY PROC[server: Rope.ROPE] RETURNS [h: STP.Handle] = BEGIN FOR i: CARDINAL IN [0..maxSlots) DO IF slot[i] # emptySlot AND Rope.Equal[slot[i].server, server, FALSE] THEN BEGIN reused _ reused + 1; h _ slot[i].h; slot[i] _ emptySlot; RETURN; END; ENDLOOP; h _ NIL; new _ new + 1; END; ReturnConnection: PROC [server: Rope.ROPE, h: STP.Handle] = BEGIN IF STP.IsOpen[h] AND NOT SaveConnection[server, h] THEN STP.Close[h ! STP.Error => CONTINUE ]; END; SaveConnection: ENTRY PROC [server: Rope.ROPE, h: STP.Handle] RETURNS [BOOLEAN] = BEGIN FOR i: CARDINAL IN [0..maxSlots) DO IF slot[i] = emptySlot THEN BEGIN slot[i] _ [server, h, TRUE]; IF NOT haveSlotTimer THEN TRUSTED BEGIN haveSlotTimer _ TRUE; Process.Detach[FORK SlotTimer[]]; END; RETURN [TRUE]; END; ENDLOOP; RETURN [FALSE]; END; SlotTimer: PROC = BEGIN i: CARDINAL _ 0; h: STP.Handle; DO [h, i] _ NextInterestingSlot[i]; IF h = NIL THEN RETURN; STP.Close[h ! STP.Error => CONTINUE ]; ENDLOOP; END; NextInterestingSlot: ENTRY PROC [start: CARDINAL] RETURNS [h: STP.Handle, index: CARDINAL] = BEGIN DO IF start = 0 THEN WAIT ForTimeout; FOR index IN [start..maxSlots) DO IF slot[index] # emptySlot THEN BEGIN IF slot[index].used THEN slot[index].used _ FALSE ELSE BEGIN -- here's one that needs to be closed h _ slot[index].h; slot[index] _ emptySlot; RETURN; END; END; ENDLOOP; FOR index IN [0 .. maxSlots) DO IF slot[index] # emptySlot THEN EXIT; -- need to stick around REPEAT FINISHED => BEGIN -- nothing more to do h _ NIL; index _ maxSlots; haveSlotTimer _ FALSE; RETURN; END; ENDLOOP; start _ 0; ENDLOOP; END; END. jFSRemoteFileImpl.mesa Last Edited by: Schroeder, December 11, 1983 3:01 pm Last Edited by: Levin, September 22, 1983 1:04 pm Exported to FSRemoteFile Internal procedures If version part was missing from client's file name, then Enumerate will produce all versions, but we only want the !H version Property stuff Internal procedures STP connection cacheing Κ–– "cedar" style˜Icode2šœ™K™4K™1code1šΟk ˜ Lšœ œœ ˜Lšœœ˜Lšœ œ$˜4Lšœœ2˜>Lšœ œ3˜ELšœ œ˜Lšœœœ˜Lšœœ#˜0Lšœœœ˜%Lšœœξ˜χLšœœ˜—šœœ˜Lšœ<œ˜XLšœ ˜Lšœ˜—™š Οnœœœœœ#˜qKš˜šœ œΟcSœ˜rKš˜Kšœœ œ˜(Kšœœ*˜@šœœ˜Kšœ˜šœ œ˜0Kš˜Kšœ œ-œœ˜KKšœ œ˜Kšœ˜—Kšœ˜—Kšœ˜—Kšœ œœ˜Kšœ œ+˜>Kšœœ œ˜Kšœ œ ˜š˜Kšœœœ˜7Kšœ˜Kšœœ˜$šœ˜Kš œœ œœœ˜4—Kšœœ ˜šœ˜ Kšœ ˜ šœ˜ Kšœ!˜!Kšœ˜Kšœ˜—Kšœœ ˜Kšœœ˜.Kšœ˜—šœ˜Kšœ<˜<—Kšœ˜—Kšœ˜Kšœ˜—šžœœœœ˜OKš˜šœœŸ3œ˜PKš˜Kšœœ œ˜(Kšœ@˜@Kš œ œ#œœœ˜]Kšœ˜—Kšœœ œ˜Kšœ œ ˜š˜šœœ ˜Kšœ˜KšœœŸ˜ Kšœœ˜(—Kšœ˜Kšœ œ˜'Kšœ˜ šœ˜Kšœ?˜?—Kšœ˜—Kšœ˜Kšœ˜—šžœœœœ˜PKš˜šœœŸ3œ˜PKš˜Kšœ œ#œœ˜AKšœ˜—Kšœœ œ˜Kšœ œ ˜š˜šœœ ˜Kšœ˜KšœœŸ˜ Kšœœ˜(—Kšœ˜Kšœ œ˜&Kšœ˜ šœ˜Kšœ?˜?—Kšœ˜—Kšœ˜Kš˜—šžœœœœœœ&œœ˜™Kš˜šœœ ˜,Kšœœ.˜B—Kšœ"˜"KšœF˜Fšœ˜šœ ˜ Kšœ"˜"—šœ ˜ Kšœ˜—šœ ˜ Kšœ&˜(Kšœ˜—šœ˜Kšœ˜—šœ˜Kšœ'œ˜GKšœœ˜ —Kšœ˜—KšœE˜EKšœ˜—š žœ œœœœ#˜‚Kš˜Kšœœ œ˜Kšœ œ ˜KšœJ˜JKšœ˜šœ˜ Kšœœœ˜7Kšœ˜Kšœ!œ˜'Kšœ5˜5Kšœ˜ šœ˜Kšœ>˜>—Kšœ˜—Kšœ˜Kšœ˜—šžœœœœœœœ œœœœœ˜­Kš˜šœ œŸSœ˜rKš˜Kšœœ œ˜(Kšœœ*˜@šœœ˜Kšœ˜šœ œ˜0Kš˜KšœD˜DKš œ œœœœ˜1Kšœ œ˜Kšœ˜—Kšœ˜—Kšœ˜—Kšœ œœ˜Kšœ œ+˜>Kšœœ œ˜Kšœ œ ˜š˜Kšœœœ˜7Kšœ˜Kšœœ˜$šœ˜Kš œœ œœœ˜4—Kšœœ ˜šœ˜ Kšœ ˜ šœ˜ Kšœ!˜!Kšœ˜Kšœ˜—Kšœœ ˜Kšœœ˜/Kšœ˜—šœ˜Kšœ<˜<—Kšœ˜—Kšœ˜Kšœ˜—šžœœœœœœœ#˜vKš˜šœœŸ3œ˜PKš˜Kšœœ"œ˜NKšœ œœœ˜$Kšœ˜—Kšœœ œ˜Kšœ œ ˜š˜Kšœœœ˜7Kšœ˜Kšœœ˜#Kšœ-˜0šœ˜Kšœ<˜<—Kšœ˜—Kšœ˜Kšœ˜——™šžœœœœœ&œœ˜”Kš˜šœœŸ3œ˜PKš˜Kšœœ œ˜(Kšœ1˜1Kšœœ œ˜0šœ˜ Kšœ/˜/Kšœ˜Kšœ œ˜K™~Kšœ œ œœ˜*Kš˜—Kšœ˜Kšœ˜—Kšœ œœ˜Kšœ œ+˜>Kšœœ œ˜Kšœ œ ˜š˜Kšœœœ˜7Kšœ˜Kšœœ˜$šœ˜Kš œœ œœœ˜4—Kšœœ ˜šœ˜ Kšœ ˜ šœ˜ Kšœ!˜!Kšœ˜Kšœ˜—Kšœœ ˜Kšœœ˜/Kšœ˜—šœ˜Kšœ<˜<—Kšœ˜—Kšœ˜Kšœ˜——™Kš œœœœœœ˜QKšœœœœ˜)Kšœ-œ œ œ˜RKš œ3œ œ œœœ˜vKšœœœœŸ/˜Wš žœœœœ œ˜SKš˜Kšœ˜šœœŸQ˜\Kšœ˜Kšœ˜!Kš˜—š ˜ Kšœœ˜Kšœœ œ œ˜NKšœ˜—Kšœ˜——™š ž œœœœœ˜BKšœœ*˜2—šžœœœœœœœ˜sKš˜Kšœ œ˜/Kšœ œ˜ Kšœ œ ˜Kšœ˜Kšœ˜šœ˜ Kšœœœ˜,Kšœ"˜"Kš˜—šœ˜ Kš œœœœ œ œ˜5šœ ˜šœ)˜)Kšœ˜Kšœ˜—šœ˜Kšœ˜Kšœ-˜-—šœ˜Kšœ˜Kšœ&˜&—šœ˜Kšœ˜Kšœ2˜2—šœ˜Kšœ˜KšœL˜L—šœ˜Kšœ˜Kšœ0˜0—šœ˜Kšœ˜Kšœ0˜0—šœ˜Kšœ˜Kšœ0˜0—šœ ˜ Kšœ˜Kšœ)˜)—Kšœœ˜—Kšœ7˜7Kšœ˜—Kšœ˜——™Kšœ œŸ8˜PKšœŸ1˜Pšœœœ˜Kšœ œ˜Kšœœ˜Kšœ˜ Kšœ˜—Kšœœœœ˜$Kšœ œŸ ˜#Kšœœœœ ˜4Kšœœœ˜Kšœ  œ%˜:š ž œœœœœ ˜@Kš˜Kšœ˜Kšœ˜ šœœŸ˜#Kšœœ˜Kšœœ ˜Kšœ)˜)Kšœ˜Kšœœ˜Kš˜—šœœŸ˜&Kšœœœ ˜Kšœœ˜Kšœ˜—Kšœ˜—š žœœœœœœ ˜JKš˜šœœœ˜#Kšœœ$œ˜Dšœ˜ Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜—Kšœ˜—Kšœœ˜Kšœ˜Kšœ˜—šžœœœœ ˜;Kš˜Kšœœ œœ˜2Kšœœ œ œ˜+Kšœ˜—šžœœœœœ œœ˜QKš˜šœœœ˜#Kšœ˜š ˜ Kšœœ˜Kšœœ˜šœ ˜Kšœœ˜Kšœœ˜!Kšœ˜—Kš œ˜Kšœ˜—Kšœ˜—Kšœ˜Kšœ˜—šž œœ˜Kš˜Kšœœ˜Kšœœ˜š˜Kšœ ˜ Kšœœœœ˜Kšœ œ œ˜&Kšœ˜—Kšœ˜—šžœœœ œœœœ˜]Kš˜š˜Kšœ œœ ˜"šœœ˜!Kšœ˜šœ˜ Kšœ˜Kšœ˜šœœŸ%˜0Kšœ˜Kšœ˜Kšœ˜Kšœ˜—Kšœ˜—Kšœ˜—šœœ˜KšœœœŸ˜=šœœœŸ˜.Kšœœ˜K˜Kšœœ˜Kšœ˜Kšœ˜—Kšœ˜—K˜ Kšœ˜—Kšœ˜——Kšœ˜—…—26F6