DIRECTORY CWF: TYPE USING [SetWriteProcedure, SWF2], DateAndTimeUnsafe: TYPE USING [Parse, Unintelligible], DFSubr: TYPE USING [AllocateDFSeq, DFSeq, FlattenDF, FreeDFSeq, StripLongName], FileIO: TYPE USING[Open], Inline: TYPE USING [BITXOR, BytePair, LowHalf], IO: TYPE USING[card, Close, CreateDribbleStream, EndOf, GetChar, GetToken, Handle, PutChar, PutF, PutFR, PutRope, rope, SkipOver, string, UserAbort, WhiteSpace], IOExtras: TYPE USING[GetLine], LongString: TYPE USING [AppendString, EqualString, EquivalentString, StringToDecimal], Process: TYPE USING [Detach], Rope: TYPE USING[Fetch, Length, ROPE, Text], RopeInline: TYPE USING[InlineFlatten], UnsafeSTP: TYPE USING [CompletionProcType, Delete, DesiredProperties, Enumerate, Error, FileInfo, GetFileInfo, Handle, NoteFileProcType, SetDesiredProperties], STPSubr: TYPE USING [Connect, HandleSTPError, StopSTP], String: TYPE USING [AppendChar, AppendString, LowerCase], Subr: TYPE USING [AbortMyself, Any, CopyString, debugflg, EndsIn, errorflg, FreeHugeZone, HugeZone, MakeTTYProcs, PagesUsedInHugeZone, PrintGreeting, strcpy, SubrInit, SubrStop, TTYProcs], Time: TYPE USING [Current], TypeScript: TYPE USING[TS, Create], UECP: TYPE USING[Argv, Parse], UserExec: TYPE USING[AcquireResource, AskUser, CommandProc, RegisterCommand, ReleaseResource, UserAbort], ViewerEvents: TYPE USING[EventProc, EventRegistration, RegisterEventProc, UnRegisterEventProc], ViewerIO: TYPE USING[CreateViewerStreams]; RemoteDeleteAllImpl: PROGRAM IMPORTS CWF, DateAndTimeUnsafe, DFSubr, FileIO, Inline, IO, IOExtras, LongString, Process, Rope, RopeInline, STP: UnsafeSTP, STPSubr, String, Subr, Time, TypeScript, UECP, UserExec, ViewerEvents, ViewerIO = { MAXDELETEFILE: CARDINAL = 9000; -- should be 12000 globalhost: STRING _ [30]; globalremotepattern: STRING _ [100]; typeScript: TypeScript.TS; in, out, logFile: IO.Handle; destroyEventRegistration: ViewerEvents.EventRegistration; possibleDelete: BOOL = TRUE; initialBackingPages: CARDINAL = 10; IFSBytesPerPage: CARDINAL = 2048; Main: UserExec.CommandProc = TRUSTED { ENABLE UNWIND => [] _ UserExec.ReleaseResource[$RemoteDeleteAll]; dfseq: DFSubr.DFSeq _ NIL; flat: Rope.Text; tok: STRING _ [100]; time, starttime: LONG CARDINAL; remnam: STRING _ [100]; stemp: STRING _ [100]; last: STRING _ [100]; host: STRING _ [100]; remotepattern: STRING _ [100]; npages: CARDINAL; parm: CARDINAL; argv: UECP.Argv _ UECP.Parse[event.commandLine]; wh: Subr.TTYProcs; Cleanup: PROC = { DFSubr.FreeDFSeq[@dfseq]; STPSubr.StopSTP[]; Subr.SubrStop[]; }; [] _ UserExec.AcquireResource[$RemoteDeleteAll, "RemoteDeleteAll", exec]; starttime _ Time.Current[]; wh _ Subr.MakeTTYProcs[in, out, typeScript, MyConfirm]; Subr.errorflg _ Subr.debugflg _ FALSE; Subr.PrintGreeting["RemoteDeleteAll"L]; { ENABLE { STP.Error => { lcode: LONG CARDINAL _ LOOPHOLE[code, CARDINAL]; out.PutF["FTP Error. "]; IF error ~= NIL THEN out.PutF["message: %s, code %d in Stp.Mesa\n", IO.string[error], IO.card[lcode]]; Subr.errorflg _ TRUE; GOTO leave; }; Subr.AbortMyself => { out.PutF["RemoteDeleteAll Aborted.\n"]; GOTO leave; }; UNWIND => Cleanup[]; }; IF argv.argc = 1 THEN GOTO usage; Subr.SubrInit[256]; dfseq _ DFSubr.AllocateDFSeq[maxEntries: MAXDELETEFILE, zoneType: huge]; IF UserExec.UserAbort[exec] THEN SIGNAL Subr.AbortMyself; flat _ RopeInline.InlineFlatten[argv[1]]; Subr.strcpy[remnam, LOOPHOLE[flat]]; time _ Time.Current[]; parm _ 2; WHILE parm < argv.argc DO flat _ RopeInline.InlineFlatten[argv[parm]]; Subr.strcpy[tok, LOOPHOLE[flat]]; IF NOT Subr.Any[tok, '.] THEN LongString.AppendString[tok, ".DF"L]; out.PutF["\nReading %s:\n", IO.string[tok]]; DFSubr.FlattenDF[dfseq: dfseq, dffilename: tok, h: wh, checkForOverwrite: FALSE, printStatus: TRUE]; parm _ parm + 1; ENDLOOP; time _ Time.Current[] - time; out.PutF["Time to read in all DF files: %r.\n", IO.card[time]]; [] _ DFSubr.StripLongName[remnam, host, stemp, last]; CWF.SWF2[remotepattern, "<%s>%s"L, stemp, last]; IF NOT Subr.EndsIn[remotepattern, "*"L] THEN String.AppendChar[remotepattern, '*]; IF NOT Subr.Any[remotepattern, '!] THEN String.AppendString[remotepattern, "!*"L]; npages _ Subr.PagesUsedInHugeZone[dfseq.dfzone]; out.PutF["%d pages used in Huge Zone, %d entries in flattened DF files.\n", IO.card[npages], IO.card[dfseq.size]]; Subr.strcpy[globalhost, host]; Subr.strcpy[globalremotepattern, remotepattern]; Process.Detach[FORK RunDeleteAll[dfseq, starttime, wh]]; dfseq _ NIL; EXITS usage => { out.PutF["Usage: RemoteDeleteAll directory dffile(s).\n"]; }; leave => NULL; }; [] _ UserExec.ReleaseResource[$RemoteDeleteAll]; }; RunDeleteAll: PROC[dfseq: DFSubr.DFSeq, starttime: LONG CARDINAL, wh: Subr.TTYProcs] = { stphandle: STP.Handle _ NIL; ndelete, nskipdf, nskipno: CARDINAL _ 0; connuser: STRING _ [40]; connpass: STRING _ [40]; haveDeleteBTree: BOOL _ FALSE; elapt: LONG CARDINAL _ starttime; nskippages, ndeletepages: LONG CARDINAL _ 0; fullspeed: BOOL _ FALSE; keepDirectory: STRING _ [100]; deleteDirectory: STRING _ [100]; NameList: TYPE = LONG POINTER TO NameListRecord; NameListRecord: TYPE = RECORD[ host: LONG STRING, directory: LONG STRING, version: CARDINAL, createtime: LONG CARDINAL, list: NameList ]; shortnamearray: LONG POINTER TO ShortRec _ NIL; ShortRec: TYPE = ARRAY [0 .. 8000] OF RECORD[ shortname: LONG STRING, list: NameList ] _ ALL[[NIL, NIL]]; hugezone: UNCOUNTED ZONE _ Subr.HugeZone[]; indigoHost: STRING _ "Indigo"L; ivyHost: STRING _ "Ivy"L; lastD: LONG STRING _ NIL; nLook, nScan, nInsert: CARDINAL _ 0; longfile: LONG STRING _ NIL; deleteFile, keepFile: IO.Handle; ComputeHost: PROC[o: LONG STRING] RETURNS[n: LONG STRING] = { RETURN[IF LongString.EquivalentString[o, indigoHost] THEN indigoHost ELSE IF LongString.EquivalentString[o, ivyHost] THEN ivyHost ELSE Subr.CopyString[o, hugezone]]; }; ComputeDirectory: PROC[o: LONG STRING] RETURNS[n: LONG STRING] = { IF lastD ~= NIL AND LongString.EquivalentString[o, lastD] THEN n _ lastD ELSE n _ lastD _ Subr.CopyString[o, hugezone]; }; AddName: PROC[host, directory, shortname: LONG STRING, version: CARDINAL, createtime: LONG CARDINAL] = { l: NameList _ NIL; element: CARDINAL _ HashedLookup[shortname]; IF shortnamearray[element].shortname = NIL THEN {-- not in table shortnamearray[element] _ [shortname: Subr.CopyString[shortname, hugezone], list: NIL]; nInsert _ nInsert + 1; }; l _ hugezone.NEW[NameListRecord]; l^ _ [host: ComputeHost[host], directory: ComputeDirectory[directory], version: version, createtime: createtime, list: shortnamearray[element].list]; shortnamearray[element].list _ l; }; HashedLookup: PROC[shortname: LONG STRING] RETURNS[element: CARDINAL] = { hv: CARDINAL _ Hash[shortname, LENGTH[shortnamearray^]]; -- hv IN [0 .. LENGTH[]) nLook _ nLook + 1; FOR i: CARDINAL IN [hv .. LENGTH[shortnamearray^]) DO nScan _ nScan + 1; IF shortnamearray[i].shortname = NIL THEN RETURN[i]; IF LongString.EquivalentString[shortnamearray[i].shortname, shortname] THEN RETURN[i]; ENDLOOP; FOR i: CARDINAL IN [0 .. hv) DO nScan _ nScan + 1; IF shortnamearray[i].shortname = NIL THEN RETURN[i]; IF LongString.EquivalentString[shortnamearray[i].shortname, shortname] THEN RETURN[i]; ENDLOOP; ERROR; -- full }; WriteKeepAndDeleteFiles: PROC = { nFiles: CARDINAL _ 0; time: LONG CARDINAL _ Time.Current[]; desiredProperties: STP.DesiredProperties _ ALL[FALSE]; EnumProc: STP.NoteFileProcType = { pages: CARDINAL; info: STP.FileInfo; continue _ yes; info _ STP.GetFileInfo[stphandle]; IF file.length > 2 AND Subr.EndsIn[file, ">!1"L] THEN RETURN; -- skip these questionable cases nFiles _ nFiles + 1; pages _ Inline.LowHalf[info.size/IFSBytesPerPage + 2]; IF Match[globalhost, info] THEN { IF NOT LongString.EquivalentString[keepDirectory, info.directory] THEN { keepFile.PutChar['\n]; -- extra space Subr.strcpy[keepDirectory, info.directory]; }; out.PutChar['+]; IF file.length > 60 THEN keepFile.PutF["%s %s %4d\n", IO.string[file], IO.string[info.create], IO.card[pages]] ELSE keepFile.PutF["%-60s %s %4d\n", IO.string[file], IO.string[info.create], IO.card[pages]]; nskippages _ nskippages + pages; nskipdf _ nskipdf + 1; } ELSE {-- insert in delete file IF NOT LongString.EquivalentString[deleteDirectory, info.directory] THEN { keepFile.PutChar['\n]; -- extra space Subr.strcpy[deleteDirectory, info.directory]; }; IF file.length > 60 THEN deleteFile.PutF["%s %s\n", IO.string[file], IO.string[info.create]] ELSE deleteFile.PutF["%-60s %s\n", IO.string[file], IO.string[info.create]]; ndeletepages _ ndeletepages + pages; ndelete _ ndelete + 1; }; IF in.UserAbort[] THEN continue _ no; }; desiredProperties[directory] _ TRUE; desiredProperties[nameBody] _ TRUE; desiredProperties[version] _ TRUE; desiredProperties[createDate] _ TRUE; desiredProperties[size] _ TRUE; STP.SetDesiredProperties[stphandle, desiredProperties]; STP.Enumerate[stphandle, globalremotepattern, EnumProc ! STP.Error => IF code = noSuchFile THEN { out.PutF["%s not found.\n", IO.string[globalremotepattern]]; CONTINUE } ELSE IF STPSubr.HandleSTPError[stphandle, code, error, wh] THEN RETRY ]; time _ Time.Current[] - time; out.PutF["\nEnumeration complete, %d files, elapsed time %r.\n", IO.card[nFiles], IO.card[time]]; out.PutF["Will offer to delete %d files.\n", IO.card[ndelete]]; out.PutF["This will free %d pages, and will leave %d pages alone.\n", IO.card[ndeletepages], IO.card[nskippages]]; ndelete _ 0; }; DeleteFile: PROC [fileName, createDate: Rope.ROPE] RETURNS[quit: BOOL _ FALSE] = { ch: CHAR; r: Rope.ROPE; longfile: STRING; mustConnect: BOOL _ FALSE; DelComplete: STP.CompletionProcType = { out.PutF["%s\n", IO.string[fileOrError]]; IF what = error AND LongString.EqualString[fileOrError, "File is protected - access denied."L] THEN mustConnect _ TRUE; }; r _ IO.PutFR["Delete %s of %s ", IO.rope[fileName], IO.rope[createDate]]; IF fullspeed THEN { out.PutRope[r]; ch _ 'y; } ELSE ch _ wh.Confirm[wh.in, wh.out, wh.data, r, 'n]; IF ch = 'q THEN { nskipno _ nskipno + 1; RETURN[quit: TRUE]; }; IF ch = 'a THEN { out.PutF["All\nDo you really want to delete without confirmation (Type ^ to confirm) "]; IF wh.in.GetChar[] = '^ THEN { ch _ 'y; fullspeed _ TRUE; }; }; -- continues on to delete this file IF ch = 'y AND possibleDelete THEN { ndelete _ ndelete + 1; stphandle _ STPSubr.Connect[host: globalhost, h: wh, onlyOne: TRUE]; DO mustConnect _ FALSE; STP.Delete[stp: stphandle, name: longfile, confirm: NIL, complete: DelComplete ! STP.Error => IF code = noSuchFile THEN { out.PutF["%s not found.\n", IO.string[longfile]]; CONTINUE } ELSE IF STPSubr.HandleSTPError[stphandle, code, error, wh] THEN RETRY ]; IF NOT mustConnect THEN EXIT; [] _ STPSubr.HandleSTPError[stphandle, accessDenied, NIL, wh]; ENDLOOP; } ELSE { nskipno _ nskipno + 1; }; }; Match: PROC[host: LONG STRING, info: STP.FileInfo] RETURNS[dontDelete: BOOL] = { element: CARDINAL; remdate: LONG CARDINAL _ 0; l: NameList; element _ HashedLookup[info.body]; IF shortnamearray[element].shortname = NIL THEN RETURN[FALSE]; -- not found l _ shortnamearray[element].list; WHILE l ~= NIL DO IF LongString.EquivalentString[l.directory, info.directory] AND LongString.EquivalentString[l.host, host] THEN { IF l.createtime > 0 THEN { IF remdate = 0 THEN remdate _ DateAndTimeUnsafe.Parse[info.create ! DateAndTimeUnsafe.Unintelligible => CONTINUE ].dt; IF remdate = l.createtime THEN RETURN[TRUE]; }; IF l.version > 0 THEN { IF info.version ~= NIL AND info.version.length > 0 THEN { vers: CARDINAL _ LongString.StringToDecimal[info.version]; IF vers = l.version THEN RETURN[TRUE]; }; }; IF l.createtime = 0 AND l.version = 0 THEN -- >, ~= RETURN[TRUE]; }; l _ l.list; ENDLOOP; RETURN[FALSE]; }; Cleanup: PROC = { IF keepFile ~= NIL THEN {keepFile.Close[]; keepFile _ NIL}; IF deleteFile ~= NIL THEN {deleteFile.Close[]; deleteFile _ NIL}; IF dfseq ~= NIL THEN {DFSubr.FreeDFSeq[@dfseq]; dfseq _ NIL}; STPSubr.StopSTP[]; Subr.SubrStop[]; }; { ENABLE { UNWIND => Cleanup[]; STP.Error => { lcode: LONG CARDINAL _ LOOPHOLE[code, CARDINAL]; out.PutF["FTP Error. "]; IF error ~= NIL THEN out.PutF["message: %s, code %d in Stp.Mesa\n", IO.string[error], IO.card[lcode]]; Subr.errorflg _ TRUE; GOTO out; }; ABORTED, Subr.AbortMyself => { out.PutF["RemoteDeleteAll Aborted.\n"]; GOTO out; }; }; shortnamearray _ hugezone.NEW[ShortRec _ ALL[[NIL, NIL]]]; out.PutF["Filling in hash table ... "]; FOR i: CARDINAL IN [0 .. dfseq.size) DO AddName[dfseq[i].host, dfseq[i].directory, dfseq[i].shortname, dfseq[i].version, dfseq[i].createtime]; ENDLOOP; out.PutF["done.\n"]; out.PutF["%d insertions, %d scans in %d looks.\n", IO.card[nInsert], IO.card[nScan], IO.card[nLook]]; out.PutF["Enumerating [%s]%s ... \n", IO.string[globalhost], IO.string[globalremotepattern]]; stphandle _ STPSubr.Connect[host: globalhost, h: wh, onlyOne: TRUE]; out.PutF["Writing list of files that will be deleted on 'RemoteDeleteAll.DeleteFiles$'\n"]; out.PutF["Writing list of files that will NOT be deleted on 'RemoteDeleteAll.KeepFiles$'\n"]; keepFile _ FileIO.Open["RemoteDeleteAll.KeepFiles$", overwrite]; keepFile.PutF["\n\nThese files will not be deleted.\n\n"]; keepFile.PutF[" FileName CreateTime IFSPages\n"]; deleteFile _ FileIO.Open["RemoteDeleteAll.DeleteFiles$", overwrite]; deleteFile.PutF["\nThese files will be deleted.\n\n"]; deleteFile.PutF[" FileName CreateTime IFSPages\n"]; WriteKeepAndDeleteFiles[]; keepFile.PutF["\n\n----------------------\n"]; keepFile.Close[]; keepFile _ NIL; deleteFile.PutF["\n\n----------------------\n"]; deleteFile.Close[]; deleteFile _ NIL; out.PutF["List of files that will be deleted written on 'RemoteDeleteAll.DeleteFiles$'\n"]; out.PutF["List of files that will NOT be deleted written on 'RemoteDeleteAll.KeepFiles$'\n"]; STPSubr.StopSTP[]; hugezone _ Subr.FreeHugeZone[hugezone]; shortnamearray _ NIL; dfseq _ NIL; IF in.UserAbort[] THEN SIGNAL Subr.AbortMyself; out.PutF["Type print RemoteDeleteAll.KeepFiles$ RemoteDeleteAll.DeleteFiles$ to print the files.\n"]; out.PutF["\n\n\n(For each file, type y to delete, q to quit, \n\ta to delete subsequent files w/o confirmation and any other char to not delete.)\n"]; out.PutF["\nEnumerating [%s]%s ... \n", IO.string[globalhost], IO.string[globalremotepattern]]; deleteFile _ FileIO.Open["RemoteDeleteAll.DeleteFiles$"]; UNTIL deleteFile.EndOf[] DO fileName, createDate: Rope.ROPE; fileName _ deleteFile.GetToken[IO.WhiteSpace]; deleteFile.SkipOver[]; -- skips over white space before create date createDate _ IOExtras.GetLine[deleteFile]; -- gets the remainder of the line IF fileName.Length[] = 0 THEN LOOP; IF fileName.Fetch[0] # '< THEN LOOP; IF DeleteFile[fileName, createDate].quit THEN EXIT; IF in.UserAbort[] THEN EXIT; ENDLOOP; deleteFile.Close[]; deleteFile _ NIL; out.PutF["\n%d files matched, %d files deleted, %d files skipped.\n", IO.card[nskipdf], IO.card[ndelete], IO.card[nskipno]]; EXITS out => NULL; }; Cleanup[]; elapt _ Time.Current[] - elapt; out.PutF["\nTotal elapsed time for RemoteDeleteAll %r.",IO.card[elapt]]; IF Subr.errorflg THEN out.PutF["\tErrors logged.\n"]; out.PutChar['\n]; PrintSeparator[]; }; Hash: PROC[shortname: LONG STRING, modulo: CARDINAL] RETURNS[hv: CARDINAL] = { h, i: CARDINAL _ 0; v: Inline.BytePair; IF shortname.length = 0 THEN ERROR; DO v.low _ LOOPHOLE[String.LowerCase[shortname[i]], CARDINAL]; i _ i + 1; v.high _ IF i >= shortname.length THEN 0 ELSE LOOPHOLE[String.LowerCase[shortname[i]], CARDINAL]; i _ i + 1; h _ Inline.BITXOR[h, v]; IF i >= shortname.length THEN EXIT; ENDLOOP; hv _ h MOD modulo; }; Init: PROC = { typeScript _ TypeScript.Create[info: [name: "RemoteDeleteAll Window", iconic: FALSE]]; [in: in, out: out, file: logFile] _ SetUpLogStreams[typeScript]; [] _ CWF.SetWriteProcedure[TTYProc]; UserExec.RegisterCommand["RemoteDeleteAll.~", Main]; destroyEventRegistration _ ViewerEvents.RegisterEventProc[MyDestroy, destroy]; }; NullTTYProc: PROC[ch: CHAR] = {}; -- prints nothing MyDestroy: ViewerEvents.EventProc = TRUSTED { IF event ~= destroy OR viewer ~= typeScript THEN RETURN; [] _ CWF.SetWriteProcedure[NullTTYProc]; -- turn off printing Subr.SubrStop[]; logFile.Close[]; ViewerEvents.UnRegisterEventProc[destroyEventRegistration, destroy]; }; SetUpLogStreams: PROC[ts: TypeScript.TS] RETURNS[in, out, file: IO.Handle] = { file _ FileIO.Open["RemoteDeleteAll.Log", overwrite]; [in, out] _ ViewerIO.CreateViewerStreams[name: "RemoteDeleteAll Window", viewer: ts, editedStream: FALSE]; out _ IO.CreateDribbleStream[out, file]; }; MyConfirm: SAFE PROC[in, out: IO.Handle, data: REF ANY, msg: Rope.ROPE, dch: CHAR] RETURNS[CHAR] = CHECKED { value: ATOM; value _ UserExec.AskUser[msg: msg, viewer: NARROW[data], keyList: LIST[$Yes, $No, $All, $Local, $Quit]]; -- order is important SELECT value FROM $All => RETURN['a]; $Local => RETURN['l]; $No => RETURN['n]; $Quit => RETURN['q]; $Yes => RETURN['y]; ENDCASE => ERROR; }; TTYProc: PROC[ch: CHAR] = { out.PutChar[ch]; }; PrintSeparator: PROC = { out.PutF["===============================\n"]; }; Init[]; }. °RemoteDeleteAllImpl.Mesa, last edit February 9, 1983 6:13 pm Pilot 6.0/ Mesa 7.0 Usage: RemoteDeleteAll directory list of dfs (nesting allowed) Last Edited by: Maxwell, May 31, 1983 11:46 am max number of files in all DF file we want to ignore MDS usage! endof MDS this is the procedure called by the Simple Executive forked as a separate process returns element with shortname = NIL if can't find (this will be the first NIL encountered) returns element ~= NIL if found NOTE: Desired properties have been set OR LongString.EquivalentString[info.body, "Dir.Dir"L] it turns out we don't get the right signal so we have to raise it ourselves the error is STP.Error[accessDenied], but the string is IFS-specific can't raise STP.Error in here, unwind will kill connection so we set a flag debugging be careful: do not use hugezone data structures after this debugging CWF.WF1["%d\n"L, @hv]; cannot print anything in this, monitor is locked ʘJšœ<™™>J™.šÏk ˜ Jšœœœ˜*Jšœœœ˜6Jšœœœ=˜OJšœœœ˜Jšœœœœ˜/šœœœE˜SJ˜N—Jšœ œœ ˜Jšœ œœ@˜VJšœ œœ ˜Jšœœœœ˜,Jšœ œœ˜&šœ œœ1˜FJ˜(J˜0—Jšœ œœ$˜7Jšœœœ'˜9šœœœ<˜LJ˜JJ˜&—Jšœœœ ˜Jšœ œœœ ˜#Jšœœœ˜šœ œœ9˜MJ˜—Jšœœœ1˜IJ˜Jšœ œœ˜*—J˜Jšœ˜šœœ˜Jšœœ˜2Jšœœ6œ˜YJ˜$—J˜Jšœ4™4Jš œœ Ïc˜2J˜Jšœ ™ Jšœ œ˜Jšœœ ˜$Jšœœ˜Jšœœ˜J˜9Jšœ ™ J˜Jšœœœ˜Jšœœ˜#Jšœœ˜!J˜Jšœ4™4šœœ˜&Jšœœ4˜AJšœœ˜J˜Jšœœ ˜Jšœœœ˜Jšœœ ˜Jšœœ ˜Jšœœ ˜Jšœœ ˜Jšœœ ˜Jšœœ˜Jšœœ˜Jšœœœ˜0J˜J˜šÏnœœ˜J˜J˜J˜J˜J˜—J˜IJ˜J˜7Jšœ œ˜&J˜'˜Jšœ˜šœ ˜Jš œœœœœ˜0J˜šœ œœ˜šœ/œ˜@Jšœ˜——Jšœœ˜Jšœ˜ J˜—˜J˜'Jšœ˜ J˜—Jšœ˜J˜J˜—Jšœœœ˜!J˜Jšœ) œ˜HJšœœœ˜9J˜)Jšœœ˜$J˜J˜ šœ˜J˜,Jšœœ˜!šœœœ˜J˜%—Jšœœ˜,˜6Jšœœœ˜-—J˜Jšœ˜—J˜Jšœ0œ ˜?J˜5Jšœ-˜0šœœ"œ˜-J˜%—šœœ˜'J˜*—J˜0˜LJšœœ˜&—J˜J˜0J˜Jšœœ%˜8Jšœœ˜ Jš˜˜ J˜:J˜—Jšœ œ˜J˜J˜0J˜—J˜Jšœ™šŸ œœ!œœ˜XJšœ œ œ˜Jšœœ˜(Jšœ œ˜Jšœ œ˜Jšœœœ˜Jšœœœ ˜!Jšœœœ˜,Jšœ œœ˜Jšœœ ˜Jšœœ ˜ Jš œ œœœœ˜0šœœœ˜Jšœœœ˜Jšœ œœ˜Jšœ œ˜Jšœ œœ˜J˜J˜—Jš œœœœ œ˜/š œ œœ œœ˜-Jšœ œœ˜J˜Jšœœœœ˜—Jšœ œœ˜+Jšœ œ ˜Jšœ œ ˜Jšœœœœ˜Jšœœ˜$Jšœ œœœ˜Jšœœ˜ J˜šŸ œœœœœœœ˜=šœœ,œ ˜DJšœœ)œ˜œ˜JJšœž˜%J˜-J˜—šœœœ˜HJšœ˜—Jšœ"œœ˜OJ˜$J˜J˜—Jšœœ˜%J˜J˜—Jšœœ˜$Jšœœ˜#Jšœœ˜"Jšœ œ˜%Jšœœ˜Jšœ4˜7šœ3˜6šœœ ˜šœœ˜Jšœœ˜J˜'—Jšœ˜—J˜Jšœ ™ Jšœ3œœœ˜eJ˜Jšœ&œœ˜]Jšœ>œ˜DJ˜[J˜]J˜@J˜:J˜pJ˜DJ˜6J˜rJ˜J˜.J˜Jšœ œ˜J˜0J˜Jšœ œ˜J˜[J˜]J˜J˜'Jšœ:™:Jšœœ˜Jšœœ˜ Jšœœœ˜/J™J˜eJ˜–Jšœ(œœ˜_J˜9J˜šœ˜Jšœœ˜ Jšœœ ˜.Jšœž,˜CJšœÏrœž!˜LJšœœœ˜#Jšœœœ˜$Jšœ'œœ˜3Jšœœœ˜Jšœ˜—J˜Jšœ œ˜J˜˜EJšœœœ˜6—Jš˜Jšœœ˜ J˜J˜ J˜Jšœ8œ˜Hšœœ˜J˜—J˜J˜J˜J˜—šŸœœ œœ œœœ˜NJšœœ˜J˜Jšœœœ˜#š˜Jšœœ!œ˜;J˜ šœ œœ˜)Jšœœ!œ˜8—J˜ Jšœ œ˜Jšœœœ˜#Jšœ˜—Jšœœ˜Jšœ ™ J˜J˜—šŸœœ˜JšœNœ˜VJ˜@Jšœœ˜$J˜4J˜NJ˜J˜—JšŸ œœœž˜3J˜Jšœ0™0šœ$˜+J˜Jšœœœœ˜8Jšœœ!ž˜=J˜J˜J˜DJ˜J˜—š Ÿœœœœœ ˜LJ˜J˜5˜TJšœœ˜—Jšœœ ˜(J˜J˜—šŸ œœœ œœœ œœ˜RJšœœœ˜Jšœœ˜ šœ+œ˜9Jšœ œ$ž˜F—Jšœ˜Jšœœ˜Jšœ œ˜Jšœœ˜Jšœ œ˜Jšœœ˜Jšœœ˜J˜J˜—šŸœœœ˜J˜J˜J˜—šŸœœ˜J˜.J˜J˜—J˜J˜J˜J˜—…—BàZ¤