DIRECTORY AlpineCmds USING [Copy], AlpineEnvironment USING [AccessList], AlpineFS USING [OpenFile, Open, OpenOrCreate, StreamFromOpenFile, WriteProperties], AlpFile USING [PropertyValuePair], AlpInstance USING [Create], AlpTransaction USING [Handle, AssertAlpineWheel, Create, Finish], DB USING [Segment, TransactionHandle, CloseTransaction, DeclareSegment, EraseSegment, MakeTransHandle], Commander USING [Handle, Register, CommandProc], CommandTool USING [ArgumentVector, FileWithSearchRules, NextArgument, Parse], Convert USING [IntFromRope], DefaultRemoteNames USING [Get], FS USING [ComponentPositions, Error, OpenFile, nullOpenFile, Close, Create, ExpandName, GetInfo, PagesForBytes, SetByteCountAndCreatedTime, SetPageCount, StreamFromOpenFile, StreamOpen], IO, RefText USING [line, Fetch, Length, TrustTextAsRope], Rope, UserCredentials USING [Get], WalnutDefs USING [Segment], WalnutKernelDefs USING [LogInfoFromRoot, RootEntry], WalnutRegistry USING [CurrentWalnutState], WalnutSchema USING [SetSchemaVersion], WalnutStream USING [logFileInfo, WriteEntry]; NewWalnutUser: CEDAR PROGRAM IMPORTS AlpineCmds, AlpineFS, AlpInstance, AlpTransaction, Commander, CommandTool, Convert, DB, DefaultRemoteNames, FS, IO, RefText, Rope, UserCredentials, WalnutRegistry, WalnutSchema, WalnutStream = BEGIN STREAM: TYPE = IO.STREAM; ROPE: TYPE = Rope.ROPE; CmdHandle: TYPE = Commander.Handle; ArgumentVector: TYPE = CommandTool.ArgumentVector; defaultServer: ROPE = "Luther.alpine"; serverRope: ROPE = "\001ServerAndDirectory\002"; serverLen: NAT = Rope.Length[serverRope]; RootEntry: TYPE = WalnutKernelDefs.RootEntry; LogInfoFromRoot: TYPE = WalnutKernelDefs.LogInfoFromRoot; field1: REF TEXT = NEW[TEXT[RefText.line]]; InitWalnutFiles: Commander.CommandProc = { argV: ArgumentVector = CommandTool.Parse[cmd]; userKey, alpineServer: ROPE; userRName: ROPE = UserCredentials.Get[].name; IF IsActive[cmd, "InitWalnutFiles"] THEN RETURN; userKey _ CommandTool.NextArgument[cmd]; IF userKey = NIL THEN userKey _ IO.PutFR["%g's Mail Database", IO.rope[userRName]]; alpineServer _ CommandTool.NextArgument[cmd]; DoMailInit[cmd, userRName, userKey, alpineServer, FALSE]; }; InitForOtherUser: Commander.CommandProc = { argV: ArgumentVector = CommandTool.Parse[cmd]; userKey, alpineServer, userRName: ROPE; IF IsActive[cmd, "NewWalnutUser"] THEN RETURN; userRName _ CommandTool.NextArgument[cmd]; IF userRName = NIL THEN { cmd.out.PutRope["\n You must supply a userRName - quitting\n"]; RETURN }; IF userRName.Find["."] = -1 THEN -- need a registry userRName _ userRName.Cat[".", DefaultRemoteNames.Get[].registry]; userKey _ IO.PutFR["%g's Mail Database", IO.rope[userRName]]; alpineServer _ CommandTool.NextArgument[cmd]; DoMailInit[cmd, userRName, userKey, alpineServer, TRUE]; }; JustDoFiles: Commander.CommandProc = { rootFile: ROPE; argV: ArgumentVector = CommandTool.Parse[cmd]; IF IsActive[cmd, "JustDoFiles"] THEN RETURN; rootFile _ CommandTool.NextArgument[cmd]; IF rootFile = NIL THEN RETURN; [] _ InitFiles[rootFile, cmd, FALSE]; }; IsActive: PROC[cmd: Commander.Handle, which: ROPE] RETURNS[is: BOOL] = { IF WalnutRegistry.CurrentWalnutState[] # active THEN RETURN[FALSE]; cmd.err.PutF["\n*** You must destroy the Walnut Control window before doing %g - quitting\n", IO.rope[which]]; RETURN[TRUE]; }; DoMailInit: PROC[ cmd: CmdHandle, userRName, userKey, alpineServer: ROPE, masquerade: BOOL] = { localName, remoteName: ROPE; serverAndDirectory: ROPE; outcome: ROPE; IF alpineServer = NIL THEN alpineServer _ defaultServer; IF alpineServer.Find["."] = -1 THEN alpineServer _ alpineServer.Concat[".alpine"]; serverAndDirectory _ IO.PutFR["[%g]<%g>", IO.rope[alpineServer], IO.rope[userRName] ]; localName _ IO.PutFR["///Users/%g/Walnut.Root", IO.rope[userRName] ]; IF ~CreateRootFile[cmd, masquerade, userKey, localName, serverAndDirectory, userRName] THEN RETURN; remoteName _ Rope.Concat[serverAndDirectory, "Walnut.Root"]; outcome _ CopyLocalToAlpineFile[ alpineFile: remoteName, from: localName, masquerade: masquerade]; cmd.out.PutRope[outcome]; [] _ InitFiles[remoteName, cmd, masquerade]; }; DoOtherInit: Commander.CommandProc = { argV: ArgumentVector = CommandTool.Parse[cmd]; userKey, localName, remoteName, baseName: ROPE; serverAndDirectory: ROPE; userRName: ROPE = UserCredentials.Get[].name; IF cmd = NIL THEN RETURN; IF IsActive[cmd, "InitOtherWalnutFiles"] THEN RETURN; baseName _ CommandTool.NextArgument[cmd]; IF baseName = NIL THEN { cmd.out.PutRope["No base name for database given - quitting\n"]; RETURN; }; IF baseName.Fetch[0] = '[ THEN serverAndDirectory _ baseName ELSE IF baseName.Fetch[0] = '/ THEN serverAndDirectory _ FS.ExpandName[baseName].fullFName ELSE serverAndDirectory _ IO.PutFR["[%g]<%g>", IO.rope[defaultServer], IO.rope[baseName]]; userKey _ CommandTool.NextArgument[cmd]; IF userKey = NIL THEN userKey _ IO.PutFR["%g's Walnut Database", IO.rope[baseName]]; localName _ IO.PutFR["///Users/%g/Walnut.Root", IO.rope[userRName] ]; IF ~CreateRootFile[cmd, FALSE, userKey, localName, serverAndDirectory, baseName] THEN RETURN; remoteName _ Rope.Concat[serverAndDirectory, "Walnut.Root"]; AlpineCmds.Copy[to: remoteName, from: localName]; [] _ InitFiles[remoteName, cmd, FALSE]; }; Confirm: PROC[cmd: CmdHandle] RETURNS[continue: BOOL] = { char: CHAR; line: ROPE; cmd.err.PutRope["\nType y(CR) or Y(CR) or CR to continue anyway. "]; line _ cmd.in.GetLineRope[]; IF line.Length[] = 0 THEN RETURN[TRUE]; -- only CR typed continue _ (char _ line.Fetch[0]) = 'y OR char = 'Y OR char = '\n; }; CreateRootFile: PROC[ cmd: CmdHandle, masquerade: BOOL, userKey, localName, serverAndDirectory, mailForName: ROPE] RETURNS[BOOL] = { template, rootStream: STREAM; rootOF: FS.OpenFile; templateRoot: ROPE; BEGIN ENABLE UNWIND => { IF template # NIL THEN template.Close[]; IF rootStream # NIL THEN rootStream.Close[]; IF rootOF # FS.nullOpenFile THEN FS.Close[rootOF]; }; templateRoot _ CommandTool.FileWithSearchRules["Template-Walnut", ".root", cmd, FALSE]; IF templateRoot = NIL THEN { cmd.out.PutRope["Couldn't find Template-Walnut.root"]; RETURN[FALSE] }; template _ FS.StreamOpen[templateRoot ! FS.Error => { cmd.out.PutRope[error.explanation]; template _ NIL; CONTINUE}]; IF template = NIL THEN RETURN[FALSE]; rootOF _ FS.Create[name: localName, setKeep: TRUE, keep: 2 ! FS.Error => { cmd.out.PutRope[error.explanation]; rootOF _ FS.nullOpenFile; template.Close[]; CONTINUE}]; IF rootOF = FS.nullOpenFile THEN RETURN[FALSE]; rootStream _ FS.StreamFromOpenFile[openFile: rootOF, accessRights: $write ! FS.Error => { cmd.out.PutRope[error.explanation]; template.Close[]; FS.Close[rootOF]; rootStream _ NIL; CONTINUE}]; IF rootStream = NIL THEN RETURN[FALSE]; DO BEGIN line: REF TEXT; pos: INTEGER; IF template.EndOf[] THEN EXIT; line _ template.GetLine[field1 ! IO.EndOfStream => GOTO done]; IF Rope.Find[RefText.TrustTextAsRope[line], "UserSuppliedKey"] = 0 THEN { rootStream.PutRope[userKey]; rootStream.PutChar['\n]; LOOP }; IF Rope.Find[RefText.TrustTextAsRope[line], "UserRName"] = 0 THEN { rootStream.PutRope[mailForName]; rootStream.PutChar['\n]; LOOP }; IF (pos_ Rope.Find[RefText.TrustTextAsRope[line], serverRope]) = -1 THEN { rootStream.PutText[line]; rootStream.PutChar['\n]; LOOP }; FOR i: NAT IN [0 .. pos) DO rootStream.PutChar[RefText.Fetch[line, i]]; ENDLOOP; rootStream.PutRope[serverAndDirectory]; FOR i: NAT IN [pos+serverLen .. RefText.Length[line]) DO rootStream.PutChar[RefText.Fetch[line, i]]; ENDLOOP; rootStream.PutChar['\n]; EXITS done => EXIT; END; ENDLOOP; template.Close[]; rootStream.Flush[]; rootStream.Close[]; RETURN[TRUE]; END; }; InitFiles: PROC[rootFile: ROPE, cmd: CmdHandle, masquerade: BOOL] RETURNS[BOOL]= { rootEntry: RootEntry; logInfoList: LIST OF LogInfoFromRoot; logInfoCount: INT _ 0; keyValue, mailFor, dbName, newMailLog, readArchiveLog: ROPE; fileFailure: ROPE = "Couldn't initialize file %g\n"; rootStream: STREAM; finished: BOOL _ FALSE; fileStore: ROPE = rootFile.Substr[start: 1, len: rootFile.SkipTo[1, "]"] - 1]; openFile: AlpineFS.OpenFile; transHandle: AlpTransaction.Handle; IF rootFile = NIL THEN { cmd.out.PutRope["\nNo file specified\n"]; RETURN[FALSE] }; transHandle _ GetTransHandle[fileStore]; IF masquerade THEN AlpTransaction.AssertAlpineWheel[transHandle, TRUE]; openFile _ AlpineFS.Open[name: rootFile, access: $read, transHandle: transHandle ! FS.Error => CONTINUE]; IF openFile = FS.nullOpenFile THEN { cmd.out.PutF["Can't open rootFile: %g\n", IO.rope[rootFile] ]; -- couldn't open RETURN[FALSE] }; rootStream _ AlpineFS.StreamFromOpenFile[openFile]; BEGIN ENABLE IO.EndOfStream, IO.Error => GOTO cantParse; rootStream.SetIndex[0]; DO curPos: INT _ rootStream.GetIndex[]; rootEntry_ NextRootEntry[rootStream]; DO IF rootEntry.ident.Equal["Key", FALSE] THEN { keyValue_ rootEntry.value; EXIT }; IF rootEntry.ident.Equal["MailFor", FALSE] THEN { mailFor_ rootEntry.value; EXIT }; IF rootEntry.ident.Equal["Database", FALSE] THEN { dbName_ rootEntry.value; EXIT }; IF rootEntry.ident.Equal["NewMailLog", FALSE] THEN { newMailLog_ rootEntry.value; EXIT }; IF rootEntry.ident.Equal["ReadArchiveLog", FALSE] THEN { readArchiveLog_ rootEntry.value; EXIT }; IF rootEntry.ident.Equal["LogInfo", FALSE] THEN { logInfoList _ CONS[rootEntry.info, logInfoList]; logInfoCount _ logInfoCount + 1; EXIT }; IF rootEntry.ident.Equal["End", FALSE] THEN { finished_ TRUE; EXIT }; rootStream.Close[ ! IO.Error, FS.Error => CONTINUE]; cmd.out.PutF["Unknown entry in RootFile at pos %g\n", IO.int[curPos]]; RETURN[FALSE] ENDLOOP; IF finished THEN EXIT; ENDLOOP; rootStream.Close[ ! IO.Error, FS.Error => CONTINUE]; EXITS cantParse => { rootStream.Close[ ! IO.Error, FS.Error => CONTINUE]; cmd.out.PutRope["Couldn't parse root file\n"]; RETURN[FALSE] }; END; IF keyValue.Length[] = 0 OR dbName.Length[] = 0 OR mailFor.Length[] = 0 OR newMailLog.Length[] = 0 OR readArchiveLog.Length[] = 0 OR logInfoCount # 2 THEN { cmd.out.PutRope["Incomplete RootFile - missing entries\n"]; RETURN[FALSE] }; BEGIN cp: FS.ComponentPositions; fn: ROPE; oServer: ROPE; CheckServer: PROC[name: ROPE] RETURNS[ok: BOOL] = { [fn, cp, ] _ FS.ExpandName[name]; oServer _ Rope.Substr[fn, cp.server.start, cp.server.length]; IF ~(ok _ Rope.Equal[fileStore, oServer, FALSE]) THEN cmd.out.PutF["\nserver for file %g does not agree with that for %g\n", IO.rope[name], IO.rope[rootFile]]; }; IF ~CheckServer[dbName] THEN RETURN[FALSE]; IF ~CheckServer[newMailLog] THEN RETURN[FALSE]; IF ~CheckServer[readArchiveLog] THEN RETURN[FALSE]; IF ~CheckServer[logInfoList.first.fileName] THEN RETURN[FALSE]; IF ~CheckServer[logInfoList.rest.first.fileName] THEN RETURN[FALSE]; END; cmd.out.PutRope["\n Initializing log files\n"]; IF ~EnsureEmptyFile[cmd, newMailLog, fileStore, masquerade, 200] THEN RETURN[FALSE]; IF ~EnsureEmptyFile[cmd, readArchiveLog, fileStore, masquerade, 0] THEN RETURN[FALSE]; FOR lil: LIST OF LogInfoFromRoot _ logInfoList, lil.rest UNTIL lil = NIL DO seqNo: ROPE _ lil.first.logSeqNo; fileName: ROPE = lil.first.fileName; openFile: AlpineFS.OpenFile; IF seqNo.Fetch[0] # 'E THEN { -- write the LogFileInfo entry in the file strm: STREAM; realSeqNo: INT; transHandle: AlpTransaction.Handle; IF ~EnsureEmptyFile[cmd, fileName, fileStore, masquerade, 200] THEN RETURN[FALSE]; transHandle _ GetTransHandle[fileStore]; IF masquerade THEN AlpTransaction.AssertAlpineWheel[transHandle, TRUE]; openFile _ AlpineFS.Open[name: fileName, access: $write, transHandle: transHandle]; strm _ AlpineFS.StreamFromOpenFile[openFile, $write]; realSeqNo _ Convert.IntFromRope[seqNo]; WalnutStream.logFileInfo.key _ keyValue.ToRefText[]; WalnutStream.logFileInfo.internalFileID _ lil.first.internalFileID; WalnutStream.logFileInfo.logSeqNo _ realSeqNo; []_ WalnutStream.WriteEntry[strm, WalnutStream.logFileInfo]; strm.Close[]; } ELSE IF ~EnsureEmptyFile[cmd, fileName, fileStore, masquerade, 0] THEN RETURN[FALSE]; ENDLOOP; BEGIN inaccessible: BOOL _ FALSE; cmd.out.PutRope[" Initializing segment file\n"]; BEGIN segment: DB.Segment = $Walnut; tHandle: AlpTransaction.Handle = GetTransHandle[fileStore]; transHandle: DB.TransactionHandle = DB.MakeTransHandle[tHandle]; IF masquerade THEN AlpTransaction.AssertAlpineWheel[tHandle, TRUE]; DB.DeclareSegment[ filePath: dbName, segment: segment, readonly: FALSE, nPagesInitial: 256, nPagesPerExtent: 256]; IF masquerade THEN AlpTransaction.AssertAlpineWheel[tHandle, TRUE]; [] _ DB.EraseSegment[segment, transHandle]; -- leaves trans open IF masquerade THEN AlpTransaction.AssertAlpineWheel[tHandle, TRUE]; WalnutSchema.SetSchemaVersion[segment ! ABORTED => GOTO cant]; DB.CloseTransaction[transHandle]; EXITS cant => { cmd.out.PutF["Couldn't initialize database %g\n", IO.rope[dbName] ]; RETURN[FALSE] }; END; END; cmd.out.PutF[" Walnut files have been properly intialized\n"]; RETURN[TRUE]; }; NextRootEntry: PROC[strm: STREAM] RETURNS[rte: RootEntry] = { []_ strm.SkipWhitespace[flushComments: TRUE]; rte.ident _ strm.GetTokenRope[].token; IF rte.ident.Equal["End", FALSE] THEN RETURN; []_ strm.SkipWhitespace[flushComments: TRUE]; IF rte.ident.Equal["LogInfo", FALSE] THEN { lif: LogInfoFromRoot; lif.fileName _ strm.GetTokenRope[IO.IDProc].token; []_ strm.SkipWhitespace[flushComments: TRUE]; lif.internalFileID _ strm.GetInt[]; []_ strm.SkipWhitespace[flushComments: TRUE]; lif.seqNoPos _ strm.GetIndex[]; lif.logSeqNo _ strm.GetTokenRope[].token; []_ strm.SkipWhitespace[flushComments: TRUE]; rte.info _ lif; } ELSE IF rte.ident.Equal["Key", FALSE] THEN rte.value _ strm.GetLineRope[] ELSE { rte.value _ strm.GetTokenRope[IO.IDProc].token; []_ strm.SkipWhitespace[flushComments: TRUE]; }; RETURN[rte]; }; nonZeroLen: ROPE = "File %g has nonZero (%g bytes) length\n"; EnsureEmptyFile: PROC[ cmd: CmdHandle, fileName, fileStore: ROPE, masquerade: BOOL, pagesWanted: INT _ 2] RETURNS[ok: BOOL] = { strm: STREAM; openFile: AlpineFS.OpenFile; numPages, length: INT; transHandle: AlpTransaction.Handle = GetTransHandle[fileStore]; prop: highWaterMark AlpFile.PropertyValuePair = [highWaterMark[0]]; IF masquerade THEN AlpTransaction.AssertAlpineWheel[transHandle, TRUE]; openFile _ AlpineFS.OpenOrCreate[name: fileName, pages: pagesWanted, transHandle: transHandle]; strm _ AlpineFS.StreamFromOpenFile[openFile, $write]; length _ strm.GetLength[]; IF length # 0 THEN { continue: BOOL _ FALSE; cmd.out.PutRope[IO.PutFR[nonZeroLen, IO.rope[fileName], IO.int[length]]]; continue _ Confirm[cmd]; IF ~continue THEN { strm.Close[]; RETURN[FALSE] }; }; numPages_ FS.GetInfo[openFile].pages; IF pagesWanted > numPages THEN FS.SetPageCount[openFile, pagesWanted]; strm.SetIndex[0]; strm.SetLength[0]; strm.Flush[]; -- make it notice the SetLength IF masquerade THEN AlpTransaction.AssertAlpineWheel[transHandle, TRUE]; AlpineFS.WriteProperties[openFile, LIST[prop]]; strm.Close[]; RETURN[TRUE]; }; CopyLocalToAlpineFile: PROC[ alpineFile: Rope.ROPE, from: Rope.ROPE, masquerade: BOOL] RETURNS[Rope.ROPE] = { openFile: AlpineFS.OpenFile; localStrm, alpStrm: STREAM; transHandle: AlpTransaction.Handle; pages, bytes: INT; fileStore: ROPE = alpineFile.Substr[start: 1, len: alpineFile.SkipTo[1, "]"] - 1]; localStrm _ FS.StreamOpen[from]; pages _ FS.PagesForBytes[bytes _ localStrm.GetLength[]]; transHandle _ GetTransHandle[fileStore]; IF masquerade THEN AlpTransaction.AssertAlpineWheel[transHandle, TRUE]; openFile _ GetAlpineFile[alpineFile, pages, transHandle]; alpStrm _ AlpineFS.StreamFromOpenFile[openFile, $write]; alpStrm.SetIndex[0]; localStrm.SetIndex[0]; CopyStreams[from: localStrm, to: alpStrm, transHandle: transHandle, masquerade: masquerade]; alpStrm.Flush[]; IF masquerade THEN AlpTransaction.AssertAlpineWheel[transHandle, TRUE]; openFile.SetByteCountAndCreatedTime[ bytes: bytes ]; IF AlpTransaction.Finish[transHandle, commit] # commit THEN { FS.Close[openFile ! FS.Error => CONTINUE]; localStrm.Close[]; RETURN[IO.PutFR[" commit failed for %g", IO.rope[alpineFile]]]; }; FS.Close[openFile ! FS.Error => CONTINUE]; localStrm.Close[]; RETURN[IO.PutFR["\n\n****%g created, copied from %g", IO.rope[alpineFile], IO.rope[from]]]; }; GetTransHandle: PROC[fileStore: ROPE] RETURNS[AlpTransaction.Handle] = { RETURN[ AlpTransaction.Create[instHandle:AlpInstance.Create[fileStore: fileStore]] ] }; GetAlpineFile: PROC[alpineFile: Rope.ROPE, pages: INT, transHandle: AlpTransaction.Handle] RETURNS[openFile: AlpineFS.OpenFile] = { initialAccess: AlpineEnvironment.AccessList = LIST[ "owner" ]; openFile _ AlpineFS.OpenOrCreate[ name: alpineFile, pages: pages, transHandle: transHandle ]; AlpineFS.WriteProperties[ file: openFile, properties: LIST[ [modifyAccess[initialAccess]], [readAccess[initialAccess]] ] ]; }; copyBuffer: REF TEXT _ NEW[TEXT[8*512]]; CopyStreams: PROC[ from, to: STREAM, transHandle: AlpTransaction.Handle, masquerade: BOOL] = { count: INT _ 0; DO IF from.GetBlock[copyBuffer, 0, 8*512] = 0 THEN EXIT; to.PutBlock[copyBuffer]; IF (count _ count + 1) >= 10 THEN { to.Flush[]; IF masquerade THEN AlpTransaction.AssertAlpineWheel[transHandle, TRUE]; count _ 0; }; ENDLOOP }; Commander.Register["InitWalnutFiles", InitWalnutFiles, "Create the files for a Walnut Mail Databse"]; Commander.Register["NewWalnutUser", InitForOtherUser, "Create the files for a Walnut Mail Databse for someone else - AlpineWheels only"]; Commander.Register["InitOtherWalnutFiles", DoOtherInit, "Create the files for walnut"]; Commander.Register["JustDoFiles", JustDoFiles, "Create the files using an existing rootFile"]; END. vNewWalnutUser.mesa Copyright c 1985, 1986 by Xerox Corporation. All rights reserved. Willie-Sue Orr, July 18, 1986 1:34:20 pm PDT special purpose code to construct a Walnut RootFile from the Template-Walnut.Root, store it on the appropriate alpine server and initialize the appropriate files on the server Types and variables Procedures Creates a rootFile, stores it on alpine, and initializes all the files names therein, erases the database and sets its version stamp to the correct value. Returns FALSE if operation fails. Creates a rootFile, stores it on alpine, and initializes all the files names therein, erases the database and sets its version stamp to the correct value. Returns FALSE if operation fails. This is for non-mail walnut databases check that all the alpine-server names are the same * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Κΐ– "Cedar" style˜head™Icodešœ Οmœ7™BJ™,L™Ibody™―code2šΟk ˜ Nšœ žœ˜Jšœžœ˜%Nšœ žœE˜SJšœžœ˜"Jšœ žœ ˜Jšœžœ-˜ANšžœžœ_˜gNšœ žœ!˜1Nšœ žœ<˜MNšœžœ˜Nšœžœ˜šžœžœ4˜—J˜Jšœ žœ"žœ˜EJ˜šžœžœ3˜PJšžœžœ˜ —J˜Jšœ<˜šžœAžœ˜IJšœ˜Jšœ˜Jšž˜J˜—šžœ;žœ˜CJšœ ˜ Jšœ˜Jšž˜J˜—šžœBžœ˜JJšœ˜Jšœ˜Jšž˜J˜—šžœžœžœ ž˜Jšœ+˜+Jšžœ˜—Jšœ'˜'šžœžœžœ)ž˜8Jšœ+˜+Jšžœ˜—Jšœ˜šž˜Jšœžœ˜ —Jšžœ˜Jšžœ˜J˜—Jšœ˜Jšœ˜Jšœ˜Jšžœžœ˜ Jšžœ˜J˜—J˜š Ÿ œžœ žœžœžœžœ˜RJ˜Jšœ žœžœ˜%Jšœžœ˜Jšœ7žœ˜šžœ˜!šž˜šœž˜ Jšœ2žœ˜DJšžœžœ˜ J˜———Jšžœ˜—Jšžœ˜J˜Jšœ>˜>Jšžœžœ˜ J˜—J˜šŸ œžœžœžœ˜=Jšœ'žœ˜-J˜&Jšžœžœžœžœ˜-J˜Jšœ'žœ˜-šžœžœžœ˜+Jšœ˜Jšœ!žœ˜2Jšœ'žœ˜-Jšœ#˜#Jšœ'žœ˜-Jšœ˜Jšœ)˜)Jšœ'žœ˜-Jšœ˜J˜Jšž˜Jšžœžœžœ ˜Ešžœ˜Jšœžœ˜/Jšœ'žœ˜-J˜J˜——Jšžœ˜ J˜—J˜Jšœ žœ-˜=J˜šŸœžœ˜Jšœ%žœžœžœ˜RJšžœžœ˜J˜Jšœžœ˜ J˜Jšœžœ˜Jšœ?˜?JšœC˜CJ˜Jšžœ žœ/žœ˜GšœD˜DJšœ˜—Jšœ5˜5J˜J˜šžœ žœž˜Jšœ žœžœ˜Jšœžœžœžœ˜IJ˜Jšžœ žœžœžœ˜2J˜—Jšœ žœ˜%Jšžœžœžœ%˜FJ˜J˜Jšœ ˜.J˜Jšžœ žœ/žœ˜GJšœ#žœ˜/J˜ Jšžœžœ˜ J˜—J˜šŸœžœ˜Jšœžœ žœ žœ˜:Jšœžœžœ˜J˜Jšœžœ˜Jšœ#˜#Jšœžœ˜Jšœ žœC˜RL˜Lšœ žœ˜ Lšœžœ.˜8Lšœ(˜(Lšžœ žœ/žœ˜GL˜Lšœ9˜9L˜Lšœ8˜8Lšœ˜Lšœ˜Lšœ\˜\Lšœ˜Lšžœ žœ/žœ˜GLšœ4˜4šžœ5žœ˜=Lšžœžœ žœ˜*Lšœ˜Lšžœžœ žœ˜?L˜L˜—Lšžœžœ žœ˜*Lšœ˜Lšžœžœ-žœžœ˜[L˜—L˜šŸœžœ žœžœ˜FLšœžœQ˜Y—L˜šŸ œžœžœ žœ%˜ZLšœžœ!˜)Lšœ.žœ ˜>L˜šœ ˜ LšœR˜R—Lšœ6žœA˜{L˜—L˜Lš œ žœžœžœžœ ˜(L˜šŸ œžœ˜Lšœ žœ2žœ˜LJšœžœ˜šž˜Jšžœ)žœžœ˜5Jšœ˜šžœžœ˜#J˜ Lšžœ žœ/žœ˜GJ˜ J˜—Jšž˜—J˜L˜—L˜J˜JšœF™FJ˜šœ6˜6Jšœ/˜/—šœ5˜5JšœT˜T—J˜JšœW˜WJšœ^˜^Nšžœ˜——…—Dn\€