DIRECTORY BasicTime, Convert, GVNames, FS, IO, PrintingP4V3, Pup, PupName, PupStream, PupWKS, Rope, RuntimeError USING [UNCAUGHT], SimpleTerminal USING [TurnOn], WatchStats USING [WatchStatsRecord, GetWatchStats], XNSPSMessages, XNSPSPrint, XNSPSSpooler, XNSPSUser; XNSPSUserImpl: CEDAR PROGRAM IMPORTS BasicTime, Convert, GVNames, FS, IO, PupName, PupStream, Rope, RuntimeError, SimpleTerminal, WatchStats, XNSPSMessages, XNSPSPrint, XNSPSSpooler EXPORTS XNSPSUser = { OPEN PrintingP4V3; ROPE: TYPE ~ Rope.ROPE; helloMsg: ROPE _ "Cedar Interpress 3.0 Print Server Administrator Entry"; CommandCode: TYPE = {login, listFiles, listQueue, help, messages, setPrinterParams, watch, quit, ambiguous, illegal}; commandTable: ARRAY CommandCode[login..quit] OF ROPE _ [ login: "Login", help: "Help", listFiles: "ListFiles", listQueue: "ListQueue", messages: "Messages", setPrinterParams: "SetPrinterParams", 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; { 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] = TRUSTED {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[] }; EasyConfirm: PROC RETURNS [yes: BOOLEAN _ FALSE] = { response: ROPE _ GetStringToSpace[]; IF Rope.Equal[response, "Yes", FALSE] OR Rope.Equal[response, "Y", 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]; }; }; }; 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]]; }; }; listFiles => { { -- For enable ENABLE FS.Error => {PutString[error.explanation]; PutString["\n\l"]; GOTO Bad}; Proc: FS.NameProc = TRUSTED { -- [fullFName: ROPE] RETURNS [continue: BOOL]; PutString[fullFName]; PutString["\n\l"]; RETURN [TRUE]; }; fileNames: ROPE; cp: FS.ComponentPositions; PutString[" --Pattern-- "]; fileNames _ GetStringToSpace[]; IF fileNames = NIL THEN { PutString[" No file specified.\n\l"]; GOTO Bad; }; [fileNames, cp, ] _ FS.ExpandName[fileNames]; IF cp.server.length # 0 THEN { PutString[" Illegal file specified (not a local file).\n\l"]; GOTO Bad; }; FS.EnumerateForNames[fileNames, Proc]; }; -- For enable PutChar['\n]; PutChar['\l]; EXITS Bad => NULL }; listQueue => { Proc: PROC [id: PrintingP4V3.RequestID, status: PrintingP4V3.InterpressMasterStatus, printAttributes: PrintingP4V3.PrintAttributes, printOptions: PrintingP4V3.PrintOptions] RETURNS [continue: BOOL] = TRUSTED { md: MACHINE DEPENDENT RECORD [lo, hi: CARDINAL]; md.lo _ id[4]; md.hi _ id[3]; IO.PutF[stream, "ID: %g\tStatus: %g\n\l", IO.card[LOOPHOLE[md]], IO.rope[(SELECT status FROM pending => "pending", inProgress => "in progress", completed => "completed", completedWithWarning => "completed with warnings", unknown => "unknown", rejected => "rejected", aborted => "aborted", canceled => "canceled", held => "held", ENDCASE => NIL)] ]; IO.PutRope[stream, "Print Attributes:\n\l"]; IF printAttributes # NIL THEN FOR i: CARDINAL IN [0 .. printAttributes.length) DO option: Attribute _ printAttributes.body[i]; WITH option: option SELECT FROM printObjectName => {IO.PutF1[stream, " name: %g\n\l", IO.rope[option.printObjectName]]}; printObjectCreateDate => { time: BasicTime.GMT _ BasicTime.FromNSTime[option.printObjectCreateDate]; IO.PutF1[stream, " createTime: %g\n\l", IO.time[time]]}; senderName => {IO.PutF1[stream, " name: %g\n\l", IO.rope[option.senderName]]}; ENDCASE => NULL; ENDLOOP; IO.PutRope[stream, "Print Options:\n\l"]; IF printOptions # NIL THEN FOR i: CARDINAL IN [0 .. printOptions.length) DO option: Option _ printOptions.body[i]; WITH option: option SELECT FROM printObjectSize => IO.PutF1[stream, " size: %g\n\l", IO.card[option.printObjectSize]]; recipientName => IO.PutF1[stream, " recipientName: %g\n\l", IO.rope[option.recipientName]]; message => IO.PutF1[stream, " message: %g\n\l", IO.rope[option.message]]; copyCount => IO.PutF1[stream, " copies: %g\n\l", IO.card[option.copyCount]]; ENDCASE => NULL; ENDLOOP; IO.Flush[stream]; RETURN[TRUE]; }; XNSPSSpooler.EnumerateQueue[Proc]; }; 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[s1: message, s2: key, case: FALSE]>=0]; }; IF sep # '\n THEN { PutString[" --matching string-- "]; key _ GetStringToCR[]; }; PutChar['\n]; PutChar['\l]; XNSPSMessages.EnumerateMessages[action]; }; setPrinterParams => { sMargin, fMargin, newsMargin, newfMargin: CARDINAL; PutString[" \n\lGetting current parameters. "]; [sMargin, fMargin] _ XNSPSPrint.GetMargins[]; PutString[IO.PutFR[" -- Current Parameters: sMargin: %g, fMargin: %g \n\l", IO.card[sMargin], IO.card[fMargin]]]; PutString[" -- New sMargin<"]; IO.Put[stream, IO.card[sMargin]]; PutString[">-- "]; newsMargin _ GetNumber[default: sMargin]; IF newsMargin NOT IN [1..30] THEN { PutString[" --must be in [1..30]-- "]; PutChar['\n]; PutChar['\l]; RETURN; }; PutString[" -- New fMargin<"]; IO.Put[stream, IO.card[fMargin]]; PutString[">-- "]; newfMargin _ GetNumber[default: fMargin]; IF newfMargin NOT IN [10..43] THEN { PutString[" --must be in [10..43]-- "]; PutChar['\n]; PutChar['\l]; RETURN; }; PutString[" -- You sure? -- "]; IF EasyConfirm[] THEN { PutOK[]; XNSPSPrint.SetMargins[newsMargin, newfMargin]; [sMargin, fMargin] _ XNSPSPrint.GetMargins[]; XNSPSMessages.LogMessage[ IO.PutFR["Setting margins to: sMargin: %g, fMargin: %g", IO.card[sMargin], IO.card[fMargin]]]; }; }; 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; }; ambiguous => { PutString["\n\lAmbiguous 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 XNSPSMessages.LogMessage[IO.PutFR1["Login: %g", IO.rope[user]]]; WHILE loggedIn DO DoCommand[! DelHit => CONTINUE] ENDLOOP; XNSPSMessages.LogMessage[IO.PutFR1["%g logout", IO.rope[user]]]; EXITS Closing => { IF loggedIn THEN XNSPSMessages.LogMessage[IO.PutFR1["%g logged off due to remote close", IO.rope[user]]]; }; TimeOut => { IF loggedIn THEN XNSPSMessages.LogMessage[IO.PutFR1["%g logged off due to TimeOut", IO.rope[user]]]; }; }; }; logStream: IO.STREAM _ NIL; MakeLogViewer: PROC = { IF logStream # NIL THEN RETURN; logStream _ SimpleTerminal.TurnOn[helloMsg].out; XNSPSMessages.RegisterTTY[logStream]; }; NewConnection: PupStream.ListenerProc = TRUSTED { otherGuy: Rope.ROPE _ PupName.HisName[remote]; TalkWithUser[stream ! PupStream.StreamClosing => CONTINUE]; IO.Close[stream]; }; Init: PROC = TRUSTED { MakeLogViewer[]; pupListener _ PupStream.CreateListener[ local: PupWKS.telnet, worker: NewConnection, getTimeout: 300000, putTimeout: 300000 ]; }; pupListener: PupStream.Listener _ NIL; Init[]; }. ¬XNSPSUserImpl.mesa Copyright (C) 1984, 1985, 1987, Xerox Corporation. All rights reserved. Tim Diebert: January 30, 1987 10:13:27 am PST SimpleTerminal.TurnOff[]; Κˆ˜codešœ™K™HK™-—K™šΟk ˜ K˜ K˜Kšœ˜Kšœ˜Kšœ˜K˜ K˜K˜K˜ Kšœ˜Kšœ˜Kšœ œœ˜Kšœœ ˜Kšœ œ#˜3K˜K˜ K˜ Kšœ ˜ —K˜KšΠln œœ˜Kšœœœm˜˜Kšœ ˜šœœ˜K˜Kšœœœ˜K˜šœ œ;˜IK˜—šœ œd˜uK˜—šœœœœ˜8Kšœ˜K˜ Kšœ˜K˜Kšœ˜Kšœ%˜%Kšœ˜Kšœ ˜ Kšœ˜K˜—š Οnœœœœœœ˜0Kš œœœ œœ˜6K˜K˜—šœœ ˜K˜—šœœœ˜K˜—š Ÿ œœœ œœ˜1Kšœœœ˜Kšœ œœ˜šœœœœ ˜VKš œœœœœ˜$Kšœ œ˜Kšœ œ˜Kšœ œœ˜Kšœœœ˜Kšœœœ˜Kšœ œœ˜K˜Kšœ œ˜Kš Ÿœœœœ"œ˜LKš Ÿ œœ œœ$œ˜RKšŸœœ˜(KšŸœœ˜(Kšœœ˜Kšœœœ˜KšŸœœœœ˜3šŸœœœœœœ œ˜AKšœœ œ ˜šœ ˜Kš œœœœœ˜PKš œ œ œœœœ˜8Kšœ œœœ˜Gšœœ ˜#šœ œœ˜šœœ œ˜4Kšœ3œœœ˜^———Kšœœœ7œ˜hKšœ˜Kšœ˜—Kšœ œ œ ˜+Kšœ˜ Kšœ˜—šŸœœ œœœœœ ˜lKšœ œ˜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šœ˜—š Ÿ œœœœœ˜4Kšœ œ˜$š œœœœœ˜PKšœœ˜ 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šœœ˜,šœœœ˜"Kšœ œ˜,šœœœ2˜AKšœ"˜"Kšœ˜—Kšœ˜Kšœ˜—Kšœ˜—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šœΟc ˜šœœ;˜DKšœ˜ —šŸœœ œ /˜MKšœ˜Kšœ˜Kšœœ˜Kšœ˜—Kšœ œœ˜+Kšœ˜Kšœ˜šœ œœ˜Kšœ&˜&Kšœ˜ Kšœ˜—Kšœœ˜.šœœ˜Kšœ>˜>Kšœ˜ Kšœ˜—Kšœ$˜&Kšœ  ˜—Kšœ˜Kšœ˜Kšœ˜—šœ˜š Ÿœœ£œ œœ˜ΡKš œœ œœ œ˜0Kšœ˜šœ(œœ˜@šœœ˜K˜2K˜K˜2K˜CKšœ(œœ˜8—Kšœ˜—Kšœ*˜,šœ ˜šœœœ˜3Kšœ,˜,šœœ˜Kšœœ"œ ˜Zšœ˜Kšœœ6˜IKšœ(œ˜:—Kšœœ"œ˜PKšœœ˜—Kšœ˜——Kšœ'˜)šœœœ˜šœœœ˜0Kšœ&˜&šœœ˜Kšœœ"œ˜XKšœœ+œ˜]Kšœ œ%œ˜KKšœ œ$œ˜NKšœœ˜—Kšœ˜——Kšœ˜Kšœœ˜ Kšœ˜—Kšœ"˜"Kšœ˜—šœ ˜ š Ÿœœ œœ œœ˜Cšœœ˜Kšœ˜Kšœ˜Kšœ˜—Kšœ˜—Kšœœœ˜š Ÿœœ œœœ˜1Kšœœœœ˜(Kšœ(œ˜9Kšœ˜—šœ œ˜Kšœ#˜#Kšœ˜Kšœ˜—Kšœ˜Kšœ(˜(Kšœ˜—šœ˜Kšœ*œ˜3Kšœ/˜/Kšœ-˜-Kšœ œAœœ˜rKšœ˜Kšœ œ˜!Kšœ˜Kšœ)˜)šœ œœ œ˜#Kšœ&˜&Kšœ˜Kšœ˜Kšœ˜—Kšœ˜Kšœ œ˜!Kšœ˜Kšœ)˜)šœ œœ œ˜$Kšœ'˜'Kšœ˜Kšœ˜Kšœ˜—Kšœ˜šœœ˜K˜Kšœ.˜.Kšœ-˜-Kšœœ7œœ˜xKšœ˜—Kšœ˜—šœ˜Kšœ&˜&šœœ˜(Kšœ œ˜$Kšœ˜Kšœ œ˜#Kšœ˜—Kšœ˜Kšœ˜—Kšœœ4œ˜S˜ Kšœ@˜@Kšœ&œ˜@Kšœ%œ˜>Kšœ%œ˜>Kšœ$œ˜