DIRECTORY Booting USING [RegisterProcs, CheckpointProc, RollbackProc], FS USING [Error, ErrorDesc, ErrorFromStream], GVBasics USING [RName], GVRetrieve USING [MBXState], IO, Labels USING [Set], MBQueue USING [Action, DequeueAction, FlushWithCallback, QueueClientAction], Menus USING [MenuProc], Process USING [Detach, Pause, SecondsToTicks], Rope, UserCredentials USING [Get], UserProfile USING [Boolean, CallWhenProfileChanges, ProfileChangedProc, Token], ViewerClasses USING [Viewer], ViewerLocks USING [CallUnderWriteLock], ViewerSpecs USING [openTopY], ViewerOps, WalnutDefs USING [Error, VersionMismatch], WalnutOps USING [ActiveMsgSetName, DeletedMsgSetName, FileName, MsgExists, MsgSetsInfo, Scavenge, Shutdown, SizeOfDatabase, SizeOfMsgSet, Startup], WalnutNewMail USING [GetLastMailBoxStatus], WalnutControlInternal USING [ forceQuitMenu, mailNotifyLabel, maybeQuitMenu, mustQuitWalnut, previousUser, readOnlyDBMenu, mailDBMenu, nonMailDBMenu, scavMenu, WaitCallOutcome, walnutMenu, walnutRootFile, workingMenu, ChangeMenu, GetUserResponse, FixUpWalnutViewers], WalnutDisplayerInternal USING [ tocDefaultLooks, tocSelectedLooks, tocUnreadLooks, userWantsQMs, plainTextStyle, displayMsgSetInIcon, QDisplayMsgSet, InternalAddToMsgMenu], WalnutWindowInternal USING [initialActiveIconic, initialActiveOpen, initialActiveRight, msgNamePrefix, msgSetNamePrefix, msgSetBorders, msgSetsVersion, msbDefaultLooks, msbSelectedLooks, activeMsgSetButton, personalMailDB, readOnlyAccess, walnut, walnutQueue, walnutRulerAfter, CloseDownWalnut, CloseTS, DisableNewMail, EnableNewMail, GetButton, OpenTS, Report, SetMailState, ShowMsgSetButtons, TakeDownWalnutViewers], WalnutWindow USING [EnumWalnutViewers]; WalnutNotifierImpl: CEDAR MONITOR IMPORTS Booting, FS, IO, Process, Rope, Labels, MBQueue, UserCredentials, UserProfile, ViewerLocks, ViewerOps, ViewerSpecs, WalnutDefs, WalnutOps, WalnutNewMail, WalnutControlInternal, WalnutDisplayerInternal, WalnutWindow, WalnutWindowInternal EXPORTS WalnutControlInternal, WalnutDisplayerInternal, WalnutWindow = BEGIN OPEN WalnutControlInternal, WalnutWindowInternal; ROPE: TYPE = Rope.ROPE; Viewer: TYPE = ViewerClasses.Viewer; scavengeMsg: ROPE = "Click Scavenge or Quit"; forceQuitRope: ROPE = "You must quit out of Walnut; Click Quit when ready"; wDefsError: ROPE = "\n **** WalnutDefs.Error: code: %g, info: %g at %g"; wVersError: ROPE = "\n **** WalnutDefs.VersionMismatch: explanation: %g at %g"; IOErr: ROPE = "\n *** IO.Error: "; FSErr: ROPE = "\n *** FS.Error: "; WalnutState: TYPE = {running, stopped, waitingForUser}; walnutState: WalnutState _ stopped; WaitForNewMailLabel: ROPE = "Checking for new mail ..."; NoMailLabel: ROPE = "Cannot retrieve mail using this database"; ROAccessLabel: ROPE = "You only have Read access to this database"; WaitCallFinished: CONDITION; MsgMenuClientData: TYPE = REF MsgMenuClientDataRec; MsgMenuClientDataRec: TYPE = RECORD[mProc: Menus.MenuProc, data: REF ANY, doReset: BOOL]; AddToMsgMenu: PUBLIC ENTRY PROC[label: ROPE, proc: Menus.MenuProc, clientData: REF ANY _ NIL, onQueue: BOOL _ FALSE, doReset: BOOL _ TRUE] = { mmcd: MsgMenuClientData; IF ~ onQueue THEN { WalnutDisplayerInternal.InternalAddToMsgMenu[label, proc, clientData, onQueue]; RETURN }; mmcd _ NEW[MsgMenuClientDataRec _ [proc, clientData, doReset]]; WalnutDisplayerInternal.InternalAddToMsgMenu[label, MsgMenuProc, mmcd, onQueue]; }; GetWalnutState: ENTRY PROC RETURNS[WalnutState] = { RETURN[walnutState] }; WalnutNotifier: PUBLIC PROC = { OPEN MBQueue; Wn: PROC = { action: MBQueue.Action _ MBQueue.DequeueAction[walnutQueue]; IF GetWalnutState[] = running THEN { WITH action SELECT FROM e1: Action.user => e1.proc[e1.parent, e1.clientData, e1.mouseButton, e1.shift, e1.control]; e2: Action.client => IF e2.proc # NIL THEN e2.proc[e2.data] ELSE { wqe: WalnutQueueEntry _ NARROW[e2.data]; WaitForWaitCall: ENTRY PROC = {ENABLE UNWIND => NULL; NOTIFY wqe.condition; WAIT WaitCallFinished}; IF wqe # NIL THEN WaitForWaitCall[]; }; ENDCASE => ERROR; } ELSE WITH action SELECT FROM -- walnut is not running e1: Action.user => NULL; e2: Action.client => IF e2.proc # NIL THEN { IF e2.proc = QuitWalnut THEN e2.proc[e2.data] ELSE NULL } ELSE { wqe: WalnutQueueEntry _ NARROW[e2.data]; Notify: ENTRY PROC = {ENABLE UNWIND => NULL; NOTIFY wqe.condition}; IF wqe # NIL THEN { wqe.outcome _ notRunning; Notify[] }; }; ENDCASE => ERROR; }; DO [] _ CarefullyApply[Wn]; ENDLOOP; }; WhatHappened: TYPE = {ok, flushAndContinue, quitting, restarted}; CarefullyApply: PROC[proc1: PROC _ NIL, proc2: PROC[BOOL] _ NIL] RETURNS[result: WhatHappened] = { ENABLE BEGIN ABORTED => { Report["\n Catching ABORTED\n"]; GOTO failure; }; WalnutDefs.Error => { IF code = $AlreadyStarted THEN GOTO ignore; IF code = $DBInternalError THEN GOTO internalError; Report[IO.PutFR[wDefsError, IO.atom[code], IO.rope[explanation], IO.time[]]]; GOTO failure; }; WalnutDefs.VersionMismatch => { Report[IO.PutFR[wVersError, IO.rope[explanation], IO.time[]]]; GOTO mismatch; }; FS.Error => { Report[FSErr, error.explanation]; GOTO failure; }; IO.Error => { ed: FS.ErrorDesc; ed _ FS.ErrorFromStream[stream ! IO.Error, FS.Error => CONTINUE]; IF ed.explanation # NIL THEN Report[IOErr, ed.explanation]; IF ec = Failure THEN { IF ed.code = $quotaExceeded THEN GOTO outOfSpace ELSE { Report[IO.PutFR["IO Failure with code %g", IO.atom[ed.code]] ]; GOTO failure; }; } ELSE { IF ed.explanation = NIL THEN Report["Other IO error"]; GOTO failure; -- better this than nothing }; }; END; IF proc1 # NIL THEN proc1[] ELSE proc2[walnutState = running]; RETURN[ok]; EXITS internalError => { Report["\n DB reported an internal error; you'll have to quit"]; Report["\nThen type WalnutScavenge to the commandTool"]; ViewerOps.BlinkIcon[walnut]; MBQueue.FlushWithCallback[walnutQueue, FlushingQueue]; Report[forceQuitRope]; [] _ GetUserResponse[forceQuitMenu]; MBQueue.QueueClientAction[walnutQueue, QuitWalnut, NIL]; RETURN[quitting]; }; ignore => RETURN[ok]; mismatch => IF MismatchHandled[] THEN RETURN[flushAndContinue] ELSE RETURN[HandleFailure[]]; failure => RETURN[HandleFailure[]]; outOfSpace => { Report["\n Out of space"]; ViewerOps.BlinkIcon[walnut]; MBQueue.FlushWithCallback[walnutQueue, FlushingQueue]; Report[forceQuitRope]; [] _ GetUserResponse[forceQuitMenu]; MBQueue.QueueClientAction[walnutQueue, QuitWalnut, NIL]; RETURN[quitting]; }; }; MismatchHandled: PROC RETURNS[ok: BOOL] = { IF (ok _ WalnutOps.MsgSetsInfo[].version = WalnutWindowInternal.msgSetsVersion) THEN WalnutWindowInternal.ShowMsgSetButtons[]; RETURN[ok]; }; HandleFailure: PROC RETURNS[WhatHappened] = { doQuit: BOOL; SetWalnutState[waitingForUser]; MBQueue.FlushWithCallback[walnutQueue, FlushingQueue]; WalnutOps.Shutdown[]; IF walnut = NIL THEN { MBQueue.QueueClientAction[walnutQueue, QuitWalnut, NIL]; RETURN[quitting]; }; Report["\nClick Quit or Retry"]; IF (doQuit _ GetUserResponse[maybeQuitMenu]) THEN { Report[" quitting ..."]; MBQueue.QueueClientAction[walnutQueue, QuitWalnut, NIL]; RETURN[quitting]; } ELSE { Report[" retrying ..."]; TRUSTED { Process.Detach[FORK WaitForRestart[walnutRootFile]] }; RETURN[restarted]; } }; WalnutQueueEntry: TYPE = REF QueueEntryObject; QueueEntryObject: TYPE = RECORD[outcome: WaitCallOutcome _ ok, condition: CONDITION]; FlushingQueue: PROC[action: MBQueue.Action] = { WITH action SELECT FROM e1: MBQueue.Action.user => NULL; e2: MBQueue.Action.client => { wqe: WalnutQueueEntry _ NARROW[e2.data]; Notify: ENTRY PROC = { NOTIFY wqe.condition }; IF wqe # NIL THEN { wqe.outcome _ flushed; Notify[] }; }; ENDCASE => ERROR; }; FlushWQueue: PUBLIC INTERNAL PROC = -- for WalnutWindowInternalImpl to call { MBQueue.FlushWithCallback[walnutQueue, FlushingQueue] }; DoWaitCall: PUBLIC PROC[proc: PROC[]] RETURNS[outcome: WaitCallOutcome] = { result: WhatHappened; wqe: WalnutQueueEntry = NEW[QueueEntryObject _ []]; MBQueue.QueueClientAction[walnutQueue, NIL, wqe]; Wait[wqe]; IF wqe.outcome = ok THEN -- ~flushed or ~notRunning IF (result _ CarefullyApply[proc1: proc]) # ok THEN wqe.outcome _ flushed; NotifyWaitCallWaiter[]; RETURN[wqe.outcome] }; Wait: ENTRY PROC[wqe: WalnutQueueEntry] = { ENABLE UNWIND => NULL; WAIT wqe.condition}; NotifyWaitCallWaiter: ENTRY PROC = { ENABLE UNWIND => NULL; NOTIFY WaitCallFinished }; DoStartupCall: PUBLIC PROC[proc: PROC[isRunning: BOOL]] RETURNS[outcome: WaitCallOutcome] = { result: WhatHappened; wqe: WalnutQueueEntry = NEW[QueueEntryObject _ []]; MBQueue.QueueClientAction[walnutQueue, NIL, wqe]; Wait[wqe]; IF wqe.outcome # flushed THEN -- ok or notRunning IF (result _ CarefullyApply[proc2: proc]) # ok THEN wqe.outcome _ flushed; NotifyWaitCallWaiter[]; RETURN[wqe.outcome] }; MsgMenuProc: Menus.MenuProc = { mmcd: MsgMenuClientData _ NARROW[clientData]; mmcd.mProc[parent, mmcd.data, mouseButton, shift, control]; IF ~mmcd.doReset THEN RETURN; WalnutWindowInternal.ShowMsgSetButtons[]; WalnutControlInternal.FixUpWalnutViewers[]; }; WaitForRestart: PROC[rootFile: ROPE, exp: ROPE _ NIL] = { Rw: PROC[BOOL] = { IF RestartWalnut[rootFile, FALSE, FALSE] THEN Report["Restart finished"] }; OpenTS[exp]; -- displays exp if ts not already open [] _ DoStartupCall[Rw]; }; RestartWalnut: PUBLIC PROC[rootFile: ROPE, scavengeFirst, firstTime: BOOL] RETURNS[restarted: BOOL] = { DO BEGIN ENABLE BEGIN ABORTED, UNWIND => GOTO retry; WalnutDefs.Error => { IF code = $AlreadyStarted THEN GOTO ignore; Report[IO.PutFR[wDefsError, IO.atom[code], IO.rope[explanation], IO.time[]]]; IF code = $BadLog OR code = $SchemaMismatch OR code = $WrongRootFile OR code = $DBInternalError THEN GOTO needScav ELSE GOTO retry }; IO.Error => { ed: FS.ErrorDesc; ed _ FS.ErrorFromStream[stream ! IO.Error, FS.Error => CONTINUE]; IF ed.explanation # NIL THEN Report[IOErr, ed.explanation]; IF ec = Failure THEN IF ed.code = $quotaExceeded THEN Report["\n Out of space"] ELSE Report[IO.PutFR["IO Failure with code %g", IO.atom[ed.code]] ]; Report["Other IO Error"]; GOTO retry; }; FS.Error => { Report[FSErr, error.explanation]; GOTO retry}; END; dbFileName, key: ROPE; wasReadOnly: BOOL _ readOnlyAccess; notFoundRope: ROPE = " not found and can't be created"; mailFor: GVBasics.RName; newMailExists: BOOL; readOnlyAccess _ FALSE; IF scavengeFirst THEN [newMailExists, mailFor, key] _ WalnutOps.Scavenge[rootFile: rootFile] ELSE [readOnlyAccess, newMailExists, mailFor, key] _ WalnutOps.Startup[ rootFile: rootFile, wantReadOnly: readOnlyAccess]; dbFileName_ WalnutOps.FileName[]; msgNamePrefix _ dbFileName.Concat["!Msg!"]; msgSetNamePrefix _ dbFileName.Concat["!MsgSet!"]; walnutRootFile _ rootFile; personalMailDB _ mailFor.Equal[UserCredentials.Get[].name, FALSE]; IF readOnlyAccess THEN { walnutMenu _ readOnlyDBMenu; personalMailDB _ FALSE } ELSE walnutMenu _ IF personalMailDB THEN mailDBMenu ELSE nonMailDBMenu; Labels.Set[mailNotifyLabel, IF personalMailDB THEN WaitForNewMailLabel ELSE IF readOnlyAccess THEN ROAccessLabel ELSE NoMailLabel]; IF personalMailDB THEN EnableNewMail[]; ShowMsgSetButtons[]; IF ~walnut.iconic THEN ViewerOps.PaintViewer[walnut, client]; IF firstTime THEN { dif: INTEGER; wH: INTEGER _ ViewerSpecs.openTopY/4; msgsInDeleted: INT_ WalnutOps.SizeOfMsgSet[WalnutOps.DeletedMsgSetName].messages; total: INT _ WalnutOps.SizeOfDatabase[].messages; LockedSetHeight: PROC = {ViewerOps.SetOpenHeight[walnut, wH - dif]}; IF (dif _ (wH-walnutRulerAfter.wy) - 100) # 0 THEN { ViewerLocks.CallUnderWriteLock[LockedSetHeight, walnut]; IF ~walnut.iconic THEN ViewerOps.ComputeColumn[walnut.column] }; IF msgsInDeleted > total/8 THEN Report[IO.PutFR[ "There are %g deleted msgs (%g total); consider doing an expunge", IO.int[msgsInDeleted], IO.int[total]] ]; }; FixUpWalnutViewers[]; IF initialActiveOpen AND firstTime THEN { IF personalMailDB OR WalnutOps.SizeOfMsgSet[WalnutOps.ActiveMsgSetName].messages # 0 THEN [] _ WalnutDisplayerInternal.QDisplayMsgSet[activeMsgSetButton]; }; IF personalMailDB THEN IF newMailExists THEN SetMailState[thereIsMail] ELSE { mbxState: GVRetrieve.MBXState _ WalnutNewMail.GetLastMailBoxStatus[].mbxState; IF mbxState = unknown THEN { SetMailState[gvWaiting]; Process.Pause[Process.SecondsToTicks[15]]; mbxState _ WalnutNewMail.GetLastMailBoxStatus[].mbxState; }; SELECT mbxState FROM unknown => SetMailState[noGV]; allEmpty, someEmpty => SetMailState[noMail]; allDown => SetMailState[noServers]; notEmpty => SetMailState[gvMail]; ENDCASE => SetMailState[noMail]; }; walnut.inhibitDestroy _ FALSE; SetWalnutState[running]; ChangeMenu[walnutMenu, FALSE]; RETURN[TRUE]; EXITS ignore => RETURN[TRUE]; retry => { Report["Start or Restart failed; click Quit or Retry"]; SetWalnutState[waitingForUser]; IF walnut = NIL THEN { CloseDownWalnut[]; RETURN[FALSE]}; IF GetUserResponse[maybeQuitMenu] THEN { CloseDownWalnut[]; RETURN[FALSE]}; WalnutOps.Shutdown[]; -- before trying to restart scavengeFirst _ FALSE; }; needScav => { Report["A scavenge is necessary\n", scavengeMsg]; IF ~GetUserResponse[scavMenu] THEN { CloseDownWalnut[]; SetWalnutState[stopped]; RETURN[FALSE]}; Report["\n Starting scavenge ...\n"]; scavengeFirst _ TRUE; } END; ENDLOOP; }; FixUpWalnutViewers: PUBLIC PROC = { msgSetList, msgList: LIST OF Viewer; v: Viewer; name: ROPE; [msgSetList, msgList] _ WalnutWindow.EnumWalnutViewers[TRUE]; FOR vL: LIST OF Viewer _ msgSetList, vL.rest UNTIL vL=NIL DO name _ NARROW[ViewerOps.FetchProp[v _ vL.first, $WalnutMsgSetName]]; []_ WalnutDisplayerInternal.QDisplayMsgSet[GetButton[name], v]; ENDLOOP; FOR vL: LIST OF Viewer_ msgList, vL.rest UNTIL vL=NIL DO name _ NARROW[ViewerOps.FetchProp[v _ vL.first, $WalnutMsgName]]; IF ~WalnutOps.MsgExists[name] THEN { IF ViewerOps.FetchProp[v, $Frozen] # NIL THEN LOOP; WalnutWindowInternal.Report["Msg: ", name, " doesn't exist; destroying viewer"]; ViewerOps.DestroyViewer[v]; }; ENDLOOP; }; QuitWalnut: PUBLIC PROC[ra: REF ANY] = { IF GetWalnutState[] = stopped THEN RETURN; -- ignore SetWalnutState[waitingForUser]; IF ra # NIL THEN { msg: ROPE = NARROW[ra]; walnut.inhibitDestroy _ TRUE; OpenTS[]; -- if after rollback, may not be open TakeDownWalnutViewers[]; -- make the user notice ViewerOps.BlinkIcon[walnut, IF walnut.iconic THEN 0 ELSE 1]; Report["\n **********", msg]; Report["You MUST quit out of Walnut; Click Quit when ready"]; [] _ GetUserResponse[forceQuitMenu]; }; CloseDownWalnut[]; SetWalnutState[stopped]; }; SetWalnutState: ENTRY PROC[newState: WalnutState] = { walnutState _ newState}; userChangedMessage: ROPE = "Logged-in user changed"; displayMsgSetInIcon: PUBLIC BOOL _ UserProfile.Boolean[key: "Walnut.DisplayMsgSetInIcon", default: FALSE]; SetWalnutProfileVars: UserProfile.ProfileChangedProc = { curUser: ROPE = UserCredentials.Get[].name; initialActiveIconic _ UserProfile.Boolean[key: "Walnut.InitialActiveIconic", default: FALSE]; initialActiveRight _ UserProfile.Boolean[key: "Walnut.InitialActiveRight", default: TRUE]; initialActiveOpen _ UserProfile.Boolean[key: "Walnut.InitialActiveOpen", default: FALSE]; msgSetBorders _ UserProfile.Boolean[key: "Walnut.MsgSetButtonBorders", default: FALSE]; msbDefaultLooks _ UserProfile.Token[key: "Walnut.MsgSetButtonDefaultLooks", default: ""]; msbSelectedLooks _ UserProfile.Token[key: "Walnut.MsgSetButtonSelectedLooks", default: "bi"]; WalnutDisplayerInternal.tocDefaultLooks _ UserProfile.Token[key: "Walnut.TOCDefaultLooks", default: ""]; WalnutDisplayerInternal.tocSelectedLooks _ UserProfile.Token[key: "Walnut.TOCSelectedLooks", default: "sb"]; WalnutDisplayerInternal.tocUnreadLooks _ UserProfile.Token[key: "Walnut.TOCUnreadLooks", default: "i"]; WalnutDisplayerInternal.userWantsQMs _ UserProfile.Boolean[key: "Walnut.ShowUnreadWithQMs", default: TRUE]; WalnutDisplayerInternal.plainTextStyle _ UserProfile.Token[key: "Walnut.PlainTextStyle", default: "cedar"]; WalnutDisplayerInternal.displayMsgSetInIcon _ UserProfile.Boolean[key: "Walnut.DisplayMsgSetInIcon", default: FALSE]; IF GetWalnutState[] # running THEN {previousUser _ curUser; RETURN}; IF ~Rope.Equal[previousUser, curUser, FALSE] THEN { mustQuitWalnut _ userChangedMessage; MBQueue.FlushWithCallback[walnutQueue, FlushingQueue]; MBQueue.QueueClientAction[walnutQueue, QuitWalnut, mustQuitWalnut]; previousUser _ curUser; }; }; WalnutCheckpointProc: Booting.CheckpointProc = { ENABLE UNWIND => { CloseTS[]}; Wcp: PROC = { ChangeMenu[workingMenu, FALSE]; Report["\nDoing Checkpoint ... "]; DisableNewMail[]; WalnutOps.Shutdown[ ! ABORTED => CONTINUE]; CloseTS[]; }; IF walnut = NIL THEN RETURN; MBQueue.FlushWithCallback[walnutQueue, FlushingQueue]; IF DoWaitCall[Wcp] # ok THEN RETURN["Walnut's Checkpoint Proc didn't get done"]; }; WalnutRollbackProc: Booting.RollbackProc = { IF GetWalnutState[] # running THEN RETURN; CloseTS[]; -- to be very sure IF ~Rope.Equal[UserCredentials.Get[].name, previousUser, FALSE] THEN MBQueue.QueueClientAction[walnutQueue, QuitWalnut, userChangedMessage] ELSE TRUSTED { Process.Detach[FORK WaitForRestart[walnutRootFile, "Restarting after Rollback"]] }; }; TRUSTED { Booting.RegisterProcs[c: WalnutCheckpointProc, r: WalnutRollbackProc]; Process.Detach[FORK WalnutNotifier[] ]; }; UserProfile.CallWhenProfileChanges[SetWalnutProfileVars]; END. ^WalnutNotifierImpl.mesa Copyright c 1984 by Xerox Corporation. All rights reserved. Willie-Sue, November 5, 1985 11:14:49 am PST Donahue, May 20, 1985 1:53:31 pm PDT Pavel, February 6, 1986 2:59:42 pm PST Contents: Notifier & restart code created July, 1983 by Willie-Sue Last edit by: Willie-Sue on: January 3, 1985 9:26:50 am PST Donahue on: December 13, 1984 10:22:29 am PST (to be consistent with new WalnutDisplayerInternal) Walnut Viewers types and global data * * * * * * * * * * exported to WalnutWindow * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * QueueWasFlushed can't be an ENTRY proc puts proc on Walnut's queue and waits for its execution to finish puts proc on Walnut's queue and waits for its execution to finish; calls proc even if walnut is not running * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * check how much space is left (if any) in the control window for a typescript) WalnutOps.Shutdown[]; -- not needed before doing scavenge * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * This is declared here because of a compiler/loader incompatibility * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * clean up Walnut at checkpoint time Κƒ˜šΟb™Jšœ Οmœ1™˜OJšœŸœ ˜Jšœ Ÿœ˜'Jšœ Ÿœ ˜J˜ J˜Jšœ Ÿœ˜*šœ Ÿœ&˜5Jšœ6˜6Jšœ(˜(—J˜JšœŸœ˜+šœŸœ˜JšœL˜LJšœ*˜*JšœC˜CJšœ1˜1—šœŸœ˜Jšœf˜fJšœ&˜&—šœŸœ=˜WJšœ?˜?Jšœ"˜"Jšœ3˜3Jšœ&˜&Jšœ8˜8JšœS˜S—šœ Ÿœ˜'J˜——šœŸœŸ˜!šŸ˜Jšœ ŸœŸœ˜J˜J˜BJ˜J˜%JšœR˜R—J˜JšŸœ?˜FJ˜—JšŸœŸœ-˜7J˜Jšœ$™$J˜JšŸœŸœŸœ˜JšœŸœ˜$J˜Jšœ Ÿœ˜-JšœŸœ8˜KJšœ Ÿœ9˜IJšœ Ÿœ?˜OJšœŸœ˜#JšœŸœ˜#J˜Jšœ Ÿœ&˜7Jšœ#˜#J˜JšœŸœ˜8Jšœ Ÿœ.˜?JšœŸœ0˜CJšœŸ œ˜J˜šœ,™,J˜JšœŸœŸœ˜3šœŸœ˜JšŸœŸœŸœ Ÿœ˜<—J˜š Οn œŸœŸœŸœŸœ˜BJšœ ŸœŸœŸœ ŸœŸœ ŸœŸœ˜LJšœ˜šŸœ Ÿœ˜JšœO˜OJšŸ˜J˜—JšœŸœ5˜?JšœP˜PJ˜——J™JšœO™O˜Jš  œŸœŸœŸœŸœ˜JJ˜š œŸœŸœ˜JšŸœ ˜ š œŸœ˜ Jšœ<˜<šŸœŸœ˜$šŸœŸœŸ˜˜J˜H—˜JšŸœ ŸœŸœ˜&šŸœ˜JšœŸœ ˜(š œŸœŸœ˜Jš œŸœŸœŸœŸœŸœ˜E—JšŸœŸœŸœ˜$J˜——JšŸœŸœ˜J˜—JšŸ˜šŸœŸœŸœΟc˜1JšœŸœ˜˜šŸœ ŸœŸ˜Jš œŸœŸœŸœŸœ˜;šŸœ˜JšœŸœ ˜(Jš œŸœŸœŸœŸœŸœŸœ˜CšŸœŸœŸ˜Jšœ'˜'—J˜———JšŸœŸœ˜——J˜—šŸ˜Jšœ˜JšŸœ˜—Jšœ˜——˜JšœŸœ/˜AJ˜š œŸœŸœŸœ ŸœŸœŸœ˜@JšœŸœ˜"šŸ ˜ šŸœ˜ J˜!JšŸœ ˜ J˜—šœ˜JšŸœŸœŸœ˜+JšŸœŸœŸœ˜3Jš œŸœŸœ ŸœŸœ ˜MJšŸœ ˜ J˜—šœ˜JšœŸœŸœŸœ ˜>JšŸœ ˜J˜—šŸœ ˜ J˜!JšŸœ ˜ J˜—šŸœ ˜ JšœŸœ ˜Jš œŸœŸœŸœ Ÿœ˜AJšŸœŸœŸœ˜;šŸœŸœ˜šŸœŸœŸœ Ÿœ˜7JšœŸœ"Ÿœ˜?JšŸœ ˜ J˜—J˜—šŸœŸ˜JšŸœŸœŸœ˜6JšŸœ ‘˜)J˜—J˜—JšŸœ˜JšŸœ ŸœŸœ Ÿœ˜>JšŸœ˜ J˜šŸœ˜šœ˜Jšœ@˜@J˜8J˜Jšœ6˜6J˜Jšœ$˜$Jšœ3Ÿœ˜8JšŸœ ˜J˜—J˜Jšœ Ÿœ˜J˜šœ ˜ šŸœŸœŸœ˜2JšŸœŸœ˜——J˜šœ Ÿœ˜#J˜—šœ˜Jšœ˜J˜Jšœ6˜6J˜Jšœ$˜$Jšœ3Ÿœ˜8JšŸœ ˜J˜———J˜—J˜š œŸœŸœŸœ˜+šŸœNŸ˜TJšœ)˜)—JšŸœ˜ J˜—J˜š  œŸœŸœ˜-JšœŸœ˜ Jšœ˜Jšœ6˜6J˜šŸœ ŸœŸœ˜Jšœ3Ÿœ˜8JšŸœ ˜J˜—J˜ šŸœ+Ÿœ˜3J˜Jšœ3Ÿœ˜8JšŸœ ˜J˜šŸœ˜J˜JšŸœŸœ#˜@JšŸœ ˜J˜——J˜—J˜Jšœ&™&JšœŸœŸœ˜.JšœŸœŸœ+Ÿ œ˜UJ˜š  œŸœ˜/šŸœŸœŸ˜JšœŸœ˜ šœ˜JšœŸœ ˜(Jš œŸœŸœŸœ˜.šŸœŸœŸœ˜Jšœ˜J˜J˜—J˜JšŸœŸœ˜——Jšœ˜—J˜š   œŸœŸœŸœ‘'˜LJšœ:˜:—J˜š   œŸœŸœŸœŸœ˜KšœA™AJ˜JšœŸœ˜3Jšœ'Ÿœ˜1Jšœ ˜ šŸœŸœ‘˜4JšŸœ-Ÿœ˜J—J˜JšŸœ ˜—Jšœ˜—J˜š œŸœŸœ˜)Jš œŸœŸœŸœŸœ˜-—J˜š œŸœŸœ˜"Jš œŸœŸœŸœŸœŸœ˜3—J˜š   œŸœŸœŸœ Ÿœ˜7JšœŸœ˜&šœk™kJ˜JšœŸœ˜3Jšœ'Ÿœ˜1Jšœ ˜ šŸœŸœ‘˜2JšŸœ-Ÿœ˜J—J˜JšŸœ ˜—Jšœ˜—J˜š  œ˜JšœŸœ ˜-Jšœ;˜;JšŸœŸœŸœ˜Jšœ)˜)Jšœ+˜+Jšœ˜—J˜—JšœO™O˜š  œŸœ ŸœŸœŸœ˜9š œŸœŸœ˜JšŸœŸœŸœŸœ˜HJ˜—Jšœ‘&˜4Jšœ˜Jšœ˜—J˜š  œŸ œ ŸœŸœ˜JJšŸœ Ÿœ˜JšŸ˜šŸ˜šŸœŸ˜ JšŸœŸœŸœ˜šœ˜JšŸœŸœŸœ˜+Jš œŸœŸœ ŸœŸœ ˜MšŸœŸœŸ˜.JšœŸœ˜0JšŸœŸœ ŸœŸœ˜"—Jšœ˜—šŸœ ˜ JšœŸœ ˜Jš œŸœŸœŸœ Ÿœ˜AJšŸœŸœŸœ˜;J˜šŸœŸ˜JšŸœŸœ˜˜>—šœ*˜*JšœA˜A—šœ(˜(Jšœ>˜>—šœ&˜&Jšœ>Ÿœ˜D—J˜Kšœk˜kK˜KšœnŸœ˜uJ˜JšŸœŸœŸœ˜DšŸœ$ŸœŸ˜1šœ&˜&Jšœ6˜6J˜CJ˜—J˜—Jšœ˜—J˜JšœO™OJ™š œ˜0JšŸœŸœ˜š œŸœ˜ JšœŸœ˜J˜"J˜JšœŸœŸœ˜+J˜ Jšœ˜—J˜JšŸœ ŸœŸœŸœ˜Jšœ6˜6JšŸœŸœŸœ-˜PJ˜—J˜š œ˜,JšŸœŸœŸœ˜*J˜Jšœ ‘˜šŸœ7ŸœŸœ˜EJšœF˜FšŸœŸ˜ JšœŸœ@˜U——J˜—J˜—šœ"™"šŸœ˜ JšœF˜FJšœŸœ˜'Jšœ˜——˜J˜9—J˜JšŸœ˜J˜J˜—…—C0]