DIRECTORY Directory USING [Error, Lookup, ignore], IO USING [PutF, Flush, RIS, ROPE, rope, STREAM, UserAborted, GetSequence, CharProc], MessageWindow USING [Append], Resource USING [AbortProc, Acquire, Release], Rope USING [Cat, Concat, Fetch, Find, IsEmpty, Length, Replace, ROPE, Substr, ToRefText], Spell USING [AbortProc, ConfirmProc, defaultModes, Filter, GetMatchingFileList, GetMatchingList, GetTheFile, GetTheOne, InformProc, IsAPattern, Modes, SpellingGenerator, SpellingList], SpellExtras USING [GetFileExtension], TiogaOps USING [RegisterFileNameProc], UserExec USING [CheckForFile, HistoryEvent, ExecHandle, CheckForAbort, UserAbort, UserAborted, Confirm, GetExecHandle, GetUserResponse, SetupAskUser, FinishAskUser, GetTheOne, GetTheFile, RopeSubst, Viewer, AcquireStreams, ReleaseStreams], UserExecExtras USING [CorrectionDisabled], UserExecPrivate USING [], UserProfile USING [Boolean, ProfileChangedProc, CallWhenProfileChanges], ViewerAbort USING [UserAbort], ViewerTools USING [GetSelectedViewer] ; UserExecUtilImpl: CEDAR MONITOR IMPORTS IO, Resource, Rope, MessageWindow, UserExec, TiogaOps, UserExecExtras, UserProfile, Directory, Spell, SpellExtras, ViewerAbort, ViewerTools EXPORTS UserExec = BEGIN OPEN IO; Viewer: TYPE = UserExec.Viewer; ExecHandle: TYPE = UserExec.ExecHandle; HistoryEvent: TYPE = UserExec.HistoryEvent; terminateCorrection: ERROR = CODE; GetTheOne: PUBLIC PROCEDURE[ unknown: ROPE, spellingList: Spell.SpellingList _ NIL, generator: Spell.SpellingGenerator _ NIL, event: HistoryEvent, exec: ExecHandle, viewer: Viewer _ NIL, filter: Spell.Filter _ NIL, modes: Spell.Modes _ NIL ] RETURNS [correct: ROPE _ NIL] = { Abort: Spell.AbortProc = { hasResponded: BOOL; value: ATOM; UserExec.CheckForAbort[exec]; IF viewer # NIL AND ViewerAbort.UserAbort[viewer] THEN IO.UserAborted[viewer]; IF viewer # NIL THEN [hasResponded, value] _ UserExec.GetUserResponse[viewer]; IF hasResponded AND value = $No THEN ERROR terminateCorrection; }; Confirm: Spell.ConfirmProc -- [msg: ROPE, timeout: INT, defaultConfirm: BOOL] RETURNS[yes: BOOL] -- = { RETURN[ UserExec.Confirm[msg: msg, timeout: timeout, defaultConfirm: defaultConfirm, exec: exec, viewer: viewer] ] }; Inform: Spell.InformProc --[msg: ROPE] -- = { IF exec # NIL THEN {out _ UserExec.AcquireStreams[exec].out; -- since this can be called from random place, must acquire and release streams rather than using GetStreams out.PutF["*n*m%g*s\n", rope[msg]]; } ELSE MessageWindow.Append[message: msg, clearFirst: TRUE]; }; out: IO.STREAM; IF Rope.IsEmpty[unknown] OR UserExecExtras.CorrectionDisabled[event] THEN RETURN[NIL]; {ENABLE UNWIND => Finish[exec, viewer, out]; IF exec # NIL THEN viewer _ exec.viewer; IF viewer # NIL THEN UserExec.SetupAskUser[viewer]; correct _ Spell.GetTheOne[unknown: unknown, spellingList: spellingList, generator: generator, filter: filter, abort: Abort, confirm: Confirm, inform: Inform, modes: modes ! terminateCorrection => CONTINUE]; IF exec # NIL THEN FixCommandLine[old: unknown, new: correct, event: event, exec: exec]; Finish[exec, viewer, out]; }; }; GetMatchingList: PUBLIC PROCEDURE[ unknown: ROPE, spellingList: Spell.SpellingList _ NIL, generator: Spell.SpellingGenerator _ NIL, event: HistoryEvent, exec: ExecHandle, viewer: Viewer _ NIL, filter: Spell.Filter _ NIL, modes: Spell.Modes _ NIL ] RETURNS [matching: LIST OF ROPE _ NIL] = { Abort: Spell.AbortProc = { hasResponded: BOOL; value: ATOM; UserExec.CheckForAbort[exec]; IF viewer # NIL AND ViewerAbort.UserAbort[viewer] THEN IO.UserAborted[viewer]; IF viewer # NIL THEN [hasResponded, value] _ UserExec.GetUserResponse[viewer]; IF hasResponded AND value = $No THEN ERROR terminateCorrection; }; Confirm: Spell.ConfirmProc -- [msg: ROPE, timeout: INT, defaultConfirm: BOOL] RETURNS[yes: BOOL] -- = { RETURN[ UserExec.Confirm[msg: msg, timeout: timeout, defaultConfirm: defaultConfirm, exec: exec, viewer: viewer] ] }; Inform: Spell.InformProc --[msg: ROPE] -- = { IF exec # NIL THEN {out _ UserExec.AcquireStreams[exec].out; -- since this can be called from random place, must acquire and release streams rather than using GetStreams out.PutF["*n*m%g*s\n", rope[msg]]; } ELSE MessageWindow.Append[message: msg, clearFirst: TRUE]; }; out: IO.STREAM; IF Rope.IsEmpty[unknown] THEN RETURN[NIL]; IF NOT Spell.IsAPattern[unknown] THEN {val: ROPE _ UserExec.GetTheOne[unknown: unknown, spellingList: spellingList, generator: generator, event: event, exec: exec, filter: filter, modes: modes]; IF val # NIL THEN RETURN[LIST[val]] ELSE RETURN[NIL]; }; {ENABLE UNWIND => Finish[exec, viewer, out]; IF exec # NIL THEN viewer _ exec.viewer; IF viewer # NIL THEN { IF Spell.defaultModes.confirm >= patternMatch THEN UserExec.SetupAskUser[viewer] ELSE viewer _ NIL; -- no point in putting up yes/no if confirmation won't be needed }; matching _ Spell.GetMatchingList[pattern: unknown, spellingList: spellingList, generator: generator, filter: filter, abort: Abort, confirm: Confirm, inform: Inform, modes: modes ! terminateCorrection => CONTINUE]; Finish[exec, viewer, out]; }; }; CheckForFile: PUBLIC PROC [file: ROPE] RETURNS [found: BOOLEAN] = TRUSTED { -- Directory fName: LONG STRING; fName _ LOOPHOLE[Rope.ToRefText[file]]; found _ TRUE; IF Rope.Length[file] = 0 THEN RETURN[FALSE]; [] _ Directory.Lookup[fileName: fName, permissions: Directory.ignore ! Directory.Error => {found _ FALSE; CONTINUE} ]; }; GetTheFile: PUBLIC PROC [file: ROPE, defaultExt: ROPE _ NIL, event: HistoryEvent, exec: ExecHandle, viewer: Viewer _ NIL, modes: Spell.Modes _ NIL] RETURNS [name: ROPE _ NIL] = { i: INT; out: IO.STREAM; IF Rope.IsEmpty[file] THEN RETURN[NIL]; {ENABLE UNWIND => Finish[exec, viewer, out]; { IF (i _ Rope.Find[s1: file, s2: "/"]) # -1 THEN {IF i # 0 THEN file _ Rope.Substr[base: file, len: i] -- ignore switches ELSE IF Rope.Find[s1: file, s2: "/", pos1: 1] = -1 THEN GOTO FullPath ELSE RETURN[NIL]; }; IF Rope.Find[file, "["] # -1 OR Rope.Find[file, "<"] # -1 THEN GOTO FullPath; EXITS FullPath => RETURN[NIL]; }; IF CheckForFile[file] THEN RETURN[file]; IF (i _ Rope.Find[file, "."]) = -1 AND Rope.Length[defaultExt] # 0 THEN name _ Rope.Cat[file, ".", defaultExt] ELSE name _ file; IF CheckForFile[name] THEN RETURN[name]; IF UserExecExtras.CorrectionDisabled[event] THEN RETURN[NIL]; { Abort: Spell.AbortProc = { hasResponded: BOOL; value: ATOM; UserExec.CheckForAbort[exec]; IF viewer # NIL AND ViewerAbort.UserAbort[viewer] THEN IO.UserAborted[viewer]; IF viewer # NIL THEN [hasResponded, value] _ UserExec.GetUserResponse[viewer]; IF hasResponded AND value = $No THEN ERROR terminateCorrection; }; Confirm: Spell.ConfirmProc -- [msg: ROPE, timeout: INT, defaultConfirm: BOOL] RETURNS[yes: BOOL] -- = { RETURN[ UserExec.Confirm[msg: msg, timeout: timeout, defaultConfirm: defaultConfirm, exec: exec, viewer: viewer] ] }; Inform: Spell.InformProc -- [msg: ROPE] -- = { IF exec # NIL THEN {out _ UserExec.AcquireStreams[exec].out; -- since this can be called from random place, must acquire and release streams rather than using GetStreams out.PutF["*n*m%g*s\n", rope[msg]]; } ELSE MessageWindow.Append[message: msg, clearFirst: TRUE]; }; IF exec # NIL THEN viewer _ exec.viewer; IF viewer # NIL THEN UserExec.SetupAskUser[viewer]; name _ Spell.GetTheFile[unknown: file, defaultExt: defaultExt, abort: Abort, confirm: Confirm, inform: Inform, modes: modes ! terminateCorrection => CONTINUE]; IF name # NIL THEN { i: INT; new: ROPE = IF (i _ Rope.Find[file, "."]) = -1 AND (i _ Rope.Find[name, "."]) # -1 THEN Rope.Substr[base: name, len: i] ELSE name; -- if original file did not have an extension and name does, strip it off for purposes of correcting history. IF exec # NIL THEN {FixCommandLine[old: file, new: new, event: event, exec: exec]; }; }; Finish[exec, viewer, out]; }; }; }; GetMatchingFileList: PUBLIC PROC [file: ROPE, defaultExt: ROPE _ NIL, event: HistoryEvent, exec: ExecHandle, viewer: Viewer _ NIL, modes: Spell.Modes _ NIL] RETURNS [fileList: LIST OF ROPE _ NIL] = { out: IO.STREAM; IF Rope.IsEmpty[file] THEN RETURN[NIL]; IF NOT Spell.IsAPattern[file] THEN {val: ROPE = UserExec.GetTheFile[file: file, defaultExt: defaultExt, event: event, exec: exec]; IF val # NIL THEN RETURN[LIST[val]] ELSE RETURN[NIL]; }; { ENABLE UNWIND => Finish[exec, viewer, out]; Abort: Spell.AbortProc = { hasResponded: BOOL; value: ATOM; UserExec.CheckForAbort[exec]; IF viewer # NIL AND ViewerAbort.UserAbort[viewer] THEN IO.UserAborted[viewer]; IF viewer # NIL THEN [hasResponded, value] _ UserExec.GetUserResponse[viewer]; IF hasResponded AND value = $No THEN ERROR terminateCorrection; }; Confirm: Spell.ConfirmProc -- [msg: ROPE, timeout: INT, defaultConfirm: BOOL] RETURNS[yes: BOOL] -- = { RETURN[ UserExec.Confirm[msg: msg, timeout: timeout, defaultConfirm: defaultConfirm, exec: exec, viewer: viewer] ] }; Inform: Spell.InformProc --[msg: ROPE] -- = { IF exec # NIL THEN {out _ UserExec.AcquireStreams[exec].out; -- since this can be called from random place, must acquire and release streams rather than using GetStreams out.PutF["*n*m%g*s\n", rope[msg]]; } ELSE MessageWindow.Append[message: msg, clearFirst: TRUE]; }; IF exec # NIL THEN viewer _ exec.viewer; IF viewer # NIL THEN { IF Spell.defaultModes.confirm >= patternMatch THEN UserExec.SetupAskUser[viewer] ELSE viewer _ NIL; -- no point in putting up yes/no if confirmation won't be needed }; fileList _ Spell.GetMatchingFileList[unknown: file, defaultExt: defaultExt, abort: Abort, inform: Inform, confirm: Confirm, modes: modes ! terminateCorrection => CONTINUE]; Finish[exec, viewer, out]; }; }; Finish: PROCEDURE [exec: UserExec.ExecHandle, viewer: Viewer, out: STREAM] = { IF out # NIL THEN UserExec.ReleaseStreams[exec]; IF viewer # NIL THEN UserExec.FinishAskUser[viewer]; }; FixCommandLine: PROC [old, new: ROPE, event: HistoryEvent, exec: UserExec.ExecHandle] = { IF event # NIL AND new # NIL THEN { event.input _ UserExec.RopeSubst[old, new, event.input]; event.commandLine _ UserExec.RopeSubst[old, new, event.commandLine]; [] _ IO.RIS[UserExec.RopeSubst[old, new, GetRestOfStream[event.commandLineStream]], event.commandLineStream]; }; }; GetRestOfStream: PROC [in: STREAM] RETURNS[ROPE] = {charProc: IO.CharProc = {RETURN[FALSE, TRUE]}; RETURN[IO.GetSequence[in, charProc]]; }; RopeSubst: PUBLIC PROC [old, new, base: ROPE, case: BOOLEAN _ FALSE, allOccurrences: BOOLEAN _ TRUE] RETURNS[ROPE] = { lenOld: INT = Rope.Length[old]; lenNew: INT = Rope.Length[new]; i: INT _ 0; WHILE (i _ Rope.Find[s1: base, s2: old, case: case, pos1: i]) # -1 DO base _ Rope.Replace[base: base, start: i, len: Rope.Length[old], with: new]; IF ~allOccurrences THEN EXIT; i _ i + lenNew; ENDLOOP; RETURN[base]; }; viewerSpellDisabled: BOOLEAN; SetViewerSpell: UserProfile.ProfileChangedProc = { viewerSpellDisabled _ UserProfile.Boolean["Spell.viewerSpellDisabled", FALSE]; }; CorrectFileName: PROC [unknown: ROPE, viewer: Viewer] RETURNS [fileName: ROPE _ NIL, search: ROPE _ NIL] = { exec: UserExec.ExecHandle; out: IO.STREAM; name: ROPE; dot: INT; IF viewerSpellDisabled THEN RETURN; FOR i: INT IN [0..Rope.Length[unknown]) DO SELECT Rope.Fetch[unknown, i] FROM '/, '[, '< => RETURN[NIL]; ENDCASE; ENDLOOP; name _ unknown; IF (dot _ Rope.Find[unknown, "."]) # -1 THEN -- something following the dot. either this is a search rope, or a misspelled extension. { True: Spell.ConfirmProc = {RETURN[TRUE]}; search _ Rope.Substr[base: unknown, start: dot + 1]; IF SpellExtras.GetFileExtension[unknown: search, confirm: True] # NIL THEN search _ NIL -- thing following dot IS name of extension. drop through and correct on whole thing, e.g. UserExecPackagge.configg, or UserExecPackage.configg. We do the latter, even though at this point we might have the fileName in hand, in order that user gets a chance to response, be informed, etc ELSE { name _ Rope.Substr[base: unknown, len: dot]; IF UserExec.CheckForFile[fileName _ Rope.Concat[name, ".mesa"]] THEN RETURN; -- e.g. UserExec.RegisterCommand }; }; { ENABLE UNWIND => IF viewer # NIL THEN Finish[exec, viewer, out]; Abort: Spell.AbortProc = { hasResponded: BOOL; value: ATOM; UserExec.CheckForAbort[exec]; IF viewer # NIL AND ViewerAbort.UserAbort[viewer] THEN IO.UserAborted[viewer]; IF viewer # NIL THEN [hasResponded, value] _ UserExec.GetUserResponse[viewer]; IF hasResponded AND value = $No THEN ERROR terminateCorrection; }; Confirm: Spell.ConfirmProc -- [msg: ROPE, timeout: INT, defaultConfirm: BOOL] RETURNS [yes: BOOL] -- = { RETURN[UserExec.Confirm[msg: msg, timeout: timeout, defaultConfirm: defaultConfirm, exec: exec, viewer: viewer]] }; Inform: Spell.InformProc --[msg: ROPE] -- = { IF exec # NIL THEN {out _ UserExec.AcquireStreams[exec].out; -- since this can be called from random place, must acquire and release streams rather than using GetStreams out.PutF["*n*m%g*s\n", rope[msg]]; } ELSE MessageWindow.Append[message: msg, clearFirst: TRUE]; }; IF viewer = NIL THEN viewer _ ViewerTools.GetSelectedViewer[]; IF viewer # NIL THEN { exec _ UserExec.GetExecHandle[viewer: viewer]; IF viewer.menu # NIL THEN UserExec.SetupAskUser[viewer] ELSE viewer _ NIL; }; fileName _ Spell.GetTheFile[unknown: name, defaultExt: "mesa", inform: Inform, confirm: Confirm, abort: IF viewer # NIL THEN Abort ELSE NIL ! terminateCorrection => CONTINUE; IO.UserAborted => -- may not be under an exec! {MessageWindow.Append["Aborted", TRUE]; GOTO Out} ]; IF fileName = NIL THEN -- one last try, maybe unknown really is the name of a file with an extension, but extension not known, or maybe it is the misspelled name of a file with no extension. {fileName _ Spell.GetTheFile[unknown: unknown, inform: Inform, confirm: Confirm, abort: IF viewer # NIL THEN Abort ELSE NIL ! terminateCorrection => CONTINUE]; search _ NIL; }; GOTO Out; EXITS Out => Finish[exec, viewer, out]; }; }; AcquireResource: PUBLIC PROC [resource: REF ANY, owner: Rope.ROPE, exec: UserExec.ExecHandle] RETURNS[newlyAcquired: BOOL] = { abort: Resource.AbortProc = { RETURN[UserExec.UserAbort[exec]]; }; out: STREAM; success: BOOL; ownedBy: ROPE; [success, ownedBy] _ Resource.Acquire[resource: resource, waitForIt: FALSE, owner: owner, abortProc: abort]; UserExec.CheckForAbort[exec]; IF success THEN RETURN; IF owner # NIL THEN { out _ UserExec.AcquireStreams[exec].out; out.PutF["*n*mWaiting for %g to finish...", rope[ownedBy]]; out.Flush[]; }; [success, ownedBy] _ Resource.Acquire[resource: resource, waitForIt: TRUE, owner: owner, abortProc: abort]; UserExec.CheckForAbort[exec]; IF owner # NIL THEN { out.PutF["proceeding\n*s"]; out.Flush[]; UserExec.ReleaseStreams[exec]; }; RETURN[TRUE]; }; ReleaseResource: PUBLIC PROC [resource: REF ANY, doWhenReleased: PROC [REF ANY] _ NIL] RETURNS[released: BOOL] = TRUSTED { -- Process IF doWhenReleased # NIL THEN ERROR; RETURN[Resource.Release[resource]]; }; TiogaOps.RegisterFileNameProc[CorrectFileName]; UserProfile.CallWhenProfileChanges[SetViewerSpell]; END. -- of UserExecUtilImpl ‚Last Edited by: teitelman, April 20, 1983 4:18 pm GetTheOne, GetTheFile, etc. File Correction note: tioga has already checked whether unknown in its entirety exists. thing following a dot is NOT the name of a known extension. Assume it is a search, and the thing preceding it the name of the file. AcquireResource -- superceded by CommanderOps.Acquire. Nobody should be calling. Κω– "cedar" style˜J™1šΟk ˜ Jšœ œ˜(Jš œœœœœ&˜TJšœœ ˜Jšœ œ˜-Jšœœ6œ˜YJšœœ­˜ΈJšœ œ˜%Jšœ œ˜&Jšœ œα˜οJšœœ˜*Jšœœ˜Jšœ œ7˜HJšœ œ ˜Jšœ œ˜%J˜J˜—JšΠblœœœ˜!J˜JšœœŽ˜˜J˜Jšœ ˜J˜Jšœœœœ˜J˜JšΟnœœ˜JšŸ œœ˜'JšŸ œœ˜+head™JšŸœœœ˜"J˜šŸ œœ œ˜Jšœ œ˜Jšœ#œ˜'Jšœ%œ˜*Jšœ˜Jšœ˜Jšœœ˜Jšœœ˜Jšœœ˜J˜šœ œœ˜!šŸœ˜Jšœœ˜Jšœœ˜ Jšœ˜Jšœ œœœ˜PJšœ œœ:˜NJšœœ œœ˜?J˜—šŸœΟcHœ˜hšœ˜Jšœh˜hJšœ˜—Jšœ˜—šŸœ œ˜.šœœ˜JšœΠboœ  m˜–Jšœ"˜"J˜—Jšœ0œ˜:Jšœ˜—Jšœœœ˜Jš œœ*œœœ˜VJšœœ˜,Jšœœœ˜(Jšœ œœ˜3JšœΔœ˜ΞJšœœœF˜XJ˜J˜J˜——J˜šŸœœ œ˜#Jšœ œ˜Jšœ#œ˜'Jšœ%œ˜*Jšœ˜Jšœ˜Jšœœ˜Jšœœ˜Jšœœ˜J˜š œ œœœœ˜*šŸœ˜Jšœœ˜Jšœ˜ Jšœ˜Jšœ œœœ˜PJšœ œœ:˜NJšœœ œœ˜?J˜—šŸœ Hœ˜hšœ˜Jšœh˜hJšœ˜—Jšœ˜—šŸœ œ˜/šœœ˜Jšœ‘œ  m˜–Jšœ"˜"J˜—Jšœ0œ˜:Jšœ˜—Jšœœœ˜Jšœœœœ˜*šœœ˜%Jšœœ’˜œJš œœœœœ˜#Jšœœœ˜J˜—Jšœœ˜,Jšœœœ˜(šœ œœ˜Jšœ,œ˜PJšœ œ @˜SJ˜—JšœΛœ˜ΥJ˜J˜J˜——J˜šŸ œœœœœ œœœ  œ˜YJšœœœ˜Jšœœ˜'Jšœœ˜ Jšœœœœ˜,˜D˜Jšœ œœ˜—Jšœ˜—šœ˜J˜——J˜šŸ œœœœœœ:œœœœœ˜³Jšœœ˜Jšœœœ˜Jšœœœœ˜'Jšœœœ˜,š˜Jšœ*˜,š˜Jšœœœ( ˜HJšœœ,œœ ˜EJšœœœ˜J˜—Jšœœœœ ˜Mš˜Jšœ œœ˜—J˜—Jšœœœ˜(Jšœ!œœ'˜nJšœ ˜Jšœœœ˜(Jšœ*œœœ˜=šœ˜šŸœ˜Jšœœ˜Jšœœ˜ Jšœ˜Jšœ œœœ˜PJšœ œœ:˜NJšœœ œœ˜?J˜—šŸœHœ˜hšœ˜Jšœh˜hJšœ˜—Jšœ˜—šŸœ œ˜0šœœ˜Jšœ‘œ  m˜–Jšœ"˜"J˜—Jšœ0œ˜:J˜—Jšœœœ˜(Jšœ œœ˜3Jšœ•œ˜Ÿšœœœ˜Jšœœ˜Jš œœœ!œ!œ!œ m˜πšœœ˜Jšœ?˜?J˜—J˜—Jšœ˜Jšœ˜—Jšœ˜Jšœ˜—J˜šŸœœœœœœ:œœœ œœœœ˜ΗJšœœœ˜Jšœœœœ˜'šœœ˜"JšœœU˜_Jš œœœœœ˜#Jšœœœ˜J˜—šœ˜Jšœœ˜+šŸœ˜Jšœœ˜Jšœœ˜ Jšœ˜Jšœ œœœ˜PJšœ œœ:˜NJšœœ œœ˜?J˜—šŸœHœ˜hšœ˜Jšœh˜hJšœ˜—Jšœ˜—šŸœ œ˜.šœœ˜Jšœ‘œ  m˜–Jšœ"˜"J˜—Jšœ0œ˜:Jšœ˜—Jšœœœ˜(šœ œ˜Jšœ,œ˜PJšœ œ @˜SJ˜—Jšœ’œ˜¬Jšœ˜J˜—J˜—J˜šŸœ œ2œ˜QJšœœœ ‘œ˜0Jšœ œœ ˜4Jšœ˜—J˜šŸœœ œ5˜Yš œ œœœ˜#Jšœ8˜8JšœD˜DJšœœœb˜mJ˜—J˜—J˜š Ÿœœœœœ˜2Jšœœœœ˜/Jšœ˜%Jšœ˜J˜—šŸ œœœœœœœœœœ˜vJšœœ˜Jšœœ˜Jšœœ˜ šœ>˜EJ˜LJšœœœ˜J˜Jšœ˜—Jšœ˜ J˜——™JšŸœœ˜J˜šŸœ$˜2JšœGœ˜NJ˜—J˜šŸœœ œœ œœ œœ˜lJšœ˜Jšœœœ˜Jšœœ˜ Jšœœ˜ Jšœœœ˜#šœœœ˜*šœ˜"Jšœœœ˜Jšœ˜—Jšœ˜—J˜Jš G™Gšœ&œ X˜†šœ˜JšŸœœœœ˜)J˜4Jšœ@œœ   ˜ψšœ˜J˜,Jšœ>œœ  ˜mJš ƒ™ƒJšœ˜—J˜J˜—J˜—šœ˜Jš œœœ œœ˜@šŸœ˜Jšœœ˜Jšœœ˜ Jšœ˜Jšœ œœœ˜NJšœ œœ:˜NJšœœ œœ˜?J˜—šŸœ Jœ˜iJšœj˜pJ˜—šŸœ œ˜.šœœ˜Jšœ‘œ  m˜–Jšœ"˜"J˜—Jšœ0œ˜:Jšœ˜—J˜Jšœ œœ*˜>šœ œ˜Jšœ.˜.Jš œœœœ œ˜JJšœ˜—š œhœ œœœœ˜Jšœœ˜ šœ ˜/Jšœ!œ˜'Jšœ˜ Jšœ˜——šœ œœ §˜ΏJš œXœ œœœœœ˜ŸJšœ œ˜ J˜—Jšœ˜ š˜Jšœ!˜!—Jšœ˜—Jšœ˜——™PšŸœœœ œœœœœœ˜€šŸœ˜Jšœ˜!J˜—Jšœœ˜ Jšœ œ˜Jšœ œ˜JšœEœ"˜lJšœ˜Jšœ œœ˜šœ œ˜Jšœ‘œ ˜(Jšœ;˜;J˜ J˜—JšœEœ"˜kJšœ˜šœ œ˜Jšœ˜Jšœ ˜ J˜J˜—Jšœœ˜ Jšœ˜—J˜šŸœœœ œœœœœœœ œœ  ˜†Jšœœœœ˜#Jšœ˜#J˜—J˜—J˜/J˜3J˜Jšœ ˜J˜J˜J˜J˜—…—:œM