DIRECTORY FS USING [Error, StreamOpen], GVPDefs, IO USING [Close, GetLength, PutChar, PutF, PutFR, PutText, STREAM, SetIndex, SetLength, UnsafeGetBlock, UnsafePutBlock], GVProtocol USING [CreateStream, Close, Failed, Handle, ReceiveByte, ReceiveBytes, ReceiveCount, ReceiveGVString, SendByte, SendBytes, SendCount, SendNow, SendRName, SendGVString], PupDefs USING [PupAddress, PupNameLookup, PupNameTrouble, PupPackageDestroy, PupPackageMake, PupSocketID], Rope USING [Concat, Fetch, Length], UserCredentials USING [Get]; GVPDriver: CEDAR PROGRAM IMPORTS FS, GVPDefs, IO, GVProtocol, PupDefs, Rope, UserCredentials EXPORTS GVPDefs = BEGIN OPEN GVPDefs; byteStream: GVProtocol.Handle _ NIL; KillByteStream: PROC[h: GVPRef] = BEGIN IF byteStream#NIL THEN BEGIN GVProtocol.Close[byteStream]; h.logStream.PutText["\nConnection with server broken\n"] END; byteStream _ NIL END; CheckByteStream: PROC[h: GVPRef] RETURNS [r: ROPE _ NIL]= BEGIN IF byteStream=NIL THEN r _ ConnectToServer[h] END; ConnectToServer: PROC[h: GVPRef] RETURNS [r: ROPE _ NIL] = BEGIN serverAddress: PupDefs.PupAddress; GVPatchSocket: PupDefs.PupSocketID = [0, 47B]; user, password, server: ROPE; server _ GetServerName[]; Set[Rope.Concat["Trying to connect to server ", server]]; serverAddress _ PupDefs.PupNameLookup[GVPatchSocket, server ! PupDefs.PupNameTrouble => BEGIN r _ IO.PutFR["Name lookup trouble, %g", [rope[e]]]; GOTO fail END]; byteStream _ GVProtocol.CreateStream[serverAddress, none, 600 ! GVProtocol.Failed => BEGIN r _ "Unable to open byte stream"; GOTO fail END]; [user, password] _ UserCredentials.Get[]; GVProtocol.SendByte[byteStream, login]; GVProtocol.SendRName[byteStream, user]; GVProtocol.SendGVString[byteStream, password]; GVProtocol.SendNow[byteStream]; r _ ServerError[h, login ! GVProtocol.Failed => BEGIN r _ "Server timed out during login"; GOTO fail END]; IF r#NIL THEN GOTO fail; h.logStream.PutF["\nConnected to Grapevine server %l%g%l\n", [rope["b"]], [rope[server]], [rope[" "]]]; Set["Connected to server"] EXITS fail => KillByteStream[h]; END; RestartServer: PUBLIC PROC [h: GVPRef, line: ROPE] RETURNS[r: ROPE] = BEGIN ENABLE GVProtocol.Failed => {r _ "Byte stream trouble"; KillByteStream[h]; GOTO fail}; remcm: IO.STREAM = FS.StreamOpen[fileName: "rem.cm", accessOptions: create ! FS.Error => {r _ "Unable to open local file rem.cm"; GOTO fail}]; Set["Creating local file rem.cm"]; FOR i: INT IN [0..Rope.Length[line]) DO remcm.PutChar[Rope.Fetch[line, i]] ENDLOOP; remcm.PutChar['\n]; remcm.Close[]; r _ CheckByteStream[h]; IF r#NIL THEN RETURN; r _ WriteFile[h, "rem.cm"]; IF r#NIL THEN RETURN; Set["Restarting server"]; GVProtocol.SendByte[byteStream, restartServer]; GVProtocol.SendNow[byteStream]; r _ ServerError[h, restartServer]; IF r#NIL THEN GOTO fail; h.logStream.PutF["\n%lServer restarted%l with command - %g\n", [rope["b"]], [rope[" "]], [rope[line]]]; Set[]; KillByteStream[h] EXITS fail => NULL END; ServerError: PROC [h: GVPRef, command: Byte] RETURNS [r: ROPE] = BEGIN reply: Byte _ GVProtocol.ReceiveByte[byteStream]; IF reply = errorCode THEN BEGIN r _ Rope.Concat["Server: ", GVProtocol.ReceiveGVString[byteStream]] END ELSE IF reply # command THEN ERROR GVProtocol.Failed[protocolError, "?? reply # command"] END; GetHeapFile: PUBLIC PROC [h: GVPRef] RETURNS [r: ROPE _ NIL] = BEGIN ENABLE GVProtocol.Failed => {r _ "Byte stream trouble"; KillByteStream[h]; GOTO fail}; bothHdrByteSize: CARDINAL = pageHdrByteSize+objHdrByteSize; fileStream: IO.STREAM; mapStream: IO.STREAM = FS.StreamOpen[fileName: "heap.map", accessOptions: create ! FS.Error => {r _ "Trouble opening heap.map here"; GOTO fail}]; pageBuffer: REF PageByteVec = NEW[PageByteVec]; RxVec: TYPE = PACKED ARRAY [0..bothHdrByteSize) OF Byte; rxBuffer: REF RxVec _ NEW[RxVec]; toIndex: CARDINAL; heapSize, mapSize: CARDINAL; heapByteSize: INT; r _ CheckByteStream[h]; IF r#NIL THEN GOTO fail; r _ ReadFile[h, "heap.segments"]; IF r#NIL THEN GOTO fail; Set["Reading heap structure"]; GVProtocol.SendByte[byteStream, readHeaders]; GVProtocol.SendNow[byteStream]; r _ ServerError[h, readHeaders]; IF r#NIL THEN GOTO fail; heapSize _ GVProtocol.ReceiveCount[byteStream]; -- pages mapSize _ (heapSize/segSize)*segSize; heapByteSize _ LONG[heapSize]*bytesPerPage; fileStream _ FS.StreamOpen[fileName: "heap.data", accessOptions: create, createByteCount: heapByteSize ! FS.Error => {r _ "Trouble opening heap.data here"; GOTO fail} ]; FOR curPage: CARDINAL IN [1..heapSize] DO -- copy one page objCount: CARDINAL _ GVProtocol.ReceiveCount[byteStream]; IF curPage MOD 10 = 0 THEN Set[IO.PutFR["Reading page %d of %d", [cardinal[curPage]], [cardinal[heapSize]]]]; TRUSTED { GVProtocol.ReceiveBytes[byteStream, [LOOPHOLE[rxBuffer], 0, bothHdrByteSize]]; }; FOR i: CARDINAL IN [0..bothHdrByteSize) DO -- copy page and first object header pageBuffer[i] _ rxBuffer[i] ENDLOOP; toIndex _ pageHdrByteSize; TRUSTED { THROUGH [0..objCount-1) DO foo: LONG POINTER = LOOPHOLE[pageBuffer]; objH: LONG POINTER TO ObjectHeader = foo + toIndex/bytesPerWord; toIndex _ toIndex+objHdrByteSize+objH.size*bytesPerWord; GVProtocol.ReceiveBytes[byteStream, [LOOPHOLE[rxBuffer], 0, objHdrByteSize]]; FOR i: CARDINAL IN [0..objHdrByteSize) DO pageBuffer[toIndex+i] _ rxBuffer[i] ENDLOOP ENDLOOP; }; fileStream.UnsafePutBlock[[LOOPHOLE[pageBuffer], 0, pageByteSize]]; ENDLOOP; h.logStream.PutF["\nFile %lheap.data%l created, size is %d bytes\n", [rope["b"]], [rope[" "]], [cardinal[LONG[heapSize]*pageByteSize]]]; FOR i: CARDINAL IN [0..mapSize) DO mapStream.PutChar[emptyPage] ENDLOOP; mapStream.Close[]; h.logStream.PutF["\nFile %lheap.map%l created, size is %d bytes\n", [rope["b"]], [rope[" "]], [cardinal[mapSize]]]; fileStream.Close[]; r _ CheckStructure[h] EXITS fail => NULL END; OpenFile: PROC[h: GVPRef, fileName: ROPE, oldNew: Byte] RETURNS [r: ROPE _ NIL, page, byte: CARDINAL] = BEGIN ENABLE GVProtocol.Failed => {r _ "Byte stream trouble"; GOTO fail}; r _ CheckByteStream[h]; IF r#NIL THEN GOTO fail; GVProtocol.SendByte[byteStream, openFile]; GVProtocol.SendGVString[byteStream, fileName]; GVProtocol.SendByte[byteStream, oldNew]; GVProtocol.SendNow[byteStream]; r _ ServerError[h, openFile]; IF r = NIL THEN BEGIN page _ GVProtocol.ReceiveCount[byteStream]; byte _ GVProtocol.ReceiveCount[byteStream] END EXITS fail => KillByteStream[h] END; SetFileLength: PROC[h: GVPRef, page, byte: CARDINAL] RETURNS [r: ROPE _ NIL] = BEGIN ENABLE GVProtocol.Failed => {r _ "Byte stream trouble"; GOTO fail}; r _ CheckByteStream[h]; IF r#NIL THEN GOTO fail; GVProtocol.SendByte[byteStream, setLength]; GVProtocol.SendCount[byteStream, page]; GVProtocol.SendCount[byteStream, byte]; GVProtocol.SendNow[byteStream]; r _ ServerError[h, setLength]; EXITS fail => KillByteStream[h] END; ReadPage: PUBLIC PROC[h: GVPRef, page: CARDINAL, pageBuffer: REF PageByteVec] RETURNS[r: ROPE _ NIL] = BEGIN ENABLE GVProtocol.Failed => {r _ "Byte stream trouble"; GOTO fail}; r _ CheckByteStream[h]; IF r#NIL THEN GOTO fail; GVProtocol.SendByte[byteStream, readPage]; GVProtocol.SendCount[byteStream, page]; GVProtocol.SendNow[byteStream]; r _ ServerError[h, readPage]; IF r#NIL THEN GOTO fail; TRUSTED { GVProtocol.ReceiveBytes[byteStream, [LOOPHOLE[pageBuffer], 0, pageByteSize]]; }; EXITS fail => KillByteStream[h] END; WritePage: PUBLIC PROC[h: GVPRef, page: CARDINAL, pageBuffer: REF PageByteVec] RETURNS[r: ROPE _ NIL] = BEGIN ENABLE GVProtocol.Failed => {r _ "Byte stream trouble"; GOTO fail}; r _ CheckByteStream[h]; IF r#NIL THEN GOTO fail; GVProtocol.SendByte[byteStream, writePage]; GVProtocol.SendCount[byteStream, page]; GVProtocol.SendBytes[byteStream, [LOOPHOLE[pageBuffer], 0, pageByteSize]]; GVProtocol.SendNow[byteStream]; r _ ServerError[h, writePage]; EXITS fail => KillByteStream[h] END; ReadFile: PUBLIC PROC[h: GVPRef, name: ROPE] RETURNS[r: ROPE _ NIL] = BEGIN page, byte, pagesToRead: CARDINAL; fileStream: IO.STREAM; pageBuffer: REF PageByteVec _ NIL; fileByteSize: INT; fileStream _ FS.StreamOpen[fileName: name, accessOptions: create ! FS.Error => {r _ "Trouble opening local file"; GOTO fail}]; [r, page, byte] _ OpenFile[h, name, oldFile]; IF r#NIL THEN GOTO fail; Set[Rope.Concat["Reading file ", name]]; pagesToRead _ IF byte=0 AND page#0 THEN page ELSE page+1; fileByteSize _ LONG[page]*pageByteSize+byte; pageBuffer _ NEW[PageByteVec]; FOR i: CARDINAL IN [0..pagesToRead) DO r _ ReadPage[h, i, pageBuffer]; IF r#NIL THEN GOTO fail; fileStream.UnsafePutBlock[[LOOPHOLE[pageBuffer], 0, pageByteSize]]; ENDLOOP; fileStream.SetLength[fileByteSize]; fileStream.Close[]; h.logStream.PutF["\nFile %l%g%l read from server, size is %d bytes\n", [rope["b"]], [rope[name]], [rope[" "]], [cardinal[fileByteSize]]] EXITS fail => NULL END; WriteFile: PUBLIC PROC[h: GVPRef, name: ROPE] RETURNS[r: ROPE _ NIL] = BEGIN page, byte, pagesToWrite: CARDINAL; length: INT; fileStream: IO.STREAM; pageBuffer: REF PageByteVec _ NIL; fileStream _ FS.StreamOpen[fileName: name, accessOptions: read ! FS.Error => {r _ "Trouble opening local file"; GOTO fail}]; [r, page, byte] _ OpenFile[h, name, oldOrNewFile]; IF r#NIL THEN GOTO fail; Set[Rope.Concat["Writing file ", name]]; length _ fileStream.GetLength[]; pagesToWrite _ (length+pageByteSize-1) / pageByteSize; page _ length / pageByteSize; byte _ length MOD pageByteSize; pageBuffer _ NEW[PageByteVec]; FOR i: CARDINAL IN [0..pagesToWrite) DO TRUSTED { [] _ fileStream.UnsafeGetBlock[[LOOPHOLE[pageBuffer], 0, pageByteSize]]; }; r _ WritePage[h, i, pageBuffer]; IF r#NIL THEN {pageBuffer _ NIL; GOTO fail} ENDLOOP; r _ SetFileLength[h, page, byte]; IF r#NIL THEN GOTO fail; h.logStream.PutF["\nFile %l%g%l written to server, size is %d bytes\n", [rope["b"]], [rope[name]], [rope[" "]], [cardinal[length]]] EXITS fail => NULL END; SetServerLength: PUBLIC PROC[h: GVPRef, name: ROPE, pages, bytes: CARDINAL] RETURNS[r: ROPE _ NIL] = BEGIN [r: r] _ OpenFile[h, name, oldFile]; IF r#NIL THEN RETURN; r _ SetFileLength[h, pages, bytes]; IF r#NIL THEN RETURN; h.logStream.PutF["\nLength of server file %l%g%l set to %d pages, %d bytes\n", [rope["b"]], [rope[name]], [rope[" "]], [cardinal[pages]], [cardinal[bytes]]] END; SetLocalLength: PUBLIC PROC[h: GVPRef, name: ROPE, pages, bytes: CARDINAL] RETURNS[r: ROPE _ NIL] = BEGIN fileStream: IO.STREAM = FS.StreamOpen[fileName: name, accessOptions: write ! FS.Error => BEGIN r _ "Unable to open local file"; GOTO fail END]; fileStream.SetLength[pages*bytesPerPage+bytes]; fileStream.Close[]; h.logStream.PutF["\nLength of local file %l%g%l set to %d pages, %d bytes\n", [rope["b"]], [rope[name]], [rope[" "]], [cardinal[pages]], [cardinal[bytes]]] EXITS fail => NULL END; ReadServerPage: PUBLIC PROC[h: GVPRef, page: CARDINAL, pageBuffer: REF PageByteVec] RETURNS [r: ROPE _ NIL] = BEGIN Set["Fetching page from server"]; [r: r] _ OpenFile[h, "heap.data", oldFile]; IF r#NIL THEN RETURN; r _ ReadPage[h, page, pageBuffer]; IF r#NIL THEN RETURN; h.heapStream.SetIndex[LONG[page] * bytesPerPage]; [] _ h.heapStream.UnsafePutBlock[[LOOPHOLE[pageBuffer], 0, bytesPerPage]]; h.logStream.PutF["\nHeap page %d read from server\n", [cardinal[page]]]; SetPageState[page, fullPage] END; WriteServerPage: PUBLIC PROC[h: GVPRef, page: CARDINAL, pageBuffer: REF PageByteVec] RETURNS [r: ROPE _ NIL] = BEGIN h.heapStream.SetIndex[LONG[page] * bytesPerPage]; TRUSTED { [] _ h.heapStream.UnsafeGetBlock[[LOOPHOLE[pageBuffer], 0, bytesPerPage]]; }; [r: r] _ OpenFile[h, "heap.data", oldFile]; IF r#NIL THEN RETURN; r _ WritePage[h, page, pageBuffer]; IF r#NIL THEN RETURN; h.logStream.PutF["\nHeap page %d updated on server\n", [cardinal[page]]] END; DriverInit: PUBLIC PROC = BEGIN PupDefs.PupPackageMake[] END; DriverTidyUp: PUBLIC PROC = BEGIN PupDefs.PupPackageDestroy[] END; END. GVPDriver.mesa, this module provides an interface to the GVPatch server HGM, May 23, 1984 10:45:20 pm PDT Steve Temple, November 18, 1982 11:10 am This module implements the interface to the GVPatch program running on the Grapevine server which we are mending. The only global data here is the bytestream. In general if anything goes wrong with a server operation we close the stream just to be on the safe side. All the basic server operation procs open a stream if need be. KillByteStream checks to see if the stream is alive (i.e. the REF to it is non-NIL) and if it is it destroys the stream and NILs out the REF. CheckByteStream is called by all procs which implement basic server operations. It assumes no stream exists if the REF is NIL and creates one on that basis. ConnectToServer opens the bytestream to the server end of the GVPatch system. It first looks up the name the user has provided and then opens a stream to the GVPatch socket on that machine (socket number 047B). If this goes OK it sends the login command byte and the name and password of the logged in user (in clear text!). If the login command byte is reflected we're home and dry. If anything fails we close the stream and drop out. RestartServer has the job of getting the server back to life again. It first wraps up the command line that the user supplied in the file rem.cm and sends that to the server. It then sends the restart command byte and waits for a reply before announcing the restart. ServerError waits for a reply byte from the server after we've sent a command. The two cases we allow for are 1) the command succeeded and we get the command byte echoed or b) the command failed and we get an error byte and a string which we return as a rope. If neither of these we raise a protocol error so the caller will close the stream. GetHeapFile reads the heap from the server. It first reads the file heap.segments and then asks for the heap structure from the server. The first thing in the structure info is the heap size and we try to open a file of that size before proceeding. We then get the structure on a page by page basis (note that the object bodies contain garbage). We also create the file heap.map (the page map) and fill it with the "empty" entry. OpenFile tries to open a file on the server. We can specify whether it should already exist or not. The file will always be opened for writing. We get back the size of the file in pages and bytes if the open succeeds. If another file on the server was open it is closed by this operation. Repeatedly opening the same file is noted by the server and handled efficiently. SetFileLength sets the length of a file on the server. The file is assumed to have been opened already with OpenFile (above). An error will come from the server if no file is open. ReadPage gets a page from the currently open file on the server. We get an error from the server if no file is open or if the page number is out of range (of the file size). WritePage writes a page of data to the server's open file. We are only allowed to write in existing pages of the file but note that if we write to the last page the length is extended to just beyond that page and so incremental writing of pages will extend the file sensibly. ReadFile reads a file from the server to a file on the local machine. The names of both files must be the same. The event is logged. WriteFile copies a named local file to a file with the same name on the server and logs the fact for us. SetServerLength just opens a server file and sets its length for us. The event is logged. We insist that the file already exist. SetLocalLength sets the length of a (pre-existing) local file and logs the event. ReadServerPage just opens the file heap.data on the server and reads a page from it into the buffer that we supply. Note that the OpenFile operation on the server is implemented in such a way that succesive opens of the same file are handled efficiently. WriteServerPage performs the reverse operation to ReadServerPage. DriverInit and DriverTidyUp just start and stop the Pup package respectively Ê^˜JšœG™GJšœ!™!Jšœ(™(J˜JšœT™TJšœW™WJšœ\™\Jšœ>™>J˜J˜šÏk ˜ Jšœœ˜J˜Jšœœ3œ7˜xJšœ œ£˜³Jšœœ]˜jJšœœ˜#Jšœœ˜J˜J˜—Jšœ œ˜š˜Jšœ œ,˜;—Jšœ ˜Jšœœ ˜J˜Jšœ œ˜$J˜J˜Jšœ]™]Jšœ/™/J˜šÏnœœ˜'šœ œœ˜J˜J˜8Jšœ˜—Jšœ ˜Jšœ˜J˜J˜—JšœS™SJšœI™IJ˜š žœœ œœœ˜@Jšœ œœ˜-Jšœ˜J˜J˜—JšœV™VJšœU™UJšœY™YJšœW™WJšœT™TJ˜Jš žœœ œœœ˜@˜J˜"J˜.Jšœœ˜J˜J˜J˜9šœX˜]Jšœœ-˜3Jšœ˜ Jšœ˜J˜J˜—šœU˜ZJ˜!Jšœ˜ Jšœ˜J˜—J˜)J˜J˜'J˜J˜'J˜J˜.J˜J˜šœ0˜5J˜$Jšœ˜ Jšœ˜J˜—Jšœœœœ˜J˜˜=J˜*—J˜Jšœ˜ Jšœ˜J˜J˜—JšœZ™ZJšœT™TJšœS™SJšœ™J˜š ž œœœœœœ˜LJšœEœ˜VJ˜šœœœH˜XJšœ)œ˜5J˜—J˜"šœœœ˜$Jšœ$œ˜.—J˜J˜J˜J˜Jšœœœœ˜J˜J˜Jšœœœœ˜J˜J˜J˜/J˜J˜"Jšœœœœ˜J˜˜?J˜(—J˜J˜Jšœ ˜Jšœ˜J˜J˜—JšœV™VJšœU™UJšœV™VJšœR™RJ˜šž œœœœ˜@Jš˜J˜1Jšœ˜Jšœ˜J˜CJš˜Jšœœœœ7˜YJšœ˜—J˜J˜Jšœ[™[Jšœ^™^Jšœ[™[JšœR™RJšœD™DJ˜Jš ž œœœ œœœ˜D˜JšœEœ˜VJ˜Jšœœ"˜;J˜Jšœ œœ˜J˜šœ œœœ7˜PJšœ4œ˜@J˜—Jšœ œœ˜/J˜Jš œœœœœ˜8Jšœ œ œ˜!Jšœ œ˜Jšœœ˜Jšœœ˜J˜J˜Jšœœœœ˜J˜J˜!Jšœœœœ˜J˜J˜J˜-J˜J˜ Jšœœœœ˜J˜Jšœ0Ïc˜8J˜%Jšœœ˜+J˜šœ œW˜fJšœ5œ ˜BJ˜—š œ œœœŸ˜:Jšœ œ'˜9šœ œ˜JšœœL˜WJ˜—Jšœ(œ$˜[J˜š œœœœŸ$˜OJ˜Jšœ˜J˜—J˜J˜Jšœ˜ šœ˜Jšœœœœ ˜)Jšœœœœ+˜@J˜8Jšœ%œ ˜MJ˜šœœœ˜)J˜#Jšœ˜ —Jšœ˜ J˜—Jšœœ ˜CJšœ˜J˜—˜EJšœ$œ˜CJ˜—šœœœ˜"J˜Jšœ˜—J˜J˜˜DJ˜/—J˜J˜J˜J˜Jšœ ˜Jšœ˜J˜J˜—Jšœ[™[Jšœ]™]Jšœ\™\Jšœ[™[J˜šžœœœ˜8Jš œœœœ˜5J˜Jšœ2œ˜DJ˜J˜Jšœœœœ˜J˜J˜*J˜.J˜(J˜J˜J˜šœœœ˜J˜+J˜*Jš˜J˜—Jšœ˜Jšœ˜J˜J˜—Jšœ^™^JšœU™UJ˜Jš ž œœœœœœ˜T˜Jšœ2œ˜CJ˜J˜Jšœœœœ˜J˜J˜+J˜'J˜'J˜J˜J˜Jšœ˜Jšœ˜J˜J˜—JšœZ™ZJšœS™SJ˜š žœœœœœ ˜MJšœœœ˜J˜Jšœ2œ˜CJ˜J˜Jšœœœœ˜J˜J˜*J˜'J˜J˜J˜Jšœœœœ˜J˜Jšœ(œ#˜ZJ˜Jšœ˜Jšœ˜J˜J˜—Jšœ[™[Jšœ\™\Jšœ[™[J˜š ž œœœœœ ˜NJšœœœ˜J˜Jšœ2œ˜CJ˜J˜Jšœœœœ˜J˜J˜+J˜'Jšœ"œ ˜JJ˜J˜J˜J˜Jšœ˜Jšœ˜J˜J˜—JšœX™XJšœ,™,J˜Jšžœœœœœœœ˜K˜Jšœœ˜"Jšœ œœ˜Jšœ œœ˜"Jšœœ˜J˜šœ œ3˜BJšœ/œ˜;J˜—J˜-Jšœœœœ˜J˜J˜(Jš œœœœœ˜9Jšœœ˜,Jšœ œ˜J˜šœœœ˜&J˜Jšœœœœ˜Jšœœ ˜CJšœ˜J˜—J˜#J˜˜FJ˜AJ˜—Jšœ ˜Jšœ˜J˜J˜—JšœW™WJšœ™J˜Jšž œœœœœœœ˜L˜Jšœœ˜#Jšœœ˜ Jšœ œœ˜Jšœ œœ˜"J˜šœ œ/˜>šœ1œ˜=J˜——J˜2Jšœœœœ˜J˜J˜(J˜ J˜6J˜Jšœœ˜Jšœ œ˜J˜šœœœ˜'Jšœ#œ#˜UJ˜ Jš œœœœœ˜+Jšœ˜J˜—J˜!Jšœœœœ˜J˜˜HJ˜;—Jšœ ˜Jšœ˜J˜J˜—JšœY™YJšœ&™&J˜š žœœœœœ˜KJšœœœ˜J˜$Jšœœœœ˜J˜#Jšœœœœ˜˜NJ˜M—Jšœ˜J˜J˜—JšœQ™QJ˜š žœœœœœ˜JJšœœœ˜šœ œœœ>˜XJš˜J˜ Jšœ˜ Jšœ˜—J˜/J˜˜MJ˜M—Jšœ ˜Jšœ˜J˜J˜Jšœ\™\Jšœ\™\JšœD™DJ˜—š žœœœœœ ˜SJšœœœ˜J˜J˜!J˜+Jšœœœœ˜J˜J˜"Jšœœœœ˜J˜Jšœœ˜1Jšœ"œ ˜JJ˜HJ˜Jšœ˜J˜J˜—JšœA™AJ˜š žœœœœœ ˜TJšœœœœ˜ J˜Jšœœ˜1Jšœ%œ#˜WJ˜J˜+Jšœœœœ˜J˜J˜#Jšœœœœ˜J˜HJšœ˜J˜J˜J˜—JšœM™MJ˜šž œœœ˜ J˜Jšœ˜J˜J˜—šž œœœ˜!J˜Jšœ˜J˜—Jšœ˜J˜J˜—…—.lMÜ