DIRECTORY BasicTime, Commander, CommandTool, Convert, FS, GVNames, IO, PDQueue, PDRemoteStream, PDUser, Process, Pup, PupName, PupStream, PupWKS, Rope, RuntimeError USING [UNCAUGHT], UserCredentials USING [Get], ViewerIO USING [CreateViewerStreams], WatchStats USING [WatchStatsRecord, GetWatchStats]; PDUserImpl: PROGRAM IMPORTS BasicTime, Commander, CommandTool, Convert, FS, GVNames, IO, PDQueue, PDRemoteStream, Process, PupName, PupStream, Rope, RuntimeError, UserCredentials, ViewerIO, WatchStats EXPORTS PDUser = BEGIN ROPE: TYPE ~ Rope.ROPE; helloMsg: ROPE _ "Cedar Peach PD Print Server"; CommandCode: TYPE = {login, cancel, cancelReprint, check, help, listQueue, messages, print, reprint, resetQueue, setLoginMessage, start, stop, wait, watch, quit, ambiguous, illegal}; commandTable: ARRAY CommandCode[login..quit] OF ROPE _ [ login: "Login", cancel: "Cancel", cancelReprint: "CancelReprint", check: "Check", help: "Help", listQueue: "ListQueue", messages: "Messages", print: "Print", reprint: "Reprint", resetQueue: "ResetQueue", setLoginMessage: "SetLoginMessage", start: "Start", stop: "Stop", wait: "Wait", watch: "Watch", quit: "Quit" ]; Upper: PROC [ch: CHAR] RETURNS [CHAR] = INLINE { RETURN [IF ch IN ['a..'z] THEN ch - ('a - 'A) ELSE ch] }; defaultRegistry: ROPE _ ".pa"; loginMessage: ROPE _ NIL; TalkWithUser: PUBLIC PROC [stream: IO.STREAM] = { user: ROPE _ NIL; loggedIn: BOOLEAN _ FALSE; TRUSTED BEGIN ENABLE {PupStream.StreamClosing => GOTO Closing; PupStream.Timeout => GOTO TimeOut}; string: REF TEXT _ NEW [TEXT [180]]; password: ROPE; account: ROPE; quitting: BOOLEAN _ FALSE; accessAllowed: BOOLEAN _ TRUE; echo: BOOLEAN _ TRUE; flushed: BOOLEAN _ FALSE; commandCode: CommandCode; lastRequest: INT _ -1; PutChar: PROC [char: CHAR] = { IO.PutChar[stream, char]; flushed _ FALSE; }; PutString: PROC [string: ROPE] = { IO.PutRope[stream, string]; flushed _ FALSE; }; PutOK: PROC = {PutString["\n\lok\n\l"]}; PutXXX: PROC = { PutString[" XXX\n\l"]}; sep: CHAR _ ' ; DelHit: ERROR = CODE; SendNow: PROC = {IO.Flush[stream]; flushed _ TRUE}; GetChar: PROC RETURNS [CHAR] = { c: CHAR; ignore: INT _ 0; IF NOT flushed THEN SendNow[]; WHILE ignore >= 0 DO mark: NAT _ 0; timingMark: NAT = 5; timingMarkReply: NAT = 6; dataMark: NAT = 1; charsAvail: INT _ 0; bytes: PACKED ARRAY [0..4] OF CHAR; charsAvail _ IO.UnsafeGetBlock[stream, [LOOPHOLE[LONG[@bytes]], 0, 1]]; IF charsAvail # 0 THEN c _ bytes[0] ELSE { gotMark: BOOL _ FALSE; IF stream.GetInfo.class = $Pup THEN { gotMark _ TRUE; mark _ PupStream.ConsumeMark[stream ! RuntimeError.UNCAUGHT => {gotMark _ FALSE; CONTINUE}]; }; }; SELECT mark FROM 0 => NULL; dataMark => {ignore _ 1}; timingMark => {ignore _ 1}; ENDCASE => ignore _ 2; ignore _ ignore - 1; ENDLOOP; IF c = '\177 THEN {PutXXX[]; ERROR DelHit}; RETURN [c] }; GetStringToSpace: PROC [stopper1: CHAR _ ' , stopper2: CHAR _ '\t] RETURNS [r: ROPE] = { c: CHAR _ GetChar[]; dashCount: NAT _ 0; inComment: BOOLEAN _ FALSE; commentHit: BOOLEAN _ FALSE; string.length _ 0; UNTIL string.length = string.maxLength OR (NOT inComment AND (c=stopper1 OR c=stopper2)) OR c='\n DO IF c= 'H - 100B OR c= 'A - 100B THEN { IF commentHit THEN {PutXXX[]; ERROR DelHit}; IF string.length > 0 THEN { IF echo THEN PutChar[c]; string.length _ string.length - 1; }; } ELSE IF c= 'W - 100B THEN { IF commentHit THEN {PutXXX[]; ERROR DelHit}; WHILE string.length > 0 DO IF echo THEN PutChar['H - 100B]; string.length _ string.length - 1; ENDLOOP; } ELSE { IF echo THEN PutChar[c]; IF c = '- THEN { commentHit _ TRUE; dashCount _ dashCount + 1; IF dashCount = 2 THEN { inComment _ NOT inComment; dashCount _ 0; }; } ELSE { WHILE dashCount > 0 DO IF NOT inComment THEN { string[string.length] _ '-; string.length _ string.length + 1; }; dashCount _ dashCount - 1; ENDLOOP; IF NOT inComment THEN { string[string.length] _ c; string.length _ string.length + 1; }; }; }; c _ GetChar[]; ENDLOOP; IF string.length <= string.maxLength THEN sep _ c ELSE sep _ ' ; IF string.length = 0 AND sep # '\n THEN { PutChar[sep]; IO.Flush[stream]; RETURN[GetStringToSpace[stopper1, stopper2]]; }; IO.Flush[stream]; RETURN [Rope.FromRefText[string]]; }; GetStringToCR: PROC [] RETURNS [ROPE] = { RETURN[GetStringToSpace['\n, '\n]]; }; GetNumber: PROC [default: INT _ 1] RETURNS [value: INT _ 0] = { r: ROPE _ GetStringToSpace[]; IF Rope.Length[r] = 0 THEN value _ default ELSE value _ Convert.IntFromRope[r ! Convert.Error => {PutString["Not a vailid number.\n\l"]; value _ default; CONTINUE}]; }; Confirm: PROC RETURNS [yes: BOOLEAN _ FALSE] = { response: ROPE _ GetStringToSpace[]; IF Rope.Equal[response, "Yes", FALSE] THEN { RETURN [TRUE] } ELSE IF Rope.Length[response] = 0 OR Upper[Rope.Fetch[response, 0]] # 'N THEN { PutString["Sorry, you must say Yes in just the right way.\n\l"]; RETURN [FALSE] } ELSE PutXXX[] }; GetCommand: PROC = { nMatches: NAT _ 0; matchLength: NAT _ 0; command: ROPE; commandCode _ illegal; PutString[">>"]; command _ GetStringToSpace[]; IF Rope.Length[command] = 0 THEN {PutChar['\n]; PutChar['\l]; IO.Flush[stream]; GetCommand[]} ELSE { FOR cmd: CommandCode IN [login..quit] DO candidate: ROPE _ commandTable[cmd]; IF Rope.Length[command] <= Rope.Length[candidate] THEN { matchLength _ 0; FOR i: INT IN [0 .. Rope.Length[command]) DO IF Upper[Rope.Fetch[command, i]] = Upper[Rope.Fetch[candidate, i]] THEN matchLength _ i+1 ELSE EXIT; ENDLOOP; IF matchLength = Rope.Length[command] THEN { commandCode _ cmd; IF matchLength = Rope.Length[candidate] THEN {nMatches _ 1; EXIT}; nMatches _ nMatches + 1; IF NOT loggedIn THEN EXIT }; }; ENDLOOP; IF nMatches > 1 THEN commandCode _ ambiguous ELSE IF commandCode <= quit THEN { candidate: ROPE _ commandTable[commandCode]; FOR i: INT IN [Rope.Length[command] .. Rope.Length[candidate]) DO PutChar[Rope.Fetch[candidate, i]]; ENDLOOP; IO.Flush[stream]; }; }; }; PutRequestStatus: PROC [request: PDQueue.Request, status: PDQueue.RequestStatus] = { PutString[IO.PutFR["%g %g", IO.rope[request.requestTime], IO.rope[request.fileName]]]; IF request.copies # 1 THEN PutString[IO.PutFR1[" (%g copies) ", IO.int[request.copies]]]; PutString[IO.PutFR[" (%g) %g\n\l", IO.rope[request.requestor], IO.rope[(SELECT status FROM canceled => "Cancelled", waiting => "Waiting", printing => "Printing", ENDCASE => NIL)]]]; }; DoCommand: PROC = { GetCommand[]; IF NOT loggedIn AND commandCode # login AND commandCode # quit THEN { PutString["Please log in.\n\l"] } ELSE { SELECT commandCode FROM login => { registryMissing: BOOLEAN _ TRUE; PutString[" --User-- "]; user _ GetStringToSpace[]; registryMissing _ Rope.Find[user, "."] < 0; IF registryMissing THEN { user _ Rope.Concat[user, defaultRegistry]; PutString[defaultRegistry]; }; PutString[" --Password-- "]; echo _ FALSE; password _ GetStringToSpace[ ! UNWIND => echo _ TRUE]; echo _ TRUE; IF sep # '\n THEN { PutString[" --Account-- "]; account _ GetStringToSpace[]; }; PutString[" -- Authenticating ... "]; SendNow[]; SELECT GVNames.Authenticate[user, password] FROM individual => {PutString["OK"]; loggedIn _ TRUE}; allDown => {PutString["all GV servers down; I'll have to trust you."]; loggedIn _ TRUE}; badPwd => {PutString["bad password"]; loggedIn _ FALSE}; ENDCASE => {PutString["bad name"]; loggedIn _ FALSE}; PutChar['\n]; PutChar['\l]; IO.Flush[stream]; IF loginMessage.Length > 0 THEN { IO.PutF1[stream, "%g\n\l", IO.rope[loginMessage]]; }; }; print => { fileName: ROPE; separator: ROPE; time: ROPE; requestNumber: INT _ 0; createDate: ROPE _ NIL; bytes: INT _ 0; copies: INT _ 1; IF NOT accessAllowed THEN { PutString["\n\lSorry, you are not allowed to queue requests at this time."]; }; PutString[" --File-- "]; fileName _ GetStringToSpace[]; IF sep # '\n THEN { PutString[" --Copies-- "]; copies _ MIN[GetNumber[default: 1], CARDINAL.LAST]; }; IF sep # '\n THEN { PutString[" --Title-- "]; separator _ GetStringToCR[]; }; [bytes, createDate] _ PDRemoteStream.Lookup[fileName, BasicTime.nullGMT, user, password ! PDRemoteStream.Error => {IO.PutF1[stream, "Error: %g\n\l", IO.rope[expl]]; GOTO Bad}]; time _ TimeToRope[]; requestNumber _ PDQueue.QueueRequest[[fileName, time, user, password, separator, copies]]; IF requestNumber < 0 THEN {PutString["\n\lPrint queue full, request denied.\n\l"]; GOTO Bad}; PutString[IO.PutFR["\n\lPrint request %g queued for %g of %g (%g bytes)\n\l", IO.int[requestNumber], IO.rope[fileName], IO.rope[createDate], IO.int[bytes]]]; BEGIN msg: ROPE _ IO.PutFR["Version of %g; %g bytes.", IO.rope[createDate], IO.int[bytes]]; PDQueue.LogMessage[msg, requestNumber]; END; lastRequest _ requestNumber; EXITS Bad => NULL }; check => { requestNumber: INT; action: PROC [request: PDQueue.Request, status: PDQueue.RequestStatus] = { PutString[IO.PutFR1["%g ", IO.int[requestNumber]]]; IF status = notFound THEN { PutString[" not found.\n\l"]; } ELSE PutRequestStatus[request, status]; }; PutString[" --Request Number<"]; IF lastRequest # -1 THEN IO.Put[stream, IO.int[lastRequest]]; PutString[">-- "]; lastRequest _ requestNumber _ GetNumber[default: lastRequest]; IF requestNumber = -1 THEN {PutChar['\n]; PutChar['\l]} ELSE IF ABS[requestNumber] > NAT.LAST THEN action[[NIL,NIL,NIL,NIL,NIL,1], notFound] ELSE PDQueue.CheckRequest[requestNumber, action]; }; cancel => { requestNumber: INT; PutString[" --Request Number-- "]; requestNumber _ GetNumber[default: -1]; IF requestNumber = -1 THEN {PutChar['\n]; PutChar['\l]} ELSE { owner: ROPE _ NIL; reqSatus: PDQueue.RequestStatus _ notFound; action: PROC [request: PDQueue.Request, status: PDQueue.RequestStatus] = { reqSatus _ status; IF status # notFound THEN owner _ request.requestor; }; PDQueue.CheckRequest[requestNumber, action]; PutChar['\n]; IF PDQueue.CancelRequest[requestNumber].ok THEN { msg: ROPE; IF reqSatus = printing THEN PDQueue.CancelReprint[]; msg _ IO.PutFR["Cancelled by %g", IO.rope[user]]; PDQueue.LogMessage[msg, requestNumber, IF Rope.IsEmpty[owner] THEN NIL ELSE owner]; PutString["Print request cancelled."]; } ELSE {PutString["No such request in queue."]}; PutChar['\n]; PutChar['\l]; }; }; cancelReprint => { PDQueue.CancelReprint[]; PutOK[]; }; listQueue => { action: PROC [requestNumber: CARDINAL, request: PDQueue.Request, status: PDQueue.RequestStatus] RETURNS [continue: BOOLEAN _ TRUE] = { IO.Put[stream, IO.int[requestNumber]]; PutChar[' ]; PutRequestStatus[request, status]; }; PutChar['\n]; IF PDQueue.GetSuspended[] THEN { PutString[" *** Printing is suspended ***"]; PutChar['\n]; PutChar['\l]; }; PDQueue.EnumerateRequests[action]; }; messages => { action: PROC [message: ROPE] RETURNS [continue: BOOLEAN _ TRUE] = { IF Match[message] THEN { PutString[message]; PutChar['\n]; PutChar['\l]; }; }; key: ROPE _ NIL; Match: PROC [message: ROPE] RETURNS [BOOLEAN] = { IF Rope.IsEmpty[key] THEN RETURN [TRUE]; RETURN [Rope.Find[message, key]>=0]; }; IF sep # '\n THEN { PutString[" --matching string-- "]; key _ GetStringToCR[]; }; PutChar['\n]; PutChar['\l]; PDQueue.EnumerateMessages[action]; }; start => { IF PDQueue.SetSuspended[FALSE].old = TRUE THEN { PDQueue.LogMessage["Printing started.",,user]; }; PutOK[]; }; stop => { IF PDQueue.SetSuspended[TRUE].old = FALSE THEN { PDQueue.LogMessage["Printing suspended.",,user]; }; PutOK[]; }; setLoginMessage => { old: ROPE _ loginMessage; PutChar['\n]; loginMessage _ GetStringToCR[]; loginMessage _ IO.PutFR["%g (%g)\n\l", IO.rope[loginMessage], IO.rope[user]]; PutString["Old message was: "]; PutString[old]; PutChar['\n]; PutChar['\l]; PDQueue.LogMessage[loginMessage]; }; reprint => { ncopies: INT _ 0; confirm: BOOLEAN _ TRUE; PutString[" --Number of copies-- "]; ncopies _ GetNumber[default: 0]; IF ncopies > 1 THEN { PutString[" --Confirm reprint of multiple copies-- "]; confirm _ Confirm[]; }; IF confirm THEN { msg: ROPE _ IO.PutFR["Reprint request for %g", IO.int[ncopies]]; IF ncopies = 1 THEN msg _ Rope.Concat[msg, " copy"] ELSE msg _ Rope.Concat[msg, " copies"]; PDQueue.LogMessage[msg,,user]; PDQueue.Reprint[ncopies]; PutOK[]; } ELSE PutXXX[]; }; resetQueue => { PutString[" --Do you really want to reset the entire queue?-- "]; IF Confirm[] THEN { PutOK[]; PDQueue.Reset[]; PDQueue.LogMessage["Queue reset",,user]; }; }; help, illegal => { PutString["\n\lValid commands are: "]; FOR cmd: CommandCode IN [login..quit] DO candidate: ROPE _ commandTable[cmd]; PutString[candidate]; IF cmd # quit THEN PutString[", "]; ENDLOOP; PutChar['\n]; PutChar['\l]; }; quit => {loggedIn _ FALSE; PutChar['\n]; PutChar['\l]; SendNow[]; quitting _ TRUE}; watch => { stats: WatchStats.WatchStatsRecord _ WatchStats.GetWatchStats[]; IO.PutF[stream, " Free disk:\t%g\n\l", IO.int[stats.diskFree]]; IO.PutF[stream, " Free mds:\t%g\n\l", IO.int[stats.mdsFree]]; IO.PutF[stream, " Free gfi:\t%g\n\l", IO.int[stats.gfiFree]]; IO.PutF[stream, " Free VM:\t%g\n\l", IO.int[stats.vmFree]]; IO.PutF[stream, " VM run:\t%g\n\l", IO.int[stats.vmRun]]; IO.PutF[stream, " Load:\t%g\n\l", IO.real[stats.cpuLoad]]; flushed _ FALSE; }; wait => { requestNumber: INT _ lastRequest; requestStatus: PDQueue.RequestStatus _ waiting; action: PROC [request: PDQueue.Request, status: PDQueue.RequestStatus] = { requestStatus _ status; }; toldUserThatItIsPrinting: BOOLEAN _ FALSE; DoPrintingMsg: PROC = { IF requestStatus = printing AND NOT toldUserThatItIsPrinting THEN { PutString["Printing..."]; toldUserThatItIsPrinting _ TRUE; }; }; IF sep # '\n THEN { PutString[" --Request Number<"]; IF lastRequest # -1 THEN IO.Put[stream, IO.int[lastRequest]]; PutString[">-- "]; lastRequest _ requestNumber _ GetNumber[default: lastRequest]; }; PutChar['\n]; PutChar['\l]; IF requestNumber IN [0..NAT.LAST] THEN { PDQueue.CheckRequest[requestNumber, action]; IF requestStatus = waiting THEN { PutString["Waiting..."]; }; DoPrintingMsg[]; UNTIL requestStatus = notFound OR requestStatus = canceled DO SendNow[]; Process.Pause[Process.MsecToTicks[10000]]; PDQueue.CheckRequest[requestNumber, action]; DoPrintingMsg[]; PutChar['.]; ENDLOOP; PutString["Done.\n\l"]; }; }; ambiguous => { PutString["\nAmbiguous command (type Help for help)\n\l"]; }; ENDCASE => ERROR; }; }; IO.PutF1[stream, "\n\l%g\n\l", IO.rope[helloMsg]]; UNTIL loggedIn OR quitting DO DoCommand[! DelHit => CONTINUE] ENDLOOP; IF loggedIn THEN PDQueue.LogMessage["Login", , user]; WHILE loggedIn DO DoCommand[! DelHit => CONTINUE] ENDLOOP; PDQueue.LogMessage["Logout", , user]; EXITS Closing => { IF loggedIn THEN PDQueue.LogMessage["Logged off due to remote close", , user]; }; TimeOut => { IF loggedIn THEN PDQueue.LogMessage["Logged off due to TimeOut", , user]; }; END; }; monthName: ARRAY BasicTime.MonthOfYear OF Rope.ROPE ~ ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", "???"]; TimeToRope: PROC [time: BasicTime.GMT _ BasicTime.nullGMT] RETURNS [r: ROPE] = BEGIN unpacked: BasicTime.Unpacked _ BasicTime.Unpack[(IF time = BasicTime.nullGMT THEN BasicTime.Now[] ELSE time)]; stream: IO.STREAM _ IO.ROS[]; zoneChar: CHAR _ ' ; IO.PutF[stream, "%2g-%g-%02g ", IO.int[unpacked.day], IO.rope[monthName[unpacked.month]], IO.int[unpacked.year MOD 100]]; IO.PutF[stream, "%2g:%02g:%02g", IO.int[unpacked.hour], IO.int[unpacked.minute], IO.int[unpacked.second]]; r _ IO.RopeFromROS[stream]; END; abortCurrent: REF BOOL _ NEW[BOOL _ FALSE]; logStream: IO.STREAM _ NIL; MakeLogViewer: PROC = BEGIN IF logStream # NIL THEN RETURN; logStream _ ViewerIO.CreateViewerStreams["Peach log"].out; PDQueue.RegisterTTY[logStream]; END; PrintFile: PUBLIC PROC [fileName: Rope.ROPE, stream: IO.STREAM] = BEGIN copies: INT _ 1; requestNumber, bytes: INT _ 0; user, password: ROPE; [user, password] _ UserCredentials.Get[]; IF fileName # NIL THEN {fileName _ FS.ExpandName[fileName].fullFName; [bytes: bytes] _ PDRemoteStream.Lookup[fileName, BasicTime.nullGMT, NIL, NIL ! PDRemoteStream.Error => {PDQueue.LogMessage[expl]; GOTO Out}]}; requestNumber _ PDQueue.QueueRequest[[fileName, TimeToRope[], user, password, NIL, copies]]; EXITS Out => RETURN; END; RePrintIt: Commander.CommandProc = TRUSTED BEGIN MakeLogViewer[]; PrintFile[NIL, cmd.out ! FS.Error => {msg _ error.explanation; GOTO Complain}; ]; EXITS Complain => {RETURN [$Failure, msg]}; END; PrintIt: Commander.CommandProc = TRUSTED BEGIN tokens: LIST OF ROPE ~ CommandTool.ParseToList[cmd].list; usageMessage: ROPE ~ "Usage: PDPrint filename\n"; MakeLogViewer[]; SELECT TRUE FROM tokens=NIL => msg _ usageMessage; ENDCASE => PrintFile[tokens.first, cmd.out ! FS.Error => {msg _ error.explanation; GOTO Complain}; ]; EXITS Complain => {RETURN [$Failure, msg]}; END; NewConnection: SAFE PROCEDURE [stream: IO.STREAM, clientData: REF ANY, remote: Pup.Address] = TRUSTED { otherGuy: Rope.ROPE _ PupName.AddressToRope[remote]; TalkWithUser[stream ! PupStream.StreamClosing => CONTINUE]; IO.Close[stream]; }; PeachIt: Commander.CommandProc = TRUSTED BEGIN MakeLogViewer[]; IF pupListener = NIL THEN {IO.PutRope[cmd.out, "Started Peach\n"]; pupListener _ PupStream.CreateListener[ local: PupWKS.telnet, worker: NewConnection, getTimeout: 300000, -- 5 minutes putTimeout: 300000 -- 5 minutes ]; PDQueue.LogMessage["Started Peach"]} ELSE {IO.PutRope[cmd.out, "Stopped Peach\n"]; PupStream.DestroyListener[pupListener]; PDQueue.LogMessage["Stop Peach"]; pupListener _ NIL}; END; pupListener: PupStream.Listener _ NIL; Commander.Register["PDReprint", RePrintIt, "Reprints last image."]; Commander.Register["PDPrint", PrintIt, "Prints PD files"]; Commander.Register["Peach", PeachIt, "Enables Peach server"]; END.... όPDUserImpl.mesa Copyright (C) 1984, 1986, Xerox Corporation. All rights reserved. Michael Plass, October 31, 1984 10:18:59 am PST Tim Diebert: February 17, 1986 1:53:38 pm PST Dave Rumph, August 8, 1986 12:55:06 pm PDT PupStream.ListenerProc Κ]˜code™K™BK™/K™-K™*K™—šΟk ˜ K˜ K˜ K˜ K˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜K˜K˜K˜ K˜Kšœ˜Kšœ œœ˜Kšœœ˜Kšœ œ˜%Kšœ œ#˜3K˜—šΠln œ˜Kšœ-œ œq˜΄Kšœ˜šœ˜K˜—Kšœœœ˜K˜šœ œ!˜/K˜—šœ œ₯˜ΆK˜—šœœœœ˜8Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ ˜ Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ#˜#Kšœ˜Kšœ ˜ K˜ Kšœ˜Kšœ ˜ Kšœ˜K˜—š Οnœœœœœœ˜0Kš œœœ œœ˜6K˜K˜—šœœ ˜K˜—šœœœ˜K˜—š Ÿ œœœ œœ˜1Kšœœœ˜Kšœ œœ˜šœ˜ šœœ ˜0Kšœœ ˜#—Kš œœœœœ˜$Kšœ œ˜Kšœ œ˜Kšœ œœ˜Kšœœœ˜Kšœœœ˜Kšœ œœ˜K˜Kšœ œ˜Kš Ÿœœœœ"œ˜LKš Ÿ œœ œœ$œ˜RKšŸœœ˜(KšŸœœ˜(Kšœœ˜Kšœœœ˜KšŸœœœœ˜3šŸœœœœ˜ Kšœœ˜Kšœœ˜Kšœœ œ ˜šœ ˜Kšœœ˜Kšœ œ˜Kšœœ˜Kšœ œ˜Kšœ œ˜Kš œœœœœ˜#Kšœ œœœ˜Gšœœ ˜#šœ˜Kšœ œœ˜šœœ˜%Kšœ œ˜Kšœ3œœœ˜\Kšœ˜—Kšœ˜——šœ˜Kšœœ˜ Kšœ˜Kšœ˜Kšœ˜—Kšœ˜Kšœ˜—Kšœ œ œ ˜+Kšœ˜ Kšœ˜—š Ÿœœ œœœœ˜XKšœœ ˜Kšœ œ˜Kšœ œœ˜Kšœ œœ˜Kšœ˜Kšœ!˜&Kšœœ œ œ ˜1šœ˜ šœœœ˜&Kšœ œ œ ˜,šœœ˜Kšœœ ˜Kšœ"˜"Kšœ˜—Kšœ˜—šœœ˜Kšœ œ œ ˜,šœ˜Kšœœ˜ Kšœ"˜"Kšœ˜—Kšœ˜—šœ˜Kšœœ ˜šœœ˜Kšœ œ˜Kšœ˜šœœ˜Kšœ œ ˜Kšœ˜Kšœ˜—Kšœ˜—šœ˜šœ˜šœœ œ˜Kšœ˜Kšœ"˜"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šœ˜—šœœœ%œ˜OKšœ@˜@Kšœœ˜Kšœ˜—Kšœ ˜ Kšœ˜—šŸ œœ˜Kšœ œ˜Kšœ œ˜Kšœ œ˜Kšœ˜Kšœ˜Kšœ˜Kšœœœ˜]šœ˜šœœ˜(Kšœ œ˜$šœ0œ˜8Kšœ˜šœœœ˜,šœ@˜BKšœœœ˜!—Kšœ˜—šœ$œ˜,Kšœ˜Kšœ&œœ˜BKšœ˜Kšœœ œ˜Kšœ˜—Kšœ˜—Kšœ˜—Kšœœ˜,šœœœ˜"Jšœ œ˜,šœœœ2˜AJšœ"˜"Jšœ˜—Kšœ˜Kšœ˜—Kšœ˜—Kšœ˜—šŸœœ>˜TKšœ œœœ˜VKšœœ œœ˜Yšœ œœ#œ˜ZKšœGœœ˜Z—Kšœ˜—šŸ œœ˜K˜ š œœ œœœ˜EKšœ˜Kšœ˜—šœ˜šœ ˜šœ ˜ Kšœœœ˜ Kšœ˜K˜Kšœ+˜+šœœ˜Kšœ*˜*Kšœ˜Kšœ˜—Kšœ˜Kšœœ˜ Kšœœ œ˜6Kšœœ˜ šœ œ˜Kšœ˜K˜Kšœ˜—Kšœ%˜%Kšœ ˜ šœ&˜0Kšœ+œ˜1KšœRœ˜XKšœ1œ˜8Kšœ'œ˜5—Kšœ ˜ Kšœ ˜ Kšœ˜šœœ˜!Kšœœ˜2Kšœ˜—Kšœ˜—šœ ˜ Kšœ œ˜Kšœ œ˜Kšœœ˜ Kšœœ˜Kšœ œœ˜Kšœœ˜Kšœœ˜šœœœ˜KšœL˜LKšœ˜—Kšœ˜Kšœ˜šœ œ˜Kšœ˜Kšœ œœœ˜3Kšœ˜—šœ œ˜Kšœ˜Kšœ˜Kšœ˜—šœY˜YKšœœ œœ˜V—K˜KšœZ˜ZKšœœ:œ˜]Kš œ œBœœœœ˜š˜Kš œœœ#œœ ˜UKšœ'˜'Kšœ˜—Kšœ˜Kšœ˜Kšœ˜—šœ ˜ Kšœœ˜šœœ>˜JKšœ œœ˜3šœœ˜Kšœ˜Kšœ˜—Kšœ#˜'Kšœ˜—Kšœ ˜ Kšœœœ œ˜=Kšœ˜Kšœ>˜>Kšœœ˜7Kšœœœœœœ œœœœœ˜TKšœ-˜1Kšœ˜—šœ ˜ Kšœœ˜Kšœ"˜"Kšœ'˜'Kšœœ˜7šœ˜Kšœœœ˜Kšœ+˜+šœœ>˜JKšœ˜Kšœœ˜4Kšœ˜—Kšœ,˜,Kšœ ˜ šœ)œ˜1Kšœœ˜ Kšœœ˜4Kšœœœ ˜1Kš œ'œœœœ˜SKšœ&˜&Kšœ˜—Kšœ*˜.Kšœ˜Kšœ˜—Kšœ˜—šœ˜Kšœ˜K˜Kšœ˜—šœ˜š œœœ;œ œœ˜†Kšœ œ˜&Kšœ ˜ Kšœ"˜"Kšœ˜—Kšœ ˜ šœœ˜ Kšœ-˜-Kšœ˜Kšœ˜—Kšœ"˜"Kšœ˜—šœ ˜ š œœ œœ œœ˜Cšœœ˜Kšœ˜Kšœ˜Kšœ˜—Kšœ˜—Kšœœœ˜š Ÿœœ œœœ˜1Jšœœœœ˜(Jšœ˜$Kšœ˜—šœ œ˜Kšœ#˜#Kšœ˜Kšœ˜—Kšœ˜Kšœ"˜"Kšœ˜—šœ ˜ šœœœœ˜0Kšœ.˜.Kšœ˜—K˜Kšœ˜—šœ ˜ šœœœœ˜0Kšœ0˜0Kšœ˜—K˜Kšœ˜—šœ˜Kšœœ ˜J˜ Kšœ˜Kšœœœœ ˜MKšœ˜Kšœ˜Kšœ˜Kšœ!˜!Kšœ˜—šœ ˜ Kšœ œ˜Kšœ œœ˜Kšœ$˜$Kšœ ˜ šœ œ˜Kšœ6˜6K˜Kšœ˜—šœ œ˜Kšœœœ!œ˜@Kšœ œ ˜3Kšœ#˜'Kšœ˜Kšœ˜K˜Kšœ˜—Kšœ ˜Kšœ˜—šœ˜KšœA˜Ašœ œ˜K˜K˜K˜(Kšœ˜—Kšœ˜—šœ˜Kšœ&˜&šœœ˜(Kšœ œ˜$Kšœ˜Kšœ œ˜#Kšœ˜—Kšœ˜Kšœ˜—Kšœœ4œ˜S˜ Kšœ@˜@Kšœ&œ˜@Kšœ%œ˜>Kšœ%œ˜>Kšœ$œ˜˜JKšœ˜Kšœ˜—Kšœœœ˜*šŸ œœ˜šœœœœ˜CKšœ˜Kšœœ˜ Kšœ˜—Kšœ˜—šœ œ˜Kšœ ˜ Kšœœœ œ˜=Kšœ˜Kšœ>˜>Kšœ˜—Kšœ˜š œœœœœ˜(Kšœ,˜,šœœ˜!Kšœ˜Kšœ˜—Kšœ˜šœœ˜=Kšœ ˜ K˜*Kšœ,˜,Kšœ˜Kšœ ˜ Kšœ˜—Kšœ˜Kšœ˜—Kšœ˜—šœ˜Kšœ>˜>Kšœ˜—Kšœœ˜—Kšœ˜—Kšœ˜—Kšœœ˜2Kš œ œ œœœ˜FKšœ œ%˜5Kšœ œœœ˜:Kšœ%˜%šœ ˜Kšœ œ>˜NKšœ˜—šœ ˜ Kšœ œ9˜IKšœ˜—Kšœ˜—Kšœ˜—K˜šœ œœœ_˜’K˜—š Ÿ œœœœœ˜TKšœ1œœœ˜nKš œœœœœ˜Kšœ œ˜Kš œœœ"œœ˜yKšœœœœ˜jKšœœ˜Kšœ˜—K˜K˜Kš œœœœœœ˜+K˜Kšœ œœœ˜K˜šŸ œœ˜Kšœ œœœ˜Kšœ:˜:Kšœ˜Kšœ˜—K˜š Ÿ œœœœ œœ˜GKšœœ˜Kšœœ˜Kšœœ˜Kšœ)˜)šœ œ˜Kšœ œ ˜.šœDœœ˜NKšœ3œ˜?——KšœNœ ˜\Kšœœ˜Kšœ˜—K™šŸ œœ˜0Kšœ˜šœ œ ˜Kšœ$œ ˜5Kšœ˜—š˜Kšœ œ˜%—Kšœ˜K˜—šŸœœ˜.Kšœœœœ%˜9Kšœœ˜1Kšœ˜šœœ˜Kšœœ˜!šœ%˜,Kšœ$œ ˜5Kšœ˜——š˜Kšœ œ˜%—Kšœ˜K˜—šŸ œœ œ œœœœœ˜gK™Kšœœ!˜4Kšœ1œ˜;Kšœ˜Kšœ˜—K˜šŸœœ˜.Kšœ˜šœœœœM˜jKšœ˜Kšœ˜KšœΟc ˜ Kšœ  ˜K˜K˜$—Kšœœ~œ˜‹Kšœ˜K˜—šœ"˜&K˜—K˜CK˜:K˜=K˜Kšœ˜——…—D"]{