DIRECTORY BasicTime, CDIO, CD, CDCleanUp, CDDirectory, CDEvents, CDPrivate, CDProperties, CDSequencer, CDValue, Convert, FileNames, FS, IO, Process, Rope, TerminalIO, TokenIO; CDOut: CEDAR PROGRAM --monitored by TokenIO IMPORTS BasicTime, CD, CDIO, CDCleanUp, CDDirectory, CDEvents, CDProperties, CDSequencer, CDValue, Convert, FileNames, FS, IO, Process, Rope, TerminalIO, TokenIO EXPORTS CDIO SHARES CDProperties = BEGIN xChipndaleFile: INT = 12121983; xVersion: INT = 15; propKeyForKey: REF = NEW[INT]; attached: BOOL _ FALSE; gDesign: CD.Design _ NIL; gDirectoryNum: INT; simpleObjectsInTableMax: INT _ 10000; gCountSimpleObjectsInTable: INT; gThisKey: REF ATOM _ NIL; gLastKey: REF ATOM _ NIL; gDirectoryMark: TokenIO.Mark; gQuiet, gEmergency: BOOL _ FALSE; gRemoveAlso: REF ATOM _ NIL; dotCnt: INT _ 0; --not exact, it's mainly to pacify user colCnt: INT _ 0; --not exact, it's mainly to pacify user FileExists: PROC [name: Rope.ROPE] RETURNS [found: BOOL_TRUE] = { [] _ FS.FileInfo[name: name, remoteCheck: FALSE ! FS.Error => IF error.group#bug THEN {found _ FALSE; CONTINUE} ]; }; GetNameInteractively: PROC [wDir: Rope.ROPE_NIL] RETURNS [name: Rope.ROPE] = { TerminalIO.WriteRope["file name:"]; IF ~Rope.IsEmpty[wDir] THEN TerminalIO.WriteRopes[" (", wDir, ")"]; name _ TerminalIO.RequestRope[" > "]; name _ CDIO.MakeName[name, "dale", wDir]; IF FileExists[name] THEN { TerminalIO.WriteRopes["File ", name, " exists already; overwrite?"]; IF ~TerminalIO.Confirm[choice: "overwrite", label: "file exists"] THEN ERROR TerminalIO.UserAbort; TerminalIO.WriteRope[" yes\n"]; }; }; WriteRope: PROC [r: Rope.ROPE] = INLINE { IF ~gQuiet THEN Feedback[gEmergency, r] }; Feedback: PROC [fork: BOOL, r: Rope.ROPE] = INLINE { IF fork THEN TRUSTED { Process.Detach[ FORK TerminalIO.WriteRope[r] ] } ELSE TerminalIO.WriteRope[r] }; WriteLayer: PUBLIC PROC [l: CD.Layer] = { TokenIO.WriteAtom[CD.LayerKey[l]]; }; WritePos: PUBLIC PROC [p: CD.Position] = { TokenIO.WriteInt[p.x]; TokenIO.WriteInt[p.y]; }; WriteRect: PUBLIC PROC[r: CD.Rect] = { TokenIO.WriteInt[r.x1]; TokenIO.WriteInt[r.y1]; TokenIO.WriteInt[r.x2]; TokenIO.WriteInt[r.y2]; }; WriteProperties: PUBLIC PROC [props: CD.PropList] = { IF ~gEmergency THEN IF gQuiet THEN Process.Yield[]--should make output slower than redraw ELSE CDSequencer.CheckAborted[gDesign]; FOR l: CD.PropList _ props, l.rest WHILE l#NIL DO WITH l.first.key SELECT FROM a: ATOM => { pp: CDProperties.PropertyProcs = CDProperties.FetchProcs[a]; IF pp#NIL AND pp.internalWrite#NIL THEN { TokenIO.WriteAtom[$Property]; TokenIO.WritePushFlag[a]; pp.internalWrite[a, l.first.val]; TokenIO.WritePopFlag[]; } ELSE WITH l.first.val SELECT FROM r: Rope.ROPE => { TokenIO.WriteAtom[$DefaultProperty]; TokenIO.WriteAtom[a]; TokenIO.WriteRope[r]; }; at: ATOM => { TokenIO.WriteAtom[$DefaultProperty]; TokenIO.WriteAtom[a]; TokenIO.WriteAtom[at]; }; ri: REF INT => { TokenIO.WriteAtom[$DefaultProperty]; TokenIO.WriteAtom[a]; TokenIO.WriteInt[ri^]; }; pl: CDPrivate.LayerRef => { TokenIO.WriteAtom[$DefaultProperty]; TokenIO.WritePushFlag[a]; TokenIO.WriteAtom[$layer]; -- now comes a property list WriteLayer[pl.number]; TokenIO.WritePopFlag[]; }; pl: CD.PropList => { TokenIO.WriteAtom[$DefaultProperty]; TokenIO.WritePushFlag[a]; TokenIO.WriteAtom[$properties]; -- now comes a property list WriteProperties[pl]; TokenIO.WritePopFlag[]; }; rl: LIST OF Rope.ROPE => { TokenIO.WriteAtom[$DefaultProperty]; TokenIO.WritePushFlag[a]; TokenIO.WriteAtom[$ropeList]; -- now comes a rope list FOR l: LIST OF Rope.ROPE _ rl, l.rest WHILE l#NIL DO TokenIO.WriteRope[l.first]; ENDLOOP; TokenIO.WritePopFlag[]; }; ENDCASE => NULL; }; ra: REF ATOM => IF ra^=$MayBeRemoved AND ra#gLastKey THEN gRemoveAlso _ ra; ENDCASE => NULL; ENDLOOP; }; WriteInstance: PUBLIC PROC [ap: CD.Instance] = { TokenIO.WriteInt[ap.location.x]; TokenIO.WriteInt[ap.location.y]; CDIO.WriteOrientation[ap.orientation]; WriteProperties[ap.properties]; WriteObject[ap.ob]; }; Length: PROC [il: CD.InstanceList] RETURNS [leng: INT_0] = INLINE { FOR list: CD.InstanceList _ il, list.rest WHILE list#NIL DO leng _ leng+1; ENDLOOP; }; WriteInstanceList: PUBLIC PROC [list: CD.InstanceList] = { TokenIO.WriteInt[Length[list]]; FOR l: CD.InstanceList _ list, l.rest WHILE l#NIL DO WriteInstance[l.first]; ENDLOOP; }; WriteObject: PUBLIC PROC [ob: CD.Object] = { WITH CDProperties.GetObjectProp[from: ob, prop: gThisKey] SELECT FROM key: REF INT => { IF key^<0 THEN {TerminalIO.WriteRope["**** circular object dependency\n"]; ERROR}; dotCnt _ dotCnt+1; IF dotCnt>=10 THEN {WriteRope["."]; dotCnt_0}; TokenIO.WriteInt[key^]; }; ENDCASE => WriteObjectDefinition[ob] }; WriteObjectDefinition: PROC [ob: CD.Object] = { key: REF INT _ NIL; WriteTableKey: PROC[ob: CD.Object] = INLINE { gDirectoryNum _ gDirectoryNum+1; key _ NEW[INT _ -gDirectoryNum]; --a negative key is not yet finished TokenIO.WriteInt[gDirectoryNum]; CDProperties.PutObjectProp[onto: ob, prop: gThisKey, val: key]; }; colCnt _ colCnt+1; IF colCnt>=10 THEN {WriteRope[":"]; colCnt_0}; IF ob.class.internalWrite=NIL THEN { ob1: CD.Object _ CDDirectory.Expand[ob].new; IF ob1=NIL THEN ob1 _ CDDirectory.ExpandByDraw[me: ob].new; IF ob1=NIL THEN { TokenIO.WritePushFlag[$Unknown]; TokenIO.WritePopFlag[]; WriteRope["*object not written\n"]; } ELSE { WriteRope["*"]; WriteObject[ob1]; CDProperties.PutObjectProp[onto: ob, prop: gThisKey, val: CDProperties.GetObjectProp[ob1, gThisKey]]; }; } ELSE { TokenIO.WritePushFlag[ob.class.objectType]; IF ob.class.inDirectory THEN WriteTableKey[ob] ELSE IF gCountSimpleObjectsInTable KillOldProps[lastAgain: TRUE]; PrepareOutput: PROC = { WITH CDValue.Fetch[gDesign, propKeyForKey, design] SELECT FROM ra: REF ATOM => gLastKey _ ra; ENDCASE => gLastKey _ NEW[ATOM]; gLastKey^ _ $MayBeRemoved; gThisKey _ NEW[ATOM _ $UsedForIO]; CDValue.Store[gDesign, propKeyForKey, gThisKey]; gDirectoryNum _ 0; }; NameForOutput: PROC [d: CD.Design] RETURNS [Rope.ROPE] = { IF Rope.Length[d.name]<=0 OR Rope.Fetch[d.name]='/ OR Rope.Fetch[d.name]='[ THEN RETURN [NIL] ELSE RETURN [d.name]; }; TimeKey: PROC [] RETURNS [Rope.ROPE] = { RETURN [Convert.RopeFromTime[from: BasicTime.Now[], end: seconds]]; }; PrepareOutput[]; TokenIO.WriteRope[NameForOutput[gDesign]]; TokenIO.WriteRope[TimeKey[]]; gDirectoryMark _ TokenIO.MarkAndWriteInt[gDirectoryNum]; -- number of entries in directory gCountSimpleObjectsInTable _ 0; [] _ CDDirectory.Enumerate[design: gDesign, action: EachDirectoryEntry]; WriteProperties[gDesign.properties^]; WritePushLevel[gDesign.actual]; TokenIO.UpdateMark[mark: gDirectoryMark, value: gDirectoryNum]; TokenIO.WriteAtom[$EndOfDesign]; KillOldProps[lastAgain: FALSE]; }; OpenStream: PROC [design: CD.Design, to: REF, emergency, quiet: BOOL] RETURNS [stream: IO.STREAM, mustClose: BOOL_TRUE, name: Rope.ROPE_NIL] = { wDir: Rope.ROPE _ CDIO.GetWorkingDirectory[design]; IF Rope.IsEmpty[wDir] THEN wDir _ FileNames.CurrentWorkingDirectory[]; WITH to SELECT FROM s: IO.STREAM => RETURN [stream_s, mustClose_FALSE, name_NIL]; r: Rope.ROPE => name _ r; ENDCASE => IF to#NIL THEN Feedback[emergency, "bad file parameter in WriteDesign\n"]; IF Rope.IsEmpty[name] THEN name _ IF emergency THEN "///temp/ChipNDale/emergency/emergency.dale" ELSE IF quiet THEN Rope.Cat["///temp/ChipNDale/saved/", design.name] ELSE GetNameInteractively[wDir]; name _ CDIO.MakeName[name, "dale", wDir]; stream _ FS.StreamOpen[fileName: name, accessOptions: $create, keep: 2, createByteCount: 25600 ! FS.Error => { mustClose _ FALSE; stream _ NIL; IF ~quiet THEN Feedback[emergency, Rope.Cat["file not opened: ", error.explanation, "\n"]]; IF error.group#bug THEN CONTINUE; } ]; IF stream=NIL AND ~quiet THEN Feedback[emergency, "file not created\n"]; }; Attach: PROC [stream: IO.STREAM, emergency: BOOL, quiet: BOOL] RETURNS [done: BOOL_TRUE] = TRUSTED { TokenIO.AttachWriter[stream ! TokenIO.Error => {done _ FALSE; CONTINUE}]; IF ~done AND emergency THEN { FOR i: INT IN [0..5] DO TokenIO.StopWriting[]; Process.Pause[Process.SecondsToTicks[1]]; TokenIO.StopWriting[]; Process.Pause[Process.SecondsToTicks[2]]; Process.Detach[ FORK TerminalIO.WriteRope["TRIES TO BREAK THE LOCK\n"]]; TokenIO.ReleaseWriter[]; -- DANGEROUS Process.Pause[Process.SecondsToTicks[1]]; done _ TRUE; TokenIO.AttachWriter[stream ! TokenIO.Error => {done _ FALSE; CONTINUE}]; IF done THEN RETURN; ENDLOOP; }; IF done THEN {gQuiet _ quiet; gEmergency _ emergency; attached _ TRUE} ELSE IF ~quiet THEN Feedback[emergency, HelpMessage["ChipNDale internal locks are hold"]]; }; Release: PROC [] = { IF attached THEN TokenIO.ReleaseWriter[]; attached _ FALSE; }; WriteDesign: PUBLIC PROC [design: CD.Design, to: REF, quiet: BOOL, emergency: BOOL] RETURNS [done: BOOL_FALSE] = { ENABLE UNWIND => Release[]; mustClose: BOOL _ FALSE; binFile: IO.STREAM; fileName: Rope.ROPE; sealMark: TokenIO.Mark; [binFile, mustClose, fileName] _ OpenStream[design, to, emergency, quiet]; IF binFile=NIL THEN RETURN; IF ~Attach[binFile, emergency, quiet] THEN RETURN; [] _ CDEvents.ProcessEvent[ev: beforeOutputEvent, design: design, x: NIL, listenToDont: FALSE]; gDesign _ design; TokenIO.WriteInt[xChipndaleFile]; TokenIO.WriteInt[xVersion]; sealMark _ TokenIO.MarkAndWriteInt[0]; -- invalid seal; it will be fixed at the end TokenIO.WriteAtom[design.technology.key]; TokenIO.WriteRope[design.technology.name]; IF CDEvents.ProcessEvent[ev: writeEvent, design: design, x: NIL, listenToDont: TRUE].dont THEN { WriteRope["output not done\n"]; Release[]; RETURN }; InnerWriteDesign[]; TokenIO.UpdateMark[sealMark, -1]; -- validate seal IF mustClose THEN IO.Close[binFile]; WriteRope["\n"]; IF ~emergency AND quiet THEN TerminalIO.WriteF["%ldesign %g written on file %g%l\n", [rope["i"]], [rope[design.name]], [rope[fileName]], [rope[" "]]] ELSE Feedback[emergency, Rope.Cat["design ", design.name, " written on file ", fileName, "\n"]]; Release[]; IF ~quiet THEN { CDValue.Store[boundTo: design, key: $CDxLastFile, value: fileName]; [] _ CDEvents.ProcessEvent[afterOutputEvent, design]; }; done _ TRUE; }; HelpMessage: PROC [r: Rope.ROPE_NIL] RETURNS [msg: Rope.ROPE_NIL] = { Write: PROC [r: Rope.ROPE] = { msg _ Rope.Cat[msg, r, "\n"] }; Write["****************************************"]; Write[r]; Write["Help for saving a design:"]; Write["wait until the background saving is done and make a copy of it! (but don't rely on background saving alone; background saving is done WITHOUT requiring monitor locks, for obvious reasons.)"]; Write["then, try regular output again, before you proceed to any of the more drastic methods following:\n"]; Write["SHIFT-SHIFT-SWAT (>2 seconds) should save all designs"]; Write["or use a command tool: (the command tool commands require you to bringover the necesarry sources and bcd's first)"]; Write[" ""_ CDSequencerImpl.savePeriod _ -1"" "]; Write[" prevents any further un-monitored automatic saving in parallel with your other command tool commands."]; Write[" ""_ CDEmergencyHandling.SaveAll[]"" "]; Write[" or"]; Write[" ""_ TokenIO.ReleaseWriter[]"" "]; Write[" to release the locks; after this try a regular output command"]; Write["****************************************"]; }; writeEvent: CDEvents.EventRegistration = CDEvents.RegisterEventType[$WriteTechnologyPrivate]; beforeOutputEvent: CDEvents.EventRegistration = CDEvents.RegisterEventType[$BeforeOutput]; afterOutputEvent: CDEvents.EventRegistration = CDEvents.RegisterEventType[$AfterOutput]; END. (CDOut.mesa Copyright c 1983, 1986 by Xerox Corporation. All rights reserved. Created by Ch. Jacobi, December 12, 1983 2:02 pm Some code from Kim Rachmeler Last Edited by: Christian Jacobi, September 4, 1986 5:08:59 pm PDT -- global variables -- protected through attachment with Attach, Release and TokenIO --may raise TerminalIO.UserAbort --feedback on terminal --feedback on terminal --don't hang the output if viewer wedges --PropertyLists guarantees properties are not reordered; going through list is ok --object has internalWrite proc --if due to funny recursion me is now written out: this will write a reference --at this place we know that CDCleanUp will fork and will set the priority down --uses globals --opens the file but does not do any output to it yet --makes the messages on failure --makes messages if failed and no danger of locking --to is either a IO.STREAM, a Rope.ROPE, or NIL --if emergency, some locks are ignored, interactive input is skipped; you better --roll back after an emergency write is done -- Module initialization ΚL˜codešœ ™ Kšœ Οmœ7™BKšœ0™0Kšœ™K™BK™—šΟk ˜ Kšœ ž˜ Kšžœ˜Kšžœ˜Kšœ ˜ Kšœ ˜ Kšœ ˜ K˜ K˜ Kšœ ˜ Kšœ˜Kšœ˜Kšœ ž˜ Kšžœ˜Kšžœ˜K˜K˜K˜ K˜—K˜šΠblœžœžœΟc˜+Kš žœ žœžœZžœžœ$˜‘Kšžœž˜ Kšžœ˜—Kšž˜K˜Kšœžœ ˜Kšœ žœ˜K˜Kšœžœžœžœ˜K˜Kšœ™Kšœ@™@Kšœ žœžœ˜K˜Kšœ žœžœ˜Kšœžœ˜Kšœžœ ˜%Kšœžœ˜ Kšœ žœ˜Kšœ žœ˜Kšœ˜Kšœž œ˜!Kšœ žœžœžœ˜K˜Kšœžœ '˜8Kšœžœ (˜8K˜š Οn œžœ žœžœ žœžœ˜Ašœžœ#žœ˜0Kš œžœ žœžœ žœžœ˜?Kšœ˜—Kšœ˜—K˜š ‘œžœ žœžœžœ žœ˜NKšœ ™ Kšœ#˜#Kšžœžœ(˜CKšœ&˜&Kšœžœ˜)šžœžœ˜KšœD˜Dšžœ@žœ˜GKšžœ˜—Kšœ˜K˜—Kšœ˜—K˜š‘ œžœ žœžœ˜)Kšœ™Kšžœ žœ˜(Kšœ˜—K˜š ‘œžœžœ žœžœ˜4Kšœ™šžœžœžœ˜Kšœ(™(Kšœžœ˜.Kšœ˜—Kšžœ˜Kšœ˜—K˜š‘ œžœžœžœ ˜)Kšœžœ˜"Kšœ˜—K˜š‘œžœžœžœ˜*Kšœ˜Kšœ˜Kšœ˜—K˜š‘ œžœžœžœ ˜&Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜—K˜š‘œžœžœ žœ˜5šžœ žœ˜Kšžœžœ '˜EKšžœ#˜'—KšœQ™Qš žœžœžœžœž˜1šžœ žœž˜šœžœ˜ Kšœ<˜<š žœžœžœžœžœ˜)K˜Kšœ˜Kšœ!˜!Kšœ˜K˜—šžœžœ žœž˜!šœžœ˜K˜$Kšœ˜Kšœ˜K˜—šœžœ˜ K˜$Kšœ˜Kšœ˜K˜—šœžœžœ˜K˜$Kšœ˜Kšœ˜K˜—šœ˜K˜$Kšœ˜Kšœ ˜8Kšœ˜Kšœ˜K˜—šœžœ˜K˜$Kšœ˜Kšœ! ˜=Kšœ˜Kšœ˜K˜—šœžœžœžœ˜K˜$Kšœ˜Kšœ ˜7š žœžœžœžœžœžœž˜4Kšœ˜Jšžœ˜—Kšœ˜K˜—Kšžœžœ˜—K˜—Kš œžœžœžœžœ žœ˜KKšžœžœ˜—Kšžœ˜—Kšœ˜—K˜š‘ œžœžœžœ˜0Kšœ ˜ Kšœ ˜ Kšžœ"˜&K˜K˜Kšœ˜K˜—š ‘œžœžœžœžœžœ˜Dš žœžœžœžœž˜;Kšœ˜Kšžœ˜—Kšœ˜—K˜š‘œžœžœžœ˜:Kšœ˜š žœžœžœžœž˜4Kšœ˜Kšžœ˜—Kšœ˜—K˜š‘ œžœžœžœ ˜,šžœ6žœž˜Ešœžœžœ˜Kšžœžœ=žœ˜RKšœžœ žœ˜AKšœ˜K˜—Kšžœ˜$—Kšœ˜—K˜š‘œžœžœ ˜/Kšœž œ˜š‘ œžœžœ žœ˜-Kšœ ˜ Kšœžœžœ $˜EKšœ ˜ Kšœ?˜?K˜—Kšœžœ žœ˜Ašžœžœžœ˜$Kšœžœ%˜,Kšžœžœžœ,˜;šžœžœžœ˜Kšœ ˜ Kšœ˜Kšœ#˜#K˜—šžœ˜Kšœ˜Kšœ˜Kšœe˜eKšœ˜—Kšœ˜—šžœ˜Kšœ™Kšœ+˜+Kšžœžœ˜.šžœžœ4žœ˜AKšœ:˜:Kšœ!˜!Kšœ˜Kšœ˜—Kšœ˜šžœžœ˜Kšœ(˜(K˜—Kšœ˜Kšœ˜Kšžœžœžœ ˜:K˜—Kšœ:žœ˜?Kšœ˜—K˜š ‘œžœžœžœžœ ˜1Kšžœžœžœžœ˜Kšžœ žœžœ˜,Kšœ˜Kšžœžœžœ%˜FKšžœ˜Kšœ-˜-Kšœ!˜!Kšœ˜—K˜š‘œ# œ˜Ršžœžœ4žœ˜TKšœžœ8˜?Kš žœžœžœžœ *˜AKšžœžœ:˜VKšœN™NKšœ˜K˜—Kšœ˜—K˜š‘œ!˜3Kšžœžœžœ˜5Kšœ˜—K˜K˜š‘ œžœ žœ˜(š‘œžœ žœžœ˜:KšœO™OKšœ(˜(Kšœ˜—Kšœ žœžœ ˜Kšœ žœžœ ˜Kšœ žœžœ˜ Kšžœ žœžœ )˜WKšžœ žœžœ žœ'˜HKšžœ žœžœžœ'˜NK˜—K˜š‘œžœ˜Kšžœžœžœ˜/K˜š‘ œžœ˜Kšœ™šžœ/žœž˜>Kšœžœžœ˜Kšžœžœžœ˜ —Kšœ˜Kšœ žœžœ˜"Kšœ0˜0Kšœ˜Kšœ˜—K˜š ‘ œžœžœ žœžœ˜:Kš žœžœžœžœžœžœ˜]Kšžœžœ ˜Kšœ˜—K˜š‘œžœžœžœ˜(Kšžœ=˜CKšœ˜—K˜Kšœ˜Kšœ*˜*Kšœ˜Kšœ9 !˜ZKšœ˜KšœH˜HKšœ%˜%Kšœ˜Kšœ?˜?Kšœ ˜ Kšœžœ˜Kšœ˜—K˜š‘ œžœ žœ žœžœžœ žœžœ žœžœ žœžœ˜Kšœ5™5Kšœ™Kšœ žœžœ˜3Kšžœžœ,˜Fšžœžœž˜Kš œžœžœžœžœžœ˜=Kšœžœ ˜Kšžœžœžœžœ<˜U—šžœžœ˜šœ˜Kšžœ žœ-˜>Kšžœžœžœ3˜EKšžœ˜!——Kšœžœ˜)šœ žœV˜ašžœ ˜ Kšœ žœ žœ˜!šžœžœ˜KšœL˜L—Kšžœžœžœ˜!Kšœ˜—Kšœ˜—Kšžœžœžœžœ+˜HKšœ˜—K˜š‘œžœ žœžœ žœ žœžœžœžœžœ˜dKšœ3ž™4Kšœ7žœžœ˜Išžœžœ žœ˜šžœžœžœž˜Kšœ˜Kšœ)˜)Kšœ˜Kšœ)˜)Kšœžœ4˜HKšœ  ˜%Kšœ)˜)Kšœžœ˜ Kšœ7žœžœ˜IKšžœžœžœ˜Kšžœ˜—K˜—Kšžœžœ5žœ˜FKšžœžœžœG˜ZKšœ˜K˜—š‘œžœ˜Kšžœ žœ˜)Kšœ žœ˜Kšœ˜—K˜š‘ œžœžœ žœ žœ žœ žœžœžœžœ˜rK™/KšœQ™QKšœ,™,Kšžœžœ˜Kšœ žœžœ˜Kšœ žœžœžœ˜@KšœJ˜JKšžœ žœžœžœ˜Kšžœžœ"žœžœ˜2KšœEžœžœ˜_Kšœ˜Kšœ"˜"Kšœ˜Kšœ' ,˜SKšœ)˜)Kšœ*˜*šžœ:žœžœžœ˜`Kšœ˜Kšœ ˜ Kšž˜Kšœ˜—Kšœ˜Kšœ" ˜2Kšžœ žœžœ˜$Kšœ˜šžœ žœžœ˜Kšœx˜x—šžœ˜Kšœ[˜[—Kšœ ˜ šžœžœ˜KšœC˜CKšœ5˜5K˜—Kšœžœ˜ Kšœ˜—K˜š‘ œžœ žœžœžœ žœžœ˜Eš‘œžœ žœ˜Jšœ˜Kšœ˜—Kšœ2˜2Kšœ ˜ Kšœ#˜#KšœΖ˜ΖKšœl˜lKšœ?˜?Kšœ{˜{Kšœ6˜6Kšœu˜uKšœ1˜1Kšœ˜Kšœ+˜+KšœN˜NKšœ2˜2Kšœ˜—K˜Kšœ™Kšœ]˜]KšœZ˜ZKšœX˜XKšžœ˜K˜—…—3ψIl